Я записываю с помощью 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
.....
}
}
Во-первых, я предсказываю, что буфер не был заполнен к тому времени, когда вы анализируете его. Вместо простого сна вам следует опросить 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) будет масштабировать результат так, чтобы, если бы вы поместили синусоидальную волну в аналого-цифровой преобразователь так, чтобы получился полномасштабный цифровой синус (-32768,32767), среднеквадратическое значение будет равно 1,0. ,
После этого вы можете теперь преобразовать среднеквадратичное значение в дБ, и у вас будет число, которое называется DBFs и обычно используется, когда речь идет о цифровых уровнях.
Преобразование: dBFS = 20*log10(rms)
и примерно:
каждая половина входного уровня — это еще -6 дБFS вниз.
Также бывает, что на каждую половину входного сигнала требуется один бит АЦП. Поскольку у вас 16-битный сигнал, теоретический уровень шума будет около -96 дБFS. Тем не менее, на практике, поскольку у вас подключен микрофон, он будет несколько выше — в значительной степени зависит от качества вашей установки. И вот где вам нужно будет экспериментировать.
Вы должны использовать 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;
}