Forum Rule: Always post complete source code & details to reproduce any issue!
Page 1 of 2 1 2 LastLast
Results 1 to 25 of 31

Thread: Do Teensy Synths with Sine Waves always click?

Hybrid View

  1. #1

    Do Teensy Synths with Sine Waves always click?

    Hi!
    Before I go nuts chasing it down...
    If i make basically the world's simplest synth in the design tool...a sine wave triggered by midi, no filter, no nothing, mixer level even down to 0.3, chip output 0.4, etc...there is an obvious click in the headphones on each note on and off.
    I've tried setting the phase of the wave to 0 just before Osc1.begin() etc etc but...
    Is this just something that happens for some reason? Are there known strategies to mitigate it (besides attack filter stuff)?
    Thanks!

  2. #2
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    608
    How do you do the note on/off?
    Are you using a AudioEffectEnvelope otherwise try that.

  3. #3
    I think a little envelope will be the next step for sure. Was just wondering if the inability to launch and terminate a sine wave at phase zero was a known thing or i am screwing it up- and whether the elegant simple solution, if any, was widely known.
    Will post code tonight.
    Thanks

  4. #4
    Senior Member
    Join Date
    Apr 2014
    Posts
    363
    Play a simple continuous sinewave and use the midi note change command to change the frequency of the sinewave for the corresponding note

  5. #5
    As an experiment? Does that get me closer to a playable synth via midi that doesn't click when notes start and stop (when keys are pressed)? That is what I am hearing and unable so far to get rid of.

  6. #6
    Senior Member
    Join Date
    Apr 2014
    Posts
    363
    There will be no clicks because continuous. To try it without midi use a for-next loop to increment up and down the frequency

  7. #7
    Right, you mean as a way to test my audio circuit? I have done that and it is clean. Notes held are also happy. Its just the note ons and note offs

  8. #8
    Senior Member
    Join Date
    Apr 2014
    Posts
    363
    Post your code a simple version. Yes held notes are OK while playing, I assumed it was the changing of notes from say A to B to C etc was the problem. as a test do that by changing the frequency value of the sound....it should happen smoothly.
    What is happening will depend how you are generating the sine wave .... wavetable or whatever and what process is in the noteon command

  9. #9
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    26,814
    You need to use the envelope effect.

    Changes you make from Arduino code take effect only every 128 audio samples. You just can't get the timing precision to turn the waveform off at a zero crossing... at least not without creating a completely different oscillator object in the library. Even then, the whole system is designed around a block update approach. The efficiency that brings, allowing you to create such complex designs (hundreds of oscillators on Teensy 4) also means certain limitations. It's just not designed to work the way you're trying to do things.

    Use the envelope effect.
    Last edited by PaulStoffregen; 03-16-2021 at 09:19 PM. Reason: typo

  10. #10
    Exactly the answer I was looking for and not necessarily dreading. Thank you

  11. #11
    Definitely was not criticising the best and most fun design environment I've ever played with. I was just making sure that I wasn't doing something wrong because I'm very new to all of this. Thank you

  12. #12
    Senior Member
    Join Date
    Apr 2014
    Posts
    363
    Yes I understand envelopes etc are the way to go. Initial post was I assumed to get a basic sine wave playing different notes without clicks.
    Code below is probably the most basic and play 5 notes no clicks

    Code:
    #include <Audio.h>
    
    const int16_t tone1[256] = {
    0,
    628,
    1256,
    1883,
    2509,
    3133,
    3756,
    4376,
    4994,
    5608,
    6220,
    6827,
    7431,
    8030,
    8624,
    9213,
    9796,
    10374,
    10945,
    11510,
    12067,
    12618,
    13160,
    13696,
    14222,
    14740,
    15249,
    15750,
    16240,
    16721,
    17192,
    17652,
    18102,
    18540,
    18968,
    19384,
    19789,
    20181,
    20562,
    20930,
    21285,
    21628,
    21957,
    22274,
    22577,
    22866,
    23142,
    23403,
    23651,
    23884,
    24103,
    24307,
    24497,
    24672,
    24832,
    24977,
    25108,
    25223,
    25322,
    25407,
    25476,
    25530,
    25569,
    25592,
    25600,
    25592,
    25569,
    25530,
    25476,
    25407,
    25322,
    25223,
    25108,
    24977,
    24832,
    24672,
    24497,
    24307,
    24103,
    23884,
    23651,
    23403,
    23142,
    22866,
    22577,
    22274,
    21957,
    21628,
    21285,
    20930,
    20562,
    20181,
    19789,
    19384,
    18968,
    18540,
    18102,
    17652,
    17192,
    16721,
    16240,
    15750,
    15249,
    14740,
    14222,
    13696,
    13160,
    12618,
    12067,
    11510,
    10945,
    10374,
    9796,
    9213,
    8624,
    8030,
    7431,
    6827,
    6220,
    5608,
    4994,
    4376,
    3756,
    3133,
    2509,
    1883,
    1256,
    628,
    0,
    -629,
    -1256,
    -1884,
    -2510,
    -3134,
    -3757,
    -4377,
    -4995,
    -5609,
    -6221,
    -6828,
    -7432,
    -8031,
    -8625,
    -9214,
    -9797,
    -10375,
    -10946,
    -11511,
    -12068,
    -12619,
    -13161,
    -13696,
    -14223,
    -14741,
    -15250,
    -15751,
    -16241,
    -16722,
    -17192,
    -17653,
    -18103,
    -18541,
    -18969,
    -19385,
    -19790,
    -20182,
    -20563,
    -20931,
    -21286,
    -21629,
    -21958,
    -22275,
    -22578,
    -22867,
    -23143,
    -23404,
    -23652,
    -23885,
    -24104,
    -24308,
    -24498,
    -24673,
    -24833,
    -24978,
    -25109,
    -25224,
    -25323,
    -25408,
    -25477,
    -25531,
    -25570,
    -25593,
    -25600,
    -25593,
    -25570,
    -25531,
    -25477,
    -25408,
    -25323,
    -25224,
    -25109,
    -24978,
    -24833,
    -24673,
    -24498,
    -24308,
    -24104,
    -23885,
    -23652,
    -23404,
    -23143,
    -22867,
    -22578,
    -22275,
    -21958,
    -21629,
    -21286,
    -20931,
    -20563,
    -20182,
    -19790,
    -19385,
    -18969,
    -18541,
    -18103,
    -17653,
    -17192,
    -16722,
    -16241,
    -15751,
    -15250,
    -14741,
    -14223,
    -13696,
    -13161,
    -12619,
    -12068,
    -11511,
    -10946,
    -10375,
    -9797,
    -9214,
    -8625,
    -8031,
    -7432,
    -6828,
    -6221,
    -5609,
    -4995,
    -4377,
    -3757,
    -3134,
    -2510,
    -1884,
    -1256,
    -629
    };
    
    
    // GUItool: begin automatically generated code
    AudioSynthWaveform       waveform1;      //xy=285,868
    AudioOutputAnalog        dac1;           //xy=739,914
    AudioConnection          patchCord1(waveform1, 0, dac1, 0);
    
    // GUItool: end automatically generated code
    
    void setup() {
      // put your setup code here, to run once:
    
     AudioMemory(15); //set memory allocation
          
      // by default the Teensy 3.1 DAC uses 3.3Vp-p output
      // if your 3.3V power has noise, switching to the
      // internal 1.2V reference can give you a clean signal
       dac1.analogReference(INTERNAL);
       // dac1.analogReference(EXTERNAL);
       
        delay(40);
        
         waveform1.begin(1.0, 1000, WAVEFORM_ARBITRARY);  
         waveform1.arbitraryWaveform(tone1, 1200); 
         
       }
    
    void loop() {
      // put your main code here, to run repeatedly:
      
     waveform1.frequency(400);
    
    delay(1000);
    waveform1.frequency(600);
    delay(1000);
    waveform1.frequency(800);
    delay(1000);
    waveform1.frequency(1000);
    delay(1000);
    waveform1.frequency(1200);
    delay(1000);
    
    
    }

  13. #13
    Yeah, I don't know. Now I get the click, and an initial transient, THEN you can hear the filter ramp up...
    What I can't seem to do is get the MIDI.read() thing to happen but still be able to twist knobs and mess with waveform while notes are ringing unless I put the wave begin stuff in the main loop. When I put my Osc.begin stuff in the GetNoteOn part of the code, it works OK but you can't change any of the parameters while a note rings...
    So I feel like I can't get the whole flow of things working at all and this clicking/envelope thing has to be something simple. HELP!

    Code:
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    
    // GUItool: begin automatically generated code
    AudioSynthWaveform       Osc1;      //xy=94.88890075683594,78.11111068725586
    AudioSynthWaveform       Osc2;      //xy=96.88890075683594,113.11111068725586
    AudioSynthWaveform       Osc3;      //xy=98.88890075683594,146.11111068725586
    AudioMixer4              Premix;         //xy=214.88890075683594,120.11111068725586
    AudioEffectEnvelope      ADSR;      //xy=347.88890838623047,120.11112403869629
    AudioFilterStateVariable LowPassFilter;        //xy=499.8888931274414,126.88888359069824
    AudioEffectFreeverb      Verb;      //xy=655.5555191040039,143.66667461395264
    AudioMixer4              MasterR;         //xy=784.8889083862305,185.8888931274414
    AudioMixer4              MasterL;         //xy=786.1110954284668,120.00000381469727
    AudioOutputI2S           i2s1;           //xy=915,153
    AudioConnection          patchCord1(Osc1, 0, Premix, 0);
    AudioConnection          patchCord2(Osc2, 0, Premix, 1);
    AudioConnection          patchCord3(Osc3, 0, Premix, 2);
    AudioConnection          patchCord4(Premix, ADSR);
    AudioConnection          patchCord5(ADSR, 0, LowPassFilter, 0);
    AudioConnection          patchCord6(LowPassFilter, 0, MasterL, 0);
    AudioConnection          patchCord7(LowPassFilter, 0, MasterR, 0);
    AudioConnection          patchCord8(LowPassFilter, 0, Verb, 0);
    AudioConnection          patchCord9(Verb, 0, MasterL, 1);
    AudioConnection          patchCord10(Verb, 0, MasterR, 1);
    AudioConnection          patchCord11(MasterR, 0, i2s1, 1);
    AudioConnection          patchCord12(MasterL, 0, i2s1, 0);
    AudioControlSGTL5000     sgtl5000_1;     //xy=863.2222442626953,263.44445037841797
    // GUItool: end automatically generated code
    
    
    #include <MIDI.h>
    
    MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
    
    //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 = WAVEFORM_SINE;
    float osc1Pitch = 261.63;
    int osc2current_waveform = 0;
    float osc2Pitch = 130.86;
    int osc3current_waveform = 0;
    float osc3Pitch = 65.44;
    float osc1Volume = 0.00;
    float osc2Volume = 0.00;
    float osc3Volume = 0.00;
    float osc3Octave = 1.00;
    float filterFreq = 6500.0;
    float resAmount = 0.700;
    float verbMix = 0.00;
    float hertz = 1.000;
    
    //midi code variables
    
    void setup() {
      
      Serial.begin(9600);
      
      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);
       
      AudioMemory(120);
    
      sgtl5000_1.enable();
      sgtl5000_1.volume(0.7);
      LowPassFilter.frequency(filterFreq);
      Verb.roomsize(0.75);
      Verb.damping(0.75);
      
      MIDI.begin(MIDI_CHANNEL_OMNI);
      MIDI.setHandleNoteOn(OnNoteOn);
      MIDI.setHandleNoteOff(OnNoteOff);
      
      Premix.gain(0, 0.7);
      Premix.gain(1, 0.7);
      Premix.gain(2, 0.7);
    
      ADSR.releaseNoteOn(50);
    
    }
    
    void loop() {
      
      getReadings();
      setWaveForms();
      applyFilters();
      reverbMix();
      MIDI.read();
      
      Osc1.begin(osc1Volume, osc1Pitch, osc1current_waveform);
      osc2Volume = ((osc2FreqVal / 1023.00)*osc1Volume); 
      osc3Volume = ((osc3LevelVal / 1023.00)*osc1Volume); 
      Osc2.begin(osc2Volume, osc2Pitch, osc2current_waveform);
      Osc3.begin(osc3Volume, osc3Pitch, osc3current_waveform);
      
    }
    
    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;
        Serial.println("osc1 square");
      }
      else if (osc1Shape2Val == 0){
        osc1current_waveform = WAVEFORM_SAWTOOTH;
        Serial.println("osc1 saw");
      }
      else {
        osc1current_waveform = WAVEFORM_SINE;
        Serial.println("osc1 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 applyFilters(){
      filterFreq =  expf((float)cutoffVal / 145.00) * 10.00 + 10.00;
      LowPassFilter.frequency(filterFreq);
      resAmount = (.690 + (resoVal / 235.000));
      LowPassFilter.resonance(resAmount);
      float attackamount = (15 + (attackVal*2));
      ADSR.attack(attackamount);
      Serial.println(attackamount);
    }
    
    void reverbMix(){
      verbMix = (reverbMixVal / 1023.00);
      MasterL.gain(0, .6);
      MasterR.gain(0, .6);
      MasterL.gain(1, .8 * verbMix);
      MasterR.gain(1, .8 * verbMix); 
    }
    
    
    
    void OnNoteOn(byte channel, byte note, byte velocity)
    {
      ADSR.noteOn(); 
      hertz = (pow (2.0, ((float)(note-69)/12.0)))*440;
      osc1Pitch = hertz;
     
      osc2Pitch = (osc1Pitch * 1.16);
      
       if (osc3Octave1Val == 0){
        osc3Octave = 0.50;}
      
       else if (osc3Octave2Val == 0){
        osc3Octave = 2.00;}
      
       else {
        osc3Octave = 1.00;} 
        
      osc3Pitch = (osc1Pitch / osc3Octave); 
      
    
      osc1Volume = float(velocity/180.00);   
      
    }
    
    void OnNoteOff(byte channel, byte note, byte velocity)
    {
      osc1Volume = 0;
      osc2Volume = 0;
      osc3Volume = 0;
      ADSR.noteOff();
    }

  14. #14
    Senior Member
    Join Date
    Jul 2020
    Posts
    1,762
    Cutting a sine wave off at a zero-crossing is going to reduce the audible transient, but not eliminate it completely.

    To do that you need to multiply the sine-wave with a smooth envelope to reduce it to zero with basically
    no discontinuities. Ditto for starting a sine wave. The standard envelope object in the Audio library is rather
    simplistic but will do better than a sudden switch-off, especially with suitable long attack and decay times.

    It does have problems when you retrigger the envelope before its completely decayed as ideally you'd mix
    together the old decaying wave with the new attack-phase of the new note - but that's more complex processing
    than a simple envelope.

    What you see on a spectrum analyzer when a sine wave starts or stops suddenly (even if at a zero crossing),
    is a pulse of energy across a wide range of frequencies, not just that of the sinewave - the transient at
    start or stop has a wide bandwidth, and that is what we hear as a click (or thump when lower frequencies
    predominate).

    One approach you can use to tame this is to pass the waveform through a band-pass filter so that the
    start-up and stop transients are band-limited - this can work well, but won't help so much it you want
    to change to a completely different note suddenly - the filter is tuned to the current note, not the new note.

    For that it may be best to have two oscilators and two bandpass filters and mix them together - in other words don't
    reuse a voice immediately for a new note, but have a second voice for the new note, and retire the old voice
    for reuse after a suitable period to allow it to ring down smoothly.

    Of course for general waveforms you need the envelope approach, not the bandpass filter, but the same idea
    applies - don't reuse the same envelope for two successive notes if you want the cleanest transition - allow
    both notes their own envelopes so one can tail off as the other ramps up...

  15. #15
    Yeah, good explanation! However, these are significant clicks. Something ain't right.

  16. #16
    However, I am now going to start over and see if I can get what you just suggested to work. Thanks.

  17. #17
    So to start fresh, for my simple three oscillator synth, I am going to focus on oscillator 1, the one that the keyboard directly controls, and it is actually going to be two oscillators under the hood. And for the monophonic version, a note on will pick the unused of the two oscillator-bandpass-adsr signals to use. The adsr proper the synth, the one with knobs for the user to twiddle before the amplifier, won't be part of this...these are just to tame transients during note on and note offs. Does this sound right? And is the knob controlled ADSR in a synth with a low pass filter controllable in real time with resonance and frequency knobs usually located ahead of the ADSR knob section in the signal path?

  18. #18
    Senior Member
    Join Date
    Jul 2020
    Posts
    1,762
    I don't know a great deal about synth architectures though, but I do know that sometimes these clicks are wanted -
    for example the Hammond organ, with its electro-mechanical nature, has switching transients (clicks) due to the
    simple use of switches for each note. However each key has 9 switches that switch at about the same time, depending
    on how fast the key is moving, one for each drawbar - apparently when creating a Hammond emulations, for an
    really authentic sound you have to emulate this multiple-transient nature of the key switching - the ear is very
    good at characterizing the nature of transients, so its probably best to be quite flexible about how much transient
    and transient bandwidth you have as controlling these parameters will enable a wider range of effects.

    Consider the difference between a harpsichord and piano and dulcimer - the transients make a lot of the character.

  19. #19
    For sure. These are clicks that are digital and bad, not attack sounds or cool instrument impacts. They sound like digital clipping or when you truncate a wave off of a zero crossing or the clicks you get down an ADAT line when your clocks aren't synced or...

  20. #20
    ...so they have to go. I just need to minimize the collateral damage to desirable transients somehow.

  21. #21
    Senior Member fdaniels's Avatar
    Join Date
    Oct 2020
    Location
    Ostwestfalen, Germany
    Posts
    120
    These clicks would also happen in an analog Oscillator if you "switch" it on fast enough. Its not a "digital" problem, its a physical limitation.

  22. #22
    So I started over and have some pretty good results.
    But now there is a problem I can't even begin to understand!
    So I have two voices working with two oscillators each, ADSR filter to smooth the transitions, bandpass filter to further control clicks, etc. And all is well! I can play nice sounds with either or both voices turned up and in any combination of waveforms (each one has a physical three-way switch to a pair of pullup resistors- so switch case 0, 1 is Saw, switch case 1, 0 is square, and switch case 1, 1 is sine.)
    So!
    If I play the attached MIDI controller, an AKAI Max25, everything works REALLY well. It's monophonic on purpose, the waveform switching and mixing is all good, no crackles or pops, etc.
    Now when I turn the arpeggiator of the AKAI on...the first waveform arbitrarily switches to a square wave!
    I've definitely run the serial monitor and have the program printing the pin states of the D1 and D2 (where that switch connects) at the moment it chooses the waveform...
    Somehow, running the arpeggiator is causing the Teensy to pick up a different switch case!
    What is weird is that no matter how fast I play, or how I overlap tones, or ANYTHING, this doesn't happen! Only the arpeggiator causes it...even if the tempo is glacial...
    This is driving me nuts!
    Any guesses?
    My first thoughts were to the hardware- the grounds, the wires, etc.
    But I can't make this happen while physically playing the MIDI controller!
    And I have a test program that shows the pins in real time that I used when wiring up the hardware- no matter how i shake or slide the box or switches, they are working fine (showing 0 or 1 as pullup switches should be...)

    Code:
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    
    // GUItool: begin automatically generated code
    AudioSynthWaveform       Osc1a;      //xy=55,52.11111068725586
    AudioSynthWaveform       Osc1b;      //xy=55,99.11111450195312
    AudioSynthWaveform       Osc2a;      //xy=56,175
    AudioSynthWaveform       Osc2b;      //xy=57,214
    AudioFilterBiquad        Osc1bBPFilter;        //xy=192,100
    AudioFilterBiquad        Osc1aBPFilter;        //xy=194,52
    AudioFilterBiquad        Osc2aBPFilter;        //xy=197,175
    AudioFilterBiquad        Osc2bBPFilter;        //xy=197,218
    AudioEffectEnvelope      Osc1aAttack;      //xy=349,64
    AudioEffectEnvelope      Osc1bAttack;      //xy=349,100
    AudioEffectEnvelope      Osc2bAttack;      //xy=352,221
    AudioEffectEnvelope      Osc2aAttack;      //xy=354,177
    AudioMixer4              Osc1Mix;         //xy=488,99
    AudioMixer4              Osc2Mix;         //xy=491,201
    AudioMixer4              MasterL;         //xy=741.111083984375,70
    AudioMixer4              MasterR;         //xy=740.888916015625,135.88888549804688
    AudioOutputI2S           i2s1;           //xy=866,104
    AudioConnection          patchCord1(Osc1a, Osc1aBPFilter);
    AudioConnection          patchCord2(Osc1b, Osc1bBPFilter);
    AudioConnection          patchCord3(Osc2a, Osc2aBPFilter);
    AudioConnection          patchCord4(Osc2b, Osc2bBPFilter);
    AudioConnection          patchCord5(Osc1bBPFilter, Osc1bAttack);
    AudioConnection          patchCord6(Osc1aBPFilter, Osc1aAttack);
    AudioConnection          patchCord7(Osc2aBPFilter, Osc2aAttack);
    AudioConnection          patchCord8(Osc2bBPFilter, Osc2bAttack);
    AudioConnection          patchCord9(Osc1aAttack, 0, Osc1Mix, 0);
    AudioConnection          patchCord10(Osc1bAttack, 0, Osc1Mix, 1);
    AudioConnection          patchCord11(Osc2bAttack, 0, Osc2Mix, 1);
    AudioConnection          patchCord12(Osc2aAttack, 0, Osc2Mix, 0);
    AudioConnection          patchCord13(Osc1Mix, 0, MasterL, 0);
    AudioConnection          patchCord14(Osc1Mix, 0, MasterR, 0);
    AudioConnection          patchCord15(Osc2Mix, 0, MasterL, 1);
    AudioConnection          patchCord16(Osc2Mix, 0, MasterR, 1);
    AudioConnection          patchCord17(MasterL, 0, i2s1, 0);
    AudioConnection          patchCord18(MasterR, 0, i2s1, 1);
    AudioControlSGTL5000     sgtl5000_1;     //xy=699.2222290039062,504.4444580078125
    // GUItool: end automatically generated code
    
    
    #include <MIDI.h>
    
    MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
    
    
    
    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;
    
    
    
    int Osc1aTurn = 0;
    int Osc1bTurn = 0;
    int Osc2aTurn = 0;
    int Osc2bTurn = 0;
    int Osc1Playing = 0;
    int Osc2Playing = 0;
    int notePlaying = 0;
    float Osc1aVolume = .5;
    float Osc1bVolume = .5;
    float Osc2Volume = .50;
    float Osc1aPitch = 0.220;
    float Osc1bPitch = 0.440;
    float Osc2aPitch = 0.220;
    float Osc2bPitch = 0.440;
    
    int Osc1current_waveform = WAVEFORM_SINE;
    int Osc2current_waveform = WAVEFORM_SINE;
    int Osc3current_waveform = WAVEFORM_SINE;
    
    void setup() {
    
      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);
      
      AudioMemory(120);
    
      sgtl5000_1.enable();
      sgtl5000_1.volume(0.8);
      
      MIDI.begin(MIDI_CHANNEL_OMNI);
      MIDI.setHandleNoteOn(OnNoteOn);
      MIDI.setHandleNoteOff(OnNoteOff);
      
      Osc1Mix.gain(0, 0.7);
      Osc1Mix.gain(1, 0.7);
      Osc2Mix.gain(0, 0.7);
      Osc2Mix.gain(1, 0.7);
      MasterL.gain(0, 0.7);
      MasterL.gain(1, 0.7);
      MasterR.gain(0, 0.7);
      MasterR.gain(1, 0.7);
    
      Osc1aAttack.release(25);
      Osc1bAttack.release(25);
      Osc2aAttack.release(25);
      Osc2bAttack.release(25);
      Osc1aAttack.releaseNoteOn(15);
      Osc1bAttack.releaseNoteOn(15);
      Osc2aAttack.releaseNoteOn(15);
      Osc2bAttack.releaseNoteOn(15);
      Osc1aBPFilter.setBandpass(0, Osc1aPitch, 0.6);
      Osc1bBPFilter.setBandpass(0, Osc1bPitch, 0.6);
      Osc2aBPFilter.setBandpass(0, Osc2aPitch, 0.6);
      Osc2bBPFilter.setBandpass(0, Osc2bPitch, 0.6);
    }
    
    void loop() {
      getReadings();
      MIDI.read();
      Osc1a.begin(Osc1aVolume, Osc1aPitch, Osc1current_waveform);
      Osc1b.begin(Osc1bVolume, Osc1bPitch, Osc1current_waveform);
      Osc2a.begin((Osc2Volume * Osc1aVolume), Osc2aPitch, Osc2current_waveform);
      Osc2b.begin((Osc2Volume * Osc1bVolume), Osc2bPitch, Osc2current_waveform);
      
    }
    
    void OnNoteOn(byte channel, byte note, byte velocity)
    {
      Serial.println("Note!");
      float hertz = (pow (2.0, ((float)(note-69)/12.0)))*440;
      Serial.print("Note: ");
      Serial.println(note);
      Serial.print("Velocity: ");
      Serial.println(velocity);
      Serial.print("");
      Serial.println("");
      
      if ((Osc1aTurn == 1) && (Osc1Playing == 0)){
        Osc1aPitch = hertz;
        Osc1aVolume = ((float)velocity/150.00); 
        Osc1aBPFilter.setBandpass(0, Osc1aPitch, 0.1);
        
        if ((osc1Shape1Val == 0) && (osc1Shape2Val == 1)){
        Osc1current_waveform = WAVEFORM_SQUARE;
        Serial.println("osc1 square");
        Serial.println(digitalRead(osc1ShapePin1));
        Serial.println(digitalRead(osc1ShapePin2));
        }
        else if (osc1Shape2Val == 0){
        Osc1current_waveform = WAVEFORM_SAWTOOTH;
        Serial.println("osc1 saw");
        }
        else if ((osc1Shape1Val == 1) && (osc1Shape2Val == 1)){
        Osc1current_waveform = WAVEFORM_SINE;
        Serial.println("osc1 sine");
        } 
        Osc1aAttack.noteOn();
        Serial.println("Osc1a On");
        Serial.print("Osc1a Volume: ");
        Serial.println(Osc1aVolume);
        Serial.print("Osc1a Frequency: ");
        Serial.println(Osc1aPitch);
        Osc1Playing = 1; 
        notePlaying = note;
        Osc1aTurn = 0;
      }
      else if ((Osc1Playing == 0) && (Osc1aTurn == 0)){
        Osc1bPitch = hertz;
        Osc1bVolume = ((float)velocity/150.00);
        Osc1bBPFilter.setBandpass(0, Osc1bPitch, 0.1);
        
        if ((osc1Shape1Val == 0) && (osc1Shape2Val == 1)){
        Osc1current_waveform = WAVEFORM_SQUARE;
        Serial.println("osc1 square");
        Serial.println(digitalRead(osc1ShapePin1));
        Serial.println(digitalRead(osc1ShapePin2));
        }
        else if (osc1Shape2Val == 0){
        Osc1current_waveform = WAVEFORM_SAWTOOTH;
        Serial.println("osc1 saw");
        }
        else if ((osc1Shape1Val == 1) && (osc1Shape2Val == 1)){
        Osc1current_waveform = WAVEFORM_SINE;
        Serial.println("osc1 sine");
        } 
        
        Osc1bAttack.noteOn();
        Serial.println("Osc1b On"); 
        Serial.print("Osc1b Volume: ");
        Serial.println(Osc1bVolume);
        Serial.print("Osc1b Frequency: ");
        Serial.println(Osc1bPitch);
        Osc1Playing = 1;
        notePlaying = note;
        Osc1aTurn = 1;
      }
     
    
    if ((Osc2aTurn == 1) && (Osc2Playing == 0)){
        Osc2aPitch = hertz;
        Osc2Volume = ((float)velocity/150.00); 
        Osc2aBPFilter.setBandpass(0, Osc2aPitch, 0.1);
        
        if (osc2Shape1Val == 0){
        Osc2current_waveform = WAVEFORM_SQUARE;
        Serial.println("osc2 square");
        }
        else if (osc2Shape2Val == 0){
        Osc2current_waveform = WAVEFORM_SAWTOOTH;
        Serial.println("osc2 saw");
        }
        else {
        Osc2current_waveform = WAVEFORM_SINE;
        Serial.println("osc2 sine");
        } 
        Osc2aAttack.noteOn();
        Serial.println("Osc2a On");
        Serial.print("Osc2a Volume: ");
        Serial.println(Osc2Volume);
        Serial.print("Osc2a Frequency: ");
        Serial.println(Osc2aPitch);
        Osc2Playing = 1; 
       // notePlaying = note;
        Osc2aTurn = 0;
      }
      else if ((Osc2Playing == 0) && (Osc2aTurn == 0)){
        Osc2bPitch = hertz;
        Osc2Volume = ((float)velocity/150.00);
        Osc2bBPFilter.setBandpass(0, Osc2bPitch, 0.1);
        
        if (osc2Shape1Val == 0){
        Osc2current_waveform = WAVEFORM_SQUARE;
        Serial.println("osc2 square");
        }
        else if (osc2Shape2Val == 0){
        Osc2current_waveform = WAVEFORM_SAWTOOTH;
        Serial.println("osc2 saw");
        }
        else {
        Osc2current_waveform = WAVEFORM_SINE;
        Serial.println("osc2 sine");
        } 
        Osc2bAttack.noteOn();
        Serial.println("Osc2b On"); 
        Serial.print("Osc2b Volume: ");
        Serial.println(Osc2Volume);
        Serial.print("Osc2b Frequency: ");
        Serial.println(Osc2bPitch);
        Osc2Playing = 1;
        //notePlaying = note;
        Osc2aTurn = 1;
      }
     
      
      
    }
    
    void OnNoteOff(byte channel, byte note, byte velocity)
    {
        if (note == notePlaying){
        Osc1aAttack.noteOff();
        Osc1bAttack.noteOff();  
        Osc2aAttack.noteOff();
        Osc2bAttack.noteOff();
        Osc1Playing = 0; 
        Osc2Playing = 0;
        }
      
    } 
    
    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);
    
      Osc2Volume = (analogRead(osc2LevelPin)/1027.00);
    
      
      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);  
    
    }

  23. #23
    Well, I kinda figured it out and it's capped off a pretty long day with a sad sort of ending.
    I have to put a delay in my button reads...and now there's a little crackle at the beginning of each time through the main loop when on sine waves.
    How do you read button press states without needing to put a little delay, or how can you make that little delay not interrupt the audio stream?

  24. #24
    Also, this is much more apparent with the sine wave...it is masked pretty well with saws and square waves.

  25. #25
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    608
    Aren't doing Osc1.begin in the main loop a little to fast changing/starting the waveform?
    You should only call the .begin function once when the waveform change command is received.

Posting Permissions

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