is it possible to record audio directly to Flash?

Status
Not open for further replies.

eddevane

Member
Hi,

I couldn't find an answer to this in the forum but I'm wondering if it's possible to save audio data from the line input directly to a flash chip (I have a W25Q128FV soldered to the Audio Board using Teensy 3.2), or if it first needs to be saved to SD and then transferred into flash memory?

Basic question I know but just hoping someone could shed some light on it before I go deeper.

I'm making a live sampler that can play one-shots or loops. So far I've adapted the Recorder sketch to do this to SD, which works ok with 1 loop, but when I try to add more the SD card gets corrupted. I would ideally bypass the need for an SD altogether as it's not my intention to permanently save the samples.

Thanks in advance!
 
Ok so I hacked together code from the examples (Record and sdToFlash) in order to record incoming audio from the line in of the audio board to SD, then immediately copy that to the flash chip. It then plays back from flash.

However now that I'm attempting to make this work with four parts rather than one, I'm running into an issue with how the RAW file is named.

My code below is currently only provisioning one sample but the idea is to call the startRecord() function by specifying a part in the parameter brackets. I just wanted to get one working first before implementing that.

Code:
#include <Bounce.h>

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

// GUItool: begin automatically generated code
AudioPlaySerialflashRaw  playFlashRaw2;  //xy=209,420
AudioPlaySerialflashRaw  playFlashRaw1;  //xy=211,392
AudioPlaySerialflashRaw  playFlashRaw3;  //xy=212,453
AudioInputI2S            i2s2;           //xy=217,266
AudioPlaySerialflashRaw  playFlashRaw4;  //xy=219,490
AudioRecordQueue         queue1;         //xy=357,259
AudioMixer4              mixer1;         //xy=488,455
AudioOutputI2S           i2s1;           //xy=628,456
AudioConnection          patchCord1(playFlashRaw2, 0, mixer1, 1);
AudioConnection          patchCord2(playFlashRaw1, 0, mixer1, 0);
AudioConnection          patchCord3(playFlashRaw3, 0, mixer1, 2);
AudioConnection          patchCord4(i2s2, 0, queue1, 0);
AudioConnection          patchCord5(playFlashRaw4, 0, mixer1, 3);
AudioConnection          patchCord6(mixer1, 0, i2s1, 0);
AudioControlSGTL5000     sgtl5000_1;     //xy=644,240
// GUItool: end automatically generated code


// Bounce objects to easily and reliably read the buttons
Bounce buttonRecord1 = Bounce(0, 8);
Bounce buttonPlay1  =  Bounce(1, 8);  // 8 = 8 ms debounce time
Bounce buttonRecord2 = Bounce(2, 8);
Bounce buttonPlay2   = Bounce(3, 8);
Bounce buttonRecord3 = Bounce(4, 8);  // 8 = 8 ms debounce time
Bounce buttonPlay3 =   Bounce(5, 8);
Bounce buttonRecord4 = Bounce(12, 8);  // 8 = 8 ms debounce time
Bounce buttonPlay4 =   Bounce(21, 8);

const int SDchipSelect = 10;    // Audio Shield has SD card CS on pin 10
const int FlashChipSelect = 6; // digital pin for flash chip CS pin
// which input on the audio shield will be used?
const int myInput = AUDIO_INPUT_LINEIN;



// Remember which mode we're doing
int modePart1 = 0;  // 0=stopped, 1=recording, 2=playing

// The file where data is recorded
File frec;

void setup() {
  SPI.setSCK(14);  // Audio shield has SCK on pin 14
  SPI.setMOSI(7);

  // Configure the pushbutton pins
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(12, INPUT_PULLUP);
  pinMode(21, INPUT_PULLUP);

  // Audio connections require memory, and the record queue
  // uses this memory to buffer incoming audio.
  AudioMemory(100);

  // Enable the audio shield, select input, and enable output
  sgtl5000_1.enable();
  sgtl5000_1.inputSelect(myInput);
  sgtl5000_1.volume(0.5);


  unsigned long startMillis = millis();
  while (!Serial && (millis() - startMillis < 10000)) ;
  delay(100);
  Serial.println("Copy all files from SD Card to SPI Flash");

  if (!SD.begin(SDchipSelect)) {
    error("Unable to access SD card");
  }
  if (!SerialFlash.begin(FlashChipSelect)) {
    error("Unable to access SPI Flash chip");
  }

}


void loop() {
  // First, read the buttons
  buttonRecord1.update();
  buttonPlay1.update();

  // Respond to button presses
  if (buttonRecord1.fallingEdge()) {
    Serial.println("Record Button Press");
    if (modePart1 == 2) stopPlaying(1);
    if (modePart1 == 0) startRecording(1);
    modePart1=1;
  }

  if (buttonPlay1.fallingEdge()) {
    Serial.println("Play Button Press");
    if (modePart1 == 1) stopRecording(1);
    if (modePart1 == 0) startPlaying(1);
  }


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

  }

}


