VirtualBox

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

最後變更 在這個檔案從62909是 62909,由 vboxsync 提交於 9 年 前

Devices: warnings, HN, ++

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

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