VirtualBox

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

Last change on this file since 70711 was 70705, checked in by vboxsync, 7 years ago

Audio/DrvHostDSound: Log enumerated devices only in verbose (and/or audio debug) mode.

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

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