Go Down

Topic: Record audio from a microphone to an SD card? (Read 18932 times) previous topic - next topic

Grumpy_Mike

Same as reply #22 only you do a bit more shifting.
Code: [Select]

byte0 = value & 0xff;
byte1 = (value >> 8) & 0xff;
byte2 = (value >> 16) & 0xff;
byte3 = (value >> 24) & 0xff;

pjrc

#31
Mar 03, 2016, 01:25 am Last Edit: Mar 03, 2016, 01:30 am by Paul Stoffregen
By the way, how you can achieve a higher sampling rate?
If you're willing to go with 32 bit ARM, the Teensy audio library has working 44.1 kHz play and record, and a lot more.

This 48 minute tutorial video shows many of the features.

https://www.youtube.com/watch?v=wqt55OAabVs

However, recording the SD card isn't show in the video.  But you can find the example code here:
https://github.com/PaulStoffregen/Audio/blob/master/examples/Recorder/Recorder.ino

If you really want to stay on 8 bit AVR, you're probably going to have to settle for lower quality sound.  Moving so much data around on a slow chip without DMA is really hard.  On a faster 32 bit chip with an optimized library that uses DMA to efficiently bring the audio in and out, it's much easier.

Lucario448

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:
Code: [Select]

/*
  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);
 }
}

CrossRoads

Thanks, the board is quasi developmental, quasi datalogger, whatever you might find a SD card & RTC & RS232 & dual hardware serial ports with lots of SRAM (16K, twice that of a Mega) useful for.  I wired one up to prove out a standalone programmer because it had the right parts - 3.3V buffered SD card and 400mA 3.3V regulator. 150mA from an Uno/Mega is not enough to power an SD card.
I think fat16lib had the code running on a 328P board also. The fast ADC and DAC are what allow the higher sampling rate. 8 or 10 bits at lower rates don't sound that good for audio. I was going for electronic drums eventually, will get there at some point.  Need to re-visit  the trigger and peak detect circuit I had working back on 1990 before we bought a  house and moved and started doing all that homeowner stuff.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Go Up