VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostAudioDSound.cpp

Last change on this file was 100142, checked in by vboxsync, 12 months ago

Audio/DrvHostAudioDSound: Return the correct pointer address in dsoundConfigQueryGUID(). Issue found by Parfait [committed too much].

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 107.4 KB
RevLine 
[54433]1/* $Id: DrvHostAudioDSound.cpp 100142 2023-06-09 15:28:11Z vboxsync $ */
2/** @file
[88235]3 * Host audio driver - DirectSound (Windows).
[54433]4 */
5
6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[54433]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[54433]26 */
[62909]27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
[56648]32#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
[88371]33#define INITGUID
[56648]34#include <VBox/log.h>
[62909]35#include <iprt/win/windows.h>
[54433]36#include <dsound.h>
[89089]37#include <mmdeviceapi.h>
[88371]38#include <functiondiscoverykeys_devpkey.h>
[89421]39#include <iprt/win/mmreg.h> /* WAVEFORMATEXTENSIBLE */
[54433]40
41#include <iprt/alloc.h>
[70472]42#include <iprt/system.h>
[54433]43#include <iprt/uuid.h>
[76409]44#include <iprt/utf16.h>
[54433]45
[88028]46#include <VBox/vmm/pdmaudioinline.h>
[88047]47#include <VBox/vmm/pdmaudiohostenuminline.h>
[88028]48
[89057]49#include "VBoxDD.h"
[88959]50
[68680]51#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
52# include <new> /* For bad_alloc. */
[88233]53# include "DrvHostAudioDSoundMMNotifClient.h"
[68680]54#endif
[54433]55
[69118]56
[62909]57/*********************************************************************************************************************************
58* Defined Constants And Macros *
59*********************************************************************************************************************************/
[58071]60/*
61 * Optional release logging, which a user can turn on with the
62 * 'VBoxManage debugvm' command.
[88514]63 * Debug logging still uses the common Log* macros from VBox.
[58071]64 * Messages which always should go to the release log use LogRel.
[88554]65 *
66 * @deprecated Use LogRelMax, LogRel2 and LogRel3 directly.
[58071]67 */
[88371]68/** General code behavior. */
[62909]69#define DSLOG(a) do { LogRel2(a); } while(0)
[88371]70/** Something which produce a lot of logging during playback/recording. */
[62909]71#define DSLOGF(a) do { LogRel3(a); } while(0)
[88371]72/** Important messages like errors. Limited in the default release log to avoid log flood. */
[62909]73#define DSLOGREL(a) \
74 do { \
75 static int8_t s_cLogged = 0; \
76 if (s_cLogged < 8) { \
77 ++s_cLogged; \
78 LogRel(a); \
79 } else DSLOG(a); \
[58071]80 } while (0)
81
[62909]82/** Maximum number of attempts to restore the sound buffer before giving up. */
83#define DRV_DSOUND_RESTORE_ATTEMPTS_MAX 3
[88554]84#if 0 /** @todo r=bird: What are these for? Nobody is using them... */
[70916]85/** Default input latency (in ms). */
86#define DRV_DSOUND_DEFAULT_LATENCY_MS_IN 50
87/** Default output latency (in ms). */
88#define DRV_DSOUND_DEFAULT_LATENCY_MS_OUT 50
[88554]89#endif
[62909]90
91
92/*********************************************************************************************************************************
93* Structures and Typedefs *
94*********************************************************************************************************************************/
[57736]95/* Dynamically load dsound.dll. */
[62909]96typedef HRESULT WINAPI FNDIRECTSOUNDENUMERATEW(LPDSENUMCALLBACKW pDSEnumCallback, PVOID pContext);
[57736]97typedef FNDIRECTSOUNDENUMERATEW *PFNDIRECTSOUNDENUMERATEW;
[62909]98typedef HRESULT WINAPI FNDIRECTSOUNDCAPTUREENUMERATEW(LPDSENUMCALLBACKW pDSEnumCallback, PVOID pContext);
[57736]99typedef FNDIRECTSOUNDCAPTUREENUMERATEW *PFNDIRECTSOUNDCAPTUREENUMERATEW;
[68597]100typedef HRESULT WINAPI FNDIRECTSOUNDCAPTURECREATE8(LPCGUID lpcGUID, LPDIRECTSOUNDCAPTURE8 *lplpDSC, LPUNKNOWN pUnkOuter);
101typedef FNDIRECTSOUNDCAPTURECREATE8 *PFNDIRECTSOUNDCAPTURECREATE8;
[57736]102
[70916]103#define VBOX_DSOUND_MAX_EVENTS 3
[58733]104
105typedef enum DSOUNDEVENT
106{
107 DSOUNDEVENT_NOTIFY = 0,
108 DSOUNDEVENT_INPUT,
109 DSOUNDEVENT_OUTPUT,
[70916]110} DSOUNDEVENT;
[58733]111
[54433]112typedef struct DSOUNDHOSTCFG
113{
[70916]114 RTUUID uuidPlay;
115 LPCGUID pGuidPlay;
116 RTUUID uuidCapture;
117 LPCGUID pGuidCapture;
[54433]118} DSOUNDHOSTCFG, *PDSOUNDHOSTCFG;
119
[65624]120typedef struct DSOUNDSTREAM
[54433]121{
[88718]122 /** Common part. */
123 PDMAUDIOBACKENDSTREAM Core;
[88566]124 /** Entry in DRVHOSTDSOUND::HeadStreams. */
[88718]125 RTLISTNODE ListEntry;
[65624]126 /** The stream's acquired configuration. */
[88718]127 PDMAUDIOSTREAMCFG Cfg;
[65624]128 /** Buffer alignment. */
[88718]129 uint8_t uAlign;
[68597]130 /** Whether this stream is in an enable state on the DirectSound side. */
[88718]131 bool fEnabled;
132 bool afPadding[2];
[88376]133 /** Size (in bytes) of the DirectSound buffer. */
[88718]134 DWORD cbBufSize;
[65624]135 union
136 {
137 struct
138 {
[68767]139 /** The actual DirectSound Buffer (DSB) used for the capturing.
140 * This is a secondary buffer and is used as a streaming buffer. */
[65624]141 LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB;
[68767]142 /** Current read offset (in bytes) within the DSB. */
143 DWORD offReadPos;
[70927]144 /** Number of buffer overruns happened. Used for logging. */
145 uint8_t cOverruns;
[65624]146 } In;
147 struct
148 {
[68767]149 /** The actual DirectSound Buffer (DSB) used for playback.
150 * This is a secondary buffer and is used as a streaming buffer. */
[65624]151 LPDIRECTSOUNDBUFFER8 pDSB;
[88376]152 /** Current write offset (in bytes) within the DSB.
153 * @note This is needed as the current write position as kept by direct sound
154 * will move ahead if we're too late. */
[68767]155 DWORD offWritePos;
156 /** Offset of last play cursor within the DSB when checked for pending. */
157 DWORD offPlayCursorLastPending;
158 /** Offset of last play cursor within the DSB when last played. */
159 DWORD offPlayCursorLastPlayed;
[73370]160 /** Total amount (in bytes) written to our internal ring buffer. */
[68767]161 uint64_t cbWritten;
[70974]162 /** Total amount (in bytes) played (to the DirectSound buffer). */
[73370]163 uint64_t cbTransferred;
164 /** Flag indicating whether playback was just (re)started. */
165 bool fFirstTransfer;
[73550]166 /** Flag indicating whether this stream is in draining mode, e.g. no new
167 * data is being written to it but DirectSound still needs to be able to
168 * play its remaining (buffered) data. */
169 bool fDrain;
[73370]170 /** How much (in bytes) the last transfer from the internal buffer
171 * to the DirectSound buffer was. */
172 uint32_t cbLastTransferred;
[88566]173 /** The RTTimeMilliTS() deadline for the draining of this stream. */
174 uint64_t msDrainDeadline;
[65624]175 } Out;
176 };
[88564]177 /** Timestamp (in ms) of the last transfer from the internal buffer to/from the
178 * DirectSound buffer. */
[88718]179 uint64_t msLastTransfer;
[88564]180 /** The stream's critical section for synchronizing access. */
[88718]181 RTCRITSECT CritSect;
[88564]182 /** Used for formatting the current DSound status. */
[88718]183 char szStatus[127];
[88564]184 /** Fixed zero terminator. */
[88718]185 char const chStateZero;
[65624]186} DSOUNDSTREAM, *PDSOUNDSTREAM;
[54433]187
[77028]188/**
[88031]189 * DirectSound-specific device entry.
[77028]190 */
191typedef struct DSOUNDDEV
192{
[88045]193 PDMAUDIOHOSTDEV Core;
[88031]194 /** The GUID if handy. */
195 GUID Guid;
[89269]196 /** The GUID as a string (empty if default). */
197 char szGuid[RTUUID_STR_LENGTH];
[88031]198} DSOUNDDEV;
199/** Pointer to a DirectSound device entry. */
200typedef DSOUNDDEV *PDSOUNDDEV;
[77028]201
202/**
203 * Structure for holding a device enumeration context.
204 */
205typedef struct DSOUNDENUMCBCTX
206{
207 /** Enumeration flags. */
208 uint32_t fFlags;
209 /** Pointer to device list to populate. */
[88044]210 PPDMAUDIOHOSTENUM pDevEnm;
[77028]211} DSOUNDENUMCBCTX, *PDSOUNDENUMCBCTX;
212
[58733]213typedef struct DRVHOSTDSOUND
214{
215 /** Pointer to the driver instance structure. */
[68683]216 PPDMDRVINS pDrvIns;
[58744]217 /** Our audio host audio interface. */
[68683]218 PDMIHOSTAUDIO IHostAudio;
219 /** Critical section to serialize access. */
220 RTCRITSECT CritSect;
[58733]221 /** DirectSound configuration options. */
[70916]222 DSOUNDHOSTCFG Cfg;
[77028]223 /** List of devices of last enumeration. */
[88361]224 PDMAUDIOHOSTENUM DeviceEnum;
[88371]225 /** Whether this backend supports any audio input.
226 * @todo r=bird: This is not actually used for anything. */
[68683]227 bool fEnabledIn;
[88371]228 /** Whether this backend supports any audio output.
229 * @todo r=bird: This is not actually used for anything. */
[68683]230 bool fEnabledOut;
[68597]231 /** The Direct Sound playback interface. */
[68683]232 LPDIRECTSOUND8 pDS;
[68597]233 /** The Direct Sound capturing interface. */
[68683]234 LPDIRECTSOUNDCAPTURE8 pDSC;
[88566]235 /** List of streams (DSOUNDSTREAM).
236 * Requires CritSect ownership. */
237 RTLISTANCHOR HeadStreams;
238
[68680]239#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
[88361]240 DrvHostAudioDSoundMMNotifClient *m_pNotificationClient;
[68680]241#endif
[58733]242} DRVHOSTDSOUND, *PDRVHOSTDSOUND;
243
[77029]244
[59987]245/*********************************************************************************************************************************
[62909]246* Internal Functions *
[59987]247*********************************************************************************************************************************/
[62909]248static HRESULT directSoundPlayRestore(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB);
[88556]249static int drvHostDSoundStreamStopPlayback(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fReset);
[68599]250
[70916]251
[88573]252#if defined(LOG_ENABLED) || defined(RTLOG_REL_ENABLED)
[88564]253/**
254 * Gets the stream status as a string for logging purposes.
255 *
256 * @returns Status string (pStreamDS->szStatus).
257 * @param pStreamDS The stream to get the status for.
258 */
259static const char *drvHostDSoundStreamStatusString(PDSOUNDSTREAM pStreamDS)
260{
261 /*
262 * Out internal stream status first.
263 */
264 size_t off;
265 if (pStreamDS->fEnabled)
266 {
267 memcpy(pStreamDS->szStatus, RT_STR_TUPLE("ENABLED "));
268 off = sizeof("ENABLED ") - 1;
269 }
270 else
271 {
272 memcpy(pStreamDS->szStatus, RT_STR_TUPLE("DISABLED"));
273 off = sizeof("DISABLED") - 1;
274 }
275
276 /*
277 * Direction specific stuff, returning with a status DWORD and string mappings for it.
278 */
279 typedef struct DRVHOSTDSOUNDSFLAGS2STR
280 {
281 const char *pszMnemonic;
282 uint32_t cchMnemonic;
283 uint32_t fFlag;
284 } DRVHOSTDSOUNDSFLAGS2STR;
285 static const DRVHOSTDSOUNDSFLAGS2STR s_aCaptureFlags[] =
286 {
287 { RT_STR_TUPLE(" CAPTURING"), DSCBSTATUS_CAPTURING },
288 { RT_STR_TUPLE(" LOOPING"), DSCBSTATUS_LOOPING },
289 };
290 static const DRVHOSTDSOUNDSFLAGS2STR s_aPlaybackFlags[] =
291 {
292 { RT_STR_TUPLE(" PLAYING"), DSBSTATUS_PLAYING },
293 { RT_STR_TUPLE(" BUFFERLOST"), DSBSTATUS_BUFFERLOST },
294 { RT_STR_TUPLE(" LOOPING"), DSBSTATUS_LOOPING },
295 { RT_STR_TUPLE(" LOCHARDWARE"), DSBSTATUS_LOCHARDWARE },
296 { RT_STR_TUPLE(" LOCSOFTWARE"), DSBSTATUS_LOCSOFTWARE },
297 { RT_STR_TUPLE(" TERMINATED"), DSBSTATUS_TERMINATED },
298 };
299 DRVHOSTDSOUNDSFLAGS2STR const *paMappings = NULL;
300 size_t cMappings = 0;
301 DWORD fStatus = 0;
302 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
303 {
304 if (pStreamDS->In.pDSCB)
305 {
306 HRESULT hrc = pStreamDS->In.pDSCB->GetStatus(&fStatus);
307 if (SUCCEEDED(hrc))
308 {
309 paMappings = s_aCaptureFlags;
310 cMappings = RT_ELEMENTS(s_aCaptureFlags);
311 }
312 else
313 RTStrPrintf(&pStreamDS->szStatus[off], sizeof(pStreamDS->szStatus) - off, "GetStatus->%Rhrc", hrc);
314 }
315 else
316 RTStrCopy(&pStreamDS->szStatus[off], sizeof(pStreamDS->szStatus) - off, "NO-DSCB");
317 }
318 else if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT)
319 {
320 if (pStreamDS->Out.fDrain)
321 {
322 memcpy(&pStreamDS->szStatus[off], RT_STR_TUPLE(" DRAINING"));
323 off += sizeof(" DRAINING") - 1;
324 }
325
326 if (pStreamDS->Out.fFirstTransfer)
327 {
328 memcpy(&pStreamDS->szStatus[off], RT_STR_TUPLE(" NOXFER"));
329 off += sizeof(" NOXFER") - 1;
330 }
331
332 if (pStreamDS->Out.pDSB)
333 {
334 HRESULT hrc = pStreamDS->Out.pDSB->GetStatus(&fStatus);
335 if (SUCCEEDED(hrc))
336 {
337 paMappings = s_aPlaybackFlags;
338 cMappings = RT_ELEMENTS(s_aPlaybackFlags);
339 }
340 else
341 RTStrPrintf(&pStreamDS->szStatus[off], sizeof(pStreamDS->szStatus) - off, "GetStatus->%Rhrc", hrc);
342 }
343 else
344 RTStrCopy(&pStreamDS->szStatus[off], sizeof(pStreamDS->szStatus) - off, "NO-DSB");
345 }
346 else
347 RTStrCopy(&pStreamDS->szStatus[off], sizeof(pStreamDS->szStatus) - off, "BAD-DIR");
348
349 /* Format flags. */
350 if (paMappings)
351 {
352 if (fStatus == 0)
353 RTStrCopy(&pStreamDS->szStatus[off], sizeof(pStreamDS->szStatus) - off, " 0");
354 else
355 {
356 for (size_t i = 0; i < cMappings; i++)
357 if (fStatus & paMappings[i].fFlag)
358 {
359 memcpy(&pStreamDS->szStatus[off], paMappings[i].pszMnemonic, paMappings[i].cchMnemonic);
360 off += paMappings[i].cchMnemonic;
361
362 fStatus &= ~paMappings[i].fFlag;
363 if (!fStatus)
364 break;
365 }
366 if (fStatus != 0)
367 off += RTStrPrintf(&pStreamDS->szStatus[off], sizeof(pStreamDS->szStatus) - off, " %#x", fStatus);
368 }
369 }
370
371 /*
372 * Finally, terminate the string. By postponing it this long, it won't be
373 * a big deal if two threads go thru here at the same time as long as the
374 * status is the same.
375 */
376 Assert(off < sizeof(pStreamDS->szStatus));
377 pStreamDS->szStatus[off] = '\0';
378
379 return pStreamDS->szStatus;
380}
381#endif /* LOG_ENABLED || RTLOG_REL_ENABLED */
382
383
[54949]384static DWORD dsoundRingDistance(DWORD offEnd, DWORD offBegin, DWORD cSize)
[54433]385{
[68767]386 AssertReturn(offEnd <= cSize, 0);
387 AssertReturn(offBegin <= cSize, 0);
388
[54949]389 return offEnd >= offBegin ? offEnd - offBegin : cSize - offBegin + offEnd;
[54433]390}
391
[58983]392
[62979]393static char *dsoundGUIDToUtf8StrA(LPCGUID pGUID)
[57199]394{
[62979]395 if (pGUID)
[57199]396 {
[57375]397 LPOLESTR lpOLEStr;
[62979]398 HRESULT hr = StringFromCLSID(*pGUID, &lpOLEStr);
[57375]399 if (SUCCEEDED(hr))
400 {
401 char *pszGUID;
402 int rc = RTUtf16ToUtf8(lpOLEStr, &pszGUID);
403 CoTaskMemFree(lpOLEStr);
[57199]404
[57375]405 return RT_SUCCESS(rc) ? pszGUID : NULL;
406 }
[57199]407 }
408
[58071]409 return RTStrDup("{Default device}");
[57199]410}
411
[88514]412
[59890]413static HRESULT directSoundPlayRestore(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB)
[54433]414{
[62909]415 RT_NOREF(pThis);
[57145]416 HRESULT hr = IDirectSoundBuffer8_Restore(pDSB);
[58071]417 if (FAILED(hr))
[70916]418 DSLOG(("DSound: Restoring playback buffer\n"));
419 else
[59263]420 DSLOGREL(("DSound: Restoring playback buffer failed with %Rhrc\n", hr));
[70916]421
[58071]422 return hr;
[54433]423}
424
[88514]425
[59890]426static HRESULT directSoundPlayUnlock(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB,
[62909]427 PVOID pv1, PVOID pv2,
[58071]428 DWORD cb1, DWORD cb2)
[54433]429{
[62909]430 RT_NOREF(pThis);
[57145]431 HRESULT hr = IDirectSoundBuffer8_Unlock(pDSB, pv1, cb1, pv2, cb2);
[58071]432 if (FAILED(hr))
[59263]433 DSLOGREL(("DSound: Unlocking playback buffer failed with %Rhrc\n", hr));
[58071]434 return hr;
[54433]435}
436
437
[65624]438static HRESULT directSoundPlayLock(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS,
[58071]439 DWORD dwOffset, DWORD dwBytes,
[62909]440 PVOID *ppv1, PVOID *ppv2,
[58071]441 DWORD *pcb1, DWORD *pcb2,
442 DWORD dwFlags)
[54433]443{
[70916]444 AssertReturn(dwBytes, VERR_INVALID_PARAMETER);
445
[62909]446 HRESULT hr = E_FAIL;
447 AssertCompile(DRV_DSOUND_RESTORE_ATTEMPTS_MAX > 0);
[59890]448 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
[54433]449 {
[70839]450 PVOID pv1, pv2;
451 DWORD cb1, cb2;
452 hr = IDirectSoundBuffer8_Lock(pStreamDS->Out.pDSB, dwOffset, dwBytes, &pv1, &cb1, &pv2, &cb2, dwFlags);
[62909]453 if (SUCCEEDED(hr))
[54433]454 {
[70839]455 if ( (!pv1 || !(cb1 & pStreamDS->uAlign))
456 && (!pv2 || !(cb2 & pStreamDS->uAlign)))
457 {
458 if (ppv1)
459 *ppv1 = pv1;
460 if (ppv2)
461 *ppv2 = pv2;
462 if (pcb1)
463 *pcb1 = cb1;
464 if (pcb2)
465 *pcb2 = cb2;
[62909]466 return S_OK;
[70839]467 }
[62909]468 DSLOGREL(("DSound: Locking playback buffer returned misaligned buffer: cb1=%#RX32, cb2=%#RX32 (alignment: %#RX32)\n",
[65624]469 *pcb1, *pcb2, pStreamDS->uAlign));
[70839]470 directSoundPlayUnlock(pThis, pStreamDS->Out.pDSB, pv1, pv2, cb1, cb2);
[62909]471 return E_FAIL;
[54433]472 }
473
[62909]474 if (hr != DSERR_BUFFERLOST)
475 break;
[54433]476
[62909]477 LogFlowFunc(("Locking failed due to lost buffer, restoring ...\n"));
[65624]478 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);
[54433]479 }
480
[70916]481 DSLOGREL(("DSound: Locking playback buffer failed with %Rhrc (dwOff=%ld, dwBytes=%ld)\n", hr, dwOffset, dwBytes));
[62909]482 return hr;
[54433]483}
484
[88514]485
486static HRESULT directSoundCaptureUnlock(LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB,
487 PVOID pv1, PVOID pv2,
488 DWORD cb1, DWORD cb2)
489{
490 HRESULT hr = IDirectSoundCaptureBuffer8_Unlock(pDSCB, pv1, cb1, pv2, cb2);
491 if (FAILED(hr))
492 DSLOGREL(("DSound: Unlocking capture buffer failed with %Rhrc\n", hr));
493 return hr;
494}
495
496
[65624]497static HRESULT directSoundCaptureLock(PDSOUNDSTREAM pStreamDS,
[58071]498 DWORD dwOffset, DWORD dwBytes,
[62909]499 PVOID *ppv1, PVOID *ppv2,
[58071]500 DWORD *pcb1, DWORD *pcb2,
501 DWORD dwFlags)
[54433]502{
[62909]503 PVOID pv1 = NULL;
504 PVOID pv2 = NULL;
[54433]505 DWORD cb1 = 0;
506 DWORD cb2 = 0;
507
[65624]508 HRESULT hr = IDirectSoundCaptureBuffer8_Lock(pStreamDS->In.pDSCB, dwOffset, dwBytes,
[57145]509 &pv1, &cb1, &pv2, &cb2, dwFlags);
[54433]510 if (FAILED(hr))
511 {
[59263]512 DSLOGREL(("DSound: Locking capture buffer failed with %Rhrc\n", hr));
[58071]513 return hr;
[54433]514 }
515
[65624]516 if ( (pv1 && (cb1 & pStreamDS->uAlign))
517 || (pv2 && (cb2 & pStreamDS->uAlign)))
[54433]518 {
[58071]519 DSLOGREL(("DSound: Locking capture buffer returned misaligned buffer: cb1=%RI32, cb2=%RI32 (alignment: %RU32)\n",
[65624]520 cb1, cb2, pStreamDS->uAlign));
521 directSoundCaptureUnlock(pStreamDS->In.pDSCB, pv1, pv2, cb1, cb2);
[58071]522 return E_FAIL;
[54433]523 }
524
525 *ppv1 = pv1;
526 *ppv2 = pv2;
527 *pcb1 = cb1;
528 *pcb2 = cb2;
529
[58071]530 return S_OK;
[54433]531}
532
[88514]533
[54433]534/*
535 * DirectSound playback
536 */
537
[77028]538/**
[88554]539 * Creates a DirectSound playback instance.
[77028]540 *
541 * @return HRESULT
[88554]542 * @param pGUID GUID of device to create the playback interface for. NULL
543 * for the default device.
544 * @param ppDS Where to return the interface to the created instance.
[77028]545 */
[88554]546static HRESULT drvHostDSoundCreateDSPlaybackInstance(LPCGUID pGUID, LPDIRECTSOUND8 *ppDS)
[54433]547{
[68597]548 LogFlowFuncEnter();
549
[88554]550 LPDIRECTSOUND8 pDS = NULL;
551 HRESULT hrc = CoCreateInstance(CLSID_DirectSound8, NULL, CLSCTX_ALL, IID_IDirectSound8, (void **)&pDS);
552 if (SUCCEEDED(hrc))
[54433]553 {
[88554]554 hrc = IDirectSound8_Initialize(pDS, pGUID);
555 if (SUCCEEDED(hrc))
[54433]556 {
[57375]557 HWND hWnd = GetDesktopWindow();
[88554]558 hrc = IDirectSound8_SetCooperativeLevel(pDS, hWnd, DSSCL_PRIORITY);
559 if (SUCCEEDED(hrc))
560 {
561 *ppDS = pDS;
562 LogFlowFunc(("LEAVE S_OK\n"));
563 return S_OK;
564 }
565 LogRelMax(64, ("DSound: Setting cooperative level for (hWnd=%p) failed: %Rhrc\n", hWnd, hrc));
[54433]566 }
[88554]567 else if (hrc == DSERR_NODRIVER) /* Usually means that no playback devices are attached. */
568 LogRelMax(64, ("DSound: DirectSound playback is currently unavailable\n"));
569 else
570 LogRelMax(64, ("DSound: DirectSound playback initialization failed: %Rhrc\n", hrc));
[58071]571
[88554]572 IDirectSound8_Release(pDS);
[54433]573 }
[88554]574 else
575 LogRelMax(64, ("DSound: Creating playback instance failed: %Rhrc\n", hrc));
[54433]576
[88554]577 LogFlowFunc(("LEAVE %Rhrc\n", hrc));
578 return hrc;
[54433]579}
580
[58733]581
[88554]582#if 0 /* not used */
[59890]583static HRESULT directSoundPlayGetStatus(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB, DWORD *pdwStatus)
[54433]584{
[65676]585 AssertPtrReturn(pThis, E_POINTER);
[59890]586 AssertPtrReturn(pDSB, E_POINTER);
[65676]587
[62909]588 AssertPtrNull(pdwStatus);
[57375]589
[54433]590 DWORD dwStatus = 0;
[62909]591 HRESULT hr = E_FAIL;
[59890]592 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
[59059]593 {
[59890]594 hr = IDirectSoundBuffer8_GetStatus(pDSB, &dwStatus);
595 if ( hr == DSERR_BUFFERLOST
596 || ( SUCCEEDED(hr)
597 && (dwStatus & DSBSTATUS_BUFFERLOST)))
598 {
599 LogFlowFunc(("Getting status failed due to lost buffer, restoring ...\n"));
600 directSoundPlayRestore(pThis, pDSB);
601 }
602 else
603 break;
[59059]604 }
605
[58071]606 if (SUCCEEDED(hr))
[54433]607 {
[58378]608 if (pdwStatus)
609 *pdwStatus = dwStatus;
[54433]610 }
[58071]611 else
[59263]612 DSLOGREL(("DSound: Retrieving playback status failed with %Rhrc\n", hr));
[54433]613
[58071]614 return hr;
[54433]615}
[88554]616#endif
[54433]617
[57375]618
[54433]619/*
620 * DirectSoundCapture
621 */
622
[88031]623#if 0 /* unused */
[65676]624static LPCGUID dsoundCaptureSelectDevice(PDRVHOSTDSOUND pThis, PPDMAUDIOSTREAMCFG pCfg)
[54433]625{
[65676]626 AssertPtrReturn(pThis, NULL);
627 AssertPtrReturn(pCfg, NULL);
[57375]628
[61723]629 int rc = VINF_SUCCESS;
630
[70916]631 LPCGUID pGUID = pThis->Cfg.pGuidCapture;
[54433]632 if (!pGUID)
633 {
[88031]634 PDSOUNDDEV pDev = NULL;
[89218]635 switch (pCfg->enmPath)
[54433]636 {
[89218]637 case PDMAUDIOPATH_IN_LINE:
[61723]638 /*
639 * At the moment we're only supporting line-in in the HDA emulation,
640 * and line-in + mic-in in the AC'97 emulation both are expected
641 * to use the host's mic-in as well.
642 *
643 * So the fall through here is intentional for now.
644 */
[89218]645 case PDMAUDIOPATH_IN_MIC:
[88031]646 pDev = (PDSOUNDDEV)DrvAudioHlpDeviceEnumGetDefaultDevice(&pThis->DeviceEnum, PDMAUDIODIR_IN);
[57375]647 break;
648
[54433]649 default:
[61723]650 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
[54433]651 break;
652 }
653
[61723]654 if ( RT_SUCCESS(rc)
655 && pDev)
[54433]656 {
[61723]657 DSLOG(("DSound: Guest source '%s' is using host recording device '%s'\n",
[89218]658 PDMAudioPathGetName(pCfg->enmPath), pDev->Core.szName));
[88031]659 pGUID = &pDev->Guid;
[54433]660 }
[88031]661 if (RT_FAILURE(rc))
662 {
663 LogRel(("DSound: Selecting recording device failed with %Rrc\n", rc));
664 return NULL;
665 }
[54433]666 }
667
[88031]668 /* This always has to be in the release log. */
[57199]669 char *pszGUID = dsoundGUIDToUtf8StrA(pGUID);
[61723]670 LogRel(("DSound: Guest source '%s' is using host recording device with GUID '%s'\n",
[89218]671 PDMAudioPathGetName(pCfg->enmPath), pszGUID ? pszGUID: "{?}"));
[88031]672 RTStrFree(pszGUID);
[57199]673
[54433]674 return pGUID;
675}
[88031]676#endif
[54433]677
[88514]678
[68597]679/**
[88554]680 * Creates a DirectSound capture instance.
[68597]681 *
[88554]682 * @returns HRESULT
683 * @param pGUID GUID of device to create the capture interface for. NULL
684 * for default.
685 * @param ppDSC Where to return the interface to the created instance.
[68597]686 */
[88554]687static HRESULT drvHostDSoundCreateDSCaptureInstance(LPCGUID pGUID, LPDIRECTSOUNDCAPTURE8 *ppDSC)
[54433]688{
[68597]689 LogFlowFuncEnter();
690
[88554]691 LPDIRECTSOUNDCAPTURE8 pDSC = NULL;
692 HRESULT hrc = CoCreateInstance(CLSID_DirectSoundCapture8, NULL, CLSCTX_ALL, IID_IDirectSoundCapture8, (void **)&pDSC);
693 if (SUCCEEDED(hrc))
[54433]694 {
[88554]695 hrc = IDirectSoundCapture_Initialize(pDSC, pGUID);
696 if (SUCCEEDED(hrc))
[54433]697 {
[77028]698 *ppDSC = pDSC;
[88554]699 LogFlowFunc(("LEAVE S_OK\n"));
700 return S_OK;
[77028]701 }
[88554]702 if (hrc == DSERR_NODRIVER) /* Usually means that no capture devices are attached. */
703 LogRelMax(64, ("DSound: Capture device currently is unavailable\n"));
704 else
705 LogRelMax(64, ("DSound: Initializing capturing device failed: %Rhrc\n", hrc));
706 IDirectSoundCapture_Release(pDSC);
[54433]707 }
[88554]708 else
709 LogRelMax(64, ("DSound: Creating capture instance failed: %Rhrc\n", hrc));
[54433]710
[88554]711 LogFlowFunc(("LEAVE %Rhrc\n", hrc));
712 return hrc;
[54433]713}
714
715
[88514]716/*********************************************************************************************************************************
717* PDMIHOSTAUDIO *
718*********************************************************************************************************************************/
[54433]719
[88514]720/**
721 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
722 */
723static DECLCALLBACK(int) drvHostDSoundHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
[54433]724{
[88514]725 RT_NOREF(pInterface);
726 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
727 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
[57375]728
[70925]729
[88534]730 /*
731 * Fill in the config structure.
732 */
733 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "DirectSound");
734 pBackendCfg->cbStream = sizeof(DSOUNDSTREAM);
735 pBackendCfg->fFlags = 0;
[88514]736 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
737 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
[70925]738
[88514]739 return VINF_SUCCESS;
[54433]740}
741
[57375]742
[77028]743/**
744 * Callback for the playback device enumeration.
745 *
746 * @return TRUE if continuing enumeration, FALSE if not.
747 * @param pGUID Pointer to GUID of enumerated device. Can be NULL.
748 * @param pwszDescription Pointer to (friendly) description of enumerated device.
749 * @param pwszModule Pointer to module name of enumerated device.
750 * @param lpContext Pointer to PDSOUNDENUMCBCTX context for storing the enumerated information.
[88031]751 *
[88539]752 * @note Carbon copy of drvHostDSoundEnumOldStyleCaptureCallback with OUT direction.
[77028]753 */
[88539]754static BOOL CALLBACK drvHostDSoundEnumOldStylePlaybackCallback(LPGUID pGUID, LPCWSTR pwszDescription,
755 LPCWSTR pwszModule, PVOID lpContext)
[54433]756{
[77028]757 PDSOUNDENUMCBCTX pEnumCtx = (PDSOUNDENUMCBCTX)lpContext;
[88539]758 AssertPtrReturn(pEnumCtx, FALSE);
[54433]759
[88044]760 PPDMAUDIOHOSTENUM pDevEnm = pEnumCtx->pDevEnm;
[77028]761 AssertPtrReturn(pDevEnm, FALSE);
[54433]762
[88031]763 AssertPtrNullReturn(pGUID, FALSE); /* pGUID can be NULL for default device(s). */
[77028]764 AssertPtrReturn(pwszDescription, FALSE);
[88031]765 RT_NOREF(pwszModule); /* Do not care about pwszModule. */
[54433]766
[77028]767 int rc;
[89500]768 size_t const cbName = RTUtf16CalcUtf8Len(pwszDescription) + 1;
769 PDSOUNDDEV pDev = (PDSOUNDDEV)PDMAudioHostDevAlloc(sizeof(DSOUNDDEV), cbName, 0);
[54433]770 if (pDev)
771 {
[88031]772 pDev->Core.enmUsage = PDMAUDIODIR_OUT;
773 pDev->Core.enmType = PDMAUDIODEVICETYPE_BUILTIN;
[77028]774
775 if (pGUID == NULL)
[89229]776 pDev->Core.fFlags = PDMAUDIOHOSTDEV_F_DEFAULT_OUT;
[77028]777
[89500]778 rc = RTUtf16ToUtf8Ex(pwszDescription, RTSTR_MAX, &pDev->Core.pszName, cbName, NULL);
[77028]779 if (RT_SUCCESS(rc))
[68597]780 {
[89269]781 if (!pGUID)
782 pDev->Core.fFlags |= PDMAUDIOHOSTDEV_F_DEFAULT_OUT;
783 else
784 {
[88031]785 memcpy(&pDev->Guid, pGUID, sizeof(pDev->Guid));
[89269]786 rc = RTUuidToStr((PCRTUUID)pGUID, pDev->szGuid, sizeof(pDev->szGuid));
787 AssertRC(rc);
788 }
789 pDev->Core.pszId = &pDev->szGuid[0];
[54433]790
[88044]791 PDMAudioHostEnumAppend(pDevEnm, &pDev->Core);
792
793 /* Note: Querying the actual device information will be done at some
794 * later point in time outside this enumeration callback to prevent
795 * DSound hangs. */
796 return TRUE;
[77028]797 }
[88053]798 PDMAudioHostDevFree(&pDev->Core);
[77028]799 }
800 else
801 rc = VERR_NO_MEMORY;
802
[88031]803 LogRel(("DSound: Error enumeration playback device '%ls': rc=%Rrc\n", pwszDescription, rc));
804 return FALSE; /* Abort enumeration. */
[54433]805}
806
[88514]807
[77028]808/**
809 * Callback for the capture device enumeration.
810 *
811 * @return TRUE if continuing enumeration, FALSE if not.
812 * @param pGUID Pointer to GUID of enumerated device. Can be NULL.
813 * @param pwszDescription Pointer to (friendly) description of enumerated device.
814 * @param pwszModule Pointer to module name of enumerated device.
815 * @param lpContext Pointer to PDSOUNDENUMCBCTX context for storing the enumerated information.
[88031]816 *
[88539]817 * @note Carbon copy of drvHostDSoundEnumOldStylePlaybackCallback with IN direction.
[77028]818 */
[88539]819static BOOL CALLBACK drvHostDSoundEnumOldStyleCaptureCallback(LPGUID pGUID, LPCWSTR pwszDescription,
820 LPCWSTR pwszModule, PVOID lpContext)
[54433]821{
[77028]822 PDSOUNDENUMCBCTX pEnumCtx = (PDSOUNDENUMCBCTX )lpContext;
[88539]823 AssertPtrReturn(pEnumCtx, FALSE);
[54433]824
[88044]825 PPDMAUDIOHOSTENUM pDevEnm = pEnumCtx->pDevEnm;
[77028]826 AssertPtrReturn(pDevEnm, FALSE);
[54433]827
[88031]828 AssertPtrNullReturn(pGUID, FALSE); /* pGUID can be NULL for default device(s). */
[77028]829 AssertPtrReturn(pwszDescription, FALSE);
[88031]830 RT_NOREF(pwszModule); /* Do not care about pwszModule. */
[77028]831
832 int rc;
[89500]833 size_t const cbName = RTUtf16CalcUtf8Len(pwszDescription) + 1;
834 PDSOUNDDEV pDev = (PDSOUNDDEV)PDMAudioHostDevAlloc(sizeof(DSOUNDDEV), cbName, 0);
[77028]835 if (pDev)
836 {
[88031]837 pDev->Core.enmUsage = PDMAUDIODIR_IN;
838 pDev->Core.enmType = PDMAUDIODEVICETYPE_BUILTIN;
[77028]839
[89500]840 rc = RTUtf16ToUtf8Ex(pwszDescription, RTSTR_MAX, &pDev->Core.pszName, cbName, NULL);
[77028]841 if (RT_SUCCESS(rc))
842 {
[89269]843 if (!pGUID)
844 pDev->Core.fFlags |= PDMAUDIOHOSTDEV_F_DEFAULT_IN;
845 else
846 {
[88031]847 memcpy(&pDev->Guid, pGUID, sizeof(pDev->Guid));
[89269]848 rc = RTUuidToStr((PCRTUUID)pGUID, pDev->szGuid, sizeof(pDev->szGuid));
849 AssertRC(rc);
850 }
851 pDev->Core.pszId = &pDev->szGuid[0];
[77028]852
[88044]853 PDMAudioHostEnumAppend(pDevEnm, &pDev->Core);
854
855 /* Note: Querying the actual device information will be done at some
856 * later point in time outside this enumeration callback to prevent
857 * DSound hangs. */
858 return TRUE;
[86575]859 }
[88053]860 PDMAudioHostDevFree(&pDev->Core);
[86575]861 }
862 else
863 rc = VERR_NO_MEMORY;
864
[88031]865 LogRel(("DSound: Error enumeration capture device '%ls', rc=%Rrc\n", pwszDescription, rc));
866 return FALSE; /* Abort enumeration. */
[86575]867}
868
[88514]869
[86575]870/**
[88514]871 * Queries information for a given (DirectSound) device.
[86575]872 *
873 * @returns VBox status code.
874 * @param pDev Audio device to query information for.
875 */
[88539]876static int drvHostDSoundEnumOldStyleQueryDeviceInfo(PDSOUNDDEV pDev)
[86575]877{
[88539]878 AssertPtr(pDev);
[86575]879 int rc;
880
[88031]881 if (pDev->Core.enmUsage == PDMAUDIODIR_OUT)
[86575]882 {
883 LPDIRECTSOUND8 pDS;
[88554]884 HRESULT hr = drvHostDSoundCreateDSPlaybackInstance(&pDev->Guid, &pDS);
[86575]885 if (SUCCEEDED(hr))
886 {
887 DSCAPS DSCaps;
888 RT_ZERO(DSCaps);
889 DSCaps.dwSize = sizeof(DSCAPS);
890 hr = IDirectSound_GetCaps(pDS, &DSCaps);
[77028]891 if (SUCCEEDED(hr))
892 {
[88031]893 pDev->Core.cMaxOutputChannels = DSCaps.dwFlags & DSCAPS_PRIMARYSTEREO ? 2 : 1;
[86575]894
895 DWORD dwSpeakerCfg;
896 hr = IDirectSound_GetSpeakerConfig(pDS, &dwSpeakerCfg);
897 if (SUCCEEDED(hr))
[77028]898 {
[86575]899 unsigned uSpeakerCount = 0;
900 switch (DSSPEAKER_CONFIG(dwSpeakerCfg))
901 {
902 case DSSPEAKER_MONO: uSpeakerCount = 1; break;
903 case DSSPEAKER_HEADPHONE: uSpeakerCount = 2; break;
904 case DSSPEAKER_STEREO: uSpeakerCount = 2; break;
905 case DSSPEAKER_QUAD: uSpeakerCount = 4; break;
906 case DSSPEAKER_SURROUND: uSpeakerCount = 4; break;
907 case DSSPEAKER_5POINT1: uSpeakerCount = 6; break;
908 case DSSPEAKER_5POINT1_SURROUND: uSpeakerCount = 6; break;
909 case DSSPEAKER_7POINT1: uSpeakerCount = 8; break;
910 case DSSPEAKER_7POINT1_SURROUND: uSpeakerCount = 8; break;
911 default: break;
912 }
[77028]913
[86575]914 if (uSpeakerCount) /* Do we need to update the channel count? */
[88031]915 pDev->Core.cMaxOutputChannels = uSpeakerCount;
[77028]916
[86575]917 rc = VINF_SUCCESS;
918 }
919 else
920 {
921 LogRel(("DSound: Error retrieving playback device speaker config, hr=%Rhrc\n", hr));
922 rc = VERR_ACCESS_DENIED; /** @todo Fudge! */
923 }
924 }
925 else
926 {
927 LogRel(("DSound: Error retrieving playback device capabilities, hr=%Rhrc\n", hr));
928 rc = VERR_ACCESS_DENIED; /** @todo Fudge! */
929 }
[77028]930
[88554]931 IDirectSound8_Release(pDS);
[86575]932 }
933 else
934 rc = VERR_GENERAL_FAILURE;
935 }
[88031]936 else if (pDev->Core.enmUsage == PDMAUDIODIR_IN)
[86575]937 {
938 LPDIRECTSOUNDCAPTURE8 pDSC;
[88554]939 HRESULT hr = drvHostDSoundCreateDSCaptureInstance(&pDev->Guid, &pDSC);
[86575]940 if (SUCCEEDED(hr))
941 {
942 DSCCAPS DSCCaps;
943 RT_ZERO(DSCCaps);
944 DSCCaps.dwSize = sizeof(DSCCAPS);
945 hr = IDirectSoundCapture_GetCaps(pDSC, &DSCCaps);
946 if (SUCCEEDED(hr))
947 {
[88031]948 pDev->Core.cMaxInputChannels = DSCCaps.dwChannels;
[77028]949 rc = VINF_SUCCESS;
950 }
951 else
[86575]952 {
953 LogRel(("DSound: Error retrieving capture device capabilities, hr=%Rhrc\n", hr));
954 rc = VERR_ACCESS_DENIED; /** @todo Fudge! */
955 }
[77028]956
[88554]957 IDirectSoundCapture_Release(pDSC);
[77028]958 }
[86575]959 else
960 rc = VERR_GENERAL_FAILURE;
[77028]961 }
962 else
[86575]963 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
[77028]964
[86575]965 return rc;
[54433]966}
967
[88371]968
[59890]969/**
[88371]970 * Queries information for @a pDevice and adds an entry to the enumeration.
971 *
972 * @returns VBox status code.
973 * @param pDevEnm The enumeration to add the device to.
974 * @param pDevice The device.
975 * @param enmType The type of device.
976 * @param fDefault Whether it's the default device.
977 */
[88539]978static int drvHostDSoundEnumNewStyleAdd(PPDMAUDIOHOSTENUM pDevEnm, IMMDevice *pDevice, EDataFlow enmType, bool fDefault)
[88371]979{
980 int rc = VINF_SUCCESS; /* ignore most errors */
981
982 /*
983 * Gather the necessary properties.
984 */
985 IPropertyStore *pProperties = NULL;
986 HRESULT hrc = pDevice->OpenPropertyStore(STGM_READ, &pProperties);
987 if (SUCCEEDED(hrc))
988 {
989 /* Get the friendly name. */
990 PROPVARIANT VarName;
991 PropVariantInit(&VarName);
992 hrc = pProperties->GetValue(PKEY_Device_FriendlyName, &VarName);
993 if (SUCCEEDED(hrc))
994 {
995 /* Get the DirectSound GUID. */
996 PROPVARIANT VarGUID;
997 PropVariantInit(&VarGUID);
998 hrc = pProperties->GetValue(PKEY_AudioEndpoint_GUID, &VarGUID);
999 if (SUCCEEDED(hrc))
1000 {
1001 /* Get the device format. */
1002 PROPVARIANT VarFormat;
1003 PropVariantInit(&VarFormat);
1004 hrc = pProperties->GetValue(PKEY_AudioEngine_DeviceFormat, &VarFormat);
1005 if (SUCCEEDED(hrc))
1006 {
1007 WAVEFORMATEX const * const pFormat = (WAVEFORMATEX const *)VarFormat.blob.pBlobData;
1008 AssertPtr(pFormat);
1009
1010 /*
1011 * Create a enumeration entry for it.
1012 */
[89500]1013 size_t const cbName = RTUtf16CalcUtf8Len(VarName.pwszVal) + 1;
1014 PDSOUNDDEV pDev = (PDSOUNDDEV)PDMAudioHostDevAlloc(sizeof(DSOUNDDEV), cbName, 0);
[88371]1015 if (pDev)
1016 {
1017 pDev->Core.enmUsage = enmType == eRender ? PDMAUDIODIR_OUT : PDMAUDIODIR_IN;
1018 pDev->Core.enmType = PDMAUDIODEVICETYPE_BUILTIN;
[89269]1019 if (fDefault)
1020 pDev->Core.fFlags |= enmType == eRender
1021 ? PDMAUDIOHOSTDEV_F_DEFAULT_OUT : PDMAUDIOHOSTDEV_F_DEFAULT_IN;
[88371]1022 if (enmType == eRender)
1023 pDev->Core.cMaxOutputChannels = pFormat->nChannels;
1024 else
1025 pDev->Core.cMaxInputChannels = pFormat->nChannels;
1026
1027 //if (fDefault)
[89269]1028 rc = RTUuidFromUtf16((PRTUUID)&pDev->Guid, VarGUID.pwszVal);
1029 if (RT_SUCCESS(rc))
[88371]1030 {
[89269]1031 rc = RTUuidToStr((PCRTUUID)&pDev->Guid, pDev->szGuid, sizeof(pDev->szGuid));
1032 AssertRC(rc);
1033 pDev->Core.pszId = &pDev->szGuid[0];
1034
[89500]1035 rc = RTUtf16ToUtf8Ex(VarName.pwszVal, RTSTR_MAX, &pDev->Core.pszName, cbName, NULL);
[88371]1036 if (RT_SUCCESS(rc))
1037 PDMAudioHostEnumAppend(pDevEnm, &pDev->Core);
1038 else
1039 PDMAudioHostDevFree(&pDev->Core);
1040 }
1041 else
1042 {
[89269]1043 LogFunc(("RTUuidFromUtf16(%ls): %Rrc\n", VarGUID.pwszVal, rc));
[88371]1044 PDMAudioHostDevFree(&pDev->Core);
1045 }
1046 }
1047 else
1048 rc = VERR_NO_MEMORY;
1049 PropVariantClear(&VarFormat);
1050 }
1051 else
1052 LogFunc(("Failed to get PKEY_AudioEngine_DeviceFormat: %Rhrc\n", hrc));
1053 PropVariantClear(&VarGUID);
1054 }
1055 else
1056 LogFunc(("Failed to get PKEY_AudioEndpoint_GUID: %Rhrc\n", hrc));
1057 PropVariantClear(&VarName);
1058 }
1059 else
1060 LogFunc(("Failed to get PKEY_Device_FriendlyName: %Rhrc\n", hrc));
1061 pProperties->Release();
1062 }
1063 else
1064 LogFunc(("OpenPropertyStore failed: %Rhrc\n", hrc));
1065
1066 if (hrc == E_OUTOFMEMORY && RT_SUCCESS_NP(rc))
1067 rc = VERR_NO_MEMORY;
1068 return rc;
1069}
1070
[88514]1071
[88371]1072/**
[59890]1073 * Does a (Re-)enumeration of the host's playback + capturing devices.
1074 *
[88514]1075 * @return VBox status code.
[77028]1076 * @param pDevEnm Where to store the enumerated devices.
[59890]1077 */
[88539]1078static int drvHostDSoundEnumerateDevices(PPDMAUDIOHOSTENUM pDevEnm)
[59890]1079{
[77028]1080 DSLOG(("DSound: Enumerating devices ...\n"));
[59890]1081
[88371]1082 /*
1083 * Use the Vista+ API.
1084 */
1085 IMMDeviceEnumerator *pEnumerator;
1086 HRESULT hrc = CoCreateInstance(__uuidof(MMDeviceEnumerator), 0, CLSCTX_ALL,
1087 __uuidof(IMMDeviceEnumerator), (void **)&pEnumerator);
1088 if (SUCCEEDED(hrc))
1089 {
1090 int rc = VINF_SUCCESS;
1091 for (unsigned idxPass = 0; idxPass < 2 && RT_SUCCESS(rc); idxPass++)
1092 {
1093 EDataFlow const enmType = idxPass == 0 ? EDataFlow::eRender : EDataFlow::eCapture;
1094
1095 /* Get the default device first. */
1096 IMMDevice *pDefaultDevice = NULL;
1097 hrc = pEnumerator->GetDefaultAudioEndpoint(enmType, eMultimedia, &pDefaultDevice);
1098 if (SUCCEEDED(hrc))
[88539]1099 rc = drvHostDSoundEnumNewStyleAdd(pDevEnm, pDefaultDevice, enmType, true);
[88371]1100 else
1101 pDefaultDevice = NULL;
1102
1103 /* Enumerate the devices. */
1104 IMMDeviceCollection *pCollection = NULL;
1105 hrc = pEnumerator->EnumAudioEndpoints(enmType, DEVICE_STATE_ACTIVE /*| DEVICE_STATE_UNPLUGGED?*/, &pCollection);
1106 if (SUCCEEDED(hrc) && pCollection != NULL)
1107 {
1108 UINT cDevices = 0;
1109 hrc = pCollection->GetCount(&cDevices);
1110 if (SUCCEEDED(hrc))
1111 {
1112 for (UINT idxDevice = 0; idxDevice < cDevices && RT_SUCCESS(rc); idxDevice++)
1113 {
1114 IMMDevice *pDevice = NULL;
1115 hrc = pCollection->Item(idxDevice, &pDevice);
1116 if (SUCCEEDED(hrc) && pDevice)
1117 {
1118 if (pDevice != pDefaultDevice)
[88539]1119 rc = drvHostDSoundEnumNewStyleAdd(pDevEnm, pDevice, enmType, false);
[88371]1120 pDevice->Release();
1121 }
1122 }
1123 }
1124 pCollection->Release();
1125 }
1126 else
1127 LogRelMax(10, ("EnumAudioEndpoints(%s) failed: %Rhrc\n", idxPass == 0 ? "output" : "input", hrc));
1128
1129 if (pDefaultDevice)
1130 pDefaultDevice->Release();
1131 }
1132 pEnumerator->Release();
1133 if (pDevEnm->cDevices > 0 || RT_FAILURE(rc))
1134 {
1135 DSLOG(("DSound: Enumerating devices done - %u device (%Rrc)\n", pDevEnm->cDevices, rc));
1136 return rc;
1137 }
1138 }
1139
1140 /*
[88539]1141 * Fall back to dsound.
[88371]1142 */
[88539]1143 /* Resolve symbols once. */
1144 static PFNDIRECTSOUNDENUMERATEW volatile s_pfnDirectSoundEnumerateW = NULL;
1145 static PFNDIRECTSOUNDCAPTUREENUMERATEW volatile s_pfnDirectSoundCaptureEnumerateW = NULL;
1146
1147 PFNDIRECTSOUNDENUMERATEW pfnDirectSoundEnumerateW = s_pfnDirectSoundEnumerateW;
1148 PFNDIRECTSOUNDCAPTUREENUMERATEW pfnDirectSoundCaptureEnumerateW = s_pfnDirectSoundCaptureEnumerateW;
1149 if (!pfnDirectSoundEnumerateW || !pfnDirectSoundCaptureEnumerateW)
[59890]1150 {
[88539]1151 RTLDRMOD hModDSound = NIL_RTLDRMOD;
1152 int rc = RTLdrLoadSystem("dsound.dll", true /*fNoUnload*/, &hModDSound);
[59890]1153 if (RT_SUCCESS(rc))
[77028]1154 {
[88539]1155 rc = RTLdrGetSymbol(hModDSound, "DirectSoundEnumerateW", (void **)&pfnDirectSoundEnumerateW);
1156 if (RT_SUCCESS(rc))
1157 s_pfnDirectSoundEnumerateW = pfnDirectSoundEnumerateW;
1158 else
1159 LogRel(("DSound: Failed to get dsound.dll export DirectSoundEnumerateW: %Rrc\n", rc));
[86575]1160
[88539]1161 rc = RTLdrGetSymbol(hModDSound, "DirectSoundCaptureEnumerateW", (void **)&pfnDirectSoundCaptureEnumerateW);
1162 if (RT_SUCCESS(rc))
1163 s_pfnDirectSoundCaptureEnumerateW = pfnDirectSoundCaptureEnumerateW;
1164 else
1165 LogRel(("DSound: Failed to get dsound.dll export DirectSoundCaptureEnumerateW: %Rrc\n", rc));
1166 RTLdrClose(hModDSound);
[77028]1167 }
1168 else
[88539]1169 LogRel(("DSound: Unable to load dsound.dll for enumerating devices: %Rrc\n", rc));
1170 if (!pfnDirectSoundEnumerateW && !pfnDirectSoundCaptureEnumerateW)
1171 return rc;
1172 }
[59890]1173
[88539]1174 /* Common callback context for both playback and capture enumerations: */
1175 DSOUNDENUMCBCTX EnumCtx;
1176 EnumCtx.fFlags = 0;
1177 EnumCtx.pDevEnm = pDevEnm;
[86575]1178
[88539]1179 /* Enumerate playback devices. */
1180 if (pfnDirectSoundEnumerateW)
1181 {
1182 DSLOG(("DSound: Enumerating playback devices ...\n"));
1183 HRESULT hr = pfnDirectSoundEnumerateW(&drvHostDSoundEnumOldStylePlaybackCallback, &EnumCtx);
1184 if (FAILED(hr))
1185 LogRel(("DSound: Error enumerating host playback devices: %Rhrc\n", hr));
1186 }
[59890]1187
[88539]1188 /* Enumerate capture devices. */
1189 if (pfnDirectSoundCaptureEnumerateW)
1190 {
1191 DSLOG(("DSound: Enumerating capture devices ...\n"));
1192 HRESULT hr = pfnDirectSoundCaptureEnumerateW(&drvHostDSoundEnumOldStyleCaptureCallback, &EnumCtx);
1193 if (FAILED(hr))
1194 LogRel(("DSound: Error enumerating host capture devices: %Rhrc\n", hr));
[59890]1195 }
[88539]1196
1197 /*
1198 * Query Information for all enumerated devices.
1199 * Note! This is problematic to do from the enumeration callbacks.
1200 */
1201 PDSOUNDDEV pDev;
1202 RTListForEach(&pDevEnm->LstDevices, pDev, DSOUNDDEV, Core.ListEntry)
[59890]1203 {
[88542]1204 drvHostDSoundEnumOldStyleQueryDeviceInfo(pDev); /* ignore rc */
[59890]1205 }
1206
[77028]1207 DSLOG(("DSound: Enumerating devices done\n"));
1208
[88539]1209 return VINF_SUCCESS;
[59890]1210}
1211
[88514]1212
[59890]1213/**
[88514]1214 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetDevices}
1215 */
1216static DECLCALLBACK(int) drvHostDSoundHA_GetDevices(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHOSTENUM pDeviceEnum)
1217{
[88539]1218 RT_NOREF(pInterface);
[88514]1219 AssertPtrReturn(pDeviceEnum, VERR_INVALID_POINTER);
1220
[88539]1221 PDMAudioHostEnumInit(pDeviceEnum);
1222 int rc = drvHostDSoundEnumerateDevices(pDeviceEnum);
1223 if (RT_FAILURE(rc))
1224 PDMAudioHostEnumDelete(pDeviceEnum);
[88514]1225
1226 LogFlowFunc(("Returning %Rrc\n", rc));
1227 return rc;
1228}
1229
1230
1231/**
1232 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
1233 */
1234static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostDSoundHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
1235{
[88554]1236 RT_NOREF(pInterface, enmDir);
[88514]1237 return PDMAUDIOBACKENDSTS_RUNNING;
1238}
1239
1240
[88554]1241/**
[89421]1242 * Converts from PDM stream config to windows WAVEFORMATEXTENSIBLE struct.
[88554]1243 *
1244 * @param pCfg The PDM audio stream config to convert from.
1245 * @param pFmt The windows structure to initialize.
1246 */
[89421]1247static void dsoundWaveFmtFromCfg(PCPDMAUDIOSTREAMCFG pCfg, PWAVEFORMATEXTENSIBLE pFmt)
[88514]1248{
[88554]1249 RT_ZERO(*pFmt);
[89421]1250 pFmt->Format.wFormatTag = WAVE_FORMAT_PCM;
1251 pFmt->Format.nChannels = PDMAudioPropsChannels(&pCfg->Props);
1252 pFmt->Format.wBitsPerSample = PDMAudioPropsSampleBits(&pCfg->Props);
1253 pFmt->Format.nSamplesPerSec = PDMAudioPropsHz(&pCfg->Props);
1254 pFmt->Format.nBlockAlign = PDMAudioPropsFrameSize(&pCfg->Props);
1255 pFmt->Format.nAvgBytesPerSec = PDMAudioPropsFramesToBytes(&pCfg->Props, PDMAudioPropsHz(&pCfg->Props));
1256 pFmt->Format.cbSize = 0; /* No extra data specified. */
1257
1258 /*
1259 * We need to use the extensible structure if there are more than two channels
1260 * or if the channels have non-standard assignments.
1261 */
1262 if ( pFmt->Format.nChannels > 2
1263 || ( pFmt->Format.nChannels == 1
1264 ? pCfg->Props.aidChannels[0] != PDMAUDIOCHANNELID_MONO
1265 : pCfg->Props.aidChannels[0] != PDMAUDIOCHANNELID_FRONT_LEFT
1266 || pCfg->Props.aidChannels[1] != PDMAUDIOCHANNELID_FRONT_RIGHT))
1267 {
1268 pFmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1269 pFmt->Format.cbSize = sizeof(*pFmt) - sizeof(pFmt->Format);
1270 pFmt->Samples.wValidBitsPerSample = PDMAudioPropsSampleBits(&pCfg->Props);
1271 pFmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1272 pFmt->dwChannelMask = 0;
1273 unsigned const cSrcChannels = pFmt->Format.nChannels;
1274 for (unsigned i = 0; i < cSrcChannels; i++)
1275 if ( pCfg->Props.aidChannels[i] >= PDMAUDIOCHANNELID_FIRST_STANDARD
1276 && pCfg->Props.aidChannels[i] < PDMAUDIOCHANNELID_END_STANDARD)
1277 pFmt->dwChannelMask |= RT_BIT_32(pCfg->Props.aidChannels[i] - PDMAUDIOCHANNELID_FIRST_STANDARD);
1278 else
1279 pFmt->Format.nChannels -= 1;
1280 }
[88514]1281}
1282
1283
1284/**
[88554]1285 * Resets the state of a DirectSound stream, clearing the buffer content.
[59890]1286 *
1287 * @param pThis Host audio driver instance.
[88514]1288 * @param pStreamDS Stream to reset state for.
[59890]1289 */
[88554]1290static void drvHostDSoundStreamReset(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
[59890]1291{
[88514]1292 RT_NOREF(pThis);
[88554]1293 LogFunc(("Resetting %s\n", pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN ? "capture" : "playback"));
[59890]1294
[88514]1295 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
[59987]1296 {
[88554]1297 /*
1298 * Input streams.
1299 */
1300 LogFunc(("Resetting capture stream '%s'\n", pStreamDS->Cfg.szName));
1301
1302 /* Reset the state: */
[88564]1303 pStreamDS->msLastTransfer = 0;
[88554]1304/** @todo r=bird: We set the read position to zero here, but shouldn't we query it
1305 * from the buffer instead given that there isn't any interface for repositioning
1306 * to the start of the buffer as with playback buffers? */
[88564]1307 pStreamDS->In.offReadPos = 0;
1308 pStreamDS->In.cOverruns = 0;
[88514]1309
[88554]1310 /* Clear the buffer content: */
1311 AssertPtr(pStreamDS->In.pDSCB);
[88514]1312 if (pStreamDS->In.pDSCB)
[59987]1313 {
[88554]1314 PVOID pv1 = NULL;
1315 DWORD cb1 = 0;
1316 PVOID pv2 = NULL;
1317 DWORD cb2 = 0;
1318 HRESULT hrc = IDirectSoundCaptureBuffer8_Lock(pStreamDS->In.pDSCB, 0, pStreamDS->cbBufSize,
1319 &pv1, &cb1, &pv2, &cb2, 0 /*fFlags*/);
1320 if (SUCCEEDED(hrc))
[88514]1321 {
1322 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv1, cb1, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb1));
1323 if (pv2 && cb2)
1324 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv2, cb2, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb2));
[88554]1325 hrc = IDirectSoundCaptureBuffer8_Unlock(pStreamDS->In.pDSCB, pv1, cb1, pv2, cb2);
1326 if (FAILED(hrc))
1327 LogRelMaxFunc(64, ("DSound: Unlocking capture buffer '%s' after reset failed: %Rhrc\n",
1328 pStreamDS->Cfg.szName, hrc));
[88514]1329 }
[88554]1330 else
1331 LogRelMaxFunc(64, ("DSound: Locking capture buffer '%s' for reset failed: %Rhrc\n",
1332 pStreamDS->Cfg.szName, hrc));
[59987]1333 }
1334 }
[88554]1335 else
[88514]1336 {
[88554]1337 /*
1338 * Output streams.
1339 */
1340 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT);
1341 LogFunc(("Resetting playback stream '%s'\n", pStreamDS->Cfg.szName));
1342
1343 /* If draining was enagaged, make sure dsound has stopped playing: */
[88514]1344 if (pStreamDS->Out.fDrain && pStreamDS->Out.pDSB)
1345 pStreamDS->Out.pDSB->Stop();
[59987]1346
[88554]1347 /* Reset the internal state: */
[88564]1348 pStreamDS->msLastTransfer = 0;
[88554]1349 pStreamDS->Out.fFirstTransfer = true;
1350 pStreamDS->Out.fDrain = false;
1351 pStreamDS->Out.cbLastTransferred = 0;
1352 pStreamDS->Out.cbTransferred = 0;
1353 pStreamDS->Out.cbWritten = 0;
1354 pStreamDS->Out.offWritePos = 0;
[88514]1355 pStreamDS->Out.offPlayCursorLastPending = 0;
[88554]1356 pStreamDS->Out.offPlayCursorLastPlayed = 0;
[88514]1357
[88554]1358 /* Reset the buffer content and repositioning the buffer to the start of the buffer. */
1359 AssertPtr(pStreamDS->Out.pDSB);
[88514]1360 if (pStreamDS->Out.pDSB)
1361 {
[88554]1362 HRESULT hrc = IDirectSoundBuffer8_SetCurrentPosition(pStreamDS->Out.pDSB, 0);
1363 if (FAILED(hrc))
1364 LogRelMaxFunc(64, ("DSound: Failed to set buffer position for '%s': %Rhrc\n", pStreamDS->Cfg.szName, hrc));
1365
1366 PVOID pv1 = NULL;
1367 DWORD cb1 = 0;
1368 PVOID pv2 = NULL;
1369 DWORD cb2 = 0;
1370 hrc = IDirectSoundBuffer8_Lock(pStreamDS->Out.pDSB, 0, pStreamDS->cbBufSize, &pv1, &cb1, &pv2, &cb2, 0 /*fFlags*/);
1371 if (hrc == DSERR_BUFFERLOST)
[88514]1372 {
[88554]1373 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);
1374 hrc = IDirectSoundBuffer8_Lock(pStreamDS->Out.pDSB, 0, pStreamDS->cbBufSize, &pv1, &cb1, &pv2, &cb2, 0 /*fFlags*/);
[88514]1375 }
[88554]1376 if (SUCCEEDED(hrc))
1377 {
1378 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv1, cb1, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb1));
1379 if (pv2 && cb2)
1380 PDMAudioPropsClearBuffer(&pStreamDS->Cfg.Props, pv2, cb2, PDMAUDIOPCMPROPS_B2F(&pStreamDS->Cfg.Props, cb2));
1381
1382 hrc = IDirectSoundBuffer8_Unlock(pStreamDS->Out.pDSB, pv1, cb1, pv2, cb2);
1383 if (FAILED(hrc))
1384 LogRelMaxFunc(64, ("DSound: Unlocking playback buffer '%s' after reset failed: %Rhrc\n",
1385 pStreamDS->Cfg.szName, hrc));
1386 }
1387 else
1388 LogRelMaxFunc(64, ("DSound: Locking playback buffer '%s' for reset failed: %Rhrc\n", pStreamDS->Cfg.szName, hrc));
[88514]1389 }
1390 }
[59890]1391}
1392
[88514]1393
[88554]1394/**
1395 * Worker for drvHostDSoundHA_StreamCreate that creates caputre stream.
1396 *
1397 * @returns Windows COM status code.
1398 * @param pThis The DSound instance data.
1399 * @param pStreamDS The stream instance data.
1400 * @param pCfgReq The requested stream config (input).
1401 * @param pCfgAcq Where to return the actual stream config. This is a
1402 * copy of @a *pCfgReq when called.
[89421]1403 * @param pWaveFmtExt On input the requested stream format. Updated to the
1404 * actual stream format on successful return.
[88554]1405 */
[89487]1406static HRESULT drvHostDSoundStreamCreateCapture(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, PCPDMAUDIOSTREAMCFG pCfgReq,
[89421]1407 PPDMAUDIOSTREAMCFG pCfgAcq, WAVEFORMATEXTENSIBLE *pWaveFmtExt)
[88514]1408{
[88554]1409 Assert(pStreamDS->In.pDSCB == NULL);
1410 HRESULT hrc;
[88514]1411
[88554]1412 /*
1413 * Create, initialize and set up a IDirectSoundCapture instance the first time
1414 * we go thru here.
1415 */
1416 /** @todo bird: Or should we rather just throw this away after we've gotten the
1417 * capture buffer? Old code would just leak it... */
1418 if (pThis->pDSC == NULL)
1419 {
1420 hrc = drvHostDSoundCreateDSCaptureInstance(pThis->Cfg.pGuidCapture, &pThis->pDSC);
1421 if (FAILED(hrc))
1422 return hrc; /* The worker has complained to the release log already. */
1423 }
[88514]1424
[88554]1425 /*
1426 * Create the capture buffer.
1427 */
1428 DSCBUFFERDESC BufferDesc =
1429 {
1430 /*.dwSize = */ sizeof(BufferDesc),
1431 /*.dwFlags = */ 0,
1432 /*.dwBufferBytes =*/ PDMAudioPropsFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize),
1433 /*.dwReserved = */ 0,
[89421]1434 /*.lpwfxFormat = */ &pWaveFmtExt->Format,
[88554]1435 /*.dwFXCount = */ 0,
1436 /*.lpDSCFXDesc = */ NULL
1437 };
[88514]1438
[88554]1439 LogRel2(("DSound: Requested capture buffer is %#x B / %u B / %RU64 ms\n", BufferDesc.dwBufferBytes, BufferDesc.dwBufferBytes,
1440 PDMAudioPropsBytesToMilli(&pCfgReq->Props, BufferDesc.dwBufferBytes)));
1441
1442 LPDIRECTSOUNDCAPTUREBUFFER pLegacyDSCB = NULL;
1443 hrc = IDirectSoundCapture_CreateCaptureBuffer(pThis->pDSC, &BufferDesc, &pLegacyDSCB, NULL);
1444 if (FAILED(hrc))
[88514]1445 {
[88554]1446 LogRelMax(64, ("DSound: Creating capture buffer for '%s' failed: %Rhrc\n", pCfgReq->szName, hrc));
1447 return hrc;
1448 }
[88514]1449
[88554]1450 /* Get the IDirectSoundCaptureBuffer8 version of the interface. */
1451 hrc = IDirectSoundCaptureBuffer_QueryInterface(pLegacyDSCB, IID_IDirectSoundCaptureBuffer8, (void **)&pStreamDS->In.pDSCB);
1452 IDirectSoundCaptureBuffer_Release(pLegacyDSCB);
1453 if (FAILED(hrc))
1454 {
1455 LogRelMax(64, ("DSound: Querying IID_IDirectSoundCaptureBuffer8 for '%s' failed: %Rhrc\n", pCfgReq->szName, hrc));
1456 return hrc;
[88514]1457 }
1458
[88554]1459 /*
1460 * Query the actual stream configuration.
1461 */
1462#if 0 /** @todo r=bird: WTF was this for? */
1463 DWORD offByteReadPos = 0;
1464 hrc = IDirectSoundCaptureBuffer8_GetCurrentPosition(pStreamDS->In.pDSCB, NULL, &offByteReadPos);
1465 if (FAILED(hrc))
1466 {
1467 offByteReadPos = 0;
1468 DSLOGREL(("DSound: Getting capture position failed with %Rhrc\n", hr));
1469 }
1470#endif
[89421]1471 RT_ZERO(*pWaveFmtExt);
1472 hrc = IDirectSoundCaptureBuffer8_GetFormat(pStreamDS->In.pDSCB, &pWaveFmtExt->Format, sizeof(*pWaveFmtExt), NULL);
[88554]1473 if (SUCCEEDED(hrc))
1474 {
1475 /** @todo r=bird: We aren't converting/checking the pWaveFmtX content... */
[88514]1476
[88554]1477 DSCBCAPS BufferCaps = { /*.dwSize = */ sizeof(BufferCaps), 0, 0, 0 };
1478 hrc = IDirectSoundCaptureBuffer8_GetCaps(pStreamDS->In.pDSCB, &BufferCaps);
1479 if (SUCCEEDED(hrc))
1480 {
1481 LogRel2(("DSound: Acquired capture buffer capabilities for '%s':\n"
1482 "DSound: dwFlags = %#RX32\n"
1483 "DSound: dwBufferBytes = %#RX32 B / %RU32 B / %RU64 ms\n"
1484 "DSound: dwReserved = %#RX32\n",
1485 pCfgReq->szName, BufferCaps.dwFlags, BufferCaps.dwBufferBytes, BufferCaps.dwBufferBytes,
1486 PDMAudioPropsBytesToMilli(&pCfgReq->Props, BufferCaps.dwBufferBytes), BufferCaps.dwReserved ));
[88514]1487
[88554]1488 /* Update buffer related stuff: */
1489 pStreamDS->In.offReadPos = 0; /** @todo shouldn't we use offBytReadPos here to "read at the initial capture position"? */
1490 pStreamDS->cbBufSize = BufferCaps.dwBufferBytes;
1491 pCfgAcq->Backend.cFramesBufferSize = PDMAudioPropsBytesToFrames(&pCfgAcq->Props, BufferCaps.dwBufferBytes);
[88514]1492
[88554]1493#if 0 /** @todo r=bird: uAlign isn't set anywhere, so this hasn't been checking anything for a while... */
1494 if (bc.dwBufferBytes & pStreamDS->uAlign)
1495 DSLOGREL(("DSound: Capture GetCaps returned misaligned buffer: size %RU32, alignment %RU32\n",
1496 bc.dwBufferBytes, pStreamDS->uAlign + 1));
1497#endif
1498 LogFlow(("returns S_OK\n"));
1499 return S_OK;
1500 }
1501 LogRelMax(64, ("DSound: Getting capture buffer capabilities for '%s' failed: %Rhrc\n", pCfgReq->szName, hrc));
1502 }
1503 else
1504 LogRelMax(64, ("DSound: Getting capture format for '%s' failed: %Rhrc\n", pCfgReq->szName, hrc));
[88514]1505
[88554]1506 IDirectSoundCaptureBuffer8_Release(pStreamDS->In.pDSCB);
1507 pStreamDS->In.pDSCB = NULL;
1508 LogFlowFunc(("returns %Rhrc\n", hrc));
1509 return hrc;
1510}
[88514]1511
1512
[88554]1513/**
1514 * Worker for drvHostDSoundHA_StreamCreate that creates playback stream.
1515 *
1516 * @returns Windows COM status code.
1517 * @param pThis The DSound instance data.
1518 * @param pStreamDS The stream instance data.
1519 * @param pCfgReq The requested stream config (input).
1520 * @param pCfgAcq Where to return the actual stream config. This is a
1521 * copy of @a *pCfgReq when called.
[89421]1522 * @param pWaveFmtExt On input the requested stream format.
[88554]1523 * Updated to the actual stream format on successful
1524 * return.
1525 */
[89487]1526static HRESULT drvHostDSoundStreamCreatePlayback(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, PCPDMAUDIOSTREAMCFG pCfgReq,
[89421]1527 PPDMAUDIOSTREAMCFG pCfgAcq, WAVEFORMATEXTENSIBLE *pWaveFmtExt)
[88554]1528{
1529 Assert(pStreamDS->Out.pDSB == NULL);
1530 HRESULT hrc;
[88514]1531
[88554]1532 /*
1533 * Create, initialize and set up a DirectSound8 instance the first time
1534 * we go thru here.
1535 */
1536 /** @todo bird: Or should we rather just throw this away after we've gotten the
1537 * sound buffer? Old code would just leak it... */
1538 if (pThis->pDS == NULL)
1539 {
1540 hrc = drvHostDSoundCreateDSPlaybackInstance(pThis->Cfg.pGuidPlay, &pThis->pDS);
1541 if (FAILED(hrc))
1542 return hrc; /* The worker has complained to the release log already. */
1543 }
[88514]1544
[88554]1545 /*
1546 * As we reuse our (secondary) buffer for playing out data as it comes in,
1547 * we're using this buffer as a so-called streaming buffer.
1548 *
1549 * See https://msdn.microsoft.com/en-us/library/windows/desktop/ee419014(v=vs.85).aspx
1550 *
1551 * However, as we do not want to use memory on the sound device directly
1552 * (as most modern audio hardware on the host doesn't have this anyway),
1553 * we're *not* going to use DSBCAPS_STATIC for that.
1554 *
1555 * Instead we're specifying DSBCAPS_LOCSOFTWARE, as this fits the bill
1556 * of copying own buffer data to our secondary's Direct Sound buffer.
1557 */
1558 DSBUFFERDESC BufferDesc =
1559 {
1560 /*.dwSize = */ sizeof(BufferDesc),
1561 /*.dwFlags = */ DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE,
1562 /*.dwBufferBytes = */ PDMAudioPropsFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize),
1563 /*.dwReserved = */ 0,
[89421]1564 /*.lpwfxFormat = */ &pWaveFmtExt->Format,
[88554]1565 /*.guid3DAlgorithm = {0, 0, 0, {0,0,0,0, 0,0,0,0}} */
1566 };
1567 LogRel2(("DSound: Requested playback buffer is %#x B / %u B / %RU64 ms\n", BufferDesc.dwBufferBytes, BufferDesc.dwBufferBytes,
1568 PDMAudioPropsBytesToMilli(&pCfgReq->Props, BufferDesc.dwBufferBytes)));
[88514]1569
[88554]1570 LPDIRECTSOUNDBUFFER pLegacyDSB = NULL;
1571 hrc = IDirectSound8_CreateSoundBuffer(pThis->pDS, &BufferDesc, &pLegacyDSB, NULL);
1572 if (FAILED(hrc))
[88514]1573 {
[88554]1574 LogRelMax(64, ("DSound: Creating playback sound buffer for '%s' failed: %Rhrc\n", pCfgReq->szName, hrc));
1575 return hrc;
1576 }
[88514]1577
[88554]1578 /* Get the IDirectSoundBuffer8 version of the interface. */
1579 hrc = IDirectSoundBuffer_QueryInterface(pLegacyDSB, IID_IDirectSoundBuffer8, (PVOID *)&pStreamDS->Out.pDSB);
1580 IDirectSoundBuffer_Release(pLegacyDSB);
1581 if (FAILED(hrc))
1582 {
1583 LogRelMax(64, ("DSound: Querying IID_IDirectSoundBuffer8 for '%s' failed: %Rhrc\n", pCfgReq->szName, hrc));
1584 return hrc;
1585 }
[88514]1586
[88554]1587 /*
1588 * Query the actual stream parameters, they may differ from what we requested.
1589 */
[89421]1590 RT_ZERO(*pWaveFmtExt);
1591 hrc = IDirectSoundBuffer8_GetFormat(pStreamDS->Out.pDSB, &pWaveFmtExt->Format, sizeof(*pWaveFmtExt), NULL);
[88554]1592 if (SUCCEEDED(hrc))
1593 {
1594 /** @todo r=bird: We aren't converting/checking the pWaveFmtX content... */
[88514]1595
[88554]1596 DSBCAPS BufferCaps = { /*.dwSize = */ sizeof(BufferCaps), 0, 0, 0, 0 };
1597 hrc = IDirectSoundBuffer8_GetCaps(pStreamDS->Out.pDSB, &BufferCaps);
1598 if (SUCCEEDED(hrc))
[88514]1599 {
[88554]1600 LogRel2(("DSound: Acquired playback buffer capabilities for '%s':\n"
1601 "DSound: dwFlags = %#RX32\n"
1602 "DSound: dwBufferBytes = %#RX32 B / %RU32 B / %RU64 ms\n"
1603 "DSound: dwUnlockTransferRate = %RU32 KB/s\n"
1604 "DSound: dwPlayCpuOverhead = %RU32%%\n",
1605 pCfgReq->szName, BufferCaps.dwFlags, BufferCaps.dwBufferBytes, BufferCaps.dwBufferBytes,
1606 PDMAudioPropsBytesToMilli(&pCfgReq->Props, BufferCaps.dwBufferBytes),
1607 BufferCaps.dwUnlockTransferRate, BufferCaps.dwPlayCpuOverhead));
[88514]1608
[88554]1609 /* Update buffer related stuff: */
1610 pStreamDS->cbBufSize = BufferCaps.dwBufferBytes;
1611 pCfgAcq->Backend.cFramesBufferSize = PDMAudioPropsBytesToFrames(&pCfgAcq->Props, BufferCaps.dwBufferBytes);
1612 pCfgAcq->Backend.cFramesPeriod = pCfgAcq->Backend.cFramesBufferSize / 4; /* total fiction */
1613 pCfgAcq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesPreBuffering * pCfgAcq->Backend.cFramesBufferSize
1614 / RT_MAX(pCfgReq->Backend.cFramesBufferSize, 1);
[88514]1615
[88554]1616#if 0 /** @todo r=bird: uAlign isn't set anywhere, so this hasn't been checking anything for a while... */
1617 if (bc.dwBufferBytes & pStreamDS->uAlign)
1618 DSLOGREL(("DSound: Playback capabilities returned misaligned buffer: size %RU32, alignment %RU32\n",
1619 bc.dwBufferBytes, pStreamDS->uAlign + 1));
1620#endif
1621 LogFlow(("returns S_OK\n"));
1622 return S_OK;
[88514]1623 }
[88554]1624 LogRelMax(64, ("DSound: Getting playback buffer capabilities for '%s' failed: %Rhrc\n", pCfgReq->szName, hrc));
1625 }
1626 else
1627 LogRelMax(64, ("DSound: Getting playback format for '%s' failed: %Rhrc\n", pCfgReq->szName, hrc));
[88514]1628
[88554]1629 IDirectSoundBuffer8_Release(pStreamDS->Out.pDSB);
1630 pStreamDS->Out.pDSB = NULL;
1631 LogFlowFunc(("returns %Rhrc\n", hrc));
1632 return hrc;
1633}
[88514]1634
1635
[88554]1636/**
1637 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
1638 */
1639static DECLCALLBACK(int) drvHostDSoundHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
[89487]1640 PCPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
[88554]1641{
1642 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
1643 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
1644 AssertPtrReturn(pStreamDS, VERR_INVALID_POINTER);
1645 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
1646 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
1647 AssertReturn(pCfgReq->enmDir == PDMAUDIODIR_IN || pCfgReq->enmDir == PDMAUDIODIR_OUT, VERR_INVALID_PARAMETER);
1648 Assert(PDMAudioStrmCfgEquals(pCfgReq, pCfgAcq));
[88514]1649
[88554]1650 const char * const pszStreamType = pCfgReq->enmDir == PDMAUDIODIR_IN ? "capture" : "playback"; RT_NOREF(pszStreamType);
[89218]1651 LogFlowFunc(("enmPath=%s '%s'\n", PDMAudioPathGetName(pCfgReq->enmPath), pCfgReq->szName));
[88566]1652 RTListInit(&pStreamDS->ListEntry); /* paranoia */
[88514]1653
[88554]1654 /*
1655 * DSound has different COM interfaces for working with input and output
1656 * streams, so we'll quickly part ways here after some common format
1657 * specification setup and logging.
1658 */
1659#if defined(RTLOG_REL_ENABLED) || defined(LOG_ENABLED)
1660 char szTmp[64];
1661#endif
1662 LogRel2(("DSound: Opening %s stream '%s' (%s)\n", pCfgReq->szName, pszStreamType,
1663 PDMAudioPropsToString(&pCfgReq->Props, szTmp, sizeof(szTmp))));
[88514]1664
[89421]1665 WAVEFORMATEXTENSIBLE WaveFmtExt;
1666 dsoundWaveFmtFromCfg(pCfgReq, &WaveFmtExt);
[88554]1667 LogRel2(("DSound: Requested %s format for '%s':\n"
1668 "DSound: wFormatTag = %RU16\n"
1669 "DSound: nChannels = %RU16\n"
1670 "DSound: nSamplesPerSec = %RU32\n"
1671 "DSound: nAvgBytesPerSec = %RU32\n"
1672 "DSound: nBlockAlign = %RU16\n"
1673 "DSound: wBitsPerSample = %RU16\n"
1674 "DSound: cbSize = %RU16\n",
[89421]1675 pszStreamType, pCfgReq->szName, WaveFmtExt.Format.wFormatTag, WaveFmtExt.Format.nChannels,
1676 WaveFmtExt.Format.nSamplesPerSec, WaveFmtExt.Format.nAvgBytesPerSec, WaveFmtExt.Format.nBlockAlign,
1677 WaveFmtExt.Format.wBitsPerSample, WaveFmtExt.Format.cbSize));
1678 if (WaveFmtExt.Format.cbSize != 0)
1679 LogRel2(("DSound: dwChannelMask = %#RX32\n"
1680 "DSound: wValidBitsPerSample = %RU16\n",
1681 WaveFmtExt.dwChannelMask, WaveFmtExt.Samples.wValidBitsPerSample));
[88514]1682
[88554]1683 HRESULT hrc;
1684 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
[89421]1685 hrc = drvHostDSoundStreamCreateCapture(pThis, pStreamDS, pCfgReq, pCfgAcq, &WaveFmtExt);
[88554]1686 else
[89421]1687 hrc = drvHostDSoundStreamCreatePlayback(pThis, pStreamDS, pCfgReq, pCfgAcq, &WaveFmtExt);
[88554]1688 int rc;
1689 if (SUCCEEDED(hrc))
1690 {
1691 LogRel2(("DSound: Acquired %s format for '%s':\n"
1692 "DSound: wFormatTag = %RU16\n"
1693 "DSound: nChannels = %RU16\n"
1694 "DSound: nSamplesPerSec = %RU32\n"
1695 "DSound: nAvgBytesPerSec = %RU32\n"
1696 "DSound: nBlockAlign = %RU16\n"
1697 "DSound: wBitsPerSample = %RU16\n"
1698 "DSound: cbSize = %RU16\n",
[89421]1699 pszStreamType, pCfgReq->szName, WaveFmtExt.Format.wFormatTag, WaveFmtExt.Format.nChannels,
1700 WaveFmtExt.Format.nSamplesPerSec, WaveFmtExt.Format.nAvgBytesPerSec, WaveFmtExt.Format.nBlockAlign,
1701 WaveFmtExt.Format.wBitsPerSample, WaveFmtExt.Format.cbSize));
1702 if (WaveFmtExt.Format.cbSize != 0)
1703 {
1704 LogRel2(("DSound: dwChannelMask = %#RX32\n"
1705 "DSound: wValidBitsPerSample = %RU16\n",
1706 WaveFmtExt.dwChannelMask, WaveFmtExt.Samples.wValidBitsPerSample));
[88514]1707
[89421]1708 /* Update the channel count and map here. */
1709 PDMAudioPropsSetChannels(&pCfgAcq->Props, WaveFmtExt.Format.nChannels);
1710 uint8_t idCh = 0;
1711 for (unsigned iBit = 0; iBit < 32 && idCh < WaveFmtExt.Format.nChannels; iBit++)
1712 if (WaveFmtExt.dwChannelMask & RT_BIT_32(iBit))
1713 {
1714 pCfgAcq->Props.aidChannels[idCh] = (unsigned)PDMAUDIOCHANNELID_FIRST_STANDARD + iBit;
1715 idCh++;
1716 }
1717 Assert(idCh == WaveFmtExt.Format.nChannels);
1718 }
1719
[88554]1720 /*
1721 * Copy the acquired config and reset the stream (clears the buffer).
1722 */
1723 PDMAudioStrmCfgCopy(&pStreamDS->Cfg, pCfgAcq);
1724 drvHostDSoundStreamReset(pThis, pStreamDS);
[88566]1725
1726 RTCritSectEnter(&pThis->CritSect);
1727 RTListAppend(&pThis->HeadStreams, &pStreamDS->ListEntry);
1728 RTCritSectLeave(&pThis->CritSect);
1729
[88554]1730 rc = VINF_SUCCESS;
1731 }
1732 else
1733 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
[88514]1734
[88554]1735 LogFlowFunc(("returns %Rrc\n", rc));
1736 return rc;
[88514]1737}
1738
1739
1740/**
1741 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
1742 */
[89213]1743static DECLCALLBACK(int) drvHostDSoundHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1744 bool fImmediate)
[88514]1745{
[88554]1746 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
[88514]1747 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
[88554]1748 AssertPtrReturn(pStreamDS, VERR_INVALID_POINTER);
[88564]1749 LogFlowFunc(("Stream '%s' {%s}\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS)));
[89213]1750 RT_NOREF(fImmediate);
[88514]1751
[88566]1752 RTCritSectEnter(&pThis->CritSect);
1753 RTListNodeRemove(&pStreamDS->ListEntry);
1754 RTCritSectLeave(&pThis->CritSect);
1755
[88514]1756 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
[88555]1757 {
1758 /*
1759 * Input.
1760 */
1761 if (pStreamDS->In.pDSCB)
1762 {
[88556]1763 HRESULT hrc = IDirectSoundCaptureBuffer_Stop(pStreamDS->In.pDSCB);
1764 if (FAILED(hrc))
1765 LogFunc(("IDirectSoundCaptureBuffer_Stop failed: %Rhrc\n", hrc));
[88555]1766
[88556]1767 drvHostDSoundStreamReset(pThis, pStreamDS);
1768
[88555]1769 IDirectSoundCaptureBuffer8_Release(pStreamDS->In.pDSCB);
1770 pStreamDS->In.pDSCB = NULL;
1771 }
1772 }
[88514]1773 else
[88555]1774 {
1775 /*
1776 * Output.
1777 */
1778 if (pStreamDS->Out.pDSB)
1779 {
[88556]1780 drvHostDSoundStreamStopPlayback(pThis, pStreamDS, true /*fReset*/);
[88514]1781
[88555]1782 IDirectSoundBuffer8_Release(pStreamDS->Out.pDSB);
1783 pStreamDS->Out.pDSB = NULL;
1784 }
[88514]1785 }
1786
[88555]1787 if (RTCritSectIsInitialized(&pStreamDS->CritSect))
1788 RTCritSectDelete(&pStreamDS->CritSect);
1789
1790 return VINF_SUCCESS;
[88514]1791}
1792
1793
1794/**
[88556]1795 * Worker for drvHostDSoundHA_StreamEnable and drvHostDSoundHA_StreamResume.
[88514]1796 *
[88556]1797 * This will try re-open the capture device if we're having trouble starting it.
1798 *
1799 * @returns VBox status code.
1800 * @param pThis The DSound host audio driver instance data.
1801 * @param pStreamDS The stream instance data.
[88514]1802 */
[88556]1803static int drvHostDSoundStreamCaptureStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
[88514]1804{
[88556]1805 /*
1806 * Check the stream status first.
1807 */
1808 int rc = VERR_AUDIO_STREAM_NOT_READY;
1809 if (pStreamDS->In.pDSCB)
1810 {
1811 DWORD fStatus = 0;
1812 HRESULT hrc = IDirectSoundCaptureBuffer8_GetStatus(pStreamDS->In.pDSCB, &fStatus);
1813 if (SUCCEEDED(hrc))
1814 {
1815 /*
1816 * Try start capturing if it's not already doing so.
1817 */
1818 if (!(fStatus & DSCBSTATUS_CAPTURING))
1819 {
1820 LogRel2(("DSound: Starting capture on '%s' ... \n", pStreamDS->Cfg.szName));
1821 hrc = IDirectSoundCaptureBuffer8_Start(pStreamDS->In.pDSCB, DSCBSTART_LOOPING);
1822 if (SUCCEEDED(hrc))
1823 rc = VINF_SUCCESS;
1824 else
1825 {
1826 /*
1827 * Failed to start, try re-create the capture buffer.
1828 */
1829 LogRelMax(64, ("DSound: Starting to capture on '%s' failed: %Rhrc - will try re-open it ...\n",
1830 pStreamDS->Cfg.szName, hrc));
[88514]1831
[88556]1832 IDirectSoundCaptureBuffer8_Release(pStreamDS->In.pDSCB);
1833 pStreamDS->In.pDSCB = NULL;
[88514]1834
[89421]1835 PDMAUDIOSTREAMCFG CfgReq = pStreamDS->Cfg;
1836 PDMAUDIOSTREAMCFG CfgAcq = pStreamDS->Cfg;
1837 WAVEFORMATEXTENSIBLE WaveFmtExt;
1838 dsoundWaveFmtFromCfg(&pStreamDS->Cfg, &WaveFmtExt);
1839 hrc = drvHostDSoundStreamCreateCapture(pThis, pStreamDS, &CfgReq, &CfgAcq, &WaveFmtExt);
[88556]1840 if (SUCCEEDED(hrc))
1841 {
1842 PDMAudioStrmCfgCopy(&pStreamDS->Cfg, &CfgAcq);
[88514]1843
[88556]1844 /*
1845 * Try starting capture again.
1846 */
1847 LogRel2(("DSound: Starting capture on re-opened '%s' ... \n", pStreamDS->Cfg.szName));
1848 hrc = IDirectSoundCaptureBuffer8_Start(pStreamDS->In.pDSCB, DSCBSTART_LOOPING);
1849 if (SUCCEEDED(hrc))
1850 rc = VINF_SUCCESS;
1851 else
1852 LogRelMax(64, ("DSound: Starting to capture on re-opened '%s' failed: %Rhrc\n",
1853 pStreamDS->Cfg.szName, hrc));
1854 }
1855 else
1856 LogRelMax(64, ("DSound: Re-opening '%s' failed: %Rhrc\n", pStreamDS->Cfg.szName, hrc));
1857 }
1858 }
1859 else
1860 {
1861 LogRel2(("DSound: Already capturing (%#x)\n", fStatus));
1862 AssertFailed();
1863 }
1864 }
1865 else
1866 LogRelMax(64, ("DSound: Retrieving capture status for '%s' failed: %Rhrc\n", pStreamDS->Cfg.szName, hrc));
1867 }
1868 LogFlowFunc(("returns %Rrc\n", rc));
1869 return rc;
[88514]1870}
1871
1872
1873/**
[89510]1874 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
[88514]1875 */
[88556]1876static DECLCALLBACK(int) drvHostDSoundHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
[88514]1877{
[88556]1878 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
1879 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
[88564]1880 LogFlowFunc(("Stream '%s' {%s}\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS)));
[88514]1881
[88556]1882 /*
1883 * We always reset the buffer before enabling the stream (normally never necessary).
1884 */
1885 drvHostDSoundStreamReset(pThis, pStreamDS);
1886 pStreamDS->fEnabled = true;
[88514]1887
[88556]1888 /*
1889 * Input streams will start capturing, while output streams will only start
1890 * playing once we get some audio data to play.
1891 */
1892 int rc = VINF_SUCCESS;
1893 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
1894 rc = drvHostDSoundStreamCaptureStart(pThis, pStreamDS);
1895 else
1896 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT);
[88514]1897
[88556]1898 LogFlowFunc(("returns %Rrc\n", rc));
1899 return rc;
[88514]1900}
1901
1902
[88556]1903/**
1904 * Worker for drvHostDSoundHA_StreamDestroy, drvHostDSoundHA_StreamDisable and
1905 * drvHostDSoundHA_StreamPause.
1906 *
1907 * @returns VBox status code.
1908 * @param pThis The DSound host audio driver instance data.
1909 * @param pStreamDS The stream instance data.
1910 * @param fReset Whether to reset the buffer and state.
1911 */
1912static int drvHostDSoundStreamStopPlayback(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, bool fReset)
[88514]1913{
[88555]1914 if (!pStreamDS->Out.pDSB)
[88556]1915 return VINF_SUCCESS;
[88514]1916
[88555]1917 LogRel2(("DSound: Stopping playback of '%s'...\n", pStreamDS->Cfg.szName));
1918 HRESULT hrc = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB);
1919 if (FAILED(hrc))
[88514]1920 {
[88555]1921 LogFunc(("IDirectSoundBuffer8_Stop -> %Rhrc; will attempt restoring the stream...\n", hrc));
1922 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);
1923 hrc = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB);
1924 if (FAILED(hrc))
[88556]1925 LogRelMax(64, ("DSound: %s playback of '%s' failed: %Rhrc\n", fReset ? "Stopping" : "Pausing",
[88555]1926 pStreamDS->Cfg.szName, hrc));
[88514]1927 }
[88555]1928 LogRel2(("DSound: Stopped playback of '%s': %Rhrc\n", pStreamDS->Cfg.szName, hrc));
[88514]1929
[88556]1930 if (fReset)
[88555]1931 drvHostDSoundStreamReset(pThis, pStreamDS);
[88556]1932 return SUCCEEDED(hrc) ? VINF_SUCCESS : VERR_AUDIO_STREAM_NOT_READY;
[88514]1933}
1934
1935
[88556]1936/**
[89510]1937 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
[88556]1938 */
1939static DECLCALLBACK(int) drvHostDSoundHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
[54433]1940{
[88556]1941 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
1942 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
[88564]1943 LogFlowFunc(("cMsLastTransfer=%RI64 ms, stream '%s' {%s} \n",
1944 pStreamDS->msLastTransfer ? RTTimeMilliTS() - pStreamDS->msLastTransfer : -1,
1945 pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS) ));
[54433]1946
[88556]1947 /*
1948 * Change the state.
1949 */
1950 pStreamDS->fEnabled = false;
1951
1952 /*
1953 * Stop the stream and maybe reset the buffer.
1954 */
1955 int rc = VINF_SUCCESS;
1956 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
[54433]1957 {
[88556]1958 if (pStreamDS->In.pDSCB)
[70916]1959 {
[88556]1960 HRESULT hrc = IDirectSoundCaptureBuffer_Stop(pStreamDS->In.pDSCB);
1961 if (SUCCEEDED(hrc))
1962 LogRel3(("DSound: Stopped capture on '%s'.\n", pStreamDS->Cfg.szName));
1963 else
[88376]1964 {
[88556]1965 LogRelMax(64, ("DSound: Stopping capture on '%s' failed: %Rhrc\n", pStreamDS->Cfg.szName, hrc));
1966 /* Don't report errors up to the caller, as it might just be a capture device change. */
[88376]1967 }
[88556]1968
1969 /* This isn't strictly speaking necessary since StreamEnable does it too... */
1970 drvHostDSoundStreamReset(pThis, pStreamDS);
[57375]1971 }
[88556]1972 }
1973 else
1974 {
1975 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT);
1976 if (pStreamDS->Out.pDSB)
[73563]1977 {
[88991]1978 rc = drvHostDSoundStreamStopPlayback(pThis, pStreamDS, true /*fReset*/);
1979 if (RT_SUCCESS(rc))
1980 LogRel3(("DSound: Stopped playback on '%s'.\n", pStreamDS->Cfg.szName));
[73563]1981 }
[54433]1982 }
1983
[88564]1984 LogFlowFunc(("returns %Rrc {%s}\n", rc, drvHostDSoundStreamStatusString(pStreamDS)));
[54433]1985 return rc;
1986}
1987
[88514]1988
[88556]1989/**
[89510]1990 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
[88556]1991 *
1992 * @note Basically the same as drvHostDSoundHA_StreamDisable, just w/o the
1993 * buffer resetting and fEnabled change.
1994 */
1995static DECLCALLBACK(int) drvHostDSoundHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
[88514]1996{
[88556]1997 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
1998 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
[88564]1999 LogFlowFunc(("cMsLastTransfer=%RI64 ms, stream '%s' {%s} \n",
2000 pStreamDS->msLastTransfer ? RTTimeMilliTS() - pStreamDS->msLastTransfer : -1,
2001 pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS) ));
[88514]2002
[88556]2003 /*
2004 * Stop the stream and maybe reset the buffer.
2005 */
2006 int rc = VINF_SUCCESS;
2007 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
[88514]2008 {
[88556]2009 if (pStreamDS->In.pDSCB)
[88514]2010 {
[88556]2011 HRESULT hrc = IDirectSoundCaptureBuffer_Stop(pStreamDS->In.pDSCB);
2012 if (SUCCEEDED(hrc))
2013 LogRel3(("DSound: Stopped capture on '%s'.\n", pStreamDS->Cfg.szName));
2014 else
2015 {
2016 LogRelMax(64, ("DSound: Stopping capture on '%s' failed: %Rhrc\n", pStreamDS->Cfg.szName, hrc));
2017 /* Don't report errors up to the caller, as it might just be a capture device change. */
2018 }
[88514]2019 }
[88556]2020 }
2021 else
2022 {
2023 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT);
2024 if (pStreamDS->Out.pDSB)
[88514]2025 {
[88556]2026 /* Don't stop draining buffers, we won't be resuming them right.
2027 They'll stop by themselves anyway. */
2028 if (pStreamDS->Out.fDrain)
2029 LogFunc(("Stream '%s' is draining\n", pStreamDS->Cfg.szName));
[88514]2030 else
2031 {
[88556]2032 rc = drvHostDSoundStreamStopPlayback(pThis, pStreamDS, false /*fReset*/);
2033 if (RT_SUCCESS(rc))
2034 LogRel3(("DSound: Stopped playback on '%s'.\n", pStreamDS->Cfg.szName));
[88514]2035 }
2036 }
2037 }
2038
[88564]2039 LogFlowFunc(("returns %Rrc {%s}\n", rc, drvHostDSoundStreamStatusString(pStreamDS)));
[88556]2040 return rc;
[88514]2041}
2042
2043
[88556]2044/**
2045 * Worker for drvHostDSoundHA_StreamResume and drvHostDSoundHA_StreamPlay that
2046 * starts playing the DirectSound Buffer.
2047 *
2048 * @returns VBox status code.
2049 * @param pThis Host audio driver instance.
2050 * @param pStreamDS Stream to start playing.
2051 */
2052static int directSoundPlayStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS)
[88514]2053{
[88556]2054 if (!pStreamDS->Out.pDSB)
2055 return VERR_AUDIO_STREAM_NOT_READY;
[88555]2056
[88556]2057 LogRel2(("DSound: Starting playback of '%s' ...\n", pStreamDS->Cfg.szName));
2058 HRESULT hrc = IDirectSoundBuffer8_Play(pStreamDS->Out.pDSB, 0, 0, DSCBSTART_LOOPING);
2059 if (SUCCEEDED(hrc))
2060 return VINF_SUCCESS;
[88555]2061
[88556]2062 for (unsigned i = 0; hrc == DSERR_BUFFERLOST && i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
2063 {
2064 LogFunc(("Restarting playback failed due to lost buffer, restoring ...\n"));
2065 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);
2066
2067 hrc = IDirectSoundBuffer8_Play(pStreamDS->Out.pDSB, 0, 0, DSCBSTART_LOOPING);
2068 if (SUCCEEDED(hrc))
2069 return VINF_SUCCESS;
2070 }
2071
2072 LogRelMax(64, ("DSound: Failed to start playback of '%s': %Rhrc\n", pStreamDS->Cfg.szName, hrc));
2073 return VERR_AUDIO_STREAM_NOT_READY;
[88555]2074}
2075
2076
[88556]2077/**
[89510]2078 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume}
[88556]2079 */
2080static DECLCALLBACK(int) drvHostDSoundHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
[88555]2081{
[88556]2082 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
2083 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
[88564]2084 LogFlowFunc(("Stream '%s' {%s}\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS)));
[88514]2085
[88556]2086 /*
2087 * Input streams will start capturing, while output streams will only start
2088 * playing if we're past the pre-buffering state.
2089 */
2090 int rc = VINF_SUCCESS;
2091 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
2092 rc = drvHostDSoundStreamCaptureStart(pThis, pStreamDS);
2093 else
[88514]2094 {
[88556]2095 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT);
2096 if (!pStreamDS->Out.fFirstTransfer)
2097 rc = directSoundPlayStart(pThis, pStreamDS);
[88514]2098 }
2099
[88564]2100 LogFlowFunc(("returns %Rrc {%s}\n", rc, drvHostDSoundStreamStatusString(pStreamDS)));
[88556]2101 return rc;
[88514]2102}
2103
2104
[88556]2105/**
[89510]2106 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
[88556]2107 */
2108static DECLCALLBACK(int) drvHostDSoundHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
[88514]2109{
[88556]2110 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
2111 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2112 AssertReturn(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT, VERR_INVALID_PARAMETER);
[88564]2113 LogFlowFunc(("cMsLastTransfer=%RI64 ms, stream '%s' {%s} \n",
2114 pStreamDS->msLastTransfer ? RTTimeMilliTS() - pStreamDS->msLastTransfer : -1,
2115 pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS) ));
[88514]2116
[88556]2117 /*
2118 * We've started the buffer in looping mode, try switch to non-looping...
2119 */
[88514]2120 int rc = VINF_SUCCESS;
[88556]2121 if (pStreamDS->Out.pDSB && !pStreamDS->Out.fDrain)
[88514]2122 {
[88556]2123 LogRel2(("DSound: Switching playback stream '%s' to drain mode...\n", pStreamDS->Cfg.szName));
2124 HRESULT hrc = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB);
2125 if (SUCCEEDED(hrc))
[88514]2126 {
[88556]2127 hrc = IDirectSoundBuffer8_Play(pStreamDS->Out.pDSB, 0, 0, 0);
2128 if (SUCCEEDED(hrc))
[88566]2129 {
2130 uint64_t const msNow = RTTimeMilliTS();
2131 pStreamDS->Out.msDrainDeadline = PDMAudioPropsBytesToMilli(&pStreamDS->Cfg.Props, pStreamDS->cbBufSize) + msNow;
2132 pStreamDS->Out.fDrain = true;
2133 }
[88556]2134 else
2135 LogRelMax(64, ("DSound: Failed to restart '%s' in drain mode: %Rhrc\n", pStreamDS->Cfg.szName, hrc));
[88514]2136 }
[88556]2137 else
[88514]2138 {
[88556]2139 Log2Func(("drain: IDirectSoundBuffer8_Stop failed: %Rhrc\n", hrc));
2140 directSoundPlayRestore(pThis, pStreamDS->Out.pDSB);
[88514]2141
[88556]2142 HRESULT hrc2 = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB);
2143 if (SUCCEEDED(hrc2))
2144 LogFunc(("Successfully stopped the stream after restoring it. (hrc=%Rhrc)\n", hrc));
2145 else
2146 {
2147 LogRelMax(64, ("DSound: Failed to stop playback stream '%s' for putting into drain mode: %Rhrc (initial), %Rhrc (after restore)\n",
2148 pStreamDS->Cfg.szName, hrc, hrc2));
2149 rc = VERR_AUDIO_STREAM_NOT_READY;
2150 }
[88514]2151 }
2152 }
[88564]2153 LogFlowFunc(("returns %Rrc {%s}\n", rc, drvHostDSoundStreamStatusString(pStreamDS)));
[88514]2154 return rc;
2155}
2156
[88566]2157
[62910]2158/**
[88514]2159 * Retrieves the number of free bytes available for writing to a DirectSound output stream.
2160 *
2161 * @return VBox status code. VERR_NOT_AVAILABLE if unable to determine or the
2162 * buffer was not recoverable.
2163 * @param pThis Host audio driver instance.
2164 * @param pStreamDS DirectSound output stream to retrieve number for.
2165 * @param pdwFree Where to return the free amount on success.
2166 * @param poffPlayCursor Where to return the play cursor offset.
2167 */
2168static int dsoundGetFreeOut(PDRVHOSTDSOUND pThis, PDSOUNDSTREAM pStreamDS, DWORD *pdwFree, DWORD *poffPlayCursor)
2169{
2170 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2171 AssertPtrReturn(pStreamDS, VERR_INVALID_POINTER);
2172 AssertPtrReturn(pdwFree, VERR_INVALID_POINTER);
2173 AssertPtr(poffPlayCursor);
2174
2175 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT); /* Paranoia. */
2176
2177 LPDIRECTSOUNDBUFFER8 pDSB = pStreamDS->Out.pDSB;
[88564]2178 AssertPtrReturn(pDSB, VERR_INVALID_POINTER);
[88514]2179
2180 HRESULT hr = S_OK;
2181
2182 /* Get the current play position which is used for calculating the free space in the buffer. */
2183 for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
2184 {
2185 DWORD offPlayCursor = 0;
2186 DWORD offWriteCursor = 0;
2187 hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &offPlayCursor, &offWriteCursor);
2188 if (SUCCEEDED(hr))
2189 {
2190 int32_t cbDiff = offWriteCursor - offPlayCursor;
2191 if (cbDiff < 0)
2192 cbDiff += pStreamDS->cbBufSize;
2193
2194 int32_t cbFree = offPlayCursor - pStreamDS->Out.offWritePos;
2195 if (cbFree < 0)
2196 cbFree += pStreamDS->cbBufSize;
2197
2198 if (cbFree > (int32_t)pStreamDS->cbBufSize - cbDiff)
2199 {
2200 /** @todo count/log these. */
2201 pStreamDS->Out.offWritePos = offWriteCursor;
2202 cbFree = pStreamDS->cbBufSize - cbDiff;
2203 }
2204
2205 /* When starting to use a DirectSound buffer, offPlayCursor and offWriteCursor
2206 * both point at position 0, so we won't be able to detect how many bytes
2207 * are writable that way.
2208 *
2209 * So use our per-stream written indicator to see if we just started a stream. */
2210 if (pStreamDS->Out.cbWritten == 0)
2211 cbFree = pStreamDS->cbBufSize;
2212
[91020]2213 LogRel4(("DSound: offPlayCursor=%RU32, offWriteCursor=%RU32, offWritePos=%RU32 -> cbFree=%RI32\n",
[89421]2214 offPlayCursor, offWriteCursor, pStreamDS->Out.offWritePos, cbFree));
[88514]2215
2216 *pdwFree = cbFree;
2217 *poffPlayCursor = offPlayCursor;
2218 return VINF_SUCCESS;
2219 }
2220
2221 if (hr != DSERR_BUFFERLOST) /** @todo MSDN doesn't state this error for GetCurrentPosition(). */
2222 break;
2223
2224 LogFunc(("Getting playing position failed due to lost buffer, restoring ...\n"));
2225
2226 directSoundPlayRestore(pThis, pDSB);
2227 }
2228
2229 if (hr != DSERR_BUFFERLOST) /* Avoid log flooding if the error is still there. */
2230 DSLOGREL(("DSound: Getting current playback position failed with %Rhrc\n", hr));
2231
2232 LogFunc(("Failed with %Rhrc\n", hr));
2233
2234 *poffPlayCursor = pStreamDS->cbBufSize;
2235 return VERR_NOT_AVAILABLE;
2236}
2237
2238
2239/**
[89504]2240 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
[88514]2241 */
[89504]2242static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvHostDSoundHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
2243 PPDMAUDIOBACKENDSTREAM pStream)
[88514]2244{
[89504]2245 RT_NOREF(pInterface);
2246 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2247 AssertPtrReturn(pStreamDS, PDMHOSTAUDIOSTREAMSTATE_INVALID);
[88514]2248
[89504]2249 if ( pStreamDS->Cfg.enmDir != PDMAUDIODIR_OUT
2250 || !pStreamDS->Out.fDrain)
2251 {
2252 LogFlowFunc(("returns OKAY for '%s' {%s}\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS)));
2253 return PDMHOSTAUDIOSTREAMSTATE_OKAY;
2254 }
2255 LogFlowFunc(("returns DRAINING for '%s' {%s}\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS)));
2256 return PDMHOSTAUDIOSTREAMSTATE_DRAINING;
[88514]2257}
2258
2259#if 0 /* This isn't working as the write cursor is more a function of time than what we do.
2260 Previously we only reported the pre-buffering status anyway, so no harm. */
2261/**
2262 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending}
2263 */
2264static DECLCALLBACK(uint32_t) drvHostDSoundHA_StreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2265{
2266 /*PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio); */ RT_NOREF(pInterface);
2267 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2268 AssertPtrReturn(pStreamDS, 0);
[88564]2269 LogFlowFunc(("Stream '%s' {%s}\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS)));
[88514]2270
2271 if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_OUT)
2272 {
2273 /* This is a similar calculation as for StreamGetReadable, only for an output buffer. */
2274 AssertPtr(pStreamDS->In.pDSCB);
2275 DWORD offPlayCursor = 0;
2276 DWORD offWriteCursor = 0;
2277 HRESULT hrc = IDirectSoundBuffer8_GetCurrentPosition(pStreamDS->Out.pDSB, &offPlayCursor, &offWriteCursor);
2278 if (SUCCEEDED(hrc))
2279 {
2280 uint32_t cbPending = dsoundRingDistance(offWriteCursor, offPlayCursor, pStreamDS->cbBufSize);
2281 Log3Func(("cbPending=%RU32\n", cbPending));
2282 return cbPending;
2283 }
2284 AssertMsgFailed(("hrc=%Rhrc\n", hrc));
2285 }
2286 /* else: For input streams we never have any pending data. */
2287
2288 return 0;
2289}
2290#endif
2291
2292/**
[89504]2293 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
[88514]2294 */
[89504]2295static DECLCALLBACK(uint32_t) drvHostDSoundHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
[88514]2296{
[89504]2297 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
2298 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2299 AssertPtrReturn(pStreamDS, 0);
2300 LogFlowFunc(("Stream '%s' {%s}\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS)));
[88514]2301
[89504]2302 DWORD cbFree = 0;
2303 DWORD offIgn = 0;
2304 int rc = dsoundGetFreeOut(pThis, pStreamDS, &cbFree, &offIgn);
2305 AssertRCReturn(rc, 0);
2306
2307 return cbFree;
[88514]2308}
2309
2310
2311/**
[62910]2312 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
2313 */
[88376]2314static DECLCALLBACK(int) drvHostDSoundHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
2315 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
[54433]2316{
[88376]2317 PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);
2318 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2319 AssertPtrReturn(pStreamDS, 0);
[88991]2320 if (cbBuf)
2321 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
[88376]2322 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
[57375]2323
[88376]2324 if (pStreamDS->fEnabled)
2325 AssertReturn(pStreamDS->cbBufSize, VERR_INTERNAL_ERROR_2);
2326 else
2327 {
[88564]2328 Log2Func(("Skipping disabled stream {%s}\n", drvHostDSoundStreamStatusString(pStreamDS)));
[88376]2329 return VINF_SUCCESS;
2330 }
[88564]2331 Log4Func(("cbBuf=%#x stream '%s' {%s}\n", cbBuf, pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS) ));
[54433]2332
[88376]2333 /*
2334 * Transfer loop.
2335 */
2336 uint32_t cbWritten = 0;
2337 while (cbBuf > 0)
2338 {
2339 /*
2340 * Figure out how much we can possibly write.
2341 */
2342 DWORD offPlayCursor = 0;
2343 DWORD cbWritable = 0;
2344 int rc = dsoundGetFreeOut(pThis, pStreamDS, &cbWritable, &offPlayCursor);
2345 AssertRCReturn(rc, rc);
2346 if (cbWritable < pStreamDS->Cfg.Props.cbFrame)
2347 break;
[65565]2348
[88376]2349 uint32_t const cbToWrite = RT_MIN(cbWritable, cbBuf);
[88564]2350 Log3Func(("offPlay=%#x offWritePos=%#x -> cbWritable=%#x cbToWrite=%#x {%s}\n", offPlayCursor, pStreamDS->Out.offWritePos,
2351 cbWritable, cbToWrite, drvHostDSoundStreamStatusString(pStreamDS) ));
[59890]2352
[88376]2353 /*
2354 * Lock that amount of buffer.
2355 */
2356 PVOID pv1 = NULL;
2357 DWORD cb1 = 0;
2358 PVOID pv2 = NULL;
2359 DWORD cb2 = 0;
2360 HRESULT hrc = directSoundPlayLock(pThis, pStreamDS, pStreamDS->Out.offWritePos, cbToWrite,
2361 &pv1, &pv2, &cb1, &cb2, 0 /*dwFlags*/);
2362 AssertMsgReturn(SUCCEEDED(hrc), ("%Rhrc\n", hrc), VERR_ACCESS_DENIED); /** @todo translate these status codes already! */
2363 //AssertMsg(cb1 + cb2 == cbToWrite, ("%#x + %#x vs %#x\n", cb1, cb2, cbToWrite));
[65624]2364
[88376]2365 /*
2366 * Copy over the data.
2367 */
2368 memcpy(pv1, pvBuf, cb1);
2369 pvBuf = (uint8_t *)pvBuf + cb1;
2370 cbBuf -= cb1;
2371 cbWritten += cb1;
2372
2373 if (pv2)
[68767]2374 {
[88376]2375 memcpy(pv2, pvBuf, cb2);
2376 pvBuf = (uint8_t *)pvBuf + cb2;
2377 cbBuf -= cb2;
2378 cbWritten += cb2;
2379 }
[68767]2380
[88376]2381 /*
2382 * Unlock and update the write position.
2383 */
2384 directSoundPlayUnlock(pThis, pStreamDS->Out.pDSB, pv1, pv2, cb1, cb2); /** @todo r=bird: pThis + pDSB parameters here for Unlock, but only pThis for Lock. Why? */
2385 pStreamDS->Out.offWritePos = (pStreamDS->Out.offWritePos + cb1 + cb2) % pStreamDS->cbBufSize;
[68767]2386
[88376]2387 /*
2388 * If this was the first chunk, kick off playing.
2389 */
2390 if (!pStreamDS->Out.fFirstTransfer)
2391 { /* likely */ }
2392 else
2393 {
2394 *pcbWritten = cbWritten;
[88556]2395 rc = directSoundPlayStart(pThis, pStreamDS);
2396 AssertRCReturn(rc, rc);
[88376]2397 pStreamDS->Out.fFirstTransfer = false;
[58983]2398 }
[70916]2399 }
[54433]2400
[88376]2401 /*
2402 * Done.
2403 */
2404 *pcbWritten = cbWritten;
[68767]2405
[88376]2406 pStreamDS->Out.cbTransferred += cbWritten;
2407 if (cbWritten)
[65624]2408 {
[89551]2409 uint64_t const msPrev = pStreamDS->msLastTransfer; RT_NOREF(msPrev);
[88564]2410 pStreamDS->Out.cbLastTransferred = cbWritten;
2411 pStreamDS->msLastTransfer = RTTimeMilliTS();
2412 LogFlowFunc(("cbLastTransferred=%RU32, msLastTransfer=%RU64 msNow=%RU64 cMsDelta=%RU64 {%s}\n",
2413 cbWritten, msPrev, pStreamDS->msLastTransfer, msPrev ? pStreamDS->msLastTransfer - msPrev : 0,
2414 drvHostDSoundStreamStatusString(pStreamDS) ));
[65624]2415 }
[88991]2416 else if ( pStreamDS->Out.fDrain
2417 && RTTimeMilliTS() >= pStreamDS->Out.msDrainDeadline)
2418 {
2419 LogRel2(("DSound: Stopping draining of '%s' {%s} ...\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS)));
2420 if (pStreamDS->Out.pDSB)
2421 {
2422 HRESULT hrc = IDirectSoundBuffer8_Stop(pStreamDS->Out.pDSB);
2423 if (FAILED(hrc))
2424 LogRelMax(64, ("DSound: Failed to stop draining stream '%s': %Rhrc\n", pStreamDS->Cfg.szName, hrc));
2425 }
2426 pStreamDS->Out.fDrain = false;
2427 pStreamDS->fEnabled = false;
2428 }
2429
[88376]2430 return VINF_SUCCESS;
[54433]2431}
2432
2433
[62910]2434/**
[89504]2435 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
2436 */
2437static DECLCALLBACK(uint32_t) drvHostDSoundHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2438{
2439 /*PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio); */ RT_NOREF(pInterface);
2440 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2441 AssertPtrReturn(pStreamDS, 0);
2442 Assert(pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN);
2443
2444 if (pStreamDS->fEnabled)
2445 {
2446 /* This is the same calculation as for StreamGetPending. */
2447 AssertPtr(pStreamDS->In.pDSCB);
2448 DWORD offCaptureCursor = 0;
2449 DWORD offReadCursor = 0;
2450 HRESULT hrc = IDirectSoundCaptureBuffer_GetCurrentPosition(pStreamDS->In.pDSCB, &offCaptureCursor, &offReadCursor);
2451 if (SUCCEEDED(hrc))
2452 {
2453 uint32_t cbPending = dsoundRingDistance(offCaptureCursor, offReadCursor, pStreamDS->cbBufSize);
2454 Log3Func(("cbPending=%RU32\n", cbPending));
2455 return cbPending;
2456 }
2457 AssertMsgFailed(("hrc=%Rhrc\n", hrc));
2458 }
2459
2460 return 0;
2461}
2462
2463
2464/**
[62910]2465 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
2466 */
[82255]2467static DECLCALLBACK(int) drvHostDSoundHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
[88376]2468 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
[54433]2469{
[88376]2470 /*PDRVHOSTDSOUND pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDSOUND, IHostAudio);*/ RT_NOREF(pInterface);
2471 PDSOUNDSTREAM pStreamDS = (PDSOUNDSTREAM)pStream;
2472 AssertPtrReturn(pStreamDS, 0);
2473 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2474 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
2475 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
[63363]2476
[88376]2477#if 0 /** @todo r=bird: shouldn't we do the same check as for output streams? */
2478 if (pStreamDS->fEnabled)
2479 AssertReturn(pStreamDS->cbBufSize, VERR_INTERNAL_ERROR_2);
2480 else
2481 {
2482 Log2Func(("Stream disabled, skipping\n"));
2483 return VINF_SUCCESS;
2484 }
2485#endif
[88564]2486 Log4Func(("cbBuf=%#x stream '%s' {%s}\n", cbBuf, pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS) ));
[65565]2487
[88376]2488 /*
2489 * Read loop.
2490 */
2491 uint32_t cbRead = 0;
2492 while (cbBuf > 0)
2493 {
2494 /*
2495 * Figure out how much we can read.
2496 */
2497 DWORD offCaptureCursor = 0;
2498 DWORD offReadCursor = 0;
2499 HRESULT hrc = IDirectSoundCaptureBuffer_GetCurrentPosition(pStreamDS->In.pDSCB, &offCaptureCursor, &offReadCursor);
2500 AssertMsgReturn(SUCCEEDED(hrc), ("%Rhrc\n", hrc), VERR_ACCESS_DENIED); /** @todo translate these status codes already! */
2501 //AssertMsg(offReadCursor == pStreamDS->In.offReadPos, ("%#x %#x\n", offReadCursor, pStreamDS->In.offReadPos));
[54433]2502
[88376]2503 uint32_t const cbReadable = dsoundRingDistance(offCaptureCursor, pStreamDS->In.offReadPos, pStreamDS->cbBufSize);
[54433]2504
[88376]2505 if (cbReadable >= pStreamDS->Cfg.Props.cbFrame)
2506 { /* likely */ }
2507 else
2508 {
2509 if (cbRead > 0)
2510 { /* likely */ }
2511 else if (pStreamDS->In.cOverruns < 32)
2512 {
2513 pStreamDS->In.cOverruns++;
2514 DSLOG(("DSound: Warning: Buffer full (size is %zu bytes), skipping to record data (overflow #%RU32)\n",
2515 pStreamDS->cbBufSize, pStreamDS->In.cOverruns));
2516 }
2517 break;
2518 }
[54433]2519
[88376]2520 uint32_t const cbToRead = RT_MIN(cbReadable, cbBuf);
[88564]2521 Log3Func(("offCapture=%#x offRead=%#x/%#x -> cbWritable=%#x cbToWrite=%#x {%s}\n", offCaptureCursor, offReadCursor,
2522 pStreamDS->In.offReadPos, cbReadable, cbToRead, drvHostDSoundStreamStatusString(pStreamDS)));
[54433]2523
[88376]2524 /*
2525 * Lock that amount of buffer.
2526 */
2527 PVOID pv1 = NULL;
2528 DWORD cb1 = 0;
2529 PVOID pv2 = NULL;
2530 DWORD cb2 = 0;
2531 hrc = directSoundCaptureLock(pStreamDS, pStreamDS->In.offReadPos, cbToRead, &pv1, &pv2, &cb1, &cb2, 0 /*dwFlags*/);
2532 AssertMsgReturn(SUCCEEDED(hrc), ("%Rhrc\n", hrc), VERR_ACCESS_DENIED); /** @todo translate these status codes already! */
2533 AssertMsg(cb1 + cb2 == cbToRead, ("%#x + %#x vs %#x\n", cb1, cb2, cbToRead));
2534
2535 /*
2536 * Copy over the data.
2537 */
2538 memcpy(pvBuf, pv1, cb1);
2539 pvBuf = (uint8_t *)pvBuf + cb1;
2540 cbBuf -= cb1;
2541 cbRead += cb1;
2542
2543 if (pv2)
[58983]2544 {
[88376]2545 memcpy(pvBuf, pv2, cb2);
2546 pvBuf = (uint8_t *)pvBuf + cb2;
2547 cbBuf -= cb2;
2548 cbRead += cb2;
[58983]2549 }
[54433]2550
[88376]2551 /*
2552 * Unlock and update the write position.
2553 */
2554 directSoundCaptureUnlock(pStreamDS->In.pDSCB, pv1, pv2, cb1, cb2); /** @todo r=bird: pDSB parameter here for Unlock, but pStreamDS for Lock. Why? */
2555 pStreamDS->In.offReadPos = (pStreamDS->In.offReadPos + cb1 + cb2) % pStreamDS->cbBufSize;
[70925]2556 }
[54433]2557
[88376]2558 /*
2559 * Done.
2560 */
[88564]2561 *pcbRead = cbRead;
2562 if (cbRead)
2563 {
[89551]2564 uint64_t const msPrev = pStreamDS->msLastTransfer; RT_NOREF(msPrev);
[88564]2565 pStreamDS->msLastTransfer = RTTimeMilliTS();
2566 LogFlowFunc(("cbRead=%RU32, msLastTransfer=%RU64 msNow=%RU64 cMsDelta=%RU64 {%s}\n",
2567 cbRead, msPrev, pStreamDS->msLastTransfer, msPrev ? pStreamDS->msLastTransfer - msPrev : 0,
2568 drvHostDSoundStreamStatusString(pStreamDS) ));
2569 }
[88376]2570
2571 return VINF_SUCCESS;
[54433]2572}
2573
[70916]2574
[62910]2575/*********************************************************************************************************************************
2576* PDMDRVINS::IBase Interface *
2577*********************************************************************************************************************************/
2578
[62909]2579/**
[62910]2580 * @callback_method_impl{PDMIBASE,pfnQueryInterface}
2581 */
2582static DECLCALLBACK(void *) drvHostDSoundQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2583{
2584 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2585 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
2586
2587 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
2588 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
2589 return NULL;
2590}
2591
[70957]2592
[62910]2593/*********************************************************************************************************************************
2594* PDMDRVREG Interface *
2595*********************************************************************************************************************************/
2596
2597/**
[62909]2598 * @callback_method_impl{FNPDMDRVDESTRUCT, pfnDestruct}
2599 */
[54433]2600static DECLCALLBACK(void) drvHostDSoundDestruct(PPDMDRVINS pDrvIns)
2601{
2602 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
[57164]2603 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
[68680]2604
[54433]2605 LogFlowFuncEnter();
2606
[68680]2607#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
2608 if (pThis->m_pNotificationClient)
2609 {
[83240]2610 pThis->m_pNotificationClient->Unregister();
[68680]2611 pThis->m_pNotificationClient->Release();
2612
2613 pThis->m_pNotificationClient = NULL;
2614 }
2615#endif
2616
[88044]2617 PDMAudioHostEnumDelete(&pThis->DeviceEnum);
[77028]2618
[68683]2619 int rc2 = RTCritSectDelete(&pThis->CritSect);
2620 AssertRC(rc2);
2621
[54433]2622 LogFlowFuncLeave();
2623}
2624
[88514]2625
[91884]2626static LPCGUID dsoundConfigQueryGUID(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, const char *pszName, RTUUID *pUuid)
[88514]2627{
[91884]2628 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
[88514]2629 LPCGUID pGuid = NULL;
2630
2631 char *pszGuid = NULL;
[91884]2632 int rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, pszName, &pszGuid);
[88514]2633 if (RT_SUCCESS(rc))
2634 {
2635 rc = RTUuidFromStr(pUuid, pszGuid);
2636 if (RT_SUCCESS(rc))
[100141]2637 pGuid = (LPCGUID)pUuid;
[88514]2638 else
2639 DSLOGREL(("DSound: Error parsing device GUID for device '%s': %Rrc\n", pszName, rc));
2640
2641 RTStrFree(pszGuid);
2642 }
2643
2644 return pGuid;
2645}
2646
2647
2648static void dsoundConfigInit(PDRVHOSTDSOUND pThis, PCFGMNODE pCfg)
2649{
[91884]2650 pThis->Cfg.pGuidPlay = dsoundConfigQueryGUID(pThis->pDrvIns, pCfg, "DeviceGuidOut", &pThis->Cfg.uuidPlay);
2651 pThis->Cfg.pGuidCapture = dsoundConfigQueryGUID(pThis->pDrvIns, pCfg, "DeviceGuidIn", &pThis->Cfg.uuidCapture);
[88514]2652
2653 DSLOG(("DSound: Configuration: DeviceGuidOut {%RTuuid}, DeviceGuidIn {%RTuuid}\n",
2654 &pThis->Cfg.uuidPlay,
2655 &pThis->Cfg.uuidCapture));
2656}
2657
2658
[54433]2659/**
[62909]2660 * @callback_method_impl{FNPDMDRVCONSTRUCT,
2661 * Construct a DirectSound Audio driver instance.}
[54433]2662 */
2663static DECLCALLBACK(int) drvHostDSoundConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
2664{
[88381]2665 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2666 PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
[62909]2667 RT_NOREF(fFlags);
[57164]2668 LogRel(("Audio: Initializing DirectSound audio driver\n"));
[58071]2669
[54433]2670 /*
[58744]2671 * Init basic data members and interfaces.
[54433]2672 */
[88566]2673 RTListInit(&pThis->HeadStreams);
[58736]2674 pThis->pDrvIns = pDrvIns;
[54433]2675 /* IBase */
[58736]2676 pDrvIns->IBase.pfnQueryInterface = drvHostDSoundQueryInterface;
[54433]2677 /* IHostAudio */
[88761]2678 pThis->IHostAudio.pfnGetConfig = drvHostDSoundHA_GetConfig;
2679 pThis->IHostAudio.pfnGetDevices = drvHostDSoundHA_GetDevices;
[89258]2680 pThis->IHostAudio.pfnSetDevice = NULL;
[88761]2681 pThis->IHostAudio.pfnGetStatus = drvHostDSoundHA_GetStatus;
[88819]2682 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
2683 pThis->IHostAudio.pfnStreamConfigHint = NULL;
[88761]2684 pThis->IHostAudio.pfnStreamCreate = drvHostDSoundHA_StreamCreate;
[88819]2685 pThis->IHostAudio.pfnStreamInitAsync = NULL;
[88761]2686 pThis->IHostAudio.pfnStreamDestroy = drvHostDSoundHA_StreamDestroy;
2687 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
[89510]2688 pThis->IHostAudio.pfnStreamEnable = drvHostDSoundHA_StreamEnable;
2689 pThis->IHostAudio.pfnStreamDisable = drvHostDSoundHA_StreamDisable;
2690 pThis->IHostAudio.pfnStreamPause = drvHostDSoundHA_StreamPause;
2691 pThis->IHostAudio.pfnStreamResume = drvHostDSoundHA_StreamResume;
2692 pThis->IHostAudio.pfnStreamDrain = drvHostDSoundHA_StreamDrain;
[89504]2693 pThis->IHostAudio.pfnStreamGetState = drvHostDSoundHA_StreamGetState;
2694 pThis->IHostAudio.pfnStreamGetPending = NULL;
[88761]2695 pThis->IHostAudio.pfnStreamGetWritable = drvHostDSoundHA_StreamGetWritable;
2696 pThis->IHostAudio.pfnStreamPlay = drvHostDSoundHA_StreamPlay;
[89504]2697 pThis->IHostAudio.pfnStreamGetReadable = drvHostDSoundHA_StreamGetReadable;
[88761]2698 pThis->IHostAudio.pfnStreamCapture = drvHostDSoundHA_StreamCapture;
[68683]2699
[58744]2700 /*
2701 * Init the static parts.
2702 */
[88044]2703 PDMAudioHostEnumInit(&pThis->DeviceEnum);
[54433]2704
[58983]2705 pThis->fEnabledIn = false;
2706 pThis->fEnabledOut = false;
[58733]2707
[88381]2708 /*
2709 * Verify that IDirectSound is available.
2710 */
[88691]2711 LPDIRECTSOUND pDirectSound = NULL;
2712 HRESULT hrc = CoCreateInstance(CLSID_DirectSound, NULL, CLSCTX_ALL, IID_IDirectSound, (void **)&pDirectSound);
[88381]2713 if (SUCCEEDED(hrc))
[88691]2714 IDirectSound_Release(pDirectSound);
[88381]2715 else
2716 {
[88691]2717 LogRel(("DSound: DirectSound not available: %Rhrc\n", hrc));
[88381]2718 return VERR_AUDIO_BACKEND_INIT_FAILED;
2719 }
[68680]2720
[88381]2721#ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
2722 /*
2723 * Set up WASAPI device change notifications (Vista+).
2724 */
2725 if (RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
[68680]2726 {
[88381]2727 /* Get the notification interface (from DrvAudio). */
[88361]2728# ifdef VBOX_WITH_AUDIO_CALLBACKS
[88819]2729 PPDMIHOSTAUDIOPORT pIHostAudioPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIHOSTAUDIOPORT);
2730 Assert(pIHostAudioPort);
[88361]2731# else
[88819]2732 PPDMIHOSTAUDIOPORT pIHostAudioPort = NULL;
[88361]2733# endif
[96472]2734#ifdef RT_EXCEPTIONS_ENABLED
[70472]2735 try
[96472]2736#endif
[70472]2737 {
[88853]2738 pThis->m_pNotificationClient = new DrvHostAudioDSoundMMNotifClient(pIHostAudioPort,
2739 pThis->Cfg.pGuidCapture == NULL,
2740 pThis->Cfg.pGuidPlay == NULL);
[70472]2741 }
[96472]2742#ifdef RT_EXCEPTIONS_ENABLED
[83812]2743 catch (std::bad_alloc &)
[70472]2744 {
[88381]2745 return VERR_NO_MEMORY;
[70472]2746 }
[96472]2747#else
2748 AssertReturn(pThis->m_pNotificationClient, VERR_NO_MEMORY);
2749#endif
2750
[88381]2751 hrc = pThis->m_pNotificationClient->Initialize();
2752 if (SUCCEEDED(hrc))
2753 {
2754 hrc = pThis->m_pNotificationClient->Register();
2755 if (SUCCEEDED(hrc))
2756 LogRel2(("DSound: Notification client is enabled (ver %#RX64)\n", RTSystemGetNtVersion()));
2757 else
2758 {
2759 LogRel(("DSound: Notification client registration failed: %Rhrc\n", hrc));
2760 return VERR_AUDIO_BACKEND_INIT_FAILED;
2761 }
2762 }
2763 else
2764 {
2765 LogRel(("DSound: Notification client initialization failed: %Rhrc\n", hrc));
2766 return VERR_AUDIO_BACKEND_INIT_FAILED;
2767 }
[68680]2768 }
[88381]2769 else
2770 LogRel2(("DSound: Notification client is disabled (ver %#RX64)\n", RTSystemGetNtVersion()));
[68680]2771#endif
[100142]2772
[88381]2773 /*
2774 * Initialize configuration values and critical section.
2775 */
2776 dsoundConfigInit(pThis, pCfg);
2777 return RTCritSectInit(&pThis->CritSect);
[54433]2778}
2779
[63360]2780
[54433]2781/**
2782 * PDM driver registration.
2783 */
2784const PDMDRVREG g_DrvHostDSound =
2785{
2786 /* u32Version */
2787 PDM_DRVREG_VERSION,
2788 /* szName */
2789 "DSoundAudio",
2790 /* szRCMod */
2791 "",
2792 /* szR0Mod */
2793 "",
2794 /* pszDescription */
2795 "DirectSound Audio host driver",
2796 /* fFlags */
[59060]2797 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
[54433]2798 /* fClass. */
2799 PDM_DRVREG_CLASS_AUDIO,
2800 /* cMaxInstances */
2801 ~0U,
2802 /* cbInstance */
2803 sizeof(DRVHOSTDSOUND),
2804 /* pfnConstruct */
2805 drvHostDSoundConstruct,
2806 /* pfnDestruct */
2807 drvHostDSoundDestruct,
2808 /* pfnRelocate */
2809 NULL,
2810 /* pfnIOCtl */
2811 NULL,
2812 /* pfnPowerOn */
2813 NULL,
2814 /* pfnReset */
2815 NULL,
2816 /* pfnSuspend */
2817 NULL,
2818 /* pfnResume */
2819 NULL,
2820 /* pfnAttach */
2821 NULL,
2822 /* pfnDetach */
2823 NULL,
2824 /* pfnPowerOff */
2825 NULL,
2826 /* pfnSoftReset */
2827 NULL,
2828 /* u32EndVersion */
2829 PDM_DRVREG_VERSION
2830};
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use