126
|
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 }
|