VirtualBox

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

Last change on this file since 58450 was 58378, checked in by vboxsync, 9 years ago

Audio: Renaming / cleanups:

  • Got rid of custom shifting and use AUDIOMIXBUF_S2B / AUDIOMIXBUF_B2S instead.
  • Renamed drvAudioStreamCfgToProps() to DrvAudioStreamCfgToProps() as this is a public function for the host backends.
  • Replaced drvAudioHstOutSamplesLive() with AudioMixBufAvail().
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 53.9 KB
Line 
1/* $Id: DrvHostDSound.cpp 58378 2015-10-22 12:46:32Z vboxsync $ */
2/** @file
3 * Windows host backend driver using DirectSound.
4 */
5
6/*
7 * Copyright (C) 2006-2015 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#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
20#include <VBox/log.h>
21#include <dsound.h>
22
23#include <iprt/alloc.h>
24#include <iprt/uuid.h>
25
26#include "AudioMixBuffer.h"
27#include "DrvAudio.h"
28#include "VBoxDD.h"
29
30/*
31 * IDirectSound* interface uses HRESULT status codes and the driver callbacks use
32 * the IPRT status codes. To minimize HRESULT->IPRT conversion most internal functions
33 * in the driver return HRESULT and conversion is done in the driver callbacks.
34 *
35 * Naming convention:
36 * 'dsound*' functions return IPRT status code;
37 * 'directSound*' - return HRESULT.
38 */
39
40/*
41 * Optional release logging, which a user can turn on with the
42 * 'VBoxManage debugvm' command.
43 * Debug logging still uses the common Log* macros from IPRT.
44 * Messages which always should go to the release log use LogRel.
45 */
46/* General code behavior. */
47#define DSLOG(a) do { LogRel2(a); } while(0)
48/* Something which produce a lot of logging during playback/recording. */
49#define DSLOGF(a) do { LogRel3(a); } while(0)
50/* Important messages like errors. Limited in the default release log to avoid log flood. */
51#define DSLOGREL(a) \
52 do { \
53 static int8_t scLogged = 0; \
54 if (scLogged < 8) { \
55 ++scLogged; \
56 LogRel(a); \
57 } \
58 else { \
59 DSLOG(a); \
60 } \
61 } while (0)
62
63/* Dynamically load dsound.dll. */
64typedef HRESULT WINAPI FNDIRECTSOUNDENUMERATEW(LPDSENUMCALLBACKW pDSEnumCallback, LPVOID pContext);
65typedef FNDIRECTSOUNDENUMERATEW *PFNDIRECTSOUNDENUMERATEW;
66typedef HRESULT WINAPI FNDIRECTSOUNDCAPTUREENUMERATEW(LPDSENUMCALLBACKW pDSEnumCallback, LPVOID pContext);
67typedef FNDIRECTSOUNDCAPTUREENUMERATEW *PFNDIRECTSOUNDCAPTUREENUMERATEW;
68
69typedef struct DSOUNDHOSTCFG
70{
71 DWORD cbBufferIn;
72 DWORD cbBufferOut;
73 RTUUID uuidPlay;
74 LPCGUID pGuidPlay;
75 RTUUID uuidCapture;
76 LPCGUID pGuidCapture;
77} DSOUNDHOSTCFG, *PDSOUNDHOSTCFG;
78
79typedef struct DRVHOSTDSOUND
80{
81 /** Pointer to the driver instance structure. */
82 PPDMDRVINS pDrvIns;
83 /** Pointer to host audio interface. */
84 PDMIHOSTAUDIO IHostAudio;
85 /** List of found host input devices. */
86 RTLISTANCHOR lstDevInput;
87 /** List of found host output devices. */
88 RTLISTANCHOR lstDevOutput;
89 /** DirectSound configuration options. */
90 DSOUNDHOSTCFG cfg;
91} DRVHOSTDSOUND, *PDRVHOSTDSOUND;
92
93typedef struct DSOUNDSTREAMOUT
94{
95 PDMAUDIOHSTSTRMOUT strmOut; /* Always must come first! */
96 LPDIRECTSOUND8 pDS;
97 LPDIRECTSOUNDBUFFER8 pDSB;
98 DWORD cbPlayWritePos;
99 DWORD csPlaybackBufferSize;
100 bool fRestartPlayback;
101 PDMAUDIOSTREAMCFG streamCfg;
102} DSOUNDSTREAMOUT, *PDSOUNDSTREAMOUT;
103
104typedef struct DSOUNDSTREAMIN
105{
106 PDMAUDIOHSTSTRMIN strmIn; /* Always must come first! */
107 LPDIRECTSOUNDCAPTURE8 pDSC;
108 LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB;
109 DWORD csCaptureReadPos;
110 DWORD csCaptureBufferSize;
111 HRESULT hrLastCaptureIn;
112 PDMAUDIORECSOURCE enmRecSource;
113 PDMAUDIOSTREAMCFG streamCfg;
114} DSOUNDSTREAMIN, *PDSOUNDSTREAMIN;
115
116/**
117 * Callback context for enumeration callbacks
118 */
119typedef struct DSOUNDENUMCBCTX
120{
121 PDRVHOSTDSOUND pDrv;
122 PPDMAUDIOBACKENDCFG pCfg;
123} DSOUNDENUMCBCTX, *PDSOUNDENUMCBCTX;
124
125typedef struct DSOUNDDEV
126{
127 RTLISTNODE Node;
128 char *pszName;
129 GUID Guid;
130} DSOUNDDEV, *PDSOUNDDEV;
131
132/** Makes DRVHOSTDSOUND out of PDMIHOSTAUDIO. */
133#define PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface) \
134 ( (PDRVHOSTDSOUND)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTDSOUND, IHostAudio)) )
135
136static void dsoundDevRemove(PDSOUNDDEV pDev);
137
138static DWORD dsoundRingDistance(DWORD offEnd, DWORD offBegin, DWORD cSize)
139{
140 return offEnd >= offBegin ? offEnd - offBegin : cSize - offBegin + offEnd;
141}
142
143static int dsoundWaveFmtFromCfg(PPDMAUDIOSTREAMCFG pCfg, PWAVEFORMATEX pFmt)
144{
145 RT_BZERO(pFmt, sizeof(WAVEFORMATEX));
146 pFmt->wFormatTag = WAVE_FORMAT_PCM;
147 pFmt->nChannels = pCfg->cChannels;
148 pFmt->nSamplesPerSec = pCfg->uHz;
149 pFmt->nAvgBytesPerSec = pCfg->uHz << (pCfg->cChannels == 2 ? 1: 0);
150 pFmt->nBlockAlign = 1 << (pCfg->cChannels == 2 ? 1: 0);
151 pFmt->cbSize = 0; /* No extra data specified. */
152
153 switch (pCfg->enmFormat)
154 {
155 case AUD_FMT_S8:
156 case AUD_FMT_U8:
157 pFmt->wBitsPerSample = 8;
158 break;
159
160 case AUD_FMT_S16:
161 case AUD_FMT_U16:
162 pFmt->wBitsPerSample = 16;
163 pFmt->nAvgBytesPerSec <<= 1;
164 pFmt->nBlockAlign <<= 1;
165 break;
166
167 case AUD_FMT_S32:
168 case AUD_FMT_U32:
169 pFmt->wBitsPerSample = 32;
170 pFmt->nAvgBytesPerSec <<= 2;
171 pFmt->nBlockAlign <<= 2;
172 break;
173
174 default:
175 AssertMsgFailed(("Wave format %ld not supported\n", pCfg->enmFormat));
176 return VERR_NOT_SUPPORTED;
177 }
178
179 return VINF_SUCCESS;
180}
181
182static char *dsoundGUIDToUtf8StrA(LPCGUID lpGUID)
183{
184 if (lpGUID)
185 {
186 LPOLESTR lpOLEStr;
187 HRESULT hr = StringFromCLSID(*lpGUID, &lpOLEStr);
188 if (SUCCEEDED(hr))
189 {
190 char *pszGUID;
191 int rc = RTUtf16ToUtf8(lpOLEStr, &pszGUID);
192 CoTaskMemFree(lpOLEStr);
193
194 return RT_SUCCESS(rc) ? pszGUID : NULL;
195 }
196 }
197
198 return RTStrDup("{Default device}");
199}
200
201static void dsoundFreeDeviceLists(PDRVHOSTDSOUND pThis)
202{
203 PDSOUNDDEV pDev;
204 while (!RTListIsEmpty(&pThis->lstDevInput))
205 {
206 pDev = RTListGetFirst(&pThis->lstDevInput, DSOUNDDEV, Node);
207 dsoundDevRemove(pDev);
208 }
209
210 while (!RTListIsEmpty(&pThis->lstDevOutput))
211 {
212 pDev = RTListGetFirst(&pThis->lstDevOutput, DSOUNDDEV, Node);
213 dsoundDevRemove(pDev);
214 }
215}
216
217static HRESULT directSoundPlayRestore(LPDIRECTSOUNDBUFFER8 pDSB)
218{
219 HRESULT hr = IDirectSoundBuffer8_Restore(pDSB);
220 if (FAILED(hr))
221 DSLOGREL(("DSound: Restore playback buffer %Rhrc\n", hr));
222 return hr;
223}
224
225static HRESULT directSoundPlayUnlock(LPDIRECTSOUNDBUFFER8 pDSB,
226 LPVOID pv1, LPVOID pv2,
227 DWORD cb1, DWORD cb2)
228{
229 HRESULT hr = IDirectSoundBuffer8_Unlock(pDSB, pv1, cb1, pv2, cb2);
230 if (FAILED(hr))
231 DSLOGREL(("DSound: Unlock playback buffer %Rhrc\n", hr));
232 return hr;
233}
234
235static HRESULT directSoundCaptureUnlock(LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB,
236 LPVOID pv1, LPVOID pv2,
237 DWORD cb1, DWORD cb2)
238{
239 HRESULT hr = IDirectSoundCaptureBuffer8_Unlock(pDSCB, pv1, cb1, pv2, cb2);
240 if (FAILED(hr))
241 DSLOGREL(("DSound: Unlock capture buffer %Rhrc\n", hr));
242 return hr;
243}
244
245static HRESULT directSoundPlayLock(LPDIRECTSOUNDBUFFER8 pDSB, PDMPCMPROPS *pProps,
246 DWORD dwOffset, DWORD dwBytes,
247 LPVOID *ppv1, LPVOID *ppv2,
248 DWORD *pcb1, DWORD *pcb2,
249 DWORD dwFlags)
250{
251 LPVOID pv1 = NULL;
252 LPVOID pv2 = NULL;
253 DWORD cb1 = 0;
254 DWORD cb2 = 0;
255
256 HRESULT hr = IDirectSoundBuffer8_Lock(pDSB, dwOffset, dwBytes, &pv1, &cb1, &pv2, &cb2, dwFlags);
257 if (hr == DSERR_BUFFERLOST)
258 {
259 hr = directSoundPlayRestore(pDSB);
260 if (SUCCEEDED(hr))
261 {
262 hr = IDirectSoundBuffer8_Lock(pDSB, dwOffset, dwBytes, &pv1, &cb1, &pv2, &cb2, dwFlags);
263 }
264 }
265
266 if (FAILED(hr))
267 {
268 DSLOGREL(("DSound: Lock playback buffer %Rhrc\n", hr));
269 return hr;
270 }
271
272 if ( (pv1 && (cb1 & pProps->uAlign))
273 || (pv2 && (cb2 & pProps->uAlign)))
274 {
275 DSLOGREL(("DSound: Locking playback buffer returned misaligned buffer: cb1=%RI32, cb2=%RI32 (alignment: %RU32)\n",
276 cb1, cb2, pProps->uAlign));
277 directSoundPlayUnlock(pDSB, pv1, pv2, cb1, cb2);
278 return E_FAIL;
279 }
280
281 *ppv1 = pv1;
282 *ppv2 = pv2;
283 *pcb1 = cb1;
284 *pcb2 = cb2;
285
286 return S_OK;
287}
288
289static HRESULT directSoundCaptureLock(LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB, PPDMPCMPROPS pProps,
290 DWORD dwOffset, DWORD dwBytes,
291 LPVOID *ppv1, LPVOID *ppv2,
292 DWORD *pcb1, DWORD *pcb2,
293 DWORD dwFlags)
294{
295 LPVOID pv1 = NULL;
296 LPVOID pv2 = NULL;
297 DWORD cb1 = 0;
298 DWORD cb2 = 0;
299
300 HRESULT hr = IDirectSoundCaptureBuffer8_Lock(pDSCB, dwOffset, dwBytes,
301 &pv1, &cb1, &pv2, &cb2, dwFlags);
302 if (FAILED(hr))
303 {
304 DSLOGREL(("DSound: Lock capture buffer %Rhrc\n", hr));
305 return hr;
306 }
307
308 if ( (pv1 && (cb1 & pProps->uAlign))
309 || (pv2 && (cb2 & pProps->uAlign)))
310 {
311 DSLOGREL(("DSound: Locking capture buffer returned misaligned buffer: cb1=%RI32, cb2=%RI32 (alignment: %RU32)\n",
312 cb1, cb2, pProps->uAlign));
313 directSoundCaptureUnlock(pDSCB, pv1, pv2, cb1, cb2);
314 return E_FAIL;
315 }
316
317 *ppv1 = pv1;
318 *ppv2 = pv2;
319 *pcb1 = cb1;
320 *pcb2 = cb2;
321
322 return S_OK;
323}
324
325
326/*
327 * DirectSound playback
328 */
329
330static void directSoundPlayInterfaceRelease(PDSOUNDSTREAMOUT pDSoundStrmOut)
331{
332 if (pDSoundStrmOut->pDS)
333 {
334 IDirectSound8_Release(pDSoundStrmOut->pDS);
335 pDSoundStrmOut->pDS = NULL;
336 }
337}
338
339static HRESULT directSoundPlayInterfaceCreate(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
340{
341 if (pDSoundStrmOut->pDS != NULL)
342 {
343 DSLOG(("DSound: DirectSound instance already exists\n"));
344 return S_OK;
345 }
346
347 HRESULT hr = CoCreateInstance(CLSID_DirectSound8, NULL, CLSCTX_ALL,
348 IID_IDirectSound8, (void **)&pDSoundStrmOut->pDS);
349 if (FAILED(hr))
350 {
351 DSLOGREL(("DSound: Create DirectSound instance %Rhrc\n", hr));
352 }
353 else
354 {
355 hr = IDirectSound8_Initialize(pDSoundStrmOut->pDS, pThis->cfg.pGuidPlay);
356 if (SUCCEEDED(hr))
357 {
358 HWND hWnd = GetDesktopWindow();
359 hr = IDirectSound8_SetCooperativeLevel(pDSoundStrmOut->pDS, hWnd, DSSCL_PRIORITY);
360 if (FAILED(hr))
361 DSLOGREL(("DSound: Set cooperative level for window %p %Rhrc\n", hWnd, hr));
362 }
363
364 if (FAILED(hr))
365 {
366 if (hr == DSERR_NODRIVER) /* Usually means that no playback devices are attached. */
367 DSLOGREL(("DSound: DirectSound playback is currently unavailable\n"));
368 else
369 DSLOGREL(("DSound: DirectSound playback initialize %Rhrc\n", hr));
370
371 directSoundPlayInterfaceRelease(pDSoundStrmOut);
372 }
373 }
374
375 return hr;
376}
377
378static void directSoundPlayClose(PDSOUNDSTREAMOUT pDSoundStrmOut)
379{
380 DSLOG(("DSound: Closing playback stream %p, buffer %p\n", pDSoundStrmOut, pDSoundStrmOut->pDSB));
381
382 if (pDSoundStrmOut->pDSB)
383 {
384 HRESULT hr = IDirectSoundBuffer8_Stop(pDSoundStrmOut->pDSB);
385 if (FAILED(hr))
386 DSLOGREL(("DSound: Stop playback stream %p when closing %Rhrc\n", pDSoundStrmOut, hr));
387
388 IDirectSoundBuffer8_Release(pDSoundStrmOut->pDSB);
389 pDSoundStrmOut->pDSB = NULL;
390 }
391
392 directSoundPlayInterfaceRelease(pDSoundStrmOut);
393}
394
395static HRESULT directSoundPlayOpen(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
396{
397 AssertPtrReturn(pThis, E_POINTER);
398 AssertPtrReturn(pDSoundStrmOut, E_POINTER);
399
400 DSLOG(("DSound: pDSoundStrmOut=%p, cbBufferOut=%ld, uHz=%RU32, cChannels=%RU8, cBits=%RU8, fSigned=%RTbool\n",
401 pDSoundStrmOut,
402 pThis->cfg.cbBufferOut,
403 pDSoundStrmOut->strmOut.Props.uHz,
404 pDSoundStrmOut->strmOut.Props.cChannels,
405 pDSoundStrmOut->strmOut.Props.cBits,
406 pDSoundStrmOut->strmOut.Props.fSigned));
407
408 if (pDSoundStrmOut->pDSB != NULL)
409 {
410 /* Should not happen but be forgiving. */
411 DSLOGREL(("DSound: DirectSoundBuffer already exists\n"));
412 directSoundPlayClose(pDSoundStrmOut);
413 }
414
415 WAVEFORMATEX wfx;
416 int rc = dsoundWaveFmtFromCfg(&pDSoundStrmOut->streamCfg, &wfx);
417 if (RT_FAILURE(rc))
418 return E_INVALIDARG;
419
420 HRESULT hr = directSoundPlayInterfaceCreate(pThis, pDSoundStrmOut);
421 if (FAILED(hr))
422 return hr;
423
424 do /* To use breaks. */
425 {
426 LPDIRECTSOUNDBUFFER pDSB = NULL;
427 DSBUFFERDESC bd;
428 RT_ZERO(bd);
429 bd.dwSize = sizeof(bd);
430 bd.lpwfxFormat = &wfx;
431 bd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
432 bd.dwBufferBytes = pThis->cfg.cbBufferOut;
433 hr = IDirectSound8_CreateSoundBuffer(pDSoundStrmOut->pDS,
434 &bd, &pDSB, NULL);
435 if (FAILED(hr))
436 {
437 DSLOGREL(("DSound: CreateSoundBuffer %Rhrc\n", hr));
438 break;
439 }
440
441 /* "Upgrade" to IDirectSoundBuffer8 intarface. */
442 hr = IDirectSoundBuffer_QueryInterface(pDSB, IID_IDirectSoundBuffer8, (void **)&pDSoundStrmOut->pDSB);
443 pDSB->Release();
444 if (FAILED(hr))
445 {
446 DSLOGREL(("DSound: Query IDirectSoundBuffer8 %Rhrc\n", hr));
447 break;
448 }
449
450 /*
451 * Query the actual parameters.
452 */
453 hr = IDirectSoundBuffer8_GetFormat(pDSoundStrmOut->pDSB, &wfx, sizeof(wfx), NULL);
454 if (FAILED(hr))
455 {
456 DSLOGREL(("DSound: Playback GetFormat %Rhrc\n", hr));
457 break;
458 }
459
460 DSBCAPS bc;
461 RT_ZERO(bc);
462 bc.dwSize = sizeof(bc);
463 hr = IDirectSoundBuffer8_GetCaps(pDSoundStrmOut->pDSB, &bc);
464 if (FAILED(hr))
465 {
466 DSLOGREL(("DSound: Playback GetCaps %Rhrc\n", hr));
467 break;
468 }
469
470 DSLOG(("DSound: Playback format:\n"
471 " dwBufferBytes = %RI32\n"
472 " wFormatTag = %RI16\n"
473 " nChannels = %RI16\n"
474 " nSamplesPerSec = %RU32\n"
475 " nAvgBytesPerSec = %RU32\n"
476 " nBlockAlign = %RI16\n"
477 " wBitsPerSample = %RI16\n"
478 " cbSize = %RI16\n",
479 bc.dwBufferBytes,
480 wfx.wFormatTag,
481 wfx.nChannels,
482 wfx.nSamplesPerSec,
483 wfx.nAvgBytesPerSec,
484 wfx.nBlockAlign,
485 wfx.wBitsPerSample,
486 wfx.cbSize));
487
488 if (bc.dwBufferBytes & pDSoundStrmOut->strmOut.Props.uAlign)
489 DSLOGREL(("DSound: Playback GetCaps returned misaligned buffer: size %RU32, alignment %RU32\n",
490 bc.dwBufferBytes, pDSoundStrmOut->strmOut.Props.uAlign + 1));
491
492 if (bc.dwBufferBytes != pThis->cfg.cbBufferOut)
493 DSLOGREL(("DSound: Playback buffer size mismatched: DirectSound %RU32, requested %RU32 bytes\n",
494 bc.dwBufferBytes, pThis->cfg.cbBufferOut));
495
496 /*
497 * Initial state.
498 * dsoundPlayStart initializes part of it to make sure that Stop/Start continues with a correct
499 * playback buffer position.
500 */
501 pDSoundStrmOut->csPlaybackBufferSize = bc.dwBufferBytes >> pDSoundStrmOut->strmOut.Props.cShift;
502 DSLOG(("DSound: csPlaybackBufferSize=%RU32\n", pDSoundStrmOut->csPlaybackBufferSize));
503
504 } while (0);
505
506 if (FAILED(hr))
507 directSoundPlayClose(pDSoundStrmOut);
508
509 return hr;
510}
511
512static void dsoundPlayClearSamples(PDSOUNDSTREAMOUT pDSoundStrmOut)
513{
514 AssertPtrReturnVoid(pDSoundStrmOut);
515
516 PPDMAUDIOHSTSTRMOUT pStrmOut = &pDSoundStrmOut->strmOut;
517
518 LPVOID pv1, pv2;
519 DWORD cb1, cb2;
520 HRESULT hr = directSoundPlayLock(pDSoundStrmOut->pDSB, &pDSoundStrmOut->strmOut.Props,
521 0 /* dwOffset */, AUDIOMIXBUF_S2B(&pStrmOut->MixBuf, pDSoundStrmOut->csPlaybackBufferSize),
522 &pv1, &pv2, &cb1, &cb2, DSBLOCK_ENTIREBUFFER);
523 if (SUCCEEDED(hr))
524 {
525 DWORD len1 = AUDIOMIXBUF_B2S(&pStrmOut->MixBuf, cb1);
526 DWORD len2 = AUDIOMIXBUF_B2S(&pStrmOut->MixBuf, cb2);
527
528 if (pv1 && len1)
529 DrvAudioClearBuf(&pDSoundStrmOut->strmOut.Props, pv1, cb1, len1);
530
531 if (pv2 && len2)
532 DrvAudioClearBuf(&pDSoundStrmOut->strmOut.Props, pv2, cb2, len2);
533
534 directSoundPlayUnlock(pDSoundStrmOut->pDSB, pv1, pv2, cb1, cb2);
535 }
536}
537
538static HRESULT directSoundPlayGetStatus(LPDIRECTSOUNDBUFFER8 pDSB, DWORD *pdwStatus)
539{
540 AssertPtrReturn(pDSB, E_POINTER);
541 /* pdwStatus is optional. */
542
543 DWORD dwStatus = 0;
544 HRESULT hr = IDirectSoundBuffer8_GetStatus(pDSB, &dwStatus);
545 if (SUCCEEDED(hr))
546 {
547 if ((dwStatus & DSBSTATUS_BUFFERLOST) != 0)
548 {
549 hr = directSoundPlayRestore(pDSB);
550 if (SUCCEEDED(hr))
551 hr = IDirectSoundBuffer8_GetStatus(pDSB, &dwStatus);
552 }
553 }
554
555 if (SUCCEEDED(hr))
556 {
557 if (pdwStatus)
558 *pdwStatus = dwStatus;
559 }
560 else
561 DSLOGREL(("DSound: Playback GetStatus %Rhrc\n", hr));
562
563 return hr;
564}
565
566static void directSoundPlayStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
567{
568 AssertPtrReturnVoid(pThis);
569 AssertPtrReturnVoid(pDSoundStrmOut);
570
571 if (pDSoundStrmOut->pDSB != NULL)
572 {
573 /* This performs some restore, so call it anyway and ignore result. */
574 directSoundPlayGetStatus(pDSoundStrmOut->pDSB, NULL /* Status */);
575
576 DSLOG(("DSound: Stopping playback\n"));
577
578 /* @todo Wait until all data in the buffer has been played. */
579 HRESULT hr = IDirectSoundBuffer8_Stop(pDSoundStrmOut->pDSB);
580 if (SUCCEEDED(hr))
581 dsoundPlayClearSamples(pDSoundStrmOut);
582 else
583 DSLOGREL(("DSound: Stop playback %Rhrc\n", hr));
584 }
585}
586
587static HRESULT directSoundPlayStart(PDSOUNDSTREAMOUT pDSoundStrmOut)
588{
589 AssertPtrReturn(pDSoundStrmOut, E_POINTER);
590
591 HRESULT hr;
592 if (pDSoundStrmOut->pDSB != NULL)
593 {
594 DWORD dwStatus;
595 hr = directSoundPlayGetStatus(pDSoundStrmOut->pDSB, &dwStatus);
596 if (SUCCEEDED(hr))
597 {
598 if (dwStatus & DSBSTATUS_PLAYING)
599 {
600 DSLOG(("DSound: Already playing\n"));
601 }
602 else
603 {
604 dsoundPlayClearSamples(pDSoundStrmOut);
605
606 pDSoundStrmOut->fRestartPlayback = true;
607
608 DSLOG(("DSound: Playback start\n"));
609
610 /* The actual IDirectSoundBuffer8_Play call will be made in drvHostDSoundPlayOut,
611 * because it is necessary to put some samples into the buffer first.
612 */
613 }
614 }
615 }
616 else
617 hr = E_UNEXPECTED;
618
619 return hr;
620}
621
622/*
623 * DirectSoundCapture
624 */
625
626static LPCGUID dsoundCaptureSelectDevice(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
627{
628 AssertPtrReturn(pThis, NULL);
629 AssertPtrReturn(pDSoundStrmIn, NULL);
630
631 LPCGUID pGUID = pThis->cfg.pGuidCapture;
632
633 if (!pGUID)
634 {
635 PDSOUNDDEV pDev = NULL;
636
637 switch (pDSoundStrmIn->enmRecSource)
638 {
639 case PDMAUDIORECSOURCE_MIC:
640 {
641 RTListForEach(&pThis->lstDevInput, pDev, DSOUNDDEV, Node)
642 {
643 if (RTStrIStr(pDev->pszName, "Mic")) /** @todo what is with non en_us windows versions? */
644 break;
645 }
646 if (RTListNodeIsDummy(&pThis->lstDevInput, pDev, DSOUNDDEV, Node))
647 pDev = NULL; /* Found nothing. */
648
649 break;
650 }
651
652 case PDMAUDIORECSOURCE_LINE_IN:
653 default:
654 /* Try opening the default device (NULL). */
655 break;
656 }
657
658 if (pDev)
659 {
660 DSLOG(("DSound: Guest \"%s\" is using host \"%s\"\n",
661 drvAudioRecSourceToString(pDSoundStrmIn->enmRecSource), pDev->pszName));
662
663 pGUID = &pDev->Guid;
664 }
665 }
666
667 char *pszGUID = dsoundGUIDToUtf8StrA(pGUID);
668 /* This always has to be in the release log. */
669 LogRel(("DSound: Guest \"%s\" is using host device with GUID: %s\n",
670 drvAudioRecSourceToString(pDSoundStrmIn->enmRecSource), pszGUID? pszGUID: "{?}"));
671 RTStrFree(pszGUID);
672
673 return pGUID;
674}
675
676static void directSoundCaptureInterfaceRelease(PDSOUNDSTREAMIN pDSoundStrmIn)
677{
678 if (pDSoundStrmIn->pDSC)
679 {
680 IDirectSoundCapture_Release(pDSoundStrmIn->pDSC);
681 pDSoundStrmIn->pDSC = NULL;
682 }
683}
684
685static HRESULT directSoundCaptureInterfaceCreate(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
686{
687 if (pDSoundStrmIn->pDSC != NULL)
688 {
689 DSLOG(("DSound: DirectSoundCapture instance already exists\n"));
690 return S_OK;
691 }
692
693 HRESULT hr = CoCreateInstance(CLSID_DirectSoundCapture8, NULL, CLSCTX_ALL,
694 IID_IDirectSoundCapture8, (void **)&pDSoundStrmIn->pDSC);
695 if (FAILED(hr))
696 {
697 DSLOGREL(("DSound: DirectSoundCapture create %Rhrc\n", hr));
698 }
699 else
700 {
701 LPCGUID pGUID = dsoundCaptureSelectDevice(pThis, pDSoundStrmIn);
702 hr = IDirectSoundCapture_Initialize(pDSoundStrmIn->pDSC, pGUID);
703 if (FAILED(hr))
704 {
705 if (hr == DSERR_NODRIVER) /* Usually means that no capture devices are attached. */
706 DSLOGREL(("DSound: DirectSound capture is currently unavailable\n"));
707 else
708 DSLOGREL(("DSound: DirectSoundCapture initialize %Rhrc\n", hr));
709
710 directSoundCaptureInterfaceRelease(pDSoundStrmIn);
711 }
712 }
713
714 return hr;
715}
716
717static void directSoundCaptureClose(PDSOUNDSTREAMIN pDSoundStrmIn)
718{
719 AssertPtrReturnVoid(pDSoundStrmIn);
720
721 DSLOG(("DSound: pDSoundStrmIn=%p, pDSCB=%p\n", pDSoundStrmIn, pDSoundStrmIn->pDSCB));
722
723 if (pDSoundStrmIn->pDSCB)
724 {
725 HRESULT hr = IDirectSoundCaptureBuffer_Stop(pDSoundStrmIn->pDSCB);
726 if (FAILED(hr))
727 DSLOGREL(("DSound: Stop capture buffer %Rhrc\n", hr));
728
729 IDirectSoundCaptureBuffer8_Release(pDSoundStrmIn->pDSCB);
730 pDSoundStrmIn->pDSCB = NULL;
731 }
732
733 directSoundCaptureInterfaceRelease(pDSoundStrmIn);
734}
735
736static HRESULT directSoundCaptureOpen(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
737{
738 AssertPtrReturn(pThis, E_POINTER);
739 AssertPtrReturn(pDSoundStrmIn, E_POINTER);
740
741 DSLOG(("DSound: pDSoundStrmIn=%p, cbBufferIn=%ld, uHz=%RU32, cChannels=%RU8, cBits=%RU8, fSigned=%RTbool\n",
742 pDSoundStrmIn,
743 pThis->cfg.cbBufferIn,
744 pDSoundStrmIn->strmIn.Props.uHz,
745 pDSoundStrmIn->strmIn.Props.cChannels,
746 pDSoundStrmIn->strmIn.Props.cBits,
747 pDSoundStrmIn->strmIn.Props.fSigned));
748
749 if (pDSoundStrmIn->pDSCB != NULL)
750 {
751 /* Should not happen but be forgiving. */
752 DSLOGREL(("DSound: DirectSoundCaptureBuffer already exists\n"));
753 directSoundCaptureClose(pDSoundStrmIn);
754 }
755
756 WAVEFORMATEX wfx;
757 int rc = dsoundWaveFmtFromCfg(&pDSoundStrmIn->streamCfg, &wfx);
758 if (RT_FAILURE(rc))
759 return E_INVALIDARG;
760
761 HRESULT hr = directSoundCaptureInterfaceCreate(pThis, pDSoundStrmIn);
762 if (FAILED(hr))
763 return hr;
764
765 do /* To use breaks. */
766 {
767 LPDIRECTSOUNDCAPTUREBUFFER pDSCB = NULL;
768 DSCBUFFERDESC bd;
769 RT_ZERO(bd);
770 bd.dwSize = sizeof(bd);
771 bd.lpwfxFormat = &wfx;
772 bd.dwBufferBytes = pThis->cfg.cbBufferIn;
773 hr = IDirectSoundCapture_CreateCaptureBuffer(pDSoundStrmIn->pDSC,
774 &bd, &pDSCB, NULL);
775 if (FAILED(hr))
776 {
777 DSLOGREL(("DSound: CreateCaptureBuffer %Rhrc\n", hr));
778 break;
779 }
780
781 hr = IDirectSoundCaptureBuffer_QueryInterface(pDSCB, IID_IDirectSoundCaptureBuffer8, (void **)&pDSoundStrmIn->pDSCB);
782 IDirectSoundCaptureBuffer_Release(pDSCB);
783 if (FAILED(hr))
784 {
785 DSLOGREL(("DSound: Query IDirectSoundCaptureBuffer8 %Rhrc\n", hr));
786 break;
787 }
788
789 /*
790 * Query the actual parameters.
791 */
792 DWORD cbReadPos = 0;
793 hr = IDirectSoundCaptureBuffer8_GetCurrentPosition(pDSoundStrmIn->pDSCB, NULL, &cbReadPos);
794 if (FAILED(hr))
795 {
796 cbReadPos = 0;
797 DSLOGREL(("DSound: Capture (open) GetCurrentPosition %Rhrc\n", hr));
798 }
799
800 RT_ZERO(wfx);
801 hr = IDirectSoundCaptureBuffer8_GetFormat(pDSoundStrmIn->pDSCB, &wfx, sizeof(wfx), NULL);
802 if (FAILED(hr))
803 {
804 DSLOGREL(("DSound: Capture GetFormat %Rhrc\n", hr));
805 break;
806 }
807
808 DSCBCAPS bc;
809 RT_ZERO(bc);
810 bc.dwSize = sizeof(bc);
811 hr = IDirectSoundCaptureBuffer8_GetCaps(pDSoundStrmIn->pDSCB, &bc);
812 if (FAILED(hr))
813 {
814 DSLOGREL(("DSound: Capture GetCaps %Rhrc\n", hr));
815 break;
816 }
817
818 DSLOG(("DSound: Capture format:\n"
819 " dwBufferBytes = %RI32\n"
820 " wFormatTag = %RI16\n"
821 " nChannels = %RI16\n"
822 " nSamplesPerSec = %RU32\n"
823 " nAvgBytesPerSec = %RU32\n"
824 " nBlockAlign = %RI16\n"
825 " wBitsPerSample = %RI16\n"
826 " cbSize = %RI16\n",
827 bc.dwBufferBytes,
828 wfx.wFormatTag,
829 wfx.nChannels,
830 wfx.nSamplesPerSec,
831 wfx.nAvgBytesPerSec,
832 wfx.nBlockAlign,
833 wfx.wBitsPerSample,
834 wfx.cbSize));
835
836 if (bc.dwBufferBytes & pDSoundStrmIn->strmIn.Props.uAlign)
837 DSLOGREL(("DSound: Capture GetCaps returned misaligned buffer: size %RU32, alignment %RU32\n",
838 bc.dwBufferBytes, pDSoundStrmIn->strmIn.Props.uAlign + 1));
839
840 if (bc.dwBufferBytes != pThis->cfg.cbBufferIn)
841 DSLOGREL(("DSound: Capture buffer size mismatched: DirectSound %RU32, requested %RU32 bytes\n",
842 bc.dwBufferBytes, pThis->cfg.cbBufferIn));
843
844 /* Initial state: reading at the initial capture position, no error. */
845 pDSoundStrmIn->csCaptureReadPos = cbReadPos >> pDSoundStrmIn->strmIn.Props.cShift;
846 pDSoundStrmIn->csCaptureBufferSize = bc.dwBufferBytes >> pDSoundStrmIn->strmIn.Props.cShift;
847 pDSoundStrmIn->hrLastCaptureIn = S_OK;
848
849 DSLOG(("DSound: csCaptureReadPos=%RU32, csCaptureBufferSize=%RU32\n",
850 pDSoundStrmIn->csCaptureReadPos, pDSoundStrmIn->csCaptureBufferSize));
851
852 } while (0);
853
854 if (FAILED(hr))
855 directSoundCaptureClose(pDSoundStrmIn);
856
857 return hr;
858}
859
860static void directSoundCaptureStop(PDSOUNDSTREAMIN pDSoundStrmIn)
861{
862 AssertPtrReturnVoid(pDSoundStrmIn);
863
864 if (pDSoundStrmIn->pDSCB)
865 {
866 DSLOG(("DSound: Stopping capture\n"));
867
868 HRESULT hr = IDirectSoundCaptureBuffer_Stop(pDSoundStrmIn->pDSCB);
869 if (FAILED(hr))
870 DSLOGREL(("DSound: Capture buffer stop %Rhrc\n", hr));
871 }
872}
873
874static HRESULT directSoundCaptureStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
875{
876 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
877 AssertPtrReturn(pDSoundStrmIn, VERR_INVALID_POINTER);
878
879 HRESULT hr;
880 if (pDSoundStrmIn->pDSCB != NULL)
881 {
882 DWORD dwStatus;
883 hr = IDirectSoundCaptureBuffer8_GetStatus(pDSoundStrmIn->pDSCB, &dwStatus);
884 if (FAILED(hr))
885 {
886 DSLOGREL(("DSound: Capture start GetStatus %Rhrc\n", hr));
887 }
888 else
889 {
890 if (dwStatus & DSCBSTATUS_CAPTURING)
891 {
892 DSLOG(("DSound: Already capturing\n"));
893 }
894 else
895 {
896 DSLOG(("DSound: Capture start\n"));
897 hr = IDirectSoundCaptureBuffer8_Start(pDSoundStrmIn->pDSCB, DSCBSTART_LOOPING);
898 if (FAILED(hr))
899 DSLOGREL(("DSound: Capture started %Rhrc\n", hr));
900 }
901 }
902 }
903 else
904 {
905 hr = E_UNEXPECTED;
906 }
907
908 return hr;
909}
910
911static int dsoundDevAdd(PRTLISTANCHOR pList, LPGUID lpGUID,
912 LPCWSTR lpwstrDescription, PDSOUNDDEV *ppDev)
913{
914 AssertPtrReturn(pList, VERR_INVALID_POINTER);
915 AssertPtrReturn(lpGUID, VERR_INVALID_POINTER);
916 AssertPtrReturn(lpwstrDescription, VERR_INVALID_POINTER);
917
918 PDSOUNDDEV pDev = (PDSOUNDDEV)RTMemAlloc(sizeof(DSOUNDDEV));
919 if (!pDev)
920 return VERR_NO_MEMORY;
921
922 int rc = RTUtf16ToUtf8(lpwstrDescription, &pDev->pszName);
923 if (RT_SUCCESS(rc))
924 memcpy(&pDev->Guid, lpGUID, sizeof(GUID));
925
926 if (RT_SUCCESS(rc))
927 RTListAppend(pList, &pDev->Node);
928
929 if (ppDev)
930 *ppDev = pDev;
931
932 return rc;
933}
934
935static void dsoundDevRemove(PDSOUNDDEV pDev)
936{
937 if (pDev)
938 {
939 RTStrFree(pDev->pszName);
940 pDev->pszName = NULL;
941
942 RTListNodeRemove(&pDev->Node);
943
944 RTMemFree(pDev);
945 }
946}
947
948static void dsoundLogDevice(const char *pszType, LPGUID lpGUID, LPCWSTR lpwstrDescription, LPCWSTR lpwstrModule)
949{
950 char *pszGUID = dsoundGUIDToUtf8StrA(lpGUID);
951 /* This always has to be in the release log. */
952 LogRel(("DSound: %s: GUID: %s [%ls] (Module: %ls)\n",
953 pszType, pszGUID? pszGUID: "{?}", lpwstrDescription, lpwstrModule));
954 RTStrFree(pszGUID);
955}
956
957static BOOL CALLBACK dsoundEnumCallback(LPGUID lpGUID, LPCWSTR lpwstrDescription,
958 LPCWSTR lpwstrModule, LPVOID lpContext)
959{
960 PDSOUNDENUMCBCTX pCtx = (PDSOUNDENUMCBCTX)lpContext;
961 AssertPtrReturn(pCtx, FALSE);
962 AssertPtrReturn(pCtx->pDrv, FALSE);
963 AssertPtrReturn(pCtx->pCfg, FALSE);
964
965 if (!lpGUID)
966 return TRUE;
967
968 AssertPtrReturn(lpwstrDescription, FALSE);
969 /* Do not care about lpwstrModule */
970
971 dsoundLogDevice("Output", lpGUID, lpwstrDescription, lpwstrModule);
972
973 int rc = dsoundDevAdd(&pCtx->pDrv->lstDevOutput,
974 lpGUID, lpwstrDescription, NULL /* ppDev */);
975 if (RT_FAILURE(rc))
976 return FALSE; /* Abort enumeration. */
977
978 pCtx->pCfg->cMaxHstStrmsOut++;
979
980 return TRUE;
981}
982
983static BOOL CALLBACK dsoundCaptureEnumCallback(LPGUID lpGUID, LPCWSTR lpwstrDescription,
984 LPCWSTR lpwstrModule, LPVOID lpContext)
985{
986 PDSOUNDENUMCBCTX pCtx = (PDSOUNDENUMCBCTX)lpContext;
987 AssertPtrReturn(pCtx, FALSE);
988 AssertPtrReturn(pCtx->pDrv, FALSE);
989 AssertPtrReturn(pCtx->pCfg, FALSE);
990
991 if (!lpGUID)
992 return TRUE;
993
994 dsoundLogDevice("Input", lpGUID, lpwstrDescription, lpwstrModule);
995
996 int rc = dsoundDevAdd(&pCtx->pDrv->lstDevInput,
997 lpGUID, lpwstrDescription, NULL /* ppDev */);
998 if (RT_FAILURE(rc))
999 return FALSE; /* Abort enumeration. */
1000
1001 pCtx->pCfg->cMaxHstStrmsIn++;
1002
1003 return TRUE;
1004}
1005
1006
1007/*
1008 * PDMIHOSTAUDIO
1009 */
1010
1011static DECLCALLBACK(int) drvHostDSoundInitOut(PPDMIHOSTAUDIO pInterface,
1012 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
1013 uint32_t *pcSamples)
1014{
1015 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1016 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
1017 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1018 /* pcSamples is optional. */
1019
1020 LogFlowFunc(("pHstStrmOut=%p, pCfg=%p\n", pHstStrmOut, pCfg));
1021
1022 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1023 PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
1024
1025 pDSoundStrmOut->streamCfg = *pCfg;
1026 pDSoundStrmOut->streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
1027
1028 int rc = DrvAudioStreamCfgToProps(&pDSoundStrmOut->streamCfg, &pDSoundStrmOut->strmOut.Props);
1029 if (RT_SUCCESS(rc))
1030 {
1031 pDSoundStrmOut->pDS = NULL;
1032 pDSoundStrmOut->pDSB = NULL;
1033 pDSoundStrmOut->cbPlayWritePos = 0;
1034 pDSoundStrmOut->fRestartPlayback = true;
1035 pDSoundStrmOut->csPlaybackBufferSize = 0;
1036
1037 if (pcSamples)
1038 *pcSamples = pThis->cfg.cbBufferOut >> pHstStrmOut->Props.cShift;
1039
1040 /* Try to open playback in case the device is already there. */
1041 directSoundPlayOpen(pThis, pDSoundStrmOut);
1042 }
1043 else
1044 {
1045 RT_ZERO(pDSoundStrmOut->streamCfg);
1046 }
1047
1048 LogFlowFuncLeaveRC(rc);
1049 return rc;
1050}
1051
1052static DECLCALLBACK(int) drvHostDSoundControlOut(PPDMIHOSTAUDIO pInterface,
1053 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PDMAUDIOSTREAMCMD enmStreamCmd)
1054{
1055 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1056 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
1057
1058 LogFlowFunc(("pHstStrmOut=%p, cmd=%d\n", pHstStrmOut, enmStreamCmd));
1059
1060 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1061 PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
1062
1063 int rc = VINF_SUCCESS;
1064 switch (enmStreamCmd)
1065 {
1066 case PDMAUDIOSTREAMCMD_ENABLE:
1067 {
1068 DSLOG(("DSound: Playback PDMAUDIOSTREAMCMD_ENABLE\n"));
1069 /* Try to start playback. If it fails, then reopen and try again. */
1070 HRESULT hr = directSoundPlayStart(pDSoundStrmOut);
1071 if (FAILED(hr))
1072 {
1073 directSoundPlayClose(pDSoundStrmOut);
1074 directSoundPlayOpen(pThis, pDSoundStrmOut);
1075
1076 hr = directSoundPlayStart(pDSoundStrmOut);
1077 }
1078
1079 if (FAILED(hr))
1080 rc = VERR_NOT_SUPPORTED;
1081
1082 break;
1083 }
1084
1085 case PDMAUDIOSTREAMCMD_DISABLE:
1086 {
1087 DSLOG(("DSound: Playback PDMAUDIOSTREAMCMD_DISABLE\n"));
1088 directSoundPlayStop(pThis, pDSoundStrmOut);
1089 break;
1090 }
1091
1092 default:
1093 {
1094 AssertMsgFailed(("Invalid command: %ld\n", enmStreamCmd));
1095 rc = VERR_INVALID_PARAMETER;
1096 break;
1097 }
1098 }
1099
1100 LogFlowFuncLeaveRC(rc);
1101 return rc;
1102}
1103
1104static DECLCALLBACK(int) drvHostDSoundPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
1105 uint32_t *pcSamplesPlayed)
1106{
1107 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1108 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
1109 /* pcSamplesPlayed is optional. */
1110
1111 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1112 PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
1113
1114 int rc = VINF_SUCCESS;
1115 uint32_t cReadTotal = 0;
1116
1117 do /* to use 'break' */
1118 {
1119 LPDIRECTSOUNDBUFFER8 pDSB = pDSoundStrmOut->pDSB;
1120 if (!pDSB)
1121 break;
1122
1123 int cShift = pHstStrmOut->Props.cShift;
1124 DWORD cbBuffer = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, pDSoundStrmOut->csPlaybackBufferSize);
1125
1126 /* Get the current play position which is used for calculating the free space in the buffer. */
1127 DWORD cbPlayPos;
1128 HRESULT hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &cbPlayPos, NULL);
1129 if (hr == DSERR_BUFFERLOST)
1130 {
1131 hr = directSoundPlayRestore(pDSB);
1132 if (SUCCEEDED(hr))
1133 {
1134 hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &cbPlayPos, NULL);
1135 }
1136 }
1137
1138 if (FAILED(hr))
1139 {
1140 if (hr != DSERR_BUFFERLOST) /* Avoid log flooding if the error is still there. */
1141 DSLOGREL(("DSound: Playback GetCurrentPosition %Rhrc\n", hr));
1142 break;
1143 }
1144
1145 DWORD cbFree = cbBuffer - dsoundRingDistance(pDSoundStrmOut->cbPlayWritePos, cbPlayPos, cbBuffer);
1146 /*
1147 * Check for full buffer, do not allow the cbPlayWritePos to catch cbPlayPos during playback,
1148 * i.e. always leave a free space for 1 audio sample.
1149 */
1150 const DWORD cbSample = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, 1);
1151 if (cbFree <= cbSample)
1152 break;
1153 cbFree -= cbSample;
1154
1155 uint32_t csLive = AudioMixBufAvail(&pHstStrmOut->MixBuf);
1156 uint32_t cbLive = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, csLive);
1157
1158 /* Do not write more than available space in the DirectSound playback buffer. */
1159 cbLive = RT_MIN(cbFree, cbLive);
1160
1161 cbLive &= ~pHstStrmOut->Props.uAlign;
1162 if (cbLive == 0 || cbLive > cbBuffer)
1163 {
1164 DSLOG(("DSound: cbLive=%RU32, cbBuffer=%ld, cbPlayWritePos=%ld, cbPlayPos=%ld\n",
1165 cbLive, cbBuffer, pDSoundStrmOut->cbPlayWritePos, cbPlayPos));
1166 break;
1167 }
1168
1169 LPVOID pv1, pv2;
1170 DWORD cb1, cb2;
1171 hr = directSoundPlayLock(pDSB, &pHstStrmOut->Props, pDSoundStrmOut->cbPlayWritePos, cbLive,
1172 &pv1, &pv2, &cb1, &cb2, 0 /* dwFlags */);
1173 if (FAILED(hr))
1174 break;
1175
1176 DWORD len1 = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cb1);
1177 DWORD len2 = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cb2);
1178
1179 uint32_t cRead = 0;
1180
1181 if (pv1 && cb1)
1182 {
1183 rc = AudioMixBufReadCirc(&pHstStrmOut->MixBuf, pv1, cb1, &cRead);
1184 if (RT_SUCCESS(rc))
1185 cReadTotal += cRead;
1186 }
1187
1188 if ( RT_SUCCESS(rc)
1189 && cReadTotal == len1
1190 && pv2 && cb2)
1191 {
1192 rc = AudioMixBufReadCirc(&pHstStrmOut->MixBuf, pv2, cb2, &cRead);
1193 if (RT_SUCCESS(rc))
1194 cReadTotal += cRead;
1195 }
1196
1197 directSoundPlayUnlock(pDSB, pv1, pv2, cb1, cb2);
1198
1199 pDSoundStrmOut->cbPlayWritePos =
1200 (pDSoundStrmOut->cbPlayWritePos + AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cReadTotal)) % cbBuffer;
1201
1202 DSLOGF(("DSound: %RU32 (%RU32 samples) out of %RU32%s, buffer write pos %ld, rc=%Rrc\n",
1203 AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cReadTotal), cReadTotal, cbLive,
1204 cbLive != AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cReadTotal) ? " !!!": "",
1205 pDSoundStrmOut->cbPlayWritePos, rc));
1206
1207 if (cReadTotal)
1208 {
1209 AudioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
1210 rc = VINF_SUCCESS; /* Played something. */
1211 }
1212
1213 if (RT_FAILURE(rc))
1214 break;
1215
1216 if (pDSoundStrmOut->fRestartPlayback)
1217 {
1218 /* The playback has been just started.
1219 * Some samples of the new sound have been copied to the buffer
1220 * and it can start playing.
1221 */
1222 pDSoundStrmOut->fRestartPlayback = false;
1223 hr = IDirectSoundBuffer8_Play(pDSoundStrmOut->pDSB, 0, 0, DSBPLAY_LOOPING);
1224 if (FAILED(hr))
1225 {
1226 DSLOGREL(("DSound: Playback start %Rhrc\n", hr));
1227 rc = VERR_NOT_SUPPORTED;
1228 }
1229 }
1230 } while (0);
1231
1232 if (pcSamplesPlayed)
1233 *pcSamplesPlayed = cReadTotal;
1234
1235 return rc;
1236}
1237
1238static DECLCALLBACK(int) drvHostDSoundFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
1239{
1240 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1241 PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
1242
1243 directSoundPlayClose(pDSoundStrmOut);
1244
1245 pDSoundStrmOut->cbPlayWritePos = 0;
1246 pDSoundStrmOut->fRestartPlayback = true;
1247 pDSoundStrmOut->csPlaybackBufferSize = 0;
1248 RT_ZERO(pDSoundStrmOut->streamCfg);
1249
1250 return VINF_SUCCESS;
1251}
1252
1253static DECLCALLBACK(int) drvHostDSoundInitIn(PPDMIHOSTAUDIO pInterface,
1254 PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
1255 PDMAUDIORECSOURCE enmRecSource,
1256 uint32_t *pcSamples)
1257{
1258 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1259 PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
1260
1261 LogFlowFunc(("pHstStrmIn=%p, pAudioSettings=%p, enmRecSource=%ld\n",
1262 pHstStrmIn, pCfg, enmRecSource));
1263
1264 pDSoundStrmIn->streamCfg = *pCfg;
1265 pDSoundStrmIn->streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
1266
1267 /** @todo caller should already init Props? */
1268 int rc = DrvAudioStreamCfgToProps(&pDSoundStrmIn->streamCfg, &pHstStrmIn->Props);
1269 if (RT_SUCCESS(rc))
1270 {
1271 /* Init the stream structure and save relevant information to it. */
1272 pDSoundStrmIn->csCaptureReadPos = 0;
1273 pDSoundStrmIn->csCaptureBufferSize = 0;
1274 pDSoundStrmIn->pDSC = NULL;
1275 pDSoundStrmIn->pDSCB = NULL;
1276 pDSoundStrmIn->enmRecSource = enmRecSource;
1277 pDSoundStrmIn->hrLastCaptureIn = S_OK;
1278
1279 if (pcSamples)
1280 *pcSamples = pThis->cfg.cbBufferIn >> pHstStrmIn->Props.cShift;
1281
1282 /* Try to open capture in case the device is already there. */
1283 directSoundCaptureOpen(pThis, pDSoundStrmIn);
1284 }
1285 else
1286 {
1287 RT_ZERO(pDSoundStrmIn->streamCfg);
1288 }
1289
1290 LogFlowFuncLeaveRC(rc);
1291 return rc;
1292}
1293
1294static DECLCALLBACK(int) drvHostDSoundControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
1295 PDMAUDIOSTREAMCMD enmStreamCmd)
1296{
1297 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1298 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1299
1300 LogFlowFunc(("pHstStrmIn=%p, enmStreamCmd=%ld\n", pHstStrmIn, enmStreamCmd));
1301
1302 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1303 PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
1304
1305 int rc = VINF_SUCCESS;
1306
1307 switch (enmStreamCmd)
1308 {
1309 case PDMAUDIOSTREAMCMD_ENABLE:
1310 {
1311 /* Try to start capture. If it fails, then reopen and try again. */
1312 HRESULT hr = directSoundCaptureStart(pThis, pDSoundStrmIn);
1313 if (FAILED(hr))
1314 {
1315 directSoundCaptureClose(pDSoundStrmIn);
1316 directSoundCaptureOpen(pThis, pDSoundStrmIn);
1317
1318 hr = directSoundCaptureStart(pThis, pDSoundStrmIn);
1319 }
1320
1321 if (FAILED(hr))
1322 rc = VERR_NOT_SUPPORTED;
1323 } break;
1324
1325 case PDMAUDIOSTREAMCMD_DISABLE:
1326 {
1327 directSoundCaptureStop(pDSoundStrmIn);
1328 } break;
1329
1330 default:
1331 {
1332 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
1333 rc = VERR_INVALID_PARAMETER;
1334 } break;
1335 }
1336
1337 return rc;
1338}
1339
1340static DECLCALLBACK(int) drvHostDSoundCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
1341 uint32_t *pcSamplesCaptured)
1342{
1343 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1344 PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
1345 LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB = pDSoundStrmIn->pDSCB;
1346
1347 int rc = VINF_SUCCESS;
1348
1349 if (pDSCB == NULL)
1350 {
1351 if (pcSamplesCaptured) /** @todo single point of return */
1352 *pcSamplesCaptured = 0;
1353 return VINF_SUCCESS;
1354 }
1355
1356 /* Get DirectSound capture position in bytes. */
1357 DWORD cbReadPos;
1358 HRESULT hr = IDirectSoundCaptureBuffer_GetCurrentPosition(pDSCB, NULL, &cbReadPos);
1359 if (FAILED(hr))
1360 {
1361 if (hr != pDSoundStrmIn->hrLastCaptureIn)
1362 {
1363 DSLOGREL(("DSound: Capture GetCurrentPosition %Rhrc\n", hr));
1364 pDSoundStrmIn->hrLastCaptureIn = hr;
1365 }
1366
1367 if (pcSamplesCaptured)
1368 *pcSamplesCaptured = 0;
1369 return VINF_SUCCESS;
1370 }
1371 pDSoundStrmIn->hrLastCaptureIn = hr;
1372
1373 if (cbReadPos & pHstStrmIn->Props.uAlign)
1374 DSLOGF(("DSound: Misaligned capture read position %ld (alignment: %RU32)\n", cbReadPos, pHstStrmIn->Props.uAlign));
1375
1376 /* Capture position in samples. */
1377 DWORD csReadPos = cbReadPos >> pHstStrmIn->Props.cShift;
1378
1379 /* Number of samples available in the DirectSound capture buffer. */
1380 DWORD csCaptured = dsoundRingDistance(csReadPos, pDSoundStrmIn->csCaptureReadPos, pDSoundStrmIn->csCaptureBufferSize);
1381 if (csCaptured == 0)
1382 {
1383 if (pcSamplesCaptured)
1384 *pcSamplesCaptured = 0;
1385 return VINF_SUCCESS;
1386 }
1387
1388 /* Using as an intermediate not circular buffer. */
1389 AudioMixBufReset(&pHstStrmIn->MixBuf);
1390
1391 /* Get number of free samples in the mix buffer and check that is has free space */
1392 uint32_t csMixFree = AudioMixBufFree(&pHstStrmIn->MixBuf);
1393 if (csMixFree == 0)
1394 {
1395 DSLOGF(("DSound: Capture buffer full\n"));
1396 if (pcSamplesCaptured)
1397 *pcSamplesCaptured = 0;
1398 return VINF_SUCCESS;
1399 }
1400
1401 DSLOGF(("DSound: Capture csMixFree=%RU32, csReadPos=%ld, csCaptureReadPos=%ld, csCaptured=%ld\n",
1402 csMixFree, csReadPos, pDSoundStrmIn->csCaptureReadPos, csCaptured));
1403
1404 /* No need to fetch more samples than mix buffer can receive. */
1405 csCaptured = RT_MIN(csCaptured, csMixFree);
1406
1407 /* Lock relevant range in the DirectSound capture buffer. */
1408 LPVOID pv1, pv2;
1409 DWORD cb1, cb2;
1410 hr = directSoundCaptureLock(pDSCB, &pHstStrmIn->Props,
1411 AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, pDSoundStrmIn->csCaptureReadPos), /* dwOffset */
1412 AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, csCaptured), /* dwBytes */
1413 &pv1, &pv2, &cb1, &cb2,
1414 0 /* dwFlags */);
1415 if (FAILED(hr))
1416 {
1417 if (pcSamplesCaptured)
1418 *pcSamplesCaptured = 0;
1419 return VINF_SUCCESS;
1420 }
1421
1422 DWORD len1 = AUDIOMIXBUF_B2S(&pHstStrmIn->MixBuf, cb1);
1423 DWORD len2 = AUDIOMIXBUF_B2S(&pHstStrmIn->MixBuf, cb2);
1424
1425 uint32_t csWrittenTotal = 0;
1426 uint32_t csWritten;
1427 if (pv1 && len1)
1428 {
1429 rc = AudioMixBufWriteAt(&pHstStrmIn->MixBuf, 0 /* offWrite */,
1430 pv1, cb1, &csWritten);
1431 if (RT_SUCCESS(rc))
1432 csWrittenTotal += csWritten;
1433 }
1434
1435 if ( RT_SUCCESS(rc)
1436 && csWrittenTotal == len1
1437 && pv2 && len2)
1438 {
1439 rc = AudioMixBufWriteAt(&pHstStrmIn->MixBuf, csWrittenTotal,
1440 pv2, cb2, &csWritten);
1441 if (RT_SUCCESS(rc))
1442 csWrittenTotal += csWritten;
1443 }
1444
1445 directSoundCaptureUnlock(pDSCB, pv1, pv2, cb1, cb2);
1446
1447 uint32_t csProcessed = 0;
1448 if (csWrittenTotal != 0)
1449 {
1450 /* Captured something. */
1451 rc = AudioMixBufMixToParent(&pHstStrmIn->MixBuf, csWrittenTotal,
1452 &csProcessed);
1453 }
1454
1455 if (RT_SUCCESS(rc))
1456 {
1457 pDSoundStrmIn->csCaptureReadPos = (pDSoundStrmIn->csCaptureReadPos + csProcessed) % pDSoundStrmIn->csCaptureBufferSize;
1458 DSLOGF(("DSound: Capture %ld (%ld+%ld), processed %RU32/%RU32\n",
1459 csCaptured, len1, len2, csProcessed, csWrittenTotal));
1460 }
1461
1462 if (pcSamplesCaptured)
1463 *pcSamplesCaptured = csProcessed;
1464
1465 return rc;
1466}
1467
1468static DECLCALLBACK(int) drvHostDSoundFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
1469{
1470 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1471 PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
1472
1473 directSoundCaptureClose(pDSoundStrmIn);
1474
1475 pDSoundStrmIn->csCaptureReadPos = 0;
1476 pDSoundStrmIn->csCaptureBufferSize = 0;
1477 RT_ZERO(pDSoundStrmIn->streamCfg);
1478
1479 return VINF_SUCCESS;
1480}
1481
1482static DECLCALLBACK(bool) drvHostDSoundIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
1483{
1484 NOREF(pInterface);
1485 NOREF(enmDir);
1486 return true; /* Always all enabled. */
1487}
1488
1489static DECLCALLBACK(int) drvHostDSoundGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
1490{
1491 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1492
1493 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1494
1495 dsoundFreeDeviceLists(pThis);
1496
1497 pCfg->cbStreamOut = sizeof(DSOUNDSTREAMOUT);
1498 pCfg->cbStreamIn = sizeof(DSOUNDSTREAMIN);
1499
1500 pCfg->cMaxHstStrmsOut = 0;
1501 pCfg->cMaxHstStrmsIn = 0;
1502
1503 RTLDRMOD hDSound = NULL;
1504 int rc = RTLdrLoadSystem("dsound.dll", true /*fNoUnload*/, &hDSound);
1505 if (RT_SUCCESS(rc))
1506 {
1507 PFNDIRECTSOUNDENUMERATEW pfnDirectSoundEnumerateW = NULL;
1508 PFNDIRECTSOUNDCAPTUREENUMERATEW pfnDirectSoundCaptureEnumerateW = NULL;
1509
1510 rc = RTLdrGetSymbol(hDSound, "DirectSoundEnumerateW", (void**)&pfnDirectSoundEnumerateW);
1511 if (RT_SUCCESS(rc))
1512 {
1513 rc = RTLdrGetSymbol(hDSound, "DirectSoundCaptureEnumerateW", (void**)&pfnDirectSoundCaptureEnumerateW);
1514 }
1515
1516 if (RT_SUCCESS(rc))
1517 {
1518 DSOUNDENUMCBCTX ctx = { pThis, pCfg };
1519
1520 HRESULT hr = pfnDirectSoundEnumerateW(&dsoundEnumCallback, &ctx);
1521 if (FAILED(hr))
1522 LogRel(("DSound: Error enumerating host playback devices: %Rhrc\n", hr));
1523
1524 LogRel(("DSound: Found %RU32 host playback devices\n", pCfg->cMaxHstStrmsOut));
1525
1526 hr = pfnDirectSoundCaptureEnumerateW(&dsoundCaptureEnumCallback, &ctx);
1527 if (FAILED(hr))
1528 LogRel(("DSound: Error enumerating host capturing devices: %Rhrc\n", hr));
1529
1530 LogRel(("DSound: Found %RU32 host capturing devices\n", pCfg->cMaxHstStrmsIn));
1531 }
1532
1533 RTLdrClose(hDSound);
1534 }
1535 else
1536 {
1537 /* No dsound.dll on this system. */
1538 LogRel(("DSound: Could not load dsound.dll %Rrc\n", rc));
1539 }
1540
1541 /* Always return success and at least default values to make the caller happy. */
1542 if (pCfg->cMaxHstStrmsOut == 0)
1543 {
1544 LogRel(("DSound: Adjusting the number of host playback devices to 1\n"));
1545 pCfg->cMaxHstStrmsOut = 1; /* Support at least one stream. */
1546 }
1547
1548 if (pCfg->cMaxHstStrmsIn < 2)
1549 {
1550 LogRel(("DSound: Adjusting the number of host capturing devices from %RU32 to 2\n", pCfg->cMaxHstStrmsIn));
1551 pCfg->cMaxHstStrmsIn = 2; /* Support at least two streams (line in + mic). */
1552 }
1553
1554 return VINF_SUCCESS;
1555}
1556
1557static DECLCALLBACK(void) drvHostDSoundShutdown(PPDMIHOSTAUDIO pInterface)
1558{
1559 NOREF(pInterface);
1560}
1561
1562static DECLCALLBACK(int) drvHostDSoundInit(PPDMIHOSTAUDIO pInterface)
1563{
1564 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1565
1566 LogFlowFuncEnter();
1567
1568 /* Verify that IDirectSound is available. */
1569 LPDIRECTSOUND pDirectSound = NULL;
1570 HRESULT hr = CoCreateInstance(CLSID_DirectSound, NULL, CLSCTX_ALL,
1571 IID_IDirectSound, (void **)&pDirectSound);
1572 if (SUCCEEDED(hr))
1573 IDirectSound_Release(pDirectSound);
1574 else
1575 DSLOGREL(("DSound: DirectSound not available %Rhrc\n", hr));
1576
1577 int rc = SUCCEEDED(hr) ? VINF_SUCCESS: VERR_NOT_SUPPORTED;
1578
1579 LogFlowFuncLeaveRC(rc);
1580 return rc;
1581}
1582
1583static DECLCALLBACK(void *) drvHostDSoundQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1584{
1585 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1586 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
1587
1588 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1589 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1590 return NULL;
1591}
1592
1593static LPCGUID dsoundConfigQueryGUID(PCFGMNODE pCfg, const char *pszName, RTUUID *pUuid)
1594{
1595 LPCGUID pGuid = NULL;
1596
1597 char *pszGuid = NULL;
1598 int rc = CFGMR3QueryStringAlloc(pCfg, pszName, &pszGuid);
1599 if (RT_SUCCESS(rc))
1600 {
1601 rc = RTUuidFromStr(pUuid, pszGuid);
1602 if (RT_SUCCESS(rc))
1603 pGuid = (LPCGUID)&pUuid;
1604 else
1605 DSLOGREL(("DSound: Error parsing device GUID for device '%s': %Rrc\n", pszName, rc));
1606
1607 RTStrFree(pszGuid);
1608 }
1609
1610 return pGuid;
1611}
1612
1613static void dSoundConfigInit(PDRVHOSTDSOUND pThis, PCFGMNODE pCfg)
1614{
1615 unsigned int uBufsizeOut, uBufsizeIn;
1616
1617 CFGMR3QueryUIntDef(pCfg, "BufsizeOut", &uBufsizeOut, _16K);
1618 CFGMR3QueryUIntDef(pCfg, "BufsizeIn", &uBufsizeIn, _16K);
1619 pThis->cfg.cbBufferOut = uBufsizeOut;
1620 pThis->cfg.cbBufferIn = uBufsizeIn;
1621
1622 pThis->cfg.pGuidPlay = dsoundConfigQueryGUID(pCfg, "DeviceGuidOut", &pThis->cfg.uuidPlay);
1623 pThis->cfg.pGuidCapture = dsoundConfigQueryGUID(pCfg, "DeviceGuidIn", &pThis->cfg.uuidCapture);
1624
1625 DSLOG(("DSound: BufsizeOut %u, BufsizeIn %u, DeviceGuidOut {%RTuuid}, DeviceGuidIn {%RTuuid}\n",
1626 pThis->cfg.cbBufferOut,
1627 pThis->cfg.cbBufferIn,
1628 &pThis->cfg.uuidPlay,
1629 &pThis->cfg.uuidCapture));
1630}
1631
1632static DECLCALLBACK(void) drvHostDSoundDestruct(PPDMDRVINS pDrvIns)
1633{
1634 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
1635 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1636 LogFlowFuncEnter();
1637
1638 if (pThis->pDrvIns)
1639 CoUninitialize();
1640
1641 LogFlowFuncLeave();
1642}
1643
1644/**
1645 * Construct a DirectSound Audio driver instance.
1646 *
1647 * @copydoc FNPDMDRVCONSTRUCT
1648 */
1649static DECLCALLBACK(int) drvHostDSoundConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1650{
1651 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
1652
1653 LogRel(("Audio: Initializing DirectSound audio driver\n"));
1654
1655 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1656
1657 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
1658 if (FAILED(hr))
1659 {
1660 DSLOGREL(("DSound: CoInitializeEx %Rhrc\n", hr));
1661 return VERR_NOT_SUPPORTED;
1662 }
1663
1664 /*
1665 * Init the static parts.
1666 */
1667 pThis->pDrvIns = pDrvIns;
1668 /* IBase */
1669 pDrvIns->IBase.pfnQueryInterface = drvHostDSoundQueryInterface;
1670 /* IHostAudio */
1671 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostDSound);
1672
1673 RTListInit(&pThis->lstDevInput);
1674 RTListInit(&pThis->lstDevOutput);
1675
1676 /*
1677 * Initialize configuration values.
1678 */
1679 dSoundConfigInit(pThis, pCfg);
1680
1681 return VINF_SUCCESS;
1682}
1683
1684/**
1685 * PDM driver registration.
1686 */
1687const PDMDRVREG g_DrvHostDSound =
1688{
1689 /* u32Version */
1690 PDM_DRVREG_VERSION,
1691 /* szName */
1692 "DSoundAudio",
1693 /* szRCMod */
1694 "",
1695 /* szR0Mod */
1696 "",
1697 /* pszDescription */
1698 "DirectSound Audio host driver",
1699 /* fFlags */
1700 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1701 /* fClass. */
1702 PDM_DRVREG_CLASS_AUDIO,
1703 /* cMaxInstances */
1704 ~0U,
1705 /* cbInstance */
1706 sizeof(DRVHOSTDSOUND),
1707 /* pfnConstruct */
1708 drvHostDSoundConstruct,
1709 /* pfnDestruct */
1710 drvHostDSoundDestruct,
1711 /* pfnRelocate */
1712 NULL,
1713 /* pfnIOCtl */
1714 NULL,
1715 /* pfnPowerOn */
1716 NULL,
1717 /* pfnReset */
1718 NULL,
1719 /* pfnSuspend */
1720 NULL,
1721 /* pfnResume */
1722 NULL,
1723 /* pfnAttach */
1724 NULL,
1725 /* pfnDetach */
1726 NULL,
1727 /* pfnPowerOff */
1728 NULL,
1729 /* pfnSoftReset */
1730 NULL,
1731 /* u32EndVersion */
1732 PDM_DRVREG_VERSION
1733};
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