VirtualBox

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

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

Audio: DrvHostDSound.cpp: Initial implementation of threaded audio notification support (output only, not enabled by default yet).

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette