VirtualBox

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

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

DSound: Streamlined type usage.

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

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