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

Thread: Experienced TeensySynth Designers: Bit of Help With Some Code Questions (first-timer)

  1. #1

    Experienced TeensySynth Designers: Bit of Help With Some Code Questions (first-timer)

    Hello,
    I have started putting a Teensy synth together. It has analog pots and switches, all working, connected to a teensy 3.5 with audio shield.

    The overall design target is: 3 oscillators (Two and Three are pitch and volume selectable, Three is octave selectable, and all three are waveform selectable) one LFO with four destinations, ADSR, filter with cutoff and resonance, and finally delay and reverb.

    I've been getting it up step-by-step with success: first the oscillators made pitches through the headphones, their level controls seemed to work, the sound was smooth, choosing different wave types was fine, etc. No crackles or pops or artifacts or whatever. Just neat sounding synthy waves of different kinds happily mixing together.

    Then I incorporated the filter and it seemed ok, too- though there was some crackling when i swept the cutoff knob around, it wasn't too terrible and I decided to keep going. I used the mono reverb from the Audio library and got a wet/dry knob working well for that. In fact, the crackling helped me hear the quality of the reverb!

    Then, I got midi in to work and print midi messages to the serial monitor (one of the projects on Teensy website, the example midi input one here: https://www.pjrc.com/teensy/td_libs_MIDI.html
    ...I also used the schematic to wire my MIDI IN port.)

    Currently, the synth 'works' (and I'm pretty excited about it, it's my first try ever) but there is quite a delay between pressing a key on my midi controller and the wave changing; there is no note-off yet; and there is some weird stuff going on with the pitches and if i just leave a note on there is quite a bit of crackling here and there, stuff I associate with 'code not working correctly' because I tested all the hardware and there was very good sound quality back when it was just waves, oscillators and so on. In other words, I'm not overdriving one of the mixers or anything. And the distortion in the filter is unrelated to the amount of resonance- it happens when the resonance is well below the stated .707 threshold.

    I haven't done the LFO yet, or the ADSR envelopes, or the delay. But I'm a bit stuck where I am.

    Code-wise, there's no doubt I have made a mess of things because I simply know nothing about how to run code efficiently. So I am hoping to post it here and pray for some GENTLE advice about:

    1. the best coding approach to efficiently and usefully change midi note to frequency and set my OSC1 to that frequency

    2. any comments about my filter implementation and why that step introduced some crackling

    3. any guidance about how to figure out (for a newbie) voice allocation or just how to get MIDI on and off working cleanly, at least mono for now

    4. any stuff in advance to look forward to with ADSR envelopes and getting the delay (time, wet/dry) to work...for now, I didn't want to get into multiplexing for this first synth so I have only a few knobs and decided to eschew delay feedback on this one

    The problem is the huge gap between my ability to read and understand, say, the code that the Teensy Tsynth runs (way too complicated for me to follow) and other examples of midi synths I'm trying to use and borrow from. I really do need help in the most basic stuff- how functions return values, how to make the main loop of code run efficiently, etc.

    There are ABSOLUTELY bits of code floating around that I'm not using but were left over from stuff I cut and pasted and tried out...for example, I left some things in for voice allocation to 8 voices even though at the moment I'd be fine with a working monophonic synth.

    I'll try to post code correctly here and hoping i sorta get the tags and stuff right. Thanks!

    Code:
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    #include <MIDI.h>
    
    MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
    
    // GUItool: begin automatically generated code
    AudioSynthWaveformModulated Osc1;   //xy=129,88
    AudioSynthWaveformModulated Osc2;   //xy=129,175
    AudioEffectChorus        chorus1;        //xy=128,452
    AudioSynthWaveformModulated Osc3;   //xy=133,256
    AudioEffectEnvelope      envelope1;      //xy=136,496
    AudioSynthWaveformModulated LFO_Osc2Freq;   //xy=295,313
    AudioSynthWaveformModulated LFO_DelayTime;   //xy=300,393
    AudioMixer4              Premix;         //xy=319,159
    AudioSynthWaveformModulated LFO_Volume;   //xy=336,435
    AudioSynthWaveformModulated LFO_Filter;   //xy=340,351
    AudioFilterStateVariable LowPassFilter;        //xy=479,212
    AudioEffectDelay         Delay;         //xy=589,340
    AudioEffectFreeverb      Verb;      //xy=723,35
    AudioOutputI2S           i2s1;           //xy=771,134
    AudioMixer4              MasterR;         //xy=776,345
    AudioMixer4              MasterL;         //xy=777,272
    AudioConnection          patchCord1(Osc1, 0, Premix, 0);
    AudioConnection          patchCord2(Osc2, 0, Premix, 1);
    AudioConnection          patchCord3(Osc3, 0, Premix, 2);
    AudioConnection          patchCord4(Premix, 0, LowPassFilter, 0);
    AudioConnection          patchCord5(LowPassFilter, 0, MasterL, 0);
    AudioConnection          patchCord6(LowPassFilter, 0, MasterR, 0);
    AudioConnection          patchCord7(LowPassFilter, 0, Verb, 0);
    AudioConnection          patchCord8(Verb, 0, MasterL, 1);
    AudioConnection          patchCord9(Verb, 0, MasterR, 1);
    AudioConnection          patchCord10(MasterR, 0, i2s1, 1);
    AudioConnection          patchCord11(MasterL, 0, i2s1, 0);
    AudioControlSGTL5000     sgtl5000_1;     //xy=771,409
    // GUItool: end automatically generated code
    
    //variables that hold the values/states of the knobs and switches
    
    int attackVal;
    int decayVal;
    int sustainVal;
    int releaseVal;
    int resoVal;
    int cutoffVal;
    int osc2FreqVal;
    int osc2LevelVal;
    int osc3FreqVal;
    int osc3LevelVal;
    int osc1Shape1Val;
    int osc1Shape2Val;
    int osc2Shape1Val;
    int osc2Shape2Val;
    int osc3Shape1Val;
    int osc3Shape2Val;
    int osc3Octave1Val;
    int osc3Octave2Val;
    int lfoRateVal;
    int lfoDepthVal;
    int lfoShape1Val;
    int lfoShape2Val;
    int lfoDest1Val;
    int lfoDest2Val;
    int lfoDest3Val;
    int volumeVal;
    int delayLengthVal;
    int delayMixVal;
    int reverbMixVal;
    
    
    //Pin assignments
    
    int attackPin = 21;
    int decayPin = 20;
    int sustainPin = 17;
    int releasePin = 16;
    int resoPin = 31;
    int cutoffPin = 32;
    int osc1ShapePin1 = 1;
    int osc1ShapePin2 = 2;
    int osc2ShapePin1 = 27;
    int osc2ShapePin2 = 28;
    int osc3ShapePin1 = 8;
    int osc3ShapePin2 = 5;
    int osc2FreqPin = A14;
    int osc2LevelPin = A18;
    int osc3FreqPin = A20;
    int osc3LevelPin = A17;
    int osc3OctavePin1 = 30;
    int osc3OctavePin2 = 29;
    int lfoRatePin = A15;
    int lfoDepthPin = A19;
    int lfoShapePin1 = 3;
    int lfoShapePin2 = 4;
    int lfoDestPin1 = 25;
    int lfoDestPin2 = 24;
    int lfoDestPin3 = 26;
    int volumePin = A1;
    int delayLengthPin = A22;
    int delayMixPin = A16;
    int reverbMixPin = A21;
    
    //oscillator and audio library object values
    
    int osc1current_waveform = 0;
    float osc1Pitch = 261.63;
    int osc2current_waveform = 0;
    float osc2Pitch = 130.86;
    int osc3current_waveform = 0;
    float osc3Pitch = 65.44;
    float osc1Volume = 1.00;
    float osc2Volume = 1.00;
    float osc3Volume = 1.00;
    float osc3Octave = 1.00;
    float filterFreq = 6500.0;
    float resAmount = 0.700;
    float verbMix = 0.00;
    float hertz = 1.000;
    
    //midi code variables
    
    unsigned long t=0;
    int ledPin = 13;
    // array for the notes to be assigned to one of eight voices
    int voice[8];
    //used to track how many voices are active
    //when limit is reached, msg prints Voice Limit Exceeded!
    int noteCount=0;
    
    void setup() {
      
      Serial.begin(38400);
      
      pinMode (osc1ShapePin1, INPUT_PULLUP);
      pinMode (osc1ShapePin2, INPUT_PULLUP);
      pinMode (osc2ShapePin1, INPUT_PULLUP);
      pinMode (osc2ShapePin2, INPUT_PULLUP);
      pinMode (osc3ShapePin1, INPUT_PULLUP);
      pinMode (osc3ShapePin2, INPUT_PULLUP);
      pinMode (osc3OctavePin1, INPUT_PULLUP);
      pinMode (osc3OctavePin2, INPUT_PULLUP);
      pinMode (lfoShapePin1, INPUT_PULLUP);
      pinMode (lfoShapePin2, INPUT_PULLUP);
      pinMode (lfoDestPin1, INPUT_PULLUP);
      pinMode (lfoDestPin2, INPUT_PULLUP);
      pinMode (lfoDestPin3, INPUT_PULLUP);
       pinMode(ledPin, OUTPUT);
      
      AudioMemory(120);
    
      sgtl5000_1.enable();
      sgtl5000_1.volume(0.4);
      LowPassFilter.frequency(filterFreq);
      Verb.roomsize(0.75);
      Verb.damping(0.45);
    
      MIDI.setHandleNoteOn(OnNoteOn);
      MIDI.setHandleNoteOff(OnNoteOff);
      MIDI.begin(MIDI_CHANNEL_OMNI);
    
    }
    
    void loop() {
    
      getReadings();
      setWaveForms();
      setWaveFreqs();
      startWaves();
      applyFilter();
      reverbMix();
      midiDetect();
    
    }
    
    void getReadings(){
      
      attackVal = analogRead(attackPin);
      decayVal = analogRead(decayPin);
      sustainVal = analogRead(sustainPin);
      releaseVal = analogRead(releasePin);
      resoVal = analogRead(resoPin);
      cutoffVal = analogRead(cutoffPin);
      osc1Shape1Val = digitalRead(osc1ShapePin1);
      osc1Shape2Val = digitalRead(osc1ShapePin2);
      osc2Shape1Val = digitalRead(osc2ShapePin1);
      osc2Shape2Val = digitalRead(osc2ShapePin2);
      osc3Shape1Val = digitalRead(osc3ShapePin1);
      osc3Shape2Val = digitalRead(osc3ShapePin2);
      osc3Octave1Val = digitalRead(osc3OctavePin1);
      osc3Octave2Val = digitalRead(osc3OctavePin2);
      osc2FreqVal = analogRead(osc2FreqPin);
      osc2LevelVal = analogRead(osc2LevelPin);
      osc3FreqVal = analogRead(osc3FreqPin);
      osc3LevelVal = analogRead(osc3LevelPin);
      lfoRateVal = analogRead(lfoRatePin);
      lfoDepthVal = analogRead(lfoDepthPin);
      lfoShape1Val = digitalRead(lfoShapePin1);
      lfoShape2Val = digitalRead(lfoShapePin2);
      lfoDest1Val = digitalRead(lfoDestPin1);
      lfoDest2Val = digitalRead(lfoDestPin2);
      lfoDest3Val = digitalRead(lfoDestPin3);
      volumeVal = analogRead(volumePin);  
      delayLengthVal = analogRead(delayLengthPin);
      delayMixVal = analogRead(delayMixPin);
      reverbMixVal = analogRead(reverbMixPin);  
    
    }
    
    void setWaveForms(){
     if (osc1Shape1Val == 0){
        osc1current_waveform = WAVEFORM_SQUARE;
      }
      else if (osc1Shape2Val == 0){
        osc1current_waveform = WAVEFORM_SAWTOOTH;
      }
      else {
        osc1current_waveform = WAVEFORM_SINE;
      } 
    
      if (osc2Shape1Val == 0){
        osc2current_waveform = WAVEFORM_SQUARE;
      }
      else if (osc2Shape2Val == 0){
        osc2current_waveform = WAVEFORM_SAWTOOTH;
      }
      else {
        osc2current_waveform = WAVEFORM_TRIANGLE;
      } 
    
       if (osc3Shape1Val == 0){
        osc3current_waveform = WAVEFORM_SQUARE;
      }
      else if (osc3Shape2Val == 0){
        osc3current_waveform = WAVEFORM_SAWTOOTH;
      }
      else {
        osc3current_waveform = WAVEFORM_TRIANGLE;
      } 
    }
    
    void setWaveFreqs(){
      osc1Pitch = hertz;
      osc2Pitch = (osc1Pitch * 3.00);
      
       if (osc3Octave1Val == 0){
        osc3Octave = 0.50;
      }
      else if (osc3Octave2Val == 0){
        osc3Octave = 2.00;
      }
      else {
        osc3Octave = 1.00;
      } 
     osc3Pitch = (osc1Pitch / osc3Octave); 
    }
    
    void startWaves(){
       osc2Volume = ((osc2FreqVal / 1023.00) * osc1Volume);
       osc3Volume = ((osc3LevelVal / 1023.00) * osc1Volume);
       
       Osc1.begin(osc1Volume, osc1Pitch, osc1current_waveform);
       Osc2.begin(osc2Volume, osc2Pitch, osc2current_waveform);
       Osc3.begin(osc3Volume, osc3Pitch, osc3current_waveform);
    }
    
    void applyFilter(){
      filterFreq =  expf((float)cutoffVal / 145.00) * 10.00 + 00.00;
      LowPassFilter.frequency(filterFreq);
      resAmount = (.690 + (resoVal / 235.000));
      LowPassFilter.resonance(resAmount);
    }
    
    void reverbMix(){
      verbMix = (reverbMixVal / 1023.00);
      MasterL.gain(0, 1.0);
      MasterR.gain(0, 1.0);
      MasterL.gain(1, verbMix);
      MasterR.gain(1, verbMix); 
    }
    
    void handleNoteOn(byte channel, byte pitch, byte velocity)
    {
     
    }
    
    
    
    void handleNoteOff(byte channel, byte pitch, byte velocity)
    {
        // Do something when the note is released.
        // Note that NoteOn messages with 0 velocity are interpreted as NoteOffs.
    }
    
    
    
    void midiDetect(){
       int type, note, velocity, channel, d1, d2;
      if (MIDI.read()) {                    // Is there a MIDI message incoming ?
        byte type = MIDI.getType();
        switch (type) {
          case midi::NoteOn:
            note = MIDI.getData1();
            velocity = MIDI.getData2();
            channel = MIDI.getChannel();
            if (velocity > 0) {
              hertz = (pow (2.0, ((float)(note-69)/12.0)))*440;
              Serial.println(String("Note On:  ch=") + channel + ", note=" + note + ", velocity=" + velocity + ", hertz=" + hertz);
            } else {
              Serial.println(String("Note Off: ch=") + channel + ", note=" + note);
            }
            break;
          case midi::NoteOff:
            note = MIDI.getData1();
            velocity = MIDI.getData2();
            channel = MIDI.getChannel();
            Serial.println(String("Note Off: ch=") + channel + ", note=" + note + ", velocity=" + velocity);
            break;
          default:
            d1 = MIDI.getData1();
            d2 = MIDI.getData2();
            Serial.println(String("Message, type=") + type + ", data = " + d1 + " " + d2);
        }
        t = millis();
      }
      if (millis() - t > 10000) {
        t += 10000;
        Serial.println("(inactivity)");
      }
    }
    
    
    
    void MIDInoteOn(int note, int vel)
    {
    
      if(noteCount==8){
        Serial.println("Voice Limit Exceeded!");
      }
    
      for (int i=0; i<8;i++){
     if(!voice[i]){
      voice[i]=note;
      Serial.print("New Note #  ");
      Serial.println(note);
      Serial.println(vel);
      Serial.print("Assigned to Voice #  ");
      Serial.println(i+1);
    
      digitalWrite(ledPin, HIGH);
      delay(100);
      digitalWrite(ledPin, LOW);
      noteCount++;
    
        if(voice[i]){return;
      }
    }
    }
    }
     
    //functin called when a Note Off msg is received.  Will scan the voice array to locate 
    //the note to be shut off.  Thus returning the element to zero value.  which frees up that
    //voice for use of a new note.
    
    void MIDInoteOff(int note, int vel)
    {
      
        for(int i=0; i<8; i++){
         
          if(voice[i]==note){
            voice[i]=0;
              Serial.print("-note off # ");
              Serial.println(note);
              Serial.print("Shut Off Voice # ");
              Serial.println(i+1);
              noteCount--;
              if(noteCount<0){noteCount=0;
          }
      digitalWrite(ledPin, HIGH);
      delay(100);
      digitalWrite(ledPin, LOW);
    
              
          }
        }
      }
    
    
    void OnNoteOn(byte channel, byte note, byte velocity)
    {
      MIDInoteOn(note, velocity);
    }
    
    void OnNoteOff(byte channel, byte note, byte velocity)
    {
      MIDInoteOff(note, velocity);
      digitalWrite(ledPin, LOW);
      Serial.print("-note off # ");
      Serial.println(note);
    }
    Click image for larger version. 

