VirtualBox

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

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

Audio: Fixes for recording on Windows hosts.

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