VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostAudioDSound.cpp@ 88556

Last change on this file since 88556 was 88556, checked in by vboxsync, 4 years ago

DrvHostAudioDSound: Restructured the stream control code. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 99.0 KB
Line 
1/* $Id: DrvHostAudioDSound.cpp 88556 2021-04-16 00:08:40Z vboxsync $ */
2/** @file
3 * Host audio driver - DirectSound (Windows).
4 */
5
6/*
7 * Copyright (C) 2006-2020 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
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
23#define INITGUID
24#include <VBox/log.h>
25#include <iprt/win/windows.h>
26#include <dsound.h>
27#include <Mmdeviceapi.h>
28#include <functiondiscoverykeys_devpkey.h>
29
30#include <iprt/alloc.h>
31#include <iprt/system.h>
32#include <iprt/uuid.h>
33#include <iprt/utf16.h>
34
35#include <VBox/vmm/pdmaudioinline.h>
36#include <VBox/vmm/pdmaudiohostenuminline.h>
37
38#include "VBoxDD.h"
39#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
40# include <new> /* For bad_alloc. */
41# include "DrvHostAudioDSoundMMNotifClient.h"
42#endif
43
44
45/*********************************************************************************************************************************
46* Defined Constants And Macros *
47*********************************************************************************************************************************/
48/*
49 * Optional release logging, which a user can turn on with the
50 * 'VBoxManage debugvm' command.
51 * Debug logging still uses the common Log* macros from VBox.
52 * Messages which always should go to the release log use LogRel.
53 *
54 * @deprecated Use LogRelMax, LogRel2 and LogRel3 directly.
55 */
56/** General code behavior. */
57#define DSLOG(a) do { LogRel2(a); } while(0)
58/** Something which produce a lot of logging during playback/recording. */
59#define DSLOGF(a) do { LogRel3(a); } while(0)
60/** Important messages like errors. Limited in the default release log to avoid log flood. */
61#define DSLOGREL(a) \
62 do { \
63 static int8_t s_cLogged = 0; \
64 if (s_cLogged < 8) { \
65 ++s_cLogged; \
66 LogRel(a); \
67 } else DSLOG(a); \
68 } while (0)
69
70/** Maximum number of attempts to restore the sound buffer before giving up. */
71#define DRV_DSOUND_RESTORE_ATTEMPTS_MAX 3
72#if 0 /** @todo r=bird: What are these for? Nobody is using them... */
73/** Default input latency (in ms). */
74#define DRV_DSOUND_DEFAULT_LATENCY_MS_IN 50
75/** Default output latency (in ms). */
76#define DRV_DSOUND_DEFAULT_LATENCY_MS_OUT 50
77#endif
78
79
80/*********************************************************************************************************************************
81* Structures and Typedefs *
82*********************************************************************************************************************************/
83/* Dynamically load dsound.dll. */
84typedef HRESULT WINAPI FNDIRECTSOUNDENUMERATEW(LPDSENUMCALLBACKW pDSEnumCallback, PVOID pContext);
85typedef FNDIRECTSOUNDENUMERATEW *PFNDIRECTSOUNDENUMERATEW;
86typedef HRESULT WINAPI FNDIRECTSOUNDCAPTUREENUMERATEW(LPDSENUMCALLBACKW pDSEnumCallback, PVOID pContext);
87typedef FNDIRECTSOUNDCAPTUREENUMERATEW *PFNDIRECTSOUNDCAPTUREENUMERATEW;
88typedef HRESULT WINAPI FNDIRECTSOUNDCAPTURECREATE8(LPCGUID lpcGUID, LPDIRECTSOUNDCAPTURE8 *lplpDSC, LPUNKNOWN pUnkOuter);
89typedef FNDIRECTSOUNDCAPTURECREATE8 *PFNDIRECTSOUNDCAPTURECREATE8;
90
91#define VBOX_DSOUND_MAX_EVENTS 3
92
93typedef enum DSOUNDEVENT
94{
95 DSOUNDEVENT_NOTIFY = 0,
96 DSOUNDEVENT_INPUT,
97 DSOUNDEVENT_OUTPUT,
98} DSOUNDEVENT;
99
100typedef struct DSOUNDHOSTCFG
101{
102 RTUUID uuidPlay;
103 LPCGUID pGuidPlay;
104 RTUUID uuidCapture;
105 LPCGUID pGuidCapture;
106} DSOUNDHOSTCFG, *PDSOUNDHOSTCFG;
107
108typedef struct DSOUNDSTREAM
109{
110 /** The stream's acquired configuration. */
111 PDMAUDIOSTREAMCFG Cfg;
112 /** Buffer alignment. */
113 uint8_t uAlign;
114 /** Whether this stream is in an enable state on the DirectSound side. */
115 bool fEnabled;
116 bool afPadding[2];
117 /** Size (in bytes) of the DirectSound buffer. */
118 DWORD cbBufSize;
119 /** The stream's critical section for synchronizing access. */
120 RTCRITSECT CritSect;
121 union
122 {
123 struct
124 {
125 /** The actual DirectSound Buffer (DSB) used for the capturing.
126 * This is a secondary buffer and is used as a streaming buffer. */
127 LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB;
128 /** Current read offset (in bytes) within the DSB. */
129 DWORD offReadPos;
130 /** Number of buffer overruns happened. Used for logging. */
131 uint8_t cOverruns;
132 } In;
133 struct
134 {
135 /** The actual DirectSound Buffer (DSB) used for playback.
136 * This is a secondary buffer and is used as a streaming buffer. */
137 LPDIRECTSOUNDBUFFER8 pDSB;
138 /** Current write offset (in bytes) within the DSB.
139 * @note This is needed as the current write position as kept by direct sound
140 * will move ahead if we're too late. */
141 DWORD offWritePos;
142 /** Offset of last play cursor within the DSB when checked for pending. */
143 DWORD offPlayCursorLastPending;
144 /** Offset of last play cursor within the DSB when last played. */
145 DWORD offPlayCursorLastPlayed;
146 /** Total amount (in bytes) written to our internal ring buffer. */
147 uint64_t cbWritten;
148 /** Total amount (in bytes) played (to the DirectSound buffer). */
149 uint64_t cbTransferred;
150 /** Flag indicating whether playback was just (re)started. */
151 bool fFirstTransfer;
152 /** Flag indicating whether this stream is in draining mode, e.g. no new
153 * data is being written to it but DirectSound still needs to be able to
154 * play its remaining (buffered) data. */
155 bool fDrain;
156 /** How much (in bytes) the last transfer from the internal buffer
157 * to the DirectSound buffer was. */
158 uint32_t cbLastTransferred;
159 /** Timestamp (in ms) of the last transfer from the internal buffer
160 * to the DirectSound buffer. */
161 uint64_t tsLastTransferredMs;
162 /** Number of buffer underruns happened. Used for logging. */
163 uint8_t cUnderruns;
164 } Out;
165 };
166#ifdef LOG_ENABLED
167 struct
168 {
169 uint64_t tsLastTransferredMs;
170 } Dbg;
171#endif
172} DSOUNDSTREAM, *PDSOUNDSTREAM;
173
174/**
175 * DirectSound-specific device entry.
176 */
177typedef struct DSOUNDDEV
178{
179 PDMAUDIOHOSTDEV Core;
180 /** The GUID if handy. */
181 GUID Guid;
182} DSOUNDDEV;
183/** Pointer to a DirectSound device entry. */
184typedef DSOUNDDEV *PDSOUNDDEV;
185
186/**
187 * Structure for holding a device enumeration context.
188 */
189typedef struct DSOUNDENUMCBCTX
190{
191 /** Enumeration flags. */
192 uint32_t fFlags;
193 /** Pointer to device list to populate. */
194 PPDMAUDIOHOSTENUM pDevEnm;
195} DSOUNDENUMCBCTX, *PDSOUNDENUMCBCTX;
196
197typedef struct DRVHOSTDSOUND
198{
199 /** Pointer to the driver instance structure. */
200 PPDMDRVINS pDrvIns;
201 /** Our audio host audio interface. */
202 PDMIHOSTAUDIO IHostAudio;
203 /** Critical section to serialize access. */
204 RTCRITSECT CritSect;
205 /** DirectSound configuration options. */
206 DSOUNDHOSTCFG Cfg;
207 /** List of devices of last enumeration. */
208 PDMAUDIOHOSTENUM DeviceEnum;
209 /** Whether this backend supports any audio input.
210 * @todo r=bird: This is not actually used for anything. */
211 bool fEnabledIn;
212 /** Whether this backend supports any audio output.
213 * @todo r=bird: This is not actually used for anything. */
214 bool fEnabledOut;
215 /** The Direct Sound playback interface. */
216 LPDIRECTSOUND8 pDS;
217 /** The Direct Sound capturing interface. */
218 LPDIRECTSOUNDCAPTURE8 pDSC;
219#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
220 DrvHostAudioDSoundMMNotifClient *m_pNotificationClient;
221#endif
222} DRVHOSTDSOUND, *PDRVHOSTDSOUND;
223
224
225/*********************************************************************************************************************************
226* Internal Functions *
227*********************************************************************************************************************************/
228static HRESULT directSoundPlayRestore(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB);
229static int drvHostDSoundStreamStopPlayback(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fReset);
230
231static int dsoundDevicesEnumerate(PDRVHOSTDSOUND pThis, PDMAUDIOHOSTENUM pDevEnm, uint32_t fEnum);
232
233static void dsoundUpdateStatusInternal(PDRVHOSTDSOUND pThis);
234
235
236static DWORD dsoundRingDistance(DWORD offEnd, DWORD offBegin, DWORD cSize)
237{
238 AssertReturn(offEnd <= cSize, 0);
239 AssertReturn(offBegin <= cSize, 0);
240
241 return offEnd >= offBegin ? offEnd - offBegin : cSize - offBegin + offEnd;
242}
243
244
245static char *dsoundGUIDToUtf8StrA(LPCGUID pGUID)
246{
247 if (pGUID)
248 {
249 LPOLESTR lpOLEStr;
250 HRESULT hr = StringFromCLSID(*pGUID, &lpOLEStr);
251 if (SUCCEEDED(hr))
252 {
253 char *pszGUID;
254 int rc = RTUtf16ToUtf8(lpOLEStr, &pszGUID);
255 CoTaskMemFree(lpOLEStr);
256
257 return RT_SUCCESS(rc) ? pszGUID : NULL;
258 }
259 }
260
261 return RTStrDup("{Default device}");
262}
263
264
265static HRESULT directSoundPlayRestore(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB)
266{
267 RT_NOREF(pThis);
268 HRESULT hr = IDirectSoundBuffer8_Restore(pDSB);
269 if (FAILED(hr))
270 DSLOG(("DSound: Restoring playback buffer\n"));
271 else
272 DSLOGREL(("DSound: Restoring playback buffer failed with %Rhrc\n", hr));
273
274 return hr;
275}
276
277
278static HRESULT directSoundPlayUnlock(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB,
279 PVOID pv1, PVOID pv2,
280 DWORD cb1, DWORD cb2)
281{
282 RT_NOREF(pThis);
283 HRESULT hr = IDirectSoundBuffer8_Unlock(pDSB, pv1, cb1, pv2, cb2);
284 if (FAILED(hr))
285 DSLOGREL(("DSound: Unlocking playback buffer failed with %Rhrc\n", hr));
286 return hr;
287}
288
289
290static HRESULT directSoundPlayLock(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS,
291 DWORD dwOffset, DWORD dwBytes,
292 PVOID *ppv1, PVOID *ppv2,
293 DWORD *pcb1, DWORD *pcb2,
294 DWORD dwFlags)
295{
296 AssertReturn(dwBytes, VERR_INVALID_PARAMETER);
297
298 HRESULT hr = E_FAIL;
299 AssertCompile(DRV_DSOUND_RESTORE_ATTEMPTS_MAX > 0);
300 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
301 {
302 PVOID pv1, pv2;
303 DWORD cb1, cb2;
304 hr = IDirectSoundBuffer8_Lock(pStreamDS->Out.pDSB, dwOffset, dwBytes, &pv1, &cb1, &pv2, &cb2, dwFlags);
305 if (SUCCEEDED(hr))
306 {
307 if ( (!pv1 || !(cb1 & pStreamDS->uAlign))
308 && (!pv2 || !(cb2 & pStreamDS->uAlign)))
309 {
310 if (ppv1)
311 *ppv1 = pv1;
312 if (ppv2)
313 *ppv2 = pv2;
314 if (pcb1)
315 *pcb1 = cb1;
316 if (pcb2)
317 *pcb2 = cb2;
318 return S_OK;
319 }
320 DSLOGREL(("DSound: Locking playback buffer returned misaligned buffer: cb1=%#RX32, cb2=%#RX32 (alignment: %#RX32)\n",
321 *pcb1, *pcb2, pStreamDS->uAlign));
322 directSoundPlayUnlock(pThis, pStreamDS->Out.pDSB, pv1, pv2, cb1, cb2);
323 return E_FAIL;
324 }
325
326 if (hr != DSERR_BUFFERLOST)
327 break;
328
329 LogFlowFunc(("Locking failed due to lost buffer, restoring ...\n"));
330 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);
331 }
332
333 DSLOGREL(("DSound: Locking playback buffer failed with %Rhrc (dwOff=%ld, dwBytes=%ld)\n", hr, dwOffset, dwBytes));
334 return hr;
335}
336
337
338static HRESULT directSoundCaptureUnlock(LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB,
339 PVOID pv1, PVOID pv2,
340 DWORD cb1, DWORD cb2)
341{
342 HRESULT hr = IDirectSoundCaptureBuffer8_Unlock(pDSCB, pv1, cb1, pv2, cb2);
343 if (FAILED(hr))
344 DSLOGREL(("DSound: Unlocking capture buffer failed with %Rhrc\n", hr));
345 return hr;
346}
347
348
349static HRESULT directSoundCaptureLock(PDSOUNDSTREAM pStreamDS,
350 DWORD dwOffset, DWORD dwBytes,
351 PVOID *ppv1, PVOID *ppv2,
352 DWORD *pcb1, DWORD *pcb2,
353 DWORD dwFlags)
354{
355 PVOID pv1 = NULL;
356 PVOID pv2 = NULL;
357 DWORD cb1 = 0;
358 DWORD cb2 = 0;
359
360 HRESULT hr = IDirectSoundCaptureBuffer8_Lock(pStreamDS->In.pDSCB, dwOffset, dwBytes,
361 &pv1, &cb1, &pv2, &cb2, dwFlags);
362 if (FAILED(hr))
363 {
364 DSLOGREL(("DSound: Locking capture buffer failed with %Rhrc\n", hr));
365 return hr;
366 }
367
368 if ( (pv1 && (cb1 & pStreamDS->uAlign))
369 || (pv2 && (cb2 & pStreamDS->uAlign)))
370 {
371 DSLOGREL(("DSound: Locking capture buffer returned misaligned buffer: cb1=%RI32, cb2=%RI32 (alignment: %RU32)\n",
372 cb1, cb2, pStreamDS->uAlign));
373 directSoundCaptureUnlock(pStreamDS->In.pDSCB, pv1, pv2, cb1, cb2);
374 return E_FAIL;
375 }
376
377 *ppv1 = pv1;
378 *ppv2 = pv2;
379 *pcb1 = cb1;
380 *pcb2 = cb2;
381
382 return S_OK;
383}
384
385
386/*
387 * DirectSound playback
388 */
389
390/**
391 * Creates a DirectSound playback instance.
392 *
393 * @return HRESULT
394 * @param pGUID GUID of device to create the playback interface for. NULL
395 * for the default device.
396 * @param ppDS Where to return the interface to the created instance.
397 */
398static HRESULT drvHostDSoundCreateDSPlaybackInstance(LPCGUID pGUID, LPDIRECTSOUND8 *ppDS)
399{
400 LogFlowFuncEnter();
401
402 LPDIRECTSOUND8 pDS = NULL;
403 HRESULT hrc = CoCreateInstance(CLSID_DirectSound8, NULL, CLSCTX_ALL, IID_IDirectSound8, (void **)&pDS);
404 if (SUCCEEDED(hrc))
405 {
406 hrc = IDirectSound8_Initialize(pDS, pGUID);
407 if (SUCCEEDED(hrc))
408 {
409 HWND hWnd = GetDesktopWindow();
410 hrc = IDirectSound8_SetCooperativeLevel(pDS, hWnd, DSSCL_PRIORITY);
411 if (SUCCEEDED(hrc))
412 {
413 *ppDS = pDS;
414 LogFlowFunc(("LEAVE S_OK\n"));
415 return S_OK;
416 }
417 LogRelMax(64, ("DSound: Setting cooperative level for (hWnd=%p) failed: %Rhrc\n", hWnd, hrc));
418 }
419 else if (hrc == DSERR_NODRIVER) /* Usually means that no playback devices are attached. */
420 LogRelMax(64, ("DSound: DirectSound playback is currently unavailable\n"));
421 else
422 LogRelMax(64, ("DSound: DirectSound playback initialization failed: %Rhrc\n", hrc));
423
424 IDirectSound8_Release(pDS);
425 }
426 else
427 LogRelMax(64, ("DSound: Creating playback instance failed: %Rhrc\n", hrc));
428
429 LogFlowFunc(("LEAVE %Rhrc\n", hrc));
430 return hrc;
431}
432
433
434#if 0 /* not used */
435static HRESULT directSoundPlayGetStatus(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB, DWORD *pdwStatus)
436{
437 AssertPtrReturn(pThis, E_POINTER);
438 AssertPtrReturn(pDSB, E_POINTER);
439
440 AssertPtrNull(pdwStatus);
441
442 DWORD dwStatus = 0;
443 HRESULT hr = E_FAIL;
444 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
445 {
446 hr = IDirectSoundBuffer8_GetStatus(pDSB, &dwStatus);
447 if ( hr == DSERR_BUFFERLOST
448 || ( SUCCEEDED(hr)
449 && (dwStatus & DSBSTATUS_BUFFERLOST)))
450 {
451 LogFlowFunc(("Getting status failed due to lost buffer, restoring ...\n"));
452 directSoundPlayRestore(pThis, pDSB);
453 }
454 else
455 break;
456 }
457
458 if (SUCCEEDED(hr))
459 {
460 if (pdwStatus)
461 *pdwStatus = dwStatus;
462 }
463 else
464 DSLOGREL(("DSound: Retrieving playback status failed with %Rhrc\n", hr));
465
466 return hr;
467}
468#endif
469
470
471/*
472 * DirectSoundCapture
473 */
474
475#if 0 /* unused */
476static LPCGUID dsoundCaptureSelectDevice(PDRVHOSTDSOUND pThis, PPDMAUDIOSTREAMCFG pCfg)
477{
478 AssertPtrReturn(pThis, NULL);
479 AssertPtrReturn(pCfg, NULL);
480
481 int rc = VINF_SUCCESS;
482
483 LPCGUID pGUID = pThis->Cfg.pGuidCapture;
484 if (!pGUID)
485 {
486 PDSOUNDDEV pDev = NULL;
487 switch (pCfg->u.enmSrc)
488 {
489 case PDMAUDIORECSRC_LINE:
490 /*
491 * At the moment we're only supporting line-in in the HDA emulation,
492 * and line-in + mic-in in the AC'97 emulation both are expected
493 * to use the host's mic-in as well.
494 *
495 * So the fall through here is intentional for now.
496 */
497 case PDMAUDIORECSRC_MIC:
498 pDev = (PDSOUNDDEV)DrvAudioHlpDeviceEnumGetDefaultDevice(&pThis->DeviceEnum, PDMAUDIODIR_IN);
499 break;
500
501 default:
502 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
503 break;
504 }
505
506 if ( RT_SUCCESS(rc)
507 && pDev)
508 {
509 DSLOG(("DSound: Guest source '%s' is using host recording device '%s'\n",
510 PDMAudioRecSrcGetName(pCfg->u.enmSrc), pDev->Core.szName));
511 pGUID = &pDev->Guid;
512 }
513 if (RT_FAILURE(rc))
514 {
515 LogRel(("DSound: Selecting recording device failed with %Rrc\n", rc));
516 return NULL;
517 }
518 }
519
520 /* This always has to be in the release log. */
521 char *pszGUID = dsoundGUIDToUtf8StrA(pGUID);
522 LogRel(("DSound: Guest source '%s' is using host recording device with GUID '%s'\n",
523 PDMAudioRecSrcGetName(pCfg->u.enmSrc), pszGUID ? pszGUID: "{?}"));
524 RTStrFree(pszGUID);
525
526 return pGUID;
527}
528#endif
529
530
531/**
532 * Creates a DirectSound capture instance.
533 *
534 * @returns HRESULT
535 * @param pGUID GUID of device to create the capture interface for. NULL
536 * for default.
537 * @param ppDSC Where to return the interface to the created instance.
538 */
539static HRESULT drvHostDSoundCreateDSCaptureInstance(LPCGUID pGUID, LPDIRECTSOUNDCAPTURE8 *ppDSC)
540{
541 LogFlowFuncEnter();
542
543 LPDIRECTSOUNDCAPTURE8 pDSC = NULL;
544 HRESULT hrc = CoCreateInstance(CLSID_DirectSoundCapture8, NULL, CLSCTX_ALL, IID_IDirectSoundCapture8, (void **)&pDSC);
545 if (SUCCEEDED(hrc))
546 {
547 hrc = IDirectSoundCapture_Initialize(pDSC, pGUID);
548 if (SUCCEEDED(hrc))
549 {
550 *ppDSC = pDSC;
551 LogFlowFunc(("LEAVE S_OK\n"));
552 return S_OK;
553 }
554 if (hrc == DSERR_NODRIVER) /* Usually means that no capture devices are attached. */
555 LogRelMax(64, ("DSound: Capture device currently is unavailable\n"));
556 else
557 LogRelMax(64, ("DSound: Initializing capturing device failed: %Rhrc\n", hrc));
558 IDirectSoundCapture_Release(pDSC);
559 }
560 else
561 LogRelMax(64, ("DSound: Creating capture instance failed: %Rhrc\n", hrc));
562
563 LogFlowFunc(("LEAVE %Rhrc\n", hrc));
564 return hrc;
565}
566
567
568/**
569 * Updates this host driver's internal status, according to the global, overall input/output
570 * state and all connected (native) audio streams.
571 *
572 * @todo r=bird: This is a 'ing waste of 'ing time! We're doing this everytime
573 * an 'ing stream is created and we doesn't 'ing use the information here
574 * for any darn thing! Given the reported slowness of enumeration and
575 * issues with the 'ing code the only appropriate response is:
576 * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARG!!!!!!!
577 *
578 * @param pThis Host audio driver instance.
579 */
580static void dsoundUpdateStatusInternal(PDRVHOSTDSOUND pThis)
581{
582#if 0 /** @todo r=bird: This isn't doing *ANYTHING* useful. So, I've just disabled it. */
583 AssertPtrReturnVoid(pThis);
584 LogFlowFuncEnter();
585
586 PDMAudioHostEnumDelete(&pThis->DeviceEnum);
587 int rc = dsoundDevicesEnumerate(pThis, &pThis->DeviceEnum);
588 if (RT_SUCCESS(rc))
589 {
590#if 0
591 if ( pThis->fEnabledOut != RT_BOOL(cbCtx.cDevOut)
592 || pThis->fEnabledIn != RT_BOOL(cbCtx.cDevIn))
593 {
594 /** @todo Use a registered callback to the audio connector (e.g "OnConfigurationChanged") to
595 * let the connector know that something has changed within the host backend. */
596 }
597#endif
598 pThis->fEnabledIn = PDMAudioHostEnumCountMatching(&pThis->DeviceEnum, PDMAUDIODIR_IN) != 0;
599 pThis->fEnabledOut = PDMAudioHostEnumCountMatching(&pThis->DeviceEnum, PDMAUDIODIR_OUT) != 0;
600 }
601
602 LogFlowFuncLeaveRC(rc);
603#else
604 RT_NOREF(pThis);
605#endif
606}
607
608
609/*********************************************************************************************************************************
610* PDMIHOSTAUDIO *
611*********************************************************************************************************************************/
612
613/**
614 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
615 */
616static DECLCALLBACK(int) drvHostDSoundHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
617{
618 RT_NOREF(pInterface);
619 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
620 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
621
622
623 /*
624 * Fill in the config structure.
625 */
626 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "DirectSound");
627 pBackendCfg->cbStream = sizeof(DSOUNDSTREAM);
628 pBackendCfg->fFlags = 0;
629 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
630 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
631
632 return VINF_SUCCESS;
633}
634
635
636/**
637 * Callback for the playback device enumeration.
638 *
639 * @return TRUE if continuing enumeration, FALSE if not.
640 * @param pGUID Pointer to GUID of enumerated device. Can be NULL.
641 * @param pwszDescription Pointer to (friendly) description of enumerated device.
642 * @param pwszModule Pointer to module name of enumerated device.
643 * @param lpContext Pointer to PDSOUNDENUMCBCTX context for storing the enumerated information.
644 *
645 * @note Carbon copy of drvHostDSoundEnumOldStyleCaptureCallback with OUT direction.
646 */
647static BOOL CALLBACK drvHostDSoundEnumOldStylePlaybackCallback(LPGUID pGUID, LPCWSTR pwszDescription,
648 LPCWSTR pwszModule, PVOID lpContext)
649{
650 PDSOUNDENUMCBCTX pEnumCtx = (PDSOUNDENUMCBCTX)lpContext;
651 AssertPtrReturn(pEnumCtx, FALSE);
652
653 PPDMAUDIOHOSTENUM pDevEnm = pEnumCtx->pDevEnm;
654 AssertPtrReturn(pDevEnm, FALSE);
655
656 AssertPtrNullReturn(pGUID, FALSE); /* pGUID can be NULL for default device(s). */
657 AssertPtrReturn(pwszDescription, FALSE);
658 RT_NOREF(pwszModule); /* Do not care about pwszModule. */
659
660 int rc;
661 PDSOUNDDEV pDev = (PDSOUNDDEV)PDMAudioHostDevAlloc(sizeof(DSOUNDDEV));
662 if (pDev)
663 {
664 pDev->Core.enmUsage = PDMAUDIODIR_OUT;
665 pDev->Core.enmType = PDMAUDIODEVICETYPE_BUILTIN;
666
667 if (pGUID == NULL)
668 pDev->Core.fFlags = PDMAUDIOHOSTDEV_F_DEFAULT;
669
670 char *pszName;
671 rc = RTUtf16ToUtf8(pwszDescription, &pszName);
672 if (RT_SUCCESS(rc))
673 {
674 RTStrCopy(pDev->Core.szName, sizeof(pDev->Core.szName), pszName);
675 RTStrFree(pszName);
676
677 if (pGUID) /* pGUID == NULL means default device. */
678 memcpy(&pDev->Guid, pGUID, sizeof(pDev->Guid));
679
680 PDMAudioHostEnumAppend(pDevEnm, &pDev->Core);
681
682 /* Note: Querying the actual device information will be done at some
683 * later point in time outside this enumeration callback to prevent
684 * DSound hangs. */
685 return TRUE;
686 }
687 PDMAudioHostDevFree(&pDev->Core);
688 }
689 else
690 rc = VERR_NO_MEMORY;
691
692 LogRel(("DSound: Error enumeration playback device '%ls': rc=%Rrc\n", pwszDescription, rc));
693 return FALSE; /* Abort enumeration. */
694}
695
696
697/**
698 * Callback for the capture device enumeration.
699 *
700 * @return TRUE if continuing enumeration, FALSE if not.
701 * @param pGUID Pointer to GUID of enumerated device. Can be NULL.
702 * @param pwszDescription Pointer to (friendly) description of enumerated device.
703 * @param pwszModule Pointer to module name of enumerated device.
704 * @param lpContext Pointer to PDSOUNDENUMCBCTX context for storing the enumerated information.
705 *
706 * @note Carbon copy of drvHostDSoundEnumOldStylePlaybackCallback with IN direction.
707 */
708static BOOL CALLBACK drvHostDSoundEnumOldStyleCaptureCallback(LPGUID pGUID, LPCWSTR pwszDescription,
709 LPCWSTR pwszModule, PVOID lpContext)
710{
711 PDSOUNDENUMCBCTX pEnumCtx = (PDSOUNDENUMCBCTX )lpContext;
712 AssertPtrReturn(pEnumCtx, FALSE);
713
714 PPDMAUDIOHOSTENUM pDevEnm = pEnumCtx->pDevEnm;
715 AssertPtrReturn(pDevEnm, FALSE);
716
717 AssertPtrNullReturn(pGUID, FALSE); /* pGUID can be NULL for default device(s). */
718 AssertPtrReturn(pwszDescription, FALSE);
719 RT_NOREF(pwszModule); /* Do not care about pwszModule. */
720
721 int rc;
722 PDSOUNDDEV pDev = (PDSOUNDDEV)PDMAudioHostDevAlloc(sizeof(DSOUNDDEV));
723 if (pDev)
724 {
725 pDev->Core.enmUsage = PDMAUDIODIR_IN;
726 pDev->Core.enmType = PDMAUDIODEVICETYPE_BUILTIN;
727
728 char *pszName;
729 rc = RTUtf16ToUtf8(pwszDescription, &pszName);
730 if (RT_SUCCESS(rc))
731 {
732 RTStrCopy(pDev->Core.szName, sizeof(pDev->Core.szName), pszName);
733 RTStrFree(pszName);
734
735 if (pGUID) /* pGUID == NULL means default capture device. */
736 memcpy(&pDev->Guid, pGUID, sizeof(pDev->Guid));
737
738 PDMAudioHostEnumAppend(pDevEnm, &pDev->Core);
739
740 /* Note: Querying the actual device information will be done at some
741 * later point in time outside this enumeration callback to prevent
742 * DSound hangs. */
743 return TRUE;
744 }
745 PDMAudioHostDevFree(&pDev->Core);
746 }
747 else
748 rc = VERR_NO_MEMORY;
749
750 LogRel(("DSound: Error enumeration capture device '%ls', rc=%Rrc\n", pwszDescription, rc));
751 return FALSE; /* Abort enumeration. */
752}
753
754
755/**
756 * Queries information for a given (DirectSound) device.
757 *
758 * @returns VBox status code.
759 * @param pDev Audio device to query information for.
760 */
761static int drvHostDSoundEnumOldStyleQueryDeviceInfo(PDSOUNDDEV pDev)
762{
763 AssertPtr(pDev);
764 int rc;
765
766 if (pDev->Core.enmUsage == PDMAUDIODIR_OUT)
767 {
768 LPDIRECTSOUND8 pDS;
769 HRESULT hr = drvHostDSoundCreateDSPlaybackInstance(&pDev->Guid, &pDS);
770 if (SUCCEEDED(hr))
771 {
772 DSCAPS DSCaps;
773 RT_ZERO(DSCaps);
774 DSCaps.dwSize = sizeof(DSCAPS);
775 hr = IDirectSound_GetCaps(pDS, &DSCaps);
776 if (SUCCEEDED(hr))
777 {
778 pDev->Core.cMaxOutputChannels = DSCaps.dwFlags & DSCAPS_PRIMARYSTEREO ? 2 : 1;
779
780 DWORD dwSpeakerCfg;
781 hr = IDirectSound_GetSpeakerConfig(pDS, &dwSpeakerCfg);
782 if (SUCCEEDED(hr))
783 {
784 unsigned uSpeakerCount = 0;
785 switch (DSSPEAKER_CONFIG(dwSpeakerCfg))
786 {
787 case DSSPEAKER_MONO: uSpeakerCount = 1; break;
788 case DSSPEAKER_HEADPHONE: uSpeakerCount = 2; break;
789 case DSSPEAKER_STEREO: uSpeakerCount = 2; break;
790 case DSSPEAKER_QUAD: uSpeakerCount = 4; break;
791 case DSSPEAKER_SURROUND: uSpeakerCount = 4; break;
792 case DSSPEAKER_5POINT1: uSpeakerCount = 6; break;
793 case DSSPEAKER_5POINT1_SURROUND: uSpeakerCount = 6; break;
794 case DSSPEAKER_7POINT1: uSpeakerCount = 8; break;
795 case DSSPEAKER_7POINT1_SURROUND: uSpeakerCount = 8; break;
796 default: break;
797 }
798
799 if (uSpeakerCount) /* Do we need to update the channel count? */
800 pDev->Core.cMaxOutputChannels = uSpeakerCount;
801
802 rc = VINF_SUCCESS;
803 }
804 else
805 {
806 LogRel(("DSound: Error retrieving playback device speaker config, hr=%Rhrc\n", hr));
807 rc = VERR_ACCESS_DENIED; /** @todo Fudge! */
808 }
809 }
810 else
811 {
812 LogRel(("DSound: Error retrieving playback device capabilities, hr=%Rhrc\n", hr));
813 rc = VERR_ACCESS_DENIED; /** @todo Fudge! */
814 }
815
816 IDirectSound8_Release(pDS);
817 }
818 else
819 rc = VERR_GENERAL_FAILURE;
820 }
821 else if (pDev->Core.enmUsage == PDMAUDIODIR_IN)
822 {
823 LPDIRECTSOUNDCAPTURE8 pDSC;
824 HRESULT hr = drvHostDSoundCreateDSCaptureInstance(&pDev->Guid, &pDSC);
825 if (SUCCEEDED(hr))
826 {
827 DSCCAPS DSCCaps;
828 RT_ZERO(DSCCaps);
829 DSCCaps.dwSize = sizeof(DSCCAPS);
830 hr = IDirectSoundCapture_GetCaps(pDSC, &DSCCaps);
831 if (SUCCEEDED(hr))
832 {
833 pDev->Core.cMaxInputChannels = DSCCaps.dwChannels;
834 rc = VINF_SUCCESS;
835 }
836 else
837 {
838 LogRel(("DSound: Error retrieving capture device capabilities, hr=%Rhrc\n", hr));
839 rc = VERR_ACCESS_DENIED; /** @todo Fudge! */
840 }
841
842 IDirectSoundCapture_Release(pDSC);
843 }
844 else
845 rc = VERR_GENERAL_FAILURE;
846 }
847 else
848 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
849
850 return rc;
851}
852
853
854/**
855 * Queries information for @a pDevice and adds an entry to the enumeration.
856 *
857 * @returns VBox status code.
858 * @param pDevEnm The enumeration to add the device to.
859 * @param pDevice The device.
860 * @param enmType The type of device.
861 * @param fDefault Whether it's the default device.
862 */
863static int drvHostDSoundEnumNewStyleAdd(PPDMAUDIOHOSTENUM pDevEnm, IMMDevice *pDevice, EDataFlow enmType, bool fDefault)
864{
865 int rc = VINF_SUCCESS; /* ignore most errors */
866
867 /*
868 * Gather the necessary properties.
869 */
870 IPropertyStore *pProperties = NULL;
871 HRESULT hrc = pDevice->OpenPropertyStore(STGM_READ, &pProperties);
872 if (SUCCEEDED(hrc))
873 {
874 /* Get the friendly name. */
875 PROPVARIANT VarName;
876 PropVariantInit(&VarName);
877 hrc = pProperties->GetValue(PKEY_Device_FriendlyName, &VarName);
878 if (SUCCEEDED(hrc))
879 {
880 /* Get the DirectSound GUID. */
881 PROPVARIANT VarGUID;
882 PropVariantInit(&VarGUID);
883 hrc = pProperties->GetValue(PKEY_AudioEndpoint_GUID, &VarGUID);
884 if (SUCCEEDED(hrc))
885 {
886 /* Get the device format. */
887 PROPVARIANT VarFormat;
888 PropVariantInit(&VarFormat);
889 hrc = pProperties->GetValue(PKEY_AudioEngine_DeviceFormat, &VarFormat);
890 if (SUCCEEDED(hrc))
891 {
892 WAVEFORMATEX const * const pFormat = (WAVEFORMATEX const *)VarFormat.blob.pBlobData;
893 AssertPtr(pFormat);
894
895 /*
896 * Create a enumeration entry for it.
897 */
898 PDSOUNDDEV pDev = (PDSOUNDDEV)PDMAudioHostDevAlloc(sizeof(DSOUNDDEV));
899 if (pDev)
900 {
901 pDev->Core.enmUsage = enmType == eRender ? PDMAUDIODIR_OUT : PDMAUDIODIR_IN;
902 pDev->Core.enmType = PDMAUDIODEVICETYPE_BUILTIN;
903 if (enmType == eRender)
904 pDev->Core.cMaxOutputChannels = pFormat->nChannels;
905 else
906 pDev->Core.cMaxInputChannels = pFormat->nChannels;
907
908 RT_NOREF(fDefault);
909 //if (fDefault)
910 hrc = UuidFromStringW(VarGUID.pwszVal, &pDev->Guid);
911 if (SUCCEEDED(hrc))
912 {
913 char *pszName;
914 rc = RTUtf16ToUtf8(VarName.pwszVal, &pszName);
915 if (RT_SUCCESS(rc))
916 {
917 RTStrCopy(pDev->Core.szName, sizeof(pDev->Core.szName), pszName);
918 RTStrFree(pszName);
919
920 PDMAudioHostEnumAppend(pDevEnm, &pDev->Core);
921 }
922 else
923 PDMAudioHostDevFree(&pDev->Core);
924 }
925 else
926 {
927 LogFunc(("UuidFromStringW(%ls): %Rhrc\n", VarGUID.pwszVal, hrc));
928 PDMAudioHostDevFree(&pDev->Core);
929 }
930 }
931 else
932 rc = VERR_NO_MEMORY;
933 PropVariantClear(&VarFormat);
934 }
935 else
936 LogFunc(("Failed to get PKEY_AudioEngine_DeviceFormat: %Rhrc\n", hrc));
937 PropVariantClear(&VarGUID);
938 }
939 else
940 LogFunc(("Failed to get PKEY_AudioEndpoint_GUID: %Rhrc\n", hrc));
941 PropVariantClear(&VarName);
942 }
943 else
944 LogFunc(("Failed to get PKEY_Device_FriendlyName: %Rhrc\n", hrc));
945 pProperties->Release();
946 }
947 else
948 LogFunc(("OpenPropertyStore failed: %Rhrc\n", hrc));
949
950 if (hrc == E_OUTOFMEMORY && RT_SUCCESS_NP(rc))
951 rc = VERR_NO_MEMORY;
952 return rc;
953}
954
955
956/**
957 * Does a (Re-)enumeration of the host's playback + capturing devices.
958 *
959 * @return VBox status code.
960 * @param pDevEnm Where to store the enumerated devices.
961 */
962static int drvHostDSoundEnumerateDevices(PPDMAUDIOHOSTENUM pDevEnm)
963{
964 DSLOG(("DSound: Enumerating devices ...\n"));
965
966 /*
967 * Use the Vista+ API.
968 */
969 IMMDeviceEnumerator *pEnumerator;
970 HRESULT hrc = CoCreateInstance(__uuidof(MMDeviceEnumerator), 0, CLSCTX_ALL,
971 __uuidof(IMMDeviceEnumerator), (void **)&pEnumerator);
972 if (SUCCEEDED(hrc))
973 {
974 int rc = VINF_SUCCESS;
975 for (unsigned idxPass = 0; idxPass < 2 && RT_SUCCESS(rc); idxPass++)
976 {
977 EDataFlow const enmType = idxPass == 0 ? EDataFlow::eRender : EDataFlow::eCapture;
978
979 /* Get the default device first. */
980 IMMDevice *pDefaultDevice = NULL;
981 hrc = pEnumerator->GetDefaultAudioEndpoint(enmType, eMultimedia, &pDefaultDevice);
982 if (SUCCEEDED(hrc))
983 rc = drvHostDSoundEnumNewStyleAdd(pDevEnm, pDefaultDevice, enmType, true);
984 else
985 pDefaultDevice = NULL;
986
987 /* Enumerate the devices. */
988 IMMDeviceCollection *pCollection = NULL;
989 hrc = pEnumerator->EnumAudioEndpoints(enmType, DEVICE_STATE_ACTIVE /*| DEVICE_STATE_UNPLUGGED?*/, &pCollection);
990 if (SUCCEEDED(hrc) && pCollection != NULL)
991 {
992 UINT cDevices = 0;
993 hrc = pCollection->GetCount(&cDevices);
994 if (SUCCEEDED(hrc))
995 {
996 for (UINT idxDevice = 0; idxDevice < cDevices && RT_SUCCESS(rc); idxDevice++)
997 {
998 IMMDevice *pDevice = NULL;
999 hrc = pCollection->Item(idxDevice, &pDevice);
1000 if (SUCCEEDED(hrc) && pDevice)
1001 {
1002 if (pDevice != pDefaultDevice)
1003 rc = drvHostDSoundEnumNewStyleAdd(pDevEnm, pDevice, enmType, false);
1004 pDevice->Release();
1005 }
1006 }
1007 }
1008 pCollection->Release();
1009 }
1010 else
1011 LogRelMax(10, ("EnumAudioEndpoints(%s) failed: %Rhrc\n", idxPass == 0 ? "output" : "input", hrc));
1012
1013 if (pDefaultDevice)
1014 pDefaultDevice->Release();
1015 }
1016 pEnumerator->Release();
1017 if (pDevEnm->cDevices > 0 || RT_FAILURE(rc))
1018 {
1019 DSLOG(("DSound: Enumerating devices done - %u device (%Rrc)\n", pDevEnm->cDevices, rc));
1020 return rc;
1021 }
1022 }
1023
1024 /*
1025 * Fall back to dsound.
1026 */
1027 /* Resolve symbols once. */
1028 static PFNDIRECTSOUNDENUMERATEW volatile s_pfnDirectSoundEnumerateW = NULL;
1029 static PFNDIRECTSOUNDCAPTUREENUMERATEW volatile s_pfnDirectSoundCaptureEnumerateW = NULL;
1030
1031 PFNDIRECTSOUNDENUMERATEW pfnDirectSoundEnumerateW = s_pfnDirectSoundEnumerateW;
1032 PFNDIRECTSOUNDCAPTUREENUMERATEW pfnDirectSoundCaptureEnumerateW = s_pfnDirectSoundCaptureEnumerateW;
1033 if (!pfnDirectSoundEnumerateW || !pfnDirectSoundCaptureEnumerateW)
1034 {
1035 RTLDRMOD hModDSound = NIL_RTLDRMOD;
1036 int rc = RTLdrLoadSystem("dsound.dll", true /*fNoUnload*/, &hModDSound);
1037 if (RT_SUCCESS(rc))
1038 {
1039 rc = RTLdrGetSymbol(hModDSound, "DirectSoundEnumerateW", (void **)&pfnDirectSoundEnumerateW);
1040 if (RT_SUCCESS(rc))
1041 s_pfnDirectSoundEnumerateW = pfnDirectSoundEnumerateW;
1042 else
1043 LogRel(("DSound: Failed to get dsound.dll export DirectSoundEnumerateW: %Rrc\n", rc));
1044
1045 rc = RTLdrGetSymbol(hModDSound, "DirectSoundCaptureEnumerateW", (void **)&pfnDirectSoundCaptureEnumerateW);
1046 if (RT_SUCCESS(rc))
1047 s_pfnDirectSoundCaptureEnumerateW = pfnDirectSoundCaptureEnumerateW;
1048 else
1049 LogRel(("DSound: Failed to get dsound.dll export DirectSoundCaptureEnumerateW: %Rrc\n", rc));
1050 RTLdrClose(hModDSound);
1051 }
1052 else
1053 LogRel(("DSound: Unable to load dsound.dll for enumerating devices: %Rrc\n", rc));
1054 if (!pfnDirectSoundEnumerateW && !pfnDirectSoundCaptureEnumerateW)
1055 return rc;
1056 }
1057
1058 /* Common callback context for both playback and capture enumerations: */
1059 DSOUNDENUMCBCTX EnumCtx;
1060 EnumCtx.fFlags = 0;
1061 EnumCtx.pDevEnm = pDevEnm;
1062
1063 /* Enumerate playback devices. */
1064 if (pfnDirectSoundEnumerateW)
1065 {
1066 DSLOG(("DSound: Enumerating playback devices ...\n"));
1067 HRESULT hr = pfnDirectSoundEnumerateW(&drvHostDSoundEnumOldStylePlaybackCallback, &EnumCtx);
1068 if (FAILED(hr))
1069 LogRel(("DSound: Error enumerating host playback devices: %Rhrc\n", hr));
1070 }
1071
1072 /* Enumerate capture devices. */
1073 if (pfnDirectSoundCaptureEnumerateW)
1074 {
1075 DSLOG(("DSound: Enumerating capture devices ...\n"));
1076 HRESULT hr = pfnDirectSoundCaptureEnumerateW(&drvHostDSoundEnumOldStyleCaptureCallback, &EnumCtx);
1077 if (FAILED(hr))
1078 LogRel(("DSound: Error enumerating host capture devices: %Rhrc\n", hr));
1079 }
1080
1081 /*
1082 * Query Information for all enumerated devices.
1083 * Note! This is problematic to do from the enumeration callbacks.
1084 */
1085 PDSOUNDDEV pDev;
1086 RTListForEach(&pDevEnm->LstDevices, pDev, DSOUNDDEV, Core.ListEntry)
1087 {
1088 drvHostDSoundEnumOldStyleQueryDeviceInfo(pDev); /* ignore rc */
1089 }
1090
1091 DSLOG(("DSound: Enumerating devices done\n"));
1092
1093 return VINF_SUCCESS;
1094}
1095
1096
1097/**
1098 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetDevices}
1099 */
1100static DECLCALLBACK(int) drvHostDSoundHA_GetDevices(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHOSTENUM pDeviceEnum)
1101{
1102 RT_NOREF(pInterface);
1103 AssertPtrReturn(pDeviceEnum, VERR_INVALID_POINTER);
1104
1105 PDMAudioHostEnumInit(pDeviceEnum);
1106 int rc = drvHostDSoundEnumerateDevices(pDeviceEnum);
1107 if (RT_FAILURE(rc))
1108 PDMAudioHostEnumDelete(pDeviceEnum);
1109
1110 LogFlowFunc(("Returning %Rrc\n", rc));
1111 return rc;
1112}
1113
1114
1115/**
1116 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
1117 */
1118static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostDSoundHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
1119{
1120 RT_NOREF(pInterface, enmDir);
1121 return PDMAUDIOBACKENDSTS_RUNNING;
1122}
1123
1124
1125/**
1126 * Converts from PDM stream config to windows WAVEFORMATEX struct.
1127 *
1128 * @param pCfg The PDM audio stream config to convert from.
1129 * @param pFmt The windows structure to initialize.
1130 */
1131static void dsoundWaveFmtFromCfg(PCPDMAUDIOSTREAMCFG pCfg, PWAVEFORMATEX pFmt)
1132{
1133 RT_ZERO(*pFmt);
1134 pFmt->wFormatTag = WAVE_FORMAT_PCM;
1135 pFmt->nChannels = PDMAudioPropsChannels(&pCfg->Props);
1136 pFmt->wBitsPerSample = PDMAudioPropsSampleBits(&pCfg->Props);
1137 pFmt->nSamplesPerSec = PDMAudioPropsHz(&pCfg->Props);
1138 pFmt->nBlockAlign = PDMAudioPropsFrameSize(&pCfg->Props);
1139 pFmt->nAvgBytesPerSec = PDMAudioPropsFramesToBytes(&pCfg->Props, PDMAudioPropsHz(&pCfg->Props));
1140 pFmt->cbSize = 0; /* No extra data specified. */
1141}
1142
1143
1144/**
1145 * Resets the state of a DirectSound stream, clearing the buffer content.
1146 *
1147 * @param pThis Host audio driver instance.
1148 * @param pStreamDS Stream to reset state for.
1149 */
1150static void drvHostDSoundStreamReset(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
1151{
1152 RT_NOREF(pThis);
1153 LogFunc(("Resetting %s\n", pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN ? "capture" : "playback"));
1154
1155 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
1156 {
1157 /*
1158 * Input streams.
1159 */
1160 LogFunc(("Resetting capture stream '%s'\n", pStreamDS->Cfg.szName));
1161
1162 /* Reset the state: */
1163/** @todo r=bird: We set the read position to zero here, but shouldn't we query it
1164 * from the buffer instead given that there isn't any interface for repositioning
1165 * to the start of the buffer as with playback buffers? */
1166 pStreamDS->In.offReadPos = 0;
1167 pStreamDS->In.cOverruns = 0;
1168
1169 /* Clear the buffer content: */
1170 AssertPtr(pStreamDS->In.pDSCB);
1171 if (pStreamDS->In.pDSCB)
1172 {
1173 PVOID pv1 = NULL;
1174 DWORD cb1 = 0;
1175 PVOID pv2 = NULL;
1176 DWORD cb2 = 0;
1177 HRESULT hrc = IDirectSoundCaptureBuffer8_Lock(pStreamDS->In.pDSCB, 0, pStreamDS->cbBufSize,
1178 &pv1, &cb1, &pv2, &cb2, 0 /*fFlags*/);
1179 if (SUCCEEDED(hrc))
1180 {
1181 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv1, cb1, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb1));
1182 if (pv2 && cb2)
1183 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv2, cb2, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb2));
1184 hrc = IDirectSoundCaptureBuffer8_Unlock(pStreamDS->In.pDSCB, pv1, cb1, pv2, cb2);
1185 if (FAILED(hrc))
1186 LogRelMaxFunc(64, ("DSound: Unlocking capture buffer '%s' after reset failed: %Rhrc\n",
1187 pStreamDS->Cfg.szName, hrc));
1188 }
1189 else
1190 LogRelMaxFunc(64, ("DSound: Locking capture buffer '%s' for reset failed: %Rhrc\n",
1191 pStreamDS->Cfg.szName, hrc));
1192 }
1193 }
1194 else
1195 {
1196 /*
1197 * Output streams.
1198 */
1199 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT);
1200 LogFunc(("Resetting playback stream '%s'\n", pStreamDS->Cfg.szName));
1201
1202 /* If draining was enagaged, make sure dsound has stopped playing: */
1203 if (pStreamDS->Out.fDrain && pStreamDS->Out.pDSB)
1204 pStreamDS->Out.pDSB->Stop();
1205
1206 /* Reset the internal state: */
1207 pStreamDS->Out.fFirstTransfer = true;
1208 pStreamDS->Out.fDrain = false;
1209 pStreamDS->Out.cUnderruns = 0;
1210 pStreamDS->Out.cbLastTransferred = 0;
1211 pStreamDS->Out.tsLastTransferredMs = 0;
1212 pStreamDS->Out.cbTransferred = 0;
1213 pStreamDS->Out.cbWritten = 0;
1214 pStreamDS->Out.offWritePos = 0;
1215 pStreamDS->Out.offPlayCursorLastPending = 0;
1216 pStreamDS->Out.offPlayCursorLastPlayed = 0;
1217
1218 /* Reset the buffer content and repositioning the buffer to the start of the buffer. */
1219 AssertPtr(pStreamDS->Out.pDSB);
1220 if (pStreamDS->Out.pDSB)
1221 {
1222 HRESULT hrc = IDirectSoundBuffer8_SetCurrentPosition(pStreamDS->Out.pDSB, 0);
1223 if (FAILED(hrc))
1224 LogRelMaxFunc(64, ("DSound: Failed to set buffer position for '%s': %Rhrc\n", pStreamDS->Cfg.szName, hrc));
1225
1226 PVOID pv1 = NULL;
1227 DWORD cb1 = 0;
1228 PVOID pv2 = NULL;
1229 DWORD cb2 = 0;
1230 hrc = IDirectSoundBuffer8_Lock(pStreamDS->Out.pDSB, 0, pStreamDS->cbBufSize, &pv1, &cb1, &pv2, &cb2, 0 /*fFlags*/);
1231 if (hrc == DSERR_BUFFERLOST)
1232 {
1233 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);
1234 hrc = IDirectSoundBuffer8_Lock(pStreamDS->Out.pDSB, 0, pStreamDS->cbBufSize, &pv1, &cb1, &pv2, &cb2, 0 /*fFlags*/);
1235 }
1236 if (SUCCEEDED(hrc))
1237 {
1238 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv1, cb1, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb1));
1239 if (pv2 && cb2)
1240 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv2, cb2, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb2));
1241
1242 hrc = IDirectSoundBuffer8_Unlock(pStreamDS->Out.pDSB, pv1, cb1, pv2, cb2);
1243 if (FAILED(hrc))
1244 LogRelMaxFunc(64, ("DSound: Unlocking playback buffer '%s' after reset failed: %Rhrc\n",
1245 pStreamDS->Cfg.szName, hrc));
1246 }
1247 else
1248 LogRelMaxFunc(64, ("DSound: Locking playback buffer '%s' for reset failed: %Rhrc\n", pStreamDS->Cfg.szName, hrc));
1249 }
1250 }
1251
1252#ifdef LOG_ENABLED
1253 pStreamDS->Dbg.tsLastTransferredMs = 0;
1254#endif
1255}
1256
1257
1258/**
1259 * Worker for drvHostDSoundHA_StreamCreate that creates caputre stream.
1260 *
1261 * @returns Windows COM status code.
1262 * @param pThis The DSound instance data.
1263 * @param pStreamDS The stream instance data.
1264 * @param pCfgReq The requested stream config (input).
1265 * @param pCfgAcq Where to return the actual stream config. This is a
1266 * copy of @a *pCfgReq when called.
1267 * @param pWaveFmtX On input the requested stream format.
1268 * Updated to the actual stream format on successful
1269 * return.
1270 */
1271static HRESULT drvHostDSoundStreamCreateCapture(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS,
1272 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq, WAVEFORMATEX *pWaveFmtX)
1273{
1274 Assert(pStreamDS->In.pDSCB == NULL);
1275 HRESULT hrc;
1276
1277 /*
1278 * Create, initialize and set up a IDirectSoundCapture instance the first time
1279 * we go thru here.
1280 */
1281 /** @todo bird: Or should we rather just throw this away after we've gotten the
1282 * capture buffer? Old code would just leak it... */
1283 if (pThis->pDSC == NULL)
1284 {
1285 hrc = drvHostDSoundCreateDSCaptureInstance(pThis->Cfg.pGuidCapture, &pThis->pDSC);
1286 if (FAILED(hrc))
1287 return hrc; /* The worker has complained to the release log already. */
1288 }
1289
1290 /*
1291 * Create the capture buffer.
1292 */
1293 DSCBUFFERDESC BufferDesc =
1294 {
1295 /*.dwSize = */ sizeof(BufferDesc),
1296 /*.dwFlags = */ 0,
1297 /*.dwBufferBytes =*/ PDMAudioPropsFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize),
1298 /*.dwReserved = */ 0,
1299 /*.lpwfxFormat = */ pWaveFmtX,
1300 /*.dwFXCount = */ 0,
1301 /*.lpDSCFXDesc = */ NULL
1302 };
1303
1304 LogRel2(("DSound: Requested capture buffer is %#x B / %u B / %RU64 ms\n", BufferDesc.dwBufferBytes, BufferDesc.dwBufferBytes,
1305 PDMAudioPropsBytesToMilli(&pCfgReq->Props, BufferDesc.dwBufferBytes)));
1306
1307 LPDIRECTSOUNDCAPTUREBUFFER pLegacyDSCB = NULL;
1308 hrc = IDirectSoundCapture_CreateCaptureBuffer(pThis->pDSC, &BufferDesc, &pLegacyDSCB, NULL);
1309 if (FAILED(hrc))
1310 {
1311 LogRelMax(64, ("DSound: Creating capture buffer for '%s' failed: %Rhrc\n", pCfgReq->szName, hrc));
1312 return hrc;
1313 }
1314
1315 /* Get the IDirectSoundCaptureBuffer8 version of the interface. */
1316 hrc = IDirectSoundCaptureBuffer_QueryInterface(pLegacyDSCB, IID_IDirectSoundCaptureBuffer8, (void **)&pStreamDS->In.pDSCB);
1317 IDirectSoundCaptureBuffer_Release(pLegacyDSCB);
1318 if (FAILED(hrc))
1319 {
1320 LogRelMax(64, ("DSound: Querying IID_IDirectSoundCaptureBuffer8 for '%s' failed: %Rhrc\n", pCfgReq->szName, hrc));
1321 return hrc;
1322 }
1323
1324 /*
1325 * Query the actual stream configuration.
1326 */
1327#if 0 /** @todo r=bird: WTF was this for? */
1328 DWORD offByteReadPos = 0;
1329 hrc = IDirectSoundCaptureBuffer8_GetCurrentPosition(pStreamDS->In.pDSCB, NULL, &offByteReadPos);
1330 if (FAILED(hrc))
1331 {
1332 offByteReadPos = 0;
1333 DSLOGREL(("DSound: Getting capture position failed with %Rhrc\n", hr));
1334 }
1335#endif
1336 RT_ZERO(*pWaveFmtX);
1337 hrc = IDirectSoundCaptureBuffer8_GetFormat(pStreamDS->In.pDSCB, pWaveFmtX, sizeof(*pWaveFmtX), NULL);
1338 if (SUCCEEDED(hrc))
1339 {
1340 /** @todo r=bird: We aren't converting/checking the pWaveFmtX content... */
1341
1342 DSCBCAPS BufferCaps = { /*.dwSize = */ sizeof(BufferCaps), 0, 0, 0 };
1343 hrc = IDirectSoundCaptureBuffer8_GetCaps(pStreamDS->In.pDSCB, &BufferCaps);
1344 if (SUCCEEDED(hrc))
1345 {
1346 LogRel2(("DSound: Acquired capture buffer capabilities for '%s':\n"
1347 "DSound: dwFlags = %#RX32\n"
1348 "DSound: dwBufferBytes = %#RX32 B / %RU32 B / %RU64 ms\n"
1349 "DSound: dwReserved = %#RX32\n",
1350 pCfgReq->szName, BufferCaps.dwFlags, BufferCaps.dwBufferBytes, BufferCaps.dwBufferBytes,
1351 PDMAudioPropsBytesToMilli(&pCfgReq->Props, BufferCaps.dwBufferBytes), BufferCaps.dwReserved ));
1352
1353 /* Update buffer related stuff: */
1354 pStreamDS->In.offReadPos = 0; /** @todo shouldn't we use offBytReadPos here to "read at the initial capture position"? */
1355 pStreamDS->cbBufSize = BufferCaps.dwBufferBytes;
1356 pCfgAcq->Backend.cFramesBufferSize = PDMAudioPropsBytesToFrames(&pCfgAcq->Props, BufferCaps.dwBufferBytes);
1357
1358#if 0 /** @todo r=bird: uAlign isn't set anywhere, so this hasn't been checking anything for a while... */
1359 if (bc.dwBufferBytes & pStreamDS->uAlign)
1360 DSLOGREL(("DSound: Capture GetCaps returned misaligned buffer: size %RU32, alignment %RU32\n",
1361 bc.dwBufferBytes, pStreamDS->uAlign + 1));
1362#endif
1363 LogFlow(("returns S_OK\n"));
1364 return S_OK;
1365 }
1366 LogRelMax(64, ("DSound: Getting capture buffer capabilities for '%s' failed: %Rhrc\n", pCfgReq->szName, hrc));
1367 }
1368 else
1369 LogRelMax(64, ("DSound: Getting capture format for '%s' failed: %Rhrc\n", pCfgReq->szName, hrc));
1370
1371 IDirectSoundCaptureBuffer8_Release(pStreamDS->In.pDSCB);
1372 pStreamDS->In.pDSCB = NULL;
1373 LogFlowFunc(("returns %Rhrc\n", hrc));
1374 return hrc;
1375}
1376
1377
1378/**
1379 * Worker for drvHostDSoundHA_StreamCreate that creates playback stream.
1380 *
1381 * @returns Windows COM status code.
1382 * @param pThis The DSound instance data.
1383 * @param pStreamDS The stream instance data.
1384 * @param pCfgReq The requested stream config (input).
1385 * @param pCfgAcq Where to return the actual stream config. This is a
1386 * copy of @a *pCfgReq when called.
1387 * @param pWaveFmtX On input the requested stream format.
1388 * Updated to the actual stream format on successful
1389 * return.
1390 */
1391static HRESULT drvHostDSoundStreamCreatePlayback(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS,
1392 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq, WAVEFORMATEX *pWaveFmtX)
1393{
1394 Assert(pStreamDS->Out.pDSB == NULL);
1395 HRESULT hrc;
1396
1397 /*
1398 * Create, initialize and set up a DirectSound8 instance the first time
1399 * we go thru here.
1400 */
1401 /** @todo bird: Or should we rather just throw this away after we've gotten the
1402 * sound buffer? Old code would just leak it... */
1403 if (pThis->pDS == NULL)
1404 {
1405 hrc = drvHostDSoundCreateDSPlaybackInstance(pThis->Cfg.pGuidPlay, &pThis->pDS);
1406 if (FAILED(hrc))
1407 return hrc; /* The worker has complained to the release log already. */
1408 }
1409
1410 /*
1411 * As we reuse our (secondary) buffer for playing out data as it comes in,
1412 * we're using this buffer as a so-called streaming buffer.
1413 *
1414 * See https://msdn.microsoft.com/en-us/library/windows/desktop/ee419014(v=vs.85).aspx
1415 *
1416 * However, as we do not want to use memory on the sound device directly
1417 * (as most modern audio hardware on the host doesn't have this anyway),
1418 * we're *not* going to use DSBCAPS_STATIC for that.
1419 *
1420 * Instead we're specifying DSBCAPS_LOCSOFTWARE, as this fits the bill
1421 * of copying own buffer data to our secondary's Direct Sound buffer.
1422 */
1423 DSBUFFERDESC BufferDesc =
1424 {
1425 /*.dwSize = */ sizeof(BufferDesc),
1426 /*.dwFlags = */ DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE,
1427 /*.dwBufferBytes = */ PDMAudioPropsFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize),
1428 /*.dwReserved = */ 0,
1429 /*.lpwfxFormat = */ pWaveFmtX
1430 /*.guid3DAlgorithm = {0, 0, 0, {0,0,0,0, 0,0,0,0}} */
1431 };
1432 LogRel2(("DSound: Requested playback buffer is %#x B / %u B / %RU64 ms\n", BufferDesc.dwBufferBytes, BufferDesc.dwBufferBytes,
1433 PDMAudioPropsBytesToMilli(&pCfgReq->Props, BufferDesc.dwBufferBytes)));
1434
1435 LPDIRECTSOUNDBUFFER pLegacyDSB = NULL;
1436 hrc = IDirectSound8_CreateSoundBuffer(pThis->pDS, &BufferDesc, &pLegacyDSB, NULL);
1437 if (FAILED(hrc))
1438 {
1439 LogRelMax(64, ("DSound: Creating playback sound buffer for '%s' failed: %Rhrc\n", pCfgReq->szName, hrc));
1440 return hrc;
1441 }
1442
1443 /* Get the IDirectSoundBuffer8 version of the interface. */
1444 hrc = IDirectSoundBuffer_QueryInterface(pLegacyDSB, IID_IDirectSoundBuffer8, (PVOID *)&pStreamDS->Out.pDSB);
1445 IDirectSoundBuffer_Release(pLegacyDSB);
1446 if (FAILED(hrc))
1447 {
1448 LogRelMax(64, ("DSound: Querying IID_IDirectSoundBuffer8 for '%s' failed: %Rhrc\n", pCfgReq->szName, hrc));
1449 return hrc;
1450 }
1451
1452 /*
1453 * Query the actual stream parameters, they may differ from what we requested.
1454 */
1455 RT_ZERO(*pWaveFmtX);
1456 hrc = IDirectSoundBuffer8_GetFormat(pStreamDS->Out.pDSB, pWaveFmtX, sizeof(*pWaveFmtX), NULL);
1457 if (SUCCEEDED(hrc))
1458 {
1459 /** @todo r=bird: We aren't converting/checking the pWaveFmtX content... */
1460
1461 DSBCAPS BufferCaps = { /*.dwSize = */ sizeof(BufferCaps), 0, 0, 0, 0 };
1462 hrc = IDirectSoundBuffer8_GetCaps(pStreamDS->Out.pDSB, &BufferCaps);
1463 if (SUCCEEDED(hrc))
1464 {
1465 LogRel2(("DSound: Acquired playback buffer capabilities for '%s':\n"
1466 "DSound: dwFlags = %#RX32\n"
1467 "DSound: dwBufferBytes = %#RX32 B / %RU32 B / %RU64 ms\n"
1468 "DSound: dwUnlockTransferRate = %RU32 KB/s\n"
1469 "DSound: dwPlayCpuOverhead = %RU32%%\n",
1470 pCfgReq->szName, BufferCaps.dwFlags, BufferCaps.dwBufferBytes, BufferCaps.dwBufferBytes,
1471 PDMAudioPropsBytesToMilli(&pCfgReq->Props, BufferCaps.dwBufferBytes),
1472 BufferCaps.dwUnlockTransferRate, BufferCaps.dwPlayCpuOverhead));
1473
1474 /* Update buffer related stuff: */
1475 pStreamDS->cbBufSize = BufferCaps.dwBufferBytes;
1476 pCfgAcq->Backend.cFramesBufferSize = PDMAudioPropsBytesToFrames(&pCfgAcq->Props, BufferCaps.dwBufferBytes);
1477 pCfgAcq->Backend.cFramesPeriod = pCfgAcq->Backend.cFramesBufferSize / 4; /* total fiction */
1478 pCfgAcq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesPreBuffering * pCfgAcq->Backend.cFramesBufferSize
1479 / RT_MAX(pCfgReq->Backend.cFramesBufferSize, 1);
1480
1481#if 0 /** @todo r=bird: uAlign isn't set anywhere, so this hasn't been checking anything for a while... */
1482 if (bc.dwBufferBytes & pStreamDS->uAlign)
1483 DSLOGREL(("DSound: Playback capabilities returned misaligned buffer: size %RU32, alignment %RU32\n",
1484 bc.dwBufferBytes, pStreamDS->uAlign + 1));
1485#endif
1486 LogFlow(("returns S_OK\n"));
1487 return S_OK;
1488 }
1489 LogRelMax(64, ("DSound: Getting playback buffer capabilities for '%s' failed: %Rhrc\n", pCfgReq->szName, hrc));
1490 }
1491 else
1492 LogRelMax(64, ("DSound: Getting playback format for '%s' failed: %Rhrc\n", pCfgReq->szName, hrc));
1493
1494 IDirectSoundBuffer8_Release(pStreamDS->Out.pDSB);
1495 pStreamDS->Out.pDSB = NULL;
1496 LogFlowFunc(("returns %Rhrc\n", hrc));
1497 return hrc;
1498}
1499
1500
1501/**
1502 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
1503 */
1504static DECLCALLBACK(int) drvHostDSoundHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1505 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1506{
1507 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
1508 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
1509 AssertPtrReturn(pStreamDS, VERR_INVALID_POINTER);
1510 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
1511 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
1512 AssertReturn(pCfgReq->enmDir == PDMAUDIODIR_IN || pCfgReq->enmDir == PDMAUDIODIR_OUT, VERR_INVALID_PARAMETER);
1513 Assert(PDMAudioStrmCfgEquals(pCfgReq, pCfgAcq));
1514
1515 const char * const pszStreamType = pCfgReq->enmDir == PDMAUDIODIR_IN ? "capture" : "playback"; RT_NOREF(pszStreamType);
1516 LogFlowFunc(("enmSrc/Dst=%s '%s'\n",
1517 pCfgReq->enmDir == PDMAUDIODIR_IN ? PDMAudioRecSrcGetName(pCfgReq->u.enmSrc)
1518 : PDMAudioPlaybackDstGetName(pCfgReq->u.enmDst), pCfgReq->szName));
1519
1520 /* For whatever reason: */
1521 dsoundUpdateStatusInternal(pThis);
1522
1523 /*
1524 * DSound has different COM interfaces for working with input and output
1525 * streams, so we'll quickly part ways here after some common format
1526 * specification setup and logging.
1527 */
1528#if defined(RTLOG_REL_ENABLED) || defined(LOG_ENABLED)
1529 char szTmp[64];
1530#endif
1531 LogRel2(("DSound: Opening %s stream '%s' (%s)\n", pCfgReq->szName, pszStreamType,
1532 PDMAudioPropsToString(&pCfgReq->Props, szTmp, sizeof(szTmp))));
1533
1534 WAVEFORMATEX WaveFmtX;
1535 dsoundWaveFmtFromCfg(pCfgReq, &WaveFmtX);
1536 LogRel2(("DSound: Requested %s format for '%s':\n"
1537 "DSound: wFormatTag = %RU16\n"
1538 "DSound: nChannels = %RU16\n"
1539 "DSound: nSamplesPerSec = %RU32\n"
1540 "DSound: nAvgBytesPerSec = %RU32\n"
1541 "DSound: nBlockAlign = %RU16\n"
1542 "DSound: wBitsPerSample = %RU16\n"
1543 "DSound: cbSize = %RU16\n",
1544 pszStreamType, pCfgReq->szName, WaveFmtX.wFormatTag, WaveFmtX.nChannels, WaveFmtX.nSamplesPerSec,
1545 WaveFmtX.nAvgBytesPerSec, WaveFmtX.nBlockAlign, WaveFmtX.wBitsPerSample, WaveFmtX.cbSize));
1546
1547 HRESULT hrc;
1548 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
1549 hrc = drvHostDSoundStreamCreateCapture(pThis, pStreamDS, pCfgReq, pCfgAcq, &WaveFmtX);
1550 else
1551 hrc = drvHostDSoundStreamCreatePlayback(pThis, pStreamDS, pCfgReq, pCfgAcq, &WaveFmtX);
1552 int rc;
1553 if (SUCCEEDED(hrc))
1554 {
1555 LogRel2(("DSound: Acquired %s format for '%s':\n"
1556 "DSound: wFormatTag = %RU16\n"
1557 "DSound: nChannels = %RU16\n"
1558 "DSound: nSamplesPerSec = %RU32\n"
1559 "DSound: nAvgBytesPerSec = %RU32\n"
1560 "DSound: nBlockAlign = %RU16\n"
1561 "DSound: wBitsPerSample = %RU16\n"
1562 "DSound: cbSize = %RU16\n",
1563 pszStreamType, pCfgReq->szName, WaveFmtX.wFormatTag, WaveFmtX.nChannels, WaveFmtX.nSamplesPerSec,
1564 WaveFmtX.nAvgBytesPerSec, WaveFmtX.nBlockAlign, WaveFmtX.wBitsPerSample, WaveFmtX.cbSize));
1565
1566 /*
1567 * Copy the acquired config and reset the stream (clears the buffer).
1568 */
1569 PDMAudioStrmCfgCopy(&pStreamDS->Cfg, pCfgAcq);
1570 drvHostDSoundStreamReset(pThis, pStreamDS);
1571 rc = VINF_SUCCESS;
1572 }
1573 else
1574 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
1575
1576 LogFlowFunc(("returns %Rrc\n", rc));
1577 return rc;
1578}
1579
1580
1581/**
1582 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
1583 */
1584static DECLCALLBACK(int) drvHostDSoundHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1585{
1586 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
1587 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
1588 AssertPtrReturn(pStreamDS, VERR_INVALID_POINTER);
1589
1590 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
1591 {
1592 /*
1593 * Input.
1594 */
1595 if (pStreamDS->In.pDSCB)
1596 {
1597 HRESULT hrc = IDirectSoundCaptureBuffer_Stop(pStreamDS->In.pDSCB);
1598 if (FAILED(hrc))
1599 LogFunc(("IDirectSoundCaptureBuffer_Stop failed: %Rhrc\n", hrc));
1600
1601 drvHostDSoundStreamReset(pThis, pStreamDS);
1602
1603 IDirectSoundCaptureBuffer8_Release(pStreamDS->In.pDSCB);
1604 pStreamDS->In.pDSCB = NULL;
1605 }
1606 }
1607 else
1608 {
1609 /*
1610 * Output.
1611 */
1612 if (pStreamDS->Out.pDSB)
1613 {
1614 drvHostDSoundStreamStopPlayback(pThis, pStreamDS, true /*fReset*/);
1615
1616 IDirectSoundBuffer8_Release(pStreamDS->Out.pDSB);
1617 pStreamDS->Out.pDSB = NULL;
1618 }
1619 }
1620
1621 if (RTCritSectIsInitialized(&pStreamDS->CritSect))
1622 RTCritSectDelete(&pStreamDS->CritSect);
1623
1624 return VINF_SUCCESS;
1625}
1626
1627
1628/**
1629 * Worker for drvHostDSoundHA_StreamEnable and drvHostDSoundHA_StreamResume.
1630 *
1631 * This will try re-open the capture device if we're having trouble starting it.
1632 *
1633 * @returns VBox status code.
1634 * @param pThis The DSound host audio driver instance data.
1635 * @param pStreamDS The stream instance data.
1636 */
1637static int drvHostDSoundStreamCaptureStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
1638{
1639 /*
1640 * Check the stream status first.
1641 */
1642 int rc = VERR_AUDIO_STREAM_NOT_READY;
1643 if (pStreamDS->In.pDSCB)
1644 {
1645 DWORD fStatus = 0;
1646 HRESULT hrc = IDirectSoundCaptureBuffer8_GetStatus(pStreamDS->In.pDSCB, &fStatus);
1647 if (SUCCEEDED(hrc))
1648 {
1649 /*
1650 * Try start capturing if it's not already doing so.
1651 */
1652 if (!(fStatus & DSCBSTATUS_CAPTURING))
1653 {
1654 LogRel2(("DSound: Starting capture on '%s' ... \n", pStreamDS->Cfg.szName));
1655 hrc = IDirectSoundCaptureBuffer8_Start(pStreamDS->In.pDSCB, DSCBSTART_LOOPING);
1656 if (SUCCEEDED(hrc))
1657 rc = VINF_SUCCESS;
1658 else
1659 {
1660 /*
1661 * Failed to start, try re-create the capture buffer.
1662 */
1663 LogRelMax(64, ("DSound: Starting to capture on '%s' failed: %Rhrc - will try re-open it ...\n",
1664 pStreamDS->Cfg.szName, hrc));
1665
1666 IDirectSoundCaptureBuffer8_Release(pStreamDS->In.pDSCB);
1667 pStreamDS->In.pDSCB = NULL;
1668
1669 PDMAUDIOSTREAMCFG CfgReq = pStreamDS->Cfg;
1670 PDMAUDIOSTREAMCFG CfgAcq = pStreamDS->Cfg;
1671 WAVEFORMATEX WaveFmtX;
1672 dsoundWaveFmtFromCfg(&pStreamDS->Cfg, &WaveFmtX);
1673 hrc = drvHostDSoundStreamCreateCapture(pThis, pStreamDS, &CfgReq, &CfgAcq, &WaveFmtX);
1674 if (SUCCEEDED(hrc))
1675 {
1676 PDMAudioStrmCfgCopy(&pStreamDS->Cfg, &CfgAcq);
1677
1678 /*
1679 * Try starting capture again.
1680 */
1681 LogRel2(("DSound: Starting capture on re-opened '%s' ... \n", pStreamDS->Cfg.szName));
1682 hrc = IDirectSoundCaptureBuffer8_Start(pStreamDS->In.pDSCB, DSCBSTART_LOOPING);
1683 if (SUCCEEDED(hrc))
1684 rc = VINF_SUCCESS;
1685 else
1686 LogRelMax(64, ("DSound: Starting to capture on re-opened '%s' failed: %Rhrc\n",
1687 pStreamDS->Cfg.szName, hrc));
1688 }
1689 else
1690 LogRelMax(64, ("DSound: Re-opening '%s' failed: %Rhrc\n", pStreamDS->Cfg.szName, hrc));
1691 }
1692 }
1693 else
1694 {
1695 LogRel2(("DSound: Already capturing (%#x)\n", fStatus));
1696 AssertFailed();
1697 }
1698 }
1699 else
1700 LogRelMax(64, ("DSound: Retrieving capture status for '%s' failed: %Rhrc\n", pStreamDS->Cfg.szName, hrc));
1701 }
1702 LogFlowFunc(("returns %Rrc\n", rc));
1703 return rc;
1704}
1705
1706
1707/**
1708 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
1709 */
1710static DECLCALLBACK(int) drvHostDSoundHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1711{
1712 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
1713 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
1714 LogFlowFunc(("fEnabled=%d '%s'\n", pStreamDS->fEnabled, pStreamDS->Cfg.szName));
1715
1716 /*
1717 * We always reset the buffer before enabling the stream (normally never necessary).
1718 */
1719 drvHostDSoundStreamReset(pThis, pStreamDS);
1720 pStreamDS->fEnabled = true;
1721
1722 /*
1723 * Input streams will start capturing, while output streams will only start
1724 * playing once we get some audio data to play.
1725 */
1726 int rc = VINF_SUCCESS;
1727 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
1728 rc = drvHostDSoundStreamCaptureStart(pThis, pStreamDS);
1729 else
1730 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT);
1731
1732 LogFlowFunc(("returns %Rrc\n", rc));
1733 return rc;
1734}
1735
1736
1737/**
1738 * Worker for drvHostDSoundHA_StreamDestroy, drvHostDSoundHA_StreamDisable and
1739 * drvHostDSoundHA_StreamPause.
1740 *
1741 * @returns VBox status code.
1742 * @param pThis The DSound host audio driver instance data.
1743 * @param pStreamDS The stream instance data.
1744 * @param fReset Whether to reset the buffer and state.
1745 */
1746static int drvHostDSoundStreamStopPlayback(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fReset)
1747{
1748 if (!pStreamDS->Out.pDSB)
1749 return VINF_SUCCESS;
1750
1751 LogRel2(("DSound: Stopping playback of '%s'...\n", pStreamDS->Cfg.szName));
1752 HRESULT hrc = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB);
1753 if (FAILED(hrc))
1754 {
1755 LogFunc(("IDirectSoundBuffer8_Stop -> %Rhrc; will attempt restoring the stream...\n", hrc));
1756 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);
1757 hrc = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB);
1758 if (FAILED(hrc))
1759 LogRelMax(64, ("DSound: %s playback of '%s' failed: %Rhrc\n", fReset ? "Stopping" : "Pausing",
1760 pStreamDS->Cfg.szName, hrc));
1761 }
1762 LogRel2(("DSound: Stopped playback of '%s': %Rhrc\n", pStreamDS->Cfg.szName, hrc));
1763
1764 if (fReset)
1765 drvHostDSoundStreamReset(pThis, pStreamDS);
1766 return SUCCEEDED(hrc) ? VINF_SUCCESS : VERR_AUDIO_STREAM_NOT_READY;
1767}
1768
1769
1770/**
1771 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
1772 */
1773static DECLCALLBACK(int) drvHostDSoundHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1774{
1775 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
1776 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
1777 LogFlowFunc(("fEnabled=%d '%s'\n", pStreamDS->fEnabled, pStreamDS->Cfg.szName));
1778
1779 /*
1780 * Change the state.
1781 */
1782 pStreamDS->fEnabled = false;
1783
1784 /*
1785 * Stop the stream and maybe reset the buffer.
1786 */
1787 int rc = VINF_SUCCESS;
1788 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
1789 {
1790 if (pStreamDS->In.pDSCB)
1791 {
1792 HRESULT hrc = IDirectSoundCaptureBuffer_Stop(pStreamDS->In.pDSCB);
1793 if (SUCCEEDED(hrc))
1794 LogRel3(("DSound: Stopped capture on '%s'.\n", pStreamDS->Cfg.szName));
1795 else
1796 {
1797 LogRelMax(64, ("DSound: Stopping capture on '%s' failed: %Rhrc\n", pStreamDS->Cfg.szName, hrc));
1798 /* Don't report errors up to the caller, as it might just be a capture device change. */
1799 }
1800
1801 /* This isn't strictly speaking necessary since StreamEnable does it too... */
1802 drvHostDSoundStreamReset(pThis, pStreamDS);
1803 }
1804 }
1805 else
1806 {
1807 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT);
1808 if (pStreamDS->Out.pDSB)
1809 {
1810 /* Don't stop draining buffers. They'll stop by themselves. */
1811 if (pStreamDS->Out.fDrain)
1812 LogFunc(("Stream '%s' is draining\n", pStreamDS->Cfg.szName));
1813 else
1814 {
1815 rc = drvHostDSoundStreamStopPlayback(pThis, pStreamDS, true /*fReset*/);
1816 if (RT_SUCCESS(rc))
1817 LogRel3(("DSound: Stopped playback on '%s'.\n", pStreamDS->Cfg.szName));
1818 }
1819 }
1820 }
1821
1822 LogFlowFunc(("returns %Rrc\n", rc));
1823 return rc;
1824}
1825
1826
1827/**
1828 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
1829 *
1830 * @note Basically the same as drvHostDSoundHA_StreamDisable, just w/o the
1831 * buffer resetting and fEnabled change.
1832 */
1833static DECLCALLBACK(int) drvHostDSoundHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1834{
1835 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
1836 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
1837 LogFlowFunc(("fEnabled=%d '%s'\n", pStreamDS->fEnabled, pStreamDS->Cfg.szName));
1838
1839 /*
1840 * Stop the stream and maybe reset the buffer.
1841 */
1842 int rc = VINF_SUCCESS;
1843 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
1844 {
1845 if (pStreamDS->In.pDSCB)
1846 {
1847 HRESULT hrc = IDirectSoundCaptureBuffer_Stop(pStreamDS->In.pDSCB);
1848 if (SUCCEEDED(hrc))
1849 LogRel3(("DSound: Stopped capture on '%s'.\n", pStreamDS->Cfg.szName));
1850 else
1851 {
1852 LogRelMax(64, ("DSound: Stopping capture on '%s' failed: %Rhrc\n", pStreamDS->Cfg.szName, hrc));
1853 /* Don't report errors up to the caller, as it might just be a capture device change. */
1854 }
1855 }
1856 }
1857 else
1858 {
1859 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT);
1860 if (pStreamDS->Out.pDSB)
1861 {
1862 /* Don't stop draining buffers, we won't be resuming them right.
1863 They'll stop by themselves anyway. */
1864 if (pStreamDS->Out.fDrain)
1865 LogFunc(("Stream '%s' is draining\n", pStreamDS->Cfg.szName));
1866 else
1867 {
1868 rc = drvHostDSoundStreamStopPlayback(pThis, pStreamDS, false /*fReset*/);
1869 if (RT_SUCCESS(rc))
1870 LogRel3(("DSound: Stopped playback on '%s'.\n", pStreamDS->Cfg.szName));
1871 }
1872 }
1873 }
1874
1875 LogFlowFunc(("returns %Rrc\n", rc));
1876 return rc;
1877}
1878
1879
1880/**
1881 * Worker for drvHostDSoundHA_StreamResume and drvHostDSoundHA_StreamPlay that
1882 * starts playing the DirectSound Buffer.
1883 *
1884 * @returns VBox status code.
1885 * @param pThis Host audio driver instance.
1886 * @param pStreamDS Stream to start playing.
1887 */
1888static int directSoundPlayStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
1889{
1890 if (!pStreamDS->Out.pDSB)
1891 return VERR_AUDIO_STREAM_NOT_READY;
1892
1893 LogRel2(("DSound: Starting playback of '%s' ...\n", pStreamDS->Cfg.szName));
1894 HRESULT hrc = IDirectSoundBuffer8_Play(pStreamDS->Out.pDSB, 0, 0, DSCBSTART_LOOPING);
1895 if (SUCCEEDED(hrc))
1896 return VINF_SUCCESS;
1897
1898 for (unsigned i = 0; hrc == DSERR_BUFFERLOST && i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
1899 {
1900 LogFunc(("Restarting playback failed due to lost buffer, restoring ...\n"));
1901 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);
1902
1903 hrc = IDirectSoundBuffer8_Play(pStreamDS->Out.pDSB, 0, 0, DSCBSTART_LOOPING);
1904 if (SUCCEEDED(hrc))
1905 return VINF_SUCCESS;
1906 }
1907
1908 LogRelMax(64, ("DSound: Failed to start playback of '%s': %Rhrc\n", pStreamDS->Cfg.szName, hrc));
1909 return VERR_AUDIO_STREAM_NOT_READY;
1910}
1911
1912
1913/**
1914 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume}
1915 */
1916static DECLCALLBACK(int) drvHostDSoundHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1917{
1918 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
1919 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
1920 LogFlowFunc(("fEnabled=%d '%s'\n", pStreamDS->fEnabled, pStreamDS->Cfg.szName));
1921
1922 /*
1923 * Input streams will start capturing, while output streams will only start
1924 * playing if we're past the pre-buffering state.
1925 */
1926 int rc = VINF_SUCCESS;
1927 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
1928 rc = drvHostDSoundStreamCaptureStart(pThis, pStreamDS);
1929 else
1930 {
1931 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT);
1932 if (!pStreamDS->Out.fFirstTransfer)
1933 rc = directSoundPlayStart(pThis, pStreamDS);
1934 }
1935
1936 LogFlowFunc(("returns %Rrc\n", rc));
1937 return rc;
1938}
1939
1940
1941/**
1942 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
1943 */
1944static DECLCALLBACK(int) drvHostDSoundHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1945{
1946 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
1947 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
1948 AssertReturn(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT, VERR_INVALID_PARAMETER);
1949 LogFlowFunc(("fEnabled=%d fDrain=%d '%s'\n", pStreamDS->fEnabled, pStreamDS->Out.fDrain, pStreamDS->Cfg.szName));
1950
1951 /*
1952 * We've started the buffer in looping mode, try switch to non-looping...
1953 */
1954 int rc = VINF_SUCCESS;
1955 if (pStreamDS->Out.pDSB && !pStreamDS->Out.fDrain)
1956 {
1957 LogRel2(("DSound: Switching playback stream '%s' to drain mode...\n", pStreamDS->Cfg.szName));
1958 HRESULT hrc = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB);
1959 if (SUCCEEDED(hrc))
1960 {
1961 hrc = IDirectSoundBuffer8_Play(pStreamDS->Out.pDSB, 0, 0, 0);
1962 if (SUCCEEDED(hrc))
1963 pStreamDS->Out.fDrain = true;
1964 else
1965 LogRelMax(64, ("DSound: Failed to restart '%s' in drain mode: %Rhrc\n", pStreamDS->Cfg.szName, hrc));
1966 }
1967 else
1968 {
1969 Log2Func(("drain: IDirectSoundBuffer8_Stop failed: %Rhrc\n", hrc));
1970 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);
1971
1972 HRESULT hrc2 = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB);
1973 if (SUCCEEDED(hrc2))
1974 LogFunc(("Successfully stopped the stream after restoring it. (hrc=%Rhrc)\n", hrc));
1975 else
1976 {
1977 LogRelMax(64, ("DSound: Failed to stop playback stream '%s' for putting into drain mode: %Rhrc (initial), %Rhrc (after restore)\n",
1978 pStreamDS->Cfg.szName, hrc, hrc2));
1979 rc = VERR_AUDIO_STREAM_NOT_READY;
1980 }
1981 }
1982 }
1983 LogFlowFunc(("returns %Rrc\n", rc));
1984 return rc;
1985}
1986
1987/**
1988 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
1989 */
1990static DECLCALLBACK(int) drvHostDSoundHA_StreamControl(PPDMIHOSTAUDIO pInterface,
1991 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
1992{
1993 /** @todo r=bird: I'd like to get rid of this pfnStreamControl method,
1994 * replacing it with individual StreamXxxx methods. That would save us
1995 * potentally huge switches and more easily see which drivers implement
1996 * which operations (grep for pfnStreamXxxx). */
1997 switch (enmStreamCmd)
1998 {
1999 case PDMAUDIOSTREAMCMD_ENABLE:
2000 return drvHostDSoundHA_StreamEnable(pInterface, pStream);
2001 case PDMAUDIOSTREAMCMD_DISABLE:
2002 return drvHostDSoundHA_StreamDisable(pInterface, pStream);
2003 case PDMAUDIOSTREAMCMD_PAUSE:
2004 return drvHostDSoundHA_StreamPause(pInterface, pStream);
2005 case PDMAUDIOSTREAMCMD_RESUME:
2006 return drvHostDSoundHA_StreamResume(pInterface, pStream);
2007 case PDMAUDIOSTREAMCMD_DRAIN:
2008 return drvHostDSoundHA_StreamDrain(pInterface, pStream);
2009
2010 case PDMAUDIOSTREAMCMD_END:
2011 case PDMAUDIOSTREAMCMD_32BIT_HACK:
2012 case PDMAUDIOSTREAMCMD_INVALID:
2013 /* no default*/
2014 break;
2015 }
2016 return VERR_NOT_SUPPORTED;
2017}
2018
2019
2020/**
2021 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
2022 */
2023static DECLCALLBACK(uint32_t) drvHostDSoundHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2024{
2025 /*PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio); */ RT_NOREF(pInterface);
2026 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2027 AssertPtrReturn(pStreamDS, 0);
2028 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN);
2029
2030 if (pStreamDS->fEnabled)
2031 {
2032 /* This is the same calculation as for StreamGetPending. */
2033 AssertPtr(pStreamDS->In.pDSCB);
2034 DWORD offCaptureCursor = 0;
2035 DWORD offReadCursor = 0;
2036 HRESULT hrc = IDirectSoundCaptureBuffer_GetCurrentPosition(pStreamDS->In.pDSCB, &offCaptureCursor, &offReadCursor);
2037 if (SUCCEEDED(hrc))
2038 {
2039 uint32_t cbPending = dsoundRingDistance(offCaptureCursor, offReadCursor, pStreamDS->cbBufSize);
2040 Log3Func(("cbPending=%RU32\n", cbPending));
2041 return cbPending;
2042 }
2043 AssertMsgFailed(("hrc=%Rhrc\n", hrc));
2044 }
2045
2046 return 0;
2047}
2048
2049
2050/**
2051 * Retrieves the number of free bytes available for writing to a DirectSound output stream.
2052 *
2053 * @return VBox status code. VERR_NOT_AVAILABLE if unable to determine or the
2054 * buffer was not recoverable.
2055 * @param pThis Host audio driver instance.
2056 * @param pStreamDS DirectSound output stream to retrieve number for.
2057 * @param pdwFree Where to return the free amount on success.
2058 * @param poffPlayCursor Where to return the play cursor offset.
2059 */
2060static int dsoundGetFreeOut(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, DWORD *pdwFree, DWORD *poffPlayCursor)
2061{
2062 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2063 AssertPtrReturn(pStreamDS, VERR_INVALID_POINTER);
2064 AssertPtrReturn(pdwFree, VERR_INVALID_POINTER);
2065 AssertPtr(poffPlayCursor);
2066
2067 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT); /* Paranoia. */
2068
2069 LPDIRECTSOUNDBUFFER8 pDSB = pStreamDS->Out.pDSB;
2070 if (!pDSB)
2071 {
2072 AssertPtr(pDSB);
2073 return VERR_INVALID_POINTER;
2074 }
2075
2076 HRESULT hr = S_OK;
2077
2078 /* Get the current play position which is used for calculating the free space in the buffer. */
2079 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
2080 {
2081 DWORD offPlayCursor = 0;
2082 DWORD offWriteCursor = 0;
2083 hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &offPlayCursor, &offWriteCursor);
2084 if (SUCCEEDED(hr))
2085 {
2086 int32_t cbDiff = offWriteCursor - offPlayCursor;
2087 if (cbDiff < 0)
2088 cbDiff += pStreamDS->cbBufSize;
2089
2090 int32_t cbFree = offPlayCursor - pStreamDS->Out.offWritePos;
2091 if (cbFree < 0)
2092 cbFree += pStreamDS->cbBufSize;
2093
2094 if (cbFree > (int32_t)pStreamDS->cbBufSize - cbDiff)
2095 {
2096 /** @todo count/log these. */
2097 pStreamDS->Out.offWritePos = offWriteCursor;
2098 cbFree = pStreamDS->cbBufSize - cbDiff;
2099 }
2100
2101 /* When starting to use a DirectSound buffer, offPlayCursor and offWriteCursor
2102 * both point at position 0, so we won't be able to detect how many bytes
2103 * are writable that way.
2104 *
2105 * So use our per-stream written indicator to see if we just started a stream. */
2106 if (pStreamDS->Out.cbWritten == 0)
2107 cbFree = pStreamDS->cbBufSize;
2108
2109 DSLOGREL(("DSound: offPlayCursor=%RU32, offWriteCursor=%RU32, offWritePos=%RU32 -> cbFree=%RI32\n",
2110 offPlayCursor, offWriteCursor, pStreamDS->Out.offWritePos, cbFree));
2111
2112 *pdwFree = cbFree;
2113 *poffPlayCursor = offPlayCursor;
2114 return VINF_SUCCESS;
2115 }
2116
2117 if (hr != DSERR_BUFFERLOST) /** @todo MSDN doesn't state this error for GetCurrentPosition(). */
2118 break;
2119
2120 LogFunc(("Getting playing position failed due to lost buffer, restoring ...\n"));
2121
2122 directSoundPlayRestore(pThis, pDSB);
2123 }
2124
2125 if (hr != DSERR_BUFFERLOST) /* Avoid log flooding if the error is still there. */
2126 DSLOGREL(("DSound: Getting current playback position failed with %Rhrc\n", hr));
2127
2128 LogFunc(("Failed with %Rhrc\n", hr));
2129
2130 *poffPlayCursor = pStreamDS->cbBufSize;
2131 return VERR_NOT_AVAILABLE;
2132}
2133
2134
2135/**
2136 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
2137 */
2138static DECLCALLBACK(uint32_t) drvHostDSoundHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2139{
2140 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
2141 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2142 AssertPtrReturn(pStreamDS, 0);
2143
2144 DWORD cbFree = 0;
2145 DWORD offIgn = 0;
2146 int rc = dsoundGetFreeOut(pThis, pStreamDS, &cbFree, &offIgn);
2147 AssertRCReturn(rc, 0);
2148
2149 return cbFree;
2150}
2151
2152#if 0 /* This isn't working as the write cursor is more a function of time than what we do.
2153 Previously we only reported the pre-buffering status anyway, so no harm. */
2154/**
2155 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending}
2156 */
2157static DECLCALLBACK(uint32_t) drvHostDSoundHA_StreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2158{
2159 /*PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio); */ RT_NOREF(pInterface);
2160 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2161 AssertPtrReturn(pStreamDS, 0);
2162
2163 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT)
2164 {
2165 /* This is a similar calculation as for StreamGetReadable, only for an output buffer. */
2166 AssertPtr(pStreamDS->In.pDSCB);
2167 DWORD offPlayCursor = 0;
2168 DWORD offWriteCursor = 0;
2169 HRESULT hrc = IDirectSoundBuffer8_GetCurrentPosition(pStreamDS->Out.pDSB, &offPlayCursor, &offWriteCursor);
2170 if (SUCCEEDED(hrc))
2171 {
2172 uint32_t cbPending = dsoundRingDistance(offWriteCursor, offPlayCursor, pStreamDS->cbBufSize);
2173 Log3Func(("cbPending=%RU32\n", cbPending));
2174 return cbPending;
2175 }
2176 AssertMsgFailed(("hrc=%Rhrc\n", hrc));
2177 }
2178 /* else: For input streams we never have any pending data. */
2179
2180 return 0;
2181}
2182#endif
2183
2184/**
2185 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
2186 */
2187static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostDSoundHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2188{
2189 RT_NOREF(pInterface);
2190 AssertPtrReturn(pStream, PDMAUDIOSTREAMSTS_FLAGS_NONE);
2191
2192 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2193
2194 PDMAUDIOSTREAMSTS fStrmStatus = PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED;
2195
2196 if (pStreamDS->fEnabled)
2197 fStrmStatus |= PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
2198
2199 return fStrmStatus;
2200}
2201
2202
2203/**
2204 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
2205 */
2206static DECLCALLBACK(int) drvHostDSoundHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
2207 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
2208{
2209 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
2210 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2211 AssertPtrReturn(pStreamDS, 0);
2212 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2213 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
2214 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
2215
2216 if (pStreamDS->fEnabled)
2217 AssertReturn(pStreamDS->cbBufSize, VERR_INTERNAL_ERROR_2);
2218 else
2219 {
2220 Log2Func(("Stream disabled, skipping\n"));
2221 return VINF_SUCCESS;
2222 }
2223
2224/** @todo Any condition under which we should call dsoundUpdateStatusInternal(pThis) here?
2225 * The old code thought it did so in case of failure, only it couldn't ever fails, so it never did. */
2226
2227 /*
2228 * Transfer loop.
2229 */
2230 uint32_t cbWritten = 0;
2231 while (cbBuf > 0)
2232 {
2233 /*
2234 * Figure out how much we can possibly write.
2235 */
2236 DWORD offPlayCursor = 0;
2237 DWORD cbWritable = 0;
2238 int rc = dsoundGetFreeOut(pThis, pStreamDS, &cbWritable, &offPlayCursor);
2239 AssertRCReturn(rc, rc);
2240 if (cbWritable < pStreamDS->Cfg.Props.cbFrame)
2241 break;
2242
2243 uint32_t const cbToWrite = RT_MIN(cbWritable, cbBuf);
2244 Log3Func(("offPlay=%#x offWritePos=%#x -> cbWritable=%#x cbToWrite=%#x%s%s\n", offPlayCursor, pStreamDS->Out.offWritePos,
2245 cbWritable, cbToWrite, pStreamDS->Out.fFirstTransfer ? " first" : "", pStreamDS->Out.fDrain ? " drain" : ""));
2246
2247 /*
2248 * Lock that amount of buffer.
2249 */
2250 PVOID pv1 = NULL;
2251 DWORD cb1 = 0;
2252 PVOID pv2 = NULL;
2253 DWORD cb2 = 0;
2254 HRESULT hrc = directSoundPlayLock(pThis, pStreamDS, pStreamDS->Out.offWritePos, cbToWrite,
2255 &pv1, &pv2, &cb1, &cb2, 0 /*dwFlags*/);
2256 AssertMsgReturn(SUCCEEDED(hrc), ("%Rhrc\n", hrc), VERR_ACCESS_DENIED); /** @todo translate these status codes already! */
2257 //AssertMsg(cb1 + cb2 == cbToWrite, ("%#x + %#x vs %#x\n", cb1, cb2, cbToWrite));
2258
2259 /*
2260 * Copy over the data.
2261 */
2262 memcpy(pv1, pvBuf, cb1);
2263 pvBuf = (uint8_t *)pvBuf + cb1;
2264 cbBuf -= cb1;
2265 cbWritten += cb1;
2266
2267 if (pv2)
2268 {
2269 memcpy(pv2, pvBuf, cb2);
2270 pvBuf = (uint8_t *)pvBuf + cb2;
2271 cbBuf -= cb2;
2272 cbWritten += cb2;
2273 }
2274
2275 /*
2276 * Unlock and update the write position.
2277 */
2278 directSoundPlayUnlock(pThis, pStreamDS->Out.pDSB, pv1, pv2, cb1, cb2); /** @todo r=bird: pThis + pDSB parameters here for Unlock, but only pThis for Lock. Why? */
2279 pStreamDS->Out.offWritePos = (pStreamDS->Out.offWritePos + cb1 + cb2) % pStreamDS->cbBufSize;
2280
2281 /*
2282 * If this was the first chunk, kick off playing.
2283 */
2284 if (!pStreamDS->Out.fFirstTransfer)
2285 { /* likely */ }
2286 else
2287 {
2288 *pcbWritten = cbWritten;
2289 rc = directSoundPlayStart(pThis, pStreamDS);
2290 AssertRCReturn(rc, rc);
2291 pStreamDS->Out.fFirstTransfer = false;
2292 }
2293 }
2294
2295 /*
2296 * Done.
2297 */
2298 *pcbWritten = cbWritten;
2299
2300 pStreamDS->Out.cbTransferred += cbWritten;
2301 if (cbWritten)
2302 {
2303 uint64_t const msPrev = pStreamDS->Out.tsLastTransferredMs;
2304 pStreamDS->Out.cbLastTransferred = cbWritten;
2305 pStreamDS->Out.tsLastTransferredMs = RTTimeMilliTS();
2306 LogFlowFunc(("cbLastTransferred=%RU32, tsLastTransferredMs=%RU64 cMsDelta=%RU64\n",
2307 cbWritten, pStreamDS->Out.tsLastTransferredMs, msPrev ? pStreamDS->Out.tsLastTransferredMs - msPrev : 0));
2308 }
2309 return VINF_SUCCESS;
2310}
2311
2312
2313/**
2314 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
2315 */
2316static DECLCALLBACK(int) drvHostDSoundHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
2317 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
2318{
2319 /*PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);*/ RT_NOREF(pInterface);
2320 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2321 AssertPtrReturn(pStreamDS, 0);
2322 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2323 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
2324 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
2325
2326#if 0 /** @todo r=bird: shouldn't we do the same check as for output streams? */
2327 if (pStreamDS->fEnabled)
2328 AssertReturn(pStreamDS->cbBufSize, VERR_INTERNAL_ERROR_2);
2329 else
2330 {
2331 Log2Func(("Stream disabled, skipping\n"));
2332 return VINF_SUCCESS;
2333 }
2334#endif
2335
2336 /*
2337 * Read loop.
2338 */
2339 uint32_t cbRead = 0;
2340 while (cbBuf > 0)
2341 {
2342 /*
2343 * Figure out how much we can read.
2344 */
2345 DWORD offCaptureCursor = 0;
2346 DWORD offReadCursor = 0;
2347 HRESULT hrc = IDirectSoundCaptureBuffer_GetCurrentPosition(pStreamDS->In.pDSCB, &offCaptureCursor, &offReadCursor);
2348 AssertMsgReturn(SUCCEEDED(hrc), ("%Rhrc\n", hrc), VERR_ACCESS_DENIED); /** @todo translate these status codes already! */
2349 //AssertMsg(offReadCursor == pStreamDS->In.offReadPos, ("%#x %#x\n", offReadCursor, pStreamDS->In.offReadPos));
2350
2351 uint32_t const cbReadable = dsoundRingDistance(offCaptureCursor, pStreamDS->In.offReadPos, pStreamDS->cbBufSize);
2352
2353 if (cbReadable >= pStreamDS->Cfg.Props.cbFrame)
2354 { /* likely */ }
2355 else
2356 {
2357 if (cbRead > 0)
2358 { /* likely */ }
2359 else if (pStreamDS->In.cOverruns < 32)
2360 {
2361 pStreamDS->In.cOverruns++;
2362 DSLOG(("DSound: Warning: Buffer full (size is %zu bytes), skipping to record data (overflow #%RU32)\n",
2363 pStreamDS->cbBufSize, pStreamDS->In.cOverruns));
2364 }
2365 break;
2366 }
2367
2368 uint32_t const cbToRead = RT_MIN(cbReadable, cbBuf);
2369 Log3Func(("offCapture=%#x offRead=%#x/%#x -> cbWritable=%#x cbToWrite=%#x\n",
2370 offCaptureCursor, offReadCursor, pStreamDS->In.offReadPos, cbReadable, cbToRead));
2371
2372 /*
2373 * Lock that amount of buffer.
2374 */
2375 PVOID pv1 = NULL;
2376 DWORD cb1 = 0;
2377 PVOID pv2 = NULL;
2378 DWORD cb2 = 0;
2379 hrc = directSoundCaptureLock(pStreamDS, pStreamDS->In.offReadPos, cbToRead, &pv1, &pv2, &cb1, &cb2, 0 /*dwFlags*/);
2380 AssertMsgReturn(SUCCEEDED(hrc), ("%Rhrc\n", hrc), VERR_ACCESS_DENIED); /** @todo translate these status codes already! */
2381 AssertMsg(cb1 + cb2 == cbToRead, ("%#x + %#x vs %#x\n", cb1, cb2, cbToRead));
2382
2383 /*
2384 * Copy over the data.
2385 */
2386 memcpy(pvBuf, pv1, cb1);
2387 pvBuf = (uint8_t *)pvBuf + cb1;
2388 cbBuf -= cb1;
2389 cbRead += cb1;
2390
2391 if (pv2)
2392 {
2393 memcpy(pvBuf, pv2, cb2);
2394 pvBuf = (uint8_t *)pvBuf + cb2;
2395 cbBuf -= cb2;
2396 cbRead += cb2;
2397 }
2398
2399 /*
2400 * Unlock and update the write position.
2401 */
2402 directSoundCaptureUnlock(pStreamDS->In.pDSCB, pv1, pv2, cb1, cb2); /** @todo r=bird: pDSB parameter here for Unlock, but pStreamDS for Lock. Why? */
2403 pStreamDS->In.offReadPos = (pStreamDS->In.offReadPos + cb1 + cb2) % pStreamDS->cbBufSize;
2404 }
2405
2406 /*
2407 * Done.
2408 */
2409 if (pcbRead)
2410 *pcbRead = cbRead;
2411
2412#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
2413 if (cbRead)
2414 {
2415 RTFILE hFile;
2416 int rc2 = RTFileOpen(&hFile, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "dsoundCapture.pcm",
2417 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
2418 if (RT_SUCCESS(rc2))
2419 {
2420 RTFileWrite(hFile, (uint8_t *)pvBuf - cbRead, cbRead, NULL);
2421 RTFileClose(hFile);
2422 }
2423 }
2424#endif
2425 return VINF_SUCCESS;
2426}
2427
2428
2429/*********************************************************************************************************************************
2430* PDMDRVINS::IBase Interface *
2431*********************************************************************************************************************************/
2432
2433/**
2434 * @callback_method_impl{PDMIBASE,pfnQueryInterface}
2435 */
2436static DECLCALLBACK(void *) drvHostDSoundQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2437{
2438 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2439 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
2440
2441 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
2442 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
2443 return NULL;
2444}
2445
2446
2447/*********************************************************************************************************************************
2448* PDMDRVREG Interface *
2449*********************************************************************************************************************************/
2450
2451/**
2452 * @callback_method_impl{FNPDMDRVDESTRUCT, pfnDestruct}
2453 */
2454static DECLCALLBACK(void) drvHostDSoundDestruct(PPDMDRVINS pDrvIns)
2455{
2456 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
2457 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2458
2459 LogFlowFuncEnter();
2460
2461#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
2462 if (pThis->m_pNotificationClient)
2463 {
2464 pThis->m_pNotificationClient->Unregister();
2465 pThis->m_pNotificationClient->Release();
2466
2467 pThis->m_pNotificationClient = NULL;
2468 }
2469#endif
2470
2471 PDMAudioHostEnumDelete(&pThis->DeviceEnum);
2472
2473 if (pThis->pDrvIns)
2474 CoUninitialize();
2475
2476 int rc2 = RTCritSectDelete(&pThis->CritSect);
2477 AssertRC(rc2);
2478
2479 LogFlowFuncLeave();
2480}
2481
2482
2483static LPCGUID dsoundConfigQueryGUID(PCFGMNODE pCfg, const char *pszName, RTUUID *pUuid)
2484{
2485 LPCGUID pGuid = NULL;
2486
2487 char *pszGuid = NULL;
2488 int rc = CFGMR3QueryStringAlloc(pCfg, pszName, &pszGuid);
2489 if (RT_SUCCESS(rc))
2490 {
2491 rc = RTUuidFromStr(pUuid, pszGuid);
2492 if (RT_SUCCESS(rc))
2493 pGuid = (LPCGUID)&pUuid;
2494 else
2495 DSLOGREL(("DSound: Error parsing device GUID for device '%s': %Rrc\n", pszName, rc));
2496
2497 RTStrFree(pszGuid);
2498 }
2499
2500 return pGuid;
2501}
2502
2503
2504static void dsoundConfigInit(PDRVHOSTDSOUND pThis, PCFGMNODE pCfg)
2505{
2506 pThis->Cfg.pGuidPlay = dsoundConfigQueryGUID(pCfg, "DeviceGuidOut", &pThis->Cfg.uuidPlay);
2507 pThis->Cfg.pGuidCapture = dsoundConfigQueryGUID(pCfg, "DeviceGuidIn", &pThis->Cfg.uuidCapture);
2508
2509 DSLOG(("DSound: Configuration: DeviceGuidOut {%RTuuid}, DeviceGuidIn {%RTuuid}\n",
2510 &pThis->Cfg.uuidPlay,
2511 &pThis->Cfg.uuidCapture));
2512}
2513
2514
2515/**
2516 * @callback_method_impl{FNPDMDRVCONSTRUCT,
2517 * Construct a DirectSound Audio driver instance.}
2518 */
2519static DECLCALLBACK(int) drvHostDSoundConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
2520{
2521 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2522 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
2523 RT_NOREF(fFlags);
2524 LogRel(("Audio: Initializing DirectSound audio driver\n"));
2525
2526 /*
2527 * Init basic data members and interfaces.
2528 */
2529 pThis->pDrvIns = pDrvIns;
2530 /* IBase */
2531 pDrvIns->IBase.pfnQueryInterface = drvHostDSoundQueryInterface;
2532 /* IHostAudio */
2533 pThis->IHostAudio.pfnGetConfig = drvHostDSoundHA_GetConfig;
2534 pThis->IHostAudio.pfnGetDevices = drvHostDSoundHA_GetDevices;
2535 pThis->IHostAudio.pfnGetStatus = drvHostDSoundHA_GetStatus;
2536 pThis->IHostAudio.pfnStreamCreate = drvHostDSoundHA_StreamCreate;
2537 pThis->IHostAudio.pfnStreamDestroy = drvHostDSoundHA_StreamDestroy;
2538 pThis->IHostAudio.pfnStreamControl = drvHostDSoundHA_StreamControl;
2539 pThis->IHostAudio.pfnStreamGetReadable = drvHostDSoundHA_StreamGetReadable;
2540 pThis->IHostAudio.pfnStreamGetWritable = drvHostDSoundHA_StreamGetWritable;
2541 pThis->IHostAudio.pfnStreamGetPending = NULL;
2542 pThis->IHostAudio.pfnStreamGetStatus = drvHostDSoundHA_StreamGetStatus;
2543 pThis->IHostAudio.pfnStreamPlay = drvHostDSoundHA_StreamPlay;
2544 pThis->IHostAudio.pfnStreamCapture = drvHostDSoundHA_StreamCapture;
2545
2546 /*
2547 * Init the static parts.
2548 */
2549 PDMAudioHostEnumInit(&pThis->DeviceEnum);
2550
2551 pThis->fEnabledIn = false;
2552 pThis->fEnabledOut = false;
2553
2554 /*
2555 * Verify that IDirectSound is available.
2556 */
2557 HRESULT hrc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
2558 if (SUCCEEDED(hrc))
2559 {
2560 LPDIRECTSOUND pDirectSound = NULL;
2561 hrc = CoCreateInstance(CLSID_DirectSound, NULL, CLSCTX_ALL, IID_IDirectSound, (void **)&pDirectSound);
2562 if (SUCCEEDED(hrc))
2563 IDirectSound_Release(pDirectSound);
2564 else
2565 {
2566 LogRel(("DSound: DirectSound not available: %Rhrc\n", hrc));
2567 return VERR_AUDIO_BACKEND_INIT_FAILED;
2568 }
2569 }
2570 else
2571 {
2572 LogRel(("DSound: CoInitializeEx(,COINIT_MULTITHREADED) failed: %Rhrc\n", hrc));
2573 return VERR_AUDIO_BACKEND_INIT_FAILED;
2574 }
2575
2576#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
2577 /*
2578 * Set up WASAPI device change notifications (Vista+).
2579 */
2580 if (RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
2581 {
2582 /* Get the notification interface (from DrvAudio). */
2583# ifdef VBOX_WITH_AUDIO_CALLBACKS
2584 PPDMIAUDIONOTIFYFROMHOST pIAudioNotifyFromHost = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIONOTIFYFROMHOST);
2585 Assert(pIAudioNotifyFromHost);
2586# else
2587 PPDMIAUDIONOTIFYFROMHOST pIAudioNotifyFromHost = NULL;
2588# endif
2589 try
2590 {
2591 pThis->m_pNotificationClient = new DrvHostAudioDSoundMMNotifClient(pIAudioNotifyFromHost);
2592 }
2593 catch (std::bad_alloc &)
2594 {
2595 return VERR_NO_MEMORY;
2596 }
2597 hrc = pThis->m_pNotificationClient->Initialize();
2598 if (SUCCEEDED(hrc))
2599 {
2600 hrc = pThis->m_pNotificationClient->Register();
2601 if (SUCCEEDED(hrc))
2602 LogRel2(("DSound: Notification client is enabled (ver %#RX64)\n", RTSystemGetNtVersion()));
2603 else
2604 {
2605 LogRel(("DSound: Notification client registration failed: %Rhrc\n", hrc));
2606 return VERR_AUDIO_BACKEND_INIT_FAILED;
2607 }
2608 }
2609 else
2610 {
2611 LogRel(("DSound: Notification client initialization failed: %Rhrc\n", hrc));
2612 return VERR_AUDIO_BACKEND_INIT_FAILED;
2613 }
2614 }
2615 else
2616 LogRel2(("DSound: Notification client is disabled (ver %#RX64)\n", RTSystemGetNtVersion()));
2617#endif
2618
2619 /*
2620 * Initialize configuration values and critical section.
2621 */
2622 dsoundConfigInit(pThis, pCfg);
2623 return RTCritSectInit(&pThis->CritSect);
2624}
2625
2626
2627/**
2628 * PDM driver registration.
2629 */
2630const PDMDRVREG g_DrvHostDSound =
2631{
2632 /* u32Version */
2633 PDM_DRVREG_VERSION,
2634 /* szName */
2635 "DSoundAudio",
2636 /* szRCMod */
2637 "",
2638 /* szR0Mod */
2639 "",
2640 /* pszDescription */
2641 "DirectSound Audio host driver",
2642 /* fFlags */
2643 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2644 /* fClass. */
2645 PDM_DRVREG_CLASS_AUDIO,
2646 /* cMaxInstances */
2647 ~0U,
2648 /* cbInstance */
2649 sizeof(DRVHOSTDSOUND),
2650 /* pfnConstruct */
2651 drvHostDSoundConstruct,
2652 /* pfnDestruct */
2653 drvHostDSoundDestruct,
2654 /* pfnRelocate */
2655 NULL,
2656 /* pfnIOCtl */
2657 NULL,
2658 /* pfnPowerOn */
2659 NULL,
2660 /* pfnReset */
2661 NULL,
2662 /* pfnSuspend */
2663 NULL,
2664 /* pfnResume */
2665 NULL,
2666 /* pfnAttach */
2667 NULL,
2668 /* pfnDetach */
2669 NULL,
2670 /* pfnPowerOff */
2671 NULL,
2672 /* pfnSoftReset */
2673 NULL,
2674 /* u32EndVersion */
2675 PDM_DRVREG_VERSION
2676};
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