我使用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点,而不是无效,很难察觉它是否有声音......
那我该怎么办?我怎样才能改进它?当我录制语音并填写所有WAVEFORMATEX
和WAVEHDR
设置时,也许这是一个选项?
谢谢!
编辑: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
.....
}
}
答案 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)
且大致:
输入电平的每个减半是另一个-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;
}