VirtualBox

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

Last change on this file since 59010 was 58983, checked in by vboxsync, 9 years ago

Audio: Added support for dynamically enabling/disabling host audio backends, more code for audio callback support (still disabled).

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