VirtualBox

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

Last change on this file since 61541 was 61345, checked in by vboxsync, 9 years ago

Audio: Fixes for DSound backend.

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