WaveOutWrite() с двойной буферизацией чертовски заикаетсяC++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 WaveOutWrite() с двойной буферизацией чертовски заикается

Сообщение Anonymous »

[Тайна раскрыта; для тех, кто ищет объяснения, оно находится внизу этого поста]

Ниже приведен генератор тонов Windows, который я пытаюсь написать с использованием функций Windows waveOut*().

Несмотря на то, что я делаю буквально все в соответствии с MSDN (например, события обратного вызова, которые должны быть сброшены вручную), я не могу добиться плавного воспроизведения прямоугольных сигналов от этой чертовой штуки - вообще-то плавного воспроизведения, но ради простоты я демонстрирую квадраты. Границы буфера всегда приветствуют меня щелчками! Похоже, Windows просто игнорирует тот факт, что я использую двойную буферизацию.

Сам генератор не зависит от размера буфера, и если я беру буфер большего размера, плавное воспроизведение продолжается в течение более длительного периода времени — но когда буфер наконец заканчивается, раздается щелчок.

Справка.

Код: Выделить всё

#define _WIN32_IE 0x0500
#define _WIN32_WINNT 0x0501
#define WINVER _WIN32_WINNT

#include 
#include 
#include 

#include 
#include 
#include 

short freq, ampl;

typedef struct {
long chnl, smpl, bits, size, swiz;
void *sink, *data[2];
} WAVE;

LRESULT APIENTRY WndProc(HWND hWnd, UINT uMsg, WPARAM wPrm, LPARAM lPrm) {
switch (uMsg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;

case WM_NOTIFY:
switch (((NMHDR*)lPrm)->idFrom) {
case 2:
freq = ((NMUPDOWN*)lPrm)->iPos;
break;

case 4:
ampl = ((NMUPDOWN*)lPrm)->iPos;
break;
}
return 0;

default:
break;
}
return DefWindowProc(hWnd, uMsg, wPrm, lPrm);
}

void FillBuf(WAVE *wave, short freq, short ampl, long *phaz) {
int16_t *data = wave->data[wave->swiz ^= 1];
float tone = 1.0 * freq / wave->smpl;
long iter;

for(iter = 0; iter < wave->size; iter++)
data[iter] = ((long)(tone * (iter + *phaz)) & 1)? ampl : -ampl;

*phaz = *phaz + iter;//2.0 * frac(0.5 * tone * (iter + *phaz)) / tone;
}

DWORD APIENTRY WaveFunc(LPVOID data) {
WAVEHDR *whdr;
WAVE *wave;
intptr_t *sink;
long size, phaz = 0;

wave = (WAVE*)data;
whdr = (WAVEHDR*)(sink = wave->sink)[1];
size = wave->chnl * wave->size * (wave->bits >>  3);
wave->data[0] = calloc(1, size);
wave->data[1] = calloc(1, size);
do {
waveOutUnprepareHeader((HWAVEOUT)sink[0], whdr, sizeof(WAVEHDR));
whdr->dwBufferLength = size;
whdr->dwFlags = 0;
whdr->dwLoops = 0;
whdr->lpData = (LPSTR)wave->data[wave->swiz];
waveOutPrepareHeader((HWAVEOUT)sink[0], whdr, sizeof(WAVEHDR));
ResetEvent((HANDLE)sink[2]);
waveOutWrite((HWAVEOUT)sink[0], whdr, sizeof(WAVEHDR));
FillBuf(wave, freq, ampl, &phaz);
} while (!WaitForSingleObject((HANDLE)sink[2], INFINITE));
return 0;
}

int APIENTRY WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR args, int show) {
WNDCLASSEX wndc = {sizeof(wndc), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0,
inst, LoadIcon(0, IDI_HAND), LoadCursor(0, IDC_ARROW),
(HBRUSH)(COLOR_BTNFACE + 1), 0, "-", 0};
INITCOMMONCONTROLSEX icct = {sizeof(icct), ICC_STANDARD_CLASSES};
MSG pmsg;

HWND mwnd, cwnd, spin;
DWORD thrd;
WAVEFORMATEX wfmt;
intptr_t data[3];
WAVE wave = {1, 44100, 16, 4096, 0, data};

//    AllocConsole();
//    freopen("CONOUT$", "wb", stdout);

InitCommonControlsEx(&icct);
RegisterClassEx(&wndc);
mwnd = CreateWindowEx(0, wndc.lpszClassName, " ",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, 320, 240,
HWND_DESKTOP, 0, wndc.hInstance, 0);

cwnd = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, 0, ES_AUTOHSCROLL
| ES_WANTRETURN | ES_MULTILINE | ES_NUMBER | WS_CHILD
| WS_VISIBLE, 10, 10, 100, 24, mwnd, (HMENU)1, 0, 0);
SendMessage(cwnd, EM_LIMITTEXT, 9, 0);
spin = CreateWindowEx(0, UPDOWN_CLASS, 0, UDS_HOTTRACK | UDS_NOTHOUSANDS
| UDS_ALIGNRIGHT | UDS_SETBUDDYINT | UDS_ARROWKEYS
| WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, mwnd, (HMENU)2, 0, 0);
SendMessage(spin, UDM_SETBUDDY, (WPARAM)cwnd, 0);
SendMessage(spin, UDM_SETRANGE32, (WPARAM)20, (LPARAM)22050);
SendMessage(spin, UDM_SETPOS32, 0, (LPARAM)(freq = 400));

