I have had the time to cobble together some code but wasn’t able to test yet.
This also creates the WAV header
#include <SdFat.h>
#include <ADCDMAGen3_RK.h>
//SYSTEM_THREAD(ENABLED);
SerialLogHandler logHandler(LOG_LEVEL_WARN, { // Logging level for non-application messages
{ "app", LOG_LEVEL_INFO } // Logging level for application messages
});
// WAV header spec information:
//https://web.archive.org/web/20140327141505/https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
//http://www.topherlee.com/software/pcm-tut-wavformat.html
typedef struct wav_header {
// RIFF Header
char riff_header[4]; // Contains "RIFF"
uint32_t wav_size; // Size of the wav portion of the file, which follows the first 8 bytes. File size - 8
char wave_header[4]; // Contains "WAVE"
// Format Header
char fmt_header[4]; // Contains "fmt " (includes trailing space)
uint32_t fmt_chunk_size; // Should be 16 for PCM
uint16_t audio_format; // Should be 1 for PCM. 3 for IEEE Float
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate; // Number of bytes per second. sample_rate * num_channels * Bytes Per Sample
uint16_t sample_alignment; // num_channels * Bytes Per Sample
uint16_t bit_depth; // Number of bits per sample
// Data
char data_header[4]; // Contains "data"
uint32_t data_bytes; // Number of bytes in data. Number of samples * num_channels * sample byte size
// uint8_t bytes[]; // Remainder of wave file is bytes
} WavHeader_t;
const int SD_SS = A2;
const int SAMPLE_PIN = A0;
const int SAMPLE_CHANNELS = 1; // 1 .. mono, 2 .. stereo
const size_t SAMPLE_FREQ = 16000; // Hz
const size_t SAMPLES_IN_BUFFER = 1024;
const uint32_t MAX_RECORDING_LENGTH_MS = 30000; // milliseconds
nrf_saadc_value_t *bufferReady = 0;
nrf_saadc_value_t buffer0[SAMPLES_IN_BUFFER];
nrf_saadc_value_t buffer1[SAMPLES_IN_BUFFER];
int fileCount = 0;
uint32_t recordingStart;
enum State { STATE_WAITING, STATE_CONNECT, STATE_RUNNING, STATE_FINISH };
State state = STATE_WAITING;
SdFat sd;
File myFile;
WavHeader_t wh;
ADCDMAGen3 adc;
void myBufferCallback(nrf_saadc_value_t *buf, size_t size);
void setup() {
Serial.begin(9600);
System.on(button_click, buttonHandler);
pinMode(D7, OUTPUT);
ret_code_t err = adc
.withSampleFreqHz(SAMPLE_FREQ)
.withDoubleBuffer(SAMPLES_IN_BUFFER, buffer0, buffer1)
.withSamplePin(A0)
.withBufferCallback(myBufferCallback)
.init();
Log.info("adc.init %lu", err);
if (sd.begin(SD_SS, SPI_FULL_SPEED))
Log.info("SD initialised");
else
Log.warn("failed to open card");
}
void loop() {
switch(state) {
case STATE_WAITING:
// Waiting for the user to press the SETUP button. The setup button handler
// will bump the state into STATE_CONNECT
break;
case STATE_CONNECT:
{
char fileName[128];
snprintf(fileName, sizeof(fileName), "rec%04d.wav", fileCount+1);
if (myFile.open(fileName, O_RDWR | O_CREAT | O_TRUNC)) {
strcpy(wh.riff_header, "RIFF");
strcpy(wh.wave_header, "WAVE");
strcpy(wh.fmt_header , "fmt ");
strcpy(wh.data_header, "data");
wh.fmt_chunk_size = 16;
wh.audio_format = 1;
wh.num_channels = SAMPLE_CHANNELS;
wh.bit_depth = 16;
wh.sample_rate = SAMPLE_FREQ;
wh.sample_alignment = wh.num_channels * wh.bit_depth / 8;
wh.byte_rate = wh.sample_rate * wh.sample_alignment;
wh.data_bytes = 0;
wh.wav_size = sizeof(wh) - 8;
if (myFile.write((uint8_t *)&wh, sizeof(wh)) < sizeof(wh)) {
Log.warn("error writing WAV header");
myFile.close();
state = STATE_WAITING;
return;
}
fileCount++;
digitalWrite(D7, HIGH);
Log.info("Writing to %s", fileName);
recordingStart = millis();
adc.start();
state = STATE_RUNNING;
}
else {
Log.warn("opening %s for write failed", fileName);
state = STATE_WAITING;
}
}
break;
case STATE_RUNNING:
if (bufferReady) {
int16_t *samples = (int16_t*)bufferReady;
bufferReady = 0;
for (int i = 0; i < SAMPLES_IN_BUFFER; i++) {
samples[i] -= 2048; // transpose 0 .. +4095 to -2048 .. +2047
samples[i] <<= 4; // 12bit -> 16bit
}
int count = myFile.write((uint8_t *)samples, SAMPLES_IN_BUFFER * 2);
wh.wav_size += count;
wh.data_bytes += count;
if (count == SAMPLES_IN_BUFFER * 2)
Log.trace("%d samples written", count/2);
else {
Log.warn("error writing %d", count/2);
state = STATE_FINISH;
}
}
if (millis() - recordingStart >= MAX_RECORDING_LENGTH_MS) {
state = STATE_FINISH;
}
break;
case STATE_FINISH:
adc.stop();
Log.info("stopping");
myFile.seek(0);
if (myFile.write((uint8_t *)&wh, sizeof(wh)) < sizeof(wh))
Log.warn("error writing WAV header");
myFile.close();
digitalWrite(D7, LOW);
state = STATE_WAITING;
break;
}
}
void buttonHandler(system_event_t event, int data) {
switch(state) {
case STATE_WAITING:
state = STATE_CONNECT;
break;
case STATE_RUNNING:
state = STATE_FINISH;
break;
}
}
void myBufferCallback(nrf_saadc_value_t *buf, size_t size) {
bufferReady = buf;
}
You can start/stop a recording with the MODE button and via MAX_RECORDING_LENGTH_MS
(currently 30 seconds) you can limit the length of a recording.