Mercurial > codedump
comparison win95kggui/dep/ft2play/audiodrivers/winmm/winmm.c @ 126:8e4ee43d3b81
remove submodules
author | Paper <mrpapersonic@gmail.com> |
---|---|
date | Sun, 01 Oct 2023 03:48:43 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
125:5cc85ef3a675 | 126:8e4ee43d3b81 |
---|---|
1 /* winmm audio driver for ft2play | |
2 ** | |
3 ** Warning: This might not be 100% thread-safe or lock-safe! | |
4 */ | |
5 | |
6 #define WIN32_LEAN_AND_MEAN | |
7 | |
8 #include <stdint.h> | |
9 #include <stdbool.h> | |
10 #include <stdlib.h> | |
11 #include <windows.h> | |
12 #include <mmsystem.h> | |
13 #include "../../pmp_mix.h" | |
14 | |
15 #define MIX_BUF_NUM 4 | |
16 | |
17 static volatile BOOL mixerOpened, mixerBusy, mixerLocked; | |
18 static uint8_t currBuffer; | |
19 static int16_t *audioBuffer[MIX_BUF_NUM]; | |
20 static int32_t bufferSize; | |
21 static HANDLE hThread, hAudioSem; | |
22 static WAVEHDR waveBlocks[MIX_BUF_NUM]; | |
23 static HWAVEOUT hWave; | |
24 | |
25 static DWORD WINAPI mixThread(LPVOID lpParam) | |
26 { | |
27 (void)lpParam; | |
28 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); | |
29 while (mixerOpened) | |
30 { | |
31 WAVEHDR *waveBlock = &waveBlocks[currBuffer]; | |
32 | |
33 if (!mixerLocked) | |
34 { | |
35 mixerBusy = true; | |
36 mix_UpdateBuffer((int16_t *)waveBlock->lpData, bufferSize); // pmp_mix.c function | |
37 mixerBusy = false; | |
38 } | |
39 | |
40 waveOutWrite(hWave, waveBlock, sizeof (WAVEHDR)); | |
41 currBuffer = (currBuffer + 1) % MIX_BUF_NUM; | |
42 WaitForSingleObject(hAudioSem, INFINITE); // wait for buffer fill request | |
43 } | |
44 | |
45 return 0; | |
46 } | |
47 | |
48 static void CALLBACK waveProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) | |
49 { | |
50 if (uMsg == WOM_DONE) | |
51 ReleaseSemaphore(hAudioSem, 1, NULL); | |
52 | |
53 (void)hWaveOut; | |
54 (void)uMsg; | |
55 (void)dwInstance; | |
56 (void)dwParam1; | |
57 (void)dwParam2; | |
58 } | |
59 | |
60 void lockMixer(void) | |
61 { | |
62 mixerLocked = true; | |
63 while (mixerBusy); | |
64 } | |
65 | |
66 void unlockMixer(void) | |
67 { | |
68 mixerBusy = false; | |
69 mixerLocked = false; | |
70 } | |
71 | |
72 void closeMixer(void) | |
73 { | |
74 mixerOpened = false; | |
75 mixerBusy = false; | |
76 | |
77 if (hAudioSem != NULL) | |
78 ReleaseSemaphore(hAudioSem, 1, NULL); | |
79 | |
80 if (hThread != NULL) | |
81 { | |
82 WaitForSingleObject(hThread, INFINITE); | |
83 CloseHandle(hThread); | |
84 hThread = NULL; | |
85 } | |
86 | |
87 if (hAudioSem != NULL) | |
88 { | |
89 CloseHandle(hAudioSem); | |
90 hAudioSem = NULL; | |
91 } | |
92 | |
93 if (hWave != NULL) | |
94 { | |
95 waveOutReset(hWave); | |
96 | |
97 for (int32_t i = 0; i < MIX_BUF_NUM; i++) | |
98 { | |
99 if (waveBlocks[i].dwUser != 0xFFFF) | |
100 waveOutUnprepareHeader(hWave, &waveBlocks[i], sizeof (WAVEHDR)); | |
101 } | |
102 | |
103 waveOutClose(hWave); | |
104 hWave = NULL; | |
105 } | |
106 | |
107 for (int32_t i = 0; i < MIX_BUF_NUM; i++) | |
108 { | |
109 if (audioBuffer[i] != NULL) | |
110 { | |
111 free(audioBuffer[i]); | |
112 audioBuffer[i] = NULL; | |
113 } | |
114 } | |
115 } | |
116 | |
117 bool openMixer(int32_t mixingFrequency, int32_t mixingBufferSize) | |
118 { | |
119 DWORD threadID; | |
120 WAVEFORMATEX wfx; | |
121 | |
122 // don't unprepare headers on error | |
123 for (int32_t i = 0; i < MIX_BUF_NUM; i++) | |
124 waveBlocks[i].dwUser = 0xFFFF; | |
125 | |
126 closeMixer(); | |
127 bufferSize = mixingBufferSize; | |
128 | |
129 ZeroMemory(&wfx, sizeof (wfx)); | |
130 wfx.nSamplesPerSec = mixingFrequency; | |
131 wfx.wBitsPerSample = 16; | |
132 wfx.nChannels = 2; | |
133 wfx.wFormatTag = WAVE_FORMAT_PCM; | |
134 wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8); | |
135 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; | |
136 | |
137 if (waveOutOpen(&hWave, WAVE_MAPPER, &wfx, (DWORD_PTR)&waveProc, 0, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) | |
138 goto omError; | |
139 | |
140 // create semaphore for buffer fill requests | |
141 hAudioSem = CreateSemaphore(NULL, MIX_BUF_NUM - 1, MIX_BUF_NUM, NULL); | |
142 if (hAudioSem == NULL) | |
143 goto omError; | |
144 | |
145 // allocate WinMM mix buffers | |
146 for (int32_t i = 0; i < MIX_BUF_NUM; i++) | |
147 { | |
148 audioBuffer[i] = (int16_t *)calloc(mixingBufferSize, wfx.nBlockAlign); | |
149 if (audioBuffer[i] == NULL) | |
150 goto omError; | |
151 } | |
152 | |
153 // initialize WinMM mix headers | |
154 memset(waveBlocks, 0, sizeof (waveBlocks)); | |
155 for (int32_t i = 0; i < MIX_BUF_NUM; i++) | |
156 { | |
157 waveBlocks[i].lpData = (LPSTR)audioBuffer[i]; | |
158 waveBlocks[i].dwBufferLength = mixingBufferSize * wfx.nBlockAlign; | |
159 waveBlocks[i].dwFlags = WHDR_DONE; | |
160 | |
161 if (waveOutPrepareHeader(hWave, &waveBlocks[i], sizeof (WAVEHDR)) != MMSYSERR_NOERROR) | |
162 goto omError; | |
163 } | |
164 | |
165 currBuffer = 0; | |
166 mixerOpened = true; | |
167 | |
168 hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)mixThread, NULL, 0, &threadID); | |
169 if (hThread == NULL) | |
170 goto omError; | |
171 | |
172 return TRUE; | |
173 | |
174 omError: | |
175 closeMixer(); | |
176 return FALSE; | |
177 } |