VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostAudioDSound.cpp@ 88371

Last change on this file since 88371 was 88371, checked in by vboxsync, 4 years ago

DrvHostAudioDSound: Try enumerate devices using MMDeviceEnumerator if we can. Fixed leak in internal device enumeration (dsoundUpdateStatusInternal) and disable it because it doesn't seem to be at all necessary. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 97.8 KB
Line 
1/* $Id: DrvHostAudioDSound.cpp 88371 2021-04-06 13:45:57Z vboxsync $ */
2/** @file
3 * Host audio driver - DirectSound (Windows).
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
23#define INITGUID
24#include <VBox/log.h>
25#include <iprt/win/windows.h>
26#include <dsound.h>
27#include <Mmdeviceapi.h>
28#include <functiondiscoverykeys_devpkey.h>
29
30#include <iprt/alloc.h>
31#include <iprt/system.h>
32#include <iprt/uuid.h>
33#include <iprt/utf16.h>
34
35#include <VBox/vmm/pdmaudioinline.h>
36#include <VBox/vmm/pdmaudiohostenuminline.h>
37
38#include "VBoxDD.h"
39#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
40# include <new> /* For bad_alloc. */
41# include "DrvHostAudioDSoundMMNotifClient.h"
42#endif
43
44
45/*********************************************************************************************************************************
46* Defined Constants And Macros *
47*********************************************************************************************************************************/
48/*
49 * IDirectSound* interface uses HRESULT status codes and the driver callbacks use
50 * the IPRT status codes. To minimize HRESULT->IPRT conversion most internal functions
51 * in the driver return HRESULT and conversion is done in the driver callbacks.
52 *
53 * Naming convention:
54 * 'dsound*' functions return IPRT status code;
55 * 'directSound*' - return HRESULT.
56 */
57
58/*
59 * Optional release logging, which a user can turn on with the
60 * 'VBoxManage debugvm' command.
61 * Debug logging still uses the common Log* macros from IPRT.
62 * Messages which always should go to the release log use LogRel.
63 */
64/** General code behavior. */
65#define DSLOG(a) do { LogRel2(a); } while(0)
66/** Something which produce a lot of logging during playback/recording. */
67#define DSLOGF(a) do { LogRel3(a); } while(0)
68/** Important messages like errors. Limited in the default release log to avoid log flood. */
69#define DSLOGREL(a) \
70 do { \
71 static int8_t s_cLogged = 0; \
72 if (s_cLogged < 8) { \
73 ++s_cLogged; \
74 LogRel(a); \
75 } else DSLOG(a); \
76 } while (0)
77
78/** Maximum number of attempts to restore the sound buffer before giving up. */
79#define DRV_DSOUND_RESTORE_ATTEMPTS_MAX 3
80/** Default input latency (in ms). */
81#define DRV_DSOUND_DEFAULT_LATENCY_MS_IN 50
82/** Default output latency (in ms). */
83#define DRV_DSOUND_DEFAULT_LATENCY_MS_OUT 50
84
85/** Makes DRVHOSTDSOUND out of PDMIHOSTAUDIO. */
86#define PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface) \
87 ( (PDRVHOSTDSOUND)((uintptr_t)pInterface - RT_UOFFSETOF(DRVHOSTDSOUND, IHostAudio)) )
88
89
90/*********************************************************************************************************************************
91* Structures and Typedefs *
92*********************************************************************************************************************************/
93/* Dynamically load dsound.dll. */
94typedef HRESULT WINAPI FNDIRECTSOUNDENUMERATEW(LPDSENUMCALLBACKW pDSEnumCallback, PVOID pContext);
95typedef FNDIRECTSOUNDENUMERATEW *PFNDIRECTSOUNDENUMERATEW;
96typedef HRESULT WINAPI FNDIRECTSOUNDCAPTUREENUMERATEW(LPDSENUMCALLBACKW pDSEnumCallback, PVOID pContext);
97typedef FNDIRECTSOUNDCAPTUREENUMERATEW *PFNDIRECTSOUNDCAPTUREENUMERATEW;
98typedef HRESULT WINAPI FNDIRECTSOUNDCAPTURECREATE8(LPCGUID lpcGUID, LPDIRECTSOUNDCAPTURE8 *lplpDSC, LPUNKNOWN pUnkOuter);
99typedef FNDIRECTSOUNDCAPTURECREATE8 *PFNDIRECTSOUNDCAPTURECREATE8;
100
101#define VBOX_DSOUND_MAX_EVENTS 3
102
103typedef enum DSOUNDEVENT
104{
105 DSOUNDEVENT_NOTIFY = 0,
106 DSOUNDEVENT_INPUT,
107 DSOUNDEVENT_OUTPUT,
108} DSOUNDEVENT;
109
110typedef struct DSOUNDHOSTCFG
111{
112 RTUUID uuidPlay;
113 LPCGUID pGuidPlay;
114 RTUUID uuidCapture;
115 LPCGUID pGuidCapture;
116} DSOUNDHOSTCFG, *PDSOUNDHOSTCFG;
117
118typedef struct DSOUNDSTREAM
119{
120 /** The stream's acquired configuration. */
121 PDMAUDIOSTREAMCFG Cfg;
122 /** Buffer alignment. */
123 uint8_t uAlign;
124 /** Whether this stream is in an enable state on the DirectSound side. */
125 bool fEnabled;
126 bool afPadding[2];
127 /** Size (in bytes) of the DirectSound buffer.
128 * @note This in *not* the size of the circular buffer above! */
129 DWORD cbBufSize;
130 /** The stream's critical section for synchronizing access. */
131 RTCRITSECT CritSect;
132 /** The internal playback / capturing buffer. */
133 PRTCIRCBUF pCircBuf;
134 union
135 {
136 struct
137 {
138 /** The actual DirectSound Buffer (DSB) used for the capturing.
139 * This is a secondary buffer and is used as a streaming buffer. */
140 LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB;
141 /** Current read offset (in bytes) within the DSB. */
142 DWORD offReadPos;
143 /** Number of buffer overruns happened. Used for logging. */
144 uint8_t cOverruns;
145 } In;
146 struct
147 {
148 /** The actual DirectSound Buffer (DSB) used for playback.
149 * This is a secondary buffer and is used as a streaming buffer. */
150 LPDIRECTSOUNDBUFFER8 pDSB;
151 /** Current write offset (in bytes) within the DSB. */
152 DWORD offWritePos;
153 /** Offset of last play cursor within the DSB when checked for pending. */
154 DWORD offPlayCursorLastPending;
155 /** Offset of last play cursor within the DSB when last played. */
156 DWORD offPlayCursorLastPlayed;
157 /** Total amount (in bytes) written to our internal ring buffer. */
158 uint64_t cbWritten;
159 /** Total amount (in bytes) played (to the DirectSound buffer). */
160 uint64_t cbTransferred;
161 /** Flag indicating whether playback was just (re)started. */
162 bool fFirstTransfer;
163 /** Flag indicating whether this stream is in draining mode, e.g. no new
164 * data is being written to it but DirectSound still needs to be able to
165 * play its remaining (buffered) data. */
166 bool fDrain;
167 /** How much (in bytes) the last transfer from the internal buffer
168 * to the DirectSound buffer was. */
169 uint32_t cbLastTransferred;
170 /** Timestamp (in ms) of the last transfer from the internal buffer
171 * to the DirectSound buffer. */
172 uint64_t tsLastTransferredMs;
173 /** Number of buffer underruns happened. Used for logging. */
174 uint8_t cUnderruns;
175 } Out;
176 };
177#ifdef LOG_ENABLED
178 struct
179 {
180 uint64_t tsLastTransferredMs;
181 } Dbg;
182#endif
183} DSOUNDSTREAM, *PDSOUNDSTREAM;
184
185/**
186 * DirectSound-specific device entry.
187 */
188typedef struct DSOUNDDEV
189{
190 PDMAUDIOHOSTDEV Core;
191 /** The GUID if handy. */
192 GUID Guid;
193} DSOUNDDEV;
194/** Pointer to a DirectSound device entry. */
195typedef DSOUNDDEV *PDSOUNDDEV;
196
197/**
198 * Structure for holding a device enumeration context.
199 */
200typedef struct DSOUNDENUMCBCTX
201{
202 /** Enumeration flags. */
203 uint32_t fFlags;
204 /** Pointer to device list to populate. */
205 PPDMAUDIOHOSTENUM pDevEnm;
206} DSOUNDENUMCBCTX, *PDSOUNDENUMCBCTX;
207
208typedef struct DRVHOSTDSOUND
209{
210 /** Pointer to the driver instance structure. */
211 PPDMDRVINS pDrvIns;
212 /** Our audio host audio interface. */
213 PDMIHOSTAUDIO IHostAudio;
214 /** Critical section to serialize access. */
215 RTCRITSECT CritSect;
216 /** DirectSound configuration options. */
217 DSOUNDHOSTCFG Cfg;
218 /** List of devices of last enumeration. */
219 PDMAUDIOHOSTENUM DeviceEnum;
220 /** Whether this backend supports any audio input.
221 * @todo r=bird: This is not actually used for anything. */
222 bool fEnabledIn;
223 /** Whether this backend supports any audio output.
224 * @todo r=bird: This is not actually used for anything. */
225 bool fEnabledOut;
226 /** The Direct Sound playback interface. */
227 LPDIRECTSOUND8 pDS;
228 /** The Direct Sound capturing interface. */
229 LPDIRECTSOUNDCAPTURE8 pDSC;
230#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
231 DrvHostAudioDSoundMMNotifClient *m_pNotificationClient;
232#endif
233 /** Pointer to the input stream. */
234 PDSOUNDSTREAM pDSStrmIn;
235 /** Pointer to the output stream. */
236 PDSOUNDSTREAM pDSStrmOut;
237} DRVHOSTDSOUND, *PDRVHOSTDSOUND;
238
239
240/*********************************************************************************************************************************
241* Internal Functions *
242*********************************************************************************************************************************/
243static HRESULT directSoundPlayRestore(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB);
244static HRESULT directSoundPlayStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS);
245static HRESULT directSoundPlayStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fFlush);
246static HRESULT directSoundCaptureStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fFlush);
247
248static int dsoundDevicesEnumerate(PDRVHOSTDSOUND pThis, PDMAUDIOHOSTENUM pDevEnm, uint32_t fEnum);
249
250static int dsoundStreamEnable(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fEnable);
251static void dsoundStreamReset(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS);
252static void dsoundUpdateStatusInternal(PDRVHOSTDSOUND pThis);
253
254
255static DWORD dsoundRingDistance(DWORD offEnd, DWORD offBegin, DWORD cSize)
256{
257 AssertReturn(offEnd <= cSize, 0);
258 AssertReturn(offBegin <= cSize, 0);
259
260 return offEnd >= offBegin ? offEnd - offBegin : cSize - offBegin + offEnd;
261}
262
263static int dsoundWaveFmtFromCfg(PPDMAUDIOSTREAMCFG pCfg, PWAVEFORMATEX pFmt)
264{
265 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
266 AssertPtrReturn(pFmt, VERR_INVALID_POINTER);
267
268 RT_BZERO(pFmt, sizeof(WAVEFORMATEX));
269
270 pFmt->wFormatTag = WAVE_FORMAT_PCM;
271 pFmt->nChannels = PDMAudioPropsChannels(&pCfg->Props);
272 pFmt->wBitsPerSample = PDMAudioPropsSampleBits(&pCfg->Props);
273 pFmt->nSamplesPerSec = PDMAudioPropsHz(&pCfg->Props);
274 pFmt->nBlockAlign = PDMAudioPropsFrameSize(&pCfg->Props);
275 pFmt->nAvgBytesPerSec = PDMAudioPropsFramesToBytes(&pCfg->Props, PDMAudioPropsHz(&pCfg->Props));
276 pFmt->cbSize = 0; /* No extra data specified. */
277
278 return VINF_SUCCESS;
279}
280
281/**
282 * Retrieves the number of free bytes available for writing to a DirectSound output stream.
283 *
284 * @return IPRT status code. VERR_NOT_AVAILABLE if unable to determine or the buffer was not recoverable.
285 * @param pThis Host audio driver instance.
286 * @param pStreamDS DirectSound output stream to retrieve number for.
287 * @param pdwFree Where to return the free amount on success.
288 */
289static int dsoundGetFreeOut(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, DWORD *pdwFree)
290{
291 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
292 AssertPtrReturn(pStreamDS, VERR_INVALID_POINTER);
293 AssertPtrReturn(pdwFree, VERR_INVALID_POINTER);
294
295 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT); /* Paranoia. */
296
297 LPDIRECTSOUNDBUFFER8 pDSB = pStreamDS->Out.pDSB;
298 if (!pDSB)
299 {
300 AssertPtr(pDSB);
301 return VERR_INVALID_POINTER;
302 }
303
304 HRESULT hr = S_OK;
305
306 /* Get the current play position which is used for calculating the free space in the buffer. */
307 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
308 {
309 DWORD cbPlayCursor, cbWriteCursor;
310 hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &cbPlayCursor, &cbWriteCursor);
311 if (SUCCEEDED(hr))
312 {
313 int32_t cbDiff = cbWriteCursor - cbPlayCursor;
314 if (cbDiff < 0)
315 cbDiff += pStreamDS->cbBufSize;
316
317 int32_t cbFree = cbPlayCursor - pStreamDS->Out.offWritePos;
318 if (cbFree < 0)
319 cbFree += pStreamDS->cbBufSize;
320
321 if (cbFree > (int32_t)pStreamDS->cbBufSize - cbDiff)
322 {
323 pStreamDS->Out.offWritePos = cbWriteCursor;
324 cbFree = pStreamDS->cbBufSize - cbDiff;
325 }
326
327 /* When starting to use a DirectSound buffer, cbPlayCursor and cbWriteCursor
328 * both point at position 0, so we won't be able to detect how many bytes
329 * are writable that way.
330 *
331 * So use our per-stream written indicator to see if we just started a stream. */
332 if (pStreamDS->Out.cbWritten == 0)
333 cbFree = pStreamDS->cbBufSize;
334
335 DSLOGREL(("DSound: cbPlayCursor=%RU32, cbWriteCursor=%RU32, offWritePos=%RU32 -> cbFree=%RI32\n",
336 cbPlayCursor, cbWriteCursor, pStreamDS->Out.offWritePos, cbFree));
337
338 *pdwFree = cbFree;
339
340 return VINF_SUCCESS;
341 }
342
343 if (hr != DSERR_BUFFERLOST) /** @todo MSDN doesn't state this error for GetCurrentPosition(). */
344 break;
345
346 LogFunc(("Getting playing position failed due to lost buffer, restoring ...\n"));
347
348 directSoundPlayRestore(pThis, pDSB);
349 }
350
351 if (hr != DSERR_BUFFERLOST) /* Avoid log flooding if the error is still there. */
352 DSLOGREL(("DSound: Getting current playback position failed with %Rhrc\n", hr));
353
354 LogFunc(("Failed with %Rhrc\n", hr));
355
356 return VERR_NOT_AVAILABLE;
357}
358
359static char *dsoundGUIDToUtf8StrA(LPCGUID pGUID)
360{
361 if (pGUID)
362 {
363 LPOLESTR lpOLEStr;
364 HRESULT hr = StringFromCLSID(*pGUID, &lpOLEStr);
365 if (SUCCEEDED(hr))
366 {
367 char *pszGUID;
368 int rc = RTUtf16ToUtf8(lpOLEStr, &pszGUID);
369 CoTaskMemFree(lpOLEStr);
370
371 return RT_SUCCESS(rc) ? pszGUID : NULL;
372 }
373 }
374
375 return RTStrDup("{Default device}");
376}
377
378static HRESULT directSoundPlayRestore(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB)
379{
380 RT_NOREF(pThis);
381 HRESULT hr = IDirectSoundBuffer8_Restore(pDSB);
382 if (FAILED(hr))
383 DSLOG(("DSound: Restoring playback buffer\n"));
384 else
385 DSLOGREL(("DSound: Restoring playback buffer failed with %Rhrc\n", hr));
386
387 return hr;
388}
389
390static HRESULT directSoundPlayUnlock(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB,
391 PVOID pv1, PVOID pv2,
392 DWORD cb1, DWORD cb2)
393{
394 RT_NOREF(pThis);
395 HRESULT hr = IDirectSoundBuffer8_Unlock(pDSB, pv1, cb1, pv2, cb2);
396 if (FAILED(hr))
397 DSLOGREL(("DSound: Unlocking playback buffer failed with %Rhrc\n", hr));
398 return hr;
399}
400
401static HRESULT directSoundCaptureUnlock(LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB,
402 PVOID pv1, PVOID pv2,
403 DWORD cb1, DWORD cb2)
404{
405 HRESULT hr = IDirectSoundCaptureBuffer8_Unlock(pDSCB, pv1, cb1, pv2, cb2);
406 if (FAILED(hr))
407 DSLOGREL(("DSound: Unlocking capture buffer failed with %Rhrc\n", hr));
408 return hr;
409}
410
411static HRESULT directSoundPlayLock(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS,
412 DWORD dwOffset, DWORD dwBytes,
413 PVOID *ppv1, PVOID *ppv2,
414 DWORD *pcb1, DWORD *pcb2,
415 DWORD dwFlags)
416{
417 AssertReturn(dwBytes, VERR_INVALID_PARAMETER);
418
419 HRESULT hr = E_FAIL;
420 AssertCompile(DRV_DSOUND_RESTORE_ATTEMPTS_MAX > 0);
421 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
422 {
423 PVOID pv1, pv2;
424 DWORD cb1, cb2;
425 hr = IDirectSoundBuffer8_Lock(pStreamDS->Out.pDSB, dwOffset, dwBytes, &pv1, &cb1, &pv2, &cb2, dwFlags);
426 if (SUCCEEDED(hr))
427 {
428 if ( (!pv1 || !(cb1 & pStreamDS->uAlign))
429 && (!pv2 || !(cb2 & pStreamDS->uAlign)))
430 {
431 if (ppv1)
432 *ppv1 = pv1;
433 if (ppv2)
434 *ppv2 = pv2;
435 if (pcb1)
436 *pcb1 = cb1;
437 if (pcb2)
438 *pcb2 = cb2;
439 return S_OK;
440 }
441 DSLOGREL(("DSound: Locking playback buffer returned misaligned buffer: cb1=%#RX32, cb2=%#RX32 (alignment: %#RX32)\n",
442 *pcb1, *pcb2, pStreamDS->uAlign));
443 directSoundPlayUnlock(pThis, pStreamDS->Out.pDSB, pv1, pv2, cb1, cb2);
444 return E_FAIL;
445 }
446
447 if (hr != DSERR_BUFFERLOST)
448 break;
449
450 LogFlowFunc(("Locking failed due to lost buffer, restoring ...\n"));
451 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);
452 }
453
454 DSLOGREL(("DSound: Locking playback buffer failed with %Rhrc (dwOff=%ld, dwBytes=%ld)\n", hr, dwOffset, dwBytes));
455 return hr;
456}
457
458static HRESULT directSoundCaptureLock(PDSOUNDSTREAM pStreamDS,
459 DWORD dwOffset, DWORD dwBytes,
460 PVOID *ppv1, PVOID *ppv2,
461 DWORD *pcb1, DWORD *pcb2,
462 DWORD dwFlags)
463{
464 PVOID pv1 = NULL;
465 PVOID pv2 = NULL;
466 DWORD cb1 = 0;
467 DWORD cb2 = 0;
468
469 HRESULT hr = IDirectSoundCaptureBuffer8_Lock(pStreamDS->In.pDSCB, dwOffset, dwBytes,
470 &pv1, &cb1, &pv2, &cb2, dwFlags);
471 if (FAILED(hr))
472 {
473 DSLOGREL(("DSound: Locking capture buffer failed with %Rhrc\n", hr));
474 return hr;
475 }
476
477 if ( (pv1 && (cb1 & pStreamDS->uAlign))
478 || (pv2 && (cb2 & pStreamDS->uAlign)))
479 {
480 DSLOGREL(("DSound: Locking capture buffer returned misaligned buffer: cb1=%RI32, cb2=%RI32 (alignment: %RU32)\n",
481 cb1, cb2, pStreamDS->uAlign));
482 directSoundCaptureUnlock(pStreamDS->In.pDSCB, pv1, pv2, cb1, cb2);
483 return E_FAIL;
484 }
485
486 *ppv1 = pv1;
487 *ppv2 = pv2;
488 *pcb1 = cb1;
489 *pcb2 = cb2;
490
491 return S_OK;
492}
493
494/*
495 * DirectSound playback
496 */
497
498/**
499 * Destroys a DirectSound playback interface.
500 *
501 * @param pDS Playback interface to destroy.
502 */
503static void directSoundPlayInterfaceDestroy(LPDIRECTSOUND8 pDS)
504{
505 if (pDS)
506 {
507 LogFlowFuncEnter();
508
509 IDirectSound8_Release(pDS);
510 pDS = NULL;
511 }
512}
513
514/**
515 * Creates a DirectSound playback interface.
516 *
517 * @return HRESULT
518 * @param pGUID GUID of device to create the playback interface for.
519 * @param ppDS Where to store the created interface. Optional.
520 */
521static HRESULT directSoundPlayInterfaceCreate(LPCGUID pGUID, LPDIRECTSOUND8 *ppDS)
522{
523 /* pGUID can be NULL, if this is the default device. */
524 /* ppDS is optional. */
525
526 LogFlowFuncEnter();
527
528 LPDIRECTSOUND8 pDS;
529 HRESULT hr = CoCreateInstance(CLSID_DirectSound8, NULL, CLSCTX_ALL,
530 IID_IDirectSound8, (void **)&pDS);
531 if (FAILED(hr))
532 {
533 DSLOGREL(("DSound: Creating playback instance failed with %Rhrc\n", hr));
534 }
535 else
536 {
537 hr = IDirectSound8_Initialize(pDS, pGUID);
538 if (SUCCEEDED(hr))
539 {
540 HWND hWnd = GetDesktopWindow();
541 hr = IDirectSound8_SetCooperativeLevel(pDS, hWnd, DSSCL_PRIORITY);
542 if (FAILED(hr))
543 DSLOGREL(("DSound: Setting cooperative level for window %p failed with %Rhrc\n", hWnd, hr));
544 }
545
546 if (FAILED(hr))
547 {
548 if (hr == DSERR_NODRIVER) /* Usually means that no playback devices are attached. */
549 DSLOGREL(("DSound: DirectSound playback is currently unavailable\n"));
550 else
551 DSLOGREL(("DSound: DirectSound playback initialization failed with %Rhrc\n", hr));
552
553 directSoundPlayInterfaceDestroy(pDS);
554 }
555 else if (ppDS)
556 {
557 *ppDS = pDS;
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/** @todo r=bird: I cannot see any code populating pCfgAcq... */
617
618 LogFlowFuncEnter();
619
620 Assert(pStreamDS->Out.pDSB == NULL);
621
622 DSLOG(("DSound: Opening playback stream (uHz=%RU32, cChannels=%RU8, cBits=%u, fSigned=%RTbool)\n", pCfgReq->Props.uHz,
623 PDMAudioPropsChannels(&pCfgReq->Props), PDMAudioPropsSampleBits(&pCfgReq->Props), pCfgReq->Props.fSigned));
624
625 WAVEFORMATEX wfx;
626 int rc = dsoundWaveFmtFromCfg(pCfgReq, &wfx);
627 if (RT_FAILURE(rc))
628 return E_INVALIDARG;
629
630 DSLOG(("DSound: Requested playback format:\n"
631 " wFormatTag = %RI16\n"
632 " nChannels = %RI16\n"
633 " nSamplesPerSec = %RU32\n"
634 " nAvgBytesPerSec = %RU32\n"
635 " nBlockAlign = %RI16\n"
636 " wBitsPerSample = %RI16\n"
637 " cbSize = %RI16\n",
638 wfx.wFormatTag,
639 wfx.nChannels,
640 wfx.nSamplesPerSec,
641 wfx.nAvgBytesPerSec,
642 wfx.nBlockAlign,
643 wfx.wBitsPerSample,
644 wfx.cbSize));
645
646 /** @todo r=bird: Why is this called every time? It triggers a device
647 * enumeration. Andy claimed on IRC that enumeration was only done once...
648 * It's generally a 'ing waste of time here too, as we dont really use any of
649 * the information we gather there. */
650 dsoundUpdateStatusInternal(pThis);
651
652 HRESULT hr = directSoundPlayInterfaceCreate(pThis->Cfg.pGuidPlay, &pThis->pDS);
653 if (FAILED(hr))
654 return hr;
655
656 do /* To use breaks. */
657 {
658 LPDIRECTSOUNDBUFFER pDSB = NULL;
659
660 DSBUFFERDESC bd;
661 RT_ZERO(bd);
662 bd.dwSize = sizeof(bd);
663 bd.lpwfxFormat = &wfx;
664
665 /*
666 * As we reuse our (secondary) buffer for playing out data as it comes in,
667 * we're using this buffer as a so-called streaming buffer.
668 *
669 * See https://msdn.microsoft.com/en-us/library/windows/desktop/ee419014(v=vs.85).aspx
670 *
671 * However, as we do not want to use memory on the sound device directly
672 * (as most modern audio hardware on the host doesn't have this anyway),
673 * we're *not* going to use DSBCAPS_STATIC for that.
674 *
675 * Instead we're specifying DSBCAPS_LOCSOFTWARE, as this fits the bill
676 * of copying own buffer data to our secondary's Direct Sound buffer.
677 */
678 bd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE;
679 bd.dwBufferBytes = PDMAudioPropsFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize);
680
681 DSLOG(("DSound: Requested playback buffer is %RU64ms (%ld bytes)\n",
682 PDMAudioPropsBytesToMilli(&pCfgReq->Props, bd.dwBufferBytes), bd.dwBufferBytes));
683
684 hr = IDirectSound8_CreateSoundBuffer(pThis->pDS, &bd, &pDSB, NULL);
685 if (FAILED(hr))
686 {
687 DSLOGREL(("DSound: Creating playback sound buffer failed with %Rhrc\n", hr));
688 break;
689 }
690
691 /* "Upgrade" to IDirectSoundBuffer8 interface. */
692 hr = IDirectSoundBuffer_QueryInterface(pDSB, IID_IDirectSoundBuffer8, (PVOID *)&pStreamDS->Out.pDSB);
693 IDirectSoundBuffer_Release(pDSB);
694 if (FAILED(hr))
695 {
696 DSLOGREL(("DSound: Querying playback sound buffer interface failed with %Rhrc\n", hr));
697 break;
698 }
699
700 /*
701 * Query the actual parameters set for this stream.
702 * Those might be different than the initially requested parameters.
703 */
704 RT_ZERO(wfx);
705 hr = IDirectSoundBuffer8_GetFormat(pStreamDS->Out.pDSB, &wfx, sizeof(wfx), NULL);
706 if (FAILED(hr))
707 {
708 DSLOGREL(("DSound: Getting playback format failed with %Rhrc\n", hr));
709 break;
710 }
711
712 DSBCAPS bc;
713 RT_ZERO(bc);
714 bc.dwSize = sizeof(bc);
715
716 hr = IDirectSoundBuffer8_GetCaps(pStreamDS->Out.pDSB, &bc);
717 if (FAILED(hr))
718 {
719 DSLOGREL(("DSound: Getting playback capabilities failed with %Rhrc\n", hr));
720 break;
721 }
722
723 DSLOG(("DSound: Acquired playback buffer is %RU64ms (%ld bytes)\n",
724 PDMAudioPropsBytesToMilli(&pCfgReq->Props, bc.dwBufferBytes), bc.dwBufferBytes));
725
726 DSLOG(("DSound: Acquired playback format:\n"
727 " dwBufferBytes = %RI32\n"
728 " dwFlags = 0x%x\n"
729 " wFormatTag = %RI16\n"
730 " nChannels = %RI16\n"
731 " nSamplesPerSec = %RU32\n"
732 " nAvgBytesPerSec = %RU32\n"
733 " nBlockAlign = %RI16\n"
734 " wBitsPerSample = %RI16\n"
735 " cbSize = %RI16\n",
736 bc.dwBufferBytes,
737 bc.dwFlags,
738 wfx.wFormatTag,
739 wfx.nChannels,
740 wfx.nSamplesPerSec,
741 wfx.nAvgBytesPerSec,
742 wfx.nBlockAlign,
743 wfx.wBitsPerSample,
744 wfx.cbSize));
745
746 if (bc.dwBufferBytes & pStreamDS->uAlign)
747 DSLOGREL(("DSound: Playback capabilities returned misaligned buffer: size %RU32, alignment %RU32\n",
748 bc.dwBufferBytes, pStreamDS->uAlign + 1));
749
750 /*
751 * Initial state.
752 * dsoundPlayStart initializes part of it to make sure that Stop/Start continues with a correct
753 * playback buffer position.
754 */
755 pStreamDS->cbBufSize = bc.dwBufferBytes;
756
757 rc = RTCircBufCreate(&pStreamDS->pCircBuf, pStreamDS->cbBufSize * 2 /* Use "double buffering" */);
758 AssertRC(rc);
759
760 pThis->pDSStrmOut = pStreamDS;
761
762 const uint32_t cfBufSize = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDS->cbBufSize);
763
764 pCfgAcq->Backend.cFramesBufferSize = cfBufSize;
765 pCfgAcq->Backend.cFramesPeriod = cfBufSize / 4;
766 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2;
767
768 } while (0);
769
770 if (FAILED(hr))
771 directSoundPlayClose(pThis, pStreamDS);
772
773 return hr;
774}
775
776static void dsoundPlayClearBuffer(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
777{
778 AssertPtrReturnVoid(pStreamDS);
779
780 PPDMAUDIOPCMPROPS pProps = &pStreamDS->Cfg.Props;
781
782 HRESULT hr = IDirectSoundBuffer_SetCurrentPosition(pStreamDS->Out.pDSB, 0 /* Position */);
783 if (FAILED(hr))
784 DSLOGREL(("DSound: Setting current position to 0 when clearing buffer failed with %Rhrc\n", hr));
785
786 PVOID pv1;
787 hr = directSoundPlayLock(pThis, pStreamDS,
788 0 /* dwOffset */, pStreamDS->cbBufSize,
789 &pv1, NULL, 0, 0, DSBLOCK_ENTIREBUFFER);
790 if (SUCCEEDED(hr))
791 {
792 PDMAudioPropsClearBuffer(pProps, pv1, pStreamDS->cbBufSize, PDMAUDIOPCMPROPS_B2F(pProps, pStreamDS->cbBufSize));
793
794 directSoundPlayUnlock(pThis, pStreamDS->Out.pDSB, pv1, NULL, 0, 0);
795
796 /* Make sure to get the last playback position and current write position from DirectSound again.
797 * Those positions in theory could have changed, re-fetch them to be sure. */
798 hr = IDirectSoundBuffer_GetCurrentPosition(pStreamDS->Out.pDSB,
799 &pStreamDS->Out.offPlayCursorLastPlayed, &pStreamDS->Out.offWritePos);
800 if (FAILED(hr))
801 DSLOGREL(("DSound: Re-fetching current position when clearing buffer failed with %Rhrc\n", hr));
802 }
803}
804
805/**
806 * Transfers audio data from the internal buffer to the DirectSound playback instance.
807 * Due to internal accounting and querying DirectSound, this function knows how much it can transfer at once.
808 *
809 * @return IPRT status code.
810 * @param pThis Host audio driver instance.
811 * @param pStreamDS Stream to transfer playback data for.
812 */
813static int dsoundPlayTransfer(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
814{
815 if (!pStreamDS->fEnabled)
816 {
817 Log2Func(("Stream disabled, skipping\n"));
818 return VINF_SUCCESS;
819 }
820
821 uint32_t cbTransferred = 0;
822
823 PRTCIRCBUF pCircBuf = pStreamDS->pCircBuf;
824 AssertPtr(pCircBuf);
825
826 LPDIRECTSOUNDBUFFER8 pDSB = pStreamDS->Out.pDSB;
827 AssertPtr(pDSB);
828
829 int rc = VINF_SUCCESS;
830
831 DWORD offPlayCursor, offWriteCursor;
832 HRESULT hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &offPlayCursor, &offWriteCursor);
833 if (FAILED(hr))
834 {
835 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
836 return rc;
837 }
838
839 DWORD cbFree, cbRemaining;
840 if (pStreamDS->Out.fFirstTransfer)
841 {
842 cbRemaining = 0;
843 cbFree = pStreamDS->cbBufSize;
844 }
845 else
846 {
847 cbFree = dsoundRingDistance(offPlayCursor, pStreamDS->Out.offWritePos, pStreamDS->cbBufSize);
848 cbRemaining = dsoundRingDistance(pStreamDS->Out.offWritePos, offPlayCursor, pStreamDS->cbBufSize);
849 }
850
851 uint32_t cbAvail = (uint32_t)RTCircBufUsed(pCircBuf);
852 uint32_t cbToTransfer = RT_MIN(cbFree, cbAvail);
853
854#ifdef LOG_ENABLED
855 if (!pStreamDS->Dbg.tsLastTransferredMs)
856 pStreamDS->Dbg.tsLastTransferredMs = RTTimeMilliTS();
857 Log3Func(("offPlay=%RU32, offWrite=%RU32, tsLastTransferredMs=%RU64ms, cbAvail=%RU32, cbFree=%RU32 -> cbToTransfer=%RU32 "
858 "(fFirst=%RTbool, fDrain=%RTbool)\n",
859 offPlayCursor, offWriteCursor, RTTimeMilliTS() - pStreamDS->Dbg.tsLastTransferredMs, cbAvail, cbFree, cbToTransfer,
860 pStreamDS->Out.fFirstTransfer, pStreamDS->Out.fDrain));
861 pStreamDS->Dbg.tsLastTransferredMs = RTTimeMilliTS();
862#endif
863
864 while (cbToTransfer)
865 {
866 DWORD cb1 = 0;
867 DWORD cb2 = 0;
868
869 void *pvBuf;
870 size_t cbBuf;
871 RTCircBufAcquireReadBlock(pCircBuf, cbToTransfer, &pvBuf, &cbBuf);
872
873 if (cbBuf)
874 {
875 PVOID pv1, pv2;
876 hr = directSoundPlayLock(pThis, pStreamDS, pStreamDS->Out.offWritePos, (DWORD)cbBuf,
877 &pv1, &pv2, &cb1, &cb2, 0 /* dwFlags */);
878 if (FAILED(hr))
879 {
880 rc = VERR_ACCESS_DENIED;
881 break;
882 }
883
884 AssertPtr(pv1);
885 Assert(cb1);
886
887 memcpy(pv1, pvBuf, cb1);
888
889 if (pv2 && cb2) /* Buffer wrap-around? Write second part. */
890 memcpy(pv2, (uint8_t *)pvBuf + cb1, cb2);
891
892 directSoundPlayUnlock(pThis, pDSB, pv1, pv2, cb1, cb2);
893
894 pStreamDS->Out.offWritePos = (pStreamDS->Out.offWritePos + cb1 + cb2) % pStreamDS->cbBufSize;
895
896 Assert(cbToTransfer >= cbBuf);
897 cbToTransfer -= (uint32_t)cbBuf;
898
899 cbTransferred += cb1 + cb2;
900 }
901
902 RTCircBufReleaseReadBlock(pCircBuf, cb1 + cb2);
903 }
904
905 pStreamDS->Out.cbTransferred += cbTransferred;
906
907 if ( pStreamDS->Out.fFirstTransfer
908 && pStreamDS->Out.cbTransferred >= PDMAudioPropsFramesToBytes(&pStreamDS->Cfg.Props, pStreamDS->Cfg.Backend.cFramesPreBuffering))
909 {
910 hr = directSoundPlayStart(pThis, pStreamDS);
911 if (SUCCEEDED(hr))
912 {
913 pStreamDS->Out.fFirstTransfer = false;
914 }
915 else
916 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
917 }
918
919 cbAvail = (uint32_t)RTCircBufUsed(pCircBuf);
920 if ( !cbAvail
921 && cbTransferred)
922 {
923 pStreamDS->Out.cbLastTransferred = cbTransferred;
924 pStreamDS->Out.tsLastTransferredMs = RTTimeMilliTS();
925
926 LogFlowFunc(("cbLastTransferred=%RU32, tsLastTransferredMs=%RU64\n",
927 pStreamDS->Out.cbLastTransferred, pStreamDS->Out.tsLastTransferredMs));
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 DSLOG(("DSound: Stopping playback\n"));
978 hr = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB);
979 if (FAILED(hr))
980 {
981 hr = directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);
982 if (FAILED(hr))
983 hr = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB);
984 }
985 }
986
987 if (SUCCEEDED(hr))
988 {
989 if (fFlush)
990 dsoundStreamReset(pThis, pStreamDS);
991 }
992
993 if (FAILED(hr))
994 DSLOGREL(("DSound: %s playback failed with %Rhrc\n", fFlush ? "Stopping" : "Pausing", hr));
995
996 return hr;
997}
998
999/**
1000 * Enables or disables a stream.
1001 *
1002 * @return IPRT status code.
1003 * @param pThis Host audio driver instance.
1004 * @param pStreamDS Stream to enable / disable.
1005 * @param fEnable Whether to enable or disable the stream.
1006 */
1007static int dsoundStreamEnable(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fEnable)
1008{
1009 RT_NOREF(pThis);
1010
1011 LogFunc(("%s %s\n",
1012 fEnable ? "Enabling" : "Disabling",
1013 pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN ? "capture" : "playback"));
1014
1015 if (fEnable)
1016 dsoundStreamReset(pThis, pStreamDS);
1017
1018 pStreamDS->fEnabled = fEnable;
1019
1020 return VINF_SUCCESS;
1021}
1022
1023
1024/**
1025 * Resets the state of a DirectSound stream.
1026 *
1027 * @param pThis Host audio driver instance.
1028 * @param pStreamDS Stream to reset state for.
1029 */
1030static void dsoundStreamReset(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
1031{
1032 RT_NOREF(pThis);
1033
1034 LogFunc(("Resetting %s\n",
1035 pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN ? "capture" : "playback"));
1036
1037 if (pStreamDS->pCircBuf)
1038 RTCircBufReset(pStreamDS->pCircBuf);
1039
1040 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
1041 {
1042 pStreamDS->In.offReadPos = 0;
1043 pStreamDS->In.cOverruns = 0;
1044
1045 /* Also reset the DirectSound Capture Buffer (DSCB) by clearing all data to make sure
1046 * not stale audio data is left. */
1047 if (pStreamDS->In.pDSCB)
1048 {
1049 PVOID pv1; PVOID pv2; DWORD cb1; DWORD cb2;
1050 HRESULT hr = directSoundCaptureLock(pStreamDS, 0 /* Offset */, pStreamDS->cbBufSize, &pv1, &pv2, &cb1, &cb2,
1051 0 /* Flags */);
1052 if (SUCCEEDED(hr))
1053 {
1054 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv1, cb1, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb1));
1055 if (pv2 && cb2)
1056 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv2, cb2, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb2));
1057 directSoundCaptureUnlock(pStreamDS->In.pDSCB, pv1, pv2, cb1, cb2);
1058 }
1059 }
1060 }
1061 else if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT)
1062 {
1063 pStreamDS->Out.fFirstTransfer = true;
1064 pStreamDS->Out.fDrain = false;
1065 pStreamDS->Out.cUnderruns = 0;
1066
1067 pStreamDS->Out.cbLastTransferred = 0;
1068 pStreamDS->Out.tsLastTransferredMs = 0;
1069
1070 pStreamDS->Out.cbTransferred = 0;
1071 pStreamDS->Out.cbWritten = 0;
1072
1073 pStreamDS->Out.offWritePos = 0;
1074 pStreamDS->Out.offPlayCursorLastPending = 0;
1075 pStreamDS->Out.offPlayCursorLastPlayed = 0;
1076
1077 /* Also reset the DirectSound Buffer (DSB) by setting the position to 0 and clear all data to make sure
1078 * not stale audio data is left. */
1079 if (pStreamDS->Out.pDSB)
1080 {
1081 HRESULT hr = IDirectSoundBuffer8_SetCurrentPosition(pStreamDS->Out.pDSB, 0);
1082 if (SUCCEEDED(hr))
1083 {
1084 PVOID pv1; PVOID pv2; DWORD cb1; DWORD cb2;
1085 hr = directSoundPlayLock(pThis, pStreamDS, 0 /* Offset */, pStreamDS->cbBufSize, &pv1, &pv2, &cb1, &cb2,
1086 0 /* Flags */);
1087 if (SUCCEEDED(hr))
1088 {
1089 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv1, cb1, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb1));
1090 if (pv2 && cb2)
1091 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv2, cb2, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb2));
1092 directSoundPlayUnlock(pThis, pStreamDS->Out.pDSB, pv1, pv2, cb1, cb2);
1093 }
1094 }
1095 }
1096 }
1097
1098#ifdef LOG_ENABLED
1099 pStreamDS->Dbg.tsLastTransferredMs = 0;
1100#endif
1101}
1102
1103
1104/**
1105 * Starts playing a DirectSound stream.
1106 *
1107 * @return HRESULT
1108 * @param pThis Host audio driver instance.
1109 * @param pStreamDS Stream to start playing.
1110 */
1111static HRESULT directSoundPlayStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
1112{
1113 HRESULT hr = S_OK;
1114
1115 DWORD fFlags = DSCBSTART_LOOPING;
1116
1117 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
1118 {
1119 DSLOG(("DSound: Starting playback\n"));
1120 hr = IDirectSoundBuffer8_Play(pStreamDS->Out.pDSB, 0, 0, fFlags);
1121 if ( SUCCEEDED(hr)
1122 || hr != DSERR_BUFFERLOST)
1123 break;
1124 else
1125 {
1126 LogFunc(("Restarting playback failed due to lost buffer, restoring ...\n"));
1127 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);
1128 }
1129 }
1130
1131 return hr;
1132}
1133
1134
1135/*
1136 * DirectSoundCapture
1137 */
1138
1139#if 0 /* unused */
1140static LPCGUID dsoundCaptureSelectDevice(PDRVHOSTDSOUND pThis, PPDMAUDIOSTREAMCFG pCfg)
1141{
1142 AssertPtrReturn(pThis, NULL);
1143 AssertPtrReturn(pCfg, NULL);
1144
1145 int rc = VINF_SUCCESS;
1146
1147 LPCGUID pGUID = pThis->Cfg.pGuidCapture;
1148 if (!pGUID)
1149 {
1150 PDSOUNDDEV pDev = NULL;
1151 switch (pCfg->u.enmSrc)
1152 {
1153 case PDMAUDIORECSRC_LINE:
1154 /*
1155 * At the moment we're only supporting line-in in the HDA emulation,
1156 * and line-in + mic-in in the AC'97 emulation both are expected
1157 * to use the host's mic-in as well.
1158 *
1159 * So the fall through here is intentional for now.
1160 */
1161 case PDMAUDIORECSRC_MIC:
1162 pDev = (PDSOUNDDEV)DrvAudioHlpDeviceEnumGetDefaultDevice(&pThis->DeviceEnum, PDMAUDIODIR_IN);
1163 break;
1164
1165 default:
1166 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
1167 break;
1168 }
1169
1170 if ( RT_SUCCESS(rc)
1171 && pDev)
1172 {
1173 DSLOG(("DSound: Guest source '%s' is using host recording device '%s'\n",
1174 PDMAudioRecSrcGetName(pCfg->u.enmSrc), pDev->Core.szName));
1175 pGUID = &pDev->Guid;
1176 }
1177 if (RT_FAILURE(rc))
1178 {
1179 LogRel(("DSound: Selecting recording device failed with %Rrc\n", rc));
1180 return NULL;
1181 }
1182 }
1183
1184 /* This always has to be in the release log. */
1185 char *pszGUID = dsoundGUIDToUtf8StrA(pGUID);
1186 LogRel(("DSound: Guest source '%s' is using host recording device with GUID '%s'\n",
1187 PDMAudioRecSrcGetName(pCfg->u.enmSrc), pszGUID ? pszGUID: "{?}"));
1188 RTStrFree(pszGUID);
1189
1190 return pGUID;
1191}
1192#endif
1193
1194/**
1195 * Transfers audio data from the DirectSound capture instance to the internal buffer.
1196 * Due to internal accounting and querying DirectSound, this function knows how much it can transfer at once.
1197 *
1198 * @return IPRT status code.
1199 * @param pThis Host audio driver instance.
1200 * @param pStreamDS Stream to capture audio data for.
1201 */
1202static int dsoundCaptureTransfer(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
1203{
1204 RT_NOREF(pThis);
1205
1206 LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB = pStreamDS->In.pDSCB;
1207 AssertPtr(pDSCB);
1208
1209 DWORD offCaptureCursor;
1210 HRESULT hr = IDirectSoundCaptureBuffer_GetCurrentPosition(pDSCB, NULL, &offCaptureCursor);
1211 if (FAILED(hr))
1212 {
1213 AssertFailed();
1214 return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1215 }
1216
1217 DWORD cbUsed = dsoundRingDistance(offCaptureCursor, pStreamDS->In.offReadPos, pStreamDS->cbBufSize);
1218
1219 PRTCIRCBUF pCircBuf = pStreamDS->pCircBuf;
1220 AssertPtr(pCircBuf);
1221
1222 uint32_t cbFree = (uint32_t)RTCircBufFree(pCircBuf);
1223 if ( !cbFree
1224 && pStreamDS->In.cOverruns < 32) /** @todo Make this configurable. */
1225 {
1226 DSLOG(("DSound: Warning: Internal buffer full (size is %zu bytes), skipping to record data (overflow #%RU32)\n",
1227 RTCircBufSize(pCircBuf), pStreamDS->In.cOverruns));
1228 DSLOG(("DSound: Warning: DSound capture buffer currently uses %RU32/%RU32 bytes\n", cbUsed, pStreamDS->cbBufSize));
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 /** @todo r=bird: I cannot see any code populating pCfgAcq... */
1378
1379 LogFlowFuncEnter();
1380
1381 Assert(pStreamDS->In.pDSCB == NULL);
1382
1383 DSLOG(("DSound: Opening capturing stream (uHz=%RU32, cChannels=%RU8, cBits=%u, fSigned=%RTbool)\n", pCfgReq->Props.uHz,
1384 PDMAudioPropsChannels(&pCfgReq->Props), PDMAudioPropsSampleBits(&pCfgReq->Props), pCfgReq->Props.fSigned));
1385
1386 WAVEFORMATEX wfx;
1387 int rc = dsoundWaveFmtFromCfg(pCfgReq, &wfx);
1388 if (RT_FAILURE(rc))
1389 return E_INVALIDARG;
1390
1391 /** @todo r=bird: Why is this called every time? It triggers a device
1392 * enumeration. Andy claimed on IRC that enumeration was only done once... */
1393 dsoundUpdateStatusInternal(pThis);
1394
1395 HRESULT hr = directSoundCaptureInterfaceCreate(pThis->Cfg.pGuidCapture, &pThis->pDSC);
1396 if (FAILED(hr))
1397 return hr;
1398
1399 do /* To use breaks. */
1400 {
1401 DSCBUFFERDESC bd;
1402 RT_ZERO(bd);
1403
1404 bd.dwSize = sizeof(bd);
1405 bd.lpwfxFormat = &wfx;
1406 bd.dwBufferBytes = PDMAudioPropsFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize);
1407
1408 DSLOG(("DSound: Requested capture buffer is %RU64ms (%ld bytes)\n",
1409 PDMAudioPropsBytesToMilli(&pCfgReq->Props, bd.dwBufferBytes), bd.dwBufferBytes));
1410
1411 LPDIRECTSOUNDCAPTUREBUFFER pDSCB;
1412 hr = IDirectSoundCapture_CreateCaptureBuffer(pThis->pDSC, &bd, &pDSCB, NULL);
1413 if (FAILED(hr))
1414 {
1415 if (hr == E_ACCESSDENIED)
1416 {
1417 DSLOGREL(("DSound: Capturing input from host not possible, access denied\n"));
1418 }
1419 else
1420 DSLOGREL(("DSound: Creating capture buffer failed with %Rhrc\n", hr));
1421 break;
1422 }
1423
1424 hr = IDirectSoundCaptureBuffer_QueryInterface(pDSCB, IID_IDirectSoundCaptureBuffer8, (void **)&pStreamDS->In.pDSCB);
1425 IDirectSoundCaptureBuffer_Release(pDSCB);
1426 if (FAILED(hr))
1427 {
1428 DSLOGREL(("DSound: Querying interface for capture buffer failed with %Rhrc\n", hr));
1429 break;
1430 }
1431
1432 /*
1433 * Query the actual parameters.
1434 */
1435 DWORD offByteReadPos = 0;
1436 hr = IDirectSoundCaptureBuffer8_GetCurrentPosition(pStreamDS->In.pDSCB, NULL, &offByteReadPos);
1437 if (FAILED(hr))
1438 {
1439 offByteReadPos = 0;
1440 DSLOGREL(("DSound: Getting capture position failed with %Rhrc\n", hr));
1441 }
1442
1443 RT_ZERO(wfx);
1444 hr = IDirectSoundCaptureBuffer8_GetFormat(pStreamDS->In.pDSCB, &wfx, sizeof(wfx), NULL);
1445 if (FAILED(hr))
1446 {
1447 DSLOGREL(("DSound: Getting capture format failed with %Rhrc\n", hr));
1448 break;
1449 }
1450
1451 DSCBCAPS bc;
1452 RT_ZERO(bc);
1453 bc.dwSize = sizeof(bc);
1454 hr = IDirectSoundCaptureBuffer8_GetCaps(pStreamDS->In.pDSCB, &bc);
1455 if (FAILED(hr))
1456 {
1457 DSLOGREL(("DSound: Getting capture capabilities failed with %Rhrc\n", hr));
1458 break;
1459 }
1460
1461 DSLOG(("DSound: Acquired capture buffer is %RU64ms (%ld bytes)\n",
1462 PDMAudioPropsBytesToMilli(&pCfgReq->Props, bc.dwBufferBytes), bc.dwBufferBytes));
1463
1464 DSLOG(("DSound: Capture format:\n"
1465 " dwBufferBytes = %RI32\n"
1466 " dwFlags = 0x%x\n"
1467 " wFormatTag = %RI16\n"
1468 " nChannels = %RI16\n"
1469 " nSamplesPerSec = %RU32\n"
1470 " nAvgBytesPerSec = %RU32\n"
1471 " nBlockAlign = %RI16\n"
1472 " wBitsPerSample = %RI16\n"
1473 " cbSize = %RI16\n",
1474 bc.dwBufferBytes,
1475 bc.dwFlags,
1476 wfx.wFormatTag,
1477 wfx.nChannels,
1478 wfx.nSamplesPerSec,
1479 wfx.nAvgBytesPerSec,
1480 wfx.nBlockAlign,
1481 wfx.wBitsPerSample,
1482 wfx.cbSize));
1483
1484 if (bc.dwBufferBytes & pStreamDS->uAlign)
1485 DSLOGREL(("DSound: Capture GetCaps returned misaligned buffer: size %RU32, alignment %RU32\n",
1486 bc.dwBufferBytes, pStreamDS->uAlign + 1));
1487
1488 /* Initial state: reading at the initial capture position, no error. */
1489 pStreamDS->In.offReadPos = 0;
1490 pStreamDS->cbBufSize = bc.dwBufferBytes;
1491
1492 rc = RTCircBufCreate(&pStreamDS->pCircBuf, pStreamDS->cbBufSize * 2 /* Use "double buffering" */);
1493 AssertRC(rc);
1494
1495 pThis->pDSStrmIn = pStreamDS;
1496
1497 pCfgAcq->Backend.cFramesBufferSize = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDS->cbBufSize);
1498
1499 } while (0);
1500
1501 if (FAILED(hr))
1502 directSoundCaptureClose(pThis, pStreamDS);
1503
1504 LogFlowFunc(("Returning %Rhrc\n", hr));
1505 return hr;
1506}
1507
1508static HRESULT directSoundCaptureStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fFlush)
1509{
1510 AssertPtrReturn(pThis, E_POINTER);
1511 AssertPtrReturn(pStreamDS, E_POINTER);
1512
1513 RT_NOREF(pThis);
1514
1515 HRESULT hr = S_OK;
1516
1517 if (pStreamDS->In.pDSCB)
1518 {
1519 DSLOG(("DSound: Stopping capture\n"));
1520 hr = IDirectSoundCaptureBuffer_Stop(pStreamDS->In.pDSCB);
1521 }
1522
1523 if (SUCCEEDED(hr))
1524 {
1525 if (fFlush)
1526 dsoundStreamReset(pThis, pStreamDS);
1527 }
1528
1529 if (FAILED(hr))
1530 DSLOGREL(("DSound: Stopping capture buffer failed with %Rhrc\n", hr));
1531
1532 return hr;
1533}
1534
1535static HRESULT directSoundCaptureStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
1536{
1537 AssertPtrReturn(pThis, E_POINTER);
1538 AssertPtrReturn(pStreamDS, E_POINTER);
1539
1540 HRESULT hr;
1541 if (pStreamDS->In.pDSCB)
1542 {
1543 DWORD dwStatus;
1544 hr = IDirectSoundCaptureBuffer8_GetStatus(pStreamDS->In.pDSCB, &dwStatus);
1545 if (FAILED(hr))
1546 {
1547 DSLOGREL(("DSound: Retrieving capture status failed with %Rhrc\n", hr));
1548 }
1549 else
1550 {
1551 if (dwStatus & DSCBSTATUS_CAPTURING)
1552 {
1553 DSLOG(("DSound: Already capturing\n"));
1554 }
1555 else
1556 {
1557 const DWORD fFlags = DSCBSTART_LOOPING;
1558
1559 DSLOG(("DSound: Starting to capture\n"));
1560 hr = IDirectSoundCaptureBuffer8_Start(pStreamDS->In.pDSCB, fFlags);
1561 if (FAILED(hr))
1562 DSLOGREL(("DSound: Starting to capture failed with %Rhrc\n", hr));
1563 }
1564 }
1565 }
1566 else
1567 hr = E_UNEXPECTED;
1568
1569 LogFlowFunc(("Returning %Rhrc\n", hr));
1570 return hr;
1571}
1572
1573/**
1574 * Callback for the playback device enumeration.
1575 *
1576 * @return TRUE if continuing enumeration, FALSE if not.
1577 * @param pGUID Pointer to GUID of enumerated device. Can be NULL.
1578 * @param pwszDescription Pointer to (friendly) description of enumerated device.
1579 * @param pwszModule Pointer to module name of enumerated device.
1580 * @param lpContext Pointer to PDSOUNDENUMCBCTX context for storing the enumerated information.
1581 *
1582 * @note Carbon copy of dsoundDevicesEnumCbCapture with OUT direction.
1583 */
1584static BOOL CALLBACK dsoundDevicesEnumCbPlayback(LPGUID pGUID, LPCWSTR pwszDescription, LPCWSTR pwszModule, PVOID lpContext)
1585{
1586 PDSOUNDENUMCBCTX pEnumCtx = (PDSOUNDENUMCBCTX)lpContext;
1587 AssertPtrReturn(pEnumCtx , FALSE);
1588
1589 PPDMAUDIOHOSTENUM pDevEnm = pEnumCtx->pDevEnm;
1590 AssertPtrReturn(pDevEnm, FALSE);
1591
1592 AssertPtrNullReturn(pGUID, FALSE); /* pGUID can be NULL for default device(s). */
1593 AssertPtrReturn(pwszDescription, FALSE);
1594 RT_NOREF(pwszModule); /* Do not care about pwszModule. */
1595
1596 int rc;
1597 PDSOUNDDEV pDev = (PDSOUNDDEV)PDMAudioHostDevAlloc(sizeof(DSOUNDDEV));
1598 if (pDev)
1599 {
1600 pDev->Core.enmUsage = PDMAUDIODIR_OUT;
1601 pDev->Core.enmType = PDMAUDIODEVICETYPE_BUILTIN;
1602
1603 if (pGUID == NULL)
1604 pDev->Core.fFlags = PDMAUDIOHOSTDEV_F_DEFAULT;
1605
1606 char *pszName;
1607 rc = RTUtf16ToUtf8(pwszDescription, &pszName);
1608 if (RT_SUCCESS(rc))
1609 {
1610 RTStrCopy(pDev->Core.szName, sizeof(pDev->Core.szName), pszName);
1611 RTStrFree(pszName);
1612
1613 if (pGUID) /* pGUID == NULL means default device. */
1614 memcpy(&pDev->Guid, pGUID, sizeof(pDev->Guid));
1615
1616 PDMAudioHostEnumAppend(pDevEnm, &pDev->Core);
1617
1618 /* Note: Querying the actual device information will be done at some
1619 * later point in time outside this enumeration callback to prevent
1620 * DSound hangs. */
1621 return TRUE;
1622 }
1623 PDMAudioHostDevFree(&pDev->Core);
1624 }
1625 else
1626 rc = VERR_NO_MEMORY;
1627
1628 LogRel(("DSound: Error enumeration playback device '%ls': rc=%Rrc\n", pwszDescription, rc));
1629 return FALSE; /* Abort enumeration. */
1630}
1631
1632/**
1633 * Callback for the capture device enumeration.
1634 *
1635 * @return TRUE if continuing enumeration, FALSE if not.
1636 * @param pGUID Pointer to GUID of enumerated device. Can be NULL.
1637 * @param pwszDescription Pointer to (friendly) description of enumerated device.
1638 * @param pwszModule Pointer to module name of enumerated device.
1639 * @param lpContext Pointer to PDSOUNDENUMCBCTX context for storing the enumerated information.
1640 *
1641 * @note Carbon copy of dsoundDevicesEnumCbPlayback with IN direction.
1642 */
1643static BOOL CALLBACK dsoundDevicesEnumCbCapture(LPGUID pGUID, LPCWSTR pwszDescription, LPCWSTR pwszModule, PVOID lpContext)
1644{
1645 PDSOUNDENUMCBCTX pEnumCtx = (PDSOUNDENUMCBCTX )lpContext;
1646 AssertPtrReturn(pEnumCtx , FALSE);
1647
1648 PPDMAUDIOHOSTENUM pDevEnm = pEnumCtx->pDevEnm;
1649 AssertPtrReturn(pDevEnm, FALSE);
1650
1651 AssertPtrNullReturn(pGUID, FALSE); /* pGUID can be NULL for default device(s). */
1652 AssertPtrReturn(pwszDescription, FALSE);
1653 RT_NOREF(pwszModule); /* Do not care about pwszModule. */
1654
1655 int rc;
1656 PDSOUNDDEV pDev = (PDSOUNDDEV)PDMAudioHostDevAlloc(sizeof(DSOUNDDEV));
1657 if (pDev)
1658 {
1659 pDev->Core.enmUsage = PDMAUDIODIR_IN;
1660 pDev->Core.enmType = PDMAUDIODEVICETYPE_BUILTIN;
1661
1662 char *pszName;
1663 rc = RTUtf16ToUtf8(pwszDescription, &pszName);
1664 if (RT_SUCCESS(rc))
1665 {
1666 RTStrCopy(pDev->Core.szName, sizeof(pDev->Core.szName), pszName);
1667 RTStrFree(pszName);
1668
1669 if (pGUID) /* pGUID == NULL means default capture device. */
1670 memcpy(&pDev->Guid, pGUID, sizeof(pDev->Guid));
1671
1672 PDMAudioHostEnumAppend(pDevEnm, &pDev->Core);
1673
1674 /* Note: Querying the actual device information will be done at some
1675 * later point in time outside this enumeration callback to prevent
1676 * DSound hangs. */
1677 return TRUE;
1678 }
1679 PDMAudioHostDevFree(&pDev->Core);
1680 }
1681 else
1682 rc = VERR_NO_MEMORY;
1683
1684 LogRel(("DSound: Error enumeration capture device '%ls', rc=%Rrc\n", pwszDescription, rc));
1685 return FALSE; /* Abort enumeration. */
1686}
1687
1688/**
1689 * Qqueries information for a given (DirectSound) device.
1690 *
1691 * @returns VBox status code.
1692 * @param pThis Host audio driver instance.
1693 * @param pDev Audio device to query information for.
1694 */
1695static int dsoundDeviceQueryInfo(PDRVHOSTDSOUND pThis, PDSOUNDDEV pDev)
1696{
1697 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1698 AssertPtrReturn(pDev, VERR_INVALID_POINTER);
1699 int rc;
1700
1701 if (pDev->Core.enmUsage == PDMAUDIODIR_OUT)
1702 {
1703 LPDIRECTSOUND8 pDS;
1704 HRESULT hr = directSoundPlayInterfaceCreate(&pDev->Guid, &pDS);
1705 if (SUCCEEDED(hr))
1706 {
1707 DSCAPS DSCaps;
1708 RT_ZERO(DSCaps);
1709 DSCaps.dwSize = sizeof(DSCAPS);
1710 hr = IDirectSound_GetCaps(pDS, &DSCaps);
1711 if (SUCCEEDED(hr))
1712 {
1713 pDev->Core.cMaxOutputChannels = DSCaps.dwFlags & DSCAPS_PRIMARYSTEREO ? 2 : 1;
1714
1715 DWORD dwSpeakerCfg;
1716 hr = IDirectSound_GetSpeakerConfig(pDS, &dwSpeakerCfg);
1717 if (SUCCEEDED(hr))
1718 {
1719 unsigned uSpeakerCount = 0;
1720 switch (DSSPEAKER_CONFIG(dwSpeakerCfg))
1721 {
1722 case DSSPEAKER_MONO: uSpeakerCount = 1; break;
1723 case DSSPEAKER_HEADPHONE: uSpeakerCount = 2; break;
1724 case DSSPEAKER_STEREO: uSpeakerCount = 2; break;
1725 case DSSPEAKER_QUAD: uSpeakerCount = 4; break;
1726 case DSSPEAKER_SURROUND: uSpeakerCount = 4; break;
1727 case DSSPEAKER_5POINT1: uSpeakerCount = 6; break;
1728 case DSSPEAKER_5POINT1_SURROUND: uSpeakerCount = 6; break;
1729 case DSSPEAKER_7POINT1: uSpeakerCount = 8; break;
1730 case DSSPEAKER_7POINT1_SURROUND: uSpeakerCount = 8; break;
1731 default: break;
1732 }
1733
1734 if (uSpeakerCount) /* Do we need to update the channel count? */
1735 pDev->Core.cMaxOutputChannels = uSpeakerCount;
1736
1737 rc = VINF_SUCCESS;
1738 }
1739 else
1740 {
1741 LogRel(("DSound: Error retrieving playback device speaker config, hr=%Rhrc\n", hr));
1742 rc = VERR_ACCESS_DENIED; /** @todo Fudge! */
1743 }
1744 }
1745 else
1746 {
1747 LogRel(("DSound: Error retrieving playback device capabilities, hr=%Rhrc\n", hr));
1748 rc = VERR_ACCESS_DENIED; /** @todo Fudge! */
1749 }
1750
1751 directSoundPlayInterfaceDestroy(pDS);
1752 }
1753 else
1754 rc = VERR_GENERAL_FAILURE;
1755 }
1756 else if (pDev->Core.enmUsage == PDMAUDIODIR_IN)
1757 {
1758 LPDIRECTSOUNDCAPTURE8 pDSC;
1759 HRESULT hr = directSoundCaptureInterfaceCreate(&pDev->Guid, &pDSC);
1760 if (SUCCEEDED(hr))
1761 {
1762 DSCCAPS DSCCaps;
1763 RT_ZERO(DSCCaps);
1764 DSCCaps.dwSize = sizeof(DSCCAPS);
1765 hr = IDirectSoundCapture_GetCaps(pDSC, &DSCCaps);
1766 if (SUCCEEDED(hr))
1767 {
1768 pDev->Core.cMaxInputChannels = DSCCaps.dwChannels;
1769 rc = VINF_SUCCESS;
1770 }
1771 else
1772 {
1773 LogRel(("DSound: Error retrieving capture device capabilities, hr=%Rhrc\n", hr));
1774 rc = VERR_ACCESS_DENIED; /** @todo Fudge! */
1775 }
1776
1777 directSoundCaptureInterfaceDestroy(pDSC);
1778 }
1779 else
1780 rc = VERR_GENERAL_FAILURE;
1781 }
1782 else
1783 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
1784
1785 return rc;
1786}
1787
1788
1789/**
1790 * Queries information for @a pDevice and adds an entry to the enumeration.
1791 *
1792 * @returns VBox status code.
1793 * @param pDevEnm The enumeration to add the device to.
1794 * @param pDevice The device.
1795 * @param enmType The type of device.
1796 * @param fDefault Whether it's the default device.
1797 */
1798static int dsoundDeviceNewStyleAdd(PPDMAUDIOHOSTENUM pDevEnm, IMMDevice *pDevice, EDataFlow enmType, bool fDefault)
1799{
1800 int rc = VINF_SUCCESS; /* ignore most errors */
1801
1802 /*
1803 * Gather the necessary properties.
1804 */
1805 IPropertyStore *pProperties = NULL;
1806 HRESULT hrc = pDevice->OpenPropertyStore(STGM_READ, &pProperties);
1807 if (SUCCEEDED(hrc))
1808 {
1809 /* Get the friendly name. */
1810 PROPVARIANT VarName;
1811 PropVariantInit(&VarName);
1812 hrc = pProperties->GetValue(PKEY_Device_FriendlyName, &VarName);
1813 if (SUCCEEDED(hrc))
1814 {
1815 /* Get the DirectSound GUID. */
1816 PROPVARIANT VarGUID;
1817 PropVariantInit(&VarGUID);
1818 hrc = pProperties->GetValue(PKEY_AudioEndpoint_GUID, &VarGUID);
1819 if (SUCCEEDED(hrc))
1820 {
1821 /* Get the device format. */
1822 PROPVARIANT VarFormat;
1823 PropVariantInit(&VarFormat);
1824 hrc = pProperties->GetValue(PKEY_AudioEngine_DeviceFormat, &VarFormat);
1825 if (SUCCEEDED(hrc))
1826 {
1827 WAVEFORMATEX const * const pFormat = (WAVEFORMATEX const *)VarFormat.blob.pBlobData;
1828 AssertPtr(pFormat);
1829
1830 /*
1831 * Create a enumeration entry for it.
1832 */
1833 PDSOUNDDEV pDev = (PDSOUNDDEV)PDMAudioHostDevAlloc(sizeof(DSOUNDDEV));
1834 if (pDev)
1835 {
1836 pDev->Core.enmUsage = enmType == eRender ? PDMAUDIODIR_OUT : PDMAUDIODIR_IN;
1837 pDev->Core.enmType = PDMAUDIODEVICETYPE_BUILTIN;
1838 if (enmType == eRender)
1839 pDev->Core.cMaxOutputChannels = pFormat->nChannels;
1840 else
1841 pDev->Core.cMaxInputChannels = pFormat->nChannels;
1842
1843 RT_NOREF(fDefault);
1844 //if (fDefault)
1845 hrc = UuidFromStringW(VarGUID.pwszVal, &pDev->Guid);
1846 if (SUCCEEDED(hrc))
1847 {
1848 char *pszName;
1849 rc = RTUtf16ToUtf8(VarName.pwszVal, &pszName);
1850 if (RT_SUCCESS(rc))
1851 {
1852 RTStrCopy(pDev->Core.szName, sizeof(pDev->Core.szName), pszName);
1853 RTStrFree(pszName);
1854
1855 PDMAudioHostEnumAppend(pDevEnm, &pDev->Core);
1856 }
1857 else
1858 PDMAudioHostDevFree(&pDev->Core);
1859 }
1860 else
1861 {
1862 LogFunc(("UuidFromStringW(%ls): %Rhrc\n", VarGUID.pwszVal, hrc));
1863 PDMAudioHostDevFree(&pDev->Core);
1864 }
1865 }
1866 else
1867 rc = VERR_NO_MEMORY;
1868 PropVariantClear(&VarFormat);
1869 }
1870 else
1871 LogFunc(("Failed to get PKEY_AudioEngine_DeviceFormat: %Rhrc\n", hrc));
1872 PropVariantClear(&VarGUID);
1873 }
1874 else
1875 LogFunc(("Failed to get PKEY_AudioEndpoint_GUID: %Rhrc\n", hrc));
1876 PropVariantClear(&VarName);
1877 }
1878 else
1879 LogFunc(("Failed to get PKEY_Device_FriendlyName: %Rhrc\n", hrc));
1880 pProperties->Release();
1881 }
1882 else
1883 LogFunc(("OpenPropertyStore failed: %Rhrc\n", hrc));
1884
1885 if (hrc == E_OUTOFMEMORY && RT_SUCCESS_NP(rc))
1886 rc = VERR_NO_MEMORY;
1887 return rc;
1888}
1889
1890/**
1891 * Does a (Re-)enumeration of the host's playback + capturing devices.
1892 *
1893 * @return IPRT status code.
1894 * @param pThis Host audio driver instance.
1895 * @param pDevEnm Where to store the enumerated devices.
1896 */
1897static int dsoundDevicesEnumerate(PDRVHOSTDSOUND pThis, PPDMAUDIOHOSTENUM pDevEnm)
1898{
1899 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1900 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1901
1902 DSLOG(("DSound: Enumerating devices ...\n"));
1903
1904 /*
1905 * Use the Vista+ API.
1906 */
1907 IMMDeviceEnumerator *pEnumerator;
1908 HRESULT hrc = CoCreateInstance(__uuidof(MMDeviceEnumerator), 0, CLSCTX_ALL,
1909 __uuidof(IMMDeviceEnumerator), (void **)&pEnumerator);
1910 if (SUCCEEDED(hrc))
1911 {
1912 int rc = VINF_SUCCESS;
1913 for (unsigned idxPass = 0; idxPass < 2 && RT_SUCCESS(rc); idxPass++)
1914 {
1915 EDataFlow const enmType = idxPass == 0 ? EDataFlow::eRender : EDataFlow::eCapture;
1916
1917 /* Get the default device first. */
1918 IMMDevice *pDefaultDevice = NULL;
1919 hrc = pEnumerator->GetDefaultAudioEndpoint(enmType, eMultimedia, &pDefaultDevice);
1920 if (SUCCEEDED(hrc))
1921 rc = dsoundDeviceNewStyleAdd(pDevEnm, pDefaultDevice, enmType, true);
1922 else
1923 pDefaultDevice = NULL;
1924
1925 /* Enumerate the devices. */
1926 IMMDeviceCollection *pCollection = NULL;
1927 hrc = pEnumerator->EnumAudioEndpoints(enmType, DEVICE_STATE_ACTIVE /*| DEVICE_STATE_UNPLUGGED?*/, &pCollection);
1928 if (SUCCEEDED(hrc) && pCollection != NULL)
1929 {
1930 UINT cDevices = 0;
1931 hrc = pCollection->GetCount(&cDevices);
1932 if (SUCCEEDED(hrc))
1933 {
1934 for (UINT idxDevice = 0; idxDevice < cDevices && RT_SUCCESS(rc); idxDevice++)
1935 {
1936 IMMDevice *pDevice = NULL;
1937 hrc = pCollection->Item(idxDevice, &pDevice);
1938 if (SUCCEEDED(hrc) && pDevice)
1939 {
1940 if (pDevice != pDefaultDevice)
1941 rc = dsoundDeviceNewStyleAdd(pDevEnm, pDevice, enmType, false);
1942 pDevice->Release();
1943 }
1944 }
1945 }
1946 pCollection->Release();
1947 }
1948 else
1949 LogRelMax(10, ("EnumAudioEndpoints(%s) failed: %Rhrc\n", idxPass == 0 ? "output" : "input", hrc));
1950
1951 if (pDefaultDevice)
1952 pDefaultDevice->Release();
1953 }
1954 pEnumerator->Release();
1955 if (pDevEnm->cDevices > 0 || RT_FAILURE(rc))
1956 {
1957 DSLOG(("DSound: Enumerating devices done - %u device (%Rrc)\n", pDevEnm->cDevices, rc));
1958 return rc;
1959 }
1960 }
1961
1962 /*
1963 * Fall back on dsound.
1964 */
1965 RTLDRMOD hDSound = NULL;
1966 int rc = RTLdrLoadSystem("dsound.dll", true /*fNoUnload*/, &hDSound);
1967 if (RT_SUCCESS(rc))
1968 {
1969 DSOUNDENUMCBCTX EnumCtx;
1970 EnumCtx.fFlags = 0;
1971 EnumCtx.pDevEnm = pDevEnm;
1972
1973 /*
1974 * Enumerate playback devices.
1975 */
1976 PFNDIRECTSOUNDENUMERATEW pfnDirectSoundEnumerateW = NULL;
1977 rc = RTLdrGetSymbol(hDSound, "DirectSoundEnumerateW", (void**)&pfnDirectSoundEnumerateW);
1978 if (RT_SUCCESS(rc))
1979 {
1980 DSLOG(("DSound: Enumerating playback devices ...\n"));
1981
1982 HRESULT hr = pfnDirectSoundEnumerateW(&dsoundDevicesEnumCbPlayback, &EnumCtx);
1983 if (FAILED(hr))
1984 LogRel(("DSound: Error enumerating host playback devices: %Rhrc\n", hr));
1985 }
1986 else
1987 LogRel(("DSound: Error starting to enumerate host playback devices: %Rrc\n", rc));
1988
1989 /*
1990 * Enumerate capture devices.
1991 */
1992 PFNDIRECTSOUNDCAPTUREENUMERATEW pfnDirectSoundCaptureEnumerateW = NULL;
1993 rc = RTLdrGetSymbol(hDSound, "DirectSoundCaptureEnumerateW", (void**)&pfnDirectSoundCaptureEnumerateW);
1994 if (RT_SUCCESS(rc))
1995 {
1996 DSLOG(("DSound: Enumerating capture devices ...\n"));
1997
1998 HRESULT hr = pfnDirectSoundCaptureEnumerateW(&dsoundDevicesEnumCbCapture, &EnumCtx);
1999 if (FAILED(hr))
2000 LogRel(("DSound: Error enumerating host capture devices: %Rhrc\n", hr));
2001 }
2002 else
2003 LogRel(("DSound: Error starting to enumerate host capture devices: %Rrc\n", rc));
2004
2005 /*
2006 * Query Information from all enumerated devices.
2007 */
2008 PDSOUNDDEV pDev;
2009 RTListForEach(&pDevEnm->LstDevices, pDev, DSOUNDDEV, Core.ListEntry)
2010 {
2011 dsoundDeviceQueryInfo(pThis, pDev); /* ignore rc */
2012 }
2013
2014 RTLdrClose(hDSound);
2015 }
2016 else
2017 {
2018 /* No dsound.dll on this system. */
2019 LogRel(("DSound: Could not load dsound.dll for enumerating devices: %Rrc\n", rc));
2020 }
2021
2022 DSLOG(("DSound: Enumerating devices done\n"));
2023
2024 return rc;
2025}
2026
2027/**
2028 * Updates this host driver's internal status, according to the global, overall input/output
2029 * state and all connected (native) audio streams.
2030 *
2031 * @todo r=bird: This is a 'ing waste of 'ing time! We're doing this everytime
2032 * an 'ing stream is created and we doesn't 'ing use the information here
2033 * for any darn thing! Given the reported slowness of enumeration and
2034 * issues with eh 'ing code the only appropriate response is:
2035 * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARG!!!!!!!
2036 *
2037 * @param pThis Host audio driver instance.
2038 */
2039static void dsoundUpdateStatusInternal(PDRVHOSTDSOUND pThis)
2040{
2041#if 0 /** @todo r=bird: This isn't doing *ANYTHING* useful. So, I've just disabled it. */
2042 AssertPtrReturnVoid(pThis);
2043 LogFlowFuncEnter();
2044
2045 PDMAudioHostEnumDelete(&pThis->DeviceEnum);
2046 int rc = dsoundDevicesEnumerate(pThis, &pThis->DeviceEnum);
2047 if (RT_SUCCESS(rc))
2048 {
2049#if 0
2050 if ( pThis->fEnabledOut != RT_BOOL(cbCtx.cDevOut)
2051 || pThis->fEnabledIn != RT_BOOL(cbCtx.cDevIn))
2052 {
2053 /** @todo Use a registered callback to the audio connector (e.g "OnConfigurationChanged") to
2054 * let the connector know that something has changed within the host backend. */
2055 }
2056#endif
2057 pThis->fEnabledIn = PDMAudioHostEnumCountMatching(&pThis->DeviceEnum, PDMAUDIODIR_IN) != 0;
2058 pThis->fEnabledOut = PDMAudioHostEnumCountMatching(&pThis->DeviceEnum, PDMAUDIODIR_OUT) != 0;
2059 }
2060
2061 LogFlowFuncLeaveRC(rc);
2062#else
2063 RT_NOREF(pThis);
2064#endif
2065}
2066
2067static int dsoundCreateStreamOut(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS,
2068 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
2069{
2070 LogFlowFunc(("pStreamDS=%p, pCfgReq=%p\n", pStreamDS, pCfgReq));
2071
2072 int rc = VINF_SUCCESS;
2073
2074 /* Try to open playback in case the device is already there. */
2075 HRESULT hr = directSoundPlayOpen(pThis, pStreamDS, pCfgReq, pCfgAcq);
2076 if (SUCCEEDED(hr))
2077 {
2078 rc = PDMAudioStrmCfgCopy(&pStreamDS->Cfg, pCfgAcq);
2079 if (RT_SUCCESS(rc))
2080 dsoundStreamReset(pThis, pStreamDS);
2081 }
2082 else
2083 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
2084
2085 LogFlowFuncLeaveRC(rc);
2086 return rc;
2087}
2088
2089static int dsoundControlStreamOut(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, PDMAUDIOSTREAMCMD enmStreamCmd)
2090{
2091 LogFlowFunc(("pStreamDS=%p, cmd=%d\n", pStreamDS, enmStreamCmd));
2092
2093 int rc = VINF_SUCCESS;
2094
2095 HRESULT hr;
2096 switch (enmStreamCmd)
2097 {
2098 case PDMAUDIOSTREAMCMD_ENABLE:
2099 {
2100 dsoundStreamEnable(pThis, pStreamDS, true /* fEnable */);
2101 break;
2102 }
2103
2104 case PDMAUDIOSTREAMCMD_RESUME:
2105 {
2106 hr = directSoundPlayStart(pThis, pStreamDS);
2107 if (FAILED(hr))
2108 rc = VERR_NOT_SUPPORTED; /** @todo Fix this. */
2109 break;
2110 }
2111
2112 case PDMAUDIOSTREAMCMD_DISABLE:
2113 {
2114 dsoundStreamEnable(pThis, pStreamDS, false /* fEnable */);
2115 hr = directSoundPlayStop(pThis, pStreamDS, true /* fFlush */);
2116 if (FAILED(hr))
2117 rc = VERR_NOT_SUPPORTED;
2118 break;
2119 }
2120
2121 case PDMAUDIOSTREAMCMD_PAUSE:
2122 {
2123 hr = directSoundPlayStop(pThis, pStreamDS, false /* fFlush */);
2124 if (FAILED(hr))
2125 rc = VERR_NOT_SUPPORTED;
2126 break;
2127 }
2128
2129 case PDMAUDIOSTREAMCMD_DRAIN:
2130 {
2131 /* Make sure we transferred everything. */
2132 pStreamDS->fEnabled = true;
2133 pStreamDS->Out.fDrain = true;
2134 rc = dsoundPlayTransfer(pThis, pStreamDS);
2135 if ( RT_SUCCESS(rc)
2136 && pStreamDS->Out.fFirstTransfer)
2137 {
2138 /* If this was the first transfer ever for this stream, make sure to also play the (short) audio data. */
2139 DSLOG(("DSound: Started playing output (short sound)\n"));
2140
2141 pStreamDS->Out.fFirstTransfer = false;
2142 pStreamDS->Out.cbLastTransferred = pStreamDS->Out.cbTransferred; /* All transferred audio data must be played. */
2143 pStreamDS->Out.tsLastTransferredMs = RTTimeMilliTS();
2144
2145 hr = directSoundPlayStart(pThis, pStreamDS);
2146 if (FAILED(hr))
2147 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
2148 }
2149 break;
2150 }
2151
2152 case PDMAUDIOSTREAMCMD_DROP:
2153 {
2154 pStreamDS->Out.cbLastTransferred = 0;
2155 pStreamDS->Out.tsLastTransferredMs = 0;
2156 RTCircBufReset(pStreamDS->pCircBuf);
2157 break;
2158 }
2159
2160 default:
2161 rc = VERR_NOT_SUPPORTED;
2162 break;
2163 }
2164
2165 LogFlowFuncLeaveRC(rc);
2166 return rc;
2167}
2168
2169/**
2170 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
2171 */
2172static DECLCALLBACK(int) drvHostDSoundHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, const void *pvBuf,
2173 uint32_t uBufSize, uint32_t *puWritten)
2174{
2175 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2176 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2177 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2178 AssertReturn(uBufSize, VERR_INVALID_PARAMETER);
2179 /* puWritten is optional. */
2180
2181 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2182 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2183
2184 int rc = VINF_SUCCESS;
2185
2186 uint32_t cbWrittenTotal = 0;
2187
2188 uint8_t *pbBuf = (uint8_t *)pvBuf;
2189 PRTCIRCBUF pCircBuf = pStreamDS->pCircBuf;
2190
2191 uint32_t cbToPlay = RT_MIN(uBufSize, (uint32_t)RTCircBufFree(pCircBuf));
2192 while (cbToPlay)
2193 {
2194 void *pvChunk;
2195 size_t cbChunk;
2196 RTCircBufAcquireWriteBlock(pCircBuf, cbToPlay, &pvChunk, &cbChunk);
2197
2198 if (cbChunk)
2199 {
2200 memcpy(pvChunk, pbBuf, cbChunk);
2201
2202 pbBuf += cbChunk;
2203 Assert(cbToPlay >= cbChunk);
2204 cbToPlay -= (uint32_t)cbChunk;
2205
2206 cbWrittenTotal += (uint32_t)cbChunk;
2207 }
2208
2209 RTCircBufReleaseWriteBlock(pCircBuf, cbChunk);
2210 }
2211
2212 Assert(cbWrittenTotal <= uBufSize);
2213 Assert(cbWrittenTotal == uBufSize);
2214
2215 pStreamDS->Out.cbWritten += cbWrittenTotal;
2216
2217 if (RT_SUCCESS(rc))
2218 {
2219 if (puWritten)
2220 *puWritten = cbWrittenTotal;
2221 }
2222 else
2223 dsoundUpdateStatusInternal(pThis);
2224
2225 return rc;
2226}
2227
2228static int dsoundDestroyStreamOut(PDRVHOSTDSOUND pThis, PPDMAUDIOBACKENDSTREAM pStream)
2229{
2230 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2231
2232 LogFlowFuncEnter();
2233
2234 HRESULT hr = directSoundPlayStop(pThis, pStreamDS, true /* fFlush */);
2235 if (SUCCEEDED(hr))
2236 {
2237 hr = directSoundPlayClose(pThis, pStreamDS);
2238 if (FAILED(hr))
2239 return VERR_GENERAL_FAILURE; /** @todo Fix. */
2240 }
2241
2242 return VINF_SUCCESS;
2243}
2244
2245static int dsoundCreateStreamIn(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS,
2246 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
2247{
2248 LogFunc(("pStreamDS=%p, pCfgReq=%p, enmRecSource=%s\n",
2249 pStreamDS, pCfgReq, PDMAudioRecSrcGetName(pCfgReq->u.enmSrc)));
2250
2251
2252 /* Try to open capture in case the device is already there. */
2253 int rc;
2254 HRESULT hr = directSoundCaptureOpen(pThis, pStreamDS, pCfgReq, pCfgAcq);
2255 if (SUCCEEDED(hr))
2256 {
2257 rc = PDMAudioStrmCfgCopy(&pStreamDS->Cfg, pCfgAcq);
2258 if (RT_SUCCESS(rc))
2259 dsoundStreamReset(pThis, pStreamDS);
2260 }
2261 else
2262 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
2263
2264 return rc;
2265}
2266
2267static int dsoundControlStreamIn(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, PDMAUDIOSTREAMCMD enmStreamCmd)
2268{
2269 LogFlowFunc(("pStreamDS=%p, enmStreamCmd=%ld\n", pStreamDS, enmStreamCmd));
2270
2271 int rc = VINF_SUCCESS;
2272
2273 HRESULT hr;
2274 switch (enmStreamCmd)
2275 {
2276 case PDMAUDIOSTREAMCMD_ENABLE:
2277 dsoundStreamEnable(pThis, pStreamDS, true /* fEnable */);
2278 RT_FALL_THROUGH();
2279 case PDMAUDIOSTREAMCMD_RESUME:
2280 {
2281 /* Try to start capture. If it fails, then reopen and try again. */
2282 hr = directSoundCaptureStart(pThis, pStreamDS);
2283 if (FAILED(hr))
2284 {
2285 hr = directSoundCaptureClose(pThis, pStreamDS);
2286 if (SUCCEEDED(hr))
2287 {
2288 PDMAUDIOSTREAMCFG CfgAcq;
2289 hr = directSoundCaptureOpen(pThis, pStreamDS, &pStreamDS->Cfg /* pCfgReq */, &CfgAcq);
2290 if (SUCCEEDED(hr))
2291 {
2292 rc = PDMAudioStrmCfgCopy(&pStreamDS->Cfg, &CfgAcq);
2293 if (RT_FAILURE(rc))
2294 break;
2295
2296 /** @todo What to do if the format has changed? */
2297
2298 hr = directSoundCaptureStart(pThis, pStreamDS);
2299 }
2300 }
2301 }
2302
2303 if (FAILED(hr))
2304 rc = VERR_NOT_SUPPORTED;
2305 break;
2306 }
2307
2308 case PDMAUDIOSTREAMCMD_DISABLE:
2309 dsoundStreamEnable(pThis, pStreamDS, false /* fEnable */);
2310 RT_FALL_THROUGH();
2311 case PDMAUDIOSTREAMCMD_PAUSE:
2312 {
2313 directSoundCaptureStop(pThis, pStreamDS,
2314 enmStreamCmd == PDMAUDIOSTREAMCMD_DISABLE /* fFlush */);
2315
2316 /* Return success in any case, as stopping the capture can fail if
2317 * the capture buffer is not around anymore.
2318 *
2319 * This can happen if the host's capturing device has been changed suddenly. */
2320 rc = VINF_SUCCESS;
2321 break;
2322 }
2323
2324 default:
2325 rc = VERR_NOT_SUPPORTED;
2326 break;
2327 }
2328
2329 return rc;
2330}
2331
2332/**
2333 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
2334 */
2335static DECLCALLBACK(int) drvHostDSoundHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
2336 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
2337{
2338
2339 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2340 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2341 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2342 AssertReturn(uBufSize, VERR_INVALID_PARAMETER);
2343
2344 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2345 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2346
2347 int rc = VINF_SUCCESS;
2348
2349 uint32_t cbReadTotal = 0;
2350
2351 uint32_t cbToRead = RT_MIN((uint32_t)RTCircBufUsed(pStreamDS->pCircBuf), uBufSize);
2352 while (cbToRead)
2353 {
2354 void *pvChunk;
2355 size_t cbChunk;
2356 RTCircBufAcquireReadBlock(pStreamDS->pCircBuf, cbToRead, &pvChunk, &cbChunk);
2357
2358 if (cbChunk)
2359 {
2360 memcpy((uint8_t *)pvBuf + cbReadTotal, pvChunk, cbChunk);
2361 cbReadTotal += (uint32_t)cbChunk;
2362 Assert(cbToRead >= cbChunk);
2363 cbToRead -= (uint32_t)cbChunk;
2364 }
2365
2366 RTCircBufReleaseReadBlock(pStreamDS->pCircBuf, cbChunk);
2367 }
2368
2369 if (RT_SUCCESS(rc))
2370 {
2371 if (puRead)
2372 *puRead = cbReadTotal;
2373 }
2374 else
2375 dsoundUpdateStatusInternal(pThis);
2376
2377 return rc;
2378}
2379
2380static int dsoundDestroyStreamIn(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
2381{
2382 LogFlowFuncEnter();
2383
2384 directSoundCaptureClose(pThis, pStreamDS);
2385
2386 return VINF_SUCCESS;
2387}
2388
2389/**
2390 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
2391 */
2392static DECLCALLBACK(int) drvHostDSoundHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
2393{
2394 RT_NOREF(pInterface);
2395 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2396 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
2397
2398 RT_BZERO(pBackendCfg, sizeof(PPDMAUDIOBACKENDCFG));
2399
2400 pBackendCfg->cbStreamOut = sizeof(DSOUNDSTREAM);
2401 pBackendCfg->cbStreamIn = sizeof(DSOUNDSTREAM);
2402
2403 RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "DirectSound");
2404
2405 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
2406 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
2407
2408 return VINF_SUCCESS;
2409}
2410
2411/**
2412 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetDevices}
2413 */
2414static DECLCALLBACK(int) drvHostDSoundHA_GetDevices(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHOSTENUM pDeviceEnum)
2415{
2416 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2417 AssertPtrReturn(pDeviceEnum, VERR_INVALID_POINTER);
2418
2419 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2420
2421 int rc = RTCritSectEnter(&pThis->CritSect);
2422 if (RT_SUCCESS(rc))
2423 {
2424 PDMAudioHostEnumInit(pDeviceEnum);
2425 rc = dsoundDevicesEnumerate(pThis, pDeviceEnum);
2426 if (RT_FAILURE(rc))
2427 PDMAudioHostEnumDelete(pDeviceEnum);
2428
2429 int rc2 = RTCritSectLeave(&pThis->CritSect);
2430 AssertRC(rc2);
2431 }
2432
2433 LogFlowFunc(("Returning %Rrc\n", rc));
2434 return rc;
2435}
2436
2437/**
2438 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
2439 */
2440static DECLCALLBACK(void) drvHostDSoundHA_Shutdown(PPDMIHOSTAUDIO pInterface)
2441{
2442 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2443
2444 LogFlowFuncEnter();
2445
2446 RT_NOREF(pThis);
2447
2448 LogFlowFuncLeave();
2449}
2450
2451/**
2452 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
2453 */
2454static DECLCALLBACK(int) drvHostDSoundHA_Init(PPDMIHOSTAUDIO pInterface)
2455{
2456 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2457 LogFlowFuncEnter();
2458
2459 int rc;
2460
2461 /* Verify that IDirectSound is available. */
2462 LPDIRECTSOUND pDirectSound = NULL;
2463 HRESULT hr = CoCreateInstance(CLSID_DirectSound, NULL, CLSCTX_ALL, IID_IDirectSound, (void **)&pDirectSound);
2464 if (SUCCEEDED(hr))
2465 {
2466 IDirectSound_Release(pDirectSound);
2467
2468 rc = VINF_SUCCESS;
2469
2470 dsoundUpdateStatusInternal(pThis);
2471 }
2472 else
2473 {
2474 DSLOGREL(("DSound: DirectSound not available: %Rhrc\n", hr));
2475 rc = VERR_NOT_SUPPORTED;
2476 }
2477
2478 LogFlowFuncLeaveRC(rc);
2479 return rc;
2480}
2481
2482static LPCGUID dsoundConfigQueryGUID(PCFGMNODE pCfg, const char *pszName, RTUUID *pUuid)
2483{
2484 LPCGUID pGuid = NULL;
2485
2486 char *pszGuid = NULL;
2487 int rc = CFGMR3QueryStringAlloc(pCfg, pszName, &pszGuid);
2488 if (RT_SUCCESS(rc))
2489 {
2490 rc = RTUuidFromStr(pUuid, pszGuid);
2491 if (RT_SUCCESS(rc))
2492 pGuid = (LPCGUID)&pUuid;
2493 else
2494 DSLOGREL(("DSound: Error parsing device GUID for device '%s': %Rrc\n", pszName, rc));
2495
2496 RTStrFree(pszGuid);
2497 }
2498
2499 return pGuid;
2500}
2501
2502static int dsoundConfigInit(PDRVHOSTDSOUND pThis, PCFGMNODE pCfg)
2503{
2504 pThis->Cfg.pGuidPlay = dsoundConfigQueryGUID(pCfg, "DeviceGuidOut", &pThis->Cfg.uuidPlay);
2505 pThis->Cfg.pGuidCapture = dsoundConfigQueryGUID(pCfg, "DeviceGuidIn", &pThis->Cfg.uuidCapture);
2506
2507 DSLOG(("DSound: Configuration: DeviceGuidOut {%RTuuid}, DeviceGuidIn {%RTuuid}\n",
2508 &pThis->Cfg.uuidPlay,
2509 &pThis->Cfg.uuidCapture));
2510
2511 return VINF_SUCCESS;
2512}
2513
2514/**
2515 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
2516 */
2517static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostDSoundHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
2518{
2519 RT_NOREF(enmDir);
2520 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
2521
2522 return PDMAUDIOBACKENDSTS_RUNNING;
2523}
2524
2525/**
2526 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
2527 */
2528static DECLCALLBACK(int) drvHostDSoundHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
2529 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
2530{
2531 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2532 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2533 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
2534 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
2535
2536 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2537 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2538
2539 int rc;
2540 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
2541 rc = dsoundCreateStreamIn(pThis, pStreamDS, pCfgReq, pCfgAcq);
2542 else
2543 rc = dsoundCreateStreamOut(pThis, pStreamDS, pCfgReq, pCfgAcq);
2544
2545 if (RT_SUCCESS(rc))
2546 {
2547 /** @todo already copied */
2548 rc = PDMAudioStrmCfgCopy(&pStreamDS->Cfg, pCfgAcq);
2549 if (RT_SUCCESS(rc))
2550 rc = RTCritSectInit(&pStreamDS->CritSect);
2551 }
2552
2553 return rc;
2554}
2555
2556/**
2557 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
2558 */
2559static DECLCALLBACK(int) drvHostDSoundHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2560{
2561 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2562 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2563
2564 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2565 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2566
2567 int rc;
2568 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
2569 rc = dsoundDestroyStreamIn(pThis, pStreamDS);
2570 else
2571 rc = dsoundDestroyStreamOut(pThis, pStreamDS);
2572
2573 if (RT_SUCCESS(rc))
2574 {
2575 if (RTCritSectIsInitialized(&pStreamDS->CritSect))
2576 rc = RTCritSectDelete(&pStreamDS->CritSect);
2577 }
2578
2579 return rc;
2580}
2581
2582/**
2583 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
2584 */
2585static DECLCALLBACK(int) drvHostDSoundHA_StreamControl(PPDMIHOSTAUDIO pInterface,
2586 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
2587{
2588 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2589 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2590
2591 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2592 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2593
2594 int rc;
2595 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
2596 rc = dsoundControlStreamIn(pThis, pStreamDS, enmStreamCmd);
2597 else
2598 rc = dsoundControlStreamOut(pThis, pStreamDS, enmStreamCmd);
2599
2600 return rc;
2601}
2602
2603/**
2604 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
2605 */
2606static DECLCALLBACK(uint32_t) drvHostDSoundHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2607{
2608 RT_NOREF(pInterface);
2609 AssertPtrReturn(pStream, PDMAUDIOSTREAMSTS_FLAGS_NONE);
2610
2611 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2612
2613 if ( pStreamDS->fEnabled
2614 && pStreamDS->pCircBuf)
2615 {
2616 return (uint32_t)RTCircBufUsed(pStreamDS->pCircBuf);
2617 }
2618
2619 return 0;
2620}
2621
2622/**
2623 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
2624 */
2625static DECLCALLBACK(uint32_t) drvHostDSoundHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2626{
2627 AssertPtrReturn(pInterface, PDMAUDIOSTREAMSTS_FLAGS_NONE);
2628 AssertPtrReturn(pStream, PDMAUDIOSTREAMSTS_FLAGS_NONE);
2629
2630 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2631
2632 if (pStreamDS->fEnabled)
2633 return (uint32_t)RTCircBufFree(pStreamDS->pCircBuf);
2634
2635 return 0;
2636}
2637
2638/**
2639 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending}
2640 */
2641static DECLCALLBACK(uint32_t) drvHostDSoundHA_StreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2642{
2643 RT_NOREF(pInterface);
2644 AssertPtrReturn(pStream, 0);
2645
2646 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2647
2648 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT)
2649 {
2650 uint32_t cbPending = 0;
2651
2652 /* Any uncommitted data left? */
2653 if (pStreamDS->pCircBuf)
2654 cbPending = (uint32_t)RTCircBufUsed(pStreamDS->pCircBuf);
2655
2656 /* Check if we have committed data which still needs to be played by
2657 * by DirectSound's streaming buffer. */
2658 if (!cbPending)
2659 {
2660 const uint64_t diffLastTransferredMs = RTTimeMilliTS() - pStreamDS->Out.tsLastTransferredMs;
2661 const uint64_t uLastTranserredChunkMs = PDMAudioPropsBytesToMilli(&pStreamDS->Cfg.Props, pStreamDS->Out.cbLastTransferred);
2662 if ( uLastTranserredChunkMs
2663 && diffLastTransferredMs < uLastTranserredChunkMs)
2664 cbPending = 1;
2665
2666 Log3Func(("diffLastTransferredMs=%RU64ms, uLastTranserredChunkMs=%RU64ms (%RU32 bytes) -> cbPending=%RU32\n",
2667 diffLastTransferredMs, uLastTranserredChunkMs, pStreamDS->Out.cbLastTransferred, cbPending));
2668 }
2669 else
2670 Log3Func(("cbPending=%RU32\n", cbPending));
2671
2672 return cbPending;
2673 }
2674 /* Note: For input streams we never have pending data left. */
2675
2676 return 0;
2677}
2678
2679/**
2680 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
2681 */
2682static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostDSoundHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2683{
2684 RT_NOREF(pInterface);
2685 AssertPtrReturn(pStream, PDMAUDIOSTREAMSTS_FLAGS_NONE);
2686
2687 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2688
2689 PDMAUDIOSTREAMSTS fStrmStatus = PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED;
2690
2691 if (pStreamDS->fEnabled)
2692 fStrmStatus |= PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
2693
2694 return fStrmStatus;
2695}
2696
2697/**
2698 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
2699 */
2700static DECLCALLBACK(int) drvHostDSoundHA_StreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2701{
2702 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2703 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2704
2705 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2706 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2707
2708 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
2709 {
2710 return dsoundCaptureTransfer(pThis, pStreamDS);
2711 }
2712 else if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT)
2713 {
2714 return dsoundPlayTransfer(pThis, pStreamDS);
2715 }
2716
2717 return VINF_SUCCESS;
2718}
2719
2720
2721/*********************************************************************************************************************************
2722* PDMDRVINS::IBase Interface *
2723*********************************************************************************************************************************/
2724
2725/**
2726 * @callback_method_impl{PDMIBASE,pfnQueryInterface}
2727 */
2728static DECLCALLBACK(void *) drvHostDSoundQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2729{
2730 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2731 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
2732
2733 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
2734 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
2735 return NULL;
2736}
2737
2738
2739/*********************************************************************************************************************************
2740* PDMDRVREG Interface *
2741*********************************************************************************************************************************/
2742
2743/**
2744 * @callback_method_impl{FNPDMDRVDESTRUCT, pfnDestruct}
2745 */
2746static DECLCALLBACK(void) drvHostDSoundDestruct(PPDMDRVINS pDrvIns)
2747{
2748 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
2749 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2750
2751 LogFlowFuncEnter();
2752
2753#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
2754 if (pThis->m_pNotificationClient)
2755 {
2756 pThis->m_pNotificationClient->Unregister();
2757 pThis->m_pNotificationClient->Release();
2758
2759 pThis->m_pNotificationClient = NULL;
2760 }
2761#endif
2762
2763 PDMAudioHostEnumDelete(&pThis->DeviceEnum);
2764
2765 if (pThis->pDrvIns)
2766 CoUninitialize();
2767
2768 int rc2 = RTCritSectDelete(&pThis->CritSect);
2769 AssertRC(rc2);
2770
2771 LogFlowFuncLeave();
2772}
2773
2774/**
2775 * @callback_method_impl{FNPDMDRVCONSTRUCT,
2776 * Construct a DirectSound Audio driver instance.}
2777 */
2778static DECLCALLBACK(int) drvHostDSoundConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
2779{
2780 RT_NOREF(fFlags);
2781 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
2782
2783 LogRel(("Audio: Initializing DirectSound audio driver\n"));
2784
2785 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2786
2787 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
2788 if (FAILED(hr))
2789 {
2790 DSLOGREL(("DSound: CoInitializeEx failed with %Rhrc\n", hr));
2791 return VERR_NOT_SUPPORTED;
2792 }
2793
2794 /*
2795 * Init basic data members and interfaces.
2796 */
2797 pThis->pDrvIns = pDrvIns;
2798 /* IBase */
2799 pDrvIns->IBase.pfnQueryInterface = drvHostDSoundQueryInterface;
2800 /* IHostAudio */
2801 pThis->IHostAudio.pfnInit = drvHostDSoundHA_Init;
2802 pThis->IHostAudio.pfnShutdown = drvHostDSoundHA_Shutdown;
2803 pThis->IHostAudio.pfnGetConfig = drvHostDSoundHA_GetConfig;
2804 pThis->IHostAudio.pfnGetStatus = drvHostDSoundHA_GetStatus;
2805 pThis->IHostAudio.pfnStreamCreate = drvHostDSoundHA_StreamCreate;
2806 pThis->IHostAudio.pfnStreamDestroy = drvHostDSoundHA_StreamDestroy;
2807 pThis->IHostAudio.pfnStreamControl = drvHostDSoundHA_StreamControl;
2808 pThis->IHostAudio.pfnStreamGetReadable = drvHostDSoundHA_StreamGetReadable;
2809 pThis->IHostAudio.pfnStreamGetWritable = drvHostDSoundHA_StreamGetWritable;
2810 pThis->IHostAudio.pfnStreamGetStatus = drvHostDSoundHA_StreamGetStatus;
2811 pThis->IHostAudio.pfnStreamIterate = drvHostDSoundHA_StreamIterate;
2812 pThis->IHostAudio.pfnStreamPlay = drvHostDSoundHA_StreamPlay;
2813 pThis->IHostAudio.pfnStreamCapture = drvHostDSoundHA_StreamCapture;
2814 pThis->IHostAudio.pfnGetDevices = drvHostDSoundHA_GetDevices;
2815 pThis->IHostAudio.pfnStreamGetPending = drvHostDSoundHA_StreamGetPending;
2816 pThis->IHostAudio.pfnStreamPlayBegin = NULL;
2817 pThis->IHostAudio.pfnStreamPlayEnd = NULL;
2818 pThis->IHostAudio.pfnStreamCaptureBegin = NULL;
2819 pThis->IHostAudio.pfnStreamCaptureEnd = NULL;
2820
2821 /*
2822 * Init the static parts.
2823 */
2824 PDMAudioHostEnumInit(&pThis->DeviceEnum);
2825
2826 pThis->fEnabledIn = false;
2827 pThis->fEnabledOut = false;
2828
2829 int rc = VINF_SUCCESS;
2830
2831#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
2832 bool fUseNotificationClient = false;
2833
2834 char szOSVersion[32];
2835 rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szOSVersion, sizeof(szOSVersion));
2836 if (RT_SUCCESS(rc))
2837 {
2838 /* IMMNotificationClient is available starting at Windows Vista. */
2839 if (RTStrVersionCompare(szOSVersion, "6.0") >= 0)
2840 fUseNotificationClient = true;
2841 }
2842
2843 if (fUseNotificationClient)
2844 {
2845 /* Get the notification interface. */
2846# ifdef VBOX_WITH_AUDIO_CALLBACKS
2847 PPDMIAUDIONOTIFYFROMHOST pIAudioNotifyFromHost = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIONOTIFYFROMHOST);
2848 Assert(pIAudioNotifyFromHost);
2849# else
2850 PPDMIAUDIONOTIFYFROMHOST pIAudioNotifyFromHost = NULL;
2851# endif
2852
2853 try
2854 {
2855 pThis->m_pNotificationClient = new DrvHostAudioDSoundMMNotifClient(pIAudioNotifyFromHost);
2856
2857 hr = pThis->m_pNotificationClient->Initialize();
2858 if (SUCCEEDED(hr))
2859 hr = pThis->m_pNotificationClient->Register();
2860
2861 if (FAILED(hr))
2862 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
2863 }
2864 catch (std::bad_alloc &)
2865 {
2866 rc = VERR_NO_MEMORY;
2867 }
2868 }
2869
2870 LogRel2(("DSound: Notification client is %s\n", fUseNotificationClient ? "enabled" : "disabled"));
2871#endif
2872
2873 if (RT_SUCCESS(rc))
2874 {
2875 /*
2876 * Initialize configuration values.
2877 */
2878 rc = dsoundConfigInit(pThis, pCfg);
2879 if (RT_SUCCESS(rc))
2880 rc = RTCritSectInit(&pThis->CritSect);
2881 }
2882
2883 return rc;
2884}
2885
2886
2887/**
2888 * PDM driver registration.
2889 */
2890const PDMDRVREG g_DrvHostDSound =
2891{
2892 /* u32Version */
2893 PDM_DRVREG_VERSION,
2894 /* szName */
2895 "DSoundAudio",
2896 /* szRCMod */
2897 "",
2898 /* szR0Mod */
2899 "",
2900 /* pszDescription */
2901 "DirectSound Audio host driver",
2902 /* fFlags */
2903 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2904 /* fClass. */
2905 PDM_DRVREG_CLASS_AUDIO,
2906 /* cMaxInstances */
2907 ~0U,
2908 /* cbInstance */
2909 sizeof(DRVHOSTDSOUND),
2910 /* pfnConstruct */
2911 drvHostDSoundConstruct,
2912 /* pfnDestruct */
2913 drvHostDSoundDestruct,
2914 /* pfnRelocate */
2915 NULL,
2916 /* pfnIOCtl */
2917 NULL,
2918 /* pfnPowerOn */
2919 NULL,
2920 /* pfnReset */
2921 NULL,
2922 /* pfnSuspend */
2923 NULL,
2924 /* pfnResume */
2925 NULL,
2926 /* pfnAttach */
2927 NULL,
2928 /* pfnDetach */
2929 NULL,
2930 /* pfnPowerOff */
2931 NULL,
2932 /* pfnSoftReset */
2933 NULL,
2934 /* u32EndVersion */
2935 PDM_DRVREG_VERSION
2936};
Note: See TracBrowser for help on using the repository browser.

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