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

时间:2014-05-01 16:02:50

标签: c++ audio voip voice-recording

我使用WaveAPI进行录制,我希望在完成录制数据后,如果缓冲区中有声音,或者没有录制任何内容(只是房间的空白)时进行检测。

我编写了一个函数来获取缓冲区绝对值的平均值,并且它可以正常工作,但是它有很多问题:

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是一个短的int数组,包含8000个单元格,并在内部存储语音,看起来像这样(例子): 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
                 .....
}
 }

3 个答案:

答案 0 :(得分:2)

首先,我预测缓冲区在分析时尚未填充。您应该轮询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转换器以便出现满量程数字正弦(-32768,32767),则Rms结果将为1.0 。

完成后,您现在可以将Rms值转换为dB,并且您将拥有一个被称为dBFS的数字,并且在谈论数字水平时通常会使用。

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

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

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

还会发生输入信号的每次减半都需要少一位的A / D转换器。由于您有一个16位信号,因此理论噪底将达到-96 dBFS左右。但实际上,由于你有一个连接的麦克风,它会比这更高 - 这在很大程度上取决于你的设置质量。那就是你需要进行实验的地方。

答案 1 :(得分:1)

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

答案 2 :(得分:0)

您已使用以下内容为waveIn分配内存:

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

但是,这不会初始化内容。将内容初始化为有意义的内容。然后,您将能够看到事情不起作用的地方。如果0是有意义的默认值,请使用:

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