VirtualBox

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

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

Audio/DrvHostDSound.cpp: Logging.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 83.9 KB
Line 
1/* $Id: DrvHostDSound.cpp 68954 2017-10-02 15:06:55Z 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 directSoundPlayInterfaceDestroy(pThis);
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 directSoundCaptureInterfaceDestroy(pThis);
1125
1126 dsoundUpdateStatusInternalEx(pThis, NULL /* Cfg */, DSOUNDENUMCBFLAGS_LOG /* fEnum */);
1127
1128 HRESULT hr = directSoundCaptureInterfaceCreate(pThis, pCfgReq);
1129 if (FAILED(hr))
1130 return hr;
1131
1132 do /* To use breaks. */
1133 {
1134 LPDIRECTSOUNDCAPTUREBUFFER pDSCB = NULL;
1135
1136 DSCBUFFERDESC bd;
1137 RT_ZERO(bd);
1138
1139 bd.dwSize = sizeof(bd);
1140 bd.lpwfxFormat = &wfx;
1141 bd.dwBufferBytes = pThis->cfg.cbBufferIn;
1142
1143 hr = IDirectSoundCapture_CreateCaptureBuffer(pThis->pDSC, &bd, &pDSCB, NULL);
1144 if (FAILED(hr))
1145 {
1146 if (hr == E_ACCESSDENIED)
1147 {
1148 DSLOGREL(("DSound: Capturing input from host not possible, access denied\n"));
1149 }
1150 else
1151 DSLOGREL(("DSound: Creating capture buffer failed with %Rhrc\n", hr));
1152 break;
1153 }
1154
1155 hr = IDirectSoundCaptureBuffer_QueryInterface(pDSCB, IID_IDirectSoundCaptureBuffer8, (void **)&pStreamDS->In.pDSCB);
1156 IDirectSoundCaptureBuffer_Release(pDSCB);
1157 if (FAILED(hr))
1158 {
1159 DSLOGREL(("DSound: Querying interface for capture buffer failed with %Rhrc\n", hr));
1160 break;
1161 }
1162
1163 /*
1164 * Query the actual parameters.
1165 */
1166 DWORD offByteReadPos = 0;
1167 hr = IDirectSoundCaptureBuffer8_GetCurrentPosition(pStreamDS->In.pDSCB, NULL, &offByteReadPos);
1168 if (FAILED(hr))
1169 {
1170 offByteReadPos = 0;
1171 DSLOGREL(("DSound: Getting capture position failed with %Rhrc\n", hr));
1172 }
1173
1174 RT_ZERO(wfx);
1175 hr = IDirectSoundCaptureBuffer8_GetFormat(pStreamDS->In.pDSCB, &wfx, sizeof(wfx), NULL);
1176 if (FAILED(hr))
1177 {
1178 DSLOGREL(("DSound: Getting capture format failed with %Rhrc\n", hr));
1179 break;
1180 }
1181
1182 DSCBCAPS bc;
1183 RT_ZERO(bc);
1184 bc.dwSize = sizeof(bc);
1185 hr = IDirectSoundCaptureBuffer8_GetCaps(pStreamDS->In.pDSCB, &bc);
1186 if (FAILED(hr))
1187 {
1188 DSLOGREL(("Getting capture capabilities failed with %Rhrc\n", hr));
1189 break;
1190 }
1191
1192 DSLOG(("DSound: Capture format:\n"
1193 " dwBufferBytes = %RI32\n"
1194 " dwFlags = 0x%x\n"
1195 " wFormatTag = %RI16\n"
1196 " nChannels = %RI16\n"
1197 " nSamplesPerSec = %RU32\n"
1198 " nAvgBytesPerSec = %RU32\n"
1199 " nBlockAlign = %RI16\n"
1200 " wBitsPerSample = %RI16\n"
1201 " cbSize = %RI16\n",
1202 bc.dwBufferBytes,
1203 bc.dwFlags,
1204 wfx.wFormatTag,
1205 wfx.nChannels,
1206 wfx.nSamplesPerSec,
1207 wfx.nAvgBytesPerSec,
1208 wfx.nBlockAlign,
1209 wfx.wBitsPerSample,
1210 wfx.cbSize));
1211
1212 if (bc.dwBufferBytes & pStreamDS->uAlign)
1213 DSLOGREL(("DSound: Capture GetCaps returned misaligned buffer: size %RU32, alignment %RU32\n",
1214 bc.dwBufferBytes, pStreamDS->uAlign + 1));
1215
1216 if (bc.dwBufferBytes != pThis->cfg.cbBufferIn)
1217 DSLOGREL(("DSound: Capture buffer size mismatched: DirectSound %RU32, requested %RU32 bytes\n",
1218 bc.dwBufferBytes, pThis->cfg.cbBufferIn));
1219
1220 /* Initial state: reading at the initial capture position, no error. */
1221 pStreamDS->In.offReadPos = offByteReadPos;
1222 pStreamDS->In.cbBufSize = bc.dwBufferBytes;
1223
1224 pStreamDS->In.hrLastCapture = S_OK;
1225
1226 DSLOG(("DSound: Started capturing offReadPos=%RU32, cbBufSize=%RU32\n",
1227 pStreamDS->In.offReadPos, pStreamDS->In.cbBufSize));
1228
1229 pCfgAcq->cFrameBufferHint = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pThis->cfg.cbBufferIn);
1230
1231 } while (0);
1232
1233 if (FAILED(hr))
1234 directSoundCaptureClose(pStreamDS);
1235
1236 LogFlowFunc(("Returning %Rhrc\n", hr));
1237 return hr;
1238}
1239
1240
1241static HRESULT directSoundCaptureStop(PDSOUNDSTREAM pStreamDS)
1242{
1243 AssertPtrReturn(pStreamDS, E_POINTER);
1244
1245 HRESULT hr = S_OK;
1246
1247 if (pStreamDS->In.pDSCB)
1248 {
1249 if (pStreamDS->fEnabled)
1250 {
1251 DSLOG(("DSound: Stopping capture\n"));
1252
1253 hr = IDirectSoundCaptureBuffer_Stop(pStreamDS->In.pDSCB);
1254 if (SUCCEEDED(hr))
1255 pStreamDS->fEnabled = false;
1256 }
1257 }
1258
1259 if (FAILED(hr))
1260 DSLOGREL(("DSound: Stopping capture buffer failed with %Rhrc\n", hr));
1261
1262 return hr;
1263}
1264
1265
1266static HRESULT directSoundCaptureStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
1267{
1268 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1269 AssertPtrReturn(pStreamDS, VERR_INVALID_POINTER);
1270
1271 HRESULT hr;
1272 if (pStreamDS->In.pDSCB != NULL)
1273 {
1274 DWORD dwStatus;
1275 hr = IDirectSoundCaptureBuffer8_GetStatus(pStreamDS->In.pDSCB, &dwStatus);
1276 if (FAILED(hr))
1277 {
1278 DSLOGREL(("DSound: Retrieving capture status failed with %Rhrc\n", hr));
1279 }
1280 else
1281 {
1282 if (dwStatus & DSCBSTATUS_CAPTURING)
1283 {
1284 DSLOG(("DSound: Already capturing\n"));
1285 }
1286 else
1287 {
1288 DWORD fFlags = 0;
1289#ifndef VBOX_WITH_AUDIO_DEVICE_CALLBACKS
1290 fFlags |= DSCBSTART_LOOPING;
1291#endif
1292 DSLOG(("DSound: Starting to capture\n"));
1293 hr = IDirectSoundCaptureBuffer8_Start(pStreamDS->In.pDSCB, fFlags);
1294 if (SUCCEEDED(hr))
1295 {
1296 pStreamDS->fEnabled = true;
1297 }
1298 else
1299 DSLOGREL(("DSound: Starting to capture failed with %Rhrc\n", hr));
1300 }
1301 }
1302 }
1303 else
1304 hr = E_UNEXPECTED;
1305
1306 if (SUCCEEDED(hr))
1307
1308
1309 LogFlowFunc(("Returning %Rhrc\n", hr));
1310 return hr;
1311}
1312
1313
1314static int dsoundDevAdd(PRTLISTANCHOR pList, LPGUID pGUID, LPCWSTR pwszDescription, PDSOUNDDEV *ppDev)
1315{
1316 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1317 AssertPtrReturn(pGUID, VERR_INVALID_POINTER);
1318 AssertPtrReturn(pwszDescription, VERR_INVALID_POINTER);
1319
1320 PDSOUNDDEV pDev = (PDSOUNDDEV)RTMemAlloc(sizeof(DSOUNDDEV));
1321 if (!pDev)
1322 return VERR_NO_MEMORY;
1323
1324 int rc = RTUtf16ToUtf8(pwszDescription, &pDev->pszName);
1325 if ( RT_SUCCESS(rc)
1326 && pGUID)
1327 {
1328 memcpy(&pDev->Guid, pGUID, sizeof(GUID));
1329 }
1330
1331 if (RT_SUCCESS(rc))
1332 RTListAppend(pList, &pDev->Node);
1333
1334 if (ppDev)
1335 *ppDev = pDev;
1336
1337 return rc;
1338}
1339
1340
1341static void dsoundDeviceRemove(PDSOUNDDEV pDev)
1342{
1343 if (pDev)
1344 {
1345 if (pDev->pszName)
1346 {
1347 RTStrFree(pDev->pszName);
1348 pDev->pszName = NULL;
1349 }
1350
1351 RTListNodeRemove(&pDev->Node);
1352
1353 RTMemFree(pDev);
1354 pDev = NULL;
1355 }
1356}
1357
1358
1359static void dsoundLogDevice(const char *pszType, LPGUID pGUID, LPCWSTR pwszDescription, LPCWSTR pwszModule)
1360{
1361 char *pszGUID = dsoundGUIDToUtf8StrA(pGUID);
1362 /* This always has to be in the release log. */
1363 LogRel(("DSound: %s: GUID: %s [%ls] (Module: %ls)\n", pszType, pszGUID ? pszGUID : "{?}", pwszDescription, pwszModule));
1364 RTStrFree(pszGUID);
1365}
1366
1367
1368static BOOL CALLBACK dsoundDevicesEnumCbPlayback(LPGUID pGUID, LPCWSTR pwszDescription, LPCWSTR pwszModule, PVOID lpContext)
1369{
1370 PDSOUNDENUMCBCTX pCtx = (PDSOUNDENUMCBCTX)lpContext;
1371 AssertPtrReturn(pCtx, FALSE);
1372 AssertPtrReturn(pCtx->pDrv, FALSE);
1373
1374 if (!pGUID)
1375 return TRUE;
1376
1377 AssertPtrReturn(pwszDescription, FALSE);
1378 /* Do not care about pwszModule. */
1379
1380 if (pCtx->fFlags & DSOUNDENUMCBFLAGS_LOG)
1381 dsoundLogDevice("Output", pGUID, pwszDescription, pwszModule);
1382
1383 int rc = dsoundDevAdd(&pCtx->pDrv->lstDevOutput,
1384 pGUID, pwszDescription, NULL /* ppDev */);
1385 if (RT_FAILURE(rc))
1386 return FALSE; /* Abort enumeration. */
1387
1388 pCtx->cDevOut++;
1389
1390 return TRUE;
1391}
1392
1393
1394static BOOL CALLBACK dsoundDevicesEnumCbCapture(LPGUID pGUID, LPCWSTR pwszDescription, LPCWSTR pwszModule, PVOID lpContext)
1395{
1396 PDSOUNDENUMCBCTX pCtx = (PDSOUNDENUMCBCTX)lpContext;
1397 AssertPtrReturn(pCtx, FALSE);
1398 AssertPtrReturn(pCtx->pDrv, FALSE);
1399
1400 if (!pGUID)
1401 return TRUE;
1402
1403 if (pCtx->fFlags & DSOUNDENUMCBFLAGS_LOG)
1404 dsoundLogDevice("Input", pGUID, pwszDescription, pwszModule);
1405
1406 int rc = dsoundDevAdd(&pCtx->pDrv->lstDevInput,
1407 pGUID, pwszDescription, NULL /* ppDev */);
1408 if (RT_FAILURE(rc))
1409 return FALSE; /* Abort enumeration. */
1410
1411 pCtx->cDevIn++;
1412
1413 return TRUE;
1414}
1415
1416
1417/**
1418 * Does a (Re-)enumeration of the host's playback + capturing devices.
1419 *
1420 * @return IPRT status code.
1421 * @param pThis Host audio driver instance.
1422 * @param pEnmCtx Enumeration context to use.
1423 * @param fEnum Enumeration flags.
1424 */
1425static int dsoundDevicesEnumerate(PDRVHOSTDSOUND pThis, PDSOUNDENUMCBCTX pEnmCtx, uint32_t fEnum)
1426{
1427 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1428 AssertPtrReturn(pEnmCtx, VERR_INVALID_POINTER);
1429
1430 dsoundDevicesClear(pThis);
1431
1432 RTLDRMOD hDSound = NULL;
1433 int rc = RTLdrLoadSystem("dsound.dll", true /*fNoUnload*/, &hDSound);
1434 if (RT_SUCCESS(rc))
1435 {
1436 PFNDIRECTSOUNDENUMERATEW pfnDirectSoundEnumerateW = NULL;
1437 PFNDIRECTSOUNDCAPTUREENUMERATEW pfnDirectSoundCaptureEnumerateW = NULL;
1438
1439 rc = RTLdrGetSymbol(hDSound, "DirectSoundEnumerateW", (void**)&pfnDirectSoundEnumerateW);
1440 if (RT_SUCCESS(rc))
1441 rc = RTLdrGetSymbol(hDSound, "DirectSoundCaptureEnumerateW", (void**)&pfnDirectSoundCaptureEnumerateW);
1442
1443 if (RT_SUCCESS(rc))
1444 {
1445 HRESULT hr = pfnDirectSoundEnumerateW(&dsoundDevicesEnumCbPlayback, pEnmCtx);
1446 if (FAILED(hr))
1447 LogRel2(("DSound: Error enumerating host playback devices: %Rhrc\n", hr));
1448
1449 hr = pfnDirectSoundCaptureEnumerateW(&dsoundDevicesEnumCbCapture, pEnmCtx);
1450 if (FAILED(hr))
1451 LogRel2(("DSound: Error enumerating host capturing devices: %Rhrc\n", hr));
1452
1453 if (fEnum & DSOUNDENUMCBFLAGS_LOG)
1454 {
1455 LogRel2(("DSound: Found %RU8 host playback devices\n", pEnmCtx->cDevOut));
1456 LogRel2(("DSound: Found %RU8 host capturing devices\n", pEnmCtx->cDevIn));
1457 }
1458 }
1459
1460 RTLdrClose(hDSound);
1461 }
1462 else
1463 {
1464 /* No dsound.dll on this system. */
1465 LogRel2(("DSound: Could not load dsound.dll: %Rrc\n", rc));
1466 }
1467
1468 return rc;
1469}
1470
1471
1472/**
1473 * Updates this host driver's internal status, according to the global, overall input/output
1474 * state and all connected (native) audio streams.
1475 *
1476 * @param pThis Host audio driver instance.
1477 * @param pCfg Where to store the backend configuration. Optional.
1478 * @param fEnum Enumeration flags.
1479 */
1480static void dsoundUpdateStatusInternalEx(PDRVHOSTDSOUND pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum)
1481{
1482 AssertPtrReturnVoid(pThis);
1483 /* pCfg is optional. */
1484
1485 PDMAUDIOBACKENDCFG Cfg;
1486 RT_ZERO(Cfg);
1487
1488 Cfg.cbStreamOut = sizeof(DSOUNDSTREAM);
1489 Cfg.cbStreamIn = sizeof(DSOUNDSTREAM);
1490
1491 DSOUNDENUMCBCTX cbCtx = { pThis, fEnum, 0, 0 };
1492
1493 int rc = dsoundDevicesEnumerate(pThis, &cbCtx, fEnum);
1494 if (RT_SUCCESS(rc))
1495 {
1496#ifdef VBOX_WITH_AUDIO_DEVICE_CALLBACKS
1497 if ( pThis->fEnabledOut != RT_BOOL(cbCtx.cDevOut)
1498 || pThis->fEnabledIn != RT_BOOL(cbCtx.cDevIn))
1499 {
1500 /** @todo Use a registered callback to the audio connector (e.g "OnConfigurationChanged") to
1501 * let the connector know that something has changed within the host backend. */
1502 }
1503#else
1504 pThis->fEnabledOut = RT_BOOL(cbCtx.cDevOut);
1505 pThis->fEnabledIn = RT_BOOL(cbCtx.cDevIn);
1506#endif
1507
1508 Cfg.cMaxStreamsIn = UINT32_MAX;
1509 Cfg.cMaxStreamsOut = UINT32_MAX;
1510
1511 if (pCfg)
1512 memcpy(pCfg, &Cfg, sizeof(PDMAUDIOBACKENDCFG));
1513 }
1514
1515 LogFlowFuncLeaveRC(rc);
1516}
1517
1518
1519static void dsoundUpdateStatusInternal(PDRVHOSTDSOUND pThis)
1520{
1521 dsoundUpdateStatusInternalEx(pThis, NULL /* pCfg */, 0 /* fEnum */);
1522}
1523
1524
1525static int dsoundCreateStreamOut(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS,
1526 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1527{
1528 LogFlowFunc(("pStreamDS=%p, pCfgReq=%p\n", pStreamDS, pCfgReq));
1529
1530 pStreamDS->Out.pDSB = NULL;
1531 pStreamDS->Out.offWritePos = 0;
1532 pStreamDS->Out.offPlayCursorLastPlayed = 0;
1533 pStreamDS->Out.offPlayCursorLastPending = 0;
1534 pStreamDS->Out.cbWritten = 0;
1535 pStreamDS->Out.fRestartPlayback = true;
1536 pStreamDS->Out.cbBufSize = 0;
1537
1538 int rc = VINF_SUCCESS;
1539
1540 /* Try to open playback in case the device is already there. */
1541 HRESULT hr = directSoundPlayOpen(pThis, pStreamDS, pCfgReq, pCfgAcq);
1542 if (FAILED(hr))
1543 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1544
1545 LogFlowFuncLeaveRC(rc);
1546 return rc;
1547}
1548
1549static int dsoundControlStreamOut(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, PDMAUDIOSTREAMCMD enmStreamCmd)
1550{
1551 LogFlowFunc(("pStreamDS=%p, cmd=%d\n", pStreamDS, enmStreamCmd));
1552
1553 int rc = VINF_SUCCESS;
1554
1555 HRESULT hr;
1556 switch (enmStreamCmd)
1557 {
1558 case PDMAUDIOSTREAMCMD_ENABLE:
1559 case PDMAUDIOSTREAMCMD_RESUME:
1560 {
1561 hr = directSoundPlayStart(pThis, pStreamDS);
1562 if (FAILED(hr))
1563 {
1564 hr = directSoundPlayClose(pThis, pStreamDS);
1565 if (SUCCEEDED(hr))
1566 {
1567 PDMAUDIOSTREAMCFG CfgAcq;
1568 hr = directSoundPlayOpen(pThis, pStreamDS, pStreamDS->pCfg /* pCfqReq */, &CfgAcq);
1569 if (SUCCEEDED(hr))
1570 {
1571 DrvAudioHlpStreamCfgFree(pStreamDS->pCfg);
1572
1573 pStreamDS->pCfg = DrvAudioHlpStreamCfgDup(&CfgAcq);
1574 AssertPtr(pStreamDS->pCfg);
1575
1576 /** @todo What to do if the format has changed? */
1577 }
1578 }
1579 if (SUCCEEDED(hr))
1580 hr = directSoundPlayStart(pThis, pStreamDS);
1581 }
1582
1583 if (FAILED(hr))
1584 rc = VERR_NOT_SUPPORTED;
1585 break;
1586 }
1587
1588 case PDMAUDIOSTREAMCMD_DISABLE:
1589 case PDMAUDIOSTREAMCMD_PAUSE:
1590 {
1591 AssertPtr(pThis->pDS);
1592
1593 hr = directSoundPlayStop(pThis, pStreamDS);
1594 if (FAILED(hr))
1595 rc = VERR_NOT_SUPPORTED;
1596 break;
1597 }
1598
1599 default:
1600 {
1601 AssertMsgFailed(("Invalid command: %ld\n", enmStreamCmd));
1602 rc = VERR_INVALID_PARAMETER;
1603 break;
1604 }
1605 }
1606
1607 LogFlowFuncLeaveRC(rc);
1608 return rc;
1609}
1610
1611
1612/**
1613 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
1614 */
1615int drvHostDSoundStreamPlay(PPDMIHOSTAUDIO pInterface,
1616 PPDMAUDIOBACKENDSTREAM pStream, const void *pvBuf, uint32_t cxBuf, uint32_t *pcxWritten)
1617{
1618 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1619 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1620 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1621 AssertReturn(cxBuf, VERR_INVALID_PARAMETER);
1622 /* pcxWritten is optional. */
1623
1624 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1625 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
1626
1627 int rc = VINF_SUCCESS;
1628
1629 uint32_t cbWrittenTotal = 0;
1630
1631#ifdef DEBUG_andy
1632 LogFlowFuncEnter();
1633#endif
1634
1635 do /* to use 'break' */
1636 {
1637 AssertPtr(pStreamDS->pCfg);
1638 PPDMAUDIOPCMPROPS pProps = &pStreamDS->pCfg->Props;
1639
1640 DWORD cbFree;
1641 rc = dsoundGetFreeOut(pThis, pStreamDS, &cbFree);
1642 if (RT_FAILURE(rc))
1643 break;
1644
1645 if (pStreamDS->Out.fRestartPlayback == false)
1646 {
1647 DWORD offPlayCursor, offWriteCursor;
1648 HRESULT hr = IDirectSoundBuffer8_GetCurrentPosition(pStreamDS->Out.pDSB, &offPlayCursor, &offWriteCursor);
1649 if (SUCCEEDED(hr))
1650 {
1651 uint32_t cbPending;
1652 if (pStreamDS->Out.offPlayCursorLastPlayed <= offPlayCursor)
1653 cbPending = offPlayCursor - pStreamDS->Out.offPlayCursorLastPlayed;
1654 else
1655 cbPending = pStreamDS->Out.cbBufSize - pStreamDS->Out.offPlayCursorLastPlayed + offPlayCursor;
1656
1657 pStreamDS->Out.cbWritten -= RT_MIN(pStreamDS->Out.cbWritten, cbPending);
1658 pStreamDS->Out.offPlayCursorLastPlayed = offPlayCursor;
1659 }
1660 }
1661
1662 /*
1663 * Check for full buffer, do not allow the offPlayWritePos to catch cbPlayPos during playback,
1664 * i.e. always leave a free space for 1 audio sample.
1665 */
1666 const DWORD cbSample = PDMAUDIOPCMPROPS_F2B(pProps, 1);
1667 if (cbFree < cbSample)
1668 break;
1669 Assert(cbFree >= cbSample);
1670 cbFree -= cbSample;
1671
1672 uint32_t cbLive = cxBuf;
1673
1674 /* Do not write more than available space in the DirectSound playback buffer. */
1675 cbLive = RT_MIN(cbFree, cbLive);
1676 cbLive &= ~pStreamDS->uAlign;
1677
1678 if (!cbLive)
1679 break;
1680
1681 LPDIRECTSOUNDBUFFER8 pDSB = pStreamDS->Out.pDSB;
1682 AssertPtr(pDSB);
1683
1684 PVOID pv1, pv2;
1685 DWORD cb1, cb2;
1686 HRESULT hr = directSoundPlayLock(pThis, pStreamDS, pStreamDS->Out.offWritePos, cbLive,
1687 &pv1, &pv2, &cb1, &cb2, 0 /* dwFlags */);
1688 if (FAILED(hr))
1689 {
1690 rc = VERR_ACCESS_DENIED;
1691 break;
1692 }
1693
1694 AssertPtr(pv1);
1695 Assert(cb1);
1696
1697 memcpy(pv1, pvBuf, cb1);
1698 cbWrittenTotal = cb1;
1699
1700 if (pv2 && cb2) /* Buffer wrap-around? Write second part. */
1701 {
1702 memcpy(pv2, (uint8_t *)pvBuf + cb1, cb2);
1703 cbWrittenTotal += cb2;
1704 }
1705
1706 Assert(cbLive == cb1 + cb2);
1707
1708 directSoundPlayUnlock(pThis, pDSB, pv1, pv2, cb1, cb2);
1709
1710 pStreamDS->Out.offWritePos = (pStreamDS->Out.offWritePos + cbWrittenTotal) % pStreamDS->Out.cbBufSize;
1711 pStreamDS->Out.cbWritten += cbWrittenTotal;
1712
1713 DSLOGF(("DSound: %RU32/%RU32, buffer write pos %ld, rc=%Rrc\n",
1714 cbWrittenTotal, cbLive, pStreamDS->Out.offWritePos, rc));
1715
1716 if (pStreamDS->Out.fRestartPlayback)
1717 {
1718 /*
1719 * The playback has been just started.
1720 * Some samples of the new sound have been copied to the buffer
1721 * and it can start playing.
1722 */
1723 pStreamDS->Out.fRestartPlayback = false;
1724
1725 DWORD fFlags = 0;
1726#ifndef VBOX_WITH_AUDIO_DEVICE_CALLBACKS
1727 fFlags |= DSCBSTART_LOOPING;
1728#endif
1729 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
1730 {
1731 hr = IDirectSoundBuffer8_Play(pStreamDS->Out.pDSB, 0, 0, fFlags);
1732 if ( SUCCEEDED(hr)
1733 || hr != DSERR_BUFFERLOST)
1734 break;
1735 else
1736 {
1737 LogFlowFunc(("Restarting playback failed due to lost buffer, restoring ...\n"));
1738 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);
1739 }
1740 }
1741
1742 if (FAILED(hr))
1743 {
1744 DSLOGREL(("DSound: Starting playback failed with %Rhrc\n", hr));
1745 rc = VERR_NOT_SUPPORTED;
1746 break;
1747 }
1748 }
1749
1750 } while (0);
1751
1752 if (RT_SUCCESS(rc))
1753 {
1754 if (pcxWritten)
1755 *pcxWritten = cbWrittenTotal;
1756 }
1757 else
1758 dsoundUpdateStatusInternal(pThis);
1759
1760 return rc;
1761}
1762
1763
1764static int dsoundDestroyStreamOut(PDRVHOSTDSOUND pThis, PPDMAUDIOBACKENDSTREAM pStream)
1765{
1766 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
1767
1768 directSoundPlayClose(pThis, pStreamDS);
1769
1770 return VINF_SUCCESS;
1771}
1772
1773static int dsoundCreateStreamIn(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS,
1774 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1775{
1776 LogFunc(("pStreamDS=%p, pCfgReq=%p, enmRecSource=%s\n",
1777 pStreamDS, pCfgReq, DrvAudioHlpRecSrcToStr(pCfgReq->DestSource.Source)));
1778
1779 /* Init the stream structure and save relevant information to it. */
1780 pStreamDS->In.offReadPos = 0;
1781 pStreamDS->In.cbBufSize = 0;
1782 pStreamDS->In.pDSCB = NULL;
1783 pStreamDS->In.hrLastCapture = S_OK;
1784
1785 int rc = VINF_SUCCESS;
1786
1787 /* Try to open capture in case the device is already there. */
1788 HRESULT hr = directSoundCaptureOpen(pThis, pStreamDS, pCfgReq, pCfgAcq);
1789 if (FAILED(hr))
1790 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1791
1792 return rc;
1793}
1794
1795static int dsoundControlStreamIn(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, PDMAUDIOSTREAMCMD enmStreamCmd)
1796{
1797 LogFlowFunc(("pStreamDS=%p, enmStreamCmd=%ld\n", pStreamDS, enmStreamCmd));
1798
1799 int rc = VINF_SUCCESS;
1800
1801 HRESULT hr;
1802 switch (enmStreamCmd)
1803 {
1804 case PDMAUDIOSTREAMCMD_ENABLE:
1805 case PDMAUDIOSTREAMCMD_RESUME:
1806 {
1807 /* Try to start capture. If it fails, then reopen and try again. */
1808 hr = directSoundCaptureStart(pThis, pStreamDS);
1809 if (FAILED(hr))
1810 {
1811 hr = directSoundCaptureClose(pStreamDS);
1812 if (SUCCEEDED(hr))
1813 {
1814 PDMAUDIOSTREAMCFG CfgAcq;
1815 hr = directSoundCaptureOpen(pThis, pStreamDS, pStreamDS->pCfg /* pCfgReq */, &CfgAcq);
1816 if (SUCCEEDED(hr))
1817 {
1818 DrvAudioHlpStreamCfgFree(pStreamDS->pCfg);
1819
1820 pStreamDS->pCfg = DrvAudioHlpStreamCfgDup(&CfgAcq);
1821 AssertPtr(pStreamDS->pCfg);
1822
1823 /** @todo What to do if the format has changed? */
1824
1825 hr = directSoundCaptureStart(pThis, pStreamDS);
1826 }
1827 }
1828 }
1829
1830 if (FAILED(hr))
1831 rc = VERR_NOT_SUPPORTED;
1832 break;
1833 }
1834
1835 case PDMAUDIOSTREAMCMD_DISABLE:
1836 case PDMAUDIOSTREAMCMD_PAUSE:
1837 {
1838 AssertPtr(pThis->pDSC);
1839
1840 directSoundCaptureStop(pStreamDS);
1841
1842 /* Return success in any case, as stopping the capture can fail if
1843 * the capture buffer is not around anymore.
1844 *
1845 * This can happen if the host's capturing device has been changed suddenly. */
1846 rc = VINF_SUCCESS;
1847 break;
1848 }
1849
1850 default:
1851 {
1852 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
1853 rc = VERR_INVALID_PARAMETER;
1854 break;
1855 }
1856 }
1857
1858 return rc;
1859}
1860
1861
1862/**
1863 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
1864 */
1865int drvHostDSoundStreamCapture(PPDMIHOSTAUDIO pInterface,
1866 PPDMAUDIOBACKENDSTREAM pStream, void *pvBuf, uint32_t cxBuf, uint32_t *pcxRead)
1867{
1868
1869 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1870 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1871 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1872 AssertReturn(cxBuf, VERR_INVALID_PARAMETER);
1873
1874 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1875 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
1876
1877 LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB = pStreamDS->In.pDSCB;
1878 AssertPtr(pDSCB);
1879
1880 int rc = VINF_SUCCESS;
1881
1882 uint32_t cbReadTotal = 0;
1883
1884 do
1885 {
1886 if (pDSCB == NULL)
1887 {
1888 rc = VERR_NOT_AVAILABLE;
1889 break;
1890 }
1891
1892 /* Get DirectSound capture position in bytes. */
1893 DWORD offCurPos;
1894 HRESULT hr = IDirectSoundCaptureBuffer_GetCurrentPosition(pDSCB, NULL, &offCurPos);
1895 if (FAILED(hr))
1896 {
1897 if (hr != pStreamDS->In.hrLastCapture)
1898 {
1899 DSLOGREL(("DSound: Getting capture position failed with %Rhrc\n", hr));
1900 pStreamDS->In.hrLastCapture = hr;
1901 }
1902
1903 rc = VERR_NOT_AVAILABLE;
1904 break;
1905 }
1906
1907 pStreamDS->In.hrLastCapture = hr;
1908
1909 if (offCurPos & pStreamDS->uAlign)
1910 DSLOGF(("DSound: Misaligned capture read position %ld (alignment: %RU32)\n",
1911 offCurPos, pStreamDS->uAlign + 1));
1912
1913 /* Number of samples available in the DirectSound capture buffer. */
1914 DWORD cbToCapture = dsoundRingDistance(offCurPos, pStreamDS->In.offReadPos, pStreamDS->In.cbBufSize);
1915 if (cbToCapture == 0)
1916 break;
1917
1918 if (cxBuf == 0)
1919 {
1920 DSLOGF(("DSound: Capture buffer full\n"));
1921 break;
1922 }
1923
1924 DSLOGF(("DSound: Capture cxBuf=%RU32, offCurPos=%ld, offReadPos=%ld, cbToCapture=%ld\n",
1925 cxBuf, offCurPos, pStreamDS->In.offReadPos, cbToCapture));
1926
1927 /* No need to fetch more samples than mix buffer can receive. */
1928 cbToCapture = RT_MIN(cbToCapture, cxBuf);
1929
1930 /* Lock relevant range in the DirectSound capture buffer. */
1931 PVOID pv1, pv2;
1932 DWORD cb1, cb2;
1933 hr = directSoundCaptureLock(pStreamDS,
1934 pStreamDS->In.offReadPos, /* dwOffset */
1935 cbToCapture, /* dwBytes */
1936 &pv1, &pv2, &cb1, &cb2,
1937 0 /* dwFlags */);
1938 if (FAILED(hr))
1939 {
1940 rc = VERR_ACCESS_DENIED;
1941 break;
1942 }
1943
1944 if (pv1 && cb1)
1945 {
1946 memcpy((uint8_t *)pvBuf + cbReadTotal, pv1, cb1);
1947 cbReadTotal += cb1;
1948 }
1949
1950 if (pv2 && cb2)
1951 {
1952 memcpy((uint8_t *)pvBuf + cbReadTotal, pv2, cb2);
1953 cbReadTotal += cb2;
1954 }
1955
1956 directSoundCaptureUnlock(pDSCB, pv1, pv2, cb1, cb2);
1957
1958 if (RT_SUCCESS(rc))
1959 {
1960 pStreamDS->In.offReadPos = (pStreamDS->In.offReadPos + cbReadTotal)
1961 % pStreamDS->In.cbBufSize;
1962 DSLOGF(("DSound: Captured %ld bytes (%RU32 total)\n", cbToCapture, cbReadTotal));
1963 }
1964
1965 } while (0);
1966
1967 if (RT_SUCCESS(rc))
1968 {
1969 if (pcxRead)
1970 *pcxRead = cbReadTotal;
1971 }
1972 else
1973 dsoundUpdateStatusInternal(pThis);
1974
1975 return rc;
1976}
1977
1978static int dsoundDestroyStreamIn(PDSOUNDSTREAM pStreamDS)
1979{
1980 directSoundCaptureClose(pStreamDS);
1981
1982 return VINF_SUCCESS;
1983}
1984
1985
1986/**
1987 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
1988 */
1989int drvHostDSoundGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
1990{
1991 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1992 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
1993
1994 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1995
1996 dsoundUpdateStatusInternalEx(pThis, pBackendCfg, 0 /* fEnum */);
1997
1998 return VINF_SUCCESS;
1999}
2000
2001#ifdef VBOX_WITH_AUDIO_DEVICE_CALLBACKS
2002
2003static int dsoundNotifyThread(PDRVHOSTDSOUND pThis, bool fShutdown)
2004{
2005 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2006
2007 if (fShutdown)
2008 {
2009 LogFlowFunc(("Shutting down thread ...\n"));
2010 pThis->fShutdown = fShutdown;
2011 }
2012
2013 /* Set the notification event so that the thread is being notified. */
2014 BOOL fRc = SetEvent(pThis->aEvents[DSOUNDEVENT_NOTIFY]);
2015 Assert(fRc);
2016
2017 return VINF_SUCCESS;
2018}
2019
2020
2021static DECLCALLBACK(int) dsoundNotificationThread(RTTHREAD hThreadSelf, void *pvUser)
2022{
2023 PDRVHOSTDSOUND pThis = (PDRVHOSTDSOUND)pvUser;
2024 AssertPtr(pThis);
2025
2026 LogFlowFuncEnter();
2027
2028 /* Let caller know that we're done initializing, regardless of the result. */
2029 int rc = RTThreadUserSignal(hThreadSelf);
2030 AssertRC(rc);
2031
2032 do
2033 {
2034 HANDLE aEvents[VBOX_DSOUND_MAX_EVENTS];
2035 DWORD cEvents = 0;
2036 for (uint8_t i = 0; i < VBOX_DSOUND_MAX_EVENTS; i++)
2037 {
2038 if (pThis->aEvents[i])
2039 aEvents[cEvents++] = pThis->aEvents[i];
2040 }
2041 Assert(cEvents);
2042
2043 LogFlowFunc(("Waiting: cEvents=%ld\n", cEvents));
2044
2045 DWORD dwObj = WaitForMultipleObjects(cEvents, aEvents, FALSE /* bWaitAll */, INFINITE);
2046 switch (dwObj)
2047 {
2048 case WAIT_FAILED:
2049 {
2050 rc = VERR_CANCELLED;
2051 break;
2052 }
2053
2054 case WAIT_TIMEOUT:
2055 {
2056 rc = VERR_TIMEOUT;
2057 break;
2058 }
2059
2060 default:
2061 {
2062 dwObj = WAIT_OBJECT_0 + cEvents - 1;
2063 if (aEvents[dwObj] == pThis->aEvents[DSOUNDEVENT_NOTIFY])
2064 {
2065 LogFlowFunc(("Notify\n"));
2066 }
2067 else if (aEvents[dwObj] == pThis->aEvents[DSOUNDEVENT_INPUT])
2068 {
2069
2070 }
2071 else if (aEvents[dwObj] == pThis->aEvents[DSOUNDEVENT_OUTPUT])
2072 {
2073 DWORD cbFree;
2074 rc = dsoundGetFreeOut(pThis->pDSStream, &cbFree);
2075 if ( RT_SUCCESS(rc)
2076 && cbFree)
2077 {
2078 PDMAUDIOCBDATA_DATA_OUTPUT Out;
2079 Out.cbInFree = cbFree;
2080 Out.cbOutWritten = 0;
2081
2082 while (!Out.cbOutWritten)
2083 {
2084 rc = pThis->pUpIAudioConnector->pfnCallback(pThis->pUpIAudioConnector,
2085 PDMAUDIOCALLBACKTYPE_OUTPUT, &Out, sizeof(Out));
2086 if (RT_FAILURE(rc))
2087 break;
2088 RTThreadSleep(100);
2089 }
2090
2091 LogFlowFunc(("Output: cbBuffer=%ld, cbFree=%ld, cbWritten=%RU32, rc=%Rrc\n",
2092 cbBuffer, cbFree, Out.cbOutWritten, rc));
2093 }
2094 }
2095 break;
2096 }
2097 }
2098
2099 if (pThis->fShutdown)
2100 break;
2101
2102 } while (RT_SUCCESS(rc));
2103
2104 pThis->fStopped = true;
2105
2106 LogFlowFunc(("Exited with fShutdown=%RTbool, rc=%Rrc\n", pThis->fShutdown, rc));
2107 return rc;
2108}
2109
2110#endif /* VBOX_WITH_AUDIO_DEVICE_CALLBACKS */
2111
2112
2113/**
2114 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
2115 */
2116void drvHostDSoundShutdown(PPDMIHOSTAUDIO pInterface)
2117{
2118 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2119
2120 LogFlowFuncEnter();
2121
2122#ifdef VBOX_WITH_AUDIO_DEVICE_CALLBACKS
2123 int rc = dsoundNotifyThread(pThis, true /* fShutdown */);
2124 AssertRC(rc);
2125
2126 int rcThread;
2127 rc = RTThreadWait(pThis->Thread, 15 * 1000 /* 15s timeout */, &rcThread);
2128 LogFlowFunc(("rc=%Rrc, rcThread=%Rrc\n", rc, rcThread));
2129
2130 Assert(pThis->fStopped);
2131
2132 if (pThis->aEvents[DSOUNDEVENT_NOTIFY])
2133 {
2134 CloseHandle(pThis->aEvents[DSOUNDEVENT_NOTIFY]);
2135 pThis->aEvents[DSOUNDEVENT_NOTIFY] = NULL;
2136 }
2137#else
2138 RT_NOREF_PV(pThis);
2139#endif
2140
2141 LogFlowFuncLeave();
2142}
2143
2144
2145/**
2146 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
2147 */
2148static DECLCALLBACK(int) drvHostDSoundInit(PPDMIHOSTAUDIO pInterface)
2149{
2150 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2151 LogFlowFuncEnter();
2152
2153 int rc;
2154
2155 /* Verify that IDirectSound is available. */
2156 LPDIRECTSOUND pDirectSound = NULL;
2157 HRESULT hr = CoCreateInstance(CLSID_DirectSound, NULL, CLSCTX_ALL, IID_IDirectSound, (void **)&pDirectSound);
2158 if (SUCCEEDED(hr))
2159 {
2160 IDirectSound_Release(pDirectSound);
2161
2162#ifdef VBOX_WITH_AUDIO_DEVICE_CALLBACKS
2163 /* Create notification event. */
2164 pThis->aEvents[DSOUNDEVENT_NOTIFY] = CreateEvent(NULL /* Security attribute */,
2165 FALSE /* bManualReset */, FALSE /* bInitialState */,
2166 NULL /* lpName */);
2167 Assert(pThis->aEvents[DSOUNDEVENT_NOTIFY] != NULL);
2168
2169 /* Start notification thread. */
2170 rc = RTThreadCreate(&pThis->Thread, dsoundNotificationThread,
2171 pThis /*pvUser*/, 0 /*cbStack*/,
2172 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "dsoundNtfy");
2173 if (RT_SUCCESS(rc))
2174 {
2175 /* Wait for the thread to initialize. */
2176 rc = RTThreadUserWait(pThis->Thread, RT_MS_1MIN);
2177 if (RT_FAILURE(rc))
2178 DSLOGREL(("DSound: Waiting for thread to initialize failed with rc=%Rrc\n", rc));
2179 }
2180 else
2181 DSLOGREL(("DSound: Creating thread failed with rc=%Rrc\n", rc));
2182#else
2183 rc = VINF_SUCCESS;
2184#endif
2185
2186 dsoundUpdateStatusInternalEx(pThis, NULL /* pCfg */, DSOUNDENUMCBFLAGS_LOG /* fEnum */);
2187 }
2188 else
2189 {
2190 DSLOGREL(("DSound: DirectSound not available: %Rhrc\n", hr));
2191 rc = VERR_NOT_SUPPORTED;
2192 }
2193
2194 LogFlowFuncLeaveRC(rc);
2195 return rc;
2196}
2197
2198
2199static LPCGUID dsoundConfigQueryGUID(PCFGMNODE pCfg, const char *pszName, RTUUID *pUuid)
2200{
2201 LPCGUID pGuid = NULL;
2202
2203 char *pszGuid = NULL;
2204 int rc = CFGMR3QueryStringAlloc(pCfg, pszName, &pszGuid);
2205 if (RT_SUCCESS(rc))
2206 {
2207 rc = RTUuidFromStr(pUuid, pszGuid);
2208 if (RT_SUCCESS(rc))
2209 pGuid = (LPCGUID)&pUuid;
2210 else
2211 DSLOGREL(("DSound: Error parsing device GUID for device '%s': %Rrc\n", pszName, rc));
2212
2213 RTStrFree(pszGuid);
2214 }
2215
2216 return pGuid;
2217}
2218
2219
2220static int dsoundConfigInit(PDRVHOSTDSOUND pThis, PCFGMNODE pCfg)
2221{
2222 unsigned int uBufsizeOut, uBufsizeIn;
2223
2224 CFGMR3QueryUIntDef(pCfg, "BufsizeOut", &uBufsizeOut, _16K);
2225 CFGMR3QueryUIntDef(pCfg, "BufsizeIn", &uBufsizeIn, _16K);
2226 pThis->cfg.cbBufferOut = uBufsizeOut;
2227 pThis->cfg.cbBufferIn = uBufsizeIn;
2228
2229 pThis->cfg.pGuidPlay = dsoundConfigQueryGUID(pCfg, "DeviceGuidOut", &pThis->cfg.uuidPlay);
2230 pThis->cfg.pGuidCapture = dsoundConfigQueryGUID(pCfg, "DeviceGuidIn", &pThis->cfg.uuidCapture);
2231
2232 DSLOG(("DSound: Configuration cbBufferIn=%ld, cbBufferOut=%ld, DeviceGuidOut {%RTuuid}, DeviceGuidIn {%RTuuid}\n",
2233 pThis->cfg.cbBufferIn,
2234 pThis->cfg.cbBufferOut,
2235 &pThis->cfg.uuidPlay,
2236 &pThis->cfg.uuidCapture));
2237
2238 return VINF_SUCCESS;
2239}
2240
2241
2242/**
2243 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
2244 */
2245static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostDSoundGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
2246{
2247 RT_NOREF(enmDir);
2248 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
2249
2250 return PDMAUDIOBACKENDSTS_RUNNING;
2251}
2252
2253
2254/**
2255 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
2256 */
2257static DECLCALLBACK(int) drvHostDSoundStreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
2258 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
2259{
2260 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2261 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2262 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
2263 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
2264
2265 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2266 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2267
2268 int rc;
2269 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
2270 rc = dsoundCreateStreamIn(pThis, pStreamDS, pCfgReq, pCfgAcq);
2271 else
2272 rc = dsoundCreateStreamOut(pThis, pStreamDS, pCfgReq, pCfgAcq);
2273
2274 if (RT_SUCCESS(rc))
2275 {
2276 pStreamDS->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq);
2277 if (!pStreamDS->pCfg)
2278 rc = VERR_NO_MEMORY;
2279 }
2280
2281 return rc;
2282}
2283
2284
2285/**
2286 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
2287 */
2288static DECLCALLBACK(int) drvHostDSoundStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2289{
2290 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2291 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2292
2293 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2294 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2295
2296 if (!pStreamDS->pCfg) /* Not (yet) configured? Skip. */
2297 return VINF_SUCCESS;
2298
2299 int rc;
2300 if (pStreamDS->pCfg->enmDir == PDMAUDIODIR_IN)
2301 rc = dsoundDestroyStreamIn(pStreamDS);
2302 else
2303 rc = dsoundDestroyStreamOut(pThis, pStreamDS);
2304
2305 if (RT_SUCCESS(rc))
2306 {
2307 DrvAudioHlpStreamCfgFree(pStreamDS->pCfg);
2308 pStreamDS->pCfg = NULL;
2309 }
2310
2311 return rc;
2312}
2313
2314
2315/**
2316 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
2317 */
2318static DECLCALLBACK(int) drvHostDSoundStreamControl(PPDMIHOSTAUDIO pInterface,
2319 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
2320{
2321 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2322 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2323
2324 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2325 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2326
2327 if (!pStreamDS->pCfg) /* Not (yet) configured? Skip. */
2328 return VINF_SUCCESS;
2329
2330 int rc;
2331 if (pStreamDS->pCfg->enmDir == PDMAUDIODIR_IN)
2332 rc = dsoundControlStreamIn(pThis, pStreamDS, enmStreamCmd);
2333 else
2334 rc = dsoundControlStreamOut(pThis, pStreamDS, enmStreamCmd);
2335
2336 return rc;
2337}
2338
2339
2340/**
2341 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
2342 */
2343static DECLCALLBACK(uint32_t) drvHostDSoundStreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2344{
2345 RT_NOREF(pInterface);
2346 AssertPtrReturn(pStream, PDMAUDIOSTREAMSTS_FLAG_NONE);
2347
2348 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2349
2350 if (pStreamDS->fEnabled)
2351 return UINT32_MAX;
2352
2353 return 0;
2354}
2355
2356
2357/**
2358 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
2359 */
2360static DECLCALLBACK(uint32_t) drvHostDSoundStreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2361{
2362 AssertPtrReturn(pInterface, PDMAUDIOSTREAMSTS_FLAG_NONE);
2363 AssertPtrReturn(pStream, PDMAUDIOSTREAMSTS_FLAG_NONE);
2364
2365 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2366 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2367
2368 if (pStreamDS->fEnabled)
2369 {
2370 DWORD cbFree;
2371 int rc = dsoundGetFreeOut(pThis, pStreamDS, &cbFree);
2372 if ( RT_SUCCESS(rc)
2373 && cbFree)
2374 {
2375 Log3Func(("cbFree=%ld\n", cbFree));
2376 return (uint32_t)cbFree;
2377 }
2378 }
2379
2380 return 0;
2381}
2382
2383
2384/**
2385 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending}
2386 */
2387static DECLCALLBACK(uint32_t) drvHostDSoundStreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2388{
2389 RT_NOREF(pInterface);
2390 AssertPtrReturn(pStream, 0);
2391
2392 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2393 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2394
2395 if (pStreamDS->pCfg->enmDir == PDMAUDIODIR_OUT)
2396 {
2397 DWORD dwStatus;
2398 HRESULT hr = directSoundPlayGetStatus(pThis, pStreamDS->Out.pDSB, &dwStatus);
2399 if (hr != DS_OK)
2400 return 0;
2401
2402 if (!(dwStatus & DSBSTATUS_PLAYING))
2403 return 0;
2404
2405 DWORD offPlayCursor, offWriteCursor;
2406 hr = IDirectSoundBuffer8_GetCurrentPosition(pStreamDS->Out.pDSB, &offPlayCursor, &offWriteCursor);
2407 if (SUCCEEDED(hr))
2408 {
2409 uint32_t cbPending;
2410 if (pStreamDS->Out.offPlayCursorLastPending <= offPlayCursor)
2411 cbPending = offPlayCursor - pStreamDS->Out.offPlayCursorLastPending;
2412 else
2413 cbPending = pStreamDS->Out.cbBufSize - pStreamDS->Out.offPlayCursorLastPending + offPlayCursor;
2414
2415 pStreamDS->Out.cbWritten -= RT_MIN(pStreamDS->Out.cbWritten, cbPending);
2416 pStreamDS->Out.offPlayCursorLastPending = offPlayCursor;
2417
2418 LogFunc(("offPlayCursor=%RU32, offWriteCursor=%RU32\n", offPlayCursor, offWriteCursor));
2419 LogFunc(("offPlayWritePos=%RU32, cbWritten=%RU64, cbPending=%RU32\n",
2420 pStreamDS->Out.offWritePos, pStreamDS->Out.cbWritten, cbPending));
2421
2422 /*
2423 * As we operate a DirectSound secondary *streaming* buffer which loops over
2424 * the data repeatedly until stopped, we have to make at least an estimate when we're actually
2425 * done playing the written data on the host.
2426 */
2427 return pStreamDS->Out.cbWritten;
2428 }
2429 else
2430 LogFunc(("Failed with %Rhrc\n", hr));
2431 }
2432 /* Note: For input streams we never have pending data left. */
2433
2434 return 0;
2435}
2436
2437
2438/**
2439 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
2440 */
2441static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostDSoundStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2442{
2443 RT_NOREF(pInterface);
2444 AssertPtrReturn(pStream, PDMAUDIOSTREAMSTS_FLAG_NONE);
2445
2446 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2447
2448 if (!pStreamDS->pCfg) /* Not (yet) configured? Skip. */
2449 return PDMAUDIOSTREAMSTS_FLAG_NONE;
2450
2451 PDMAUDIOSTREAMSTS strmSts = PDMAUDIOSTREAMSTS_FLAG_INITIALIZED;
2452 if (pStreamDS->pCfg->enmDir == PDMAUDIODIR_IN)
2453 {
2454 if (pStreamDS->fEnabled)
2455 strmSts |= PDMAUDIOSTREAMSTS_FLAG_ENABLED;
2456 }
2457 else
2458 {
2459 if (pStreamDS->fEnabled)
2460 strmSts |= PDMAUDIOSTREAMSTS_FLAG_ENABLED;
2461 }
2462
2463 return strmSts;
2464}
2465
2466
2467/**
2468 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
2469 */
2470static DECLCALLBACK(int) drvHostDSoundStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2471{
2472 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2473 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2474
2475 LogFlowFuncEnter();
2476
2477 /* Nothing to do here for DSound. */
2478 return VINF_SUCCESS;
2479}
2480
2481#ifdef VBOX_WITH_AUDIO_CALLBACKS
2482/**
2483 * @interface_method_impl{PDMIHOSTAUDIO,pfnSetCallback}
2484 */
2485static DECLCALLBACK(int) drvHostDSoundSetCallback(PPDMIHOSTAUDIO pInterface, PFNPDMHOSTAUDIOCALLBACK pfnCallback)
2486{
2487 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2488 /* pfnCallback will be handled below. */
2489
2490 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2491
2492 int rc = RTCritSectEnter(&pThis->CritSect);
2493 if (RT_SUCCESS(rc))
2494 {
2495 LogFunc(("pfnCallback=%p\n", pfnCallback));
2496
2497 if (pfnCallback) /* Register. */
2498 {
2499 Assert(pThis->pfnCallback == NULL);
2500 pThis->pfnCallback = pfnCallback;
2501
2502#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
2503 if (pThis->m_pNotificationClient)
2504 pThis->m_pNotificationClient->RegisterCallback(pThis->pDrvIns, pfnCallback);
2505#endif
2506 }
2507 else /* Unregister. */
2508 {
2509 if (pThis->pfnCallback)
2510 pThis->pfnCallback = NULL;
2511
2512#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
2513 if (pThis->m_pNotificationClient)
2514 pThis->m_pNotificationClient->UnregisterCallback();
2515#endif
2516 }
2517
2518 int rc2 = RTCritSectLeave(&pThis->CritSect);
2519 AssertRC(rc2);
2520 }
2521
2522 return rc;
2523}
2524#endif
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 try
2646 {
2647 pThis->m_pNotificationClient = new VBoxMMNotificationClient();
2648
2649 HRESULT hr = pThis->m_pNotificationClient->Initialize();
2650 if (FAILED(hr))
2651 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
2652 }
2653 catch (std::bad_alloc &ex)
2654 {
2655 NOREF(ex);
2656 rc = VERR_NO_MEMORY;
2657 }
2658#endif
2659
2660 if (RT_SUCCESS(rc))
2661 {
2662 /*
2663 * Initialize configuration values.
2664 */
2665 rc = dsoundConfigInit(pThis, pCfg);
2666 if (RT_SUCCESS(rc))
2667 rc = RTCritSectInit(&pThis->CritSect);
2668 }
2669
2670 return rc;
2671}
2672
2673
2674/**
2675 * PDM driver registration.
2676 */
2677const PDMDRVREG g_DrvHostDSound =
2678{
2679 /* u32Version */
2680 PDM_DRVREG_VERSION,
2681 /* szName */
2682 "DSoundAudio",
2683 /* szRCMod */
2684 "",
2685 /* szR0Mod */
2686 "",
2687 /* pszDescription */
2688 "DirectSound Audio host driver",
2689 /* fFlags */
2690 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2691 /* fClass. */
2692 PDM_DRVREG_CLASS_AUDIO,
2693 /* cMaxInstances */
2694 ~0U,
2695 /* cbInstance */
2696 sizeof(DRVHOSTDSOUND),
2697 /* pfnConstruct */
2698 drvHostDSoundConstruct,
2699 /* pfnDestruct */
2700 drvHostDSoundDestruct,
2701 /* pfnRelocate */
2702 NULL,
2703 /* pfnIOCtl */
2704 NULL,
2705 /* pfnPowerOn */
2706 NULL,
2707 /* pfnReset */
2708 NULL,
2709 /* pfnSuspend */
2710 NULL,
2711 /* pfnResume */
2712 NULL,
2713 /* pfnAttach */
2714 NULL,
2715 /* pfnDetach */
2716 NULL,
2717 /* pfnPowerOff */
2718 NULL,
2719 /* pfnSoftReset */
2720 NULL,
2721 /* u32EndVersion */
2722 PDM_DRVREG_VERSION
2723};
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