Teensy Audio Library - Strange Pops/Clicks in Output

Status
Not open for further replies.
Hi all,

I am having an audio output issue concerning clicks/pops/glitchiness in the audio.
The audio below is just the right channel (using each DAC as a mono channel), but it is doing the same with the left channel.
Occurs both through headphones on the audio shield and through USB audio (haven't tested the DACs yet, but last time I checked with an oscilloscope I could not see anything strange like this in the waveforms).

The problem seemed to start after I added 2 extra buttons & 4 extra pots, which I am pretty sure is more than in the examples.

Code:
#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

int i = 0;
int current_waveform = 0;
extern const int16_t myWaveform[256];  // defined in myWaveform.ino

float knob2;
float knob3;
float knob4;
float knob5;
float knob6;
float knob7;
float knob8;
float knob9;
float knob10;
float knob11;
float knob12;
float knob13;
float knob14;
float knob15;

float knob16;
float knob17;
float knob18;
float knob19;
float knob20;
float knob21;
float knob22;
float knob23;
float knob24;
float knob25;
float knob26;
float knob27;
float knob28;
float knob29;

Bounce button0 = Bounce(0, 15);
Bounce button1 = Bounce(1, 15);  // 15 = 15 ms debounce time
Bounce button2 = Bounce(2, 15);

Bounce button3 = Bounce(3, 15);
Bounce button4 = Bounce(4, 15);  // 15 = 15 ms debounce time
Bounce button5 = Bounce(5, 15);
Bounce button6 = Bounce(24, 15);
Bounce button7 = Bounce(25, 15);

// GUItool: begin automatically generated code
AudioSynthWaveform       waveform1;      //xy=140,129
AudioSynthWaveform       waveform2;      //xy=170,392
AudioSynthWaveformSineModulated sine_fm2;       //xy=214,452
AudioSynthWaveformSineModulated sine_fm1;       //xy=219,187
AudioSynthWaveform       waveform3;      //xy=226,234
AudioSynthWaveformModulated waveformMod1;   //xy=232,280
AudioSynthWaveform       waveform4;      //xy=235,498
AudioSynthWaveformModulated waveformMod2;   //xy=260,544
AudioEffectEnvelope      envelope1;      //xy=414,294
AudioMixer4              mixer1;         //xy=425,183
AudioEffectEnvelope      envelope2;      //xy=437,542
AudioMixer4              mixer3;         //xy=444,464
AudioMixer4              mixer2;         //xy=561,264
AudioMixer4              mixer4;         //xy=564,524
//AudioOutputUSB           usb;           //xy=715,483
AudioOutputAnalogStereo  dacs;          //xy=721,387
AudioOutputI2S           i2s1;           //xy=721,431
AudioConnection          patchCord1(waveform1, sine_fm1);
AudioConnection          patchCord2(waveform1, 0, mixer1, 0);
AudioConnection          patchCord3(waveform1, 0, waveformMod1, 0);
AudioConnection          patchCord4(waveform2, sine_fm2);
AudioConnection          patchCord5(waveform2, 0, mixer3, 0);
AudioConnection          patchCord6(waveform2, 0, waveformMod2, 1);
AudioConnection          patchCord7(sine_fm2, 0, mixer3, 2);
AudioConnection          patchCord8(sine_fm1, 0, mixer1, 2);
AudioConnection          patchCord9(waveform3, 0, mixer1, 1);
AudioConnection          patchCord10(waveform3, 0, waveformMod1, 1);
AudioConnection          patchCord11(waveformMod1, 0, mixer1, 3);
AudioConnection          patchCord12(waveform4, 0, mixer3, 1);
AudioConnection          patchCord13(waveform4, 0, waveformMod2, 0);
AudioConnection          patchCord14(waveformMod2, 0, mixer3, 3);
AudioConnection          patchCord15(envelope1, 0, mixer2, 1);
AudioConnection          patchCord16(mixer1, 0, mixer2, 0);
AudioConnection          patchCord17(mixer1, envelope1);
AudioConnection          patchCord18(envelope2, 0, mixer4, 1);
AudioConnection          patchCord19(mixer3, 0, mixer4, 0);
AudioConnection          patchCord20(mixer3, envelope2);
AudioConnection          patchCord21(mixer2, 0, dacs, 0);
AudioConnection          patchCord22(mixer2, 0, i2s1, 0);
//AudioConnection          patchCord23(mixer2, 0, usb, 0);
AudioConnection          patchCord24(mixer4, 0, dacs, 1);
AudioConnection          patchCord25(mixer4, 0, i2s1, 1);
//AudioConnection          patchCord26(mixer4, 0, usb, 1);
//AudioControlSGTL5000     sgtl5000_1;     //xy=424,110
// GUItool: end automatically generated code


AudioControlSGTL5000 audioShield;

void setup() {
  Serial.begin(9600);

// Configure both to use "myWaveform" for WAVEFORM_ARBITRARY
waveformMod1.arbitraryWaveform(myWaveform, 172.0);
current_waveform = WAVEFORM_TRIANGLE_VARIABLE;
waveformMod1.begin(current_waveform);

waveformMod2.arbitraryWaveform(myWaveform, 172.0);
current_waveform = WAVEFORM_TRIANGLE_VARIABLE;
waveformMod2.begin(current_waveform);
  
  AudioMemory(20);
  //sgtl5000_1.enable();
  //sgtl5000_1.volume(0.32);
  audioShield.enable();
  audioShield.volume(0.25); //0.1?
  dacs.analogReference(EXTERNAL); // = 3.3v / (INTERNAL) = 1.2v
  
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(24, INPUT_PULLUP);
  pinMode(25, INPUT_PULLUP);


  //delay(300);
  
  mixer1.gain(0, 0.75);
  mixer1.gain(1, 0.0);
  mixer1.gain(2, 0.0);
  mixer1.gain(3, 0.0);
  mixer2.gain(0, 0.15);
  mixer2.gain(1, 0.0);
  mixer2.gain(2, 0.0);
  mixer2.gain(3, 0.0);
  mixer3.gain(0, 0.75);
  mixer3.gain(1, 0.0);
  mixer3.gain(2, 0.0);
  mixer3.gain(3, 0.0);
  mixer4.gain(0, 0.15);
  mixer4.gain(1, 0.0);
  mixer4.gain(2, 0.0);
  mixer4.gain(3, 0.0);
  
  waveform1.begin(WAVEFORM_SAWTOOTH);
  waveform1.amplitude(0.75);
  waveform1.frequency(50);
  waveform1.pulseWidth(0.15);
  sine_fm1.frequency(440);
  sine_fm1.amplitude(0.75);
  waveform3.begin(WAVEFORM_SAWTOOTH);
  waveform3.frequency(200);
  waveform3.amplitude(0.75);
  waveform3.pulseWidth(0.15);
  envelope1.attack(10);
  envelope1.hold(10);
  envelope1.decay(25);
  envelope1.sustain(0.4);
  envelope1.release(70);

  waveform2.begin(WAVEFORM_SAWTOOTH);
  waveform2.amplitude(0.75);
  waveform2.frequency(50);
  waveform2.pulseWidth(0.15);
  sine_fm2.frequency(440);
  sine_fm2.amplitude(0.75);
  waveform4.begin(WAVEFORM_SAWTOOTH);
  waveform4.frequency(200);
  waveform4.amplitude(0.75);
  waveform4.pulseWidth(0.15);
  envelope2.attack(10);
  envelope2.hold(10);
  envelope2.decay(25);
  envelope2.sustain(0.4);
  envelope2.release(70);
}

int waveform_type = WAVEFORM_SAWTOOTH;
int mixer1_setting = 0;
int mixer2_setting = 0;
int mixer3_setting = 0;
int mixer4_setting = 0;
elapsedMillis timeout = 0;
bool mixer2_envelope = false;
bool mixer4_envelope = false;

float mapf(float x, float in_min, float in_max, float out_min, float out_max) 
 {
   return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
 }

void loop() {
  /*
  Serial.print(digitalRead(photoTransistor));
  Serial.print (" - Time taken = ");
  Serial.print (elapsedTime/1000000, 4); //for S
  Serial.print ("s - ");
  Serial.print(motorhz); //print revolutions per second or Hz. And print new line or enter.
  Serial.print(" Hz - "); //print the word "Hz". */

  knob2 = (float)analogRead(A2) / 1023.0;
  knob3 = (float)analogRead(A3) / 1023.0;
  knob4 = (float)analogRead(A6) / 1023.0;
  knob5 = (float)analogRead(A2) / 1023.0;
  knob6 = (float)analogRead(A3) / 1023.0;
  knob7 = (float)analogRead(A6) / 1023.0;
  knob8 = (float)analogRead(A2) / 1023.0;
  knob9 = (float)analogRead(A3) / 1023.0;
  knob10 = (float)analogRead(A6) / 1023.0;
  knob11 = (float)analogRead(A7) / 1023.0;
  knob12 = (float)analogRead(A2) / 1023.0;
  knob13 = (float)analogRead(A3) / 1023.0;
  knob14 = (float)analogRead(A6) / 1023.0;
  knob15 = (float)analogRead(A7) / 1023.0;
  
  knob16 = (float)analogRead(A16) / 1023.0;
  knob17 = (float)analogRead(A17) / 1023.0;
  knob18 = (float)analogRead(A18) / 1023.0;
  knob19 = (float)analogRead(A16) / 1023.0;
  knob20 = (float)analogRead(A17) / 1023.0;
  knob21 = (float)analogRead(A18) / 1023.0;
  knob22 = (float)analogRead(A16) / 1023.0;
  knob23 = (float)analogRead(A17) / 1023.0;
  knob24 = (float)analogRead(A18) / 1023.0;
  knob25 = (float)analogRead(A20) / 1023.0;
  knob26 = (float)analogRead(A16) / 1023.0;
  knob27 = (float)analogRead(A17) / 1023.0;
  knob28 = (float)analogRead(A18) / 1023.0;
  knob29 = (float)analogRead(A20) / 1023.0;
  
  button0.update();
  button1.update();
  button2.update();
  button3.update();
  button4.update();
  button5.update();
  button6.update();
  button7.update();

  // Button 0 or 2 changes the waveform type
  if (button6.fallingEdge()) { // change from button 2
    switch (current_waveform) {
      case WAVEFORM_SINE:
        current_waveform = WAVEFORM_SAWTOOTH;
        Serial.println("Sawtooth");
        break;
      case WAVEFORM_SAWTOOTH:
        current_waveform = WAVEFORM_SAWTOOTH_REVERSE;
        Serial.println("Reverse Sawtooth");
        break;
      case WAVEFORM_SAWTOOTH_REVERSE:
        current_waveform = WAVEFORM_SQUARE;
        Serial.println("Square");
        break;
      case WAVEFORM_SQUARE:
        current_waveform = WAVEFORM_TRIANGLE;
        Serial.println("Triangle");
        break;
      case WAVEFORM_TRIANGLE:
        current_waveform = WAVEFORM_TRIANGLE_VARIABLE;
        Serial.println("Variable Triangle");
        break;
      case WAVEFORM_TRIANGLE_VARIABLE:
        current_waveform = WAVEFORM_ARBITRARY;
        Serial.println("Arbitary Waveform");
        break;
      case WAVEFORM_ARBITRARY:
        current_waveform = WAVEFORM_PULSE;
        Serial.println("Pulse");
        break;
      case WAVEFORM_PULSE:
        current_waveform = WAVEFORM_SAMPLE_HOLD;
        Serial.println("Sample & Hold");
        break;
      case WAVEFORM_SAMPLE_HOLD:
        current_waveform = WAVEFORM_SINE;
        Serial.println("Sine");
        break;
    }
    waveformMod1.begin(current_waveform);   
  }
if (button7.fallingEdge()) { // change from button 2
    switch (current_waveform) {
      case WAVEFORM_SINE:
        current_waveform = WAVEFORM_SAWTOOTH;
        Serial.println("Sawtooth");
        break;
      case WAVEFORM_SAWTOOTH:
        current_waveform = WAVEFORM_SAWTOOTH_REVERSE;
        Serial.println("Reverse Sawtooth");
        break;
      case WAVEFORM_SAWTOOTH_REVERSE:
        current_waveform = WAVEFORM_SQUARE;
        Serial.println("Square");
        break;
      case WAVEFORM_SQUARE:
        current_waveform = WAVEFORM_TRIANGLE;
        Serial.println("Triangle");
        break;
      case WAVEFORM_TRIANGLE:
        current_waveform = WAVEFORM_TRIANGLE_VARIABLE;
        Serial.println("Variable Triangle");
        break;
      case WAVEFORM_TRIANGLE_VARIABLE:
        current_waveform = WAVEFORM_ARBITRARY;
        Serial.println("Arbitary Waveform");
        break;
      case WAVEFORM_ARBITRARY:
        current_waveform = WAVEFORM_PULSE;
        Serial.println("Pulse");
        break;
      case WAVEFORM_PULSE:
        current_waveform = WAVEFORM_SAMPLE_HOLD;
        Serial.println("Sample & Hold");
        break;
      case WAVEFORM_SAMPLE_HOLD:
        current_waveform = WAVEFORM_SINE;
        Serial.println("Sine");
        break;
    }
      waveformMod2.begin(current_waveform); 
}

  // Left changes the type of control waveform
  if (button0.fallingEdge()) {
    Serial.print("Control waveform 1: ");
    if (waveform_type == WAVEFORM_SAWTOOTH) {
      waveform_type = WAVEFORM_SINE;
      Serial.println("Sine");
    } else if (waveform_type == WAVEFORM_SINE) {
      waveform_type = WAVEFORM_SQUARE;
      Serial.println("Square");
    } else if (waveform_type == WAVEFORM_SQUARE) {
      waveform_type = WAVEFORM_TRIANGLE;
      Serial.println("Triangle");
    } else if (waveform_type == WAVEFORM_TRIANGLE) {
      waveform_type = WAVEFORM_PULSE;
      Serial.println("Pulse");
    } else if (waveform_type == WAVEFORM_PULSE) {
      waveform_type = WAVEFORM_SAWTOOTH;
      Serial.println("Sawtooth");
    }
    waveform1.begin(waveform_type);
    waveform3.begin(waveform_type);
  }

    if (button3.fallingEdge()) {
    Serial.print("Control waveform 2: ");
    if (waveform_type == WAVEFORM_SAWTOOTH) {
      waveform_type = WAVEFORM_SINE;
      Serial.println("Sine");
    } else if (waveform_type == WAVEFORM_SINE) {
      waveform_type = WAVEFORM_SQUARE;
      Serial.println("Square");
    } else if (waveform_type == WAVEFORM_SQUARE) {
      waveform_type = WAVEFORM_TRIANGLE;
      Serial.println("Triangle");
    } else if (waveform_type == WAVEFORM_TRIANGLE) {
      waveform_type = WAVEFORM_PULSE;
      Serial.println("Pulse");
    } else if (waveform_type == WAVEFORM_PULSE) {
      waveform_type = WAVEFORM_SAWTOOTH;
      Serial.println("Sawtooth");
    }
    waveform2.begin(waveform_type);
    waveform4.begin(waveform_type);
  }

  // middle button switch which source we hear from mixer1
  if (button1.fallingEdge()) {
    if (mixer1_setting == 0) {
      mixer1.gain(0, 0.75);
      mixer1.gain(1, 0.0);
      mixer1.gain(2, 0.0);
      mixer1.gain(3, 0.0);
      Serial.println("Mixer1: VCO");
      knob2 = mapf(knob2, 0.0, 1.0, 0.0, 0.001);
      knob2 = constrain(knob2, 0.0, 0.001);
      mixer1_setting = 1;
    } else if (mixer1_setting == 1) {
      mixer1.gain(0, 0.0);
      mixer1.gain(1, 0.75);
      mixer1.gain(2, 0.0);
      mixer1.gain(3, 0.0);
      Serial.println("Mixer1: LFO");
      knob5 = mapf(knob5, 0.0, 1.0, 0.001, 1.0);
      knob5 = constrain(knob5, 0.001, 1.0);
      mixer1_setting = 2;
    } else if (mixer1_setting == 2) {
      mixer1.gain(0, 0.0);
      mixer1.gain(1, 0.0);
      mixer1.gain(2, 0.75);
      mixer1.gain(3, 0.0);
      Serial.println("Mixer1: FM");
      mixer1_setting = 3;
    } else if (mixer1_setting == 3) {
      mixer1.gain(0, 0.0);
      mixer1.gain(1, 0.0);
      mixer1.gain(2, 0.0);
      mixer1.gain(3, 0.75);
      Serial.println("Mixer1: Modulation Mode");
      mixer1_setting = 0;
    }
  }

  if (button4.fallingEdge()) {
    if (mixer3_setting == 0) {
      mixer3.gain(0, 0.75);
      mixer3.gain(1, 0.0);
      mixer3.gain(2, 0.0);
      mixer3.gain(3, 0.0);
      Serial.println("Mixer3: VCO");  
      knob16 = mapf(knob16, 0.0, 1.0, 0.0, 0.001);
      knob16 = constrain(knob16, 0.0, 0.001);
      mixer3_setting = 1;
    } else if (mixer3_setting == 1) {
      mixer3.gain(0, 0.0);
      mixer3.gain(1, 0.75);
      mixer3.gain(2, 0.0);
      mixer3.gain(3, 0.0);
      Serial.println("Mixer3: LFO");
      knob19 = mapf(knob19, 0.0, 1.0, 0.001, 1.0);
      knob19 = constrain(knob19, 0.001, 1.0);
      mixer3_setting = 2;
    } else if (mixer3_setting == 2) {
      mixer3.gain(0, 0.0);
      mixer3.gain(1, 0.0);
      mixer3.gain(2, 0.75);
      mixer3.gain(3, 0.0);
      Serial.println("Mixer3: FM");
      mixer3_setting = 3;
    } else if (mixer3_setting == 3) {
      mixer3.gain(0, 0.0);
      mixer3.gain(1, 0.0);
      mixer3.gain(2, 0.0);
      mixer3.gain(3, 0.75);
      Serial.println("Mixer3: Modulation Mode");
      mixer3_setting = 0;
    }
  }

  // Right button activates the envelope 
  if (button2.fallingEdge()) {
    mixer2.gain(0, 0.0);
    mixer2.gain(1, 1.0);
    mixer2_envelope = true;
    timeout = 0;
    envelope1.noteOn();
  }
  if (button2.risingEdge()) {
    envelope1.noteOff();
    timeout = 0;
  }
  
  if (button5.fallingEdge()) {
    mixer4.gain(0, 0.0);
    mixer4.gain(1, 1.0);
    mixer4_envelope = true;
    timeout = 0;
    envelope2.noteOn();
  }
  if (button5.risingEdge()) {
    envelope2.noteOff();
    timeout = 0;
  }

  // after 4 seconds of inactivity, go back to
  // steady listening instead of the envelope
  if (mixer2_envelope == true && timeout > 4000) {
    mixer2.gain(0, 0.15);
    mixer2.gain(1, 0.0);
    mixer2_envelope = false;
  }
    if (mixer4_envelope == true && timeout > 4000) { //yo
    mixer4.gain(0, 0.15);
    mixer4.gain(1, 0.0);
    mixer4_envelope = false;
  }
  
  // DAC0>>>>
  waveform1.frequency(360 * knob2 + 0.25);
  waveform1.amplitude(knob3);
  waveform1.phase(360 * knob4);

  waveform3.frequency(360 * knob5 + 0.25);
  waveform1.amplitude(knob6);
  waveform1.phase(360 * knob7);

  waveform1.frequency(360 * knob8 + 0.25);
  sine_fm1.frequency(knob9 * 1500 + 50);
  waveform1.amplitude(knob10);
  waveform1.phase(360 * knob11);

  waveform1.frequency(360 * knob12 + 0.25);
  waveformMod1.frequency(360 * knob13 + 0.25);
  waveformMod1.frequencyModulation(2.0 * knob14 - 1);
  waveform1.phase(360 * knob15);
  //> constants
  waveform1.amplitude(1.0);
  waveform3.amplitude(1.0);
  waveformMod1.amplitude(1.0);
  waveform3.frequency(1.2);

  //DAC1>>>>
  waveform2.frequency(360 * knob16 + 0.25);
  waveform2.amplitude(knob17);
  waveform2.phase(360 * knob18);

  waveform4.frequency(360 * knob19 + 0.25);
  waveform2.amplitude(knob20);
  waveform2.phase(360 * knob21);

  waveform2.frequency(360 * knob22 + 0.25);
  sine_fm2.frequency(knob23 * 1500 + 50); // 
  waveform2.amplitude(knob24);
  waveform2.phase(360 * knob25);

  waveform2.frequency(360 * knob26 + 0.25);
  waveformMod2.frequency(360 * knob27 + 0.25);
  waveformMod2.frequencyModulation(2.0 * knob28 - 1);
  waveform2.phase(360 * knob29);
  //> constants
  waveform2.amplitude(1.0);
  waveform4.amplitude(1.0);
  waveformMod2.amplitude(1.0);
  waveform4.frequency(1.2);
}
 

Attachments

  • Teensyclicks.wav.zip
    764.8 KB · Views: 67
My first guess would be changing the phase of the waveforms every time loop() runs. Why do that? Normally phase is something you'd set up initially (and usually only important for certain types of modulation or other very special circumstances). Of course you can control the phase arbitrarily, but changing the phase of a waveform causes it to "jump" to a different place within its 360 degree cycle. My guess with just a quick look is that might be messing up your waveforms.

Also, as a precaution increase AudioMemory. Probably not an issue here, but if the library is running out of buffers you'll get all sorts of terrible glitches and wrong results.
 
Thanks so much for the reply Paul.

I included pot control of phase after trying it in Examples > Audio > Synthesis > Waveforms and thinking it would sound quite cool with FM, etc and could be used as a basis for further musical experimentation. It was actually just the right channel (DAC1) it was occurring with, which again seems strange to me.

I understand why it would cause this issue and it would totally make sense to me if removing it from the code had fixed it. However, the problem remained until I included the following code from the Waveforms example above:
Code:
AudioNoInterrupts();
etc...
AudioInterrupts();
Would the above lines of code prevent the use of attachInterrupt(s)?

Below are some screenshots of the waveforms in Audacity:
Screen Shot 2018-08-03 at 16.04.20.png

Screen Shot 2018-08-03 at 16.03.05.png
 
Can you create a smaller program without pots & buttons, which still produces these waveforms? Maybe use delays and alter the settings as needed.

If there's a subtle problem within the audio library, I'd really like to find and fix it. Or your code is doing something wrong, I can probably help too. But I just don't have the time to set up the hardware for testing with 27 pots and 8 switches, not to mention how to adjust them all for the same results you're seeing. A simple stand-alone program that just does the wrong behavior automatically without any input makes testing much faster and more reliable.
 
Status
Not open for further replies.
Back
Top