From fc1ea6ee5b5135e70e1a0e9b2737f6189b740691 Mon Sep 17 00:00:00 2001 From: Takamichi Horikawa Date: Thu, 26 Jan 2017 20:32:26 +0900 Subject: refactor win32 sound output and improve stability under load --- win32/dsoundout.c | 77 ++++++++++++++++--------- win32/dsoundout.h | 6 +- win32/main.c | 34 ++++++++++- win32/soundout.c | 41 +++----------- win32/soundout.h | 5 +- win32/waveout.c | 166 +++++++++++++++++++++++++++++++++++++++--------------- win32/waveout.h | 6 +- 7 files changed, 212 insertions(+), 123 deletions(-) (limited to 'win32') diff --git a/win32/dsoundout.c b/win32/dsoundout.c index 0f7ebc4..2581c8a 100644 --- a/win32/dsoundout.c +++ b/win32/dsoundout.c @@ -3,6 +3,7 @@ #include struct dsound_state { + struct sound_state soundout; IDirectSound8 *ds; DWORD sectlen; IDirectSoundBuffer *dsbuf; @@ -69,7 +70,32 @@ static DWORD WINAPI bufupdatethread(LPVOID p) { } } -struct dsound_state *dsound_init(HWND hwnd, unsigned srate, unsigned sectlen, +static void dsound_pause(struct sound_state *state, int pause) { + struct dsound_state *dsound = (struct dsound_state *)state; + if (pause) { + dsound->playing = 0; + WaitForSingleObject(dsound->mtx_cbproc, INFINITE); + ReleaseMutex(dsound->mtx_cbproc); + } else { + dsound->playing = 1; + dsound->dsbuf->lpVtbl->Play(dsound->dsbuf, 0, 0, DSBPLAY_LOOPING); + } +} + +static void dsound_free(struct sound_state *state) { + struct dsound_state *dsound = (struct dsound_state *)state; + dsound_pause(state, 1); + dsound->terminate = 1; + SetEvent(dsound->e_posnotf); + WaitForSingleObject(dsound->t_update, INFINITE); + CloseHandle(dsound->mtx_cbproc); + dsound->dsbuf->lpVtbl->Release(dsound->dsbuf); + dsound->ds->lpVtbl->Release(dsound->ds); + HeapFree(GetProcessHeap(), 0, dsound); +} + + +struct sound_state *dsound_init(HWND hwnd, unsigned srate, unsigned sectlen, sound_callback cbfunc, void *userptr) { HANDLE heap = GetProcessHeap(); struct dsound_state *dsound = HeapAlloc(heap, HEAP_ZERO_MEMORY, @@ -87,7 +113,6 @@ struct dsound_state *dsound_init(HWND hwnd, unsigned srate, unsigned sectlen, &IID_IDirectSound8, (void **)&dsound->ds); if (hr != S_OK) { - MessageBoxW(hwnd, L"cannot create instance of DirectSound8", L"Error", MB_ICONSTOP); goto err_dsound; } hr = dsound->ds->lpVtbl->Initialize(dsound->ds, 0); @@ -127,15 +152,24 @@ struct dsound_state *dsound_init(HWND hwnd, unsigned srate, unsigned sectlen, goto err_dsbuf; } - dsound->e_posnotf = CreateEventW(NULL, FALSE, FALSE, L"SNDNOTF"); + dsound->mtx_cbproc = CreateMutexW(0, FALSE, 0); + if (!dsound->mtx_cbproc) { + goto err_dsnotf; + } + dsound->e_posnotf = CreateEventW(NULL, FALSE, FALSE, 0); + if (!dsound->e_posnotf) { + goto err_mtx; + } dsound->t_update = CreateThread(NULL, 0, bufupdatethread, dsound, 0, NULL); + if (!dsound->t_update) { + goto err_event; + } SetThreadPriority(dsound->t_update, THREAD_PRIORITY_HIGHEST); dsound->sectlen = sectlen; dsound->playing = 0; dsound->terminate = 0; dsound->cbfunc = cbfunc; dsound->userptr = userptr; - dsound->mtx_cbproc = CreateMutexW(0, FALSE, 0); DSBPOSITIONNOTIFY posnotf[4] = { @@ -146,7 +180,17 @@ struct dsound_state *dsound_init(HWND hwnd, unsigned srate, unsigned sectlen, }; dsnotf->lpVtbl->SetNotificationPositions(dsnotf, 4, posnotf); dsnotf->lpVtbl->Release(dsnotf); - return dsound; + dsound->soundout.pause = dsound_pause; + dsound->soundout.free = dsound_free; + dsound->soundout.apiname = L"DirectSound"; + return (struct sound_state *)dsound; + +err_event: + CloseHandle(dsound->e_posnotf); +err_mtx: + CloseHandle(dsound->mtx_cbproc); +err_dsnotf: + dsnotf->lpVtbl->Release(dsnotf); err_dsbuf: dsound->dsbuf->lpVtbl->Release(dsound->dsbuf); err_ds: @@ -156,26 +200,3 @@ err_dsound: err: return 0; } - -void dsound_pause(struct dsound_state *dsound, int pause) { - if (pause) { - dsound->playing = 0; - WaitForSingleObject(dsound->mtx_cbproc, INFINITE); - ReleaseMutex(dsound->mtx_cbproc); - } else { - dsound->playing = 1; - dsound->dsbuf->lpVtbl->Play(dsound->dsbuf, 0, 0, DSBPLAY_LOOPING); - } -} - -void dsound_delete(struct dsound_state *dsound) { - if (!dsound) return; - dsound_pause(dsound, 1); - dsound->terminate = 1; - SetEvent(dsound->e_posnotf); - WaitForSingleObject(dsound->t_update, INFINITE); - CloseHandle(dsound->mtx_cbproc); - dsound->dsbuf->lpVtbl->Release(dsound->dsbuf); - dsound->ds->lpVtbl->Release(dsound->ds); - HeapFree(GetProcessHeap(), 0, dsound); -} diff --git a/win32/dsoundout.h b/win32/dsoundout.h index 10482e6..0a96889 100644 --- a/win32/dsoundout.h +++ b/win32/dsoundout.h @@ -3,11 +3,7 @@ #include "soundout.h" -struct dsound_state; - -struct dsound_state *dsound_init(HWND hwnd, unsigned srate, unsigned sectlen, +struct sound_state *dsound_init(HWND hwnd, unsigned srate, unsigned sectlen, sound_callback cbfunc, void *userptr); -void dsound_delete(struct dsound_state *dsound); -void dsound_pause(struct dsound_state *dsound, int pause); #endif // MYON_DSOUNDOUT_H_INCLUDED diff --git a/win32/main.c b/win32/main.c index d4caf05..07ef1bd 100644 --- a/win32/main.c +++ b/win32/main.c @@ -14,13 +14,14 @@ enum { ID_OPENFILE = 0x10, - TIMER_FMDSP = 1, + ID_PAUSE, }; #define FMPLAYER_CLASSNAME L"myon_fmplayer_ym2608_win32" #define FMPLAYER_CDSTAG 0xFD809800UL enum { + TIMER_FMDSP = 1, SRATE = 55467, SECTLEN = 4096, PPZ8MIX = 0xa000, @@ -46,6 +47,8 @@ static struct { void *drum_rom; uint8_t opna_adpcm_ram[OPNA_ADPCM_RAM_SIZE]; void *ppz8_buf; + bool paused; + HWND driverinfo; } g; @@ -292,10 +295,12 @@ static void openfile(HWND hwnd, const wchar_t *path) { if (!g.sound) { g.sound = sound_init(hwnd, SRATE, SECTLEN, sound_cb, &g.opna_timer); + SetWindowText(g.driverinfo, g.sound->apiname); } fmdsp_vram_init(&g.fmdsp, &g.work, g.vram); if (!g.sound) goto err_fmp; g.sound->pause(g.sound, 0); + g.paused = false; CloseHandle(file); return; err_fmp: @@ -393,11 +398,31 @@ static bool on_create(HWND hwnd, CREATESTRUCT *cs) { 40, 25, hwnd, (HMENU)ID_OPENFILE, g.hinst, 0 ); + HWND pbutton = CreateWindowEx( + 0, + L"BUTTON", + L"&Pause", + WS_TABSTOP | WS_VISIBLE | WS_CHILD, + 55, 10, + 50, 25, + hwnd, (HMENU)ID_PAUSE, g.hinst, 0 + ); + g.driverinfo = CreateWindowEx( + 0, + L"STATIC", + L"", + WS_VISIBLE | WS_CHILD, + 110, 15, + 100, 25, + hwnd, 0, g.hinst, 0 + ); NONCLIENTMETRICS ncm; ncm.cbSize = sizeof(ncm); SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0); HFONT font = CreateFontIndirect(&ncm.lfMessageFont); SetWindowFont(button, font, TRUE); + SetWindowFont(pbutton, font, TRUE); + SetWindowFont(g.driverinfo, font, TRUE); loadrom(); loadfont(); fmdsp_init(&g.fmdsp, g.font_loaded ? &g.font : 0); @@ -416,12 +441,17 @@ static void on_command(HWND hwnd, int id, HWND hwnd_c, UINT code) { case ID_OPENFILE: openfiledialog(hwnd); break; + case ID_PAUSE: + if (g.sound) { + g.paused = !g.paused; + g.sound->pause(g.sound, g.paused); + } } } static void on_destroy(HWND hwnd) { (void)hwnd; - if (g.sound) g.sound->delete(g.sound); + if (g.sound) g.sound->free(g.sound); if (g.fmp) HeapFree(g.heap, 0, g.fmp); if (g.drum_rom) HeapFree(g.heap, 0, g.drum_rom); if (g.ppz8_buf) HeapFree(g.heap, 0, g.ppz8_buf); diff --git a/win32/soundout.c b/win32/soundout.c index bb57bc1..7f04e16 100644 --- a/win32/soundout.c +++ b/win32/soundout.c @@ -2,43 +2,16 @@ #include "dsoundout.h" #include "waveout.h" -static void soundout_dsound_pause(struct sound_state *state, int pause) { - dsound_pause((struct dsound_state *)state->driver_state, pause); -} - -static void soundout_dsound_delete(struct sound_state *state) { - dsound_delete((struct dsound_state *)state->driver_state); - HeapFree(GetProcessHeap(), 0, state); -} - -static void soundout_waveout_pause(struct sound_state *state, int pause) { - waveout_pause((struct waveout_state *)state->driver_state, pause); -} - -static void soundout_waveout_delete(struct sound_state *state) { - waveout_delete((struct waveout_state *)state->driver_state); - HeapFree(GetProcessHeap(), 0, state); -} - struct sound_state *sound_init(HWND hwnd, unsigned srate, unsigned sectlen, sound_callback cbfunc, void *userptr) { - HANDLE heap = GetProcessHeap(); - struct sound_state *sound = HeapAlloc(heap, 0, sizeof(struct sound_state)); - if (!sound) return 0; - struct dsound_state *dsound = dsound_init(hwnd, srate, sectlen, cbfunc, userptr); - if (dsound) { - sound->driver_state = dsound; - sound->pause = soundout_dsound_pause; - sound->delete = soundout_dsound_delete; - return sound; + struct sound_state *state; + state = dsound_init(hwnd, srate, sectlen, cbfunc, userptr); + if (state) { + return state; } - struct waveout_state *waveout = waveout_init(hwnd, srate, sectlen, cbfunc, userptr); - if (waveout) { - sound->driver_state = waveout; - sound->pause = soundout_waveout_pause; - sound->delete = soundout_waveout_delete; - return sound; + state = waveout_init(hwnd, srate, sectlen, cbfunc, userptr); + if (state) { + return state; } - HeapFree(heap, 0, sound); return 0; } diff --git a/win32/soundout.h b/win32/soundout.h index edfa501..93b0286 100644 --- a/win32/soundout.h +++ b/win32/soundout.h @@ -6,10 +6,9 @@ typedef void (*sound_callback)(void *userdata, int16_t *buf, unsigned frames); struct sound_state { - void *driver_state; void (*pause)(struct sound_state *state, int pause); - void (*delete)(struct sound_state *state); - void *userptr; + void (*free)(struct sound_state *state); + const wchar_t *apiname; }; struct sound_state *sound_init(HWND hwnd, unsigned srate, unsigned sectlen, diff --git a/win32/waveout.c b/win32/waveout.c index 9c96483..8827eec 100644 --- a/win32/waveout.c +++ b/win32/waveout.c @@ -1,27 +1,98 @@ #include "waveout.h" +enum { + HDRCNT = 4 +}; + struct waveout_state { + struct sound_state soundout; HWAVEOUT wo; + HANDLE t_update; + HANDLE e_waveout; + HANDLE mtx_cbproc; DWORD firstout; + DWORD paused; + DWORD terminate; sound_callback cbfunc; void *userptr; - WAVEHDR waveheaders[4]; + WAVEHDR waveheaders[HDRCNT]; + DWORD nextout; int16_t *wavebuf; }; -static void CALLBACK waveout_cbproc(HWAVEOUT wo, UINT msg, - DWORD_PTR instance, - DWORD_PTR p1, DWORD_PTR p2) { - (void)wo; - (void)p2; - if (msg != WOM_DONE) return; - struct waveout_state *waveout = (struct waveout_state *)instance; - WAVEHDR *wh = (WAVEHDR *)p1; - waveout->cbfunc(waveout->userptr, (int16_t *)wh->lpData, wh->dwBufferLength/4); - waveOutWrite(waveout->wo, wh, sizeof(*wh)); +static DWORD CALLBACK bufupdatethread(void *p) { + struct waveout_state *waveout = (struct waveout_state *)p; + for (;;) { + WaitForSingleObject(waveout->e_waveout, INFINITE); + if (waveout->terminate) ExitThread(0); + int i, whi; + for (i = 0;; i = (i+1)%HDRCNT) { + whi = (i + waveout->nextout) % HDRCNT; + WAVEHDR *wh = &waveout->waveheaders[whi]; + if (!(wh->dwFlags & WHDR_DONE)) break; + //wh->dwFlags &= ~WHDR_DONE; + + if (WaitForSingleObject(waveout->mtx_cbproc, 0) == WAIT_OBJECT_0) { + if (!waveout->paused) { + waveout->cbfunc(waveout->userptr, + (int16_t *)wh->lpData, wh->dwBufferLength/4); + } else { + ZeroMemory(wh->lpData, wh->dwBufferLength); + } + ReleaseMutex(waveout->mtx_cbproc); + } else { + ZeroMemory(wh->lpData, wh->dwBufferLength); + } + waveOutWrite(waveout->wo, wh, sizeof(*wh)); + } + waveout->nextout = whi; + } } -struct waveout_state *waveout_init(HWND hwnd, unsigned srate, unsigned sectlen, +static void waveout_pause(struct sound_state *state, int pause) { + struct waveout_state *waveout = (struct waveout_state *)state; + if (pause) { + WaitForSingleObject(waveout->mtx_cbproc, INFINITE); + waveout->paused = 1; + ReleaseMutex(waveout->mtx_cbproc); + } else { + if (waveout->firstout) { + WaitForSingleObject(waveout->mtx_cbproc, INFINITE); + waveout->firstout = 0; + waveout->paused = 0; + waveOutReset(waveout->wo); + for (int i = 0; i < HDRCNT; i++) { + WAVEHDR *wh = &waveout->waveheaders[i]; + waveout->cbfunc(waveout->userptr, + (int16_t *)wh->lpData, wh->dwBufferLength/4); + waveOutWrite(waveout->wo, wh, sizeof(*wh)); + } + ReleaseMutex(waveout->mtx_cbproc); + } else { + waveout->paused = 0; + } + } +} + +static void waveout_free(struct sound_state *state) { + struct waveout_state *waveout = (struct waveout_state *)state; + waveout->terminate = 1; + SetEvent(waveout->e_waveout); + WaitForSingleObject(waveout->t_update, INFINITE); + waveOutReset(waveout->wo); + for (int i = 0; i < HDRCNT; i++) { + WAVEHDR *wh = &waveout->waveheaders[i]; + waveOutUnprepareHeader(waveout->wo, wh, sizeof(*wh)); + } + waveOutClose(waveout->wo); + CloseHandle(waveout->mtx_cbproc); + CloseHandle(waveout->e_waveout); + HANDLE heap = GetProcessHeap(); + HeapFree(heap, 0, waveout->wavebuf); + HeapFree(heap, 0, waveout); +} + +struct sound_state *waveout_init(HWND hwnd, unsigned srate, unsigned sectlen, sound_callback cbfunc, void *userptr) { HANDLE heap = GetProcessHeap(); struct waveout_state *waveout = HeapAlloc(heap, HEAP_ZERO_MEMORY, @@ -30,10 +101,20 @@ struct waveout_state *waveout_init(HWND hwnd, unsigned srate, unsigned sectlen, MessageBoxW(hwnd, L"cannot allocate memory for WaveOut", L"Error", MB_ICONSTOP); goto err; } - waveout->wavebuf = HeapAlloc(heap, HEAP_ZERO_MEMORY, sectlen*4); + waveout->e_waveout = CreateEventW(0, FALSE, FALSE, 0); + if (!waveout->e_waveout) { + MessageBoxW(hwnd, L"cannot create event for WaveOut", L"Error", MB_ICONSTOP); + goto err_waveout; + } + waveout->mtx_cbproc = CreateMutexW(0, FALSE, 0); + if (!waveout->mtx_cbproc) { + MessageBoxW(hwnd, L"cannot create mutex for WaveOut", L"Error", MB_ICONSTOP); + goto err_event; + } + waveout->wavebuf = HeapAlloc(heap, HEAP_ZERO_MEMORY, sectlen*HDRCNT); if (!waveout->wavebuf) { MessageBoxW(hwnd, L"cannot allocate buffer memory for WaveOut", L"Error", MB_ICONSTOP); - goto err_waveout; + goto err_mtx; } WAVEFORMATEX format; format.wFormatTag = WAVE_FORMAT_PCM; @@ -45,12 +126,12 @@ struct waveout_state *waveout_init(HWND hwnd, unsigned srate, unsigned sectlen, format.cbSize = 0; HRESULT hr; hr = waveOutOpen(&waveout->wo, WAVE_MAPPER, &format, - (DWORD_PTR)waveout_cbproc, (DWORD_PTR)waveout, CALLBACK_FUNCTION); + (DWORD_PTR)waveout->e_waveout, 0, CALLBACK_EVENT); if (hr != MMSYSERR_NOERROR) { MessageBoxW(hwnd, L"cannot WaveOutOpen", L"Error", MB_ICONSTOP); goto err_wavebuf; } - for (int i = 0; i < 4; i++) { + for (int i = 0; i < HDRCNT; i++) { WAVEHDR *wh = &waveout->waveheaders[i]; wh->lpData = ((char *)waveout->wavebuf) + sectlen*i; wh->dwBufferLength = sectlen; @@ -58,43 +139,36 @@ struct waveout_state *waveout_init(HWND hwnd, unsigned srate, unsigned sectlen, waveOutPrepareHeader(waveout->wo, wh, sizeof(*wh)); } waveout->firstout = 1; + waveout->paused = 1; + waveout->terminate = 0; waveout->cbfunc = cbfunc; waveout->userptr = userptr; - return waveout; - -err_wavebuf: - HeapFree(heap, 0, waveout->wavebuf); -err_waveout: - HeapFree(heap, 0, waveout); -err: - return 0; -} - -void waveout_pause(struct waveout_state *waveout, int pause) { - if (pause) { - waveOutPause(waveout->wo); - } else { - if (waveout->firstout) { - waveout->firstout = 0; - for (int i = 0; i < 4; i++) { - WAVEHDR *wh = &waveout->waveheaders[i]; - waveout->cbfunc(waveout->userptr, (int16_t *)wh->lpData, wh->dwBufferLength/4); - waveOutWrite(waveout->wo, wh, sizeof(*wh)); - } - } else { - waveOutRestart(waveout->wo); - } + waveout->t_update = CreateThread(0, 0, bufupdatethread, waveout, 0, 0); + if (!waveout->t_update) { + MessageBoxW(hwnd, L"cannot create thread for Waveout", L"Error", MB_ICONSTOP); + goto err_waveoutopen; } -} + SetThreadPriority(waveout->t_update, THREAD_PRIORITY_HIGHEST); + waveout->soundout.pause = waveout_pause; + waveout->soundout.free = waveout_free; + waveout->soundout.apiname = L"WinMM"; + return (struct sound_state *)waveout; -void waveout_delete(struct waveout_state *waveout) { - if (!waveout) return; - waveout_pause(waveout, 1); - for (int i = 0; i < 4; i++) { +err_waveoutopen: + waveOutReset(waveout->wo); + for (int i = 0; i < HDRCNT; i++) { WAVEHDR *wh = &waveout->waveheaders[i]; waveOutUnprepareHeader(waveout->wo, wh, sizeof(*wh)); } - HANDLE heap = GetProcessHeap(); + waveOutClose(waveout->wo); +err_wavebuf: HeapFree(heap, 0, waveout->wavebuf); +err_mtx: + CloseHandle(waveout->mtx_cbproc); +err_event: + CloseHandle(waveout->e_waveout); +err_waveout: HeapFree(heap, 0, waveout); +err: + return 0; } diff --git a/win32/waveout.h b/win32/waveout.h index ff33b01..d8c4c3a 100644 --- a/win32/waveout.h +++ b/win32/waveout.h @@ -3,11 +3,7 @@ #include "soundout.h" -struct waveout_state; - -struct waveout_state *waveout_init(HWND hwnd, unsigned srate, unsigned sectlen, +struct sound_state *waveout_init(HWND hwnd, unsigned srate, unsigned sectlen, sound_callback cbfunc, void *userptr); -void waveout_delete(struct waveout_state *waveout); -void waveout_pause(struct waveout_state *waveout, int pause); #endif // MYON_WAVEOUT_H_INCLUDED -- cgit v1.2.3