diff options
Diffstat (limited to '')
-rw-r--r-- | include/VBox/vmm/pdmaudioinline.h | 901 |
1 files changed, 901 insertions, 0 deletions
diff --git a/include/VBox/vmm/pdmaudioinline.h b/include/VBox/vmm/pdmaudioinline.h new file mode 100644 index 00000000..7fcec860 --- /dev/null +++ b/include/VBox/vmm/pdmaudioinline.h @@ -0,0 +1,901 @@ +/* $Id: pdmaudioinline.h $ */ +/** @file + * PDM - Audio Helpers, Inlined Code. (DEV,++) + * + * This is all inlined because it's too tedious to create a couple libraries to + * contain it all (same bad excuse as for intnetinline.h & pdmnetinline.h). + */ + +/* + * Copyright (C) 2006-2020 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#ifndef VBOX_INCLUDED_vmm_pdmaudioinline_h +#define VBOX_INCLUDED_vmm_pdmaudioinline_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <VBox/err.h> +#include <VBox/log.h> +#include <VBox/vmm/pdmaudioifs.h> + +#include <iprt/asm.h> +#include <iprt/asm-math.h> +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/string.h> + + +/** @defgroup grp_pdm_audio_inline The PDM Audio Helper APIs + * @ingroup grp_pdm + * @{ + */ + +/* Fix later: */ +DECLINLINE(bool) PDMAudioPropsAreValid(PCPDMAUDIOPCMPROPS pProps); +DECLINLINE(bool) PDMAudioPropsAreEqual(PCPDMAUDIOPCMPROPS pProps1, PCPDMAUDIOPCMPROPS pProps2); + + + +/** + * Gets the name of an audio direction enum value. + * + * @returns Pointer to read-only name string on success, "bad" if + * passed an invalid enum value. + * @param enmDir The audio direction value to name. + */ +DECLINLINE(const char *) PDMAudioDirGetName(PDMAUDIODIR enmDir) +{ + switch (enmDir) + { + case PDMAUDIODIR_UNKNOWN: return "Unknown"; + case PDMAUDIODIR_IN: return "Input"; + case PDMAUDIODIR_OUT: return "Output"; + case PDMAUDIODIR_DUPLEX: return "Duplex"; + + /* no default */ + case PDMAUDIODIR_END: + case PDMAUDIODIR_INVALID: + case PDMAUDIODIR_32BIT_HACK: + break; + } + AssertMsgFailedReturn(("Invalid audio direction %d\n", enmDir), "bad"); +} + +/** + * Gets the name of an audio mixer control enum value. + * + * @returns Pointer to read-only name, "bad" if invalid input. + * @param enmMixerCtl The audio mixer control value. + */ +DECLINLINE(const char *) PDMAudioMixerCtlGetName(PDMAUDIOMIXERCTL enmMixerCtl) +{ + switch (enmMixerCtl) + { + case PDMAUDIOMIXERCTL_UNKNOWN: return "Unknown"; + case PDMAUDIOMIXERCTL_VOLUME_MASTER: return "Master Volume"; + case PDMAUDIOMIXERCTL_FRONT: return "Front"; + case PDMAUDIOMIXERCTL_CENTER_LFE: return "Center / LFE"; + case PDMAUDIOMIXERCTL_REAR: return "Rear"; + case PDMAUDIOMIXERCTL_LINE_IN: return "Line-In"; + case PDMAUDIOMIXERCTL_MIC_IN: return "Microphone-In"; + /* no default */ + case PDMAUDIOMIXERCTL_END: + case PDMAUDIOMIXERCTL_INVALID: + case PDMAUDIOMIXERCTL_32BIT_HACK: + break; + } + AssertMsgFailedReturn(("Invalid mixer control %ld\n", enmMixerCtl), "bad"); +} + +/** + * Gets the name of a playback destination enum value. + * + * @returns Pointer to read-only name, "bad" if invalid input. + * @param enmPlaybackDst The playback destination value. + */ +DECLINLINE(const char *) PDMAudioPlaybackDstGetName(PDMAUDIOPLAYBACKDST enmPlaybackDst) +{ + switch (enmPlaybackDst) + { + case PDMAUDIOPLAYBACKDST_UNKNOWN: return "Unknown"; + case PDMAUDIOPLAYBACKDST_FRONT: return "Front"; + case PDMAUDIOPLAYBACKDST_CENTER_LFE: return "Center / LFE"; + case PDMAUDIOPLAYBACKDST_REAR: return "Rear"; + /* no default */ + case PDMAUDIOPLAYBACKDST_INVALID: + case PDMAUDIOPLAYBACKDST_END: + case PDMAUDIOPLAYBACKDST_32BIT_HACK: + break; + } + AssertMsgFailedReturn(("Invalid playback destination %ld\n", enmPlaybackDst), "bad"); +} + +/** + * Gets the name of a recording source enum value. + * + * @returns Pointer to read-only name, "bad" if invalid input. + * @param enmRecSrc The recording source value. + */ +DECLINLINE(const char *) PDMAudioRecSrcGetName(PDMAUDIORECSRC enmRecSrc) +{ + switch (enmRecSrc) + { + case PDMAUDIORECSRC_UNKNOWN: return "Unknown"; + case PDMAUDIORECSRC_MIC: return "Microphone In"; + case PDMAUDIORECSRC_CD: return "CD"; + case PDMAUDIORECSRC_VIDEO: return "Video"; + case PDMAUDIORECSRC_AUX: return "AUX"; + case PDMAUDIORECSRC_LINE: return "Line In"; + case PDMAUDIORECSRC_PHONE: return "Phone"; + /* no default */ + case PDMAUDIORECSRC_END: + case PDMAUDIORECSRC_32BIT_HACK: + break; + } + AssertMsgFailedReturn(("Invalid recording source %ld\n", enmRecSrc), "bad"); +} + +/** + * Checks whether the audio format is signed. + * + * @returns @c true for signed format, @c false for unsigned. + * @param enmFmt The audio format. + */ +DECLINLINE(bool) PDMAudioFormatIsSigned(PDMAUDIOFMT enmFmt) +{ + switch (enmFmt) + { + case PDMAUDIOFMT_S8: + case PDMAUDIOFMT_S16: + case PDMAUDIOFMT_S32: + return true; + + case PDMAUDIOFMT_U8: + case PDMAUDIOFMT_U16: + case PDMAUDIOFMT_U32: + return false; + + /* no default */ + case PDMAUDIOFMT_INVALID: + case PDMAUDIOFMT_END: + case PDMAUDIOFMT_32BIT_HACK: + break; + } + AssertMsgFailedReturn(("Bogus audio format %ld\n", enmFmt), false); +} + +/** + * Gets the encoding width in bits of the give audio format. + * + * @returns Bit count. 0 if invalid input. + * @param enmFmt The audio format. + */ +DECLINLINE(uint8_t) PDMAudioFormatGetBits(PDMAUDIOFMT enmFmt) +{ + switch (enmFmt) + { + case PDMAUDIOFMT_S8: + case PDMAUDIOFMT_U8: + return 8; + + case PDMAUDIOFMT_U16: + case PDMAUDIOFMT_S16: + return 16; + + case PDMAUDIOFMT_U32: + case PDMAUDIOFMT_S32: + return 32; + + /* no default */ + case PDMAUDIOFMT_INVALID: + case PDMAUDIOFMT_END: + case PDMAUDIOFMT_32BIT_HACK: + break; + } + AssertMsgFailedReturn(("Bogus audio format %ld\n", enmFmt), 0); +} + +/** + * Gets the name of an audio format enum value. + * + * @returns Pointer to read-only name on success, returns "bad" on if + * invalid enum value. + * @param enmFmt The audio format to name. + */ +DECLINLINE(const char *) PDMAudioFormatGetName(PDMAUDIOFMT enmFmt) +{ + switch (enmFmt) + { + case PDMAUDIOFMT_U8: return "U8"; + case PDMAUDIOFMT_U16: return "U16"; + case PDMAUDIOFMT_U32: return "U32"; + case PDMAUDIOFMT_S8: return "S8"; + case PDMAUDIOFMT_S16: return "S16"; + case PDMAUDIOFMT_S32: return "S32"; + /* no default */ + case PDMAUDIOFMT_INVALID: + case PDMAUDIOFMT_END: + case PDMAUDIOFMT_32BIT_HACK: + break; + } + AssertMsgFailedReturn(("Bogus audio format %d\n", enmFmt), "bad"); +} + +/** + * Initializes a stream configuration from PCM properties. + * + * @return IPRT status code. + * @param pCfg The stream configuration to initialize. + * @param pProps The PCM properties to use. + */ +DECLINLINE(int) PDMAudioStrmCfgInitWithProps(PPDMAUDIOSTREAMCFG pCfg, PCPDMAUDIOPCMPROPS pProps) +{ + AssertPtrReturn(pProps, VERR_INVALID_POINTER); + AssertPtrReturn(pCfg, VERR_INVALID_POINTER); + + RT_ZERO(*pCfg); + pCfg->Backend.cFramesPreBuffering = UINT32_MAX; /* Explicitly set to "undefined". */ + + memcpy(&pCfg->Props, pProps, sizeof(PDMAUDIOPCMPROPS)); + + return VINF_SUCCESS; +} + +/** + * Checks whether stream configuration matches the given PCM properties. + * + * @returns @c true if equal, @c false if not. + * @param pCfg The stream configuration. + * @param pProps The PCM properties to match with. + */ +DECLINLINE(bool) PDMAudioStrmCfgMatchesProps(PCPDMAUDIOSTREAMCFG pCfg, PCPDMAUDIOPCMPROPS pProps) +{ + AssertPtrReturn(pCfg, false); + return PDMAudioPropsAreEqual(pProps, &pCfg->Props); +} + +/** + * Frees an audio stream allocated by PDMAudioStrmCfgDup(). + * + * @param pCfg The stream configuration to free. + */ +DECLINLINE(void) PDMAudioStrmCfgFree(PPDMAUDIOSTREAMCFG pCfg) +{ + if (pCfg) + RTMemFree(pCfg); +} + +/** + * Checks whether the given stream configuration is valid or not. + * + * @returns true/false accordingly. + * @param pCfg Stream configuration to check. + * + * @remarks This just performs a generic check of value ranges. Further, it + * will assert if the input is invalid. + * + * @sa PDMAudioPropsAreValid + */ +DECLINLINE(bool) PDMAudioStrmCfgIsValid(PCPDMAUDIOSTREAMCFG pCfg) +{ + AssertPtrReturn(pCfg, false); + AssertMsgReturn(pCfg->enmDir >= PDMAUDIODIR_UNKNOWN && pCfg->enmDir < PDMAUDIODIR_END, + ("%d\n", pCfg->enmDir), false); + AssertMsgReturn(pCfg->enmLayout >= PDMAUDIOSTREAMLAYOUT_UNKNOWN && pCfg->enmLayout < PDMAUDIOSTREAMLAYOUT_END, + ("%d\n", pCfg->enmLayout), false); + return PDMAudioPropsAreValid(&pCfg->Props); +} + +/** + * Copies one stream configuration to another. + * + * @returns IPRT status code. + * @param pDstCfg The destination stream configuration. + * @param pSrcCfg The source stream configuration. + */ +DECLINLINE(int) PDMAudioStrmCfgCopy(PPDMAUDIOSTREAMCFG pDstCfg, PCPDMAUDIOSTREAMCFG pSrcCfg) +{ + AssertPtrReturn(pDstCfg, VERR_INVALID_POINTER); + AssertPtrReturn(pSrcCfg, VERR_INVALID_POINTER); + + /* This used to be VBOX_STRICT only and return VERR_INVALID_PARAMETER, but + that's making release builds work differently from debug & strict builds, + which is a terrible idea: */ + Assert(PDMAudioStrmCfgIsValid(pSrcCfg)); + + memcpy(pDstCfg, pSrcCfg, sizeof(PDMAUDIOSTREAMCFG)); + + return VINF_SUCCESS; +} + +/** + * Duplicates an audio stream configuration. + * + * @returns Pointer to duplicate on success, NULL on failure. Must be freed + * using PDMAudioStrmCfgFree(). + * + * @param pCfg The audio stream configuration to duplicate. + */ +DECLINLINE(PPDMAUDIOSTREAMCFG) PDMAudioStrmCfgDup(PCPDMAUDIOSTREAMCFG pCfg) +{ + AssertPtrReturn(pCfg, NULL); + + PPDMAUDIOSTREAMCFG pDst = (PPDMAUDIOSTREAMCFG)RTMemAllocZ(sizeof(PDMAUDIOSTREAMCFG)); + if (pDst) + { + int rc = PDMAudioStrmCfgCopy(pDst, pCfg); + if (RT_SUCCESS(rc)) + return pDst; + + PDMAudioStrmCfgFree(pDst); + } + return NULL; +} + +/** + * Logs an audio stream configuration. + * + * @param pCfg The stream configuration to log. + */ +DECLINLINE(void) PDMAudioStrmCfgLog(PCPDMAUDIOSTREAMCFG pCfg) +{ + if (pCfg) + LogFunc(("szName=%s enmDir=%RU32 uHz=%RU32 cBits=%RU8%s cChannels=%RU8\n", pCfg->szName, pCfg->enmDir, + pCfg->Props.uHz, pCfg->Props.cbSample * 8, pCfg->Props.fSigned ? "S" : "U", pCfg->Props.cChannels)); +} + +/** + * Converts a stream command enum value to a string. + * + * @returns Pointer to read-only stream command name on success, + * "bad" if invalid command value. + * @param enmCmd The stream command to name. + */ +DECLINLINE(const char *) PDMAudioStrmCmdGetName(PDMAUDIOSTREAMCMD enmCmd) +{ + switch (enmCmd) + { + case PDMAUDIOSTREAMCMD_INVALID: return "Invalid"; + case PDMAUDIOSTREAMCMD_UNKNOWN: return "Unknown"; + case PDMAUDIOSTREAMCMD_ENABLE: return "Enable"; + case PDMAUDIOSTREAMCMD_DISABLE: return "Disable"; + case PDMAUDIOSTREAMCMD_PAUSE: return "Pause"; + case PDMAUDIOSTREAMCMD_RESUME: return "Resume"; + case PDMAUDIOSTREAMCMD_DRAIN: return "Drain"; + case PDMAUDIOSTREAMCMD_DROP: return "Drop"; + case PDMAUDIOSTREAMCMD_END: + case PDMAUDIOSTREAMCMD_32BIT_HACK: + break; + /* no default! */ + } + AssertMsgFailedReturn(("Invalid stream command %d\n", enmCmd), "bad"); +} + +/** + * Checks if the stream status is one that can be read from. + * + * @returns @c true if ready to be read from, @c false if not. + * @param fStatus Stream status to evaluate, PDMAUDIOSTREAMSTS_FLAGS_XXX. + */ +DECLINLINE(bool) PDMAudioStrmStatusCanRead(PDMAUDIOSTREAMSTS fStatus) +{ + AssertReturn(fStatus & PDMAUDIOSTREAMSTS_VALID_MASK, false); + /* + return fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED + && fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED + && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PAUSED) + && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT);*/ + return (fStatus & ( PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED + | PDMAUDIOSTREAMSTS_FLAGS_ENABLED + | PDMAUDIOSTREAMSTS_FLAGS_PAUSED + | PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT )) + == ( PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED + | PDMAUDIOSTREAMSTS_FLAGS_ENABLED); +} + +/** + * Checks if the stream status is one that can be written to. + * + * @returns @c true if ready to be written to, @c false if not. + * @param fStatus Stream status to evaluate, PDMAUDIOSTREAMSTS_FLAGS_XXX. + */ +DECLINLINE(bool) PDMAudioStrmStatusCanWrite(PDMAUDIOSTREAMSTS fStatus) +{ + AssertReturn(fStatus & PDMAUDIOSTREAMSTS_VALID_MASK, false); + /* + return fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED + && fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED + && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PAUSED) + && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE) + && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT);*/ + return (fStatus & ( PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED + | PDMAUDIOSTREAMSTS_FLAGS_ENABLED + | PDMAUDIOSTREAMSTS_FLAGS_PAUSED + | PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE + | PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT)) + == ( PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED + | PDMAUDIOSTREAMSTS_FLAGS_ENABLED); +} + +/** + * Checks if the stream status is a read-to-operate one. + * + * @returns @c true if ready to operate, @c false if not. + * @param fStatus Stream status to evaluate, PDMAUDIOSTREAMSTS_FLAGS_XXX. + */ +DECLINLINE(bool) PDMAudioStrmStatusIsReady(PDMAUDIOSTREAMSTS fStatus) +{ + AssertReturn(fStatus & PDMAUDIOSTREAMSTS_VALID_MASK, false); + /* + return fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED + && fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED + && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT);*/ + return (fStatus & ( PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED + | PDMAUDIOSTREAMSTS_FLAGS_ENABLED + | PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT)) + == ( PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED + | PDMAUDIOSTREAMSTS_FLAGS_ENABLED); +} + + +/********************************************************************************************************************************* +* PCM Property Helpers * +*********************************************************************************************************************************/ + +/** + * Gets the bitrate. + * + * Divide the result by 8 to get the byte rate. + * + * @returns Bit rate. + * @param pProps PCM properties to calculate bitrate for. + */ +DECLINLINE(uint32_t) PDMAudioPropsGetBitrate(PCPDMAUDIOPCMPROPS pProps) +{ + return pProps->cbSample * pProps->cChannels * pProps->uHz * 8; +} + +/** + * Rounds down the given byte amount to the nearest frame boundrary. + * + * @returns Rounded byte amount. + * @param pProps PCM properties to use. + * @param cb The size (in bytes) to round. + */ +DECLINLINE(uint32_t) PDMAudioPropsFloorBytesToFrame(PCPDMAUDIOPCMPROPS pProps, uint32_t cb) +{ + AssertPtrReturn(pProps, 0); + return PDMAUDIOPCMPROPS_F2B(pProps, PDMAUDIOPCMPROPS_B2F(pProps, cb)); +} + +/** + * Rounds up the given byte amount to the nearest frame boundrary. + * + * @returns Rounded byte amount. + * @param pProps PCM properties to use. + * @param cb The size (in bytes) to round. + */ +DECLINLINE(uint32_t) PDMAudioPropsRoundUpBytesToFrame(PCPDMAUDIOPCMPROPS pProps, uint32_t cb) +{ + AssertPtrReturn(pProps, 0); + uint32_t const cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */); + AssertReturn(cbFrame, 0); + return PDMAUDIOPCMPROPS_F2B(pProps, PDMAUDIOPCMPROPS_B2F(pProps, cb + cbFrame - 1)); +} + +/** + * Checks if the given size is aligned on a frame boundrary. + * + * @returns @c true if properly aligned, @c false if not. + * @param pProps PCM properties to use. + * @param cb The size (in bytes) to check. + */ +DECLINLINE(bool) PDMAudioPropsIsSizeAligned(PCPDMAUDIOPCMPROPS pProps, uint32_t cb) +{ + AssertPtrReturn(pProps, false); + uint32_t const cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */); + AssertReturn(cbFrame, false); + return cb % cbFrame == 0; +} + +/** + * Converts bytes to frames (rounding down of course). + * + * @returns Number of frames. + * @param pProps PCM properties to use. + * @param cb The number of bytes to convert. + */ +DECLINLINE(uint32_t) PDMAudioPropsBytesToFrames(PCPDMAUDIOPCMPROPS pProps, uint32_t cb) +{ + AssertPtrReturn(pProps, 0); + return PDMAUDIOPCMPROPS_B2F(pProps, cb); +} + +/** + * Converts bytes to milliseconds. + * + * @return Number milliseconds @a cb takes to play or record. + * @param pProps PCM properties to use. + * @param cb The number of bytes to convert. + * + * @note Rounds up the result. + */ +DECLINLINE(uint64_t) PDMAudioPropsBytesToMilli(PCPDMAUDIOPCMPROPS pProps, uint32_t cb) +{ + AssertPtrReturn(pProps, 0); + + /* Check parameters to prevent division by chainsaw: */ + uint32_t const uHz = pProps->uHz; + if (uHz) + { + const unsigned cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */); + if (cbFrame) + { + /* Round cb up to closest frame size: */ + cb = (cb + cbFrame - 1) / cbFrame; + + /* Convert to milliseconds. */ + return (cb * (uint64_t)RT_MS_1SEC + uHz - 1) / uHz; + } + } + return 0; +} + +/** + * Converts bytes to microseconds. + * + * @return Number microseconds @a cb takes to play or record. + * @param pProps PCM properties to use. + * @param cb The number of bytes to convert. + * + * @note Rounds up the result. + */ +DECLINLINE(uint64_t) PDMAudioPropsBytesToMicro(PCPDMAUDIOPCMPROPS pProps, uint32_t cb) +{ + AssertPtrReturn(pProps, 0); + + /* Check parameters to prevent division by chainsaw: */ + uint32_t const uHz = pProps->uHz; + if (uHz) + { + const unsigned cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */); + if (cbFrame) + { + /* Round cb up to closest frame size: */ + cb = (cb + cbFrame - 1) / cbFrame; + + /* Convert to microseconds. */ + return (cb * (uint64_t)RT_US_1SEC + uHz - 1) / uHz; + } + } + return 0; +} + +/** + * Converts bytes to nanoseconds. + * + * @return Number nanoseconds @a cb takes to play or record. + * @param pProps PCM properties to use. + * @param cb The number of bytes to convert. + * + * @note Rounds up the result. + */ +DECLINLINE(uint64_t) PDMAudioPropsBytesToNano(PCPDMAUDIOPCMPROPS pProps, uint32_t cb) +{ + AssertPtrReturn(pProps, 0); + + /* Check parameters to prevent division by chainsaw: */ + uint32_t const uHz = pProps->uHz; + if (uHz) + { + const unsigned cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */); + if (cbFrame) + { + /* Round cb up to closest frame size: */ + cb = (cb + cbFrame - 1) / cbFrame; + + /* Convert to nanoseconds. */ + return (cb * (uint64_t)RT_NS_1SEC + uHz - 1) / uHz; + } + } + return 0; +} + +/** + * Converts frames to bytes. + * + * @returns Number of bytes. + * @param pProps The PCM properties to use. + * @param cFrames Number of audio frames to convert. + * @sa PDMAUDIOPCMPROPS_F2B + */ +DECLINLINE(uint32_t) PDMAudioPropsFramesToBytes(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames) +{ + AssertPtrReturn(pProps, 0); + return PDMAUDIOPCMPROPS_F2B(pProps, cFrames); +} + +/** + * Converts frames to milliseconds. + * + * @returns milliseconds. + * @param pProps The PCM properties to use. + * @param cFrames Number of audio frames to convert. + * @note No rounding here, result is floored. + */ +DECLINLINE(uint64_t) PDMAudioPropsFramesToMilli(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames) +{ + AssertPtrReturn(pProps, 0); + + /* Check input to prevent division by chainsaw: */ + uint32_t const uHz = pProps->uHz; + if (uHz) + return ASMMultU32ByU32DivByU32(cFrames, RT_MS_1SEC, uHz); + return 0; +} + +/** + * Converts frames to nanoseconds. + * + * @returns Nanoseconds. + * @param pProps The PCM properties to use. + * @param cFrames Number of audio frames to convert. + * @note No rounding here, result is floored. + */ +DECLINLINE(uint64_t) PDMAudioPropsFramesToNano(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames) +{ + AssertPtrReturn(pProps, 0); + + /* Check input to prevent division by chainsaw: */ + uint32_t const uHz = pProps->uHz; + if (uHz) + return ASMMultU32ByU32DivByU32(cFrames, RT_NS_1SEC, uHz); + return 0; +} + +/** + * Converts milliseconds to frames. + * + * @returns Number of frames + * @param pProps The PCM properties to use. + * @param cMs The number of milliseconds to convert. + * + * @note The result is rounded rather than floored (hysterical raisins). + */ +DECLINLINE(uint32_t) PDMAudioPropsMilliToFrames(PCPDMAUDIOPCMPROPS pProps, uint64_t cMs) +{ + AssertPtrReturn(pProps, 0); + + uint32_t const uHz = pProps->uHz; + uint32_t cFrames; + if (cMs < RT_MS_1SEC) + cFrames = 0; + else + { + cFrames = cMs / RT_MS_1SEC * uHz; + cMs %= RT_MS_1SEC; + } + cFrames += (ASMMult2xU32RetU64(uHz, (uint32_t)cMs) + RT_MS_1SEC - 1) / RT_MS_1SEC; + return cFrames; +} + +/** + * Converts milliseconds to bytes. + * + * @returns Number of bytes (frame aligned). + * @param pProps The PCM properties to use. + * @param cMs The number of milliseconds to convert. + * + * @note The result is rounded rather than floored (hysterical raisins). + */ +DECLINLINE(uint32_t) PDMAudioPropsMilliToBytes(PCPDMAUDIOPCMPROPS pProps, uint64_t cMs) +{ + return PDMAUDIOPCMPROPS_F2B(pProps, PDMAudioPropsMilliToFrames(pProps, cMs)); +} + +/** + * Converts nanoseconds to frames. + * + * @returns Number of frames + * @param pProps The PCM properties to use. + * @param cNs The number of nanoseconds to convert. + * + * @note The result is rounded rather than floored (hysterical raisins). + */ +DECLINLINE(uint32_t) PDMAudioPropsNanoToFrames(PCPDMAUDIOPCMPROPS pProps, uint64_t cNs) +{ + AssertPtrReturn(pProps, 0); + + uint32_t const uHz = pProps->uHz; + uint32_t cFrames; + if (cNs < RT_NS_1SEC) + cFrames = 0; + else + { + cFrames = cNs / RT_NS_1SEC * uHz; + cNs %= RT_NS_1SEC; + } + cFrames += (ASMMult2xU32RetU64(uHz, (uint32_t)cNs) + RT_NS_1SEC - 1) / RT_NS_1SEC; + return cFrames; +} + +/** + * Converts nanoseconds to bytes. + * + * @returns Number of bytes (frame aligned). + * @param pProps The PCM properties to use. + * @param cNs The number of nanoseconds to convert. + * + * @note The result is rounded rather than floored (hysterical raisins). + */ +DECLINLINE(uint32_t) PDMAudioPropsNanoToBytes(PCPDMAUDIOPCMPROPS pProps, uint64_t cNs) +{ + return PDMAUDIOPCMPROPS_F2B(pProps, PDMAudioPropsNanoToFrames(pProps, cNs)); +} + +/** + * Clears a sample buffer by the given amount of audio frames with silence (according to the format + * given by the PCM properties). + * + * @param pProps The PCM properties to apply. + * @param pvBuf The buffer to clear. + * @param cbBuf The buffer size in bytes. + * @param cFrames The number of audio frames to clear. Capped at @a cbBuf + * if exceeding the buffer. If the size is an unaligned + * number of frames, the extra bytes may be left + * uninitialized in some configurations. + */ +DECLINLINE(void) PDMAudioPropsClearBuffer(PCPDMAUDIOPCMPROPS pProps, void *pvBuf, size_t cbBuf, uint32_t cFrames) +{ + /* + * Validate input + */ + AssertPtrReturnVoid(pProps); + Assert(pProps->cbSample); + if (!cbBuf || !cFrames) + return; + AssertPtrReturnVoid(pvBuf); + + Assert(pProps->fSwapEndian == false); /** @todo Swapping Endianness is not supported yet. */ + + /* + * Decide how much needs clearing. + */ + size_t cbToClear = PDMAudioPropsFramesToBytes(pProps, cFrames); + AssertStmt(cbToClear <= cbBuf, cbToClear = cbBuf); + + Log2Func(("pProps=%p, pvBuf=%p, cFrames=%RU32, fSigned=%RTbool, cBytes=%RU8\n", + pProps, pvBuf, cFrames, pProps->fSigned, pProps->cbSample)); + + /* + * Do the job. + */ + if (pProps->fSigned) + RT_BZERO(pvBuf, cbToClear); + else /* Unsigned formats. */ + { + switch (pProps->cbSample) + { + case 1: /* 8 bit */ + memset(pvBuf, 0x80, cbToClear); + break; + + case 2: /* 16 bit */ + { + uint16_t *pu16Dst = (uint16_t *)pvBuf; + size_t cLeft = cbToClear / sizeof(uint16_t); + while (cLeft-- > 0) + *pu16Dst++ = 0x80; + break; + } + + /** @todo Add 24 bit? */ + + case 4: /* 32 bit */ + ASMMemFill32(pvBuf, cbToClear & ~(size_t)3, 0x80); + break; + + default: + AssertMsgFailed(("Invalid bytes per sample: %RU8\n", pProps->cbSample)); + } + } +} + +/** + * Compares two sets of PCM properties. + * + * @returns @c true if the same, @c false if not. + * @param pProps1 The first set of properties to compare. + * @param pProps2 The second set of properties to compare. + */ +DECLINLINE(bool) PDMAudioPropsAreEqual(PCPDMAUDIOPCMPROPS pProps1, PCPDMAUDIOPCMPROPS pProps2) +{ + AssertPtrReturn(pProps1, false); + AssertPtrReturn(pProps2, false); + + if (pProps1 == pProps2) /* If the pointers match, take a shortcut. */ + return true; + + return pProps1->uHz == pProps2->uHz + && pProps1->cChannels == pProps2->cChannels + && pProps1->cbSample == pProps2->cbSample + && pProps1->fSigned == pProps2->fSigned + && pProps1->fSwapEndian == pProps2->fSwapEndian; +} + +/** + * Checks whether the given PCM properties are valid or not. + * + * @returns true/false accordingly. + * @param pProps The PCM properties to check. + * + * @remarks This just performs a generic check of value ranges. Further, it + * will assert if the input is invalid. + * + * @sa PDMAudioStrmCfgIsValid + */ +DECLINLINE(bool) PDMAudioPropsAreValid(PCPDMAUDIOPCMPROPS pProps) +{ + AssertPtrReturn(pProps, false); + + AssertReturn(pProps->cChannels != 0, false); + AssertMsgReturn(pProps->cbSample == 1 || pProps->cbSample == 2 || pProps->cbSample == 4, + ("%u\n", pProps->cbSample), false); + AssertMsgReturn(pProps->uHz >= 1000 && pProps->uHz < 1000000, ("%u\n", pProps->uHz), false); + AssertMsgReturn(pProps->cShift == PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pProps->cbSample, pProps->cChannels), + ("cShift=%u cbSample=%u cChannels=%u\n", pProps->cShift, pProps->cbSample, pProps->cChannels), + false); + return true; +} + +/** + * Get number of bytes per frame. + * + * @returns Number of bytes per audio frame. + * @param pProps PCM properties to use. + * @sa PDMAUDIOPCMPROPS_F2B + */ +DECLINLINE(uint32_t) PDMAudioPropsBytesPerFrame(PCPDMAUDIOPCMPROPS pProps) +{ + return PDMAUDIOPCMPROPS_F2B(pProps, 1 /*cFrames*/); +} + +/** + * Prints PCM properties to the debug log. + * + * @param pProps Stream configuration to log. + */ +DECLINLINE(void) PDMAudioPropsLog(PCPDMAUDIOPCMPROPS pProps) +{ + AssertPtrReturnVoid(pProps); + + Log(("uHz=%RU32, cChannels=%RU8, cBits=%RU8%s", + pProps->uHz, pProps->cChannels, pProps->cbSample * 8, pProps->fSigned ? "S" : "U")); +} + +/** @} */ + +#endif /* !VBOX_INCLUDED_vmm_pdmaudioinline_h */ |