VirtualBox

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

Last change on this file since 63719 was 63715, checked in by vboxsync, 9 years ago

Build fix (warnings as errors).

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