VirtualBox

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

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

DrvHostAudioDSound.cpp: Ripped out the extra ~600ms buffering. When asked to DRAIN the backend, try switch the buffer to non-looping mode so that it will stop when it's done (may need to clear the rest, not sure). Don't RESUME disabled streams - this worked because we used to fill the buffer with silence upon disabling a stream (no longer true with new draining code). bugref:9890

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