Help needed with teensy-synth polyphony!

Status
Not open for further replies.

albnys

Member
I´m currently building my first digital synth using a teensy 3.2 with the teensy audio library.
It´s coming along pretty well but I need help to make it polyphonic. I´ve tried to analyze the code of other
polyphonic teensy synths, but since I´m pretty new to coding, I can´t quite figure out how it´s done.

Would somebody be willing to share how you make your synths polyphonic? :)
Thanks!
 
The way this is usually done is by having a multiple set of identical flowgraphs, each being able to
generate one note and do its envelope, filtering etc.

Stick references to these in an array or arrays.

As notes start you have to dynamically allocate an index for each note, and use it to initialize that
instance for the particular note. As notes end you can recycle indexes (typically delayed for the envelop
decay period).

So a datastructure is needed to keep track of active notes and the mapping to indexes.
 
I first did it manually
By creating a Voice class
which contained the different Audio Objects
3 AudioSynthWaveform:s
1 WaveTable (that was implemented later)

one mixer + envelope

one problem with classes is that the AudioConnections cannot be instanced the same way as before
they need to be instanced in the class constructor
by using pointers (the simplest is to create it as an array) then it's easier to extend if more connections need to be used

here is my Voice class
Code:
class Voice
{
 public:
    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};
    
    byte note = 0;
    byte isNoteOn = 0;
    byte isSustain = 0;
    byte oscApitchMult = 64; // set at middle
    byte oscBpitchMult = 64; // set at middle
    byte oscCpitchMult = 64; // set at middle
    float newAmp = 0.0;
    
    AudioSynthWaveform               oscA;
    AudioSynthWaveform               oscB;
    AudioSynthWaveform               oscC;
    AudioSynthWavetable              waveTable;
    AudioMixer4                      mix;
    AudioEffectEnvelope              env;
    AudioConnection                  *patchCord[5]; // total patchCordCount:5 including array typed ones.

    Voice() // constructor (this is called when class-object is created)
    {
        int pci = 0; // used only for adding new patchcords


        patchCord[pci++] = new AudioConnection(oscA, 0, mix, 0);
        patchCord[pci++] = new AudioConnection(oscB, 0, mix, 1);
        patchCord[pci++] = new AudioConnection(oscC, 0, mix, 2);
        patchCord[pci++] = new AudioConnection(waveTable, 0, mix, 3);
        patchCord[pci++] = new AudioConnection(mix, 0, env, 0);
    }

    void begin()
    {
    
    }
    
    
    /*
     * this takes care of all the tasks that
     * needs to be taken care of when doing
     * a note on/off
     */
     
    void noteOn(byte Note, byte velocity)
    {
        float newAmp = 0.0f;
        if (Note >= sizeof(noteFreqs)) return;
        
        note = Note;
        isNoteOn = 1;
        
        newAmp = (float)velocity*(1.0f / 127.0f);
          
          oscA.frequency(GetBendedFreq(oscApitchMult));
          oscB.frequency(GetBendedFreq(oscBpitchMult));
          oscC.frequency(GetBendedFreq(oscCpitchMult));
    
          oscA.amplitude(newAmp);
          oscB.amplitude(newAmp);
          oscC.amplitude(newAmp);
    
          waveTable.playNote(note, velocity);
          
          env.noteOn();
    }
    
    void noteOff()
    {
        isNoteOn = 0;
        if (!isSustain)
        {
            env.noteOff();
            waveTable.stop();
        }
    }
    
    bool isNotPlaying()
    {
        if (!env.isActive())
            return true;
        else if(!waveTable.isPlaying())
            return true;
        else
            return false;
    }
    
    float GetBendedFreq(byte pitchMult)
    {
        if (pitchMult < 64)
            return noteFreqs[note - 12*(64-pitchMult)];
        else if (pitchMult > 64)
            return noteFreqs[note + 12*(pitchMult-64)];
        else
            return noteFreqs[note];
    }
    
};

it's then used in this
https://github.com/manicken/teensy4polysynth2/blob/main/src/Synth.h

simplified
Code:
class Synth
{
 public:
    AudioInputUSB                    usb1;
    Voice                            voices[80];
    AudioMixer<80>                   mixVoices; // this is a "c++ template" mixer which can take any amount of inputs
    AudioOutputPT8211_2              pt8211_2_1;
    AudioOutputUSB                   usb2;
    AudioConnection                  *patchCord[84]; // total patchCordCount:84 including array typed ones.

Synth() // constructor (this is called when class-object is created)
    {
        int pci = 0; // used only for adding new patchcords


        patchCord[pci++] = new AudioConnection(mixVoices, 0, pt8211_2_1, 0);
        patchCord[pci++] = new AudioConnection(mixVoices, 0, pt8211_2_1, 1);
        patchCord[pci++] = new AudioConnection(mixVoices, 0, usb2, 0);
        patchCord[pci++] = new AudioConnection(mixVoices, 0, usb2, 1);
        for (int i = 0; i < 80; i++)
        {
            // here the public instances of Voice can be accessed 
            patchCord[pci++] = new AudioConnection(voices[i].env, 0, mixVoices, i);
        }
    }


    void noteOn(byte note, byte velocity)
    {
        digitalWrite(NOTE_PRESSED_STATE_LED, HIGH); //any note "pressed"
        // fist checks if this note is already playing
        // it that is the case then it "reuses" this "slot"
        // this makes sure that not all "slots" is filled
        // with the same playing note
        // if the MIDI keyboard is for some reason
        // not sending a noteoff (my keyboard is sometimes glitching)
        // and when sending MIDI from my computer for fast playing songs
        for (int i = 0; i < VOICE_COUNT; i++) 
        {
            // first check if the note was played recently
            if (voices[i].note == note) 
            {
                voices[i].noteOn(note, velocity);
                digitalWrite(NOTE_OVERFLOWN_LED, LOW);
                return; 
            }
        }
        // then if the note has not allready been played
        // // second see if there is any free "spot"
        for (int i = 0; i < VOICE_COUNT; i++) 
        {
            if (voices[i].isNotPlaying())
            {
                voices[i].noteOn(note, velocity);
                //digitalWrite(NOTE_OVERFLOWN_LED, LOW); // clear overflown notification
                return;
            }
        }
        digitalWrite(NOTE_OVERFLOWN_LED, HIGH); // this is a notification that there was no free spots
    }
}

that Synth class can then easly be instanced in the "main" code file
and the noteOn noteOff can be called from there



But then I realized that it was very tedious to change the structure of the whole design
for small changes, and to put new objects in there.

so I did some changes to the Design Tool to generate everything(Audio Structure) for me
in a class hierarchy structure

You can still use my example
https://github.com/manicken/teensy4polysynth2/tree/main/src
to create the design manually

But I recommend to use the new Tool
https://manicken.github.io/

a whole thread just for that Tool
https://forum.pjrc.com/threads/65740-Audio-System-Design-Tool-update
 
Status
Not open for further replies.
Back
Top