VirtualBox

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

Last change on this file since 75606 was 74572, checked in by vboxsync, 6 years ago

Audio/DrvHostDSound.cpp: Removed unneeded assertion in dsoundStreamReset(), as that function can be called also on failure when no stream direction is set (yet).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 82.6 KB
Line 
1/* $Id: DrvHostDSound.cpp 74572 2018-10-02 08:23:22Z 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
1066#ifdef LOG_ENABLED
1067 pStreamDS->Dbg.tsLastTransferredMs = 0;
1068#endif
1069}
1070
1071
1072/**
1073 * Starts playing a DirectSound stream.
1074 *
1075 * @return HRESULT
1076 * @param pThis Host audio driver instance.
1077 * @param pStreamDS Stream to start playing.
1078 */
1079static HRESULT directSoundPlayStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
1080{
1081 HRESULT hr = S_OK;
1082
1083 DWORD fFlags = DSCBSTART_LOOPING;
1084
1085 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
1086 {
1087 DSLOG(("DSound: Starting playback\n"));
1088 hr = IDirectSoundBuffer8_Play(pStreamDS->Out.pDSB, 0, 0, fFlags);
1089 if ( SUCCEEDED(hr)
1090 || hr != DSERR_BUFFERLOST)
1091 break;
1092 else
1093 {
1094 LogFunc(("Restarting playback failed due to lost buffer, restoring ...\n"));
1095 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);
1096 }
1097 }
1098
1099 return hr;
1100}
1101
1102
1103/*
1104 * DirectSoundCapture
1105 */
1106
1107static LPCGUID dsoundCaptureSelectDevice(PDRVHOSTDSOUND pThis, PPDMAUDIOSTREAMCFG pCfg)
1108{
1109 AssertPtrReturn(pThis, NULL);
1110 AssertPtrReturn(pCfg, NULL);
1111
1112 int rc = VINF_SUCCESS;
1113
1114 LPCGUID pGUID = pThis->Cfg.pGuidCapture;
1115 if (!pGUID)
1116 {
1117 PDSOUNDDEV pDev = NULL;
1118
1119 switch (pCfg->DestSource.Source)
1120 {
1121 case PDMAUDIORECSOURCE_LINE:
1122 /*
1123 * At the moment we're only supporting line-in in the HDA emulation,
1124 * and line-in + mic-in in the AC'97 emulation both are expected
1125 * to use the host's mic-in as well.
1126 *
1127 * So the fall through here is intentional for now.
1128 */
1129 case PDMAUDIORECSOURCE_MIC:
1130 {
1131 RTListForEach(&pThis->lstDevInput, pDev, DSOUNDDEV, Node)
1132 {
1133 if (RTStrIStr(pDev->pszName, "Mic")) /** @todo What is with non en_us windows versions? */
1134 break;
1135 }
1136
1137 if (RTListNodeIsDummy(&pThis->lstDevInput, pDev, DSOUNDDEV, Node))
1138 pDev = NULL; /* Found nothing. */
1139
1140 break;
1141 }
1142
1143 default:
1144 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
1145 break;
1146 }
1147
1148 if ( RT_SUCCESS(rc)
1149 && pDev)
1150 {
1151 DSLOG(("DSound: Guest source '%s' is using host recording device '%s'\n",
1152 DrvAudioHlpRecSrcToStr(pCfg->DestSource.Source), pDev->pszName));
1153
1154 pGUID = &pDev->Guid;
1155 }
1156 }
1157
1158 if (RT_FAILURE(rc))
1159 {
1160 LogRel(("DSound: Selecting recording device failed with %Rrc\n", rc));
1161 return NULL;
1162 }
1163
1164 char *pszGUID = dsoundGUIDToUtf8StrA(pGUID);
1165
1166 /* This always has to be in the release log. */
1167 LogRel(("DSound: Guest source '%s' is using host recording device with GUID '%s'\n",
1168 DrvAudioHlpRecSrcToStr(pCfg->DestSource.Source), pszGUID ? pszGUID: "{?}"));
1169
1170 if (pszGUID)
1171 {
1172 RTStrFree(pszGUID);
1173 pszGUID = NULL;
1174 }
1175
1176 return pGUID;
1177}
1178
1179/**
1180 * Transfers audio data from the DirectSound capture instance to the internal buffer.
1181 * Due to internal accounting and querying DirectSound, this function knows how much it can transfer at once.
1182 *
1183 * @return IPRT status code.
1184 * @param pThis Host audio driver instance.
1185 * @param pStreamDS Stream to capture audio data for.
1186 */
1187static int dsoundCaptureTransfer(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
1188{
1189 RT_NOREF(pThis);
1190
1191 LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB = pStreamDS->In.pDSCB;
1192 AssertPtr(pDSCB);
1193
1194 DWORD offCaptureCursor;
1195 HRESULT hr = IDirectSoundCaptureBuffer_GetCurrentPosition(pDSCB, NULL, &offCaptureCursor);
1196 if (FAILED(hr))
1197 {
1198 AssertFailed();
1199 return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1200 }
1201
1202 DWORD cbUsed = dsoundRingDistance(offCaptureCursor, pStreamDS->In.offReadPos, pStreamDS->cbBufSize);
1203
1204 PRTCIRCBUF pCircBuf = pStreamDS->pCircBuf;
1205 AssertPtr(pCircBuf);
1206
1207 uint32_t cbFree = (uint32_t)RTCircBufFree(pCircBuf);
1208 if ( !cbFree
1209 || pStreamDS->In.cOverruns < 32) /** @todo Make this configurable. */
1210 {
1211 DSLOG(("DSound: Warning: Capture buffer full, skipping to record data (%RU32 bytes)\n", cbUsed));
1212 pStreamDS->In.cOverruns++;
1213 }
1214
1215 DWORD cbToCapture = RT_MIN(cbUsed, cbFree);
1216
1217 Log3Func(("cbUsed=%ld, cbToCapture=%ld\n", cbUsed, cbToCapture));
1218
1219 while (cbToCapture)
1220 {
1221 void *pvBuf;
1222 size_t cbBuf;
1223 RTCircBufAcquireWriteBlock(pCircBuf, cbToCapture, &pvBuf, &cbBuf);
1224
1225 if (cbBuf)
1226 {
1227 PVOID pv1, pv2;
1228 DWORD cb1, cb2;
1229 hr = directSoundCaptureLock(pStreamDS, pStreamDS->In.offReadPos, (DWORD)cbBuf,
1230 &pv1, &pv2, &cb1, &cb2, 0 /* dwFlags */);
1231 if (FAILED(hr))
1232 break;
1233
1234 AssertPtr(pv1);
1235 Assert(cb1);
1236
1237 memcpy(pvBuf, pv1, cb1);
1238
1239 if (pv2 && cb2) /* Buffer wrap-around? Write second part. */
1240 memcpy((uint8_t *)pvBuf + cb1, pv2, cb2);
1241
1242 directSoundCaptureUnlock(pDSCB, pv1, pv2, cb1, cb2);
1243
1244 pStreamDS->In.offReadPos = (pStreamDS->In.offReadPos + cb1 + cb2) % pStreamDS->cbBufSize;
1245
1246 Assert(cbToCapture >= cbBuf);
1247 cbToCapture -= (uint32_t)cbBuf;
1248 }
1249
1250#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
1251 if (cbBuf)
1252 {
1253 RTFILE fh;
1254 int rc2 = RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "dsoundCapture.pcm",
1255 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1256 if (RT_SUCCESS(rc2))
1257 {
1258 RTFileWrite(fh, pvBuf, cbBuf, NULL);
1259 RTFileClose(fh);
1260 }
1261 }
1262#endif
1263 RTCircBufReleaseWriteBlock(pCircBuf, cbBuf);
1264 }
1265
1266 return VINF_SUCCESS;
1267}
1268
1269/**
1270 * Destroys the DirectSound capturing interface.
1271 *
1272 * @return IPRT status code.
1273 * @param pThis Driver instance to destroy capturing interface for.
1274 */
1275static void directSoundCaptureInterfaceDestroy(PDRVHOSTDSOUND pThis)
1276{
1277 if (pThis->pDSC)
1278 {
1279 LogFlowFuncEnter();
1280
1281 IDirectSoundCapture_Release(pThis->pDSC);
1282 pThis->pDSC = NULL;
1283 }
1284}
1285
1286/**
1287 * Creates the DirectSound capturing interface.
1288 *
1289 * @return IPRT status code.
1290 * @param pThis Driver instance to create the capturing interface for.
1291 * @param pCfg Audio stream to use for creating the capturing interface.
1292 */
1293static HRESULT directSoundCaptureInterfaceCreate(PDRVHOSTDSOUND pThis, PPDMAUDIOSTREAMCFG pCfg)
1294{
1295 AssertPtrReturn(pThis, E_POINTER);
1296 AssertPtrReturn(pCfg, E_POINTER);
1297
1298 if (pThis->pDSC)
1299 return S_OK;
1300
1301 LogFlowFuncEnter();
1302
1303 HRESULT hr = CoCreateInstance(CLSID_DirectSoundCapture8, NULL, CLSCTX_ALL,
1304 IID_IDirectSoundCapture8, (void **)&pThis->pDSC);
1305 if (FAILED(hr))
1306 {
1307 DSLOGREL(("DSound: Creating capture instance failed with %Rhrc\n", hr));
1308 }
1309 else
1310 {
1311 LPCGUID pGUID = dsoundCaptureSelectDevice(pThis, pCfg);
1312 /* pGUID can be NULL when using the default device. */
1313
1314 hr = IDirectSoundCapture_Initialize(pThis->pDSC, pGUID);
1315 if (FAILED(hr))
1316 {
1317 if (hr == DSERR_NODRIVER) /* Usually means that no capture devices are attached. */
1318 DSLOGREL(("DSound: Capture device currently is unavailable\n"));
1319 else
1320 DSLOGREL(("DSound: Initializing capturing device failed with %Rhrc\n", hr));
1321
1322 directSoundCaptureInterfaceDestroy(pThis);
1323 }
1324 }
1325
1326 LogFlowFunc(("Returning %Rhrc\n", hr));
1327 return hr;
1328}
1329
1330static HRESULT directSoundCaptureClose(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
1331{
1332 AssertPtrReturn(pThis, E_POINTER);
1333 AssertPtrReturn(pStreamDS, E_POINTER);
1334
1335 LogFlowFuncEnter();
1336
1337 HRESULT hr = directSoundCaptureStop(pThis, pStreamDS, true /* fFlush */);
1338 if (FAILED(hr))
1339 return hr;
1340
1341 if ( pStreamDS
1342 && pStreamDS->In.pDSCB)
1343 {
1344 DSLOG(("DSound: Closing capturing stream\n"));
1345
1346 IDirectSoundCaptureBuffer8_Release(pStreamDS->In.pDSCB);
1347 pStreamDS->In.pDSCB = NULL;
1348 }
1349
1350 LogFlowFunc(("Returning %Rhrc\n", hr));
1351 return hr;
1352}
1353
1354static HRESULT directSoundCaptureOpen(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS,
1355 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1356{
1357 AssertPtrReturn(pThis, E_POINTER);
1358 AssertPtrReturn(pStreamDS, E_POINTER);
1359 AssertPtrReturn(pCfgReq, E_POINTER);
1360 AssertPtrReturn(pCfgAcq, E_POINTER);
1361
1362 LogFlowFuncEnter();
1363
1364 Assert(pStreamDS->In.pDSCB == NULL);
1365
1366 DSLOG(("DSound: Opening capturing stream (uHz=%RU32, cChannels=%RU8, cBits=%RU8, fSigned=%RTbool)\n",
1367 pCfgReq->Props.uHz,
1368 pCfgReq->Props.cChannels,
1369 pCfgReq->Props.cBytes * 8,
1370 pCfgReq->Props.fSigned));
1371
1372 WAVEFORMATEX wfx;
1373 int rc = dsoundWaveFmtFromCfg(pCfgReq, &wfx);
1374 if (RT_FAILURE(rc))
1375 return E_INVALIDARG;
1376
1377 dsoundUpdateStatusInternalEx(pThis, NULL /* Cfg */, DSOUNDENUMCBFLAGS_LOG /* fEnum */);
1378
1379 HRESULT hr = directSoundCaptureInterfaceCreate(pThis, pCfgReq);
1380 if (FAILED(hr))
1381 return hr;
1382
1383 do /* To use breaks. */
1384 {
1385 DSCBUFFERDESC bd;
1386 RT_ZERO(bd);
1387
1388 bd.dwSize = sizeof(bd);
1389 bd.lpwfxFormat = &wfx;
1390 bd.dwBufferBytes = DrvAudioHlpFramesToBytes(pCfgReq->Backend.cfBufferSize, &pCfgReq->Props);
1391
1392 DSLOG(("DSound: Requested capture buffer is %RU64ms (%ld bytes)\n",
1393 DrvAudioHlpBytesToMilli(bd.dwBufferBytes, &pCfgReq->Props), bd.dwBufferBytes));
1394
1395 LPDIRECTSOUNDCAPTUREBUFFER pDSCB;
1396 hr = IDirectSoundCapture_CreateCaptureBuffer(pThis->pDSC, &bd, &pDSCB, NULL);
1397 if (FAILED(hr))
1398 {
1399 if (hr == E_ACCESSDENIED)
1400 {
1401 DSLOGREL(("DSound: Capturing input from host not possible, access denied\n"));
1402 }
1403 else
1404 DSLOGREL(("DSound: Creating capture buffer failed with %Rhrc\n", hr));
1405 break;
1406 }
1407
1408 hr = IDirectSoundCaptureBuffer_QueryInterface(pDSCB, IID_IDirectSoundCaptureBuffer8, (void **)&pStreamDS->In.pDSCB);
1409 IDirectSoundCaptureBuffer_Release(pDSCB);
1410 if (FAILED(hr))
1411 {
1412 DSLOGREL(("DSound: Querying interface for capture buffer failed with %Rhrc\n", hr));
1413 break;
1414 }
1415
1416 /*
1417 * Query the actual parameters.
1418 */
1419 DWORD offByteReadPos = 0;
1420 hr = IDirectSoundCaptureBuffer8_GetCurrentPosition(pStreamDS->In.pDSCB, NULL, &offByteReadPos);
1421 if (FAILED(hr))
1422 {
1423 offByteReadPos = 0;
1424 DSLOGREL(("DSound: Getting capture position failed with %Rhrc\n", hr));
1425 }
1426
1427 RT_ZERO(wfx);
1428 hr = IDirectSoundCaptureBuffer8_GetFormat(pStreamDS->In.pDSCB, &wfx, sizeof(wfx), NULL);
1429 if (FAILED(hr))
1430 {
1431 DSLOGREL(("DSound: Getting capture format failed with %Rhrc\n", hr));
1432 break;
1433 }
1434
1435 DSCBCAPS bc;
1436 RT_ZERO(bc);
1437 bc.dwSize = sizeof(bc);
1438 hr = IDirectSoundCaptureBuffer8_GetCaps(pStreamDS->In.pDSCB, &bc);
1439 if (FAILED(hr))
1440 {
1441 DSLOGREL(("DSound: Getting capture capabilities failed with %Rhrc\n", hr));
1442 break;
1443 }
1444
1445 DSLOG(("DSound: Acquired capture buffer is %RU64ms (%ld bytes)\n",
1446 DrvAudioHlpBytesToMilli(bc.dwBufferBytes, &pCfgReq->Props), bc.dwBufferBytes));
1447
1448 DSLOG(("DSound: Capture format:\n"
1449 " dwBufferBytes = %RI32\n"
1450 " dwFlags = 0x%x\n"
1451 " wFormatTag = %RI16\n"
1452 " nChannels = %RI16\n"
1453 " nSamplesPerSec = %RU32\n"
1454 " nAvgBytesPerSec = %RU32\n"
1455 " nBlockAlign = %RI16\n"
1456 " wBitsPerSample = %RI16\n"
1457 " cbSize = %RI16\n",
1458 bc.dwBufferBytes,
1459 bc.dwFlags,
1460 wfx.wFormatTag,
1461 wfx.nChannels,
1462 wfx.nSamplesPerSec,
1463 wfx.nAvgBytesPerSec,
1464 wfx.nBlockAlign,
1465 wfx.wBitsPerSample,
1466 wfx.cbSize));
1467
1468 if (bc.dwBufferBytes & pStreamDS->uAlign)
1469 DSLOGREL(("DSound: Capture GetCaps returned misaligned buffer: size %RU32, alignment %RU32\n",
1470 bc.dwBufferBytes, pStreamDS->uAlign + 1));
1471
1472 /* Initial state: reading at the initial capture position, no error. */
1473 pStreamDS->In.offReadPos = 0;
1474 pStreamDS->cbBufSize = bc.dwBufferBytes;
1475
1476 rc = RTCircBufCreate(&pStreamDS->pCircBuf, pStreamDS->cbBufSize) * 2; /* Use "double buffering". */
1477 AssertRC(rc);
1478
1479 pThis->pDSStrmIn = pStreamDS;
1480
1481 pCfgAcq->Backend.cfBufferSize = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDS->cbBufSize);
1482
1483 } while (0);
1484
1485 if (FAILED(hr))
1486 directSoundCaptureClose(pThis, pStreamDS);
1487
1488 LogFlowFunc(("Returning %Rhrc\n", hr));
1489 return hr;
1490}
1491
1492static HRESULT directSoundCaptureStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fFlush)
1493{
1494 AssertPtrReturn(pThis, E_POINTER);
1495 AssertPtrReturn(pStreamDS, E_POINTER);
1496
1497 RT_NOREF(pThis);
1498
1499 HRESULT hr = S_OK;
1500
1501 if (pStreamDS->In.pDSCB)
1502 {
1503 DSLOG(("DSound: Stopping capture\n"));
1504 hr = IDirectSoundCaptureBuffer_Stop(pStreamDS->In.pDSCB);
1505 }
1506
1507 if (SUCCEEDED(hr))
1508 {
1509 if (fFlush)
1510 dsoundStreamReset(pThis, pStreamDS);
1511 }
1512
1513 if (FAILED(hr))
1514 DSLOGREL(("DSound: Stopping capture buffer failed with %Rhrc\n", hr));
1515
1516 return hr;
1517}
1518
1519static HRESULT directSoundCaptureStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
1520{
1521 AssertPtrReturn(pThis, E_POINTER);
1522 AssertPtrReturn(pStreamDS, E_POINTER);
1523
1524 HRESULT hr;
1525 if (pStreamDS->In.pDSCB)
1526 {
1527 DWORD dwStatus;
1528 hr = IDirectSoundCaptureBuffer8_GetStatus(pStreamDS->In.pDSCB, &dwStatus);
1529 if (FAILED(hr))
1530 {
1531 DSLOGREL(("DSound: Retrieving capture status failed with %Rhrc\n", hr));
1532 }
1533 else
1534 {
1535 if (dwStatus & DSCBSTATUS_CAPTURING)
1536 {
1537 DSLOG(("DSound: Already capturing\n"));
1538 }
1539 else
1540 {
1541 const DWORD fFlags = DSCBSTART_LOOPING;
1542
1543 DSLOG(("DSound: Starting to capture\n"));
1544 hr = IDirectSoundCaptureBuffer8_Start(pStreamDS->In.pDSCB, fFlags);
1545 if (FAILED(hr))
1546 DSLOGREL(("DSound: Starting to capture failed with %Rhrc\n", hr));
1547 }
1548 }
1549 }
1550 else
1551 hr = E_UNEXPECTED;
1552
1553 LogFlowFunc(("Returning %Rhrc\n", hr));
1554 return hr;
1555}
1556
1557static int dsoundDevAdd(PRTLISTANCHOR pList, LPGUID pGUID, LPCWSTR pwszDescription, PDSOUNDDEV *ppDev)
1558{
1559 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1560 AssertPtrReturn(pGUID, VERR_INVALID_POINTER);
1561 AssertPtrReturn(pwszDescription, VERR_INVALID_POINTER);
1562
1563 PDSOUNDDEV pDev = (PDSOUNDDEV)RTMemAlloc(sizeof(DSOUNDDEV));
1564 if (!pDev)
1565 return VERR_NO_MEMORY;
1566
1567 int rc = RTUtf16ToUtf8(pwszDescription, &pDev->pszName);
1568 if ( RT_SUCCESS(rc)
1569 && pGUID)
1570 {
1571 memcpy(&pDev->Guid, pGUID, sizeof(GUID));
1572 }
1573
1574 if (RT_SUCCESS(rc))
1575 RTListAppend(pList, &pDev->Node);
1576
1577 if (ppDev)
1578 *ppDev = pDev;
1579
1580 return rc;
1581}
1582
1583static void dsoundDeviceRemove(PDSOUNDDEV pDev)
1584{
1585 if (pDev)
1586 {
1587 if (pDev->pszName)
1588 {
1589 RTStrFree(pDev->pszName);
1590 pDev->pszName = NULL;
1591 }
1592
1593 RTListNodeRemove(&pDev->Node);
1594
1595 RTMemFree(pDev);
1596 pDev = NULL;
1597 }
1598}
1599
1600static void dsoundLogDevice(const char *pszType, LPGUID pGUID, LPCWSTR pwszDescription, LPCWSTR pwszModule)
1601{
1602 char *pszGUID = dsoundGUIDToUtf8StrA(pGUID);
1603 /* This always has to be in the release log.
1604 * Only print this when we're running in verbose (audio debug) mode, as this can generate a lot of content. */
1605 LogRel2(("DSound: %s: GUID: %s [%ls] (Module: %ls)\n", pszType, pszGUID ? pszGUID : "{?}", pwszDescription, pwszModule));
1606 RTStrFree(pszGUID);
1607}
1608
1609static BOOL CALLBACK dsoundDevicesEnumCbPlayback(LPGUID pGUID, LPCWSTR pwszDescription, LPCWSTR pwszModule, PVOID lpContext)
1610{
1611 PDSOUNDENUMCBCTX pCtx = (PDSOUNDENUMCBCTX)lpContext;
1612 AssertPtrReturn(pCtx, FALSE);
1613 AssertPtrReturn(pCtx->pDrv, FALSE);
1614
1615 if (!pGUID)
1616 return TRUE;
1617
1618 AssertPtrReturn(pwszDescription, FALSE);
1619 /* Do not care about pwszModule. */
1620
1621 if (pCtx->fFlags & DSOUNDENUMCBFLAGS_LOG)
1622 dsoundLogDevice("Output", pGUID, pwszDescription, pwszModule);
1623
1624 int rc = dsoundDevAdd(&pCtx->pDrv->lstDevOutput,
1625 pGUID, pwszDescription, NULL /* ppDev */);
1626 if (RT_FAILURE(rc))
1627 return FALSE; /* Abort enumeration. */
1628
1629 pCtx->cDevOut++;
1630
1631 return TRUE;
1632}
1633
1634static BOOL CALLBACK dsoundDevicesEnumCbCapture(LPGUID pGUID, LPCWSTR pwszDescription, LPCWSTR pwszModule, PVOID lpContext)
1635{
1636 PDSOUNDENUMCBCTX pCtx = (PDSOUNDENUMCBCTX)lpContext;
1637 AssertPtrReturn(pCtx, FALSE);
1638 AssertPtrReturn(pCtx->pDrv, FALSE);
1639
1640 if (!pGUID)
1641 return TRUE;
1642
1643 if (pCtx->fFlags & DSOUNDENUMCBFLAGS_LOG)
1644 dsoundLogDevice("Input", pGUID, pwszDescription, pwszModule);
1645
1646 int rc = dsoundDevAdd(&pCtx->pDrv->lstDevInput,
1647 pGUID, pwszDescription, NULL /* ppDev */);
1648 if (RT_FAILURE(rc))
1649 return FALSE; /* Abort enumeration. */
1650
1651 pCtx->cDevIn++;
1652
1653 return TRUE;
1654}
1655
1656/**
1657 * Does a (Re-)enumeration of the host's playback + capturing devices.
1658 *
1659 * @return IPRT status code.
1660 * @param pThis Host audio driver instance.
1661 * @param pEnmCtx Enumeration context to use.
1662 * @param fEnum Enumeration flags.
1663 */
1664static int dsoundDevicesEnumerate(PDRVHOSTDSOUND pThis, PDSOUNDENUMCBCTX pEnmCtx, uint32_t fEnum)
1665{
1666 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1667 AssertPtrReturn(pEnmCtx, VERR_INVALID_POINTER);
1668
1669 dsoundDevicesClear(pThis);
1670
1671 RTLDRMOD hDSound = NULL;
1672 int rc = RTLdrLoadSystem("dsound.dll", true /*fNoUnload*/, &hDSound);
1673 if (RT_SUCCESS(rc))
1674 {
1675 PFNDIRECTSOUNDENUMERATEW pfnDirectSoundEnumerateW = NULL;
1676 PFNDIRECTSOUNDCAPTUREENUMERATEW pfnDirectSoundCaptureEnumerateW = NULL;
1677
1678 rc = RTLdrGetSymbol(hDSound, "DirectSoundEnumerateW", (void**)&pfnDirectSoundEnumerateW);
1679 if (RT_SUCCESS(rc))
1680 rc = RTLdrGetSymbol(hDSound, "DirectSoundCaptureEnumerateW", (void**)&pfnDirectSoundCaptureEnumerateW);
1681
1682 if (RT_SUCCESS(rc))
1683 {
1684 HRESULT hr = pfnDirectSoundEnumerateW(&dsoundDevicesEnumCbPlayback, pEnmCtx);
1685 if (FAILED(hr))
1686 LogRel2(("DSound: Error enumerating host playback devices: %Rhrc\n", hr));
1687
1688 hr = pfnDirectSoundCaptureEnumerateW(&dsoundDevicesEnumCbCapture, pEnmCtx);
1689 if (FAILED(hr))
1690 LogRel2(("DSound: Error enumerating host capturing devices: %Rhrc\n", hr));
1691
1692 if (fEnum & DSOUNDENUMCBFLAGS_LOG)
1693 {
1694 LogRel2(("DSound: Found %RU8 host playback devices\n", pEnmCtx->cDevOut));
1695 LogRel2(("DSound: Found %RU8 host capturing devices\n", pEnmCtx->cDevIn));
1696 }
1697 }
1698
1699 RTLdrClose(hDSound);
1700 }
1701 else
1702 {
1703 /* No dsound.dll on this system. */
1704 LogRel2(("DSound: Could not load dsound.dll: %Rrc\n", rc));
1705 }
1706
1707 return rc;
1708}
1709
1710/**
1711 * Updates this host driver's internal status, according to the global, overall input/output
1712 * state and all connected (native) audio streams.
1713 *
1714 * @param pThis Host audio driver instance.
1715 * @param pCfg Where to store the backend configuration. Optional.
1716 * @param fEnum Enumeration flags.
1717 */
1718static void dsoundUpdateStatusInternalEx(PDRVHOSTDSOUND pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum)
1719{
1720 AssertPtrReturnVoid(pThis);
1721 /* pCfg is optional. */
1722
1723 PDMAUDIOBACKENDCFG Cfg;
1724 RT_ZERO(Cfg);
1725
1726 Cfg.cbStreamOut = sizeof(DSOUNDSTREAM);
1727 Cfg.cbStreamIn = sizeof(DSOUNDSTREAM);
1728
1729 DSOUNDENUMCBCTX cbCtx = { pThis, fEnum, 0, 0 };
1730
1731 int rc = dsoundDevicesEnumerate(pThis, &cbCtx, fEnum);
1732 if (RT_SUCCESS(rc))
1733 {
1734#if 0
1735 if ( pThis->fEnabledOut != RT_BOOL(cbCtx.cDevOut)
1736 || pThis->fEnabledIn != RT_BOOL(cbCtx.cDevIn))
1737 {
1738 /** @todo Use a registered callback to the audio connector (e.g "OnConfigurationChanged") to
1739 * let the connector know that something has changed within the host backend. */
1740 }
1741#endif
1742 pThis->fEnabledOut = RT_BOOL(cbCtx.cDevOut);
1743 pThis->fEnabledIn = RT_BOOL(cbCtx.cDevIn);
1744
1745 RTStrPrintf2(Cfg.szName, sizeof(Cfg.szName), "DirectSound audio driver");
1746
1747 Cfg.cMaxStreamsIn = UINT32_MAX;
1748 Cfg.cMaxStreamsOut = UINT32_MAX;
1749
1750 if (pCfg)
1751 memcpy(pCfg, &Cfg, sizeof(PDMAUDIOBACKENDCFG));
1752 }
1753
1754 LogFlowFuncLeaveRC(rc);
1755}
1756
1757static void dsoundUpdateStatusInternal(PDRVHOSTDSOUND pThis)
1758{
1759 dsoundUpdateStatusInternalEx(pThis, NULL /* pCfg */, 0 /* fEnum */);
1760}
1761
1762static int dsoundCreateStreamOut(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS,
1763 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1764{
1765 LogFlowFunc(("pStreamDS=%p, pCfgReq=%p\n", pStreamDS, pCfgReq));
1766
1767 int rc = VINF_SUCCESS;
1768
1769 /* Try to open playback in case the device is already there. */
1770 HRESULT hr = directSoundPlayOpen(pThis, pStreamDS, pCfgReq, pCfgAcq);
1771 if (SUCCEEDED(hr))
1772 {
1773 rc = DrvAudioHlpStreamCfgCopy(&pStreamDS->Cfg, pCfgAcq);
1774 if (RT_SUCCESS(rc))
1775 dsoundStreamReset(pThis, pStreamDS);
1776 }
1777 else
1778 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
1779
1780 LogFlowFuncLeaveRC(rc);
1781 return rc;
1782}
1783
1784static int dsoundControlStreamOut(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, PDMAUDIOSTREAMCMD enmStreamCmd)
1785{
1786 LogFlowFunc(("pStreamDS=%p, cmd=%d\n", pStreamDS, enmStreamCmd));
1787
1788 int rc = VINF_SUCCESS;
1789
1790 HRESULT hr;
1791 switch (enmStreamCmd)
1792 {
1793 case PDMAUDIOSTREAMCMD_ENABLE:
1794 {
1795 dsoundStreamEnable(pThis, pStreamDS, true /* fEnable */);
1796 break;
1797 }
1798
1799 case PDMAUDIOSTREAMCMD_RESUME:
1800 {
1801 hr = directSoundPlayStart(pThis, pStreamDS);
1802 if (FAILED(hr))
1803 rc = VERR_NOT_SUPPORTED; /** @todo Fix this. */
1804 break;
1805 }
1806
1807 case PDMAUDIOSTREAMCMD_DISABLE:
1808 {
1809 dsoundStreamEnable(pThis, pStreamDS, false /* fEnable */);
1810 hr = directSoundPlayStop(pThis, pStreamDS, true /* fFlush */);
1811 if (FAILED(hr))
1812 rc = VERR_NOT_SUPPORTED;
1813 break;
1814 }
1815
1816 case PDMAUDIOSTREAMCMD_PAUSE:
1817 {
1818 hr = directSoundPlayStop(pThis, pStreamDS, false /* fFlush */);
1819 if (FAILED(hr))
1820 rc = VERR_NOT_SUPPORTED;
1821 break;
1822 }
1823
1824 case PDMAUDIOSTREAMCMD_DRAIN:
1825 {
1826 /* Make sure we transferred everything. */
1827 pStreamDS->fEnabled = true;
1828 pStreamDS->Out.fDrain = true;
1829 rc = dsoundPlayTransfer(pThis, pStreamDS);
1830 if ( RT_SUCCESS(rc)
1831 && pStreamDS->Out.fFirstTransfer)
1832 {
1833 /* If this was the first transfer ever for this stream, make sure to also play the (short) audio data. */
1834 DSLOG(("DSound: Started playing output (short sound)\n"));
1835
1836 pStreamDS->Out.fFirstTransfer = false;
1837 pStreamDS->Out.cbLastTransferred = pStreamDS->Out.cbTransferred; /* All transferred audio data must be played. */
1838 pStreamDS->Out.tsLastTransferredMs = RTTimeMilliTS();
1839
1840 hr = directSoundPlayStart(pThis, pStreamDS);
1841 if (FAILED(hr))
1842 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1843 }
1844 break;
1845 }
1846
1847 case PDMAUDIOSTREAMCMD_DROP:
1848 {
1849 pStreamDS->Out.cbLastTransferred = 0;
1850 pStreamDS->Out.tsLastTransferredMs = 0;
1851 RTCircBufReset(pStreamDS->pCircBuf);
1852 break;
1853 }
1854
1855 default:
1856 rc = VERR_NOT_SUPPORTED;
1857 break;
1858 }
1859
1860 LogFlowFuncLeaveRC(rc);
1861 return rc;
1862}
1863
1864/**
1865 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
1866 */
1867int drvHostDSoundStreamPlay(PPDMIHOSTAUDIO pInterface,
1868 PPDMAUDIOBACKENDSTREAM pStream, const void *pvBuf, uint32_t cxBuf, uint32_t *pcxWritten)
1869{
1870 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1871 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1872 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1873 AssertReturn(cxBuf, VERR_INVALID_PARAMETER);
1874 /* pcxWritten is optional. */
1875
1876 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1877 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
1878
1879 int rc = VINF_SUCCESS;
1880
1881 uint32_t cbWrittenTotal = 0;
1882
1883 uint8_t *pbBuf = (uint8_t *)pvBuf;
1884 PRTCIRCBUF pCircBuf = pStreamDS->pCircBuf;
1885
1886 uint32_t cbToPlay = RT_MIN(cxBuf, (uint32_t)RTCircBufFree(pCircBuf));
1887 while (cbToPlay)
1888 {
1889 void *pvChunk;
1890 size_t cbChunk;
1891 RTCircBufAcquireWriteBlock(pCircBuf, cbToPlay, &pvChunk, &cbChunk);
1892
1893 if (cbChunk)
1894 {
1895 memcpy(pvChunk, pbBuf, cbChunk);
1896
1897 pbBuf += cbChunk;
1898 Assert(cbToPlay >= cbChunk);
1899 cbToPlay -= (uint32_t)cbChunk;
1900
1901 cbWrittenTotal += (uint32_t)cbChunk;
1902 }
1903
1904 RTCircBufReleaseWriteBlock(pCircBuf, cbChunk);
1905 }
1906
1907 Assert(cbWrittenTotal <= cxBuf);
1908 Assert(cbWrittenTotal == cxBuf);
1909
1910 pStreamDS->Out.cbWritten += cbWrittenTotal;
1911
1912 if (RT_SUCCESS(rc))
1913 {
1914 if (pcxWritten)
1915 *pcxWritten = cbWrittenTotal;
1916 }
1917 else
1918 dsoundUpdateStatusInternal(pThis);
1919
1920 return rc;
1921}
1922
1923static int dsoundDestroyStreamOut(PDRVHOSTDSOUND pThis, PPDMAUDIOBACKENDSTREAM pStream)
1924{
1925 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
1926
1927 LogFlowFuncEnter();
1928
1929 HRESULT hr = directSoundPlayStop(pThis, pStreamDS, true /* fFlush */);
1930 if (SUCCEEDED(hr))
1931 {
1932 hr = directSoundPlayClose(pThis, pStreamDS);
1933 if (FAILED(hr))
1934 return VERR_GENERAL_FAILURE; /** @todo Fix. */
1935 }
1936
1937 return VINF_SUCCESS;
1938}
1939
1940static int dsoundCreateStreamIn(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS,
1941 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1942{
1943 LogFunc(("pStreamDS=%p, pCfgReq=%p, enmRecSource=%s\n",
1944 pStreamDS, pCfgReq, DrvAudioHlpRecSrcToStr(pCfgReq->DestSource.Source)));
1945
1946 int rc = VINF_SUCCESS;
1947
1948 /* Try to open capture in case the device is already there. */
1949 HRESULT hr = directSoundCaptureOpen(pThis, pStreamDS, pCfgReq, pCfgAcq);
1950 if (SUCCEEDED(hr))
1951 {
1952 rc = DrvAudioHlpStreamCfgCopy(&pStreamDS->Cfg, pCfgAcq);
1953 if (RT_SUCCESS(rc))
1954 dsoundStreamReset(pThis, pStreamDS);
1955 }
1956 else
1957 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
1958
1959 return rc;
1960}
1961
1962static int dsoundControlStreamIn(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, PDMAUDIOSTREAMCMD enmStreamCmd)
1963{
1964 LogFlowFunc(("pStreamDS=%p, enmStreamCmd=%ld\n", pStreamDS, enmStreamCmd));
1965
1966 int rc = VINF_SUCCESS;
1967
1968 HRESULT hr;
1969 switch (enmStreamCmd)
1970 {
1971 case PDMAUDIOSTREAMCMD_ENABLE:
1972 dsoundStreamEnable(pThis, pStreamDS, true /* fEnable */);
1973 RT_FALL_THROUGH();
1974 case PDMAUDIOSTREAMCMD_RESUME:
1975 {
1976 /* Try to start capture. If it fails, then reopen and try again. */
1977 hr = directSoundCaptureStart(pThis, pStreamDS);
1978 if (FAILED(hr))
1979 {
1980 hr = directSoundCaptureClose(pThis, pStreamDS);
1981 if (SUCCEEDED(hr))
1982 {
1983 PDMAUDIOSTREAMCFG CfgAcq;
1984 hr = directSoundCaptureOpen(pThis, pStreamDS, &pStreamDS->Cfg /* pCfgReq */, &CfgAcq);
1985 if (SUCCEEDED(hr))
1986 {
1987 rc = DrvAudioHlpStreamCfgCopy(&pStreamDS->Cfg, &CfgAcq);
1988 if (RT_FAILURE(rc))
1989 break;
1990
1991 /** @todo What to do if the format has changed? */
1992
1993 hr = directSoundCaptureStart(pThis, pStreamDS);
1994 }
1995 }
1996 }
1997
1998 if (FAILED(hr))
1999 rc = VERR_NOT_SUPPORTED;
2000 break;
2001 }
2002
2003 case PDMAUDIOSTREAMCMD_DISABLE:
2004 dsoundStreamEnable(pThis, pStreamDS, false /* fEnable */);
2005 RT_FALL_THROUGH();
2006 case PDMAUDIOSTREAMCMD_PAUSE:
2007 {
2008 directSoundCaptureStop(pThis, pStreamDS,
2009 enmStreamCmd == PDMAUDIOSTREAMCMD_DISABLE /* fFlush */);
2010
2011 /* Return success in any case, as stopping the capture can fail if
2012 * the capture buffer is not around anymore.
2013 *
2014 * This can happen if the host's capturing device has been changed suddenly. */
2015 rc = VINF_SUCCESS;
2016 break;
2017 }
2018
2019 default:
2020 rc = VERR_NOT_SUPPORTED;
2021 break;
2022 }
2023
2024 return rc;
2025}
2026
2027/**
2028 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
2029 */
2030int drvHostDSoundStreamCapture(PPDMIHOSTAUDIO pInterface,
2031 PPDMAUDIOBACKENDSTREAM pStream, void *pvBuf, uint32_t cxBuf, uint32_t *pcxRead)
2032{
2033
2034 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2035 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2036 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2037 AssertReturn(cxBuf, VERR_INVALID_PARAMETER);
2038
2039 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2040 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2041
2042 int rc = VINF_SUCCESS;
2043
2044 uint32_t cbReadTotal = 0;
2045
2046 uint32_t cbToRead = RT_MIN((uint32_t)RTCircBufUsed(pStreamDS->pCircBuf), cxBuf);
2047 while (cbToRead)
2048 {
2049 void *pvChunk;
2050 size_t cbChunk;
2051 RTCircBufAcquireReadBlock(pStreamDS->pCircBuf, cbToRead, &pvChunk, &cbChunk);
2052
2053 if (cbChunk)
2054 {
2055 memcpy((uint8_t *)pvBuf + cbReadTotal, pvChunk, cbChunk);
2056 cbReadTotal += (uint32_t)cbChunk;
2057 Assert(cbToRead >= cbChunk);
2058 cbToRead -= (uint32_t)cbChunk;
2059 }
2060
2061 RTCircBufReleaseReadBlock(pStreamDS->pCircBuf, cbChunk);
2062 }
2063
2064 if (RT_SUCCESS(rc))
2065 {
2066 if (pcxRead)
2067 *pcxRead = cbReadTotal;
2068 }
2069 else
2070 dsoundUpdateStatusInternal(pThis);
2071
2072 return rc;
2073}
2074
2075static int dsoundDestroyStreamIn(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
2076{
2077 LogFlowFuncEnter();
2078
2079 directSoundCaptureClose(pThis, pStreamDS);
2080
2081 return VINF_SUCCESS;
2082}
2083
2084/**
2085 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
2086 */
2087int drvHostDSoundGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
2088{
2089 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2090 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
2091
2092 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2093
2094 dsoundUpdateStatusInternalEx(pThis, pBackendCfg, 0 /* fEnum */);
2095
2096 return VINF_SUCCESS;
2097}
2098
2099/**
2100 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
2101 */
2102void drvHostDSoundShutdown(PPDMIHOSTAUDIO pInterface)
2103{
2104 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2105
2106 LogFlowFuncEnter();
2107
2108 RT_NOREF(pThis);
2109
2110 LogFlowFuncLeave();
2111}
2112
2113/**
2114 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
2115 */
2116static DECLCALLBACK(int) drvHostDSoundInit(PPDMIHOSTAUDIO pInterface)
2117{
2118 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2119 LogFlowFuncEnter();
2120
2121 int rc;
2122
2123 /* Verify that IDirectSound is available. */
2124 LPDIRECTSOUND pDirectSound = NULL;
2125 HRESULT hr = CoCreateInstance(CLSID_DirectSound, NULL, CLSCTX_ALL, IID_IDirectSound, (void **)&pDirectSound);
2126 if (SUCCEEDED(hr))
2127 {
2128 IDirectSound_Release(pDirectSound);
2129
2130 rc = VINF_SUCCESS;
2131
2132 dsoundUpdateStatusInternalEx(pThis, NULL /* pCfg */, DSOUNDENUMCBFLAGS_LOG /* fEnum */);
2133 }
2134 else
2135 {
2136 DSLOGREL(("DSound: DirectSound not available: %Rhrc\n", hr));
2137 rc = VERR_NOT_SUPPORTED;
2138 }
2139
2140 LogFlowFuncLeaveRC(rc);
2141 return rc;
2142}
2143
2144static LPCGUID dsoundConfigQueryGUID(PCFGMNODE pCfg, const char *pszName, RTUUID *pUuid)
2145{
2146 LPCGUID pGuid = NULL;
2147
2148 char *pszGuid = NULL;
2149 int rc = CFGMR3QueryStringAlloc(pCfg, pszName, &pszGuid);
2150 if (RT_SUCCESS(rc))
2151 {
2152 rc = RTUuidFromStr(pUuid, pszGuid);
2153 if (RT_SUCCESS(rc))
2154 pGuid = (LPCGUID)&pUuid;
2155 else
2156 DSLOGREL(("DSound: Error parsing device GUID for device '%s': %Rrc\n", pszName, rc));
2157
2158 RTStrFree(pszGuid);
2159 }
2160
2161 return pGuid;
2162}
2163
2164static int dsoundConfigInit(PDRVHOSTDSOUND pThis, PCFGMNODE pCfg)
2165{
2166 pThis->Cfg.pGuidPlay = dsoundConfigQueryGUID(pCfg, "DeviceGuidOut", &pThis->Cfg.uuidPlay);
2167 pThis->Cfg.pGuidCapture = dsoundConfigQueryGUID(pCfg, "DeviceGuidIn", &pThis->Cfg.uuidCapture);
2168
2169 DSLOG(("DSound: Configuration: DeviceGuidOut {%RTuuid}, DeviceGuidIn {%RTuuid}\n",
2170 &pThis->Cfg.uuidPlay,
2171 &pThis->Cfg.uuidCapture));
2172
2173 return VINF_SUCCESS;
2174}
2175
2176/**
2177 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
2178 */
2179static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostDSoundGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
2180{
2181 RT_NOREF(enmDir);
2182 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
2183
2184 return PDMAUDIOBACKENDSTS_RUNNING;
2185}
2186
2187/**
2188 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
2189 */
2190static DECLCALLBACK(int) drvHostDSoundStreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
2191 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
2192{
2193 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2194 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2195 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
2196 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
2197
2198 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2199 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2200
2201 int rc;
2202 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
2203 rc = dsoundCreateStreamIn(pThis, pStreamDS, pCfgReq, pCfgAcq);
2204 else
2205 rc = dsoundCreateStreamOut(pThis, pStreamDS, pCfgReq, pCfgAcq);
2206
2207 if (RT_SUCCESS(rc))
2208 {
2209 rc = DrvAudioHlpStreamCfgCopy(&pStreamDS->Cfg, pCfgAcq);
2210 if (RT_SUCCESS(rc))
2211 rc = RTCritSectInit(&pStreamDS->CritSect);
2212 }
2213
2214 return rc;
2215}
2216
2217/**
2218 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
2219 */
2220static DECLCALLBACK(int) drvHostDSoundStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2221{
2222 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2223 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2224
2225 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2226 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2227
2228 int rc;
2229 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
2230 rc = dsoundDestroyStreamIn(pThis, pStreamDS);
2231 else
2232 rc = dsoundDestroyStreamOut(pThis, pStreamDS);
2233
2234 if (RT_SUCCESS(rc))
2235 {
2236 if (RTCritSectIsInitialized(&pStreamDS->CritSect))
2237 rc = RTCritSectDelete(&pStreamDS->CritSect);
2238 }
2239
2240 return rc;
2241}
2242
2243/**
2244 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
2245 */
2246static DECLCALLBACK(int) drvHostDSoundStreamControl(PPDMIHOSTAUDIO pInterface,
2247 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
2248{
2249 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2250 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2251
2252 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2253 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2254
2255 int rc;
2256 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
2257 rc = dsoundControlStreamIn(pThis, pStreamDS, enmStreamCmd);
2258 else
2259 rc = dsoundControlStreamOut(pThis, pStreamDS, enmStreamCmd);
2260
2261 return rc;
2262}
2263
2264/**
2265 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
2266 */
2267static DECLCALLBACK(uint32_t) drvHostDSoundStreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2268{
2269 RT_NOREF(pInterface);
2270 AssertPtrReturn(pStream, PDMAUDIOSTREAMSTS_FLAG_NONE);
2271
2272 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2273
2274 if ( pStreamDS->fEnabled
2275 && pStreamDS->pCircBuf)
2276 {
2277 return (uint32_t)RTCircBufUsed(pStreamDS->pCircBuf);
2278 }
2279
2280 return 0;
2281}
2282
2283/**
2284 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
2285 */
2286static DECLCALLBACK(uint32_t) drvHostDSoundStreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2287{
2288 AssertPtrReturn(pInterface, PDMAUDIOSTREAMSTS_FLAG_NONE);
2289 AssertPtrReturn(pStream, PDMAUDIOSTREAMSTS_FLAG_NONE);
2290
2291 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2292
2293 if (pStreamDS->fEnabled)
2294 return (uint32_t)RTCircBufFree(pStreamDS->pCircBuf);
2295
2296 return 0;
2297}
2298
2299/**
2300 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending}
2301 */
2302static DECLCALLBACK(uint32_t) drvHostDSoundStreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2303{
2304 RT_NOREF(pInterface);
2305 AssertPtrReturn(pStream, 0);
2306
2307 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2308
2309 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT)
2310 {
2311 uint32_t cbPending = 0;
2312
2313 /* Any uncommitted data left? */
2314 if (pStreamDS->pCircBuf)
2315 cbPending = (uint32_t)RTCircBufUsed(pStreamDS->pCircBuf);
2316
2317 /* Check if we have committed data which still needs to be played by
2318 * by DirectSound's streaming buffer. */
2319 if (!cbPending)
2320 {
2321 const uint64_t diffLastTransferredMs = RTTimeMilliTS() - pStreamDS->Out.tsLastTransferredMs;
2322 const uint64_t uLastTranserredChunkMs = DrvAudioHlpBytesToMilli(pStreamDS->Out.cbLastTransferred, &pStreamDS->Cfg.Props);
2323 if ( uLastTranserredChunkMs
2324 && diffLastTransferredMs < uLastTranserredChunkMs)
2325 cbPending = 1;
2326
2327 Log3Func(("diffLastTransferredMs=%RU64ms, uLastTranserredChunkMs=%RU64ms (%RU32 bytes) -> cbPending=%RU32\n",
2328 diffLastTransferredMs, uLastTranserredChunkMs, pStreamDS->Out.cbLastTransferred, cbPending));
2329 }
2330 else
2331 Log3Func(("cbPending=%RU32\n", cbPending));
2332
2333 return cbPending;
2334 }
2335 /* Note: For input streams we never have pending data left. */
2336
2337 return 0;
2338}
2339
2340/**
2341 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
2342 */
2343static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostDSoundStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2344{
2345 RT_NOREF(pInterface);
2346 AssertPtrReturn(pStream, PDMAUDIOSTREAMSTS_FLAG_NONE);
2347
2348 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2349
2350 PDMAUDIOSTREAMSTS strmSts = PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
2351
2352 if (pStreamDS->fEnabled)
2353 strmSts |= PDMAUDIOSTREAMSTS_FLAG_ENABLED;
2354
2355 return strmSts;
2356}
2357
2358/**
2359 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
2360 */
2361static DECLCALLBACK(int) drvHostDSoundStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2362{
2363 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2364 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2365
2366 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2367 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2368
2369 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
2370 {
2371 return dsoundCaptureTransfer(pThis, pStreamDS);
2372 }
2373 else if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT)
2374 {
2375 return dsoundPlayTransfer(pThis, pStreamDS);
2376 }
2377
2378 return VINF_SUCCESS;
2379}
2380
2381#ifdef VBOX_WITH_AUDIO_CALLBACKS
2382/**
2383 * @interface_method_impl{PDMIHOSTAUDIO,pfnSetCallback}
2384 */
2385static DECLCALLBACK(int) drvHostDSoundSetCallback(PPDMIHOSTAUDIO pInterface, PFNPDMHOSTAUDIOCALLBACK pfnCallback)
2386{
2387 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2388 /* pfnCallback will be handled below. */
2389
2390 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2391
2392 int rc = RTCritSectEnter(&pThis->CritSect);
2393 if (RT_SUCCESS(rc))
2394 {
2395 LogFunc(("pfnCallback=%p\n", pfnCallback));
2396
2397 if (pfnCallback) /* Register. */
2398 {
2399 Assert(pThis->pfnCallback == NULL);
2400 pThis->pfnCallback = pfnCallback;
2401
2402#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
2403 if (pThis->m_pNotificationClient)
2404 pThis->m_pNotificationClient->RegisterCallback(pThis->pDrvIns, pfnCallback);
2405#endif
2406 }
2407 else /* Unregister. */
2408 {
2409 if (pThis->pfnCallback)
2410 pThis->pfnCallback = NULL;
2411
2412#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
2413 if (pThis->m_pNotificationClient)
2414 pThis->m_pNotificationClient->UnregisterCallback();
2415#endif
2416 }
2417
2418 int rc2 = RTCritSectLeave(&pThis->CritSect);
2419 AssertRC(rc2);
2420 }
2421
2422 return rc;
2423}
2424#endif
2425
2426
2427/*********************************************************************************************************************************
2428* PDMDRVINS::IBase Interface *
2429*********************************************************************************************************************************/
2430
2431/**
2432 * @callback_method_impl{PDMIBASE,pfnQueryInterface}
2433 */
2434static DECLCALLBACK(void *) drvHostDSoundQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2435{
2436 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2437 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
2438
2439 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
2440 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
2441 return NULL;
2442}
2443
2444
2445/*********************************************************************************************************************************
2446* PDMDRVREG Interface *
2447*********************************************************************************************************************************/
2448
2449/**
2450 * @callback_method_impl{FNPDMDRVDESTRUCT, pfnDestruct}
2451 */
2452static DECLCALLBACK(void) drvHostDSoundDestruct(PPDMDRVINS pDrvIns)
2453{
2454 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
2455 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2456
2457 LogFlowFuncEnter();
2458
2459#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
2460 if (pThis->m_pNotificationClient)
2461 {
2462 pThis->m_pNotificationClient->Dispose();
2463 pThis->m_pNotificationClient->Release();
2464
2465 pThis->m_pNotificationClient = NULL;
2466 }
2467#endif
2468
2469 if (pThis->pDrvIns)
2470 CoUninitialize();
2471
2472 int rc2 = RTCritSectDelete(&pThis->CritSect);
2473 AssertRC(rc2);
2474
2475 LogFlowFuncLeave();
2476}
2477
2478/**
2479 * @callback_method_impl{FNPDMDRVCONSTRUCT,
2480 * Construct a DirectSound Audio driver instance.}
2481 */
2482static DECLCALLBACK(int) drvHostDSoundConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
2483{
2484 RT_NOREF(fFlags);
2485 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
2486
2487 LogRel(("Audio: Initializing DirectSound audio driver\n"));
2488
2489 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2490
2491 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
2492 if (FAILED(hr))
2493 {
2494 DSLOGREL(("DSound: CoInitializeEx failed with %Rhrc\n", hr));
2495 return VERR_NOT_SUPPORTED;
2496 }
2497
2498 /*
2499 * Init basic data members and interfaces.
2500 */
2501 pThis->pDrvIns = pDrvIns;
2502 /* IBase */
2503 pDrvIns->IBase.pfnQueryInterface = drvHostDSoundQueryInterface;
2504 /* IHostAudio */
2505 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostDSound);
2506 pThis->IHostAudio.pfnStreamGetPending = drvHostDSoundStreamGetPending;
2507
2508#ifdef VBOX_WITH_AUDIO_CALLBACKS
2509 /* This backend supports host audio callbacks. */
2510 pThis->IHostAudio.pfnSetCallback = drvHostDSoundSetCallback;
2511 pThis->pfnCallback = NULL;
2512#endif
2513
2514 /*
2515 * Init the static parts.
2516 */
2517 RTListInit(&pThis->lstDevInput);
2518 RTListInit(&pThis->lstDevOutput);
2519
2520 pThis->fEnabledIn = false;
2521 pThis->fEnabledOut = false;
2522
2523 int rc = VINF_SUCCESS;
2524
2525#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
2526 bool fUseNotificationClient = false;
2527
2528 char szOSVersion[32];
2529 rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szOSVersion, sizeof(szOSVersion));
2530 if (RT_SUCCESS(rc))
2531 {
2532 /* IMMNotificationClient is available starting at Windows Vista. */
2533 if (RTStrVersionCompare(szOSVersion, "6.0") >= 0)
2534 fUseNotificationClient = true;
2535 }
2536
2537 if (fUseNotificationClient)
2538 {
2539 try
2540 {
2541 pThis->m_pNotificationClient = new VBoxMMNotificationClient();
2542
2543 HRESULT hr = pThis->m_pNotificationClient->Initialize();
2544 if (FAILED(hr))
2545 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
2546 }
2547 catch (std::bad_alloc &ex)
2548 {
2549 NOREF(ex);
2550 rc = VERR_NO_MEMORY;
2551 }
2552 }
2553
2554 LogRel2(("DSound: Notification client is %s\n", fUseNotificationClient ? "enabled" : "disabled"));
2555#endif
2556
2557 if (RT_SUCCESS(rc))
2558 {
2559 /*
2560 * Initialize configuration values.
2561 */
2562 rc = dsoundConfigInit(pThis, pCfg);
2563 if (RT_SUCCESS(rc))
2564 rc = RTCritSectInit(&pThis->CritSect);
2565 }
2566
2567 return rc;
2568}
2569
2570
2571/**
2572 * PDM driver registration.
2573 */
2574const PDMDRVREG g_DrvHostDSound =
2575{
2576 /* u32Version */
2577 PDM_DRVREG_VERSION,
2578 /* szName */
2579 "DSoundAudio",
2580 /* szRCMod */
2581 "",
2582 /* szR0Mod */
2583 "",
2584 /* pszDescription */
2585 "DirectSound Audio host driver",
2586 /* fFlags */
2587 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2588 /* fClass. */
2589 PDM_DRVREG_CLASS_AUDIO,
2590 /* cMaxInstances */
2591 ~0U,
2592 /* cbInstance */
2593 sizeof(DRVHOSTDSOUND),
2594 /* pfnConstruct */
2595 drvHostDSoundConstruct,
2596 /* pfnDestruct */
2597 drvHostDSoundDestruct,
2598 /* pfnRelocate */
2599 NULL,
2600 /* pfnIOCtl */
2601 NULL,
2602 /* pfnPowerOn */
2603 NULL,
2604 /* pfnReset */
2605 NULL,
2606 /* pfnSuspend */
2607 NULL,
2608 /* pfnResume */
2609 NULL,
2610 /* pfnAttach */
2611 NULL,
2612 /* pfnDetach */
2613 NULL,
2614 /* pfnPowerOff */
2615 NULL,
2616 /* pfnSoftReset */
2617 NULL,
2618 /* u32EndVersion */
2619 PDM_DRVREG_VERSION
2620};
2621
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