Name:	Screen Shot 2021-03-14 at 7.46.17 PM.jpg 
Views:	43 
Size:	89.6 KB 
ID:	24063

    Trevor

  2. #2
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    Your can check my example at https://manicken.github.io
    Available at the Tool (top-right menu) -examples-"manicken poly synth"
    I belive it's a simple example.
    I have used the code from "Notes and Volts"
    https://m.youtube.com/watch?v=UJcZxyB5rVc
    as a Starting point.

    It uses handler functions to receive the midi data,
    I think that makes it faster.

    Note. currently it uses wavetable instrument files that need to be downloaded from my github, before it can be compiled, alternative the references need to be commented out.

  3. #3
    Senior Member
    Join Date
    Jul 2020
    Posts
    1,748
    You've created the I2S output before the master mixers, so it will be run before the mixers that provide its data.
    There can be issues if things are run out of order like this, sometimes, and these are often intermittent, and
    the latencies and timing wont be as expected of course. Its possible this might be making some glitches, not sure
    though.

    If you stick to the auto-generated code from the audio design tool it puts objects in a consistent order. The audio
    processing chain is run strictly in the order the audio objects were created, irrespective of connection topology.

    You have calls to delay(100) - that's not a good idea if you want timeliness of response.

  4. #4
    Thanks! I did use the auto generate tool and did not alter what it created. And afaik the function with the delay in it is not called atm (it was part of an attempt to learn how multiple voices are assigned) but I will comment it out and make sure. Not sure why the audio tool generated the objects in the order it did...

  5. #5
    Senior Member
    Join Date
    Jul 2020
    Posts
    1,748
    Sounds like there's a bug? Or maybe I was wrong about it generating a breadth-first traversal (which is what the
    Audio library seems to assume).

    Thinking about it there's perhaps an assumption that if the signal wire runs backwards (graphically) its a cyclic link? Certainly
    flattening the graph has to have some source of information about which links are "backwards" in any cycle. Those
    links will have a block's worth of latency. Not fluent with JS though so don't really relish delving through the audio
    tool code.

  6. #6
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    In my modified tool it sorts the items in columns based on the major grid size and then in each column it sorts by the y pos (row)
    1 4 7
    2 5 8
    3 6 9

  7. #7
    Can you provide any guidance about whether, in this exact design (three oscillators with three waveform types each) there are benefits to wavetables vs. using the modulation-ready oscillators in the audio library? And then, when does it become necessary or clearly more efficient to use wavetables? My next design has a fourth sound source using FM, and I'm pretty sure the arduino project i am swiping it from used wavetables. In this design, one of the waves will be modulated by an LFO, but that is it so far.

  8. #8
    Senior Member
    Join Date
    Jul 2020
    Posts
    1,748
    You can use AudioProcessorUsage() and AudioProcessorUsageMax() to instrument your code and see what the
    audio lib CPU load is for various configurations.

  9. #9
    Holy Toledo, that is NOT the Design Tool I've been using! That looks a lot more sophisticated and up to date. I'm switching.
    ...hey, wait. Where do I find that version besides your github? I want the ladder filter and some of the other functionality...is this your own design tool environment, not generally available to Teensy users?
    Last edited by trevorbryden; 03-15-2021 at 07:11 PM.

  10. #10
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    Quote Originally Posted by trevorbryden View Post
    Where do I find that version besides your github?
    the current version can be downloaded from
    https://github.com/manicken/manicken.github.io

    Quote Originally Posted by trevorbryden View Post
    I want the ladder filter and some of the other functionality...
    I have updated it with the ladder filter.
    Other functionality???

    Quote Originally Posted by trevorbryden View Post
    is this your own design tool environment, not generally available to Teensy users?
    This is a modified version of the official tool.
    And will be available as long as github allows it, and will always be free.

    The funny thing is that I have not yet had any time to do any advanced synth stuff myself.
    (except playing with the PureData alike interfacing I have implemented)
    Have recently bought a teensy 4.1 to allow much more flash + native sd-card slot.

    My thread about it is here
    https://forum.pjrc.com/threads/65740...gn-Tool-update

Posting Permissions

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