porting I2S code from legacy driver to new driver

greengnu
Posts: 66
Joined: Wed May 08, 2019 8:45 pm

porting I2S code from legacy driver to new driver

Postby greengnu » Mon Mar 24, 2025 8:31 am

I'm trying to port some code (microphone input from an INMP441 microphone) from the old legacy driver to the new driver. The code works on the legacy driver code path, but with the new driver I'm only getting audio noise out (with a very distored version of the actual audio somewhat audible somewhere under that noise).

It's an arduino sketch but the code in question isn't arduino specific and uses i2s.h and i2s_std.h directly. What exactly did I miss when porting the code?

instead of I2S_STD_MSB_SLOT_DEFAULT_CONFIG I also tried I2S_STD_PCM_SLOT_DEFAULT_CONFIG and I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG - with the last one I don't get any noise but the recording is extremely distorted and slowed down.

Code: Select all

#define USE_LEGACY_DRIVER 0
 
#if USE_LEGACY_DRIVER
#include <driver/i2s.h>
#else
#include <driver/i2s_std.h>
#endif
 
#include <WiFi.h>
#include <WebServer.h>
 
// ===========================
// Enter your WiFi credentials
// ===========================
const char* ssid = "******";
const char* password = "******";
 
// I2S pins 
#define I2S_WS            46 
#define I2S_SCK           3 
#define I2S_SD            45
 
// I2S peripheral to use (0 or 1)
#define I2S_PORT          I2S_NUM_0
 
//---- Sampling ------------
#define SAMPLE_RATE       22050
#define SAMPLE_BITS       16
#define DMA_BUF_COUNT     2
#define DMA_BUF_LEN       1024 // number of samples, dma buffer bytes = DMA_BUF_LEN * NUM_CHANNELS * SAMPLE_BITS / 8
#define NUM_CHANNELS      1
 
WebServer Audioserver(82);
 
//---- Audio WAV configuration ------------
 
struct WAVHeader {
  char chunkId[4];          // 4 bytes
  uint32_t chunkSize;       // 4 bytes
  char format[4];           // 4 bytes
  char subchunk1Id[4];      // 4 bytes
  uint32_t subchunk1Size;   // 4 bytes
  uint16_t audioFormat;     // 2 bytes
  uint16_t numChannels;     // 2 bytes
  uint32_t sampleRate;      // 4 bytes
  uint32_t byteRate;        // 4 bytes
  uint16_t blockAlign;      // 2 bytes
  uint16_t bitsPerSample;   // 2 bytes
  char subchunk2Id[4];      // 4 bytes
  uint32_t subchunk2Size;   // 4 bytes
};
 
void initializeWAVHeader(WAVHeader &header, uint32_t sampleRate, uint16_t bitsPerSample, uint16_t numChannels) {
 
  strncpy(header.chunkId, "RIFF", 4);
  strncpy(header.format, "WAVE", 4);
  strncpy(header.subchunk1Id, "fmt ", 4);
  strncpy(header.subchunk2Id, "data", 4);
 
  header.chunkSize = 0; // Placeholder for Chunk Size (to be updated later)
  header.subchunk1Size = 16; // PCM format size (constant for uncompressed audio)
  header.audioFormat = 1; // PCM audio format (constant for uncompressed audio)
  header.numChannels = numChannels;
  header.sampleRate = sampleRate;
  header.bitsPerSample = bitsPerSample;
  header.byteRate = (sampleRate * bitsPerSample * numChannels) / 8;
  header.blockAlign = (bitsPerSample * numChannels) / 8;
  header.subchunk2Size = 0; // Placeholder for data size (to be updated later)
}
 
