DrvHostDSound.cpp revision 7da9e7e719adde3baba3f6fa1d0bcfb170cf9911
/* $Id$ */
/** @file
* Windows host backend driver using DirectSound.
*/
/*
* Copyright (C) 2006-2015 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
* --------------------------------------------------------------------
*
* This code is based on: dsoundaudio.c
*
* QEMU DirectSound audio driver
*
* Copyright (c) 2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <dsound.h>
#include "AudioMixBuffer.h"
#include "DrvAudio.h"
#include "VBoxDD.h"
#ifdef LOG_GROUP
#endif
#define LOG_GROUP LOG_GROUP_DEV_AUDIO
#define DSLOGREL(a) \
do { \
if (scLogged < 8) { \
++scLogged; \
LogRel(a); \
} \
else { \
DSLOG(a); \
} \
} while (0)
typedef struct DSOUNDHOSTCFG
{
int cbBufferIn;
int cbBufferOut;
typedef struct DRVHOSTDSOUND
{
/** Pointer to the driver instance structure. */
/** Pointer to host audio interface. */
/** List of found host input devices. */
/** List of found host output devices. */
/** Configuration options. */
typedef struct DSOUNDSTREAMOUT
{
bool fReinitPlayPos;
typedef struct DSOUNDSTREAMIN
{
/**
* Callback context for enumeration callbacks
*/
typedef struct DSOUNDENUMCBCTX
{
typedef struct DSOUNDDEV
{
char *pszName;
} DSOUNDDEV, *PDSOUNDDEV;
/** Makes DRVHOSTDSOUND out of PDMIHOSTAUDIO. */
#define PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface) \
{
}
{
{
case AUD_FMT_S8:
case AUD_FMT_U8:
break;
case AUD_FMT_S16:
case AUD_FMT_U16:
break;
case AUD_FMT_S32:
case AUD_FMT_U32:
break;
default:
return VERR_NOT_SUPPORTED;
}
return VINF_SUCCESS;
}
{
{
}
{
}
}
{
return VINF_SUCCESS;
return VERR_INVALID_STATE;
}
{
return VINF_SUCCESS;
return VERR_ACCESS_DENIED;
}
{
return VINF_SUCCESS;
return VERR_ACCESS_DENIED;
}
{
int rc = VINF_SUCCESS;
if (hr == DSERR_BUFFERLOST)
{
if (RT_SUCCESS(rc))
{
}
}
{
}
{
return VERR_INVALID_STATE;
}
return VINF_SUCCESS;
}
{
{
return VERR_ACCESS_DENIED;
}
{
return VERR_INVALID_PARAMETER;
}
return VINF_SUCCESS;
}
/*
* DirectSound playback
*/
{
if (pDSoundStrmOut->pDS)
{
}
}
{
{
DSLOG(("DSound: DirectSound instance already exists\n"));
return VINF_SUCCESS;
}
{
}
else
{
{
{
}
}
{
if (hr == DSERR_NODRIVER)
{
DSLOGREL(("DSound: DirectSound playback is currently unavailable\n"));
}
else
{
}
}
}
}
{
if (pDSoundStrmOut->pDSB)
{
{
}
}
}
{
DSLOG(("DSound: playback open %p size %d bytes, freq %d, chan %d, bits %d, sign %d\n",
{
/* Should not happen but be forgiving. */
DSLOGREL(("DSound: DirectSoundBuffer already exists\n"));
}
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
do /* To use breaks. */
{
{
break;
}
/* Query the actual parameters. */
{
break;
}
{
break;
}
DSLOG(("DSound: playback format: size %d bytes\n"
" tag = %d\n"
" nChannels = %d\n"
" nSamplesPerSec = %d\n"
" nAvgBytesPerSec = %d\n"
" nBlockAlign = %d\n"
" wBitsPerSample = %d\n"
" cbSize = %d\n",
{
DSLOGREL(("DSound: playback GetCaps returned misaligned buffer size %ld, alignment %d\n",
}
{
DSLOGREL(("DSound: playback buffer size mismatch dsound %d, requested %d bytes\n",
}
/* Initial state.
* playback buffer position.
*/
DSLOG(("DSound: playback open csPlaybackBufferSize %d samples\n", pDSoundStrmOut->csPlaybackBufferSize));
} while (0);
return VINF_SUCCESS;
return VERR_NOT_SUPPORTED;
}
{
if (RT_SUCCESS(rc))
{
}
}
{
int rc = VINF_SUCCESS;
{
if ((dwStatus & DSBSTATUS_BUFFERLOST) != 0)
{
if (RT_SUCCESS(rc))
{
}
}
}
{
if (RT_SUCCESS(rc))
{
}
}
if (RT_SUCCESS(rc))
{
}
return rc;
}
{
{
/* This performs some restore, so call it anyway and ignore result. */
DSLOG(("DSound: playback stop\n"));
{
}
}
}
{
int rc = VINF_SUCCESS;
{
if (RT_FAILURE(rc))
{
}
else
{
if (dwStatus & DSBSTATUS_PLAYING)
{
DSLOG(("DSound: already playing\n"));
}
else
{
pDSoundStrmOut->fReinitPlayPos = true;
DSLOG(("DSound: playback start\n"));
{
}
}
}
}
else
{
}
return rc;
}
/*
* DirectSoundCapture
*/
{
if (!pGUID)
{
switch (pDSoundStrmIn->enmRecSource)
{
case PDMAUDIORECSOURCE_MIC:
{
{
break;
}
} break;
default:
/* Try opening the default device (NULL). */
break;
}
if (pDev)
{
DSLOG(("DSound: Guest \"%s\" is using host \"%s\"\n",
}
}
return pGUID;
}
{
if (pDSoundStrmIn->pDSC)
{
}
}
{
{
DSLOG(("DSound: DirectSoundCapture instance already exists\n"));
return VINF_SUCCESS;
}
{
}
else
{
{
if (hr == DSERR_NODRIVER)
{
DSLOGREL(("DSound: DirectSound capture is currently unavailable\n"));
}
else
{
}
}
}
}
{
if (pDSoundStrmIn->pDSCB)
{
{
}
}
}
{
DSLOG(("DSound: capture open %p size %d bytes freq %d, chan %d, bits %d, sign %d\n",
{
/* Should not happen but be forgiving. */
DSLOGREL(("DSound: DirectSoundCaptureBuffer already exists\n"));
}
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
do /* To use breaks. */
{
{
break;
}
/* Query the actual parameters. */
{
cbReadPos = 0;
}
{
break;
}
{
break;
}
DSLOG(("DSound: capture buffer format: size %d bytes\n"
" tag = %d\n"
" nChannels = %d\n"
" nSamplesPerSec = %d\n"
" nAvgBytesPerSec = %d\n"
" nBlockAlign = %d\n"
" wBitsPerSample = %d\n"
" cbSize = %d\n",
{
DSLOGREL(("DSound: GetCaps returned misaligned buffer size %ld, alignment %d\n",
}
{
DSLOGREL(("DSound: buffer size mismatch dsound %d, requested %d bytes\n",
}
/* Initial state: reading at the initial capture position. */
DSLOG(("DSound: capture open csCaptureReadPos %d, csCaptureBufferSize %d samples\n",
} while (0);
return VINF_SUCCESS;
return VERR_NOT_SUPPORTED;
}
{
if (pDSoundStrmIn->pDSCB)
{
DSLOG(("DSound: capture stop\n"));
{
}
}
}
{
{
{
}
else
{
if (dwStatus & DSCBSTATUS_CAPTURING)
{
DSLOG(("DSound: already capturing\n"));
}
else
{
DSLOG(("DSound: capture start\n"));
{
}
}
}
}
else
{
}
}
{
if (!pDev)
return VERR_NO_MEMORY;
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
if (ppDev)
return rc;
}
{
if (pDev)
{
}
}
{
{
char *pszGUID;
}
return NULL;
}
static void dsoundLogDevice(const char *pszType, LPGUID lpGUID, LPCWSTR lpwstrDescription, LPCWSTR lpwstrModule)
{
DSLOG(("DSound: %s: GUID: %s [%ls] (Module: %ls)\n",
}
{
if (!lpGUID)
return TRUE;
if (RT_FAILURE(rc))
return FALSE; /* Abort enumeration. */
return TRUE;
}
{
if (!lpGUID)
return TRUE;
if (RT_FAILURE(rc))
return FALSE; /* Abort enumeration. */
return TRUE;
}
/*
* PDMIHOSTAUDIO
*/
{
if (RT_SUCCESS(rc))
{
pDSoundStrmOut->cbPlayWritePos = 0;
pDSoundStrmOut->fReinitPlayPos = true;
if (pcSamples)
/* Try to open playback in case the device is already there. */
}
else
{
}
return rc;
}
{
int rc = VINF_SUCCESS;
switch (enmStreamCmd)
{
case PDMAUDIOSTREAMCMD_ENABLE:
{
/* Try to start playback. If it fails, then reopen and try again. */
if (RT_FAILURE(rc))
{
}
} break;
{
} break;
default:
{
} break;
}
return rc;
}
static DECLCALLBACK(int) drvHostDSoundPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
{
int rc = VINF_SUCCESS;
if (!pDSB)
{
if (pcSamplesPlayed) /** @todo single point of return */
*pcSamplesPlayed = 0;
return VINF_SUCCESS;
}
if (hr == DSERR_BUFFERLOST)
{
if (RT_FAILURE(rc))
{
if (pcSamplesPlayed)
*pcSamplesPlayed = 0;
return VINF_SUCCESS;
}
if (hr == DSERR_BUFFERLOST)
{
/* Avoid log flooding if the error is still there. */
if (pcSamplesPlayed)
*pcSamplesPlayed = 0;
return VINF_SUCCESS;
}
}
{
if (pcSamplesPlayed)
*pcSamplesPlayed = 0;
return VINF_SUCCESS;
}
int cbFree;
if (pDSoundStrmOut->fReinitPlayPos)
{
pDSoundStrmOut->fReinitPlayPos = false;
}
else
{
{
/* Full buffer. */
if (pcSamplesPlayed)
*pcSamplesPlayed = 0;
return VINF_SUCCESS;
}
}
/* Do not write more than available space in the DirectSound playback buffer. */
{
DSLOG(("DSound: cbLive=%d cbBuffer=%d cbPlayWritePos=%d cbPlayPos=%d\n",
if (pcSamplesPlayed)
*pcSamplesPlayed = 0;
return VINF_SUCCESS;
}
if (RT_FAILURE(rc))
{
if (pcSamplesPlayed)
*pcSamplesPlayed = 0;
return VINF_SUCCESS;
}
uint32_t cReadTotal = 0;
{
if (RT_SUCCESS(rc))
cReadTotal += cRead;
}
if ( RT_SUCCESS(rc)
&& cReadTotal == len1
{
if (RT_SUCCESS(rc))
cReadTotal += cRead;
}
LogFlow(("DSound: PlayOut %RU32 (%RU32 samples) out of %d%s, ds write pos %d -> %d, rc=%Rrc\n",
if (cReadTotal)
{
}
if (pcSamplesPlayed)
return rc;
}
static DECLCALLBACK(int) drvHostDSoundFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
{
pDSoundStrmOut->cbPlayWritePos = 0;
pDSoundStrmOut->fReinitPlayPos = true;
return VINF_SUCCESS;
}
{
LogFlowFunc(("pHstStrmIn=%p, pAudioSettings=%p, enmRecSource=%ld\n",
/** @todo caller should already init Props? */
if (RT_SUCCESS(rc))
{
/* Init the stream structure and save relevant information to it. */
if (pcSamples)
/* Try to open capture in case the device is already there. */
}
else
{
}
return rc;
}
static DECLCALLBACK(int) drvHostDSoundControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
{
int rc = VINF_SUCCESS;
switch (enmStreamCmd)
{
case PDMAUDIOSTREAMCMD_ENABLE:
{
/* Try to start capture. If it fails, then reopen and try again. */
if (RT_FAILURE(rc))
{
}
} break;
{
} break;
default:
{
} break;
}
return rc;
}
static DECLCALLBACK(int) drvHostDSoundCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
{
int rc;
{
if (pcSamplesCaptured) /** @todo single point of return */
*pcSamplesCaptured = 0;
return VINF_SUCCESS;
}
/* Get DirectSound capture position in bytes. */
{
{
}
if (pcSamplesCaptured)
*pcSamplesCaptured = 0;
return VINF_SUCCESS;
}
{
DSLOG(("DSound: CaptureIn misaligned read position %d(%d)\n", cbReadPos, pHstStrmIn->Props.uAlign));
}
/* Capture position in samples. */
/* Number of samples available in the DirectSound capture buffer. */
DWORD csCaptured = dsoundRingDistance(csReadPos, pDSoundStrmIn->csCaptureReadPos, pDSoundStrmIn->csCaptureBufferSize);
if (csCaptured == 0)
{
if (pcSamplesCaptured)
*pcSamplesCaptured = 0;
return VINF_SUCCESS;
}
/* Using as an intermediate not circular buffer. */
/* Get number of free samples in the mix buffer and check that is has free space */
if (csMixFree == 0)
{
DSLOG(("DSound: capture mix buffer full\n"));
if (pcSamplesCaptured)
*pcSamplesCaptured = 0;
return VINF_SUCCESS;
}
LogFlow(("DSound: CaptureIn csMixFree = %d, csReadPos = %d, csCaptureReadPos = %d, csCaptured = %d\n",
/* No need to fetch more samples than mix buffer can receive. */
/* Lock relevant range in the DirectSound capture buffer. */
0 /* dwFlags */);
if (RT_FAILURE(rc))
{
if (pcSamplesCaptured)
*pcSamplesCaptured = 0;
return VINF_SUCCESS;
}
uint32_t csWrittenTotal = 0;
{
if (RT_SUCCESS(rc))
}
if ( RT_SUCCESS(rc)
&& csWrittenTotal == len1
{
if (RT_SUCCESS(rc))
}
uint32_t csProcessed = 0;
if (csWrittenTotal != 0)
{
/* Captured something. */
&csProcessed);
}
if (RT_SUCCESS(rc))
{
pDSoundStrmIn->csCaptureReadPos = (pDSoundStrmIn->csCaptureReadPos + csProcessed) % pDSoundStrmIn->csCaptureBufferSize;
LogFlow(("DSound: CaptureIn %d (%d+%d), processed %d/%d\n",
}
if (pcSamplesCaptured)
return rc;
}
static DECLCALLBACK(int) drvHostDSoundFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
{
return VINF_SUCCESS;
}
{
return true; /* Always all enabled. */
}
{
pCfg->cMaxHstStrmsOut = 0;
pCfg->cMaxHstStrmsIn = 0;
if (pCfg->cMaxHstStrmsOut == 0)
if (pCfg->cMaxHstStrmsIn == 0)
return VINF_SUCCESS;
}
{
/* Verify that IDirectSound is available. */
IID_IDirectSound, (void **)&pDirectSound);
else
return rc;
}
/*
* PDMIBASE
*/
{
return NULL;
}
/*
* PDM driver.
*/
{
if (RT_SUCCESS(rc))
{
if (pszString)
{
if (RT_SUCCESS(rc))
*ppszString = pszString;
else
}
else
rc = VERR_NO_MEMORY;
}
return rc;
}
{
if (pszGuid)
{
if (RT_SUCCESS(rc))
else
}
return pGuid;
}
{
}
{
}
/**
* Construct a DirectSound Audio driver instance.
*
* @copydoc FNPDMDRVCONSTRUCT
*/
static DECLCALLBACK(int) drvHostDSoundConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{
DSLOGREL(("Audio: Initializing DirectSound audio driver\n"));
{
return VERR_NOT_SUPPORTED;
}
/*
* Init the static parts.
*/
/* IBase */
/* IHostAudio */
/*
* Initialize configuration values.
*/
return VINF_SUCCESS;
}
/**
* PDM driver registration.
*/
const PDMDRVREG g_DrvHostDSound =
{
/* u32Version */
/* szName */
"DSoundAudio",
/* szRCMod */
"",
/* szR0Mod */
"",
/* pszDescription */
"DirectSound Audio host driver",
/* fFlags */
/* fClass. */
/* cMaxInstances */
~0U,
/* cbInstance */
sizeof(DRVHOSTDSOUND),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
NULL,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32EndVersion */
};