VirtualBox

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

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

Audio/DrvHostDSound: Always apply the backend configuration values on output stream creation; those could have been changed by DirectSound.

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