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

Thread: is it possible to record audio directly to Flash?

  1. #1
    Junior Member
    Join Date
    Aug 2015
    Posts
    5

    is it possible to record audio directly to Flash?

    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!

  2. #2
    Junior Member
    Join Date
    Aug 2015
    Posts
    5
    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.

Posting Permissions

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