MIDI stream getting stomped

I’m expanding my working Daisy Seed monosynth/sequencer to be duophonic. With an additional voice added, the incoming Midi stream is getting corrupted. I’m hoping that some kind soul who has more experience with Daisy than I do (just over 10 days now!) can tell me what I’m missing, and how I might fix this. I’ve boiled my code down to a minimal sample that produces the problem. I do notice that if I reduce processing by, for example, removing the LFO, the problem becomes much less severe. Thanks in advance for any help you can offer.

Here’s the input Midi data (channel, note, velocity), produced by an Arduino Mega-based sequencer. Pairs of [channel 1, channel 2] messages are sent, about 3-4 pairs per second.

1, 36, 127
2, 36, 127
1, 36, 127
2, 36, 127
1, 36, 127
2, 36, 127
1, 36, 127
2, 36, 127
1, 36, 127
2, 36, 127
1, 36, 127
2, 36, 127

Here’s a sample of Midi as received and printed by my DaisyDuino code:

1, 36, 127
2, 36, 127
1, 127, 145
1, 36, 127
1, 36, 127
2, 36, 144
2, 36, 127
2, 36, 127
1, 36, 145
1, 36, 127
1, 36, 127
2, 36, 127

And here’s the code:

#include <MIDI.h>
#include "DaisyDuino.h"

const uint8_t NUM_VOICES = 2;

MoogLadder flt[NUM_VOICES];
Oscillator osc[NUM_VOICES];
Oscillator lfo[NUM_VOICES];
AdEnv ampEnv[NUM_VOICES];

MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI1);

void handleNoteOn(uint8_t channel, uint8_t pitch, uint8_t velocity)  {
  Serial.print(channel);
  Serial.print(F(", "));
  Serial.print(pitch);
  Serial.print(F(", "));
  Serial.println(velocity);

  osc[channel - 1].SetFreq(mtof(pitch));
  ampEnv[channel - 1].Trigger();
}

void audioCallback(float **in, float **out, size_t size)  {
  for (size_t i = 0; i < size; i++)  {
    float totalOscSample = 0;
    for (uint8_t voice = 0; voice < NUM_VOICES; ++voice)  {
      float sample = lfo[voice].Process() *
                     ampEnv[voice].Process() *
                     flt[voice].Process(osc[voice].Process());
      totalOscSample += sample;
    }
    out[0][i] = totalOscSample;
    out[1][i] = totalOscSample;
  }
}

void setup()  {
  Serial.begin(9600);
  delay(1000);

  DAISY.init(DAISY_SEED, AUDIO_SR_48K);
  float samplerate = DAISY.get_samplerate();

  for (uint8_t voice = 0; voice < NUM_VOICES; ++voice) {
    osc[voice].Init(samplerate);
    osc[voice].SetWaveform(Oscillator::WAVE_SAW);
    flt[voice].Init(samplerate);
    lfo[voice].Init(samplerate);
    ampEnv[voice].Init(samplerate);
  }

  DAISY.begin(audioCallback);

  MIDI1.setHandleNoteOn(handleNoteOn);
  MIDI1.begin(MIDI_CHANNEL_OMNI);  // Listen to all incoming messages
}

void loop()  {
  MIDI1.read();
}

I’m not super familiar with how the Arduino MIDI library works, but what it looks like is happening is that there is too much time being spent in the audio callback, with not enough time to listen to the MIDI input (via MIDI1.read()).

With the upcoming DaisyDuino update we’re working on you’ll be able to adjust the block size of the audio callback, which would potentially free up enough time between calls to handle the MIDI.

There’s also an upcoming fix to the core stm32 library for daisy that will allow the data-cache to be enabled, and that will add a decent boost to performance, and could resolve this issue on its own. Not sure exactly when that will go live, but I have roughly documented the steps for implementing that improvement in this thread.

Something you can try in the meantime would be to restructure your program to only copy the audio buffer in the AudioCallback, and process it in the main loop. Something like:

pseudo-code:

float synth_buffer[48];
bool fill_buffer_flag;

void AudioCallback(**in, **out,  size)
{
    for (size_t i = 0; i < size; i++)
        output[0][i] = output[1][i] = synth_buffer[i];
    fill_buffer_flag = true;
}

void loop()
{
    midi1.read();
    if (fill_buffer_flag)
    {
        for (size_t i = 0; i < 48; i++)
            // Proccess your synth stuff.
        fill_buffer_flag = false;
    }
}

This may prevent the midi.read() from getting missed, but it could result in buffer underruns in your audio path if your processing loop is too busy, or the MIDI input is for some reason slow to read.

Data cache update seems to have completely fixed the problem! Thanks very much, Stephen :slight_smile: