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