VirtualBox

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

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

Audio/DrvHostDSound: LogRel nits.

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