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

Thread: Teensy Synth crackling sound when midi notes trigger

  1. #1
    Junior Member
    Join Date
    Jun 2020
    Posts
    7

    Teensy Synth crackling sound when midi notes trigger

    I'm new to Teensy and wonder if anyone can help me by suggesting any way forward to this problem?

    I'm using a Teensy 4.1 and the new Audio Board for the 4.

    Background
    I have always wanted to build a synth and have been following Notes and Volts series on YouTube. I've reached episode 9 (https://www.youtube.com/watch?v=l34CNfwfuIY&t=328s ) where you build a 5 pin DIN traditional midi-input circuit. Everything was going well!

    Problem
    When I play notes, the synth plays the right sounds but they are accompanied by a slight crackling sound (only when the notes are triggered).

    What I have tried so far:

    1. I swapped the midi keyboard for another one - no change.
    2. I found a post on this forum suggesting using the pjrc midi input circuit rather than the Notes and Volts one (there is an additional 1uf capacitor on the pjrc version). I fitted capacitor - no change.
    3. I've downloaded a trial version of Ableton Live and set it up so that it provides the USB midi notes (instead of a physical keyboard) - the notes sound, but so does the crackling.



    So I am thinking - it can only be the code (see below) or the boards?

    The code? - Lots of people seem to have completed the Notes and Volts Teensy Synth and I haven't seen any mention in the comments on YouTube of the issue.

    The board? The examples (e.g. guitar, PlaySynthMusic) all work fine, although of course they don't need any midi notes. And the MidiSynth examples are not compatible with Teensy 4 so I can't test it with those. Might it be that the Notes and Volts code, which was designed for Teensy 3.2 is not able to run properly on Teensy 4 even though it compiles and loads OK?

    I also thought maybe it's my soldering? But I have checked all the solder joints with a magnifying glass and they're all good.

    And now I am stumped!

    Any advice much appreciated.

    Thanks in advance.

    Code:
    // Teensy-Synth Part 9
    // 5-PIN MIDI INPUT
    // By Notes and Volts
    // www.notesandvolts.com
    
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    #include <MIDI.h>
    
    // GUItool: begin automatically generated code
    AudioSynthWaveform       waveform2;      //xy=382,453
    AudioSynthWaveform       waveform1;      //xy=386,394
    AudioSynthNoisePink      pink1;          //xy=394,504
    AudioMixer4              mixer1;         //xy=577,454
    AudioFilterStateVariable filter1;        //xy=725,457
    AudioEffectEnvelope      envelope1;      //xy=888,458
    AudioOutputI2S           i2s1;           //xy=1049,465
    AudioConnection          patchCord1(waveform2, 0, mixer1, 1);
    AudioConnection          patchCord2(waveform1, 0, mixer1, 0);
    AudioConnection          patchCord3(pink1, 0, mixer1, 2);
    AudioConnection          patchCord4(mixer1, 0, filter1, 0);
    AudioConnection          patchCord5(filter1, 0, envelope1, 0);
    AudioConnection          patchCord6(envelope1, 0, i2s1, 0);
    AudioConnection          patchCord7(envelope1, 0, i2s1, 1);
    AudioControlSGTL5000     sgtl5000_1;     //xy=565,556
    // GUItool: end automatically generated code
    
    
    
    
    // GLOBAL VARIABLES
    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;
    
    MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
    
    
    void setup() {
      AudioMemory(30);
      usbMIDI.setHandleControlChange(myControlChange);
      usbMIDI.setHandleNoteOff(myNoteOff);
      usbMIDI.setHandleNoteOn(myNoteOn);
      usbMIDI.setHandlePitchChange(myPitchBend);
    
      MIDI.begin();
    
      MIDI.setHandleNoteOn(myNoteOn);
      MIDI.setHandleNoteOff(myNoteOff);
      MIDI.setHandlePitchBend(myPitchBend);
      MIDI.setHandleControlChange(myControlChange);
      
      sgtl5000_1.enable();
      sgtl5000_1.volume(0.32);
      waveform1.begin(WAVEFORM_SAWTOOTH);
      waveform1.amplitude(0.75);
      waveform1.frequency(82.41);
      waveform1.pulseWidth(0.15);
    
      waveform2.begin(WAVEFORM_SAWTOOTH);
      waveform2.amplitude(0.75);
      waveform2.frequency(123);
      waveform2.pulseWidth(0.15);
    
      pink1.amplitude(1.0);
    
      mixer1.gain(0, 1.0);
      mixer1.gain(1, 1.0);
      mixer1.gain(2, 0.0);
    
      envelope1.attack(0);
      envelope1.decay(0);
      envelope1.sustain(1);
      envelope1.release(500);
    }
    
    
    void loop() {
      usbMIDI.read();
      MIDI.read();
      LFOupdate(false, LFOmodeSelect, FILfactor, LFOdepth);
    }
    
    void myNoteOn(byte channel, byte note, byte velocity) {
      if ( note > 23 && note < 108 ) {
        globalNote = note;
        globalVelocity = velocity;
        keyBuff(note, true);
        LFOupdate(true, LFOmodeSelect, FILfactor, LFOdepth);
      }
    }
    
    void myNoteOff(byte channel, byte note, byte velocity) {
      if ( note > 23 && note < 108 ) {
        keyBuff(note, false);
      }
    }
    
    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();
    }
    
    void oscStop() {
      envelope1.noteOff();
    }
    
    void oscSet() {
      waveform1.frequency(noteFreqs[globalNote] * bendFactor * LFOpitch);
      waveform2.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;
          }
        }
      }
    }
    Last edited by Robert; 06-29-2020 at 09:34 PM.

  2. #2
    Junior Member
    Join Date
    Jun 2020
    Posts
    7
    Just a quick note to add that I have now swapped a 3.6 for the 4.1 and the crackle problem persists. It's bad enough to make make it unpleasant to listen to. I notice that it is worse when I haven't released the previous note before pressing the next one, and this is the same using USBmidi when there is any overlap of notes, but even without overlap the crackle is still present.

  3. #3
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,422
    I suspect that you need a non-zero value for the envelope's attack. Try
    Code:
      envelope1.attack(10);
    Pete

  4. #4
    Junior Member
    Join Date
    Jun 2020
    Posts
    7
    Hi Pete, thank you for looking at this - it is much appreciated. I've made the change but unfortunately no luck. I've also been able to use the Pure Data app to load Notes and Volts' virtual fascia and changed other controls using this, but can't get rid off the crackle.

  5. #5
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,422
    I've just realized that you also have the gains of the first two inputs to mixer1 set to 1. The sum of the gains of the inputs to a mixer should be 1 unless you know that the inputs are attenuated.
    Try:
    Code:
      mixer1.gain(0, 0.5);
      mixer1.gain(1, 0.5);
    Pete

  6. #6
    Senior Member
    Join Date
    Apr 2019
    Posts
    109
    Hello Robert, I tried the code from your first post on a T4.1 with the Audio Board Rev D and cannot hear any crackling. I've sent MIDI via both Serial1 and the USB Client with Ableton Live. There's a click when going from one note to another quickly, because it's monophonic, but no crackling. It could be interference on the Audio Board from some residue bridging between joints. But since it's also happening on a T3.6, I suspect it's not the soldering. Can you record the crackling? I've heard enough crackling whilst developing a synth to recognise different types by now.

  7. #7
    Junior Member
    Join Date
    Jun 2020
    Posts
    7
    Thanks Pete and thanks also to UHF for going to the trouble of trying the code. I really appreciate your help.

    Changing the gain on the mixers, as suggested by Pete, has reduced the crackling a lot. There is still some "ttt" noise at note on. I think it may be the cross-over of notes as suggested by UHF? If so, to be honest, I can live with this and will try to learn to play in a more staccato way.

    I've recorded a short sample. Terrible playing but I had to lean over the laptop to reach the keyboard (that's my excuse). Audio files can't be uploaded with this message. However, there is a link below.

    Best wishes

    Robert

    http://s000.tinyupload.com/?file_id=...95457651914351

  8. #8
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,422
    Increase the attack: try 50.

    Pete

  9. #9
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,422
    Actually, it might be that the release time is too long. I looked at the waveform and the click/crackle might also be caused by the release time being interrupted by the next note on. In many of the notes, the release time has only lasted about 150ms before the next note starts. This causes the release to be cut off abruptly so that the next note can start.
    Try a release of about 100.

    Pete

  10. #10
    Senior Member
    Join Date
    Apr 2019
    Posts
    109
    Hello, it sounds fine, it's just the change-over from one note to another. The best way to eliminate it, is to make it polyphonic.

    But try adding envelope1.releaseNoteOn(milliseconds); This might have a positive effect.

    releaseNoteOn(milliseconds) - Set a quick release time to be used when a new note is started while the envelop is in any state passing the signal. This will add latency before your new attack phase begins, so short times are recommended. Zero may be used to completely disable this feature (never extra latency). Longer times help reduce clicks or pops. The default is 5 millisecond.

  11. #11
    Junior Member
    Join Date
    Jun 2020
    Posts
    7
    Thank you both for your help. I think the ultimate answer is polyphonic as UHF says and looking around the pjrc projects I see that there are a couple of interesting ones to explore including string-flow. best wishes Robert

Posting Permissions

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