VirtualBox

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

Last change on this file since 63214 was 62979, checked in by vboxsync, 8 years ago

Please, don't use the windows 1.x 'long-pointer' HN. It's obsolete for 20 years.

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