VirtualBox

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

Last change on this file since 94267 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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