【问题标题】:Detect wheter a sound buffer is filled with data or not检测声音缓冲区是否充满数据
【发布时间】:2014-06-18 03:46:05
【问题描述】:

我正在使用 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 个点,声音比 void 高,这很难检测到它是否有声音......

那我该怎么办?我该如何改进它?当我录制声音并填写所有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
                 .....
}
 }

【问题讨论】:

  • 如果真正的问题是编码问题而不是声学问题,您更有可能得到好的答案。您的代码总体上看起来不错,但是 (a) 您如何得出 avg_voice?,以及 (b) 您是否尝试查看在 wave[] 中收集的值(打印或调试器)?
  • 很难看出问题所在,因为您发布的代码中没有任何内容说明 wave 是如何初始化的。
  • @Arun avg_voice 是我输入的typedef,我只是简单地调试并查看了缓冲区在无效和语音时的平均值,然后手动输入一个数字avg_voice,这就是为什么我认为我的代码不好。
  • @RSahu 我忘了提。缓冲区是一个包含 8000 个短整数单元的数组(这就是缓冲区 - short int *wave
  • @Amit 在传递给isEmpty之前是如何初始化的?

标签: c++ audio voip voice-recording


【解决方案1】:

您已经为waveIn 分配了内存,使用:

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

但是,这不会初始化内容。将内容初始化为有意义的东西。然后,您将能够看到哪里有问题。如果0 是有意义的默认值,请使用:

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

【讨论】:

  • 我很确定当我记录到缓冲区时,所有的单元格都充满了信息,所以如果数组被初始化,它确实会发生变化。我已经添加了录制声音的代码......另外,我认为你不明白我的问题:我有一个完整的 short int 数组,大小为 NUMPTS(即 8000),并且数组包含数据来自麦克风的录音...
  • 我需要检测用户是否对着麦克风讲话,缓冲区实际上是否包含语音,或者是空缓冲区(空缓冲区不是必需的 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 左右。但在实践中,由于您连接了麦克风,因此它会比这高一些 - 在很大程度上取决于您的设置质量。这就是您需要进行试验的地方。

【讨论】:

  • @Beed。我不知道我在想什么。我将编辑我的答案。
  • @jaket 谢谢!我用谷歌搜索了 RMS,但我不明白几件事:1) 什么是 scaleShortToDouble,为什么是 1.0/0x8000? 2)我已经看到您将 short int 更改为 double,然后对其进行平方。然后你在平方和上使用 sqrt 除以长度。这是什么意思?正如我之前听说过的 RMS 一样。 3)如何使用Rms返回的double,看看它是否足够响亮?
  • @Amit。 1) scaleShortToDouble 只需乘以 1/(2^15) 将波形重新缩放到 -1.0 到 1.0 的范围。在浮点中进行信号处理通常更容易。 2) RMS 代表均方根。见en.wikipedia.org/wiki/Root_mean_square。 3)您需要测量您的本底噪声(考虑房间的环境噪声)并将阈值设置在比该值高一点的地方。我将更新我的答案以包含一个 `* sqrt(2) 并在其中解释更多内容。
  • 非常感谢!我注意到当我说话的时候,RMS 通常是 0.033 ~ 0.037,所以我把声音的限制设置为 0.04,效果很好!
  • 你在这里建立了一个噪声门,这些通常有滞后——也就是说,动态行为——在多个样本上运行
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-03
    • 1970-01-01
    • 2021-03-24
    相关资源
    最近更新 更多