midi stuck notes when playing fast. Envelope release issue ?

emmanuel63

Well-known member
Hi,
I would like know if someone can reproduce my problem.
I have a simple 8 voices poly synth, running on a T4.0. I try to make the smallest code I could to help debugging.

When attached to a midi keyboard (real one or from laptop), I get stuck notes when playing lots of notes very fast (I mean some notes are playing forever). Here are some tests results :
- when setting the envelopes release time to 0, there is no stuck notes
- when triggering notes without midi messages (using a MPR121 capacitive module), there is no stuck notes.

I suspect a MIDI problem, because triggering notes with hardware (MPR121) is working fine, even randomly and very fast. I wonder if envelopes release management could cause trouble with reading incoming messages.

Thanks for any help,
Emmanuel

Code:
#include <Audio.h>


// GUItool: begin automatically generated code
AudioSynthWaveformModulated waveformMod7;   //xy=161,471
AudioSynthWaveformModulated waveformMod8;   //xy=163,519
AudioSynthWaveformModulated waveformMod6;   //xy=164,427
AudioSynthWaveformModulated waveformMod5;   //xy=166,375
AudioSynthWaveformModulated waveformMod4;   //xy=168,323
AudioSynthWaveformModulated waveformMod3;   //xy=174,272
AudioSynthWaveformModulated waveformMod2;   //xy=175,222
AudioSynthWaveformModulated waveformMod1;   //xy=179,164
AudioEffectEnvelope      envelope1;      //xy=400,168
AudioEffectEnvelope      envelope2;      //xy=400,221
AudioEffectEnvelope      envelope4;      //xy=401,325
AudioEffectEnvelope      envelope3;      //xy=403,274
AudioEffectEnvelope      envelope5;      //xy=404,374
AudioEffectEnvelope      envelope6;      //xy=407,423
AudioEffectEnvelope      envelope7;      //xy=411,470
AudioEffectEnvelope      envelope8;      //xy=412,517
AudioMixer4              mixer2;         //xy=592,385
AudioMixer4              mixer1;         //xy=594,299
AudioMixer4              mixer3;         //xy=719,330
AudioOutputI2S2          i2s2_1;         //xy=864,331
AudioConnection          patchCord1(waveformMod7, envelope7);
AudioConnection          patchCord2(waveformMod8, envelope8);
AudioConnection          patchCord3(waveformMod6, envelope6);
AudioConnection          patchCord4(waveformMod5, envelope5);
AudioConnection          patchCord5(waveformMod4, envelope4);
AudioConnection          patchCord6(waveformMod3, envelope3);
AudioConnection          patchCord7(waveformMod2, envelope2);
AudioConnection          patchCord8(waveformMod1, envelope1);
AudioConnection          patchCord9(envelope1, 0, mixer1, 0);
AudioConnection          patchCord10(envelope2, 0, mixer1, 1);
AudioConnection          patchCord11(envelope4, 0, mixer1, 3);
AudioConnection          patchCord12(envelope3, 0, mixer1, 2);
AudioConnection          patchCord13(envelope5, 0, mixer2, 0);
AudioConnection          patchCord14(envelope6, 0, mixer2, 1);
AudioConnection          patchCord15(envelope7, 0, mixer2, 2);
AudioConnection          patchCord16(envelope8, 0, mixer2, 3);
AudioConnection          patchCord17(mixer2, 0, mixer3, 1);
AudioConnection          patchCord18(mixer1, 0, mixer3, 0);
AudioConnection          patchCord19(mixer3, 0, i2s2_1, 0);
AudioConnection          patchCord20(mixer3, 0, i2s2_1, 1);
// GUItool: end automatically generated code




const byte polyphony = 8;
AudioSynthWaveformModulated *osc1[] = {&waveformMod1, &waveformMod2, &waveformMod3, &waveformMod4, &waveformMod5, &waveformMod6, &waveformMod7, &waveformMod8};           //OSC1
AudioEffectEnvelope *envelope[] = {&envelope1, &envelope2, &envelope3, &envelope4, &envelope5, &envelope6, &envelope7, &envelope8};

