Feedback-Loop or Teensy out of memory?

Status
Not open for further replies.

AdmiralCrunch

Well-known member
I try to build a simple looper.

There should be 4 samples that a user can trigger via button and the play/stop/rec-buttons for the looper. In rec-mode, the looper should record when the user is "playing" with the samples.
I have adopted the code from Paul's Record-Example and chaged it to my needs... seems to "work somehow" (lets say I get closer to what I need :D) .. but now I have a strange effect.. somehow, after I record the first overdub, the output becomes loud and distorted.. it sound like a feedback-loop, but I can't find one in my logic.. what happens, when the teensy runs out of memory during runtime?

I am udung Teensy3.2 .. the .h/.cpp-samplefiles are from Examples->Audio->SamplePlayer


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



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

#include "AudioSampleKick.h" 
#include "AudioSampleSnare.h"
#include "AudioSampleGong.h"
#include "AudioSampleHihat.h"

// GUItool: begin automatically generated code
AudioPlayMemory          playMem3;       //xy=285,443
AudioPlayMemory          playMem4;       //xy=285,483
AudioPlayMemory          playMem2;       //xy=286,402
AudioPlayMemory          playMem1;       //xy=287,360
AudioMixer4              mixer2;         //xy=424,420
AudioPlaySdRaw           playRaw1;       //xy=426,300
AudioPlaySdRaw           playRaw2;     //xy=426,336
AudioMixer4              mixer1;         //xy=598,320
AudioOutputI2S           i2s1;           //xy=743,319
AudioAnalyzePeak         peak1;          //xy=837,476
AudioRecordQueue         queue1;         //xy=839,439
AudioConnection          patchCord1(playMem3, 0, mixer2, 2);
AudioConnection          patchCord2(playMem4, 0, mixer2, 3);
AudioConnection          patchCord3(playMem2, 0, mixer2, 1);
AudioConnection          patchCord4(playMem1, 0, mixer2, 0);
AudioConnection          patchCord5(mixer2, 0, mixer1, 2);
AudioConnection          patchCord6(playRaw1, 0, mixer1, 0);
AudioConnection          patchCord7(playRaw2, 0, mixer1, 1);
AudioConnection          patchCord8(mixer1, 0, i2s1, 0);
AudioConnection          patchCord9(mixer1, 0, i2s1, 1);
AudioConnection          patchCord10(mixer1, queue1);
AudioConnection          patchCord11(mixer1, peak1);
AudioControlSGTL5000     sgtl5000_1;     //xy=863,319
// GUItool: end automatically generated code

// For a stereo recording version, see this forum thread:
// https://forum.pjrc.com/threads/46150?p=158388&viewfull=1#post158388

// A much more advanced sound recording and data logging project:
// https://github.com/WMXZ-EU/microSoundRecorder
// https://github.com/WMXZ-EU/microSoundRecorder/wiki/Hardware-setup
// https://forum.pjrc.com/threads/52175?p=185386&viewfull=1#post185386

// 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);
Bounce buttonSample0 =   Bounce(3, 8);
Bounce buttonSample1 =   Bounce(4, 8);


// which input on the audio shield will be used?
//const int myInput = AUDIO_INPUT_LINEIN;
//const int myInput = AUDIO_INPUT_MIC;


// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  7
#define SDCARD_SCK_PIN   14

// Use these with the Teensy 3.5 & 3.6 SD card
//#define SDCARD_CS_PIN    BUILTIN_SDCARD
//#define SDCARD_MOSI_PIN  11  // not actually used
//#define SDCARD_SCK_PIN   13  // not actually used

// Use these for the SD+Wiz820 or other adaptors
//#define SDCARD_CS_PIN    4
//#define SDCARD_MOSI_PIN  11
//#define SDCARD_SCK_PIN   13


// Remember which mode we're doing
int mode = 0;  // 0=stopped, 1=recording, 2=playing
bool loopExists = false;
bool loopIsPlaying = false;
String modus = "stopped";
byte actFile = 1;
byte recFile = 0;

// 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);
  pinMode(4, INPUT_PULLUP);

  // 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.5);

  // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here if no SD card, but print a message
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }

  if (SD.exists("RECORD.RAW")) {      
    SD.remove("RECORD.RAW");
  }
  if (SD.exists("RECORD2.RAW")) {      
    SD.remove("RECORD2.RAW");
  }
}



