xref: /openbmc/qemu/audio/dsoundaudio.c (revision b45c03f5)
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-common.h"
30 #include "audio.h"
31 
32 #define AUDIO_CAP "dsound"
33 #include "audio_int.h"
34 
35 #include <windows.h>
36 #include <mmsystem.h>
37 #include <objbase.h>
38 #include <dsound.h>
39 
40 #include "audio_win_int.h"
41 
42 /* #define DEBUG_DSOUND */
43 
44 typedef struct {
45     int bufsize_in;
46     int bufsize_out;
47     int latency_millis;
48 } DSoundConf;
49 
50 typedef struct {
51     LPDIRECTSOUND dsound;
52     LPDIRECTSOUNDCAPTURE dsound_capture;
53     struct audsettings settings;
54     DSoundConf conf;
55 } dsound;
56 
57 typedef struct {
58     HWVoiceOut hw;
59     LPDIRECTSOUNDBUFFER dsound_buffer;
60     DWORD old_pos;
61     int first_time;
62     dsound *s;
63 #ifdef DEBUG_DSOUND
64     DWORD old_ppos;
65     DWORD played;
66     DWORD mixed;
67 #endif
68 } DSoundVoiceOut;
69 
70 typedef struct {
71     HWVoiceIn hw;
72     int first_time;
73     LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
74     dsound *s;
75 } DSoundVoiceIn;
76 
77 static void dsound_log_hresult (HRESULT hr)
78 {
79     const char *str = "BUG";
80 
81     switch (hr) {
82     case DS_OK:
83         str = "The method succeeded";
84         break;
85 #ifdef DS_NO_VIRTUALIZATION
86     case DS_NO_VIRTUALIZATION:
87         str = "The buffer was created, but another 3D algorithm was substituted";
88         break;
89 #endif
90 #ifdef DS_INCOMPLETE
91     case DS_INCOMPLETE:
92         str = "The method succeeded, but not all the optional effects were obtained";
93         break;
94 #endif
95 #ifdef DSERR_ACCESSDENIED
96     case DSERR_ACCESSDENIED:
97         str = "The request failed because access was denied";
98         break;
99 #endif
100 #ifdef DSERR_ALLOCATED
101     case DSERR_ALLOCATED:
102         str = "The request failed because resources, such as a priority level, were already in use by another caller";
103         break;
104 #endif
105 #ifdef DSERR_ALREADYINITIALIZED
106     case DSERR_ALREADYINITIALIZED:
107         str = "The object is already initialized";
108         break;
109 #endif
110 #ifdef DSERR_BADFORMAT
111     case DSERR_BADFORMAT:
112         str = "The specified wave format is not supported";
113         break;
114 #endif
115 #ifdef DSERR_BADSENDBUFFERGUID
116     case DSERR_BADSENDBUFFERGUID:
117         str = "The GUID specified in an audiopath file does not match a valid mix-in buffer";
118         break;
119 #endif
120 #ifdef DSERR_BUFFERLOST
121     case DSERR_BUFFERLOST:
122         str = "The buffer memory has been lost and must be restored";
123         break;
124 #endif
125 #ifdef DSERR_BUFFERTOOSMALL
126     case DSERR_BUFFERTOOSMALL:
127         str = "The buffer size is not great enough to enable effects processing";
128         break;
129 #endif
130 #ifdef DSERR_CONTROLUNAVAIL
131     case DSERR_CONTROLUNAVAIL:
132         str = "The buffer control (volume, pan, and so on) requested by the caller is not available. Controls must be specified when the buffer is created, using the dwFlags member of DSBUFFERDESC";
133         break;
134 #endif
135 #ifdef DSERR_DS8_REQUIRED
136     case DSERR_DS8_REQUIRED:
137         str = "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface";
138         break;
139 #endif
140 #ifdef DSERR_FXUNAVAILABLE
141     case DSERR_FXUNAVAILABLE:
142         str = "The effects requested could not be found on the system, or they are in the wrong order or in the wrong location; for example, an effect expected in hardware was found in software";
143         break;
144 #endif
145 #ifdef DSERR_GENERIC
146     case DSERR_GENERIC :
147         str = "An undetermined error occurred inside the DirectSound subsystem";
148         break;
149 #endif
150 #ifdef DSERR_INVALIDCALL
151     case DSERR_INVALIDCALL:
152         str = "This function is not valid for the current state of this object";
153         break;
154 #endif
155 #ifdef DSERR_INVALIDPARAM
156     case DSERR_INVALIDPARAM:
157         str = "An invalid parameter was passed to the returning function";
158         break;
159 #endif
160 #ifdef DSERR_NOAGGREGATION
161     case DSERR_NOAGGREGATION:
162         str = "The object does not support aggregation";
163         break;
164 #endif
165 #ifdef DSERR_NODRIVER
166     case DSERR_NODRIVER:
167         str = "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID";
168         break;
169 #endif
170 #ifdef DSERR_NOINTERFACE
171     case DSERR_NOINTERFACE:
172         str = "The requested COM interface is not available";
173         break;
174 #endif
175 #ifdef DSERR_OBJECTNOTFOUND
176     case DSERR_OBJECTNOTFOUND:
177         str = "The requested object was not found";
178         break;
179 #endif
180 #ifdef DSERR_OTHERAPPHASPRIO
181     case DSERR_OTHERAPPHASPRIO:
182         str = "Another application has a higher priority level, preventing this call from succeeding";
183         break;
184 #endif
185 #ifdef DSERR_OUTOFMEMORY
186     case DSERR_OUTOFMEMORY:
187         str = "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request";
188         break;
189 #endif
190 #ifdef DSERR_PRIOLEVELNEEDED
191     case DSERR_PRIOLEVELNEEDED:
192         str = "A cooperative level of DSSCL_PRIORITY or higher is required";
193         break;
194 #endif
195 #ifdef DSERR_SENDLOOP
196     case DSERR_SENDLOOP:
197         str = "A circular loop of send effects was detected";
198         break;
199 #endif
200 #ifdef DSERR_UNINITIALIZED
201     case DSERR_UNINITIALIZED:
202         str = "The Initialize method has not been called or has not been called successfully before other methods were called";
203         break;
204 #endif
205 #ifdef DSERR_UNSUPPORTED
206     case DSERR_UNSUPPORTED:
207         str = "The function called is not supported at this time";
208         break;
209 #endif
210     default:
211         AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT %#lx)\n", hr);
212         return;
213     }
214 
215     AUD_log (AUDIO_CAP, "Reason: %s\n", str);
216 }
217 
218 static void GCC_FMT_ATTR (2, 3) dsound_logerr (
219     HRESULT hr,
220     const char *fmt,
221     ...
222     )
223 {
224     va_list ap;
225 
226     va_start (ap, fmt);
227     AUD_vlog (AUDIO_CAP, fmt, ap);
228     va_end (ap);
229 
230     dsound_log_hresult (hr);
231 }
232 
233 static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
234     HRESULT hr,
235     const char *typ,
236     const char *fmt,
237     ...
238     )
239 {
240     va_list ap;
241 
242     AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
243     va_start (ap, fmt);
244     AUD_vlog (AUDIO_CAP, fmt, ap);
245     va_end (ap);
246 
247     dsound_log_hresult (hr);
248 }
249 
250 static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
251 {
252     return (millis * info->bytes_per_second) / 1000;
253 }
254 
255 #ifdef DEBUG_DSOUND
256 static void print_wave_format (WAVEFORMATEX *wfx)
257 {
258     dolog ("tag             = %d\n", wfx->wFormatTag);
259     dolog ("nChannels       = %d\n", wfx->nChannels);
260     dolog ("nSamplesPerSec  = %ld\n", wfx->nSamplesPerSec);
261     dolog ("nAvgBytesPerSec = %ld\n", wfx->nAvgBytesPerSec);
262     dolog ("nBlockAlign     = %d\n", wfx->nBlockAlign);
263     dolog ("wBitsPerSample  = %d\n", wfx->wBitsPerSample);
264     dolog ("cbSize          = %d\n", wfx->cbSize);
265 }
266 #endif
267 
268 static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb, dsound *s)
269 {
270     HRESULT hr;
271 
272     hr = IDirectSoundBuffer_Restore (dsb);
273 
274     if (hr != DS_OK) {
275         dsound_logerr (hr, "Could not restore playback buffer\n");
276         return -1;
277     }
278     return 0;
279 }
280 
281 #include "dsound_template.h"
282 #define DSBTYPE_IN
283 #include "dsound_template.h"
284 #undef DSBTYPE_IN
285 
286 static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp,
287                                   dsound *s)
288 {
289     HRESULT hr;
290 
291     hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
292     if (FAILED (hr)) {
293         dsound_logerr (hr, "Could not get playback buffer status\n");
294         return -1;
295     }
296 
297     if (*statusp & DSERR_BUFFERLOST) {
298         dsound_restore_out(dsb, s);
299         return -1;
300     }
301 
302     return 0;
303 }
304 
305 static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
306                                  DWORD *statusp)
307 {
308     HRESULT hr;
309 
310     hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp);
311     if (FAILED (hr)) {
312         dsound_logerr (hr, "Could not get capture buffer status\n");
313         return -1;
314     }
315 
316     return 0;
317 }
318 
319 static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
320 {
321     int src_len1 = dst_len;
322     int src_len2 = 0;
323     int pos = hw->rpos + dst_len;
324     struct st_sample *src1 = hw->mix_buf + hw->rpos;
325     struct st_sample *src2 = NULL;
326 
327     if (pos > hw->samples) {
328         src_len1 = hw->samples - hw->rpos;
329         src2 = hw->mix_buf;
330         src_len2 = dst_len - src_len1;
331         pos = src_len2;
332     }
333 
334     if (src_len1) {
335         hw->clip (dst, src1, src_len1);
336     }
337 
338     if (src_len2) {
339         dst = advance (dst, src_len1 << hw->info.shift);
340         hw->clip (dst, src2, src_len2);
341     }
342 
343     hw->rpos = pos % hw->samples;
344 }
345 
346 static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
347                                  dsound *s)
348 {
349     int err;
350     LPVOID p1, p2;
351     DWORD blen1, blen2, len1, len2;
352 
353     err = dsound_lock_out (
354         dsb,
355         &hw->info,
356         0,
357         hw->samples << hw->info.shift,
358         &p1, &p2,
359         &blen1, &blen2,
360         1,
361         s
362         );
363     if (err) {
364         return;
365     }
366 
367     len1 = blen1 >> hw->info.shift;
368     len2 = blen2 >> hw->info.shift;
369 
370 #ifdef DEBUG_DSOUND
371     dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
372            p1, blen1, len1,
373            p2, blen2, len2);
374 #endif
375 
376     if (p1 && len1) {
377         audio_pcm_info_clear_buf (&hw->info, p1, len1);
378     }
379 
380     if (p2 && len2) {
381         audio_pcm_info_clear_buf (&hw->info, p2, len2);
382     }
383 
384     dsound_unlock_out (dsb, p1, p2, blen1, blen2);
385 }
386 
387 static int dsound_open (dsound *s)
388 {
389     HRESULT hr;
390     HWND hwnd;
391 
392     hwnd = GetForegroundWindow ();
393     hr = IDirectSound_SetCooperativeLevel (
394         s->dsound,
395         hwnd,
396         DSSCL_PRIORITY
397         );
398 
399     if (FAILED (hr)) {
400         dsound_logerr (hr, "Could not set cooperative level for window %p\n",
401                        hwnd);
402         return -1;
403     }
404 
405     return 0;
406 }
407 
408 static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
409 {
410     HRESULT hr;
411     DWORD status;
412     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
413     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
414     dsound *s = ds->s;
415 
416     if (!dsb) {
417         dolog ("Attempt to control voice without a buffer\n");
418         return 0;
419     }
420 
421     switch (cmd) {
422     case VOICE_ENABLE:
423         if (dsound_get_status_out (dsb, &status, s)) {
424             return -1;
425         }
426 
427         if (status & DSBSTATUS_PLAYING) {
428             dolog ("warning: Voice is already playing\n");
429             return 0;
430         }
431 
432         dsound_clear_sample (hw, dsb, s);
433 
434         hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
435         if (FAILED (hr)) {
436             dsound_logerr (hr, "Could not start playing buffer\n");
437             return -1;
438         }
439         break;
440 
441     case VOICE_DISABLE:
442         if (dsound_get_status_out (dsb, &status, s)) {
443             return -1;
444         }
445 
446         if (status & DSBSTATUS_PLAYING) {
447             hr = IDirectSoundBuffer_Stop (dsb);
448             if (FAILED (hr)) {
449                 dsound_logerr (hr, "Could not stop playing buffer\n");
450                 return -1;
451             }
452         }
453         else {
454             dolog ("warning: Voice is not playing\n");
455         }
456         break;
457     }
458     return 0;
459 }
460 
461 static int dsound_write (SWVoiceOut *sw, void *buf, int len)
462 {
463     return audio_pcm_sw_write (sw, buf, len);
464 }
465 
466 static int dsound_run_out (HWVoiceOut *hw, int live)
467 {
468     int err;
469     HRESULT hr;
470     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
471     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
472     int len, hwshift;
473     DWORD blen1, blen2;
474     DWORD len1, len2;
475     DWORD decr;
476     DWORD wpos, ppos, old_pos;
477     LPVOID p1, p2;
478     int bufsize;
479     dsound *s = ds->s;
480     DSoundConf *conf = &s->conf;
481 
482     if (!dsb) {
483         dolog ("Attempt to run empty with playback buffer\n");
484         return 0;
485     }
486 
487     hwshift = hw->info.shift;
488     bufsize = hw->samples << hwshift;
489 
490     hr = IDirectSoundBuffer_GetCurrentPosition (
491         dsb,
492         &ppos,
493         ds->first_time ? &wpos : NULL
494         );
495     if (FAILED (hr)) {
496         dsound_logerr (hr, "Could not get playback buffer position\n");
497         return 0;
498     }
499 
500     len = live << hwshift;
501 
502     if (ds->first_time) {
503         if (conf->latency_millis) {
504             DWORD cur_blat;
505 
506             cur_blat = audio_ring_dist (wpos, ppos, bufsize);
507             ds->first_time = 0;
508             old_pos = wpos;
509             old_pos +=
510                 millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat;
511             old_pos %= bufsize;
512             old_pos &= ~hw->info.align;
513         }
514         else {
515             old_pos = wpos;
516         }
517 #ifdef DEBUG_DSOUND
518         ds->played = 0;
519         ds->mixed = 0;
520 #endif
521     }
522     else {
523         if (ds->old_pos == ppos) {
524 #ifdef DEBUG_DSOUND
525             dolog ("old_pos == ppos\n");
526 #endif
527             return 0;
528         }
529 
530 #ifdef DEBUG_DSOUND
531         ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize);
532 #endif
533         old_pos = ds->old_pos;
534     }
535 
536     if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
537         len = ppos - old_pos;
538     }
539     else {
540         if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) {
541             len = bufsize - old_pos + ppos;
542         }
543     }
544 
545     if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) {
546         dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n",
547                len, bufsize, old_pos, ppos);
548         return 0;
549     }
550 
551     len &= ~hw->info.align;
552     if (!len) {
553         return 0;
554     }
555 
556 #ifdef DEBUG_DSOUND
557     ds->old_ppos = ppos;
558 #endif
559     err = dsound_lock_out (
560         dsb,
561         &hw->info,
562         old_pos,
563         len,
564         &p1, &p2,
565         &blen1, &blen2,
566         0,
567         s
568         );
569     if (err) {
570         return 0;
571     }
572 
573     len1 = blen1 >> hwshift;
574     len2 = blen2 >> hwshift;
575     decr = len1 + len2;
576 
577     if (p1 && len1) {
578         dsound_write_sample (hw, p1, len1);
579     }
580 
581     if (p2 && len2) {
582         dsound_write_sample (hw, p2, len2);
583     }
584 
585     dsound_unlock_out (dsb, p1, p2, blen1, blen2);
586     ds->old_pos = (old_pos + (decr << hwshift)) % bufsize;
587 
588 #ifdef DEBUG_DSOUND
589     ds->mixed += decr << hwshift;
590 
591     dolog ("played %lu mixed %lu diff %ld sec %f\n",
592            ds->played,
593            ds->mixed,
594            ds->mixed - ds->played,
595            abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second);
596 #endif
597     return decr;
598 }
599 
600 static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
601 {
602     HRESULT hr;
603     DWORD status;
604     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
605     LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
606 
607     if (!dscb) {
608         dolog ("Attempt to control capture voice without a buffer\n");
609         return -1;
610     }
611 
612     switch (cmd) {
613     case VOICE_ENABLE:
614         if (dsound_get_status_in (dscb, &status)) {
615             return -1;
616         }
617 
618         if (status & DSCBSTATUS_CAPTURING) {
619             dolog ("warning: Voice is already capturing\n");
620             return 0;
621         }
622 
623         /* clear ?? */
624 
625         hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
626         if (FAILED (hr)) {
627             dsound_logerr (hr, "Could not start capturing\n");
628             return -1;
629         }
630         break;
631 
632     case VOICE_DISABLE:
633         if (dsound_get_status_in (dscb, &status)) {
634             return -1;
635         }
636 
637         if (status & DSCBSTATUS_CAPTURING) {
638             hr = IDirectSoundCaptureBuffer_Stop (dscb);
639             if (FAILED (hr)) {
640                 dsound_logerr (hr, "Could not stop capturing\n");
641                 return -1;
642             }
643         }
644         else {
645             dolog ("warning: Voice is not capturing\n");
646         }
647         break;
648     }
649     return 0;
650 }
651 
652 static int dsound_read (SWVoiceIn *sw, void *buf, int len)
653 {
654     return audio_pcm_sw_read (sw, buf, len);
655 }
656 
657 static int dsound_run_in (HWVoiceIn *hw)
658 {
659     int err;
660     HRESULT hr;
661     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
662     LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
663     int live, len, dead;
664     DWORD blen1, blen2;
665     DWORD len1, len2;
666     DWORD decr;
667     DWORD cpos, rpos;
668     LPVOID p1, p2;
669     int hwshift;
670     dsound *s = ds->s;
671 
672     if (!dscb) {
673         dolog ("Attempt to run without capture buffer\n");
674         return 0;
675     }
676 
677     hwshift = hw->info.shift;
678 
679     live = audio_pcm_hw_get_live_in (hw);
680     dead = hw->samples - live;
681     if (!dead) {
682         return 0;
683     }
684 
685     hr = IDirectSoundCaptureBuffer_GetCurrentPosition (
686         dscb,
687         &cpos,
688         ds->first_time ? &rpos : NULL
689         );
690     if (FAILED (hr)) {
691         dsound_logerr (hr, "Could not get capture buffer position\n");
692         return 0;
693     }
694 
695     if (ds->first_time) {
696         ds->first_time = 0;
697         if (rpos & hw->info.align) {
698             ldebug ("warning: Misaligned capture read position %ld(%d)\n",
699                     rpos, hw->info.align);
700         }
701         hw->wpos = rpos >> hwshift;
702     }
703 
704     if (cpos & hw->info.align) {
705         ldebug ("warning: Misaligned capture position %ld(%d)\n",
706                 cpos, hw->info.align);
707     }
708     cpos >>= hwshift;
709 
710     len = audio_ring_dist (cpos, hw->wpos, hw->samples);
711     if (!len) {
712         return 0;
713     }
714     len = audio_MIN (len, dead);
715 
716     err = dsound_lock_in (
717         dscb,
718         &hw->info,
719         hw->wpos << hwshift,
720         len << hwshift,
721         &p1,
722         &p2,
723         &blen1,
724         &blen2,
725         0,
726         s
727         );
728     if (err) {
729         return 0;
730     }
731 
732     len1 = blen1 >> hwshift;
733     len2 = blen2 >> hwshift;
734     decr = len1 + len2;
735 
736     if (p1 && len1) {
737         hw->conv (hw->conv_buf + hw->wpos, p1, len1);
738     }
739 
740     if (p2 && len2) {
741         hw->conv (hw->conv_buf, p2, len2);
742     }
743 
744     dsound_unlock_in (dscb, p1, p2, blen1, blen2);
745     hw->wpos = (hw->wpos + decr) % hw->samples;
746     return decr;
747 }
748 
749 static DSoundConf glob_conf = {
750     .bufsize_in         = 16384,
751     .bufsize_out        = 16384,
752     .latency_millis     = 10
753 };
754 
755 static void dsound_audio_fini (void *opaque)
756 {
757     HRESULT hr;
758     dsound *s = opaque;
759 
760     if (!s->dsound) {
761         g_free(s);
762         return;
763     }
764 
765     hr = IDirectSound_Release (s->dsound);
766     if (FAILED (hr)) {
767         dsound_logerr (hr, "Could not release DirectSound\n");
768     }
769     s->dsound = NULL;
770 
771     if (!s->dsound_capture) {
772         g_free(s);
773         return;
774     }
775 
776     hr = IDirectSoundCapture_Release (s->dsound_capture);
777     if (FAILED (hr)) {
778         dsound_logerr (hr, "Could not release DirectSoundCapture\n");
779     }
780     s->dsound_capture = NULL;
781 
782     g_free(s);
783 }
784 
785 static void *dsound_audio_init (void)
786 {
787     int err;
788     HRESULT hr;
789     dsound *s = g_malloc0(sizeof(dsound));
790 
791     s->conf = glob_conf;
792     hr = CoInitialize (NULL);
793     if (FAILED (hr)) {
794         dsound_logerr (hr, "Could not initialize COM\n");
795         g_free(s);
796         return NULL;
797     }
798 
799     hr = CoCreateInstance (
800         &CLSID_DirectSound,
801         NULL,
802         CLSCTX_ALL,
803         &IID_IDirectSound,
804         (void **) &s->dsound
805         );
806     if (FAILED (hr)) {
807         dsound_logerr (hr, "Could not create DirectSound instance\n");
808         g_free(s);
809         return NULL;
810     }
811 
812     hr = IDirectSound_Initialize (s->dsound, NULL);
813     if (FAILED (hr)) {
814         dsound_logerr (hr, "Could not initialize DirectSound\n");
815 
816         hr = IDirectSound_Release (s->dsound);
817         if (FAILED (hr)) {
818             dsound_logerr (hr, "Could not release DirectSound\n");
819         }
820         g_free(s);
821         return NULL;
822     }
823 
824     hr = CoCreateInstance (
825         &CLSID_DirectSoundCapture,
826         NULL,
827         CLSCTX_ALL,
828         &IID_IDirectSoundCapture,
829         (void **) &s->dsound_capture
830         );
831     if (FAILED (hr)) {
832         dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
833     }
834     else {
835         hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
836         if (FAILED (hr)) {
837             dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
838 
839             hr = IDirectSoundCapture_Release (s->dsound_capture);
840             if (FAILED (hr)) {
841                 dsound_logerr (hr, "Could not release DirectSoundCapture\n");
842             }
843             s->dsound_capture = NULL;
844         }
845     }
846 
847     err = dsound_open (s);
848     if (err) {
849         dsound_audio_fini (s);
850         return NULL;
851     }
852 
853     return s;
854 }
855 
856 static struct audio_option dsound_options[] = {
857     {
858         .name  = "LATENCY_MILLIS",
859         .tag   = AUD_OPT_INT,
860         .valp  = &glob_conf.latency_millis,
861         .descr = "(undocumented)"
862     },
863     {
864         .name  = "BUFSIZE_OUT",
865         .tag   = AUD_OPT_INT,
866         .valp  = &glob_conf.bufsize_out,
867         .descr = "(undocumented)"
868     },
869     {
870         .name  = "BUFSIZE_IN",
871         .tag   = AUD_OPT_INT,
872         .valp  = &glob_conf.bufsize_in,
873         .descr = "(undocumented)"
874     },
875     { /* End of list */ }
876 };
877 
878 static struct audio_pcm_ops dsound_pcm_ops = {
879     .init_out = dsound_init_out,
880     .fini_out = dsound_fini_out,
881     .run_out  = dsound_run_out,
882     .write    = dsound_write,
883     .ctl_out  = dsound_ctl_out,
884 
885     .init_in  = dsound_init_in,
886     .fini_in  = dsound_fini_in,
887     .run_in   = dsound_run_in,
888     .read     = dsound_read,
889     .ctl_in   = dsound_ctl_in
890 };
891 
892 struct audio_driver dsound_audio_driver = {
893     .name           = "dsound",
894     .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound",
895     .options        = dsound_options,
896     .init           = dsound_audio_init,
897     .fini           = dsound_audio_fini,
898     .pcm_ops        = &dsound_pcm_ops,
899     .can_be_default = 1,
900     .max_voices_out = INT_MAX,
901     .max_voices_in  = 1,
902     .voice_size_out = sizeof (DSoundVoiceOut),
903     .voice_size_in  = sizeof (DSoundVoiceIn)
904 };
905