Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 9 of 9

Thread: Issue accessing class properly in AudioSynthWaveform array

  1. #1

    Issue accessing class properly in AudioSynthWaveform array

    Hello,

    I am having a seemingly simple issue trying get polyphony from the Notes and Volts synth using the MidiSynth example. The issue I am running into is assigning AudioSynthWaveforms to an array of them... when I try to using classes from members of the array, which should be exactly the same as using the classes from the waveform object outside the array, it just stops working. I have attached the code in question. In the oscPlay() function you can comment out the section of code that is active and uncomment the inactive to see the problem.

    C++ isn't my native language, but I can't for the life of me figure what is causing it to break. Any help would be appreciated.

    Thanks for your time!

    Code:
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    
    const int TOTAL_VOICES = 4;
    
    // GUItool: begin automatically generated code
    AudioSynthNoisePink      pink1;          //xy=55,87
    AudioSynthNoisePink      pink2; //xy=55,212
    AudioSynthNoisePink      pink3; //xy=55,339
    AudioSynthNoisePink      pink4;          //xy=55,464
    AudioSynthWaveform       waveform1;      //xy=64,21
    AudioSynthWaveform       waveform2;      //xy=64,54
    AudioSynthWaveform       waveform4; //xy=64,146
    AudioSynthWaveform       waveform3; //xy=64,179
    AudioSynthWaveform       waveform5; //xy=64,273
    AudioSynthWaveform       waveform6; //xy=64,306
    AudioSynthWaveform       waveform7;      //xy=64,398
    AudioSynthWaveform       waveform8;      //xy=64,431
    AudioMixer4              mixer1;         //xy=199,61
    AudioMixer4              mixer2; //xy=199,185
    AudioMixer4              mixer3; //xy=199,313
    AudioMixer4              mixer4;         //xy=199,437
    AudioFilterStateVariable filter1;        //xy=320,67
    AudioFilterStateVariable filter3; //xy=319,319
    AudioFilterStateVariable filter2; //xy=323,191
    AudioFilterStateVariable filter4;        //xy=323,443
    AudioEffectEnvelope      envelope1;      //xy=460,54
    AudioEffectEnvelope      envelope2; //xy=460,307
    AudioEffectEnvelope      envelope3; //xy=461,178
    AudioEffectEnvelope      envelope4;      //xy=461,430
    AudioMixer4              mixer5;         //xy=653,244
    AudioOutputI2S           i2s1;           //xy=779,245
    AudioConnection          patchCord1(pink1, 0, mixer1, 2);
    AudioConnection          patchCord2(pink2, 0, mixer2, 2);
    AudioConnection          patchCord3(pink3, 0, mixer3, 2);
    AudioConnection          patchCord4(pink4, 0, mixer4, 2);
    AudioConnection          patchCord5(waveform1, 0, mixer1, 0);
    AudioConnection          patchCord6(waveform2, 0, mixer1, 1);
    AudioConnection          patchCord7(waveform4, 0, mixer2, 0);
    AudioConnection          patchCord8(waveform3, 0, mixer2, 1);
    AudioConnection          patchCord9(waveform5, 0, mixer3, 0);
    AudioConnection          patchCord10(waveform6, 0, mixer3, 1);
    AudioConnection          patchCord11(waveform7, 0, mixer4, 0);
    AudioConnection          patchCord12(waveform8, 0, mixer4, 1);
    AudioConnection          patchCord13(mixer1, 0, filter1, 0);
    AudioConnection          patchCord14(mixer2, 0, filter2, 0);
    AudioConnection          patchCord15(mixer3, 0, filter3, 0);
    AudioConnection          patchCord16(mixer4, 0, filter4, 0);
    AudioConnection          patchCord17(filter1, 0, envelope1, 0);
    AudioConnection          patchCord18(filter3, 0, envelope3, 0);
    AudioConnection          patchCord19(filter2, 0, envelope2, 0);
    AudioConnection          patchCord20(filter4, 0, envelope4, 0);
    AudioConnection          patchCord21(envelope1, 0, mixer5, 0);
    AudioConnection          patchCord22(envelope3, 0, mixer5, 2);
    AudioConnection          patchCord23(envelope2, 0, mixer5, 1);
    AudioConnection          patchCord24(envelope4, 0, mixer5, 3);
    AudioConnection          patchCord25(mixer5, 0, i2s1, 0);
    AudioConnection          patchCord26(mixer5, 0, i2s1, 1);
    AudioControlSGTL5000     sgtl5000_1;     //xy=1023,20
    // GUItool: end automatically generated code
    
    AudioSynthNoisePink noiseBank[] = {pink1, pink2, pink3, pink4};
    AudioSynthWaveform oscBank1[] = {waveform1, waveform3, waveform5, waveform7};
    AudioSynthWaveform oscBank2[] = {waveform2, waveform4, waveform6, waveform8};
    AudioFilterStateVariable filterBank[] = {filter1, filter2, filter3, filter4};
    AudioEffectEnvelope envelopeBank[] = {envelope1, envelope2, envelope3, envelope4};
    AudioMixer4 oscMixerBank[] = {mixer1, mixer2, mixer3, mixer4};
    
    //from midisynth
    
    struct voice_t {
      int wavetable_id;
      byte channel;
      byte note;
    };
    voice_t voices[TOTAL_VOICES];
    
    const byte BUFFER = 8; //Size of keyboard buffer
    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 globalNote = 0;
    byte globalVelocity = 0;
    int octave = 0;
    const float DIV127 = (1.0 / 127.0);
    float detuneFactor = 1;
    float bendFactor = 1;
    int bendRange = 12;
    
    unsigned int LFOspeed = 2000;
    float LFOpitch = 1;
    float LFOdepth = 0;
    byte LFOmodeSelect = 0;
    
    int FILfreq =  10000;
    float FILfactor = 1;
    
    
    void setup() {
      Init();
    }
    
    
    void loop() {
      usbMIDI.read();
      LFOupdate(false, LFOmodeSelect, FILfactor, LFOdepth);
    }
    
    int allocateVoice(byte channel, byte note);
    int findVoice(byte channel, byte note);
    void freeVoices();
    
    int used_voices = 0;
    int stopped_voices = 0;
    int evict_voice = 0;
    int notes_played = 0;
    
    void myNoteOn(byte channel, byte note, byte velocity) {
      if ( note > 23 && note < 108 ) {
        globalNote = note;
        globalVelocity = velocity;
        //keyBuff(note, true);
        oscPlay(note);
        LFOupdate(true, LFOmodeSelect, FILfactor, LFOdepth);
      }
    }
    
    void myNoteOff(byte channel, byte note, byte velocity) {
      if ( note > 23 && note < 108 ) {
        //keyBuff(note, false);
        oscStop();
      }
    }
    
    void myPitchBend(byte channel, int bend) {
      float bendF = bend;
      bendF = bendF / 8192;
      bendF = bendF * bendRange;
      bendF = bendF / 12;
      bendFactor = pow(2, bendF);
      oscSet();
    }
    
    void keyBuff(byte note, bool playNote) {
      static byte buff[BUFFER];
      static byte buffSize = 0;
      /*
    
        // Add Note
        if (playNote == true && (buffSize < BUFFER) ) {
        oscPlay(note);
        buff[buffSize] = note;
        buffSize++;
        return;
        }
    
        // Remove Note
        else if (playNote == false && buffSize != 0) {
        for (byte found = 0; found < buffSize; found++) {
          if (buff[found] == note) {
            for (byte gap = found; gap < (buffSize - 1); gap++) {
              buff[gap] = buff[gap + 1];
            }
            buffSize--;
            buff[buffSize] = 255;
            if (buffSize != 0) {
              oscPlay(buff[buffSize - 1]);
              return;
            }
            else {
              oscStop();
              return;
            }
          }
        }
        }
      */
    }
    
    void oscPlay(byte note) {
    
      waveform1.frequency(noteFreqs[note] * bendFactor * LFOpitch);
      waveform2.frequency(noteFreqs[note + octave] * detuneFactor * bendFactor * LFOpitch);
      float velo = (globalVelocity * DIV127);
      waveform1.amplitude(velo);
      waveform2.amplitude(velo);
      pink1.amplitude(velo);
    
      envelope1.noteOn();
      /*
        oscBank1[0].frequency(noteFreqs[note] * bendFactor * LFOpitch);
        oscBank2[0].frequency(noteFreqs[note + octave] * detuneFactor * bendFactor * LFOpitch);
        float velo = (globalVelocity * DIV127);
        oscBank1[0].amplitude(velo);
        oscBank2[0].amplitude(velo);
        noiseBank[0].amplitude(velo);
    
        envelopeBank[0].noteOn();
    
        Serial.print("Velocity in OscPlay: ");
        Serial.print(velo);
        Serial.println();
        Serial.print("envelope is active after noteOn: ");
        Serial.print(envelopeBank[0].isActive());
        Serial.println();
      */
    }
    
    void oscStop() {
      envelope1.noteOff();
      //envelopeBank[0].noteOff();
    }
    
    void oscSet() {
      oscBank1[0].frequency(noteFreqs[globalNote] * bendFactor * LFOpitch);
      oscBank2[0].frequency(noteFreqs[globalNote + octave] * detuneFactor * bendFactor * LFOpitch);
    }
    
    void myControlChange(byte channel, byte control, byte value) {
      switch (control) {
        case 100:
          mixer1.gain(0, (value * DIV127));
          break;
    
        case 101:
          mixer1.gain(1, (value * DIV127));
          break;
    
        case 102:
          mixer1.gain(2, (value * DIV127));
          break;
    
        case 103:
          switch (value) {
            case 0:
              octave = 24;
              break;
            case 1:
              octave = 12;
              break;
            case 2:
              octave = 0;
              break;
            case 3:
              octave = -12;
              break;
            case 4:
              octave = -24;
              break;
          }
          oscSet();
          break;
    
        case 104:
          envelope1.attack(3000 * (value * DIV127));
          break;
    
        case 105:
          envelope1.decay(3000 * (value * DIV127));
          break;
    
        case 106:
          envelope1.sustain(value * DIV127);
          break;
    
        case 107:
          envelope1.release(3000 * (value * DIV127));
          break;
    
        case 108:
          switch (value) {
            case 0:
              waveform1.begin(WAVEFORM_SINE);
              break;
            case 1:
              waveform1.begin(WAVEFORM_TRIANGLE);
              break;
            case 2:
              waveform1.begin(WAVEFORM_SAWTOOTH);
              break;
            case 3:
              waveform1.begin(WAVEFORM_PULSE);
              break;
          }
          break;
    
        case 109:
          switch (value) {
            case 0:
              waveform2.begin(WAVEFORM_SINE);
              break;
            case 1:
              waveform2.begin(WAVEFORM_TRIANGLE);
              break;
            case 2:
              waveform2.begin(WAVEFORM_SAWTOOTH);
              break;
            case 3:
              waveform2.begin(WAVEFORM_PULSE);
              break;
          }
    
        case 110:
          detuneFactor = 1 - (0.05 * (value * DIV127));
          oscSet();
          break;
    
        case 111:
          FILfactor = value * DIV127;
          FILfreq = 10000 * (value * DIV127);
          if (LFOmodeSelect < 1 || LFOmodeSelect > 5)filter1.frequency(FILfreq);
          break;
    
        case 112:
          filter1.resonance((4.3 * (value * DIV127)) + 0.7);
          break;
    
        case 113:
          if (value <= 12 && value > 0) bendRange = value;
          break;
    
        case 114:
          {
            float xSpeed = value * DIV127;
            xSpeed = pow(100, (xSpeed - 1));
            LFOspeed = (70000 * xSpeed);
            break;
          }
    
        case 115:
          LFOdepth = value * DIV127;
          break;
    
        case 116:
          LFOmodeSelect = value;
          break;
      }
    }
    
    void LFOupdate(bool retrig, byte mode, float FILtop, float FILbottom) {
      static float LFO = 0;
      static unsigned long LFOtime = 0;
      static bool LFOdirection = false;
      unsigned long currentMicros = micros();
      static bool LFOstop = false;
      static float LFOrange = 0;
      static byte oldMode = 0;
      static bool retriggered = false;
    
      if (retrig == true) retriggered = true;
    
    
      if (currentMicros - LFOtime >= LFOspeed) {
        LFOtime = currentMicros;
    
        if (mode != oldMode) {
          if (mode == 0 || mode == 8) {
            LFOpitch = 1;
            oscSet();
            filter1.frequency(FILfreq);
          }
          else if (mode >= 1 || mode <= 7) {
            LFOpitch = 1;
            oscSet();
          }
          else if (mode >= 9 || mode <= 13) {
            filter1.frequency(FILfreq);
          }
          oldMode = mode;
        }
    
        LFOrange = FILtop - FILbottom;
        if (LFOrange < 0) LFOrange = 0;
    
        // LFO Modes
        switch (mode) {
    
          case 0: //Filter OFF
            return;
            break;
          case 1: //Filter FREE
            filter1.frequency(10000 * ((LFOrange * LFO) + LFOdepth));
            break;
          case 2: //Filter DOWN
            if (retriggered == true) {
              LFOdirection = true;
              LFO = 1.0;
            }
            filter1.frequency(10000 * ((LFOrange * LFO) + LFOdepth));
            break;
          case 3: //Filter UP
            if (retriggered == true) {
              LFOdirection = false;
              LFO = 0;
            }
            filter1.frequency(10000 * ((LFOrange * LFO) + LFOdepth));
            break;
          case 4: //Filter 1-DN
            if (retriggered == true) {
              LFOstop = false;
              LFOdirection = true;
              LFO = 1.0;
            }
            if (LFOstop == false) filter1.frequency(10000 * ((LFOrange * LFO) + LFOdepth));
            break;
          case 5: //Filter 1-UP
            if (retriggered == true) {
              LFOstop = false;
              LFOdirection = false;
              LFO = 0;
            }
            if (LFOstop == false) filter1.frequency(10000 * ((LFOrange * LFO) + LFOdepth));
            break;
          case 8: //Pitch OFF
            return;
            break;
          case 9: //Pitch FREE
            LFOpitch = (LFO * LFOdepth) + 1;
            oscSet();
            break;
          case 10: //Pitch DOWN
            if (retriggered == true) {
              LFOdirection = true;
              LFO = 1.0;
            }
            LFOpitch = (LFO * LFOdepth) + 1;
            oscSet();
            break;
          case 11: //Pitch UP
            if (retriggered == true) {
              LFOdirection = false;
              LFO = 0;
            }
            LFOpitch = (LFO * LFOdepth) + 1;
            oscSet();
            break;
          case 12: //Pitch 1-DN
            if (retriggered == true) {
              LFOstop = false;
              LFOdirection = true;
              LFO = 1.0;
            }
            if (LFOstop == false) {
              LFOpitch = (LFO * LFOdepth) + 1;
              oscSet();
            }
            break;
          case 13: //Pitch 1-UP
            if (retriggered == true) {
              LFOstop = false;
              LFOdirection = false;
              LFO = 0;
            }
            if (LFOstop == false) {
              LFOpitch = (LFO * LFOdepth) + 1;
              oscSet();
            }
            break;
        }
    
        retriggered = false;
    
        // Update LFO
        if (LFOdirection == false) { //UP
          LFO = (LFO + 0.01);
          if (LFO >= 1) {
            LFOdirection = true;
            LFOstop = true;
          }
        }
    
        if (LFOdirection == true) { //Down
          LFO = (LFO - 0.01);
          if (LFO <= 0) {
            LFOdirection = false;
            LFOstop = true;
          }
        }
      }
    }
    
    int allocateVoice(byte channel, byte note) {
      int i;
      int nonfree_voices = stopped_voices + used_voices;
      if (nonfree_voices < TOTAL_VOICES) {
        for (i = nonfree_voices; i < TOTAL_VOICES && voices[i].channel != channel; ++i);
        if (i < TOTAL_VOICES) {
          voice_t temp = voices[i];
          voices[i] = voices[nonfree_voices];
          voices[nonfree_voices] = temp;
        }
        i = nonfree_voices;
        used_voices++;
      }
      else {
        if (stopped_voices) {
          i = evict_voice % stopped_voices;
          voice_t temp = voices[i];
          stopped_voices--;
          voices[i] = voices[stopped_voices];
          voices[stopped_voices] = temp;
          used_voices++;
          i = stopped_voices;
        }
        else
          i = evict_voice;
      }
    
      voices[i].channel = channel;
      voices[i].note = note;
    
      evict_voice++;
      evict_voice %= TOTAL_VOICES;
    
      return voices[i].wavetable_id;
    }
    
    int findVoice(byte channel, byte note) {
      int i;
      //find match
      int nonfree_voices = stopped_voices + used_voices;
      for (i = stopped_voices; i < nonfree_voices && !(voices[i].channel == channel && voices[i].note == note); ++i);
      //return TOTAL_VOICES if no match
      if (i == (nonfree_voices)) return TOTAL_VOICES;
    
      voice_t temp = voices[i];
      voices[i] = voices[stopped_voices];
      voices[stopped_voices] = temp;
      --used_voices;
    
      return voices[stopped_voices++].wavetable_id;
    }
    
    void freeVoices() {
      for (int i = 0; i < stopped_voices; i++)
        if (!true/*wavetable[voices[i].wavetable_id].isPlaying() == false*/) {
          voice_t temp = voices[i];
          --stopped_voices;
          voices[i] = voices[stopped_voices];
          int nonfree_voices = stopped_voices + used_voices;
          voices[stopped_voices] = voices[nonfree_voices];
          voices[nonfree_voices] = temp;
        }
    }
    
    const char* note_map[] = {
      "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
    };
    
    void printVoices() {
      static int last_notes_played = notes_played;
      if (last_notes_played == notes_played)
        return;
      last_notes_played = notes_played;
      int usage = AudioProcessorUsage();
      Serial.printf("\nCPU:%03i voices:%02i CPU/Voice:%02i evict:%02i", usage, used_voices, usage / used_voices, evict_voice);
      for (int i = 0; i < used_voices; ++i)
        Serial.printf(" %02hhu %-2s", voices[i].channel, note_map[voices[i].note % 12]);
    }
    
    void Init()
    {
      AudioMemory(120);
      usbMIDI.setHandleControlChange(myControlChange);
      usbMIDI.setHandleNoteOff(myNoteOff);
      usbMIDI.setHandleNoteOn(myNoteOn);
      usbMIDI.setHandlePitchChange(myPitchBend);
      sgtl5000_1.enable();
      sgtl5000_1.volume(0.52);
      sgtl5000_1.dacVolumeRamp();
    
      envelopeBank[0] = envelope1;
      envelopeBank[1] = envelope2;
      envelopeBank[2] = envelope3;
      envelopeBank[3] = envelope4;
    
      for (int i = 0; i < TOTAL_VOICES; i++)
      {
        oscBank1[i].begin(WAVEFORM_SINE);
        oscBank1[i].amplitude(1.0);
        oscBank1[i].frequency(82.41);
        oscBank1[i].pulseWidth(0.15);
    
        oscBank2[i].begin(WAVEFORM_SINE);
        oscBank2[i].amplitude(1.0);
        oscBank2[i].frequency(82.41);
        oscBank2[i].pulseWidth(0.15);
    
        noiseBank[i].amplitude(0.3);
    
        for (int j = 0; j < 4; j++)
        {
          oscMixerBank[i].gain(j, 1.0);
        }
    
        envelopeBank[i].delay(0);
        envelopeBank[i].attack(0);
        envelopeBank[i].decay(0);
        envelopeBank[i].sustain(1);
        envelopeBank[i].release(500.0);
        envelopeBank[i].releaseNoteOn(7.0);
    
        mixer5.gain(i, 1.0);
      }
    }

  2. #2
    I’m suspect your array initializers made copies of the AudioSynthWaveform objects.
    I’d try using pointers, or maybe references.

    I just checked - arrays of references aren't allowed in C++. So, pointers it is.
    Last edited by tele_player; 01-14-2020 at 05:03 PM. Reason: clarification

  3. #3
    I think I understand... Essentially I need to create an array of AudioSynthWaveform pointers and reference that? I don't use Cpp terribly often, so I apologize for my ignorance on the issue. Can you use a pointer to the class to access member functions or do you need to specifically create a pointer to the member function of the class?

    If you've indeed hit upon the issue, which I feel pretty confident you have, I wonder if it is not possible to declare the audiosynthwaveform objects in an array initially so no copies are made and one could work with that directly.

    Anyways, thanks for taking the time to reply

  4. #4
    Senior Member
    Join Date
    Nov 2017
    Location
    Belgium
    Posts
    215
    Quote Originally Posted by RABB17 View Post
    I wonder if it is not possible to declare the audiosynthwaveform objects in an array initially so no copies are made and one could work with that directly.
    Yes, like this.
    Code:
    AudioSynthWaveform*  waveform = new AudioSynthWaveform[4];
    See following thread for a complete example: https://forum.pjrc.com/threads/58158...-from-an-Array

  5. #5
    Good deal!

    Thanks so much for the help

  6. #6
    Quote Originally Posted by neurofun View Post
    Yes, like this.
    Code:
    AudioSynthWaveform*  waveform = new AudioSynthWaveform[4];
    Worked like a charm. Hopefully, I'll have the time to have the sketch finished by tonight. I'll post it in the forum when it's complete.

  7. #7
    This might seem pedantic; I prefer the term 'precise'.

    @neurofun suggested the following, to create an array of 4 objects of type AudioSynthwaveform
    AudioSynthWaveform* waveform = new AudioSynthWaveform[4];

    Yes, it creates an array, and the constructors are called. But it creates an unnamed array, and stores the address of that nameless array in a pointer variable named waveform.

    And yes, waveform can the be dereferenced using array syntax, with [].

    BUT, here's the distinction: waveform IS A POINTER, not an array. Since it's a pointer, you can assign to it:

    waveform = (void *)NULL;


    On the other hand:

    AudioSynthWaveform waveform[4];

    Creates a true array:

    waveform = (void *)NULL; //will result in a compiler error.

    Like I said, pedantic.

  8. #8
    Senior Member
    Join Date
    Nov 2017
    Location
    Belgium
    Posts
    215
    @tele_player
    I think being precise or pedantic as you say, is important to avoid confusion or misunderstandings, especially when dealing with technological stuff.
    Thanks for the clarification.

  9. #9
    Indeed, I find precision a quality to be admired in engineering endeavors Thank you for the edification! I posted a quick and dirty 4 voice version of the notes and volts synth in the Audio projects.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •