VirtualBox

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

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

Audio: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.4 KB
Line 
1/* $Id: DrvHostDSound.cpp 61609 2016-06-09 10:22:39Z 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 LPCGUID pGUID = pThis->cfg.pGuidCapture;
897
898 if (!pGUID)
899 {
900 PDSOUNDDEV pDev = NULL;
901
902 switch (pDSoundStream->enmRecSource)
903 {
904 case PDMAUDIORECSOURCE_MIC:
905 {
906 RTListForEach(&pThis->lstDevInput, pDev, DSOUNDDEV, Node)
907 {
908 if (RTStrIStr(pDev->pszName, "Mic")) /** @todo what is with non en_us windows versions? */
909 break;
910 }
911 if (RTListNodeIsDummy(&pThis->lstDevInput, pDev, DSOUNDDEV, Node))
912 pDev = NULL; /* Found nothing. */
913
914 break;
915 }
916
917 case PDMAUDIORECSOURCE_LINE:
918 default:
919 /* Try opening the default device (NULL). */
920 break;
921 }
922
923 if (pDev)
924 {
925 DSLOG(("DSound: Guest \"%s\" is using host \"%s\"\n",
926 DrvAudioHlpRecSrcToStr(pDSoundStream->enmRecSource), pDev->pszName));
927
928 pGUID = &pDev->Guid;
929 }
930 }
931
932 char *pszGUID = dsoundGUIDToUtf8StrA(pGUID);
933 /* This always has to be in the release log. */
934 LogRel(("DSound: Guest \"%s\" is using host device with GUID: %s\n",
935 DrvAudioHlpRecSrcToStr(pDSoundStream->enmRecSource), pszGUID ? pszGUID: "{?}"));
936 RTStrFree(pszGUID);
937
938 return pGUID;
939}
940
941static void directSoundCaptureInterfaceRelease(PDSOUNDSTREAMIN pDSoundStream)
942{
943 if (pDSoundStream->pDSC)
944 {
945 LogFlowFuncEnter();
946 IDirectSoundCapture_Release(pDSoundStream->pDSC);
947 pDSoundStream->pDSC = NULL;
948 }
949}
950
951static HRESULT directSoundCaptureInterfaceCreate(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStream)
952{
953 if (pDSoundStream->pDSC != NULL)
954 {
955 DSLOG(("DSound: DirectSoundCapture instance already exists\n"));
956 return S_OK;
957 }
958
959 HRESULT hr = CoCreateInstance(CLSID_DirectSoundCapture8, NULL, CLSCTX_ALL,
960 IID_IDirectSoundCapture8, (void **)&pDSoundStream->pDSC);
961 if (FAILED(hr))
962 {
963 DSLOGREL(("DSound: Creating capture instance failed with %Rhrc\n", hr));
964 }
965 else
966 {
967 LPCGUID pGUID = dsoundCaptureSelectDevice(pThis, pDSoundStream);
968 hr = IDirectSoundCapture_Initialize(pDSoundStream->pDSC, pGUID);
969 if (FAILED(hr))
970 {
971 if (hr == DSERR_NODRIVER) /* Usually means that no capture devices are attached. */
972 DSLOGREL(("DSound: Capture device currently is unavailable\n"));
973 else
974 DSLOGREL(("DSound: Initializing capturing device failed with %Rhrc\n", hr));
975
976 directSoundCaptureInterfaceRelease(pDSoundStream);
977 }
978 }
979
980 LogFlowFunc(("Returning %Rhrc\n", hr));
981 return hr;
982}
983
984static HRESULT directSoundCaptureClose(PDSOUNDSTREAMIN pDSoundStream)
985{
986 AssertPtrReturn(pDSoundStream, E_POINTER);
987
988 DSLOG(("DSound: pDSoundStream=%p, pDSCB=%p\n", pDSoundStream, pDSoundStream->pDSCB));
989
990 HRESULT hr = S_OK;
991
992 if (pDSoundStream->pDSCB)
993 {
994 hr = IDirectSoundCaptureBuffer_Stop(pDSoundStream->pDSCB);
995 if (SUCCEEDED(hr))
996 {
997 IDirectSoundCaptureBuffer8_Release(pDSoundStream->pDSCB);
998 pDSoundStream->pDSCB = NULL;
999 }
1000 else
1001 DSLOGREL(("DSound: Stopping capture buffer failed with %Rhrc\n", hr));
1002 }
1003
1004 if (SUCCEEDED(hr))
1005 directSoundCaptureInterfaceRelease(pDSoundStream);
1006
1007 LogFlowFunc(("Returning %Rhrc\n", hr));
1008 return hr;
1009}
1010
1011static HRESULT directSoundCaptureOpen(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStream)
1012{
1013 AssertPtrReturn(pThis, E_POINTER);
1014 AssertPtrReturn(pDSoundStream, E_POINTER);
1015
1016 DSLOG(("DSound: pDSoundStream=%p, cbBufferIn=%ld, uHz=%RU32, cChannels=%RU8, cBits=%RU8, fSigned=%RTbool\n",
1017 pDSoundStream,
1018 pThis->cfg.cbBufferIn,
1019 pDSoundStream->Stream.Props.uHz,
1020 pDSoundStream->Stream.Props.cChannels,
1021 pDSoundStream->Stream.Props.cBits,
1022 pDSoundStream->Stream.Props.fSigned));
1023
1024 if (pDSoundStream->pDSCB != NULL)
1025 {
1026 /* Should not happen but be forgiving. */
1027 DSLOGREL(("DSound: DirectSoundCaptureBuffer already exists\n"));
1028 directSoundCaptureClose(pDSoundStream);
1029 }
1030
1031 WAVEFORMATEX wfx;
1032 int rc = dsoundWaveFmtFromCfg(&pDSoundStream->streamCfg, &wfx);
1033 if (RT_FAILURE(rc))
1034 return E_INVALIDARG;
1035
1036 HRESULT hr = directSoundCaptureInterfaceCreate(pThis, pDSoundStream);
1037 if (FAILED(hr))
1038 return hr;
1039
1040 do /* To use breaks. */
1041 {
1042 LPDIRECTSOUNDCAPTUREBUFFER pDSCB = NULL;
1043 DSCBUFFERDESC bd;
1044 RT_ZERO(bd);
1045 bd.dwSize = sizeof(bd);
1046 bd.lpwfxFormat = &wfx;
1047 bd.dwBufferBytes = pThis->cfg.cbBufferIn;
1048 hr = IDirectSoundCapture_CreateCaptureBuffer(pDSoundStream->pDSC,
1049 &bd, &pDSCB, NULL);
1050 if (FAILED(hr))
1051 {
1052 if (hr == E_ACCESSDENIED)
1053 {
1054 DSLOGREL(("DSound: Capturing input from host not possible, access denied\n"));
1055 }
1056 else
1057 DSLOGREL(("DSound: Creating capture buffer failed with %Rhrc\n", hr));
1058 break;
1059 }
1060
1061 hr = IDirectSoundCaptureBuffer_QueryInterface(pDSCB, IID_IDirectSoundCaptureBuffer8, (void **)&pDSoundStream->pDSCB);
1062 IDirectSoundCaptureBuffer_Release(pDSCB);
1063 if (FAILED(hr))
1064 {
1065 DSLOGREL(("DSound: Querying interface for capture buffer failed with %Rhrc\n", hr));
1066 break;
1067 }
1068
1069 /*
1070 * Query the actual parameters.
1071 */
1072 DWORD cbReadPos = 0;
1073 hr = IDirectSoundCaptureBuffer8_GetCurrentPosition(pDSoundStream->pDSCB, NULL, &cbReadPos);
1074 if (FAILED(hr))
1075 {
1076 cbReadPos = 0;
1077 DSLOGREL(("DSound: Getting capture position failed with %Rhrc\n", hr));
1078 }
1079
1080 RT_ZERO(wfx);
1081 hr = IDirectSoundCaptureBuffer8_GetFormat(pDSoundStream->pDSCB, &wfx, sizeof(wfx), NULL);
1082 if (FAILED(hr))
1083 {
1084 DSLOGREL(("DSound: Getting capture format failed with %Rhrc\n", hr));
1085 break;
1086 }
1087
1088 DSCBCAPS bc;
1089 RT_ZERO(bc);
1090 bc.dwSize = sizeof(bc);
1091 hr = IDirectSoundCaptureBuffer8_GetCaps(pDSoundStream->pDSCB, &bc);
1092 if (FAILED(hr))
1093 {
1094 DSLOGREL(("Getting capture capabilities failed with %Rhrc\n", hr));
1095 break;
1096 }
1097
1098 DSLOG(("DSound: Capture format:\n"
1099 " dwBufferBytes = %RI32\n"
1100 " dwFlags = 0x%x\n"
1101 " wFormatTag = %RI16\n"
1102 " nChannels = %RI16\n"
1103 " nSamplesPerSec = %RU32\n"
1104 " nAvgBytesPerSec = %RU32\n"
1105 " nBlockAlign = %RI16\n"
1106 " wBitsPerSample = %RI16\n"
1107 " cbSize = %RI16\n",
1108 bc.dwBufferBytes,
1109 bc.dwFlags,
1110 wfx.wFormatTag,
1111 wfx.nChannels,
1112 wfx.nSamplesPerSec,
1113 wfx.nAvgBytesPerSec,
1114 wfx.nBlockAlign,
1115 wfx.wBitsPerSample,
1116 wfx.cbSize));
1117
1118 if (bc.dwBufferBytes & pDSoundStream->Stream.Props.uAlign)
1119 DSLOGREL(("DSound: Capture GetCaps returned misaligned buffer: size %RU32, alignment %RU32\n",
1120 bc.dwBufferBytes, pDSoundStream->Stream.Props.uAlign + 1));
1121
1122 if (bc.dwBufferBytes != pThis->cfg.cbBufferIn)
1123 DSLOGREL(("DSound: Capture buffer size mismatched: DirectSound %RU32, requested %RU32 bytes\n",
1124 bc.dwBufferBytes, pThis->cfg.cbBufferIn));
1125
1126 /* Initial state: reading at the initial capture position, no error. */
1127 pDSoundStream->csCaptureReadPos = cbReadPos >> pDSoundStream->Stream.Props.cShift;
1128 pDSoundStream->csCaptureBufferSize = bc.dwBufferBytes >> pDSoundStream->Stream.Props.cShift;
1129 pDSoundStream->hrLastCapture = S_OK;
1130
1131 DSLOG(("DSound: csCaptureReadPos=%RU32, csCaptureBufferSize=%RU32\n",
1132 pDSoundStream->csCaptureReadPos, pDSoundStream->csCaptureBufferSize));
1133
1134 } while (0);
1135
1136 if (FAILED(hr))
1137 directSoundCaptureClose(pDSoundStream);
1138
1139 LogFlowFunc(("Returning %Rhrc\n", hr));
1140 return hr;
1141}
1142
1143static HRESULT directSoundCaptureStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStream)
1144{
1145 AssertPtrReturn(pThis , E_POINTER);
1146 AssertPtrReturn(pDSoundStream, E_POINTER);
1147
1148 NOREF(pThis);
1149
1150 HRESULT hr;
1151
1152 if (pDSoundStream->pDSCB)
1153 {
1154 DSLOG(("DSound: Stopping capture\n"));
1155
1156 hr = IDirectSoundCaptureBuffer_Stop(pDSoundStream->pDSCB);
1157 if (FAILED(hr))
1158 DSLOGREL(("DSound: Stopping capture buffer failed with %Rhrc\n", hr));
1159 }
1160 else
1161 hr = E_UNEXPECTED;
1162
1163 if (SUCCEEDED(hr))
1164 pDSoundStream->fEnabled = false;
1165
1166 LogFlowFunc(("Returning %Rhrc\n", hr));
1167 return hr;
1168}
1169
1170static HRESULT directSoundCaptureStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStream)
1171{
1172 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1173 AssertPtrReturn(pDSoundStream, VERR_INVALID_POINTER);
1174
1175 HRESULT hr;
1176 if (pDSoundStream->pDSCB != NULL)
1177 {
1178 DWORD dwStatus;
1179 hr = IDirectSoundCaptureBuffer8_GetStatus(pDSoundStream->pDSCB, &dwStatus);
1180 if (FAILED(hr))
1181 {
1182 DSLOGREL(("DSound: Retrieving capture status failed with %Rhrc\n", hr));
1183 }
1184 else
1185 {
1186 if (dwStatus & DSCBSTATUS_CAPTURING)
1187 {
1188 DSLOG(("DSound: Already capturing\n"));
1189 }
1190 else
1191 {
1192 DWORD fFlags = 0;
1193#ifndef VBOX_WITH_AUDIO_CALLBACKS
1194 fFlags |= DSCBSTART_LOOPING;
1195#endif
1196 DSLOG(("DSound: Starting to capture\n"));
1197 hr = IDirectSoundCaptureBuffer8_Start(pDSoundStream->pDSCB, fFlags);
1198 if (FAILED(hr))
1199 DSLOGREL(("DSound: Starting to capture failed with %Rhrc\n", hr));
1200 }
1201 }
1202 }
1203 else
1204 hr = E_UNEXPECTED;
1205
1206 if (SUCCEEDED(hr))
1207 pDSoundStream->fEnabled = true;
1208
1209 LogFlowFunc(("Returning %Rhrc\n", hr));
1210 return hr;
1211}
1212
1213static int dsoundDevAdd(PRTLISTANCHOR pList, LPGUID lpGUID,
1214 LPCWSTR lpwstrDescription, PDSOUNDDEV *ppDev)
1215{
1216 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1217 AssertPtrReturn(lpGUID, VERR_INVALID_POINTER);
1218 AssertPtrReturn(lpwstrDescription, VERR_INVALID_POINTER);
1219
1220 PDSOUNDDEV pDev = (PDSOUNDDEV)RTMemAlloc(sizeof(DSOUNDDEV));
1221 if (!pDev)
1222 return VERR_NO_MEMORY;
1223
1224 int rc = RTUtf16ToUtf8(lpwstrDescription, &pDev->pszName);
1225 if (RT_SUCCESS(rc))
1226 memcpy(&pDev->Guid, lpGUID, sizeof(GUID));
1227
1228 if (RT_SUCCESS(rc))
1229 RTListAppend(pList, &pDev->Node);
1230
1231 if (ppDev)
1232 *ppDev = pDev;
1233
1234 return rc;
1235}
1236
1237static void dsoundDeviceRemove(PDSOUNDDEV pDev)
1238{
1239 if (pDev)
1240 {
1241 RTStrFree(pDev->pszName);
1242 pDev->pszName = NULL;
1243
1244 RTListNodeRemove(&pDev->Node);
1245
1246 RTMemFree(pDev);
1247 }
1248}
1249
1250static void dsoundLogDevice(const char *pszType, LPGUID lpGUID, LPCWSTR lpwstrDescription, LPCWSTR lpwstrModule)
1251{
1252 char *pszGUID = dsoundGUIDToUtf8StrA(lpGUID);
1253 /* This always has to be in the release log. */
1254 LogRel(("DSound: %s: GUID: %s [%ls] (Module: %ls)\n",
1255 pszType, pszGUID? pszGUID: "{?}", lpwstrDescription, lpwstrModule));
1256 RTStrFree(pszGUID);
1257}
1258
1259static BOOL CALLBACK dsoundDevicesEnumCbPlayback(LPGUID lpGUID, LPCWSTR lpwstrDescription,
1260 LPCWSTR lpwstrModule, LPVOID lpContext)
1261{
1262 PDSOUNDENUMCBCTX pCtx = (PDSOUNDENUMCBCTX)lpContext;
1263 AssertPtrReturn(pCtx, FALSE);
1264 AssertPtrReturn(pCtx->pDrv, FALSE);
1265
1266 if (!lpGUID)
1267 return TRUE;
1268
1269 AssertPtrReturn(lpwstrDescription, FALSE);
1270 /* Do not care about lpwstrModule. */
1271
1272 if (pCtx->fFlags & DSOUNDENUMCBFLAGS_LOG)
1273 dsoundLogDevice("Output", lpGUID, lpwstrDescription, lpwstrModule);
1274
1275 int rc = dsoundDevAdd(&pCtx->pDrv->lstDevOutput,
1276 lpGUID, lpwstrDescription, NULL /* ppDev */);
1277 if (RT_FAILURE(rc))
1278 return FALSE; /* Abort enumeration. */
1279
1280 pCtx->cDevOut++;
1281
1282 return TRUE;
1283}
1284
1285static BOOL CALLBACK dsoundDevicesEnumCbCapture(LPGUID lpGUID, LPCWSTR lpwstrDescription,
1286 LPCWSTR lpwstrModule, LPVOID lpContext)
1287{
1288 PDSOUNDENUMCBCTX pCtx = (PDSOUNDENUMCBCTX)lpContext;
1289 AssertPtrReturn(pCtx, FALSE);
1290 AssertPtrReturn(pCtx->pDrv, FALSE);
1291
1292 if (!lpGUID)
1293 return TRUE;
1294
1295 if (pCtx->fFlags & DSOUNDENUMCBFLAGS_LOG)
1296 dsoundLogDevice("Input", lpGUID, lpwstrDescription, lpwstrModule);
1297
1298 int rc = dsoundDevAdd(&pCtx->pDrv->lstDevInput,
1299 lpGUID, lpwstrDescription, NULL /* ppDev */);
1300 if (RT_FAILURE(rc))
1301 return FALSE; /* Abort enumeration. */
1302
1303 pCtx->cDevIn++;
1304
1305 return TRUE;
1306}
1307
1308/**
1309 * Does a (Re-)enumeration of the host's playback + capturing devices.
1310 *
1311 * @return IPRT status code.
1312 * @param pThis Host audio driver instance.
1313 * @param pEnmCtx Enumeration context to use.
1314 * @param fEnum Enumeration flags.
1315 */
1316static int dsoundDevicesEnumerate(PDRVHOSTDSOUND pThis, PDSOUNDENUMCBCTX pEnmCtx, uint32_t fEnum)
1317{
1318 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1319 AssertPtrReturn(pEnmCtx, VERR_INVALID_POINTER);
1320
1321 dsoundDevicesClear(pThis);
1322
1323 RTLDRMOD hDSound = NULL;
1324 int rc = RTLdrLoadSystem("dsound.dll", true /*fNoUnload*/, &hDSound);
1325 if (RT_SUCCESS(rc))
1326 {
1327 PFNDIRECTSOUNDENUMERATEW pfnDirectSoundEnumerateW = NULL;
1328 PFNDIRECTSOUNDCAPTUREENUMERATEW pfnDirectSoundCaptureEnumerateW = NULL;
1329
1330 rc = RTLdrGetSymbol(hDSound, "DirectSoundEnumerateW", (void**)&pfnDirectSoundEnumerateW);
1331 if (RT_SUCCESS(rc))
1332 rc = RTLdrGetSymbol(hDSound, "DirectSoundCaptureEnumerateW", (void**)&pfnDirectSoundCaptureEnumerateW);
1333
1334 if (RT_SUCCESS(rc))
1335 {
1336 HRESULT hr = pfnDirectSoundEnumerateW(&dsoundDevicesEnumCbPlayback, pEnmCtx);
1337 if (FAILED(hr))
1338 LogRel2(("DSound: Error enumerating host playback devices: %Rhrc\n", hr));
1339
1340 hr = pfnDirectSoundCaptureEnumerateW(&dsoundDevicesEnumCbCapture, pEnmCtx);
1341 if (FAILED(hr))
1342 LogRel2(("DSound: Error enumerating host capturing devices: %Rhrc\n", hr));
1343
1344 if (fEnum & DSOUNDENUMCBFLAGS_LOG)
1345 {
1346 LogRel2(("DSound: Found %RU8 host playback devices\n", pEnmCtx->cDevOut));
1347 LogRel2(("DSound: Found %RU8 host capturing devices\n", pEnmCtx->cDevIn));
1348 }
1349 }
1350
1351 RTLdrClose(hDSound);
1352 }
1353 else
1354 {
1355 /* No dsound.dll on this system. */
1356 LogRel2(("DSound: Could not load dsound.dll: %Rrc\n", rc));
1357 }
1358
1359 return rc;
1360}
1361
1362/**
1363 * Updates this host driver's internal status, according to the global, overall input/output
1364 * state and all connected (native) audio streams.
1365 *
1366 * @param pThis Host audio driver instance.
1367 * @param pCfg Where to store the backend configuration. Optional.
1368 * @param fEnum Enumeration flags.
1369 */
1370void dsoundUpdateStatusInternalEx(PDRVHOSTDSOUND pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum)
1371{
1372 AssertPtrReturnVoid(pThis);
1373 /* pCfg is optional. */
1374
1375 PDMAUDIOBACKENDCFG Cfg;
1376 RT_ZERO(Cfg);
1377
1378 Cfg.cbStreamOut = sizeof(DSOUNDSTREAMOUT);
1379 Cfg.cbStreamIn = sizeof(DSOUNDSTREAMIN);
1380
1381 DSOUNDENUMCBCTX cbCtx = { pThis, fEnum, 0, 0 };
1382
1383 int rc = dsoundDevicesEnumerate(pThis, &cbCtx, fEnum);
1384 if (RT_SUCCESS(rc))
1385 {
1386#ifdef VBOX_WITH_AUDIO_CALLBACKS
1387 if ( pThis->fEnabledOut != RT_BOOL(cbCtx.cDevOut)
1388 || pThis->fEnabledIn != RT_BOOL(cbCtx.cDevIn))
1389 {
1390 /** @todo Use a registered callback to the audio connector (e.g "OnConfigurationChanged") to
1391 * let the connector know that something has changed within the host backend. */
1392 }
1393#else
1394 pThis->fEnabledOut = RT_BOOL(cbCtx.cDevOut);
1395 pThis->fEnabledIn = RT_BOOL(cbCtx.cDevIn);
1396#endif
1397
1398 Cfg.cSources = cbCtx.cDevIn;
1399 Cfg.cSinks = cbCtx.cDevOut;
1400 Cfg.cMaxStreamsIn = UINT32_MAX;
1401 Cfg.cMaxStreamsOut = UINT32_MAX;
1402
1403 if (pCfg)
1404 memcpy(pCfg, &Cfg, sizeof(PDMAUDIOBACKENDCFG));
1405 }
1406
1407 LogFlowFuncLeaveRC(rc);
1408}
1409
1410void dsoundUpdateStatusInternal(PDRVHOSTDSOUND pThis)
1411{
1412 dsoundUpdateStatusInternalEx(pThis, NULL /* pCfg */, 0 /* fEnum */);
1413}
1414
1415static int dsoundCreateStreamOut(PPDMIHOSTAUDIO pInterface,
1416 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples)
1417{
1418 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1419 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1420 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1421 /* pcSamples is optional. */
1422
1423 LogFlowFunc(("pStream=%p, pCfg=%p\n", pStream, pCfg));
1424
1425 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1426 PDSOUNDSTREAMOUT pDSoundStream = (PDSOUNDSTREAMOUT)pStream;
1427
1428 pDSoundStream->streamCfg = *pCfg;
1429 pDSoundStream->streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
1430
1431 int rc = DrvAudioHlpStreamCfgToProps(&pDSoundStream->streamCfg, &pDSoundStream->Stream.Props);
1432 if (RT_SUCCESS(rc))
1433 {
1434 pDSoundStream->pDS = NULL;
1435 pDSoundStream->pDSB = NULL;
1436 pDSoundStream->cbPlayWritePos = 0;
1437 pDSoundStream->fRestartPlayback = true;
1438 pDSoundStream->csPlaybackBufferSize = 0;
1439
1440 if (pcSamples)
1441 *pcSamples = pThis->cfg.cbBufferOut >> pStream->Props.cShift;
1442
1443 /* Try to open playback in case the device is already there. */
1444 directSoundPlayOpen(pThis, pDSoundStream);
1445 }
1446 else
1447 {
1448 RT_ZERO(pDSoundStream->streamCfg);
1449 }
1450
1451 LogFlowFuncLeaveRC(rc);
1452 return rc;
1453}
1454
1455static int dsoundControlStreamOut(PPDMIHOSTAUDIO pInterface,
1456 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
1457{
1458 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1459 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1460
1461 LogFlowFunc(("pStream=%p, cmd=%d\n", pStream, enmStreamCmd));
1462
1463 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1464 PDSOUNDSTREAMOUT pDSoundStream = (PDSOUNDSTREAMOUT)pStream;
1465
1466 int rc = VINF_SUCCESS;
1467
1468 HRESULT hr;
1469 switch (enmStreamCmd)
1470 {
1471 case PDMAUDIOSTREAMCMD_ENABLE:
1472 case PDMAUDIOSTREAMCMD_RESUME:
1473 {
1474 DSLOG(("DSound: Playback PDMAUDIOSTREAMCMD_ENABLE\n"));
1475 /* Try to start playback. If it fails, then reopen and try again. */
1476 hr = directSoundPlayStart(pThis, pDSoundStream);
1477 if (FAILED(hr))
1478 {
1479 hr = directSoundPlayClose(pThis, pDSoundStream);
1480 if (SUCCEEDED(hr))
1481 hr = directSoundPlayOpen(pThis, pDSoundStream);
1482 if (SUCCEEDED(hr))
1483 hr = directSoundPlayStart(pThis, pDSoundStream);
1484 }
1485
1486 if (FAILED(hr))
1487 rc = VERR_NOT_SUPPORTED;
1488 break;
1489 }
1490
1491 case PDMAUDIOSTREAMCMD_DISABLE:
1492 case PDMAUDIOSTREAMCMD_PAUSE:
1493 {
1494 DSLOG(("DSound: Playback PDMAUDIOSTREAMCMD_DISABLE\n"));
1495 hr = directSoundPlayStop(pThis, pDSoundStream);
1496 if (FAILED(hr))
1497 rc = VERR_NOT_SUPPORTED;
1498 break;
1499 }
1500
1501 default:
1502 {
1503 AssertMsgFailed(("Invalid command: %ld\n", enmStreamCmd));
1504 rc = VERR_INVALID_PARAMETER;
1505 break;
1506 }
1507 }
1508
1509 LogFlowFuncLeaveRC(rc);
1510 return rc;
1511}
1512
1513static DECLCALLBACK(int) drvHostDSoundStreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
1514 uint32_t *pcSamplesPlayed)
1515{
1516 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1517 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1518 /* pcSamplesPlayed is optional. */
1519
1520 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1521 PDSOUNDSTREAMOUT pDSoundStream = (PDSOUNDSTREAMOUT)pStream;
1522
1523 int rc = VINF_SUCCESS;
1524 uint32_t cReadTotal = 0;
1525
1526#ifdef DEBUG_andy
1527 LogFlowFuncEnter();
1528#endif
1529
1530 do /* to use 'break' */
1531 {
1532 DWORD cbBuffer, cbFree, cbPlayPos;
1533 rc = dsoundGetPosOut(pThis, pDSoundStream, &cbBuffer, &cbFree, &cbPlayPos);
1534 if (RT_FAILURE(rc))
1535 break;
1536
1537 /*
1538 * Check for full buffer, do not allow the cbPlayWritePos to catch cbPlayPos during playback,
1539 * i.e. always leave a free space for 1 audio sample.
1540 */
1541 const DWORD cbSample = AUDIOMIXBUF_S2B(&pStream->MixBuf, 1);
1542 if (cbFree <= cbSample)
1543 break;
1544 cbFree -= cbSample;
1545
1546 uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);
1547 uint32_t cbLive = AUDIOMIXBUF_S2B(&pStream->MixBuf, cLive);
1548
1549 /* Do not write more than available space in the DirectSound playback buffer. */
1550 cbLive = RT_MIN(cbFree, cbLive);
1551
1552 cbLive &= ~pStream->Props.uAlign;
1553 if (cbLive == 0 || cbLive > cbBuffer)
1554 {
1555 DSLOG(("DSound: cbLive=%RU32, cbBuffer=%ld, cbPlayWritePos=%ld, cbPlayPos=%ld\n",
1556 cbLive, cbBuffer, pDSoundStream->cbPlayWritePos, cbPlayPos));
1557 break;
1558 }
1559
1560 LPDIRECTSOUNDBUFFER8 pDSB = pDSoundStream->pDSB;
1561 AssertPtr(pDSB);
1562
1563 LPVOID pv1, pv2;
1564 DWORD cb1, cb2;
1565 HRESULT hr = directSoundPlayLock(pThis, pDSB, &pStream->Props, pDSoundStream->cbPlayWritePos, cbLive,
1566 &pv1, &pv2, &cb1, &cb2, 0 /* dwFlags */);
1567 if (FAILED(hr))
1568 {
1569 rc = VERR_ACCESS_DENIED;
1570 break;
1571 }
1572
1573 DWORD len1 = AUDIOMIXBUF_B2S(&pStream->MixBuf, cb1);
1574 DWORD len2 = AUDIOMIXBUF_B2S(&pStream->MixBuf, cb2);
1575
1576 uint32_t cRead = 0;
1577
1578 if (pv1 && cb1)
1579 {
1580 rc = AudioMixBufReadCirc(&pStream->MixBuf, pv1, cb1, &cRead);
1581 if (RT_SUCCESS(rc))
1582 cReadTotal += cRead;
1583 }
1584
1585 if ( RT_SUCCESS(rc)
1586 && cReadTotal == len1
1587 && pv2 && cb2)
1588 {
1589 rc = AudioMixBufReadCirc(&pStream->MixBuf, pv2, cb2, &cRead);
1590 if (RT_SUCCESS(rc))
1591 cReadTotal += cRead;
1592 }
1593
1594 directSoundPlayUnlock(pThis, pDSB, pv1, pv2, cb1, cb2);
1595
1596 pDSoundStream->cbPlayWritePos =
1597 (pDSoundStream->cbPlayWritePos + AUDIOMIXBUF_S2B(&pStream->MixBuf, cReadTotal)) % cbBuffer;
1598
1599 DSLOGF(("DSound: %RU32 (%RU32 samples) out of %RU32%s, buffer write pos %ld, rc=%Rrc\n",
1600 AUDIOMIXBUF_S2B(&pStream->MixBuf, cReadTotal), cReadTotal, cbLive,
1601 cbLive != AUDIOMIXBUF_S2B(&pStream->MixBuf, cReadTotal) ? " !!!": "",
1602 pDSoundStream->cbPlayWritePos, rc));
1603
1604 if (cReadTotal)
1605 {
1606 AudioMixBufFinish(&pStream->MixBuf, cReadTotal);
1607 rc = VINF_SUCCESS; /* Played something. */
1608 }
1609
1610 if (RT_FAILURE(rc))
1611 break;
1612
1613 if (pDSoundStream->fRestartPlayback)
1614 {
1615 /*
1616 * The playback has been just started.
1617 * Some samples of the new sound have been copied to the buffer
1618 * and it can start playing.
1619 */
1620 pDSoundStream->fRestartPlayback = false;
1621
1622 DWORD fFlags = 0;
1623#ifndef VBOX_WITH_AUDIO_CALLBACKS
1624 fFlags |= DSCBSTART_LOOPING;
1625#endif
1626 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
1627 {
1628 hr = IDirectSoundBuffer8_Play(pDSoundStream->pDSB, 0, 0, fFlags);
1629 if ( SUCCEEDED(hr)
1630 || hr != DSERR_BUFFERLOST)
1631 break;
1632 else
1633 {
1634 LogFlowFunc(("Restarting playback failed due to lost buffer, restoring ...\n"));
1635 directSoundPlayRestore(pThis, pDSoundStream->pDSB);
1636 }
1637 }
1638
1639 if (FAILED(hr))
1640 {
1641 DSLOGREL(("DSound: Starting playback failed with %Rhrc\n", hr));
1642 rc = VERR_NOT_SUPPORTED;
1643 break;
1644 }
1645 }
1646
1647 } while (0);
1648
1649 if (RT_FAILURE(rc))
1650 {
1651 dsoundUpdateStatusInternal(pThis);
1652 }
1653 else
1654 {
1655 if (pcSamplesPlayed)
1656 *pcSamplesPlayed = cReadTotal;
1657 }
1658
1659 LogFlowFuncLeaveRC(rc);
1660 return rc;
1661}
1662
1663static int dsoundDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1664{
1665 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1666 PDSOUNDSTREAMOUT pDSoundStream = (PDSOUNDSTREAMOUT)pStream;
1667
1668 directSoundPlayClose(pThis, pDSoundStream);
1669
1670 pDSoundStream->cbPlayWritePos = 0;
1671 pDSoundStream->fRestartPlayback = true;
1672 pDSoundStream->csPlaybackBufferSize = 0;
1673
1674 RT_ZERO(pDSoundStream->streamCfg);
1675
1676 return VINF_SUCCESS;
1677}
1678
1679static int dsoundCreateStreamIn(PPDMIHOSTAUDIO pInterface,
1680 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples)
1681{
1682 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1683 PDSOUNDSTREAMIN pDSoundStream = (PDSOUNDSTREAMIN)pStream;
1684
1685 LogFlowFunc(("pStream=%p, pAudioSettings=%p, enmRecSource=%ld\n",
1686 pStream, pCfg, pCfg->DestSource.Source));
1687
1688 pDSoundStream->streamCfg = *pCfg;
1689 pDSoundStream->streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
1690
1691 /** @todo caller should already init Props? */
1692 int rc = DrvAudioHlpStreamCfgToProps(&pDSoundStream->streamCfg, &pStream->Props);
1693 if (RT_SUCCESS(rc))
1694 {
1695 /* Init the stream structure and save relevant information to it. */
1696 pDSoundStream->csCaptureReadPos = 0;
1697 pDSoundStream->csCaptureBufferSize = 0;
1698 pDSoundStream->pDSC = NULL;
1699 pDSoundStream->pDSCB = NULL;
1700 pDSoundStream->enmRecSource = pCfg->DestSource.Source;
1701 pDSoundStream->hrLastCapture = S_OK;
1702
1703 if (pcSamples)
1704 *pcSamples = pThis->cfg.cbBufferIn >> pStream->Props.cShift;
1705
1706 /* Try to open capture in case the device is already there. */
1707 directSoundCaptureOpen(pThis, pDSoundStream);
1708 }
1709 else
1710 {
1711 RT_ZERO(pDSoundStream->streamCfg);
1712 }
1713
1714 LogFlowFuncLeaveRC(rc);
1715 return rc;
1716}
1717
1718static int dsoundControlStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
1719 PDMAUDIOSTREAMCMD enmStreamCmd)
1720{
1721 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1722 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1723
1724 LogFlowFunc(("pStream=%p, enmStreamCmd=%ld\n", pStream, enmStreamCmd));
1725
1726 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1727 PDSOUNDSTREAMIN pDSoundStream = (PDSOUNDSTREAMIN)pStream;
1728
1729 int rc = VINF_SUCCESS;
1730
1731 HRESULT hr;
1732 switch (enmStreamCmd)
1733 {
1734 case PDMAUDIOSTREAMCMD_ENABLE:
1735 case PDMAUDIOSTREAMCMD_RESUME:
1736 {
1737 /* Try to start capture. If it fails, then reopen and try again. */
1738 hr = directSoundCaptureStart(pThis, pDSoundStream);
1739 if (FAILED(hr))
1740 {
1741 hr = directSoundCaptureClose(pDSoundStream);
1742 if (SUCCEEDED(hr))
1743 {
1744 hr = directSoundCaptureOpen(pThis, pDSoundStream);
1745 if (SUCCEEDED(hr))
1746 hr = directSoundCaptureStart(pThis, pDSoundStream);
1747 }
1748 }
1749
1750 if (FAILED(hr))
1751 rc = VERR_NOT_SUPPORTED;
1752 break;
1753 }
1754
1755 case PDMAUDIOSTREAMCMD_DISABLE:
1756 case PDMAUDIOSTREAMCMD_PAUSE:
1757 {
1758 hr = directSoundCaptureStop(pThis, pDSoundStream);
1759 if (FAILED(hr))
1760 rc = VERR_NOT_SUPPORTED;
1761 break;
1762 }
1763
1764 default:
1765 {
1766 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
1767 rc = VERR_INVALID_PARAMETER;
1768 break;
1769 }
1770 }
1771
1772 return rc;
1773}
1774
1775static DECLCALLBACK(int) drvHostDSoundStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
1776 uint32_t *pcSamplesCaptured)
1777{
1778 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1779
1780 PDSOUNDSTREAMIN pDSoundStream = (PDSOUNDSTREAMIN)pStream;
1781 LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB = pDSoundStream->pDSCB;
1782
1783 int rc = VINF_SUCCESS;
1784
1785 uint32_t cCaptured = 0;
1786
1787 do
1788 {
1789 if (pDSCB == NULL)
1790 {
1791 rc = VERR_NOT_AVAILABLE;
1792 break;
1793 }
1794
1795 /* Get DirectSound capture position in bytes. */
1796 DWORD cbReadPos;
1797 HRESULT hr = IDirectSoundCaptureBuffer_GetCurrentPosition(pDSCB, NULL, &cbReadPos);
1798 if (FAILED(hr))
1799 {
1800 if (hr != pDSoundStream->hrLastCapture)
1801 {
1802 DSLOGREL(("DSound: Getting capture position failed with %Rhrc\n", hr));
1803 pDSoundStream->hrLastCapture = hr;
1804 }
1805
1806 rc = VERR_NOT_AVAILABLE;
1807 break;
1808 }
1809
1810 pDSoundStream->hrLastCapture = hr;
1811
1812 if (cbReadPos & pStream->Props.uAlign)
1813 DSLOGF(("DSound: Misaligned capture read position %ld (alignment: %RU32)\n", cbReadPos, pStream->Props.uAlign));
1814
1815 /* Capture position in samples. */
1816 DWORD csReadPos = cbReadPos >> pStream->Props.cShift;
1817
1818 /* Number of samples available in the DirectSound capture buffer. */
1819 DWORD csCaptured = dsoundRingDistance(csReadPos, pDSoundStream->csCaptureReadPos, pDSoundStream->csCaptureBufferSize);
1820 if (csCaptured == 0)
1821 break;
1822
1823 /* Using as an intermediate not circular buffer. */
1824 AudioMixBufReset(&pStream->MixBuf);
1825
1826 /* Get number of free samples in the mix buffer and check that is has free space */
1827 uint32_t csMixFree = AudioMixBufFree(&pStream->MixBuf);
1828 if (csMixFree == 0)
1829 {
1830 DSLOGF(("DSound: Capture buffer full\n"));
1831 break;
1832 }
1833
1834 DSLOGF(("DSound: Capture csMixFree=%RU32, csReadPos=%ld, csCaptureReadPos=%ld, csCaptured=%ld\n",
1835 csMixFree, csReadPos, pDSoundStream->csCaptureReadPos, csCaptured));
1836
1837 /* No need to fetch more samples than mix buffer can receive. */
1838 csCaptured = RT_MIN(csCaptured, csMixFree);
1839
1840 /* Lock relevant range in the DirectSound capture buffer. */
1841 LPVOID pv1, pv2;
1842 DWORD cb1, cb2;
1843 hr = directSoundCaptureLock(pDSCB, &pStream->Props,
1844 AUDIOMIXBUF_S2B(&pStream->MixBuf, pDSoundStream->csCaptureReadPos), /* dwOffset */
1845 AUDIOMIXBUF_S2B(&pStream->MixBuf, csCaptured), /* dwBytes */
1846 &pv1, &pv2, &cb1, &cb2,
1847 0 /* dwFlags */);
1848 if (FAILED(hr))
1849 {
1850 rc = VERR_ACCESS_DENIED;
1851 break;
1852 }
1853
1854 DWORD len1 = AUDIOMIXBUF_B2S(&pStream->MixBuf, cb1);
1855 DWORD len2 = AUDIOMIXBUF_B2S(&pStream->MixBuf, cb2);
1856
1857 uint32_t csWrittenTotal = 0;
1858 uint32_t csWritten;
1859 if (pv1 && len1)
1860 {
1861 rc = AudioMixBufWriteAt(&pStream->MixBuf, 0 /* offWrite */,
1862 pv1, cb1, &csWritten);
1863 if (RT_SUCCESS(rc))
1864 csWrittenTotal += csWritten;
1865 }
1866
1867 if ( RT_SUCCESS(rc)
1868 && csWrittenTotal == len1
1869 && pv2 && len2)
1870 {
1871 rc = AudioMixBufWriteAt(&pStream->MixBuf, csWrittenTotal,
1872 pv2, cb2, &csWritten);
1873 if (RT_SUCCESS(rc))
1874 csWrittenTotal += csWritten;
1875 }
1876
1877 directSoundCaptureUnlock(pDSCB, pv1, pv2, cb1, cb2);
1878
1879 if (csWrittenTotal) /* Captured something? */
1880 rc = AudioMixBufMixToParent(&pStream->MixBuf, csWrittenTotal, &cCaptured);
1881
1882 if (RT_SUCCESS(rc))
1883 {
1884 pDSoundStream->csCaptureReadPos = (pDSoundStream->csCaptureReadPos + cCaptured) % pDSoundStream->csCaptureBufferSize;
1885 DSLOGF(("DSound: Capture %ld (%ld+%ld), processed %RU32/%RU32\n",
1886 csCaptured, len1, len2, cCaptured, csWrittenTotal));
1887 }
1888
1889 } while (0);
1890
1891 if (RT_FAILURE(rc))
1892 {
1893 dsoundUpdateStatusInternal(pThis);
1894 }
1895 else
1896 {
1897 if (pcSamplesCaptured)
1898 *pcSamplesCaptured = cCaptured;
1899 }
1900
1901 LogFlowFuncLeaveRC(rc);
1902 return rc;
1903}
1904
1905static int dsoundDestroyStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1906{
1907 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1908 PDSOUNDSTREAMIN pDSoundStream = (PDSOUNDSTREAMIN)pStream;
1909
1910 directSoundCaptureClose(pDSoundStream);
1911
1912 pDSoundStream->csCaptureReadPos = 0;
1913 pDSoundStream->csCaptureBufferSize = 0;
1914 RT_ZERO(pDSoundStream->streamCfg);
1915
1916 return VINF_SUCCESS;
1917}
1918
1919static DECLCALLBACK(int) drvHostDSoundGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
1920{
1921 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1922 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1923
1924 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1925
1926 dsoundUpdateStatusInternalEx(pThis, pCfg, 0 /* fEnum */);
1927
1928 return VINF_SUCCESS;
1929}
1930
1931#ifdef VBOX_WITH_AUDIO_CALLBACKS
1932static int dsoundNotifyThread(PDRVHOSTDSOUND pThis, bool fShutdown)
1933{
1934 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1935
1936 if (fShutdown)
1937 {
1938 LogFlowFunc(("Shutting down thread ...\n"));
1939 pThis->fShutdown = fShutdown;
1940 }
1941
1942 /* Set the notification event so that the thread is being notified. */
1943 BOOL fRc = SetEvent(pThis->aEvents[DSOUNDEVENT_NOTIFY]);
1944 Assert(fRc);
1945
1946 return VINF_SUCCESS;
1947}
1948
1949static DECLCALLBACK(int) dsoundNotificationThread(RTTHREAD hThreadSelf, void *pvUser)
1950{
1951 PDRVHOSTDSOUND pThis = (PDRVHOSTDSOUND)pvUser;
1952 AssertPtr(pThis);
1953
1954 LogFlowFuncEnter();
1955
1956 /* Let caller know that we're done initializing, regardless of the result. */
1957 int rc = RTThreadUserSignal(hThreadSelf);
1958 AssertRC(rc);
1959
1960 do
1961 {
1962 HANDLE aEvents[VBOX_DSOUND_MAX_EVENTS];
1963 DWORD cEvents = 0;
1964 for (uint8_t i = 0; i < VBOX_DSOUND_MAX_EVENTS; i++)
1965 {
1966 if (pThis->aEvents[i])
1967 aEvents[cEvents++] = pThis->aEvents[i];
1968 }
1969 Assert(cEvents);
1970
1971 LogFlowFunc(("Waiting: cEvents=%ld\n", cEvents));
1972
1973 DWORD dwObj = WaitForMultipleObjects(cEvents, aEvents, FALSE /* bWaitAll */, INFINITE);
1974 switch (dwObj)
1975 {
1976 case WAIT_FAILED:
1977 {
1978 rc = VERR_CANCELLED;
1979 break;
1980 }
1981
1982 case WAIT_TIMEOUT:
1983 {
1984 rc = VERR_TIMEOUT;
1985 break;
1986 }
1987
1988 default:
1989 {
1990 dwObj = WAIT_OBJECT_0 + cEvents - 1;
1991 if (aEvents[dwObj] == pThis->aEvents[DSOUNDEVENT_NOTIFY])
1992 {
1993 LogFlowFunc(("Notify\n"));
1994 }
1995 else if (aEvents[dwObj] == pThis->aEvents[DSOUNDEVENT_INPUT])
1996 {
1997
1998 }
1999 else if (aEvents[dwObj] == pThis->aEvents[DSOUNDEVENT_OUTPUT])
2000 {
2001 DWORD cbBuffer, cbFree, cbPlayPos;
2002 rc = dsoundGetPosOut(pThis->pDSStream, &cbBuffer, &cbFree, &cbPlayPos);
2003 if ( RT_SUCCESS(rc)
2004 && cbFree)
2005 {
2006 PDMAUDIOCALLBACKDATAOUT Out;
2007 Out.cbInFree = cbFree;
2008 Out.cbOutWritten = 0;
2009
2010 while (!Out.cbOutWritten)
2011 {
2012 rc = pThis->pUpIAudioConnector->pfnCallback(pThis->pUpIAudioConnector,
2013 PDMAUDIOCALLBACKTYPE_OUTPUT, &Out, sizeof(Out));
2014 if (RT_FAILURE(rc))
2015 break;
2016 RTThreadSleep(100);
2017 }
2018
2019 LogFlowFunc(("Output: cbBuffer=%ld, cbFree=%ld, cbPlayPos=%ld, cbWritten=%RU32, rc=%Rrc\n",
2020 cbBuffer, cbFree, cbPlayPos, Out.cbOutWritten, rc));
2021 }
2022 }
2023 break;
2024 }
2025 }
2026
2027 if (pThis->fShutdown)
2028 break;
2029
2030 } while (RT_SUCCESS(rc));
2031
2032 pThis->fStopped = true;
2033
2034 LogFlowFunc(("Exited with fShutdown=%RTbool, rc=%Rrc\n", pThis->fShutdown, rc));
2035 return rc;
2036}
2037#endif /* VBOX_WITH_AUDIO_CALLBACKS */
2038
2039static DECLCALLBACK(void) drvHostDSoundShutdown(PPDMIHOSTAUDIO pInterface)
2040{
2041 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2042
2043 LogFlowFuncEnter();
2044
2045#ifdef VBOX_WITH_AUDIO_CALLBACKS
2046 int rc = dsoundNotifyThread(pThis, true /* fShutdown */);
2047 AssertRC(rc);
2048
2049 int rcThread;
2050 rc = RTThreadWait(pThis->Thread, 15 * 1000 /* 15s timeout */, &rcThread);
2051 LogFlowFunc(("rc=%Rrc, rcThread=%Rrc\n", rc, rcThread));
2052
2053 Assert(pThis->fStopped);
2054
2055 if (pThis->aEvents[DSOUNDEVENT_NOTIFY])
2056 {
2057 CloseHandle(pThis->aEvents[DSOUNDEVENT_NOTIFY]);
2058 pThis->aEvents[DSOUNDEVENT_NOTIFY] = NULL;
2059}
2060#endif
2061
2062 LogFlowFuncLeave();
2063}
2064
2065static DECLCALLBACK(int) drvHostDSoundInit(PPDMIHOSTAUDIO pInterface)
2066{
2067 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2068
2069 LogFlowFuncEnter();
2070
2071 int rc;
2072
2073 /* Verify that IDirectSound is available. */
2074 LPDIRECTSOUND pDirectSound = NULL;
2075 HRESULT hr = CoCreateInstance(CLSID_DirectSound, NULL, CLSCTX_ALL,
2076 IID_IDirectSound, (void **)&pDirectSound);
2077 if (SUCCEEDED(hr))
2078 {
2079 IDirectSound_Release(pDirectSound);
2080
2081#ifdef VBOX_WITH_AUDIO_CALLBACKS
2082 /* Create notification event. */
2083 pThis->aEvents[DSOUNDEVENT_NOTIFY] = CreateEvent(NULL /* Security attribute */,
2084 FALSE /* bManualReset */, FALSE /* bInitialState */,
2085 NULL /* lpName */);
2086 Assert(pThis->aEvents[DSOUNDEVENT_NOTIFY] != NULL);
2087
2088 /* Start notification thread. */
2089 rc = RTThreadCreate(&pThis->Thread, dsoundNotificationThread,
2090 pThis /*pvUser*/, 0 /*cbStack*/,
2091 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "dsoundNtfy");
2092 if (RT_SUCCESS(rc))
2093 {
2094 /* Wait for the thread to initialize. */
2095 rc = RTThreadUserWait(pThis->Thread, RT_MS_1MIN);
2096 if (RT_FAILURE(rc))
2097 DSLOGREL(("DSound: Waiting for thread to initialize failed with rc=%Rrc\n", rc));
2098 }
2099 else
2100 DSLOGREL(("DSound: Creating thread failed with rc=%Rrc\n", rc));
2101#else
2102 rc = VINF_SUCCESS;
2103#endif
2104
2105 dsoundUpdateStatusInternalEx(pThis, NULL /* pCfg */, DSOUNDENUMCBFLAGS_LOG /* fEnum */);
2106 }
2107 else
2108 {
2109 DSLOGREL(("DSound: DirectSound not available: %Rhrc\n", hr));
2110 rc = VERR_NOT_SUPPORTED;
2111 }
2112
2113 LogFlowFuncLeaveRC(rc);
2114 return rc;
2115}
2116
2117static DECLCALLBACK(void *) drvHostDSoundQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2118{
2119 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2120 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
2121
2122 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
2123 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
2124 return NULL;
2125}
2126
2127static LPCGUID dsoundConfigQueryGUID(PCFGMNODE pCfg, const char *pszName, RTUUID *pUuid)
2128{
2129 LPCGUID pGuid = NULL;
2130
2131 char *pszGuid = NULL;
2132 int rc = CFGMR3QueryStringAlloc(pCfg, pszName, &pszGuid);
2133 if (RT_SUCCESS(rc))
2134 {
2135 rc = RTUuidFromStr(pUuid, pszGuid);
2136 if (RT_SUCCESS(rc))
2137 pGuid = (LPCGUID)&pUuid;
2138 else
2139 DSLOGREL(("DSound: Error parsing device GUID for device '%s': %Rrc\n", pszName, rc));
2140
2141 RTStrFree(pszGuid);
2142 }
2143
2144 return pGuid;
2145}
2146
2147static void dsoundConfigInit(PDRVHOSTDSOUND pThis, PCFGMNODE pCfg)
2148{
2149 unsigned int uBufsizeOut, uBufsizeIn;
2150
2151 CFGMR3QueryUIntDef(pCfg, "BufsizeOut", &uBufsizeOut, _16K);
2152 CFGMR3QueryUIntDef(pCfg, "BufsizeIn", &uBufsizeIn, _16K);
2153 pThis->cfg.cbBufferOut = uBufsizeOut;
2154 pThis->cfg.cbBufferIn = uBufsizeIn;
2155
2156 pThis->cfg.pGuidPlay = dsoundConfigQueryGUID(pCfg, "DeviceGuidOut", &pThis->cfg.uuidPlay);
2157 pThis->cfg.pGuidCapture = dsoundConfigQueryGUID(pCfg, "DeviceGuidIn", &pThis->cfg.uuidCapture);
2158
2159 DSLOG(("DSound: BufsizeOut %u, BufsizeIn %u, DeviceGuidOut {%RTuuid}, DeviceGuidIn {%RTuuid}\n",
2160 pThis->cfg.cbBufferOut,
2161 pThis->cfg.cbBufferIn,
2162 &pThis->cfg.uuidPlay,
2163 &pThis->cfg.uuidCapture));
2164}
2165
2166/*
2167 * PDMIHOSTAUDIO
2168 */
2169
2170static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostDSoundGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
2171{
2172 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
2173
2174 return PDMAUDIOBACKENDSTS_RUNNING;
2175}
2176
2177static DECLCALLBACK(int) drvHostDSoundStreamCreate(PPDMIHOSTAUDIO pInterface,
2178 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples)
2179{
2180 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2181 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2182 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2183
2184 int rc;
2185 if (pCfg->enmDir == PDMAUDIODIR_IN)
2186 rc = dsoundCreateStreamIn(pInterface, pStream, pCfg, pcSamples);
2187 else
2188 rc = dsoundCreateStreamOut(pInterface, pStream, pCfg, pcSamples);
2189
2190 LogFlowFunc(("%s: rc=%Rrc\n", pStream->szName, rc));
2191 return rc;
2192}
2193
2194static DECLCALLBACK(int) drvHostDSoundStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
2195{
2196 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2197 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2198
2199 int rc;
2200 if (pStream->enmDir == PDMAUDIODIR_IN)
2201 rc = dsoundDestroyStreamIn(pInterface, pStream);
2202 else
2203 rc = dsoundDestroyStreamOut(pInterface, pStream);
2204
2205 return rc;
2206}
2207
2208static DECLCALLBACK(int) drvHostDSoundStreamControl(PPDMIHOSTAUDIO pInterface,
2209 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
2210{
2211 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2212 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2213
2214 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
2215
2216 int rc;
2217 if (pStream->enmDir == PDMAUDIODIR_IN)
2218 rc = dsoundControlStreamIn(pInterface, pStream, enmStreamCmd);
2219 else
2220 rc = dsoundControlStreamOut(pInterface, pStream, enmStreamCmd);
2221
2222 return rc;
2223}
2224
2225static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostDSoundStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
2226{
2227 AssertPtrReturn(pInterface, PDMAUDIOSTRMSTS_FLAG_NONE);
2228 AssertPtrReturn(pStream, PDMAUDIOSTRMSTS_FLAG_NONE);
2229
2230 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
2231
2232 PDMAUDIOSTRMSTS strmSts = PDMAUDIOSTRMSTS_FLAG_INITIALIZED;
2233 if (pStream->enmDir == PDMAUDIODIR_IN)
2234 {
2235 PDSOUNDSTREAMIN pDSoundStream = (PDSOUNDSTREAMIN)pStream;
2236 if (pDSoundStream->fEnabled)
2237 strmSts |= PDMAUDIOSTRMSTS_FLAG_ENABLED | PDMAUDIOSTRMSTS_FLAG_DATA_READABLE;
2238 }
2239 else
2240 {
2241 PDSOUNDSTREAMOUT pDSoundStream = (PDSOUNDSTREAMOUT)pStream;
2242 if (pDSoundStream->fEnabled)
2243 {
2244 strmSts |= PDMAUDIOSTRMSTS_FLAG_ENABLED;
2245
2246 DWORD cbFree;
2247 int rc = dsoundGetPosOut(pThis, pDSoundStream, NULL /* cbBuffer */, &cbFree, NULL /* cbPlayPos */);
2248 if ( RT_SUCCESS(rc)
2249 && cbFree)
2250 {
2251 LogFlowFunc(("cbFree=%ld\n", cbFree));
2252 strmSts |= PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE;
2253 }
2254 }
2255 }
2256
2257 return strmSts;
2258}
2259
2260static DECLCALLBACK(int) drvHostDSoundStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
2261{
2262 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2263 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2264
2265 LogFlowFuncEnter();
2266
2267 /* Nothing to do here for DSound. */
2268 return VINF_SUCCESS;
2269}
2270
2271static DECLCALLBACK(void) drvHostDSoundDestruct(PPDMDRVINS pDrvIns)
2272{
2273 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
2274 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2275 LogFlowFuncEnter();
2276
2277 if (pThis->pDrvIns)
2278 CoUninitialize();
2279
2280 LogFlowFuncLeave();
2281}
2282
2283/**
2284 * Construct a DirectSound Audio driver instance.
2285 *
2286 * @copydoc FNPDMDRVCONSTRUCT
2287 */
2288static DECLCALLBACK(int) drvHostDSoundConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
2289{
2290 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
2291
2292 LogRel(("Audio: Initializing DirectSound audio driver\n"));
2293
2294 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2295
2296 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
2297 if (FAILED(hr))
2298 {
2299 DSLOGREL(("DSound: CoInitializeEx failed with %Rhrc\n", hr));
2300 return VERR_NOT_SUPPORTED;
2301 }
2302
2303 /*
2304 * Init basic data members and interfaces.
2305 */
2306 pThis->pDrvIns = pDrvIns;
2307 /* IBase */
2308 pDrvIns->IBase.pfnQueryInterface = drvHostDSoundQueryInterface;
2309 /* IHostAudio */
2310 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostDSound);
2311
2312#ifdef VBOX_WITH_AUDIO_CALLBACKS
2313 /*
2314 * Get the IAudioConnector interface of the above driver/device.
2315 */
2316 pThis->pUpIAudioConnector = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
2317 if (!pThis->pUpIAudioConnector)
2318 {
2319 AssertMsgFailed(("Configuration error: No audio connector interface above!\n"));
2320 return VERR_PDM_MISSING_INTERFACE_ABOVE;
2321 }
2322#endif
2323
2324 /*
2325 * Init the static parts.
2326 */
2327 RTListInit(&pThis->lstDevInput);
2328 RTListInit(&pThis->lstDevOutput);
2329
2330 pThis->fEnabledIn = false;
2331 pThis->fEnabledOut = false;
2332#ifdef VBOX_WITH_AUDIO_CALLBACKS
2333 pThis->fStopped = false;
2334 pThis->fShutdown = false;
2335
2336 RT_ZERO(pThis->aEvents);
2337 pThis->cEvents = 0;
2338#endif
2339
2340 /*
2341 * Initialize configuration values.
2342 */
2343 dsoundConfigInit(pThis, pCfg);
2344
2345 return VINF_SUCCESS;
2346}
2347
2348/**
2349 * PDM driver registration.
2350 */
2351const PDMDRVREG g_DrvHostDSound =
2352{
2353 /* u32Version */
2354 PDM_DRVREG_VERSION,
2355 /* szName */
2356 "DSoundAudio",
2357 /* szRCMod */
2358 "",
2359 /* szR0Mod */
2360 "",
2361 /* pszDescription */
2362 "DirectSound Audio host driver",
2363 /* fFlags */
2364 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2365 /* fClass. */
2366 PDM_DRVREG_CLASS_AUDIO,
2367 /* cMaxInstances */
2368 ~0U,
2369 /* cbInstance */
2370 sizeof(DRVHOSTDSOUND),
2371 /* pfnConstruct */
2372 drvHostDSoundConstruct,
2373 /* pfnDestruct */
2374 drvHostDSoundDestruct,
2375 /* pfnRelocate */
2376 NULL,
2377 /* pfnIOCtl */
2378 NULL,
2379 /* pfnPowerOn */
2380 NULL,
2381 /* pfnReset */
2382 NULL,
2383 /* pfnSuspend */
2384 NULL,
2385 /* pfnResume */
2386 NULL,
2387 /* pfnAttach */
2388 NULL,
2389 /* pfnDetach */
2390 NULL,
2391 /* pfnPowerOff */
2392 NULL,
2393 /* pfnSoftReset */
2394 NULL,
2395 /* u32EndVersion */
2396 PDM_DRVREG_VERSION
2397};
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette