VirtualBox

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

Last change on this file since 63711 was 63711, checked in by vboxsync, 8 years ago

Audio: Implemented support for audio device enumeration handling, audio device information and audio backend notifications. This will enable to let the backends tell the audio subsystem that the host audio configuration has changed and react accordingly to it. For now only the Core Audio backend supports device enumeration. Further this also will get rid of the static initialization on the device emulation side, which, if at VM startup no audio input(s) / output(s) were available, was triggering a warning. The NULL backend therefore does not need to act as a (static) fallback anymore.

Work in progress.

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