summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Audio/AudioHlp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Audio/AudioHlp.cpp')
-rw-r--r--src/VBox/Devices/Audio/AudioHlp.cpp656
1 files changed, 656 insertions, 0 deletions
diff --git a/src/VBox/Devices/Audio/AudioHlp.cpp b/src/VBox/Devices/Audio/AudioHlp.cpp
new file mode 100644
index 00000000..b3f54167
--- /dev/null
+++ b/src/VBox/Devices/Audio/AudioHlp.cpp
@@ -0,0 +1,656 @@
+/* $Id: AudioHlp.cpp $ */
+/** @file
+ * Audio helper routines.
+ *
+ * These are used with both drivers and devices.
+ */
+
+/*
+ * Copyright (C) 2006-2023 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+#include <iprt/formats/riff.h>
+
+#define LOG_GROUP LOG_GROUP_DRV_AUDIO
+#include <VBox/log.h>
+
+#include <VBox/err.h>
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/pdm.h>
+#include <VBox/vmm/pdmaudioinline.h>
+
+#include "AudioHlp.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct AUDIOWAVEFILEHDR
+{
+ RTRIFFHDR Hdr;
+ RTRIFFWAVEFMTEXTCHUNK FmtExt;
+ RTRIFFCHUNK Data;
+} AUDIOWAVEFILEHDR;
+
+
+#if 0 /* unused, no header prototypes */
+
+/**
+ * Retrieves the matching PDMAUDIOFMT for the given bits + signing flag.
+ *
+ * @return Matching PDMAUDIOFMT value.
+ * @retval PDMAUDIOFMT_INVALID if unsupported @a cBits value.
+ *
+ * @param cBits The number of bits in the audio format.
+ * @param fSigned Whether the audio format is signed @c true or not.
+ */
+PDMAUDIOFMT DrvAudioAudFmtBitsToFormat(uint8_t cBits, bool fSigned)
+{
+ if (fSigned)
+ {
+ switch (cBits)
+ {
+ case 8: return PDMAUDIOFMT_S8;
+ case 16: return PDMAUDIOFMT_S16;
+ case 32: return PDMAUDIOFMT_S32;
+ default: AssertMsgFailedReturn(("Bogus audio bits %RU8\n", cBits), PDMAUDIOFMT_INVALID);
+ }
+ }
+ else
+ {
+ switch (cBits)
+ {
+ case 8: return PDMAUDIOFMT_U8;
+ case 16: return PDMAUDIOFMT_U16;
+ case 32: return PDMAUDIOFMT_U32;
+ default: AssertMsgFailedReturn(("Bogus audio bits %RU8\n", cBits), PDMAUDIOFMT_INVALID);
+ }
+ }
+}
+
+/**
+ * Returns an unique file name for this given audio connector instance.
+ *
+ * @return Allocated file name. Must be free'd using RTStrFree().
+ * @param uInstance Driver / device instance.
+ * @param pszPath Path name of the file to delete. The path must exist.
+ * @param pszSuffix File name suffix to use.
+ */
+char *DrvAudioDbgGetFileNameA(uint8_t uInstance, const char *pszPath, const char *pszSuffix)
+{
+ char szFileName[64];
+ RTStrPrintf(szFileName, sizeof(szFileName), "drvAudio%RU8-%s", uInstance, pszSuffix);
+
+ char szFilePath[RTPATH_MAX];
+ int rc2 = RTStrCopy(szFilePath, sizeof(szFilePath), pszPath);
+ AssertRC(rc2);
+ rc2 = RTPathAppend(szFilePath, sizeof(szFilePath), szFileName);
+ AssertRC(rc2);
+
+ return RTStrDup(szFilePath);
+}
+
+#endif /* unused */
+
+/**
+ * Checks whether a given stream configuration is valid or not.
+ *
+ * @note See notes on AudioHlpPcmPropsAreValid().
+ *
+ * Returns @c true if configuration is valid, @c false if not.
+ * @param pCfg Stream configuration to check.
+ */
+bool AudioHlpStreamCfgIsValid(PCPDMAUDIOSTREAMCFG pCfg)
+{
+ /* Ugly! HDA attach code calls us with uninitialized (all zero) config. */
+ if (PDMAudioPropsHz(&pCfg->Props) != 0)
+ {
+ if (PDMAudioStrmCfgIsValid(pCfg))
+ {
+ if ( pCfg->enmDir == PDMAUDIODIR_IN
+ || pCfg->enmDir == PDMAUDIODIR_OUT)
+ return AudioHlpPcmPropsAreValidAndSupported(&pCfg->Props);
+ }
+ }
+ return false;
+}
+
+/**
+ * Calculates the audio bit rate of the given bits per sample, the Hz and the number
+ * of audio channels.
+ *
+ * Divide the result by 8 to get the byte rate.
+ *
+ * @returns Bitrate.
+ * @param cBits Number of bits per sample.
+ * @param uHz Hz (Hertz) rate.
+ * @param cChannels Number of audio channels.
+ */
+uint32_t AudioHlpCalcBitrate(uint8_t cBits, uint32_t uHz, uint8_t cChannels)
+{
+ return cBits * uHz * cChannels;
+}
+
+
+/**
+ * Checks whether given PCM properties are valid *and* supported by the audio stack or not.
+ *
+ * @returns @c true if the properties are valid and supported, @c false if not.
+ * @param pProps The PCM properties to check.
+ *
+ * @note Use PDMAudioPropsAreValid() to just check the validation bits.
+ */
+bool AudioHlpPcmPropsAreValidAndSupported(PCPDMAUDIOPCMPROPS pProps)
+{
+ AssertPtrReturn(pProps, false);
+
+ if (!PDMAudioPropsAreValid(pProps))
+ return false;
+
+ /* Properties seem valid, now check if we actually support those. */
+ switch (PDMAudioPropsSampleSize(pProps))
+ {
+ case 1: /* 8 bit */
+ /* Signed / unsigned. */
+ break;
+ case 2: /* 16 bit */
+ /* Signed / unsigned. */
+ break;
+ /** @todo Do we need support for 24 bit samples? */
+ case 4: /* 32 bit */
+ /* Signed / unsigned. */
+ break;
+ case 8: /* 64-bit raw */
+ if ( !PDMAudioPropsIsSigned(pProps)
+ || !pProps->fRaw)
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ if (!pProps->fSwapEndian) /** @todo Handling Big Endian audio data is not supported yet. */
+ return true;
+ return false;
+}
+
+
+/*********************************************************************************************************************************
+* Audio File Helpers *
+*********************************************************************************************************************************/
+
+/**
+ * Constructs an unique file name, based on the given path and the audio file type.
+ *
+ * @returns VBox status code.
+ * @param pszDst Where to store the constructed file name.
+ * @param cbDst Size of the destination buffer (bytes; incl terminator).
+ * @param pszPath Base path to use. If NULL or empty, the user's
+ * temporary directory will be used.
+ * @param pszNameFmt A name for better identifying the file.
+ * @param va Arguments for @a pszNameFmt.
+ * @param uInstance Device / driver instance which is using this file.
+ * @param enmType Audio file type to construct file name for.
+ * @param fFlags File naming flags, AUDIOHLPFILENAME_FLAGS_XXX.
+ * @param chTweak Retry tweak character.
+ */
+static int audioHlpConstructPathWorker(char *pszDst, size_t cbDst, const char *pszPath, const char *pszNameFmt, va_list va,
+ uint32_t uInstance, AUDIOHLPFILETYPE enmType, uint32_t fFlags, char chTweak)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrNullReturn(pszPath, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszNameFmt, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~AUDIOHLPFILENAME_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
+
+ /* Validate the type and translate it into a suffix. */
+ const char *pszSuffix = NULL;
+ switch (enmType)
+ {
+ case AUDIOHLPFILETYPE_RAW: pszSuffix = ".pcm"; break;
+ case AUDIOHLPFILETYPE_WAV: pszSuffix = ".wav"; break;
+ case AUDIOHLPFILETYPE_INVALID:
+ case AUDIOHLPFILETYPE_32BIT_HACK:
+ break; /* no default */
+ }
+ AssertMsgReturn(pszSuffix, ("enmType=%d\n", enmType), VERR_INVALID_PARAMETER);
+
+ /*
+ * The directory. Make sure it exists and ends with a path separator.
+ */
+ int rc;
+ if (!pszPath || !*pszPath)
+ rc = RTPathTemp(pszDst, cbDst);
+ else
+ {
+ AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
+ rc = RTStrCopy(pszDst, cbDst, pszPath);
+ }
+ AssertRCReturn(rc, rc);
+
+ if (!RTDirExists(pszDst))
+ {
+ rc = RTDirCreateFullPath(pszDst, RTFS_UNIX_IRWXU);
+ AssertRCReturn(rc, rc);
+ }
+
+ size_t offDst = RTPathEnsureTrailingSeparator(pszDst, cbDst);
+ AssertReturn(offDst > 0, VERR_BUFFER_OVERFLOW);
+ Assert(offDst < cbDst);
+
+ /*
+ * The filename.
+ */
+ /* Start with a ISO timestamp w/ colons replaced by dashes if requested. */
+ if (fFlags & AUDIOHLPFILENAME_FLAGS_TS)
+ {
+ RTTIMESPEC NowTimeSpec;
+ RTTIME NowUtc;
+ AssertReturn(RTTimeToString(RTTimeExplode(&NowUtc, RTTimeNow(&NowTimeSpec)), &pszDst[offDst], cbDst - offDst),
+ VERR_BUFFER_OVERFLOW);
+
+ /* Change the two colons in the time part to dashes. */
+ char *pchColon = &pszDst[offDst];
+ while ((pchColon = strchr(pchColon, ':')) != NULL)
+ *pchColon++ = '-';
+
+ offDst += strlen(&pszDst[offDst]);
+ Assert(pszDst[offDst - 1] == 'Z');
+
+ /* Append a dash to separate the timestamp from the name. */
+ AssertReturn(offDst + 2 <= cbDst, VERR_BUFFER_OVERFLOW);
+ pszDst[offDst++] = '-';
+ pszDst[offDst] = '\0';
+ }
+
+ /* Append the filename, instance, retry-tweak and suffix. */
+ va_list vaCopy;
+ va_copy(vaCopy, va);
+ ssize_t cchTail;
+ if (chTweak == '\0')
+ cchTail = RTStrPrintf2(&pszDst[offDst], cbDst - offDst, "%N-%u%s", pszNameFmt, &vaCopy, uInstance, pszSuffix);
+ else
+ cchTail = RTStrPrintf2(&pszDst[offDst], cbDst - offDst, "%N-%u%c%s", pszNameFmt, &vaCopy, uInstance, chTweak, pszSuffix);
+ va_end(vaCopy);
+ AssertReturn(cchTail > 0, VERR_BUFFER_OVERFLOW);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for AudioHlpFileCreateF and AudioHlpFileCreateAndOpenEx that allocates
+ * and initializes a AUDIOHLPFILE instance.
+ */
+static int audioHlpFileCreateWorker(PAUDIOHLPFILE *ppFile, uint32_t fFlags, AUDIOHLPFILETYPE enmType, const char *pszPath)
+{
+ AssertReturn(!(fFlags & ~AUDIOHLPFILE_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
+
+ size_t const cbPath = strlen(pszPath) + 1;
+ PAUDIOHLPFILE pFile = (PAUDIOHLPFILE)RTMemAllocVar(RT_UOFFSETOF_DYN(AUDIOHLPFILE, szName[cbPath]));
+ AssertPtrReturn(pFile, VERR_NO_MEMORY);
+
+ pFile->enmType = enmType;
+ pFile->fFlags = fFlags;
+ pFile->cbWaveData = 0;
+ pFile->hFile = NIL_RTFILE;
+ memcpy(pFile->szName, pszPath, cbPath);
+
+ *ppFile = pFile;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Creates an instance of AUDIOHLPFILE with the given filename and type.
+ *
+ * @note This does <b>NOT</b> create the file, see AudioHlpFileOpen for that.
+ *
+ * @returns VBox status code.
+ * @param ppFile Where to return the pointer to the audio debug file
+ * instance on success.
+ * @param fFlags AUDIOHLPFILE_FLAGS_XXX.
+ * @param enmType The audio file type to produce.
+ * @param pszPath The directory path. The temporary directory will be
+ * used if NULL or empty.
+ * @param fFilename AUDIOHLPFILENAME_FLAGS_XXX.
+ * @param uInstance The instance number (will be appended to the filename
+ * with a dash inbetween).
+ * @param pszNameFmt The filename format string.
+ * @param ... Arguments to the filename format string.
+ */
+int AudioHlpFileCreateF(PAUDIOHLPFILE *ppFile, uint32_t fFlags, AUDIOHLPFILETYPE enmType,
+ const char *pszPath, uint32_t fFilename, uint32_t uInstance, const char *pszNameFmt, ...)
+{
+ *ppFile = NULL;
+
+ /*
+ * Construct the filename first.
+ */
+ char szPath[RTPATH_MAX];
+ va_list va;
+ va_start(va, pszNameFmt);
+ int rc = audioHlpConstructPathWorker(szPath, sizeof(szPath), pszPath, pszNameFmt, va, uInstance, enmType, fFilename, '\0');
+ va_end(va);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Allocate and initializes a debug file instance with that filename path.
+ */
+ return audioHlpFileCreateWorker(ppFile, fFlags, enmType, szPath);
+}
+
+
+/**
+ * Destroys a formerly created audio file.
+ *
+ * @param pFile Audio file (object) to destroy.
+ */
+void AudioHlpFileDestroy(PAUDIOHLPFILE pFile)
+{
+ if (pFile)
+ {
+ AudioHlpFileClose(pFile);
+ RTMemFree(pFile);
+ }
+}
+
+
+/**
+ * Opens or creates an audio file.
+ *
+ * @returns VBox status code.
+ * @param pFile Pointer to audio file handle to use.
+ * @param fOpen Open flags.
+ * Use AUDIOHLPFILE_DEFAULT_OPEN_FLAGS for the default open flags.
+ * @param pProps PCM properties to use.
+ */
+int AudioHlpFileOpen(PAUDIOHLPFILE pFile, uint64_t fOpen, PCPDMAUDIOPCMPROPS pProps)
+{
+ int rc;
+
+ AssertPtrReturn(pFile, VERR_INVALID_POINTER);
+ /** @todo Validate fOpen flags. */
+ AssertPtrReturn(pProps, VERR_INVALID_POINTER);
+ Assert(PDMAudioPropsAreValid(pProps));
+
+ /*
+ * Raw files just needs to be opened.
+ */
+ if (pFile->enmType == AUDIOHLPFILETYPE_RAW)
+ rc = RTFileOpen(&pFile->hFile, pFile->szName, fOpen);
+ /*
+ * Wave files needs a header to be constructed and we need to take note of where
+ * there are sizes to update later when closing the file.
+ */
+ else if (pFile->enmType == AUDIOHLPFILETYPE_WAV)
+ {
+ /* Construct the header. */
+ AUDIOWAVEFILEHDR FileHdr;
+ FileHdr.Hdr.uMagic = RTRIFFHDR_MAGIC;
+ FileHdr.Hdr.cbFile = 0; /* need to update this later */
+ FileHdr.Hdr.uFileType = RTRIFF_FILE_TYPE_WAVE;
+ FileHdr.FmtExt.Chunk.uMagic = RTRIFFWAVEFMT_MAGIC;
+ FileHdr.FmtExt.Chunk.cbChunk = sizeof(RTRIFFWAVEFMTEXTCHUNK) - sizeof(RTRIFFCHUNK);
+ FileHdr.FmtExt.Data.Core.uFormatTag = RTRIFFWAVEFMT_TAG_EXTENSIBLE;
+ FileHdr.FmtExt.Data.Core.cChannels = PDMAudioPropsChannels(pProps);
+ FileHdr.FmtExt.Data.Core.uHz = PDMAudioPropsHz(pProps);
+ FileHdr.FmtExt.Data.Core.cbRate = PDMAudioPropsFramesToBytes(pProps, PDMAudioPropsHz(pProps));
+ FileHdr.FmtExt.Data.Core.cbFrame = PDMAudioPropsFrameSize(pProps);
+ FileHdr.FmtExt.Data.Core.cBitsPerSample = PDMAudioPropsSampleBits(pProps);
+ FileHdr.FmtExt.Data.cbExtra = sizeof(FileHdr.FmtExt.Data) - sizeof(FileHdr.FmtExt.Data.Core);
+ FileHdr.FmtExt.Data.cValidBitsPerSample = PDMAudioPropsSampleBits(pProps);
+ FileHdr.FmtExt.Data.fChannelMask = 0;
+ for (uintptr_t idxCh = 0; idxCh < FileHdr.FmtExt.Data.Core.cChannels; idxCh++)
+ {
+ PDMAUDIOCHANNELID const idCh = (PDMAUDIOCHANNELID)pProps->aidChannels[idxCh];
+ AssertLogRelMsgReturn(idCh >= PDMAUDIOCHANNELID_FIRST_STANDARD && idCh < PDMAUDIOCHANNELID_END_STANDARD,
+ ("Invalid channel ID %d for channel #%u", idCh, idxCh), VERR_INVALID_PARAMETER);
+ AssertLogRelMsgReturn(!(FileHdr.FmtExt.Data.fChannelMask & RT_BIT_32(idCh - PDMAUDIOCHANNELID_FIRST_STANDARD)),
+ ("Channel #%u repeats channel ID %d", idxCh, idCh), VERR_INVALID_PARAMETER);
+ FileHdr.FmtExt.Data.fChannelMask |= RT_BIT_32(idCh - PDMAUDIOCHANNELID_FIRST_STANDARD);
+ }
+
+ RTUUID UuidTmp;
+ rc = RTUuidFromStr(&UuidTmp, RTRIFFWAVEFMTEXT_SUBTYPE_PCM);
+ AssertRCReturn(rc, rc);
+ FileHdr.FmtExt.Data.SubFormat = UuidTmp; /* (64-bit field maybe unaligned) */
+
+ FileHdr.Data.uMagic = RTRIFFWAVEDATACHUNK_MAGIC;
+ FileHdr.Data.cbChunk = 0; /* need to update this later */
+
+ /* Open the file and write out the header. */
+ rc = RTFileOpen(&pFile->hFile, pFile->szName, fOpen);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileWrite(pFile->hFile, &FileHdr, sizeof(FileHdr), NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTFileClose(pFile->hFile);
+ pFile->hFile = NIL_RTFILE;
+ }
+ }
+ }
+ else
+ AssertFailedStmt(rc = VERR_INTERNAL_ERROR_3);
+ if (RT_SUCCESS(rc))
+ {
+ pFile->cbWaveData = 0;
+ LogRel2(("Audio: Opened file '%s'\n", pFile->szName));
+ }
+ else
+ LogRel(("Audio: Failed opening file '%s': %Rrc\n", pFile->szName, rc));
+ return rc;
+}
+
+
+/**
+ * Creates a debug file structure and opens a file for it, extended version.
+ *
+ * @returns VBox status code.
+ * @param ppFile Where to return the debug file instance on success.
+ * @param enmType The file type.
+ * @param pszDir The directory to open the file in.
+ * @param iInstance The device/driver instance.
+ * @param fFilename AUDIOHLPFILENAME_FLAGS_XXX.
+ * @param fCreate AUDIOHLPFILE_FLAGS_XXX.
+ * @param pProps PCM audio properties for the file.
+ * @param fOpen RTFILE_O_XXX or AUDIOHLPFILE_DEFAULT_OPEN_FLAGS.
+ * @param pszNameFmt The base filename.
+ * @param ... Filename format arguments.
+ */
+int AudioHlpFileCreateAndOpenEx(PAUDIOHLPFILE *ppFile, AUDIOHLPFILETYPE enmType, const char *pszDir,
+ uint32_t iInstance, uint32_t fFilename, uint32_t fCreate,
+ PCPDMAUDIOPCMPROPS pProps, uint64_t fOpen, const char *pszNameFmt, ...)
+{
+ *ppFile = NULL;
+
+ for (uint32_t iTry = 0; ; iTry++)
+ {
+ /* Format the path to the filename. */
+ char szFile[RTPATH_MAX];
+ va_list va;
+ va_start(va, pszNameFmt);
+ int rc = audioHlpConstructPathWorker(szFile, sizeof(szFile), pszDir, pszNameFmt, va, iInstance, enmType, fFilename,
+ iTry == 0 ? '\0' : iTry + 'a');
+ va_end(va);
+ AssertRCReturn(rc, rc);
+
+ /* Create an debug audio file instance with the filename path. */
+ PAUDIOHLPFILE pFile = NULL;
+ rc = audioHlpFileCreateWorker(&pFile, fCreate, enmType, szFile);
+ AssertRCReturn(rc, rc);
+
+ /* Try open it. */
+ rc = AudioHlpFileOpen(pFile, fOpen, pProps);
+ if (RT_SUCCESS(rc))
+ {
+ *ppFile = pFile;
+ return rc;
+ }
+ AudioHlpFileDestroy(pFile);
+
+ AssertReturn(iTry < 16, rc);
+ }
+}
+
+
+/**
+ * Creates a debug wav-file structure and opens a file for it, default flags.
+ *
+ * @returns VBox status code.
+ * @param ppFile Where to return the debug file instance on success.
+ * @param pszDir The directory to open the file in.
+ * @param pszName The base filename.
+ * @param iInstance The device/driver instance.
+ * @param pProps PCM audio properties for the file.
+ */
+int AudioHlpFileCreateAndOpen(PAUDIOHLPFILE *ppFile, const char *pszDir, const char *pszName,
+ uint32_t iInstance, PCPDMAUDIOPCMPROPS pProps)
+{
+ return AudioHlpFileCreateAndOpenEx(ppFile, AUDIOHLPFILETYPE_WAV, pszDir, iInstance,
+ AUDIOHLPFILENAME_FLAGS_NONE, AUDIOHLPFILE_FLAGS_NONE,
+ pProps, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS, "%s", pszName);
+}
+
+
+/**
+ * Closes an audio file.
+ *
+ * @returns VBox status code.
+ * @param pFile Audio file handle to close.
+ */
+int AudioHlpFileClose(PAUDIOHLPFILE pFile)
+{
+ if (!pFile || pFile->hFile == NIL_RTFILE)
+ return VINF_SUCCESS;
+
+ /*
+ * Wave files needs to update the data size and file size in the header.
+ */
+ if (pFile->enmType == AUDIOHLPFILETYPE_WAV)
+ {
+ uint32_t const cbFile = sizeof(AUDIOWAVEFILEHDR) - sizeof(RTRIFFCHUNK) + (uint32_t)pFile->cbWaveData;
+ uint32_t const cbData = (uint32_t)pFile->cbWaveData;
+
+ int rc2;
+ rc2 = RTFileWriteAt(pFile->hFile, RT_UOFFSETOF(AUDIOWAVEFILEHDR, Hdr.cbFile), &cbFile, sizeof(cbFile), NULL);
+ AssertRC(rc2);
+ rc2 = RTFileWriteAt(pFile->hFile, RT_UOFFSETOF(AUDIOWAVEFILEHDR, Data.cbChunk), &cbData, sizeof(cbData), NULL);
+ AssertRC(rc2);
+ }
+
+ /*
+ * Do the closing.
+ */
+ int rc = RTFileClose(pFile->hFile);
+ if (RT_SUCCESS(rc) || rc == VERR_INVALID_HANDLE)
+ pFile->hFile = NIL_RTFILE;
+
+ if (RT_SUCCESS(rc))
+ LogRel2(("Audio: Closed file '%s' (%'RU64 bytes PCM data)\n", pFile->szName, pFile->cbWaveData));
+ else
+ LogRel(("Audio: Failed closing file '%s': %Rrc\n", pFile->szName, rc));
+
+ /*
+ * Delete empty file if requested.
+ */
+ if ( !(pFile->fFlags & AUDIOHLPFILE_FLAGS_KEEP_IF_EMPTY)
+ && pFile->cbWaveData == 0
+ && RT_SUCCESS(rc))
+ AudioHlpFileDelete(pFile);
+
+ return rc;
+}
+
+
+/**
+ * Deletes an audio file.
+ *
+ * @returns VBox status code.
+ * @param pFile Audio file to delete.
+ */
+int AudioHlpFileDelete(PAUDIOHLPFILE pFile)
+{
+ AssertPtrReturn(pFile, VERR_INVALID_POINTER);
+
+ int rc = RTFileDelete(pFile->szName);
+ if (RT_SUCCESS(rc))
+ LogRel2(("Audio: Deleted file '%s'\n", pFile->szName));
+ else if (rc == VERR_FILE_NOT_FOUND) /* Don't bitch if the file is not around anymore. */
+ rc = VINF_SUCCESS;
+
+ if (RT_FAILURE(rc))
+ LogRel(("Audio: Failed deleting file '%s', rc=%Rrc\n", pFile->szName, rc));
+
+ return rc;
+}
+
+
+/**
+ * Returns whether the given audio file is open and in use or not.
+ *
+ * @returns True if open, false if not.
+ * @param pFile Audio file to check open status for.
+ */
+bool AudioHlpFileIsOpen(PAUDIOHLPFILE pFile)
+{
+ if (!pFile || pFile->hFile == NIL_RTFILE)
+ return false;
+
+ return RTFileIsValid(pFile->hFile);
+}
+
+
+/**
+ * Write PCM data to a wave (.WAV) file.
+ *
+ * @returns VBox status code.
+ * @param pFile Audio file to write PCM data to.
+ * @param pvBuf Audio data to write.
+ * @param cbBuf Size (in bytes) of audio data to write.
+ */
+int AudioHlpFileWrite(PAUDIOHLPFILE pFile, const void *pvBuf, size_t cbBuf)
+{
+ AssertPtrReturn(pFile, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+
+ if (!cbBuf)
+ return VINF_SUCCESS;
+
+ int rc = RTFileWrite(pFile->hFile, pvBuf, cbBuf, NULL);
+ if (RT_SUCCESS(rc))
+ pFile->cbWaveData += cbBuf;
+
+ return rc;
+}
+