VirtualBox

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

Last change on this file since 69183 was 69119, checked in by vboxsync, 8 years ago

Audio: More cleanups (missing keywords, incorrect #endif docs, stuff)

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

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