VirtualBox

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

Last change on this file since 62920 was 62911, checked in by vboxsync, 9 years ago

DrvHoustDSound.cpp: 'static' is a magic word which shall prefix every function definition, unless it's really needed externally (see guidelines). It makes it immediately clear that the function is only used inside this file and avoids unnecessary grepping for other users.

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