VirtualBox

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

Last change on this file since 63519 was 63481, checked in by vboxsync, 9 years ago

Audio: callback fixes

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

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