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