VirtualBox

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

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

Audio: Made PPDMAUDIOBACKENDSTREAM a typedef instead of a weird #define void-pointer. bugref:9890

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

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