VirtualBox

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

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

DrvHostAudioDSound: We don't need to enter the critical section when doing drvHostDSoundHA_GetDevices. Change the dynamic dsound.dll resolving to be done once only. bugref:9890

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