]>
Commit | Line | Data |
---|---|---|
d5631638 | 1 | /* public domain */ |
2 | ||
3 | #include "qemu-common.h" | |
e0bda367 | 4 | #include "sysemu.h" |
d5631638 | 5 | #include "audio.h" |
6 | ||
7 | #define AUDIO_CAP "winwave" | |
8 | #include "audio_int.h" | |
9 | ||
10 | #include <windows.h> | |
11 | #include <mmsystem.h> | |
12 | ||
13 | #include "audio_win_int.h" | |
14 | ||
15 | static struct { | |
16 | int dac_headers; | |
17 | int dac_samples; | |
18 | } conf = { | |
19 | .dac_headers = 4, | |
20 | .dac_samples = 1024 | |
21 | }; | |
22 | ||
23 | typedef struct { | |
24 | HWVoiceOut hw; | |
25 | HWAVEOUT hwo; | |
26 | WAVEHDR *hdrs; | |
e0bda367 | 27 | HANDLE event; |
d5631638 | 28 | void *pcm_buf; |
29 | int avail; | |
30 | int pending; | |
31 | int curhdr; | |
32 | CRITICAL_SECTION crit_sect; | |
33 | } WaveVoiceOut; | |
34 | ||
35 | static void winwave_log_mmresult (MMRESULT mr) | |
36 | { | |
37 | const char *str = "BUG"; | |
38 | ||
39 | switch (mr) { | |
40 | case MMSYSERR_NOERROR: | |
41 | str = "Success"; | |
42 | break; | |
43 | ||
44 | case MMSYSERR_INVALHANDLE: | |
45 | str = "Specified device handle is invalid"; | |
46 | break; | |
47 | ||
48 | case MMSYSERR_BADDEVICEID: | |
49 | str = "Specified device id is out of range"; | |
50 | break; | |
51 | ||
52 | case MMSYSERR_NODRIVER: | |
53 | str = "No device driver is present"; | |
54 | break; | |
55 | ||
56 | case MMSYSERR_NOMEM: | |
57 | str = "Unable to allocate or locl memory"; | |
58 | break; | |
59 | ||
60 | case WAVERR_SYNC: | |
61 | str = "Device is synchronous but waveOutOpen was called " | |
62 | "without using the WINWAVE_ALLOWSYNC flag"; | |
63 | break; | |
64 | ||
65 | case WAVERR_UNPREPARED: | |
66 | str = "The data block pointed to by the pwh parameter " | |
67 | "hasn't been prepared"; | |
68 | break; | |
69 | ||
70 | default: | |
71 | AUD_log (AUDIO_CAP, "Reason: Unknown (MMRESULT %#x)\n", mr); | |
72 | return; | |
73 | } | |
74 | ||
75 | AUD_log (AUDIO_CAP, "Reason: %s\n", str); | |
76 | } | |
77 | ||
78 | static void GCC_FMT_ATTR (2, 3) winwave_logerr ( | |
79 | MMRESULT mr, | |
80 | const char *fmt, | |
81 | ... | |
82 | ) | |
83 | { | |
84 | va_list ap; | |
85 | ||
86 | va_start (ap, fmt); | |
87 | AUD_vlog (AUDIO_CAP, fmt, ap); | |
88 | va_end (ap); | |
89 | ||
90 | winwave_log_mmresult (mr); | |
91 | } | |
92 | ||
93 | static void winwave_anal_close_out (WaveVoiceOut *wave) | |
94 | { | |
95 | MMRESULT mr; | |
96 | ||
97 | mr = waveOutClose (wave->hwo); | |
98 | if (mr != MMSYSERR_NOERROR) { | |
99 | winwave_logerr (mr, "waveOutClose\n"); | |
100 | } | |
101 | wave->hwo = NULL; | |
102 | } | |
103 | ||
104 | static void CALLBACK winwave_callback ( | |
105 | HWAVEOUT hwo, | |
106 | UINT msg, | |
107 | DWORD_PTR dwInstance, | |
108 | DWORD_PTR dwParam1, | |
109 | DWORD_PTR dwParam2 | |
110 | ) | |
111 | { | |
112 | WaveVoiceOut *wave = (WaveVoiceOut *) dwInstance; | |
113 | ||
114 | switch (msg) { | |
115 | case WOM_DONE: | |
116 | { | |
117 | WAVEHDR *h = (WAVEHDR *) dwParam1; | |
118 | if (!h->dwUser) { | |
119 | h->dwUser = 1; | |
120 | EnterCriticalSection (&wave->crit_sect); | |
121 | { | |
122 | wave->avail += conf.dac_samples; | |
123 | } | |
124 | LeaveCriticalSection (&wave->crit_sect); | |
e0bda367 | 125 | if (wave->hw.poll_mode) { |
126 | if (!SetEvent (wave->event)) { | |
127 | AUD_log (AUDIO_CAP, "SetEvent failed %lx\n", | |
128 | GetLastError ()); | |
129 | } | |
130 | } | |
d5631638 | 131 | } |
132 | } | |
133 | break; | |
134 | ||
135 | case WOM_CLOSE: | |
136 | case WOM_OPEN: | |
137 | break; | |
138 | ||
139 | default: | |
140 | AUD_log (AUDIO_CAP, "unknown wave callback msg %x\n", msg); | |
141 | } | |
142 | } | |
143 | ||
144 | static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as) | |
145 | { | |
146 | int i; | |
147 | int err; | |
148 | MMRESULT mr; | |
149 | WAVEFORMATEX wfx; | |
150 | WaveVoiceOut *wave; | |
151 | ||
152 | wave = (WaveVoiceOut *) hw; | |
153 | ||
154 | InitializeCriticalSection (&wave->crit_sect); | |
155 | ||
156 | err = waveformat_from_audio_settings (&wfx, as); | |
157 | if (err) { | |
158 | goto err0; | |
159 | } | |
160 | ||
161 | mr = waveOutOpen (&wave->hwo, WAVE_MAPPER, &wfx, | |
162 | (DWORD_PTR) winwave_callback, | |
163 | (DWORD_PTR) wave, CALLBACK_FUNCTION); | |
164 | if (mr != MMSYSERR_NOERROR) { | |
165 | winwave_logerr (mr, "waveOutOpen\n"); | |
166 | goto err1; | |
167 | } | |
168 | ||
169 | wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers, | |
170 | sizeof (*wave->hdrs)); | |
171 | if (!wave->hdrs) { | |
172 | goto err2; | |
173 | } | |
174 | ||
175 | audio_pcm_init_info (&hw->info, as); | |
176 | hw->samples = conf.dac_samples * conf.dac_headers; | |
177 | wave->avail = hw->samples; | |
178 | ||
179 | wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.dac_samples, | |
180 | conf.dac_headers << hw->info.shift); | |
181 | if (!wave->pcm_buf) { | |
182 | goto err3; | |
183 | } | |
184 | ||
185 | for (i = 0; i < conf.dac_headers; ++i) { | |
186 | WAVEHDR *h = &wave->hdrs[i]; | |
187 | ||
188 | h->dwUser = 0; | |
189 | h->dwBufferLength = conf.dac_samples << hw->info.shift; | |
190 | h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength); | |
191 | h->dwFlags = 0; | |
192 | ||
193 | mr = waveOutPrepareHeader (wave->hwo, h, sizeof (*h)); | |
194 | if (mr != MMSYSERR_NOERROR) { | |
195 | winwave_logerr (mr, "waveOutPrepareHeader(%d)\n", wave->curhdr); | |
196 | goto err4; | |
197 | } | |
198 | } | |
199 | ||
200 | return 0; | |
201 | ||
202 | err4: | |
203 | qemu_free (wave->pcm_buf); | |
204 | err3: | |
205 | qemu_free (wave->hdrs); | |
206 | err2: | |
207 | winwave_anal_close_out (wave); | |
208 | err1: | |
209 | err0: | |
210 | return -1; | |
211 | } | |
212 | ||
213 | static int winwave_write (SWVoiceOut *sw, void *buf, int len) | |
214 | { | |
215 | return audio_pcm_sw_write (sw, buf, len); | |
216 | } | |
217 | ||
218 | static int winwave_run_out (HWVoiceOut *hw, int live) | |
219 | { | |
220 | WaveVoiceOut *wave = (WaveVoiceOut *) hw; | |
221 | int decr; | |
e0bda367 | 222 | int doreset; |
d5631638 | 223 | |
224 | EnterCriticalSection (&wave->crit_sect); | |
225 | { | |
226 | decr = audio_MIN (live, wave->avail); | |
227 | decr = audio_pcm_hw_clip_out (hw, wave->pcm_buf, decr, wave->pending); | |
228 | wave->pending += decr; | |
229 | wave->avail -= decr; | |
230 | } | |
231 | LeaveCriticalSection (&wave->crit_sect); | |
232 | ||
e0bda367 | 233 | doreset = hw->poll_mode && (wave->pending >= conf.dac_samples); |
234 | if (doreset && !ResetEvent (wave->event)) { | |
235 | AUD_log (AUDIO_CAP, "ResetEvent failed %lx\n", GetLastError ()); | |
236 | } | |
237 | ||
d5631638 | 238 | while (wave->pending >= conf.dac_samples) { |
239 | MMRESULT mr; | |
240 | WAVEHDR *h = &wave->hdrs[wave->curhdr]; | |
241 | ||
242 | h->dwUser = 0; | |
243 | mr = waveOutWrite (wave->hwo, h, sizeof (*h)); | |
244 | if (mr != MMSYSERR_NOERROR) { | |
245 | winwave_logerr (mr, "waveOutWrite(%d)\n", wave->curhdr); | |
246 | break; | |
247 | } | |
248 | ||
249 | wave->pending -= conf.dac_samples; | |
250 | wave->curhdr = (wave->curhdr + 1) % conf.dac_headers; | |
251 | } | |
e0bda367 | 252 | |
d5631638 | 253 | return decr; |
254 | } | |
255 | ||
256 | static void winwave_fini_out (HWVoiceOut *hw) | |
257 | { | |
258 | WaveVoiceOut *wave = (WaveVoiceOut *) hw; | |
259 | ||
260 | winwave_anal_close_out (wave); | |
261 | ||
262 | qemu_free (wave->pcm_buf); | |
263 | wave->pcm_buf = NULL; | |
264 | ||
265 | qemu_free (wave->hdrs); | |
266 | wave->hdrs = NULL; | |
e0bda367 | 267 | |
268 | if (wave->event) { | |
269 | if (!CloseHandle (wave->event)) { | |
270 | AUD_log (AUDIO_CAP, "CloseHandle failed %lx\n", GetLastError ()); | |
271 | } | |
272 | wave->event = NULL; | |
273 | } | |
274 | } | |
275 | ||
276 | static void winwave_poll_out (void *opaque) | |
277 | { | |
278 | (void) opaque; | |
279 | audio_run ("winwave_poll_out"); | |
d5631638 | 280 | } |
281 | ||
282 | static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...) | |
283 | { | |
e0bda367 | 284 | WaveVoiceOut *wave = (WaveVoiceOut *) hw; |
285 | ||
d5631638 | 286 | switch (cmd) { |
287 | case VOICE_ENABLE: | |
e0bda367 | 288 | { |
289 | va_list ap; | |
290 | int poll_mode; | |
291 | ||
292 | va_start (ap, cmd); | |
293 | poll_mode = va_arg (ap, int); | |
294 | va_end (ap); | |
295 | ||
296 | if (poll_mode && !wave->event) { | |
297 | wave->event = CreateEvent (NULL, TRUE, TRUE, NULL); | |
298 | if (!wave->event) { | |
299 | AUD_log (AUDIO_CAP, | |
300 | "CreateEvent: %lx, poll mode will be disabled\n", | |
301 | GetLastError ()); | |
302 | } | |
303 | } | |
304 | ||
305 | if (wave->event) { | |
306 | int ret; | |
307 | ||
308 | ret = qemu_add_wait_object (wave->event, winwave_poll_out, | |
309 | wave); | |
310 | hw->poll_mode = (ret == 0); | |
311 | } | |
312 | else { | |
313 | hw->poll_mode = 0; | |
314 | } | |
315 | } | |
d5631638 | 316 | return 0; |
317 | ||
318 | case VOICE_DISABLE: | |
e0bda367 | 319 | if (wave->event) { |
320 | qemu_del_wait_object (wave->event, winwave_poll_out, wave); | |
321 | } | |
d5631638 | 322 | return 0; |
323 | } | |
324 | return -1; | |
325 | } | |
326 | ||
327 | static void *winwave_audio_init (void) | |
328 | { | |
329 | return &conf; | |
330 | } | |
331 | ||
332 | static void winwave_audio_fini (void *opaque) | |
333 | { | |
334 | (void) opaque; | |
335 | } | |
336 | ||
337 | static struct audio_option winwave_options[] = { | |
338 | { | |
339 | .name = "DAC_HEADERS", | |
340 | .tag = AUD_OPT_INT, | |
341 | .valp = &conf.dac_headers, | |
342 | .descr = "DAC number of headers", | |
343 | }, | |
344 | { | |
345 | .name = "DAC_SAMPLES", | |
346 | .tag = AUD_OPT_INT, | |
347 | .valp = &conf.dac_samples, | |
348 | .descr = "DAC number of samples per header", | |
349 | }, | |
350 | { /* End of list */ } | |
351 | }; | |
352 | ||
353 | static struct audio_pcm_ops winwave_pcm_ops = { | |
354 | .init_out = winwave_init_out, | |
355 | .fini_out = winwave_fini_out, | |
356 | .run_out = winwave_run_out, | |
357 | .write = winwave_write, | |
358 | .ctl_out = winwave_ctl_out | |
359 | }; | |
360 | ||
361 | struct audio_driver winwave_audio_driver = { | |
362 | .name = "winwave", | |
363 | .descr = "Windows Waveform Audio http://msdn.microsoft.com", | |
364 | .options = winwave_options, | |
365 | .init = winwave_audio_init, | |
366 | .fini = winwave_audio_fini, | |
367 | .pcm_ops = &winwave_pcm_ops, | |
368 | .can_be_default = 1, | |
369 | .max_voices_out = INT_MAX, | |
370 | .max_voices_in = 0, | |
371 | .voice_size_out = sizeof (WaveVoiceOut), | |
372 | .voice_size_in = 0 | |
373 | }; |