Proposed Synth improvement: monophonic note memory


#1

 

Wanted to share this, since I figure it might be a good addition to the synthesiser class, or might at least help out someone else who's trying to solve the same problem.

When a synth is monophonic, users will expect it to remember held notes. When you lift one key, the synth will play back the previously held note. It's pretty simple to accomplish this by using a Juce Array to hold onto Note On messages then clear them when Note Off messages come through. Then if there is only one voice, the synth can trigger the voice to play previously held notes.

Having a list of held notes is useful for other purposes too -- I'm planning on doing an arpeggiator and adding legato next, so this was an important step towards those features. So anyway, I hope you'll consider adding something like this, I think it's general enough that should probably be a part of the synthesiser class.

Here's my first pass at doing this (anyone, feel free to suggest how this could be better! I'm not a veteran programmer so no doubt this could be refined!). 

 

void Synthesiser::handleMidiEvent (const MidiMessage& m)

{

    if (m.isNoteOn())

    {

        //add held notes to a Juce Array so we can use for note memory, arpeggiator or other uses

        heldNotesList.add(m);

        

        //send the note on to a voice

        noteOn (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity());

    }

    else if (m.isNoteOff())

    {

        const int noteNumberToRemove = m.getNoteNumber();

        

        for (int i=0; i < heldNotesList.size(); ++i) //find the released note in the notes list

        {

            //if there is only one voice (monophonic operation) play a previously held note

            if (getNumVoices() == 1 && heldNotesList[i].getNoteNumber() == noteNumberToRemove)

            {

                //can't compare MidiMessages, so comparing their note numbers instead.

                int lastInList = heldNotesList.getLast().getNoteNumber();

                

                //if the note removed was the last in the list, play a previously held note (if any)

                if (noteNumberToRemove == lastInList)

                {

                    heldNotesList.removeLast(1);

                    

                    if (heldNotesList.size() > 0)

                    {

                        const MidiMessage previousNote = heldNotesList.getLast();

                        noteOn (previousNote.getChannel(), previousNote.getNoteNumber(), previousNote.getFloatVelocity() );

                    }

                }

                else heldNotesList.remove(i);

            }

        }

        //send note off message to corresponding voice

        noteOff (m.getChannel(), m.getNoteNumber(), true);

    }

//rest of midi handling stuff

}

Nick