summaryrefslogtreecommitdiff
path: root/Peak/src/p_win32.c
diff options
context:
space:
mode:
Diffstat (limited to 'Peak/src/p_win32.c')
-rw-r--r--Peak/src/p_win32.c391
1 files changed, 332 insertions, 59 deletions
diff --git a/Peak/src/p_win32.c b/Peak/src/p_win32.c
index c1a289b..cf99b6c 100644
--- a/Peak/src/p_win32.c
+++ b/Peak/src/p_win32.c
@@ -1,40 +1,65 @@
+/*
+ * Win32 window, input, StretchDIBits present, and waveOut audio.
+ * user32.dll, gdi32.dll, and winmm.dll are loaded at runtime.
+ *
+ * * 0.4.0 - @vasco - win32
+ * * 0.5.0 - @vasco - audio start/stop, s16le pull callback
+ */
+
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
+/* windows.h must precede mmsystem.h */
#include <windows.h>
+#include <mmsystem.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#ifdef PEAK_VULKAN
+#define VK_USE_PLATFORM_WIN32_KHR
+#include <vulkan/vulkan.h>
+#endif
#define PEAK_WIN32_USER32 "user32.dll"
#define PEAK_WIN32_GDI32 "gdi32.dll"
+#define PEAK_WIN32_WINMM "winmm.dll"
#define PEAK_WIN32_CLASS "PeakWindow"
#define PEAK_WIN32_PROP "Peak"
+#define PEAK_AUDIO_FRAMES 256
+#define PEAK_AUDIO_BUFFERS 2
#define PEAK_USER32_API(X) \
- X(RegisterClassExA, ATOM, WINAPI, (const WNDCLASSEXA *)) \
- X(UnregisterClassA, BOOL, WINAPI, (LPCSTR, HINSTANCE)) \
- X(CreateWindowExA, HWND, WINAPI, (DWORD, LPCSTR, LPCSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID)) \
- X(DestroyWindow, BOOL, WINAPI, (HWND)) \
- X(ShowWindow, BOOL, WINAPI, (HWND, int)) \
- X(GetDC, HDC, WINAPI, (HWND)) \
- X(ReleaseDC, int, WINAPI, (HWND, HDC)) \
- X(PeekMessageA, BOOL, WINAPI, (LPMSG, HWND, UINT, UINT, UINT)) \
- X(TranslateMessage, BOOL, WINAPI, (const MSG *)) \
- X(DispatchMessageA, LRESULT, WINAPI, (const MSG *)) \
- X(DefWindowProcA, LRESULT, WINAPI, (HWND, UINT, WPARAM, LPARAM)) \
- X(SetPropA, BOOL, WINAPI, (HWND, LPCSTR, HANDLE)) \
- X(GetPropA, HANDLE, WINAPI, (HWND, LPCSTR)) \
- X(RemovePropA, HANDLE, WINAPI, (HWND, LPCSTR)) \
- X(BeginPaint, HDC, WINAPI, (HWND, LPPAINTSTRUCT)) \
- X(EndPaint, BOOL, WINAPI, (HWND, const PAINTSTRUCT *)) \
- X(AdjustWindowRectEx, BOOL, WINAPI, (LPRECT, DWORD, BOOL, DWORD)) \
- X(LoadCursorA, HCURSOR, WINAPI, (HINSTANCE, LPCSTR)) \
- X(GetKeyState, SHORT, WINAPI, (int))
+ X(RegisterClassExA, ATOM, WINAPI, (const WNDCLASSEXA *)) \
+ X(UnregisterClassA, BOOL, WINAPI, (LPCSTR, HINSTANCE)) \
+ X(CreateWindowExA, HWND, WINAPI, (DWORD, LPCSTR, LPCSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID)) \
+ X(DestroyWindow, BOOL, WINAPI, (HWND)) \
+ X(ShowWindow, BOOL, WINAPI, (HWND, int)) \
+ X(GetDC, HDC, WINAPI, (HWND)) \
+ X(ReleaseDC, int, WINAPI, (HWND, HDC)) \
+ X(PeekMessageA, BOOL, WINAPI, (LPMSG, HWND, UINT, UINT, UINT)) \
+ X(TranslateMessage, BOOL, WINAPI, (const MSG *)) \
+ X(DispatchMessageA, LRESULT, WINAPI, (const MSG *)) \
+ X(DefWindowProcA, LRESULT, WINAPI, (HWND, UINT, WPARAM, LPARAM)) \
+ X(SetPropA, BOOL, WINAPI, (HWND, LPCSTR, HANDLE)) \
+ X(GetPropA, HANDLE, WINAPI, (HWND, LPCSTR)) \
+ X(RemovePropA, HANDLE, WINAPI, (HWND, LPCSTR)) \
+ X(BeginPaint, HDC, WINAPI, (HWND, LPPAINTSTRUCT)) \
+ X(EndPaint, BOOL, WINAPI, (HWND, const PAINTSTRUCT *)) \
+ X(AdjustWindowRectEx, BOOL, WINAPI, (LPRECT, DWORD, BOOL, DWORD)) \
+ X(LoadCursorA, HCURSOR, WINAPI, (HINSTANCE, LPCSTR)) \
+ X(GetKeyState, SHORT, WINAPI, (int))
#define PEAK_GDI32_API(X) \
- X(StretchDIBits, int, WINAPI, (HDC, int, int, int, int, int, int, int, int, const void *, const BITMAPINFO *, UINT, DWORD))
+ X(StretchDIBits, int, WINAPI, (HDC, int, int, int, int, int, int, int, int, const void *, const BITMAPINFO *, UINT, DWORD))
+
+#define PEAK_WINMM_API(X) \
+ X(waveOutOpen, MMRESULT, WINAPI, (LPHWAVEOUT, UINT, LPCWAVEFORMATEX, DWORD_PTR, DWORD_PTR, DWORD)) \
+ X(waveOutClose, MMRESULT, WINAPI, (HWAVEOUT)) \
+ X(waveOutPrepareHeader, MMRESULT, WINAPI, (HWAVEOUT, LPWAVEHDR, UINT)) \
+ X(waveOutUnprepareHeader, MMRESULT, WINAPI, (HWAVEOUT, LPWAVEHDR, UINT)) \
+ X(waveOutWrite, MMRESULT, WINAPI, (HWAVEOUT, LPWAVEHDR, UINT)) \
+ X(waveOutReset, MMRESULT, WINAPI, (HWAVEOUT))
typedef struct {
#define X(name, ret, conv, args) ret (conv *name) args;
@@ -49,6 +74,25 @@ typedef struct {
} PeakGdi32Api;
typedef struct {
+#define X(name, ret, conv, args) ret (conv *name) args;
+ PEAK_WINMM_API(X)
+#undef X
+} PeakWinmmApi;
+
+typedef struct {
+ volatile int run;
+ HANDLE thread;
+ HANDLE event;
+ HWAVEOUT out;
+ WAVEHDR hdr[PEAK_AUDIO_BUFFERS];
+ int16_t *pcm[PEAK_AUDIO_BUFFERS];
+ uint32_t channels;
+ uint32_t bytes;
+ void (*fill)(int16_t *out, size_t frames, void *userdata);
+ void *userdata;
+} PeakAudio;
+
+typedef struct {
HMODULE user32;
HMODULE gdi32;
int class_reg;
@@ -67,9 +111,28 @@ struct peak_window_internal_t {
struct peak_win32_win *w;
};
+static int peak_internal_user32_load(HMODULE handle);
+static int peak_internal_gdi32_load(HMODULE handle);
+static PeakKeyCode peak_internal_win32_key_map(WPARAM vk);
+static PeakKeyMod peak_internal_win32_mod_map(void);
+static int peak_internal_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height);
+static LRESULT CALLBACK peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+static int peak_platform_init(void);
+static void peak_platform_quit(void);
+static PeakWindowInternal peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags);
+static void peak_platform_window_close(PeakWindowInternal *intern);
+static uint32_t *peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height);
+static void peak_platform_window_present(PeakWindowInternal *intern);
+static bool peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev);
+static int peak_internal_winmm_load(void);
+static void peak_internal_win32_audio_fill(int i);
+static DWORD WINAPI peak_internal_win32_audio_thread(LPVOID arg);
+
static PeakWin32 peak_win32;
static PeakUser32Api peak_user32;
static PeakGdi32Api peak_gdi32;
+static PeakWinmmApi peak_winmm;
+static PeakAudio peak_audio;
static int
peak_internal_user32_load(HMODULE handle)
@@ -103,34 +166,45 @@ peak_internal_win32_key_map(WPARAM vk)
if (vk >= 'A' && vk <= 'Z')
return (PeakKeyCode)(PEAK_KEY_A + (int)(vk - 'A'));
switch (vk) {
- case VK_UP: return PEAK_KEY_UP;
- case VK_DOWN: return PEAK_KEY_DOWN;
- case VK_LEFT: return PEAK_KEY_LEFT;
- case VK_RIGHT: return PEAK_KEY_RIGHT;
- case VK_SPACE: return PEAK_KEY_SPACE;
- case VK_ESCAPE: return PEAK_KEY_ESCAPE;
- case VK_RETURN: return PEAK_KEY_ENTER;
- default: return PEAK_KEY_UNKNOWN;
+ case VK_UP:
+ return PEAK_KEY_UP;
+ case VK_DOWN:
+ return PEAK_KEY_DOWN;
+ case VK_LEFT:
+ return PEAK_KEY_LEFT;
+ case VK_RIGHT:
+ return PEAK_KEY_RIGHT;
+ case VK_SPACE:
+ return PEAK_KEY_SPACE;
+ case VK_ESCAPE:
+ return PEAK_KEY_ESCAPE;
+ case VK_RETURN:
+ return PEAK_KEY_ENTER;
+ default:
+ return PEAK_KEY_UNKNOWN;
}
}
static PeakKeyMod
peak_internal_win32_mod_map(void)
{
- if (peak_user32.GetKeyState(VK_CONTROL) & 0x8000) return PEAK_KEYMOD_CTRL;
- if (peak_user32.GetKeyState(VK_MENU) & 0x8000) return PEAK_KEYMOD_ALT;
- if (peak_user32.GetKeyState(VK_SHIFT) & 0x8000) return PEAK_KEYMOD_SHIFT;
- if (peak_user32.GetKeyState(VK_CAPITAL) & 1) return PEAK_KEYMOD_CAPS;
+ if (peak_user32.GetKeyState(VK_CONTROL) & 0x8000)
+ return PEAK_KEYMOD_CTRL;
+ if (peak_user32.GetKeyState(VK_MENU) & 0x8000)
+ return PEAK_KEYMOD_ALT;
+ if (peak_user32.GetKeyState(VK_SHIFT) & 0x8000)
+ return PEAK_KEYMOD_SHIFT;
+ if (peak_user32.GetKeyState(VK_CAPITAL) & 1)
+ return PEAK_KEYMOD_CAPS;
return (PeakKeyMod)0;
}
static int
-peak_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height)
+peak_internal_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height)
{
uint32_t *buffer;
- buffer = calloc((size_t)width * height, sizeof *buffer);
- if (!buffer)
+ if (!(buffer = calloc((size_t)width * height, sizeof(*buffer))))
return 0;
free(w->buffer);
w->buffer = buffer;
@@ -144,9 +218,10 @@ peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
struct peak_win32_win *w;
PeakEvent ev;
+ CREATESTRUCTA *cs;
if (msg == WM_NCCREATE) {
- CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam;
+ cs = (CREATESTRUCTA *)lparam;
peak_user32.SetPropA(hwnd, PEAK_WIN32_PROP, cs->lpCreateParams);
}
@@ -156,45 +231,46 @@ peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
switch (msg) {
case WM_CLOSE:
- memset(&ev, 0, sizeof ev);
+ memset(&ev, 0, sizeof(ev));
ev.type = PEAK_EVENT_WINDOW_CLOSE;
peak_q_push(&w->q, ev);
return 0;
case WM_SIZE: {
uint32_t width, height;
+
if (wparam == SIZE_MINIMIZED)
return 0;
width = (uint32_t)LOWORD(lparam);
height = (uint32_t)HIWORD(lparam);
if (!width || !height || (width == w->width && height == w->height))
return 0;
- if (!peak_win32_buffer(w, width, height))
+ if (!peak_internal_win32_buffer(w, width, height))
return 0;
- memset(&ev, 0, sizeof ev);
+ memset(&ev, 0, sizeof(ev));
ev.type = PEAK_EVENT_WINDOW_RESIZE;
ev.resize.width = w->width;
ev.resize.height = w->height;
peak_q_push(&w->q, ev);
return 0;
}
- case WM_KEYDOWN:
- case WM_KEYUP:
- case WM_SYSKEYDOWN:
+ case WM_KEYDOWN: /* FALLTHROUGH */
+ case WM_KEYUP: /* FALLTHROUGH */
+ case WM_SYSKEYDOWN: /* FALLTHROUGH */
case WM_SYSKEYUP:
- memset(&ev, 0, sizeof ev);
+ memset(&ev, 0, sizeof(ev));
ev.type = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP;
ev.key.key = peak_internal_win32_key_map(wparam);
ev.key.mod = peak_internal_win32_mod_map();
peak_q_push(&w->q, ev);
return 0;
- case WM_MOUSEMOVE:
- case WM_LBUTTONDOWN:
- case WM_LBUTTONUP:
- case WM_RBUTTONDOWN:
- case WM_RBUTTONUP:
- case WM_MBUTTONDOWN:
+ case WM_MOUSEMOVE: /* FALLTHROUGH */
+ case WM_LBUTTONDOWN: /* FALLTHROUGH */
+ case WM_LBUTTONUP: /* FALLTHROUGH */
+ case WM_RBUTTONDOWN: /* FALLTHROUGH */
+ case WM_RBUTTONUP: /* FALLTHROUGH */
+ case WM_MBUTTONDOWN: /* FALLTHROUGH */
case WM_MBUTTONUP:
- memset(&ev, 0, sizeof ev);
+ memset(&ev, 0, sizeof(ev));
ev.type = PEAK_EVENT_POINTER;
ev.pointer.x = (float)(short)LOWORD(lparam);
ev.pointer.y = (float)(short)HIWORD(lparam);
@@ -215,6 +291,7 @@ peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
return 0;
case WM_PAINT: {
PAINTSTRUCT ps;
+
peak_user32.BeginPaint(hwnd, &ps);
peak_user32.EndPaint(hwnd, &ps);
return 0;
@@ -255,8 +332,8 @@ peak_platform_init(void)
}
}
if (!peak_win32.class_reg) {
- memset(&wc, 0, sizeof wc);
- wc.cbSize = sizeof wc;
+ memset(&wc, 0, sizeof(wc));
+ wc.cbSize = sizeof(wc);
wc.style = CS_OWNDC;
wc.lpfnWndProc = peak_internal_win32_wndproc;
wc.hInstance = GetModuleHandleA(NULL);
@@ -293,10 +370,9 @@ peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uin
if (!peak_user32.CreateWindowExA && !peak_platform_init())
return intern;
- w = calloc(1, sizeof *w);
- if (!w)
+ if (!(w = calloc(1, sizeof(*w))))
return intern;
- if (!peak_win32_buffer(w, width, height)) {
+ if (!peak_internal_win32_buffer(w, width, height)) {
free(w);
return intern;
}
@@ -335,6 +411,7 @@ static void
peak_platform_window_close(PeakWindowInternal *intern)
{
struct peak_win32_win *w;
+
if (!intern || !intern->w)
return;
w = intern->w;
@@ -352,7 +429,9 @@ peak_platform_window_close(PeakWindowInternal *intern)
static uint32_t *
peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height)
{
- struct peak_win32_win *w = intern ? intern->w : NULL;
+ struct peak_win32_win *w;
+
+ w = intern ? intern->w : NULL;
if (!w) {
*width = 0;
*height = 0;
@@ -366,13 +445,14 @@ peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *h
static void
peak_platform_window_present(PeakWindowInternal *intern)
{
- struct peak_win32_win *w = intern ? intern->w : NULL;
+ struct peak_win32_win *w;
BITMAPINFO bmi;
+ w = intern ? intern->w : NULL;
if (!w || !w->hwnd || !w->hdc || !w->buffer)
return;
- memset(&bmi, 0, sizeof bmi);
+ memset(&bmi, 0, sizeof(bmi));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = (LONG)w->width;
bmi.bmiHeader.biHeight = -(LONG)w->height;
@@ -389,8 +469,9 @@ static bool
peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev)
{
MSG msg;
- struct peak_win32_win *w = intern ? intern->w : NULL;
+ struct peak_win32_win *w;
+ w = intern ? intern->w : NULL;
if (!w || !w->hwnd)
return 0;
@@ -402,3 +483,195 @@ peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev)
}
return peak_q_pop(&w->q, ev);
}
+
+static int
+peak_internal_winmm_load(void)
+{
+ HMODULE handle;
+
+ if (peak_winmm.waveOutOpen)
+ return 1;
+ if (!(handle = LoadLibraryA(PEAK_WIN32_WINMM))) {
+ fputs("Failed to load winmm.dll. What system are you fucking using and abusing?", stderr);
+ return 0;
+ }
+#define X(name, ret, conv, args) peak_winmm.name = (ret (conv *) args)(void *)GetProcAddress(handle, #name);
+ PEAK_WINMM_API(X)
+#undef X
+#define X(name, ret, conv, args) || !peak_winmm.name
+ if (0 PEAK_WINMM_API(X)) {
+ fputs("Failed to load winmm symbols", stderr);
+ return 0;
+ }
+#undef X
+ return 1;
+}
+
+static void
+peak_internal_win32_audio_fill(int i)
+{
+ memset(peak_audio.pcm[i], 0, peak_audio.bytes);
+ if (peak_audio.fill)
+ peak_audio.fill(peak_audio.pcm[i], PEAK_AUDIO_FRAMES, peak_audio.userdata);
+ peak_winmm.waveOutWrite(peak_audio.out, &peak_audio.hdr[i], sizeof(WAVEHDR));
+}
+
+static DWORD WINAPI
+peak_internal_win32_audio_thread(LPVOID arg)
+{
+ int i, did;
+
+ (void)arg;
+ for (;;) {
+ did = 0;
+ if (!peak_audio.run)
+ break;
+ for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) {
+ if (peak_audio.hdr[i].dwFlags & WHDR_DONE) {
+ peak_internal_win32_audio_fill(i);
+ did = 1;
+ }
+ }
+ if (did)
+ continue;
+ WaitForSingleObject(peak_audio.event, INFINITE);
+ ResetEvent(peak_audio.event);
+ }
+ return 0;
+}
+
+static int
+peak_platform_audio_start(uint32_t channels, uint32_t rate, void (*fill)(int16_t *out, size_t frames, void *userdata), void *userdata)
+{
+ WAVEFORMATEX fmt;
+ int i;
+
+ if (channels > 32)
+ return 0;
+ if (!peak_internal_winmm_load())
+ return 0;
+
+ memset(&fmt, 0, sizeof(fmt));
+ fmt.wFormatTag = WAVE_FORMAT_PCM;
+ fmt.nChannels = (WORD)channels;
+ fmt.nSamplesPerSec = rate;
+ fmt.wBitsPerSample = 16;
+ fmt.nBlockAlign = (WORD)(channels * 2);
+ fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign;
+
+ peak_audio.event = CreateEventA(NULL, TRUE, FALSE, NULL);
+ if (!peak_audio.event)
+ return 0;
+ if (peak_winmm.waveOutOpen(&peak_audio.out, WAVE_MAPPER, &fmt, (DWORD_PTR)peak_audio.event, 0, CALLBACK_EVENT) != MMSYSERR_NOERROR) {
+ CloseHandle(peak_audio.event);
+ peak_audio.event = NULL;
+ fputs("Failed to open waveOut device", stderr);
+ return 0;
+ }
+
+ peak_audio.channels = channels;
+ peak_audio.bytes = (uint32_t)channels * PEAK_AUDIO_FRAMES * sizeof(int16_t);
+ peak_audio.fill = fill;
+ peak_audio.userdata = userdata;
+ peak_audio.run = 1;
+
+ for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) {
+ if (!(peak_audio.pcm[i] = calloc(1, peak_audio.bytes)))
+ goto fail;
+ memset(&peak_audio.hdr[i], 0, sizeof(peak_audio.hdr[i]));
+ peak_audio.hdr[i].lpData = (LPSTR)peak_audio.pcm[i];
+ peak_audio.hdr[i].dwBufferLength = peak_audio.bytes;
+ if (peak_winmm.waveOutPrepareHeader(peak_audio.out, &peak_audio.hdr[i], sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
+ goto fail;
+ peak_audio.hdr[i].dwFlags |= WHDR_DONE;
+ }
+
+ for (i = 0; i < PEAK_AUDIO_BUFFERS; i++)
+ peak_internal_win32_audio_fill(i);
+
+ peak_audio.thread = CreateThread(NULL, 0, peak_internal_win32_audio_thread, NULL, 0, NULL);
+ if (!peak_audio.thread)
+ goto fail;
+ return 1;
+
+fail:
+ peak_platform_audio_stop();
+ return 0;
+}
+
+static uint64_t
+peak_platform_get_time(void)
+{
+ LARGE_INTEGER count, freq;
+ QueryPerformanceCounter(&count);
+ QueryPerformanceFrequency(&freq);
+ return (uint64_t)((count.QuadPart * 1000000000ull) / freq.QuadPart);
+}
+
+static void
+peak_platform_sleep_ns(int64_t ns)
+{
+ if (ns <= 0) return;
+ Sleep((DWORD)(ns / 1000000));
+}
+
+static const char **
+peak_platform_vulkan_get_extensions(uint32_t *count)
+{
+ static const char *exts[] = {
+ "VK_KHR_surface",
+ "VK_KHR_win32_surface",
+ };
+ if (count) *count = 2;
+ return exts;
+}
+
+static int
+peak_platform_vulkan_create_surface(PeakWindowInternal *intern, void *instance, const void *allocator, void *out_surface)
+{
+#ifdef PEAK_VULKAN
+ VkWin32SurfaceCreateInfoKHR ci;
+ if (!intern || !intern->w || !intern->w->hwnd) return 0;
+ memset(&ci, 0, sizeof ci);
+ ci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
+ ci.hwnd = intern->w->hwnd;
+ ci.hinstance = GetModuleHandleA(NULL);
+ return vkCreateWin32SurfaceKHR((VkInstance)instance, &ci, (const VkAllocationCallbacks *)allocator, (VkSurfaceKHR *)out_surface) == VK_SUCCESS;
+#else
+ (void)intern; (void)instance; (void)allocator; (void)out_surface;
+ return 0;
+#endif
+}
+
+static void
+peak_platform_audio_stop(void)
+{
+ int i;
+
+ peak_audio.run = 0;
+ if (peak_audio.event)
+ SetEvent(peak_audio.event);
+ if (peak_audio.thread) {
+ WaitForSingleObject(peak_audio.thread, INFINITE);
+ CloseHandle(peak_audio.thread);
+ peak_audio.thread = NULL;
+ }
+ if (peak_audio.out) {
+ peak_winmm.waveOutReset(peak_audio.out);
+ for (i = 0; i < PEAK_AUDIO_BUFFERS; i++) {
+ if (peak_audio.hdr[i].dwFlags & WHDR_PREPARED)
+ peak_winmm.waveOutUnprepareHeader(peak_audio.out, &peak_audio.hdr[i], sizeof(WAVEHDR));
+ free(peak_audio.pcm[i]);
+ peak_audio.pcm[i] = NULL;
+ memset(&peak_audio.hdr[i], 0, sizeof(peak_audio.hdr[i]));
+ }
+ peak_winmm.waveOutClose(peak_audio.out);
+ peak_audio.out = NULL;
+ }
+ if (peak_audio.event) {
+ CloseHandle(peak_audio.event);
+ peak_audio.event = NULL;
+ }
+ peak_audio.fill = NULL;
+ peak_audio.userdata = NULL;
+}