void startRecording(int part) {
  Serial.print("startRecording");
  if (part == 1) {
    Serial.println(" part 1");
    if (SD.exists("RECORDPART1.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.
      Serial.print("removed RECORDPART1.RAW");
      SD.remove("RECORDPART1.RAW");
    }
    frec = SD.open("RECORDPART1.RAW", FILE_WRITE);
    if (frec) {
      queue1.begin();
      modePart1 = 1;
    }
  }
}

void continueRecording(int part) {
  if (queue1.available() >= 2) {
    if (part == 1) {
      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(int part) {
  if (part == 1) {
    Serial.println("stopRecording part 1");
    queue1.end();
    if (modePart1 == 1) {
      while (queue1.available() > 0) {
        frec.write((byte*)queue1.readBuffer(), 256);
        queue1.freeBuffer();
      }
      frec.close();
    }
    sdToFlash(); // as soon as file has been recorded to SD, copy over to flash chip
    modePart1 = 0;
  }
}


void startPlaying(int part) {
  if (part == 1) {
    Serial.println("startPlaying part 1");
    playFlashRaw1.play("RECORDPART1.RAW");

    // changed from 2 to 0 in order to allow retriggering of start of sample
    modePart1 = 0;
  }
}

void continuePlaying(int part) {
  if (part == 1) {
    if (!playFlashRaw1.isPlaying()) {
      playFlashRaw1.stop();
      modePart1 = 0;
    }
  }
}

void stopPlaying(int part) {
  if(part ==1){
  Serial.println("stopPlaying");
  if (modePart1 == 2) playFlashRaw1.stop();
  modePart1 = 0;
  }
}



void sdToFlash() {


  File rootdir = SD.open("/");
  while (1) {
    // open a file from the SD card
    Serial.println();
    File f = rootdir.openNextFile();
    if (!f) break;
    const char *filename = f.name();
    Serial.print(filename);
    Serial.print("    ");
    unsigned long length = f.size();
    Serial.println(length);

    // check if this file is already on the Flash chip
    if (SerialFlash.exists(filename)) {
      Serial.println("  already exists on the Flash chip");
      SerialFlashFile ff = SerialFlash.open(filename);
      if (ff && ff.size() == f.size()) {
        Serial.println("  size is the same, comparing data...");
        if (compareFiles(f, ff) == true) {
          Serial.println("  files are identical :)");
          f.close();
          ff.close();
          continue;  // advance to next file
        } else {
          Serial.println("  files are different");
        }
      } else {
        Serial.print("  size is different, ");
        Serial.print(ff.size());
        Serial.println(" bytes");
      }
      // delete the copy on the Flash chip, if different
      Serial.println("  delete file from Flash chip");
      SerialFlash.remove(filename);
    }

    // create the file on the Flash chip and copy data
    if (SerialFlash.create(filename, length)) {
      SerialFlashFile ff = SerialFlash.open(filename);
      if (ff) {
        Serial.print("  copying");
        // copy data loop
        unsigned long count = 0;
        unsigned char dotcount = 9;
        while (count < length) {
          char buf[256];
          unsigned int n;
          n = f.read(buf, 256);
          ff.write(buf, n);
          count = count + n;
          Serial.print(".");
          if (++dotcount > 100) {
            Serial.println();
            dotcount = 0;
          }
        }
        ff.close();
        if (dotcount > 0) Serial.println();
      } else {
        Serial.println("  error opening freshly created file!");
      }
    } else {
      Serial.println("  unable to create file");
    }
    f.close();
  }
  rootdir.close();
  delay(10);
  Serial.println("Finished All Files");
}

// copy sd to flash

bool compareFiles(File &file, SerialFlashFile &ffile) {
  file.seek(0);
  ffile.seek(0);
  unsigned long count = file.size();
  while (count > 0) {
    char buf1[128], buf2[128];
    unsigned long n = count;
    if (n > 128) n = 128;
    file.read(buf1, n);
    ffile.read(buf2, n);
    if (memcmp(buf1, buf2, n) != 0) return false; // differ
    count = count - n;
  }
  return true;  // all data identical
}

void error(const char *message) {
  while (1) {
    Serial.println(message);
    delay(2500);
  }
}

I renamed RECORD.RAW to RECORDPART1.RAW, but I get this message in the serial monitor when the sdToFlash() function is called:
RECORD2.RAW 334848
already exists on the Flash chip
size is the same, comparing data...
files are identical :)

RECORD.RAW 112640
already exists on the Flash chip
size is different, 176896 bytes
delete file from Flash chip
copying

the file length is the same as when I run the hacked Record sketch, which is below.

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>
//#include <SerialFlash.h>
//
//// GUItool: begin automatically generated code
//AudioInputI2S            i2s2;           //xy=105,63
//AudioAnalyzePeak         peak1;          //xy=278,108
//AudioRecordQueue         queue1;         //xy=281,63
//AudioPlaySdRaw           playRaw1;       //xy=302,157
//AudioOutputI2S           i2s1;           //xy=470,120
//AudioConnection          patchCord1(i2s2, 0, queue1, 0);
//AudioConnection          patchCord2(i2s2, 0, peak1, 0);
//AudioConnection          patchCord3(playRaw1, 0, i2s1, 0);
//AudioConnection          patchCord4(playRaw1, 0, i2s1, 1);
//AudioControlSGTL5000     sgtl5000_1;     //xy=265,212
//// GUItool: end automatically generated code


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

// GUItool: begin automatically generated code
AudioInputI2S            i2s2;           //xy=193,295
AudioPlaySerialflashRaw  playFlashRaw1;  //xy=320,450
AudioRecordQueue         queue1;         //xy=358,329
AudioOutputI2S           i2s1;           //xy=596,450
AudioConnection          patchCord1(i2s2, 0, queue1, 0);
AudioConnection          patchCord2(playFlashRaw1, 0, i2s1, 0);
AudioConnection          patchCord3(playFlashRaw1, 0, i2s1, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=265,212
// 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);


const int SDchipSelect = 10;    // Audio Shield has SD card CS on pin 10
const int FlashChipSelect = 6; // digital pin for flash chip CS pin
// 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

// The file where data is recorded
File frec;

void setup() {
    SPI.setSCK(14);  // Audio shield has SCK on pin 14
  SPI.setMOSI(7);
  // Configure the pushbutton pins
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  pinMode(2, 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);


  unsigned long startMillis = millis();
  while (!Serial && (millis() - startMillis < 10000)) ;
  delay(100);
  Serial.println("Copy all files from SD Card to SPI Flash");

  if (!SD.begin(SDchipSelect)) {
    error("Unable to access SD card");
  }
  if (!SerialFlash.begin(FlashChipSelect)) {
    error("Unable to access SPI Flash chip");
  }
  
}


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

  // 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 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();
  }
  sdToFlash(); // as soon as file has been recorded to SD, copy over to flash chip
  mode = 0;
}


void startPlaying() {
  Serial.println("startPlaying");
  playFlashRaw1.play("RECORD.RAW");

  // changed from 2 to 0 in order to allow retriggering of start of sample
  mode = 0;
}

void continuePlaying() {
  if (!playFlashRaw1.isPlaying()) {
    playFlashRaw1.stop();
    mode = 0;
  }
}

void stopPlaying() {
  Serial.println("stopPlaying");
  if (mode == 2) playFlashRaw1.stop();
  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 :-)
}


void sdToFlash(){


  File rootdir = SD.open("/");
  while (1) {
    // open a file from the SD card
    Serial.println();
    File f = rootdir.openNextFile();
    if (!f) break;
    const char *filename = f.name();
    Serial.print(filename);
    Serial.print("    ");
    unsigned long length = f.size();
    Serial.println(length);

    // check if this file is already on the Flash chip
    if (SerialFlash.exists(filename)) {
      Serial.println("  already exists on the Flash chip");
      SerialFlashFile ff = SerialFlash.open(filename);
      if (ff && ff.size() == f.size()) {
        Serial.println("  size is the same, comparing data...");
        if (compareFiles(f, ff) == true) {
          Serial.println("  files are identical :)");
          f.close();
          ff.close();
          continue;  // advance to next file
        } else {
          Serial.println("  files are different");
        }
      } else {
        Serial.print("  size is different, ");
        Serial.print(ff.size());
        Serial.println(" bytes");
      }
      // delete the copy on the Flash chip, if different
      Serial.println("  delete file from Flash chip");
      SerialFlash.remove(filename);
    }

    // create the file on the Flash chip and copy data
    if (SerialFlash.create(filename, length)) {
      SerialFlashFile ff = SerialFlash.open(filename);
      if (ff) {
        Serial.print("  copying");
        // copy data loop
        unsigned long count = 0;
        unsigned char dotcount = 9;
        while (count < length) {
          char buf[256];
          unsigned int n;
          n = f.read(buf, 256);
          ff.write(buf, n);
          count = count + n;
          Serial.print(".");
          if (++dotcount > 100) {
             Serial.println();
             dotcount = 0;
          }
        }
        ff.close();
        if (dotcount > 0) Serial.println();
      } else {
        Serial.println("  error opening freshly created file!");
      }
    } else {
      Serial.println("  unable to create file");
    }
    f.close();
  }
  rootdir.close();
  delay(10);
  Serial.println("Finished All Files");
}

// copy sd to flash

bool compareFiles(File &file, SerialFlashFile &ffile) {
  file.seek(0);
  ffile.seek(0);
  unsigned long count = file.size();
  while (count > 0) {
    char buf1[128], buf2[128];
    unsigned long n = count;
    if (n > 128) n = 128;
    file.read(buf1, n);
    ffile.read(buf2, n);
    if (memcmp(buf1, buf2, n) != 0) return false; // differ
    count = count - n;
  }
  return true;  // all data identical
}

void error(const char *message) {
  while (1) {
    Serial.println(message);
    delay(2500);
  }
}


Why does the old RECORD.RAW still show up when I try to write RECORDPART1.RAW? Evidently I must be doing something wrong. Any suggested welcome.
 
Status
Not open for further replies.
Back
Top