VirtualBox

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

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

Audio: Warn on non-available audio streams based on the host audio backend's capabilities -- now the device emulation can ask the backend for its configuration to figure out how many input/output streams are supported by that backend. As the device emulation knows how many channels it finally implements, we there then can decide how to deal with the missing streams.

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