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