Hello, I am trying to make the microphone record an audio file on the SD, it turns out that I can see the audio level curves, but when importing it into Audacity I only hear noise. I thought that the same thing recorded mla and had generated a 1khz tone and it does it well, that is, recording to the SD in WAV format does it, although it does not do the PDM to PCM signals well, I will give you the code to See if someone works for you or helps me solve it. To record in the Serial we put start and to stop we put stop.
#include <SD.h>
#include <mic.h>
// Pin CS de la tarjeta SD
#define SD_CS 2
// Nombre del archivo de salida
#define WAV_FILE "audio.wav"
// Número de muestras por ciclo
#define SAMPLES 800
// Configuración del micrófono
mic_config_t mic_config = {
.channel_cnt = 1,
.sampling_rate = 16000, // Frecuencia de muestreo
.buf_size = 1600,
.debug_pin = LED_BUILTIN
};
MG24_ADC_Class Mic(&mic_config);
// Buffer de grabación
int16_t recording_buf[SAMPLES];
volatile bool record_ready = false;
volatile bool isRecording = false;
File audioFile;
unsigned long totalSamples = 0;
// Prototipos de funciones
void writeWavHeader(File &file);
void finalizeWavHeader(File &file);
void startRecording();
void stopRecording();
void audio_rec_callback(uint16_t *buf, uint32_t buf_len);
// Configuración del sistema
void setup() {
Serial.begin(115200);
while (!Serial)
;
// Inicializar SD
if (!SD.begin(SD_CS)) {
Serial.println("Error: No se pudo inicializar la tarjeta SD.");
while (1)
;
}
Serial.println("Tarjeta SD inicializada correctamente.");
// Inicializar micrófono
Mic.set_callback(audio_rec_callback);
if (!Mic.begin()) {
Serial.println("Error: No se pudo inicializar el micrófono.");
while (1)
;
}
Serial.println("Micrófono inicializado correctamente. Enviar 'start' para comenzar la grabación.");
}
// Ciclo principal
void loop() {
// Leer comandos del puerto serie
if (Serial.available()) {
String command = Serial.readStringUntil('\n');
command.trim();
if (command.equalsIgnoreCase("start") && !isRecording) {
startRecording();
} else if (command.equalsIgnoreCase("stop") && isRecording) {
stopRecording();
} else {
Serial.println("Comando no reconocido. Usar 'start' o 'stop'.");
}
}
// Escribir datos en la tarjeta SD si hay muestras disponibles
/*if (isRecording && record_ready) {
for (int i = 0; i < SAMPLES; i++) {
int16_t sample = recording_buf[i];
audioFile.write((byte)(sample & 0xFF)); // Byte bajo
audioFile.write((byte)((sample >> 8) & 0xFF)); // Byte alto
totalSamples++;
}
audioFile.flush(); // Asegurar que los datos se guarden correctamente
record_ready = false;
}*/
// Escribir datos en la tarjeta SD si hay muestras disponibles
if (isRecording && record_ready) {
// Imprimir las primeras 20 muestras
Serial.println("Primeras 20 muestras capturadas:");
for (int i = 0; i < 20; i++) {
Serial.print("Muestra ");
Serial.print(i);
Serial.print(": ");
Serial.println(recording_buf[i]);
}
// Ahora que ya imprimimos, restablecemos 'record_ready'
record_ready = false;
// AQUI guardas en la SD si quieres
for (int i = 0; i < SAMPLES; i++) {
int16_t sample = recording_buf[i];
audioFile.write((byte)(sample & 0xFF));
audioFile.write((byte)((sample >> 8) & 0xFF));
totalSamples++;
}
}
}
// Iniciar grabación
void startRecording() {
Serial.println("Iniciando grabación...");
// Abrir archivo WAV
audioFile = SD.open(WAV_FILE, FILE_WRITE);
if (!audioFile) {
Serial.println("Error: No se pudo crear el archivo de audio.");
return;
}
writeWavHeader(audioFile); // Escribir encabezado WAV
isRecording = true;
totalSamples = 0; // Reiniciar contador de muestras
Serial.println("Grabación iniciada.");
}
// Detener grabación
void stopRecording() {
Serial.println("Deteniendo grabación...");
// Finalizar encabezado WAV
finalizeWavHeader(audioFile);
audioFile.close();
isRecording = false;
Serial.println("Grabación detenida. Archivo guardado como audio.wav.");
}
// Escribir encabezado WAV vacío
void writeWavHeader(File &file) {
byte header[44];
// RIFF chunk descriptor
memcpy(header, "RIFF", 4);
uint32_t fileSize = 0; // Temporal, se actualiza al final
memcpy(header + 4, &fileSize, 4);
memcpy(header + 8, "WAVE", 4);
// fmt subchunk
memcpy(header + 12, "fmt ", 4);
uint32_t subChunk1Size = 16; // Tamaño del bloque fmt
memcpy(header + 16, &subChunk1Size, 4);
uint16_t audioFormat = 1; // PCM
memcpy(header + 20, &audioFormat, 2);
uint16_t numChannels = 1; // Mono
memcpy(header + 22, &numChannels, 2);
uint32_t sampleRate = mic_config.sampling_rate;
memcpy(header + 24, &sampleRate, 4);
uint32_t byteRate = sampleRate * numChannels * 2; // SampleRate * NumChannels * BytesPerSample
memcpy(header + 28, &byteRate, 4);
uint16_t blockAlign = numChannels * 2;
memcpy(header + 32, &blockAlign, 2);
uint16_t bitsPerSample = 16;
memcpy(header + 34, &bitsPerSample, 2);
// data subchunk
memcpy(header + 36, "data", 4);
uint32_t subChunk2Size = 0; // Temporal, se actualiza al final
memcpy(header + 40, &subChunk2Size, 4);
file.write(header, 44);
}
// Finalizar encabezado WAV con tamaño correcto
void finalizeWavHeader(File &file) {
uint32_t fileSize = 44 + totalSamples * 2; // Tamaño total del archivo
uint32_t dataSize = totalSamples * 2; // Tamaño de los datos
file.seek(4); // Posición del tamaño del archivo
file.write((byte *)&fileSize, 4);
file.seek(40); // Posición del tamaño de los datos
file.write((byte *)&dataSize, 4);
}
// Callback del micrófono
void audio_rec_callback(uint16_t *buf, uint32_t buf_len) {
static uint32_t idx = 0;
if (isRecording) {
for (uint32_t i = 0; i < buf_len; i++) {
// Convertir PDM (12-bit unsigned) a PCM (16-bit signed)
int16_t sample = (int16_t)((buf[i] - 3072) * 64); // Offset más alto
recording_buf[idx++] = sample;
if (idx >= SAMPLES) {
idx = 0;
record_ready = true;
break;
}
}
}
}
void generateTone() {
int frequency = 1000; // 1kHz
int sampleRate = 16000;
float amplitude = 10000;
for (int i = 0; i < SAMPLES; i++) {
int16_t sample = (int16_t)(amplitude * sin(2 * PI * frequency * i / sampleRate));
audioFile.write((byte)(sample & 0xFF));
audioFile.write((byte)((sample >> 8) & 0xFF));
}
}
Un saludo y gracias.