VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostDSound.cpp@ 87179

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

scm

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