#if USE_LEGACY_DRIVER
void mic_i2s_init() {
    i2s_config_t i2sConfig = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
    .sample_rate = SAMPLE_RATE ,
    .bits_per_sample = i2s_bits_per_sample_t(SAMPLE_BITS),
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
    .communication_format = I2S_COMM_FORMAT_STAND_I2S,
    .intr_alloc_flags = 0,
    .dma_buf_count = DMA_BUF_COUNT,
    .dma_buf_len = DMA_BUF_LEN,
    .use_apll = false
  };
  i2s_driver_install(I2S_PORT, &i2sConfig, 0, NULL);
 
  i2s_pin_config_t pinConfig = {
    .bck_io_num = I2S_SCK, 
    .ws_io_num = I2S_WS ,
    .data_out_num = I2S_PIN_NO_CHANGE,
    .data_in_num = I2S_SD 
  };
  i2s_set_pin(I2S_PORT, &pinConfig);
}
#else
i2s_chan_handle_t mic_i2s_init() {
    i2s_chan_handle_t rx_chan = NULL;
 
    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_PORT, I2S_ROLE_MASTER);
    chan_cfg.dma_desc_num = DMA_BUF_COUNT;
    chan_cfg.dma_frame_num = DMA_BUF_LEN;
    i2s_new_channel(&chan_cfg, NULL, &rx_chan)
    i2s_std_config_t std_cfg = {
        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
        .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
        .gpio_cfg = {
            .mclk = I2S_GPIO_UNUSED,
            .bclk = static_cast<gpio_num_t>(I2S_SCK),
            .ws = static_cast<gpio_num_t>(I2S_WS),
            .dout = I2S_GPIO_UNUSED,//DATA_OUT_IO,
            .din = static_cast<gpio_num_t>(I2S_SD),
            .invert_flags = {
                .mclk_inv = false,
                .bclk_inv = false,
                .ws_inv = false,
            },
        },
    };
    i2s_channel_init_std_mode(rx_chan, &std_cfg);
    i2s_channel_enable(rx_chan);
 
    return rx_chan;
}
#endif
 
void handleAudioStream() {
  const int buf_size = DMA_BUF_LEN*NUM_CHANNELS*sizeof(uint16_t);
  uint8_t buf[buf_size];
 
  // I2S
#if USE_LEGACY_DRIVER
  mic_i2s_init();
#else
  i2s_chan_handle_t rx_chan = mic_i2s_init();
#endif
 
  // Get access to the client object
  WiFiClient Audioclient = Audioserver.client();
 
  // Send the 200 OK response with the headers
  Audioclient.print("HTTP/1.1 200 OK\r\n");
  Audioclient.print("Content-Type: audio/wav\r\n");
  Audioclient.print("Access-Control-Allow-Origin: *\r\n");
  Audioclient.print("\r\n");
  // Send the initial part of the WAV header
  WAVHeader wavHeader;
  initializeWAVHeader(wavHeader, SAMPLE_RATE, SAMPLE_BITS, NUM_CHANNELS);
  Audioclient.write(reinterpret_cast<const uint8_t*>(&wavHeader), sizeof(wavHeader));
 
  size_t bytesRead = 0;
  unsigned long last_time_stamp = millis();
  while (true) {
    if (!Audioclient.connected()) {
#if USE_LEGACY_DRIVER
      i2s_driver_uninstall(I2S_PORT);
#else
      i2s_channel_disable(rx_chan);
      i2s_del_channel(rx_chan);
#endif
      break;
    }
    // Read audio data from I2S DMA
#if USE_LEGACY_DRIVER
    i2s_read(I2S_PORT, buf, buf_size, &bytesRead, portMAX_DELAY);
#else
    i2s_channel_read(rx_chan, buf, buf_size, &bytesRead, portMAX_DELAY);
#endif
 
    // Send audio data
    if (bytesRead > 0) {
      Audioclient.write((uint8_t*)buf, bytesRead);
    }
  }
}
 
void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
 
  WiFi.begin(ssid, password);
  WiFi.setSleep(false);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
 
  Audioserver.on("/audio", HTTP_GET, handleAudioStream);
  Audioserver.begin();
 
  Serial.print("Server Ready! Use 'http://");
  Serial.print(WiFi.localIP());
  Serial.print(":82/audio");
  Serial.println("' to connect");
}
 
void loop() {
  Audioserver.handleClient();
}

juampe
Posts: 1
Joined: Wed Apr 23, 2025 2:10 pm

Re: porting I2S code from legacy driver to new driver

Postby juampe » Wed Apr 23, 2025 2:28 pm

Hi,

Have you figured it out?

I've been figthing with this for a while and have just uploaded a sample code to a repository to kee it safe https://github.com/JPG-Consulting/ESP32-I2S-INMP441

I think your problem starts at this line:

Code: Select all

.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
I finally managed to get a similar wave to the legacy driver by setting slot_cfg by hand. After some research the INMP441 is 24 bits, so 16 bit does not seem correct and there is no int24_t; so I set it to I2S_DATA_BIT_WIDTH_16BIT; however I found it wouldn't work either. Finaly figured out that setting it to 32 bits seems to get mre data than what I was wanting to get s data_bit_width was also set to 32 bits. Changed the value of data_bit_width to I2S_DATA_BIT_WIDTH_24BIT and now it seems to be working.

Not sure if this is your case. If it solves the issue please tell me to know if this is the final way to get it going as Ihave yet to test a real recording, the plotter looks good, but have not got a chance to test the full thing until tomorrow :)

Who is online

Users browsing this forum: No registered users and 1 guest