麦克风的低频分量太大

Microphone has too large component of lower frequency

我使用knowles sph0645lm4h-b麦克风采集数据,是24位PCM格式,18位数据精度。然后将 24 位 PCM 数据截断为 18 位数据,因为根据规范,最后 6 位始终为 0。之后,将 18 位数据存储为 32 位无符号整数。当MSB位为0时,表示为正整数,MSB位为0时,表示为负整数。

在那之后,我发现所有数据都是正面的,无论我用哪种声音测试。我用双频测试了它,做了一个 FFT,然后我发现结果几乎是正确的,除了大约 0-100Hz 的较低频率更大。但是我用我用于 FFT 算法的数据重建了声音。重建的声音几乎是正确的,但有噪音。

我使用缓冲区存储麦克风数据,这些数据使用 DMA 传输。缓冲区是

uint16_t fft_buffer[FFT_LENGTH*4]

DMA 配置如下:

DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(SPI2->DR);
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)fft_buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize =DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_BufferSize = FFT_LENGTH*4;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

从缓冲区中提取数据,截断为 18 位并将其扩展为 32 位并存储在 fft_integer:

int32_t fft_integer[FFT_LENGTH];

fft_buffer存储一个通道的原始数据和另一个通道的冗余数据。原始数据存储在数组的两个元素中,如 fft_buffer[4] 和 fft_buffer[5],它们都是 16 位。而 fft_integer 只存储来自一个通道的数据,每个数据占用一个 32bits.This 这就是 fft_buffer 数组大小为 [FFT_LENGTH*4] 的原因。 2 个元素用于来自一个通道的数据,2 个元素用于另一个通道。但是对于fft_integer,fft_integer数组的大小是FFT_LENGTH。因为存储的是一个通道的数据,一个int32_t.

类型的元素可以存储18bits
for (t=0;t<FFT_LENGTH*4;t=t+4){
    uint8_t  first_8_bits, second_8_bits, last_2_bits;
    uint32_t store_int;
    /* get the first 8 bits, middle 8 bits and last 2 bits, combine it to a new value */
    first_8_bits = fft_buffer[t]>>8;
    second_8_bits = fft_buffer[t]&0xFF;
    last_2_bits = (fft_buffer[t+1]>>8)>>6;

    store_int = ((first_8_bits <<10)+(second_8_bits <<2)+last_2_bits);

    /* convert it to signed integer number according to the MSB of value
     * if MSB is 1, then set all the bits before MSB to 1
     */
    const uint8_t negative = ((store_int & (1 << 17)) != 0);
    int32_t nativeInt;
    if (negative)
        nativeInt = store_int | ~((1 << 18) - 1);
    else
        nativeInt = store_int;

    fft_integer[cnt] = nativeInt;
    cnt++;
}

麦克风使用的是I2S接口,是单声道麦克风,也就是说只有一半的数据在一半的传输时间内有效。它工作约128ms,然后将停止工作。

这张图片显示了数据,我将其转换为整数。

我的问题是,虽然可以重建相似的声音,但为什么会有大量的低频分量。我确定硬件配置没有问题。

我做了一个实验,看看buffer中存放了哪些原始数据。我做了以下测试:

uint8_t a, b, c, d
for (t=0;t<FFT_LENGTH*4;t=t+4){
    a = (fft_buffer[t]&0xFF00)>>8;
    b = fft_buffer[t]&0x00FF;
    c = (fft_buffer[t+1]&0xFF00)>>8;
    /* set the tri-state to 0 */
    d = fft_buffer[t+1]&0x0000;
    printf("%.2x",a);
    printf("%.2x",b);
    printf("%.2x",c);
    printf("%.2x\n",d);

}

PCM数据显示如下:

0ec40000
0ec48000
0ec50000
0ec60000
0ec60000
0ec5c000
...    
0cf28000
0cf20000
0cf10000
0cf04000
0cef8000
0cef0000
0cedc000
0ced4000
0cee4000
0ced8000
0cec4000
0cebc000
0ceb4000
....    
0b554000
0b548000
0b538000
0b53c000
0b524000
0b50c000
0b50c000
...

内存中的原始数据:

c4 0e ff 00
c5 0e ff 40
...
52 0b ff c0
50 0b ff c0

我用它作为小端。

原始数据中从DC开始的大低频分量是由于错误地将24位二进制补码样本转换为int32_t而导致的大DC偏移造成的。直流偏移是听不见的,除非它导致削波或算术溢出发生。实际上并没有任何高达 100Hz 的低频,这仅仅是 FFT 对强直流 (0Hz) 元素的响应的人工制品。这就是为什么您听不到任何低频的原因。

下面我尽可能清楚地陈述了一些假设,以便答案可能会根据实际情况进行调整。

给定:

Raw data in Memory:

c4 0e ff 00
c5 0e ff 40
...
52 0b ff c0
50 0b ff c0

I use it as little endian.

2 elements are used for data from one channel and 2 element is used for the other channel

并给出后续评论:

fft_buffer[0] stores the higher 16 bits, fft_buffer[1] stores the lower 16 bits

那么数据实际上是 cross-endian 例如,for:

c4 0e ff 00

然后

fft_buffer[n]   = 0x0ec4 ;
fft_buffer[n+1] = 0x00ff ;

重建样本应该是:

0x00ff0ec4

那么翻译就是将 fft_buffer 重新解释为 32 位数组,交换 16 位字序,然后将符号位移动到 int32_t 符号位位置和(可选)重新缩放,例如:

c4 0e ff 00 => 0x00ff0ec4
0x00ff0ec4<< 8    = 0xff0ec400
0xff0ec400/ 16384 = 0xffff0ec4(-61756)

因此:

// Reinterpret DMA buffer as 32bit samples
int32_t* fft_buffer32 = (int32_t*)fft_buffer ;

// For each even numbered DMA buffer sample...
for( t = 0; t < FFT_LENGTH * 2; t += 2 )
{
    // ... swap 16 bit word order
    int32_t sample = fft_buffer32 [t] << 16 | 
                     fft_buffer32 [t] >> 16 ;

    // ... from 24 to 32 bit 2's complement and rescale to
    //     maintain original magnitude. Copy to single channel
    //     fft_integer array.
    fft_integer[t / 2] = (sample << 8) / 16384 ;
}