VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/dsoundaudio.c@ 2390

Last change on this file since 2390 was 1071, checked in by vboxsync, 18 years ago

fix Vista crash if sound is enabled; made audio LogRel consistent; winmm was never used, show dsound instead

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette