VirtualBox

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

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

Audio: Remove DEV_AUDIO logging group and split it up into per device and driver groups for finer grained logging

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