void loop() {
  // First, read the buttons
  buttonRecord.update();
  buttonStop.update();
  buttonPlay.update();
  buttonSample0.update();
  buttonSample1.update();


  // if playMode make looping
  if(loopIsPlaying == true) {
    if(actFile == 0) {
      if (playRaw1.isPlaying() == false) {        
        startPlaying(true);
      }  
    }
    if(actFile == 1) {
      if (playRaw2.isPlaying() == false) {
        startPlaying(true);
      }  
    }    
  }
 

  // SAMPLE BUTTONS
  if (buttonSample0.fallingEdge()) {
    playMem1.play(AudioSampleSnare);
  }
  if (buttonSample1.fallingEdge()) {
    playMem2.play(AudioSampleGong);
  }


  // RECORD-BTN
  if (buttonRecord.fallingEdge()) { 
    if (mode == 1) {
      //Serial.print(recFile); Serial.print(" is recording "); Serial.print(" - stopRecording"); Serial.println(recFile);
      stopRecording(true);
      loopIsPlaying = true;
    } //else {
    if (mode == 2) {
      //Serial.print(actFile); Serial.print(" is playing  "); Serial.print(" - stopPlaying"); Serial.println(actFile); 
      stopPlaying();  
    }  
    if (mode == 0) {
      //Serial.print(actFile); Serial.print(" is stopped  "); Serial.print(" - startRecording"); Serial.println(recFile); 
      startRecording();  
      loopIsPlaying = false;
    }     
  }

  // STOP-BTN
  if (buttonStop.fallingEdge()) {    
    if (mode == 1) {
      //Serial.print(recFile); Serial.print(" is recording "); Serial.print(" - stopRecording"); Serial.println(recFile);
      stopRecording(false);  
      loopIsPlaying = false;    
    }
    if (mode == 2) {
      //Serial.print(actFile); Serial.print(" is playing "); Serial.print(" - stopPlaying"); Serial.println(actFile);
      stopPlaying();
      loopIsPlaying = false; 
    }
  }

  // PLAY-BTN
  if (buttonPlay.fallingEdge()) {
    if (mode == 1) {
      //Serial.print(recFile); Serial.print(" is recording "); Serial.print(" - stopRecording"); Serial.println(recFile);
      stopRecording(true);
      loopIsPlaying = true;
    } 
    if (mode == 0) {
      //Serial.print(actFile); Serial.print(" is stopped ");  Serial.print(" - startPlaying"); Serial.println(actFile);
      startPlaying(true);
      loopIsPlaying = true;
    }
    if (mode == 2) {
      //Serial.print(actFile); Serial.print(" is playing ");  Serial.print(" - startPlaying"); Serial.println(actFile);
      startPlaying(true);
      loopIsPlaying = true;
    }
  }

  // If we're playing or recording, carry on...
  if (mode == 1) {
    continueRecording();
  }
  if (mode == 2) {
    continuePlaying();
  }

  
  int volPot = analogRead(A2);
  float volVal = (float)volPot / 1023.0;
  mixer1.gain(0, volVal);
  mixer1.gain(1, volVal);
  mixer1.gain(2, volVal);
}




void startRecording() {
  
  if(actFile == 0) {
    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");
    }
    //Serial.println("startRecording RECORD2");
    frec = SD.open("RECORD2.RAW", FILE_WRITE);
    if (frec) {
      queue1.begin();
      mode = 1;
    } 
  }

  if(actFile == 1) {
    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");
    }
    //Serial.println("startRecording RECORD");
    frec = SD.open("RECORD.RAW", FILE_WRITE);
    if (frec) {
      queue1.begin();
      mode = 1;
    } 
  }   
}

void continueRecording() {

  if(actFile == 0) {
      if (playRaw1.isPlaying() == false) {
        //Serial.println(".....---------------->> loop raw1");
        startPlaying(false);
      }  
    }
    if(actFile == 1) {
      if (playRaw2.isPlaying() == false) {
        //Serial.println("....---------------->> loop raw2");
        startPlaying(false);
      }  
    }
  
  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(bool goOn) {
  //Serial.print("stopRecording actFile: "); Serial.println(actFile);
  queue1.end();
  if (mode == 1) {
    while (queue1.available() > 0) {
      frec.write((byte*)queue1.readBuffer(), 256);
      queue1.freeBuffer();
    }
    frec.close();
  }
 
  if(actFile == 0) {
    actFile = 1;
    recFile = 0;
  } else if(actFile == 1) {
    actFile = 0;
    recFile = 1;
  }  
  //Serial.print("new  actFile: "); Serial.println(actFile);
   mode = 0;
/*
   if(goOn == true) {
    startPlaying();
   }
    */
}


void startPlaying(bool setMode) {  
  
  if(actFile == 0) {
    playRaw1.play("RECORD.RAW");
    if(setMode == true) { mode = 2; } 
  } 
  if(actFile == 1) {
    playRaw2.play("RECORD2.RAW");
    if(setMode == true) { mode = 2; }
  }   
    
}

void continuePlaying() {
  if(actFile == 0) {
    if (!playRaw1.isPlaying()) {
      playRaw1.stop();
      mode = 0;
    }  
  }
  if(actFile == 1) {
    if (!playRaw2.isPlaying()) {
      playRaw2.stop();
      mode = 0;
    }  
  }  
}

void stopPlaying() {
  //Serial.println("stopPlaying");
  if(actFile == 0) {
    if (mode == 2) playRaw1.stop();
    loopIsPlaying = false;
    mode = 0;
  }
  if(actFile == 1) {
    if (mode == 2) playRaw2.stop();
    loopIsPlaying = false;
    mode = 0;
  }
  
}

void adjustMicLevel() {
  // TODO: read the peak1 object and adjust sgtl5000_1.micGain()
  // if anyone gets this working, please submit a github pull request :-)
}
 
I think you are overdriving the audio in mixer1. The sum of the gains in a mixer should be 1. But if the pot is at its maximum value, your code will set each of the gains to 1 making a total of 3. Even if the pot is set to one half of its full range, the mixer will be overdriven.
Code:
  int volPot = analogRead(A2);
  float volVal = (float)volPot / 1023.0;
  mixer1.gain(0, volVal);
  mixer1.gain(1, volVal);
  mixer1.gain(2, volVal);
Try calculating volVal like this:
Code:
  float volVal = (float)volPot / 1023.0/3.0;

Pete
 
Status
Not open for further replies.
Back
Top