VirtualBox

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

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

Audio: Implemented backend-independent (pre-)buffering support. Work in progress.

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

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