const float noteFreqs[128] = {8.176, 8.662, 9.177, 9.723, 10.301, 10.913, 11.562, 12.25, 12.978, 13.75, 14.568, 15.434, 16.352, 17.324, 18.354, 19.445, 20.602, 21.827, 23.125, 24.5, 25.957, 27.5, 29.135, 30.868, 32.703, 34.648, 36.708, 38.891, 41.203, 43.654, 46.249, 48.999, 51.913, 55, 58.27, 61.735, 65.406, 69.296, 73.416, 77.782, 82.407, 87.307, 92.499, 97.999, 103.826, 110, 116.541, 123.471, 130.813, 138.591, 146.832, 155.563, 164.814, 174.614, 184.997, 195.998, 207.652, 220, 233.082, 246.942, 261.626, 277.183, 293.665, 311.127, 329.628, 349.228, 369.994, 391.995, 415.305, 440, 466.164, 493.883, 523.251, 554.365, 587.33, 622.254, 659.255, 698.456, 739.989, 783.991, 830.609, 880, 932.328, 987.767, 1046.502, 1108.731, 1174.659, 1244.508, 1318.51, 1396.913, 1479.978, 1567.982, 1661.219, 1760, 1864.655, 1975.533, 2093.005, 2217.461, 2349.318, 2489.016, 2637.02, 2793.826, 2959.955, 3135.963, 3322.438, 3520, 3729.31, 3951.066, 4186.009, 4434.922, 4698.636, 4978.032, 5274.041, 5587.652, 5919.911, 6271.927, 6644.875, 7040, 7458.62, 7902.133, 8372.018, 8869.844, 9397.273, 9956.063, 10548.08, 11175.3, 11839.82, 12543.85};


struct voices {
  unsigned int counter;          //use to time-stamp a voice when triggered
  byte note;
};
voices voice[polyphony];



void setup() {
  AudioMemory(500);


  //***INIT OSCILLATORS AND ENVELOPES***//
  for (byte i = 0; i < polyphony; i++) {
    osc1[i]->begin(1, 300, WAVEFORM_SINE);
    envelope[i]->attack(1);
    envelope[i]->release(300);
  }

  //***VOICES SUMMING MIXERS***//
  for (byte mixer_input = 0; mixer_input < 4; mixer_input++) {
    mixer1.gain(mixer_input, 0.1);   //low gain for your ears !
    mixer2.gain(mixer_input, 0.1);
    mixer3.gain(mixer_input, 0.2);
  }

  //****MIDI CALLBACK****//
  usbMIDI.setHandleNoteOn(myNoteOn);
  usbMIDI.setHandleNoteOff(myNoteOff);

}

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


//*********************************************************MIDI NOTE_ON NOTE_OFF*********************************************************//
void myNoteOn(byte MIDI_CHANNEL, byte note, byte velocity) {
  voiceOn(note);
}

void myNoteOff(byte MIDI_CHANNEL, byte note, byte velocity) {
  voiceOff(note);
}




//*********************************************************VOICES FUNCTION*********************************************************//
void voiceOn(byte myNote) {
  for (byte i = 0; i <= polyphony; i++) {       // note " i <= polyphony " to loop to number 8

    //WHEN POLYPHONY IS REACHED, steal oldest voice
    if ( i == polyphony) {
      Serial.println("all voices used");

      //find oldest voice
      unsigned int oldest = 65535;
      unsigned int oldest_voice = 0;
      for (byte j = 0; j < polyphony; j++) {
        if (voice[j].counter < oldest) {
          oldest = voice[j].counter;
          oldest_voice = j;
        }
      }
      //re-trigger oldest voice with new note
      voice[oldest_voice].counter++;
      voice[oldest_voice].note = myNote;
      osc1[oldest_voice]->frequency(noteFreqs[myNote]);

      envelope[oldest_voice]->noteOn();
      return;
    }


    //IF FREE VOICES, find first one
    if (!envelope[i]->isActive()) {
      voice[i].counter++;
      voice[i].note = myNote;
      osc1[i]->frequency(noteFreqs[myNote]);
      envelope[i]->noteOn();
      return;
    }
  }
}

void voiceOff(byte myNote) {
  for (byte i = 0; i < polyphony; i++) {
    if (voice[i].note == myNote) {
      envelope[i]->noteOff();
      voice[i].note = 0;
      break;
    }
  }
}
 
If polyphony is reached, you steal the oldest voice, but you don't turn it off. Just before you retrigger the oldest voice with the new note, use noteOff to turn off the old voice.

