Audio loop overdub write to sd card how to?

Hello, I'm using a teensy 4.0 with Rev 4 Audio Shield. I have audio coming in(linein from a piezo).
I want to build a looper pedal.

  1. record music (working)
  2. play back music(working)
    and 3) record while playing back so that the next time it loops, the added sounds are also there.

For instance:
start playing and recording music, hit a button to tell the teensy the point where it stops, have it loop all the music up to that point. If i hit again, it'll toggle between recording (not replacing, but adding sound onto the loop) and not having any recording and only playing.

My question:
How do I continuously update the sdcard to that as I write the new total audio information is written? Is there example code for doing this that you could provide/link to? The code that I have attached can record and playback, but if I try to record in the middle of playback it glitches. Eventually I want to scale this to have multiple pedals/loops that are independent, but first step is playing the audio in a loop and adding recording to the sound.
Thanks!

You can't write in multiple locations, as you can only retrieve one at a time. How do you take the sd card data, modify it, rewrite, and send back? Can you maybe take a chunk of a few seconds, store it not in the sd card and edit it with the new audio + old audio, and restore that chunk, then retrieve the next? What would be the maximum size of this audio chunk if I want up to 6 layers? I would prefer more, but 6 should be enough for most cases.

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

//write wav
unsigned long ChunkSize = 0L;
unsigned long Subchunk1Size = 16;
unsigned int AudioFormat = 1;
unsigned int numChannels = 1;
unsigned long sampleRate = 44100;
unsigned int bitsPerSample = 16;
unsigned long byteRate = sampleRate*numChannels*(bitsPerSample/8);// samplerate x channels x (bitspersample / 8)
unsigned int blockAlign = numChannels*bitsPerSample/8;
unsigned long Subchunk2Size = 0L;
unsigned long recByteSaved = 0L;
unsigned long NumSamples = 0L;
byte byte1, byte2, byte3, byte4;

const int myInput = AUDIO_INPUT_LINEIN;
//const int myInput = AUDIO_INPUT_MIC;

int count = 1;

// GUItool: begin automatically generated code
AudioInputI2S            audioInput;     //xy=62.5,192.0000057220459
AudioPlaySdWav           audioSD;        //xy=204,377.00000381469727
AudioRecordQueue         queue1;         //xy=268,205.00000381469727
AudioOutputI2S           audioOutput;    //xy=475,373.00000381469727
AudioConnection          patchCord1(audioInput, 0, queue1, 0);
AudioConnection          patchCord2(audioSD, 0, audioOutput, 0);
AudioConnection          patchCord3(audioSD, 0, audioOutput, 1);
AudioControlSGTL5000     audioShield;    //xy=450,251.00000381469727
// GUItool: end automatically generated code
File frec;
elapsedMillis  msecs;
int recTime = 0;
bool firstRec = true;
// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  7
#define SDCARD_SCK_PIN   14


void setup() {
  Serial.begin(9600);
  AudioMemory(60);
  audioShield.enable();
  audioShield.inputSelect(myInput);
  audioShield.micGain(40);  //0-63
  audioShield.volume(0.5);  //0-1

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

void startRecording() {
  Serial.println("startRecording");
  if (SD.exists("RECORD.WAV")) {
    SD.remove("RECORD.WAV");
  }
  frec = SD.open("RECORD.WAV", FILE_WRITE);
  if (frec) {
    queue1.begin();
    recByteSaved = 0L;
  }
}

void continueRecording() {
  
  if (queue1.available() >= 2) {
    byte buffer[512];
    memcpy(buffer, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    memcpy(buffer + 256, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    // write all 512 bytes to the SD card
    frec.write(buffer, 512);
    recByteSaved += 512;
//    elapsedMicros usec = 0;
//    Serial.print("SD write, us=");
//    Serial.println(usec);
  }
}


void writeOutHeader() { // update WAV header with final filesize/datasize

//  NumSamples = (recByteSaved*8)/bitsPerSample/numChannels;
//  Subchunk2Size = NumSamples*numChannels*bitsPerSample/8; // number of samples x number of channels x number of bytes per sample
  Subchunk2Size = recByteSaved;
  ChunkSize = Subchunk2Size + 36;
  frec.seek(0);
  frec.write("RIFF");
  byte1 = ChunkSize & 0xff;
  byte2 = (ChunkSize >> 8) & 0xff;
  byte3 = (ChunkSize >> 16) & 0xff;
  byte4 = (ChunkSize >> 24) & 0xff;  
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  frec.write("WAVE");
  frec.write("fmt ");
  byte1 = Subchunk1Size & 0xff;
  byte2 = (Subchunk1Size >> 8) & 0xff;
  byte3 = (Subchunk1Size >> 16) & 0xff;
  byte4 = (Subchunk1Size >> 24) & 0xff;  
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  byte1 = AudioFormat & 0xff;
  byte2 = (AudioFormat >> 8) & 0xff;
  frec.write(byte1);  frec.write(byte2); 
  byte1 = numChannels & 0xff;
  byte2 = (numChannels >> 8) & 0xff;
  frec.write(byte1);  frec.write(byte2); 
  byte1 = sampleRate & 0xff;
  byte2 = (sampleRate >> 8) & 0xff;
  byte3 = (sampleRate >> 16) & 0xff;
  byte4 = (sampleRate >> 24) & 0xff;  
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  byte1 = byteRate & 0xff;
  byte2 = (byteRate >> 8) & 0xff;
  byte3 = (byteRate >> 16) & 0xff;
  byte4 = (byteRate >> 24) & 0xff;  
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  byte1 = blockAlign & 0xff;
  byte2 = (blockAlign >> 8) & 0xff;
  frec.write(byte1);  frec.write(byte2); 
  byte1 = bitsPerSample & 0xff;
  byte2 = (bitsPerSample >> 8) & 0xff;
  frec.write(byte1);  frec.write(byte2); 
  frec.write("data");
  byte1 = Subchunk2Size & 0xff;
  byte2 = (Subchunk2Size >> 8) & 0xff;
  byte3 = (Subchunk2Size >> 16) & 0xff;
  byte4 = (Subchunk2Size >> 24) & 0xff;  
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  frec.close();
  Serial.println("header written"); 
  Serial.print("Subchunk2: "); 
  Serial.println(Subchunk2Size); 
}

void stopRecording() {
  Serial.println("stopRecording");
  queue1.end();

  while (queue1.available() > 0) {
    frec.write((byte*)queue1.readBuffer(), 256);
    queue1.freeBuffer();
    recByteSaved += 256;
  }
  writeOutHeader();
  frec.close();

}


void startPlaying() {
  Serial.println("startPlaying");
  audioSD.play("RECORD.WAV");
}

void stopPlaying() {
  Serial.println("stopPlaying");
  audioSD.stop();
}


void loop() {
  // send data only when you receive data:
  if (Serial.available() > 0) {
    if(count == 1){count = -1;}
    count++;
    //count is either 1 2 or 3, first time it is 0. On the first time, clicking stops recording and starts playing. on all subsequent clicks, it toggles
    
    // read the incoming byte:
    byte incomingByte = Serial.read();
    Serial.println(count);
    // Respond to button presses
    if (count == 0){
      startRecording();
      
    }
    if ( count == 1 ) {
      if(firstRec == true){
        recTime = millis();
      }
      Serial.println("stop");
      //if (mode == 2) stopPlaying();
      stopRecording();
      startPlaying();
    }
    Serial.println("_____");
  }
  if(count == 0){
    continueRecording();
  }
}

quick reply to keep this open, as I haven't figured out a solution

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.