VirtualBox

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

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

Audio: VBoxMMNotificationClient.* -> DrvHostAudioDSoundMMNotifClient.* bugref:9890

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