Pete
 
Thank you Pete. Unfortunately, it doesn't solve the problem.
I think the problem is due to some kind of MIDI data bottleneck...
Emmanuel
 
Hi Pete,
I did a 2nd try with your idea and it works.
I add a "while" condition to make sure oldest voice is closed before retrigering. It seems to do the job.
Meanwhile, it is still mysterious for me...
Emmanuel

Code:
#include <Audio.h>


// GUItool: begin automatically generated code
AudioSynthWaveformModulated waveformMod7;   //xy=161,471
AudioSynthWaveformModulated waveformMod8;   //xy=163,519
AudioSynthWaveformModulated waveformMod6;   //xy=164,427
AudioSynthWaveformModulated waveformMod5;   //xy=166,375
AudioSynthWaveformModulated waveformMod4;   //xy=168,323
AudioSynthWaveformModulated waveformMod3;   //xy=174,272
AudioSynthWaveformModulated waveformMod2;   //xy=175,222
AudioSynthWaveformModulated waveformMod1;   //xy=179,164
AudioEffectEnvelope      envelope1;      //xy=400,168
AudioEffectEnvelope      envelope2;      //xy=400,221
AudioEffectEnvelope      envelope4;      //xy=401,325
AudioEffectEnvelope      envelope3;      //xy=403,274
AudioEffectEnvelope      envelope5;      //xy=404,374
AudioEffectEnvelope      envelope6;      //xy=407,423
AudioEffectEnvelope      envelope7;      //xy=411,470
AudioEffectEnvelope      envelope8;      //xy=412,517
AudioMixer4              mixer2;         //xy=592,385
AudioMixer4              mixer1;         //xy=594,299
AudioMixer4              mixer3;         //xy=719,330
AudioOutputI2S2          i2s2_1;         //xy=864,331
AudioConnection          patchCord1(waveformMod7, envelope7);
AudioConnection          patchCord2(waveformMod8, envelope8);
AudioConnection          patchCord3(waveformMod6, envelope6);
AudioConnection          patchCord4(waveformMod5, envelope5);
AudioConnection          patchCord5(waveformMod4, envelope4);
AudioConnection          patchCord6(waveformMod3, envelope3);
AudioConnection          patchCord7(waveformMod2, envelope2);
AudioConnection          patchCord8(waveformMod1, envelope1);
AudioConnection          patchCord9(envelope1, 0, mixer1, 0);
AudioConnection          patchCord10(envelope2, 0, mixer1, 1);
AudioConnection          patchCord11(envelope4, 0, mixer1, 3);
AudioConnection          patchCord12(envelope3, 0, mixer1, 2);
AudioConnection          patchCord13(envelope5, 0, mixer2, 0);
AudioConnection          patchCord14(envelope6, 0, mixer2, 1);
AudioConnection          patchCord15(envelope7, 0, mixer2, 2);
AudioConnection          patchCord16(envelope8, 0, mixer2, 3);
AudioConnection          patchCord17(mixer2, 0, mixer3, 1);
AudioConnection          patchCord18(mixer1, 0, mixer3, 0);
AudioConnection          patchCord19(mixer3, 0, i2s2_1, 0);
AudioConnection          patchCord20(mixer3, 0, i2s2_1, 1);
// GUItool: end automatically generated code




const byte polyphony = 8;
AudioSynthWaveformModulated *osc1[] = {&waveformMod1, &waveformMod2, &waveformMod3, &waveformMod4, &waveformMod5, &waveformMod6, &waveformMod7, &waveformMod8};           //OSC1
AudioEffectEnvelope *envelope[] = {&envelope1, &envelope2, &envelope3, &envelope4, &envelope5, &envelope6, &envelope7, &envelope8};

