VirtualBox

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

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

Audio/DrvHostDSound.cpp: Removed unnecessary code to stop the playback buffer within the notification thread; the logic here is that the audio connector tells the backend when to stop playing as soon as there is no pending playback data left to be played (reported by the backend).

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