VirtualBox

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

Last change on this file since 69111 was 68902, checked in by vboxsync, 7 years ago

Audio/DrvAudio: Removed assertions from DrvAudioHlpStreamCfgIsValid() and DrvAudioHlpPCMPropsAreValid().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.7 KB
Line 
1/* $Id: DrvAudioCommon.cpp 68902 2017-09-28 09:23:18Z vboxsync $ */
2/** @file
3 * Intermedia audio driver, common routines. These are also used
4 * in the drivers which are bound to Main, e.g. the VRDE or the
5 * video audio recording drivers.
6 */
7
8/*
9 * Copyright (C) 2006-2017 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#include <iprt/alloc.h>
20#include <iprt/asm-math.h>
21#include <iprt/assert.h>
22#include <iprt/dir.h>
23#include <iprt/file.h>
24#include <iprt/string.h>
25#include <iprt/uuid.h>
26
27#define LOG_GROUP LOG_GROUP_DRV_AUDIO
28#include <VBox/log.h>
29
30#include <VBox/err.h>
31#include <VBox/vmm/pdmdev.h>
32#include <VBox/vmm/pdm.h>
33#include <VBox/vmm/mm.h>
34
35#include <ctype.h>
36#include <stdlib.h>
37
38#include "DrvAudio.h"
39#include "AudioMixBuffer.h"
40
41#pragma pack(1)
42/**
43 * Structure for building up a .WAV file header.
44 */
45typedef struct AUDIOWAVFILEHDR
46{
47 uint32_t u32RIFF;
48 uint32_t u32Size;
49 uint32_t u32WAVE;
50
51 uint32_t u32Fmt;
52 uint32_t u32Size1;
53 uint16_t u16AudioFormat;
54 uint16_t u16NumChannels;
55 uint32_t u32SampleRate;
56 uint32_t u32ByteRate;
57 uint16_t u16BlockAlign;
58 uint16_t u16BitsPerSample;
59
60 uint32_t u32ID2;
61 uint32_t u32Size2;
62} AUDIOWAVFILEHDR, *PAUDIOWAVFILEHDR;
63#pragma pack()
64
65/**
66 * Structure for keeeping the internal .WAV file data
67 */
68typedef struct AUDIOWAVFILEDATA
69{
70 /** The file header/footer. */
71 AUDIOWAVFILEHDR Hdr;
72} AUDIOWAVFILEDATA, *PAUDIOWAVFILEDATA;
73
74/**
75 * Retrieves the matching PDMAUDIOFMT for given bits + signing flag.
76 *
77 * @return IPRT status code.
78 * @return PDMAUDIOFMT Resulting audio format or PDMAUDIOFMT_INVALID if invalid.
79 * @param cBits Bits to retrieve audio format for.
80 * @param fSigned Signed flag for bits to retrieve audio format for.
81 */
82PDMAUDIOFMT DrvAudioAudFmtBitsToAudFmt(uint8_t cBits, bool fSigned)
83{
84 if (fSigned)
85 {
86 switch (cBits)
87 {
88 case 8: return PDMAUDIOFMT_S8;
89 case 16: return PDMAUDIOFMT_S16;
90 case 32: return PDMAUDIOFMT_S32;
91 default: break;
92 }
93 }
94 else
95 {
96 switch (cBits)
97 {
98 case 8: return PDMAUDIOFMT_U8;
99 case 16: return PDMAUDIOFMT_U16;
100 case 32: return PDMAUDIOFMT_U32;
101 default: break;
102 }
103 }
104
105 AssertMsgFailed(("Bogus audio bits %RU8\n", cBits));
106 return PDMAUDIOFMT_INVALID;
107}
108
109/**
110 * Clears a sample buffer by the given amount of audio samples.
111 *
112 * @return IPRT status code.
113 * @param pPCMProps PCM properties to use for the buffer to clear.
114 * @param pvBuf Buffer to clear.
115 * @param cbBuf Size (in bytes) of the buffer.
116 * @param cSamples Number of audio samples to clear in the buffer.
117 */
118void DrvAudioHlpClearBuf(const PPDMAUDIOPCMPROPS pPCMProps, void *pvBuf, size_t cbBuf, uint32_t cSamples)
119{
120 AssertPtrReturnVoid(pPCMProps);
121 AssertPtrReturnVoid(pvBuf);
122
123 if (!cbBuf || !cSamples)
124 return;
125
126 Assert(pPCMProps->cBits);
127 size_t cbToClear = cSamples * (pPCMProps->cBits / 8 /* Bytes */);
128 Assert(cbBuf >= cbToClear);
129
130 if (cbBuf < cbToClear)
131 cbToClear = cbBuf;
132
133 Log2Func(("pPCMProps=%p, pvBuf=%p, cSamples=%RU32, fSigned=%RTbool, cBits=%RU8\n",
134 pPCMProps, pvBuf, cSamples, pPCMProps->fSigned, pPCMProps->cBits));
135
136 if (pPCMProps->fSigned)
137 {
138 RT_BZERO(pvBuf, cbToClear);
139 }
140 else
141 {
142 switch (pPCMProps->cBits)
143 {
144 case 8:
145 {
146 memset(pvBuf, 0x80, cbToClear);
147 break;
148 }
149
150 case 16:
151 {
152 uint16_t *p = (uint16_t *)pvBuf;
153 int16_t s = INT16_MAX;
154
155 if (pPCMProps->fSwapEndian)
156 s = RT_BSWAP_U16(s);
157
158 for (uint32_t i = 0; i < cSamples; i++)
159 p[i] = s;
160
161 break;
162 }
163
164 case 32:
165 {
166 uint32_t *p = (uint32_t *)pvBuf;
167 int32_t s = INT32_MAX;
168
169 if (pPCMProps->fSwapEndian)
170 s = RT_BSWAP_U32(s);
171
172 for (uint32_t i = 0; i < cSamples; i++)
173 p[i] = s;
174
175 break;
176 }
177
178 default:
179 {
180 AssertMsgFailed(("Invalid bits: %RU8\n", pPCMProps->cBits));
181 break;
182 }
183 }
184 }
185}
186
187/**
188 * Allocates an audio device.
189 *
190 * @returns Newly allocated audio device, or NULL if failed.
191 * @param cbData How much additional data (in bytes) should be allocated to provide
192 * a (backend) specific area to store additional data.
193 * Optional, can be 0.
194 */
195PPDMAUDIODEVICE DrvAudioHlpDeviceAlloc(size_t cbData)
196{
197 PPDMAUDIODEVICE pDev = (PPDMAUDIODEVICE)RTMemAllocZ(sizeof(PDMAUDIODEVICE));
198 if (!pDev)
199 return NULL;
200
201 if (cbData)
202 {
203 pDev->pvData = RTMemAllocZ(cbData);
204 if (!pDev->pvData)
205 {
206 RTMemFree(pDev);
207 return NULL;
208 }
209 }
210
211 pDev->cbData = cbData;
212
213 pDev->cMaxInputChannels = 0;
214 pDev->cMaxOutputChannels = 0;
215
216 return pDev;
217}
218
219/**
220 * Frees an audio device.
221 *
222 * @param pDev Device to free.
223 */
224void DrvAudioHlpDeviceFree(PPDMAUDIODEVICE pDev)
225{
226 if (!pDev)
227 return;
228
229 Assert(pDev->cRefCount == 0);
230
231 if (pDev->pvData)
232 {
233 Assert(pDev->cbData);
234
235 RTMemFree(pDev->pvData);
236 pDev->pvData = NULL;
237 }
238
239 RTMemFree(pDev);
240 pDev = NULL;
241}
242
243/**
244 * Duplicates an audio device entry.
245 *
246 * @returns Duplicated audio device entry on success, or NULL on failure.
247 * @param pDev Audio device entry to duplicate.
248 * @param fCopyUserData Whether to also copy the user data portion or not.
249 */
250PPDMAUDIODEVICE DrvAudioHlpDeviceDup(const PPDMAUDIODEVICE pDev, bool fCopyUserData)
251{
252 AssertPtrReturn(pDev, NULL);
253
254 PPDMAUDIODEVICE pDevDup = DrvAudioHlpDeviceAlloc(fCopyUserData ? pDev->cbData : 0);
255 if (pDevDup)
256 {
257 memcpy(pDevDup, pDev, sizeof(PDMAUDIODEVICE));
258
259 if ( fCopyUserData
260 && pDevDup->cbData)
261 {
262 memcpy(pDevDup->pvData, pDev->pvData, pDevDup->cbData);
263 }
264 else
265 {
266 pDevDup->cbData = 0;
267 pDevDup->pvData = NULL;
268 }
269 }
270
271 return pDevDup;
272}
273
274/**
275 * Initializes an audio device enumeration structure.
276 *
277 * @returns IPRT status code.
278 * @param pDevEnm Device enumeration to initialize.
279 */
280int DrvAudioHlpDeviceEnumInit(PPDMAUDIODEVICEENUM pDevEnm)
281{
282 AssertPtrReturn(pDevEnm, VERR_INVALID_POINTER);
283
284 RTListInit(&pDevEnm->lstDevices);
285 pDevEnm->cDevices = 0;
286
287 return VINF_SUCCESS;
288}
289
290/**
291 * Frees audio device enumeration data.
292 *
293 * @param pDevEnm Device enumeration to destroy.
294 */
295void DrvAudioHlpDeviceEnumFree(PPDMAUDIODEVICEENUM pDevEnm)
296{
297 if (!pDevEnm)
298 return;
299
300 PPDMAUDIODEVICE pDev, pDevNext;
301 RTListForEachSafe(&pDevEnm->lstDevices, pDev, pDevNext, PDMAUDIODEVICE, Node)
302 {
303 RTListNodeRemove(&pDev->Node);
304
305 DrvAudioHlpDeviceFree(pDev);
306
307 pDevEnm->cDevices--;
308 }
309
310 /* Sanity. */
311 Assert(RTListIsEmpty(&pDevEnm->lstDevices));
312 Assert(pDevEnm->cDevices == 0);
313}
314
315/**
316 * Adds an audio device to a device enumeration.
317 *
318 * @return IPRT status code.
319 * @param pDevEnm Device enumeration to add device to.
320 * @param pDev Device to add. The pointer will be owned by the device enumeration then.
321 */
322int DrvAudioHlpDeviceEnumAdd(PPDMAUDIODEVICEENUM pDevEnm, PPDMAUDIODEVICE pDev)
323{
324 AssertPtrReturn(pDevEnm, VERR_INVALID_POINTER);
325 AssertPtrReturn(pDev, VERR_INVALID_POINTER);
326
327 RTListAppend(&pDevEnm->lstDevices, &pDev->Node);
328 pDevEnm->cDevices++;
329
330 return VINF_SUCCESS;
331}
332
333/**
334 * Duplicates a device enumeration.
335 *
336 * @returns Duplicated device enumeration, or NULL on failure.
337 * Must be free'd with DrvAudioHlpDeviceEnumFree().
338 * @param pDevEnm Device enumeration to duplicate.
339 */
340PPDMAUDIODEVICEENUM DrvAudioHlpDeviceEnumDup(const PPDMAUDIODEVICEENUM pDevEnm)
341{
342 AssertPtrReturn(pDevEnm, NULL);
343
344 PPDMAUDIODEVICEENUM pDevEnmDup = (PPDMAUDIODEVICEENUM)RTMemAlloc(sizeof(PDMAUDIODEVICEENUM));
345 if (!pDevEnmDup)
346 return NULL;
347
348 int rc2 = DrvAudioHlpDeviceEnumInit(pDevEnmDup);
349 AssertRC(rc2);
350
351 PPDMAUDIODEVICE pDev;
352 RTListForEach(&pDevEnm->lstDevices, pDev, PDMAUDIODEVICE, Node)
353 {
354 PPDMAUDIODEVICE pDevDup = DrvAudioHlpDeviceDup(pDev, true /* fCopyUserData */);
355 if (!pDevDup)
356 {
357 rc2 = VERR_NO_MEMORY;
358 break;
359 }
360
361 rc2 = DrvAudioHlpDeviceEnumAdd(pDevEnmDup, pDevDup);
362 if (RT_FAILURE(rc2))
363 {
364 DrvAudioHlpDeviceFree(pDevDup);
365 break;
366 }
367 }
368
369 if (RT_FAILURE(rc2))
370 {
371 DrvAudioHlpDeviceEnumFree(pDevEnmDup);
372 pDevEnmDup = NULL;
373 }
374
375 return pDevEnmDup;
376}
377
378/**
379 * Copies device enumeration entries from the source to the destination enumeration.
380 *
381 * @returns IPRT status code.
382 * @param pDstDevEnm Destination enumeration to store enumeration entries into.
383 * @param pSrcDevEnm Source enumeration to use.
384 * @param enmUsage Which entries to copy. Specify PDMAUDIODIR_ANY to copy all entries.
385 * @param fCopyUserData Whether to also copy the user data portion or not.
386 */
387int DrvAudioHlpDeviceEnumCopyEx(PPDMAUDIODEVICEENUM pDstDevEnm, const PPDMAUDIODEVICEENUM pSrcDevEnm,
388 PDMAUDIODIR enmUsage, bool fCopyUserData)
389{
390 AssertPtrReturn(pDstDevEnm, VERR_INVALID_POINTER);
391 AssertPtrReturn(pSrcDevEnm, VERR_INVALID_POINTER);
392
393 int rc = VINF_SUCCESS;
394
395 PPDMAUDIODEVICE pSrcDev;
396 RTListForEach(&pSrcDevEnm->lstDevices, pSrcDev, PDMAUDIODEVICE, Node)
397 {
398 if ( enmUsage != PDMAUDIODIR_ANY
399 && enmUsage != pSrcDev->enmUsage)
400 {
401 continue;
402 }
403
404 PPDMAUDIODEVICE pDstDev = DrvAudioHlpDeviceDup(pSrcDev, fCopyUserData);
405 if (!pDstDev)
406 {
407 rc = VERR_NO_MEMORY;
408 break;
409 }
410
411 rc = DrvAudioHlpDeviceEnumAdd(pDstDevEnm, pDstDev);
412 if (RT_FAILURE(rc))
413 break;
414 }
415
416 return rc;
417}
418
419/**
420 * Copies all device enumeration entries from the source to the destination enumeration.
421 *
422 * Note: Does *not* copy the user-specific data assigned to a device enumeration entry.
423 * To do so, use DrvAudioHlpDeviceEnumCopyEx().
424 *
425 * @returns IPRT status code.
426 * @param pDstDevEnm Destination enumeration to store enumeration entries into.
427 * @param pSrcDevEnm Source enumeration to use.
428 */
429int DrvAudioHlpDeviceEnumCopy(PPDMAUDIODEVICEENUM pDstDevEnm, const PPDMAUDIODEVICEENUM pSrcDevEnm)
430{
431 return DrvAudioHlpDeviceEnumCopyEx(pDstDevEnm, pSrcDevEnm, PDMAUDIODIR_ANY, false /* fCopyUserData */);
432}
433
434/**
435 * Returns the default device of a given device enumeration.
436 * This assumes that only one default device per usage is set.
437 *
438 * @returns Default device if found, or NULL if none found.
439 * @param pDevEnm Device enumeration to get default device for.
440 * @param enmUsage Usage to get default device for.
441 */
442PPDMAUDIODEVICE DrvAudioHlpDeviceEnumGetDefaultDevice(const PPDMAUDIODEVICEENUM pDevEnm, PDMAUDIODIR enmUsage)
443{
444 AssertPtrReturn(pDevEnm, NULL);
445
446 PPDMAUDIODEVICE pDev;
447 RTListForEach(&pDevEnm->lstDevices, pDev, PDMAUDIODEVICE, Node)
448 {
449 if (enmUsage != PDMAUDIODIR_ANY)
450 {
451 if (enmUsage != pDev->enmUsage) /* Wrong usage? Skip. */
452 continue;
453 }
454
455 if (pDev->fFlags & PDMAUDIODEV_FLAGS_DEFAULT)
456 return pDev;
457 }
458
459 return NULL;
460}
461
462/**
463 * Logs an audio device enumeration.
464 *
465 * @param pszDesc Logging description.
466 * @param pDevEnm Device enumeration to log.
467 */
468void DrvAudioHlpDeviceEnumPrint(const char *pszDesc, const PPDMAUDIODEVICEENUM pDevEnm)
469{
470 AssertPtrReturnVoid(pszDesc);
471 AssertPtrReturnVoid(pDevEnm);
472
473 LogFunc(("%s: %RU16 devices\n", pszDesc, pDevEnm->cDevices));
474
475 PPDMAUDIODEVICE pDev;
476 RTListForEach(&pDevEnm->lstDevices, pDev, PDMAUDIODEVICE, Node)
477 {
478 char *pszFlags = DrvAudioHlpAudDevFlagsToStrA(pDev->fFlags);
479
480 LogFunc(("Device '%s':\n", pDev->szName));
481 LogFunc(("\tUsage = %s\n", DrvAudioHlpAudDirToStr(pDev->enmUsage)));
482 LogFunc(("\tFlags = %s\n", pszFlags ? pszFlags : "<NONE>"));
483 LogFunc(("\tInput channels = %RU8\n", pDev->cMaxInputChannels));
484 LogFunc(("\tOutput channels = %RU8\n", pDev->cMaxOutputChannels));
485 LogFunc(("\tData = %p (%zu bytes)\n", pDev->pvData, pDev->cbData));
486
487 if (pszFlags)
488 RTStrFree(pszFlags);
489 }
490}
491
492/**
493 * Converts an audio direction to a string.
494 *
495 * @returns Stringified audio direction, or "Unknown", if not found.
496 * @param enmDir Audio direction to convert.
497 */
498const char *DrvAudioHlpAudDirToStr(PDMAUDIODIR enmDir)
499{
500 switch (enmDir)
501 {
502 case PDMAUDIODIR_UNKNOWN: return "Unknown";
503 case PDMAUDIODIR_IN: return "Input";
504 case PDMAUDIODIR_OUT: return "Output";
505 case PDMAUDIODIR_ANY: return "Duplex";
506 default: break;
507 }
508
509 AssertMsgFailed(("Invalid audio direction %ld\n", enmDir));
510 return "Unknown";
511}
512
513/**
514 * Converts an audio mixer control to a string.
515 *
516 * @returns Stringified audio mixer control or "Unknown", if not found.
517 * @param enmMixerCtl Audio mixer control to convert.
518 */
519const char *DrvAudioHlpAudMixerCtlToStr(PDMAUDIOMIXERCTL enmMixerCtl)
520{
521 switch (enmMixerCtl)
522 {
523 case PDMAUDIOMIXERCTL_VOLUME_MASTER: return "Master Volume";
524 case PDMAUDIOMIXERCTL_FRONT: return "Front";
525 case PDMAUDIOMIXERCTL_CENTER_LFE: return "Center / LFE";
526 case PDMAUDIOMIXERCTL_REAR: return "Rear";
527 case PDMAUDIOMIXERCTL_LINE_IN: return "Line-In";
528 case PDMAUDIOMIXERCTL_MIC_IN: return "Microphone-In";
529 default: break;
530 }
531
532 AssertMsgFailed(("Invalid mixer control %ld\n", enmMixerCtl));
533 return "Unknown";
534}
535
536/**
537 * Converts an audio device flags to a string.
538 *
539 * @returns Stringified audio flags. Must be free'd with RTStrFree().
540 * NULL if no flags set.
541 * @param fFlags Audio flags to convert.
542 */
543char *DrvAudioHlpAudDevFlagsToStrA(PDMAUDIODEVFLAG fFlags)
544{
545#define APPEND_FLAG_TO_STR(_aFlag) \
546 if (fFlags & PDMAUDIODEV_FLAGS_##_aFlag) \
547 { \
548 if (pszFlags) \
549 { \
550 rc2 = RTStrAAppend(&pszFlags, " "); \
551 if (RT_FAILURE(rc2)) \
552 break; \
553 } \
554 \
555 rc2 = RTStrAAppend(&pszFlags, #_aFlag); \
556 if (RT_FAILURE(rc2)) \
557 break; \
558 } \
559
560 char *pszFlags = NULL;
561 int rc2 = VINF_SUCCESS;
562
563 do
564 {
565 APPEND_FLAG_TO_STR(DEFAULT);
566 APPEND_FLAG_TO_STR(HOTPLUG);
567 APPEND_FLAG_TO_STR(BUGGY);
568 APPEND_FLAG_TO_STR(IGNORE);
569 APPEND_FLAG_TO_STR(LOCKED);
570 APPEND_FLAG_TO_STR(DEAD);
571
572 } while (0);
573
574 if (!pszFlags)
575 rc2 = RTStrAAppend(&pszFlags, "NONE");
576
577 if ( RT_FAILURE(rc2)
578 && pszFlags)
579 {
580 RTStrFree(pszFlags);
581 pszFlags = NULL;
582 }
583
584#undef APPEND_FLAG_TO_STR
585
586 return pszFlags;
587}
588
589/**
590 * Converts a recording source enumeration to a string.
591 *
592 * @returns Stringified recording source, or "Unknown", if not found.
593 * @param enmRecSrc Recording source to convert.
594 */
595const char *DrvAudioHlpRecSrcToStr(const PDMAUDIORECSOURCE enmRecSrc)
596{
597 switch (enmRecSrc)
598 {
599 case PDMAUDIORECSOURCE_UNKNOWN: return "Unknown";
600 case PDMAUDIORECSOURCE_MIC: return "Microphone In";
601 case PDMAUDIORECSOURCE_CD: return "CD";
602 case PDMAUDIORECSOURCE_VIDEO: return "Video";
603 case PDMAUDIORECSOURCE_AUX: return "AUX";
604 case PDMAUDIORECSOURCE_LINE: return "Line In";
605 case PDMAUDIORECSOURCE_PHONE: return "Phone";
606 default:
607 break;
608 }
609
610 AssertMsgFailed(("Invalid recording source %ld\n", enmRecSrc));
611 return "Unknown";
612}
613
614/**
615 * Returns wether the given audio format has signed bits or not.
616 *
617 * @return IPRT status code.
618 * @return bool @c true for signed bits, @c false for unsigned.
619 * @param enmFmt Audio format to retrieve value for.
620 */
621bool DrvAudioHlpAudFmtIsSigned(PDMAUDIOFMT enmFmt)
622{
623 switch (enmFmt)
624 {
625 case PDMAUDIOFMT_S8:
626 case PDMAUDIOFMT_S16:
627 case PDMAUDIOFMT_S32:
628 return true;
629
630 case PDMAUDIOFMT_U8:
631 case PDMAUDIOFMT_U16:
632 case PDMAUDIOFMT_U32:
633 return false;
634
635 default:
636 break;
637 }
638
639 AssertMsgFailed(("Bogus audio format %ld\n", enmFmt));
640 return false;
641}
642
643/**
644 * Returns the bits of a given audio format.
645 *
646 * @return IPRT status code.
647 * @return uint8_t Bits of audio format.
648 * @param enmFmt Audio format to retrieve value for.
649 */
650uint8_t DrvAudioHlpAudFmtToBits(PDMAUDIOFMT enmFmt)
651{
652 switch (enmFmt)
653 {
654 case PDMAUDIOFMT_S8:
655 case PDMAUDIOFMT_U8:
656 return 8;
657
658 case PDMAUDIOFMT_U16:
659 case PDMAUDIOFMT_S16:
660 return 16;
661
662 case PDMAUDIOFMT_U32:
663 case PDMAUDIOFMT_S32:
664 return 32;
665
666 default:
667 break;
668 }
669
670 AssertMsgFailed(("Bogus audio format %ld\n", enmFmt));
671 return 0;
672}
673
674/**
675 * Converts an audio format to a string.
676 *
677 * @returns Stringified audio format, or "Unknown", if not found.
678 * @param enmFmt Audio format to convert.
679 */
680const char *DrvAudioHlpAudFmtToStr(PDMAUDIOFMT enmFmt)
681{
682 switch (enmFmt)
683 {
684 case PDMAUDIOFMT_U8:
685 return "U8";
686
687 case PDMAUDIOFMT_U16:
688 return "U16";
689
690 case PDMAUDIOFMT_U32:
691 return "U32";
692
693 case PDMAUDIOFMT_S8:
694 return "S8";
695
696 case PDMAUDIOFMT_S16:
697 return "S16";
698
699 case PDMAUDIOFMT_S32:
700 return "S32";
701
702 default:
703 break;
704 }
705
706 AssertMsgFailed(("Bogus audio format %ld\n", enmFmt));
707 return "Unknown";
708}
709
710/**
711 * Converts a given string to an audio format.
712 *
713 * @returns Audio format for the given string, or PDMAUDIOFMT_INVALID if not found.
714 * @param pszFmt String to convert to an audio format.
715 */
716PDMAUDIOFMT DrvAudioHlpStrToAudFmt(const char *pszFmt)
717{
718 AssertPtrReturn(pszFmt, PDMAUDIOFMT_INVALID);
719
720 if (!RTStrICmp(pszFmt, "u8"))
721 return PDMAUDIOFMT_U8;
722 else if (!RTStrICmp(pszFmt, "u16"))
723 return PDMAUDIOFMT_U16;
724 else if (!RTStrICmp(pszFmt, "u32"))
725 return PDMAUDIOFMT_U32;
726 else if (!RTStrICmp(pszFmt, "s8"))
727 return PDMAUDIOFMT_S8;
728 else if (!RTStrICmp(pszFmt, "s16"))
729 return PDMAUDIOFMT_S16;
730 else if (!RTStrICmp(pszFmt, "s32"))
731 return PDMAUDIOFMT_S32;
732
733 AssertMsgFailed(("Invalid audio format '%s'\n", pszFmt));
734 return PDMAUDIOFMT_INVALID;
735}
736
737/**
738 * Checks whether two given PCM properties are equal.
739 *
740 * @returns @c true if equal, @c false if not.
741 * @param pProps1 First properties to compare.
742 * @param pProps2 Second properties to compare.
743 */
744bool DrvAudioHlpPCMPropsAreEqual(const PPDMAUDIOPCMPROPS pProps1, const PPDMAUDIOPCMPROPS pProps2)
745{
746 AssertPtrReturn(pProps1, false);
747 AssertPtrReturn(pProps2, false);
748
749 if (pProps1 == pProps2) /* If the pointers match, take a shortcut. */
750 return true;
751
752 return pProps1->uHz == pProps2->uHz
753 && pProps1->cChannels == pProps2->cChannels
754 && pProps1->cBits == pProps2->cBits
755 && pProps1->fSigned == pProps2->fSigned
756 && pProps1->fSwapEndian == pProps2->fSwapEndian;
757}
758
759/**
760 * Checks whether given PCM properties are valid or not.
761 *
762 * Returns @c true if properties are valid, @c false if not.
763 * @param pProps PCM properties to check.
764 */
765bool DrvAudioHlpPCMPropsAreValid(const PPDMAUDIOPCMPROPS pProps)
766{
767 AssertPtrReturn(pProps, false);
768
769 /* Minimum 1 channel (mono), maximum 7.1 (= 8) channels. */
770 bool fValid = ( pProps->cChannels >= 1
771 && pProps->cChannels <= 8);
772
773 if (fValid)
774 {
775 switch (pProps->cBits)
776 {
777 case 8:
778 case 16:
779 /** @todo Do we need support for 24-bit samples? */
780 case 32:
781 break;
782 default:
783 fValid = false;
784 break;
785 }
786 }
787
788 if (!fValid)
789 return false;
790
791 fValid &= pProps->uHz > 0;
792 fValid &= pProps->cShift == PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pProps->cBits, pProps->cChannels);
793 fValid &= pProps->fSwapEndian == false; /** @todo Handling Big Endian audio data is not supported yet. */
794
795 return fValid;
796}
797
798/**
799 * Checks whether the given PCM properties are equal with the given
800 * stream configuration.
801 *
802 * @returns @c true if equal, @c false if not.
803 * @param pProps PCM properties to compare.
804 * @param pCfg Stream configuration to compare.
805 */
806bool DrvAudioHlpPCMPropsAreEqual(const PPDMAUDIOPCMPROPS pProps, const PPDMAUDIOSTREAMCFG pCfg)
807{
808 AssertPtrReturn(pProps, false);
809 AssertPtrReturn(pCfg, false);
810
811 return DrvAudioHlpPCMPropsAreEqual(pProps, &pCfg->Props);
812}
813
814/**
815 * Prints PCM properties to the debug log.
816 *
817 * @param pProps Stream configuration to log.
818 */
819void DrvAudioHlpPCMPropsPrint(const PPDMAUDIOPCMPROPS pProps)
820{
821 AssertPtrReturnVoid(pProps);
822
823 Log(("uHz=%RU32, cChannels=%RU8, cBits=%RU8%s",
824 pProps->uHz, pProps->cChannels, pProps->cBits, pProps->fSigned ? "S" : "U"));
825}
826
827/**
828 * Converts PCM properties to a audio stream configuration.
829 *
830 * @return IPRT status code.
831 * @param pProps Pointer to PCM properties to convert.
832 * @param pCfg Pointer to audio stream configuration to store result into.
833 */
834int DrvAudioHlpPCMPropsToStreamCfg(const PPDMAUDIOPCMPROPS pProps, PPDMAUDIOSTREAMCFG pCfg)
835{
836 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
837 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
838
839 memcpy(&pCfg->Props, pProps, sizeof(PDMAUDIOPCMPROPS));
840 return VINF_SUCCESS;
841}
842
843/**
844 * Checks whether a given stream configuration is valid or not.
845 *
846 * Returns @c true if configuration is valid, @c false if not.
847 * @param pCfg Stream configuration to check.
848 */
849bool DrvAudioHlpStreamCfgIsValid(const PPDMAUDIOSTREAMCFG pCfg)
850{
851 AssertPtrReturn(pCfg, false);
852
853 bool fValid = ( pCfg->enmDir == PDMAUDIODIR_IN
854 || pCfg->enmDir == PDMAUDIODIR_OUT);
855
856 fValid &= ( pCfg->enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED
857 || pCfg->enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
858
859 if (fValid)
860 fValid = DrvAudioHlpPCMPropsAreValid(&pCfg->Props);
861
862 return fValid;
863}
864
865/**
866 * Frees an allocated audio stream configuration.
867 *
868 * @param pCfg Audio stream configuration to free.
869 */
870void DrvAudioHlpStreamCfgFree(PPDMAUDIOSTREAMCFG pCfg)
871{
872 if (pCfg)
873 {
874 RTMemFree(pCfg);
875 pCfg = NULL;
876 }
877}
878
879/**
880 * Copies a source stream configuration to a destination stream configuration.
881 *
882 * @returns IPRT status code.
883 * @param pDstCfg Destination stream configuration to copy source to.
884 * @param pSrcCfg Source stream configuration to copy to destination.
885 */
886int DrvAudioHlpStreamCfgCopy(PPDMAUDIOSTREAMCFG pDstCfg, const PPDMAUDIOSTREAMCFG pSrcCfg)
887{
888 AssertPtrReturn(pDstCfg, VERR_INVALID_POINTER);
889 AssertPtrReturn(pSrcCfg, VERR_INVALID_POINTER);
890
891#ifdef VBOX_STRICT
892 if (!DrvAudioHlpStreamCfgIsValid(pSrcCfg))
893 {
894 AssertMsgFailed(("Stream config '%s' (%p) is invalid\n", pSrcCfg->szName, pSrcCfg));
895 return VERR_INVALID_PARAMETER;
896 }
897#endif
898
899 memcpy(pDstCfg, pSrcCfg, sizeof(PDMAUDIOSTREAMCFG));
900
901 return VINF_SUCCESS;
902}
903
904/**
905 * Duplicates an audio stream configuration.
906 * Must be free'd with DrvAudioHlpStreamCfgFree().
907 *
908 * @return Duplicates audio stream configuration on success, or NULL on failure.
909 * @param pCfg Audio stream configuration to duplicate.
910 */
911PPDMAUDIOSTREAMCFG DrvAudioHlpStreamCfgDup(const PPDMAUDIOSTREAMCFG pCfg)
912{
913 AssertPtrReturn(pCfg, NULL);
914
915 PPDMAUDIOSTREAMCFG pDst = (PPDMAUDIOSTREAMCFG)RTMemAllocZ(sizeof(PDMAUDIOSTREAMCFG));
916 if (!pDst)
917 return NULL;
918
919 int rc2 = DrvAudioHlpStreamCfgCopy(pDst, pCfg);
920 if (RT_FAILURE(rc2))
921 {
922 DrvAudioHlpStreamCfgFree(pDst);
923 pDst = NULL;
924 }
925
926 AssertPtr(pDst);
927 return pDst;
928}
929
930/**
931 * Prints an audio stream configuration to the debug log.
932 *
933 * @param pCfg Stream configuration to log.
934 */
935void DrvAudioHlpStreamCfgPrint(const PPDMAUDIOSTREAMCFG pCfg)
936{
937 if (!pCfg)
938 return;
939
940 LogFunc(("szName=%s, enmDir=%RU32 (uHz=%RU32, cBits=%RU8%s, cChannels=%RU8)\n",
941 pCfg->szName, pCfg->enmDir,
942 pCfg->Props.uHz, pCfg->Props.cBits, pCfg->Props.fSigned ? "S" : "U", pCfg->Props.cChannels));
943}
944
945/**
946 * Converts a stream command to a string.
947 *
948 * @returns Stringified stream command, or "Unknown", if not found.
949 * @param enmCmd Stream command to convert.
950 */
951const char *DrvAudioHlpStreamCmdToStr(PDMAUDIOSTREAMCMD enmCmd)
952{
953 switch (enmCmd)
954 {
955 case PDMAUDIOSTREAMCMD_UNKNOWN: return "Unknown";
956 case PDMAUDIOSTREAMCMD_ENABLE: return "Enable";
957 case PDMAUDIOSTREAMCMD_DISABLE: return "Disable";
958 case PDMAUDIOSTREAMCMD_PAUSE: return "Pause";
959 case PDMAUDIOSTREAMCMD_RESUME: return "Resume";
960 default: break;
961 }
962
963 AssertMsgFailed(("Invalid stream command %ld\n", enmCmd));
964 return "Unknown";
965}
966
967/**
968 * Calculates the audio bit rate of the given bits per sample, the Hz and the number
969 * of audio channels.
970 *
971 * Divide the result by 8 to get the byte rate.
972 *
973 * @returns The calculated bit rate.
974 * @param cBits Number of bits per sample.
975 * @param uHz Hz (Hertz) rate.
976 * @param cChannels Number of audio channels.
977 */
978uint32_t DrvAudioHlpCalcBitrate(uint8_t cBits, uint32_t uHz, uint8_t cChannels)
979{
980 return (cBits * uHz * cChannels);
981}
982
983/**
984 * Calculates the audio bit rate out of a given audio stream configuration.
985 *
986 * Divide the result by 8 to get the byte rate.
987 *
988 * @returns The calculated bit rate.
989 * @param pProps PCM properties to calculate bitrate for.
990 *
991 * @remark
992 */
993uint32_t DrvAudioHlpCalcBitrate(const PPDMAUDIOPCMPROPS pProps)
994{
995 return DrvAudioHlpCalcBitrate(pProps->cBits, pProps->uHz, pProps->cChannels);
996}
997
998/**
999 * Sanitizes the file name component so that unsupported characters
1000 * will be replaced by an underscore ("_").
1001 *
1002 * @return IPRT status code.
1003 * @param pszPath Path to sanitize.
1004 * @param cbPath Size (in bytes) of path to sanitize.
1005 */
1006int DrvAudioHlpSanitizeFileName(char *pszPath, size_t cbPath)
1007{
1008 RT_NOREF(cbPath);
1009 int rc = VINF_SUCCESS;
1010#ifdef RT_OS_WINDOWS
1011 /* Filter out characters not allowed on Windows platforms, put in by
1012 RTTimeSpecToString(). */
1013 /** @todo Use something like RTPathSanitize() if available later some time. */
1014 static RTUNICP const s_uszValidRangePairs[] =
1015 {
1016 ' ', ' ',
1017 '(', ')',
1018 '-', '.',
1019 '0', '9',
1020 'A', 'Z',
1021 'a', 'z',
1022 '_', '_',
1023 0xa0, 0xd7af,
1024 '\0'
1025 };
1026 ssize_t cReplaced = RTStrPurgeComplementSet(pszPath, s_uszValidRangePairs, '_' /* Replacement */);
1027 if (cReplaced < 0)
1028 rc = VERR_INVALID_UTF8_ENCODING;
1029#else
1030 RT_NOREF(pszPath);
1031#endif
1032 return rc;
1033}
1034
1035/**
1036 * Constructs an unique file name, based on the given path and the audio file type.
1037 *
1038 * @returns IPRT status code.
1039 * @param pszFile Where to store the constructed file name.
1040 * @param cchFile Size (in characters) of the file name buffer.
1041 * @param pszPath Base path to use.
1042 * @param pszName A name for better identifying the file. Optional.
1043 * @param enmType Audio file type to construct file name for.
1044 */
1045int DrvAudioHlpGetFileName(char *pszFile, size_t cchFile, const char *pszPath, const char *pszName, PDMAUDIOFILETYPE enmType)
1046{
1047 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
1048 AssertReturn(cchFile, VERR_INVALID_PARAMETER);
1049 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1050 /* pszName is optional. */
1051
1052 int rc;
1053
1054 do
1055 {
1056 char szFilePath[RTPATH_MAX];
1057 RTStrPrintf(szFilePath, sizeof(szFilePath), "%s", pszPath);
1058
1059 /* Create it when necessary. */
1060 if (!RTDirExists(szFilePath))
1061 {
1062 rc = RTDirCreateFullPath(szFilePath, RTFS_UNIX_IRWXU);
1063 if (RT_FAILURE(rc))
1064 break;
1065 }
1066
1067 /* The actually drop directory consist of the current time stamp and a
1068 * unique number when necessary. */
1069 char pszTime[64];
1070 RTTIMESPEC time;
1071 if (!RTTimeSpecToString(RTTimeNow(&time), pszTime, sizeof(pszTime)))
1072 {
1073 rc = VERR_BUFFER_OVERFLOW;
1074 break;
1075 }
1076
1077 rc = DrvAudioHlpSanitizeFileName(pszTime, sizeof(pszTime));
1078 if (RT_FAILURE(rc))
1079 break;
1080
1081 rc = RTPathAppend(szFilePath, sizeof(szFilePath), pszTime);
1082 if (RT_FAILURE(rc))
1083 break;
1084
1085 if (pszName) /* Optional name given? */
1086 {
1087 rc = RTStrCat(szFilePath, sizeof(szFilePath), "-");
1088 if (RT_FAILURE(rc))
1089 break;
1090
1091 rc = RTStrCat(szFilePath, sizeof(szFilePath), pszName);
1092 if (RT_FAILURE(rc))
1093 break;
1094 }
1095
1096 switch (enmType)
1097 {
1098 case PDMAUDIOFILETYPE_WAV:
1099 rc = RTStrCat(szFilePath, sizeof(szFilePath), ".wav");
1100 break;
1101
1102 default:
1103 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1104 }
1105
1106 if (RT_FAILURE(rc))
1107 break;
1108
1109 RTStrPrintf(pszFile, cchFile, "%s", szFilePath);
1110
1111 } while (0);
1112
1113 LogFlowFuncLeaveRC(rc);
1114 return rc;
1115}
1116
1117/**
1118 * Opens or creates a wave (.WAV) file.
1119 *
1120 * @returns IPRT status code.
1121 * @param pFile Pointer to audio file handle to use.
1122 * @param pszFile File path of file to open or create.
1123 * @param fOpen Open flags.
1124 * @param pProps PCM properties to use.
1125 * @param fFlags Audio file flags.
1126 */
1127int DrvAudioHlpWAVFileOpen(PPDMAUDIOFILE pFile, const char *pszFile, uint32_t fOpen, const PPDMAUDIOPCMPROPS pProps,
1128 PDMAUDIOFILEFLAGS fFlags)
1129{
1130 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1131 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
1132 /** @todo Validate fOpen flags. */
1133 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
1134 RT_NOREF(fFlags); /** @todo Validate fFlags flags. */
1135
1136 Assert(pProps->cChannels);
1137 Assert(pProps->uHz);
1138 Assert(pProps->cBits);
1139
1140 pFile->pvData = (PAUDIOWAVFILEDATA)RTMemAllocZ(sizeof(AUDIOWAVFILEDATA));
1141 if (!pFile->pvData)
1142 return VERR_NO_MEMORY;
1143 pFile->cbData = sizeof(PAUDIOWAVFILEDATA);
1144
1145 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1146 AssertPtr(pData);
1147
1148 /* Header. */
1149 pData->Hdr.u32RIFF = AUDIO_MAKE_FOURCC('R','I','F','F');
1150 pData->Hdr.u32Size = 36;
1151 pData->Hdr.u32WAVE = AUDIO_MAKE_FOURCC('W','A','V','E');
1152
1153 pData->Hdr.u32Fmt = AUDIO_MAKE_FOURCC('f','m','t',' ');
1154 pData->Hdr.u32Size1 = 16; /* Means PCM. */
1155 pData->Hdr.u16AudioFormat = 1; /* PCM, linear quantization. */
1156 pData->Hdr.u16NumChannels = pProps->cChannels;
1157 pData->Hdr.u32SampleRate = pProps->uHz;
1158 pData->Hdr.u32ByteRate = DrvAudioHlpCalcBitrate(pProps->cBits, pProps->uHz, pProps->cChannels) / 8;
1159 pData->Hdr.u16BlockAlign = pProps->cChannels * pProps->cBits / 8;
1160 pData->Hdr.u16BitsPerSample = pProps->cBits;
1161
1162 /* Data chunk. */
1163 pData->Hdr.u32ID2 = AUDIO_MAKE_FOURCC('d','a','t','a');
1164 pData->Hdr.u32Size2 = 0;
1165
1166 int rc = RTFileOpen(&pFile->hFile, pszFile, fOpen);
1167 if (RT_SUCCESS(rc))
1168 {
1169 rc = RTFileWrite(pFile->hFile, &pData->Hdr, sizeof(pData->Hdr), NULL);
1170 if (RT_FAILURE(rc))
1171 {
1172 RTFileClose(pFile->hFile);
1173 pFile->hFile = NIL_RTFILE;
1174 }
1175 }
1176
1177 if (RT_SUCCESS(rc))
1178 {
1179 pFile->enmType = PDMAUDIOFILETYPE_WAV;
1180
1181 RTStrPrintf(pFile->szName, RT_ELEMENTS(pFile->szName), "%s", pszFile);
1182 }
1183 else
1184 {
1185 RTMemFree(pFile->pvData);
1186 pFile->pvData = NULL;
1187 pFile->cbData = 0;
1188 }
1189
1190 return rc;
1191}
1192
1193/**
1194 * Closes a wave (.WAV) audio file.
1195 *
1196 * @returns IPRT status code.
1197 * @param pFile Audio file handle to close.
1198 */
1199int DrvAudioHlpWAVFileClose(PPDMAUDIOFILE pFile)
1200{
1201 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1202
1203 Assert(pFile->enmType == PDMAUDIOFILETYPE_WAV);
1204
1205 if (pFile->hFile != NIL_RTFILE)
1206 {
1207 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1208 AssertPtr(pData);
1209
1210 /* Update the header with the current data size. */
1211 RTFileWriteAt(pFile->hFile, 0, &pData->Hdr, sizeof(pData->Hdr), NULL);
1212
1213 RTFileClose(pFile->hFile);
1214 pFile->hFile = NIL_RTFILE;
1215 }
1216
1217 if (pFile->pvData)
1218 {
1219 RTMemFree(pFile->pvData);
1220 pFile->pvData = NULL;
1221 }
1222
1223 pFile->cbData = 0;
1224 pFile->enmType = PDMAUDIOFILETYPE_UNKNOWN;
1225
1226 return VINF_SUCCESS;
1227}
1228
1229/**
1230 * Returns the raw PCM audio data size of a wave file.
1231 * This does *not* include file headers and other data which does
1232 * not belong to the actual PCM audio data.
1233 *
1234 * @returns Size (in bytes) of the raw PCM audio data.
1235 * @param pFile Audio file handle to retrieve the audio data size for.
1236 */
1237size_t DrvAudioHlpWAVFileGetDataSize(PPDMAUDIOFILE pFile)
1238{
1239 AssertPtrReturn(pFile, 0);
1240
1241 Assert(pFile->enmType == PDMAUDIOFILETYPE_WAV);
1242
1243 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1244 AssertPtr(pData);
1245
1246 return pData->Hdr.u32Size2;
1247}
1248
1249/**
1250 * Write PCM data to a wave (.WAV) file.
1251 *
1252 * @returns IPRT status code.
1253 * @param pFile Audio file handle to write PCM data to.
1254 * @param pvBuf Audio data to write.
1255 * @param cbBuf Size (in bytes) of audio data to write.
1256 * @param fFlags Additional write flags. Not being used at the moment and must be 0.
1257 */
1258int DrvAudioHlpWAVFileWrite(PPDMAUDIOFILE pFile, const void *pvBuf, size_t cbBuf, uint32_t fFlags)
1259{
1260 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1261 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1262
1263 AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); /** @todo fFlags are currently not implemented. */
1264
1265 Assert(pFile->enmType == PDMAUDIOFILETYPE_WAV);
1266
1267 if (!cbBuf)
1268 return VINF_SUCCESS;
1269
1270 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1271 AssertPtr(pData);
1272
1273 int rc = RTFileWrite(pFile->hFile, pvBuf, cbBuf, NULL);
1274 if (RT_SUCCESS(rc))
1275 {
1276 pData->Hdr.u32Size += (uint32_t)cbBuf;
1277 pData->Hdr.u32Size2 += (uint32_t)cbBuf;
1278 }
1279
1280 return rc;
1281}
1282
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