VirtualBox

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

Last change on this file since 58900 was 58744, checked in by vboxsync, 9 years ago

Audio: Update for audio notification support.

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