const float noteFreqs[128] = {8.176, 8.662, 9.177, 9.723, 10.301, 10.913, 11.562, 12.25, 12.978, 13.75, 14.568, 15.434, 16.352, 17.324, 18.354, 19.445, 20.602, 21.827, 23.125, 24.5, 25.957, 27.5, 29.135, 30.868, 32.703, 34.648, 36.708, 38.891, 41.203, 43.654, 46.249, 48.999, 51.913, 55, 58.27, 61.735, 65.406, 69.296, 73.416, 77.782, 82.407, 87.307, 92.499, 97.999, 103.826, 110, 116.541, 123.471, 130.813, 138.591, 146.832, 155.563, 164.814, 174.614, 184.997, 195.998, 207.652, 220, 233.082, 246.942, 261.626, 277.183, 293.665, 311.127, 329.628, 349.228, 369.994, 391.995, 415.305, 440, 466.164, 493.883, 523.251, 554.365, 587.33, 622.254, 659.255, 698.456, 739.989, 783.991, 830.609, 880, 932.328, 987.767, 1046.502, 1108.731, 1174.659, 1244.508, 1318.51, 1396.913, 1479.978, 1567.982, 1661.219, 1760, 1864.655, 1975.533, 2093.005, 2217.461, 2349.318, 2489.016, 2637.02, 2793.826, 2959.955, 3135.963, 3322.438, 3520, 3729.31, 3951.066, 4186.009, 4434.922, 4698.636, 4978.032, 5274.041, 5587.652, 5919.911, 6271.927, 6644.875, 7040, 7458.62, 7902.133, 8372.018, 8869.844, 9397.273, 9956.063, 10548.08, 11175.3, 11839.82, 12543.85};


struct voices {
  unsigned int counter;          //use to time-stamp a voice when triggered
  byte note;
};
voices voice[polyphony];

const int myRelease = 200;



void setup() {
  AudioMemory(500);


  //***INIT OSCILLATORS AND ENVELOPES***//
  for (byte i = 0; i < polyphony; i++) {
    osc1[i]->begin(1, 300, WAVEFORM_SINE);
    envelope[i]->attack(10);
    envelope[i]->release(myRelease);
  }

  //***VOICES SUMMING MIXERS***//
  for (byte mixer_input = 0; mixer_input < 4; mixer_input++) {
    mixer1.gain(mixer_input, 0.1);   //low gain for your ears !
    mixer2.gain(mixer_input, 0.1);
    mixer3.gain(mixer_input, 0.1);
  }

  //****MIDI CALLBACK****//
  usbMIDI.setHandleNoteOn(myNoteOn);
  usbMIDI.setHandleNoteOff(myNoteOff);

}

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


//*********************************************************MIDI NOTE_ON NOTE_OFF*********************************************************//
void myNoteOn(byte MIDI_CHANNEL, byte note, byte velocity) {
  voiceOn(note);
}

void myNoteOff(byte MIDI_CHANNEL, byte note, byte velocity) {
  voiceOff(note);
}




//*********************************************************VOICES FUNCTION*********************************************************//
void voiceOn(byte myNote) {
  for (byte i = 0; i <= polyphony; i++) {       // note " i <= polyphony " to loop to number 8


    //IF FREE VOICES, find first one
    if (i < polyphony) {
      if (!envelope[i]->isActive()) {
        voice[i].counter++;
        voice[i].note = myNote;
        osc1[i]->frequency(noteFreqs[myNote]);
        envelope[i]->noteOn();
        Serial.print("voice = "); Serial.println(i);
        return;
      }
    }

    //WHEN POLYPHONY IS REACHED, steal oldest voice
    if (i == polyphony) {
      Serial.println("all voices used");

      //find oldest voice
      unsigned int oldest = 65535;
      unsigned int oldest_voice = 0;
      for (byte j = 0; j < polyphony; j++) {
        if (voice[j].counter < oldest) {
          oldest = voice[j].counter;
          oldest_voice = j;
        }
      }
      //re-trigger oldest voice with new note
      voice[oldest_voice].counter++;
      voice[oldest_voice].note = myNote;
      osc1[oldest_voice]->frequency(noteFreqs[myNote]);

      envelope[oldest_voice]->release(0);
      envelope[oldest_voice]->noteOff();

      while (envelope[oldest_voice]->isActive()) {
        //it seems closing the envelope takes time, even wit release = 0
      }
      
      envelope[oldest_voice]->noteOn();
      envelope[oldest_voice]->release(myRelease);
      Serial.print("stealed voice = "); Serial.println(oldest_voice);
    }
  }

}

void voiceOff(byte myNote) {
  for (byte i = 0; i < polyphony; i++) {
    if (voice[i].note == myNote && envelope[i]->isActive()) {
      envelope[i]->noteOff();
      voice[i].note = 0;
      break;
    }
  }
}
 
Back
Top