VirtualBox

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

Last change on this file since 68253 was 68136, checked in by vboxsync, 7 years ago

Audio: Header updates.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.1 KB
Line 
1/* $Id: DrvAudioCommon.cpp 68136 2017-07-27 10:52:43Z 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 AssertMsg(fValid, ("Channel count (%RU8) invalid\n", pProps->cChannels));
774
775 if (fValid)
776 {
777 switch (pProps->cBits)
778 {
779 case 8:
780 case 16:
781 /** @todo Do we need support for 24-bit samples? */
782 case 32:
783 break;
784 default:
785 fValid = false;
786 break;
787 }
788
789 AssertMsg(fValid, ("Bits (%RU8) invalid\n", pProps->cBits));
790 }
791
792 if (!fValid)
793 return false;
794
795 fValid &= pProps->uHz > 0;
796 AssertMsg(fValid, ("Hz (%RU32) invalid\n", pProps->uHz));
797
798 fValid &= pProps->cShift == PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pProps->cBits, pProps->cChannels);
799 AssertMsg(fValid, ("Shift (%RU8) invalid\n", pProps->cShift));
800
801 fValid &= pProps->fSwapEndian == false; /** @todo Handling Big Endian audio data is not supported yet. */
802 AssertMsg(fValid, ("Swap endian (%RTbool) invalid\n", pProps->fSwapEndian));
803
804 return fValid;
805}
806
807/**
808 * Checks whether the given PCM properties are equal with the given
809 * stream configuration.
810 *
811 * @returns @c true if equal, @c false if not.
812 * @param pProps PCM properties to compare.
813 * @param pCfg Stream configuration to compare.
814 */
815bool DrvAudioHlpPCMPropsAreEqual(const PPDMAUDIOPCMPROPS pProps, const PPDMAUDIOSTREAMCFG pCfg)
816{
817 AssertPtrReturn(pProps, false);
818 AssertPtrReturn(pCfg, false);
819
820 return DrvAudioHlpPCMPropsAreEqual(pProps, &pCfg->Props);
821}
822
823/**
824 * Prints PCM properties to the debug log.
825 *
826 * @param pProps Stream configuration to log.
827 */
828void DrvAudioHlpPCMPropsPrint(const PPDMAUDIOPCMPROPS pProps)
829{
830 AssertPtrReturnVoid(pProps);
831
832 Log(("uHz=%RU32, cChannels=%RU8, cBits=%RU8%s",
833 pProps->uHz, pProps->cChannels, pProps->cBits, pProps->fSigned ? "S" : "U"));
834}
835
836/**
837 * Converts PCM properties to a audio stream configuration.
838 *
839 * @return IPRT status code.
840 * @param pProps Pointer to PCM properties to convert.
841 * @param pCfg Pointer to audio stream configuration to store result into.
842 */
843int DrvAudioHlpPCMPropsToStreamCfg(const PPDMAUDIOPCMPROPS pProps, PPDMAUDIOSTREAMCFG pCfg)
844{
845 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
846 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
847
848 memcpy(&pCfg->Props, pProps, sizeof(PDMAUDIOPCMPROPS));
849 return VINF_SUCCESS;
850}
851
852/**
853 * Checks whether a given stream configuration is valid or not.
854 *
855 * Returns @c true if configuration is valid, @c false if not.
856 * @param pCfg Stream configuration to check.
857 */
858bool DrvAudioHlpStreamCfgIsValid(const PPDMAUDIOSTREAMCFG pCfg)
859{
860 AssertPtrReturn(pCfg, false);
861
862 bool fValid = ( pCfg->enmDir == PDMAUDIODIR_IN
863 || pCfg->enmDir == PDMAUDIODIR_OUT);
864 AssertMsg(fValid, ("Stream direction not set / invalid\n"));
865
866 fValid &= ( pCfg->enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED
867 || pCfg->enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
868 AssertMsg(fValid, ("Stream layout not set / invalid\n"));
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 AssertPtrReturnVoid(pCfg);
949
950 LogFlowFunc(("uHz=%RU32, cChannels=%RU8, cBits=%RU8%s",
951 pCfg->Props.uHz, pCfg->Props.cChannels, pCfg->Props.cBits, pCfg->Props.fSigned ? "S" : "U"));
952}
953
954/**
955 * Converts a stream command to a string.
956 *
957 * @returns Stringified stream command, or "Unknown", if not found.
958 * @param enmCmd Stream command to convert.
959 */
960const char *DrvAudioHlpStreamCmdToStr(PDMAUDIOSTREAMCMD enmCmd)
961{
962 switch (enmCmd)
963 {
964 case PDMAUDIOSTREAMCMD_UNKNOWN: return "Unknown";
965 case PDMAUDIOSTREAMCMD_ENABLE: return "Enable";
966 case PDMAUDIOSTREAMCMD_DISABLE: return "Disable";
967 case PDMAUDIOSTREAMCMD_PAUSE: return "Pause";
968 case PDMAUDIOSTREAMCMD_RESUME: return "Resume";
969 default: break;
970 }
971
972 AssertMsgFailed(("Invalid stream command %ld\n", enmCmd));
973 return "Unknown";
974}
975
976/**
977 * Calculates the audio bit rate of the given bits per sample, the Hz and the number
978 * of audio channels.
979 *
980 * Divide the result by 8 to get the byte rate.
981 *
982 * @returns The calculated bit rate.
983 * @param cBits Number of bits per sample.
984 * @param uHz Hz (Hertz) rate.
985 * @param cChannels Number of audio channels.
986 */
987uint32_t DrvAudioHlpCalcBitrate(uint8_t cBits, uint32_t uHz, uint8_t cChannels)
988{
989 return (cBits * uHz * cChannels);
990}
991
992/**
993 * Calculates the audio bit rate out of a given audio stream configuration.
994 *
995 * Divide the result by 8 to get the byte rate.
996 *
997 * @returns The calculated bit rate.
998 * @param pProps PCM properties to calculate bitrate for.
999 *
1000 * @remark
1001 */
1002uint32_t DrvAudioHlpCalcBitrate(const PPDMAUDIOPCMPROPS pProps)
1003{
1004 return DrvAudioHlpCalcBitrate(pProps->cBits, pProps->uHz, pProps->cChannels);
1005}
1006
1007/**
1008 * Sanitizes the file name component so that unsupported characters
1009 * will be replaced by an underscore ("_").
1010 *
1011 * @return IPRT status code.
1012 * @param pszPath Path to sanitize.
1013 * @param cbPath Size (in bytes) of path to sanitize.
1014 */
1015int DrvAudioHlpSanitizeFileName(char *pszPath, size_t cbPath)
1016{
1017 RT_NOREF(cbPath);
1018 int rc = VINF_SUCCESS;
1019#ifdef RT_OS_WINDOWS
1020 /* Filter out characters not allowed on Windows platforms, put in by
1021 RTTimeSpecToString(). */
1022 /** @todo Use something like RTPathSanitize() if available later some time. */
1023 static RTUNICP const s_uszValidRangePairs[] =
1024 {
1025 ' ', ' ',
1026 '(', ')',
1027 '-', '.',
1028 '0', '9',
1029 'A', 'Z',
1030 'a', 'z',
1031 '_', '_',
1032 0xa0, 0xd7af,
1033 '\0'
1034 };
1035 ssize_t cReplaced = RTStrPurgeComplementSet(pszPath, s_uszValidRangePairs, '_' /* Replacement */);
1036 if (cReplaced < 0)
1037 rc = VERR_INVALID_UTF8_ENCODING;
1038#else
1039 RT_NOREF(pszPath);
1040#endif
1041 return rc;
1042}
1043
1044/**
1045 * Constructs an unique file name, based on the given path and the audio file type.
1046 *
1047 * @returns IPRT status code.
1048 * @param pszFile Where to store the constructed file name.
1049 * @param cchFile Size (in characters) of the file name buffer.
1050 * @param pszPath Base path to use.
1051 * @param pszName A name for better identifying the file. Optional.
1052 * @param enmType Audio file type to construct file name for.
1053 */
1054int DrvAudioHlpGetFileName(char *pszFile, size_t cchFile, const char *pszPath, const char *pszName, PDMAUDIOFILETYPE enmType)
1055{
1056 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
1057 AssertReturn(cchFile, VERR_INVALID_PARAMETER);
1058 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1059 /* pszName is optional. */
1060
1061 int rc;
1062
1063 do
1064 {
1065 char szFilePath[RTPATH_MAX];
1066 RTStrPrintf(szFilePath, sizeof(szFilePath), "%s", pszPath);
1067
1068 /* Create it when necessary. */
1069 if (!RTDirExists(szFilePath))
1070 {
1071 rc = RTDirCreateFullPath(szFilePath, RTFS_UNIX_IRWXU);
1072 if (RT_FAILURE(rc))
1073 break;
1074 }
1075
1076 /* The actually drop directory consist of the current time stamp and a
1077 * unique number when necessary. */
1078 char pszTime[64];
1079 RTTIMESPEC time;
1080 if (!RTTimeSpecToString(RTTimeNow(&time), pszTime, sizeof(pszTime)))
1081 {
1082 rc = VERR_BUFFER_OVERFLOW;
1083 break;
1084 }
1085
1086 rc = DrvAudioHlpSanitizeFileName(pszTime, sizeof(pszTime));
1087 if (RT_FAILURE(rc))
1088 break;
1089
1090 rc = RTPathAppend(szFilePath, sizeof(szFilePath), pszTime);
1091 if (RT_FAILURE(rc))
1092 break;
1093
1094 if (pszName) /* Optional name given? */
1095 {
1096 rc = RTStrCat(szFilePath, sizeof(szFilePath), "-");
1097 if (RT_FAILURE(rc))
1098 break;
1099
1100 rc = RTStrCat(szFilePath, sizeof(szFilePath), pszName);
1101 if (RT_FAILURE(rc))
1102 break;
1103 }
1104
1105 switch (enmType)
1106 {
1107 case PDMAUDIOFILETYPE_WAV:
1108 rc = RTStrCat(szFilePath, sizeof(szFilePath), ".wav");
1109 break;
1110
1111 default:
1112 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1113 }
1114
1115 if (RT_FAILURE(rc))
1116 break;
1117
1118 RTStrPrintf(pszFile, cchFile, "%s", szFilePath);
1119
1120 } while (0);
1121
1122 LogFlowFuncLeaveRC(rc);
1123 return rc;
1124}
1125
1126/**
1127 * Opens or creates a wave (.WAV) file.
1128 *
1129 * @returns IPRT status code.
1130 * @param pFile Pointer to audio file handle to use.
1131 * @param pszFile File path of file to open or create.
1132 * @param fOpen Open flags.
1133 * @param pProps PCM properties to use.
1134 * @param fFlags Audio file flags.
1135 */
1136int DrvAudioHlpWAVFileOpen(PPDMAUDIOFILE pFile, const char *pszFile, uint32_t fOpen, const PPDMAUDIOPCMPROPS pProps,
1137 PDMAUDIOFILEFLAGS fFlags)
1138{
1139 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1140 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
1141 /** @todo Validate fOpen flags. */
1142 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
1143 RT_NOREF(fFlags); /** @todo Validate fFlags flags. */
1144
1145 Assert(pProps->cChannels);
1146 Assert(pProps->uHz);
1147 Assert(pProps->cBits);
1148
1149 pFile->pvData = (PAUDIOWAVFILEDATA)RTMemAllocZ(sizeof(AUDIOWAVFILEDATA));
1150 if (!pFile->pvData)
1151 return VERR_NO_MEMORY;
1152 pFile->cbData = sizeof(PAUDIOWAVFILEDATA);
1153
1154 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1155 AssertPtr(pData);
1156
1157 /* Header. */
1158 pData->Hdr.u32RIFF = AUDIO_MAKE_FOURCC('R','I','F','F');
1159 pData->Hdr.u32Size = 36;
1160 pData->Hdr.u32WAVE = AUDIO_MAKE_FOURCC('W','A','V','E');
1161
1162 pData->Hdr.u32Fmt = AUDIO_MAKE_FOURCC('f','m','t',' ');
1163 pData->Hdr.u32Size1 = 16; /* Means PCM. */
1164 pData->Hdr.u16AudioFormat = 1; /* PCM, linear quantization. */
1165 pData->Hdr.u16NumChannels = pProps->cChannels;
1166 pData->Hdr.u32SampleRate = pProps->uHz;
1167 pData->Hdr.u32ByteRate = DrvAudioHlpCalcBitrate(pProps->cBits, pProps->uHz, pProps->cChannels) / 8;
1168 pData->Hdr.u16BlockAlign = pProps->cChannels * pProps->cBits / 8;
1169 pData->Hdr.u16BitsPerSample = pProps->cBits;
1170
1171 /* Data chunk. */
1172 pData->Hdr.u32ID2 = AUDIO_MAKE_FOURCC('d','a','t','a');
1173 pData->Hdr.u32Size2 = 0;
1174
1175 int rc = RTFileOpen(&pFile->hFile, pszFile, fOpen);
1176 if (RT_SUCCESS(rc))
1177 {
1178 rc = RTFileWrite(pFile->hFile, &pData->Hdr, sizeof(pData->Hdr), NULL);
1179 if (RT_FAILURE(rc))
1180 {
1181 RTFileClose(pFile->hFile);
1182 pFile->hFile = NIL_RTFILE;
1183 }
1184 }
1185
1186 if (RT_SUCCESS(rc))
1187 {
1188 pFile->enmType = PDMAUDIOFILETYPE_WAV;
1189
1190 RTStrPrintf(pFile->szName, RT_ELEMENTS(pFile->szName), "%s", pszFile);
1191 }
1192 else
1193 {
1194 RTMemFree(pFile->pvData);
1195 pFile->pvData = NULL;
1196 pFile->cbData = 0;
1197 }
1198
1199 return rc;
1200}
1201
1202/**
1203 * Closes a wave (.WAV) audio file.
1204 *
1205 * @returns IPRT status code.
1206 * @param pFile Audio file handle to close.
1207 */
1208int DrvAudioHlpWAVFileClose(PPDMAUDIOFILE pFile)
1209{
1210 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1211
1212 Assert(pFile->enmType == PDMAUDIOFILETYPE_WAV);
1213
1214 if (pFile->hFile != NIL_RTFILE)
1215 {
1216 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1217 AssertPtr(pData);
1218
1219 /* Update the header with the current data size. */
1220 RTFileWriteAt(pFile->hFile, 0, &pData->Hdr, sizeof(pData->Hdr), NULL);
1221
1222 RTFileClose(pFile->hFile);
1223 pFile->hFile = NIL_RTFILE;
1224 }
1225
1226 if (pFile->pvData)
1227 {
1228 RTMemFree(pFile->pvData);
1229 pFile->pvData = NULL;
1230 }
1231
1232 pFile->cbData = 0;
1233 pFile->enmType = PDMAUDIOFILETYPE_UNKNOWN;
1234
1235 return VINF_SUCCESS;
1236}
1237
1238/**
1239 * Returns the raw PCM audio data size of a wave file.
1240 * This does *not* include file headers and other data which does
1241 * not belong to the actual PCM audio data.
1242 *
1243 * @returns Size (in bytes) of the raw PCM audio data.
1244 * @param pFile Audio file handle to retrieve the audio data size for.
1245 */
1246size_t DrvAudioHlpWAVFileGetDataSize(PPDMAUDIOFILE pFile)
1247{
1248 AssertPtrReturn(pFile, 0);
1249
1250 Assert(pFile->enmType == PDMAUDIOFILETYPE_WAV);
1251
1252 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1253 AssertPtr(pData);
1254
1255 return pData->Hdr.u32Size2;
1256}
1257
1258/**
1259 * Write PCM data to a wave (.WAV) file.
1260 *
1261 * @returns IPRT status code.
1262 * @param pFile Audio file handle to write PCM data to.
1263 * @param pvBuf Audio data to write.
1264 * @param cbBuf Size (in bytes) of audio data to write.
1265 * @param fFlags Additional write flags. Not being used at the moment and must be 0.
1266 */
1267int DrvAudioHlpWAVFileWrite(PPDMAUDIOFILE pFile, const void *pvBuf, size_t cbBuf, uint32_t fFlags)
1268{
1269 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1270 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1271
1272 AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); /** @todo fFlags are currently not implemented. */
1273
1274 Assert(pFile->enmType == PDMAUDIOFILETYPE_WAV);
1275
1276 if (!cbBuf)
1277 return VINF_SUCCESS;
1278
1279 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1280 AssertPtr(pData);
1281
1282 int rc = RTFileWrite(pFile->hFile, pvBuf, cbBuf, NULL);
1283 if (RT_SUCCESS(rc))
1284 {
1285 pData->Hdr.u32Size += (uint32_t)cbBuf;
1286 pData->Hdr.u32Size2 += (uint32_t)cbBuf;
1287 }
1288
1289 return rc;
1290}
1291
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