У меня есть четыре буфера, которые я использую для воспроизведения звука в синтезаторе. Сначала я отправляю два буфера, а затем в подпрограмме обратного вызова я записываю данные в следующий буфер и затем отправляю этот буфер.
Когда я генерирую каждый буфер, я просто помещаю в него синусоидальную волну, период которой кратен длине буфера.
Когда я выполняю, я слышу короткие паузы между каждым буфером. Я увеличил размер буфера до 16K сэмплов при 44100 Гц, чтобы четко слышать, что весь буфер воспроизводится, но между ними есть прерывание.
Я думаю, что происходит, что функция обратного вызова вызывается только тогда, когда ВСЕ буферы, которые были написаны, завершены. Мне нужен синтез, чтобы опережать воспроизведение, поэтому мне нужен обратный вызов, когда каждый буфер завершен.
Как люди обычно решают эту проблему?
Обновление: меня попросили добавить код. Вот что у меня есть:
Сначала я подключаюсь к устройству WaveOut:
// Always grab the mapped wav device.
UINT deviceId = WAVE_MAPPER;
// This is an excelent tutorial:
// http://planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=4422&lngWId=3
WAVEFORMATEX wfx;
wfx.nSamplesPerSec = 44100;
wfx.wBitsPerSample = 16;
wfx.nChannels = 1;
wfx.cbSize = 0;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels;
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
_waveChangeEventHandle = CreateMutex(NULL,false,NULL);
MMRESULT res;
res = waveOutOpen(&_wo, deviceId, &wfx, (DWORD_PTR)WavCallback,
(DWORD_PTR)this, CALLBACK_FUNCTION);
Я инициализирую четыре кадра, которые буду использовать:
for (int i=0; i<_numFrames; ++i)
{
WAVEHDR *header = _outputFrames+i;
ZeroMemory(header, sizeof(WAVEHDR));
// Block size is in bytes. We have 2 bytes per sample.
header->dwBufferLength = _codeSpec->OutputNumSamples*2;
header->lpData = (LPSTR)malloc(2 * _codeSpec->OutputNumSamples);
ZeroMemory(header->lpData, 2*_codeSpec->OutputNumSamples);
res = waveOutPrepareHeader(_wo, header, sizeof(WAVEHDR));
if (res != MMSYSERR_NOERROR)
{
printf("Error preparing header: %d\n", res - MMSYSERR_BASE);
}
}
SubmitBuffer();
SubmitBuffer();
Вот код SubmitBuffer:
void Vodec::SubmitBuffer()
{
WAVEHDR *header = _outputFrames+_curFrame;
MMRESULT res;
res = waveOutWrite(_wo, header, sizeof(WAVEHDR));
if (res != MMSYSERR_NOERROR)
{
if (res = WAVERR_STILLPLAYING)
{
printf("Cannot write when still playing.");
}
else
{
printf("Error calling waveOutWrite: %d\n", res-WAVERR_BASE);
}
}
_curFrame = (_curFrame+1)&0x3;
if (_pointQueue != NULL)
{
RenderQueue();
_nextFrame = (_nextFrame + 1) & 0x3;
}
}
И вот мой код обратного вызова:
void CALLBACK Vodec::WavCallback(HWAVEOUT hWaveOut,
UINT uMsg,
DWORD dwInstance,
DWORD dwParam1,
DWORD dwParam2 )
{
// Only listen for end of block messages.
if(uMsg != WOM_DONE) return;
Vodec *instance = (Vodec *)dwInstance;
instance->SubmitBuffer();
}
Код RenderQueue довольно прост — просто копирует часть буфера шаблона в выходной буфер:
void Vodec::RenderQueue()
{
double white = _pointQueue->White;
white = 10.0; // For now just override with a constant value
int numSamples = _codeSpec->OutputNumSamples;
signed short int *data = (signed short int *)_outputFrames[_nextFrame].lpData;
for (int i=0; i<numSamples; ++i)
{
Sample x = white * _noise->Samples[i];
data[i] = (signed short int)(x);
}
_sampleOffset += numSamples;
if (_sampleOffset >= _pointQueue->DurationInSamples)
{
_sampleOffset = 0;
_pointQueue = _pointQueue->next;
}
}
ОБНОВЛЕНИЕ: В основном решена проблема. Мне нужно увеличить _nextFrame вместе с _curFrame (не условно). Буфер воспроизведения опережал буфер записи.
Однако, когда я уменьшаю буфер воспроизведения до 1024 сэмплов, он снова становится прерывистым. На 2048 образцах это понятно. Это происходит как для сборок Debug, так и для Release.
1024 сэмпла — это примерно 23 мс аудиоданных. wav — это довольно высокоуровневый API, начиная с Windows Vista. Если вы хотите воспроизведение аудио с низкой задержкой, вы должны использовать CoreAudio. Вы можете уменьшить задержки до 10 мс в режиме совместного использования и до 3 мс в эксклюзивном режиме. Кроме того, звук зависит от процессов, запущенных в настоящее время в вашей системе. Другими словами, это зависит от того, как часто ваш аудиопоток может работать для получения данных. Вы также должны посмотреть на MultiMedia Class Scheduler Service а также AvSetMmThreadCharacteristics функция.
Других решений пока нет …