cwnd = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, 0, ES_AUTOHSCROLL
| ES_WANTRETURN | ES_MULTILINE | ES_NUMBER | WS_CHILD
| WS_VISIBLE, 10, 44, 100, 24, mwnd, (HMENU)3, 0, 0);
SendMessage(cwnd, EM_LIMITTEXT, 9, 0);
spin = CreateWindowEx(0, UPDOWN_CLASS, 0, UDS_HOTTRACK | UDS_NOTHOUSANDS
| UDS_ALIGNRIGHT | UDS_SETBUDDYINT | UDS_ARROWKEYS
| WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, mwnd, (HMENU)4, 0, 0);
SendMessage(spin, UDM_SETBUDDY, (WPARAM)cwnd, 0);
SendMessage(spin, UDM_SETRANGE32, (WPARAM)0, (LPARAM)32767);
SendMessage(spin, UDM_SETPOS32, 0, (LPARAM)(ampl = 32767));

wfmt = (WAVEFORMATEX){WAVE_FORMAT_PCM, wave.chnl, wave.smpl,
((wave.chnl * wave.bits) >> 3) * wave.smpl,
(wave.chnl * wave.bits) >>  3, wave.bits};
data[1] = (intptr_t)calloc(1, sizeof(WAVEHDR));
waveOutOpen((LPHWAVEOUT)&data[0], WAVE_MAPPER, &wfmt,
data[2] = (intptr_t)CreateEvent(0, 1, 0, 0), 0,
CALLBACK_EVENT);
SetThreadPriority(CreateThread(0, 0, WaveFunc, &wave, 0, &thrd),
THREAD_PRIORITY_TIME_CRITICAL);

while (pmsg.message != WM_QUIT) {
if (PeekMessage(&pmsg, 0, 0, 0, PM_REMOVE)) {
TranslateMessage(&pmsg);
DispatchMessage(&pmsg);
continue;
}
Sleep(1);
}
waveOutClose((HWAVEOUT)data[0]);
fclose(stdout);
FreeConsole();

exit(pmsg.wParam);
return 0;
}
[UPDATE:]

Продублировал заголовок, как мне сказали, безрезультатно:

Код: Выделить всё

#define _WIN32_IE 0x0500
#define _WIN32_WINNT 0x0501
#define WINVER _WIN32_WINNT

#include 
#include 
#include 

#include 
#include 
#include 

short freq, ampl;

typedef struct {
long chnl, smpl, bits, size, swiz;
void *sink, *data[2];
} WAVE;

LRESULT APIENTRY WndProc(HWND hWnd, UINT uMsg, WPARAM wPrm, LPARAM lPrm) {
switch (uMsg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;

case WM_NOTIFY:
switch (((NMHDR*)lPrm)->idFrom) {
case 2:
freq = ((NMUPDOWN*)lPrm)->iPos;
break;

case 4:
ampl = ((NMUPDOWN*)lPrm)->iPos;
break;
}
return 0;

default:
break;
}
return DefWindowProc(hWnd, uMsg, wPrm, lPrm);
}

void FillBuf(WAVE *wave, short freq, short ampl, long *phaz) {
int16_t *data = wave->data[wave->swiz ^= 1];
float tone = 1.0 * freq / wave->smpl;
long iter;

for(iter = 0; iter < wave->size; iter++)
data[iter] = ((long)(tone * (iter + *phaz)) & 1)? ampl : -ampl;

*phaz = *phaz + iter;//2.0 * frac(0.5 * tone * (iter + *phaz)) / tone;
}

DWORD APIENTRY WaveFunc(LPVOID data) {
WAVEHDR *whdr;
WAVE *wave;
intptr_t *sink;
long size, phaz = 0;

wave = (WAVE*)data;
whdr = (WAVEHDR*)(sink = wave->sink)[1];
size = wave->chnl * wave->size * (wave->bits >>  3);

whdr[0].dwBufferLength = whdr[1].dwBufferLength = size;
whdr[0].dwFlags        = whdr[1].dwFlags        = 0;
whdr[0].dwLoops        = whdr[1].dwLoops        = 0;
whdr[0].lpData = (LPSTR)(wave->data[0] = calloc(1, size));
whdr[1].lpData = (LPSTR)(wave->data[1] = calloc(1, size));

do {
waveOutUnprepareHeader((HWAVEOUT)sink[0], &whdr[wave->swiz], sizeof(WAVEHDR));
waveOutPrepareHeader((HWAVEOUT)sink[0], &whdr[wave->swiz], sizeof(WAVEHDR));
ResetEvent((HANDLE)sink[2]);
waveOutWrite((HWAVEOUT)sink[0], &whdr[wave->swiz], sizeof(WAVEHDR));
FillBuf(wave, freq, ampl, &phaz);
} while (!WaitForSingleObject((HANDLE)sink[2], INFINITE));
return 0;
}

