VirtualBox

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

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

Audio/DrvHostDSound.cpp: Use the notification thread by default to achieve a (hopefully) better audio output result. Work in progress.

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