VirtualBox

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

Last change on this file since 73097 was 73097, checked in by vboxsync, 6 years ago

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

  • 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 73097 2018-07-12 21:06:33Z 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_UOFFSETOF(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