VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio_old/DrvHostDSound.cpp@ 61541

Last change on this file since 61541 was 61413, checked in by vboxsync, 9 years ago

Audio: Use the old audio code for now when doing a release. Set VBOX_WITH_AUDIO_STABLE to an empty value to enable the new audio code (default for non-release builds for now).

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