master
c 684 lines 18.2 KB
Raw
1 /*
2 * QEMU DirectSound audio driver
3 *
4 * Copyright (c) 2005 Vassili Karpov (malc)
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25 /*
26 * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation
27 */
28
29 #include "qemu/osdep.h"
30 #include "qemu/audio.h"
31 #include "qemu/error-report.h"
32 #include "audio_int.h"
33 #include "qemu/module.h"
34 #include "qapi/error.h"
35 #include "qom/object.h"
36
37 #include <windows.h>
38 #include <mmsystem.h>
39 #include <objbase.h>
40 #include <dsound.h>
41
42 #include "trace.h"
43 #include "audio_win_int.h"
44
45 #define TYPE_AUDIO_DSOUND "audio-dsound"
46 OBJECT_DECLARE_SIMPLE_TYPE(AudioDsound, AUDIO_DSOUND)
47
48 static AudioBackendClass *audio_dsound_parent_class;
49
50 struct AudioDsound {
51 AudioMixengBackend parent_obj;
52
53 LPDIRECTSOUND dsound;
54 LPDIRECTSOUNDCAPTURE dsound_capture;
55 struct audsettings settings;
56 };
57
58 typedef struct {
59 HWVoiceOut hw;
60 LPDIRECTSOUNDBUFFER dsound_buffer;
61 bool first_time;
62 } DSoundVoiceOut;
63
64 typedef struct {
65 HWVoiceIn hw;
66 LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
67 bool first_time;
68 } DSoundVoiceIn;
69
70 static const char *dserror(HRESULT hr)
71 {
72 switch (hr) {
73 case DS_OK:
74 return "The method succeeded";
75 #ifdef DS_NO_VIRTUALIZATION
76 case DS_NO_VIRTUALIZATION:
77 return "The buffer was created, but another 3D algorithm was substituted";
78 #endif
79 #ifdef DS_INCOMPLETE
80 case DS_INCOMPLETE:
81 return "The method succeeded, but not all the optional effects were obtained";
82 #endif
83 #ifdef DSERR_ACCESSDENIED
84 case DSERR_ACCESSDENIED:
85 return "The request failed because access was denied";
86 #endif
87 #ifdef DSERR_ALLOCATED
88 case DSERR_ALLOCATED:
89 return "The request failed because resources, "
90 "such as a priority level, were already in use "
91 "by another caller";
92 #endif
93 #ifdef DSERR_ALREADYINITIALIZED
94 case DSERR_ALREADYINITIALIZED:
95 return "The object is already initialized";
96 #endif
97 #ifdef DSERR_BADFORMAT
98 case DSERR_BADFORMAT:
99 return "The specified wave format is not supported";
100 #endif
101 #ifdef DSERR_BADSENDBUFFERGUID
102 case DSERR_BADSENDBUFFERGUID:
103 return "The GUID specified in an audiopath file "
104 "does not match a valid mix-in buffer";
105 #endif
106 #ifdef DSERR_BUFFERLOST
107 case DSERR_BUFFERLOST:
108 return "The buffer memory has been lost and must be restored";
109 #endif
110 #ifdef DSERR_BUFFERTOOSMALL
111 case DSERR_BUFFERTOOSMALL:
112 return "The buffer size is not great enough to "
113 "enable effects processing";
114 #endif
115 #ifdef DSERR_CONTROLUNAVAIL
116 case DSERR_CONTROLUNAVAIL:
117 return "The buffer control (volume, pan, and so on) "
118 "requested by the caller is not available. "
119 "Controls must be specified when the buffer is created, "
120 "using the dwFlags member of DSBUFFERDESC";
121 #endif
122 #ifdef DSERR_DS8_REQUIRED
123 case DSERR_DS8_REQUIRED:
124 return "A DirectSound object of class CLSID_DirectSound8 or later "
125 "is required for the requested functionality. "
126 "For more information, see IDirectSound8 Interface";
127 #endif
128 #ifdef DSERR_FXUNAVAILABLE
129 case DSERR_FXUNAVAILABLE:
130 return "The effects requested could not be found on the system, "
131 "or they are in the wrong order or in the wrong location; "
132 "for example, an effect expected in hardware "
133 "was found in software";
134 #endif
135 #ifdef DSERR_GENERIC
136 case DSERR_GENERIC:
137 return "An undetermined error occurred inside the DirectSound subsystem";
138 #endif
139 #ifdef DSERR_INVALIDCALL
140 case DSERR_INVALIDCALL:
141 return "This function is not valid for the current state of this object";
142 #endif
143 #ifdef DSERR_INVALIDPARAM
144 case DSERR_INVALIDPARAM:
145 return "An invalid parameter was passed to the returning function";
146 #endif
147 #ifdef DSERR_NOAGGREGATION
148 case DSERR_NOAGGREGATION:
149 return "The object does not support aggregation";
150 #endif
151 #ifdef DSERR_NODRIVER
152 case DSERR_NODRIVER:
153 return "No sound driver is available for use, "
154 "or the given GUID is not a valid DirectSound device ID";
155 #endif
156 #ifdef DSERR_NOINTERFACE
157 case DSERR_NOINTERFACE:
158 return "The requested COM interface is not available";
159 #endif
160 #ifdef DSERR_OBJECTNOTFOUND
161 case DSERR_OBJECTNOTFOUND:
162 return "The requested object was not found";
163 #endif
164 #ifdef DSERR_OTHERAPPHASPRIO
165 case DSERR_OTHERAPPHASPRIO:
166 return "Another application has a higher priority level, "
167 "preventing this call from succeeding";
168 #endif
169 #ifdef DSERR_OUTOFMEMORY
170 case DSERR_OUTOFMEMORY:
171 return "The DirectSound subsystem could not allocate "
172 "sufficient memory to complete the caller's request";
173 #endif
174 #ifdef DSERR_PRIOLEVELNEEDED
175 case DSERR_PRIOLEVELNEEDED:
176 return "A cooperative level of DSSCL_PRIORITY or higher is required";
177 #endif
178 #ifdef DSERR_SENDLOOP
179 case DSERR_SENDLOOP:
180 return "A circular loop of send effects was detected";
181 #endif
182 #ifdef DSERR_UNINITIALIZED
183 case DSERR_UNINITIALIZED:
184 return "The Initialize method has not been called "
185 "or has not been called successfully "
186 "before other methods were called";
187 #endif
188 #ifdef DSERR_UNSUPPORTED
189 case DSERR_UNSUPPORTED:
190 return "The function called is not supported at this time";
191 #endif
192 default:
193 return NULL;
194 }
195
196 }
197
198 static void dserror_set(Error **errp, HRESULT hr, const char *msg)
199 {
200 const char *str = dserror(hr);
201
202 if (str) {
203 error_setg(errp, "%s: %s", msg, str);
204 } else {
205 error_setg(errp, "%s: Unknown (HRESULT: 0x%lx)", msg, hr);
206 }
207 }
208
209 static void dsound_log_hresult(HRESULT hr)
210 {
211 const char *str = dserror(hr);
212
213 if (str) {
214 error_printf(" Reason: %s", str);
215 } else {
216 error_printf(" Reason: Unknown (HRESULT: 0x%lx)", hr);
217 }
218 }
219
220 static void G_GNUC_PRINTF(2, 3) dsound_logerr(HRESULT hr, const char *fmt, ...)
221 {
222 va_list ap;
223
224 error_printf("dsound: ");
225 va_start(ap, fmt);
226 error_vprintf(fmt, ap);
227 va_end(ap);
228 dsound_log_hresult(hr);
229 error_printf("\n");
230 }
231
232 static void G_GNUC_PRINTF(3, 4) dsound_logerr2(HRESULT hr, const char *typ,
233 const char *fmt, ...)
234 {
235 va_list ap;
236
237 error_printf("dsound: Could not initialize %s: ", typ);
238 va_start(ap, fmt);
239 error_vprintf(fmt, ap);
240 va_end(ap);
241 dsound_log_hresult(hr);
242 error_printf("\n");
243 }
244
245
246 static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb, AudioDsound *s)
247 {
248 HRESULT hr;
249
250 hr = IDirectSoundBuffer_Restore (dsb);
251
252 if (hr != DS_OK) {
253 dsound_logerr(hr, "Could not restore playback buffer");
254 return -1;
255 }
256 return 0;
257 }
258
259 #include "dsound_template.h"
260 #define DSBTYPE_IN
261 #include "dsound_template.h"
262 #undef DSBTYPE_IN
263
264 static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp,
265 AudioDsound *s)
266 {
267 HRESULT hr;
268
269 hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
270 if (FAILED (hr)) {
271 dsound_logerr(hr, "Could not get playback buffer status");
272 return -1;
273 }
274
275 if (*statusp & DSBSTATUS_BUFFERLOST) {
276 dsound_restore_out(dsb, s);
277 return -1;
278 }
279
280 return 0;
281 }
282
283 static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
284 DWORD *statusp)
285 {
286 HRESULT hr;
287
288 hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp);
289 if (FAILED (hr)) {
290 dsound_logerr(hr, "Could not get capture buffer status");
291 return -1;
292 }
293
294 return 0;
295 }
296
297 static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
298 AudioDsound *s)
299 {
300 int err;
301 LPVOID p1, p2;
302 DWORD blen1, blen2, len1, len2;
303
304 err = dsound_lock_out (
305 dsb,
306 &hw->info,
307 0,
308 hw->size_emul,
309 &p1, &p2,
310 &blen1, &blen2,
311 1,
312 s
313 );
314 if (err) {
315 return;
316 }
317
318 len1 = blen1 / hw->info.bytes_per_frame;
319 len2 = blen2 / hw->info.bytes_per_frame;
320
321 trace_dsound_clear_sample(p1, blen1, len1, p2, blen2, len2);
322
323 if (p1 && len1) {
324 audio_pcm_info_clear_buf (&hw->info, p1, len1);
325 }
326
327 if (p2 && len2) {
328 audio_pcm_info_clear_buf (&hw->info, p2, len2);
329 }
330
331 dsound_unlock_out (dsb, p1, p2, blen1, blen2);
332 }
333
334 static void dsound_enable_out(HWVoiceOut *hw, bool enable)
335 {
336 HRESULT hr;
337 DWORD status;
338 AudioDsound *s = AUDIO_DSOUND(hw->s);
339 DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
340 LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
341
342 if (!dsb) {
343 error_report("dsound: Attempt to control voice without a buffer");
344 return;
345 }
346
347 if (enable) {
348 if (dsound_get_status_out(dsb, &status, s)) {
349 return;
350 }
351
352 if (status & DSBSTATUS_PLAYING) {
353 warn_report("dsound: Voice is already playing");
354 return;
355 }
356
357 dsound_clear_sample(hw, dsb, s);
358
359 hr = IDirectSoundBuffer_Play(dsb, 0, 0, DSBPLAY_LOOPING);
360 if (FAILED(hr)) {
361 dsound_logerr(hr, "Could not start playing buffer");
362 return;
363 }
364 } else {
365 if (dsound_get_status_out(dsb, &status, s)) {
366 return;
367 }
368
369 if (status & DSBSTATUS_PLAYING) {
370 hr = IDirectSoundBuffer_Stop(dsb);
371 if (FAILED(hr)) {
372 dsound_logerr(hr, "Could not stop playing buffer");
373 return;
374 }
375 } else {
376 warn_report("dsound: Voice is not playing");
377 }
378 }
379 }
380
381 static size_t dsound_buffer_get_free(HWVoiceOut *hw)
382 {
383 DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
384 LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
385 HRESULT hr;
386 DWORD ppos, wpos;
387
388 hr = IDirectSoundBuffer_GetCurrentPosition(
389 dsb, &ppos, ds->first_time ? &wpos : NULL);
390 if (FAILED(hr)) {
391 dsound_logerr(hr, "Could not get playback buffer position");
392 return 0;
393 }
394
395 if (ds->first_time) {
396 hw->pos_emul = wpos;
397 ds->first_time = false;
398 }
399
400 return audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
401 }
402
403 static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
404 {
405 DSoundVoiceOut *ds = (DSoundVoiceOut *)hw;
406 LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
407 DWORD act_size;
408 size_t req_size;
409 int err;
410 void *ret;
411
412 req_size = MIN(*size, hw->size_emul - hw->pos_emul);
413 assert(req_size > 0);
414
415 err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
416 &act_size, NULL, false, AUDIO_DSOUND(hw->s));
417 if (err) {
418 error_report("dsound: Failed to lock buffer");
419 *size = 0;
420 return NULL;
421 }
422
423 *size = act_size;
424 return ret;
425 }
426
427 static size_t dsound_put_buffer_out(HWVoiceOut *hw, void *buf, size_t len)
428 {
429 DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
430 LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
431 int err = dsound_unlock_out(dsb, buf, NULL, len, 0);
432
433 if (err) {
434 error_report("dsound: Failed to unlock buffer");
435 return 0;
436 }
437 hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
438
439 return len;
440 }
441
442 static void dsound_enable_in(HWVoiceIn *hw, bool enable)
443 {
444 HRESULT hr;
445 DWORD status;
446 DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
447 LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
448
449 if (!dscb) {
450 error_report("dsound: Attempt to control capture voice without a buffer");
451 return;
452 }
453
454 if (enable) {
455 if (dsound_get_status_in(dscb, &status)) {
456 return;
457 }
458
459 if (status & DSCBSTATUS_CAPTURING) {
460 warn_report("dsound: Voice is already capturing");
461 return;
462 }
463
464 /* clear ?? */
465
466 hr = IDirectSoundCaptureBuffer_Start(dscb, DSCBSTART_LOOPING);
467 if (FAILED(hr)) {
468 dsound_logerr(hr, "Could not start capturing");
469 return;
470 }
471 } else {
472 if (dsound_get_status_in(dscb, &status)) {
473 return;
474 }
475
476 if (status & DSCBSTATUS_CAPTURING) {
477 hr = IDirectSoundCaptureBuffer_Stop(dscb);
478 if (FAILED(hr)) {
479 dsound_logerr(hr, "Could not stop capturing");
480 return;
481 }
482 } else {
483 warn_report("dsound: Voice is not capturing");
484 }
485 }
486 }
487
488 static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size)
489 {
490 DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
491 LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
492 HRESULT hr;
493 DWORD rpos, act_size;
494 size_t req_size;
495 int err;
496 void *ret;
497
498 hr = IDirectSoundCaptureBuffer_GetCurrentPosition(dscb, NULL, &rpos);
499 if (FAILED(hr)) {
500 dsound_logerr(hr, "Could not get capture buffer position");
501 *size = 0;
502 return NULL;
503 }
504
505 if (ds->first_time) {
506 hw->pos_emul = rpos;
507 ds->first_time = false;
508 }
509
510 req_size = audio_ring_dist(rpos, hw->pos_emul, hw->size_emul);
511 req_size = MIN(*size, MIN(req_size, hw->size_emul - hw->pos_emul));
512
513 if (req_size == 0) {
514 *size = 0;
515 return NULL;
516 }
517
518 err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
519 &act_size, NULL, false, AUDIO_DSOUND(hw->s));
520 if (err) {
521 error_report("dsound: Failed to lock buffer");
522 *size = 0;
523 return NULL;
524 }
525
526 *size = act_size;
527 return ret;
528 }
529
530 static void dsound_put_buffer_in(HWVoiceIn *hw, void *buf, size_t len)
531 {
532 DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
533 LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
534 int err = dsound_unlock_in(dscb, buf, NULL, len, 0);
535
536 if (err) {
537 error_report("dsound: Failed to unlock buffer");
538 return;
539 }
540 hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
541 }
542
543 static void audio_dsound_finalize(Object *obj)
544 {
545 AudioDsound *s = AUDIO_DSOUND(obj);
546 HRESULT hr;
547
548 if (!s->dsound) {
549 return;
550 }
551
552 hr = IDirectSound_Release (s->dsound);
553 if (FAILED (hr)) {
554 dsound_logerr(hr, "Could not release DirectSound");
555 }
556 s->dsound = NULL;
557
558 if (!s->dsound_capture) {
559 return;
560 }
561
562 hr = IDirectSoundCapture_Release (s->dsound_capture);
563 if (FAILED (hr)) {
564 dsound_logerr(hr, "Could not release DirectSoundCapture");
565 }
566 s->dsound_capture = NULL;
567 }
568
569 static bool
570 audio_dsound_realize(AudioBackend *abe, Audiodev *dev, Error **errp)
571 {
572 AudioDsound *s = AUDIO_DSOUND(abe);
573 HRESULT hr;
574 AudiodevDsoundOptions *dso;
575
576 assert(dev->driver == AUDIODEV_DRIVER_DSOUND);
577
578 if (!audio_dsound_parent_class->realize(abe, dev, errp)) {
579 return false;
580 }
581
582 dso = &dev->u.dsound;
583
584 if (!dso->has_latency) {
585 dso->has_latency = true;
586 dso->latency = 10000; /* 10 ms */
587 }
588
589 hr = CoInitialize (NULL);
590 if (FAILED (hr)) {
591 dserror_set(errp, hr, "Could not initialize COM");
592 return false;
593 }
594
595 hr = CoCreateInstance (
596 &CLSID_DirectSound,
597 NULL,
598 CLSCTX_ALL,
599 &IID_IDirectSound,
600 (void **) &s->dsound
601 );
602 if (FAILED (hr)) {
603 dserror_set(errp, hr, "Could not create DirectSound instance");
604 return false;
605 }
606
607 hr = IDirectSound_Initialize (s->dsound, NULL);
608 if (FAILED (hr)) {
609 dserror_set(errp, hr, "Could not initialize DirectSound");
610 return false;
611 }
612
613 hr = CoCreateInstance (
614 &CLSID_DirectSoundCapture,
615 NULL,
616 CLSCTX_ALL,
617 &IID_IDirectSoundCapture,
618 (void **) &s->dsound_capture
619 );
620 if (FAILED (hr)) {
621 dserror_set(errp, hr, "Could not create DirectSoundCapture instance");
622 return false;
623 }
624
625 hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
626 if (FAILED(hr)) {
627 dserror_set(errp, hr, "Could not initialize DirectSoundCapture");
628 return false;
629 }
630
631 hr = IDirectSound_SetCooperativeLevel (
632 s->dsound,
633 GetDesktopWindow(),
634 DSSCL_PRIORITY
635 );
636 if (FAILED(hr)) {
637 dserror_set(errp, hr, "Could not set cooperative level");
638 return false;
639 }
640
641 return true;
642 }
643
644 static void audio_dsound_class_init(ObjectClass *klass, const void *data)
645 {
646 AudioBackendClass *b = AUDIO_BACKEND_CLASS(klass);
647 AudioMixengBackendClass *k = AUDIO_MIXENG_BACKEND_CLASS(klass);
648
649 audio_dsound_parent_class = AUDIO_BACKEND_CLASS(object_class_get_parent(klass));
650
651 b->realize = audio_dsound_realize;
652 k->max_voices_out = INT_MAX;
653 k->max_voices_in = 1;
654 k->voice_size_out = sizeof(DSoundVoiceOut);
655 k->voice_size_in = sizeof(DSoundVoiceIn);
656
657 k->init_out = dsound_init_out;
658 k->fini_out = dsound_fini_out;
659 k->write = audio_generic_write;
660 k->buffer_get_free = dsound_buffer_get_free;
661 k->get_buffer_out = dsound_get_buffer_out;
662 k->put_buffer_out = dsound_put_buffer_out;
663 k->enable_out = dsound_enable_out;
664
665 k->init_in = dsound_init_in;
666 k->fini_in = dsound_fini_in;
667 k->read = audio_generic_read;
668 k->get_buffer_in = dsound_get_buffer_in;
669 k->put_buffer_in = dsound_put_buffer_in;
670 k->enable_in = dsound_enable_in;
671 }
672
673 static const TypeInfo audio_types[] = {
674 {
675 .name = TYPE_AUDIO_DSOUND,
676 .parent = TYPE_AUDIO_MIXENG_BACKEND,
677 .instance_size = sizeof(AudioDsound),
678 .class_init = audio_dsound_class_init,
679 .instance_finalize = audio_dsound_finalize,
680 },
681 };
682
683 DEFINE_TYPES(audio_types)
684 module_obj(TYPE_AUDIO_DSOUND);