Okay basically, what I want to do is to record audio using a MAX9814 electret Mic amp. I manage to get the when i used the code without SDFat. What i want to do is like a recorder. I can record audio when button is pressed, saved it into .wav file, then saved into my sd card. I also can listen to the recording from the wav file.
These are the codes:
// Simple ADC DMA example
// Repository: https://github.com/rickkas7/ADCDMAGen3_RK
// License: MIT (free for use in open or closed source products)
#include "Particle.h"
#include "ADCDMAGen3_RK.h"
// Works threaded or not
SYSTEM_THREAD(ENABLED);
// Print debug message to USB serial
SerialLogHandler logHandler;
const size_t SAMPLE_FREQ = 16000; // Hz
const size_t SAMPLES_IN_BUFFER = 1024;
// This is where the samples are stored
static nrf_saadc_value_t buffer0[SAMPLES_IN_BUFFER];
static nrf_saadc_value_t buffer1[SAMPLES_IN_BUFFER];
static nrf_saadc_value_t *bufferReady = 0;
ADCDMAGen3 adc;
void myBufferCallback(nrf_saadc_value_t *buf, size_t size);
void setup() {
// Optional, just for testing so I can see the logs below
// waitFor(Serial.isConnected, 10000);
Serial.begin(9600);
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);
adc.start();
}
void loop() {
if (bufferReady) {
int16_t *samples = (int16_t *)bufferReady;
bufferReady = 0;
Serial.println(*samples);
// Do something with the samples here
// For the default 12-bit sampling, each index in samples will be 0 - 4095 (inclusive)
}
}
void myBufferCallback(nrf_saadc_value_t *buf, size_t size) {
// This gets executed after each sample buffer has been read.
// Note: This is executed in interrupt context, so beware of what you do here!
// We just remember the buffer and handle it from loop
bufferReady = buf;
}
And this is the result that I got. (I got the result by copying the whole terminal, paste it into excel, make line graph). So what i was hoping is to save this as wav file so that i can play the recording.
I suspect that equal sign ( = ) should acutally be a opening parenthesis ( ( ).
You'd only need to write the WAV header populated with the correct values (mono, 16bit, 16kHz sampling), convert the unsigned 12 bit samples in signed 16 bit samples (as shown in the other post) and append them to the header and finally go back to the header and update the length field in the WAV header.
Alright understood, I'll try it when i got home. Thank you very much. So far am i on the right track?
I will need to read up more about this. I will let u know if i need help. Sorry, I'm not too familiar with IT. What I had experienced before is Arduino, which is much simplier than this hahaha. Once again, thank you @ScruffR
Yup, the code you found in this thread should get you started with a PCM file (which you could play back with software like Audacity without needing to add the WAV header to begin with).
So if i want to make into wav file, i just need to add wav header am i right? When i add this header, it will automatically change my raw file into wav file right?
Why i manage to get the pcm data (raw data) using this only
rickkas7/ADCDMAGen3_RK
The raw data that i got is this
The graph in this picture is obtained by using the ADXDMAGen3 library only. I did not use this thread
A file is a file and the file extensions just help the OS to "link" applications that can deal with a particular sort of file and such a file in an abstract way.
The WAV header is there to tell a playback application how (speed, bytes per sample, how many bytes to read from the file, ...) to play the raw PCM data.
So once you have the raw PCM data you'd already have everything needed to play it back.
Your graph above shows just very few samples (about 12.5 milliseconds) so not a lot to deal with let alone to hear if played back.
Icic alright, i think i understand the overall hehehe. But is it correct that i was able to get the raw PCM data just from the ADCDMAGen3 lib? The graph that I obtained came from just the ADCDMAGEN3 lib without any changes. The only changes is i put the Serial.println.
Hi @ScruffR, Thanks for the help. I managed to get the samples using this code
for (int i=0; i < SAMPLES_IN_BUFFER; i++)
Serial.println(samples[i]);
Just an update, I manage to write the file inside my sd card using this example. Thanks @shanevanj .
However, I tried to understand the code in this thread and I only understand it a little bit.
This is what i have done.
// Simple ADC DMA example
// Repository: https://github.com/rickkas7/ADCDMAGen3_RK
// License: MIT (free for use in open or closed source products)
#include "Particle.h"
#include "ADCDMAGen3_RK.h"
#include "adc_hal.h"
#include "gpio_hal.h"
#include "pinmap_hal.h"
#include "pinmap_impl.h"
#include <SdFat.h>
void buttonHandler(system_event_t event, int data);
SdFat sd;
File myFile;
int fileCount=0;
int button=D2;
int buttonstate;
const int SPI_CS = A5;
const size_t SAMPLES_IN_BUFFER = 1024;
const size_t SAMPLE_FREQ = 16000; // Hz
const unsigned long MAX_RECORDING_LENGTH_MS = 10000;
enum State { STATE_WAITING, STATE_CONNECT, STATE_RUNNING, STATE_FINISH };
State state = STATE_WAITING;
unsigned long recordingStart;
// Print debug message to USB serial
SerialLogHandler logHandler;
// Works threaded or not
SYSTEM_THREAD(ENABLED);
// This is where the samples are stored
static nrf_saadc_value_t buffer0[SAMPLES_IN_BUFFER];
static nrf_saadc_value_t buffer1[SAMPLES_IN_BUFFER];
static nrf_saadc_value_t *bufferReady = 0;
ADCDMAGen3 adc;
void myBufferCallback(nrf_saadc_value_t *buf, size_t size);
int result = 0;
void setup() {
// Optional, just for testing so I can see the logs below
// waitFor(Serial.isConnected, 10000);
pinMode(button, INPUT);
Serial.begin(9600);
if(sd.begin(SPI_CS, SPI_FULL_SPEED))
//the sd is underlined.
Serial.println("SD initialised");
else
Serial.println("failed to open card");
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);
adc.start();
}
void loop() {
buttonstate=digitalRead(button);
if(buttonstate = HIGH){
State state = STATE_CONNECT;
}
else
{
switch(state){
case STATE_WAITING:
//// Waiting for the user to press the tactile switch
break;
case STATE_CONNECT:
char fileName[128];
snprintf(fileName, sizeof(fileName), "rec%04d.pcm", fileCount+1);
if(myFile.open(fileName, O_RDWR | O_CREAT | O_TRUNC))
{
fileCount++;
digitalWrite(D7,HIGH);
Serial.printlnf("Writing to %s", fileName);
recordingStart = millis();
if (bufferReady)
{
int16_t *samples = (int16_t *)bufferReady;
bufferReady = 0;
}
state = STATE_RUNNING;
}
else
{
Serial.printlnf("opening %s for write failed", fileName);
state = STATE_WAITING;
}
break;
case STATE_RUNNING:
//how to continue
break;
case STATE_FINISH:
//how to continue
break;
}
}
}
void myBufferCallback(nrf_saadc_value_t *buf, size_t size) {
// This gets executed after each sample buffer has been read.
// Note: This is executed in interrupt context, so beware of what you do here!
// We just remember the buffer and handle it from loop
bufferReady = buf;
}
Am I on the right track? Is there anything wrong so far? How do I continue this since I'm not using Photon. What I want to change is to record the audio for 10 seconds when the tactile button is pressed. Then the data will be converted into wav file and sent to the sd card. Once again, thank you very much.
Hi @ScruffR, i changed the SD_SS into A5 because the SPI_SS of Argon is A5 pin. Furthermore, I changed the code from
for (int i = 0; i < SAMPLES_IN_BUFFER; i++)
to
for (unsigned int i = 0; i < SAMPLES_IN_BUFFER; i++)
because the error on this was comparison between signed and unsigned integer expressions.
I don't know whether this will affect the code or not.
When I tried to run it, I pressed the mode button. The terminal gives me this message
0000151191 [app] WARN: opening rec0001.pcm for write failed
So from this, I know that the program failed on this part
if (myFile.open(fileName, O_RDWR | O_CREAT | O_TRUNC))
May I know what is the meaning of this part so that I can try to fix it? There are a lot of things in the code that you gave me I don't understand. After the whole thing is done, do u mind if I send you the codes that I'm not too sure so you can help me understand? If too troublesome, I'll try to find out myself. But now, I cannot figure out on how to fix this. This is very difficult for a beginner
A5 is only the default pin which would be used if not stated otherwise.
An SPI slave can use whichever pin it wants - that's how multiple SPI devices can share the same bus by dedicateing one unique SlaveSelect pin for each of them.
I guess this was rather a warning not an error and it wouldn't affect the code unless you had 2+ billion samples - however, it's good style to resolve warnings nonetheless.
Have you tried the funcionality of your SD first?
When you have multiple parts that need to interact, it's best to first test each of them individually with the most simple test code possible and only after that start putting them together gradually.
This makes determining the cause of eventual problems much easier.
Unfortunately I have no MAX9814 board, otherwise I'd try that code with the exact same setup you have.
But as it comes to the code, sure you can ask about anything.
I guess I need build a test rig with some kind of mock-up mic
Thank you so much for trying to help me. Sorry if i'm a slow learner hahaha.
I just noticed something. This code doesn't appear in my terminal at all
if (sd.begin(SD_SS, SPI_FULL_SPEED))
Log.info("SD initialised");
else
Log.warn("failed to open card");
}
It doesn't show SD initialised or failed to open card. I'll try to fix the hardware. If i make the SPI_SS is A2, meaning I put the CS on the module to A2 right?