检测声音缓冲区是否充满数据

Detect wheter a sound buffer is filled with data or not

本文关键字:数据 满数据 声音 缓冲区 是否 检测      更新时间:2023-10-16

我正在使用WaveAPI进行录制,我想在录制完数据后检测缓冲区中是否有声音,或者它什么都没有录制(只是房间的空隙(。

我写了一个函数,可以得到缓冲区绝对值的平均值,它的工作原理是"Ok",但它有很多问题:

1( 我检测到,当它是空的时,平均值为~860,当我说话时,它为~875,这几乎没有什么不同。怎么可能呢?我正在录制1秒。

2( 有时,我看到平均值约为860,有时约为500,有时甚至约为400。为什么它每次都在变化?我的意思是,它不应该是一样的吗?因为在所有的时候,它都捕捉到了空虚,而没有改变吗?

这是我写的函数:

bool isEmpty(short int *wave)
{
int avg = 0;
for (int i = 0 ; i < NUMPTS ; i++)
{
    if (wave[i] < 0)
        avg = avg + (wave[i]) * -1;
    else
        avg = avg + (wave[i]);
}
avg = avg / NUMPTS;
if (avg > avg_voice)
    return false;
return true;
}

这个功能还不够好,因为它并不完全正确,我必须不断地将avg_voice更改为其他内容,有时缓冲区平均只有10个点,声音比空白高,这很难检测它是否有声音。。。。

那么我该怎么办呢?我该如何改进?当我录制语音并填写所有WAVEFORMATEXWAVEHDR设置时,可能有这样的选项?

谢谢!

编辑:wave是一个包含8000单元格的短int数组,它将语音存储在内部,看起来如下(示例(:wave[0] = -123;wave[1] = -205;wave[2] = -212'

这样。。。

第二次编辑:我记录的数据是这样的:

void StartRecord()
{
short int *waveIn = new short int[NUMPTS];
HWAVEIN hWaveIn;
WAVEHDR WaveInHdr;
MMRESULT result;
HWAVEOUT hWaveOut;
WAVEFORMATEX pFormat;
pFormat.wFormatTag = WAVE_FORMAT_PCM;
pFormat.nChannels = 1;
pFormat.nSamplesPerSec = sampleRate;
pFormat.nAvgBytesPerSec = 2 * sampleRate;
pFormat.nBlockAlign = 2;
pFormat.wBitsPerSample = 16;
pFormat.cbSize = 0;
result = waveInOpen(&hWaveIn, WAVE_MAPPER, &pFormat, 0, 0, WAVE_FORMAT_DIRECT);
if(result)
{
    char fault[256];
    waveInGetErrorTextA(result, fault, 256);
    MessageBoxA(NULL, fault, "Failed to open waveform input device.", MB_OK | MB_ICONEXCLAMATION);
    return;
}
WaveInHdr.lpData = (LPSTR)waveIn;
WaveInHdr.dwBufferLength = 2 * NUMPTS;
WaveInHdr.dwBytesRecorded = 0;
WaveInHdr.dwUser = 0;
WaveInHdr.dwFlags = 0;
WaveInHdr.dwLoops = 0;
while (true)
{
    waveInPrepareHeader(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));
    result = waveInAddBuffer(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));
    result = waveInStart(hWaveIn);
    if(result)
    {
        MessageBoxA(NULL, "Failed to start recording", NULL, MB_OK | MB_ICONEXCLAMATION);
        return;
    }
    // Wait until finished recording 
    Sleep(seconds * 1000); //Sleep for as long as there was recorded
    waveInUnprepareHeader(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));
    if (isEmpty(waveIn)) // Checks here
                 .....
}
 }

首先,我预测在分析缓冲区时缓冲区尚未填充。与其简单地休眠,不如轮询WaveInHdr.dwFlags以设置WHDR_DONE位。

result = waveInStart(hWaveIn);
if(result)
{
    MessageBoxA(NULL, "Failed to start recording", NULL, MB_OK | MB_ICONEXCLAMATION);
    return;
}
// Wait until finished recording 
while ((WaveInHdr.dwFlags & WHDR_DONE) == 0)
    Sleep(100);

其次,我建议用一种更好的方法来测量响度。RMS也许:

double Rms(short int *wave, int length)
{
    double sumSquared = 0;
    double scaleShortToDouble = 1.0/0x8000;
    for (int i = 0 ; i < length; i++)
    {
         double s = wave[i] * scaleShortToDouble;
         sumSquared += s * s;
    }
    return sqrt(2) * sqrt(sumSquared/length);
}

我已经将短裤转换为-1.0到1.0范围内的双打,因为它更容易计算。额外的sqrt(2(将对结果进行缩放,这样,如果您将正弦波输入a/D转换器,从而产生全刻度数字正弦(-3276382767(,则Rms结果将为1.0。

完成后,您现在可以将Rms值转换为dB,您将得到一个称为dBFS的数字,该数字在谈论数字电平时常用。

转换为:dBFS = 20*log10(rms),大致为:

  • 0 dBFS=1.0`
  • -6 dBFS=0.5
  • -12 dBFS=0.25

输入电平的每个减半是另一个-6dBFS下降。

输入信号的每次减半也将需要A/D转换器的少一位。由于你有一个16位的信号,你的理论本底噪声将在-96 dBFS左右。然而,在实践中,由于你连接了麦克风,它会比这个高一些——这在很大程度上取决于你的设置质量。这就是你需要进行实验的地方。

您必须使用RMS,因为正弦曲线的平均值为0,所以如果取平均值,您只会得到麦克风的电压偏移。这就是为什么你会得到不一致但低的值,860/2^15大约是动态范围的2%。

您已使用为waveIn分配内存

short int *waveIn = new short int[NUMPTS];

但是,这并不能初始化内容。将内容初始化为有意义的内容。然后,你将能够看到哪些地方不起作用。如果0是有意义的默认值,则使用:

for (int i = 0; i < NUMPTS; ++i )
{
   waveIn[i] = 0;
}