VirtualBox

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

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

Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 77.3 KB
Line 
1/* $Id: DrvHostDSound.cpp 63714 2016-09-05 13:03:53Z 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_DEVICE_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_DEVICE_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_DEVICE_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_DEVICE_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_DEVICE_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_DEVICE_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_DEVICE_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_DEVICE_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_DEVICE_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_DEVICE_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_DEVICE_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_DEVICE_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_DEVICE_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_DEVICE_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_DEVICE_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_DEVICE_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_DEVICE_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