aboutsummaryrefslogtreecommitdiff
path: root/win32/waveout.c
diff options
context:
space:
mode:
authorTakamichi Horikawa <takamichiho@gmail.com>2017-01-26 20:32:26 +0900
committerTakamichi Horikawa <takamichiho@gmail.com>2017-01-26 20:32:26 +0900
commitfc1ea6ee5b5135e70e1a0e9b2737f6189b740691 (patch)
tree53f206e2f77e3450f8c0def24c488ef19ac3a72d /win32/waveout.c
parent49ecf760ec6e8b145850be6f3b13020502e10eb4 (diff)
refactor win32 sound output and improve stability under load
Diffstat (limited to 'win32/waveout.c')
-rw-r--r--win32/waveout.c166
1 files changed, 120 insertions, 46 deletions
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;
}