VirtualBox

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

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

DrvHostDSound.cpp: Logging.

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