VirtualBox

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

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

DrvHostAudioDSound: Refreshed the stream creation code. bugref:9890

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