adafruit / Adafruit_nRF52_Arduino

Adafruit code for the Nordic nRF52 BLE SoC on Arduino
Other
615 stars 496 forks source link

PDM library drops microphone samples #798

Closed jremington closed 5 months ago

jremington commented 11 months ago

Operating System

Windows 10

IDE version

Arduino 1.8.19

Board

Adafruit Clue

BSP version

github latest

Sketch

//working 11/6/2023, except gain too low, glitches when recording 440 Hz tone

/*
  This example reads audio data from the on-board PDM microphone
  and saves to a QSPI flash file audio.dat
  // 2Mb flash = 2097152 bytes, 4096 512 byte blocks
*/

#include <Adafruit_Arcada.h>
uint32_t buttons, last_buttons;

Adafruit_Arcada arcada;
#include <PDM.h>

// buffer for samples, each sample is 16-bits
int16_t sampleBuffer[256];
// number of samples read
volatile int samplesRead;

File file;
char outputFile[15] = {0};

void setup() {
  //  Serial.begin(115200);
  //  while (!Serial) yield();

  // configure the data receive callback
  PDM.onReceive(onPDMdata);

  // optionally set the gain, defaults to 20 (increasing gain makes no difference, sjr)
  // PDM.setGain(100);  // tried 30, 50, 100

  if (!arcada.arcadaBegin()) {
    while (1) yield();
  }
  //Arcada_FilesystemType
  arcada.filesysBegin(ARCADA_FILESYS_QSPI);

  // Start TFT and fill black
  arcada.displayBegin();

  // Turn on backlight
  arcada.setBacklight(255);
  arcada.display->setTextWrap(false);
  arcada.display->fillScreen(ARCADA_BLACK);
  arcada.display->setTextColor(ARCADA_GREEN);
  arcada.display->setTextSize(2);
  arcada.display->println("Audio Recorder");

  // initialize PDM with:
  // - one channel (mono mode)
  // - a 16 kHz sample rate
  if (!PDM.begin(1, 16000)) {
    arcada.display->println("PDM failure");
    while (1) yield();
  }

  arcada.display->println("A start/stop B end");
  delay(300); //wait for microphone to settle
}
int nframes = 0; //frame count, max 4000 on QSPI flash
int filenum = 0; //file number
int recording = 0; //run/stop mode
void loop() {

  buttons = arcada.variantReadButtons();
  if (buttons != last_buttons) {
    last_buttons = buttons;

    if (buttons & ARCADA_BUTTONMASK_B) recording = -1;

    if (buttons & ARCADA_BUTTONMASK_A) {
      recording = 1;
      snprintf(outputFile, sizeof(outputFile), "/audio%02d.dat", filenum); //generate a name
      file = arcada.open(outputFile, O_CREAT | O_WRITE);
      if (!file) {
        arcada.display->println("output file open failure");
        while (1) yield();
      }
      else { //display output file name
        arcada.display->println(outputFile);
        delay(250); //skip button clicks
      }
    } //button A pressed
  } //buttons

  //      int x, avg = 0;
  while (recording > 0) {
    // wait for samples to be read
    if (samplesRead) {
      //for(int i=0; i<samplesRead; i++) sampleBuffer[i] <<=3;
      file.write((char *)sampleBuffer, 512);
      nframes++;
      samplesRead = 0;
    }//samples read

    if (nframes > 4000) recording = -1; //out of space on QSPI flash

    // check buttons for stop
    buttons = arcada.variantReadButtons();
    if (buttons != last_buttons) {
      last_buttons = buttons;
      if (buttons & ARCADA_BUTTONMASK_A) { //stop and close file
        recording = 0;
        file.close();
        arcada.display->println(nframes);
        filenum++; //next file name
      }
    } //buttons changed
  } // while recording > 0

  if (recording < 0) { //end session and expose QSPI flash to host
    arcada.display->println("stopped");
    delay(10);
    arcada.filesysBeginMSD(ARCADA_FILESYS_QSPI); //expose QSPI flash as drive
    while (1) yield();
  }
} //loop

void onPDMdata() {
  // query the number of bytes available
  int bytesAvailable = PDM.available();

  // read into the sample buffer
  PDM.read(sampleBuffer, bytesAvailable);

  // 16-bit, 2 bytes per sample
  samplesRead = bytesAvailable / 2;
}

What happened ?

I'm using the Adafruit Clue as a recorder of audio clips, and discovered that audio samples are dropped every 2048 samples (as I have presently coded it).

Please see complete code and complete description of the problem at https://forums.adafruit.com/viewtopic.php?t=205785

How to reproduce ?

run posted sketch, record continuous tone and check example data output.

Debug Log

No response

Screenshots

See screenshots in post https://forums.adafruit.com/viewtopic.php?t=205785

jremington commented 10 months ago

Audacity showing of a typical glitch at buffer boundary, 440 Hz tone recorded. The recording sounds terrible when played back!

Capture

jremington commented 5 months ago

Resolved. The dropped samples appear to be a timing conflict between PDM buffer copying and the QSPI flash writes. Increase PDM microphone buffer size to 1024 samples (2048 bytes), and write 2048 byte frames to the QSPI flash.