Correct way to create a unison or octave based voice allocation function?

I am using Synthesiser class and wondering what the correct way to set up a unison or copied and pitch shifted voice allocation function.

ie.

  • If I press E3 on a keyboard and want it to be allocated to TWO voices (so there are two voices playing E3 identically now)?
  • Or If I press E3 on a keyboard and want it to be allocated to E3 and E3 + x pitch increments, (eg. E3 & E3 +12 = E4) say for an octave effect?

Is this the best function to override?

void Synthesiser::noteOn (const int midiChannel,
                          const int midiNoteNumber,
                          const float velocity)
{
    const ScopedLock sl (lock);

    for (auto* sound : sounds)
    {
        if (sound->appliesToNote (midiNoteNumber) && sound->appliesToChannel (midiChannel))
        {
            // If hitting a note that's still ringing, stop it first (it could be
            // still playing because of the sustain or sostenuto pedal).
            for (auto* voice : voices)
                if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel))
                    stopVoice (voice, 1.0f, true);

            startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes),
                        sound, midiChannel, midiNoteNumber, velocity);
        }
    }
}

Could I simply add another stopVoice and startVoice function to it, like this:

void Synthesiser::noteOn (const int midiChannel,
                          const int midiNoteNumber,
                          const float velocity)
{
    const ScopedLock sl (lock);

    for (auto* sound : sounds)
    {
        if (sound->appliesToNote (midiNoteNumber) && sound->appliesToChannel (midiChannel))
        {
            // If hitting a note that's still ringing, stop it first (it could be
            // still playing because of the sustain or sostenuto pedal).
            for (auto* voice : voices)
                if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel))
                    stopVoice (voice, 1.0f, true);


            for (auto* voice : voices)
                if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel))
                    stopVoice (voice, 1.0f, true);


            startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes),
                        sound, midiChannel, midiNoteNumber, velocity);

 startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes),
                            sound, midiChannel, midiNoteNumber, velocity);
        }
    }
}

And then it would start two voices with the same pitch and midi information? Or if I wanted the pitch or velocity altered in some way I could add increments or multipliers to this second stopVoice/startVoice functions so the doubled voice is slightly different.

Does that make sense?

The problem I am running into with this is with trying to have it allocate a unison voice. Because for example, in the above case, once the first startVoice runs, it will allocate to a certain voice, and then when the second startVoice runs, it will run findVoiceToSteal which includes this:

// The oldest note that's playing with the target pitch is ideal..
    for (auto* voice : usableVoices)
        if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
            return voice; 

And thus it will just re-steal the voice that it just allocated the first note copy to. And I will still have only one voice playing a given note.

Looking at it, I’m not even sure why findVoiceToSteal needs to have this bit in it, because the noteOn function is stopping any voice with the same pitch as the new incoming note, so by the time findVoicetoSteal runs, there shouldn’t even be another note existing with the same midiNoteNumber.

If that’s the case, then I could just delete that bit from findVoiceToSteal and use the double allocation method in noteOn and maybe that will work?

Thoughts?

Thanks