VirtualBox

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

Last change on this file since 88012 was 88012, checked in by vboxsync, 4 years ago

Audio: DrvAudioHlpPCMPropsPrint -> DrvAudioHlpPcmPropsLog. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.3 KB
Line 
1/* $Id: DrvAudioCommon.cpp 88012 2021-03-08 12:41:31Z vboxsync $ */
2/** @file
3 * Intermedia audio driver, common routines.
4 *
5 * These are also used in the drivers which are bound to Main, e.g. the VRDE
6 * or the video audio recording drivers.
7 */
8
9/*
10 * Copyright (C) 2006-2020 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 */
20
21
22/*********************************************************************************************************************************
23* Header Files *
24*********************************************************************************************************************************/
25#include <iprt/alloc.h>
26#include <iprt/asm-math.h>
27#include <iprt/assert.h>
28#include <iprt/dir.h>
29#include <iprt/file.h>
30#include <iprt/string.h>
31#include <iprt/uuid.h>
32
33#define LOG_GROUP LOG_GROUP_DRV_AUDIO
34#include <VBox/log.h>
35
36#include <VBox/err.h>
37#include <VBox/vmm/pdmdev.h>
38#include <VBox/vmm/pdm.h>
39#include <VBox/vmm/mm.h>
40
41#include <ctype.h>
42#include <stdlib.h>
43
44#include "DrvAudio.h"
45#include "AudioMixBuffer.h"
46
47
48/*********************************************************************************************************************************
49* Structures and Typedefs *
50*********************************************************************************************************************************/
51/**
52 * Structure for building up a .WAV file header.
53 */
54typedef struct AUDIOWAVFILEHDR
55{
56 uint32_t u32RIFF;
57 uint32_t u32Size;
58 uint32_t u32WAVE;
59
60 uint32_t u32Fmt;
61 uint32_t u32Size1;
62 uint16_t u16AudioFormat;
63 uint16_t u16NumChannels;
64 uint32_t u32SampleRate;
65 uint32_t u32ByteRate;
66 uint16_t u16BlockAlign;
67 uint16_t u16BitsPerSample;
68
69 uint32_t u32ID2;
70 uint32_t u32Size2;
71} AUDIOWAVFILEHDR, *PAUDIOWAVFILEHDR;
72AssertCompileSize(AUDIOWAVFILEHDR, 11*4);
73
74/**
75 * Structure for keeeping the internal .WAV file data
76 */
77typedef struct AUDIOWAVFILEDATA
78{
79 /** The file header/footer. */
80 AUDIOWAVFILEHDR Hdr;
81} AUDIOWAVFILEDATA, *PAUDIOWAVFILEDATA;
82
83
84
85
86/**
87 * Retrieves the matching PDMAUDIOFMT for the given bits + signing flag.
88 *
89 * @return Matching PDMAUDIOFMT value.
90 * @retval PDMAUDIOFMT_INVALID if unsupported @a cBits value.
91 *
92 * @param cBits The number of bits in the audio format.
93 * @param fSigned Whether the audio format is signed @c true or not.
94 */
95PDMAUDIOFMT DrvAudioAudFmtBitsToFormat(uint8_t cBits, bool fSigned)
96{
97 if (fSigned)
98 {
99 switch (cBits)
100 {
101 case 8: return PDMAUDIOFMT_S8;
102 case 16: return PDMAUDIOFMT_S16;
103 case 32: return PDMAUDIOFMT_S32;
104 default: AssertMsgFailedReturn(("Bogus audio bits %RU8\n", cBits), PDMAUDIOFMT_INVALID);
105 }
106 }
107 else
108 {
109 switch (cBits)
110 {
111 case 8: return PDMAUDIOFMT_U8;
112 case 16: return PDMAUDIOFMT_U16;
113 case 32: return PDMAUDIOFMT_U32;
114 default: AssertMsgFailedReturn(("Bogus audio bits %RU8\n", cBits), PDMAUDIOFMT_INVALID);
115 }
116 }
117}
118
119/**
120 * Returns an unique file name for this given audio connector instance.
121 *
122 * @return Allocated file name. Must be free'd using RTStrFree().
123 * @param uInstance Driver / device instance.
124 * @param pszPath Path name of the file to delete. The path must exist.
125 * @param pszSuffix File name suffix to use.
126 */
127char *DrvAudioDbgGetFileNameA(uint8_t uInstance, const char *pszPath, const char *pszSuffix)
128{
129 char szFileName[64];
130 RTStrPrintf(szFileName, sizeof(szFileName), "drvAudio%RU8-%s", uInstance, pszSuffix);
131
132 char szFilePath[RTPATH_MAX];
133 int rc2 = RTStrCopy(szFilePath, sizeof(szFilePath), pszPath);
134 AssertRC(rc2);
135 rc2 = RTPathAppend(szFilePath, sizeof(szFilePath), szFileName);
136 AssertRC(rc2);
137
138 return RTStrDup(szFilePath);
139}
140
141/**
142 * Allocates an audio device.
143 *
144 * @returns Newly allocated audio device, or NULL if failed.
145 * @param cbData How much additional data (in bytes) should be allocated to provide
146 * a (backend) specific area to store additional data.
147 * Optional, can be 0.
148 */
149PPDMAUDIODEVICE DrvAudioHlpDeviceAlloc(size_t cbData)
150{
151 PPDMAUDIODEVICE pDev = (PPDMAUDIODEVICE)RTMemAllocZ(sizeof(PDMAUDIODEVICE));
152 if (!pDev)
153 return NULL;
154
155 if (cbData)
156 {
157 pDev->pvData = RTMemAllocZ(cbData);
158 if (!pDev->pvData)
159 {
160 RTMemFree(pDev);
161 return NULL;
162 }
163 }
164
165 pDev->cbData = cbData;
166
167 pDev->cMaxInputChannels = 0;
168 pDev->cMaxOutputChannels = 0;
169
170 return pDev;
171}
172
173/**
174 * Frees an audio device.
175 *
176 * @param pDev Device to free.
177 */
178void DrvAudioHlpDeviceFree(PPDMAUDIODEVICE pDev)
179{
180 if (!pDev)
181 return;
182
183 Assert(pDev->cRefCount == 0);
184
185 if (pDev->pvData)
186 {
187 Assert(pDev->cbData);
188
189 RTMemFree(pDev->pvData);
190 pDev->pvData = NULL;
191 }
192
193 RTMemFree(pDev);
194 pDev = NULL;
195}
196
197/**
198 * Duplicates an audio device entry.
199 *
200 * @returns Duplicated audio device entry on success, or NULL on failure.
201 * @param pDev Audio device entry to duplicate.
202 * @param fCopyUserData Whether to also copy the user data portion or not.
203 */
204PPDMAUDIODEVICE DrvAudioHlpDeviceDup(const PPDMAUDIODEVICE pDev, bool fCopyUserData)
205{
206 AssertPtrReturn(pDev, NULL);
207
208 PPDMAUDIODEVICE pDevDup = DrvAudioHlpDeviceAlloc(fCopyUserData ? pDev->cbData : 0);
209 if (pDevDup)
210 {
211 memcpy(pDevDup, pDev, sizeof(PDMAUDIODEVICE));
212
213 if ( fCopyUserData
214 && pDevDup->cbData)
215 {
216 memcpy(pDevDup->pvData, pDev->pvData, pDevDup->cbData);
217 }
218 else
219 {
220 pDevDup->cbData = 0;
221 pDevDup->pvData = NULL;
222 }
223 }
224
225 return pDevDup;
226}
227
228/**
229 * Initializes an audio device enumeration structure.
230 *
231 * @returns IPRT status code.
232 * @param pDevEnm Device enumeration to initialize.
233 */
234int DrvAudioHlpDeviceEnumInit(PPDMAUDIODEVICEENUM pDevEnm)
235{
236 AssertPtrReturn(pDevEnm, VERR_INVALID_POINTER);
237
238 RTListInit(&pDevEnm->lstDevices);
239 pDevEnm->cDevices = 0;
240
241 return VINF_SUCCESS;
242}
243
244/**
245 * Frees audio device enumeration data.
246 *
247 * @param pDevEnm Device enumeration to destroy.
248 */
249void DrvAudioHlpDeviceEnumFree(PPDMAUDIODEVICEENUM pDevEnm)
250{
251 if (!pDevEnm)
252 return;
253
254 PPDMAUDIODEVICE pDev, pDevNext;
255 RTListForEachSafe(&pDevEnm->lstDevices, pDev, pDevNext, PDMAUDIODEVICE, Node)
256 {
257 RTListNodeRemove(&pDev->Node);
258
259 DrvAudioHlpDeviceFree(pDev);
260
261 pDevEnm->cDevices--;
262 }
263
264 /* Sanity. */
265 Assert(RTListIsEmpty(&pDevEnm->lstDevices));
266 Assert(pDevEnm->cDevices == 0);
267}
268
269/**
270 * Adds an audio device to a device enumeration.
271 *
272 * @return IPRT status code.
273 * @param pDevEnm Device enumeration to add device to.
274 * @param pDev Device to add. The pointer will be owned by the device enumeration then.
275 */
276int DrvAudioHlpDeviceEnumAdd(PPDMAUDIODEVICEENUM pDevEnm, PPDMAUDIODEVICE pDev)
277{
278 AssertPtrReturn(pDevEnm, VERR_INVALID_POINTER);
279 AssertPtrReturn(pDev, VERR_INVALID_POINTER);
280
281 RTListAppend(&pDevEnm->lstDevices, &pDev->Node);
282 pDevEnm->cDevices++;
283
284 return VINF_SUCCESS;
285}
286
287/**
288 * Duplicates a device enumeration.
289 *
290 * @returns Duplicated device enumeration, or NULL on failure.
291 * Must be free'd with DrvAudioHlpDeviceEnumFree().
292 * @param pDevEnm Device enumeration to duplicate.
293 */
294PPDMAUDIODEVICEENUM DrvAudioHlpDeviceEnumDup(const PPDMAUDIODEVICEENUM pDevEnm)
295{
296 AssertPtrReturn(pDevEnm, NULL);
297
298 PPDMAUDIODEVICEENUM pDevEnmDup = (PPDMAUDIODEVICEENUM)RTMemAlloc(sizeof(PDMAUDIODEVICEENUM));
299 if (!pDevEnmDup)
300 return NULL;
301
302 int rc2 = DrvAudioHlpDeviceEnumInit(pDevEnmDup);
303 AssertRC(rc2);
304
305 PPDMAUDIODEVICE pDev;
306 RTListForEach(&pDevEnm->lstDevices, pDev, PDMAUDIODEVICE, Node)
307 {
308 PPDMAUDIODEVICE pDevDup = DrvAudioHlpDeviceDup(pDev, true /* fCopyUserData */);
309 if (!pDevDup)
310 {
311 rc2 = VERR_NO_MEMORY;
312 break;
313 }
314
315 rc2 = DrvAudioHlpDeviceEnumAdd(pDevEnmDup, pDevDup);
316 if (RT_FAILURE(rc2))
317 {
318 DrvAudioHlpDeviceFree(pDevDup);
319 break;
320 }
321 }
322
323 if (RT_FAILURE(rc2))
324 {
325 DrvAudioHlpDeviceEnumFree(pDevEnmDup);
326 pDevEnmDup = NULL;
327 }
328
329 return pDevEnmDup;
330}
331
332/**
333 * Copies device enumeration entries from the source to the destination enumeration.
334 *
335 * @returns IPRT status code.
336 * @param pDstDevEnm Destination enumeration to store enumeration entries into.
337 * @param pSrcDevEnm Source enumeration to use.
338 * @param enmUsage Which entries to copy. Specify PDMAUDIODIR_ANY to copy all entries.
339 * @param fCopyUserData Whether to also copy the user data portion or not.
340 */
341int DrvAudioHlpDeviceEnumCopyEx(PPDMAUDIODEVICEENUM pDstDevEnm, const PPDMAUDIODEVICEENUM pSrcDevEnm,
342 PDMAUDIODIR enmUsage, bool fCopyUserData)
343{
344 AssertPtrReturn(pDstDevEnm, VERR_INVALID_POINTER);
345 AssertPtrReturn(pSrcDevEnm, VERR_INVALID_POINTER);
346
347 int rc = VINF_SUCCESS;
348
349 PPDMAUDIODEVICE pSrcDev;
350 RTListForEach(&pSrcDevEnm->lstDevices, pSrcDev, PDMAUDIODEVICE, Node)
351 {
352 if ( enmUsage != PDMAUDIODIR_ANY
353 && enmUsage != pSrcDev->enmUsage)
354 {
355 continue;
356 }
357
358 PPDMAUDIODEVICE pDstDev = DrvAudioHlpDeviceDup(pSrcDev, fCopyUserData);
359 if (!pDstDev)
360 {
361 rc = VERR_NO_MEMORY;
362 break;
363 }
364
365 rc = DrvAudioHlpDeviceEnumAdd(pDstDevEnm, pDstDev);
366 if (RT_FAILURE(rc))
367 break;
368 }
369
370 return rc;
371}
372
373/**
374 * Copies all device enumeration entries from the source to the destination enumeration.
375 *
376 * Note: Does *not* copy the user-specific data assigned to a device enumeration entry.
377 * To do so, use DrvAudioHlpDeviceEnumCopyEx().
378 *
379 * @returns IPRT status code.
380 * @param pDstDevEnm Destination enumeration to store enumeration entries into.
381 * @param pSrcDevEnm Source enumeration to use.
382 */
383int DrvAudioHlpDeviceEnumCopy(PPDMAUDIODEVICEENUM pDstDevEnm, const PPDMAUDIODEVICEENUM pSrcDevEnm)
384{
385 return DrvAudioHlpDeviceEnumCopyEx(pDstDevEnm, pSrcDevEnm, PDMAUDIODIR_ANY, false /* fCopyUserData */);
386}
387
388/**
389 * Returns the default device of a given device enumeration.
390 * This assumes that only one default device per usage is set.
391 *
392 * @returns Default device if found, or NULL if none found.
393 * @param pDevEnm Device enumeration to get default device for.
394 * @param enmUsage Usage to get default device for.
395 */
396PPDMAUDIODEVICE DrvAudioHlpDeviceEnumGetDefaultDevice(const PPDMAUDIODEVICEENUM pDevEnm, PDMAUDIODIR enmUsage)
397{
398 AssertPtrReturn(pDevEnm, NULL);
399
400 PPDMAUDIODEVICE pDev;
401 RTListForEach(&pDevEnm->lstDevices, pDev, PDMAUDIODEVICE, Node)
402 {
403 if (enmUsage != PDMAUDIODIR_ANY)
404 {
405 if (enmUsage != pDev->enmUsage) /* Wrong usage? Skip. */
406 continue;
407 }
408
409 if (pDev->fFlags & PDMAUDIODEV_FLAGS_DEFAULT)
410 return pDev;
411 }
412
413 return NULL;
414}
415
416/**
417 * Returns the number of enumerated devices of a given device enumeration.
418 *
419 * @returns Number of devices if found, or 0 if none found.
420 * @param pDevEnm Device enumeration to get default device for.
421 * @param enmUsage Usage to get default device for.
422 */
423uint16_t DrvAudioHlpDeviceEnumGetDeviceCount(const PPDMAUDIODEVICEENUM pDevEnm, PDMAUDIODIR enmUsage)
424{
425 AssertPtrReturn(pDevEnm, 0);
426
427 if (enmUsage == PDMAUDIODIR_ANY)
428 return pDevEnm->cDevices;
429
430 uint32_t cDevs = 0;
431
432 PPDMAUDIODEVICE pDev;
433 RTListForEach(&pDevEnm->lstDevices, pDev, PDMAUDIODEVICE, Node)
434 {
435 if (enmUsage == pDev->enmUsage)
436 cDevs++;
437 }
438
439 return cDevs;
440}
441
442/**
443 * Logs an audio device enumeration.
444 *
445 * @param pszDesc Logging description.
446 * @param pDevEnm Device enumeration to log.
447 */
448void DrvAudioHlpDeviceEnumPrint(const char *pszDesc, const PPDMAUDIODEVICEENUM pDevEnm)
449{
450 AssertPtrReturnVoid(pszDesc);
451 AssertPtrReturnVoid(pDevEnm);
452
453 LogFunc(("%s: %RU16 devices\n", pszDesc, pDevEnm->cDevices));
454
455 PPDMAUDIODEVICE pDev;
456 RTListForEach(&pDevEnm->lstDevices, pDev, PDMAUDIODEVICE, Node)
457 {
458 char *pszFlags = DrvAudioHlpAudDevFlagsToStrA(pDev->fFlags);
459
460 LogFunc(("Device '%s':\n", pDev->szName));
461 LogFunc(("\tUsage = %s\n", DrvAudioHlpAudDirToStr(pDev->enmUsage)));
462 LogFunc(("\tFlags = %s\n", pszFlags ? pszFlags : "<NONE>"));
463 LogFunc(("\tInput channels = %RU8\n", pDev->cMaxInputChannels));
464 LogFunc(("\tOutput channels = %RU8\n", pDev->cMaxOutputChannels));
465 LogFunc(("\tData = %p (%zu bytes)\n", pDev->pvData, pDev->cbData));
466
467 if (pszFlags)
468 RTStrFree(pszFlags);
469 }
470}
471
472/**
473 * Converts an audio direction to a string.
474 *
475 * @returns Stringified audio direction, or "Unknown", if not found.
476 * @param enmDir Audio direction to convert.
477 */
478const char *DrvAudioHlpAudDirToStr(PDMAUDIODIR enmDir)
479{
480 switch (enmDir)
481 {
482 case PDMAUDIODIR_UNKNOWN: return "Unknown";
483 case PDMAUDIODIR_IN: return "Input";
484 case PDMAUDIODIR_OUT: return "Output";
485 case PDMAUDIODIR_ANY: return "Duplex";
486 default: break;
487 }
488
489 AssertMsgFailed(("Invalid audio direction %ld\n", enmDir));
490 return "Unknown";
491}
492
493/**
494 * Converts an audio mixer control to a string.
495 *
496 * @returns Stringified audio mixer control or "Unknown", if not found.
497 * @param enmMixerCtl Audio mixer control to convert.
498 */
499const char *DrvAudioHlpAudMixerCtlToStr(PDMAUDIOMIXERCTL enmMixerCtl)
500{
501 switch (enmMixerCtl)
502 {
503 case PDMAUDIOMIXERCTL_VOLUME_MASTER: return "Master Volume";
504 case PDMAUDIOMIXERCTL_FRONT: return "Front";
505 case PDMAUDIOMIXERCTL_CENTER_LFE: return "Center / LFE";
506 case PDMAUDIOMIXERCTL_REAR: return "Rear";
507 case PDMAUDIOMIXERCTL_LINE_IN: return "Line-In";
508 case PDMAUDIOMIXERCTL_MIC_IN: return "Microphone-In";
509 default: break;
510 }
511
512 AssertMsgFailed(("Invalid mixer control %ld\n", enmMixerCtl));
513 return "Unknown";
514}
515
516/**
517 * Converts an audio device flags to a string.
518 *
519 * @returns Stringified audio flags. Must be free'd with RTStrFree().
520 * NULL if no flags set.
521 * @param fFlags Audio flags (PDMAUDIODEV_FLAGS_XXX) to convert.
522 */
523char *DrvAudioHlpAudDevFlagsToStrA(uint32_t fFlags)
524{
525#define APPEND_FLAG_TO_STR(_aFlag) \
526 if (fFlags & PDMAUDIODEV_FLAGS_##_aFlag) \
527 { \
528 if (pszFlags) \
529 { \
530 rc2 = RTStrAAppend(&pszFlags, " "); \
531 if (RT_FAILURE(rc2)) \
532 break; \
533 } \
534 \
535 rc2 = RTStrAAppend(&pszFlags, #_aFlag); \
536 if (RT_FAILURE(rc2)) \
537 break; \
538 } \
539
540 char *pszFlags = NULL;
541 int rc2 = VINF_SUCCESS;
542
543 do
544 {
545 APPEND_FLAG_TO_STR(DEFAULT);
546 APPEND_FLAG_TO_STR(HOTPLUG);
547 APPEND_FLAG_TO_STR(BUGGY);
548 APPEND_FLAG_TO_STR(IGNORE);
549 APPEND_FLAG_TO_STR(LOCKED);
550 APPEND_FLAG_TO_STR(DEAD);
551
552 } while (0);
553
554 if (!pszFlags)
555 rc2 = RTStrAAppend(&pszFlags, "NONE");
556
557 if ( RT_FAILURE(rc2)
558 && pszFlags)
559 {
560 RTStrFree(pszFlags);
561 pszFlags = NULL;
562 }
563
564#undef APPEND_FLAG_TO_STR
565
566 return pszFlags;
567}
568
569/**
570 * Converts a playback destination enumeration to a string.
571 *
572 * @returns Stringified playback destination, or "Unknown", if not found.
573 * @param enmPlaybackDst Playback destination to convert.
574 */
575const char *DrvAudioHlpPlaybackDstToStr(const PDMAUDIOPLAYBACKDST enmPlaybackDst)
576{
577 switch (enmPlaybackDst)
578 {
579 case PDMAUDIOPLAYBACKDST_UNKNOWN: return "Unknown";
580 case PDMAUDIOPLAYBACKDST_FRONT: return "Front";
581 case PDMAUDIOPLAYBACKDST_CENTER_LFE: return "Center / LFE";
582 case PDMAUDIOPLAYBACKDST_REAR: return "Rear";
583 default:
584 break;
585 }
586
587 AssertMsgFailed(("Invalid playback destination %ld\n", enmPlaybackDst));
588 return "Unknown";
589}
590
591/**
592 * Converts a recording source enumeration to a string.
593 *
594 * @returns Stringified recording source, or "Unknown", if not found.
595 * @param enmRecSrc Recording source to convert.
596 */
597const char *DrvAudioHlpRecSrcToStr(const PDMAUDIORECSRC enmRecSrc)
598{
599 switch (enmRecSrc)
600 {
601 case PDMAUDIORECSRC_UNKNOWN: return "Unknown";
602 case PDMAUDIORECSRC_MIC: return "Microphone In";
603 case PDMAUDIORECSRC_CD: return "CD";
604 case PDMAUDIORECSRC_VIDEO: return "Video";
605 case PDMAUDIORECSRC_AUX: return "AUX";
606 case PDMAUDIORECSRC_LINE: return "Line In";
607 case PDMAUDIORECSRC_PHONE: return "Phone";
608 default:
609 break;
610 }
611
612 AssertMsgFailed(("Invalid recording source %ld\n", enmRecSrc));
613 return "Unknown";
614}
615
616/**
617 * Returns wether the given audio format has signed bits or not.
618 *
619 * @return IPRT status code.
620 * @return bool @c true for signed bits, @c false for unsigned.
621 * @param enmFmt Audio format to retrieve value for.
622 */
623bool DrvAudioHlpAudFmtIsSigned(PDMAUDIOFMT enmFmt)
624{
625 switch (enmFmt)
626 {
627 case PDMAUDIOFMT_S8:
628 case PDMAUDIOFMT_S16:
629 case PDMAUDIOFMT_S32:
630 return true;
631
632 case PDMAUDIOFMT_U8:
633 case PDMAUDIOFMT_U16:
634 case PDMAUDIOFMT_U32:
635 return false;
636
637 default:
638 break;
639 }
640
641 AssertMsgFailed(("Bogus audio format %ld\n", enmFmt));
642 return false;
643}
644
645/**
646 * Returns the bits of a given audio format.
647 *
648 * @return IPRT status code.
649 * @return uint8_t Bits of audio format.
650 * @param enmFmt Audio format to retrieve value for.
651 */
652uint8_t DrvAudioHlpAudFmtToBits(PDMAUDIOFMT enmFmt)
653{
654 switch (enmFmt)
655 {
656 case PDMAUDIOFMT_S8:
657 case PDMAUDIOFMT_U8:
658 return 8;
659
660 case PDMAUDIOFMT_U16:
661 case PDMAUDIOFMT_S16:
662 return 16;
663
664 case PDMAUDIOFMT_U32:
665 case PDMAUDIOFMT_S32:
666 return 32;
667
668 default:
669 break;
670 }
671
672 AssertMsgFailed(("Bogus audio format %ld\n", enmFmt));
673 return 0;
674}
675
676/**
677 * Converts an audio format to a string.
678 *
679 * @returns Stringified audio format, or "Unknown", if not found.
680 * @param enmFmt Audio format to convert.
681 */
682const char *DrvAudioHlpAudFmtToStr(PDMAUDIOFMT enmFmt)
683{
684 switch (enmFmt)
685 {
686 case PDMAUDIOFMT_U8:
687 return "U8";
688
689 case PDMAUDIOFMT_U16:
690 return "U16";
691
692 case PDMAUDIOFMT_U32:
693 return "U32";
694
695 case PDMAUDIOFMT_S8:
696 return "S8";
697
698 case PDMAUDIOFMT_S16:
699 return "S16";
700
701 case PDMAUDIOFMT_S32:
702 return "S32";
703
704 default:
705 break;
706 }
707
708 AssertMsgFailed(("Bogus audio format %ld\n", enmFmt));
709 return "Unknown";
710}
711
712/**
713 * Converts a given string to an audio format.
714 *
715 * @returns Audio format for the given string, or PDMAUDIOFMT_INVALID if not found.
716 * @param pszFmt String to convert to an audio format.
717 */
718PDMAUDIOFMT DrvAudioHlpStrToAudFmt(const char *pszFmt)
719{
720 AssertPtrReturn(pszFmt, PDMAUDIOFMT_INVALID);
721
722 if (!RTStrICmp(pszFmt, "u8"))
723 return PDMAUDIOFMT_U8;
724 if (!RTStrICmp(pszFmt, "u16"))
725 return PDMAUDIOFMT_U16;
726 if (!RTStrICmp(pszFmt, "u32"))
727 return PDMAUDIOFMT_U32;
728 if (!RTStrICmp(pszFmt, "s8"))
729 return PDMAUDIOFMT_S8;
730 if (!RTStrICmp(pszFmt, "s16"))
731 return PDMAUDIOFMT_S16;
732 if (!RTStrICmp(pszFmt, "s32"))
733 return PDMAUDIOFMT_S32;
734
735 AssertMsgFailed(("Invalid audio format '%s'\n", pszFmt));
736 return PDMAUDIOFMT_INVALID;
737}
738
739/**
740 * Initializes a stream configuration with its default values.
741 *
742 * @param pCfg Stream configuration to initialize.
743 */
744void DrvAudioHlpStreamCfgInit(PPDMAUDIOSTREAMCFG pCfg)
745{
746 AssertPtrReturnVoid(pCfg);
747
748 RT_ZERO(*pCfg);
749
750 pCfg->Backend.cFramesPreBuffering = UINT32_MAX; /* Explicitly set to "undefined". */
751}
752
753/**
754 * Initializes a stream configuration from PCM properties.
755 *
756 * @return IPRT status code.
757 * @param pCfg Stream configuration to initialize.
758 * @param pProps PCM properties to use.
759 */
760int DrvAudioHlpStreamCfgInitFromPcmProps(PPDMAUDIOSTREAMCFG pCfg, PCPDMAUDIOPCMPROPS pProps)
761{
762 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
763 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
764
765 DrvAudioHlpStreamCfgInit(pCfg);
766
767 memcpy(&pCfg->Props, pProps, sizeof(PDMAUDIOPCMPROPS));
768 return VINF_SUCCESS;
769}
770
771/**
772 * Checks whether a given stream configuration is valid or not.
773 *
774 * Returns @c true if configuration is valid, @c false if not.
775 * @param pCfg Stream configuration to check.
776 */
777bool DrvAudioHlpStreamCfgIsValid(PCPDMAUDIOSTREAMCFG pCfg)
778{
779 AssertPtrReturn(pCfg, false);
780
781 bool fValid = ( pCfg->enmDir == PDMAUDIODIR_IN
782 || pCfg->enmDir == PDMAUDIODIR_OUT);
783
784 fValid &= ( pCfg->enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED
785 || pCfg->enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
786
787 if (fValid)
788 fValid = DrvAudioHlpPCMPropsAreValid(&pCfg->Props);
789
790 return fValid;
791}
792
793/**
794 * Frees an allocated audio stream configuration.
795 *
796 * @param pCfg Audio stream configuration to free.
797 */
798void DrvAudioHlpStreamCfgFree(PPDMAUDIOSTREAMCFG pCfg)
799{
800 if (pCfg)
801 {
802 RTMemFree(pCfg);
803 pCfg = NULL;
804 }
805}
806
807/**
808 * Copies a source stream configuration to a destination stream configuration.
809 *
810 * @returns IPRT status code.
811 * @param pDstCfg Destination stream configuration to copy source to.
812 * @param pSrcCfg Source stream configuration to copy to destination.
813 */
814int DrvAudioHlpStreamCfgCopy(PPDMAUDIOSTREAMCFG pDstCfg, PCPDMAUDIOSTREAMCFG pSrcCfg)
815{
816 AssertPtrReturn(pDstCfg, VERR_INVALID_POINTER);
817 AssertPtrReturn(pSrcCfg, VERR_INVALID_POINTER);
818
819#ifdef VBOX_STRICT
820 if (!DrvAudioHlpStreamCfgIsValid(pSrcCfg))
821 {
822 AssertMsgFailed(("Stream config '%s' (%p) is invalid\n", pSrcCfg->szName, pSrcCfg));
823 return VERR_INVALID_PARAMETER;
824 }
825#endif
826
827 memcpy(pDstCfg, pSrcCfg, sizeof(PDMAUDIOSTREAMCFG));
828
829 return VINF_SUCCESS;
830}
831
832/**
833 * Duplicates an audio stream configuration.
834 * Must be free'd with DrvAudioHlpStreamCfgFree().
835 *
836 * @return Duplicates audio stream configuration on success, or NULL on failure.
837 * @param pCfg Audio stream configuration to duplicate.
838 */
839PPDMAUDIOSTREAMCFG DrvAudioHlpStreamCfgDup(PCPDMAUDIOSTREAMCFG pCfg)
840{
841 AssertPtrReturn(pCfg, NULL);
842
843#ifdef VBOX_STRICT
844 if (!DrvAudioHlpStreamCfgIsValid(pCfg))
845 {
846 AssertMsgFailed(("Stream config '%s' (%p) is invalid\n", pCfg->szName, pCfg));
847 return NULL;
848 }
849#endif
850
851 PPDMAUDIOSTREAMCFG pDst = (PPDMAUDIOSTREAMCFG)RTMemAllocZ(sizeof(PDMAUDIOSTREAMCFG));
852 if (!pDst)
853 return NULL;
854
855 int rc2 = DrvAudioHlpStreamCfgCopy(pDst, pCfg);
856 if (RT_FAILURE(rc2))
857 {
858 DrvAudioHlpStreamCfgFree(pDst);
859 pDst = NULL;
860 }
861
862 AssertPtr(pDst);
863 return pDst;
864}
865
866/**
867 * Prints an audio stream configuration to the debug log.
868 *
869 * @param pCfg Stream configuration to log.
870 */
871void DrvAudioHlpStreamCfgPrint(PCPDMAUDIOSTREAMCFG pCfg)
872{
873 if (!pCfg)
874 return;
875
876 LogFunc(("szName=%s, enmDir=%RU32 (uHz=%RU32, cBits=%RU8%s, cChannels=%RU8)\n",
877 pCfg->szName, pCfg->enmDir,
878 pCfg->Props.uHz, pCfg->Props.cbSample * 8, pCfg->Props.fSigned ? "S" : "U", pCfg->Props.cChannels));
879}
880
881/**
882 * Converts a stream command to a string.
883 *
884 * @returns Stringified stream command, or "Unknown", if not found.
885 * @param enmCmd Stream command to convert.
886 */
887const char *DrvAudioHlpStreamCmdToStr(PDMAUDIOSTREAMCMD enmCmd)
888{
889 switch (enmCmd)
890 {
891 case PDMAUDIOSTREAMCMD_INVALID: return "Invalid";
892 case PDMAUDIOSTREAMCMD_UNKNOWN: return "Unknown";
893 case PDMAUDIOSTREAMCMD_ENABLE: return "Enable";
894 case PDMAUDIOSTREAMCMD_DISABLE: return "Disable";
895 case PDMAUDIOSTREAMCMD_PAUSE: return "Pause";
896 case PDMAUDIOSTREAMCMD_RESUME: return "Resume";
897 case PDMAUDIOSTREAMCMD_DRAIN: return "Drain";
898 case PDMAUDIOSTREAMCMD_DROP: return "Drop";
899 case PDMAUDIOSTREAMCMD_32BIT_HACK:
900 break;
901 }
902 AssertMsgFailed(("Invalid stream command %d\n", enmCmd));
903 return "Unknown";
904}
905
906/**
907 * Returns @c true if the given stream status indicates a can-be-read-from stream,
908 * @c false if not.
909 *
910 * @returns @c true if ready to be read from, @c if not.
911 * @param fStatus Stream status to evaluate, PDMAUDIOSTREAMSTS_FLAGS_XXX.
912 */
913bool DrvAudioHlpStreamStatusCanRead(PDMAUDIOSTREAMSTS fStatus)
914{
915 AssertReturn(fStatus & PDMAUDIOSTREAMSTS_VALID_MASK, false);
916
917 return fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED
918 && fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED
919 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PAUSED)
920 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT);
921}
922
923/**
924 * Returns @c true if the given stream status indicates a can-be-written-to stream,
925 * @c false if not.
926 *
927 * @returns @c true if ready to be written to, @c if not.
928 * @param fStatus Stream status to evaluate, PDMAUDIOSTREAMSTS_FLAGS_XXX.
929 */
930bool DrvAudioHlpStreamStatusCanWrite(PDMAUDIOSTREAMSTS fStatus)
931{
932 AssertReturn(fStatus & PDMAUDIOSTREAMSTS_VALID_MASK, false);
933
934 return fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED
935 && fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED
936 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PAUSED)
937 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)
938 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT);
939}
940
941/**
942 * Returns @c true if the given stream status indicates a ready-to-operate stream,
943 * @c false if not.
944 *
945 * @returns @c true if ready to operate, @c if not.
946 * @param fStatus Stream status to evaluate.
947 */
948bool DrvAudioHlpStreamStatusIsReady(PDMAUDIOSTREAMSTS fStatus)
949{
950 AssertReturn(fStatus & PDMAUDIOSTREAMSTS_VALID_MASK, false);
951
952 return fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED
953 && fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED
954 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT);
955}
956
957/**
958 * Calculates the audio bit rate of the given bits per sample, the Hz and the number
959 * of audio channels.
960 *
961 * Divide the result by 8 to get the byte rate.
962 *
963 * @returns Bitrate.
964 * @param cBits Number of bits per sample.
965 * @param uHz Hz (Hertz) rate.
966 * @param cChannels Number of audio channels.
967 */
968uint32_t DrvAudioHlpCalcBitrate(uint8_t cBits, uint32_t uHz, uint8_t cChannels)
969{
970 return cBits * uHz * cChannels;
971}
972
973
974/*********************************************************************************************************************************
975* PCM Property Helpers *
976*********************************************************************************************************************************/
977
978/**
979 * Gets the bitrate.
980 *
981 * Divide the result by 8 to get the byte rate.
982 *
983 * @returns Bit rate.
984 * @param pProps PCM properties to calculate bitrate for.
985 */
986uint32_t DrvAudioHlpGetBitrate(PCPDMAUDIOPCMPROPS pProps)
987{
988 return DrvAudioHlpCalcBitrate(pProps->cbSample * 8, pProps->uHz, pProps->cChannels);
989}
990
991/**
992 * Rounds down the given byte amount to the nearest frame boundrary.
993 *
994 * @returns Rounded byte amount.
995 * @param pProps PCM properties to use.
996 * @param cb The size (in bytes) to round.
997 */
998uint32_t DrvAudioHlpFloorBytesToFrame(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
999{
1000 AssertPtrReturn(pProps, 0);
1001 return PDMAUDIOPCMPROPS_F2B(pProps, PDMAUDIOPCMPROPS_B2F(pProps, cb));
1002}
1003
1004/**
1005 * Checks if the given size is aligned on a frame boundrary.
1006 *
1007 * @returns @c true if properly aligned, @c false if not.
1008 * @param pProps PCM properties to use.
1009 * @param cb The size (in bytes) to check.
1010 */
1011bool DrvAudioHlpIsBytesAligned(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
1012{
1013 AssertPtrReturn(pProps, false);
1014 uint32_t const cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */);
1015 AssertReturn(cbFrame, false);
1016 return cb % cbFrame == 0;
1017}
1018
1019/**
1020 * Returns the bytes per second for given PCM properties.
1021 *
1022 * @returns Bytes per second.
1023 * @param pProps PCM properties to retrieve size for.
1024 */
1025DECLINLINE(uint64_t) drvAudioHlpBytesPerSec(PCPDMAUDIOPCMPROPS pProps)
1026{
1027 return PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */) * pProps->uHz;
1028}
1029
1030/**
1031 * Converts bytes to frames (rounding down of course).
1032 *
1033 * @returns Number of frames.
1034 * @param pProps PCM properties to use.
1035 * @param cb The number of bytes to convert.
1036 */
1037uint32_t DrvAudioHlpBytesToFrames(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
1038{
1039 AssertPtrReturn(pProps, 0);
1040 return PDMAUDIOPCMPROPS_B2F(pProps, cb);
1041}
1042
1043/**
1044 * Converts bytes to milliseconds.
1045 *
1046 * @return Number milliseconds @a cb takes to play or record.
1047 * @param pProps PCM properties to use.
1048 * @param cb The number of bytes to convert.
1049 *
1050 * @note Rounds up the result.
1051 */
1052uint64_t DrvAudioHlpBytesToMilli(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
1053{
1054 AssertPtrReturn(pProps, 0);
1055
1056 /* Check parameters to prevent division by chainsaw: */
1057 uint32_t const uHz = pProps->uHz;
1058 if (uHz)
1059 {
1060 const unsigned cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */);
1061 if (cbFrame)
1062 {
1063 /* Round cb up to closest frame size: */
1064 cb = (cb + cbFrame - 1) / cbFrame;
1065
1066 /* Convert to milliseconds. */
1067 return (cb * (uint64_t)RT_MS_1SEC + uHz - 1) / uHz;
1068 }
1069 }
1070 return 0;
1071}
1072
1073/**
1074 * Converts bytes to microseconds.
1075 *
1076 * @return Number microseconds @a cb takes to play or record.
1077 * @param pProps PCM properties to use.
1078 * @param cb The number of bytes to convert.
1079 *
1080 * @note Rounds up the result.
1081 */
1082uint64_t DrvAudioHlpBytesToMicro(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
1083{
1084 AssertPtrReturn(pProps, 0);
1085
1086 /* Check parameters to prevent division by chainsaw: */
1087 uint32_t const uHz = pProps->uHz;
1088 if (uHz)
1089 {
1090 const unsigned cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */);
1091 if (cbFrame)
1092 {
1093 /* Round cb up to closest frame size: */
1094 cb = (cb + cbFrame - 1) / cbFrame;
1095
1096 /* Convert to microseconds. */
1097 return (cb * (uint64_t)RT_US_1SEC + uHz - 1) / uHz;
1098 }
1099 }
1100 return 0;
1101}
1102
1103/**
1104 * Converts bytes to nanoseconds.
1105 *
1106 * @return Number nanoseconds @a cb takes to play or record.
1107 * @param pProps PCM properties to use.
1108 * @param cb The number of bytes to convert.
1109 *
1110 * @note Rounds up the result.
1111 */
1112uint64_t DrvAudioHlpBytesToNano(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
1113{
1114 AssertPtrReturn(pProps, 0);
1115
1116 /* Check parameters to prevent division by chainsaw: */
1117 uint32_t const uHz = pProps->uHz;
1118 if (uHz)
1119 {
1120 const unsigned cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */);
1121 if (cbFrame)
1122 {
1123 /* Round cb up to closest frame size: */
1124 cb = (cb + cbFrame - 1) / cbFrame;
1125
1126 /* Convert to nanoseconds. */
1127 return (cb * (uint64_t)RT_NS_1SEC + uHz - 1) / uHz;
1128 }
1129 }
1130 return 0;
1131}
1132
1133/**
1134 * Converts frames to bytes.
1135 *
1136 * @returns Number of bytes.
1137 * @param pProps The PCM properties to use.
1138 * @param cFrames Number of audio frames to convert.
1139 * @sa PDMAUDIOPCMPROPS_F2B
1140 */
1141uint32_t DrvAudioHlpFramesToBytes(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
1142{
1143 AssertPtrReturn(pProps, 0);
1144 return PDMAUDIOPCMPROPS_F2B(pProps, cFrames);
1145}
1146
1147/**
1148 * Converts frames to milliseconds.
1149 *
1150 * @returns milliseconds.
1151 * @param pProps The PCM properties to use.
1152 * @param cFrames Number of audio frames to convert.
1153 * @note No rounding here, result is floored.
1154 */
1155uint64_t DrvAudioHlpFramesToMilli(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
1156{
1157 AssertPtrReturn(pProps, 0);
1158
1159 /* Check input to prevent division by chainsaw: */
1160 uint32_t const uHz = pProps->uHz;
1161 if (uHz)
1162 return ASMMultU32ByU32DivByU32(cFrames, RT_MS_1SEC, uHz);
1163 return 0;
1164}
1165
1166/**
1167 * Converts frames to nanoseconds.
1168 *
1169 * @returns Nanoseconds.
1170 * @param pProps The PCM properties to use.
1171 * @param cFrames Number of audio frames to convert.
1172 * @note No rounding here, result is floored.
1173 */
1174uint64_t DrvAudioHlpFramesToNano(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
1175{
1176 AssertPtrReturn(pProps, 0);
1177
1178 /* Check input to prevent division by chainsaw: */
1179 uint32_t const uHz = pProps->uHz;
1180 if (uHz)
1181 return ASMMultU32ByU32DivByU32(cFrames, RT_NS_1SEC, uHz);
1182 return 0;
1183}
1184
1185/**
1186 * Converts milliseconds to frames.
1187 *
1188 * @returns Number of frames
1189 * @param pProps The PCM properties to use.
1190 * @param cMs The number of milliseconds to convert.
1191 *
1192 * @note The result is rounded rather than floored (hysterical raisins).
1193 */
1194uint32_t DrvAudioHlpMilliToFrames(PCPDMAUDIOPCMPROPS pProps, uint64_t cMs)
1195{
1196 AssertPtrReturn(pProps, 0);
1197
1198 uint32_t const uHz = pProps->uHz;
1199 uint32_t cFrames;
1200 if (cMs < RT_MS_1SEC)
1201 cFrames = 0;
1202 else
1203 {
1204 cFrames = cMs / RT_MS_1SEC * uHz;
1205 cMs %= RT_MS_1SEC;
1206 }
1207 cFrames += (ASMMult2xU32RetU64(uHz, (uint32_t)cMs) + RT_MS_1SEC - 1) / RT_MS_1SEC;
1208 return cFrames;
1209}
1210
1211/**
1212 * Converts milliseconds to bytes.
1213 *
1214 * @returns Number of bytes (frame aligned).
1215 * @param pProps The PCM properties to use.
1216 * @param cMs The number of milliseconds to convert.
1217 *
1218 * @note The result is rounded rather than floored (hysterical raisins).
1219 */
1220uint32_t DrvAudioHlpMilliToBytes(PCPDMAUDIOPCMPROPS pProps, uint64_t cMs)
1221{
1222 return PDMAUDIOPCMPROPS_F2B(pProps, DrvAudioHlpMilliToFrames(pProps, cMs));
1223}
1224
1225/**
1226 * Converts nanoseconds to frames.
1227 *
1228 * @returns Number of frames
1229 * @param pProps The PCM properties to use.
1230 * @param cNs The number of nanoseconds to convert.
1231 *
1232 * @note The result is rounded rather than floored (hysterical raisins).
1233 */
1234uint32_t DrvAudioHlpNanoToFrames(PCPDMAUDIOPCMPROPS pProps, uint64_t cNs)
1235{
1236 AssertPtrReturn(pProps, 0);
1237
1238 uint32_t const uHz = pProps->uHz;
1239 uint32_t cFrames;
1240 if (cNs < RT_NS_1SEC)
1241 cFrames = 0;
1242 else
1243 {
1244 cFrames = cNs / RT_NS_1SEC * uHz;
1245 cNs %= RT_NS_1SEC;
1246 }
1247 cFrames += (ASMMult2xU32RetU64(uHz, (uint32_t)cNs) + RT_NS_1SEC - 1) / RT_NS_1SEC;
1248 return cFrames;
1249}
1250
1251/**
1252 * Converts nanoseconds to bytes.
1253 *
1254 * @returns Number of bytes (frame aligned).
1255 * @param pProps The PCM properties to use.
1256 * @param cNs The number of nanoseconds to convert.
1257 *
1258 * @note The result is rounded rather than floored (hysterical raisins).
1259 */
1260uint32_t DrvAudioHlpNanoToBytes(PCPDMAUDIOPCMPROPS pProps, uint64_t cNs)
1261{
1262 return PDMAUDIOPCMPROPS_F2B(pProps, DrvAudioHlpNanoToFrames(pProps, cNs));
1263}
1264
1265/**
1266 * Clears a sample buffer by the given amount of audio frames with silence (according to the format
1267 * given by the PCM properties).
1268 *
1269 * @param pPCMProps PCM properties to use for the buffer to clear.
1270 * @param pvBuf Buffer to clear.
1271 * @param cbBuf Size (in bytes) of the buffer.
1272 * @param cFrames Number of audio frames to clear in the buffer.
1273 */
1274void DrvAudioHlpClearBuf(PCPDMAUDIOPCMPROPS pPCMProps, void *pvBuf, size_t cbBuf, uint32_t cFrames)
1275{
1276 /*
1277 * Validate input
1278 */
1279 AssertPtrReturnVoid(pPCMProps);
1280 Assert(pPCMProps->cbSample);
1281 if (!cbBuf || !cFrames)
1282 return;
1283 AssertPtrReturnVoid(pvBuf);
1284
1285 Assert(pPCMProps->fSwapEndian == false); /** @todo Swapping Endianness is not supported yet. */
1286
1287 /*
1288 * Decide how much needs clearing.
1289 */
1290 size_t cbToClear = DrvAudioHlpFramesToBytes(pPCMProps, cFrames);
1291 AssertStmt(cbToClear <= cbBuf, cbToClear = cbBuf);
1292
1293 Log2Func(("pPCMProps=%p, pvBuf=%p, cFrames=%RU32, fSigned=%RTbool, cBytes=%RU8\n",
1294 pPCMProps, pvBuf, cFrames, pPCMProps->fSigned, pPCMProps->cbSample));
1295
1296 /*
1297 * Do the job.
1298 */
1299 if (pPCMProps->fSigned)
1300 RT_BZERO(pvBuf, cbToClear);
1301 else /* Unsigned formats. */
1302 {
1303 switch (pPCMProps->cbSample)
1304 {
1305 case 1: /* 8 bit */
1306 memset(pvBuf, 0x80, cbToClear);
1307 break;
1308
1309 case 2: /* 16 bit */
1310 {
1311 uint16_t *pu16Dst = (uint16_t *)pvBuf;
1312 size_t cLeft = cbToClear / sizeof(uint16_t);
1313 while (cLeft-- > 0)
1314 *pu16Dst++ = 0x80;
1315 break;
1316 }
1317
1318 /** @todo Add 24 bit? */
1319
1320 case 4: /* 32 bit */
1321 ASMMemFill32(pvBuf, cbToClear & ~(size_t)3, 0x80);
1322 break;
1323
1324 default:
1325 AssertMsgFailed(("Invalid bytes per sample: %RU8\n", pPCMProps->cbSample));
1326 }
1327 }
1328}
1329
1330/**
1331 * Checks whether two given PCM properties are equal.
1332 *
1333 * @returns @c true if equal, @c false if not.
1334 * @param pProps1 First properties to compare.
1335 * @param pProps2 Second properties to compare.
1336 */
1337bool DrvAudioHlpPCMPropsAreEqual(PCPDMAUDIOPCMPROPS pProps1, PCPDMAUDIOPCMPROPS pProps2)
1338{
1339 AssertPtrReturn(pProps1, false);
1340 AssertPtrReturn(pProps2, false);
1341
1342 if (pProps1 == pProps2) /* If the pointers match, take a shortcut. */
1343 return true;
1344
1345 return pProps1->uHz == pProps2->uHz
1346 && pProps1->cChannels == pProps2->cChannels
1347 && pProps1->cbSample == pProps2->cbSample
1348 && pProps1->fSigned == pProps2->fSigned
1349 && pProps1->fSwapEndian == pProps2->fSwapEndian;
1350}
1351
1352/**
1353 * Checks whether given PCM properties are valid or not.
1354 *
1355 * Returns @c true if properties are valid, @c false if not.
1356 * @param pProps PCM properties to check.
1357 */
1358bool DrvAudioHlpPCMPropsAreValid(PCPDMAUDIOPCMPROPS pProps)
1359{
1360 AssertPtrReturn(pProps, false);
1361
1362 /* Minimum 1 channel (mono), maximum 7.1 (= 8) channels. */
1363 bool fValid = ( pProps->cChannels >= 1
1364 && pProps->cChannels <= 8);
1365
1366 if (fValid)
1367 {
1368 switch (pProps->cbSample)
1369 {
1370 case 1: /* 8 bit */
1371 if (pProps->fSigned)
1372 fValid = false;
1373 break;
1374 case 2: /* 16 bit */
1375 if (!pProps->fSigned)
1376 fValid = false;
1377 break;
1378 /** @todo Do we need support for 24 bit samples? */
1379 case 4: /* 32 bit */
1380 if (!pProps->fSigned)
1381 fValid = false;
1382 break;
1383 default:
1384 fValid = false;
1385 break;
1386 }
1387 }
1388
1389 if (!fValid)
1390 return false;
1391
1392 fValid &= pProps->uHz > 0;
1393 fValid &= pProps->cShift == PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pProps->cbSample, pProps->cChannels);
1394 fValid &= pProps->fSwapEndian == false; /** @todo Handling Big Endian audio data is not supported yet. */
1395
1396 return fValid;
1397}
1398
1399/**
1400 * Checks whether the given PCM properties are equal with the given
1401 * stream configuration.
1402 *
1403 * @returns @c true if equal, @c false if not.
1404 * @param pProps PCM properties to compare.
1405 * @param pCfg Stream configuration to compare.
1406 */
1407bool DrvAudioHlpPCMPropsAreEqual(PCPDMAUDIOPCMPROPS pProps, PCPDMAUDIOSTREAMCFG pCfg)
1408{
1409 AssertPtrReturn(pProps, false);
1410 AssertPtrReturn(pCfg, false);
1411
1412 return DrvAudioHlpPCMPropsAreEqual(pProps, &pCfg->Props);
1413}
1414
1415/**
1416 * Get number of bytes per frame.
1417 *
1418 * @returns Number of bytes per audio frame.
1419 * @param pProps PCM properties to use.
1420 * @sa PDMAUDIOPCMPROPS_F2B
1421 */
1422uint32_t DrvAudioHlpBytesPerFrame(PCPDMAUDIOPCMPROPS pProps)
1423{
1424 return PDMAUDIOPCMPROPS_F2B(pProps, 1 /*cFrames*/);
1425}
1426
1427/**
1428 * Prints PCM properties to the debug log.
1429 *
1430 * @param pProps Stream configuration to log.
1431 */
1432void DrvAudioHlpPcmPropsLog(PCPDMAUDIOPCMPROPS pProps)
1433{
1434 AssertPtrReturnVoid(pProps);
1435
1436 Log(("uHz=%RU32, cChannels=%RU8, cBits=%RU8%s",
1437 pProps->uHz, pProps->cChannels, pProps->cbSample * 8, pProps->fSigned ? "S" : "U"));
1438}
1439
1440
1441/*********************************************************************************************************************************
1442* Audio File Helpers *
1443*********************************************************************************************************************************/
1444
1445/**
1446 * Sanitizes the file name component so that unsupported characters
1447 * will be replaced by an underscore ("_").
1448 *
1449 * @return IPRT status code.
1450 * @param pszPath Path to sanitize.
1451 * @param cbPath Size (in bytes) of path to sanitize.
1452 */
1453int DrvAudioHlpFileNameSanitize(char *pszPath, size_t cbPath)
1454{
1455 RT_NOREF(cbPath);
1456 int rc = VINF_SUCCESS;
1457#ifdef RT_OS_WINDOWS
1458 /* Filter out characters not allowed on Windows platforms, put in by
1459 RTTimeSpecToString(). */
1460 /** @todo Use something like RTPathSanitize() if available later some time. */
1461 static RTUNICP const s_uszValidRangePairs[] =
1462 {
1463 ' ', ' ',
1464 '(', ')',
1465 '-', '.',
1466 '0', '9',
1467 'A', 'Z',
1468 'a', 'z',
1469 '_', '_',
1470 0xa0, 0xd7af,
1471 '\0'
1472 };
1473 ssize_t cReplaced = RTStrPurgeComplementSet(pszPath, s_uszValidRangePairs, '_' /* Replacement */);
1474 if (cReplaced < 0)
1475 rc = VERR_INVALID_UTF8_ENCODING;
1476#else
1477 RT_NOREF(pszPath);
1478#endif
1479 return rc;
1480}
1481
1482/**
1483 * Constructs an unique file name, based on the given path and the audio file type.
1484 *
1485 * @returns IPRT status code.
1486 * @param pszFile Where to store the constructed file name.
1487 * @param cchFile Size (in characters) of the file name buffer.
1488 * @param pszPath Base path to use.
1489 * If NULL or empty, the system's temporary directory will be used.
1490 * @param pszName A name for better identifying the file.
1491 * @param uInstance Device / driver instance which is using this file.
1492 * @param enmType Audio file type to construct file name for.
1493 * @param fFlags File naming flags, PDMAUDIOFILENAME_FLAGS_XXX.
1494 */
1495int DrvAudioHlpFileNameGet(char *pszFile, size_t cchFile, const char *pszPath, const char *pszName,
1496 uint32_t uInstance, PDMAUDIOFILETYPE enmType, uint32_t fFlags)
1497{
1498 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
1499 AssertReturn(cchFile, VERR_INVALID_PARAMETER);
1500 /* pszPath can be NULL. */
1501 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
1502 /** @todo Validate fFlags. */
1503
1504 int rc;
1505
1506 char *pszPathTmp = NULL;
1507
1508 do
1509 {
1510 if ( pszPath == NULL
1511 || !strlen(pszPath))
1512 {
1513 char szTemp[RTPATH_MAX];
1514 rc = RTPathTemp(szTemp, sizeof(szTemp));
1515 if (RT_SUCCESS(rc))
1516 {
1517 pszPathTmp = RTStrDup(szTemp);
1518 }
1519 else
1520 break;
1521 }
1522 else
1523 pszPathTmp = RTStrDup(pszPath);
1524
1525 AssertPtrBreakStmt(pszPathTmp, rc = VERR_NO_MEMORY);
1526
1527 char szFilePath[RTPATH_MAX];
1528 rc = RTStrCopy(szFilePath, sizeof(szFilePath), pszPathTmp);
1529 AssertRCBreak(rc);
1530
1531 /* Create it when necessary. */
1532 if (!RTDirExists(szFilePath))
1533 {
1534 rc = RTDirCreateFullPath(szFilePath, RTFS_UNIX_IRWXU);
1535 if (RT_FAILURE(rc))
1536 break;
1537 }
1538
1539 char szFileName[RTPATH_MAX];
1540 szFileName[0] = '\0';
1541
1542 if (fFlags & PDMAUDIOFILENAME_FLAGS_TS)
1543 {
1544 RTTIMESPEC time;
1545 if (!RTTimeSpecToString(RTTimeNow(&time), szFileName, sizeof(szFileName)))
1546 {
1547 rc = VERR_BUFFER_OVERFLOW;
1548 break;
1549 }
1550
1551 rc = DrvAudioHlpFileNameSanitize(szFileName, sizeof(szFileName));
1552 if (RT_FAILURE(rc))
1553 break;
1554
1555 rc = RTStrCat(szFileName, sizeof(szFileName), "-");
1556 if (RT_FAILURE(rc))
1557 break;
1558 }
1559
1560 rc = RTStrCat(szFileName, sizeof(szFileName), pszName);
1561 if (RT_FAILURE(rc))
1562 break;
1563
1564 rc = RTStrCat(szFileName, sizeof(szFileName), "-");
1565 if (RT_FAILURE(rc))
1566 break;
1567
1568 char szInst[16];
1569 RTStrPrintf2(szInst, sizeof(szInst), "%RU32", uInstance);
1570 rc = RTStrCat(szFileName, sizeof(szFileName), szInst);
1571 if (RT_FAILURE(rc))
1572 break;
1573
1574 switch (enmType)
1575 {
1576 case PDMAUDIOFILETYPE_RAW:
1577 rc = RTStrCat(szFileName, sizeof(szFileName), ".pcm");
1578 break;
1579
1580 case PDMAUDIOFILETYPE_WAV:
1581 rc = RTStrCat(szFileName, sizeof(szFileName), ".wav");
1582 break;
1583
1584 default:
1585 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1586 break;
1587 }
1588
1589 if (RT_FAILURE(rc))
1590 break;
1591
1592 rc = RTPathAppend(szFilePath, sizeof(szFilePath), szFileName);
1593 if (RT_FAILURE(rc))
1594 break;
1595
1596 rc = RTStrCopy(pszFile, cchFile, szFilePath);
1597
1598 } while (0);
1599
1600 RTStrFree(pszPathTmp);
1601
1602 LogFlowFuncLeaveRC(rc);
1603 return rc;
1604}
1605
1606/**
1607 * Creates an audio file.
1608 *
1609 * @returns IPRT status code.
1610 * @param enmType Audio file type to open / create.
1611 * @param pszFile File path of file to open or create.
1612 * @param fFlags Audio file flags, PDMAUDIOFILE_FLAGS_XXX.
1613 * @param ppFile Where to store the created audio file handle.
1614 * Needs to be destroyed with DrvAudioHlpFileDestroy().
1615 */
1616int DrvAudioHlpFileCreate(PDMAUDIOFILETYPE enmType, const char *pszFile, uint32_t fFlags, PPDMAUDIOFILE *ppFile)
1617{
1618 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
1619 /** @todo Validate fFlags. */
1620
1621 PPDMAUDIOFILE pFile = (PPDMAUDIOFILE)RTMemAlloc(sizeof(PDMAUDIOFILE));
1622 if (!pFile)
1623 return VERR_NO_MEMORY;
1624
1625 int rc = VINF_SUCCESS;
1626
1627 switch (enmType)
1628 {
1629 case PDMAUDIOFILETYPE_RAW:
1630 case PDMAUDIOFILETYPE_WAV:
1631 pFile->enmType = enmType;
1632 break;
1633
1634 default:
1635 rc = VERR_INVALID_PARAMETER;
1636 break;
1637 }
1638
1639 if (RT_SUCCESS(rc))
1640 {
1641 RTStrPrintf(pFile->szName, RT_ELEMENTS(pFile->szName), "%s", pszFile);
1642 pFile->hFile = NIL_RTFILE;
1643 pFile->fFlags = fFlags;
1644 pFile->pvData = NULL;
1645 pFile->cbData = 0;
1646 }
1647
1648 if (RT_FAILURE(rc))
1649 {
1650 RTMemFree(pFile);
1651 pFile = NULL;
1652 }
1653 else
1654 *ppFile = pFile;
1655
1656 return rc;
1657}
1658
1659/**
1660 * Destroys a formerly created audio file.
1661 *
1662 * @param pFile Audio file (object) to destroy.
1663 */
1664void DrvAudioHlpFileDestroy(PPDMAUDIOFILE pFile)
1665{
1666 if (!pFile)
1667 return;
1668
1669 DrvAudioHlpFileClose(pFile);
1670
1671 RTMemFree(pFile);
1672 pFile = NULL;
1673}
1674
1675/**
1676 * Opens or creates an audio file.
1677 *
1678 * @returns IPRT status code.
1679 * @param pFile Pointer to audio file handle to use.
1680 * @param fOpen Open flags.
1681 * Use PDMAUDIOFILE_DEFAULT_OPEN_FLAGS for the default open flags.
1682 * @param pProps PCM properties to use.
1683 */
1684int DrvAudioHlpFileOpen(PPDMAUDIOFILE pFile, uint32_t fOpen, PCPDMAUDIOPCMPROPS pProps)
1685{
1686 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1687 /** @todo Validate fOpen flags. */
1688 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
1689
1690 int rc;
1691
1692 if (pFile->enmType == PDMAUDIOFILETYPE_RAW)
1693 {
1694 rc = RTFileOpen(&pFile->hFile, pFile->szName, fOpen);
1695 }
1696 else if (pFile->enmType == PDMAUDIOFILETYPE_WAV)
1697 {
1698 Assert(pProps->cChannels);
1699 Assert(pProps->uHz);
1700 Assert(pProps->cbSample);
1701
1702 pFile->pvData = (PAUDIOWAVFILEDATA)RTMemAllocZ(sizeof(AUDIOWAVFILEDATA));
1703 if (pFile->pvData)
1704 {
1705 pFile->cbData = sizeof(PAUDIOWAVFILEDATA);
1706
1707 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1708 AssertPtr(pData);
1709
1710 /* Header. */
1711 pData->Hdr.u32RIFF = AUDIO_MAKE_FOURCC('R','I','F','F');
1712 pData->Hdr.u32Size = 36;
1713 pData->Hdr.u32WAVE = AUDIO_MAKE_FOURCC('W','A','V','E');
1714
1715 pData->Hdr.u32Fmt = AUDIO_MAKE_FOURCC('f','m','t',' ');
1716 pData->Hdr.u32Size1 = 16; /* Means PCM. */
1717 pData->Hdr.u16AudioFormat = 1; /* PCM, linear quantization. */
1718 pData->Hdr.u16NumChannels = pProps->cChannels;
1719 pData->Hdr.u32SampleRate = pProps->uHz;
1720 pData->Hdr.u32ByteRate = DrvAudioHlpGetBitrate(pProps) / 8;
1721 pData->Hdr.u16BlockAlign = pProps->cChannels * pProps->cbSample;
1722 pData->Hdr.u16BitsPerSample = pProps->cbSample * 8;
1723
1724 /* Data chunk. */
1725 pData->Hdr.u32ID2 = AUDIO_MAKE_FOURCC('d','a','t','a');
1726 pData->Hdr.u32Size2 = 0;
1727
1728 rc = RTFileOpen(&pFile->hFile, pFile->szName, fOpen);
1729 if (RT_SUCCESS(rc))
1730 {
1731 rc = RTFileWrite(pFile->hFile, &pData->Hdr, sizeof(pData->Hdr), NULL);
1732 if (RT_FAILURE(rc))
1733 {
1734 RTFileClose(pFile->hFile);
1735 pFile->hFile = NIL_RTFILE;
1736 }
1737 }
1738
1739 if (RT_FAILURE(rc))
1740 {
1741 RTMemFree(pFile->pvData);
1742 pFile->pvData = NULL;
1743 pFile->cbData = 0;
1744 }
1745 }
1746 else
1747 rc = VERR_NO_MEMORY;
1748 }
1749 else
1750 rc = VERR_INVALID_PARAMETER;
1751
1752 if (RT_SUCCESS(rc))
1753 {
1754 LogRel2(("Audio: Opened file '%s'\n", pFile->szName));
1755 }
1756 else
1757 LogRel(("Audio: Failed opening file '%s', rc=%Rrc\n", pFile->szName, rc));
1758
1759 return rc;
1760}
1761
1762/**
1763 * Closes an audio file.
1764 *
1765 * @returns IPRT status code.
1766 * @param pFile Audio file handle to close.
1767 */
1768int DrvAudioHlpFileClose(PPDMAUDIOFILE pFile)
1769{
1770 if (!pFile)
1771 return VINF_SUCCESS;
1772
1773 size_t cbSize = DrvAudioHlpFileGetDataSize(pFile);
1774
1775 int rc = VINF_SUCCESS;
1776
1777 if (pFile->enmType == PDMAUDIOFILETYPE_RAW)
1778 {
1779 if (RTFileIsValid(pFile->hFile))
1780 rc = RTFileClose(pFile->hFile);
1781 }
1782 else if (pFile->enmType == PDMAUDIOFILETYPE_WAV)
1783 {
1784 if (RTFileIsValid(pFile->hFile))
1785 {
1786 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1787 if (pData) /* The .WAV file data only is valid when a file actually has been created. */
1788 {
1789 /* Update the header with the current data size. */
1790 RTFileWriteAt(pFile->hFile, 0, &pData->Hdr, sizeof(pData->Hdr), NULL);
1791 }
1792
1793 rc = RTFileClose(pFile->hFile);
1794 }
1795
1796 if (pFile->pvData)
1797 {
1798 RTMemFree(pFile->pvData);
1799 pFile->pvData = NULL;
1800 }
1801 }
1802 else
1803 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1804
1805 if ( RT_SUCCESS(rc)
1806 && !cbSize
1807 && !(pFile->fFlags & PDMAUDIOFILE_FLAGS_KEEP_IF_EMPTY))
1808 {
1809 rc = DrvAudioHlpFileDelete(pFile);
1810 }
1811
1812 pFile->cbData = 0;
1813
1814 if (RT_SUCCESS(rc))
1815 {
1816 pFile->hFile = NIL_RTFILE;
1817 LogRel2(("Audio: Closed file '%s' (%zu bytes)\n", pFile->szName, cbSize));
1818 }
1819 else
1820 LogRel(("Audio: Failed closing file '%s', rc=%Rrc\n", pFile->szName, rc));
1821
1822 return rc;
1823}
1824
1825/**
1826 * Deletes an audio file.
1827 *
1828 * @returns IPRT status code.
1829 * @param pFile Audio file handle to delete.
1830 */
1831int DrvAudioHlpFileDelete(PPDMAUDIOFILE pFile)
1832{
1833 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1834
1835 int rc = RTFileDelete(pFile->szName);
1836 if (RT_SUCCESS(rc))
1837 {
1838 LogRel2(("Audio: Deleted file '%s'\n", pFile->szName));
1839 }
1840 else if (rc == VERR_FILE_NOT_FOUND) /* Don't bitch if the file is not around (anymore). */
1841 rc = VINF_SUCCESS;
1842
1843 if (RT_FAILURE(rc))
1844 LogRel(("Audio: Failed deleting file '%s', rc=%Rrc\n", pFile->szName, rc));
1845
1846 return rc;
1847}
1848
1849/**
1850 * Returns the raw audio data size of an audio file.
1851 *
1852 * Note: This does *not* include file headers and other data which does
1853 * not belong to the actual PCM audio data.
1854 *
1855 * @returns Size (in bytes) of the raw PCM audio data.
1856 * @param pFile Audio file handle to retrieve the audio data size for.
1857 */
1858size_t DrvAudioHlpFileGetDataSize(PPDMAUDIOFILE pFile)
1859{
1860 AssertPtrReturn(pFile, 0);
1861
1862 size_t cbSize = 0;
1863
1864 if (pFile->enmType == PDMAUDIOFILETYPE_RAW)
1865 {
1866 cbSize = RTFileTell(pFile->hFile);
1867 }
1868 else if (pFile->enmType == PDMAUDIOFILETYPE_WAV)
1869 {
1870 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1871 if (pData) /* The .WAV file data only is valid when a file actually has been created. */
1872 cbSize = pData->Hdr.u32Size2;
1873 }
1874
1875 return cbSize;
1876}
1877
1878/**
1879 * Returns whether the given audio file is open and in use or not.
1880 *
1881 * @return bool True if open, false if not.
1882 * @param pFile Audio file handle to check open status for.
1883 */
1884bool DrvAudioHlpFileIsOpen(PPDMAUDIOFILE pFile)
1885{
1886 if (!pFile)
1887 return false;
1888
1889 return RTFileIsValid(pFile->hFile);
1890}
1891
1892/**
1893 * Write PCM data to a wave (.WAV) file.
1894 *
1895 * @returns IPRT status code.
1896 * @param pFile Audio file handle to write PCM data to.
1897 * @param pvBuf Audio data to write.
1898 * @param cbBuf Size (in bytes) of audio data to write.
1899 * @param fFlags Additional write flags. Not being used at the moment and must be 0.
1900 */
1901int DrvAudioHlpFileWrite(PPDMAUDIOFILE pFile, const void *pvBuf, size_t cbBuf, uint32_t fFlags)
1902{
1903 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1904 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1905
1906 AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); /** @todo fFlags are currently not implemented. */
1907
1908 if (!cbBuf)
1909 return VINF_SUCCESS;
1910
1911 AssertReturn(RTFileIsValid(pFile->hFile), VERR_WRONG_ORDER);
1912
1913 int rc;
1914
1915 if (pFile->enmType == PDMAUDIOFILETYPE_RAW)
1916 {
1917 rc = RTFileWrite(pFile->hFile, pvBuf, cbBuf, NULL);
1918 }
1919 else if (pFile->enmType == PDMAUDIOFILETYPE_WAV)
1920 {
1921 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1922 AssertPtr(pData);
1923
1924 rc = RTFileWrite(pFile->hFile, pvBuf, cbBuf, NULL);
1925 if (RT_SUCCESS(rc))
1926 {
1927 pData->Hdr.u32Size += (uint32_t)cbBuf;
1928 pData->Hdr.u32Size2 += (uint32_t)cbBuf;
1929 }
1930 }
1931 else
1932 rc = VERR_NOT_SUPPORTED;
1933
1934 return rc;
1935}
1936
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