VirtualBox

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

Last change on this file since 60404 was 60353, checked in by vboxsync, 8 years ago

Audio: Added HDA support for newer Linux guests; more work on surround support (disabled by default).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 72.8 KB
Line 
1/* $Id: DrvHostDSound.cpp 60353 2016-04-06 11:54:39Z vboxsync $ */
2/** @file
3 * Windows host backend driver using DirectSound.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
20#include <VBox/log.h>
21#include <dsound.h>
22
23#include <iprt/alloc.h>
24#include <iprt/uuid.h>
25
26#include "AudioMixBuffer.h"
27#include "DrvAudio.h"
28#include "VBoxDD.h"
29
30/*
31 * IDirectSound* interface uses HRESULT status codes and the driver callbacks use
32 * the IPRT status codes. To minimize HRESULT->IPRT conversion most internal functions
33 * in the driver return HRESULT and conversion is done in the driver callbacks.
34 *
35 * Naming convention:
36 * 'dsound*' functions return IPRT status code;
37 * 'directSound*' - return HRESULT.
38 */
39
40/*
41 * Optional release logging, which a user can turn on with the
42 * 'VBoxManage debugvm' command.
43 * Debug logging still uses the common Log* macros from IPRT.
44 * Messages which always should go to the release log use LogRel.
45 */
46/* General code behavior. */
47#define DSLOG(a) do { LogRel2(a); } while(0)
48/* Something which produce a lot of logging during playback/recording. */
49#define DSLOGF(a) do { LogRel3(a); } while(0)
50/* Important messages like errors. Limited in the default release log to avoid log flood. */
51#define DSLOGREL(a) \
52 do { \
53 static int8_t scLogged = 0; \
54 if (scLogged < 8) { \
55 ++scLogged; \
56 LogRel(a); \
57 } \
58 else { \
59 DSLOG(a); \
60 } \
61 } while (0)
62
63/* Dynamically load dsound.dll. */
64typedef HRESULT WINAPI FNDIRECTSOUNDENUMERATEW(LPDSENUMCALLBACKW pDSEnumCallback, LPVOID pContext);
65typedef FNDIRECTSOUNDENUMERATEW *PFNDIRECTSOUNDENUMERATEW;
66typedef HRESULT WINAPI FNDIRECTSOUNDCAPTUREENUMERATEW(LPDSENUMCALLBACKW pDSEnumCallback, LPVOID pContext);
67typedef FNDIRECTSOUNDCAPTUREENUMERATEW *PFNDIRECTSOUNDCAPTUREENUMERATEW;
68
69#ifdef VBOX_WITH_AUDIO_CALLBACKS
70# define VBOX_DSOUND_MAX_EVENTS 3
71
72typedef enum DSOUNDEVENT
73{
74 DSOUNDEVENT_NOTIFY = 0,
75 DSOUNDEVENT_INPUT,
76 DSOUNDEVENT_OUTPUT,
77 } DSOUNDEVENT;
78#endif /* VBOX_WITH_AUDIO_CALLBACKS */
79
80typedef struct DSOUNDHOSTCFG
81{
82 DWORD cbBufferIn;
83 DWORD cbBufferOut;
84 RTUUID uuidPlay;
85 LPCGUID pGuidPlay;
86 RTUUID uuidCapture;
87 LPCGUID pGuidCapture;
88} DSOUNDHOSTCFG, *PDSOUNDHOSTCFG;
89
90typedef struct DSOUNDSTREAMOUT
91{
92 PDMAUDIOHSTSTRMOUT strmOut; /* Always must come first! */
93 LPDIRECTSOUND8 pDS; /** @todo Move this out of this structure! Not required per-stream (e.g. for multi-channel). */
94 LPDIRECTSOUNDBUFFER8 pDSB;
95 DWORD cbPlayWritePos;
96 DWORD csPlaybackBufferSize;
97 bool fEnabled;
98 bool fRestartPlayback;
99 PDMAUDIOSTREAMCFG streamCfg;
100} DSOUNDSTREAMOUT, *PDSOUNDSTREAMOUT;
101
102typedef struct DSOUNDSTREAMIN
103{
104 PDMAUDIOHSTSTRMIN strmIn; /* Always must come first! */
105 LPDIRECTSOUNDCAPTURE8 pDSC;
106 LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB;
107 DWORD csCaptureReadPos;
108 DWORD csCaptureBufferSize;
109 HRESULT hrLastCaptureIn;
110 PDMAUDIORECSOURCE enmRecSource;
111 bool fEnabled;
112 PDMAUDIOSTREAMCFG streamCfg;
113} DSOUNDSTREAMIN, *PDSOUNDSTREAMIN;
114
115typedef struct DRVHOSTDSOUND
116{
117 /** Pointer to the driver instance structure. */
118 PPDMDRVINS pDrvIns;
119 /** Our audio host audio interface. */
120 PDMIHOSTAUDIO IHostAudio;
121 /** List of found host input devices. */
122 RTLISTANCHOR lstDevInput;
123 /** List of found host output devices. */
124 RTLISTANCHOR lstDevOutput;
125 /** DirectSound configuration options. */
126 DSOUNDHOSTCFG cfg;
127 /** Whether this backend supports any audio input. */
128 bool fEnabledIn;
129 /** Whether this backend supports any audio output. */
130 bool fEnabledOut;
131#ifdef VBOX_WITH_AUDIO_CALLBACKS
132 /** Pointer to the audio connector interface of the driver/device above us. */
133 PPDMIAUDIOCONNECTOR pUpIAudioConnector;
134 /** Stopped indicator. */
135 bool fStopped;
136 /** Shutdown indicator. */
137 bool fShutdown;
138 /** Notification thread. */
139 RTTHREAD Thread;
140 /** Array of events to wait for in notification thread. */
141 HANDLE aEvents[VBOX_DSOUND_MAX_EVENTS];
142 /** Number of events to wait for in notification thread.
143 * Must not exceed VBOX_DSOUND_MAX_EVENTS. */
144 uint8_t cEvents;
145 /** Pointer to the input stream. */
146 PDSOUNDSTREAMIN pDSStrmIn;
147 /** Pointer to the output stream. */
148 PDSOUNDSTREAMOUT pDSStrmOut;
149#endif
150} DRVHOSTDSOUND, *PDRVHOSTDSOUND;
151
152/** No flags specified. */
153#define DSOUNDENUMCBFLAGS_NONE 0
154/** (Release) log found devices. */
155#define DSOUNDENUMCBFLAGS_LOG RT_BIT(0)
156
157/**
158 * Callback context for enumeration callbacks
159 */
160typedef struct DSOUNDENUMCBCTX
161{
162 /** Pointer to host backend driver. */
163 PDRVHOSTDSOUND pDrv;
164 /** Enumeration flags. */
165 uint32_t fFlags;
166 /** Number of found input devices. */
167 uint8_t cDevIn;
168 /** Number of found output devices. */
169 uint8_t cDevOut;
170} DSOUNDENUMCBCTX, *PDSOUNDENUMCBCTX;
171
172typedef struct DSOUNDDEV
173{
174 RTLISTNODE Node;
175 char *pszName;
176 GUID Guid;
177} DSOUNDDEV, *PDSOUNDDEV;
178
179/*********************************************************************************************************************************
180* Defines *
181*********************************************************************************************************************************/
182
183/** Maximum number of attempts to restore the sound buffer before giving up. */
184#define DRV_DSOUND_RESTORE_ATTEMPTS_MAX 3
185
186/** Makes DRVHOSTDSOUND out of PDMIHOSTAUDIO. */
187#define PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface) \
188 ( (PDRVHOSTDSOUND)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTDSOUND, IHostAudio)) )
189
190/*********************************************************************************************************************************
191* Prototypes *
192*********************************************************************************************************************************/
193
194static HRESULT directSoundPlayRestore(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB);
195
196static void dsoundDeviceRemove(PDSOUNDDEV pDev);
197static int dsoundDevicesEnumerate(PDRVHOSTDSOUND pThis, PPDMAUDIOBACKENDCFG pCfg);
198#ifdef VBOX_WITH_AUDIO_CALLBACKS
199static int dsoundNotifyThread(PDRVHOSTDSOUND pThis, bool fShutdown);
200#endif
201
202static DWORD dsoundRingDistance(DWORD offEnd, DWORD offBegin, DWORD cSize)
203{
204 return offEnd >= offBegin ? offEnd - offBegin : cSize - offBegin + offEnd;
205}
206
207static int dsoundWaveFmtFromCfg(PPDMAUDIOSTREAMCFG pCfg, PWAVEFORMATEX pFmt)
208{
209 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
210 AssertPtrReturn(pFmt, VERR_INVALID_POINTER);
211
212 RT_BZERO(pFmt, sizeof(WAVEFORMATEX));
213
214 pFmt->wFormatTag = WAVE_FORMAT_PCM;
215 pFmt->nChannels = pCfg->cChannels;
216 pFmt->nSamplesPerSec = pCfg->uHz;
217 pFmt->nAvgBytesPerSec = pCfg->uHz << (pCfg->cChannels == 2 ? 1: 0);
218 pFmt->nBlockAlign = 1 << (pCfg->cChannels == 2 ? 1: 0);
219 pFmt->cbSize = 0; /* No extra data specified. */
220
221 switch (pCfg->enmFormat)
222 {
223 case AUD_FMT_S8:
224 case AUD_FMT_U8:
225 pFmt->wBitsPerSample = 8;
226 break;
227
228 case AUD_FMT_S16:
229 case AUD_FMT_U16:
230 pFmt->wBitsPerSample = 16;
231 pFmt->nAvgBytesPerSec <<= 1;
232 pFmt->nBlockAlign <<= 1;
233 break;
234
235 case AUD_FMT_S32:
236 case AUD_FMT_U32:
237 pFmt->wBitsPerSample = 32;
238 pFmt->nAvgBytesPerSec <<= 2;
239 pFmt->nBlockAlign <<= 2;
240 break;
241
242 default:
243 AssertMsgFailed(("Wave format %ld not supported\n", pCfg->enmFormat));
244 return VERR_NOT_SUPPORTED;
245 }
246
247 return VINF_SUCCESS;
248}
249
250static int dsoundGetPosOut(PDRVHOSTDSOUND pThis,
251 PDSOUNDSTREAMOUT pDSoundStrmOut, DWORD *pdwBuffer, DWORD *pdwFree, DWORD *pdwPlayPos)
252{
253 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
254 AssertPtrReturn(pDSoundStrmOut, VERR_INVALID_POINTER);
255
256 LPDIRECTSOUNDBUFFER8 pDSB = pDSoundStrmOut->pDSB;
257 if (!pDSB)
258 return VERR_INVALID_POINTER;
259
260 DWORD cbBuffer = AUDIOMIXBUF_S2B(&pDSoundStrmOut->strmOut.MixBuf, pDSoundStrmOut->csPlaybackBufferSize);
261
262 /* Get the current play position which is used for calculating the free space in the buffer. */
263 DWORD cbPlayPos;
264
265 HRESULT hr;
266 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
267 {
268 hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &cbPlayPos, NULL);
269 if ( SUCCEEDED(hr)
270 || hr != DSERR_BUFFERLOST) /** @todo: MSDN doesn't state this error for GetCurrentPosition(). */
271 {
272 break;
273 }
274 else
275 {
276 LogFlowFunc(("Getting playing position failed due to lost buffer, restoring ...\n"));
277 directSoundPlayRestore(pThis, pDSB);
278 }
279 }
280
281 int rc = VINF_SUCCESS;
282
283 if (FAILED(hr))
284 {
285 if (hr != DSERR_BUFFERLOST) /* Avoid log flooding if the error is still there. */
286 DSLOGREL(("DSound: Getting current playback position failed with %Rhrc\n", hr));
287 LogFlowFunc(("Failed with %Rhrc\n", hr));
288
289 rc = VERR_NOT_AVAILABLE;
290 }
291 else
292 {
293 if (pdwBuffer)
294 *pdwBuffer = cbBuffer;
295
296 if (pdwFree)
297 *pdwFree = cbBuffer - dsoundRingDistance(pDSoundStrmOut->cbPlayWritePos, cbPlayPos, cbBuffer);
298
299 if (pdwPlayPos)
300 *pdwPlayPos = cbPlayPos;
301 }
302
303 LogFlowFuncLeaveRC(rc);
304 return rc;
305}
306
307static char *dsoundGUIDToUtf8StrA(LPCGUID lpGUID)
308{
309 if (lpGUID)
310 {
311 LPOLESTR lpOLEStr;
312 HRESULT hr = StringFromCLSID(*lpGUID, &lpOLEStr);
313 if (SUCCEEDED(hr))
314 {
315 char *pszGUID;
316 int rc = RTUtf16ToUtf8(lpOLEStr, &pszGUID);
317 CoTaskMemFree(lpOLEStr);
318
319 return RT_SUCCESS(rc) ? pszGUID : NULL;
320 }
321 }
322
323 return RTStrDup("{Default device}");
324}
325
326/**
327 * Clears the list of the host's playback + capturing devices.
328 *
329 * @param pThis Host audio driver instance.
330 */
331static void dsoundDevicesClear(PDRVHOSTDSOUND pThis)
332{
333 AssertPtrReturnVoid(pThis);
334
335 PDSOUNDDEV pDev;
336 while (!RTListIsEmpty(&pThis->lstDevInput))
337 {
338 pDev = RTListGetFirst(&pThis->lstDevInput, DSOUNDDEV, Node);
339 dsoundDeviceRemove(pDev);
340 }
341
342 while (!RTListIsEmpty(&pThis->lstDevOutput))
343 {
344 pDev = RTListGetFirst(&pThis->lstDevOutput, DSOUNDDEV, Node);
345 dsoundDeviceRemove(pDev);
346 }
347}
348
349static HRESULT directSoundPlayRestore(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB)
350{
351 HRESULT hr = IDirectSoundBuffer8_Restore(pDSB);
352 if (FAILED(hr))
353 DSLOGREL(("DSound: Restoring playback buffer failed with %Rhrc\n", hr));
354 return hr;
355}
356
357static HRESULT directSoundPlayUnlock(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB,
358 LPVOID pv1, LPVOID pv2,
359 DWORD cb1, DWORD cb2)
360{
361 HRESULT hr = IDirectSoundBuffer8_Unlock(pDSB, pv1, cb1, pv2, cb2);
362 if (FAILED(hr))
363 DSLOGREL(("DSound: Unlocking playback buffer failed with %Rhrc\n", hr));
364 return hr;
365}
366
367static HRESULT directSoundCaptureUnlock(LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB,
368 LPVOID pv1, LPVOID pv2,
369 DWORD cb1, DWORD cb2)
370{
371 HRESULT hr = IDirectSoundCaptureBuffer8_Unlock(pDSCB, pv1, cb1, pv2, cb2);
372 if (FAILED(hr))
373 DSLOGREL(("DSound: Unlocking capture buffer failed with %Rhrc\n", hr));
374 return hr;
375}
376
377static HRESULT directSoundPlayLock(PDRVHOSTDSOUND pThis,
378 LPDIRECTSOUNDBUFFER8 pDSB, PDMPCMPROPS *pProps,
379 DWORD dwOffset, DWORD dwBytes,
380 LPVOID *ppv1, LPVOID *ppv2,
381 DWORD *pcb1, DWORD *pcb2,
382 DWORD dwFlags)
383{
384 LPVOID pv1 = NULL;
385 LPVOID pv2 = NULL;
386 DWORD cb1 = 0;
387 DWORD cb2 = 0;
388
389 HRESULT hr;
390 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
391 {
392 hr = IDirectSoundBuffer8_Lock(pDSB, dwOffset, dwBytes, &pv1, &cb1, &pv2, &cb2, dwFlags);
393 if ( SUCCEEDED(hr)
394 || hr != DSERR_BUFFERLOST)
395 break;
396 else
397 {
398 LogFlowFunc(("Locking failed due to lost buffer, restoring ...\n"));
399 directSoundPlayRestore(pThis, pDSB);
400 }
401 }
402
403 if (FAILED(hr))
404 {
405 DSLOGREL(("DSound: Locking playback buffer failed with %Rhrc\n", hr));
406 return hr;
407 }
408
409 if ( (pv1 && (cb1 & pProps->uAlign))
410 || (pv2 && (cb2 & pProps->uAlign)))
411 {
412 DSLOGREL(("DSound: Locking playback buffer returned misaligned buffer: cb1=%RI32, cb2=%RI32 (alignment: %RU32)\n",
413 cb1, cb2, pProps->uAlign));
414 directSoundPlayUnlock(pThis, pDSB, pv1, pv2, cb1, cb2);
415 return E_FAIL;
416 }
417
418 *ppv1 = pv1;
419 *ppv2 = pv2;
420 *pcb1 = cb1;
421 *pcb2 = cb2;
422
423 return S_OK;
424}
425
426static HRESULT directSoundCaptureLock(LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB, PPDMPCMPROPS pProps,
427 DWORD dwOffset, DWORD dwBytes,
428 LPVOID *ppv1, LPVOID *ppv2,
429 DWORD *pcb1, DWORD *pcb2,
430 DWORD dwFlags)
431{
432 LPVOID pv1 = NULL;
433 LPVOID pv2 = NULL;
434 DWORD cb1 = 0;
435 DWORD cb2 = 0;
436
437 HRESULT hr = IDirectSoundCaptureBuffer8_Lock(pDSCB, dwOffset, dwBytes,
438 &pv1, &cb1, &pv2, &cb2, dwFlags);
439 if (FAILED(hr))
440 {
441 DSLOGREL(("DSound: Locking capture buffer failed with %Rhrc\n", hr));
442 return hr;
443 }
444
445 if ( (pv1 && (cb1 & pProps->uAlign))
446 || (pv2 && (cb2 & pProps->uAlign)))
447 {
448 DSLOGREL(("DSound: Locking capture buffer returned misaligned buffer: cb1=%RI32, cb2=%RI32 (alignment: %RU32)\n",
449 cb1, cb2, pProps->uAlign));
450 directSoundCaptureUnlock(pDSCB, pv1, pv2, cb1, cb2);
451 return E_FAIL;
452 }
453
454 *ppv1 = pv1;
455 *ppv2 = pv2;
456 *pcb1 = cb1;
457 *pcb2 = cb2;
458
459 return S_OK;
460}
461
462
463/*
464 * DirectSound playback
465 */
466
467static void directSoundPlayInterfaceRelease(PDSOUNDSTREAMOUT pDSoundStrmOut)
468{
469 if (pDSoundStrmOut->pDS)
470 {
471 IDirectSound8_Release(pDSoundStrmOut->pDS);
472 pDSoundStrmOut->pDS = NULL;
473 }
474}
475
476static HRESULT directSoundPlayInterfaceCreate(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
477{
478 if (pDSoundStrmOut->pDS != NULL)
479 {
480 DSLOG(("DSound: DirectSound instance already exists\n"));
481 return S_OK;
482 }
483
484 HRESULT hr = CoCreateInstance(CLSID_DirectSound8, NULL, CLSCTX_ALL,
485 IID_IDirectSound8, (void **)&pDSoundStrmOut->pDS);
486 if (FAILED(hr))
487 {
488 DSLOGREL(("DSound: Creating playback instance failed with %Rhrc\n", hr));
489 }
490 else
491 {
492 hr = IDirectSound8_Initialize(pDSoundStrmOut->pDS, pThis->cfg.pGuidPlay);
493 if (SUCCEEDED(hr))
494 {
495 HWND hWnd = GetDesktopWindow();
496 hr = IDirectSound8_SetCooperativeLevel(pDSoundStrmOut->pDS, hWnd, DSSCL_PRIORITY);
497 if (FAILED(hr))
498 DSLOGREL(("DSound: Setting cooperative level for window %p failed with %Rhrc\n", hWnd, hr));
499 }
500
501 if (FAILED(hr))
502 {
503 if (hr == DSERR_NODRIVER) /* Usually means that no playback devices are attached. */
504 DSLOGREL(("DSound: DirectSound playback is currently unavailable\n"));
505 else
506 DSLOGREL(("DSound: DirectSound playback initialization failed with %Rhrc\n", hr));
507
508 directSoundPlayInterfaceRelease(pDSoundStrmOut);
509 }
510 }
511
512 return hr;
513}
514
515static HRESULT directSoundPlayClose(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
516{
517 AssertPtrReturn(pThis, E_POINTER);
518 AssertPtrReturn(pDSoundStrmOut, E_POINTER);
519
520 DSLOG(("DSound: Closing playback stream %p, buffer %p\n", pDSoundStrmOut, pDSoundStrmOut->pDSB));
521
522 HRESULT hr = S_OK;
523
524 if (pDSoundStrmOut->pDSB)
525 {
526 hr = IDirectSoundBuffer8_Stop(pDSoundStrmOut->pDSB);
527 if (SUCCEEDED(hr))
528 {
529#ifdef VBOX_WITH_AUDIO_CALLBACKS
530 if (pThis->aEvents[DSOUNDEVENT_OUTPUT] != NULL)
531 {
532 CloseHandle(pThis->aEvents[DSOUNDEVENT_OUTPUT]);
533 pThis->aEvents[DSOUNDEVENT_OUTPUT] = NULL;
534
535 if (pThis->cEvents)
536 pThis->cEvents--;
537
538 pThis->pDSStrmOut = NULL;
539 }
540
541 int rc2 = dsoundNotifyThread(pThis, false /* fShutdown */);
542 AssertRC(rc2);
543#endif
544 IDirectSoundBuffer8_Release(pDSoundStrmOut->pDSB);
545 pDSoundStrmOut->pDSB = NULL;
546 }
547 else
548 DSLOGREL(("DSound: Stop playback stream %p when closing %Rhrc\n", pDSoundStrmOut, hr));
549 }
550
551 if (SUCCEEDED(hr))
552 directSoundPlayInterfaceRelease(pDSoundStrmOut);
553
554 return hr;
555}
556
557static HRESULT directSoundPlayOpen(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
558{
559 AssertPtrReturn(pThis, E_POINTER);
560 AssertPtrReturn(pDSoundStrmOut, E_POINTER);
561
562 DSLOG(("DSound: pDSoundStrmOut=%p, cbBufferOut=%ld, uHz=%RU32, cChannels=%RU8, cBits=%RU8, fSigned=%RTbool\n",
563 pDSoundStrmOut,
564 pThis->cfg.cbBufferOut,
565 pDSoundStrmOut->strmOut.Props.uHz,
566 pDSoundStrmOut->strmOut.Props.cChannels,
567 pDSoundStrmOut->strmOut.Props.cBits,
568 pDSoundStrmOut->strmOut.Props.fSigned));
569
570 if (pDSoundStrmOut->pDSB != NULL)
571 {
572 /* Should not happen but be forgiving. */
573 DSLOGREL(("DSound: Playback buffer already exists\n"));
574 directSoundPlayClose(pThis, pDSoundStrmOut);
575 }
576
577 WAVEFORMATEX wfx;
578 int rc = dsoundWaveFmtFromCfg(&pDSoundStrmOut->streamCfg, &wfx);
579 if (RT_FAILURE(rc))
580 return E_INVALIDARG;
581
582 HRESULT hr = directSoundPlayInterfaceCreate(pThis, pDSoundStrmOut);
583 if (FAILED(hr))
584 return hr;
585
586 do /* To use breaks. */
587 {
588 LPDIRECTSOUNDBUFFER pDSB = NULL;
589
590 DSBUFFERDESC bd;
591 RT_ZERO(bd);
592 bd.dwSize = sizeof(bd);
593 bd.lpwfxFormat = &wfx;
594
595 /*
596 * As we reuse our (secondary) buffer for playing out data as it comes in,
597 * we're using this buffer as a so-called static buffer.
598 *
599 * However, as we do not want to use memory on the sound device directly
600 * (as most modern audio hardware on the host doesn't have this anyway),
601 * we're *not* going to use DSBCAPS_STATIC for that.
602 *
603 * Instead we're specifying DSBCAPS_LOCSOFTWARE, as this fits the bill
604 * of copying own buffer data (from AudioMixBuf) to our secondary's Direct Sound buffer.
605 */
606 bd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE;
607#ifdef VBOX_WITH_AUDIO_CALLBACKS
608 bd.dwFlags |= DSBCAPS_CTRLPOSITIONNOTIFY;
609#endif
610 bd.dwBufferBytes = pThis->cfg.cbBufferOut;
611
612 hr = IDirectSound8_CreateSoundBuffer(pDSoundStrmOut->pDS, &bd, &pDSB, NULL);
613 if (FAILED(hr))
614 {
615 DSLOGREL(("DSound: Creating playback sound buffer failed with %Rhrc\n", hr));
616 break;
617 }
618
619 /* "Upgrade" to IDirectSoundBuffer8 interface. */
620 hr = IDirectSoundBuffer_QueryInterface(pDSB, IID_IDirectSoundBuffer8, (LPVOID *)&pDSoundStrmOut->pDSB);
621 IDirectSoundBuffer_Release(pDSB);
622 if (FAILED(hr))
623 {
624 DSLOGREL(("DSound: Querying playback sound buffer interface failed with %Rhrc\n", hr));
625 break;
626 }
627
628 /*
629 * Query the actual parameters.
630 */
631 hr = IDirectSoundBuffer8_GetFormat(pDSoundStrmOut->pDSB, &wfx, sizeof(wfx), NULL);
632 if (FAILED(hr))
633 {
634 DSLOGREL(("DSound: Getting playback format failed with %Rhrc\n", hr));
635 break;
636 }
637
638 DSBCAPS bc;
639 RT_ZERO(bc);
640 bc.dwSize = sizeof(bc);
641 hr = IDirectSoundBuffer8_GetCaps(pDSoundStrmOut->pDSB, &bc);
642 if (FAILED(hr))
643 {
644 DSLOGREL(("DSound: Getting playback capabilities failed with %Rhrc\n", hr));
645 break;
646 }
647
648 DSLOG(("DSound: Playback format:\n"
649 " dwBufferBytes = %RI32\n"
650 " dwFlags = 0x%x\n"
651 " wFormatTag = %RI16\n"
652 " nChannels = %RI16\n"
653 " nSamplesPerSec = %RU32\n"
654 " nAvgBytesPerSec = %RU32\n"
655 " nBlockAlign = %RI16\n"
656 " wBitsPerSample = %RI16\n"
657 " cbSize = %RI16\n",
658 bc.dwBufferBytes,
659 bc.dwFlags,
660 wfx.wFormatTag,
661 wfx.nChannels,
662 wfx.nSamplesPerSec,
663 wfx.nAvgBytesPerSec,
664 wfx.nBlockAlign,
665 wfx.wBitsPerSample,
666 wfx.cbSize));
667
668 if (bc.dwBufferBytes & pDSoundStrmOut->strmOut.Props.uAlign)
669 DSLOGREL(("DSound: Playback capabilities returned misaligned buffer: size %RU32, alignment %RU32\n",
670 bc.dwBufferBytes, pDSoundStrmOut->strmOut.Props.uAlign + 1));
671
672 if (bc.dwBufferBytes != pThis->cfg.cbBufferOut)
673 DSLOGREL(("DSound: Playback buffer size mismatched: DirectSound %RU32, requested %RU32 bytes\n",
674 bc.dwBufferBytes, pThis->cfg.cbBufferOut));
675
676 /*
677 * Initial state.
678 * dsoundPlayStart initializes part of it to make sure that Stop/Start continues with a correct
679 * playback buffer position.
680 */
681 pDSoundStrmOut->csPlaybackBufferSize = bc.dwBufferBytes >> pDSoundStrmOut->strmOut.Props.cShift;
682 DSLOG(("DSound: csPlaybackBufferSize=%RU32\n", pDSoundStrmOut->csPlaybackBufferSize));
683
684#ifdef VBOX_WITH_AUDIO_CALLBACKS
685 /*
686 * Install notification.
687 */
688 pThis->aEvents[DSOUNDEVENT_OUTPUT] = CreateEvent(NULL /* Security attribute */,
689 FALSE /* bManualReset */, FALSE /* bInitialState */,
690 NULL /* lpName */);
691 if (pThis->aEvents[DSOUNDEVENT_OUTPUT] == NULL)
692 {
693 hr = HRESULT_FROM_WIN32(GetLastError());
694 DSLOGREL(("DSound: CreateEvent for output failed with %Rhrc\n", hr));
695 break;
696 }
697
698 LPDIRECTSOUNDNOTIFY8 pNotify;
699 hr = IDirectSoundNotify_QueryInterface(pDSoundStrmOut->pDSB, IID_IDirectSoundNotify8, (LPVOID *)&pNotify);
700 if (SUCCEEDED(hr))
701 {
702 DSBPOSITIONNOTIFY dsBufPosNotify;
703 RT_ZERO(dsBufPosNotify);
704 dsBufPosNotify.dwOffset = DSBPN_OFFSETSTOP;
705 dsBufPosNotify.hEventNotify = pThis->aEvents[DSOUNDEVENT_OUTPUT];
706
707 hr = IDirectSoundNotify_SetNotificationPositions(pNotify, 1 /* Count */, &dsBufPosNotify);
708 if (FAILED(hr))
709 DSLOGREL(("DSound: Setting playback position notification failed with %Rhrc\n", hr));
710
711 IDirectSoundNotify_Release(pNotify);
712 }
713 else
714 DSLOGREL(("DSound: Querying interface for position notification failed with %Rhrc\n", hr));
715
716 if (FAILED(hr))
717 break;
718
719 pThis->pDSStrmOut = pDSoundStrmOut;
720
721 Assert(pThis->cEvents < VBOX_DSOUND_MAX_EVENTS);
722 pThis->cEvents++;
723
724 /* Let the thread know. */
725 dsoundNotifyThread(pThis, false /* fShutdown */);
726
727 /* Trigger the just installed output notification. */
728 hr = IDirectSoundBuffer8_Play(pDSoundStrmOut->pDSB, 0, 0, 0);
729
730#endif /* VBOX_WITH_AUDIO_CALLBACKS */
731
732 } while (0);
733
734 if (FAILED(hr))
735 directSoundPlayClose(pThis, pDSoundStrmOut);
736
737 return hr;
738}
739
740static void dsoundPlayClearSamples(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
741{
742 AssertPtrReturnVoid(pDSoundStrmOut);
743
744 PPDMAUDIOHSTSTRMOUT pStrmOut = &pDSoundStrmOut->strmOut;
745
746 LPVOID pv1, pv2;
747 DWORD cb1, cb2;
748 HRESULT hr = directSoundPlayLock(pThis, pDSoundStrmOut->pDSB, &pDSoundStrmOut->strmOut.Props,
749 0 /* dwOffset */, AUDIOMIXBUF_S2B(&pStrmOut->MixBuf, pDSoundStrmOut->csPlaybackBufferSize),
750 &pv1, &pv2, &cb1, &cb2, DSBLOCK_ENTIREBUFFER);
751 if (SUCCEEDED(hr))
752 {
753 DWORD len1 = AUDIOMIXBUF_B2S(&pStrmOut->MixBuf, cb1);
754 DWORD len2 = AUDIOMIXBUF_B2S(&pStrmOut->MixBuf, cb2);
755
756 if (pv1 && len1)
757 DrvAudioClearBuf(&pDSoundStrmOut->strmOut.Props, pv1, cb1, len1);
758
759 if (pv2 && len2)
760 DrvAudioClearBuf(&pDSoundStrmOut->strmOut.Props, pv2, cb2, len2);
761
762 directSoundPlayUnlock(pThis, pDSoundStrmOut->pDSB, pv1, pv2, cb1, cb2);
763 }
764}
765
766static HRESULT directSoundPlayGetStatus(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB, DWORD *pdwStatus)
767{
768 AssertPtrReturn(pThis, E_POINTER);
769 AssertPtrReturn(pDSB, E_POINTER);
770 /* pdwStatus is optional. */
771
772 DWORD dwStatus = 0;
773
774 HRESULT hr;
775 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
776 {
777 hr = IDirectSoundBuffer8_GetStatus(pDSB, &dwStatus);
778 if ( hr == DSERR_BUFFERLOST
779 || ( SUCCEEDED(hr)
780 && (dwStatus & DSBSTATUS_BUFFERLOST)))
781 {
782 LogFlowFunc(("Getting status failed due to lost buffer, restoring ...\n"));
783 directSoundPlayRestore(pThis, pDSB);
784 }
785 else
786 break;
787 }
788
789 if (SUCCEEDED(hr))
790 {
791 if (pdwStatus)
792 *pdwStatus = dwStatus;
793 }
794 else
795 DSLOGREL(("DSound: Retrieving playback status failed with %Rhrc\n", hr));
796
797 return hr;
798}
799
800static HRESULT directSoundPlayStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
801{
802 AssertPtrReturn(pThis, E_POINTER);
803 AssertPtrReturn(pDSoundStrmOut, E_POINTER);
804
805 HRESULT hr;
806
807 if (pDSoundStrmOut->pDSB != NULL)
808 {
809 DSLOG(("DSound: Stopping playback\n"));
810
811 HRESULT hr2 = IDirectSoundBuffer8_Stop(pDSoundStrmOut->pDSB);
812 if (FAILED(hr2))
813 {
814 hr2 = directSoundPlayRestore(pThis, pDSoundStrmOut->pDSB);
815 if (FAILED(hr2))
816 hr2 = IDirectSoundBuffer8_Stop(pDSoundStrmOut->pDSB);
817 }
818
819 if (FAILED(hr2))
820 DSLOG(("DSound: Stopping playback failed with %Rhrc\n", hr2));
821
822 hr = S_OK; /* Always report success here. */
823 }
824 else
825 hr = E_UNEXPECTED;
826
827 if (SUCCEEDED(hr))
828 {
829 dsoundPlayClearSamples(pThis, pDSoundStrmOut);
830 pDSoundStrmOut->fEnabled = false;
831 }
832 else
833 DSLOGREL(("DSound: Stopping playback failed with %Rhrc\n", hr));
834
835 return hr;
836}
837
838static HRESULT directSoundPlayStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
839{
840 AssertPtrReturn(pThis, E_POINTER);
841 AssertPtrReturn(pDSoundStrmOut, E_POINTER);
842
843 HRESULT hr;
844 if (pDSoundStrmOut->pDSB != NULL)
845 {
846 DWORD dwStatus;
847 hr = directSoundPlayGetStatus(pThis, pDSoundStrmOut->pDSB, &dwStatus);
848 if (SUCCEEDED(hr))
849 {
850 if (dwStatus & DSBSTATUS_PLAYING)
851 {
852 DSLOG(("DSound: Already playing\n"));
853 }
854 else
855 {
856 dsoundPlayClearSamples(pThis, pDSoundStrmOut);
857
858 pDSoundStrmOut->fRestartPlayback = true;
859 pDSoundStrmOut->fEnabled = true;
860
861 DSLOG(("DSound: Playback started\n"));
862
863 /*
864 * The actual IDirectSoundBuffer8_Play call will be made in drvHostDSoundPlayOut,
865 * because it is necessary to put some samples into the buffer first.
866 */
867 }
868 }
869 }
870 else
871 hr = E_UNEXPECTED;
872
873 if (FAILED(hr))
874 DSLOGREL(("DSound: Starting playback failed with %Rhrc\n", hr));
875
876 return hr;
877}
878
879/*
880 * DirectSoundCapture
881 */
882
883static LPCGUID dsoundCaptureSelectDevice(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
884{
885 AssertPtrReturn(pThis, NULL);
886 AssertPtrReturn(pDSoundStrmIn, NULL);
887
888 LPCGUID pGUID = pThis->cfg.pGuidCapture;
889
890 if (!pGUID)
891 {
892 PDSOUNDDEV pDev = NULL;
893
894 switch (pDSoundStrmIn->enmRecSource)
895 {
896 case PDMAUDIORECSOURCE_MIC:
897 {
898 RTListForEach(&pThis->lstDevInput, pDev, DSOUNDDEV, Node)
899 {
900 if (RTStrIStr(pDev->pszName, "Mic")) /** @todo what is with non en_us windows versions? */
901 break;
902 }
903 if (RTListNodeIsDummy(&pThis->lstDevInput, pDev, DSOUNDDEV, Node))
904 pDev = NULL; /* Found nothing. */
905
906 break;
907 }
908
909 case PDMAUDIORECSOURCE_LINE:
910 default:
911 /* Try opening the default device (NULL). */
912 break;
913 }
914
915 if (pDev)
916 {
917 DSLOG(("DSound: Guest \"%s\" is using host \"%s\"\n",
918 drvAudioRecSourceToString(pDSoundStrmIn->enmRecSource), pDev->pszName));
919
920 pGUID = &pDev->Guid;
921 }
922 }
923
924 char *pszGUID = dsoundGUIDToUtf8StrA(pGUID);
925 /* This always has to be in the release log. */
926 LogRel(("DSound: Guest \"%s\" is using host device with GUID: %s\n",
927 drvAudioRecSourceToString(pDSoundStrmIn->enmRecSource), pszGUID? pszGUID: "{?}"));
928 RTStrFree(pszGUID);
929
930 return pGUID;
931}
932
933static void directSoundCaptureInterfaceRelease(PDSOUNDSTREAMIN pDSoundStrmIn)
934{
935 if (pDSoundStrmIn->pDSC)
936 {
937 LogFlowFuncEnter();
938 IDirectSoundCapture_Release(pDSoundStrmIn->pDSC);
939 pDSoundStrmIn->pDSC = NULL;
940 }
941}
942
943static HRESULT directSoundCaptureInterfaceCreate(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
944{
945 if (pDSoundStrmIn->pDSC != NULL)
946 {
947 DSLOG(("DSound: DirectSoundCapture instance already exists\n"));
948 return S_OK;
949 }
950
951 HRESULT hr = CoCreateInstance(CLSID_DirectSoundCapture8, NULL, CLSCTX_ALL,
952 IID_IDirectSoundCapture8, (void **)&pDSoundStrmIn->pDSC);
953 if (FAILED(hr))
954 {
955 DSLOGREL(("DSound: Creating capture instance failed with %Rhrc\n", hr));
956 }
957 else
958 {
959 LPCGUID pGUID = dsoundCaptureSelectDevice(pThis, pDSoundStrmIn);
960 hr = IDirectSoundCapture_Initialize(pDSoundStrmIn->pDSC, pGUID);
961 if (FAILED(hr))
962 {
963 if (hr == DSERR_NODRIVER) /* Usually means that no capture devices are attached. */
964 DSLOGREL(("DSound: Capture device currently is unavailable\n"));
965 else
966 DSLOGREL(("DSound: Initializing capturing device failed with %Rhrc\n", hr));
967
968 directSoundCaptureInterfaceRelease(pDSoundStrmIn);
969 }
970 }
971
972 LogFlowFunc(("Returning %Rhrc\n", hr));
973 return hr;
974}
975
976static HRESULT directSoundCaptureClose(PDSOUNDSTREAMIN pDSoundStrmIn)
977{
978 AssertPtrReturn(pDSoundStrmIn, E_POINTER);
979
980 DSLOG(("DSound: pDSoundStrmIn=%p, pDSCB=%p\n", pDSoundStrmIn, pDSoundStrmIn->pDSCB));
981
982 HRESULT hr = S_OK;
983
984 if (pDSoundStrmIn->pDSCB)
985 {
986 hr = IDirectSoundCaptureBuffer_Stop(pDSoundStrmIn->pDSCB);
987 if (SUCCEEDED(hr))
988 {
989 IDirectSoundCaptureBuffer8_Release(pDSoundStrmIn->pDSCB);
990 pDSoundStrmIn->pDSCB = NULL;
991 }
992 else
993 DSLOGREL(("DSound: Stopping capture buffer failed with %Rhrc\n", hr));
994 }
995
996 if (SUCCEEDED(hr))
997 directSoundCaptureInterfaceRelease(pDSoundStrmIn);
998
999 LogFlowFunc(("Returning %Rhrc\n", hr));
1000 return hr;
1001}
1002
1003static HRESULT directSoundCaptureOpen(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
1004{
1005 AssertPtrReturn(pThis, E_POINTER);
1006 AssertPtrReturn(pDSoundStrmIn, E_POINTER);
1007
1008 DSLOG(("DSound: pDSoundStrmIn=%p, cbBufferIn=%ld, uHz=%RU32, cChannels=%RU8, cBits=%RU8, fSigned=%RTbool\n",
1009 pDSoundStrmIn,
1010 pThis->cfg.cbBufferIn,
1011 pDSoundStrmIn->strmIn.Props.uHz,
1012 pDSoundStrmIn->strmIn.Props.cChannels,
1013 pDSoundStrmIn->strmIn.Props.cBits,
1014 pDSoundStrmIn->strmIn.Props.fSigned));
1015
1016 if (pDSoundStrmIn->pDSCB != NULL)
1017 {
1018 /* Should not happen but be forgiving. */
1019 DSLOGREL(("DSound: DirectSoundCaptureBuffer already exists\n"));
1020 directSoundCaptureClose(pDSoundStrmIn);
1021 }
1022
1023 WAVEFORMATEX wfx;
1024 int rc = dsoundWaveFmtFromCfg(&pDSoundStrmIn->streamCfg, &wfx);
1025 if (RT_FAILURE(rc))
1026 return E_INVALIDARG;
1027
1028 HRESULT hr = directSoundCaptureInterfaceCreate(pThis, pDSoundStrmIn);
1029 if (FAILED(hr))
1030 return hr;
1031
1032 do /* To use breaks. */
1033 {
1034 LPDIRECTSOUNDCAPTUREBUFFER pDSCB = NULL;
1035 DSCBUFFERDESC bd;
1036 RT_ZERO(bd);
1037 bd.dwSize = sizeof(bd);
1038 bd.lpwfxFormat = &wfx;
1039 bd.dwBufferBytes = pThis->cfg.cbBufferIn;
1040 hr = IDirectSoundCapture_CreateCaptureBuffer(pDSoundStrmIn->pDSC,
1041 &bd, &pDSCB, NULL);
1042 if (FAILED(hr))
1043 {
1044 if (hr == E_ACCESSDENIED)
1045 {
1046 DSLOGREL(("DSound: Capturing input from host not possible, access denied\n"));
1047 }
1048 else
1049 DSLOGREL(("DSound: Creating capture buffer failed with %Rhrc\n", hr));
1050 break;
1051 }
1052
1053 hr = IDirectSoundCaptureBuffer_QueryInterface(pDSCB, IID_IDirectSoundCaptureBuffer8, (void **)&pDSoundStrmIn->pDSCB);
1054 IDirectSoundCaptureBuffer_Release(pDSCB);
1055 if (FAILED(hr))
1056 {
1057 DSLOGREL(("DSound: Querying interface for capture buffer failed with %Rhrc\n", hr));
1058 break;
1059 }
1060
1061 /*
1062 * Query the actual parameters.
1063 */
1064 DWORD cbReadPos = 0;
1065 hr = IDirectSoundCaptureBuffer8_GetCurrentPosition(pDSoundStrmIn->pDSCB, NULL, &cbReadPos);
1066 if (FAILED(hr))
1067 {
1068 cbReadPos = 0;
1069 DSLOGREL(("DSound: Getting capture position failed with %Rhrc\n", hr));
1070 }
1071
1072 RT_ZERO(wfx);
1073 hr = IDirectSoundCaptureBuffer8_GetFormat(pDSoundStrmIn->pDSCB, &wfx, sizeof(wfx), NULL);
1074 if (FAILED(hr))
1075 {
1076 DSLOGREL(("DSound: Getting capture format failed with %Rhrc\n", hr));
1077 break;
1078 }
1079
1080 DSCBCAPS bc;
1081 RT_ZERO(bc);
1082 bc.dwSize = sizeof(bc);
1083 hr = IDirectSoundCaptureBuffer8_GetCaps(pDSoundStrmIn->pDSCB, &bc);
1084 if (FAILED(hr))
1085 {
1086 DSLOGREL(("Getting capture capabilities failed with %Rhrc\n", hr));
1087 break;
1088 }
1089
1090 DSLOG(("DSound: Capture format:\n"
1091 " dwBufferBytes = %RI32\n"
1092 " dwFlags = 0x%x\n"
1093 " wFormatTag = %RI16\n"
1094 " nChannels = %RI16\n"
1095 " nSamplesPerSec = %RU32\n"
1096 " nAvgBytesPerSec = %RU32\n"
1097 " nBlockAlign = %RI16\n"
1098 " wBitsPerSample = %RI16\n"
1099 " cbSize = %RI16\n",
1100 bc.dwBufferBytes,
1101 bc.dwFlags,
1102 wfx.wFormatTag,
1103 wfx.nChannels,
1104 wfx.nSamplesPerSec,
1105 wfx.nAvgBytesPerSec,
1106 wfx.nBlockAlign,
1107 wfx.wBitsPerSample,
1108 wfx.cbSize));
1109
1110 if (bc.dwBufferBytes & pDSoundStrmIn->strmIn.Props.uAlign)
1111 DSLOGREL(("DSound: Capture GetCaps returned misaligned buffer: size %RU32, alignment %RU32\n",
1112 bc.dwBufferBytes, pDSoundStrmIn->strmIn.Props.uAlign + 1));
1113
1114 if (bc.dwBufferBytes != pThis->cfg.cbBufferIn)
1115 DSLOGREL(("DSound: Capture buffer size mismatched: DirectSound %RU32, requested %RU32 bytes\n",
1116 bc.dwBufferBytes, pThis->cfg.cbBufferIn));
1117
1118 /* Initial state: reading at the initial capture position, no error. */
1119 pDSoundStrmIn->csCaptureReadPos = cbReadPos >> pDSoundStrmIn->strmIn.Props.cShift;
1120 pDSoundStrmIn->csCaptureBufferSize = bc.dwBufferBytes >> pDSoundStrmIn->strmIn.Props.cShift;
1121 pDSoundStrmIn->hrLastCaptureIn = S_OK;
1122
1123 DSLOG(("DSound: csCaptureReadPos=%RU32, csCaptureBufferSize=%RU32\n",
1124 pDSoundStrmIn->csCaptureReadPos, pDSoundStrmIn->csCaptureBufferSize));
1125
1126 } while (0);
1127
1128 if (FAILED(hr))
1129 directSoundCaptureClose(pDSoundStrmIn);
1130
1131 LogFlowFunc(("Returning %Rhrc\n", hr));
1132 return hr;
1133}
1134
1135static HRESULT directSoundCaptureStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
1136{
1137 AssertPtrReturn(pThis , E_POINTER);
1138 AssertPtrReturn(pDSoundStrmIn, E_POINTER);
1139
1140 NOREF(pThis);
1141
1142 HRESULT hr;
1143
1144 if (pDSoundStrmIn->pDSCB)
1145 {
1146 DSLOG(("DSound: Stopping capture\n"));
1147
1148 hr = IDirectSoundCaptureBuffer_Stop(pDSoundStrmIn->pDSCB);
1149 if (FAILED(hr))
1150 DSLOGREL(("DSound: Stopping capture buffer failed with %Rhrc\n", hr));
1151 }
1152 else
1153 hr = E_UNEXPECTED;
1154
1155 if (SUCCEEDED(hr))
1156 pDSoundStrmIn->fEnabled = false;
1157
1158 LogFlowFunc(("Returning %Rhrc\n", hr));
1159 return hr;
1160}
1161
1162static HRESULT directSoundCaptureStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
1163{
1164 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1165 AssertPtrReturn(pDSoundStrmIn, VERR_INVALID_POINTER);
1166
1167 HRESULT hr;
1168 if (pDSoundStrmIn->pDSCB != NULL)
1169 {
1170 DWORD dwStatus;
1171 hr = IDirectSoundCaptureBuffer8_GetStatus(pDSoundStrmIn->pDSCB, &dwStatus);
1172 if (FAILED(hr))
1173 {
1174 DSLOGREL(("DSound: Retrieving capture status failed with %Rhrc\n", hr));
1175 }
1176 else
1177 {
1178 if (dwStatus & DSCBSTATUS_CAPTURING)
1179 {
1180 DSLOG(("DSound: Already capturing\n"));
1181 }
1182 else
1183 {
1184 DWORD fFlags = 0;
1185#ifndef VBOX_WITH_AUDIO_CALLBACKS
1186 fFlags |= DSCBSTART_LOOPING;
1187#endif
1188 DSLOG(("DSound: Starting to capture\n"));
1189 hr = IDirectSoundCaptureBuffer8_Start(pDSoundStrmIn->pDSCB, fFlags);
1190 if (FAILED(hr))
1191 DSLOGREL(("DSound: Starting to capture failed with %Rhrc\n", hr));
1192 }
1193 }
1194 }
1195 else
1196 hr = E_UNEXPECTED;
1197
1198 if (SUCCEEDED(hr))
1199 pDSoundStrmIn->fEnabled = true;
1200
1201 LogFlowFunc(("Returning %Rhrc\n", hr));
1202 return hr;
1203}
1204
1205static int dsoundDevAdd(PRTLISTANCHOR pList, LPGUID lpGUID,
1206 LPCWSTR lpwstrDescription, PDSOUNDDEV *ppDev)
1207{
1208 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1209 AssertPtrReturn(lpGUID, VERR_INVALID_POINTER);
1210 AssertPtrReturn(lpwstrDescription, VERR_INVALID_POINTER);
1211
1212 PDSOUNDDEV pDev = (PDSOUNDDEV)RTMemAlloc(sizeof(DSOUNDDEV));
1213 if (!pDev)
1214 return VERR_NO_MEMORY;
1215
1216 int rc = RTUtf16ToUtf8(lpwstrDescription, &pDev->pszName);
1217 if (RT_SUCCESS(rc))
1218 memcpy(&pDev->Guid, lpGUID, sizeof(GUID));
1219
1220 if (RT_SUCCESS(rc))
1221 RTListAppend(pList, &pDev->Node);
1222
1223 if (ppDev)
1224 *ppDev = pDev;
1225
1226 return rc;
1227}
1228
1229static void dsoundDeviceRemove(PDSOUNDDEV pDev)
1230{
1231 if (pDev)
1232 {
1233 RTStrFree(pDev->pszName);
1234 pDev->pszName = NULL;
1235
1236 RTListNodeRemove(&pDev->Node);
1237
1238 RTMemFree(pDev);
1239 }
1240}
1241
1242static void dsoundLogDevice(const char *pszType, LPGUID lpGUID, LPCWSTR lpwstrDescription, LPCWSTR lpwstrModule)
1243{
1244 char *pszGUID = dsoundGUIDToUtf8StrA(lpGUID);
1245 /* This always has to be in the release log. */
1246 LogRel(("DSound: %s: GUID: %s [%ls] (Module: %ls)\n",
1247 pszType, pszGUID? pszGUID: "{?}", lpwstrDescription, lpwstrModule));
1248 RTStrFree(pszGUID);
1249}
1250
1251static BOOL CALLBACK dsoundDevicesEnumCbPlayback(LPGUID lpGUID, LPCWSTR lpwstrDescription,
1252 LPCWSTR lpwstrModule, LPVOID lpContext)
1253{
1254 PDSOUNDENUMCBCTX pCtx = (PDSOUNDENUMCBCTX)lpContext;
1255 AssertPtrReturn(pCtx, FALSE);
1256 AssertPtrReturn(pCtx->pDrv, FALSE);
1257
1258 if (!lpGUID)
1259 return TRUE;
1260
1261 AssertPtrReturn(lpwstrDescription, FALSE);
1262 /* Do not care about lpwstrModule. */
1263
1264 if (pCtx->fFlags & DSOUNDENUMCBFLAGS_LOG)
1265 dsoundLogDevice("Output", lpGUID, lpwstrDescription, lpwstrModule);
1266
1267 int rc = dsoundDevAdd(&pCtx->pDrv->lstDevOutput,
1268 lpGUID, lpwstrDescription, NULL /* ppDev */);
1269 if (RT_FAILURE(rc))
1270 return FALSE; /* Abort enumeration. */
1271
1272 pCtx->cDevOut++;
1273
1274 return TRUE;
1275}
1276
1277static BOOL CALLBACK dsoundDevicesEnumCbCapture(LPGUID lpGUID, LPCWSTR lpwstrDescription,
1278 LPCWSTR lpwstrModule, LPVOID lpContext)
1279{
1280 PDSOUNDENUMCBCTX pCtx = (PDSOUNDENUMCBCTX)lpContext;
1281 AssertPtrReturn(pCtx, FALSE);
1282 AssertPtrReturn(pCtx->pDrv, FALSE);
1283
1284 if (!lpGUID)
1285 return TRUE;
1286
1287 if (pCtx->fFlags & DSOUNDENUMCBFLAGS_LOG)
1288 dsoundLogDevice("Input", lpGUID, lpwstrDescription, lpwstrModule);
1289
1290 int rc = dsoundDevAdd(&pCtx->pDrv->lstDevInput,
1291 lpGUID, lpwstrDescription, NULL /* ppDev */);
1292 if (RT_FAILURE(rc))
1293 return FALSE; /* Abort enumeration. */
1294
1295 pCtx->cDevIn++;
1296
1297 return TRUE;
1298}
1299
1300/**
1301 * Does a (Re-)enumeration of the host's playback + capturing devices.
1302 *
1303 * @return IPRT status code.
1304 * @param pThis Host audio driver instance.
1305 * @param pEnmCtx Enumeration context to use.
1306 * @param fEnum Enumeration flags.
1307 */
1308static int dsoundDevicesEnumerate(PDRVHOSTDSOUND pThis, PDSOUNDENUMCBCTX pEnmCtx, uint32_t fEnum)
1309{
1310 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1311 AssertPtrReturn(pEnmCtx, VERR_INVALID_POINTER);
1312
1313 dsoundDevicesClear(pThis);
1314
1315 RTLDRMOD hDSound = NULL;
1316 int rc = RTLdrLoadSystem("dsound.dll", true /*fNoUnload*/, &hDSound);
1317 if (RT_SUCCESS(rc))
1318 {
1319 PFNDIRECTSOUNDENUMERATEW pfnDirectSoundEnumerateW = NULL;
1320 PFNDIRECTSOUNDCAPTUREENUMERATEW pfnDirectSoundCaptureEnumerateW = NULL;
1321
1322 rc = RTLdrGetSymbol(hDSound, "DirectSoundEnumerateW", (void**)&pfnDirectSoundEnumerateW);
1323 if (RT_SUCCESS(rc))
1324 rc = RTLdrGetSymbol(hDSound, "DirectSoundCaptureEnumerateW", (void**)&pfnDirectSoundCaptureEnumerateW);
1325
1326 if (RT_SUCCESS(rc))
1327 {
1328 HRESULT hr = pfnDirectSoundEnumerateW(&dsoundDevicesEnumCbPlayback, pEnmCtx);
1329 if (FAILED(hr))
1330 LogRel2(("DSound: Error enumerating host playback devices: %Rhrc\n", hr));
1331
1332 hr = pfnDirectSoundCaptureEnumerateW(&dsoundDevicesEnumCbCapture, pEnmCtx);
1333 if (FAILED(hr))
1334 LogRel2(("DSound: Error enumerating host capturing devices: %Rhrc\n", hr));
1335
1336 if (fEnum & DSOUNDENUMCBFLAGS_LOG)
1337 {
1338 LogRel2(("DSound: Found %RU8 host playback devices\n", pEnmCtx->cDevOut));
1339 LogRel2(("DSound: Found %RU8 host capturing devices\n", pEnmCtx->cDevIn));
1340 }
1341 }
1342
1343 RTLdrClose(hDSound);
1344 }
1345 else
1346 {
1347 /* No dsound.dll on this system. */
1348 LogRel2(("DSound: Could not load dsound.dll: %Rrc\n", rc));
1349 }
1350
1351 return rc;
1352}
1353
1354/**
1355 * Updates this host driver's internal status, according to the global, overall input/output
1356 * state and all connected (native) audio streams.
1357 *
1358 * @param pThis Host audio driver instance.
1359 * @param pCfg Where to store the backend configuration. Optional.
1360 * @param fEnum Enumeration flags.
1361 */
1362void dsoundUpdateStatusInternalEx(PDRVHOSTDSOUND pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum)
1363{
1364 AssertPtrReturnVoid(pThis);
1365 /* pCfg is optional. */
1366
1367 PDMAUDIOBACKENDCFG Cfg;
1368 RT_ZERO(Cfg);
1369
1370 Cfg.cbStreamOut = sizeof(DSOUNDSTREAMOUT);
1371 Cfg.cbStreamIn = sizeof(DSOUNDSTREAMIN);
1372
1373 DSOUNDENUMCBCTX cbCtx = { pThis, fEnum, 0, 0 };
1374
1375 int rc = dsoundDevicesEnumerate(pThis, &cbCtx, fEnum);
1376 if (RT_SUCCESS(rc))
1377 {
1378#ifdef VBOX_WITH_AUDIO_CALLBACKS
1379 if ( pThis->fEnabledOut != RT_BOOL(cbCtx.cDevOut)
1380 || pThis->fEnabledIn != RT_BOOL(cbCtx.cDevIn))
1381 {
1382 /** @todo Use a registered callback to the audio connector (e.g "OnConfigurationChanged") to
1383 * let the connector know that something has changed within the host backend. */
1384 }
1385#else
1386 pThis->fEnabledOut = RT_BOOL(cbCtx.cDevOut);
1387 pThis->fEnabledIn = RT_BOOL(cbCtx.cDevIn);
1388#endif
1389
1390 Cfg.cSources = cbCtx.cDevIn;
1391 Cfg.cSinks = cbCtx.cDevOut;
1392 Cfg.cMaxStreamsIn = UINT32_MAX;
1393 Cfg.cMaxStreamsOut = UINT32_MAX;
1394
1395 if (pCfg)
1396 memcpy(pCfg, &Cfg, sizeof(PDMAUDIOBACKENDCFG));
1397 }
1398
1399 LogFlowFuncLeaveRC(rc);
1400}
1401
1402void dsoundUpdateStatusInternal(PDRVHOSTDSOUND pThis)
1403{
1404 dsoundUpdateStatusInternalEx(pThis, NULL /* pCfg */, 0 /* fEnum */);
1405}
1406
1407/*
1408 * PDMIHOSTAUDIO
1409 */
1410
1411static DECLCALLBACK(int) drvHostDSoundInitOut(PPDMIHOSTAUDIO pInterface,
1412 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
1413 uint32_t *pcSamples)
1414{
1415 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1416 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
1417 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1418 /* pcSamples is optional. */
1419
1420 LogFlowFunc(("pHstStrmOut=%p, pCfg=%p\n", pHstStrmOut, pCfg));
1421
1422 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1423 PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
1424
1425 pDSoundStrmOut->streamCfg = *pCfg;
1426 pDSoundStrmOut->streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
1427
1428 int rc = DrvAudioStreamCfgToProps(&pDSoundStrmOut->streamCfg, &pDSoundStrmOut->strmOut.Props);
1429 if (RT_SUCCESS(rc))
1430 {
1431 pDSoundStrmOut->pDS = NULL;
1432 pDSoundStrmOut->pDSB = NULL;
1433 pDSoundStrmOut->cbPlayWritePos = 0;
1434 pDSoundStrmOut->fRestartPlayback = true;
1435 pDSoundStrmOut->csPlaybackBufferSize = 0;
1436
1437 if (pcSamples)
1438 *pcSamples = pThis->cfg.cbBufferOut >> pHstStrmOut->Props.cShift;
1439
1440 /* Try to open playback in case the device is already there. */
1441 directSoundPlayOpen(pThis, pDSoundStrmOut);
1442 }
1443 else
1444 {
1445 RT_ZERO(pDSoundStrmOut->streamCfg);
1446 }
1447
1448 LogFlowFuncLeaveRC(rc);
1449 return rc;
1450}
1451
1452static DECLCALLBACK(int) drvHostDSoundControlOut(PPDMIHOSTAUDIO pInterface,
1453 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PDMAUDIOSTREAMCMD enmStreamCmd)
1454{
1455 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1456 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
1457
1458 LogFlowFunc(("pHstStrmOut=%p, cmd=%d\n", pHstStrmOut, enmStreamCmd));
1459
1460 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1461 PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
1462
1463 int rc = VINF_SUCCESS;
1464
1465 HRESULT hr;
1466 switch (enmStreamCmd)
1467 {
1468 case PDMAUDIOSTREAMCMD_ENABLE:
1469 case PDMAUDIOSTREAMCMD_RESUME:
1470 {
1471 DSLOG(("DSound: Playback PDMAUDIOSTREAMCMD_ENABLE\n"));
1472 /* Try to start playback. If it fails, then reopen and try again. */
1473 hr = directSoundPlayStart(pThis, pDSoundStrmOut);
1474 if (FAILED(hr))
1475 {
1476 hr = directSoundPlayClose(pThis, pDSoundStrmOut);
1477 if (SUCCEEDED(hr))
1478 hr = directSoundPlayOpen(pThis, pDSoundStrmOut);
1479 if (SUCCEEDED(hr))
1480 hr = directSoundPlayStart(pThis, pDSoundStrmOut);
1481 }
1482
1483 if (FAILED(hr))
1484 rc = VERR_NOT_SUPPORTED;
1485 break;
1486 }
1487
1488 case PDMAUDIOSTREAMCMD_DISABLE:
1489 case PDMAUDIOSTREAMCMD_PAUSE:
1490 {
1491 DSLOG(("DSound: Playback PDMAUDIOSTREAMCMD_DISABLE\n"));
1492 hr = directSoundPlayStop(pThis, pDSoundStrmOut);
1493 if (FAILED(hr))
1494 rc = VERR_NOT_SUPPORTED;
1495 break;
1496 }
1497
1498 default:
1499 {
1500 AssertMsgFailed(("Invalid command: %ld\n", enmStreamCmd));
1501 rc = VERR_INVALID_PARAMETER;
1502 break;
1503 }
1504 }
1505
1506 LogFlowFuncLeaveRC(rc);
1507 return rc;
1508}
1509
1510static DECLCALLBACK(int) drvHostDSoundPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
1511 uint32_t *pcSamplesPlayed)
1512{
1513 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1514 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
1515 /* pcSamplesPlayed is optional. */
1516
1517 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1518 PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
1519
1520 int rc = VINF_SUCCESS;
1521 uint32_t cReadTotal = 0;
1522
1523#ifdef DEBUG_andy
1524 LogFlowFuncEnter();
1525#endif
1526
1527 do /* to use 'break' */
1528 {
1529 DWORD cbBuffer, cbFree, cbPlayPos;
1530 rc = dsoundGetPosOut(pThis, pDSoundStrmOut, &cbBuffer, &cbFree, &cbPlayPos);
1531 if (RT_FAILURE(rc))
1532 break;
1533
1534 /*
1535 * Check for full buffer, do not allow the cbPlayWritePos to catch cbPlayPos during playback,
1536 * i.e. always leave a free space for 1 audio sample.
1537 */
1538 const DWORD cbSample = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, 1);
1539 if (cbFree <= cbSample)
1540 break;
1541 cbFree -= cbSample;
1542
1543 uint32_t csLive = AudioMixBufAvail(&pHstStrmOut->MixBuf);
1544 uint32_t cbLive = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, csLive);
1545
1546 /* Do not write more than available space in the DirectSound playback buffer. */
1547 cbLive = RT_MIN(cbFree, cbLive);
1548
1549 cbLive &= ~pHstStrmOut->Props.uAlign;
1550 if (cbLive == 0 || cbLive > cbBuffer)
1551 {
1552 DSLOG(("DSound: cbLive=%RU32, cbBuffer=%ld, cbPlayWritePos=%ld, cbPlayPos=%ld\n",
1553 cbLive, cbBuffer, pDSoundStrmOut->cbPlayWritePos, cbPlayPos));
1554 break;
1555 }
1556
1557 LPDIRECTSOUNDBUFFER8 pDSB = pDSoundStrmOut->pDSB;
1558 AssertPtr(pDSB);
1559
1560 LPVOID pv1, pv2;
1561 DWORD cb1, cb2;
1562 HRESULT hr = directSoundPlayLock(pThis, pDSB, &pHstStrmOut->Props, pDSoundStrmOut->cbPlayWritePos, cbLive,
1563 &pv1, &pv2, &cb1, &cb2, 0 /* dwFlags */);
1564 if (FAILED(hr))
1565 {
1566 rc = VERR_ACCESS_DENIED;
1567 break;
1568 }
1569
1570 DWORD len1 = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cb1);
1571 DWORD len2 = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cb2);
1572
1573 uint32_t cRead = 0;
1574
1575 if (pv1 && cb1)
1576 {
1577 rc = AudioMixBufReadCirc(&pHstStrmOut->MixBuf, pv1, cb1, &cRead);
1578 if (RT_SUCCESS(rc))
1579 cReadTotal += cRead;
1580 }
1581
1582 if ( RT_SUCCESS(rc)
1583 && cReadTotal == len1
1584 && pv2 && cb2)
1585 {
1586 rc = AudioMixBufReadCirc(&pHstStrmOut->MixBuf, pv2, cb2, &cRead);
1587 if (RT_SUCCESS(rc))
1588 cReadTotal += cRead;
1589 }
1590
1591 directSoundPlayUnlock(pThis, pDSB, pv1, pv2, cb1, cb2);
1592
1593 pDSoundStrmOut->cbPlayWritePos =
1594 (pDSoundStrmOut->cbPlayWritePos + AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cReadTotal)) % cbBuffer;
1595
1596 DSLOGF(("DSound: %RU32 (%RU32 samples) out of %RU32%s, buffer write pos %ld, rc=%Rrc\n",
1597 AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cReadTotal), cReadTotal, cbLive,
1598 cbLive != AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cReadTotal) ? " !!!": "",
1599 pDSoundStrmOut->cbPlayWritePos, rc));
1600
1601 if (cReadTotal)
1602 {
1603 AudioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
1604 rc = VINF_SUCCESS; /* Played something. */
1605 }
1606
1607 if (RT_FAILURE(rc))
1608 break;
1609
1610 if (pDSoundStrmOut->fRestartPlayback)
1611 {
1612 /*
1613 * The playback has been just started.
1614 * Some samples of the new sound have been copied to the buffer
1615 * and it can start playing.
1616 */
1617 pDSoundStrmOut->fRestartPlayback = false;
1618
1619 DWORD fFlags = 0;
1620#ifndef VBOX_WITH_AUDIO_CALLBACKS
1621 fFlags |= DSCBSTART_LOOPING;
1622#endif
1623 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
1624 {
1625 hr = IDirectSoundBuffer8_Play(pDSoundStrmOut->pDSB, 0, 0, fFlags);
1626 if ( SUCCEEDED(hr)
1627 || hr != DSERR_BUFFERLOST)
1628 break;
1629 else
1630 {
1631 LogFlowFunc(("Restarting playback failed due to lost buffer, restoring ...\n"));
1632 directSoundPlayRestore(pThis, pDSoundStrmOut->pDSB);
1633 }
1634 }
1635
1636 if (FAILED(hr))
1637 {
1638 DSLOGREL(("DSound: Starting playback failed with %Rhrc\n", hr));
1639 rc = VERR_NOT_SUPPORTED;
1640 break;
1641 }
1642 }
1643
1644 } while (0);
1645
1646 if (RT_FAILURE(rc))
1647 {
1648 dsoundUpdateStatusInternal(pThis);
1649 }
1650 else
1651 {
1652 if (pcSamplesPlayed)
1653 *pcSamplesPlayed = cReadTotal;
1654 }
1655
1656 LogFlowFuncLeaveRC(rc);
1657 return rc;
1658}
1659
1660static DECLCALLBACK(int) drvHostDSoundFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
1661{
1662 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1663 PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
1664
1665 directSoundPlayClose(pThis, pDSoundStrmOut);
1666
1667 pDSoundStrmOut->cbPlayWritePos = 0;
1668 pDSoundStrmOut->fRestartPlayback = true;
1669 pDSoundStrmOut->csPlaybackBufferSize = 0;
1670
1671 RT_ZERO(pDSoundStrmOut->streamCfg);
1672
1673 return VINF_SUCCESS;
1674}
1675
1676static DECLCALLBACK(int) drvHostDSoundInitIn(PPDMIHOSTAUDIO pInterface,
1677 PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
1678 PDMAUDIORECSOURCE enmRecSource,
1679 uint32_t *pcSamples)
1680{
1681 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1682 PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
1683
1684 LogFlowFunc(("pHstStrmIn=%p, pAudioSettings=%p, enmRecSource=%ld\n",
1685 pHstStrmIn, pCfg, enmRecSource));
1686
1687 pDSoundStrmIn->streamCfg = *pCfg;
1688 pDSoundStrmIn->streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
1689
1690 /** @todo caller should already init Props? */
1691 int rc = DrvAudioStreamCfgToProps(&pDSoundStrmIn->streamCfg, &pHstStrmIn->Props);
1692 if (RT_SUCCESS(rc))
1693 {
1694 /* Init the stream structure and save relevant information to it. */
1695 pDSoundStrmIn->csCaptureReadPos = 0;
1696 pDSoundStrmIn->csCaptureBufferSize = 0;
1697 pDSoundStrmIn->pDSC = NULL;
1698 pDSoundStrmIn->pDSCB = NULL;
1699 pDSoundStrmIn->enmRecSource = enmRecSource;
1700 pDSoundStrmIn->hrLastCaptureIn = S_OK;
1701
1702 if (pcSamples)
1703 *pcSamples = pThis->cfg.cbBufferIn >> pHstStrmIn->Props.cShift;
1704
1705 /* Try to open capture in case the device is already there. */
1706 directSoundCaptureOpen(pThis, pDSoundStrmIn);
1707 }
1708 else
1709 {
1710 RT_ZERO(pDSoundStrmIn->streamCfg);
1711 }
1712
1713 LogFlowFuncLeaveRC(rc);
1714 return rc;
1715}
1716
1717static DECLCALLBACK(int) drvHostDSoundControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
1718 PDMAUDIOSTREAMCMD enmStreamCmd)
1719{
1720 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1721 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1722
1723 LogFlowFunc(("pHstStrmIn=%p, enmStreamCmd=%ld\n", pHstStrmIn, enmStreamCmd));
1724
1725 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1726 PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
1727
1728 int rc = VINF_SUCCESS;
1729
1730 HRESULT hr;
1731 switch (enmStreamCmd)
1732 {
1733 case PDMAUDIOSTREAMCMD_ENABLE:
1734 case PDMAUDIOSTREAMCMD_RESUME:
1735 {
1736 /* Try to start capture. If it fails, then reopen and try again. */
1737 hr = directSoundCaptureStart(pThis, pDSoundStrmIn);
1738 if (FAILED(hr))
1739 {
1740 hr = directSoundCaptureClose(pDSoundStrmIn);
1741 if (SUCCEEDED(hr))
1742 {
1743 hr = directSoundCaptureOpen(pThis, pDSoundStrmIn);
1744 if (SUCCEEDED(hr))
1745 hr = directSoundCaptureStart(pThis, pDSoundStrmIn);
1746 }
1747 }
1748
1749 if (FAILED(hr))
1750 rc = VERR_NOT_SUPPORTED;
1751 break;
1752 }
1753
1754 case PDMAUDIOSTREAMCMD_DISABLE:
1755 case PDMAUDIOSTREAMCMD_PAUSE:
1756 {
1757 hr = directSoundCaptureStop(pThis, pDSoundStrmIn);
1758 if (FAILED(hr))
1759 rc = VERR_NOT_SUPPORTED;
1760 break;
1761 }
1762
1763 default:
1764 {
1765 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
1766 rc = VERR_INVALID_PARAMETER;
1767 break;
1768 }
1769 }
1770
1771 return rc;
1772}
1773
1774static DECLCALLBACK(int) drvHostDSoundCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
1775 uint32_t *pcSamplesCaptured)
1776{
1777 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1778
1779 PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
1780 LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB = pDSoundStrmIn->pDSCB;
1781
1782 int rc = VINF_SUCCESS;
1783
1784 uint32_t cCaptured = 0;
1785
1786 do
1787 {
1788 if (pDSCB == NULL)
1789 {
1790 rc = VERR_NOT_AVAILABLE;
1791 break;
1792 }
1793
1794 /* Get DirectSound capture position in bytes. */
1795 DWORD cbReadPos;
1796 HRESULT hr = IDirectSoundCaptureBuffer_GetCurrentPosition(pDSCB, NULL, &cbReadPos);
1797 if (FAILED(hr))
1798 {
1799 if (hr != pDSoundStrmIn->hrLastCaptureIn)
1800 {
1801 DSLOGREL(("DSound: Getting capture position failed with %Rhrc\n", hr));
1802 pDSoundStrmIn->hrLastCaptureIn = hr;
1803 }
1804
1805 rc = VERR_NOT_AVAILABLE;
1806 break;
1807 }
1808
1809 pDSoundStrmIn->hrLastCaptureIn = hr;
1810
1811 if (cbReadPos & pHstStrmIn->Props.uAlign)
1812 DSLOGF(("DSound: Misaligned capture read position %ld (alignment: %RU32)\n", cbReadPos, pHstStrmIn->Props.uAlign));
1813
1814 /* Capture position in samples. */
1815 DWORD csReadPos = cbReadPos >> pHstStrmIn->Props.cShift;
1816
1817 /* Number of samples available in the DirectSound capture buffer. */
1818 DWORD csCaptured = dsoundRingDistance(csReadPos, pDSoundStrmIn->csCaptureReadPos, pDSoundStrmIn->csCaptureBufferSize);
1819 if (csCaptured == 0)
1820 break;
1821
1822 /* Using as an intermediate not circular buffer. */
1823 AudioMixBufReset(&pHstStrmIn->MixBuf);
1824
1825 /* Get number of free samples in the mix buffer and check that is has free space */
1826 uint32_t csMixFree = AudioMixBufFree(&pHstStrmIn->MixBuf);
1827 if (csMixFree == 0)
1828 {
1829 DSLOGF(("DSound: Capture buffer full\n"));
1830 break;
1831 }
1832
1833 DSLOGF(("DSound: Capture csMixFree=%RU32, csReadPos=%ld, csCaptureReadPos=%ld, csCaptured=%ld\n",
1834 csMixFree, csReadPos, pDSoundStrmIn->csCaptureReadPos, csCaptured));
1835
1836 /* No need to fetch more samples than mix buffer can receive. */
1837 csCaptured = RT_MIN(csCaptured, csMixFree);
1838
1839 /* Lock relevant range in the DirectSound capture buffer. */
1840 LPVOID pv1, pv2;
1841 DWORD cb1, cb2;
1842 hr = directSoundCaptureLock(pDSCB, &pHstStrmIn->Props,
1843 AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, pDSoundStrmIn->csCaptureReadPos), /* dwOffset */
1844 AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, csCaptured), /* dwBytes */
1845 &pv1, &pv2, &cb1, &cb2,
1846 0 /* dwFlags */);
1847 if (FAILED(hr))
1848 {
1849 rc = VERR_ACCESS_DENIED;
1850 break;
1851 }
1852
1853 DWORD len1 = AUDIOMIXBUF_B2S(&pHstStrmIn->MixBuf, cb1);
1854 DWORD len2 = AUDIOMIXBUF_B2S(&pHstStrmIn->MixBuf, cb2);
1855
1856 uint32_t csWrittenTotal = 0;
1857 uint32_t csWritten;
1858 if (pv1 && len1)
1859 {
1860 rc = AudioMixBufWriteAt(&pHstStrmIn->MixBuf, 0 /* offWrite */,
1861 pv1, cb1, &csWritten);
1862 if (RT_SUCCESS(rc))
1863 csWrittenTotal += csWritten;
1864 }
1865
1866 if ( RT_SUCCESS(rc)
1867 && csWrittenTotal == len1
1868 && pv2 && len2)
1869 {
1870 rc = AudioMixBufWriteAt(&pHstStrmIn->MixBuf, csWrittenTotal,
1871 pv2, cb2, &csWritten);
1872 if (RT_SUCCESS(rc))
1873 csWrittenTotal += csWritten;
1874 }
1875
1876 directSoundCaptureUnlock(pDSCB, pv1, pv2, cb1, cb2);
1877
1878 if (csWrittenTotal) /* Captured something? */
1879 rc = AudioMixBufMixToParent(&pHstStrmIn->MixBuf, csWrittenTotal, &cCaptured);
1880
1881 if (RT_SUCCESS(rc))
1882 {
1883 pDSoundStrmIn->csCaptureReadPos = (pDSoundStrmIn->csCaptureReadPos + cCaptured) % pDSoundStrmIn->csCaptureBufferSize;
1884 DSLOGF(("DSound: Capture %ld (%ld+%ld), processed %RU32/%RU32\n",
1885 csCaptured, len1, len2, cCaptured, csWrittenTotal));
1886 }
1887
1888 } while (0);
1889
1890 if (RT_FAILURE(rc))
1891 {
1892 dsoundUpdateStatusInternal(pThis);
1893 }
1894 else
1895 {
1896 if (pcSamplesCaptured)
1897 *pcSamplesCaptured = cCaptured;
1898 }
1899
1900 LogFlowFuncLeaveRC(rc);
1901 return rc;
1902}
1903
1904static DECLCALLBACK(int) drvHostDSoundFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
1905{
1906 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1907 PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
1908
1909 directSoundCaptureClose(pDSoundStrmIn);
1910
1911 pDSoundStrmIn->csCaptureReadPos = 0;
1912 pDSoundStrmIn->csCaptureBufferSize = 0;
1913 RT_ZERO(pDSoundStrmIn->streamCfg);
1914
1915 return VINF_SUCCESS;
1916}
1917
1918/** @todo Replace PDMAUDIODIR with a (registered? unique) channel ID to provide multi-channel input/output. */
1919static DECLCALLBACK(bool) drvHostDSoundIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
1920{
1921 AssertPtrReturn(pInterface, false);
1922
1923 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1924
1925 if (enmDir == PDMAUDIODIR_IN)
1926 return pThis->fEnabledIn;
1927
1928 return pThis->fEnabledOut;
1929}
1930
1931static DECLCALLBACK(int) drvHostDSoundGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
1932{
1933 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1934 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1935
1936 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1937
1938 dsoundUpdateStatusInternalEx(pThis, pCfg, 0 /* fEnum */);
1939
1940 return VINF_SUCCESS;
1941}
1942
1943#ifdef VBOX_WITH_AUDIO_CALLBACKS
1944static int dsoundNotifyThread(PDRVHOSTDSOUND pThis, bool fShutdown)
1945{
1946 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1947
1948 if (fShutdown)
1949 {
1950 LogFlowFunc(("Shutting down thread ...\n"));
1951 pThis->fShutdown = fShutdown;
1952 }
1953
1954 /* Set the notification event so that the thread is being notified. */
1955 BOOL fRc = SetEvent(pThis->aEvents[DSOUNDEVENT_NOTIFY]);
1956 Assert(fRc);
1957
1958 return VINF_SUCCESS;
1959}
1960
1961static DECLCALLBACK(int) drvHostDSoundThread(RTTHREAD hThreadSelf, void *pvUser)
1962{
1963 PDRVHOSTDSOUND pThis = (PDRVHOSTDSOUND)pvUser;
1964 AssertPtr(pThis);
1965
1966 LogFlowFuncEnter();
1967
1968 /* Let caller know that we're done initializing, regardless of the result. */
1969 int rc = RTThreadUserSignal(hThreadSelf);
1970 AssertRC(rc);
1971
1972 do
1973 {
1974 HANDLE aEvents[VBOX_DSOUND_MAX_EVENTS];
1975 DWORD cEvents = 0;
1976 for (uint8_t i = 0; i < VBOX_DSOUND_MAX_EVENTS; i++)
1977 {
1978 if (pThis->aEvents[i])
1979 aEvents[cEvents++] = pThis->aEvents[i];
1980 }
1981 Assert(cEvents);
1982
1983 LogFlowFunc(("Waiting: cEvents=%ld\n", cEvents));
1984
1985 DWORD dwObj = WaitForMultipleObjects(cEvents, aEvents, FALSE /* bWaitAll */, INFINITE);
1986 switch (dwObj)
1987 {
1988 case WAIT_FAILED:
1989 {
1990 rc = VERR_CANCELLED;
1991 break;
1992 }
1993
1994 case WAIT_TIMEOUT:
1995 {
1996 rc = VERR_TIMEOUT;
1997 break;
1998 }
1999
2000 default:
2001 {
2002 dwObj = WAIT_OBJECT_0 + cEvents - 1;
2003 if (aEvents[dwObj] == pThis->aEvents[DSOUNDEVENT_NOTIFY])
2004 {
2005 LogFlowFunc(("Notify\n"));
2006 }
2007 else if (aEvents[dwObj] == pThis->aEvents[DSOUNDEVENT_INPUT])
2008 {
2009
2010 }
2011 else if (aEvents[dwObj] == pThis->aEvents[DSOUNDEVENT_OUTPUT])
2012 {
2013 DWORD cbBuffer, cbFree, cbPlayPos;
2014 rc = dsoundGetPosOut(pThis->pDSStrmOut, &cbBuffer, &cbFree, &cbPlayPos);
2015 if ( RT_SUCCESS(rc)
2016 && cbFree)
2017 {
2018 PDMAUDIOCALLBACKDATAOUT Out;
2019 Out.cbInFree = cbFree;
2020 Out.cbOutWritten = 0;
2021
2022 while (!Out.cbOutWritten)
2023 {
2024 rc = pThis->pUpIAudioConnector->pfnCallback(pThis->pUpIAudioConnector,
2025 PDMAUDIOCALLBACKTYPE_OUTPUT, &Out, sizeof(Out));
2026 if (RT_FAILURE(rc))
2027 break;
2028 RTThreadSleep(100);
2029 }
2030
2031 LogFlowFunc(("Output: cbBuffer=%ld, cbFree=%ld, cbPlayPos=%ld, cbWritten=%RU32, rc=%Rrc\n",
2032 cbBuffer, cbFree, cbPlayPos, Out.cbOutWritten, rc));
2033 }
2034 }
2035 break;
2036 }
2037 }
2038
2039 if (pThis->fShutdown)
2040 break;
2041
2042 } while (RT_SUCCESS(rc));
2043
2044 pThis->fStopped = true;
2045
2046 LogFlowFunc(("Exited with fShutdown=%RTbool, rc=%Rrc\n", pThis->fShutdown, rc));
2047 return rc;
2048}
2049#endif /* VBOX_WITH_AUDIO_CALLBACKS */
2050
2051static DECLCALLBACK(void) drvHostDSoundShutdown(PPDMIHOSTAUDIO pInterface)
2052{
2053 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2054
2055 LogFlowFuncEnter();
2056
2057#ifdef VBOX_WITH_AUDIO_CALLBACKS
2058 int rc = dsoundNotifyThread(pThis, true /* fShutdown */);
2059 AssertRC(rc);
2060
2061 int rcThread;
2062 rc = RTThreadWait(pThis->Thread, 15 * 1000 /* 15s timeout */, &rcThread);
2063 LogFlowFunc(("rc=%Rrc, rcThread=%Rrc\n", rc, rcThread));
2064
2065 Assert(pThis->fStopped);
2066
2067 if (pThis->aEvents[DSOUNDEVENT_NOTIFY])
2068 {
2069 CloseHandle(pThis->aEvents[DSOUNDEVENT_NOTIFY]);
2070 pThis->aEvents[DSOUNDEVENT_NOTIFY] = NULL;
2071}
2072#endif
2073
2074 LogFlowFuncLeave();
2075}
2076
2077static DECLCALLBACK(int) drvHostDSoundInit(PPDMIHOSTAUDIO pInterface)
2078{
2079 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2080
2081 LogFlowFuncEnter();
2082
2083 int rc;
2084
2085 /* Verify that IDirectSound is available. */
2086 LPDIRECTSOUND pDirectSound = NULL;
2087 HRESULT hr = CoCreateInstance(CLSID_DirectSound, NULL, CLSCTX_ALL,
2088 IID_IDirectSound, (void **)&pDirectSound);
2089 if (SUCCEEDED(hr))
2090 {
2091 IDirectSound_Release(pDirectSound);
2092
2093#ifdef VBOX_WITH_AUDIO_CALLBACKS
2094 /* Create notification event. */
2095 pThis->aEvents[DSOUNDEVENT_NOTIFY] = CreateEvent(NULL /* Security attribute */,
2096 FALSE /* bManualReset */, FALSE /* bInitialState */,
2097 NULL /* lpName */);
2098 Assert(pThis->aEvents[DSOUNDEVENT_NOTIFY] != NULL);
2099
2100 /* Start notification thread. */
2101 rc = RTThreadCreate(&pThis->Thread, drvHostDSoundThread,
2102 pThis /*pvUser*/, 0 /*cbStack*/,
2103 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "dSoundNtfy");
2104 if (RT_SUCCESS(rc))
2105 {
2106 /* Wait for the thread to initialize. */
2107 rc = RTThreadUserWait(pThis->Thread, RT_MS_1MIN);
2108 if (RT_FAILURE(rc))
2109 DSLOGREL(("DSound: Waiting for thread to initialize failed with rc=%Rrc\n", rc));
2110 }
2111 else
2112 DSLOGREL(("DSound: Creating thread failed with rc=%Rrc\n", rc));
2113#else
2114 rc = VINF_SUCCESS;
2115#endif
2116
2117 dsoundUpdateStatusInternalEx(pThis, NULL /* pCfg */, DSOUNDENUMCBFLAGS_LOG /* fEnum */);
2118 }
2119 else
2120 {
2121 DSLOGREL(("DSound: DirectSound not available: %Rhrc\n", hr));
2122 rc = VERR_NOT_SUPPORTED;
2123 }
2124
2125 LogFlowFuncLeaveRC(rc);
2126 return rc;
2127}
2128
2129static DECLCALLBACK(void *) drvHostDSoundQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2130{
2131 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2132 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
2133
2134 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
2135 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
2136 return NULL;
2137}
2138
2139static LPCGUID dsoundConfigQueryGUID(PCFGMNODE pCfg, const char *pszName, RTUUID *pUuid)
2140{
2141 LPCGUID pGuid = NULL;
2142
2143 char *pszGuid = NULL;
2144 int rc = CFGMR3QueryStringAlloc(pCfg, pszName, &pszGuid);
2145 if (RT_SUCCESS(rc))
2146 {
2147 rc = RTUuidFromStr(pUuid, pszGuid);
2148 if (RT_SUCCESS(rc))
2149 pGuid = (LPCGUID)&pUuid;
2150 else
2151 DSLOGREL(("DSound: Error parsing device GUID for device '%s': %Rrc\n", pszName, rc));
2152
2153 RTStrFree(pszGuid);
2154 }
2155
2156 return pGuid;
2157}
2158
2159static void dSoundConfigInit(PDRVHOSTDSOUND pThis, PCFGMNODE pCfg)
2160{
2161 unsigned int uBufsizeOut, uBufsizeIn;
2162
2163 CFGMR3QueryUIntDef(pCfg, "BufsizeOut", &uBufsizeOut, _16K);
2164 CFGMR3QueryUIntDef(pCfg, "BufsizeIn", &uBufsizeIn, _16K);
2165 pThis->cfg.cbBufferOut = uBufsizeOut;
2166 pThis->cfg.cbBufferIn = uBufsizeIn;
2167
2168 pThis->cfg.pGuidPlay = dsoundConfigQueryGUID(pCfg, "DeviceGuidOut", &pThis->cfg.uuidPlay);
2169 pThis->cfg.pGuidCapture = dsoundConfigQueryGUID(pCfg, "DeviceGuidIn", &pThis->cfg.uuidCapture);
2170
2171 DSLOG(("DSound: BufsizeOut %u, BufsizeIn %u, DeviceGuidOut {%RTuuid}, DeviceGuidIn {%RTuuid}\n",
2172 pThis->cfg.cbBufferOut,
2173 pThis->cfg.cbBufferIn,
2174 &pThis->cfg.uuidPlay,
2175 &pThis->cfg.uuidCapture));
2176}
2177
2178static DECLCALLBACK(void) drvHostDSoundDestruct(PPDMDRVINS pDrvIns)
2179{
2180 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
2181 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2182 LogFlowFuncEnter();
2183
2184 if (pThis->pDrvIns)
2185 CoUninitialize();
2186
2187 LogFlowFuncLeave();
2188}
2189
2190/**
2191 * Construct a DirectSound Audio driver instance.
2192 *
2193 * @copydoc FNPDMDRVCONSTRUCT
2194 */
2195static DECLCALLBACK(int) drvHostDSoundConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
2196{
2197 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
2198
2199 LogRel(("Audio: Initializing DirectSound audio driver\n"));
2200
2201 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2202
2203 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
2204 if (FAILED(hr))
2205 {
2206 DSLOGREL(("DSound: CoInitializeEx failed with %Rhrc\n", hr));
2207 return VERR_NOT_SUPPORTED;
2208 }
2209
2210 /*
2211 * Init basic data members and interfaces.
2212 */
2213 pThis->pDrvIns = pDrvIns;
2214 /* IBase */
2215 pDrvIns->IBase.pfnQueryInterface = drvHostDSoundQueryInterface;
2216 /* IHostAudio */
2217 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostDSound);
2218
2219#ifdef VBOX_WITH_AUDIO_CALLBACKS
2220 /*
2221 * Get the IAudioConnector interface of the above driver/device.
2222 */
2223 pThis->pUpIAudioConnector = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
2224 if (!pThis->pUpIAudioConnector)
2225 {
2226 AssertMsgFailed(("Configuration error: No audio connector interface above!\n"));
2227 return VERR_PDM_MISSING_INTERFACE_ABOVE;
2228 }
2229#endif
2230
2231 /*
2232 * Init the static parts.
2233 */
2234 RTListInit(&pThis->lstDevInput);
2235 RTListInit(&pThis->lstDevOutput);
2236
2237 pThis->fEnabledIn = false;
2238 pThis->fEnabledOut = false;
2239#ifdef VBOX_WITH_AUDIO_CALLBACKS
2240 pThis->fStopped = false;
2241 pThis->fShutdown = false;
2242
2243 RT_ZERO(pThis->aEvents);
2244 pThis->cEvents = 0;
2245#endif
2246
2247 /*
2248 * Initialize configuration values.
2249 */
2250 dSoundConfigInit(pThis, pCfg);
2251
2252 return VINF_SUCCESS;
2253}
2254
2255/**
2256 * PDM driver registration.
2257 */
2258const PDMDRVREG g_DrvHostDSound =
2259{
2260 /* u32Version */
2261 PDM_DRVREG_VERSION,
2262 /* szName */
2263 "DSoundAudio",
2264 /* szRCMod */
2265 "",
2266 /* szR0Mod */
2267 "",
2268 /* pszDescription */
2269 "DirectSound Audio host driver",
2270 /* fFlags */
2271 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2272 /* fClass. */
2273 PDM_DRVREG_CLASS_AUDIO,
2274 /* cMaxInstances */
2275 ~0U,
2276 /* cbInstance */
2277 sizeof(DRVHOSTDSOUND),
2278 /* pfnConstruct */
2279 drvHostDSoundConstruct,
2280 /* pfnDestruct */
2281 drvHostDSoundDestruct,
2282 /* pfnRelocate */
2283 NULL,
2284 /* pfnIOCtl */
2285 NULL,
2286 /* pfnPowerOn */
2287 NULL,
2288 /* pfnReset */
2289 NULL,
2290 /* pfnSuspend */
2291 NULL,
2292 /* pfnResume */
2293 NULL,
2294 /* pfnAttach */
2295 NULL,
2296 /* pfnDetach */
2297 NULL,
2298 /* pfnPowerOff */
2299 NULL,
2300 /* pfnSoftReset */
2301 NULL,
2302 /* u32EndVersion */
2303 PDM_DRVREG_VERSION
2304};
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use