VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/AudioHlp.cpp@ 100836

Last change on this file since 100836 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.6 KB
Line 
1/* $Id: AudioHlp.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * Audio helper routines.
4 *
5 * These are used with both drivers and devices.
6 */
7
8/*
9 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
10 *
11 * This file is part of VirtualBox base platform packages, as
12 * available from https://www.virtualbox.org.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation, in version 3 of the
17 * License.
18 *
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see <https://www.gnu.org/licenses>.
26 *
27 * SPDX-License-Identifier: GPL-3.0-only
28 */
29
30
31/*********************************************************************************************************************************
32* Header Files *
33*********************************************************************************************************************************/
34#include <iprt/assert.h>
35#include <iprt/dir.h>
36#include <iprt/file.h>
37#include <iprt/mem.h>
38#include <iprt/string.h>
39#include <iprt/uuid.h>
40#include <iprt/formats/riff.h>
41
42#define LOG_GROUP LOG_GROUP_DRV_AUDIO
43#include <VBox/log.h>
44
45#include <VBox/err.h>
46#include <VBox/vmm/pdmdev.h>
47#include <VBox/vmm/pdm.h>
48#include <VBox/vmm/pdmaudioinline.h>
49
50#include "AudioHlp.h"
51
52
53/*********************************************************************************************************************************
54* Structures and Typedefs *
55*********************************************************************************************************************************/
56typedef struct AUDIOWAVEFILEHDR
57{
58 RTRIFFHDR Hdr;
59 RTRIFFWAVEFMTEXTCHUNK FmtExt;
60 RTRIFFCHUNK Data;
61} AUDIOWAVEFILEHDR;
62
63
64#if 0 /* unused, no header prototypes */
65
66/**
67 * Retrieves the matching PDMAUDIOFMT for the given bits + signing flag.
68 *
69 * @return Matching PDMAUDIOFMT value.
70 * @retval PDMAUDIOFMT_INVALID if unsupported @a cBits value.
71 *
72 * @param cBits The number of bits in the audio format.
73 * @param fSigned Whether the audio format is signed @c true or not.
74 */
75PDMAUDIOFMT DrvAudioAudFmtBitsToFormat(uint8_t cBits, bool fSigned)
76{
77 if (fSigned)
78 {
79 switch (cBits)
80 {
81 case 8: return PDMAUDIOFMT_S8;
82 case 16: return PDMAUDIOFMT_S16;
83 case 32: return PDMAUDIOFMT_S32;
84 default: AssertMsgFailedReturn(("Bogus audio bits %RU8\n", cBits), PDMAUDIOFMT_INVALID);
85 }
86 }
87 else
88 {
89 switch (cBits)
90 {
91 case 8: return PDMAUDIOFMT_U8;
92 case 16: return PDMAUDIOFMT_U16;
93 case 32: return PDMAUDIOFMT_U32;
94 default: AssertMsgFailedReturn(("Bogus audio bits %RU8\n", cBits), PDMAUDIOFMT_INVALID);
95 }
96 }
97}
98
99/**
100 * Returns an unique file name for this given audio connector instance.
101 *
102 * @return Allocated file name. Must be free'd using RTStrFree().
103 * @param uInstance Driver / device instance.
104 * @param pszPath Path name of the file to delete. The path must exist.
105 * @param pszSuffix File name suffix to use.
106 */
107char *DrvAudioDbgGetFileNameA(uint8_t uInstance, const char *pszPath, const char *pszSuffix)
108{
109 char szFileName[64];
110 RTStrPrintf(szFileName, sizeof(szFileName), "drvAudio%RU8-%s", uInstance, pszSuffix);
111
112 char szFilePath[RTPATH_MAX];
113 int rc2 = RTStrCopy(szFilePath, sizeof(szFilePath), pszPath);
114 AssertRC(rc2);
115 rc2 = RTPathAppend(szFilePath, sizeof(szFilePath), szFileName);
116 AssertRC(rc2);
117
118 return RTStrDup(szFilePath);
119}
120
121#endif /* unused */
122
123/**
124 * Checks whether a given stream configuration is valid or not.
125 *
126 * @note See notes on AudioHlpPcmPropsAreValid().
127 *
128 * Returns @c true if configuration is valid, @c false if not.
129 * @param pCfg Stream configuration to check.
130 */
131bool AudioHlpStreamCfgIsValid(PCPDMAUDIOSTREAMCFG pCfg)
132{
133 /* Ugly! HDA attach code calls us with uninitialized (all zero) config. */
134 if (PDMAudioPropsHz(&pCfg->Props) != 0)
135 {
136 if (PDMAudioStrmCfgIsValid(pCfg))
137 {
138 if ( pCfg->enmDir == PDMAUDIODIR_IN
139 || pCfg->enmDir == PDMAUDIODIR_OUT)
140 return AudioHlpPcmPropsAreValidAndSupported(&pCfg->Props);
141 }
142 }
143 return false;
144}
145
146/**
147 * Calculates the audio bit rate of the given bits per sample, the Hz and the number
148 * of audio channels.
149 *
150 * Divide the result by 8 to get the byte rate.
151 *
152 * @returns Bitrate.
153 * @param cBits Number of bits per sample.
154 * @param uHz Hz (Hertz) rate.
155 * @param cChannels Number of audio channels.
156 */
157uint32_t AudioHlpCalcBitrate(uint8_t cBits, uint32_t uHz, uint8_t cChannels)
158{
159 return cBits * uHz * cChannels;
160}
161
162
163/**
164 * Checks whether given PCM properties are valid *and* supported by the audio stack or not.
165 *
166 * @returns @c true if the properties are valid and supported, @c false if not.
167 * @param pProps The PCM properties to check.
168 *
169 * @note Use PDMAudioPropsAreValid() to just check the validation bits.
170 */
171bool AudioHlpPcmPropsAreValidAndSupported(PCPDMAUDIOPCMPROPS pProps)
172{
173 AssertPtrReturn(pProps, false);
174
175 if (!PDMAudioPropsAreValid(pProps))
176 return false;
177
178 /* Properties seem valid, now check if we actually support those. */
179 switch (PDMAudioPropsSampleSize(pProps))
180 {
181 case 1: /* 8 bit */
182 /* Signed / unsigned. */
183 break;
184 case 2: /* 16 bit */
185 /* Signed / unsigned. */
186 break;
187 /** @todo Do we need support for 24 bit samples? */
188 case 4: /* 32 bit */
189 /* Signed / unsigned. */
190 break;
191 case 8: /* 64-bit raw */
192 if ( !PDMAudioPropsIsSigned(pProps)
193 || !pProps->fRaw)
194 return false;
195 break;
196 default:
197 return false;
198 }
199
200 if (!pProps->fSwapEndian) /** @todo Handling Big Endian audio data is not supported yet. */
201 return true;
202 return false;
203}
204
205
206/*********************************************************************************************************************************
207* Audio File Helpers *
208*********************************************************************************************************************************/
209
210/**
211 * Constructs an unique file name, based on the given path and the audio file type.
212 *
213 * @returns VBox status code.
214 * @param pszDst Where to store the constructed file name.
215 * @param cbDst Size of the destination buffer (bytes; incl terminator).
216 * @param pszPath Base path to use. If NULL or empty, the user's
217 * temporary directory will be used.
218 * @param pszNameFmt A name for better identifying the file.
219 * @param va Arguments for @a pszNameFmt.
220 * @param uInstance Device / driver instance which is using this file.
221 * @param enmType Audio file type to construct file name for.
222 * @param fFlags File naming flags, AUDIOHLPFILENAME_FLAGS_XXX.
223 * @param chTweak Retry tweak character.
224 */
225static int audioHlpConstructPathWorker(char *pszDst, size_t cbDst, const char *pszPath, const char *pszNameFmt, va_list va,
226 uint32_t uInstance, AUDIOHLPFILETYPE enmType, uint32_t fFlags, char chTweak)
227{
228 /*
229 * Validate input.
230 */
231 AssertPtrNullReturn(pszPath, VERR_INVALID_POINTER);
232 AssertPtrReturn(pszNameFmt, VERR_INVALID_POINTER);
233 AssertReturn(!(fFlags & ~AUDIOHLPFILENAME_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
234
235 /* Validate the type and translate it into a suffix. */
236 const char *pszSuffix = NULL;
237 switch (enmType)
238 {
239 case AUDIOHLPFILETYPE_RAW: pszSuffix = ".pcm"; break;
240 case AUDIOHLPFILETYPE_WAV: pszSuffix = ".wav"; break;
241 case AUDIOHLPFILETYPE_INVALID:
242 case AUDIOHLPFILETYPE_32BIT_HACK:
243 break; /* no default */
244 }
245 AssertMsgReturn(pszSuffix, ("enmType=%d\n", enmType), VERR_INVALID_PARAMETER);
246
247 /*
248 * The directory. Make sure it exists and ends with a path separator.
249 */
250 int rc;
251 if (!pszPath || !*pszPath)
252 rc = RTPathTemp(pszDst, cbDst);
253 else
254 {
255 AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
256 rc = RTStrCopy(pszDst, cbDst, pszPath);
257 }
258 AssertRCReturn(rc, rc);
259
260 if (!RTDirExists(pszDst))
261 {
262 rc = RTDirCreateFullPath(pszDst, RTFS_UNIX_IRWXU);
263 AssertRCReturn(rc, rc);
264 }
265
266 size_t offDst = RTPathEnsureTrailingSeparator(pszDst, cbDst);
267 AssertReturn(offDst > 0, VERR_BUFFER_OVERFLOW);
268 Assert(offDst < cbDst);
269
270 /*
271 * The filename.
272 */
273 /* Start with a ISO timestamp w/ colons replaced by dashes if requested. */
274 if (fFlags & AUDIOHLPFILENAME_FLAGS_TS)
275 {
276 RTTIMESPEC NowTimeSpec;
277 RTTIME NowUtc;
278 AssertReturn(RTTimeToString(RTTimeExplode(&NowUtc, RTTimeNow(&NowTimeSpec)), &pszDst[offDst], cbDst - offDst),
279 VERR_BUFFER_OVERFLOW);
280
281 /* Change the two colons in the time part to dashes. */
282 char *pchColon = &pszDst[offDst];
283 while ((pchColon = strchr(pchColon, ':')) != NULL)
284 *pchColon++ = '-';
285
286 offDst += strlen(&pszDst[offDst]);
287 Assert(pszDst[offDst - 1] == 'Z');
288
289 /* Append a dash to separate the timestamp from the name. */
290 AssertReturn(offDst + 2 <= cbDst, VERR_BUFFER_OVERFLOW);
291 pszDst[offDst++] = '-';
292 pszDst[offDst] = '\0';
293 }
294
295 /* Append the filename, instance, retry-tweak and suffix. */
296 va_list vaCopy;
297 va_copy(vaCopy, va);
298 ssize_t cchTail;
299 if (chTweak == '\0')
300 cchTail = RTStrPrintf2(&pszDst[offDst], cbDst - offDst, "%N-%u%s", pszNameFmt, &vaCopy, uInstance, pszSuffix);
301 else
302 cchTail = RTStrPrintf2(&pszDst[offDst], cbDst - offDst, "%N-%u%c%s", pszNameFmt, &vaCopy, uInstance, chTweak, pszSuffix);
303 va_end(vaCopy);
304 AssertReturn(cchTail > 0, VERR_BUFFER_OVERFLOW);
305
306 return VINF_SUCCESS;
307}
308
309
310/**
311 * Worker for AudioHlpFileCreateF and AudioHlpFileCreateAndOpenEx that allocates
312 * and initializes a AUDIOHLPFILE instance.
313 */
314static int audioHlpFileCreateWorker(PAUDIOHLPFILE *ppFile, uint32_t fFlags, AUDIOHLPFILETYPE enmType, const char *pszPath)
315{
316 AssertReturn(!(fFlags & ~AUDIOHLPFILE_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
317
318 size_t const cbPath = strlen(pszPath) + 1;
319 PAUDIOHLPFILE pFile = (PAUDIOHLPFILE)RTMemAllocVar(RT_UOFFSETOF_DYN(AUDIOHLPFILE, szName[cbPath]));
320 AssertPtrReturn(pFile, VERR_NO_MEMORY);
321
322 pFile->enmType = enmType;
323 pFile->fFlags = fFlags;
324 pFile->cbWaveData = 0;
325 pFile->hFile = NIL_RTFILE;
326 memcpy(pFile->szName, pszPath, cbPath);
327
328 *ppFile = pFile;
329 return VINF_SUCCESS;
330}
331
332
333/**
334 * Creates an instance of AUDIOHLPFILE with the given filename and type.
335 *
336 * @note This does <b>NOT</b> create the file, see AudioHlpFileOpen for that.
337 *
338 * @returns VBox status code.
339 * @param ppFile Where to return the pointer to the audio debug file
340 * instance on success.
341 * @param fFlags AUDIOHLPFILE_FLAGS_XXX.
342 * @param enmType The audio file type to produce.
343 * @param pszPath The directory path. The temporary directory will be
344 * used if NULL or empty.
345 * @param fFilename AUDIOHLPFILENAME_FLAGS_XXX.
346 * @param uInstance The instance number (will be appended to the filename
347 * with a dash inbetween).
348 * @param pszNameFmt The filename format string.
349 * @param ... Arguments to the filename format string.
350 */
351int AudioHlpFileCreateF(PAUDIOHLPFILE *ppFile, uint32_t fFlags, AUDIOHLPFILETYPE enmType,
352 const char *pszPath, uint32_t fFilename, uint32_t uInstance, const char *pszNameFmt, ...)
353{
354 *ppFile = NULL;
355
356 /*
357 * Construct the filename first.
358 */
359 char szPath[RTPATH_MAX];
360 va_list va;
361 va_start(va, pszNameFmt);
362 int rc = audioHlpConstructPathWorker(szPath, sizeof(szPath), pszPath, pszNameFmt, va, uInstance, enmType, fFilename, '\0');
363 va_end(va);
364 AssertRCReturn(rc, rc);
365
366 /*
367 * Allocate and initializes a debug file instance with that filename path.
368 */
369 return audioHlpFileCreateWorker(ppFile, fFlags, enmType, szPath);
370}
371
372
373/**
374 * Destroys a formerly created audio file.
375 *
376 * @param pFile Audio file (object) to destroy.
377 */
378void AudioHlpFileDestroy(PAUDIOHLPFILE pFile)
379{
380 if (pFile)
381 {
382 AudioHlpFileClose(pFile);
383 RTMemFree(pFile);
384 }
385}
386
387
388/**
389 * Opens or creates an audio file.
390 *
391 * @returns VBox status code.
392 * @param pFile Pointer to audio file handle to use.
393 * @param fOpen Open flags.
394 * Use AUDIOHLPFILE_DEFAULT_OPEN_FLAGS for the default open flags.
395 * @param pProps PCM properties to use.
396 */
397int AudioHlpFileOpen(PAUDIOHLPFILE pFile, uint64_t fOpen, PCPDMAUDIOPCMPROPS pProps)
398{
399 int rc;
400
401 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
402 /** @todo Validate fOpen flags. */
403 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
404 Assert(PDMAudioPropsAreValid(pProps));
405
406 /*
407 * Raw files just needs to be opened.
408 */
409 if (pFile->enmType == AUDIOHLPFILETYPE_RAW)
410 rc = RTFileOpen(&pFile->hFile, pFile->szName, fOpen);
411 /*
412 * Wave files needs a header to be constructed and we need to take note of where
413 * there are sizes to update later when closing the file.
414 */
415 else if (pFile->enmType == AUDIOHLPFILETYPE_WAV)
416 {
417 /* Construct the header. */
418 AUDIOWAVEFILEHDR FileHdr;
419 FileHdr.Hdr.uMagic = RTRIFFHDR_MAGIC;
420 FileHdr.Hdr.cbFile = 0; /* need to update this later */
421 FileHdr.Hdr.uFileType = RTRIFF_FILE_TYPE_WAVE;
422 FileHdr.FmtExt.Chunk.uMagic = RTRIFFWAVEFMT_MAGIC;
423 FileHdr.FmtExt.Chunk.cbChunk = sizeof(RTRIFFWAVEFMTEXTCHUNK) - sizeof(RTRIFFCHUNK);
424 FileHdr.FmtExt.Data.Core.uFormatTag = RTRIFFWAVEFMT_TAG_EXTENSIBLE;
425 FileHdr.FmtExt.Data.Core.cChannels = PDMAudioPropsChannels(pProps);
426 FileHdr.FmtExt.Data.Core.uHz = PDMAudioPropsHz(pProps);
427 FileHdr.FmtExt.Data.Core.cbRate = PDMAudioPropsFramesToBytes(pProps, PDMAudioPropsHz(pProps));
428 FileHdr.FmtExt.Data.Core.cbFrame = PDMAudioPropsFrameSize(pProps);
429 FileHdr.FmtExt.Data.Core.cBitsPerSample = PDMAudioPropsSampleBits(pProps);
430 FileHdr.FmtExt.Data.cbExtra = sizeof(FileHdr.FmtExt.Data) - sizeof(FileHdr.FmtExt.Data.Core);
431 FileHdr.FmtExt.Data.cValidBitsPerSample = PDMAudioPropsSampleBits(pProps);
432 FileHdr.FmtExt.Data.fChannelMask = 0;
433 for (uintptr_t idxCh = 0; idxCh < FileHdr.FmtExt.Data.Core.cChannels; idxCh++)
434 {
435 PDMAUDIOCHANNELID const idCh = (PDMAUDIOCHANNELID)pProps->aidChannels[idxCh];
436 AssertLogRelMsgReturn(idCh >= PDMAUDIOCHANNELID_FIRST_STANDARD && idCh < PDMAUDIOCHANNELID_END_STANDARD,
437 ("Invalid channel ID %d for channel #%u", idCh, idxCh), VERR_INVALID_PARAMETER);
438 AssertLogRelMsgReturn(!(FileHdr.FmtExt.Data.fChannelMask & RT_BIT_32(idCh - PDMAUDIOCHANNELID_FIRST_STANDARD)),
439 ("Channel #%u repeats channel ID %d", idxCh, idCh), VERR_INVALID_PARAMETER);
440 FileHdr.FmtExt.Data.fChannelMask |= RT_BIT_32(idCh - PDMAUDIOCHANNELID_FIRST_STANDARD);
441 }
442
443 RTUUID UuidTmp;
444 rc = RTUuidFromStr(&UuidTmp, RTRIFFWAVEFMTEXT_SUBTYPE_PCM);
445 AssertRCReturn(rc, rc);
446 FileHdr.FmtExt.Data.SubFormat = UuidTmp; /* (64-bit field maybe unaligned) */
447
448 FileHdr.Data.uMagic = RTRIFFWAVEDATACHUNK_MAGIC;
449 FileHdr.Data.cbChunk = 0; /* need to update this later */
450
451 /* Open the file and write out the header. */
452 rc = RTFileOpen(&pFile->hFile, pFile->szName, fOpen);
453 if (RT_SUCCESS(rc))
454 {
455 rc = RTFileWrite(pFile->hFile, &FileHdr, sizeof(FileHdr), NULL);
456 if (RT_FAILURE(rc))
457 {
458 RTFileClose(pFile->hFile);
459 pFile->hFile = NIL_RTFILE;
460 }
461 }
462 }
463 else
464 AssertFailedStmt(rc = VERR_INTERNAL_ERROR_3);
465 if (RT_SUCCESS(rc))
466 {
467 pFile->cbWaveData = 0;
468 LogRel2(("Audio: Opened file '%s'\n", pFile->szName));
469 }
470 else
471 LogRel(("Audio: Failed opening file '%s': %Rrc\n", pFile->szName, rc));
472 return rc;
473}
474
475
476/**
477 * Creates a debug file structure and opens a file for it, extended version.
478 *
479 * @returns VBox status code.
480 * @param ppFile Where to return the debug file instance on success.
481 * @param enmType The file type.
482 * @param pszDir The directory to open the file in.
483 * @param iInstance The device/driver instance.
484 * @param fFilename AUDIOHLPFILENAME_FLAGS_XXX.
485 * @param fCreate AUDIOHLPFILE_FLAGS_XXX.
486 * @param pProps PCM audio properties for the file.
487 * @param fOpen RTFILE_O_XXX or AUDIOHLPFILE_DEFAULT_OPEN_FLAGS.
488 * @param pszNameFmt The base filename.
489 * @param ... Filename format arguments.
490 */
491int AudioHlpFileCreateAndOpenEx(PAUDIOHLPFILE *ppFile, AUDIOHLPFILETYPE enmType, const char *pszDir,
492 uint32_t iInstance, uint32_t fFilename, uint32_t fCreate,
493 PCPDMAUDIOPCMPROPS pProps, uint64_t fOpen, const char *pszNameFmt, ...)
494{
495 *ppFile = NULL;
496
497 for (uint32_t iTry = 0; ; iTry++)
498 {
499 /* Format the path to the filename. */
500 char szFile[RTPATH_MAX];
501 va_list va;
502 va_start(va, pszNameFmt);
503 int rc = audioHlpConstructPathWorker(szFile, sizeof(szFile), pszDir, pszNameFmt, va, iInstance, enmType, fFilename,
504 iTry == 0 ? '\0' : iTry + 'a');
505 va_end(va);
506 AssertRCReturn(rc, rc);
507
508 /* Create an debug audio file instance with the filename path. */
509 PAUDIOHLPFILE pFile = NULL;
510 rc = audioHlpFileCreateWorker(&pFile, fCreate, enmType, szFile);
511 AssertRCReturn(rc, rc);
512
513 /* Try open it. */
514 rc = AudioHlpFileOpen(pFile, fOpen, pProps);
515 if (RT_SUCCESS(rc))
516 {
517 *ppFile = pFile;
518 return rc;
519 }
520 AudioHlpFileDestroy(pFile);
521
522 AssertReturn(iTry < 16, rc);
523 }
524}
525
526
527/**
528 * Creates a debug wav-file structure and opens a file for it, default flags.
529 *
530 * @returns VBox status code.
531 * @param ppFile Where to return the debug file instance on success.
532 * @param pszDir The directory to open the file in.
533 * @param pszName The base filename.
534 * @param iInstance The device/driver instance.
535 * @param pProps PCM audio properties for the file.
536 */
537int AudioHlpFileCreateAndOpen(PAUDIOHLPFILE *ppFile, const char *pszDir, const char *pszName,
538 uint32_t iInstance, PCPDMAUDIOPCMPROPS pProps)
539{
540 return AudioHlpFileCreateAndOpenEx(ppFile, AUDIOHLPFILETYPE_WAV, pszDir, iInstance,
541 AUDIOHLPFILENAME_FLAGS_NONE, AUDIOHLPFILE_FLAGS_NONE,
542 pProps, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS, "%s", pszName);
543}
544
545
546/**
547 * Closes an audio file.
548 *
549 * @returns VBox status code.
550 * @param pFile Audio file handle to close.
551 */
552int AudioHlpFileClose(PAUDIOHLPFILE pFile)
553{
554 if (!pFile || pFile->hFile == NIL_RTFILE)
555 return VINF_SUCCESS;
556
557 /*
558 * Wave files needs to update the data size and file size in the header.
559 */
560 if (pFile->enmType == AUDIOHLPFILETYPE_WAV)
561 {
562 uint32_t const cbFile = sizeof(AUDIOWAVEFILEHDR) - sizeof(RTRIFFCHUNK) + (uint32_t)pFile->cbWaveData;
563 uint32_t const cbData = (uint32_t)pFile->cbWaveData;
564
565 int rc2;
566 rc2 = RTFileWriteAt(pFile->hFile, RT_UOFFSETOF(AUDIOWAVEFILEHDR, Hdr.cbFile), &cbFile, sizeof(cbFile), NULL);
567 AssertRC(rc2);
568 rc2 = RTFileWriteAt(pFile->hFile, RT_UOFFSETOF(AUDIOWAVEFILEHDR, Data.cbChunk), &cbData, sizeof(cbData), NULL);
569 AssertRC(rc2);
570 }
571
572 /*
573 * Do the closing.
574 */
575 int rc = RTFileClose(pFile->hFile);
576 if (RT_SUCCESS(rc) || rc == VERR_INVALID_HANDLE)
577 pFile->hFile = NIL_RTFILE;
578
579 if (RT_SUCCESS(rc))
580 LogRel2(("Audio: Closed file '%s' (%'RU64 bytes PCM data)\n", pFile->szName, pFile->cbWaveData));
581 else
582 LogRel(("Audio: Failed closing file '%s': %Rrc\n", pFile->szName, rc));
583
584 /*
585 * Delete empty file if requested.
586 */
587 if ( !(pFile->fFlags & AUDIOHLPFILE_FLAGS_KEEP_IF_EMPTY)
588 && pFile->cbWaveData == 0
589 && RT_SUCCESS(rc))
590 AudioHlpFileDelete(pFile);
591
592 return rc;
593}
594
595
596/**
597 * Deletes an audio file.
598 *
599 * @returns VBox status code.
600 * @param pFile Audio file to delete.
601 */
602int AudioHlpFileDelete(PAUDIOHLPFILE pFile)
603{
604 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
605
606 int rc = RTFileDelete(pFile->szName);
607 if (RT_SUCCESS(rc))
608 LogRel2(("Audio: Deleted file '%s'\n", pFile->szName));
609 else if (rc == VERR_FILE_NOT_FOUND) /* Don't bitch if the file is not around anymore. */
610 rc = VINF_SUCCESS;
611
612 if (RT_FAILURE(rc))
613 LogRel(("Audio: Failed deleting file '%s', rc=%Rrc\n", pFile->szName, rc));
614
615 return rc;
616}
617
618
619/**
620 * Returns whether the given audio file is open and in use or not.
621 *
622 * @returns True if open, false if not.
623 * @param pFile Audio file to check open status for.
624 */
625bool AudioHlpFileIsOpen(PAUDIOHLPFILE pFile)
626{
627 if (!pFile || pFile->hFile == NIL_RTFILE)
628 return false;
629
630 return RTFileIsValid(pFile->hFile);
631}
632
633
634/**
635 * Write PCM data to a wave (.WAV) file.
636 *
637 * @returns VBox status code.
638 * @param pFile Audio file to write PCM data to.
639 * @param pvBuf Audio data to write.
640 * @param cbBuf Size (in bytes) of audio data to write.
641 */
642int AudioHlpFileWrite(PAUDIOHLPFILE pFile, const void *pvBuf, size_t cbBuf)
643{
644 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
645 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
646
647 if (!cbBuf)
648 return VINF_SUCCESS;
649
650 int rc = RTFileWrite(pFile->hFile, pvBuf, cbBuf, NULL);
651 if (RT_SUCCESS(rc))
652 pFile->cbWaveData += cbBuf;
653
654 return rc;
655}
656
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette