VirtualBox

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

Last change on this file since 69304 was 69288, checked in by vboxsync, 7 years ago

Devices/Audio: scm updates

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