int APIENTRY WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR args, int show) {
WNDCLASSEX wndc = {sizeof(wndc), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0,
inst, LoadIcon(0, IDI_HAND), LoadCursor(0, IDC_ARROW),
(HBRUSH)(COLOR_BTNFACE + 1), 0, "-", 0};
INITCOMMONCONTROLSEX icct = {sizeof(icct), ICC_STANDARD_CLASSES};
MSG pmsg;

HWND mwnd, cwnd, spin;
DWORD thrd;
WAVEFORMATEX wfmt;
intptr_t sink[3];
WAVE wave = {1, 44100, 16, 4096, 0, sink};

//    AllocConsole();
//    freopen("CONOUT$", "wb", stdout);

InitCommonControlsEx(&icct);
RegisterClassEx(&wndc);
mwnd = CreateWindowEx(0, wndc.lpszClassName, " ",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, 320, 240,
HWND_DESKTOP, 0, wndc.hInstance, 0);

cwnd = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, 0, ES_AUTOHSCROLL
| ES_WANTRETURN | ES_MULTILINE | ES_NUMBER | WS_CHILD
| WS_VISIBLE, 10, 10, 100, 24, mwnd, (HMENU)1, 0, 0);
SendMessage(cwnd, EM_LIMITTEXT, 9, 0);
spin = CreateWindowEx(0, UPDOWN_CLASS, 0, UDS_HOTTRACK | UDS_NOTHOUSANDS
| UDS_ALIGNRIGHT | UDS_SETBUDDYINT | UDS_ARROWKEYS
| WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, mwnd, (HMENU)2, 0, 0);
SendMessage(spin, UDM_SETBUDDY, (WPARAM)cwnd, 0);
SendMessage(spin, UDM_SETRANGE32, (WPARAM)20, (LPARAM)22050);
SendMessage(spin, UDM_SETPOS32, 0, (LPARAM)(freq = 400));

cwnd = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, 0, ES_AUTOHSCROLL
| ES_WANTRETURN | ES_MULTILINE | ES_NUMBER | WS_CHILD
| WS_VISIBLE, 10, 44, 100, 24, mwnd, (HMENU)3, 0, 0);
SendMessage(cwnd, EM_LIMITTEXT, 9, 0);
spin = CreateWindowEx(0, UPDOWN_CLASS, 0, UDS_HOTTRACK | UDS_NOTHOUSANDS
| UDS_ALIGNRIGHT | UDS_SETBUDDYINT | UDS_ARROWKEYS
| WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, mwnd, (HMENU)4, 0, 0);
SendMessage(spin, UDM_SETBUDDY, (WPARAM)cwnd, 0);
SendMessage(spin, UDM_SETRANGE32, (WPARAM)0, (LPARAM)32767);
SendMessage(spin, UDM_SETPOS32, 0, (LPARAM)(ampl = 32767));

wfmt = (WAVEFORMATEX){WAVE_FORMAT_PCM, wave.chnl, wave.smpl,
((wave.chnl * wave.bits) >> 3) * wave.smpl,
(wave.chnl * wave.bits) >> 3, wave.bits};
sink[1] = (intptr_t)calloc(2, sizeof(WAVEHDR));
waveOutOpen((LPHWAVEOUT)&sink[0], WAVE_MAPPER, &wfmt,
sink[2] = (intptr_t)CreateEvent(0, 1, 0, 0), 0,
CALLBACK_EVENT);
SetThreadPriority(CreateThread(0, 0, WaveFunc, &wave, 0, &thrd),
THREAD_PRIORITY_TIME_CRITICAL);

while (pmsg.message != WM_QUIT) {
if (PeekMessage(&pmsg, 0, 0, 0, PM_REMOVE)) {
TranslateMessage(&pmsg);
DispatchMessage(&pmsg);
continue;
}
Sleep(1);
}
waveOutClose((HWAVEOUT)sink[0]);
fclose(stdout);
FreeConsole();

exit(pmsg.wParam);
return 0;
}
[ЧТО НА САМОМ ДЕЛЕ ПРОИЗОШЛО:]

Воспроизведение заикалось из-за того, что в Windows закончились данные в тот самый момент, когда я переключил буферы. Чтобы избежать этого, вы должны предоставить системе ОБА буфера ДО того, как начнется цикл обратной связи, чтобы, когда один из буферов закончит воспроизведение, следующий был уже подготовлен и отправлен, а вы пополните тот, который только что удалился.

И пусть заблудшие души (как я двумя днями ранее) наконец обретут здесь ясность =)

Серьезно, на данный момент это единственная страница на Интернет, где было предложено реально работающее решение, в котором вместо правильного подхода не используются таймеры или что-то еще.

Подробнее здесь: https://stackoverflow.com/questions/496 ... -like-hell
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»