VirtualBox

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

Last change on this file since 74038 was 73838, checked in by vboxsync, 6 years ago

Audio: Added the backend's (friendly) name into the backend configuration.

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