cancel
Showing results for 
Search instead for 
Did you mean: 

Extracting 24-bit Audio from INMP441: Very Low Amplitude and High Distortion Issue

crazyblueer
Associate

 

Hello gurus,

I am in desperate need of your help. I am trying to extract audio from an INMP441 microphone using a Nucleo STM32F4RE11 and store it as a WAV file on an SD card. According to the datasheet, the microphone outputs signed PCM 24-bit audio in MSB-first order, wrapped in a 32-bit frame.

To handle this, I have configured the I²S "Data and Frame Format" to "32 Bits Data on 32 Bits Frame", attempting to extract the 24-bit data from the 32-bit frame and convert it to Little Endian format. However, I am facing issues with the extracted audio:

  • While I can recognize my voice's timbre, the amplitude is extremely low, and the waveform appears very weak.

  • When I previously tested "16 Bits Data on 32 Bits Frame", my voice sounded unnaturally deep, almost like a male voice.

I suspect there may be an issue with my bit extraction or size allocation. Here's a brief summary of my setup:

  1. I declared an int32_t variable to store the raw I²S data and used the following function to record audio:

     
    HAL_I2S_Receive_DMA(&hi2s3, (int32_t *)data_i2s, sizeof(data_i2s)/4);

    However, I am concerned that this might be incorrect since the function prototype expects a uint16_t * pointer. Given that I’m handling int32_t data, I’m unsure of the correct way to manage this typecast.

  2. For extracting the 24-bit data, I have performed bit-shifting and swap the bytes to store in Little Endian format, but I’m not sure that I’ve done it correctly because the extracted audio I got is so low. 

I have attached screenshots and code snippets for better context. I would greatly appreciate any insights on how to debug this issue.

Thank you so much!

 

void write2wave_file(uint8_t *data, uint16_t data_size) {

uint32_t temp_number;

myprintf("Writing...\n");

static int first_time = 0;

if (first_time == 0) {

fres = f_write(&fil, (void *)wav_file_header, sizeof(wav_file_header), (UINT *)&temp_number);

if (fres != FR_OK) {

myprintf("Header write error: %d\n", fres);

f_close(&fil);

return;

}

first_time = 1;

}

int sample_read = data_size / sizeof(int32_t); // 32-bit container for 24-bit data

for (int i = 0; i < sample_read; i+=2) {

int32_t raw_data = ((int32_t *)data)[i] >> 8;



uint8_t swapped_data[3];

swapped_data[2] = (raw_data>>0) & 0xFF; // LSB

swapped_data[1] = (raw_data >> ‌‌ & 0xFF; 

swapped_data[0] = (raw_data >> 16) & 0xFF;

int retry = 0;

while (retry < 3) {

fres = f_write(&fil,swapped_data, 3, (UINT *)&temp_number); // Write 3 bytes per sample

if (fres == FR_OK) break;

myprintf("Write error %d, retry %d...\n", fres, retry + 1);

HAL_Delay(10);

retry++;

}

if (fres != FR_OK) {

myprintf("Write failed after retries: %d\n", fres);

f_close(&fil);

return;

}

}

wav_file_size += (sample_read / 2) * 3;

myprintf("how much: %d\n", wav_file_size);

// Periodically flush to prevent corruption

static int write_count = 0;

write_count++;

if (write_count % 10 == 0) {

f_sync(&fil);

}

}

4 REPLIES 4
AScha.3
Chief III

Hi,

you didnt write: which cpu...?  (still H747?)

The I2S should be set : master receive , 24b in 32b frame . And use the SAI in I2S mode , much better than I2S.

 

If you feel a post has answered your question, please click "Accept as Solution".

Sorry I should have clarified. The board I am using is the Nucleo STM32F411RE. I will try to use SAI but for now I am concerned most with how I allocate the size of the audio data. So I have just done some researched and an account named "Saket_OM" said  "In the STM32 HAL library, the HAL_I2SEx_TransmitReceive_DMA function indeed uses uint16_t pointers for data transfer. This is because the I2S peripheral in the STM32 typically handles data in 16-bit chunks, even if the actual audio data is 24-bit or 32-bit.

When you set the data width to "Half Word" in the DMA settings, it means that each DMA transfer will handle 16 bits (2 bytes) at a time. This is why you are receiving 16-bit data chunks, which you then need to reassemble into 32-bit variables.

If you want to receive 32-bit data directly, you would need to set the DMA data width to "Word" (32 bits). However, the HAL function HAL_I2SEx_TransmitReceive_DMA does not support uint32_t pointers directly. This is a limitation of the HAL library's API design.

To work around this, you can continue using uint16_t pointers and manually reassemble the 32-bit data from two 16-bit transfers."

 

So I think declaring an int32_t variable and reference the (int32_t) pointer in the HAL_I2S_Receive_DMA function is not right

SO on F411 now - this has no SAI.  

And on 32b frames i had some problems onmy tests with F411, i concluded: here 32b mode never working  as it should - but seems, no problem, almost not used by users.

So i tried the SAI on H743 (or other from H7 series) and using it on I2S mode works perfect, also on 32b data or 24 in 32b frame.

For your INM... mic : it has 24b data, so you have to use : I2S master receive , 24b in 32b frame.

Can you set this on F411 ? (i cannot try now, i am at work...)

If you feel a post has answered your question, please click "Accept as Solution".

ed

I checked in rm, F411 can :

AScha3_0-1743071947482.png

24b in 32b frame - can be set. Just test, its really working correct...

If you feel a post has answered your question, please click "Accept as Solution".