VirtualBox

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

Last change on this file since 65635 was 65624, checked in by vboxsync, 8 years ago

Audio: More abstraction for the backends: Now the backend stream's data is completely separate from the audio connector interface. That way the backends cannot mess with the audio connector's data (e.g. mixing buffers and friends) anymore, and those are forced to use the audio connector API as meant now. Needs more testing, partly work in progress.

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