VirtualBox

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

Last change on this file since 73370 was 73345, checked in by vboxsync, 6 years ago

Audio/DrvAudioCommon.cpp: Fixed DrvAudioHlpFramesToBytes(), d'oh!

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.6 KB
Line 
1/* $Id: DrvAudioCommon.cpp 73345 2018-07-25 08:36:42Z 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-2018 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/mm.h>
40
41#include <ctype.h>
42#include <stdlib.h>
43
44#include "DrvAudio.h"
45#include "AudioMixBuffer.h"
46
47
48/*********************************************************************************************************************************
49* Structures and Typedefs *
50*********************************************************************************************************************************/
51/**
52 * Structure for building up a .WAV file header.
53 */
54typedef struct AUDIOWAVFILEHDR
55{
56 uint32_t u32RIFF;
57 uint32_t u32Size;
58 uint32_t u32WAVE;
59
60 uint32_t u32Fmt;
61 uint32_t u32Size1;
62 uint16_t u16AudioFormat;
63 uint16_t u16NumChannels;
64 uint32_t u32SampleRate;
65 uint32_t u32ByteRate;
66 uint16_t u16BlockAlign;
67 uint16_t u16BitsPerSample;
68
69 uint32_t u32ID2;
70 uint32_t u32Size2;
71} AUDIOWAVFILEHDR, *PAUDIOWAVFILEHDR;
72AssertCompileSize(AUDIOWAVFILEHDR, 11*4);
73
74/**
75 * Structure for keeeping the internal .WAV file data
76 */
77typedef struct AUDIOWAVFILEDATA
78{
79 /** The file header/footer. */
80 AUDIOWAVFILEHDR Hdr;
81} AUDIOWAVFILEDATA, *PAUDIOWAVFILEDATA;
82
83
84
85
86/**
87 * Retrieves the matching PDMAUDIOFMT for given bits + signing flag.
88 *
89 * @return IPRT status code.
90 * @return PDMAUDIOFMT Resulting audio format or PDMAUDIOFMT_INVALID if invalid.
91 * @param cBits Bits to retrieve audio format for.
92 * @param fSigned Signed flag for bits to retrieve audio format for.
93 */
94PDMAUDIOFMT DrvAudioAudFmtBitsToAudFmt(uint8_t cBits, bool fSigned)
95{
96 if (fSigned)
97 {
98 switch (cBits)
99 {
100 case 8: return PDMAUDIOFMT_S8;
101 case 16: return PDMAUDIOFMT_S16;
102 case 32: return PDMAUDIOFMT_S32;
103 default: break;
104 }
105 }
106 else
107 {
108 switch (cBits)
109 {
110 case 8: return PDMAUDIOFMT_U8;
111 case 16: return PDMAUDIOFMT_U16;
112 case 32: return PDMAUDIOFMT_U32;
113 default: break;
114 }
115 }
116
117 AssertMsgFailed(("Bogus audio bits %RU8\n", cBits));
118 return PDMAUDIOFMT_INVALID;
119}
120
121/**
122 * Clears a sample buffer by the given amount of audio samples.
123 *
124 * @return IPRT status code.
125 * @param pPCMProps PCM properties to use for the buffer to clear.
126 * @param pvBuf Buffer to clear.
127 * @param cbBuf Size (in bytes) of the buffer.
128 * @param cSamples Number of audio samples to clear in the buffer.
129 */
130void DrvAudioHlpClearBuf(const PPDMAUDIOPCMPROPS pPCMProps, void *pvBuf, size_t cbBuf, uint32_t cSamples)
131{
132 AssertPtrReturnVoid(pPCMProps);
133 AssertPtrReturnVoid(pvBuf);
134
135 if (!cbBuf || !cSamples)
136 return;
137
138 Assert(pPCMProps->cBits);
139 size_t cbToClear = cSamples * (pPCMProps->cBits / 8 /* Bytes */);
140 Assert(cbBuf >= cbToClear);
141
142 if (cbBuf < cbToClear)
143 cbToClear = cbBuf;
144
145 Log2Func(("pPCMProps=%p, pvBuf=%p, cSamples=%RU32, fSigned=%RTbool, cBits=%RU8\n",
146 pPCMProps, pvBuf, cSamples, pPCMProps->fSigned, pPCMProps->cBits));
147
148 if (pPCMProps->fSigned)
149 {
150 RT_BZERO(pvBuf, cbToClear);
151 }
152 else
153 {
154 switch (pPCMProps->cBits)
155 {
156 case 8:
157 {
158 memset(pvBuf, 0x80, cbToClear);
159 break;
160 }
161
162 case 16:
163 {
164 uint16_t *p = (uint16_t *)pvBuf;
165 int16_t s = INT16_MAX;
166
167 if (pPCMProps->fSwapEndian)
168 s = RT_BSWAP_U16(s);
169
170 for (uint32_t i = 0; i < cSamples; i++)
171 p[i] = s;
172
173 break;
174 }
175
176 case 32:
177 {
178 uint32_t *p = (uint32_t *)pvBuf;
179 int32_t s = INT32_MAX;
180
181 if (pPCMProps->fSwapEndian)
182 s = RT_BSWAP_U32(s);
183
184 for (uint32_t i = 0; i < cSamples; i++)
185 p[i] = s;
186
187 break;
188 }
189
190 default:
191 {
192 AssertMsgFailed(("Invalid bits: %RU8\n", pPCMProps->cBits));
193 break;
194 }
195 }
196 }
197}
198
199/**
200 * Returns an unique file name for this given audio connector instance.
201 *
202 * @return Allocated file name. Must be free'd using RTStrFree().
203 * @param uInstance Driver / device instance.
204 * @param pszPath Path name of the file to delete. The path must exist.
205 * @param pszSuffix File name suffix to use.
206 */
207char *DrvAudioDbgGetFileNameA(uint8_t uInstance, const char *pszPath, const char *pszSuffix)
208{
209 char szFileName[64];
210 RTStrPrintf(szFileName, sizeof(szFileName), "drvAudio%RU8-%s", uInstance, pszSuffix);
211
212 char szFilePath[RTPATH_MAX];
213 int rc2 = RTStrCopy(szFilePath, sizeof(szFilePath), pszPath);
214 AssertRC(rc2);
215 rc2 = RTPathAppend(szFilePath, sizeof(szFilePath), szFileName);
216 AssertRC(rc2);
217
218 return RTStrDup(szFilePath);
219}
220
221/**
222 * Allocates an audio device.
223 *
224 * @returns Newly allocated audio device, or NULL if failed.
225 * @param cbData How much additional data (in bytes) should be allocated to provide
226 * a (backend) specific area to store additional data.
227 * Optional, can be 0.
228 */
229PPDMAUDIODEVICE DrvAudioHlpDeviceAlloc(size_t cbData)
230{
231 PPDMAUDIODEVICE pDev = (PPDMAUDIODEVICE)RTMemAllocZ(sizeof(PDMAUDIODEVICE));
232 if (!pDev)
233 return NULL;
234
235 if (cbData)
236 {
237 pDev->pvData = RTMemAllocZ(cbData);
238 if (!pDev->pvData)
239 {
240 RTMemFree(pDev);
241 return NULL;
242 }
243 }
244
245 pDev->cbData = cbData;
246
247 pDev->cMaxInputChannels = 0;
248 pDev->cMaxOutputChannels = 0;
249
250 return pDev;
251}
252
253/**
254 * Frees an audio device.
255 *
256 * @param pDev Device to free.
257 */
258void DrvAudioHlpDeviceFree(PPDMAUDIODEVICE pDev)
259{
260 if (!pDev)
261 return;
262
263 Assert(pDev->cRefCount == 0);
264
265 if (pDev->pvData)
266 {
267 Assert(pDev->cbData);
268
269 RTMemFree(pDev->pvData);
270 pDev->pvData = NULL;
271 }
272
273 RTMemFree(pDev);
274 pDev = NULL;
275}
276
277/**
278 * Duplicates an audio device entry.
279 *
280 * @returns Duplicated audio device entry on success, or NULL on failure.
281 * @param pDev Audio device entry to duplicate.
282 * @param fCopyUserData Whether to also copy the user data portion or not.
283 */
284PPDMAUDIODEVICE DrvAudioHlpDeviceDup(const PPDMAUDIODEVICE pDev, bool fCopyUserData)
285{
286 AssertPtrReturn(pDev, NULL);
287
288 PPDMAUDIODEVICE pDevDup = DrvAudioHlpDeviceAlloc(fCopyUserData ? pDev->cbData : 0);
289 if (pDevDup)
290 {
291 memcpy(pDevDup, pDev, sizeof(PDMAUDIODEVICE));
292
293 if ( fCopyUserData
294 && pDevDup->cbData)
295 {
296 memcpy(pDevDup->pvData, pDev->pvData, pDevDup->cbData);
297 }
298 else
299 {
300 pDevDup->cbData = 0;
301 pDevDup->pvData = NULL;
302 }
303 }
304
305 return pDevDup;
306}
307
308/**
309 * Initializes an audio device enumeration structure.
310 *
311 * @returns IPRT status code.
312 * @param pDevEnm Device enumeration to initialize.
313 */
314int DrvAudioHlpDeviceEnumInit(PPDMAUDIODEVICEENUM pDevEnm)
315{
316 AssertPtrReturn(pDevEnm, VERR_INVALID_POINTER);
317
318 RTListInit(&pDevEnm->lstDevices);
319 pDevEnm->cDevices = 0;
320
321 return VINF_SUCCESS;
322}
323
324/**
325 * Frees audio device enumeration data.
326 *
327 * @param pDevEnm Device enumeration to destroy.
328 */
329void DrvAudioHlpDeviceEnumFree(PPDMAUDIODEVICEENUM pDevEnm)
330{
331 if (!pDevEnm)
332 return;
333
334 PPDMAUDIODEVICE pDev, pDevNext;
335 RTListForEachSafe(&pDevEnm->lstDevices, pDev, pDevNext, PDMAUDIODEVICE, Node)
336 {
337 RTListNodeRemove(&pDev->Node);
338
339 DrvAudioHlpDeviceFree(pDev);
340
341 pDevEnm->cDevices--;
342 }
343
344 /* Sanity. */
345 Assert(RTListIsEmpty(&pDevEnm->lstDevices));
346 Assert(pDevEnm->cDevices == 0);
347}
348
349/**
350 * Adds an audio device to a device enumeration.
351 *
352 * @return IPRT status code.
353 * @param pDevEnm Device enumeration to add device to.
354 * @param pDev Device to add. The pointer will be owned by the device enumeration then.
355 */
356int DrvAudioHlpDeviceEnumAdd(PPDMAUDIODEVICEENUM pDevEnm, PPDMAUDIODEVICE pDev)
357{
358 AssertPtrReturn(pDevEnm, VERR_INVALID_POINTER);
359 AssertPtrReturn(pDev, VERR_INVALID_POINTER);
360
361 RTListAppend(&pDevEnm->lstDevices, &pDev->Node);
362 pDevEnm->cDevices++;
363
364 return VINF_SUCCESS;
365}
366
367/**
368 * Duplicates a device enumeration.
369 *
370 * @returns Duplicated device enumeration, or NULL on failure.
371 * Must be free'd with DrvAudioHlpDeviceEnumFree().
372 * @param pDevEnm Device enumeration to duplicate.
373 */
374PPDMAUDIODEVICEENUM DrvAudioHlpDeviceEnumDup(const PPDMAUDIODEVICEENUM pDevEnm)
375{
376 AssertPtrReturn(pDevEnm, NULL);
377
378 PPDMAUDIODEVICEENUM pDevEnmDup = (PPDMAUDIODEVICEENUM)RTMemAlloc(sizeof(PDMAUDIODEVICEENUM));
379 if (!pDevEnmDup)
380 return NULL;
381
382 int rc2 = DrvAudioHlpDeviceEnumInit(pDevEnmDup);
383 AssertRC(rc2);
384
385 PPDMAUDIODEVICE pDev;
386 RTListForEach(&pDevEnm->lstDevices, pDev, PDMAUDIODEVICE, Node)
387 {
388 PPDMAUDIODEVICE pDevDup = DrvAudioHlpDeviceDup(pDev, true /* fCopyUserData */);
389 if (!pDevDup)
390 {
391 rc2 = VERR_NO_MEMORY;
392 break;
393 }
394
395 rc2 = DrvAudioHlpDeviceEnumAdd(pDevEnmDup, pDevDup);
396 if (RT_FAILURE(rc2))
397 {
398 DrvAudioHlpDeviceFree(pDevDup);
399 break;
400 }
401 }
402
403 if (RT_FAILURE(rc2))
404 {
405 DrvAudioHlpDeviceEnumFree(pDevEnmDup);
406 pDevEnmDup = NULL;
407 }
408
409 return pDevEnmDup;
410}
411
412/**
413 * Copies device enumeration entries from the source to the destination enumeration.
414 *
415 * @returns IPRT status code.
416 * @param pDstDevEnm Destination enumeration to store enumeration entries into.
417 * @param pSrcDevEnm Source enumeration to use.
418 * @param enmUsage Which entries to copy. Specify PDMAUDIODIR_ANY to copy all entries.
419 * @param fCopyUserData Whether to also copy the user data portion or not.
420 */
421int DrvAudioHlpDeviceEnumCopyEx(PPDMAUDIODEVICEENUM pDstDevEnm, const PPDMAUDIODEVICEENUM pSrcDevEnm,
422 PDMAUDIODIR enmUsage, bool fCopyUserData)
423{
424 AssertPtrReturn(pDstDevEnm, VERR_INVALID_POINTER);
425 AssertPtrReturn(pSrcDevEnm, VERR_INVALID_POINTER);
426
427 int rc = VINF_SUCCESS;
428
429 PPDMAUDIODEVICE pSrcDev;
430 RTListForEach(&pSrcDevEnm->lstDevices, pSrcDev, PDMAUDIODEVICE, Node)
431 {
432 if ( enmUsage != PDMAUDIODIR_ANY
433 && enmUsage != pSrcDev->enmUsage)
434 {
435 continue;
436 }
437
438 PPDMAUDIODEVICE pDstDev = DrvAudioHlpDeviceDup(pSrcDev, fCopyUserData);
439 if (!pDstDev)
440 {
441 rc = VERR_NO_MEMORY;
442 break;
443 }
444
445 rc = DrvAudioHlpDeviceEnumAdd(pDstDevEnm, pDstDev);
446 if (RT_FAILURE(rc))
447 break;
448 }
449
450 return rc;
451}
452
453/**
454 * Copies all device enumeration entries from the source to the destination enumeration.
455 *
456 * Note: Does *not* copy the user-specific data assigned to a device enumeration entry.
457 * To do so, use DrvAudioHlpDeviceEnumCopyEx().
458 *
459 * @returns IPRT status code.
460 * @param pDstDevEnm Destination enumeration to store enumeration entries into.
461 * @param pSrcDevEnm Source enumeration to use.
462 */
463int DrvAudioHlpDeviceEnumCopy(PPDMAUDIODEVICEENUM pDstDevEnm, const PPDMAUDIODEVICEENUM pSrcDevEnm)
464{
465 return DrvAudioHlpDeviceEnumCopyEx(pDstDevEnm, pSrcDevEnm, PDMAUDIODIR_ANY, false /* fCopyUserData */);
466}
467
468/**
469 * Returns the default device of a given device enumeration.
470 * This assumes that only one default device per usage is set.
471 *
472 * @returns Default device if found, or NULL if none found.
473 * @param pDevEnm Device enumeration to get default device for.
474 * @param enmUsage Usage to get default device for.
475 */
476PPDMAUDIODEVICE DrvAudioHlpDeviceEnumGetDefaultDevice(const PPDMAUDIODEVICEENUM pDevEnm, PDMAUDIODIR enmUsage)
477{
478 AssertPtrReturn(pDevEnm, NULL);
479
480 PPDMAUDIODEVICE pDev;
481 RTListForEach(&pDevEnm->lstDevices, pDev, PDMAUDIODEVICE, Node)
482 {
483 if (enmUsage != PDMAUDIODIR_ANY)
484 {
485 if (enmUsage != pDev->enmUsage) /* Wrong usage? Skip. */
486 continue;
487 }
488
489 if (pDev->fFlags & PDMAUDIODEV_FLAGS_DEFAULT)
490 return pDev;
491 }
492
493 return NULL;
494}
495
496/**
497 * Logs an audio device enumeration.
498 *
499 * @param pszDesc Logging description.
500 * @param pDevEnm Device enumeration to log.
501 */
502void DrvAudioHlpDeviceEnumPrint(const char *pszDesc, const PPDMAUDIODEVICEENUM pDevEnm)
503{
504 AssertPtrReturnVoid(pszDesc);
505 AssertPtrReturnVoid(pDevEnm);
506
507 LogFunc(("%s: %RU16 devices\n", pszDesc, pDevEnm->cDevices));
508
509 PPDMAUDIODEVICE pDev;
510 RTListForEach(&pDevEnm->lstDevices, pDev, PDMAUDIODEVICE, Node)
511 {
512 char *pszFlags = DrvAudioHlpAudDevFlagsToStrA(pDev->fFlags);
513
514 LogFunc(("Device '%s':\n", pDev->szName));
515 LogFunc(("\tUsage = %s\n", DrvAudioHlpAudDirToStr(pDev->enmUsage)));
516 LogFunc(("\tFlags = %s\n", pszFlags ? pszFlags : "<NONE>"));
517 LogFunc(("\tInput channels = %RU8\n", pDev->cMaxInputChannels));
518 LogFunc(("\tOutput channels = %RU8\n", pDev->cMaxOutputChannels));
519 LogFunc(("\tData = %p (%zu bytes)\n", pDev->pvData, pDev->cbData));
520
521 if (pszFlags)
522 RTStrFree(pszFlags);
523 }
524}
525
526/**
527 * Converts an audio direction to a string.
528 *
529 * @returns Stringified audio direction, or "Unknown", if not found.
530 * @param enmDir Audio direction to convert.
531 */
532const char *DrvAudioHlpAudDirToStr(PDMAUDIODIR enmDir)
533{
534 switch (enmDir)
535 {
536 case PDMAUDIODIR_UNKNOWN: return "Unknown";
537 case PDMAUDIODIR_IN: return "Input";
538 case PDMAUDIODIR_OUT: return "Output";
539 case PDMAUDIODIR_ANY: return "Duplex";
540 default: break;
541 }
542
543 AssertMsgFailed(("Invalid audio direction %ld\n", enmDir));
544 return "Unknown";
545}
546
547/**
548 * Converts an audio mixer control to a string.
549 *
550 * @returns Stringified audio mixer control or "Unknown", if not found.
551 * @param enmMixerCtl Audio mixer control to convert.
552 */
553const char *DrvAudioHlpAudMixerCtlToStr(PDMAUDIOMIXERCTL enmMixerCtl)
554{
555 switch (enmMixerCtl)
556 {
557 case PDMAUDIOMIXERCTL_VOLUME_MASTER: return "Master Volume";
558 case PDMAUDIOMIXERCTL_FRONT: return "Front";
559 case PDMAUDIOMIXERCTL_CENTER_LFE: return "Center / LFE";
560 case PDMAUDIOMIXERCTL_REAR: return "Rear";
561 case PDMAUDIOMIXERCTL_LINE_IN: return "Line-In";
562 case PDMAUDIOMIXERCTL_MIC_IN: return "Microphone-In";
563 default: break;
564 }
565
566 AssertMsgFailed(("Invalid mixer control %ld\n", enmMixerCtl));
567 return "Unknown";
568}
569
570/**
571 * Converts an audio device flags to a string.
572 *
573 * @returns Stringified audio flags. Must be free'd with RTStrFree().
574 * NULL if no flags set.
575 * @param fFlags Audio flags to convert.
576 */
577char *DrvAudioHlpAudDevFlagsToStrA(PDMAUDIODEVFLAG fFlags)
578{
579#define APPEND_FLAG_TO_STR(_aFlag) \
580 if (fFlags & PDMAUDIODEV_FLAGS_##_aFlag) \
581 { \
582 if (pszFlags) \
583 { \
584 rc2 = RTStrAAppend(&pszFlags, " "); \
585 if (RT_FAILURE(rc2)) \
586 break; \
587 } \
588 \
589 rc2 = RTStrAAppend(&pszFlags, #_aFlag); \
590 if (RT_FAILURE(rc2)) \
591 break; \
592 } \
593
594 char *pszFlags = NULL;
595 int rc2 = VINF_SUCCESS;
596
597 do
598 {
599 APPEND_FLAG_TO_STR(DEFAULT);
600 APPEND_FLAG_TO_STR(HOTPLUG);
601 APPEND_FLAG_TO_STR(BUGGY);
602 APPEND_FLAG_TO_STR(IGNORE);
603 APPEND_FLAG_TO_STR(LOCKED);
604 APPEND_FLAG_TO_STR(DEAD);
605
606 } while (0);
607
608 if (!pszFlags)
609 rc2 = RTStrAAppend(&pszFlags, "NONE");
610
611 if ( RT_FAILURE(rc2)
612 && pszFlags)
613 {
614 RTStrFree(pszFlags);
615 pszFlags = NULL;
616 }
617
618#undef APPEND_FLAG_TO_STR
619
620 return pszFlags;
621}
622
623/**
624 * Converts a playback destination enumeration to a string.
625 *
626 * @returns Stringified playback destination, or "Unknown", if not found.
627 * @param enmPlaybackDst Playback destination to convert.
628 */
629const char *DrvAudioHlpPlaybackDstToStr(const PDMAUDIOPLAYBACKDEST enmPlaybackDst)
630{
631 switch (enmPlaybackDst)
632 {
633 case PDMAUDIOPLAYBACKDEST_UNKNOWN: return "Unknown";
634 case PDMAUDIOPLAYBACKDEST_FRONT: return "Front";
635 case PDMAUDIOPLAYBACKDEST_CENTER_LFE: return "Center / LFE";
636 case PDMAUDIOPLAYBACKDEST_REAR: return "Rear";
637 default:
638 break;
639 }
640
641 AssertMsgFailed(("Invalid playback destination %ld\n", enmPlaybackDst));
642 return "Unknown";
643}
644
645/**
646 * Converts a recording source enumeration to a string.
647 *
648 * @returns Stringified recording source, or "Unknown", if not found.
649 * @param enmRecSrc Recording source to convert.
650 */
651const char *DrvAudioHlpRecSrcToStr(const PDMAUDIORECSOURCE enmRecSrc)
652{
653 switch (enmRecSrc)
654 {
655 case PDMAUDIORECSOURCE_UNKNOWN: return "Unknown";
656 case PDMAUDIORECSOURCE_MIC: return "Microphone In";
657 case PDMAUDIORECSOURCE_CD: return "CD";
658 case PDMAUDIORECSOURCE_VIDEO: return "Video";
659 case PDMAUDIORECSOURCE_AUX: return "AUX";
660 case PDMAUDIORECSOURCE_LINE: return "Line In";
661 case PDMAUDIORECSOURCE_PHONE: return "Phone";
662 default:
663 break;
664 }
665
666 AssertMsgFailed(("Invalid recording source %ld\n", enmRecSrc));
667 return "Unknown";
668}
669
670/**
671 * Returns wether the given audio format has signed bits or not.
672 *
673 * @return IPRT status code.
674 * @return bool @c true for signed bits, @c false for unsigned.
675 * @param enmFmt Audio format to retrieve value for.
676 */
677bool DrvAudioHlpAudFmtIsSigned(PDMAUDIOFMT enmFmt)
678{
679 switch (enmFmt)
680 {
681 case PDMAUDIOFMT_S8:
682 case PDMAUDIOFMT_S16:
683 case PDMAUDIOFMT_S32:
684 return true;
685
686 case PDMAUDIOFMT_U8:
687 case PDMAUDIOFMT_U16:
688 case PDMAUDIOFMT_U32:
689 return false;
690
691 default:
692 break;
693 }
694
695 AssertMsgFailed(("Bogus audio format %ld\n", enmFmt));
696 return false;
697}
698
699/**
700 * Returns the bits of a given audio format.
701 *
702 * @return IPRT status code.
703 * @return uint8_t Bits of audio format.
704 * @param enmFmt Audio format to retrieve value for.
705 */
706uint8_t DrvAudioHlpAudFmtToBits(PDMAUDIOFMT enmFmt)
707{
708 switch (enmFmt)
709 {
710 case PDMAUDIOFMT_S8:
711 case PDMAUDIOFMT_U8:
712 return 8;
713
714 case PDMAUDIOFMT_U16:
715 case PDMAUDIOFMT_S16:
716 return 16;
717
718 case PDMAUDIOFMT_U32:
719 case PDMAUDIOFMT_S32:
720 return 32;
721
722 default:
723 break;
724 }
725
726 AssertMsgFailed(("Bogus audio format %ld\n", enmFmt));
727 return 0;
728}
729
730/**
731 * Converts an audio format to a string.
732 *
733 * @returns Stringified audio format, or "Unknown", if not found.
734 * @param enmFmt Audio format to convert.
735 */
736const char *DrvAudioHlpAudFmtToStr(PDMAUDIOFMT enmFmt)
737{
738 switch (enmFmt)
739 {
740 case PDMAUDIOFMT_U8:
741 return "U8";
742
743 case PDMAUDIOFMT_U16:
744 return "U16";
745
746 case PDMAUDIOFMT_U32:
747 return "U32";
748
749 case PDMAUDIOFMT_S8:
750 return "S8";
751
752 case PDMAUDIOFMT_S16:
753 return "S16";
754
755 case PDMAUDIOFMT_S32:
756 return "S32";
757
758 default:
759 break;
760 }
761
762 AssertMsgFailed(("Bogus audio format %ld\n", enmFmt));
763 return "Unknown";
764}
765
766/**
767 * Converts a given string to an audio format.
768 *
769 * @returns Audio format for the given string, or PDMAUDIOFMT_INVALID if not found.
770 * @param pszFmt String to convert to an audio format.
771 */
772PDMAUDIOFMT DrvAudioHlpStrToAudFmt(const char *pszFmt)
773{
774 AssertPtrReturn(pszFmt, PDMAUDIOFMT_INVALID);
775
776 if (!RTStrICmp(pszFmt, "u8"))
777 return PDMAUDIOFMT_U8;
778 else if (!RTStrICmp(pszFmt, "u16"))
779 return PDMAUDIOFMT_U16;
780 else if (!RTStrICmp(pszFmt, "u32"))
781 return PDMAUDIOFMT_U32;
782 else if (!RTStrICmp(pszFmt, "s8"))
783 return PDMAUDIOFMT_S8;
784 else if (!RTStrICmp(pszFmt, "s16"))
785 return PDMAUDIOFMT_S16;
786 else if (!RTStrICmp(pszFmt, "s32"))
787 return PDMAUDIOFMT_S32;
788
789 AssertMsgFailed(("Invalid audio format '%s'\n", pszFmt));
790 return PDMAUDIOFMT_INVALID;
791}
792
793/**
794 * Checks whether two given PCM properties are equal.
795 *
796 * @returns @c true if equal, @c false if not.
797 * @param pProps1 First properties to compare.
798 * @param pProps2 Second properties to compare.
799 */
800bool DrvAudioHlpPCMPropsAreEqual(const PPDMAUDIOPCMPROPS pProps1, const PPDMAUDIOPCMPROPS pProps2)
801{
802 AssertPtrReturn(pProps1, false);
803 AssertPtrReturn(pProps2, false);
804
805 if (pProps1 == pProps2) /* If the pointers match, take a shortcut. */
806 return true;
807
808 return pProps1->uHz == pProps2->uHz
809 && pProps1->cChannels == pProps2->cChannels
810 && pProps1->cBits == pProps2->cBits
811 && pProps1->fSigned == pProps2->fSigned
812 && pProps1->fSwapEndian == pProps2->fSwapEndian;
813}
814
815/**
816 * Checks whether given PCM properties are valid or not.
817 *
818 * Returns @c true if properties are valid, @c false if not.
819 * @param pProps PCM properties to check.
820 */
821bool DrvAudioHlpPCMPropsAreValid(const PPDMAUDIOPCMPROPS pProps)
822{
823 AssertPtrReturn(pProps, false);
824
825 /* Minimum 1 channel (mono), maximum 7.1 (= 8) channels. */
826 bool fValid = ( pProps->cChannels >= 1
827 && pProps->cChannels <= 8);
828
829 if (fValid)
830 {
831 switch (pProps->cBits)
832 {
833 case 8:
834 case 16:
835 /** @todo Do we need support for 24-bit samples? */
836 case 32:
837 break;
838 default:
839 fValid = false;
840 break;
841 }
842 }
843
844 if (!fValid)
845 return false;
846
847 fValid &= pProps->uHz > 0;
848 fValid &= pProps->cShift == PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pProps->cBits, pProps->cChannels);
849 fValid &= pProps->fSwapEndian == false; /** @todo Handling Big Endian audio data is not supported yet. */
850
851 return fValid;
852}
853
854/**
855 * Checks whether the given PCM properties are equal with the given
856 * stream configuration.
857 *
858 * @returns @c true if equal, @c false if not.
859 * @param pProps PCM properties to compare.
860 * @param pCfg Stream configuration to compare.
861 */
862bool DrvAudioHlpPCMPropsAreEqual(const PPDMAUDIOPCMPROPS pProps, const PPDMAUDIOSTREAMCFG pCfg)
863{
864 AssertPtrReturn(pProps, false);
865 AssertPtrReturn(pCfg, false);
866
867 return DrvAudioHlpPCMPropsAreEqual(pProps, &pCfg->Props);
868}
869
870/**
871 * Returns the bytes per frame for given PCM properties.
872 *
873 * @return Bytes per (audio) frame.
874 * @param pProps PCM properties to retrieve bytes per frame for.
875 */
876uint32_t DrvAudioHlpPCMPropsBytesPerFrame(const PPDMAUDIOPCMPROPS pProps)
877{
878 return (pProps->cBits / 8) * pProps->cChannels;
879}
880
881/**
882 * Prints PCM properties to the debug log.
883 *
884 * @param pProps Stream configuration to log.
885 */
886void DrvAudioHlpPCMPropsPrint(const PPDMAUDIOPCMPROPS pProps)
887{
888 AssertPtrReturnVoid(pProps);
889
890 Log(("uHz=%RU32, cChannels=%RU8, cBits=%RU8%s",
891 pProps->uHz, pProps->cChannels, pProps->cBits, pProps->fSigned ? "S" : "U"));
892}
893
894/**
895 * Converts PCM properties to a audio stream configuration.
896 *
897 * @return IPRT status code.
898 * @param pProps Pointer to PCM properties to convert.
899 * @param pCfg Pointer to audio stream configuration to store result into.
900 */
901int DrvAudioHlpPCMPropsToStreamCfg(const PPDMAUDIOPCMPROPS pProps, PPDMAUDIOSTREAMCFG pCfg)
902{
903 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
904 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
905
906 memcpy(&pCfg->Props, pProps, sizeof(PDMAUDIOPCMPROPS));
907 return VINF_SUCCESS;
908}
909
910/**
911 * Checks whether a given stream configuration is valid or not.
912 *
913 * Returns @c true if configuration is valid, @c false if not.
914 * @param pCfg Stream configuration to check.
915 */
916bool DrvAudioHlpStreamCfgIsValid(const PPDMAUDIOSTREAMCFG pCfg)
917{
918 AssertPtrReturn(pCfg, false);
919
920 bool fValid = ( pCfg->enmDir == PDMAUDIODIR_IN
921 || pCfg->enmDir == PDMAUDIODIR_OUT);
922
923 fValid &= ( pCfg->enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED
924 || pCfg->enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
925
926 if (fValid)
927 fValid = DrvAudioHlpPCMPropsAreValid(&pCfg->Props);
928
929 return fValid;
930}
931
932/**
933 * Frees an allocated audio stream configuration.
934 *
935 * @param pCfg Audio stream configuration to free.
936 */
937void DrvAudioHlpStreamCfgFree(PPDMAUDIOSTREAMCFG pCfg)
938{
939 if (pCfg)
940 {
941 RTMemFree(pCfg);
942 pCfg = NULL;
943 }
944}
945
946/**
947 * Copies a source stream configuration to a destination stream configuration.
948 *
949 * @returns IPRT status code.
950 * @param pDstCfg Destination stream configuration to copy source to.
951 * @param pSrcCfg Source stream configuration to copy to destination.
952 */
953int DrvAudioHlpStreamCfgCopy(PPDMAUDIOSTREAMCFG pDstCfg, const PPDMAUDIOSTREAMCFG pSrcCfg)
954{
955 AssertPtrReturn(pDstCfg, VERR_INVALID_POINTER);
956 AssertPtrReturn(pSrcCfg, VERR_INVALID_POINTER);
957
958#ifdef VBOX_STRICT
959 if (!DrvAudioHlpStreamCfgIsValid(pSrcCfg))
960 {
961 AssertMsgFailed(("Stream config '%s' (%p) is invalid\n", pSrcCfg->szName, pSrcCfg));
962 return VERR_INVALID_PARAMETER;
963 }
964#endif
965
966 memcpy(pDstCfg, pSrcCfg, sizeof(PDMAUDIOSTREAMCFG));
967
968 return VINF_SUCCESS;
969}
970
971/**
972 * Duplicates an audio stream configuration.
973 * Must be free'd with DrvAudioHlpStreamCfgFree().
974 *
975 * @return Duplicates audio stream configuration on success, or NULL on failure.
976 * @param pCfg Audio stream configuration to duplicate.
977 */
978PPDMAUDIOSTREAMCFG DrvAudioHlpStreamCfgDup(const PPDMAUDIOSTREAMCFG pCfg)
979{
980 AssertPtrReturn(pCfg, NULL);
981
982 PPDMAUDIOSTREAMCFG pDst = (PPDMAUDIOSTREAMCFG)RTMemAllocZ(sizeof(PDMAUDIOSTREAMCFG));
983 if (!pDst)
984 return NULL;
985
986 int rc2 = DrvAudioHlpStreamCfgCopy(pDst, pCfg);
987 if (RT_FAILURE(rc2))
988 {
989 DrvAudioHlpStreamCfgFree(pDst);
990 pDst = NULL;
991 }
992
993 AssertPtr(pDst);
994 return pDst;
995}
996
997/**
998 * Prints an audio stream configuration to the debug log.
999 *
1000 * @param pCfg Stream configuration to log.
1001 */
1002void DrvAudioHlpStreamCfgPrint(const PPDMAUDIOSTREAMCFG pCfg)
1003{
1004 if (!pCfg)
1005 return;
1006
1007 LogFunc(("szName=%s, enmDir=%RU32 (uHz=%RU32, cBits=%RU8%s, cChannels=%RU8)\n",
1008 pCfg->szName, pCfg->enmDir,
1009 pCfg->Props.uHz, pCfg->Props.cBits, pCfg->Props.fSigned ? "S" : "U", pCfg->Props.cChannels));
1010}
1011
1012/**
1013 * Converts a stream command to a string.
1014 *
1015 * @returns Stringified stream command, or "Unknown", if not found.
1016 * @param enmCmd Stream command to convert.
1017 */
1018const char *DrvAudioHlpStreamCmdToStr(PDMAUDIOSTREAMCMD enmCmd)
1019{
1020 switch (enmCmd)
1021 {
1022 case PDMAUDIOSTREAMCMD_UNKNOWN: return "Unknown";
1023 case PDMAUDIOSTREAMCMD_ENABLE: return "Enable";
1024 case PDMAUDIOSTREAMCMD_DISABLE: return "Disable";
1025 case PDMAUDIOSTREAMCMD_PAUSE: return "Pause";
1026 case PDMAUDIOSTREAMCMD_RESUME: return "Resume";
1027 case PDMAUDIOSTREAMCMD_DRAIN: return "Drain";
1028 case PDMAUDIOSTREAMCMD_DROP: return "Drop";
1029 default: break;
1030 }
1031
1032 AssertMsgFailed(("Invalid stream command %ld\n", enmCmd));
1033 return "Unknown";
1034}
1035
1036/**
1037 * Calculates the audio bit rate of the given bits per sample, the Hz and the number
1038 * of audio channels.
1039 *
1040 * Divide the result by 8 to get the byte rate.
1041 *
1042 * @returns The calculated bit rate.
1043 * @param cBits Number of bits per sample.
1044 * @param uHz Hz (Hertz) rate.
1045 * @param cChannels Number of audio channels.
1046 */
1047uint32_t DrvAudioHlpCalcBitrate(uint8_t cBits, uint32_t uHz, uint8_t cChannels)
1048{
1049 return (cBits * uHz * cChannels);
1050}
1051
1052/**
1053 * Calculates the audio bit rate out of a given audio stream configuration.
1054 *
1055 * Divide the result by 8 to get the byte rate.
1056 *
1057 * @returns The calculated bit rate.
1058 * @param pProps PCM properties to calculate bitrate for.
1059 *
1060 * @remark
1061 */
1062uint32_t DrvAudioHlpCalcBitrate(const PPDMAUDIOPCMPROPS pProps)
1063{
1064 return DrvAudioHlpCalcBitrate(pProps->cBits, pProps->uHz, pProps->cChannels);
1065}
1066
1067/**
1068 * Returns the number of audio frames for a given amount of bytes.
1069 *
1070 * @return Calculated audio frames for given bytes.
1071 * @param pProps PCM properties to calulate frames for.
1072 * @param cbBytes Bytes to convert to audio frames.
1073 */
1074uint32_t DrvAudioHlpBytesToFrames(const PPDMAUDIOPCMPROPS pProps, uint32_t cbBytes)
1075{
1076 AssertPtrReturn(pProps, 0);
1077
1078 return cbBytes / ((pProps->cBits / 8) * pProps->cChannels);
1079}
1080
1081/**
1082 * Returns the time (in ms) for given byte amount and PCM properties.
1083 *
1084 * @return uint64_t Calculated time (in ms).
1085 * @param pProps PCM properties to calculate amount of bytes for.
1086 * @param cbBytes Amount of bytes to calculate time for.
1087 */
1088uint64_t DrvAudioHlpBytesToMs(const PPDMAUDIOPCMPROPS pProps, uint32_t cbBytes)
1089{
1090 AssertPtrReturn(pProps, 0);
1091
1092 if (!cbBytes)
1093 return 0;
1094
1095 const float dbBytesPerMs = ((pProps->cBits / 8) * pProps->cChannels * pProps->uHz) / 1000;
1096 Assert(dbBytesPerMs >= 0.0f);
1097 if (!dbBytesPerMs) /* Prevent division by zero. */
1098 return 0;
1099
1100 return cbBytes / dbBytesPerMs;
1101}
1102
1103/**
1104 * Returns the bytes for a given audio frames amount and PCM properties.
1105 *
1106 * @return Calculated bytes for given audio frames.
1107 * @param pProps PCM properties to calculate bytes for.
1108 * @param cFrames Amount of audio frames to calculate bytes for.
1109 */
1110uint32_t DrvAudioHlpFramesToBytes(const PPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
1111{
1112 AssertPtrReturn(pProps, 0);
1113
1114 if (!cFrames)
1115 return 0;
1116
1117 return cFrames * ((pProps->cBits / 8) * pProps->cChannels);
1118}
1119
1120/**
1121 * Returns the time (in ms) for given audio frames amount and PCM properties.
1122 *
1123 * @return uint64_t Calculated time (in ms).
1124 * @param pProps PCM properties to calculate time (in ms) for.
1125 * @param cFrames Amount of audio frames to calculate time for.
1126 */
1127uint64_t DrvAudioHlpFramesToMs(const PPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
1128{
1129 AssertPtrReturn(pProps, 0);
1130
1131 if (!cFrames)
1132 return 0;
1133
1134 if (!pProps->uHz) /* Prevent division by zero. */
1135 return 0;
1136
1137 return cFrames / (pProps->uHz / 1000 /* ms */);
1138}
1139
1140/**
1141 * Returns the amount of bytes for a given time (in ms) and PCM properties.
1142 *
1143 * @return uint32_t Calculated amount of bytes.
1144 * @param pProps PCM properties to calculate amount of bytes for.
1145 * @param uMs Time (in ms) to calculate amount of bytes for.
1146 */
1147uint32_t DrvAudioHlpMsToBytes(const PPDMAUDIOPCMPROPS pProps, uint32_t uMs)
1148{
1149 AssertPtrReturn(pProps, 0);
1150
1151 if (!uMs)
1152 return 0;
1153
1154 return float(((pProps->cBits / 8) * pProps->cChannels * pProps->uHz) / 1000) * uMs;
1155}
1156
1157/**
1158 * Returns the amount of audio for a given time (in ms) and PCM properties.
1159 *
1160 * @return uint32_t Calculated amount of audio frames.
1161 * @param pProps PCM properties to calculate amount of bytes for.
1162 * @param uMs Time (in ms) to calculate amount of bytes for.
1163 */
1164uint32_t DrvAudioHlpMsToFrames(const PPDMAUDIOPCMPROPS pProps, uint32_t uMs)
1165{
1166 AssertPtrReturn(pProps, 0);
1167
1168 const uint32_t cbFrame = (pProps->cBits / 8) * pProps->cChannels;
1169 if (!cbFrame) /* Prevent division by zero. */
1170 return 0;
1171
1172 return DrvAudioHlpMsToBytes(pProps, uMs) / cbFrame;
1173}
1174
1175/**
1176 * Sanitizes the file name component so that unsupported characters
1177 * will be replaced by an underscore ("_").
1178 *
1179 * @return IPRT status code.
1180 * @param pszPath Path to sanitize.
1181 * @param cbPath Size (in bytes) of path to sanitize.
1182 */
1183int DrvAudioHlpSanitizeFileName(char *pszPath, size_t cbPath)
1184{
1185 RT_NOREF(cbPath);
1186 int rc = VINF_SUCCESS;
1187#ifdef RT_OS_WINDOWS
1188 /* Filter out characters not allowed on Windows platforms, put in by
1189 RTTimeSpecToString(). */
1190 /** @todo Use something like RTPathSanitize() if available later some time. */
1191 static RTUNICP const s_uszValidRangePairs[] =
1192 {
1193 ' ', ' ',
1194 '(', ')',
1195 '-', '.',
1196 '0', '9',
1197 'A', 'Z',
1198 'a', 'z',
1199 '_', '_',
1200 0xa0, 0xd7af,
1201 '\0'
1202 };
1203 ssize_t cReplaced = RTStrPurgeComplementSet(pszPath, s_uszValidRangePairs, '_' /* Replacement */);
1204 if (cReplaced < 0)
1205 rc = VERR_INVALID_UTF8_ENCODING;
1206#else
1207 RT_NOREF(pszPath);
1208#endif
1209 return rc;
1210}
1211
1212/**
1213 * Constructs an unique file name, based on the given path and the audio file type.
1214 *
1215 * @returns IPRT status code.
1216 * @param pszFile Where to store the constructed file name.
1217 * @param cchFile Size (in characters) of the file name buffer.
1218 * @param pszPath Base path to use.
1219 * @param pszName A name for better identifying the file.
1220 * @param uInstance Device / driver instance which is using this file.
1221 * @param enmType Audio file type to construct file name for.
1222 * @param fFlags File naming flags.
1223 */
1224int DrvAudioHlpGetFileName(char *pszFile, size_t cchFile, const char *pszPath, const char *pszName,
1225 uint32_t uInstance, PDMAUDIOFILETYPE enmType, PDMAUDIOFILENAMEFLAGS fFlags)
1226{
1227 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
1228 AssertReturn(cchFile, VERR_INVALID_PARAMETER);
1229 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1230 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
1231 /** @todo Validate fFlags. */
1232
1233 int rc;
1234
1235 do
1236 {
1237 char szFilePath[RTPATH_MAX + 1];
1238 RTStrPrintf2(szFilePath, sizeof(szFilePath), "%s", pszPath);
1239
1240 /* Create it when necessary. */
1241 if (!RTDirExists(szFilePath))
1242 {
1243 rc = RTDirCreateFullPath(szFilePath, RTFS_UNIX_IRWXU);
1244 if (RT_FAILURE(rc))
1245 break;
1246 }
1247
1248 char szFileName[RTPATH_MAX + 1];
1249 szFileName[0] = '\0';
1250
1251 if (fFlags & PDMAUDIOFILENAME_FLAG_TS)
1252 {
1253 RTTIMESPEC time;
1254 if (!RTTimeSpecToString(RTTimeNow(&time), szFileName, sizeof(szFileName)))
1255 {
1256 rc = VERR_BUFFER_OVERFLOW;
1257 break;
1258 }
1259
1260 rc = DrvAudioHlpSanitizeFileName(szFileName, sizeof(szFileName));
1261 if (RT_FAILURE(rc))
1262 break;
1263
1264 rc = RTStrCat(szFileName, sizeof(szFileName), "-");
1265 if (RT_FAILURE(rc))
1266 break;
1267 }
1268
1269 rc = RTStrCat(szFileName, sizeof(szFileName), pszName);
1270 if (RT_FAILURE(rc))
1271 break;
1272
1273 rc = RTStrCat(szFileName, sizeof(szFileName), "-");
1274 if (RT_FAILURE(rc))
1275 break;
1276
1277 char szInst[16];
1278 RTStrPrintf2(szInst, sizeof(szInst), "%RU32", uInstance);
1279 rc = RTStrCat(szFileName, sizeof(szFileName), szInst);
1280 if (RT_FAILURE(rc))
1281 break;
1282
1283 switch (enmType)
1284 {
1285 case PDMAUDIOFILETYPE_RAW:
1286 rc = RTStrCat(szFileName, sizeof(szFileName), ".pcm");
1287 break;
1288
1289 case PDMAUDIOFILETYPE_WAV:
1290 rc = RTStrCat(szFileName, sizeof(szFileName), ".wav");
1291 break;
1292
1293 default:
1294 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1295 break;
1296 }
1297
1298 if (RT_FAILURE(rc))
1299 break;
1300
1301 rc = RTPathAppend(szFilePath, sizeof(szFilePath), szFileName);
1302 if (RT_FAILURE(rc))
1303 break;
1304
1305 RTStrPrintf2(pszFile, cchFile, "%s", szFilePath);
1306
1307 } while (0);
1308
1309 LogFlowFuncLeaveRC(rc);
1310 return rc;
1311}
1312
1313/**
1314 * Creates an audio file.
1315 *
1316 * @returns IPRT status code.
1317 * @param enmType Audio file type to open / create.
1318 * @param pszFile File path of file to open or create.
1319 * @param fFlags Audio file flags.
1320 * @param ppFile Where to store the created audio file handle.
1321 * Needs to be destroyed with DrvAudioHlpFileDestroy().
1322 */
1323int DrvAudioHlpFileCreate(PDMAUDIOFILETYPE enmType, const char *pszFile, PDMAUDIOFILEFLAGS fFlags, PPDMAUDIOFILE *ppFile)
1324{
1325 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
1326 /** @todo Validate fFlags. */
1327
1328 PPDMAUDIOFILE pFile = (PPDMAUDIOFILE)RTMemAlloc(sizeof(PDMAUDIOFILE));
1329 if (!pFile)
1330 return VERR_NO_MEMORY;
1331
1332 int rc = VINF_SUCCESS;
1333
1334 switch (enmType)
1335 {
1336 case PDMAUDIOFILETYPE_RAW:
1337 case PDMAUDIOFILETYPE_WAV:
1338 pFile->enmType = enmType;
1339 break;
1340
1341 default:
1342 rc = VERR_INVALID_PARAMETER;
1343 break;
1344 }
1345
1346 if (RT_SUCCESS(rc))
1347 {
1348 RTStrPrintf(pFile->szName, RT_ELEMENTS(pFile->szName), "%s", pszFile);
1349 pFile->hFile = NIL_RTFILE;
1350 pFile->fFlags = fFlags;
1351 pFile->pvData = NULL;
1352 pFile->cbData = 0;
1353 }
1354
1355 if (RT_FAILURE(rc))
1356 {
1357 RTMemFree(pFile);
1358 pFile = NULL;
1359 }
1360 else
1361 *ppFile = pFile;
1362
1363 return rc;
1364}
1365
1366/**
1367 * Destroys a formerly created audio file.
1368 *
1369 * @param pFile Audio file (object) to destroy.
1370 */
1371void DrvAudioHlpFileDestroy(PPDMAUDIOFILE pFile)
1372{
1373 if (!pFile)
1374 return;
1375
1376 DrvAudioHlpFileClose(pFile);
1377
1378 RTMemFree(pFile);
1379 pFile = NULL;
1380}
1381
1382/**
1383 * Opens or creates an audio file.
1384 *
1385 * @returns IPRT status code.
1386 * @param pFile Pointer to audio file handle to use.
1387 * @param fOpen Open flags.
1388 * Use PDMAUDIOFILE_DEFAULT_OPEN_FLAGS for the default open flags.
1389 * @param pProps PCM properties to use.
1390 */
1391int DrvAudioHlpFileOpen(PPDMAUDIOFILE pFile, uint32_t fOpen, const PPDMAUDIOPCMPROPS pProps)
1392{
1393 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1394 /** @todo Validate fOpen flags. */
1395 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
1396
1397 int rc;
1398
1399 if (pFile->enmType == PDMAUDIOFILETYPE_RAW)
1400 {
1401 rc = RTFileOpen(&pFile->hFile, pFile->szName, fOpen);
1402 }
1403 else if (pFile->enmType == PDMAUDIOFILETYPE_WAV)
1404 {
1405 Assert(pProps->cChannels);
1406 Assert(pProps->uHz);
1407 Assert(pProps->cBits);
1408
1409 pFile->pvData = (PAUDIOWAVFILEDATA)RTMemAllocZ(sizeof(AUDIOWAVFILEDATA));
1410 if (pFile->pvData)
1411 {
1412 pFile->cbData = sizeof(PAUDIOWAVFILEDATA);
1413
1414 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1415 AssertPtr(pData);
1416
1417 /* Header. */
1418 pData->Hdr.u32RIFF = AUDIO_MAKE_FOURCC('R','I','F','F');
1419 pData->Hdr.u32Size = 36;
1420 pData->Hdr.u32WAVE = AUDIO_MAKE_FOURCC('W','A','V','E');
1421
1422 pData->Hdr.u32Fmt = AUDIO_MAKE_FOURCC('f','m','t',' ');
1423 pData->Hdr.u32Size1 = 16; /* Means PCM. */
1424 pData->Hdr.u16AudioFormat = 1; /* PCM, linear quantization. */
1425 pData->Hdr.u16NumChannels = pProps->cChannels;
1426 pData->Hdr.u32SampleRate = pProps->uHz;
1427 pData->Hdr.u32ByteRate = DrvAudioHlpCalcBitrate(pProps->cBits, pProps->uHz, pProps->cChannels) / 8;
1428 pData->Hdr.u16BlockAlign = pProps->cChannels * pProps->cBits / 8;
1429 pData->Hdr.u16BitsPerSample = pProps->cBits;
1430
1431 /* Data chunk. */
1432 pData->Hdr.u32ID2 = AUDIO_MAKE_FOURCC('d','a','t','a');
1433 pData->Hdr.u32Size2 = 0;
1434
1435 rc = RTFileOpen(&pFile->hFile, pFile->szName, fOpen);
1436 if (RT_SUCCESS(rc))
1437 {
1438 rc = RTFileWrite(pFile->hFile, &pData->Hdr, sizeof(pData->Hdr), NULL);
1439 if (RT_FAILURE(rc))
1440 {
1441 RTFileClose(pFile->hFile);
1442 pFile->hFile = NIL_RTFILE;
1443 }
1444 }
1445
1446 if (RT_FAILURE(rc))
1447 {
1448 RTMemFree(pFile->pvData);
1449 pFile->pvData = NULL;
1450 pFile->cbData = 0;
1451 }
1452 }
1453 else
1454 rc = VERR_NO_MEMORY;
1455 }
1456 else
1457 rc = VERR_INVALID_PARAMETER;
1458
1459 if (RT_SUCCESS(rc))
1460 {
1461 LogRel2(("Audio: Opened file '%s'\n", pFile->szName));
1462 }
1463 else
1464 LogRel(("Audio: Failed opening file '%s', rc=%Rrc\n", pFile->szName, rc));
1465
1466 return rc;
1467}
1468
1469/**
1470 * Closes an audio file.
1471 *
1472 * @returns IPRT status code.
1473 * @param pFile Audio file handle to close.
1474 */
1475int DrvAudioHlpFileClose(PPDMAUDIOFILE pFile)
1476{
1477 if (!pFile)
1478 return VINF_SUCCESS;
1479
1480 size_t cbSize = DrvAudioHlpFileGetDataSize(pFile);
1481
1482 int rc = VINF_SUCCESS;
1483
1484 if (pFile->enmType == PDMAUDIOFILETYPE_RAW)
1485 {
1486 if (RTFileIsValid(pFile->hFile))
1487 rc = RTFileClose(pFile->hFile);
1488 }
1489 else if (pFile->enmType == PDMAUDIOFILETYPE_WAV)
1490 {
1491 if (RTFileIsValid(pFile->hFile))
1492 {
1493 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1494 if (pData) /* The .WAV file data only is valid when a file actually has been created. */
1495 {
1496 /* Update the header with the current data size. */
1497 RTFileWriteAt(pFile->hFile, 0, &pData->Hdr, sizeof(pData->Hdr), NULL);
1498 }
1499
1500 rc = RTFileClose(pFile->hFile);
1501 }
1502
1503 if (pFile->pvData)
1504 {
1505 RTMemFree(pFile->pvData);
1506 pFile->pvData = NULL;
1507 }
1508 }
1509 else
1510 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1511
1512 if ( RT_SUCCESS(rc)
1513 && !cbSize
1514 && !(pFile->fFlags & PDMAUDIOFILE_FLAG_KEEP_IF_EMPTY))
1515 {
1516 rc = DrvAudioHlpFileDelete(pFile);
1517 }
1518
1519 pFile->cbData = 0;
1520
1521 if (RT_SUCCESS(rc))
1522 {
1523 pFile->hFile = NIL_RTFILE;
1524 LogRel2(("Audio: Closed file '%s' (%zu bytes)\n", pFile->szName, cbSize));
1525 }
1526 else
1527 LogRel(("Audio: Failed closing file '%s', rc=%Rrc\n", pFile->szName, rc));
1528
1529 return rc;
1530}
1531
1532/**
1533 * Deletes an audio file.
1534 *
1535 * @returns IPRT status code.
1536 * @param pFile Audio file handle to delete.
1537 */
1538int DrvAudioHlpFileDelete(PPDMAUDIOFILE pFile)
1539{
1540 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1541
1542 int rc = RTFileDelete(pFile->szName);
1543 if (RT_SUCCESS(rc))
1544 {
1545 LogRel2(("Audio: Deleted file '%s'\n", pFile->szName));
1546 }
1547 else if (rc == VERR_FILE_NOT_FOUND) /* Don't bitch if the file is not around (anymore). */
1548 rc = VINF_SUCCESS;
1549
1550 if (RT_FAILURE(rc))
1551 LogRel(("Audio: Failed deleting file '%s', rc=%Rrc\n", pFile->szName, rc));
1552
1553 return rc;
1554}
1555
1556/**
1557 * Returns the raw audio data size of an audio file.
1558 *
1559 * Note: This does *not* include file headers and other data which does
1560 * not belong to the actual PCM audio data.
1561 *
1562 * @returns Size (in bytes) of the raw PCM audio data.
1563 * @param pFile Audio file handle to retrieve the audio data size for.
1564 */
1565size_t DrvAudioHlpFileGetDataSize(PPDMAUDIOFILE pFile)
1566{
1567 AssertPtrReturn(pFile, 0);
1568
1569 size_t cbSize = 0;
1570
1571 if (pFile->enmType == PDMAUDIOFILETYPE_RAW)
1572 {
1573 cbSize = RTFileTell(pFile->hFile);
1574 }
1575 else if (pFile->enmType == PDMAUDIOFILETYPE_WAV)
1576 {
1577 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1578 if (pData) /* The .WAV file data only is valid when a file actually has been created. */
1579 cbSize = pData->Hdr.u32Size2;
1580 }
1581
1582 return cbSize;
1583}
1584
1585/**
1586 * Returns whether the given audio file is open and in use or not.
1587 *
1588 * @return bool True if open, false if not.
1589 * @param pFile Audio file handle to check open status for.
1590 */
1591bool DrvAudioHlpFileIsOpen(PPDMAUDIOFILE pFile)
1592{
1593 if (!pFile)
1594 return false;
1595
1596 return RTFileIsValid(pFile->hFile);
1597}
1598
1599/**
1600 * Write PCM data to a wave (.WAV) file.
1601 *
1602 * @returns IPRT status code.
1603 * @param pFile Audio file handle to write PCM data to.
1604 * @param pvBuf Audio data to write.
1605 * @param cbBuf Size (in bytes) of audio data to write.
1606 * @param fFlags Additional write flags. Not being used at the moment and must be 0.
1607 */
1608int DrvAudioHlpFileWrite(PPDMAUDIOFILE pFile, const void *pvBuf, size_t cbBuf, uint32_t fFlags)
1609{
1610 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1611 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1612
1613 AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); /** @todo fFlags are currently not implemented. */
1614
1615 if (!cbBuf)
1616 return VINF_SUCCESS;
1617
1618 AssertReturn(RTFileIsValid(pFile->hFile), VERR_WRONG_ORDER);
1619
1620 int rc;
1621
1622 if (pFile->enmType == PDMAUDIOFILETYPE_RAW)
1623 {
1624 rc = RTFileWrite(pFile->hFile, pvBuf, cbBuf, NULL);
1625 }
1626 else if (pFile->enmType == PDMAUDIOFILETYPE_WAV)
1627 {
1628 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1629 AssertPtr(pData);
1630
1631 rc = RTFileWrite(pFile->hFile, pvBuf, cbBuf, NULL);
1632 if (RT_SUCCESS(rc))
1633 {
1634 pData->Hdr.u32Size += (uint32_t)cbBuf;
1635 pData->Hdr.u32Size2 += (uint32_t)cbBuf;
1636 }
1637 }
1638 else
1639 rc = VERR_NOT_SUPPORTED;
1640
1641 return rc;
1642}
1643
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