2 * QEMU DirectSound audio driver
4 * Copyright (c) 2005 Vassili Karpov (malc)
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
26 * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation
29 #include "qemu/osdep.h"
30 #include "qemu-common.h"
33 #define AUDIO_CAP "dsound"
34 #include "audio_int.h"
41 #include "audio_win_int.h"
43 /* #define DEBUG_DSOUND */
53 LPDIRECTSOUNDCAPTURE dsound_capture
;
54 struct audsettings settings
;
60 LPDIRECTSOUNDBUFFER dsound_buffer
;
74 LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer
;
78 static void dsound_log_hresult (HRESULT hr
)
80 const char *str
= "BUG";
84 str
= "The method succeeded";
86 #ifdef DS_NO_VIRTUALIZATION
87 case DS_NO_VIRTUALIZATION
:
88 str
= "The buffer was created, but another 3D algorithm was substituted";
93 str
= "The method succeeded, but not all the optional effects were obtained";
96 #ifdef DSERR_ACCESSDENIED
97 case DSERR_ACCESSDENIED
:
98 str
= "The request failed because access was denied";
101 #ifdef DSERR_ALLOCATED
102 case DSERR_ALLOCATED
:
103 str
= "The request failed because resources, such as a priority level, were already in use by another caller";
106 #ifdef DSERR_ALREADYINITIALIZED
107 case DSERR_ALREADYINITIALIZED
:
108 str
= "The object is already initialized";
111 #ifdef DSERR_BADFORMAT
112 case DSERR_BADFORMAT
:
113 str
= "The specified wave format is not supported";
116 #ifdef DSERR_BADSENDBUFFERGUID
117 case DSERR_BADSENDBUFFERGUID
:
118 str
= "The GUID specified in an audiopath file does not match a valid mix-in buffer";
121 #ifdef DSERR_BUFFERLOST
122 case DSERR_BUFFERLOST
:
123 str
= "The buffer memory has been lost and must be restored";
126 #ifdef DSERR_BUFFERTOOSMALL
127 case DSERR_BUFFERTOOSMALL
:
128 str
= "The buffer size is not great enough to enable effects processing";
131 #ifdef DSERR_CONTROLUNAVAIL
132 case DSERR_CONTROLUNAVAIL
:
133 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";
136 #ifdef DSERR_DS8_REQUIRED
137 case DSERR_DS8_REQUIRED
:
138 str
= "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface";
141 #ifdef DSERR_FXUNAVAILABLE
142 case DSERR_FXUNAVAILABLE
:
143 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";
148 str
= "An undetermined error occurred inside the DirectSound subsystem";
151 #ifdef DSERR_INVALIDCALL
152 case DSERR_INVALIDCALL
:
153 str
= "This function is not valid for the current state of this object";
156 #ifdef DSERR_INVALIDPARAM
157 case DSERR_INVALIDPARAM
:
158 str
= "An invalid parameter was passed to the returning function";
161 #ifdef DSERR_NOAGGREGATION
162 case DSERR_NOAGGREGATION
:
163 str
= "The object does not support aggregation";
166 #ifdef DSERR_NODRIVER
168 str
= "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID";
171 #ifdef DSERR_NOINTERFACE
172 case DSERR_NOINTERFACE
:
173 str
= "The requested COM interface is not available";
176 #ifdef DSERR_OBJECTNOTFOUND
177 case DSERR_OBJECTNOTFOUND
:
178 str
= "The requested object was not found";
181 #ifdef DSERR_OTHERAPPHASPRIO
182 case DSERR_OTHERAPPHASPRIO
:
183 str
= "Another application has a higher priority level, preventing this call from succeeding";
186 #ifdef DSERR_OUTOFMEMORY
187 case DSERR_OUTOFMEMORY
:
188 str
= "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request";
191 #ifdef DSERR_PRIOLEVELNEEDED
192 case DSERR_PRIOLEVELNEEDED
:
193 str
= "A cooperative level of DSSCL_PRIORITY or higher is required";
196 #ifdef DSERR_SENDLOOP
198 str
= "A circular loop of send effects was detected";
201 #ifdef DSERR_UNINITIALIZED
202 case DSERR_UNINITIALIZED
:
203 str
= "The Initialize method has not been called or has not been called successfully before other methods were called";
206 #ifdef DSERR_UNSUPPORTED
207 case DSERR_UNSUPPORTED
:
208 str
= "The function called is not supported at this time";
212 AUD_log (AUDIO_CAP
, "Reason: Unknown (HRESULT %#lx)\n", hr
);
216 AUD_log (AUDIO_CAP
, "Reason: %s\n", str
);
219 static void GCC_FMT_ATTR (2, 3) dsound_logerr (
228 AUD_vlog (AUDIO_CAP
, fmt
, ap
);
231 dsound_log_hresult (hr
);
234 static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
243 AUD_log (AUDIO_CAP
, "Could not initialize %s\n", typ
);
245 AUD_vlog (AUDIO_CAP
, fmt
, ap
);
248 dsound_log_hresult (hr
);
251 static DWORD
millis_to_bytes (struct audio_pcm_info
*info
, DWORD millis
)
253 return (millis
* info
->bytes_per_second
) / 1000;
257 static void print_wave_format (WAVEFORMATEX
*wfx
)
259 dolog ("tag = %d\n", wfx
->wFormatTag
);
260 dolog ("nChannels = %d\n", wfx
->nChannels
);
261 dolog ("nSamplesPerSec = %ld\n", wfx
->nSamplesPerSec
);
262 dolog ("nAvgBytesPerSec = %ld\n", wfx
->nAvgBytesPerSec
);
263 dolog ("nBlockAlign = %d\n", wfx
->nBlockAlign
);
264 dolog ("wBitsPerSample = %d\n", wfx
->wBitsPerSample
);
265 dolog ("cbSize = %d\n", wfx
->cbSize
);
269 static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb
, dsound
*s
)
273 hr
= IDirectSoundBuffer_Restore (dsb
);
276 dsound_logerr (hr
, "Could not restore playback buffer\n");
282 #include "dsound_template.h"
284 #include "dsound_template.h"
287 static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb
, DWORD
*statusp
,
292 hr
= IDirectSoundBuffer_GetStatus (dsb
, statusp
);
294 dsound_logerr (hr
, "Could not get playback buffer status\n");
298 if (*statusp
& DSERR_BUFFERLOST
) {
299 dsound_restore_out(dsb
, s
);
306 static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb
,
311 hr
= IDirectSoundCaptureBuffer_GetStatus (dscb
, statusp
);
313 dsound_logerr (hr
, "Could not get capture buffer status\n");
320 static void dsound_write_sample (HWVoiceOut
*hw
, uint8_t *dst
, int dst_len
)
322 int src_len1
= dst_len
;
324 int pos
= hw
->rpos
+ dst_len
;
325 struct st_sample
*src1
= hw
->mix_buf
+ hw
->rpos
;
326 struct st_sample
*src2
= NULL
;
328 if (pos
> hw
->samples
) {
329 src_len1
= hw
->samples
- hw
->rpos
;
331 src_len2
= dst_len
- src_len1
;
336 hw
->clip (dst
, src1
, src_len1
);
340 dst
= advance (dst
, src_len1
<< hw
->info
.shift
);
341 hw
->clip (dst
, src2
, src_len2
);
344 hw
->rpos
= pos
% hw
->samples
;
347 static void dsound_clear_sample (HWVoiceOut
*hw
, LPDIRECTSOUNDBUFFER dsb
,
352 DWORD blen1
, blen2
, len1
, len2
;
354 err
= dsound_lock_out (
358 hw
->samples
<< hw
->info
.shift
,
368 len1
= blen1
>> hw
->info
.shift
;
369 len2
= blen2
>> hw
->info
.shift
;
372 dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
378 audio_pcm_info_clear_buf (&hw
->info
, p1
, len1
);
382 audio_pcm_info_clear_buf (&hw
->info
, p2
, len2
);
385 dsound_unlock_out (dsb
, p1
, p2
, blen1
, blen2
);
388 static int dsound_open (dsound
*s
)
393 hwnd
= GetForegroundWindow ();
394 hr
= IDirectSound_SetCooperativeLevel (
401 dsound_logerr (hr
, "Could not set cooperative level for window %p\n",
409 static int dsound_ctl_out (HWVoiceOut
*hw
, int cmd
, ...)
413 DSoundVoiceOut
*ds
= (DSoundVoiceOut
*) hw
;
414 LPDIRECTSOUNDBUFFER dsb
= ds
->dsound_buffer
;
418 dolog ("Attempt to control voice without a buffer\n");
424 if (dsound_get_status_out (dsb
, &status
, s
)) {
428 if (status
& DSBSTATUS_PLAYING
) {
429 dolog ("warning: Voice is already playing\n");
433 dsound_clear_sample (hw
, dsb
, s
);
435 hr
= IDirectSoundBuffer_Play (dsb
, 0, 0, DSBPLAY_LOOPING
);
437 dsound_logerr (hr
, "Could not start playing buffer\n");
443 if (dsound_get_status_out (dsb
, &status
, s
)) {
447 if (status
& DSBSTATUS_PLAYING
) {
448 hr
= IDirectSoundBuffer_Stop (dsb
);
450 dsound_logerr (hr
, "Could not stop playing buffer\n");
455 dolog ("warning: Voice is not playing\n");
462 static int dsound_write (SWVoiceOut
*sw
, void *buf
, int len
)
464 return audio_pcm_sw_write (sw
, buf
, len
);
467 static int dsound_run_out (HWVoiceOut
*hw
, int live
)
471 DSoundVoiceOut
*ds
= (DSoundVoiceOut
*) hw
;
472 LPDIRECTSOUNDBUFFER dsb
= ds
->dsound_buffer
;
477 DWORD wpos
, ppos
, old_pos
;
481 DSoundConf
*conf
= &s
->conf
;
484 dolog ("Attempt to run empty with playback buffer\n");
488 hwshift
= hw
->info
.shift
;
489 bufsize
= hw
->samples
<< hwshift
;
491 hr
= IDirectSoundBuffer_GetCurrentPosition (
494 ds
->first_time
? &wpos
: NULL
497 dsound_logerr (hr
, "Could not get playback buffer position\n");
501 len
= live
<< hwshift
;
503 if (ds
->first_time
) {
504 if (conf
->latency_millis
) {
507 cur_blat
= audio_ring_dist (wpos
, ppos
, bufsize
);
511 millis_to_bytes (&hw
->info
, conf
->latency_millis
) - cur_blat
;
513 old_pos
&= ~hw
->info
.align
;
524 if (ds
->old_pos
== ppos
) {
526 dolog ("old_pos == ppos\n");
532 ds
->played
+= audio_ring_dist (ds
->old_pos
, ppos
, hw
->bufsize
);
534 old_pos
= ds
->old_pos
;
537 if ((old_pos
< ppos
) && ((old_pos
+ len
) > ppos
)) {
538 len
= ppos
- old_pos
;
541 if ((old_pos
> ppos
) && ((old_pos
+ len
) > (ppos
+ bufsize
))) {
542 len
= bufsize
- old_pos
+ ppos
;
546 if (audio_bug(__func__
, len
< 0 || len
> bufsize
)) {
547 dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n",
548 len
, bufsize
, old_pos
, ppos
);
552 len
&= ~hw
->info
.align
;
560 err
= dsound_lock_out (
574 len1
= blen1
>> hwshift
;
575 len2
= blen2
>> hwshift
;
579 dsound_write_sample (hw
, p1
, len1
);
583 dsound_write_sample (hw
, p2
, len2
);
586 dsound_unlock_out (dsb
, p1
, p2
, blen1
, blen2
);
587 ds
->old_pos
= (old_pos
+ (decr
<< hwshift
)) % bufsize
;
590 ds
->mixed
+= decr
<< hwshift
;
592 dolog ("played %lu mixed %lu diff %ld sec %f\n",
595 ds
->mixed
- ds
->played
,
596 abs (ds
->mixed
- ds
->played
) / (double) hw
->info
.bytes_per_second
);
601 static int dsound_ctl_in (HWVoiceIn
*hw
, int cmd
, ...)
605 DSoundVoiceIn
*ds
= (DSoundVoiceIn
*) hw
;
606 LPDIRECTSOUNDCAPTUREBUFFER dscb
= ds
->dsound_capture_buffer
;
609 dolog ("Attempt to control capture voice without a buffer\n");
615 if (dsound_get_status_in (dscb
, &status
)) {
619 if (status
& DSCBSTATUS_CAPTURING
) {
620 dolog ("warning: Voice is already capturing\n");
626 hr
= IDirectSoundCaptureBuffer_Start (dscb
, DSCBSTART_LOOPING
);
628 dsound_logerr (hr
, "Could not start capturing\n");
634 if (dsound_get_status_in (dscb
, &status
)) {
638 if (status
& DSCBSTATUS_CAPTURING
) {
639 hr
= IDirectSoundCaptureBuffer_Stop (dscb
);
641 dsound_logerr (hr
, "Could not stop capturing\n");
646 dolog ("warning: Voice is not capturing\n");
653 static int dsound_read (SWVoiceIn
*sw
, void *buf
, int len
)
655 return audio_pcm_sw_read (sw
, buf
, len
);
658 static int dsound_run_in (HWVoiceIn
*hw
)
662 DSoundVoiceIn
*ds
= (DSoundVoiceIn
*) hw
;
663 LPDIRECTSOUNDCAPTUREBUFFER dscb
= ds
->dsound_capture_buffer
;
674 dolog ("Attempt to run without capture buffer\n");
678 hwshift
= hw
->info
.shift
;
680 live
= audio_pcm_hw_get_live_in (hw
);
681 dead
= hw
->samples
- live
;
686 hr
= IDirectSoundCaptureBuffer_GetCurrentPosition (
689 ds
->first_time
? &rpos
: NULL
692 dsound_logerr (hr
, "Could not get capture buffer position\n");
696 if (ds
->first_time
) {
698 if (rpos
& hw
->info
.align
) {
699 ldebug ("warning: Misaligned capture read position %ld(%d)\n",
700 rpos
, hw
->info
.align
);
702 hw
->wpos
= rpos
>> hwshift
;
705 if (cpos
& hw
->info
.align
) {
706 ldebug ("warning: Misaligned capture position %ld(%d)\n",
707 cpos
, hw
->info
.align
);
711 len
= audio_ring_dist (cpos
, hw
->wpos
, hw
->samples
);
715 len
= audio_MIN (len
, dead
);
717 err
= dsound_lock_in (
733 len1
= blen1
>> hwshift
;
734 len2
= blen2
>> hwshift
;
738 hw
->conv (hw
->conv_buf
+ hw
->wpos
, p1
, len1
);
742 hw
->conv (hw
->conv_buf
, p2
, len2
);
745 dsound_unlock_in (dscb
, p1
, p2
, blen1
, blen2
);
746 hw
->wpos
= (hw
->wpos
+ decr
) % hw
->samples
;
750 static DSoundConf glob_conf
= {
752 .bufsize_out
= 16384,
756 static void dsound_audio_fini (void *opaque
)
766 hr
= IDirectSound_Release (s
->dsound
);
768 dsound_logerr (hr
, "Could not release DirectSound\n");
772 if (!s
->dsound_capture
) {
777 hr
= IDirectSoundCapture_Release (s
->dsound_capture
);
779 dsound_logerr (hr
, "Could not release DirectSoundCapture\n");
781 s
->dsound_capture
= NULL
;
786 static void *dsound_audio_init (void)
790 dsound
*s
= g_malloc0(sizeof(dsound
));
793 hr
= CoInitialize (NULL
);
795 dsound_logerr (hr
, "Could not initialize COM\n");
800 hr
= CoCreateInstance (
808 dsound_logerr (hr
, "Could not create DirectSound instance\n");
813 hr
= IDirectSound_Initialize (s
->dsound
, NULL
);
815 dsound_logerr (hr
, "Could not initialize DirectSound\n");
817 hr
= IDirectSound_Release (s
->dsound
);
819 dsound_logerr (hr
, "Could not release DirectSound\n");
825 hr
= CoCreateInstance (
826 &CLSID_DirectSoundCapture
,
829 &IID_IDirectSoundCapture
,
830 (void **) &s
->dsound_capture
833 dsound_logerr (hr
, "Could not create DirectSoundCapture instance\n");
836 hr
= IDirectSoundCapture_Initialize (s
->dsound_capture
, NULL
);
838 dsound_logerr (hr
, "Could not initialize DirectSoundCapture\n");
840 hr
= IDirectSoundCapture_Release (s
->dsound_capture
);
842 dsound_logerr (hr
, "Could not release DirectSoundCapture\n");
844 s
->dsound_capture
= NULL
;
848 err
= dsound_open (s
);
850 dsound_audio_fini (s
);
857 static struct audio_option dsound_options
[] = {
859 .name
= "LATENCY_MILLIS",
861 .valp
= &glob_conf
.latency_millis
,
862 .descr
= "(undocumented)"
865 .name
= "BUFSIZE_OUT",
867 .valp
= &glob_conf
.bufsize_out
,
868 .descr
= "(undocumented)"
871 .name
= "BUFSIZE_IN",
873 .valp
= &glob_conf
.bufsize_in
,
874 .descr
= "(undocumented)"
876 { /* End of list */ }
879 static struct audio_pcm_ops dsound_pcm_ops
= {
880 .init_out
= dsound_init_out
,
881 .fini_out
= dsound_fini_out
,
882 .run_out
= dsound_run_out
,
883 .write
= dsound_write
,
884 .ctl_out
= dsound_ctl_out
,
886 .init_in
= dsound_init_in
,
887 .fini_in
= dsound_fini_in
,
888 .run_in
= dsound_run_in
,
890 .ctl_in
= dsound_ctl_in
893 struct audio_driver dsound_audio_driver
= {
895 .descr
= "DirectSound http://wikipedia.org/wiki/DirectSound",
896 .options
= dsound_options
,
897 .init
= dsound_audio_init
,
898 .fini
= dsound_audio_fini
,
899 .pcm_ops
= &dsound_pcm_ops
,
901 .max_voices_out
= INT_MAX
,
903 .voice_size_out
= sizeof (DSoundVoiceOut
),
904 .voice_size_in
= sizeof (DSoundVoiceIn
)