1. The microphone gain setting does not seem to do anything, so recorded sound levels are at the default gain, and very low. Unfortunately, using sample rate 16 kHz, the Clue processor clock rate appears to be too slow to permit scaling up each individual sample buffer, while also sampling and writing to the file.
2. Occasional sampling errors (1 or 2 audio samples dropped) cause audible glitches when recording a pure tone, which is very annoying. The error is periodic, but rare enough that I have not been able to determine the frequency. I suspect that this is a beat phenomenon with the audio sample rate and the microphone clock.
I'm wondering if anyone has encountered this glitch with other nrf52840 PDM microphone setups, as I have no other to test.
Raw, recorded data across the glitch at sample 34048
Screen shot from Audacity showing recorded 440 Hz signal:>34042, -1975
>34043, -1685
>34044, -1340
>34045, -965
>34046, -565
>34047, -140
>34048, 1915 <
>34049, 2155
Recorder code. This code writes a raw data file, which I convert to .wav on the PC using a standalone program.
Code: Select all
//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; //tried scaling, abandoned because blocks are skipped
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;
}