VirtualBox

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

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

PDM/Audio: Fixed crashes on termination.

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