VirtualBox

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

最後變更 在這個檔案從76992是 76992,由 vboxsync 提交於 6 年 前

Audio/DrvHostDSound: Also clear / reset internal DirectSound buffers in dsoundStreamReset() and do so when enabling a stream via dsoundStreamEnable() to make sure no stale data is left.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette