VirtualBox

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

Last change on this file since 68085 was 68085, checked in by vboxsync, 7 years ago

Build fix.

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