VirtualBox

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

Last change on this file since 63616 was 63534, checked in by vboxsync, 8 years ago

Audio: Renaming.

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