]> git.proxmox.com Git - qemu.git/blame - audio/winwaveaudio.c
CRIS: Segmented addressing only for kernel mode.
[qemu.git] / audio / winwaveaudio.c
CommitLineData
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
15static struct {
16 int dac_headers;
17 int dac_samples;
18} conf = {
19 .dac_headers = 4,
20 .dac_samples = 1024
21};
22
23typedef 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
35static 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
78static 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
93static 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
104static 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
144static 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
213static int winwave_write (SWVoiceOut *sw, void *buf, int len)
214{
215 return audio_pcm_sw_write (sw, buf, len);
216}
217
218static 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
256static 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
276static void winwave_poll_out (void *opaque)
277{
278 (void) opaque;
279 audio_run ("winwave_poll_out");
d5631638 280}
281
282static 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
327static void *winwave_audio_init (void)
328{
329 return &conf;
330}
331
332static void winwave_audio_fini (void *opaque)
333{
334 (void) opaque;
335}
336
337static 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
353static 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
361struct 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};