VirtualBox

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

Last change on this file since 63713 was 63711, checked in by vboxsync, 8 years ago

Audio: Implemented support for audio device enumeration handling, audio device information and audio backend notifications. This will enable to let the backends tell the audio subsystem that the host audio configuration has changed and react accordingly to it. For now only the Core Audio backend supports device enumeration. Further this also will get rid of the static initialization on the device emulation side, which, if at VM startup no audio input(s) / output(s) were available, was triggering a warning. The NULL backend therefore does not need to act as a (static) fallback anymore.

Work in progress.

  • 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 63711 2016-09-05 12:04:01Z 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;
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.

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