I don't know what happened, but I thought I've replied that last post. I've said that the board looks awesome, and already acknowledged that with a AVR MCU I can't expect a recording sampling rate higher than 8 KHz; but, I wanna do this with what I have on hand. And also wondering if by writing data directly to the SD card there would be a jitter in the sampling rate.
On the other hand, I made this sketch for my audio recorder, compilable as it is, for now it is not well documented; but I hope that the code is self-explanatory.
I'm open to hear any suggestion, improvement, optimization, etc.; that I should make to this code. If you think that a comment is redundant or irrelevant, then just ignore it (again, it is not well documented yet).
Now here is the code:
/*
SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 10
*/
#include <SPI.h>
#include <SD.h>
PROGMEM const byte header [44] =
// This contains the header of a WAV file. Without this, it is not recognized as such.
// 44 bytes long.
{
0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
0x45, 0x66, 0x6D, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
0x01, 0x00, 0x40, 0x1F, 0x00, 0x00, 0x40, 0x1F, 0x00, 0x00, 0x01,
0x00, 0x08, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
};
const byte firstPos = 4;
// Position in the file to write down the overall file size minus 8.
const byte secondPos = 40;
// Position in the file to write down the overall file size minus 44.
const byte button = 2; // Pin for the button.
const byte led = 4;
// Pin for the status LED. Unfortunately, the built-in LED is already
// used by the SPI.
byte buttonState = LOW;
// Remember the button state before test the loop's condition. LOW is default or
// not pressed.
File recording;
void setup() {
pinMode(button, INPUT);
pinMode(led, OUTPUT);
pinMode(10, OUTPUT);
ADCSRA = (ADCSRA & 0xf8) | 0x04; // ADC fast mode
digitalWrite(led, LOW);
if (!SD.begin(10)) {
errorStatus(); // SD card failure
}
if (SD.exists("REC001.wav")) {
if (!SD.remove("REC001.wav")) {
errorStatus();
// Any previous (or incomplete) recording should be deleted
// in order to make a new one. This error might occur due to
// write-protected or damaged card.
}
}
}
void loop() {
if (digitalRead(button) == HIGH) {
recording = SD.open("REC001.wav", FILE_WRITE);
if (recording) {
writeHeader();
standby();
buttonState = LOW;
record();
finalize();
finishStatus();
} else {
errorStatus(); // Might be write-protected card or else
}
}
}
void writeHeader() {
// Puts the "idetification" of a typical WAV file (because .wav isn't enough)
for (byte pos = 0; pos < sizeof(header); pos++) {
recording.write(pgm_read_byte(&header[pos]));
}
}
void record() {
/*
* This is how the recording process works. This will last until (normally)
* the press of the button.
*
* DO NOT RESET OR POWER-down THE BOARD WHILE IN THIS STATE, UNTIL THE STATUS LED
* GETS IN THE FINISHED STATE; otherwise file corruption and/or SD card damage will occur.
*
* Exceeding 4 GiB file size (149hr 7min 50sec of audio recording), when meeting a file system's
* limitation, or filling up the memory; may cause unexpected behavior.
*/
byte sample = 0;
digitalWrite(led, HIGH);
while (buttonState == LOW) {
sample = analogRead(A0) >> 2;
recording.write(sample);
buttonState = digitalRead(button);
delayMicroseconds(0);
// Make a trial an error test to know the amount of delay
}
digitalWrite(led, LOW);
}
void finalize() {
// Fills up two necessary variables in the file's header,
// then ensures a successfully saved recording.
byte finalValue[4];
unsigned long fileSize = recording.size();
unsigned long riffSize = fileSize - 8;
unsigned long dataSize = fileSize - 44;
finalValue[0] = riffSize & 0xff;
finalValue[1] = (riffSize >> 8) & 0xff;
finalValue[2] = (riffSize >> 16) & 0xff;
finalValue[3] = (riffSize >> 24) & 0xff;
// Is possible to make a fuction that returns an array of bytes?
recording.seek(firstPos);
recording.write(finalValue, 4);
// Check if already in little-endian order
finalValue[0] = dataSize & 0xff;
finalValue[1] = (dataSize >> 8) & 0xff;
finalValue[2] = (dataSize >> 16) & 0xff;
finalValue[3] = (dataSize >> 24) & 0xff;
// Is possible to make a fuction that returns an array of bytes?
recording.seek(secondPos);
recording.write(finalValue, 4);
// Check if already in little-endian order
recording.close();
}
void errorStatus() {
// The status LED endlessly indicates a critical error.
// The only way out, is by resetting the MCU (reset button or power down).
while (true) {
digitalWrite(led, HIGH);
delay(250);
digitalWrite(led, LOW);
delay(250);
}
}
void finishStatus() {
/* The status LED endlessly indicates a successfully saved recording.
* The only way out, is by resetting the MCU (reset button or power down).
*
* From here, it is safe to reset or power-down the board.
*
* If there is a way to generate filenames automatically, then this function
* will become useless.
*/
while (true) {
digitalWrite(led, HIGH);
delay(1000);
digitalWrite(led, LOW);
delay(1000);
}
}
void standby() {
// The status LED endlessly indicates that the MCU is ready to record
// (until you press the button)
while (true) {
digitalWrite(led, HIGH);
if (digitalRead(button) == HIGH) {
break;
}
delay(500);
digitalWrite(led, LOW);
if (digitalRead(button) == HIGH) {
break;
}
delay(500);
}
}