summaryrefslogtreecommitdiffstats
path: root/include/VBox/vmm/pdmaudioinline.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--include/VBox/vmm/pdmaudioinline.h1507
1 files changed, 1507 insertions, 0 deletions
diff --git a/include/VBox/vmm/pdmaudioinline.h b/include/VBox/vmm/pdmaudioinline.h
new file mode 100644
index 00000000..5d3175c0
--- /dev/null
+++ b/include/VBox/vmm/pdmaudioinline.h
@@ -0,0 +1,1507 @@
+/* $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-2022 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#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
+ * @{
+ */
+
+
+/**
+ * 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_INVALID: return "invalid";
+ 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_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_INVALID: return "Invalid";
+ 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_32BIT_HACK:
+ break;
+ }
+ AssertMsgFailedReturn(("Invalid mixer control %ld\n", enmMixerCtl), "bad");
+}
+
+/**
+ * Gets the name of a path enum value.
+ *
+ * @returns Pointer to read-only name, "bad" if invalid input.
+ * @param enmPath The path value to name.
+ */
+DECLINLINE(const char *) PDMAudioPathGetName(PDMAUDIOPATH enmPath)
+{
+ switch (enmPath)
+ {
+ case PDMAUDIOPATH_INVALID: return "invalid";
+ case PDMAUDIOPATH_UNKNOWN: return "unknown";
+
+ case PDMAUDIOPATH_OUT_FRONT: return "front";
+ case PDMAUDIOPATH_OUT_CENTER_LFE: return "center-lfe";
+ case PDMAUDIOPATH_OUT_REAR: return "rear";
+
+ case PDMAUDIOPATH_IN_MIC: return "mic";
+ case PDMAUDIOPATH_IN_CD: return "cd";
+ case PDMAUDIOPATH_IN_VIDEO: return "video-in";
+ case PDMAUDIOPATH_IN_AUX: return "aux-in";
+ case PDMAUDIOPATH_IN_LINE: return "line-in";
+ case PDMAUDIOPATH_IN_PHONE: return "phone";
+
+ /* no default */
+ case PDMAUDIOPATH_END:
+ case PDMAUDIOPATH_32BIT_HACK:
+ break;
+ }
+ AssertMsgFailedReturn(("Unknown enmPath=%d\n", enmPath), "bad");
+}
+
+/**
+ * Gets the name of a channel.
+ *
+ * @returns Pointer to read-only name, "bad" if invalid input.
+ * @param enmChannelId The channel ID to name.
+ */
+DECLINLINE(const char *) PDMAudioChannelIdGetName(PDMAUDIOCHANNELID enmChannelId)
+{
+ switch (enmChannelId)
+ {
+ case PDMAUDIOCHANNELID_INVALID: return "invalid";
+ case PDMAUDIOCHANNELID_UNUSED_ZERO: return "unused-zero";
+ case PDMAUDIOCHANNELID_UNUSED_SILENCE: return "unused-silence";
+ case PDMAUDIOCHANNELID_UNKNOWN: return "unknown";
+
+ case PDMAUDIOCHANNELID_FRONT_LEFT: return "FL";
+ case PDMAUDIOCHANNELID_FRONT_RIGHT: return "FR";
+ case PDMAUDIOCHANNELID_FRONT_CENTER: return "FC";
+ case PDMAUDIOCHANNELID_LFE: return "LFE";
+ case PDMAUDIOCHANNELID_REAR_LEFT: return "BL";
+ case PDMAUDIOCHANNELID_REAR_RIGHT: return "BR";
+ case PDMAUDIOCHANNELID_FRONT_LEFT_OF_CENTER: return "FLC";
+ case PDMAUDIOCHANNELID_FRONT_RIGHT_OF_CENTER: return "FRC";
+ case PDMAUDIOCHANNELID_REAR_CENTER: return "BC";
+ case PDMAUDIOCHANNELID_SIDE_LEFT: return "SL";
+ case PDMAUDIOCHANNELID_SIDE_RIGHT: return "SR";
+ case PDMAUDIOCHANNELID_TOP_CENTER: return "TC";
+ case PDMAUDIOCHANNELID_FRONT_LEFT_HEIGHT: return "TFL";
+ case PDMAUDIOCHANNELID_FRONT_CENTER_HEIGHT: return "TFC";
+ case PDMAUDIOCHANNELID_FRONT_RIGHT_HEIGHT: return "TFR";
+ case PDMAUDIOCHANNELID_REAR_LEFT_HEIGHT: return "TBL";
+ case PDMAUDIOCHANNELID_REAR_CENTER_HEIGHT: return "TBC";
+ case PDMAUDIOCHANNELID_REAR_RIGHT_HEIGHT: return "TBR";
+
+ /* no default */
+ case PDMAUDIOCHANNELID_END:
+ case PDMAUDIOCHANNELID_32BIT_HACK:
+ break;
+ }
+ AssertMsgFailedReturn(("Unknown enmChannelId=%d\n", enmChannelId), "bad");
+}
+
+
+/*********************************************************************************************************************************
+* Volume Helpers *
+*********************************************************************************************************************************/
+
+/**
+ * Initializes a PDMAUDIOVOLUME structure to max.
+ *
+ * @param pVol The structure to initialize.
+ */
+DECLINLINE(void) PDMAudioVolumeInitMax(PPDMAUDIOVOLUME pVol)
+{
+ pVol->fMuted = false;
+ for (uintptr_t i = 0; i < RT_ELEMENTS(pVol->auChannels); i++)
+ pVol->auChannels[i] = PDMAUDIO_VOLUME_MAX;
+}
+
+
+/**
+ * Initializes a PDMAUDIOVOLUME structure from a simple stereo setting.
+ *
+ * The additional channels will simply be assigned the higer of the two.
+ *
+ * @param pVol The structure to initialize.
+ * @param fMuted Muted.
+ * @param bLeft The left channel volume.
+ * @param bRight The right channel volume.
+ */
+DECLINLINE(void) PDMAudioVolumeInitFromStereo(PPDMAUDIOVOLUME pVol, bool fMuted, uint8_t bLeft, uint8_t bRight)
+{
+ pVol->fMuted = fMuted;
+ pVol->auChannels[0] = bLeft;
+ pVol->auChannels[1] = bRight;
+
+ uint8_t const bOther = RT_MAX(bLeft, bRight);
+ for (uintptr_t i = 2; i < RT_ELEMENTS(pVol->auChannels); i++)
+ pVol->auChannels[i] = bOther;
+}
+
+
+/**
+ * Combines two volume settings (typically master and sink).
+ *
+ * @param pVol Where to return the combined volume
+ * @param pVol1 The first volume settings to combine.
+ * @param pVol2 The second volume settings.
+ */
+DECLINLINE(void) PDMAudioVolumeCombine(PPDMAUDIOVOLUME pVol, PCPDMAUDIOVOLUME pVol1, PCPDMAUDIOVOLUME pVol2)
+{
+ if (pVol1->fMuted || pVol2->fMuted)
+ {
+ pVol->fMuted = true;
+ for (uintptr_t i = 0; i < RT_ELEMENTS(pVol->auChannels); i++)
+ pVol->auChannels[i] = 0;
+ }
+ else
+ {
+ pVol->fMuted = false;
+ /** @todo Very crude implementation for now -- needs more work! (At least
+ * when used in audioMixerSinkUpdateVolume it was considered as such.) */
+ for (uintptr_t i = 0; i < RT_ELEMENTS(pVol->auChannels); i++)
+ {
+#if 0 /* bird: I think the shift variant should produce the exact same result, w/o two conditionals per iteration. */
+ /* 255 * 255 / 255 = 0xFF (255) */
+ /* 17 * 127 / 255 = 8 */
+ /* 39 * 39 / 255 = 5 */
+ pVol->auChannels[i] = (uint8_t)( (RT_MAX(pVol1->auChannels[i], 1U) * RT_MAX(pVol2->auChannels[i], 1U))
+ / PDMAUDIO_VOLUME_MAX);
+#else
+ /* (((255 + 1) * (255 + 1)) >> 8) - 1 = 0xFF (255) */
+ /* ((( 17 + 1) * (127 + 1)) >> 8) - 1 = 0x8 (8) */
+ /* ((( 39 + 1) * ( 39 + 1)) >> 8) - 1 = 0x5 (5) */
+ pVol->auChannels[i] = (uint8_t)((((1U + pVol1->auChannels[i]) * (1U + pVol2->auChannels[i])) >> 8) - 1U);
+#endif
+ }
+ }
+}
+
+
+/*********************************************************************************************************************************
+* PCM Property Helpers *
+*********************************************************************************************************************************/
+
+/**
+ * Assigns default channel IDs according to the channel count.
+ *
+ * The assignments are taken from the standard speaker channel layouts table
+ * in the wikipedia article on surround sound:
+ * https://en.wikipedia.org/wiki/Surround_sound#Standard_speaker_channels
+ */
+DECLINLINE(void) PDMAudioPropsSetDefaultChannelIds(PPDMAUDIOPCMPROPS pProps)
+{
+ unsigned cChannels = pProps->cChannelsX;
+ switch (cChannels)
+ {
+ case 1:
+ pProps->aidChannels[0] = PDMAUDIOCHANNELID_MONO;
+ break;
+ case 2:
+ pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
+ pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
+ break;
+ case 3: /* 2.1 */
+ pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
+ pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
+ pProps->aidChannels[2] = PDMAUDIOCHANNELID_LFE;
+ break;
+ case 4: /* 4.0 */
+ pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
+ pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
+ pProps->aidChannels[2] = PDMAUDIOCHANNELID_REAR_LEFT;
+ pProps->aidChannels[3] = PDMAUDIOCHANNELID_REAR_RIGHT;
+ break;
+ case 5: /* 4.1 */
+ pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
+ pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
+ pProps->aidChannels[2] = PDMAUDIOCHANNELID_FRONT_CENTER;
+ pProps->aidChannels[3] = PDMAUDIOCHANNELID_LFE;
+ pProps->aidChannels[4] = PDMAUDIOCHANNELID_REAR_CENTER;
+ break;
+ case 6: /* 5.1 */
+ pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
+ pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
+ pProps->aidChannels[2] = PDMAUDIOCHANNELID_FRONT_CENTER;
+ pProps->aidChannels[3] = PDMAUDIOCHANNELID_LFE;
+ pProps->aidChannels[4] = PDMAUDIOCHANNELID_REAR_LEFT;
+ pProps->aidChannels[5] = PDMAUDIOCHANNELID_REAR_RIGHT;
+ break;
+ case 7: /* 6.1 */
+ pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
+ pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
+ pProps->aidChannels[2] = PDMAUDIOCHANNELID_FRONT_CENTER;
+ pProps->aidChannels[3] = PDMAUDIOCHANNELID_LFE;
+ pProps->aidChannels[4] = PDMAUDIOCHANNELID_REAR_LEFT;
+ pProps->aidChannels[5] = PDMAUDIOCHANNELID_REAR_RIGHT;
+ pProps->aidChannels[6] = PDMAUDIOCHANNELID_REAR_CENTER;
+ break;
+ case 8: /* 7.1 */
+ pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
+ pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
+ pProps->aidChannels[2] = PDMAUDIOCHANNELID_FRONT_CENTER;
+ pProps->aidChannels[3] = PDMAUDIOCHANNELID_LFE;
+ pProps->aidChannels[4] = PDMAUDIOCHANNELID_REAR_LEFT;
+ pProps->aidChannels[5] = PDMAUDIOCHANNELID_REAR_RIGHT;
+ pProps->aidChannels[6] = PDMAUDIOCHANNELID_FRONT_LEFT_OF_CENTER;
+ pProps->aidChannels[7] = PDMAUDIOCHANNELID_FRONT_RIGHT_OF_CENTER;
+ break;
+ case 9: /* 9.0 */
+ pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
+ pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
+ pProps->aidChannels[2] = PDMAUDIOCHANNELID_FRONT_CENTER;
+ pProps->aidChannels[3] = PDMAUDIOCHANNELID_REAR_LEFT;
+ pProps->aidChannels[4] = PDMAUDIOCHANNELID_REAR_RIGHT;
+ pProps->aidChannels[5] = PDMAUDIOCHANNELID_SIDE_LEFT;
+ pProps->aidChannels[6] = PDMAUDIOCHANNELID_SIDE_RIGHT;
+ pProps->aidChannels[7] = PDMAUDIOCHANNELID_FRONT_LEFT_HEIGHT;
+ pProps->aidChannels[8] = PDMAUDIOCHANNELID_FRONT_RIGHT_HEIGHT;
+ break;
+ case 10: /* 9.1 */
+ pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
+ pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
+ pProps->aidChannels[2] = PDMAUDIOCHANNELID_FRONT_CENTER;
+ pProps->aidChannels[3] = PDMAUDIOCHANNELID_LFE;
+ pProps->aidChannels[4] = PDMAUDIOCHANNELID_REAR_LEFT;
+ pProps->aidChannels[5] = PDMAUDIOCHANNELID_REAR_RIGHT;
+ pProps->aidChannels[6] = PDMAUDIOCHANNELID_SIDE_LEFT;
+ pProps->aidChannels[7] = PDMAUDIOCHANNELID_SIDE_RIGHT;
+ pProps->aidChannels[8] = PDMAUDIOCHANNELID_FRONT_LEFT_HEIGHT;
+ pProps->aidChannels[9] = PDMAUDIOCHANNELID_FRONT_RIGHT_HEIGHT;
+ break;
+ case 11: /* 11.0 */
+ pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
+ pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
+ pProps->aidChannels[2] = PDMAUDIOCHANNELID_FRONT_CENTER;
+ pProps->aidChannels[3] = PDMAUDIOCHANNELID_REAR_LEFT;
+ pProps->aidChannels[4] = PDMAUDIOCHANNELID_REAR_RIGHT;
+ pProps->aidChannels[5] = PDMAUDIOCHANNELID_FRONT_LEFT_OF_CENTER;
+ pProps->aidChannels[6] = PDMAUDIOCHANNELID_FRONT_RIGHT_OF_CENTER;
+ pProps->aidChannels[7] = PDMAUDIOCHANNELID_SIDE_LEFT;
+ pProps->aidChannels[8] = PDMAUDIOCHANNELID_SIDE_RIGHT;
+ pProps->aidChannels[9] = PDMAUDIOCHANNELID_FRONT_LEFT_HEIGHT;
+ pProps->aidChannels[10]= PDMAUDIOCHANNELID_FRONT_RIGHT_HEIGHT;
+ break;
+ default:
+ AssertFailed();
+ cChannels = 12;
+ RT_FALL_THROUGH();
+ case 12: /* 11.1 */
+ pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
+ pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
+ pProps->aidChannels[2] = PDMAUDIOCHANNELID_FRONT_CENTER;
+ pProps->aidChannels[3] = PDMAUDIOCHANNELID_LFE;
+ pProps->aidChannels[4] = PDMAUDIOCHANNELID_REAR_LEFT;
+ pProps->aidChannels[5] = PDMAUDIOCHANNELID_REAR_RIGHT;
+ pProps->aidChannels[6] = PDMAUDIOCHANNELID_FRONT_LEFT_OF_CENTER;
+ pProps->aidChannels[7] = PDMAUDIOCHANNELID_FRONT_RIGHT_OF_CENTER;
+ pProps->aidChannels[8] = PDMAUDIOCHANNELID_SIDE_LEFT;
+ pProps->aidChannels[9] = PDMAUDIOCHANNELID_SIDE_RIGHT;
+ pProps->aidChannels[10]= PDMAUDIOCHANNELID_FRONT_LEFT_HEIGHT;
+ pProps->aidChannels[11]= PDMAUDIOCHANNELID_FRONT_RIGHT_HEIGHT;
+ break;
+ case 0:
+ break;
+ }
+ AssertCompile(RT_ELEMENTS(pProps->aidChannels) >= 12);
+
+ while (cChannels < RT_ELEMENTS(pProps->aidChannels))
+ pProps->aidChannels[cChannels++] = PDMAUDIOCHANNELID_INVALID;
+}
+
+
+/**
+ * Initialize PCM audio properties.
+ */
+DECLINLINE(void) PDMAudioPropsInit(PPDMAUDIOPCMPROPS pProps, uint8_t cbSample, bool fSigned, uint8_t cChannels, uint32_t uHz)
+{
+ pProps->cbFrame = cbSample * cChannels;
+ pProps->cbSampleX = cbSample;
+ pProps->cChannelsX = cChannels;
+ pProps->cShiftX = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(cbSample, cChannels);
+ pProps->fSigned = fSigned;
+ pProps->fSwapEndian = false;
+ pProps->fRaw = false;
+ pProps->uHz = uHz;
+
+ Assert(pProps->cbFrame == (uint32_t)cbSample * cChannels);
+ Assert(pProps->cbSampleX == cbSample);
+ Assert(pProps->cChannelsX == cChannels);
+
+ PDMAudioPropsSetDefaultChannelIds(pProps);
+}
+
+/**
+ * Initialize PCM audio properties, extended version.
+ */
+DECLINLINE(void) PDMAudioPropsInitEx(PPDMAUDIOPCMPROPS pProps, uint8_t cbSample, bool fSigned, uint8_t cChannels, uint32_t uHz,
+ bool fLittleEndian, bool fRaw)
+{
+ Assert(!fRaw || cbSample == sizeof(int64_t));
+ pProps->cbFrame = cbSample * cChannels;
+ pProps->cbSampleX = cbSample;
+ pProps->cChannelsX = cChannels;
+ pProps->cShiftX = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(cbSample, cChannels);
+ pProps->fSigned = fSigned;
+#ifdef RT_LITTLE_ENDIAN
+ pProps->fSwapEndian = !fLittleEndian;
+#else
+ pProps->fSwapEndian = fLittleEndian;
+#endif
+ pProps->fRaw = fRaw;
+ pProps->uHz = uHz;
+
+ Assert(pProps->cbFrame == (uint32_t)cbSample * cChannels);
+ Assert(pProps->cbSampleX == cbSample);
+ Assert(pProps->cChannelsX == cChannels);
+
+ PDMAudioPropsSetDefaultChannelIds(pProps);
+}
+
+/**
+ * Modifies the channel count.
+ *
+ * @note This will reset the channel IDs to defaults.
+ *
+ * @param pProps The PCM properties to update.
+ * @param cChannels The new channel count.
+ */
+DECLINLINE(void) PDMAudioPropsSetChannels(PPDMAUDIOPCMPROPS pProps, uint8_t cChannels)
+{
+ Assert(cChannels > 0); Assert(cChannels < 16);
+ pProps->cChannelsX = cChannels;
+ pProps->cbFrame = pProps->cbSampleX * cChannels;
+ pProps->cShiftX = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pProps->cbSampleX, cChannels);
+
+ PDMAudioPropsSetDefaultChannelIds(pProps);
+}
+
+/**
+ * Modifies the sample size.
+ *
+ * @param pProps The PCM properties to update.
+ * @param cbSample The new sample size (in bytes).
+ */
+DECLINLINE(void) PDMAudioPropsSetSampleSize(PPDMAUDIOPCMPROPS pProps, uint8_t cbSample)
+{
+ Assert(cbSample == 1 || cbSample == 2 || cbSample == 4 || cbSample == 8);
+ pProps->cbSampleX = cbSample;
+ pProps->cbFrame = cbSample * pProps->cChannelsX;
+ pProps->cShiftX = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(cbSample, pProps->cChannelsX);
+}
+
+/**
+ * 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)
+{
+ Assert(pProps->cbFrame == pProps->cbSampleX * pProps->cChannelsX);
+ return pProps->cbFrame * pProps->uHz * 8;
+}
+
+/**
+ * Gets the number of channels.
+ * @returns The channel count.
+ * @param pProps The PCM properties.
+ */
+DECL_FORCE_INLINE(uint8_t) PDMAudioPropsChannels(PCPDMAUDIOPCMPROPS pProps)
+{
+ return pProps->cChannelsX;
+}
+
+/**
+ * Gets the sample size in bytes.
+ * @returns Number of bytes per sample.
+ * @param pProps The PCM properties.
+ */
+DECL_FORCE_INLINE(uint8_t) PDMAudioPropsSampleSize(PCPDMAUDIOPCMPROPS pProps)
+{
+ return pProps->cbSampleX;
+}
+
+/**
+ * Gets the sample size in bits.
+ * @returns Number of bits per sample.
+ * @param pProps The PCM properties.
+ */
+DECLINLINE(uint8_t) PDMAudioPropsSampleBits(PCPDMAUDIOPCMPROPS pProps)
+{
+ return pProps->cbSampleX * 8;
+}
+
+/**
+ * Gets the frame size in bytes.
+ * @returns Number of bytes per frame.
+ * @param pProps The PCM properties.
+ */
+DECL_FORCE_INLINE(uint8_t) PDMAudioPropsFrameSize(PCPDMAUDIOPCMPROPS pProps)
+{
+ return pProps->cbFrame;
+}
+
+/**
+ * Gets the frequency.
+ * @returns Frequency.
+ * @param pProps The PCM properties.
+ */
+DECL_FORCE_INLINE(uint32_t) PDMAudioPropsHz(PCPDMAUDIOPCMPROPS pProps)
+{
+ return pProps->uHz;
+}
+
+/**
+ * Checks if the format is signed or unsigned.
+ * @returns true if signed, false if unsigned.
+ * @param pProps The PCM properties.
+ */
+DECL_FORCE_INLINE(bool) PDMAudioPropsIsSigned(PCPDMAUDIOPCMPROPS pProps)
+{
+ return pProps->fSigned;
+}
+
+/**
+ * Checks if the format is little-endian or not.
+ * @returns true if little-endian (or if 8-bit), false if big-endian.
+ * @param pProps The PCM properties.
+ */
+DECL_FORCE_INLINE(bool) PDMAudioPropsIsLittleEndian(PCPDMAUDIOPCMPROPS pProps)
+{
+#ifdef RT_LITTLE_ENDIAN
+ return !pProps->fSwapEndian || pProps->cbSampleX < 2;
+#else
+ return pProps->fSwapEndian || pProps->cbSampleX < 2;
+#endif
+}
+
+/**
+ * Checks if the format is big-endian or not.
+ * @returns true if big-endian (or if 8-bit), false if little-endian.
+ * @param pProps The PCM properties.
+ */
+DECL_FORCE_INLINE(bool) PDMAudioPropsIsBigEndian(PCPDMAUDIOPCMPROPS pProps)
+{
+#ifdef RT_LITTLE_ENDIAN
+ return pProps->fSwapEndian || pProps->cbSampleX < 2;
+#else
+ return !pProps->fSwapEndian || pProps->cbSampleX < 2;
+#endif
+}
+
+/**
+ * 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 = PDMAudioPropsFrameSize(pProps);
+ 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 = PDMAudioPropsFrameSize(pProps);
+ 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 = PDMAudioPropsFrameSize(pProps);
+ 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 = PDMAudioPropsFrameSize(pProps);
+ 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 = PDMAudioPropsFrameSize(pProps);
+ 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 bytes to nanoseconds, 64-bit version.
+ *
+ * @return Number nanoseconds @a cb takes to play or record.
+ * @param pProps PCM properties to use.
+ * @param cb The number of bytes to convert (64-bit).
+ *
+ * @note Rounds up the result.
+ */
+DECLINLINE(uint64_t) PDMAudioPropsBytesToNano64(PCPDMAUDIOPCMPROPS pProps, uint64_t cb)
+{
+ AssertPtrReturn(pProps, 0);
+
+ /* Check parameters to prevent division by chainsaw: */
+ uint32_t const uHz = pProps->uHz;
+ if (uHz)
+ {
+ const unsigned cbFrame = PDMAudioPropsFrameSize(pProps);
+ if (cbFrame)
+ {
+ /* Round cb up to closest frame size: */
+ cb = (cb + cbFrame - 1) / cbFrame;
+
+ /* Convert to nanoseconds. */
+ return (cb * 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 milliseconds, but not returning more than @a cMsMax
+ *
+ * This is a convenience for logging and such.
+ *
+ * @returns milliseconds (32-bit).
+ * @param pProps The PCM properties to use.
+ * @param cFrames Number of audio frames to convert.
+ * @param cMsMax Max return value (32-bit).
+ * @note No rounding here, result is floored.
+ */
+DECLINLINE(uint32_t) PDMAudioPropsFramesToMilliMax(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames, uint32_t cMsMax)
+{
+ AssertPtrReturn(pProps, 0);
+
+ /* Check input to prevent division by chainsaw: */
+ uint32_t const uHz = pProps->uHz;
+ if (uHz)
+ {
+ uint32_t const cMsResult = ASMMultU32ByU32DivByU32(cFrames, RT_MS_1SEC, uHz);
+ return RT_MIN(cMsResult, cMsMax);
+ }
+ return 0;
+}
+
+/**
+ * Converts frames to microseconds.
+ *
+ * @returns microseconds.
+ * @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) PDMAudioPropsFramesToMicro(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_US_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 frames to NT ticks (100 ns units).
+ *
+ * @returns NT ticks.
+ * @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) PDMAudioPropsFramesToNtTicks(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 / 100, 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 frames, 64-bit return.
+ *
+ * @returns Number of frames (64-bit).
+ * @param pProps The PCM properties to use.
+ * @param cNs The number of nanoseconds to convert.
+ *
+ * @note The result is floored!
+ */
+DECLINLINE(uint64_t) PDMAudioPropsNanoToFrames64(PCPDMAUDIOPCMPROPS pProps, uint64_t cNs)
+{
+ AssertPtrReturn(pProps, 0);
+
+ uint32_t const uHz = pProps->uHz;
+ uint64_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;
+ 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));
+}
+
+/**
+ * Converts nanoseconds to bytes, 64-bit version.
+ *
+ * @returns Number of bytes (frame aligned), 64-bit.
+ * @param pProps The PCM properties to use.
+ * @param cNs The number of nanoseconds to convert.
+ *
+ * @note The result is floored.
+ */
+DECLINLINE(uint64_t) PDMAudioPropsNanoToBytes64(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->cbSampleX);
+ if (!cbBuf || !cFrames)
+ return;
+ AssertPtrReturnVoid(pvBuf);
+
+ /*
+ * 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, cbSample=%RU8\n",
+ pProps, pvBuf, cFrames, pProps->fSigned, pProps->cbSampleX));
+
+ /*
+ * Do the job.
+ */
+ if (pProps->fSigned)
+ RT_BZERO(pvBuf, cbToClear);
+ else /* Unsigned formats. */
+ {
+ switch (pProps->cbSampleX)
+ {
+ case 1: /* 8 bit */
+ memset(pvBuf, 0x80, cbToClear);
+ break;
+
+ case 2: /* 16 bit */
+ {
+ uint16_t *pu16Dst = (uint16_t *)pvBuf;
+ uint16_t const u16Offset = !pProps->fSwapEndian ? UINT16_C(0x8000) : UINT16_C(0x80);
+ cbBuf /= sizeof(*pu16Dst);
+ while (cbBuf-- > 0)
+ *pu16Dst++ = u16Offset;
+ break;
+ }
+
+ case 4: /* 32 bit */
+ ASMMemFill32(pvBuf, cbToClear & ~(size_t)(sizeof(uint32_t) - 1),
+ !pProps->fSwapEndian ? UINT32_C(0x80000000) : UINT32_C(0x80));
+ break;
+
+ default:
+ AssertMsgFailed(("Invalid bytes per sample: %RU8\n", pProps->cbSampleX));
+ }
+ }
+}
+
+/**
+ * Checks if the given buffer is silence.
+ *
+ * @param pProps The PCM properties to use checking the buffer.
+ * @param pvBuf The buffer to check.
+ * @param cbBuf The number of bytes to check (must be frame aligned).
+ */
+DECLINLINE(bool) PDMAudioPropsIsBufferSilence(PCPDMAUDIOPCMPROPS pProps, void const *pvBuf, size_t cbBuf)
+{
+ /*
+ * Validate input
+ */
+ AssertPtrReturn(pProps, false);
+ if (!cbBuf)
+ return false;
+ AssertPtrReturn(pvBuf, false);
+
+ /*
+ * Do the job.
+ */
+ if (pProps->fSigned)
+ return ASMMemIsZero(pvBuf, cbBuf);
+
+ switch (pProps->cbSampleX)
+ {
+ case 1: /* 8 bit */
+ return ASMMemIsAllU8(pvBuf, cbBuf, 0x80);
+
+ case 2: /* 16 bit */
+ {
+ uint16_t const *pu16 = (uint16_t const *)pvBuf;
+ uint16_t const u16Offset = !pProps->fSwapEndian ? UINT16_C(0x8000) : UINT16_C(0x80);
+ cbBuf /= sizeof(*pu16);
+ while (cbBuf-- > 0)
+ if (*pu16 != u16Offset)
+ return false;
+ return true;
+ }
+
+ case 4: /* 32 bit */
+ {
+ uint32_t const *pu32 = (uint32_t const *)pvBuf;
+ uint32_t const u32Offset = !pProps->fSwapEndian ? UINT32_C(0x80000000) : UINT32_C(0x80);
+ cbBuf /= sizeof(*pu32);
+ while (cbBuf-- > 0)
+ if (*pu32 != u32Offset)
+ return false;
+ return true;
+ }
+
+ default:
+ AssertMsgFailed(("Invalid bytes per sample: %RU8\n", pProps->cbSampleX));
+ return false;
+ }
+}
+
+/**
+ * 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)
+{
+ uintptr_t idxCh;
+ AssertPtrReturn(pProps1, false);
+ AssertPtrReturn(pProps2, false);
+
+ if (pProps1 == pProps2) /* If the pointers match, take a shortcut. */
+ return true;
+
+ if (pProps1->uHz != pProps2->uHz)
+ return false;
+ if (pProps1->cChannelsX != pProps2->cChannelsX)
+ return false;
+ if (pProps1->cbSampleX != pProps2->cbSampleX)
+ return false;
+ if (pProps1->fSigned != pProps2->fSigned)
+ return false;
+ if (pProps1->fSwapEndian != pProps2->fSwapEndian)
+ return false;
+ if (pProps1->fRaw != pProps2->fRaw)
+ return false;
+
+ idxCh = pProps1->cChannelsX;
+ while (idxCh-- > 0)
+ if (pProps1->aidChannels[idxCh] != pProps2->aidChannels[idxCh])
+ return false;
+
+ return true;
+}
+
+/**
+ * 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.
+ *
+ * @sa PDMAudioStrmCfgIsValid
+ */
+DECLINLINE(bool) PDMAudioPropsAreValid(PCPDMAUDIOPCMPROPS pProps)
+{
+ AssertPtrReturn(pProps, false);
+
+ /* Channels. */
+ if ( pProps->cChannelsX != 0
+ && pProps->cChannelsX <= PDMAUDIO_MAX_CHANNELS
+ /* Sample size. */
+ && ( pProps->cbSampleX == 1
+ || pProps->cbSampleX == 2
+ || pProps->cbSampleX == 4
+ || (pProps->cbSampleX == 8 && pProps->fRaw))
+ /* Hertz rate. */
+ && pProps->uHz >= 1000
+ && pProps->uHz < 1000000
+ /* Raw format: Here we only support int64_t as sample size currently, if enabled. */
+ && ( !pProps->fRaw
+ || (pProps->fSigned && pProps->cbSampleX == sizeof(int64_t)))
+ )
+ {
+ /* A few more sanity checks to see if the structure has been properly initialized (via PDMAudioPropsInit[Ex]). */
+ AssertMsgReturn(pProps->cShiftX == PDMAUDIOPCMPROPS_MAKE_SHIFT(pProps),
+ ("cShift=%u cbSample=%u cChannels=%u\n", pProps->cShiftX, pProps->cbSampleX, pProps->cChannelsX),
+ false);
+ AssertMsgReturn(pProps->cbFrame == pProps->cbSampleX * pProps->cChannelsX,
+ ("cbFrame=%u cbSample=%u cChannels=%u\n", pProps->cbFrame, pProps->cbSampleX, pProps->cChannelsX),
+ false);
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * 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 PCM properties to use.
+ */
+DECLINLINE(void) PDMAudioPropsLog(PCPDMAUDIOPCMPROPS pProps)
+{
+ AssertPtrReturnVoid(pProps);
+
+ Log(("uHz=%RU32, cChannels=%RU8, cBits=%RU8%s",
+ pProps->uHz, pProps->cChannelsX, pProps->cbSampleX * 8, pProps->fSigned ? "S" : "U"));
+}
+
+/** Max necessary buffer space for PDMAudioPropsToString */
+#define PDMAUDIOPROPSTOSTRING_MAX sizeof("16ch S64 4294967296Hz swap raw")
+
+/**
+ * Formats the PCM audio properties into a string buffer.
+ *
+ * @returns pszDst
+ * @param pProps PCM properties to use.
+ * @param pszDst The destination buffer.
+ * @param cchDst The size of the destination buffer. Recommended to be at
+ * least PDMAUDIOPROPSTOSTRING_MAX bytes.
+ */
+DECLINLINE(char *) PDMAudioPropsToString(PCPDMAUDIOPCMPROPS pProps, char *pszDst, size_t cchDst)
+{
+ /* 2ch S64 44100Hz swap raw */
+ RTStrPrintf(pszDst, cchDst, "%uch %c%u %RU32Hz%s%s",
+ PDMAudioPropsChannels(pProps), PDMAudioPropsIsSigned(pProps) ? 'S' : 'U', PDMAudioPropsSampleBits(pProps),
+ PDMAudioPropsHz(pProps), pProps->fSwapEndian ? " swap" : "", pProps->fRaw ? " raw" : "");
+ return pszDst;
+}
+
+
+/*********************************************************************************************************************************
+* Stream Configuration Helpers *
+*********************************************************************************************************************************/
+
+/**
+ * Initializes a stream configuration from PCM properties.
+ *
+ * @returns VBox 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);
+}
+
+/**
+ * Checks whether two stream configuration matches.
+ *
+ * @returns @c true if equal, @c false if not.
+ * @param pCfg1 The first stream configuration.
+ * @param pCfg2 The second stream configuration.
+ */
+DECLINLINE(bool) PDMAudioStrmCfgEquals(PCPDMAUDIOSTREAMCFG pCfg1, PCPDMAUDIOSTREAMCFG pCfg2)
+{
+ if (!pCfg1 || !pCfg2)
+ return false;
+ if (pCfg1 == pCfg2)
+ return pCfg1 != NULL;
+ if (PDMAudioPropsAreEqual(&pCfg1->Props, &pCfg2->Props))
+ return pCfg1->enmDir == pCfg2->enmDir
+ && pCfg1->enmPath == pCfg2->enmPath
+ && pCfg1->Device.cMsSchedulingHint == pCfg2->Device.cMsSchedulingHint
+ && pCfg1->Backend.cFramesPeriod == pCfg2->Backend.cFramesPeriod
+ && pCfg1->Backend.cFramesBufferSize == pCfg2->Backend.cFramesBufferSize
+ && pCfg1->Backend.cFramesPreBuffering == pCfg2->Backend.cFramesPreBuffering
+ && strcmp(pCfg1->szName, pCfg2->szName) == 0;
+ return false;
+}
+
+/**
+ * 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);
+ return PDMAudioPropsAreValid(&pCfg->Props);
+}
+
+/**
+ * Copies one stream configuration to another.
+ *
+ * @returns VBox 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.cbSampleX * 8, pCfg->Props.fSigned ? "S" : "U", pCfg->Props.cChannelsX));
+}
+
+/**
+ * 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_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_END:
+ case PDMAUDIOSTREAMCMD_32BIT_HACK:
+ break;
+ /* no default! */
+ }
+ AssertMsgFailedReturn(("Invalid stream command %d\n", enmCmd), "bad");
+}
+
+/** Max necessary buffer space for PDMAudioStrmCfgToString */
+#define PDMAUDIOSTRMCFGTOSTRING_MAX \
+ sizeof("'01234567890123456789012345678901234567890123456789012345678901234' unknown 16ch S64 4294967295Hz swap raw, 9999999ms buffer, 9999999ms period, 9999999ms pre-buffer, 4294967295ms sched, center-lfe")
+
+/**
+ * Formats an audio stream configuration.
+ *
+ * @param pCfg The stream configuration to stringify.
+ * @param pszDst The destination buffer.
+ * @param cbDst The size of the destination buffer. Recommend this be
+ * at least PDMAUDIOSTRMCFGTOSTRING_MAX bytes.
+ */
+DECLINLINE(const char *) PDMAudioStrmCfgToString(PCPDMAUDIOSTREAMCFG pCfg, char *pszDst, size_t cbDst)
+{
+ /* 'front' output 2ch 44100Hz raw, 300ms buffer, 75ms period, 150ms pre-buffer, 10ms sched */
+ RTStrPrintf(pszDst, cbDst,
+ "'%s' %s %uch %c%u %RU32Hz%s%s, %RU32ms buffer, %RU32ms period, %RU32ms pre-buffer, %RU32ms sched%s%s",
+ pCfg->szName, PDMAudioDirGetName(pCfg->enmDir), PDMAudioPropsChannels(&pCfg->Props),
+ PDMAudioPropsIsSigned(&pCfg->Props) ? 'S' : 'U', PDMAudioPropsSampleBits(&pCfg->Props),
+ PDMAudioPropsHz(&pCfg->Props), pCfg->Props.fSwapEndian ? " swap" : "", pCfg->Props.fRaw ? " raw" : "",
+ PDMAudioPropsFramesToMilliMax(&pCfg->Props, pCfg->Backend.cFramesBufferSize, 9999999),
+ PDMAudioPropsFramesToMilliMax(&pCfg->Props, pCfg->Backend.cFramesPeriod, 9999999),
+ PDMAudioPropsFramesToMilliMax(&pCfg->Props, pCfg->Backend.cFramesPreBuffering, 9999999),
+ pCfg->Device.cMsSchedulingHint,
+ pCfg->enmPath == PDMAUDIOPATH_UNKNOWN ? "" : ", ",
+ pCfg->enmPath == PDMAUDIOPATH_UNKNOWN ? "" : PDMAudioPathGetName(pCfg->enmPath) );
+ return pszDst;
+}
+
+
+/*********************************************************************************************************************************
+* Stream Status Helpers *
+*********************************************************************************************************************************/
+
+/**
+ * Converts a audio stream state enum value to a string.
+ *
+ * @returns Pointer to read-only audio stream state string on success,
+ * "illegal" if invalid command value.
+ * @param enmStreamState The state to convert.
+ */
+DECLINLINE(const char *) PDMAudioStreamStateGetName(PDMAUDIOSTREAMSTATE enmStreamState)
+{
+ switch (enmStreamState)
+ {
+ case PDMAUDIOSTREAMSTATE_INVALID: return "invalid";
+ case PDMAUDIOSTREAMSTATE_NOT_WORKING: return "not-working";
+ case PDMAUDIOSTREAMSTATE_NEED_REINIT: return "need-reinit";
+ case PDMAUDIOSTREAMSTATE_INACTIVE: return "inactive";
+ case PDMAUDIOSTREAMSTATE_ENABLED: return "enabled";
+ case PDMAUDIOSTREAMSTATE_ENABLED_READABLE: return "enabled-readable";
+ case PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE: return "enabled-writable";
+ /* no default: */
+ case PDMAUDIOSTREAMSTATE_END:
+ case PDMAUDIOSTREAMSTATE_32BIT_HACK:
+ break;
+ }
+ AssertMsgFailedReturn(("Invalid audio stream state: %d\n", enmStreamState), "illegal");
+}
+
+/**
+ * Converts a host audio (backend) stream state enum value to a string.
+ *
+ * @returns Pointer to read-only host audio stream state string on success,
+ * "illegal" if invalid command value.
+ * @param enmHostAudioStreamState The state to convert.
+ */
+DECLINLINE(const char *) PDMHostAudioStreamStateGetName(PDMHOSTAUDIOSTREAMSTATE enmHostAudioStreamState)
+{
+ switch (enmHostAudioStreamState)
+ {
+ case PDMHOSTAUDIOSTREAMSTATE_INVALID: return "invalid";
+ case PDMHOSTAUDIOSTREAMSTATE_INITIALIZING: return "initializing";
+ case PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING: return "not-working";
+ case PDMHOSTAUDIOSTREAMSTATE_OKAY: return "okay";
+ case PDMHOSTAUDIOSTREAMSTATE_DRAINING: return "draining";
+ case PDMHOSTAUDIOSTREAMSTATE_INACTIVE: return "inactive";
+ /* no default: */
+ case PDMHOSTAUDIOSTREAMSTATE_END:
+ case PDMHOSTAUDIOSTREAMSTATE_32BIT_HACK:
+ break;
+ }
+ AssertMsgFailedReturn(("Invalid host audio stream state: %d\n", enmHostAudioStreamState), "illegal");
+}
+
+/** @} */
+
+#endif /* !VBOX_INCLUDED_vmm_pdmaudioinline_h */