esp32-i2s-slm icon indicating copy to clipboard operation
esp32-i2s-slm copied to clipboard

MIC_CONVERT

Open otger opened this issue 1 year ago • 0 comments

Hi,

First of all, let me thank you for this project, which has helped me greatly in my own project. I am adapting your code to a project to check the noise of a street, and I think I've found a problem in your code, although I may be wrong.

When you configure the I2S driver you set the bits_per_sample to 32:

void mic_i2s_init() {
  // Setup I2S to sample mono channel for SAMPLE_RATE * SAMPLE_BITS
  // NOTE: Recent update to Arduino_esp32 (1.0.2 -> 1.0.3)
  //       seems to have swapped ONLY_LEFT and ONLY_RIGHT channels
  const i2s_config_t i2s_config = {
    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,

Where SAMPLE_BITS = 32.

After reading data with i2s, you shift the samples with the MIC_CONVERTmacro:

#define MIC_BITS          24          // valid number of bits in I2S data
#define MIC_CONVERT(s)    (s >> (SAMPLE_BITS - MIC_BITS))
    i2s_read(I2S_PORT, &samples, SAMPLES_SHORT * sizeof(SAMPLE_T), &bytes_read, portMAX_DELAY);

    TickType_t start_tick = xTaskGetTickCount();
    
    // Convert (including shifting) integer microphone values to floats, 
    // using the same buffer (assumed sample size is same as size of float), 
    // to save a bit of memory
    SAMPLE_T* int_samples = (SAMPLE_T*)&samples;
    for(int i=0; i<SAMPLES_SHORT; i++) samples[i] = MIC_CONVERT(int_samples[i]);

The problem is that in my setup (with an INMP441) when I set the sample bits to 32, the values are already right aligned. If I print some samples with #define SAMPLE_BITS 32:

ffa92dfc ffa5ebfc ffa3e9fc ffac6400 ffb067fc ffb4de00 ffc0d3fc ffcf2c00 
ffd65400 ffdb7800 ffe39200 ffead1fc fff199fc fffb6ffc 0003ec00 000e3bfc 
00191200 001d07fc 0025c9fc 0029c9fc 00313c00 00344e00 00370c00 003d5dfc 
003f6800 003c97fc 003821fc 0036b400 0033f800 0037b400 003c4ffc 00439c00 
0047f5fc 00478800 0044b800 00437600 004245fc 003a5e00 00313c00 00315200 
002f5c00 002895fc 002711fc 0023ec00 00225e00 001f2400 002131fc 001bd200 
00114ffc 00052dfc fffdd9fc fff4b9fc ffe841fc ffdb25fc ffd5de00 ffd4b7fc 
ffd139fc ffcae200 ffc695fc ffc36bfc ffbdb800 ffbddffc ffbc5400 ffb78bfc 
ffb5d5fc ffb8a3fc ffbe0800 ffbe59fc ffc30ffc ffc775fc ffcd9a00 ffd58c00 
ffd96000 ffd08c00 ffce3000 ffd155fc ffd23c00 ffd051fc ffd25c00 ffd89a00 
ffdef9fc ffe8cc00 fff11200 fff575fc fff55ffc fff9c5fc fffc9200 fffc0ffc 
fffd7e00 0000fbfc 0009d000 0011ac00 001743fc 0020f600 0024b200 00280e00 
002c8bfc 00297bfc 002b29fc 00266600 00236400 00220400 00228e00 00201800

If I set #define SAMPLE_BITS 24 then values output by the mic are left shifted 8 bits:

ffe97400 ffe97400 fff0c300 fffd5300 000acc00 000acc00 0012fc00 001a5f00 
00225f00 00225f00 00321000 00436b00 0058f700 0058f700 00740400 00930b00 
00b59b00 00b59b00 00d0f000 00e54400 00ef9400 00ef9400 00e7d400 00d56b00 
00ae3300 00ae3300 007fbc00 0049e000 00071000 00071000 ffb95f00 ff691c00 
ff1a2300 ff1a2300 fecbbc00 fe85af00 fe590000 fe590000 fe432800 fe418000 
fe5c7b00 fe5c7b00 fe8c8f00 fed64800 ff2fc000 ff2fc000 ff8ee400 fff0bc00 
004a1800 004a1800 009dd400 00df4700 010b1b00 010b1b00 01223800 01224300 
0108af00 0108af00 00db4b00 00a35000 005c0400 005c0400 001bd300 ffd71400 
ff9e5c00 ff9e5c00 ff73ac00 ff588000 ff51af00 ff51af00 ff655800 ff950300 
ffcb2800 ffcb2800 00169400 00673f00 00c08000 00c08000 01111300 015f2800 
01a0ef00 01a0ef00 01cb1000 01dea400 01d29f00 01d29f00 01a41c00 01579300 
00eccb00 00eccb00 00783f00 fffba800 ff833300 ff833300 ff147300 feb52800 
fe67bf00 fe67bf00 fe30d300 fe16c300 fe163700 fe163700 fe31fc00 fe668700 

So you should shift the samples if you set SAMPLE_BITS to 24 only.

It seems that the LSbits are crappy (either 00 or FC only) when SAMPLE_BITS is set to 32, and might be losing resolution.

The problem is that you might be working with 16 bits samples and not 24 if you set SAMPLE_BITSto 32 and shift the samples by 8 bits. This made me think that the way to calculate short_SPL_dB could be wrong, as you count the reference as 24 bits, not 16 bits:

    // Calculate dB values relative to MIC_REF_AMPL and adjust for microphone reference
    double short_RMS = sqrt(double(q.sum_sqr_SPL) / SAMPLES_SHORT);
    double short_SPL_dB = MIC_OFFSET_DB + MIC_REF_DB + 20 * log10(short_RMS / MIC_REF_AMPL);
// Calculate reference amplitude value at compile time
constexpr double MIC_REF_AMPL = pow(10, double(MIC_SENSITIVITY)/20) * ((1<<(MIC_BITS-1))-1);

In my opinion, at least with an INMP441, the way to operate should be: initialize the driver with #define SAMPLE_BITS 24 and shift samples 8 bits to the right:

for(int i=0; i<ACQ_PACK_SAMPLES; i++) samples[i] = (float) (int_samples[i] >> 8);

This implementation breaks your solution for several MEMS, but as I only have INMP441 sensors I do not know if this happens in all of them or just on the INMP441.

Thank you for your time

otger avatar Oct 07 '22 21:10 otger