XIAO BLE Sense MG24 microphone to SD card

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.