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

Thread: Using analog input for audio library filter

  1. #1

    Using analog input for audio library filter

    I'm just getting started with the audio shield and audio library, it's great fun.

    I'm working on a little test sampler which records a single channel of audio. That part works great.

    I'd also like to use a potentiometer to control a filter on the playback. It's just about working in the sense that I can hear the filter, however there is also loads of crackly noise. I'm nearly certain that this is a result of the filter frequency value being updated hundreds of times a second from the analogRead() reading from the potentiometer.

    What's the best way to avoid this? I'd like to have the potentiometer readings update as fast as possible, to get nice smooth control of the filter.

    Thanks

    (code below, though it's a total mess!)
    Code:
    // Record sound as raw data to a SD card, and play it back.
    //
    // Requires the audio shield:
    //   http://www.pjrc.com/store/teensy3_audio.html
    //
    // Three pushbuttons need to be connected:
    //   Record Button: pin 0 to GND
    //   Stop Button:   pin 1 to GND
    //   Play Button:   pin 2 to GND
    //
    // This example code is in the public domain.
    
    #include <Bounce.h>
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    
    // GUItool: begin automatically generated code
    AudioPlaySdRaw           playRaw1;       //xy=190,295
    AudioInputI2S            i2s1;           //xy=226,112
    AudioFilterBiquad        biquad1;        //xy=399,354
    AudioRecordQueue         queue1;         //xy=431,191
    AudioOutputI2S           i2s2;           //xy=768,436
    AudioConnection          patchCord1(playRaw1, biquad1);
    AudioConnection          patchCord2(i2s1, 0, queue1, 0);
    AudioConnection          patchCord3(i2s1, 1, queue1, 0);
    AudioConnection          patchCord4(biquad1, 0, i2s2, 0);
    AudioConnection          patchCord5(biquad1, 0, i2s2, 1);
    AudioControlSGTL5000     sgtl5000_1;     //xy=281,607
    // GUItool: end automatically generated code
    
    
    const int numReadings = 400;
    
    int readings[numReadings];      // the readings from the analog input
    int avgindex = 0;                  // the avgindex of the current reading
    int total = 0;                  // the running total
    int average = 0;                // the average
    
    int inputPin = 20;
    
    
    // Bounce objects to easily and reliably read the buttons
    Bounce buttonRecord = Bounce(0, 8);
    Bounce buttonStop =   Bounce(1, 8);  // 8 = 8 ms debounce time
    Bounce buttonPlay =   Bounce(2, 8);
    float filtercontrol;
    float filterfreqcal = 15000;
    
    // which input on the audio shield will be used?
    const int myInput = AUDIO_INPUT_LINEIN;
    //const int myInput = AUDIO_INPUT_MIC;
    
    // Remember which mode we're doing
    int mode = 0;  // 0=stopped, 1=recording, 2=playing
    
    // The file where data is recorded
    File frec;
    
    void setup() {
      // Configure the pushbutton pins
      pinMode(0, INPUT_PULLUP);
      pinMode(1, INPUT_PULLUP);
      pinMode(2, INPUT_PULLUP);
      pinMode(3, INPUT_PULLUP);
      
      biquad1.setLowpass(0, 15000, 0.707);
    
      // Audio connections require memory, and the record queue
      // uses this memory to buffer incoming audio.
      AudioMemory(60);
    
      // Enable the audio shield, select input, and enable output
      sgtl5000_1.enable();
      sgtl5000_1.inputSelect(myInput);
      sgtl5000_1.volume(0.8);
    
      // Initialize the SD card
      SPI.setMOSI(7);
      SPI.setSCK(14);
      if (!(SD.begin(10))) {
        // stop here if no SD card, but print a message
        while (1) {
          Serial.println("Unable to access the SD card");
          delay(500);
        }
      }
    for (int thisReading = 0; thisReading < numReadings; thisReading++)
        readings[thisReading] = 0;    
    }
    
    
    void loop() {
      // First, read the buttons
      
    
      buttonRecord.update();
      buttonStop.update();
      buttonPlay.update();
    
    
    setFilter();
    
      
      
    
    
      // Respond to button presses
      if (buttonRecord.fallingEdge()) {
        Serial.println("Record Button Press");
        if (mode == 2) stopPlaying();
        if (mode == 0) startRecording();
      }
      if (buttonStop.fallingEdge()) {
        Serial.println("Stop Button Press");
        if (mode == 1) stopRecording();
        if (mode == 2) stopPlaying();
      }
      if (buttonPlay.fallingEdge()) {
        Serial.println("Play Button Press");
        if (mode == 1) stopRecording();
        if (mode == 0) startPlaying();
        if (mode == 2) startPlaying();
      }
    
    
      // If we're playing or recording, carry on...
      if (mode == 1) {
        continueRecording();
      }
      if (mode == 2) {
        continuePlaying();
      }
    
      // when using a microphone, continuously adjust gain
      if (myInput == AUDIO_INPUT_MIC) adjustMicLevel();
    }
    
    
    void startRecording() {
      Serial.println("startRecording");
      if (SD.exists("RECORD.RAW")) {
        // The SD library writes new data to the end of the
        // file, so to start a new recording, the old file
        // must be deleted before new data is written.
        SD.remove("RECORD.RAW");
      }
      frec = SD.open("RECORD.RAW", FILE_WRITE);
      if (frec) {
        queue1.begin();
        mode = 1;
      }
    }
    
    void continueRecording() {
      if (queue1.available() >= 2) {
        byte buffer[512];
        // Fetch 2 blocks from the audio library and copy
        // into a 512 byte buffer.  The Arduino SD library
        // is most efficient when full 512 byte sector size
        // writes are used.
        memcpy(buffer, queue1.readBuffer(), 256);
        queue1.freeBuffer();
        memcpy(buffer+256, queue1.readBuffer(), 256);
        queue1.freeBuffer();
        // write all 512 bytes to the SD card
        elapsedMicros usec = 0;
        frec.write(buffer, 512);
        // Uncomment these lines to see how long SD writes
        // are taking.  A pair of audio blocks arrives every
        // 5802 microseconds, so hopefully most of the writes
        // take well under 5802 us.  Some will take more, as
        // the SD library also must write to the FAT tables
        // and the SD card controller manages media erase and
        // wear leveling.  The queue1 object can buffer
        // approximately 301700 us of audio, to allow time
        // for occasional high SD card latency, as long as
        // the average write time is under 5802 us.
        //Serial.print("SD write, us=");
        //Serial.println(usec);
      }
    }
    
    void stopRecording() {
      Serial.println("stopRecording");
      queue1.end();
      if (mode == 1) {
        while (queue1.available() > 0) {
          frec.write((byte*)queue1.readBuffer(), 256);
          queue1.freeBuffer();
        }
        frec.close();
      }
      mode = 0;
    }
    
    
    void startPlaying() {
      Serial.println("startPlaying");
      playRaw1.play("RECORD.RAW");
      mode = 2;
    }
    
    void continuePlaying() {
      if (!playRaw1.isPlaying()) {
      playRaw1.play("RECORD.RAW");
      //  mode = 0;
      }
    }
    
    void stopPlaying() {
      Serial.println("stopPlaying");
      if (mode == 2) playRaw1.stop();
      mode = 0;
    }
    
    void setFilter() {
        // subtract the last reading:
      total= total - readings[avgindex];         
      // read from the sensor:  
      readings[avgindex] = analogRead(inputPin); 
      // add the reading to the total:
      total= total + readings[avgindex];       
      // advance to the next position in the array:  
      avgindex = avgindex + 1;                    
    
      // if we're at the end of the array...
      if (avgindex >= numReadings)              
        // ...wrap around to the beginning: 
        avgindex = 0;                           
    
      // calculate the average:
      average = total / numReadings;         
      // send it to the computer as ASCII digits
     
      delay(1);        // delay in between reads for stability       
      
       
      filtercontrol = map(average,0,1023,100,15000);
        biquad1.setLowpass(0, filtercontrol, 0.707);
         Serial.println(average);   
      
    }
    
    void adjustMicLevel() {
      // TODO: read the peak1 object and adjust sgtl5000_1.micGain()
      // if anyone gets this working, please submit a github pull request :-)
    }

  2. #2
    ah ha, I think I've got the solution - using the filter (rather than the biquad), and using a signal input to control it...

    if that definitely works i'll post the fixed code in case anyone else makes the same mistake.

    (any other tips are still welcome, of course!)

  3. #3
    Senior Member
    Join Date
    Nov 2013
    Posts
    719
    I expect you can significantly reduce the noise by not updating the filter parameters quite as often (and needlessly) as you are.

    Take a look at https://github.com/PaulStoffregen/Au...ControlDAP.ino It is using two methods to avoid hammering the filter parameters.

    The first thing is it avoids updating the filter faster than once every 10ms, at least if there is an actual difference every 10ms for a couple hundred of milliseconds any noise the adjustments introduce will be like a 'tap... tap... tap...' rather than 'flickflickflickflickflickflickflickflick'

    The second thing it does is to map the value derived directly from the pot into a value which effectively cuts the pot reading from having 1024 unique possible values to only 140 unique possible values and then it checks that this value is actually different from the last value used before using it. This is where noise introduction is definitely reduced IMHO.

    Last time I tried that CalcBiquadToneControlDAP.ino it seemed pretty good, if there had been more noise when I was turning the pot I probably would have put a 10uF Electro from A to B and a 10nF non-pol from A to W (assuming A was the GND end) on the pot before touching the code.

    HTH

  4. #4
    Thanks a bunch robsoles, some really great feedback and suggestions there. I've had some good results with another method as well, so I'll do some comparisons and report back on what work best!

  5. #5
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    21,815
    Whenever you hear "crackly noise", always suspect clipping. Other things can cause distortion too, but clipping is the most common problem.

    Filters can have gain > 1.0. If yours does, be sure to attenuate your signal before the filter.

  6. #6
    Thanks for all the help, I'm pretty happy with this solution I've worked out. It uses a sine wave to set the filter frequency. Sounds nice and smooth now. Posting here for anyone who happens to find this thread.

    I've now run into a separate problem, so it's time for a new thread!

    Code:
    #include <Bounce.h>
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    
    // GUItool: begin automatically generated code
    AudioPlaySdRaw           playRaw2;       //xy=189,349
    AudioPlaySdRaw           playRaw1;       //xy=190,295
    AudioInputI2S            i2s1;           //xy=226,112
    AudioSynthWaveformSine   sine1;          //xy=261,453
    AudioMixer4              mixer1;         //xy=395,309
    AudioRecordQueue         queue2;         //xy=450,219
    AudioRecordQueue         queue1;         //xy=453,89
    AudioFilterStateVariable filter1;        //xy=461,421
    AudioOutputI2S           i2s2;           //xy=609,428
    AudioConnection          patchCord1(playRaw2, 0, mixer1, 1);
    AudioConnection          patchCord2(playRaw1, 0, mixer1, 0);
    AudioConnection          patchCord3(i2s1, 0, queue2, 0);
    AudioConnection          patchCord4(i2s1, 0, queue1, 0);
    AudioConnection          patchCord5(sine1, 0, filter1, 1);
    AudioConnection          patchCord6(mixer1, 0, filter1, 0);
    AudioConnection          patchCord7(filter1, 0, i2s2, 0);
    AudioConnection          patchCord8(filter1, 0, i2s2, 1);
    AudioControlSGTL5000     sgtl5000_1;     //xy=281,607
    // GUItool: end automatically generated code
    
    
    
    const int numReadings = 20;
    
    int readings[numReadings];      // the readings from the analog input
    int avgindex = 0;                  // the avgindex of the current reading
    int total = 0;                  // the running total
    int average = 0;                // the average
    
    int inputPin = A10;
    
    
    // Bounce objects to easily and reliably read the buttons
    Bounce buttonRecord = Bounce(0, 8);
    Bounce buttonPlay =   Bounce(1, 8);  // 8 = 8 ms debounce time
    
    Bounce buttonRecord2 = Bounce(2, 8);
    Bounce buttonPlay2 =   Bounce(3, 8);
    
    float filtercontrol;
    float filterfreqcal = 15000;
    
    // which input on the audio shield will be used?
    
    
    // Remember which mode we're doing
    int mode = 0; 
    int mode2 = 0;
    // 0=stopped, 1=recording, 2=playing
    int loopMode = 0;
    // The file where data is recorded
    File frec;
    File frec2;
    
    void setup() {
      // Configure the pushbutton pins
      pinMode(0, INPUT_PULLUP);
      pinMode(1, INPUT_PULLUP);
      pinMode(2, INPUT_PULLUP);
      pinMode(3, INPUT_PULLUP);
      pinMode(5, INPUT_PULLUP);
      pinMode(16, OUTPUT);
      pinMode(17, OUTPUT);
      pinMode(20, OUTPUT);
      pinMode(21, OUTPUT);
     
      mixer1.gain(0,1);
      mixer1.gain(1,1);
      filter1.frequency(15000);
      filter1.resonance(1.5);
    sine1.frequency(5);
      // Audio connections require memory, and the record queue
      // uses this memory to buffer incoming audio.
      AudioMemory(120);
    const int myInput = AUDIO_INPUT_LINEIN;
      // Enable the audio shield, select input, and enable output
      sgtl5000_1.enable();
      sgtl5000_1.inputSelect(myInput);
      sgtl5000_1.volume(0.8);
    
      // Initialize the SD card
      SPI.setMOSI(7);
      SPI.setSCK(14);
      if (!(SD.begin(10))) {
        // stop here if no SD card, but print a message
        while (1) {
          Serial.println("Unable to access the SD card");
          delay(500);
        }
      }
    for (int thisReading = 0; thisReading < numReadings; thisReading++)
        readings[thisReading] = 0;    
    }
    
    
    void loop() {
      // First, read the buttons
    
    if (digitalRead(5) == 1) {
     loopMode = 1; 
         Serial.println("Loop On");
    } else {
     loopMode = 0; 
      //  Serial.println("Loop Off");
    }
    
      buttonRecord.update();
      buttonPlay.update();
      
      buttonRecord2.update();
      buttonPlay2.update();
    
    
    setFilter();
    
    
    
      if (mode == 1) {
     digitalWrite(16, HIGH); 
    } else {
      digitalWrite(16, LOW); 
    }
    if (mode == 2) {
     digitalWrite(17, HIGH); 
    } else {
      digitalWrite(17, LOW); 
    }
    
      if (mode2 == 1) {
     digitalWrite(20, HIGH); 
    } else {
      digitalWrite(20, LOW); 
    }
    if (mode2 == 2) {
     digitalWrite(21, HIGH); 
    } else {
      digitalWrite(21, LOW); 
    }
      
    
    
      // Respond to button presses
      if (buttonRecord.fallingEdge()) {
        Serial.println("Record Button Press");
        if (mode == 2) stopPlaying();
        if (mode == 0) startRecording();
       else if (mode == 1) stopRecording();
      }
      
    
    
    if (loopMode == 1) {
    
      if (buttonPlay.fallingEdge()) {
        Serial.println("Play Button Press");
        if (mode == 1) stopRecording();
        if (mode == 0) startPlaying();
        if (mode == 2) startPlaying();
      }
    }
    
    if (loopMode == 0) {
        if (buttonPlay.fallingEdge()) {
        Serial.println("Play Button Press OneShot");
       if (mode == 0) startPlaying();
        if (mode == 1) {
          stopRecording();
          startPlaying();
        }
      }
        if (buttonPlay.risingEdge() && mode != 1) {
         stopPlaying(); 
        }
    
    }
    
      // If we're playing or recording, carry on...
      if (mode == 1) {
        continueRecording();
      }
      if (mode == 2) {
        continuePlaying();
      }
    
    
    // second channel
    
      // Respond to button presses
      if (buttonRecord2.fallingEdge()) {
        Serial.println("Record Button Press 2");
        if (mode2 == 2) stopPlaying2();
        if (mode2 == 0) startRecording2();
       else if (mode2 == 1) stopRecording2();
      }
    
    if (loopMode == 1) {
    
      if (buttonPlay2.fallingEdge()) {
        Serial.println("Play Button Press 2");
        if (mode2 == 1) stopRecording2();
        if (mode2 == 0) startPlaying2();
        if (mode2 == 2) startPlaying2();
      }
    }
    
    if (loopMode == 0) {
        if (buttonPlay2.fallingEdge()) {
        Serial.println("Play Button Press OneShot 2");
       if (mode2 == 0) startPlaying2();
        if (mode2 == 1) {
          stopRecording2();
          startPlaying2();
        }
      }
        if (buttonPlay2.risingEdge() && mode2 != 1) {
         stopPlaying2(); 
        }
    
    }
    
      // If we're playing or recording, carry on...
      if (mode2 == 1) {
        continueRecording2();
      }
      if (mode2 == 2) {
        continuePlaying2();
      }
    
    
    }
    
    
    void startRecording() {
      Serial.println("startRecording");
      if (SD.exists("RECORD.RAW")) {
        // The SD library writes new data to the end of the
        // file, so to start a new recording, the old file
        // must be deleted before new data is written.
        SD.remove("RECORD.RAW");
      }
      frec = SD.open("RECORD.RAW", FILE_WRITE);
      if (frec) {
        queue1.begin();
        mode = 1;
      }
    }
    
    void continueRecording() {
      if (queue1.available() >= 2) {
        byte buffer[512];
        // Fetch 2 blocks from the audio library and copy
        // into a 512 byte buffer.  The Arduino SD library
        // is most efficient when full 512 byte sector size
        // writes are used.
        memcpy(buffer, queue1.readBuffer(), 256);
        queue1.freeBuffer();
        memcpy(buffer+256, queue1.readBuffer(), 256);
        queue1.freeBuffer();
        // write all 512 bytes to the SD card
        elapsedMicros usec = 0;
        frec.write(buffer, 512);
        // Uncomment these lines to see how long SD writes
        // are taking.  A pair of audio blocks arrives every
        // 5802 microseconds, so hopefully most of the writes
        // take well under 5802 us.  Some will take more, as
        // the SD library also must write to the FAT tables
        // and the SD card controller manages media erase and
        // wear leveling.  The queue1 object can buffer
        // approximately 301700 us of audio, to allow time
        // for occasional high SD card latency, as long as
        // the average write time is under 5802 us.
        Serial.print("SD write, us=");
        Serial.println(usec);
      }
    }
    
    void stopRecording() {
      Serial.println("stopRecording");
      queue1.end();
      if (mode == 1) {
        while (queue1.available() > 0) {
          frec.write((byte*)queue1.readBuffer(), 256);
          queue1.freeBuffer();
        }
        frec.close();
      }
      mode = 0;
    }
    
    
    
    void startPlaying() {
      Serial.println("startPlaying");
      playRaw1.play("RECORD.RAW");
      mode = 2;
    }
    
    void continuePlaying() {
      if (!playRaw1.isPlaying()) {
      playRaw1.play("RECORD.RAW");
      //  mode = 0;
      }
    }
    
    void stopPlaying() {
    
      if (mode != 0) {
          Serial.println("stopPlaying");
        playRaw1.stop();
      mode = 0;
      }
    }
    
    // second channel
    void startRecording2() {
      Serial.println("startRecording2");
      if (SD.exists("RECORD2.RAW")) {
        // The SD library writes new data to the end of the
        // file, so to start a new recording, the old file
        // must be deleted before new data is written.
        SD.remove("RECORD2.RAW");
        
      }
      frec2 = SD.open("RECORD2.RAW", FILE_WRITE);
      if (frec2) {
        queue2.begin();
        mode2 = 1; 
        Serial.println("made it this far");
      }
    }
    
    void continueRecording2() {
      if (queue2.available() >= 2) {
        byte buffer2[512];
        // Fetch 2 blocks from the audio library and copy
        // into a 512 byte buffer.  The Arduino SD library
        // is most efficient when full 512 byte sector size
        // writes are used.
        memcpy(buffer2, queue2.readBuffer(), 256);
        queue2.freeBuffer();
        memcpy(buffer2+256, queue2.readBuffer(), 256);
        queue2.freeBuffer();
        // write all 512 bytes to the SD card
        elapsedMicros usec = 0;
        frec2.write(buffer2, 512);
        // Uncomment these lines to see how long SD writes
        // are taking.  A pair of audio blocks arrives every
        // 5802 microseconds, so hopefully most of the writes
        // take well under 5802 us.  Some will take more, as
        // the SD library also must write to the FAT tables
        // and the SD card controller manages media erase and
        // wear leveling.  The queue1 object can buffer
        // approximately 301700 us of audio, to allow time
        // for occasional high SD card latency, as long as
        // the average write time is under 5802 us.
        //Serial.print("SD write, us=");
        //Serial.println(usec);
      }
    }
    
    void stopRecording2() {
      Serial.println("stopRecording2");
      queue2.end();
      if (mode2 == 1) {
        while (queue2.available() > 0) {
          frec2.write((byte*)queue2.readBuffer(), 256);
          queue2.freeBuffer();
          
        }
        frec2.close();
      }
      mode2 = 0;
    }
    
    
    void startPlaying2() {
      Serial.println("startPlaying2");
      playRaw2.play("RECORD2.RAW");
      mode2 = 2;
    }
    
    void continuePlaying2() {
      if (!playRaw2.isPlaying()) {
      playRaw2.play("RECORD2.RAW");
      //  mode = 0;
      }
    }
    
    void stopPlaying2() {
      Serial.println("stopPlaying2");
      if (mode2 == 2) playRaw2.stop();
      mode2 = 0;
    }
    
    
    void setFilter() {
        // subtract the last reading:
      total= total - readings[avgindex];         
      // read from the sensor:  
      readings[avgindex] = analogRead(inputPin); 
      // add the reading to the total:
      total= total + readings[avgindex];       
      // advance to the next position in the array:  
      avgindex = avgindex + 1;                    
    
      // if we're at the end of the array...
      if (avgindex >= numReadings)              
        // ...wrap around to the beginning: 
        avgindex = 0;                           
    
      // calculate the average:
      average = total / numReadings;         
      // send it to the computer as ASCII digits
     
      delay(1);        // delay in between reads for stability       
      
       
      filtercontrol = map(average,0,1023,100,12000);
        filter1.frequency(filtercontrol);
        // Serial.println(filtercontrol);   
      
    }

Posting Permissions

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