VirtualBox

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

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

PDM/Audio: AudioMixBuffer documentation, renaming.

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