MIDI all notes off

Frukost

Well-known member
I have built a sequencer, but I have trouble making the all notes off message work as intended when it stops. What happens is that i get hanging notes after the sequencer stops. Not sure where I fail.

The gist of my code is that whenever I stop my sequencer, the allChannelsAllNotesOff() function is called. I've triple checked the midi command value for the all notes off message... All the other messages are working as expected. Happy if someone could look at my code!

Code:
[B]MIDI.cpp[/B]

void MIDI::noteOff(const uint8_t voiceNumber) {
    using namespace constants;

    Serial1.write(MIDInoteOff + voiceNumber);
    Serial1.write(m_previousMIDINote[voiceNumber]);
    Serial1.write(0);
}

void MIDI::allNotesOff(const uint8_t voiceNumber) {
    using namespace constants;

    Serial1.write(MIDIchannel + voiceNumber);  // Channel CC message
    Serial1.write(MIDIallNotesOff);
    Serial1.write(0);
}

void MIDI::allChannelsAllNotesOff() {
    using namespace constants;

    for (uint8_t voiceNumber = voice1; voiceNumber <= voice4; ++voiceNumber) {
        allNotesOff(voiceNumber);
    }
}


[B]Constants.h[/B]

constexpr uint8_t MIDIstart = 0xFA;        // 250
constexpr uint8_t MIDIstop = 0xFC;         // 252
constexpr uint8_t MIDInoteOn = 0x90;       // 144
constexpr uint8_t MIDInoteOff = 0x80;      // 128
constexpr uint8_t MIDIchannel = 0xB0;      // 176
constexpr uint8_t MIDIallNotesOff = 0x7B;  // 123

enum voiceNumbers {
    voice1,
    voice2,
    voice3,
    voice4,
    allVoices
};
 
The sequencer output goes to a synthesizer, correct?
Could it be that the synthesizer does not support the Channel Mode Message: All Notes Off?
(your partial code looks OK to me)

Paul
 
Thanks for your feedback. Yes, synthesizer.

I checked the manual for one of the synths I'm using, and it does in fact confirm that there isn't any support for all notes off messages. For another one of my synths, it's allegedly accepting all notes off messages according to other users. I still get hanging notes, so I think I'll try contacting their support next.
 
In this bit
Code:
for (uint8_t voiceNumber = voice1; voiceNumber <= voice4; ++voiceNumber) {
        allNotesOff(voiceNumber);
    }
The synth may not have completed silencing all notes in voice1 before allNotesOff(voice2) is sent so a delay may be worth playing with.

Ears pricked up after wrestling code for a sequencer for days, just got it working so posting the guts as food for thought.
Code:
void Seq2(int trkIndex) {
  // Called by a Timer
  // Prepare the sequence
  if (TrackData[trkIndex].IsRunning != false) {
    if (TrackData[trkIndex].NoteSent == false) {
      TrackData[trkIndex].NoteSent = true; // so we don't come back here if the seq is started
      if (TrackNoteData[trkIndex][CurrStep[trkIndex]].Active == true) { // the step might have been muted
        // this chooses per Track vel or per step vel
        if (TrackData[trkIndex].VelByTrack == true) {
          byte vel = TrackData[trkIndex].Vel;
          MIDI.sendNoteOn(TrackNoteData[trkIndex][CurrStep[trkIndex]].NoteNum, vel, TrackData[trkIndex].MidiChannel);
        }
        else if (TrackData[trkIndex].VelByTrack != true) {
          byte vel = TrackNoteData[trkIndex][CurrStep[trkIndex]].NoteVel;
          MIDI.sendNoteOn(TrackNoteData[trkIndex][CurrStep[trkIndex]].NoteNum, vel, TrackData[trkIndex].MidiChannel);
        }
      }
    }
    TrackData[trkIndex].TickCount++;
    if (TrackData[trkIndex].TickCount == TrackData[trkIndex].NoteLength) {
      // yet to add per Track  or per Step Note Length 
      if (TrackNoteData[trkIndex][CurrStep[trkIndex]].Active == true) { // the step might have been muted
        MIDI.sendNoteOff(TrackNoteData[trkIndex][CurrStep[trkIndex]].NoteNum, 0, TrackData[trkIndex].MidiChannel);
      }
    }
  }
  if (TrackData[trkIndex].TickCount >= 95) { // thinking of 96 ticks per note
    TrackData[trkIndex].NoteSent = false;
    TrackData[trkIndex].TickCount = 0;
    StepCount[trkIndex]++;
    CurrStep[trkIndex] = StepCount[trkIndex];
    //CurrStep and StepCount have same value here, playing around with FirstStep, LastStep
    //and variable Start step. The goal is to be able to create a base to implement Euclidean
    //or "normal" linear sequences like eg. Beatstep Pro

    // Now, this bit makes us deaf to the Halt command until the current Step is complete
    if (TrackData[trkIndex].Halt == true) {
      TrackData[trkIndex].IsRunning = false;
    }
    if (StepCount[trkIndex] > TrackData[trkIndex].LastStep) {
      // Now, the Sequence has completed then repeat
      TrackData[trkIndex].TickCount = 0;
      StepCount[trkIndex] = TrackData[trkIndex].FirstStep;
      CurrStep[trkIndex] = StepCount[trkIndex];
    }
  }
}
:)
 
The synth may not have completed silencing all notes in voice1 before allNotesOff(voice2) is sent so a delay may be worth playing with.
Interesting suggestion! I tried just now, but unfortunately there was no difference. As far as I understand the Serial.port buffers the data, and the transmit speed is set to midi standard.
 
From the MIDI 1.0 spec (my emphasis):

All Notes Off (123) is a mode message which provides an efficient method of turning off all voices turned
on via MIDI. While this message is useful for some applications, there is no requirement that a receiver
recognize it. Since recognition of All Notes Off is not required of the receiver, all notes should first be
turned off by transmitting individual Note-Off messages prior to sending an All Notes Off.

So it is efficient if implemented but the safe and secure method, assuming you have a list of notes turned on, is (sadly) sending all the corresponding Note-Off messages.

The specification wording is classic post-hoc standardization:

Receivers should ignore an All Notes Off message while Omni is on (Modes 1 & 2). For example, if a
receiver gets an All Notes Off from a sequencer that inserts such messages whenever all keys are
released on a track, and two tracks were recorded on such a sequencer (even on different MIDI
channels), the All Notes Off message would cut off sustaining notes recorded on the other track.

While MIDI devices should be able to respond to the All Notes Off message, an All Notes Off message
should not be sent periodically as part of normal operation. This message should only be used to indicate
that the entire MIDI system is "at rest" (i.e. when a sequence has stopped). However, a receiver should
respond to an All Notes Off (unless Omni is on) whenever it is received, even when the system is not "at
rest".
 
I did not know this, and this solved the problem. Thank you!

It seems that sending individual note off messages prior to sending the all notes off command makes the latter completely redundant, doesn't it?
 
Yes, it does. This has the feel of "great new idea, nice in theory", spoiled by backwards compatibility and divergent implementations in practice.

Also the "not in OMNI mode" caveat at least has some justification.
 
Back
Top