VirtualBox

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

Last change on this file since 54336 was 54336, checked in by vboxsync, 10 years ago

Audio: dsoundaudio use correct capture buffer position

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.8 KB
Line 
1/* $Id: dsoundaudio.c 54336 2015-02-20 17:02:04Z vboxsync $ */
2/** @file
3 * DirectSound Windows Host Audio Backend.
4 */
5
6/*
7 * Copyright (C) 2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 * --------------------------------------------------------------------
17 *
18 * QEMU DirectSound audio driver
19 *
20 * Copyright (c) 2005 Vassili Karpov (malc)
21 *
22 * Permission is hereby granted, free of charge, to any person obtaining a copy
23 * of this software and associated documentation files (the "Software"), to deal
24 * in the Software without restriction, including without limitation the rights
25 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
26 * copies of the Software, and to permit persons to whom the Software is
27 * furnished to do so, subject to the following conditions:
28 *
29 * The above copyright notice and this permission notice shall be included in
30 * all copies or substantial portions of the Software.
31 *
32 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
35 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
38 * THE SOFTWARE.
39 */
40
41/*
42 * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation
43 */
44
45#define LOG_GROUP LOG_GROUP_DEV_AUDIO
46#define _WIN32_DCOM
47#include <windows.h>
48#include <objbase.h>
49#include <dsound.h>
50
51#include "VBoxDD.h"
52#include "vl_vbox.h"
53#include "audio.h"
54#include <iprt/alloc.h>
55#include <iprt/uuid.h>
56#include <VBox/log.h>
57
58
59#define AUDIO_CAP "dsound"
60#include "audio_int.h"
61
62#define DSLOGF(a) do { LogRel2(a); } while(0)
63#define DSLOGREL(a) \
64 do { \
65 static int8_t scLogged = 0; \
66 if (scLogged < 8) { \
67 ++scLogged; \
68 LogRel(a); \
69 } \
70 else { \
71 DSLOGF(a); \
72 } \
73 } while (0)
74
75static struct {
76 int lock_retries;
77 int restore_retries;
78 int getstatus_retries;
79 int bufsize_in;
80 int bufsize_out;
81 int latency_millis;
82 char *device_guid_out;
83 char *device_guid_in;
84} conf = {
85 1,
86 1,
87 1,
88 16384,
89 16384,
90 10,
91 NULL,
92 NULL
93};
94
95typedef struct {
96 RTUUID devguid_play;
97 LPCGUID devguidp_play;
98 RTUUID devguid_capture;
99 LPCGUID devguidp_capture;
100} dsound;
101
102static dsound glob_dsound;
103
104typedef struct {
105 HWVoiceOut hw;
106 LPDIRECTSOUND dsound;
107 LPDIRECTSOUNDBUFFER dsound_buffer;
108 DWORD old_pos;
109 int first_time;
110 int playback_buffer_size;
111 audsettings_t as;
112} DSoundVoiceOut;
113
114typedef struct {
115 HWVoiceIn hw;
116 int last_read_pos;
117 int capture_buffer_size;
118 LPDIRECTSOUNDCAPTURE dsound_capture;
119 LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
120 audsettings_t as;
121 HRESULT hr_last_run_in;
122} DSoundVoiceIn;
123
124static void dsound_clear_sample (DSoundVoiceOut *ds);
125
126static void dsound_log_hresult (HRESULT hr)
127{
128 const char *str = "BUG";
129
130 switch (hr) {
131 case DS_OK:
132 str = "The method succeeded";
133 break;
134#ifdef DS_NO_VIRTUALIZATION
135 case DS_NO_VIRTUALIZATION:
136 str = "The buffer was created, but another 3D algorithm was substituted";
137 break;
138#endif
139#ifdef DS_INCOMPLETE
140 case DS_INCOMPLETE:
141 str = "The method succeeded, but not all the optional effects were obtained";
142 break;
143#endif
144#ifdef DSERR_ACCESSDENIED
145 case DSERR_ACCESSDENIED:
146 str = "The request failed because access was denied";
147 break;
148#endif
149#ifdef DSERR_ALLOCATED
150 case DSERR_ALLOCATED:
151 str = "The request failed because resources, such as a priority level, were already in use by another caller";
152 break;
153#endif
154#ifdef DSERR_ALREADYINITIALIZED
155 case DSERR_ALREADYINITIALIZED:
156 str = "The object is already initialized";
157 break;
158#endif
159#ifdef DSERR_BADFORMAT
160 case DSERR_BADFORMAT:
161 str = "The specified wave format is not supported";
162 break;
163#endif
164#ifdef DSERR_BADSENDBUFFERGUID
165 case DSERR_BADSENDBUFFERGUID:
166 str = "The GUID specified in an audiopath file does not match a valid mix-in buffer";
167 break;
168#endif
169#ifdef DSERR_BUFFERLOST
170 case DSERR_BUFFERLOST:
171 str = "The buffer memory has been lost and must be restored";
172 break;
173#endif
174#ifdef DSERR_BUFFERTOOSMALL
175 case DSERR_BUFFERTOOSMALL:
176 str = "The buffer size is not great enough to enable effects processing";
177 break;
178#endif
179#ifdef DSERR_CONTROLUNAVAIL
180 case DSERR_CONTROLUNAVAIL:
181 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";
182 break;
183#endif
184#ifdef DSERR_DS8_REQUIRED
185 case DSERR_DS8_REQUIRED:
186 str = "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface";
187 break;
188#endif
189#ifdef DSERR_FXUNAVAILABLE
190 case DSERR_FXUNAVAILABLE:
191 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";
192 break;
193#endif
194#ifdef DSERR_GENERIC
195 case DSERR_GENERIC :
196 str = "An undetermined error occurred inside the DirectSound subsystem";
197 break;
198#endif
199#ifdef DSERR_INVALIDCALL
200 case DSERR_INVALIDCALL:
201 str = "This function is not valid for the current state of this object";
202 break;
203#endif
204#ifdef DSERR_INVALIDPARAM
205 case DSERR_INVALIDPARAM:
206 str = "An invalid parameter was passed to the returning function";
207 break;
208#endif
209#ifdef DSERR_NOAGGREGATION
210 case DSERR_NOAGGREGATION:
211 str = "The object does not support aggregation";
212 break;
213#endif
214#ifdef DSERR_NODRIVER
215 case DSERR_NODRIVER:
216 str = "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID";
217 break;
218#endif
219#ifdef DSERR_NOINTERFACE
220 case DSERR_NOINTERFACE:
221 str = "The requested COM interface is not available";
222 break;
223#endif
224#ifdef DSERR_OBJECTNOTFOUND
225 case DSERR_OBJECTNOTFOUND:
226 str = "The requested object was not found";
227 break;
228#endif
229#ifdef DSERR_OTHERAPPHASPRIO
230 case DSERR_OTHERAPPHASPRIO:
231 str = "Another application has a higher priority level, preventing this call from succeeding";
232 break;
233#endif
234#ifdef DSERR_OUTOFMEMORY
235 case DSERR_OUTOFMEMORY:
236 str = "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request";
237 break;
238#endif
239#ifdef DSERR_PRIOLEVELNEEDED
240 case DSERR_PRIOLEVELNEEDED:
241 str = "A cooperative level of DSSCL_PRIORITY or higher is required";
242 break;
243#endif
244#ifdef DSERR_SENDLOOP
245 case DSERR_SENDLOOP:
246 str = "A circular loop of send effects was detected";
247 break;
248#endif
249#ifdef DSERR_UNINITIALIZED
250 case DSERR_UNINITIALIZED:
251 str = "The Initialize method has not been called or has not been called successfully before other methods were called";
252 break;
253#endif
254#ifdef DSERR_UNSUPPORTED
255 case DSERR_UNSUPPORTED:
256 str = "The function called is not supported at this time";
257 break;
258#endif
259 default:
260 AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT %#lx)\n", hr);
261 return;
262 }
263
264 AUD_log (AUDIO_CAP, "Reason: %s\n", str);
265}
266
267static void GCC_FMT_ATTR (2, 3) dsound_logerr (
268 HRESULT hr,
269 const char *fmt,
270 ...
271 )
272{
273 va_list ap;
274
275 va_start (ap, fmt);
276 AUD_vlog (AUDIO_CAP, fmt, ap);
277 va_end (ap);
278
279 dsound_log_hresult (hr);
280}
281
282static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
283 HRESULT hr,
284 const char *typ,
285 const char *fmt,
286 ...
287 )
288{
289 va_list ap;
290
291 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
292 va_start (ap, fmt);
293 AUD_vlog (AUDIO_CAP, fmt, ap);
294 va_end (ap);
295
296 dsound_log_hresult (hr);
297}
298
299static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
300{
301 return (millis * info->bytes_per_second) / 1000;
302}
303
304static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
305{
306 HRESULT hr;
307 int i;
308
309 for (i = 0; i < conf.restore_retries; ++i) {
310 hr = IDirectSoundBuffer_Restore (dsb);
311
312 switch (hr) {
313 case DS_OK:
314 return 0;
315
316 case DSERR_BUFFERLOST:
317 continue;
318
319 default:
320 DSLOGREL(("DSound: restore playback buffer %Rhrc\n", hr));
321 return -1;
322 }
323 }
324
325 DSLOGF(("DSound: %d attempts to restore playback buffer failed\n", i));
326 return -1;
327}
328
329static int waveformat_from_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
330{
331 memset (wfx, 0, sizeof (*wfx));
332
333 wfx->wFormatTag = WAVE_FORMAT_PCM;
334 wfx->nChannels = as->nchannels;
335 wfx->nSamplesPerSec = as->freq;
336 wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2);
337 wfx->nBlockAlign = 1 << (as->nchannels == 2);
338 wfx->cbSize = 0;
339
340 switch (as->fmt) {
341 case AUD_FMT_S8:
342 case AUD_FMT_U8:
343 wfx->wBitsPerSample = 8;
344 break;
345
346 case AUD_FMT_S16:
347 case AUD_FMT_U16:
348 wfx->wBitsPerSample = 16;
349 wfx->nAvgBytesPerSec <<= 1;
350 wfx->nBlockAlign <<= 1;
351 break;
352
353 case AUD_FMT_S32:
354 case AUD_FMT_U32:
355 wfx->wBitsPerSample = 32;
356 wfx->nAvgBytesPerSec <<= 2;
357 wfx->nBlockAlign <<= 2;
358 break;
359
360 default:
361 dolog ("Internal logic error: Bad audio format %d\n", as->freq);
362 return -1;
363 }
364
365 return 0;
366}
367
368static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
369{
370 if (wfx->wFormatTag != WAVE_FORMAT_PCM) {
371 dolog ("Invalid wave format, tag is not PCM, but %d\n",
372 wfx->wFormatTag);
373 return -1;
374 }
375
376 if (!wfx->nSamplesPerSec) {
377 dolog ("Invalid wave format, frequency is zero\n");
378 return -1;
379 }
380 as->freq = wfx->nSamplesPerSec;
381
382 switch (wfx->nChannels) {
383 case 1:
384 as->nchannels = 1;
385 break;
386
387 case 2:
388 as->nchannels = 2;
389 break;
390
391 default:
392 dolog (
393 "Invalid wave format, number of channels is not 1 or 2, but %d\n",
394 wfx->nChannels
395 );
396 return -1;
397 }
398
399 switch (wfx->wBitsPerSample) {
400 case 8:
401 as->fmt = AUD_FMT_U8;
402 break;
403
404 case 16:
405 as->fmt = AUD_FMT_S16;
406 break;
407
408 case 32:
409 as->fmt = AUD_FMT_S32;
410 break;
411
412 default:
413 dolog ("Invalid wave format, bits per sample is not "
414 "8, 16 or 32, but %d\n",
415 wfx->wBitsPerSample);
416 return -1;
417 }
418
419 return 0;
420}
421
422/*
423 * DirectSound playback
424 */
425
426static void dsoundPlayInterfaceRelease (DSoundVoiceOut *ds)
427{
428 if (ds->dsound) {
429 IDirectSound_Release (ds->dsound);
430 ds->dsound = NULL;
431 }
432}
433
434static int dsoundPlayInterfaceCreate (DSoundVoiceOut *ds)
435{
436 dsound *s = &glob_dsound;
437
438 HRESULT hr;
439
440 if (ds->dsound != NULL) {
441 DSLOGF(("DSound: DirectSound instance already exists\n"));
442 return 0;
443 }
444
445 hr = CoCreateInstance (&CLSID_DirectSound, NULL, CLSCTX_ALL,
446 &IID_IDirectSound, (void **) &ds->dsound);
447 if (FAILED (hr)) {
448 DSLOGREL(("DSound: DirectSound create instance %Rhrc\n", hr));
449 }
450 else {
451 hr = IDirectSound_Initialize (ds->dsound, s->devguidp_play);
452 if (SUCCEEDED(hr)) {
453 HWND hwnd = GetDesktopWindow ();
454 hr = IDirectSound_SetCooperativeLevel (ds->dsound, hwnd, DSSCL_PRIORITY);
455 if (FAILED (hr)) {
456 DSLOGREL(("DSound: set cooperative level for window %p %Rhrc\n", hwnd, hr));
457 }
458 }
459 if (FAILED (hr)) {
460 if (hr == DSERR_NODRIVER) {
461 DSLOGREL(("DSound: DirectSound playback is currently unavailable\n"));
462 }
463 else {
464 DSLOGREL(("DSound: DirectSound initialize %Rhrc\n", hr));
465 }
466 dsoundPlayInterfaceRelease (ds);
467 }
468 }
469
470 return SUCCEEDED (hr)? 0: -1;
471}
472
473static void dsoundPlayClose (DSoundVoiceOut *ds)
474{
475 dsound *s = &glob_dsound;
476
477 HRESULT hr;
478
479 DSLOGF(("DSound: playback close %p buffer %p\n", ds, ds->dsound_buffer));
480
481 if (ds->dsound_buffer) {
482 hr = IDirectSoundBuffer_Stop (ds->dsound_buffer);
483 if (FAILED (hr)) {
484 DSLOGREL(("DSound: playback close Stop %Rhrc\n", hr));
485 }
486
487 IDirectSoundBuffer_Release (ds->dsound_buffer);
488 ds->dsound_buffer = NULL;
489 }
490
491 dsoundPlayInterfaceRelease (ds);
492}
493
494static int dsoundPlayOpen (DSoundVoiceOut *ds)
495{
496 dsound *s = &glob_dsound;
497
498 int err;
499 HRESULT hr;
500 WAVEFORMATEX wfx;
501 DSBUFFERDESC bd;
502 DSBCAPS bc;
503
504 DSLOGF(("DSound: playback open %p size %d samples, freq %d, chan %d, bits %d, sign %d\n",
505 ds,
506 ds->hw.samples,
507 ds->hw.info.freq,
508 ds->hw.info.nchannels,
509 ds->hw.info.bits,
510 ds->hw.info.sign));
511
512 if (ds->dsound_buffer != NULL) {
513 /* Should not happen but be forgiving. */
514 DSLOGREL(("DSound: DirectSoundBuffer already exists\n"));
515 dsoundPlayClose (ds);
516 }
517
518 err = waveformat_from_audio_settings (&wfx, &ds->as);
519 if (err) {
520 return err;
521 }
522
523 err = dsoundPlayInterfaceCreate (ds);
524 if (err) {
525 return err;
526 }
527
528 memset (&bd, 0, sizeof (bd));
529 bd.dwSize = sizeof (bd);
530 bd.lpwfxFormat = &wfx;
531 bd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
532 bd.dwBufferBytes = ds->hw.samples << ds->hw.info.shift;
533 hr = IDirectSound_CreateSoundBuffer (ds->dsound,
534 &bd, &ds->dsound_buffer, NULL);
535 if (FAILED (hr)) {
536 DSLOGREL(("DSound: playback CreateSoundBuffer %Rhrc\n", hr));
537 goto fail0;
538 }
539
540 /* Query the actual parameters. */
541
542 hr = IDirectSoundBuffer_GetFormat (ds->dsound_buffer, &wfx, sizeof (wfx), NULL);
543 if (FAILED (hr)) {
544 DSLOGREL(("DSound: playback GetFormat %Rhrc\n", hr));
545 goto fail0;
546 }
547
548 memset (&bc, 0, sizeof (bc));
549 bc.dwSize = sizeof (bc);
550 hr = IDirectSoundBuffer_GetCaps (ds->dsound_buffer, &bc);
551 if (FAILED (hr)) {
552 DSLOGREL(("DSound: playback GetCaps %Rhrc\n", hr));
553 goto fail0;
554 }
555
556 DSLOGF(("DSound: playback format: size %d bytes\n"
557 " tag = %d\n"
558 " nChannels = %d\n"
559 " nSamplesPerSec = %d\n"
560 " nAvgBytesPerSec = %d\n"
561 " nBlockAlign = %d\n"
562 " wBitsPerSample = %d\n"
563 " cbSize = %d\n",
564 bc.dwBufferBytes,
565 wfx.wFormatTag,
566 wfx.nChannels,
567 wfx.nSamplesPerSec,
568 wfx.nAvgBytesPerSec,
569 wfx.nBlockAlign,
570 wfx.wBitsPerSample,
571 wfx.cbSize));
572
573 if (bc.dwBufferBytes & ds->hw.info.align) {
574 DSLOGREL(("DSound: playback GetCaps returned misaligned buffer size %ld, alignment %d\n",
575 bc.dwBufferBytes, ds->hw.info.align + 1));
576 }
577
578 if (ds->hw.samples != 0 && ds->hw.samples != (bc.dwBufferBytes >> ds->hw.info.shift)) {
579 DSLOGREL(("DSound: playback buffer size mismatch dsound %d, hw %d bytes\n",
580 bc.dwBufferBytes, ds->hw.samples << ds->hw.info.shift));
581 }
582
583 /* Initial state.
584 * dsoundPlayStart initializes part of it to make sure that Stop/Start continues with a correct
585 * playback buffer position.
586 */
587 ds->playback_buffer_size = bc.dwBufferBytes >> ds->hw.info.shift;
588 DSLOGF(("DSound: playback open playback_buffer_size %d\n", ds->playback_buffer_size));
589
590 return 0;
591
592 fail0:
593 dsoundPlayClose (ds);
594 return -1;
595}
596
597static int dsoundPlayGetStatus (DSoundVoiceOut *ds, DWORD *statusp)
598{
599 HRESULT hr;
600 DWORD status;
601 int i;
602
603 if (ds->dsound_buffer != NULL) {
604 for (i = 0; i < RT_MAX(conf.getstatus_retries, 1); ++i) {
605 hr = IDirectSoundBuffer_GetStatus (ds->dsound_buffer, &status);
606 if (FAILED (hr)) {
607 DSLOGF(("DSound: playback start GetStatus %Rhrc\n", hr));
608 break;
609 }
610
611 if ((status & DSBSTATUS_BUFFERLOST) == 0) {
612 break;
613 }
614
615 if (dsound_restore_out (ds->dsound_buffer)) {
616 hr = E_FAIL;
617 break;
618 }
619 }
620 }
621 else
622 {
623 hr = E_FAIL;
624 }
625
626 if (SUCCEEDED (hr)) {
627 *statusp = status;
628 return 0;
629 }
630 return -1;
631}
632
633static void dsoundPlayStop (DSoundVoiceOut *ds)
634{
635 HRESULT hr;
636 DWORD status;
637
638 if (ds->dsound_buffer != NULL) {
639 /* This performs some restore, so call it anyway and ignore result. */
640 dsoundPlayGetStatus (ds, &status);
641
642 hr = IDirectSoundBuffer_Stop (ds->dsound_buffer);
643 if (FAILED (hr)) {
644 DSLOGF(("DSound: stop playback buffer %Rhrc\n", hr));
645 }
646 }
647}
648
649static int dsoundPlayStart (DSoundVoiceOut *ds)
650{
651 HRESULT hr;
652 DWORD status;
653
654 if (ds->dsound_buffer != NULL) {
655 if (dsoundPlayGetStatus (ds, &status)) {
656 DSLOGF(("DSound: playback start GetStatus failed\n"));
657 hr = E_FAIL;
658 }
659 else {
660 if (status & DSBSTATUS_PLAYING) {
661 DSLOGF(("DSound: already playing\n"));
662 }
663 else {
664 dsound_clear_sample (ds);
665
666 /* Reinit the playback buffer position. */
667 ds->first_time = 1;
668
669 DSLOGF(("DSound: playback start\n"));
670
671 hr = IDirectSoundBuffer_Play (ds->dsound_buffer, 0, 0, DSBPLAY_LOOPING);
672 if (FAILED (hr)) {
673 DSLOGREL(("DSound: playback start %Rhrc\n", hr));
674 }
675 }
676 }
677 }
678 else {
679 hr = E_FAIL;
680 }
681
682 return SUCCEEDED (hr)? 0: -1;
683}
684
685/*
686 * DirectSoundCapture
687 */
688
689static void dsoundCaptureInterfaceRelease (DSoundVoiceIn *ds)
690{
691 if (ds->dsound_capture) {
692 IDirectSoundCapture_Release (ds->dsound_capture);
693 ds->dsound_capture = NULL;
694 }
695}
696
697static int dsoundCaptureInterfaceCreate (DSoundVoiceIn *ds)
698{
699 dsound *s = &glob_dsound;
700
701 HRESULT hr;
702
703 if (ds->dsound_capture != NULL) {
704 DSLOGF(("DSound: DirectSoundCapture instance already exists\n"));
705 return 0;
706 }
707
708 hr = CoCreateInstance (&CLSID_DirectSoundCapture, NULL, CLSCTX_ALL,
709 &IID_IDirectSoundCapture, (void **) &ds->dsound_capture);
710 if (FAILED (hr)) {
711 DSLOGREL(("DSound: DirectSoundCapture create instance %Rhrc\n", hr));
712 }
713 else {
714 hr = IDirectSoundCapture_Initialize (ds->dsound_capture, s->devguidp_capture);
715 if (FAILED (hr)) {
716 if (hr == DSERR_NODRIVER) {
717 DSLOGREL(("DSound: DirectSound capture is currently unavailable\n"));
718 }
719 else {
720 DSLOGREL(("DSound: DirectSoundCapture initialize %Rhrc\n", hr));
721 }
722 dsoundCaptureInterfaceRelease (ds);
723 }
724 }
725
726 return SUCCEEDED (hr)? 0: -1;
727}
728
729static void dsoundCaptureClose (DSoundVoiceIn *ds)
730{
731 dsound *s = &glob_dsound;
732
733 DSLOGF(("DSound: capture close %p buffer %p\n", ds, ds->dsound_capture_buffer));
734
735 if (ds->dsound_capture_buffer) {
736 HRESULT hr = IDirectSoundCaptureBuffer_Stop (ds->dsound_capture_buffer);
737 if (FAILED (hr)) {
738 DSLOGF(("DSound: close capture buffer stop %Rhrc\n", hr));
739 }
740
741 IDirectSoundCaptureBuffer_Release (ds->dsound_capture_buffer);
742 ds->dsound_capture_buffer = NULL;
743 }
744
745 dsoundCaptureInterfaceRelease (ds);
746}
747
748static int dsoundCaptureOpen (DSoundVoiceIn *ds)
749{
750 dsound *s = &glob_dsound;
751
752 int err;
753 HRESULT hr;
754 WAVEFORMATEX wfx;
755 DSCBUFFERDESC bd;
756 DSCBCAPS bc;
757 DWORD rpos;
758
759 DSLOGF(("DSound: capture open %p size %d samples, freq %d, chan %d, bits %d, sign %d\n",
760 ds,
761 ds->hw.samples,
762 ds->hw.info.freq,
763 ds->hw.info.nchannels,
764 ds->hw.info.bits,
765 ds->hw.info.sign));
766
767 if (ds->dsound_capture_buffer != NULL) {
768 /* Should not happen but be forgiving. */
769 DSLOGREL(("DSound: DirectSoundCaptureBuffer already exists\n"));
770 dsoundCaptureClose (ds);
771 }
772
773 err = waveformat_from_audio_settings (&wfx, &ds->as);
774 if (err) {
775 return err;
776 }
777
778 err = dsoundCaptureInterfaceCreate (ds);
779 if (err) {
780 return err;
781 }
782
783 memset (&bd, 0, sizeof (bd));
784 bd.dwSize = sizeof (bd);
785 bd.lpwfxFormat = &wfx;
786 bd.dwBufferBytes = ds->hw.samples << ds->hw.info.shift;
787 hr = IDirectSoundCapture_CreateCaptureBuffer (ds->dsound_capture,
788 &bd, &ds->dsound_capture_buffer, NULL);
789
790 if (FAILED (hr)) {
791 DSLOGREL(("DSound: create capture buffer %Rhrc\n", hr));
792 ds->dsound_capture_buffer = NULL;
793 goto fail0;
794 }
795
796 /* Query the actual parameters. */
797
798 hr = IDirectSoundCaptureBuffer_GetCurrentPosition (ds->dsound_capture_buffer, NULL, &rpos);
799 if (FAILED (hr)) {
800 rpos = 0;
801 DSLOGF(("DSound: open GetCurrentPosition %Rhrc\n", hr));
802 }
803
804 memset (&wfx, 0, sizeof (wfx));
805 hr = IDirectSoundCaptureBuffer_GetFormat (ds->dsound_capture_buffer, &wfx, sizeof (wfx), NULL);
806 if (FAILED (hr)) {
807 DSLOGREL(("DSound: capture buffer GetFormat %Rhrc\n", hr));
808 goto fail0;
809 }
810
811 memset (&bc, 0, sizeof (bc));
812 bc.dwSize = sizeof (bc);
813 hr = IDirectSoundCaptureBuffer_GetCaps (ds->dsound_capture_buffer, &bc);
814 if (FAILED (hr)) {
815 DSLOGREL(("DSound: capture buffer GetCaps %Rhrc\n", hr));
816 goto fail0;
817 }
818
819 DSLOGF(("DSound: capture buffer format: size %d bytes\n"
820 " tag = %d\n"
821 " nChannels = %d\n"
822 " nSamplesPerSec = %d\n"
823 " nAvgBytesPerSec = %d\n"
824 " nBlockAlign = %d\n"
825 " wBitsPerSample = %d\n"
826 " cbSize = %d\n",
827 bc.dwBufferBytes,
828 wfx.wFormatTag,
829 wfx.nChannels,
830 wfx.nSamplesPerSec,
831 wfx.nAvgBytesPerSec,
832 wfx.nBlockAlign,
833 wfx.wBitsPerSample,
834 wfx.cbSize));
835
836 if (bc.dwBufferBytes & ds->hw.info.align) {
837 DSLOGREL(("DSound: GetCaps returned misaligned buffer size %ld, alignment %d\n",
838 bc.dwBufferBytes, ds->hw.info.align + 1));
839 }
840
841 if (ds->hw.samples != 0 && ds->hw.samples != (bc.dwBufferBytes >> ds->hw.info.shift)) {
842 DSLOGREL(("DSound: buffer size mismatch dsound %d, hw %d bytes\n",
843 bc.dwBufferBytes, ds->hw.samples << ds->hw.info.shift));
844 }
845
846 /* Initial state: reading at the initial capture position. */
847 ds->hw.wpos = 0;
848 ds->last_read_pos = rpos >> ds->hw.info.shift;
849 ds->capture_buffer_size = bc.dwBufferBytes >> ds->hw.info.shift;
850 DSLOGF(("DSound: capture open last_read_pos %d, capture_buffer_size %d\n", ds->last_read_pos, ds->capture_buffer_size));
851
852 ds->hr_last_run_in = S_OK;
853
854 return 0;
855
856 fail0:
857 dsoundCaptureClose (ds);
858 return -1;
859}
860
861static void dsoundCaptureStop (DSoundVoiceIn *ds)
862{
863 if (ds->dsound_capture_buffer) {
864 HRESULT hr = IDirectSoundCaptureBuffer_Stop (ds->dsound_capture_buffer);
865 if (FAILED (hr)) {
866 DSLOGF(("DSound: stop capture buffer %Rhrc\n", hr));
867 }
868 }
869}
870
871static int dsoundCaptureStart (DSoundVoiceIn *ds)
872{
873 HRESULT hr;
874 DWORD status;
875
876 if (ds->dsound_capture_buffer != NULL) {
877 hr = IDirectSoundCaptureBuffer_GetStatus (ds->dsound_capture_buffer, &status);
878 if (FAILED (hr)) {
879 DSLOGF(("DSound: start GetStatus %Rhrc\n", hr));
880 }
881 else {
882 if (status & DSCBSTATUS_CAPTURING) {
883 DSLOGF(("DSound: already capturing\n"));
884 }
885 else {
886 /** @todo Fill the capture beffer with silence here. */
887
888 DSLOGF(("DSound: capture start\n"));
889 hr = IDirectSoundCaptureBuffer_Start (ds->dsound_capture_buffer, DSCBSTART_LOOPING);
890 if (FAILED (hr)) {
891 DSLOGREL(("DSound: start %Rhrc\n", hr));
892 }
893 }
894 }
895 }
896 else {
897 hr = E_FAIL;
898 }
899
900 return SUCCEEDED (hr)? 0: -1;
901}
902
903#include "dsound_template.h"
904#define DSBTYPE_IN
905#include "dsound_template.h"
906#undef DSBTYPE_IN
907
908static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
909{
910 int src_len1 = dst_len;
911 int src_len2 = 0;
912 int pos = hw->rpos + dst_len;
913 st_sample_t *src1 = hw->mix_buf + hw->rpos;
914 st_sample_t *src2 = NULL;
915
916 if (pos > hw->samples) {
917 src_len1 = hw->samples - hw->rpos;
918 src2 = hw->mix_buf;
919 src_len2 = dst_len - src_len1;
920 pos = src_len2;
921 }
922
923 if (src_len1) {
924 hw->clip (dst, src1, src_len1);
925 }
926
927 if (src_len2) {
928 dst = advance (dst, src_len1 << hw->info.shift);
929 hw->clip (dst, src2, src_len2);
930 }
931
932 hw->rpos = pos % hw->samples;
933}
934
935static void dsound_clear_sample (DSoundVoiceOut *ds)
936{
937 int err;
938 LPVOID p1, p2;
939 DWORD blen1, blen2, len1, len2;
940
941 err = dsound_lock_out (
942 ds->dsound_buffer,
943 &ds->hw.info,
944 0,
945 ds->playback_buffer_size << ds->hw.info.shift,
946 &p1, &p2,
947 &blen1, &blen2,
948 1
949 );
950 if (err) {
951 return;
952 }
953
954 len1 = blen1 >> ds->hw.info.shift;
955 len2 = blen2 >> ds->hw.info.shift;
956
957 if (p1 && len1) {
958 audio_pcm_info_clear_buf (&ds->hw.info, p1, len1);
959 }
960
961 if (p2 && len2) {
962 audio_pcm_info_clear_buf (&ds->hw.info, p2, len2);
963 }
964
965 dsound_unlock_out (ds->dsound_buffer, p1, p2, blen1, blen2);
966}
967
968static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
969{
970 DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
971
972 switch (cmd) {
973 case VOICE_ENABLE:
974 /* Try to start playback. If it fails, then reopen and try again. */
975 if (dsoundPlayStart (ds)) {
976 dsoundPlayClose (ds);
977 dsoundPlayOpen (ds);
978
979 if (dsoundPlayStart (ds)) {
980 return -1;
981 }
982 }
983 break;
984
985 case VOICE_DISABLE:
986 dsoundPlayStop (ds);
987 break;
988 }
989 return 0;
990}
991
992static int dsound_write (SWVoiceOut *sw, void *buf, int len)
993{
994 return audio_pcm_sw_write (sw, buf, len);
995}
996
997static int dsound_run_out (HWVoiceOut *hw)
998{
999 int err;
1000 HRESULT hr;
1001 DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
1002 LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
1003 int live, len, hwshift;
1004 DWORD blen1, blen2;
1005 DWORD len1, len2;
1006 DWORD decr;
1007 DWORD wpos, ppos, old_pos;
1008 LPVOID p1, p2;
1009 int bufsize;
1010
1011 if (!dsb) {
1012 DSLOGF(("DSound: run_out no playback buffer\n"));
1013 return 0;
1014 }
1015
1016 hwshift = hw->info.shift;
1017 bufsize = ds->playback_buffer_size << hwshift;
1018
1019 live = audio_pcm_hw_get_live_out (hw);
1020
1021 hr = IDirectSoundBuffer_GetCurrentPosition (
1022 dsb,
1023 &ppos,
1024 &wpos
1025 );
1026 if (hr == DSERR_BUFFERLOST) {
1027 if (dsound_restore_out(dsb))
1028 return 0;
1029 hr = IDirectSoundBuffer_GetCurrentPosition(dsb, &ppos, &wpos);
1030 if (hr == DSERR_BUFFERLOST)
1031 return 0; // Avoid log flooding if the error is still there.
1032 }
1033 if (FAILED (hr)) {
1034 DSLOGF(("DSound: get playback buffer position %Rhrc\n", hr));
1035 return 0;
1036 }
1037
1038 len = live << hwshift;
1039
1040 if (ds->first_time) {
1041 if (conf.latency_millis) {
1042 DWORD cur_blat;
1043 DWORD conf_blat;
1044
1045 conf_blat = millis_to_bytes (&hw->info, conf.latency_millis);
1046 cur_blat = audio_ring_dist (wpos, ppos, bufsize);
1047 old_pos = wpos;
1048 if (conf_blat > cur_blat) /* Do not write before wpos. */
1049 old_pos += conf_blat - cur_blat;
1050 old_pos %= bufsize;
1051 old_pos &= ~hw->info.align;
1052 }
1053 else {
1054 old_pos = wpos;
1055 }
1056 ds->first_time = 0;
1057 }
1058 else {
1059 if (ds->old_pos == ppos) {
1060 /* Full buffer. */
1061 return 0;
1062 }
1063
1064 old_pos = ds->old_pos;
1065 }
1066
1067 if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
1068 len = ppos - old_pos;
1069 }
1070 else {
1071 if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) {
1072 len = bufsize - old_pos + ppos;
1073 }
1074 }
1075
1076 if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) {
1077 DSLOGF(("DSound: error len=%d bufsize=%d old_pos=%ld ppos=%ld\n",
1078 len, bufsize, old_pos, ppos));
1079 return 0;
1080 }
1081
1082 len &= ~hw->info.align;
1083 if (!len) {
1084 return 0;
1085 }
1086
1087 err = dsound_lock_out (
1088 dsb,
1089 &hw->info,
1090 old_pos,
1091 len,
1092 &p1, &p2,
1093 &blen1, &blen2,
1094 0
1095 );
1096 if (err) {
1097 return 0;
1098 }
1099
1100 len1 = blen1 >> hwshift;
1101 len2 = blen2 >> hwshift;
1102 decr = len1 + len2;
1103
1104 if (p1 && len1) {
1105 dsound_write_sample (hw, p1, len1);
1106 }
1107
1108 if (p2 && len2) {
1109 dsound_write_sample (hw, p2, len2);
1110 }
1111
1112 dsound_unlock_out (dsb, p1, p2, blen1, blen2);
1113 ds->old_pos = (old_pos + (decr << hwshift)) % bufsize;
1114
1115 return decr;
1116}
1117
1118static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
1119{
1120 DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
1121
1122 switch (cmd) {
1123 case VOICE_ENABLE:
1124 /* Try to start capture. If it fails, then reopen and try again. */
1125 if (dsoundCaptureStart (ds)) {
1126 dsoundCaptureClose (ds);
1127 dsoundCaptureOpen (ds);
1128
1129 if (dsoundCaptureStart (ds)) {
1130 return -1;
1131 }
1132 }
1133 break;
1134
1135 case VOICE_DISABLE:
1136 dsoundCaptureStop (ds);
1137 break;
1138 }
1139 return 0;
1140}
1141
1142static int dsound_read (SWVoiceIn *sw, void *buf, int len)
1143{
1144 return audio_pcm_sw_read (sw, buf, len);
1145}
1146
1147static int dsound_run_in (HWVoiceIn *hw)
1148{
1149 int err;
1150 HRESULT hr;
1151 DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
1152 LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
1153 int live, len, dead;
1154 int ltmp;
1155 DWORD blen1, blen2;
1156 int len1, len2;
1157 int decr;
1158 DWORD rpos;
1159 LPVOID p1, p2;
1160 int hwshift;
1161
1162 if (!dscb) {
1163 DSLOGF(("DSound: run_in no capture buffer\n"));
1164 return 0;
1165 }
1166
1167 hwshift = hw->info.shift;
1168
1169 live = audio_pcm_hw_get_live_in (hw);
1170 dead = hw->samples - live;
1171 if (!dead) {
1172 return 0;
1173 }
1174
1175 hr = IDirectSoundCaptureBuffer_GetCurrentPosition (
1176 dscb,
1177 NULL,
1178 &rpos
1179 );
1180 if (FAILED (hr)) {
1181 if (hr != ds->hr_last_run_in) {
1182 DSLOGREL(("DSound: run_in GetCurrentPosition %Rhrc\n", hr));
1183 }
1184 ds->hr_last_run_in = hr;
1185 return 0;
1186 }
1187 ds->hr_last_run_in = hr;
1188
1189 if (rpos & hw->info.align) {
1190 DSLOGF(("DSound: run_in misaligned read position %ld(%d)\n", rpos, hw->info.align));
1191 }
1192
1193 rpos >>= hwshift;
1194
1195 /* Number of samples available in the capture buffer. */
1196 len = audio_ring_dist (rpos, ds->last_read_pos, ds->capture_buffer_size);
1197 if (!len) {
1198 return 0;
1199 }
1200 len = audio_MIN (len, dead);
1201
1202 err = dsound_lock_in (
1203 dscb,
1204 &hw->info,
1205 ds->last_read_pos << hwshift,
1206 len << hwshift,
1207 &p1,
1208 &p2,
1209 &blen1,
1210 &blen2,
1211 0
1212 );
1213 if (err) {
1214 return 0;
1215 }
1216
1217 len1 = blen1 >> hwshift;
1218 len2 = blen2 >> hwshift;
1219 decr = len1 + len2;
1220
1221 if (p1 && len1) {
1222 ltmp = audio_MIN(len1, hw->samples - hw->wpos);
1223 hw->conv (hw->conv_buf + hw->wpos, p1, ltmp, &pcm_in_volume);
1224 if (len1 > ltmp) {
1225 hw->conv (hw->conv_buf, (void *)((uintptr_t)p1 + (ltmp << hwshift)), len1 - ltmp, &pcm_in_volume);
1226 }
1227 hw->wpos = (hw->wpos + len1) % hw->samples;
1228 }
1229
1230 if (p2 && len2) {
1231 ltmp = audio_MIN(len2, hw->samples - hw->wpos);
1232 hw->conv (hw->conv_buf + hw->wpos, p2, ltmp, &pcm_in_volume);
1233 if (len2 > ltmp) {
1234 hw->conv (hw->conv_buf, (void *)((uintptr_t)p2 + (ltmp << hwshift)), len2 - ltmp, &pcm_in_volume);
1235 }
1236 hw->wpos = (hw->wpos + len2) % hw->samples;
1237 }
1238
1239 dsound_unlock_in (dscb, p1, p2, blen1, blen2);
1240 ds->last_read_pos = (ds->last_read_pos + decr) % ds->capture_buffer_size;
1241 return decr;
1242}
1243
1244static int dsoundIsAvailable (void)
1245{
1246 LPDIRECTSOUND dsound;
1247 HRESULT hr = CoCreateInstance (&CLSID_DirectSound, NULL, CLSCTX_ALL,
1248 &IID_IDirectSound, (void **) &dsound);
1249 if (SUCCEEDED(hr)) {
1250 IDirectSound_Release (dsound);
1251 return 1;
1252 }
1253
1254 DSLOGREL(("DSound: is unavailable %Rhrc\n", hr));
1255 return 0;
1256}
1257
1258static void dsound_audio_fini (void *opaque)
1259{
1260 dsound *s = opaque;
1261 NOREF(s);
1262 CoUninitialize();
1263}
1264
1265static void *dsound_audio_init (void)
1266{
1267 HRESULT hr;
1268 dsound *s = &glob_dsound;
1269
1270 hr = CoInitializeEx (NULL, COINIT_MULTITHREADED);
1271 if (FAILED (hr)) {
1272 DSLOGREL(("DSound: COM initialize %Rhrc\n", hr));
1273 return NULL;
1274 }
1275
1276 if (conf.device_guid_out) {
1277 int rc = RTUuidFromStr(&s->devguid_play, conf.device_guid_out);
1278 if (FAILED (rc)) {
1279 LogRel(("DSound: Could not parse DirectSound output device GUID\n"));
1280 }
1281 s->devguidp_play = (LPCGUID)&s->devguid_play;
1282 } else {
1283 s->devguidp_play = NULL;
1284 }
1285
1286 if (conf.device_guid_in) {
1287 int rc = RTUuidFromStr(&s->devguid_capture, conf.device_guid_in);
1288 if (RT_FAILURE(rc)) {
1289 LogRel(("DSound: Could not parse DirectSound input device GUID\n"));
1290 }
1291 s->devguidp_capture = (LPCGUID)&s->devguid_capture;
1292 } else {
1293 s->devguidp_capture = NULL;
1294 }
1295
1296 /* Check that DSound interface is available. */
1297 if (dsoundIsAvailable ())
1298 return s;
1299
1300 dsound_audio_fini (s);
1301 return NULL;
1302}
1303
1304static struct audio_option dsound_options[] = {
1305 {"LockRetries", AUD_OPT_INT, &conf.lock_retries,
1306 "Number of times to attempt locking the buffer", NULL, 0},
1307 {"RestoreRetries", AUD_OPT_INT, &conf.restore_retries,
1308 "Number of times to attempt restoring the buffer", NULL, 0},
1309 {"GetStatusRetries", AUD_OPT_INT, &conf.getstatus_retries,
1310 "Number of times to attempt getting status of the buffer", NULL, 0},
1311 {"LatencyMillis", AUD_OPT_INT, &conf.latency_millis,
1312 "(undocumented)", NULL, 0},
1313 {"BufsizeOut", AUD_OPT_INT, &conf.bufsize_out,
1314 "(undocumented)", NULL, 0},
1315 {"BufsizeIn", AUD_OPT_INT, &conf.bufsize_in,
1316 "(undocumented)", NULL, 0},
1317 {"DeviceGuidOut", AUD_OPT_STR, &conf.device_guid_out,
1318 "DirectSound output device GUID", NULL, 0},
1319 {"DeviceGuidIn", AUD_OPT_STR, &conf.device_guid_in,
1320 "DirectSound input device GUID", NULL, 0},
1321 {NULL, 0, NULL, NULL, NULL, 0}
1322};
1323
1324static struct audio_pcm_ops dsound_pcm_ops = {
1325 dsound_init_out,
1326 dsound_fini_out,
1327 dsound_run_out,
1328 dsound_write,
1329 dsound_ctl_out,
1330
1331 dsound_init_in,
1332 dsound_fini_in,
1333 dsound_run_in,
1334 dsound_read,
1335 dsound_ctl_in
1336};
1337
1338struct audio_driver dsound_audio_driver = {
1339 INIT_FIELD (name = ) "dsound",
1340 INIT_FIELD (descr = )
1341 "DirectSound http://wikipedia.org/wiki/DirectSound",
1342 INIT_FIELD (options = ) dsound_options,
1343 INIT_FIELD (init = ) dsound_audio_init,
1344 INIT_FIELD (fini = ) dsound_audio_fini,
1345 INIT_FIELD (pcm_ops = ) &dsound_pcm_ops,
1346 INIT_FIELD (can_be_default = ) 1,
1347 INIT_FIELD (max_voices_out = ) INT_MAX,
1348 INIT_FIELD (max_voices_in = ) 1,
1349 INIT_FIELD (voice_size_out = ) sizeof (DSoundVoiceOut),
1350 INIT_FIELD (voice_size_in = ) sizeof (DSoundVoiceIn)
1351};
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