VirtualBox

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

Last change on this file since 58075 was 58075, checked in by vboxsync, 10 years ago

DrvHostDSound: optional release logging

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 53.5 KB
Line 
1/* $Id: DrvHostDSound.cpp 58075 2015-10-07 09:28:30Z 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 LPVOID pv1, pv2;
517 DWORD cb1, cb2;
518 HRESULT hr = directSoundPlayLock(pDSoundStrmOut->pDSB, &pDSoundStrmOut->strmOut.Props,
519 0, pDSoundStrmOut->csPlaybackBufferSize << pDSoundStrmOut->strmOut.Props.cShift,
520 &pv1, &pv2, &cb1, &cb2, DSBLOCK_ENTIREBUFFER);
521 if (SUCCEEDED(hr))
522 {
523 int len1 = cb1 >> pDSoundStrmOut->strmOut.Props.cShift;
524 int len2 = cb2 >> pDSoundStrmOut->strmOut.Props.cShift;
525
526 if (pv1 && len1)
527 drvAudioClearBuf(&pDSoundStrmOut->strmOut.Props, pv1, len1);
528
529 if (pv2 && len2)
530 drvAudioClearBuf(&pDSoundStrmOut->strmOut.Props, pv2, len2);
531
532 directSoundPlayUnlock(pDSoundStrmOut->pDSB, pv1, pv2, cb1, cb2);
533 }
534}
535
536static HRESULT directSoundPlayGetStatus(LPDIRECTSOUNDBUFFER8 pDSB, DWORD *pStatus)
537{
538 AssertPtrReturn(pDSB, E_POINTER);
539 /* pStatus is optional. */
540
541 DWORD dwStatus = 0;
542 HRESULT hr = IDirectSoundBuffer8_GetStatus(pDSB, &dwStatus);
543 if (SUCCEEDED(hr))
544 {
545 if ((dwStatus & DSBSTATUS_BUFFERLOST) != 0)
546 {
547 hr = directSoundPlayRestore(pDSB);
548 if (SUCCEEDED(hr))
549 hr = IDirectSoundBuffer8_GetStatus(pDSB, &dwStatus);
550 }
551 }
552
553 if (SUCCEEDED(hr))
554 {
555 if (pStatus)
556 *pStatus = dwStatus;
557 }
558 else
559 DSLOGREL(("DSound: Playback GetStatus %Rhrc\n", hr));
560
561 return hr;
562}
563
564static void directSoundPlayStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
565{
566 AssertPtrReturnVoid(pThis);
567 AssertPtrReturnVoid(pDSoundStrmOut);
568
569 if (pDSoundStrmOut->pDSB != NULL)
570 {
571 /* This performs some restore, so call it anyway and ignore result. */
572 directSoundPlayGetStatus(pDSoundStrmOut->pDSB, NULL /* Status */);
573
574 DSLOG(("DSound: Stopping playback\n"));
575
576 /* @todo Wait until all data in the buffer has been played. */
577 HRESULT hr = IDirectSoundBuffer8_Stop(pDSoundStrmOut->pDSB);
578 if (SUCCEEDED(hr))
579 dsoundPlayClearSamples(pDSoundStrmOut);
580 else
581 DSLOGREL(("DSound: Stop playback %Rhrc\n", hr));
582 }
583}
584
585static HRESULT directSoundPlayStart(PDSOUNDSTREAMOUT pDSoundStrmOut)
586{
587 AssertPtrReturn(pDSoundStrmOut, E_POINTER);
588
589 HRESULT hr;
590 if (pDSoundStrmOut->pDSB != NULL)
591 {
592 DWORD dwStatus;
593 hr = directSoundPlayGetStatus(pDSoundStrmOut->pDSB, &dwStatus);
594 if (SUCCEEDED(hr))
595 {
596 if (dwStatus & DSBSTATUS_PLAYING)
597 {
598 DSLOG(("DSound: Already playing\n"));
599 }
600 else
601 {
602 dsoundPlayClearSamples(pDSoundStrmOut);
603
604 pDSoundStrmOut->fRestartPlayback = true;
605
606 DSLOG(("DSound: Playback start\n"));
607
608 /* The actual IDirectSoundBuffer8_Play call will be made in drvHostDSoundPlayOut,
609 * because it is necessary to put some samples into the buffer first.
610 */
611 }
612 }
613 }
614 else
615 hr = E_UNEXPECTED;
616
617 return hr;
618}
619
620/*
621 * DirectSoundCapture
622 */
623
624static LPCGUID dsoundCaptureSelectDevice(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
625{
626 AssertPtrReturn(pThis, NULL);
627 AssertPtrReturn(pDSoundStrmIn, NULL);
628
629 LPCGUID pGUID = pThis->cfg.pGuidCapture;
630
631 if (!pGUID)
632 {
633 PDSOUNDDEV pDev = NULL;
634
635 switch (pDSoundStrmIn->enmRecSource)
636 {
637 case PDMAUDIORECSOURCE_MIC:
638 {
639 RTListForEach(&pThis->lstDevInput, pDev, DSOUNDDEV, Node)
640 {
641 if (RTStrIStr(pDev->pszName, "Mic")) /** @todo what is with non en_us windows versions? */
642 break;
643 }
644 if (RTListNodeIsDummy(&pThis->lstDevInput, pDev, DSOUNDDEV, Node))
645 pDev = NULL; /* Found nothing. */
646
647 break;
648 }
649
650 case PDMAUDIORECSOURCE_LINE_IN:
651 default:
652 /* Try opening the default device (NULL). */
653 break;
654 }
655
656 if (pDev)
657 {
658 DSLOG(("DSound: Guest \"%s\" is using host \"%s\"\n",
659 drvAudioRecSourceToString(pDSoundStrmIn->enmRecSource), pDev->pszName));
660
661 pGUID = &pDev->Guid;
662 }
663 }
664
665 char *pszGUID = dsoundGUIDToUtf8StrA(pGUID);
666 /* This always has to be in the release log. */
667 LogRel(("DSound: Guest \"%s\" is using host device with GUID: %s\n",
668 drvAudioRecSourceToString(pDSoundStrmIn->enmRecSource), pszGUID? pszGUID: "{?}"));
669 RTStrFree(pszGUID);
670
671 return pGUID;
672}
673
674static void directSoundCaptureInterfaceRelease(PDSOUNDSTREAMIN pDSoundStrmIn)
675{
676 if (pDSoundStrmIn->pDSC)
677 {
678 IDirectSoundCapture_Release(pDSoundStrmIn->pDSC);
679 pDSoundStrmIn->pDSC = NULL;
680 }
681}
682
683static HRESULT directSoundCaptureInterfaceCreate(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
684{
685 if (pDSoundStrmIn->pDSC != NULL)
686 {
687 DSLOG(("DSound: DirectSoundCapture instance already exists\n"));
688 return S_OK;
689 }
690
691 HRESULT hr = CoCreateInstance(CLSID_DirectSoundCapture8, NULL, CLSCTX_ALL,
692 IID_IDirectSoundCapture8, (void **)&pDSoundStrmIn->pDSC);
693 if (FAILED(hr))
694 {
695 DSLOGREL(("DSound: DirectSoundCapture create %Rhrc\n", hr));
696 }
697 else
698 {
699 LPCGUID pGUID = dsoundCaptureSelectDevice(pThis, pDSoundStrmIn);
700 hr = IDirectSoundCapture_Initialize(pDSoundStrmIn->pDSC, pGUID);
701 if (FAILED(hr))
702 {
703 if (hr == DSERR_NODRIVER) /* Usually means that no capture devices are attached. */
704 DSLOGREL(("DSound: DirectSound capture is currently unavailable\n"));
705 else
706 DSLOGREL(("DSound: DirectSoundCapture initialize %Rhrc\n", hr));
707
708 directSoundCaptureInterfaceRelease(pDSoundStrmIn);
709 }
710 }
711
712 return hr;
713}
714
715static void directSoundCaptureClose(PDSOUNDSTREAMIN pDSoundStrmIn)
716{
717 AssertPtrReturnVoid(pDSoundStrmIn);
718
719 DSLOG(("DSound: pDSoundStrmIn=%p, pDSCB=%p\n", pDSoundStrmIn, pDSoundStrmIn->pDSCB));
720
721 if (pDSoundStrmIn->pDSCB)
722 {
723 HRESULT hr = IDirectSoundCaptureBuffer_Stop(pDSoundStrmIn->pDSCB);
724 if (FAILED(hr))
725 DSLOGREL(("DSound: Stop capture buffer %Rhrc\n", hr));
726
727 IDirectSoundCaptureBuffer8_Release(pDSoundStrmIn->pDSCB);
728 pDSoundStrmIn->pDSCB = NULL;
729 }
730
731 directSoundCaptureInterfaceRelease(pDSoundStrmIn);
732}
733
734static HRESULT directSoundCaptureOpen(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
735{
736 AssertPtrReturn(pThis, E_POINTER);
737 AssertPtrReturn(pDSoundStrmIn, E_POINTER);
738
739 DSLOG(("DSound: pDSoundStrmIn=%p, cbBufferIn=%ld, uHz=%RU32, cChannels=%RU8, cBits=%RU8, fSigned=%RTbool\n",
740 pDSoundStrmIn,
741 pThis->cfg.cbBufferIn,
742 pDSoundStrmIn->strmIn.Props.uHz,
743 pDSoundStrmIn->strmIn.Props.cChannels,
744 pDSoundStrmIn->strmIn.Props.cBits,
745 pDSoundStrmIn->strmIn.Props.fSigned));
746
747 if (pDSoundStrmIn->pDSCB != NULL)
748 {
749 /* Should not happen but be forgiving. */
750 DSLOGREL(("DSound: DirectSoundCaptureBuffer already exists\n"));
751 directSoundCaptureClose(pDSoundStrmIn);
752 }
753
754 WAVEFORMATEX wfx;
755 int rc = dsoundWaveFmtFromCfg(&pDSoundStrmIn->streamCfg, &wfx);
756 if (RT_FAILURE(rc))
757 return E_INVALIDARG;
758
759 HRESULT hr = directSoundCaptureInterfaceCreate(pThis, pDSoundStrmIn);
760 if (FAILED(hr))
761 return hr;
762
763 do /* To use breaks. */
764 {
765 LPDIRECTSOUNDCAPTUREBUFFER pDSCB = NULL;
766 DSCBUFFERDESC bd;
767 RT_ZERO(bd);
768 bd.dwSize = sizeof(bd);
769 bd.lpwfxFormat = &wfx;
770 bd.dwBufferBytes = pThis->cfg.cbBufferIn;
771 hr = IDirectSoundCapture_CreateCaptureBuffer(pDSoundStrmIn->pDSC,
772 &bd, &pDSCB, NULL);
773 if (FAILED(hr))
774 {
775 DSLOGREL(("DSound: CreateCaptureBuffer %Rhrc\n", hr));
776 break;
777 }
778
779 hr = IDirectSoundCaptureBuffer_QueryInterface(pDSCB, IID_IDirectSoundCaptureBuffer8, (void **)&pDSoundStrmIn->pDSCB);
780 IDirectSoundCaptureBuffer_Release(pDSCB);
781 if (FAILED(hr))
782 {
783 DSLOGREL(("DSound: Query IDirectSoundCaptureBuffer8 %Rhrc\n", hr));
784 break;
785 }
786
787 /*
788 * Query the actual parameters.
789 */
790 DWORD cbReadPos = 0;
791 hr = IDirectSoundCaptureBuffer8_GetCurrentPosition(pDSoundStrmIn->pDSCB, NULL, &cbReadPos);
792 if (FAILED(hr))
793 {
794 cbReadPos = 0;
795 DSLOGREL(("DSound: Capture (open) GetCurrentPosition %Rhrc\n", hr));
796 }
797
798 RT_ZERO(wfx);
799 hr = IDirectSoundCaptureBuffer8_GetFormat(pDSoundStrmIn->pDSCB, &wfx, sizeof(wfx), NULL);
800 if (FAILED(hr))
801 {
802 DSLOGREL(("DSound: Capture GetFormat %Rhrc\n", hr));
803 break;
804 }
805
806 DSCBCAPS bc;
807 RT_ZERO(bc);
808 bc.dwSize = sizeof(bc);
809 hr = IDirectSoundCaptureBuffer8_GetCaps(pDSoundStrmIn->pDSCB, &bc);
810 if (FAILED(hr))
811 {
812 DSLOGREL(("DSound: Capture GetCaps %Rhrc\n", hr));
813 break;
814 }
815
816 DSLOG(("DSound: Capture format:\n"
817 " dwBufferBytes = %RI32\n"
818 " wFormatTag = %RI16\n"
819 " nChannels = %RI16\n"
820 " nSamplesPerSec = %RU32\n"
821 " nAvgBytesPerSec = %RU32\n"
822 " nBlockAlign = %RI16\n"
823 " wBitsPerSample = %RI16\n"
824 " cbSize = %RI16\n",
825 bc.dwBufferBytes,
826 wfx.wFormatTag,
827 wfx.nChannels,
828 wfx.nSamplesPerSec,
829 wfx.nAvgBytesPerSec,
830 wfx.nBlockAlign,
831 wfx.wBitsPerSample,
832 wfx.cbSize));
833
834 if (bc.dwBufferBytes & pDSoundStrmIn->strmIn.Props.uAlign)
835 DSLOGREL(("DSound: Capture GetCaps returned misaligned buffer: size %RU32, alignment %RU32\n",
836 bc.dwBufferBytes, pDSoundStrmIn->strmIn.Props.uAlign + 1));
837
838 if (bc.dwBufferBytes != pThis->cfg.cbBufferIn)
839 DSLOGREL(("DSound: Capture buffer size mismatched: DirectSound %RU32, requested %RU32 bytes\n",
840 bc.dwBufferBytes, pThis->cfg.cbBufferIn));
841
842 /* Initial state: reading at the initial capture position, no error. */
843 pDSoundStrmIn->csCaptureReadPos = cbReadPos >> pDSoundStrmIn->strmIn.Props.cShift;
844 pDSoundStrmIn->csCaptureBufferSize = bc.dwBufferBytes >> pDSoundStrmIn->strmIn.Props.cShift;
845 pDSoundStrmIn->hrLastCaptureIn = S_OK;
846
847 DSLOG(("DSound: csCaptureReadPos=%RU32, csCaptureBufferSize=%RU32\n",
848 pDSoundStrmIn->csCaptureReadPos, pDSoundStrmIn->csCaptureBufferSize));
849
850 } while (0);
851
852 if (FAILED(hr))
853 directSoundCaptureClose(pDSoundStrmIn);
854
855 return hr;
856}
857
858static void directSoundCaptureStop(PDSOUNDSTREAMIN pDSoundStrmIn)
859{
860 AssertPtrReturnVoid(pDSoundStrmIn);
861
862 if (pDSoundStrmIn->pDSCB)
863 {
864 DSLOG(("DSound: Stopping capture\n"));
865
866 HRESULT hr = IDirectSoundCaptureBuffer_Stop(pDSoundStrmIn->pDSCB);
867 if (FAILED(hr))
868 DSLOGREL(("DSound: Capture buffer stop %Rhrc\n", hr));
869 }
870}
871
872static HRESULT directSoundCaptureStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
873{
874 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
875 AssertPtrReturn(pDSoundStrmIn, VERR_INVALID_POINTER);
876
877 HRESULT hr;
878 if (pDSoundStrmIn->pDSCB != NULL)
879 {
880 DWORD dwStatus;
881 hr = IDirectSoundCaptureBuffer8_GetStatus(pDSoundStrmIn->pDSCB, &dwStatus);
882 if (FAILED(hr))
883 {
884 DSLOGREL(("DSound: Capture start GetStatus %Rhrc\n", hr));
885 }
886 else
887 {
888 if (dwStatus & DSCBSTATUS_CAPTURING)
889 {
890 DSLOG(("DSound: Already capturing\n"));
891 }
892 else
893 {
894 DSLOG(("DSound: Capture start\n"));
895 hr = IDirectSoundCaptureBuffer8_Start(pDSoundStrmIn->pDSCB, DSCBSTART_LOOPING);
896 if (FAILED(hr))
897 DSLOGREL(("DSound: Capture started %Rhrc\n", hr));
898 }
899 }
900 }
901 else
902 {
903 hr = E_UNEXPECTED;
904 }
905
906 return hr;
907}
908
909static int dsoundDevAdd(PRTLISTANCHOR pList, LPGUID lpGUID,
910 LPCWSTR lpwstrDescription, PDSOUNDDEV *ppDev)
911{
912 AssertPtrReturn(pList, VERR_INVALID_POINTER);
913 AssertPtrReturn(lpGUID, VERR_INVALID_POINTER);
914 AssertPtrReturn(lpwstrDescription, VERR_INVALID_POINTER);
915
916 PDSOUNDDEV pDev = (PDSOUNDDEV)RTMemAlloc(sizeof(DSOUNDDEV));
917 if (!pDev)
918 return VERR_NO_MEMORY;
919
920 int rc = RTUtf16ToUtf8(lpwstrDescription, &pDev->pszName);
921 if (RT_SUCCESS(rc))
922 memcpy(&pDev->Guid, lpGUID, sizeof(GUID));
923
924 if (RT_SUCCESS(rc))
925 RTListAppend(pList, &pDev->Node);
926
927 if (ppDev)
928 *ppDev = pDev;
929
930 return rc;
931}
932
933static void dsoundDevRemove(PDSOUNDDEV pDev)
934{
935 if (pDev)
936 {
937 RTStrFree(pDev->pszName);
938 pDev->pszName = NULL;
939
940 RTListNodeRemove(&pDev->Node);
941
942 RTMemFree(pDev);
943 }
944}
945
946static void dsoundLogDevice(const char *pszType, LPGUID lpGUID, LPCWSTR lpwstrDescription, LPCWSTR lpwstrModule)
947{
948 char *pszGUID = dsoundGUIDToUtf8StrA(lpGUID);
949 /* This always has to be in the release log. */
950 LogRel(("DSound: %s: GUID: %s [%ls] (Module: %ls)\n",
951 pszType, pszGUID? pszGUID: "{?}", lpwstrDescription, lpwstrModule));
952 RTStrFree(pszGUID);
953}
954
955static BOOL CALLBACK dsoundEnumCallback(LPGUID lpGUID, LPCWSTR lpwstrDescription,
956 LPCWSTR lpwstrModule, LPVOID lpContext)
957{
958 PDSOUNDENUMCBCTX pCtx = (PDSOUNDENUMCBCTX)lpContext;
959 AssertPtrReturn(pCtx, FALSE);
960 AssertPtrReturn(pCtx->pDrv, FALSE);
961 AssertPtrReturn(pCtx->pCfg, FALSE);
962
963 if (!lpGUID)
964 return TRUE;
965
966 AssertPtrReturn(lpwstrDescription, FALSE);
967 /* Do not care about lpwstrModule */
968
969 dsoundLogDevice("Output", lpGUID, lpwstrDescription, lpwstrModule);
970
971 int rc = dsoundDevAdd(&pCtx->pDrv->lstDevOutput,
972 lpGUID, lpwstrDescription, NULL /* ppDev */);
973 if (RT_FAILURE(rc))
974 return FALSE; /* Abort enumeration. */
975
976 pCtx->pCfg->cMaxHstStrmsOut++;
977
978 return TRUE;
979}
980
981static BOOL CALLBACK dsoundCaptureEnumCallback(LPGUID lpGUID, LPCWSTR lpwstrDescription,
982 LPCWSTR lpwstrModule, LPVOID lpContext)
983{
984 PDSOUNDENUMCBCTX pCtx = (PDSOUNDENUMCBCTX)lpContext;
985 AssertPtrReturn(pCtx, FALSE);
986 AssertPtrReturn(pCtx->pDrv, FALSE);
987 AssertPtrReturn(pCtx->pCfg, FALSE);
988
989 if (!lpGUID)
990 return TRUE;
991
992 dsoundLogDevice("Input", lpGUID, lpwstrDescription, lpwstrModule);
993
994 int rc = dsoundDevAdd(&pCtx->pDrv->lstDevInput,
995 lpGUID, lpwstrDescription, NULL /* ppDev */);
996 if (RT_FAILURE(rc))
997 return FALSE; /* Abort enumeration. */
998
999 pCtx->pCfg->cMaxHstStrmsIn++;
1000
1001 return TRUE;
1002}
1003
1004
1005/*
1006 * PDMIHOSTAUDIO
1007 */
1008
1009static DECLCALLBACK(int) drvHostDSoundInitOut(PPDMIHOSTAUDIO pInterface,
1010 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
1011 uint32_t *pcSamples)
1012{
1013 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1014 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
1015 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1016 /* pcSamples is optional. */
1017
1018 LogFlowFunc(("pHstStrmOut=%p, pCfg=%p\n", pHstStrmOut, pCfg));
1019
1020 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1021 PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
1022
1023 pDSoundStrmOut->streamCfg = *pCfg;
1024 pDSoundStrmOut->streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
1025
1026 int rc = drvAudioStreamCfgToProps(&pDSoundStrmOut->streamCfg, &pDSoundStrmOut->strmOut.Props);
1027 if (RT_SUCCESS(rc))
1028 {
1029 pDSoundStrmOut->pDS = NULL;
1030 pDSoundStrmOut->pDSB = NULL;
1031 pDSoundStrmOut->cbPlayWritePos = 0;
1032 pDSoundStrmOut->fRestartPlayback = true;
1033 pDSoundStrmOut->csPlaybackBufferSize = 0;
1034
1035 if (pcSamples)
1036 *pcSamples = pThis->cfg.cbBufferOut >> pHstStrmOut->Props.cShift;
1037
1038 /* Try to open playback in case the device is already there. */
1039 directSoundPlayOpen(pThis, pDSoundStrmOut);
1040 }
1041 else
1042 {
1043 RT_ZERO(pDSoundStrmOut->streamCfg);
1044 }
1045
1046 LogFlowFuncLeaveRC(rc);
1047 return rc;
1048}
1049
1050static DECLCALLBACK(int) drvHostDSoundControlOut(PPDMIHOSTAUDIO pInterface,
1051 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PDMAUDIOSTREAMCMD enmStreamCmd)
1052{
1053 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1054 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
1055
1056 LogFlowFunc(("pHstStrmOut=%p, cmd=%d\n", pHstStrmOut, enmStreamCmd));
1057
1058 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1059 PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
1060
1061 int rc = VINF_SUCCESS;
1062 switch (enmStreamCmd)
1063 {
1064 case PDMAUDIOSTREAMCMD_ENABLE:
1065 {
1066 DSLOG(("DSound: Playback PDMAUDIOSTREAMCMD_ENABLE\n"));
1067 /* Try to start playback. If it fails, then reopen and try again. */
1068 HRESULT hr = directSoundPlayStart(pDSoundStrmOut);
1069 if (FAILED(hr))
1070 {
1071 directSoundPlayClose(pDSoundStrmOut);
1072 directSoundPlayOpen(pThis, pDSoundStrmOut);
1073
1074 hr = directSoundPlayStart(pDSoundStrmOut);
1075 }
1076
1077 if (FAILED(hr))
1078 rc = VERR_NOT_SUPPORTED;
1079
1080 break;
1081 }
1082
1083 case PDMAUDIOSTREAMCMD_DISABLE:
1084 {
1085 DSLOG(("DSound: Playback PDMAUDIOSTREAMCMD_DISABLE\n"));
1086 directSoundPlayStop(pThis, pDSoundStrmOut);
1087 break;
1088 }
1089
1090 default:
1091 {
1092 AssertMsgFailed(("Invalid command: %ld\n", enmStreamCmd));
1093 rc = VERR_INVALID_PARAMETER;
1094 break;
1095 }
1096 }
1097
1098 LogFlowFuncLeaveRC(rc);
1099 return rc;
1100}
1101
1102static DECLCALLBACK(int) drvHostDSoundPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
1103 uint32_t *pcSamplesPlayed)
1104{
1105 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1106 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
1107 /* pcSamplesPlayed is optional. */
1108
1109 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1110 PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
1111
1112 int rc = VINF_SUCCESS;
1113 uint32_t cReadTotal = 0;
1114
1115 do /* to use 'break' */
1116 {
1117 LPDIRECTSOUNDBUFFER8 pDSB = pDSoundStrmOut->pDSB;
1118 if (!pDSB)
1119 break;
1120
1121 int cShift = pHstStrmOut->Props.cShift;
1122 DWORD cbBuffer = pDSoundStrmOut->csPlaybackBufferSize << cShift;
1123
1124 /* Get the current play position which is used for calculating the free space in the buffer. */
1125 DWORD cbPlayPos;
1126 HRESULT hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &cbPlayPos, NULL);
1127 if (hr == DSERR_BUFFERLOST)
1128 {
1129 hr = directSoundPlayRestore(pDSB);
1130 if (SUCCEEDED(hr))
1131 {
1132 hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &cbPlayPos, NULL);
1133 }
1134 }
1135
1136 if (FAILED(hr))
1137 {
1138 if (hr != DSERR_BUFFERLOST) /* Avoid log flooding if the error is still there. */
1139 DSLOGREL(("DSound: Playback GetCurrentPosition %Rhrc\n", hr));
1140 break;
1141 }
1142
1143 DWORD cbFree = cbBuffer - dsoundRingDistance(pDSoundStrmOut->cbPlayWritePos, cbPlayPos, cbBuffer);
1144
1145 /* Check for full buffer, do not allow the cbPlayWritePos to catch cbPlayPos during playback,
1146 * i.e. always leave a free space for 1 audio sample.
1147 */
1148 if (cbFree <= (1U << cShift))
1149 break;
1150 cbFree -= (1U << cShift);
1151
1152 uint32_t csLive = drvAudioHstOutSamplesLive(pHstStrmOut);
1153 uint32_t cbLive = csLive << cShift;
1154
1155 /* Do not write more than available space in the DirectSound playback buffer. */
1156 cbLive = RT_MIN(cbFree, cbLive);
1157
1158 cbLive &= ~pHstStrmOut->Props.uAlign;
1159 if (cbLive == 0 || cbLive > cbBuffer)
1160 {
1161 DSLOG(("DSound: cbLive=%RU32, cbBuffer=%ld, cbPlayWritePos=%ld, cbPlayPos=%ld\n",
1162 cbLive, cbBuffer, pDSoundStrmOut->cbPlayWritePos, cbPlayPos));
1163 break;
1164 }
1165
1166 LPVOID pv1, pv2;
1167 DWORD cb1, cb2;
1168 hr = directSoundPlayLock(pDSB, &pHstStrmOut->Props, pDSoundStrmOut->cbPlayWritePos, cbLive,
1169 &pv1, &pv2, &cb1, &cb2, 0 /* dwFlags */);
1170 if (FAILED(hr))
1171 break;
1172
1173 DWORD len1 = cb1 >> cShift;
1174 DWORD len2 = cb2 >> cShift;
1175
1176 uint32_t cRead = 0;
1177
1178 if (pv1 && cb1)
1179 {
1180 rc = AudioMixBufReadCirc(&pHstStrmOut->MixBuf, pv1, cb1, &cRead);
1181 if (RT_SUCCESS(rc))
1182 cReadTotal += cRead;
1183 }
1184
1185 if ( RT_SUCCESS(rc)
1186 && cReadTotal == len1
1187 && pv2 && cb2)
1188 {
1189 rc = AudioMixBufReadCirc(&pHstStrmOut->MixBuf, pv2, cb2, &cRead);
1190 if (RT_SUCCESS(rc))
1191 cReadTotal += cRead;
1192 }
1193
1194 directSoundPlayUnlock(pDSB, pv1, pv2, cb1, cb2);
1195
1196 pDSoundStrmOut->cbPlayWritePos = (pDSoundStrmOut->cbPlayWritePos + (cReadTotal << cShift)) % cbBuffer;
1197
1198 DSLOGF(("DSound: %RU32 (%RU32 samples) out of %RU32%s, buffer write pos %ld, rc=%Rrc\n",
1199 AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cReadTotal), cReadTotal, cbLive,
1200 cbLive != AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cReadTotal) ? " !!!": "",
1201 pDSoundStrmOut->cbPlayWritePos, rc));
1202
1203 if (cReadTotal)
1204 {
1205 AudioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
1206 rc = VINF_SUCCESS; /* Played something. */
1207 }
1208
1209 if (RT_FAILURE(rc))
1210 break;
1211
1212 if (pDSoundStrmOut->fRestartPlayback)
1213 {
1214 /* The playback has been just started.
1215 * Some samples of the new sound have been copied to the buffer
1216 * and it can start playing.
1217 */
1218 pDSoundStrmOut->fRestartPlayback = false;
1219 hr = IDirectSoundBuffer8_Play(pDSoundStrmOut->pDSB, 0, 0, DSBPLAY_LOOPING);
1220 if (FAILED(hr))
1221 {
1222 DSLOGREL(("DSound: Playback start %Rhrc\n", hr));
1223 rc = VERR_NOT_SUPPORTED;
1224 }
1225 }
1226 } while (0);
1227
1228 if (pcSamplesPlayed)
1229 *pcSamplesPlayed = cReadTotal;
1230
1231 return rc;
1232}
1233
1234static DECLCALLBACK(int) drvHostDSoundFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
1235{
1236 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1237 PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
1238
1239 directSoundPlayClose(pDSoundStrmOut);
1240
1241 pDSoundStrmOut->cbPlayWritePos = 0;
1242 pDSoundStrmOut->fRestartPlayback = true;
1243 pDSoundStrmOut->csPlaybackBufferSize = 0;
1244 RT_ZERO(pDSoundStrmOut->streamCfg);
1245
1246 return VINF_SUCCESS;
1247}
1248
1249static DECLCALLBACK(int) drvHostDSoundInitIn(PPDMIHOSTAUDIO pInterface,
1250 PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
1251 PDMAUDIORECSOURCE enmRecSource,
1252 uint32_t *pcSamples)
1253{
1254 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1255 PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
1256
1257 LogFlowFunc(("pHstStrmIn=%p, pAudioSettings=%p, enmRecSource=%ld\n",
1258 pHstStrmIn, pCfg, enmRecSource));
1259
1260 pDSoundStrmIn->streamCfg = *pCfg;
1261 pDSoundStrmIn->streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
1262
1263 /** @todo caller should already init Props? */
1264 int rc = drvAudioStreamCfgToProps(&pDSoundStrmIn->streamCfg, &pHstStrmIn->Props);
1265 if (RT_SUCCESS(rc))
1266 {
1267 /* Init the stream structure and save relevant information to it. */
1268 pDSoundStrmIn->csCaptureReadPos = 0;
1269 pDSoundStrmIn->csCaptureBufferSize = 0;
1270 pDSoundStrmIn->pDSC = NULL;
1271 pDSoundStrmIn->pDSCB = NULL;
1272 pDSoundStrmIn->enmRecSource = enmRecSource;
1273 pDSoundStrmIn->hrLastCaptureIn = S_OK;
1274
1275 if (pcSamples)
1276 *pcSamples = pThis->cfg.cbBufferIn >> pHstStrmIn->Props.cShift;
1277
1278 /* Try to open capture in case the device is already there. */
1279 directSoundCaptureOpen(pThis, pDSoundStrmIn);
1280 }
1281 else
1282 {
1283 RT_ZERO(pDSoundStrmIn->streamCfg);
1284 }
1285
1286 LogFlowFuncLeaveRC(rc);
1287 return rc;
1288}
1289
1290static DECLCALLBACK(int) drvHostDSoundControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
1291 PDMAUDIOSTREAMCMD enmStreamCmd)
1292{
1293 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1294 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1295
1296 LogFlowFunc(("pHstStrmIn=%p, enmStreamCmd=%ld\n", pHstStrmIn, enmStreamCmd));
1297
1298 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1299 PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
1300
1301 int rc = VINF_SUCCESS;
1302
1303 switch (enmStreamCmd)
1304 {
1305 case PDMAUDIOSTREAMCMD_ENABLE:
1306 {
1307 /* Try to start capture. If it fails, then reopen and try again. */
1308 HRESULT hr = directSoundCaptureStart(pThis, pDSoundStrmIn);
1309 if (FAILED(hr))
1310 {
1311 directSoundCaptureClose(pDSoundStrmIn);
1312 directSoundCaptureOpen(pThis, pDSoundStrmIn);
1313
1314 hr = directSoundCaptureStart(pThis, pDSoundStrmIn);
1315 }
1316
1317 if (FAILED(hr))
1318 rc = VERR_NOT_SUPPORTED;
1319 } break;
1320
1321 case PDMAUDIOSTREAMCMD_DISABLE:
1322 {
1323 directSoundCaptureStop(pDSoundStrmIn);
1324 } break;
1325
1326 default:
1327 {
1328 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
1329 rc = VERR_INVALID_PARAMETER;
1330 } break;
1331 }
1332
1333 return rc;
1334}
1335
1336static DECLCALLBACK(int) drvHostDSoundCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
1337 uint32_t *pcSamplesCaptured)
1338{
1339 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1340 PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
1341 LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB = pDSoundStrmIn->pDSCB;
1342
1343 int rc = VINF_SUCCESS;
1344
1345 if (pDSCB == NULL)
1346 {
1347 if (pcSamplesCaptured) /** @todo single point of return */
1348 *pcSamplesCaptured = 0;
1349 return VINF_SUCCESS;
1350 }
1351
1352 /* Get DirectSound capture position in bytes. */
1353 DWORD cbReadPos;
1354 HRESULT hr = IDirectSoundCaptureBuffer_GetCurrentPosition(pDSCB, NULL, &cbReadPos);
1355 if (FAILED(hr))
1356 {
1357 if (hr != pDSoundStrmIn->hrLastCaptureIn)
1358 {
1359 DSLOGREL(("DSound: Capture GetCurrentPosition %Rhrc\n", hr));
1360 pDSoundStrmIn->hrLastCaptureIn = hr;
1361 }
1362
1363 if (pcSamplesCaptured)
1364 *pcSamplesCaptured = 0;
1365 return VINF_SUCCESS;
1366 }
1367 pDSoundStrmIn->hrLastCaptureIn = hr;
1368
1369 if (cbReadPos & pHstStrmIn->Props.uAlign)
1370 DSLOGF(("DSound: Misaligned capture read position %ld (alignment: %RU32)\n", cbReadPos, pHstStrmIn->Props.uAlign));
1371
1372 /* Capture position in samples. */
1373 DWORD csReadPos = cbReadPos >> pHstStrmIn->Props.cShift;
1374
1375 /* Number of samples available in the DirectSound capture buffer. */
1376 DWORD csCaptured = dsoundRingDistance(csReadPos, pDSoundStrmIn->csCaptureReadPos, pDSoundStrmIn->csCaptureBufferSize);
1377 if (csCaptured == 0)
1378 {
1379 if (pcSamplesCaptured)
1380 *pcSamplesCaptured = 0;
1381 return VINF_SUCCESS;
1382 }
1383
1384 /* Using as an intermediate not circular buffer. */
1385 AudioMixBufReset(&pHstStrmIn->MixBuf);
1386
1387 /* Get number of free samples in the mix buffer and check that is has free space */
1388 uint32_t csMixFree = AudioMixBufFree(&pHstStrmIn->MixBuf);
1389 if (csMixFree == 0)
1390 {
1391 DSLOGF(("DSound: Capture buffer full\n"));
1392 if (pcSamplesCaptured)
1393 *pcSamplesCaptured = 0;
1394 return VINF_SUCCESS;
1395 }
1396
1397 DSLOGF(("DSound: Capture csMixFree=%RU32, csReadPos=%ld, csCaptureReadPos=%ld, csCaptured=%ld\n",
1398 csMixFree, csReadPos, pDSoundStrmIn->csCaptureReadPos, csCaptured));
1399
1400 /* No need to fetch more samples than mix buffer can receive. */
1401 csCaptured = RT_MIN(csCaptured, csMixFree);
1402
1403 /* Lock relevant range in the DirectSound capture buffer. */
1404 LPVOID pv1, pv2;
1405 DWORD cb1, cb2;
1406 hr = directSoundCaptureLock(pDSCB, &pHstStrmIn->Props,
1407 pDSoundStrmIn->csCaptureReadPos << pHstStrmIn->Props.cShift,
1408 csCaptured << pHstStrmIn->Props.cShift,
1409 &pv1, &pv2, &cb1, &cb2,
1410 0 /* dwFlags */);
1411 if (FAILED(hr))
1412 {
1413 if (pcSamplesCaptured)
1414 *pcSamplesCaptured = 0;
1415 return VINF_SUCCESS;
1416 }
1417
1418 DWORD len1 = cb1 >> pHstStrmIn->Props.cShift;
1419 DWORD len2 = cb2 >> pHstStrmIn->Props.cShift;
1420
1421 uint32_t csWrittenTotal = 0;
1422 uint32_t csWritten;
1423 if (pv1 && len1)
1424 {
1425 rc = AudioMixBufWriteAt(&pHstStrmIn->MixBuf, 0 /* offWrite */,
1426 pv1, cb1, &csWritten);
1427 if (RT_SUCCESS(rc))
1428 csWrittenTotal += csWritten;
1429 }
1430
1431 if ( RT_SUCCESS(rc)
1432 && csWrittenTotal == len1
1433 && pv2 && len2)
1434 {
1435 rc = AudioMixBufWriteAt(&pHstStrmIn->MixBuf, csWrittenTotal,
1436 pv2, cb2, &csWritten);
1437 if (RT_SUCCESS(rc))
1438 csWrittenTotal += csWritten;
1439 }
1440
1441 directSoundCaptureUnlock(pDSCB, pv1, pv2, cb1, cb2);
1442
1443 uint32_t csProcessed = 0;
1444 if (csWrittenTotal != 0)
1445 {
1446 /* Captured something. */
1447 rc = AudioMixBufMixToParent(&pHstStrmIn->MixBuf, csWrittenTotal,
1448 &csProcessed);
1449 }
1450
1451 if (RT_SUCCESS(rc))
1452 {
1453 pDSoundStrmIn->csCaptureReadPos = (pDSoundStrmIn->csCaptureReadPos + csProcessed) % pDSoundStrmIn->csCaptureBufferSize;
1454 DSLOGF(("DSound: Capture %ld (%ld+%ld), processed %RU32/%RU32\n",
1455 csCaptured, len1, len2, csProcessed, csWrittenTotal));
1456 }
1457
1458 if (pcSamplesCaptured)
1459 *pcSamplesCaptured = csProcessed;
1460
1461 return rc;
1462}
1463
1464static DECLCALLBACK(int) drvHostDSoundFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
1465{
1466 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1467 PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
1468
1469 directSoundCaptureClose(pDSoundStrmIn);
1470
1471 pDSoundStrmIn->csCaptureReadPos = 0;
1472 pDSoundStrmIn->csCaptureBufferSize = 0;
1473 RT_ZERO(pDSoundStrmIn->streamCfg);
1474
1475 return VINF_SUCCESS;
1476}
1477
1478static DECLCALLBACK(bool) drvHostDSoundIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
1479{
1480 NOREF(pInterface);
1481 NOREF(enmDir);
1482 return true; /* Always all enabled. */
1483}
1484
1485static DECLCALLBACK(int) drvHostDSoundGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
1486{
1487 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1488
1489 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1490
1491 dsoundFreeDeviceLists(pThis);
1492
1493 pCfg->cbStreamOut = sizeof(DSOUNDSTREAMOUT);
1494 pCfg->cbStreamIn = sizeof(DSOUNDSTREAMIN);
1495
1496 pCfg->cMaxHstStrmsOut = 0;
1497 pCfg->cMaxHstStrmsIn = 0;
1498
1499 RTLDRMOD hDSound = NULL;
1500 int rc = RTLdrLoadSystem("dsound.dll", true /*fNoUnload*/, &hDSound);
1501 if (RT_SUCCESS(rc))
1502 {
1503 PFNDIRECTSOUNDENUMERATEW pfnDirectSoundEnumerateW = NULL;
1504 PFNDIRECTSOUNDCAPTUREENUMERATEW pfnDirectSoundCaptureEnumerateW = NULL;
1505
1506 rc = RTLdrGetSymbol(hDSound, "DirectSoundEnumerateW", (void**)&pfnDirectSoundEnumerateW);
1507 if (RT_SUCCESS(rc))
1508 {
1509 rc = RTLdrGetSymbol(hDSound, "DirectSoundCaptureEnumerateW", (void**)&pfnDirectSoundCaptureEnumerateW);
1510 }
1511
1512 if (RT_SUCCESS(rc))
1513 {
1514 DSOUNDENUMCBCTX ctx = { pThis, pCfg };
1515
1516 HRESULT hr = pfnDirectSoundEnumerateW(&dsoundEnumCallback, &ctx);
1517 if (FAILED(hr))
1518 LogRel(("DSound: Error enumerating host playback devices: %Rhrc\n", hr));
1519
1520 LogRel(("DSound: Found %RU32 host playback devices\n", pCfg->cMaxHstStrmsOut));
1521
1522 hr = pfnDirectSoundCaptureEnumerateW(&dsoundCaptureEnumCallback, &ctx);
1523 if (FAILED(hr))
1524 LogRel(("DSound: Error enumerating host capturing devices: %Rhrc\n", hr));
1525
1526 LogRel(("DSound: Found %RU32 host capturing devices\n", pCfg->cMaxHstStrmsIn));
1527 }
1528
1529 RTLdrClose(hDSound);
1530 }
1531 else
1532 {
1533 /* No dsound.dll on this system. */
1534 LogRel(("DSound: Could not load dsound.dll %Rrc\n", rc));
1535 }
1536
1537 /* Always return success and at least default values to make the caller happy. */
1538 if (pCfg->cMaxHstStrmsOut == 0)
1539 {
1540 LogRel(("DSound: Adjusting the number of host playback devices to 1\n"));
1541 pCfg->cMaxHstStrmsOut = 1; /* Support at least one stream. */
1542 }
1543
1544 if (pCfg->cMaxHstStrmsIn < 2)
1545 {
1546 LogRel(("DSound: Adjusting the number of host capturing devices from %RU32 to 2\n", pCfg->cMaxHstStrmsIn));
1547 pCfg->cMaxHstStrmsIn = 2; /* Support at least two streams (line in + mic). */
1548 }
1549
1550 return VINF_SUCCESS;
1551}
1552
1553static DECLCALLBACK(void) drvHostDSoundShutdown(PPDMIHOSTAUDIO pInterface)
1554{
1555 NOREF(pInterface);
1556}
1557
1558static DECLCALLBACK(int) drvHostDSoundInit(PPDMIHOSTAUDIO pInterface)
1559{
1560 PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
1561
1562 LogFlowFuncEnter();
1563
1564 /* Verify that IDirectSound is available. */
1565 LPDIRECTSOUND pDirectSound = NULL;
1566 HRESULT hr = CoCreateInstance(CLSID_DirectSound, NULL, CLSCTX_ALL,
1567 IID_IDirectSound, (void **)&pDirectSound);
1568 if (SUCCEEDED(hr))
1569 IDirectSound_Release(pDirectSound);
1570 else
1571 DSLOGREL(("DSound: DirectSound not available %Rhrc\n", hr));
1572
1573 int rc = SUCCEEDED(hr) ? VINF_SUCCESS: VERR_NOT_SUPPORTED;
1574
1575 LogFlowFuncLeaveRC(rc);
1576 return rc;
1577}
1578
1579static DECLCALLBACK(void *) drvHostDSoundQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1580{
1581 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1582 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
1583
1584 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1585 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1586 return NULL;
1587}
1588
1589static LPCGUID dsoundConfigQueryGUID(PCFGMNODE pCfg, const char *pszName, RTUUID *pUuid)
1590{
1591 LPCGUID pGuid = NULL;
1592
1593 char *pszGuid = NULL;
1594 int rc = CFGMR3QueryStringAlloc(pCfg, pszName, &pszGuid);
1595 if (RT_SUCCESS(rc))
1596 {
1597 rc = RTUuidFromStr(pUuid, pszGuid);
1598 if (RT_SUCCESS(rc))
1599 pGuid = (LPCGUID)&pUuid;
1600 else
1601 DSLOGREL(("DSound: Error parsing device GUID for device '%s': %Rrc\n", pszName, rc));
1602
1603 RTStrFree(pszGuid);
1604 }
1605
1606 return pGuid;
1607}
1608
1609static void dSoundConfigInit(PDRVHOSTDSOUND pThis, PCFGMNODE pCfg)
1610{
1611 unsigned int uBufsizeOut, uBufsizeIn;
1612
1613 CFGMR3QueryUIntDef(pCfg, "BufsizeOut", &uBufsizeOut, _16K);
1614 CFGMR3QueryUIntDef(pCfg, "BufsizeIn", &uBufsizeIn, _16K);
1615 pThis->cfg.cbBufferOut = uBufsizeOut;
1616 pThis->cfg.cbBufferIn = uBufsizeIn;
1617
1618 pThis->cfg.pGuidPlay = dsoundConfigQueryGUID(pCfg, "DeviceGuidOut", &pThis->cfg.uuidPlay);
1619 pThis->cfg.pGuidCapture = dsoundConfigQueryGUID(pCfg, "DeviceGuidIn", &pThis->cfg.uuidCapture);
1620
1621 DSLOG(("DSound: BufsizeOut %u, BufsizeIn %u, DeviceGuidOut {%RTuuid}, DeviceGuidIn {%RTuuid}\n",
1622 pThis->cfg.cbBufferOut,
1623 pThis->cfg.cbBufferIn,
1624 &pThis->cfg.uuidPlay,
1625 &pThis->cfg.uuidCapture));
1626}
1627
1628static DECLCALLBACK(void) drvHostDSoundDestruct(PPDMDRVINS pDrvIns)
1629{
1630 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
1631 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1632 LogFlowFuncEnter();
1633
1634 if (pThis->pDrvIns)
1635 CoUninitialize();
1636
1637 LogFlowFuncLeave();
1638}
1639
1640/**
1641 * Construct a DirectSound Audio driver instance.
1642 *
1643 * @copydoc FNPDMDRVCONSTRUCT
1644 */
1645static DECLCALLBACK(int) drvHostDSoundConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1646{
1647 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
1648
1649 LogRel(("Audio: Initializing DirectSound audio driver\n"));
1650
1651 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1652
1653 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
1654 if (FAILED(hr))
1655 {
1656 DSLOGREL(("DSound: CoInitializeEx %Rhrc\n", hr));
1657 return VERR_NOT_SUPPORTED;
1658 }
1659
1660 /*
1661 * Init the static parts.
1662 */
1663 pThis->pDrvIns = pDrvIns;
1664 /* IBase */
1665 pDrvIns->IBase.pfnQueryInterface = drvHostDSoundQueryInterface;
1666 /* IHostAudio */
1667 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostDSound);
1668
1669 RTListInit(&pThis->lstDevInput);
1670 RTListInit(&pThis->lstDevOutput);
1671
1672 /*
1673 * Initialize configuration values.
1674 */
1675 dSoundConfigInit(pThis, pCfg);
1676
1677 return VINF_SUCCESS;
1678}
1679
1680/**
1681 * PDM driver registration.
1682 */
1683const PDMDRVREG g_DrvHostDSound =
1684{
1685 /* u32Version */
1686 PDM_DRVREG_VERSION,
1687 /* szName */
1688 "DSoundAudio",
1689 /* szRCMod */
1690 "",
1691 /* szR0Mod */
1692 "",
1693 /* pszDescription */
1694 "DirectSound Audio host driver",
1695 /* fFlags */
1696 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1697 /* fClass. */
1698 PDM_DRVREG_CLASS_AUDIO,
1699 /* cMaxInstances */
1700 ~0U,
1701 /* cbInstance */
1702 sizeof(DRVHOSTDSOUND),
1703 /* pfnConstruct */
1704 drvHostDSoundConstruct,
1705 /* pfnDestruct */
1706 drvHostDSoundDestruct,
1707 /* pfnRelocate */
1708 NULL,
1709 /* pfnIOCtl */
1710 NULL,
1711 /* pfnPowerOn */
1712 NULL,
1713 /* pfnReset */
1714 NULL,
1715 /* pfnSuspend */
1716 NULL,
1717 /* pfnResume */
1718 NULL,
1719 /* pfnAttach */
1720 NULL,
1721 /* pfnDetach */
1722 NULL,
1723 /* pfnPowerOff */
1724 NULL,
1725 /* pfnSoftReset */
1726 NULL,
1727 /* u32EndVersion */
1728 PDM_DRVREG_VERSION
1729};
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