VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvAudioCommon.cpp@ 88034

Last change on this file since 88034 was 88034, checked in by vboxsync, 4 years ago

Audio: Converted the core audio code to extending PDMAUDIODEVICE rather than the pvData scheme. Eliminated PDMAUDIODEVICE::pvData and added a magic. Untested. [fix] bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.3 KB
Line 
1/* $Id: DrvAudioCommon.cpp 88034 2021-03-09 01:32:50Z vboxsync $ */
2/** @file
3 * Intermedia audio driver, common routines.
4 *
5 * These are also used in the drivers which are bound to Main, e.g. the VRDE
6 * or the video audio recording drivers.
7 */
8
9/*
10 * Copyright (C) 2006-2020 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 */
20
21
22/*********************************************************************************************************************************
23* Header Files *
24*********************************************************************************************************************************/
25#include <iprt/alloc.h>
26#include <iprt/asm-math.h>
27#include <iprt/assert.h>
28#include <iprt/dir.h>
29#include <iprt/file.h>
30#include <iprt/string.h>
31#include <iprt/uuid.h>
32
33#define LOG_GROUP LOG_GROUP_DRV_AUDIO
34#include <VBox/log.h>
35
36#include <VBox/err.h>
37#include <VBox/vmm/pdmdev.h>
38#include <VBox/vmm/pdm.h>
39#include <VBox/vmm/pdmaudioinline.h>
40#include <VBox/vmm/mm.h>
41
42#include <ctype.h>
43#include <stdlib.h>
44
45#include "DrvAudio.h"
46#include "AudioMixBuffer.h"
47
48
49/*********************************************************************************************************************************
50* Structures and Typedefs *
51*********************************************************************************************************************************/
52/**
53 * Structure for building up a .WAV file header.
54 */
55typedef struct AUDIOWAVFILEHDR
56{
57 uint32_t u32RIFF;
58 uint32_t u32Size;
59 uint32_t u32WAVE;
60
61 uint32_t u32Fmt;
62 uint32_t u32Size1;
63 uint16_t u16AudioFormat;
64 uint16_t u16NumChannels;
65 uint32_t u32SampleRate;
66 uint32_t u32ByteRate;
67 uint16_t u16BlockAlign;
68 uint16_t u16BitsPerSample;
69
70 uint32_t u32ID2;
71 uint32_t u32Size2;
72} AUDIOWAVFILEHDR, *PAUDIOWAVFILEHDR;
73AssertCompileSize(AUDIOWAVFILEHDR, 11*4);
74
75/**
76 * Structure for keeeping the internal .WAV file data
77 */
78typedef struct AUDIOWAVFILEDATA
79{
80 /** The file header/footer. */
81 AUDIOWAVFILEHDR Hdr;
82} AUDIOWAVFILEDATA, *PAUDIOWAVFILEDATA;
83
84
85
86#if 0 /* unused, no header prototypes */
87
88/**
89 * Retrieves the matching PDMAUDIOFMT for the given bits + signing flag.
90 *
91 * @return Matching PDMAUDIOFMT value.
92 * @retval PDMAUDIOFMT_INVALID if unsupported @a cBits value.
93 *
94 * @param cBits The number of bits in the audio format.
95 * @param fSigned Whether the audio format is signed @c true or not.
96 */
97PDMAUDIOFMT DrvAudioAudFmtBitsToFormat(uint8_t cBits, bool fSigned)
98{
99 if (fSigned)
100 {
101 switch (cBits)
102 {
103 case 8: return PDMAUDIOFMT_S8;
104 case 16: return PDMAUDIOFMT_S16;
105 case 32: return PDMAUDIOFMT_S32;
106 default: AssertMsgFailedReturn(("Bogus audio bits %RU8\n", cBits), PDMAUDIOFMT_INVALID);
107 }
108 }
109 else
110 {
111 switch (cBits)
112 {
113 case 8: return PDMAUDIOFMT_U8;
114 case 16: return PDMAUDIOFMT_U16;
115 case 32: return PDMAUDIOFMT_U32;
116 default: AssertMsgFailedReturn(("Bogus audio bits %RU8\n", cBits), PDMAUDIOFMT_INVALID);
117 }
118 }
119}
120
121/**
122 * Returns an unique file name for this given audio connector instance.
123 *
124 * @return Allocated file name. Must be free'd using RTStrFree().
125 * @param uInstance Driver / device instance.
126 * @param pszPath Path name of the file to delete. The path must exist.
127 * @param pszSuffix File name suffix to use.
128 */
129char *DrvAudioDbgGetFileNameA(uint8_t uInstance, const char *pszPath, const char *pszSuffix)
130{
131 char szFileName[64];
132 RTStrPrintf(szFileName, sizeof(szFileName), "drvAudio%RU8-%s", uInstance, pszSuffix);
133
134 char szFilePath[RTPATH_MAX];
135 int rc2 = RTStrCopy(szFilePath, sizeof(szFilePath), pszPath);
136 AssertRC(rc2);
137 rc2 = RTPathAppend(szFilePath, sizeof(szFilePath), szFileName);
138 AssertRC(rc2);
139
140 return RTStrDup(szFilePath);
141}
142
143#endif /* unused */
144
145/**
146 * Allocates an audio device.
147 *
148 * @returns Newly allocated audio device, or NULL on failure.
149 * @param cb The total device structure size. This must be at least the
150 * size of PDMAUDIODEVICE. The idea is that the caller extends
151 * the PDMAUDIODEVICE structure and appends additional data
152 * after it in its private structure.
153 */
154PPDMAUDIODEVICE PDMAudioDeviceAlloc(size_t cb)
155{
156 AssertReturn(cb >= sizeof(PDMAUDIODEVICE), NULL);
157 AssertReturn(cb < _4M, NULL);
158
159 PPDMAUDIODEVICE pDev = (PPDMAUDIODEVICE)RTMemAllocZ(RT_ALIGN_Z(cb, 64));
160 if (pDev)
161 {
162 pDev->uMagic = PDMAUDIODEVICE_MAGIC;
163 pDev->cbData = (uint32_t)(cb - sizeof(PDMAUDIODEVICE));
164 RTListInit(&pDev->Node);
165
166 //pDev->cMaxInputChannels = 0;
167 //pDev->cMaxOutputChannels = 0;
168 }
169 return pDev;
170}
171
172/**
173 * Frees an audio device allocated by PDMAudioDeviceAlloc.
174 *
175 * @param pDev The device to free. NULL is ignored.
176 */
177void PDMAudioDeviceFree(PPDMAUDIODEVICE pDev)
178{
179 if (pDev)
180 {
181 Assert(pDev->uMagic == PDMAUDIODEVICE_MAGIC);
182 Assert(pDev->cRefCount == 0);
183 pDev->uMagic = PDMAUDIODEVICE_MAGIC_DEAD;
184 pDev->cbData = 0;
185
186 RTMemFree(pDev);
187 }
188}
189
190/**
191 * Duplicates an audio device entry.
192 *
193 * @returns Duplicated audio device entry on success, or NULL on failure.
194 * @param pDev The audio device enum entry to duplicate.
195 * @param fCopyUserData Whether to also copy the user data portion or not.
196 */
197PPDMAUDIODEVICE PDMAudioDeviceDup(PPDMAUDIODEVICE pDev, bool fCopyUserData)
198{
199 AssertPtrReturn(pDev, NULL);
200
201 PPDMAUDIODEVICE pDevDup = PDMAudioDeviceAlloc(sizeof(*pDev) + (fCopyUserData ? pDev->cbData : 0));
202 if (pDevDup)
203 {
204 memcpy(pDevDup, pDev, sizeof(PDMAUDIODEVICE));
205 RTListInit(&pDevDup->Node);
206
207 if ( fCopyUserData
208 && pDev->cbData)
209 {
210 /** @todo r=bird: This ASSUMES that no special data is stored here, like
211 * pointers or similar that cannot just be memcpy'ied. */
212 memcpy(pDevDup + 1, pDev + 1, pDev->cbData);
213 }
214 }
215
216 return pDevDup;
217}
218
219/**
220 * Initializes an audio device enumeration structure.
221 *
222 * @returns IPRT status code.
223 * @param pDevEnm Device enumeration to initialize.
224 */
225int DrvAudioHlpDeviceEnumInit(PPDMAUDIODEVICEENUM pDevEnm)
226{
227 AssertPtrReturn(pDevEnm, VERR_INVALID_POINTER);
228
229 RTListInit(&pDevEnm->LstDevices);
230 pDevEnm->cDevices = 0;
231
232 return VINF_SUCCESS;
233}
234
235/**
236 * Frees audio device enumeration data.
237 *
238 * @param pDevEnm Device enumeration to destroy.
239 */
240void DrvAudioHlpDeviceEnumFree(PPDMAUDIODEVICEENUM pDevEnm)
241{
242 if (!pDevEnm)
243 return;
244
245 PPDMAUDIODEVICE pDev, pDevNext;
246 RTListForEachSafe(&pDevEnm->LstDevices, pDev, pDevNext, PDMAUDIODEVICE, Node)
247 {
248 RTListNodeRemove(&pDev->Node);
249
250 PDMAudioDeviceFree(pDev);
251
252 pDevEnm->cDevices--;
253 }
254
255 /* Sanity. */
256 Assert(RTListIsEmpty(&pDevEnm->LstDevices));
257 Assert(pDevEnm->cDevices == 0);
258}
259
260/**
261 * Adds an audio device to a device enumeration.
262 *
263 * @return IPRT status code.
264 * @param pDevEnm Device enumeration to add device to.
265 * @param pDev Device to add. The pointer will be owned by the device enumeration then.
266 */
267int DrvAudioHlpDeviceEnumAdd(PPDMAUDIODEVICEENUM pDevEnm, PPDMAUDIODEVICE pDev)
268{
269 AssertPtrReturn(pDevEnm, VERR_INVALID_POINTER);
270 AssertPtrReturn(pDev, VERR_INVALID_POINTER);
271
272 RTListAppend(&pDevEnm->LstDevices, &pDev->Node);
273 pDevEnm->cDevices++;
274
275 return VINF_SUCCESS;
276}
277
278/**
279 * Duplicates a device enumeration.
280 *
281 * @returns Duplicated device enumeration, or NULL on failure.
282 * Must be free'd with DrvAudioHlpDeviceEnumFree().
283 * @param pDevEnm Device enumeration to duplicate.
284 */
285PPDMAUDIODEVICEENUM DrvAudioHlpDeviceEnumDup(const PPDMAUDIODEVICEENUM pDevEnm)
286{
287 AssertPtrReturn(pDevEnm, NULL);
288
289 PPDMAUDIODEVICEENUM pDevEnmDup = (PPDMAUDIODEVICEENUM)RTMemAlloc(sizeof(PDMAUDIODEVICEENUM));
290 if (!pDevEnmDup)
291 return NULL;
292
293 int rc2 = DrvAudioHlpDeviceEnumInit(pDevEnmDup);
294 AssertRC(rc2);
295
296 PPDMAUDIODEVICE pDev;
297 RTListForEach(&pDevEnm->LstDevices, pDev, PDMAUDIODEVICE, Node)
298 {
299 PPDMAUDIODEVICE pDevDup = PDMAudioDeviceDup(pDev, true /* fCopyUserData */);
300 if (!pDevDup)
301 {
302 rc2 = VERR_NO_MEMORY;
303 break;
304 }
305
306 rc2 = DrvAudioHlpDeviceEnumAdd(pDevEnmDup, pDevDup);
307 if (RT_FAILURE(rc2))
308 {
309 PDMAudioDeviceFree(pDevDup);
310 break;
311 }
312 }
313
314 if (RT_FAILURE(rc2))
315 {
316 DrvAudioHlpDeviceEnumFree(pDevEnmDup);
317 pDevEnmDup = NULL;
318 }
319
320 return pDevEnmDup;
321}
322
323/**
324 * Copies device enumeration entries from the source to the destination enumeration.
325 *
326 * @returns IPRT status code.
327 * @param pDstDevEnm Destination enumeration to store enumeration entries into.
328 * @param pSrcDevEnm Source enumeration to use.
329 * @param enmUsage Which entries to copy. Specify PDMAUDIODIR_DUPLEX to copy all entries.
330 * @param fCopyUserData Whether to also copy the user data portion or not.
331 */
332int DrvAudioHlpDeviceEnumCopyEx(PPDMAUDIODEVICEENUM pDstDevEnm, const PPDMAUDIODEVICEENUM pSrcDevEnm,
333 PDMAUDIODIR enmUsage, bool fCopyUserData)
334{
335 AssertPtrReturn(pDstDevEnm, VERR_INVALID_POINTER);
336 AssertPtrReturn(pSrcDevEnm, VERR_INVALID_POINTER);
337
338 int rc = VINF_SUCCESS;
339
340 PPDMAUDIODEVICE pSrcDev;
341 RTListForEach(&pSrcDevEnm->LstDevices, pSrcDev, PDMAUDIODEVICE, Node)
342 {
343 if ( enmUsage != PDMAUDIODIR_DUPLEX
344 && enmUsage != pSrcDev->enmUsage)
345 {
346 continue;
347 }
348
349 PPDMAUDIODEVICE pDstDev = PDMAudioDeviceDup(pSrcDev, fCopyUserData);
350 if (!pDstDev)
351 {
352 rc = VERR_NO_MEMORY;
353 break;
354 }
355
356 rc = DrvAudioHlpDeviceEnumAdd(pDstDevEnm, pDstDev);
357 if (RT_FAILURE(rc))
358 break;
359 }
360
361 return rc;
362}
363
364/**
365 * Copies all device enumeration entries from the source to the destination enumeration.
366 *
367 * Note: Does *not* copy the user-specific data assigned to a device enumeration entry.
368 * To do so, use DrvAudioHlpDeviceEnumCopyEx().
369 *
370 * @returns IPRT status code.
371 * @param pDstDevEnm Destination enumeration to store enumeration entries into.
372 * @param pSrcDevEnm Source enumeration to use.
373 */
374int DrvAudioHlpDeviceEnumCopy(PPDMAUDIODEVICEENUM pDstDevEnm, const PPDMAUDIODEVICEENUM pSrcDevEnm)
375{
376 return DrvAudioHlpDeviceEnumCopyEx(pDstDevEnm, pSrcDevEnm, PDMAUDIODIR_DUPLEX, false /* fCopyUserData */);
377}
378
379/**
380 * Returns the default device of a given device enumeration.
381 * This assumes that only one default device per usage is set.
382 *
383 * @returns Default device if found, or NULL if none found.
384 * @param pDevEnm Device enumeration to get default device for.
385 * @param enmUsage Usage to get default device for.
386 */
387PPDMAUDIODEVICE DrvAudioHlpDeviceEnumGetDefaultDevice(const PPDMAUDIODEVICEENUM pDevEnm, PDMAUDIODIR enmUsage)
388{
389 AssertPtrReturn(pDevEnm, NULL);
390
391 PPDMAUDIODEVICE pDev;
392 RTListForEach(&pDevEnm->LstDevices, pDev, PDMAUDIODEVICE, Node)
393 {
394 if (enmUsage != PDMAUDIODIR_DUPLEX)
395 {
396 if (enmUsage != pDev->enmUsage) /* Wrong usage? Skip. */
397 continue;
398 }
399
400 if (pDev->fFlags & PDMAUDIODEV_FLAGS_DEFAULT)
401 return pDev;
402 }
403
404 return NULL;
405}
406
407/**
408 * Returns the number of enumerated devices of a given device enumeration.
409 *
410 * @returns Number of devices if found, or 0 if none found.
411 * @param pDevEnm Device enumeration to get default device for.
412 * @param enmUsage Usage to get default device for.
413 */
414uint16_t DrvAudioHlpDeviceEnumGetDeviceCount(const PPDMAUDIODEVICEENUM pDevEnm, PDMAUDIODIR enmUsage)
415{
416 AssertPtrReturn(pDevEnm, 0);
417
418 if (enmUsage == PDMAUDIODIR_DUPLEX)
419 return pDevEnm->cDevices;
420
421 uint32_t cDevs = 0;
422
423 PPDMAUDIODEVICE pDev;
424 RTListForEach(&pDevEnm->LstDevices, pDev, PDMAUDIODEVICE, Node)
425 {
426 if (enmUsage == pDev->enmUsage)
427 cDevs++;
428 }
429
430 return cDevs;
431}
432
433/**
434 * Logs an audio device enumeration.
435 *
436 * @param pszDesc Logging description.
437 * @param pDevEnm Device enumeration to log.
438 */
439void DrvAudioHlpDeviceEnumPrint(const char *pszDesc, const PPDMAUDIODEVICEENUM pDevEnm)
440{
441 AssertPtrReturnVoid(pszDesc);
442 AssertPtrReturnVoid(pDevEnm);
443
444 LogFunc(("%s: %RU16 devices\n", pszDesc, pDevEnm->cDevices));
445
446 PPDMAUDIODEVICE pDev;
447 RTListForEach(&pDevEnm->LstDevices, pDev, PDMAUDIODEVICE, Node)
448 {
449 char *pszFlags = DrvAudioHlpAudDevFlagsToStrA(pDev->fFlags);
450
451 LogFunc(("Device '%s':\n", pDev->szName));
452 LogFunc((" Usage = %s\n", PDMAudioDirGetName(pDev->enmUsage)));
453 LogFunc((" Flags = %s\n", pszFlags ? pszFlags : "<NONE>"));
454 LogFunc((" Input channels = %RU8\n", pDev->cMaxInputChannels));
455 LogFunc((" Output channels = %RU8\n", pDev->cMaxOutputChannels));
456 LogFunc((" Data = %RU32 bytes\n", pDev->cbData));
457
458 if (pszFlags)
459 RTStrFree(pszFlags);
460 }
461}
462
463/**
464 * Converts an audio device flags to a string.
465 *
466 * @returns Stringified audio flags. Must be free'd with RTStrFree().
467 * NULL if no flags set.
468 * @param fFlags Audio flags (PDMAUDIODEV_FLAGS_XXX) to convert.
469 */
470char *DrvAudioHlpAudDevFlagsToStrA(uint32_t fFlags)
471{
472#define APPEND_FLAG_TO_STR(_aFlag) \
473 if (fFlags & PDMAUDIODEV_FLAGS_##_aFlag) \
474 { \
475 if (pszFlags) \
476 { \
477 rc2 = RTStrAAppend(&pszFlags, " "); \
478 if (RT_FAILURE(rc2)) \
479 break; \
480 } \
481 \
482 rc2 = RTStrAAppend(&pszFlags, #_aFlag); \
483 if (RT_FAILURE(rc2)) \
484 break; \
485 } \
486
487 char *pszFlags = NULL;
488 int rc2 = VINF_SUCCESS;
489
490 do
491 {
492 APPEND_FLAG_TO_STR(DEFAULT);
493 APPEND_FLAG_TO_STR(HOTPLUG);
494 APPEND_FLAG_TO_STR(BUGGY);
495 APPEND_FLAG_TO_STR(IGNORE);
496 APPEND_FLAG_TO_STR(LOCKED);
497 APPEND_FLAG_TO_STR(DEAD);
498
499 } while (0);
500
501 if (!pszFlags)
502 rc2 = RTStrAAppend(&pszFlags, "NONE");
503
504 if ( RT_FAILURE(rc2)
505 && pszFlags)
506 {
507 RTStrFree(pszFlags);
508 pszFlags = NULL;
509 }
510
511#undef APPEND_FLAG_TO_STR
512
513 return pszFlags;
514}
515
516/**
517 * Converts a given string to an audio format.
518 *
519 * @returns Audio format for the given string, or PDMAUDIOFMT_INVALID if not found.
520 * @param pszFmt String to convert to an audio format.
521 */
522PDMAUDIOFMT DrvAudioHlpStrToAudFmt(const char *pszFmt)
523{
524 AssertPtrReturn(pszFmt, PDMAUDIOFMT_INVALID);
525
526 if (!RTStrICmp(pszFmt, "u8"))
527 return PDMAUDIOFMT_U8;
528 if (!RTStrICmp(pszFmt, "u16"))
529 return PDMAUDIOFMT_U16;
530 if (!RTStrICmp(pszFmt, "u32"))
531 return PDMAUDIOFMT_U32;
532 if (!RTStrICmp(pszFmt, "s8"))
533 return PDMAUDIOFMT_S8;
534 if (!RTStrICmp(pszFmt, "s16"))
535 return PDMAUDIOFMT_S16;
536 if (!RTStrICmp(pszFmt, "s32"))
537 return PDMAUDIOFMT_S32;
538
539 AssertMsgFailed(("Invalid audio format '%s'\n", pszFmt));
540 return PDMAUDIOFMT_INVALID;
541}
542
543/**
544 * Checks whether a given stream configuration is valid or not.
545 *
546 * @note See notes on DrvAudioHlpPcmPropsAreValid().
547 *
548 * Returns @c true if configuration is valid, @c false if not.
549 * @param pCfg Stream configuration to check.
550 */
551bool DrvAudioHlpStreamCfgIsValid(PCPDMAUDIOSTREAMCFG pCfg)
552{
553 AssertPtrReturn(pCfg, false);
554
555 AssertReturn(PDMAudioStrmCfgIsValid(pCfg), false);
556
557 bool fValid = ( pCfg->enmDir == PDMAUDIODIR_IN
558 || pCfg->enmDir == PDMAUDIODIR_OUT);
559
560 fValid &= ( pCfg->enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED
561 || pCfg->enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
562
563 if (fValid)
564 fValid = DrvAudioHlpPcmPropsAreValid(&pCfg->Props);
565
566 return fValid;
567}
568
569/**
570 * Calculates the audio bit rate of the given bits per sample, the Hz and the number
571 * of audio channels.
572 *
573 * Divide the result by 8 to get the byte rate.
574 *
575 * @returns Bitrate.
576 * @param cBits Number of bits per sample.
577 * @param uHz Hz (Hertz) rate.
578 * @param cChannels Number of audio channels.
579 */
580uint32_t DrvAudioHlpCalcBitrate(uint8_t cBits, uint32_t uHz, uint8_t cChannels)
581{
582 return cBits * uHz * cChannels;
583}
584
585
586/**
587 * Checks whether given PCM properties are valid or not.
588 *
589 * @note This is more of a supported than valid check. There is code for
590 * unsigned samples elsewhere (like DrvAudioHlpClearBuf()), but this
591 * function will flag such properties as not valid.
592 *
593 * @todo r=bird: See note and explain properly.
594 *
595 * @returns @c true if the properties are valid, @c false if not.
596 * @param pProps The PCM properties to check.
597 */
598bool DrvAudioHlpPcmPropsAreValid(PCPDMAUDIOPCMPROPS pProps)
599{
600 AssertPtrReturn(pProps, false);
601
602 AssertReturn(PDMAudioPropsAreValid(pProps), false);
603
604 /** @todo r=bird: This code is cannot make up its mind whether to return on
605 * false, or whether to return at the end. (hint: just return
606 * immediately, duh.) */
607
608 /* Minimum 1 channel (mono), maximum 7.1 (= 8) channels. */
609 bool fValid = ( pProps->cChannels >= 1
610 && pProps->cChannels <= 8);
611
612 if (fValid)
613 {
614 switch (pProps->cbSample)
615 {
616 case 1: /* 8 bit */
617 if (pProps->fSigned)
618 fValid = false;
619 break;
620 case 2: /* 16 bit */
621 if (!pProps->fSigned)
622 fValid = false;
623 break;
624 /** @todo Do we need support for 24 bit samples? */
625 case 4: /* 32 bit */
626 if (!pProps->fSigned)
627 fValid = false;
628 break;
629 default:
630 fValid = false;
631 break;
632 }
633 }
634
635 if (!fValid)
636 return false;
637
638 fValid &= pProps->uHz > 0;
639 fValid &= pProps->cShift == PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pProps->cbSample, pProps->cChannels);
640 fValid &= pProps->fSwapEndian == false; /** @todo Handling Big Endian audio data is not supported yet. */
641
642 return fValid;
643}
644
645
646/*********************************************************************************************************************************
647* Audio File Helpers *
648*********************************************************************************************************************************/
649
650/**
651 * Sanitizes the file name component so that unsupported characters
652 * will be replaced by an underscore ("_").
653 *
654 * @return IPRT status code.
655 * @param pszPath Path to sanitize.
656 * @param cbPath Size (in bytes) of path to sanitize.
657 */
658int DrvAudioHlpFileNameSanitize(char *pszPath, size_t cbPath)
659{
660 RT_NOREF(cbPath);
661 int rc = VINF_SUCCESS;
662#ifdef RT_OS_WINDOWS
663 /* Filter out characters not allowed on Windows platforms, put in by
664 RTTimeSpecToString(). */
665 /** @todo Use something like RTPathSanitize() if available later some time. */
666 static RTUNICP const s_uszValidRangePairs[] =
667 {
668 ' ', ' ',
669 '(', ')',
670 '-', '.',
671 '0', '9',
672 'A', 'Z',
673 'a', 'z',
674 '_', '_',
675 0xa0, 0xd7af,
676 '\0'
677 };
678 ssize_t cReplaced = RTStrPurgeComplementSet(pszPath, s_uszValidRangePairs, '_' /* Replacement */);
679 if (cReplaced < 0)
680 rc = VERR_INVALID_UTF8_ENCODING;
681#else
682 RT_NOREF(pszPath);
683#endif
684 return rc;
685}
686
687/**
688 * Constructs an unique file name, based on the given path and the audio file type.
689 *
690 * @returns IPRT status code.
691 * @param pszFile Where to store the constructed file name.
692 * @param cchFile Size (in characters) of the file name buffer.
693 * @param pszPath Base path to use.
694 * If NULL or empty, the system's temporary directory will be used.
695 * @param pszName A name for better identifying the file.
696 * @param uInstance Device / driver instance which is using this file.
697 * @param enmType Audio file type to construct file name for.
698 * @param fFlags File naming flags, PDMAUDIOFILENAME_FLAGS_XXX.
699 */
700int DrvAudioHlpFileNameGet(char *pszFile, size_t cchFile, const char *pszPath, const char *pszName,
701 uint32_t uInstance, PDMAUDIOFILETYPE enmType, uint32_t fFlags)
702{
703 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
704 AssertReturn(cchFile, VERR_INVALID_PARAMETER);
705 /* pszPath can be NULL. */
706 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
707 /** @todo Validate fFlags. */
708
709 int rc;
710
711 char *pszPathTmp = NULL;
712
713 do
714 {
715 if ( pszPath == NULL
716 || !strlen(pszPath))
717 {
718 char szTemp[RTPATH_MAX];
719 rc = RTPathTemp(szTemp, sizeof(szTemp));
720 if (RT_SUCCESS(rc))
721 {
722 pszPathTmp = RTStrDup(szTemp);
723 }
724 else
725 break;
726 }
727 else
728 pszPathTmp = RTStrDup(pszPath);
729
730 AssertPtrBreakStmt(pszPathTmp, rc = VERR_NO_MEMORY);
731
732 char szFilePath[RTPATH_MAX];
733 rc = RTStrCopy(szFilePath, sizeof(szFilePath), pszPathTmp);
734 AssertRCBreak(rc);
735
736 /* Create it when necessary. */
737 if (!RTDirExists(szFilePath))
738 {
739 rc = RTDirCreateFullPath(szFilePath, RTFS_UNIX_IRWXU);
740 if (RT_FAILURE(rc))
741 break;
742 }
743
744 char szFileName[RTPATH_MAX];
745 szFileName[0] = '\0';
746
747 if (fFlags & PDMAUDIOFILENAME_FLAGS_TS)
748 {
749 RTTIMESPEC time;
750 if (!RTTimeSpecToString(RTTimeNow(&time), szFileName, sizeof(szFileName)))
751 {
752 rc = VERR_BUFFER_OVERFLOW;
753 break;
754 }
755
756 rc = DrvAudioHlpFileNameSanitize(szFileName, sizeof(szFileName));
757 if (RT_FAILURE(rc))
758 break;
759
760 rc = RTStrCat(szFileName, sizeof(szFileName), "-");
761 if (RT_FAILURE(rc))
762 break;
763 }
764
765 rc = RTStrCat(szFileName, sizeof(szFileName), pszName);
766 if (RT_FAILURE(rc))
767 break;
768
769 rc = RTStrCat(szFileName, sizeof(szFileName), "-");
770 if (RT_FAILURE(rc))
771 break;
772
773 char szInst[16];
774 RTStrPrintf2(szInst, sizeof(szInst), "%RU32", uInstance);
775 rc = RTStrCat(szFileName, sizeof(szFileName), szInst);
776 if (RT_FAILURE(rc))
777 break;
778
779 switch (enmType)
780 {
781 case PDMAUDIOFILETYPE_RAW:
782 rc = RTStrCat(szFileName, sizeof(szFileName), ".pcm");
783 break;
784
785 case PDMAUDIOFILETYPE_WAV:
786 rc = RTStrCat(szFileName, sizeof(szFileName), ".wav");
787 break;
788
789 default:
790 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
791 break;
792 }
793
794 if (RT_FAILURE(rc))
795 break;
796
797 rc = RTPathAppend(szFilePath, sizeof(szFilePath), szFileName);
798 if (RT_FAILURE(rc))
799 break;
800
801 rc = RTStrCopy(pszFile, cchFile, szFilePath);
802
803 } while (0);
804
805 RTStrFree(pszPathTmp);
806
807 LogFlowFuncLeaveRC(rc);
808 return rc;
809}
810
811/**
812 * Creates an audio file.
813 *
814 * @returns IPRT status code.
815 * @param enmType Audio file type to open / create.
816 * @param pszFile File path of file to open or create.
817 * @param fFlags Audio file flags, PDMAUDIOFILE_FLAGS_XXX.
818 * @param ppFile Where to store the created audio file handle.
819 * Needs to be destroyed with DrvAudioHlpFileDestroy().
820 */
821int DrvAudioHlpFileCreate(PDMAUDIOFILETYPE enmType, const char *pszFile, uint32_t fFlags, PPDMAUDIOFILE *ppFile)
822{
823 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
824 /** @todo Validate fFlags. */
825
826 PPDMAUDIOFILE pFile = (PPDMAUDIOFILE)RTMemAlloc(sizeof(PDMAUDIOFILE));
827 if (!pFile)
828 return VERR_NO_MEMORY;
829
830 int rc = VINF_SUCCESS;
831
832 switch (enmType)
833 {
834 case PDMAUDIOFILETYPE_RAW:
835 case PDMAUDIOFILETYPE_WAV:
836 pFile->enmType = enmType;
837 break;
838
839 default:
840 rc = VERR_INVALID_PARAMETER;
841 break;
842 }
843
844 if (RT_SUCCESS(rc))
845 {
846 RTStrPrintf(pFile->szName, RT_ELEMENTS(pFile->szName), "%s", pszFile);
847 pFile->hFile = NIL_RTFILE;
848 pFile->fFlags = fFlags;
849 pFile->pvData = NULL;
850 pFile->cbData = 0;
851 }
852
853 if (RT_FAILURE(rc))
854 {
855 RTMemFree(pFile);
856 pFile = NULL;
857 }
858 else
859 *ppFile = pFile;
860
861 return rc;
862}
863
864/**
865 * Destroys a formerly created audio file.
866 *
867 * @param pFile Audio file (object) to destroy.
868 */
869void DrvAudioHlpFileDestroy(PPDMAUDIOFILE pFile)
870{
871 if (!pFile)
872 return;
873
874 DrvAudioHlpFileClose(pFile);
875
876 RTMemFree(pFile);
877 pFile = NULL;
878}
879
880/**
881 * Opens or creates an audio file.
882 *
883 * @returns IPRT status code.
884 * @param pFile Pointer to audio file handle to use.
885 * @param fOpen Open flags.
886 * Use PDMAUDIOFILE_DEFAULT_OPEN_FLAGS for the default open flags.
887 * @param pProps PCM properties to use.
888 */
889int DrvAudioHlpFileOpen(PPDMAUDIOFILE pFile, uint32_t fOpen, PCPDMAUDIOPCMPROPS pProps)
890{
891 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
892 /** @todo Validate fOpen flags. */
893 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
894
895 int rc;
896
897 if (pFile->enmType == PDMAUDIOFILETYPE_RAW)
898 {
899 rc = RTFileOpen(&pFile->hFile, pFile->szName, fOpen);
900 }
901 else if (pFile->enmType == PDMAUDIOFILETYPE_WAV)
902 {
903 Assert(pProps->cChannels);
904 Assert(pProps->uHz);
905 Assert(pProps->cbSample);
906
907 pFile->pvData = (PAUDIOWAVFILEDATA)RTMemAllocZ(sizeof(AUDIOWAVFILEDATA));
908 if (pFile->pvData)
909 {
910 pFile->cbData = sizeof(PAUDIOWAVFILEDATA);
911
912 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
913 AssertPtr(pData);
914
915 /* Header. */
916 pData->Hdr.u32RIFF = AUDIO_MAKE_FOURCC('R','I','F','F');
917 pData->Hdr.u32Size = 36;
918 pData->Hdr.u32WAVE = AUDIO_MAKE_FOURCC('W','A','V','E');
919
920 pData->Hdr.u32Fmt = AUDIO_MAKE_FOURCC('f','m','t',' ');
921 pData->Hdr.u32Size1 = 16; /* Means PCM. */
922 pData->Hdr.u16AudioFormat = 1; /* PCM, linear quantization. */
923 pData->Hdr.u16NumChannels = pProps->cChannels;
924 pData->Hdr.u32SampleRate = pProps->uHz;
925 pData->Hdr.u32ByteRate = PDMAudioPropsGetBitrate(pProps) / 8;
926 pData->Hdr.u16BlockAlign = pProps->cChannels * pProps->cbSample;
927 pData->Hdr.u16BitsPerSample = pProps->cbSample * 8;
928
929 /* Data chunk. */
930 pData->Hdr.u32ID2 = AUDIO_MAKE_FOURCC('d','a','t','a');
931 pData->Hdr.u32Size2 = 0;
932
933 rc = RTFileOpen(&pFile->hFile, pFile->szName, fOpen);
934 if (RT_SUCCESS(rc))
935 {
936 rc = RTFileWrite(pFile->hFile, &pData->Hdr, sizeof(pData->Hdr), NULL);
937 if (RT_FAILURE(rc))
938 {
939 RTFileClose(pFile->hFile);
940 pFile->hFile = NIL_RTFILE;
941 }
942 }
943
944 if (RT_FAILURE(rc))
945 {
946 RTMemFree(pFile->pvData);
947 pFile->pvData = NULL;
948 pFile->cbData = 0;
949 }
950 }
951 else
952 rc = VERR_NO_MEMORY;
953 }
954 else
955 rc = VERR_INVALID_PARAMETER;
956
957 if (RT_SUCCESS(rc))
958 {
959 LogRel2(("Audio: Opened file '%s'\n", pFile->szName));
960 }
961 else
962 LogRel(("Audio: Failed opening file '%s', rc=%Rrc\n", pFile->szName, rc));
963
964 return rc;
965}
966
967/**
968 * Closes an audio file.
969 *
970 * @returns IPRT status code.
971 * @param pFile Audio file handle to close.
972 */
973int DrvAudioHlpFileClose(PPDMAUDIOFILE pFile)
974{
975 if (!pFile)
976 return VINF_SUCCESS;
977
978 size_t cbSize = DrvAudioHlpFileGetDataSize(pFile);
979
980 int rc = VINF_SUCCESS;
981
982 if (pFile->enmType == PDMAUDIOFILETYPE_RAW)
983 {
984 if (RTFileIsValid(pFile->hFile))
985 rc = RTFileClose(pFile->hFile);
986 }
987 else if (pFile->enmType == PDMAUDIOFILETYPE_WAV)
988 {
989 if (RTFileIsValid(pFile->hFile))
990 {
991 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
992 if (pData) /* The .WAV file data only is valid when a file actually has been created. */
993 {
994 /* Update the header with the current data size. */
995 RTFileWriteAt(pFile->hFile, 0, &pData->Hdr, sizeof(pData->Hdr), NULL);
996 }
997
998 rc = RTFileClose(pFile->hFile);
999 }
1000
1001 if (pFile->pvData)
1002 {
1003 RTMemFree(pFile->pvData);
1004 pFile->pvData = NULL;
1005 }
1006 }
1007 else
1008 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1009
1010 if ( RT_SUCCESS(rc)
1011 && !cbSize
1012 && !(pFile->fFlags & PDMAUDIOFILE_FLAGS_KEEP_IF_EMPTY))
1013 {
1014 rc = DrvAudioHlpFileDelete(pFile);
1015 }
1016
1017 pFile->cbData = 0;
1018
1019 if (RT_SUCCESS(rc))
1020 {
1021 pFile->hFile = NIL_RTFILE;
1022 LogRel2(("Audio: Closed file '%s' (%zu bytes)\n", pFile->szName, cbSize));
1023 }
1024 else
1025 LogRel(("Audio: Failed closing file '%s', rc=%Rrc\n", pFile->szName, rc));
1026
1027 return rc;
1028}
1029
1030/**
1031 * Deletes an audio file.
1032 *
1033 * @returns IPRT status code.
1034 * @param pFile Audio file handle to delete.
1035 */
1036int DrvAudioHlpFileDelete(PPDMAUDIOFILE pFile)
1037{
1038 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1039
1040 int rc = RTFileDelete(pFile->szName);
1041 if (RT_SUCCESS(rc))
1042 {
1043 LogRel2(("Audio: Deleted file '%s'\n", pFile->szName));
1044 }
1045 else if (rc == VERR_FILE_NOT_FOUND) /* Don't bitch if the file is not around (anymore). */
1046 rc = VINF_SUCCESS;
1047
1048 if (RT_FAILURE(rc))
1049 LogRel(("Audio: Failed deleting file '%s', rc=%Rrc\n", pFile->szName, rc));
1050
1051 return rc;
1052}
1053
1054/**
1055 * Returns the raw audio data size of an audio file.
1056 *
1057 * Note: This does *not* include file headers and other data which does
1058 * not belong to the actual PCM audio data.
1059 *
1060 * @returns Size (in bytes) of the raw PCM audio data.
1061 * @param pFile Audio file handle to retrieve the audio data size for.
1062 */
1063size_t DrvAudioHlpFileGetDataSize(PPDMAUDIOFILE pFile)
1064{
1065 AssertPtrReturn(pFile, 0);
1066
1067 size_t cbSize = 0;
1068
1069 if (pFile->enmType == PDMAUDIOFILETYPE_RAW)
1070 {
1071 cbSize = RTFileTell(pFile->hFile);
1072 }
1073 else if (pFile->enmType == PDMAUDIOFILETYPE_WAV)
1074 {
1075 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1076 if (pData) /* The .WAV file data only is valid when a file actually has been created. */
1077 cbSize = pData->Hdr.u32Size2;
1078 }
1079
1080 return cbSize;
1081}
1082
1083/**
1084 * Returns whether the given audio file is open and in use or not.
1085 *
1086 * @return bool True if open, false if not.
1087 * @param pFile Audio file handle to check open status for.
1088 */
1089bool DrvAudioHlpFileIsOpen(PPDMAUDIOFILE pFile)
1090{
1091 if (!pFile)
1092 return false;
1093
1094 return RTFileIsValid(pFile->hFile);
1095}
1096
1097/**
1098 * Write PCM data to a wave (.WAV) file.
1099 *
1100 * @returns IPRT status code.
1101 * @param pFile Audio file handle to write PCM data to.
1102 * @param pvBuf Audio data to write.
1103 * @param cbBuf Size (in bytes) of audio data to write.
1104 * @param fFlags Additional write flags. Not being used at the moment and must be 0.
1105 */
1106int DrvAudioHlpFileWrite(PPDMAUDIOFILE pFile, const void *pvBuf, size_t cbBuf, uint32_t fFlags)
1107{
1108 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1109 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1110
1111 AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); /** @todo fFlags are currently not implemented. */
1112
1113 if (!cbBuf)
1114 return VINF_SUCCESS;
1115
1116 AssertReturn(RTFileIsValid(pFile->hFile), VERR_WRONG_ORDER);
1117
1118 int rc;
1119
1120 if (pFile->enmType == PDMAUDIOFILETYPE_RAW)
1121 {
1122 rc = RTFileWrite(pFile->hFile, pvBuf, cbBuf, NULL);
1123 }
1124 else if (pFile->enmType == PDMAUDIOFILETYPE_WAV)
1125 {
1126 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1127 AssertPtr(pData);
1128
1129 rc = RTFileWrite(pFile->hFile, pvBuf, cbBuf, NULL);
1130 if (RT_SUCCESS(rc))
1131 {
1132 pData->Hdr.u32Size += (uint32_t)cbBuf;
1133 pData->Hdr.u32Size2 += (uint32_t)cbBuf;
1134 }
1135 }
1136 else
1137 rc = VERR_NOT_SUPPORTED;
1138
1139 return rc;
1140}
1141
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