VirtualBox

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

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

Audio/DrvHostDSound: Implemented support for the generic audio device enumeration and fixed a bug using non-default input/output devices (if available).

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

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