VirtualBox

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

Last change on this file since 69111 was 68955, checked in by vboxsync, 7 years ago

Audio/DrvHostDSound.cpp: Don't destroy the existing DirectSound interface(s) when creating new audio streams; this might put existing streams on that interface in trouble without letting them know.

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