VirtualBox

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

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

Audio: Preparing to move some of the DrvAudio.h stuff into PDM. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.4 KB
Line 
1/* $Id: DrvAudioCommon.cpp 88023 2021-03-08 18:01:15Z 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 PDMAudioFormatIsSigned(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 PDMAudioFormatGetBits(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 PDMAudioStrmCfgInit(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 PDMAudioStrmCfgInitWithProps(PPDMAUDIOSTREAMCFG pCfg, PCPDMAUDIOPCMPROPS pProps)
761{
762 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
763 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
764
765 PDMAudioStrmCfgInit(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 * Checks whether stream configuration matches the given PCM properties.
795 *
796 * @returns @c true if equal, @c false if not.
797 * @param pCfg Stream configuration.
798 * @param pProps PCM properties to match with.
799 */
800bool PDMAudioStrmCfgMatchesProps(PCPDMAUDIOSTREAMCFG pCfg, PCPDMAUDIOPCMPROPS pProps)
801{
802 AssertPtrReturn(pCfg, false);
803 return PDMAudioPropsAreEqual(pProps, &pCfg->Props);
804}
805
806/**
807 * Frees an allocated audio stream configuration.
808 *
809 * @param pCfg Audio stream configuration to free.
810 */
811void PDMAudioStrmCfgFree(PPDMAUDIOSTREAMCFG pCfg)
812{
813 if (pCfg)
814 {
815 RTMemFree(pCfg);
816 pCfg = NULL;
817 }
818}
819
820/**
821 * Copies a source stream configuration to a destination stream configuration.
822 *
823 * @returns IPRT status code.
824 * @param pDstCfg Destination stream configuration to copy source to.
825 * @param pSrcCfg Source stream configuration to copy to destination.
826 */
827int PDMAudioStrmCfgCopy(PPDMAUDIOSTREAMCFG pDstCfg, PCPDMAUDIOSTREAMCFG pSrcCfg)
828{
829 AssertPtrReturn(pDstCfg, VERR_INVALID_POINTER);
830 AssertPtrReturn(pSrcCfg, VERR_INVALID_POINTER);
831
832#ifdef VBOX_STRICT
833 if (!DrvAudioHlpStreamCfgIsValid(pSrcCfg))
834 {
835 AssertMsgFailed(("Stream config '%s' (%p) is invalid\n", pSrcCfg->szName, pSrcCfg));
836 return VERR_INVALID_PARAMETER;
837 }
838#endif
839
840 memcpy(pDstCfg, pSrcCfg, sizeof(PDMAUDIOSTREAMCFG));
841
842 return VINF_SUCCESS;
843}
844
845/**
846 * Duplicates an audio stream configuration.
847 * Must be free'd with PDMAudioStrmCfgFree().
848 *
849 * @return Duplicates audio stream configuration on success, or NULL on failure.
850 * @param pCfg Audio stream configuration to duplicate.
851 */
852PPDMAUDIOSTREAMCFG PDMAudioStrmCfgDup(PCPDMAUDIOSTREAMCFG pCfg)
853{
854 AssertPtrReturn(pCfg, NULL);
855
856#ifdef VBOX_STRICT
857 if (!DrvAudioHlpStreamCfgIsValid(pCfg))
858 {
859 AssertMsgFailed(("Stream config '%s' (%p) is invalid\n", pCfg->szName, pCfg));
860 return NULL;
861 }
862#endif
863
864 PPDMAUDIOSTREAMCFG pDst = (PPDMAUDIOSTREAMCFG)RTMemAllocZ(sizeof(PDMAUDIOSTREAMCFG));
865 if (!pDst)
866 return NULL;
867
868 int rc2 = PDMAudioStrmCfgCopy(pDst, pCfg);
869 if (RT_FAILURE(rc2))
870 {
871 PDMAudioStrmCfgFree(pDst);
872 pDst = NULL;
873 }
874
875 AssertPtr(pDst);
876 return pDst;
877}
878
879/**
880 * Prints an audio stream configuration to the debug log.
881 *
882 * @param pCfg Stream configuration to log.
883 */
884void PDMAudioStrmCfgLog(PCPDMAUDIOSTREAMCFG pCfg)
885{
886 if (pCfg)
887 LogFunc(("szName=%s enmDir=%RU32 uHz=%RU32 cBits=%RU8%s cChannels=%RU8\n", pCfg->szName, pCfg->enmDir,
888 pCfg->Props.uHz, pCfg->Props.cbSample * 8, pCfg->Props.fSigned ? "S" : "U", pCfg->Props.cChannels));
889}
890
891/**
892 * Converts a stream command to a string.
893 *
894 * @returns Stringified stream command, or "Unknown", if not found.
895 * @param enmCmd Stream command to convert.
896 */
897const char *DrvAudioHlpStreamCmdToStr(PDMAUDIOSTREAMCMD enmCmd)
898{
899 switch (enmCmd)
900 {
901 case PDMAUDIOSTREAMCMD_INVALID: return "Invalid";
902 case PDMAUDIOSTREAMCMD_UNKNOWN: return "Unknown";
903 case PDMAUDIOSTREAMCMD_ENABLE: return "Enable";
904 case PDMAUDIOSTREAMCMD_DISABLE: return "Disable";
905 case PDMAUDIOSTREAMCMD_PAUSE: return "Pause";
906 case PDMAUDIOSTREAMCMD_RESUME: return "Resume";
907 case PDMAUDIOSTREAMCMD_DRAIN: return "Drain";
908 case PDMAUDIOSTREAMCMD_DROP: return "Drop";
909 case PDMAUDIOSTREAMCMD_32BIT_HACK:
910 break;
911 }
912 AssertMsgFailed(("Invalid stream command %d\n", enmCmd));
913 return "Unknown";
914}
915
916/**
917 * Returns @c true if the given stream status indicates a can-be-read-from stream,
918 * @c false if not.
919 *
920 * @returns @c true if ready to be read from, @c if not.
921 * @param fStatus Stream status to evaluate, PDMAUDIOSTREAMSTS_FLAGS_XXX.
922 */
923bool DrvAudioHlpStreamStatusCanRead(PDMAUDIOSTREAMSTS fStatus)
924{
925 AssertReturn(fStatus & PDMAUDIOSTREAMSTS_VALID_MASK, false);
926
927 return fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED
928 && fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED
929 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PAUSED)
930 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT);
931}
932
933/**
934 * Returns @c true if the given stream status indicates a can-be-written-to stream,
935 * @c false if not.
936 *
937 * @returns @c true if ready to be written to, @c if not.
938 * @param fStatus Stream status to evaluate, PDMAUDIOSTREAMSTS_FLAGS_XXX.
939 */
940bool DrvAudioHlpStreamStatusCanWrite(PDMAUDIOSTREAMSTS fStatus)
941{
942 AssertReturn(fStatus & PDMAUDIOSTREAMSTS_VALID_MASK, false);
943
944 return fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED
945 && fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED
946 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PAUSED)
947 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)
948 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT);
949}
950
951/**
952 * Returns @c true if the given stream status indicates a ready-to-operate stream,
953 * @c false if not.
954 *
955 * @returns @c true if ready to operate, @c if not.
956 * @param fStatus Stream status to evaluate.
957 */
958bool DrvAudioHlpStreamStatusIsReady(PDMAUDIOSTREAMSTS fStatus)
959{
960 AssertReturn(fStatus & PDMAUDIOSTREAMSTS_VALID_MASK, false);
961
962 return fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED
963 && fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED
964 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT);
965}
966
967/**
968 * Calculates the audio bit rate of the given bits per sample, the Hz and the number
969 * of audio channels.
970 *
971 * Divide the result by 8 to get the byte rate.
972 *
973 * @returns Bitrate.
974 * @param cBits Number of bits per sample.
975 * @param uHz Hz (Hertz) rate.
976 * @param cChannels Number of audio channels.
977 */
978uint32_t DrvAudioHlpCalcBitrate(uint8_t cBits, uint32_t uHz, uint8_t cChannels)
979{
980 return cBits * uHz * cChannels;
981}
982
983
984/*********************************************************************************************************************************
985* PCM Property Helpers *
986*********************************************************************************************************************************/
987
988/**
989 * Gets the bitrate.
990 *
991 * Divide the result by 8 to get the byte rate.
992 *
993 * @returns Bit rate.
994 * @param pProps PCM properties to calculate bitrate for.
995 */
996uint32_t PDMAudioPropsGetBitrate(PCPDMAUDIOPCMPROPS pProps)
997{
998 return pProps->cbSample * pProps->cChannels * pProps->uHz * 8;
999}
1000
1001/**
1002 * Rounds down the given byte amount to the nearest frame boundrary.
1003 *
1004 * @returns Rounded byte amount.
1005 * @param pProps PCM properties to use.
1006 * @param cb The size (in bytes) to round.
1007 */
1008uint32_t PDMAudioPropsFloorBytesToFrame(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
1009{
1010 AssertPtrReturn(pProps, 0);
1011 return PDMAUDIOPCMPROPS_F2B(pProps, PDMAUDIOPCMPROPS_B2F(pProps, cb));
1012}
1013
1014/**
1015 * Checks if the given size is aligned on a frame boundrary.
1016 *
1017 * @returns @c true if properly aligned, @c false if not.
1018 * @param pProps PCM properties to use.
1019 * @param cb The size (in bytes) to check.
1020 */
1021bool PDMAudioPropsIsSizeAligned(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
1022{
1023 AssertPtrReturn(pProps, false);
1024 uint32_t const cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */);
1025 AssertReturn(cbFrame, false);
1026 return cb % cbFrame == 0;
1027}
1028
1029/**
1030 * Converts bytes to frames (rounding down of course).
1031 *
1032 * @returns Number of frames.
1033 * @param pProps PCM properties to use.
1034 * @param cb The number of bytes to convert.
1035 */
1036uint32_t PDMAudioPropsBytesToFrames(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
1037{
1038 AssertPtrReturn(pProps, 0);
1039 return PDMAUDIOPCMPROPS_B2F(pProps, cb);
1040}
1041
1042/**
1043 * Converts bytes to milliseconds.
1044 *
1045 * @return Number milliseconds @a cb takes to play or record.
1046 * @param pProps PCM properties to use.
1047 * @param cb The number of bytes to convert.
1048 *
1049 * @note Rounds up the result.
1050 */
1051uint64_t PDMAudioPropsBytesToMilli(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
1052{
1053 AssertPtrReturn(pProps, 0);
1054
1055 /* Check parameters to prevent division by chainsaw: */
1056 uint32_t const uHz = pProps->uHz;
1057 if (uHz)
1058 {
1059 const unsigned cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */);
1060 if (cbFrame)
1061 {
1062 /* Round cb up to closest frame size: */
1063 cb = (cb + cbFrame - 1) / cbFrame;
1064
1065 /* Convert to milliseconds. */
1066 return (cb * (uint64_t)RT_MS_1SEC + uHz - 1) / uHz;
1067 }
1068 }
1069 return 0;
1070}
1071
1072/**
1073 * Converts bytes to microseconds.
1074 *
1075 * @return Number microseconds @a cb takes to play or record.
1076 * @param pProps PCM properties to use.
1077 * @param cb The number of bytes to convert.
1078 *
1079 * @note Rounds up the result.
1080 */
1081uint64_t PDMAudioPropsBytesToMicro(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
1082{
1083 AssertPtrReturn(pProps, 0);
1084
1085 /* Check parameters to prevent division by chainsaw: */
1086 uint32_t const uHz = pProps->uHz;
1087 if (uHz)
1088 {
1089 const unsigned cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */);
1090 if (cbFrame)
1091 {
1092 /* Round cb up to closest frame size: */
1093 cb = (cb + cbFrame - 1) / cbFrame;
1094
1095 /* Convert to microseconds. */
1096 return (cb * (uint64_t)RT_US_1SEC + uHz - 1) / uHz;
1097 }
1098 }
1099 return 0;
1100}
1101
1102/**
1103 * Converts bytes to nanoseconds.
1104 *
1105 * @return Number nanoseconds @a cb takes to play or record.
1106 * @param pProps PCM properties to use.
1107 * @param cb The number of bytes to convert.
1108 *
1109 * @note Rounds up the result.
1110 */
1111uint64_t PDMAudioPropsBytesToNano(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
1112{
1113 AssertPtrReturn(pProps, 0);
1114
1115 /* Check parameters to prevent division by chainsaw: */
1116 uint32_t const uHz = pProps->uHz;
1117 if (uHz)
1118 {
1119 const unsigned cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */);
1120 if (cbFrame)
1121 {
1122 /* Round cb up to closest frame size: */
1123 cb = (cb + cbFrame - 1) / cbFrame;
1124
1125 /* Convert to nanoseconds. */
1126 return (cb * (uint64_t)RT_NS_1SEC + uHz - 1) / uHz;
1127 }
1128 }
1129 return 0;
1130}
1131
1132/**
1133 * Converts frames to bytes.
1134 *
1135 * @returns Number of bytes.
1136 * @param pProps The PCM properties to use.
1137 * @param cFrames Number of audio frames to convert.
1138 * @sa PDMAUDIOPCMPROPS_F2B
1139 */
1140uint32_t PDMAudioPropsFramesToBytes(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
1141{
1142 AssertPtrReturn(pProps, 0);
1143 return PDMAUDIOPCMPROPS_F2B(pProps, cFrames);
1144}
1145
1146/**
1147 * Converts frames to milliseconds.
1148 *
1149 * @returns milliseconds.
1150 * @param pProps The PCM properties to use.
1151 * @param cFrames Number of audio frames to convert.
1152 * @note No rounding here, result is floored.
1153 */
1154uint64_t PDMAudioPropsFramesToMilli(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
1155{
1156 AssertPtrReturn(pProps, 0);
1157
1158 /* Check input to prevent division by chainsaw: */
1159 uint32_t const uHz = pProps->uHz;
1160 if (uHz)
1161 return ASMMultU32ByU32DivByU32(cFrames, RT_MS_1SEC, uHz);
1162 return 0;
1163}
1164
1165/**
1166 * Converts frames to nanoseconds.
1167 *
1168 * @returns Nanoseconds.
1169 * @param pProps The PCM properties to use.
1170 * @param cFrames Number of audio frames to convert.
1171 * @note No rounding here, result is floored.
1172 */
1173uint64_t PDMAudioPropsFramesToNano(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
1174{
1175 AssertPtrReturn(pProps, 0);
1176
1177 /* Check input to prevent division by chainsaw: */
1178 uint32_t const uHz = pProps->uHz;
1179 if (uHz)
1180 return ASMMultU32ByU32DivByU32(cFrames, RT_NS_1SEC, uHz);
1181 return 0;
1182}
1183
1184/**
1185 * Converts milliseconds to frames.
1186 *
1187 * @returns Number of frames
1188 * @param pProps The PCM properties to use.
1189 * @param cMs The number of milliseconds to convert.
1190 *
1191 * @note The result is rounded rather than floored (hysterical raisins).
1192 */
1193uint32_t PDMAudioPropsMilliToFrames(PCPDMAUDIOPCMPROPS pProps, uint64_t cMs)
1194{
1195 AssertPtrReturn(pProps, 0);
1196
1197 uint32_t const uHz = pProps->uHz;
1198 uint32_t cFrames;
1199 if (cMs < RT_MS_1SEC)
1200 cFrames = 0;
1201 else
1202 {
1203 cFrames = cMs / RT_MS_1SEC * uHz;
1204 cMs %= RT_MS_1SEC;
1205 }
1206 cFrames += (ASMMult2xU32RetU64(uHz, (uint32_t)cMs) + RT_MS_1SEC - 1) / RT_MS_1SEC;
1207 return cFrames;
1208}
1209
1210/**
1211 * Converts milliseconds to bytes.
1212 *
1213 * @returns Number of bytes (frame aligned).
1214 * @param pProps The PCM properties to use.
1215 * @param cMs The number of milliseconds to convert.
1216 *
1217 * @note The result is rounded rather than floored (hysterical raisins).
1218 */
1219uint32_t PDMAudioPropsMilliToBytes(PCPDMAUDIOPCMPROPS pProps, uint64_t cMs)
1220{
1221 return PDMAUDIOPCMPROPS_F2B(pProps, PDMAudioPropsMilliToFrames(pProps, cMs));
1222}
1223
1224/**
1225 * Converts nanoseconds to frames.
1226 *
1227 * @returns Number of frames
1228 * @param pProps The PCM properties to use.
1229 * @param cNs The number of nanoseconds to convert.
1230 *
1231 * @note The result is rounded rather than floored (hysterical raisins).
1232 */
1233uint32_t PDMAudioPropsNanoToFrames(PCPDMAUDIOPCMPROPS pProps, uint64_t cNs)
1234{
1235 AssertPtrReturn(pProps, 0);
1236
1237 uint32_t const uHz = pProps->uHz;
1238 uint32_t cFrames;
1239 if (cNs < RT_NS_1SEC)
1240 cFrames = 0;
1241 else
1242 {
1243 cFrames = cNs / RT_NS_1SEC * uHz;
1244 cNs %= RT_NS_1SEC;
1245 }
1246 cFrames += (ASMMult2xU32RetU64(uHz, (uint32_t)cNs) + RT_NS_1SEC - 1) / RT_NS_1SEC;
1247 return cFrames;
1248}
1249
1250/**
1251 * Converts nanoseconds to bytes.
1252 *
1253 * @returns Number of bytes (frame aligned).
1254 * @param pProps The PCM properties to use.
1255 * @param cNs The number of nanoseconds to convert.
1256 *
1257 * @note The result is rounded rather than floored (hysterical raisins).
1258 */
1259uint32_t PDMAudioPropsNanoToBytes(PCPDMAUDIOPCMPROPS pProps, uint64_t cNs)
1260{
1261 return PDMAUDIOPCMPROPS_F2B(pProps, PDMAudioPropsNanoToFrames(pProps, cNs));
1262}
1263
1264/**
1265 * Clears a sample buffer by the given amount of audio frames with silence (according to the format
1266 * given by the PCM properties).
1267 *
1268 * @param pProps The PCM properties to apply.
1269 * @param pvBuf The buffer to clear.
1270 * @param cbBuf The buffer size in bytes.
1271 * @param cFrames The number of audio frames to clear. Capped at @a cbBuf
1272 * if exceeding the buffer. If the size is an unaligned
1273 * number of frames, the extra bytes may be left
1274 * uninitialized in some configurations.
1275 */
1276void PDMAudioPropsClearBuffer(PCPDMAUDIOPCMPROPS pProps, void *pvBuf, size_t cbBuf, uint32_t cFrames)
1277{
1278 /*
1279 * Validate input
1280 */
1281 AssertPtrReturnVoid(pProps);
1282 Assert(pProps->cbSample);
1283 if (!cbBuf || !cFrames)
1284 return;
1285 AssertPtrReturnVoid(pvBuf);
1286
1287 Assert(pProps->fSwapEndian == false); /** @todo Swapping Endianness is not supported yet. */
1288
1289 /*
1290 * Decide how much needs clearing.
1291 */
1292 size_t cbToClear = PDMAudioPropsFramesToBytes(pProps, cFrames);
1293 AssertStmt(cbToClear <= cbBuf, cbToClear = cbBuf);
1294
1295 Log2Func(("pProps=%p, pvBuf=%p, cFrames=%RU32, fSigned=%RTbool, cBytes=%RU8\n",
1296 pProps, pvBuf, cFrames, pProps->fSigned, pProps->cbSample));
1297
1298 /*
1299 * Do the job.
1300 */
1301 if (pProps->fSigned)
1302 RT_BZERO(pvBuf, cbToClear);
1303 else /* Unsigned formats. */
1304 {
1305 switch (pProps->cbSample)
1306 {
1307 case 1: /* 8 bit */
1308 memset(pvBuf, 0x80, cbToClear);
1309 break;
1310
1311 case 2: /* 16 bit */
1312 {
1313 uint16_t *pu16Dst = (uint16_t *)pvBuf;
1314 size_t cLeft = cbToClear / sizeof(uint16_t);
1315 while (cLeft-- > 0)
1316 *pu16Dst++ = 0x80;
1317 break;
1318 }
1319
1320 /** @todo Add 24 bit? */
1321
1322 case 4: /* 32 bit */
1323 ASMMemFill32(pvBuf, cbToClear & ~(size_t)3, 0x80);
1324 break;
1325
1326 default:
1327 AssertMsgFailed(("Invalid bytes per sample: %RU8\n", pProps->cbSample));
1328 }
1329 }
1330}
1331
1332/**
1333 * Compares two sets of PCM properties.
1334 *
1335 * @returns @c true if the same, @c false if not.
1336 * @param pProps1 The first set of properties to compare.
1337 * @param pProps2 The second set of properties to compare.
1338 */
1339bool PDMAudioPropsAreEqual(PCPDMAUDIOPCMPROPS pProps1, PCPDMAUDIOPCMPROPS pProps2)
1340{
1341 AssertPtrReturn(pProps1, false);
1342 AssertPtrReturn(pProps2, false);
1343
1344 if (pProps1 == pProps2) /* If the pointers match, take a shortcut. */
1345 return true;
1346
1347 return pProps1->uHz == pProps2->uHz
1348 && pProps1->cChannels == pProps2->cChannels
1349 && pProps1->cbSample == pProps2->cbSample
1350 && pProps1->fSigned == pProps2->fSigned
1351 && pProps1->fSwapEndian == pProps2->fSwapEndian;
1352}
1353
1354/**
1355 * Checks whether given PCM properties are valid or not.
1356 *
1357 * @note This is more of a supported than valid check. There is code for
1358 * unsigned samples elsewhere (like DrvAudioHlpClearBuf()), but this
1359 * function will flag such properties as not valid.
1360 *
1361 * @todo r=bird: See note and explain properly.
1362 *
1363 * @returns @c true if the properties are valid, @c false if not.
1364 * @param pProps The PCM properties to check.
1365 */
1366bool DrvAudioHlpPcmPropsAreValid(PCPDMAUDIOPCMPROPS pProps)
1367{
1368 AssertPtrReturn(pProps, false);
1369
1370 /** @todo r=bird: This code is cannot make up its mind whether to return on
1371 * false, or whether to return at the end. (hint: just return
1372 * immediately, duh.) */
1373
1374 /* Minimum 1 channel (mono), maximum 7.1 (= 8) channels. */
1375 bool fValid = ( pProps->cChannels >= 1
1376 && pProps->cChannels <= 8);
1377
1378 if (fValid)
1379 {
1380 switch (pProps->cbSample)
1381 {
1382 case 1: /* 8 bit */
1383 if (pProps->fSigned)
1384 fValid = false;
1385 break;
1386 case 2: /* 16 bit */
1387 if (!pProps->fSigned)
1388 fValid = false;
1389 break;
1390 /** @todo Do we need support for 24 bit samples? */
1391 case 4: /* 32 bit */
1392 if (!pProps->fSigned)
1393 fValid = false;
1394 break;
1395 default:
1396 fValid = false;
1397 break;
1398 }
1399 }
1400
1401 if (!fValid)
1402 return false;
1403
1404 fValid &= pProps->uHz > 0;
1405 fValid &= pProps->cShift == PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pProps->cbSample, pProps->cChannels);
1406 fValid &= pProps->fSwapEndian == false; /** @todo Handling Big Endian audio data is not supported yet. */
1407
1408 return fValid;
1409}
1410
1411/**
1412 * Get number of bytes per frame.
1413 *
1414 * @returns Number of bytes per audio frame.
1415 * @param pProps PCM properties to use.
1416 * @sa PDMAUDIOPCMPROPS_F2B
1417 */
1418uint32_t PDMAudioPropsBytesPerFrame(PCPDMAUDIOPCMPROPS pProps)
1419{
1420 return PDMAUDIOPCMPROPS_F2B(pProps, 1 /*cFrames*/);
1421}
1422
1423/**
1424 * Prints PCM properties to the debug log.
1425 *
1426 * @param pProps Stream configuration to log.
1427 */
1428void PDMAudioPropsLog(PCPDMAUDIOPCMPROPS pProps)
1429{
1430 AssertPtrReturnVoid(pProps);
1431
1432 Log(("uHz=%RU32, cChannels=%RU8, cBits=%RU8%s",
1433 pProps->uHz, pProps->cChannels, pProps->cbSample * 8, pProps->fSigned ? "S" : "U"));
1434}
1435
1436
1437/*********************************************************************************************************************************
1438* Audio File Helpers *
1439*********************************************************************************************************************************/
1440
1441/**
1442 * Sanitizes the file name component so that unsupported characters
1443 * will be replaced by an underscore ("_").
1444 *
1445 * @return IPRT status code.
1446 * @param pszPath Path to sanitize.
1447 * @param cbPath Size (in bytes) of path to sanitize.
1448 */
1449int DrvAudioHlpFileNameSanitize(char *pszPath, size_t cbPath)
1450{
1451 RT_NOREF(cbPath);
1452 int rc = VINF_SUCCESS;
1453#ifdef RT_OS_WINDOWS
1454 /* Filter out characters not allowed on Windows platforms, put in by
1455 RTTimeSpecToString(). */
1456 /** @todo Use something like RTPathSanitize() if available later some time. */
1457 static RTUNICP const s_uszValidRangePairs[] =
1458 {
1459 ' ', ' ',
1460 '(', ')',
1461 '-', '.',
1462 '0', '9',
1463 'A', 'Z',
1464 'a', 'z',
1465 '_', '_',
1466 0xa0, 0xd7af,
1467 '\0'
1468 };
1469 ssize_t cReplaced = RTStrPurgeComplementSet(pszPath, s_uszValidRangePairs, '_' /* Replacement */);
1470 if (cReplaced < 0)
1471 rc = VERR_INVALID_UTF8_ENCODING;
1472#else
1473 RT_NOREF(pszPath);
1474#endif
1475 return rc;
1476}
1477
1478/**
1479 * Constructs an unique file name, based on the given path and the audio file type.
1480 *
1481 * @returns IPRT status code.
1482 * @param pszFile Where to store the constructed file name.
1483 * @param cchFile Size (in characters) of the file name buffer.
1484 * @param pszPath Base path to use.
1485 * If NULL or empty, the system's temporary directory will be used.
1486 * @param pszName A name for better identifying the file.
1487 * @param uInstance Device / driver instance which is using this file.
1488 * @param enmType Audio file type to construct file name for.
1489 * @param fFlags File naming flags, PDMAUDIOFILENAME_FLAGS_XXX.
1490 */
1491int DrvAudioHlpFileNameGet(char *pszFile, size_t cchFile, const char *pszPath, const char *pszName,
1492 uint32_t uInstance, PDMAUDIOFILETYPE enmType, uint32_t fFlags)
1493{
1494 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
1495 AssertReturn(cchFile, VERR_INVALID_PARAMETER);
1496 /* pszPath can be NULL. */
1497 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
1498 /** @todo Validate fFlags. */
1499
1500 int rc;
1501
1502 char *pszPathTmp = NULL;
1503
1504 do
1505 {
1506 if ( pszPath == NULL
1507 || !strlen(pszPath))
1508 {
1509 char szTemp[RTPATH_MAX];
1510 rc = RTPathTemp(szTemp, sizeof(szTemp));
1511 if (RT_SUCCESS(rc))
1512 {
1513 pszPathTmp = RTStrDup(szTemp);
1514 }
1515 else
1516 break;
1517 }
1518 else
1519 pszPathTmp = RTStrDup(pszPath);
1520
1521 AssertPtrBreakStmt(pszPathTmp, rc = VERR_NO_MEMORY);
1522
1523 char szFilePath[RTPATH_MAX];
1524 rc = RTStrCopy(szFilePath, sizeof(szFilePath), pszPathTmp);
1525 AssertRCBreak(rc);
1526
1527 /* Create it when necessary. */
1528 if (!RTDirExists(szFilePath))
1529 {
1530 rc = RTDirCreateFullPath(szFilePath, RTFS_UNIX_IRWXU);
1531 if (RT_FAILURE(rc))
1532 break;
1533 }
1534
1535 char szFileName[RTPATH_MAX];
1536 szFileName[0] = '\0';
1537
1538 if (fFlags & PDMAUDIOFILENAME_FLAGS_TS)
1539 {
1540 RTTIMESPEC time;
1541 if (!RTTimeSpecToString(RTTimeNow(&time), szFileName, sizeof(szFileName)))
1542 {
1543 rc = VERR_BUFFER_OVERFLOW;
1544 break;
1545 }
1546
1547 rc = DrvAudioHlpFileNameSanitize(szFileName, sizeof(szFileName));
1548 if (RT_FAILURE(rc))
1549 break;
1550
1551 rc = RTStrCat(szFileName, sizeof(szFileName), "-");
1552 if (RT_FAILURE(rc))
1553 break;
1554 }
1555
1556 rc = RTStrCat(szFileName, sizeof(szFileName), pszName);
1557 if (RT_FAILURE(rc))
1558 break;
1559
1560 rc = RTStrCat(szFileName, sizeof(szFileName), "-");
1561 if (RT_FAILURE(rc))
1562 break;
1563
1564 char szInst[16];
1565 RTStrPrintf2(szInst, sizeof(szInst), "%RU32", uInstance);
1566 rc = RTStrCat(szFileName, sizeof(szFileName), szInst);
1567 if (RT_FAILURE(rc))
1568 break;
1569
1570 switch (enmType)
1571 {
1572 case PDMAUDIOFILETYPE_RAW:
1573 rc = RTStrCat(szFileName, sizeof(szFileName), ".pcm");
1574 break;
1575
1576 case PDMAUDIOFILETYPE_WAV:
1577 rc = RTStrCat(szFileName, sizeof(szFileName), ".wav");
1578 break;
1579
1580 default:
1581 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1582 break;
1583 }
1584
1585 if (RT_FAILURE(rc))
1586 break;
1587
1588 rc = RTPathAppend(szFilePath, sizeof(szFilePath), szFileName);
1589 if (RT_FAILURE(rc))
1590 break;
1591
1592 rc = RTStrCopy(pszFile, cchFile, szFilePath);
1593
1594 } while (0);
1595
1596 RTStrFree(pszPathTmp);
1597
1598 LogFlowFuncLeaveRC(rc);
1599 return rc;
1600}
1601
1602/**
1603 * Creates an audio file.
1604 *
1605 * @returns IPRT status code.
1606 * @param enmType Audio file type to open / create.
1607 * @param pszFile File path of file to open or create.
1608 * @param fFlags Audio file flags, PDMAUDIOFILE_FLAGS_XXX.
1609 * @param ppFile Where to store the created audio file handle.
1610 * Needs to be destroyed with DrvAudioHlpFileDestroy().
1611 */
1612int DrvAudioHlpFileCreate(PDMAUDIOFILETYPE enmType, const char *pszFile, uint32_t fFlags, PPDMAUDIOFILE *ppFile)
1613{
1614 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
1615 /** @todo Validate fFlags. */
1616
1617 PPDMAUDIOFILE pFile = (PPDMAUDIOFILE)RTMemAlloc(sizeof(PDMAUDIOFILE));
1618 if (!pFile)
1619 return VERR_NO_MEMORY;
1620
1621 int rc = VINF_SUCCESS;
1622
1623 switch (enmType)
1624 {
1625 case PDMAUDIOFILETYPE_RAW:
1626 case PDMAUDIOFILETYPE_WAV:
1627 pFile->enmType = enmType;
1628 break;
1629
1630 default:
1631 rc = VERR_INVALID_PARAMETER;
1632 break;
1633 }
1634
1635 if (RT_SUCCESS(rc))
1636 {
1637 RTStrPrintf(pFile->szName, RT_ELEMENTS(pFile->szName), "%s", pszFile);
1638 pFile->hFile = NIL_RTFILE;
1639 pFile->fFlags = fFlags;
1640 pFile->pvData = NULL;
1641 pFile->cbData = 0;
1642 }
1643
1644 if (RT_FAILURE(rc))
1645 {
1646 RTMemFree(pFile);
1647 pFile = NULL;
1648 }
1649 else
1650 *ppFile = pFile;
1651
1652 return rc;
1653}
1654
1655/**
1656 * Destroys a formerly created audio file.
1657 *
1658 * @param pFile Audio file (object) to destroy.
1659 */
1660void DrvAudioHlpFileDestroy(PPDMAUDIOFILE pFile)
1661{
1662 if (!pFile)
1663 return;
1664
1665 DrvAudioHlpFileClose(pFile);
1666
1667 RTMemFree(pFile);
1668 pFile = NULL;
1669}
1670
1671/**
1672 * Opens or creates an audio file.
1673 *
1674 * @returns IPRT status code.
1675 * @param pFile Pointer to audio file handle to use.
1676 * @param fOpen Open flags.
1677 * Use PDMAUDIOFILE_DEFAULT_OPEN_FLAGS for the default open flags.
1678 * @param pProps PCM properties to use.
1679 */
1680int DrvAudioHlpFileOpen(PPDMAUDIOFILE pFile, uint32_t fOpen, PCPDMAUDIOPCMPROPS pProps)
1681{
1682 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1683 /** @todo Validate fOpen flags. */
1684 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
1685
1686 int rc;
1687
1688 if (pFile->enmType == PDMAUDIOFILETYPE_RAW)
1689 {
1690 rc = RTFileOpen(&pFile->hFile, pFile->szName, fOpen);
1691 }
1692 else if (pFile->enmType == PDMAUDIOFILETYPE_WAV)
1693 {
1694 Assert(pProps->cChannels);
1695 Assert(pProps->uHz);
1696 Assert(pProps->cbSample);
1697
1698 pFile->pvData = (PAUDIOWAVFILEDATA)RTMemAllocZ(sizeof(AUDIOWAVFILEDATA));
1699 if (pFile->pvData)
1700 {
1701 pFile->cbData = sizeof(PAUDIOWAVFILEDATA);
1702
1703 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1704 AssertPtr(pData);
1705
1706 /* Header. */
1707 pData->Hdr.u32RIFF = AUDIO_MAKE_FOURCC('R','I','F','F');
1708 pData->Hdr.u32Size = 36;
1709 pData->Hdr.u32WAVE = AUDIO_MAKE_FOURCC('W','A','V','E');
1710
1711 pData->Hdr.u32Fmt = AUDIO_MAKE_FOURCC('f','m','t',' ');
1712 pData->Hdr.u32Size1 = 16; /* Means PCM. */
1713 pData->Hdr.u16AudioFormat = 1; /* PCM, linear quantization. */
1714 pData->Hdr.u16NumChannels = pProps->cChannels;
1715 pData->Hdr.u32SampleRate = pProps->uHz;
1716 pData->Hdr.u32ByteRate = PDMAudioPropsGetBitrate(pProps) / 8;
1717 pData->Hdr.u16BlockAlign = pProps->cChannels * pProps->cbSample;
1718 pData->Hdr.u16BitsPerSample = pProps->cbSample * 8;
1719
1720 /* Data chunk. */
1721 pData->Hdr.u32ID2 = AUDIO_MAKE_FOURCC('d','a','t','a');
1722 pData->Hdr.u32Size2 = 0;
1723
1724 rc = RTFileOpen(&pFile->hFile, pFile->szName, fOpen);
1725 if (RT_SUCCESS(rc))
1726 {
1727 rc = RTFileWrite(pFile->hFile, &pData->Hdr, sizeof(pData->Hdr), NULL);
1728 if (RT_FAILURE(rc))
1729 {
1730 RTFileClose(pFile->hFile);
1731 pFile->hFile = NIL_RTFILE;
1732 }
1733 }
1734
1735 if (RT_FAILURE(rc))
1736 {
1737 RTMemFree(pFile->pvData);
1738 pFile->pvData = NULL;
1739 pFile->cbData = 0;
1740 }
1741 }
1742 else
1743 rc = VERR_NO_MEMORY;
1744 }
1745 else
1746 rc = VERR_INVALID_PARAMETER;
1747
1748 if (RT_SUCCESS(rc))
1749 {
1750 LogRel2(("Audio: Opened file '%s'\n", pFile->szName));
1751 }
1752 else
1753 LogRel(("Audio: Failed opening file '%s', rc=%Rrc\n", pFile->szName, rc));
1754
1755 return rc;
1756}
1757
1758/**
1759 * Closes an audio file.
1760 *
1761 * @returns IPRT status code.
1762 * @param pFile Audio file handle to close.
1763 */
1764int DrvAudioHlpFileClose(PPDMAUDIOFILE pFile)
1765{
1766 if (!pFile)
1767 return VINF_SUCCESS;
1768
1769 size_t cbSize = DrvAudioHlpFileGetDataSize(pFile);
1770
1771 int rc = VINF_SUCCESS;
1772
1773 if (pFile->enmType == PDMAUDIOFILETYPE_RAW)
1774 {
1775 if (RTFileIsValid(pFile->hFile))
1776 rc = RTFileClose(pFile->hFile);
1777 }
1778 else if (pFile->enmType == PDMAUDIOFILETYPE_WAV)
1779 {
1780 if (RTFileIsValid(pFile->hFile))
1781 {
1782 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1783 if (pData) /* The .WAV file data only is valid when a file actually has been created. */
1784 {
1785 /* Update the header with the current data size. */
1786 RTFileWriteAt(pFile->hFile, 0, &pData->Hdr, sizeof(pData->Hdr), NULL);
1787 }
1788
1789 rc = RTFileClose(pFile->hFile);
1790 }
1791
1792 if (pFile->pvData)
1793 {
1794 RTMemFree(pFile->pvData);
1795 pFile->pvData = NULL;
1796 }
1797 }
1798 else
1799 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1800
1801 if ( RT_SUCCESS(rc)
1802 && !cbSize
1803 && !(pFile->fFlags & PDMAUDIOFILE_FLAGS_KEEP_IF_EMPTY))
1804 {
1805 rc = DrvAudioHlpFileDelete(pFile);
1806 }
1807
1808 pFile->cbData = 0;
1809
1810 if (RT_SUCCESS(rc))
1811 {
1812 pFile->hFile = NIL_RTFILE;
1813 LogRel2(("Audio: Closed file '%s' (%zu bytes)\n", pFile->szName, cbSize));
1814 }
1815 else
1816 LogRel(("Audio: Failed closing file '%s', rc=%Rrc\n", pFile->szName, rc));
1817
1818 return rc;
1819}
1820
1821/**
1822 * Deletes an audio file.
1823 *
1824 * @returns IPRT status code.
1825 * @param pFile Audio file handle to delete.
1826 */
1827int DrvAudioHlpFileDelete(PPDMAUDIOFILE pFile)
1828{
1829 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1830
1831 int rc = RTFileDelete(pFile->szName);
1832 if (RT_SUCCESS(rc))
1833 {
1834 LogRel2(("Audio: Deleted file '%s'\n", pFile->szName));
1835 }
1836 else if (rc == VERR_FILE_NOT_FOUND) /* Don't bitch if the file is not around (anymore). */
1837 rc = VINF_SUCCESS;
1838
1839 if (RT_FAILURE(rc))
1840 LogRel(("Audio: Failed deleting file '%s', rc=%Rrc\n", pFile->szName, rc));
1841
1842 return rc;
1843}
1844
1845/**
1846 * Returns the raw audio data size of an audio file.
1847 *
1848 * Note: This does *not* include file headers and other data which does
1849 * not belong to the actual PCM audio data.
1850 *
1851 * @returns Size (in bytes) of the raw PCM audio data.
1852 * @param pFile Audio file handle to retrieve the audio data size for.
1853 */
1854size_t DrvAudioHlpFileGetDataSize(PPDMAUDIOFILE pFile)
1855{
1856 AssertPtrReturn(pFile, 0);
1857
1858 size_t cbSize = 0;
1859
1860 if (pFile->enmType == PDMAUDIOFILETYPE_RAW)
1861 {
1862 cbSize = RTFileTell(pFile->hFile);
1863 }
1864 else if (pFile->enmType == PDMAUDIOFILETYPE_WAV)
1865 {
1866 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1867 if (pData) /* The .WAV file data only is valid when a file actually has been created. */
1868 cbSize = pData->Hdr.u32Size2;
1869 }
1870
1871 return cbSize;
1872}
1873
1874/**
1875 * Returns whether the given audio file is open and in use or not.
1876 *
1877 * @return bool True if open, false if not.
1878 * @param pFile Audio file handle to check open status for.
1879 */
1880bool DrvAudioHlpFileIsOpen(PPDMAUDIOFILE pFile)
1881{
1882 if (!pFile)
1883 return false;
1884
1885 return RTFileIsValid(pFile->hFile);
1886}
1887
1888/**
1889 * Write PCM data to a wave (.WAV) file.
1890 *
1891 * @returns IPRT status code.
1892 * @param pFile Audio file handle to write PCM data to.
1893 * @param pvBuf Audio data to write.
1894 * @param cbBuf Size (in bytes) of audio data to write.
1895 * @param fFlags Additional write flags. Not being used at the moment and must be 0.
1896 */
1897int DrvAudioHlpFileWrite(PPDMAUDIOFILE pFile, const void *pvBuf, size_t cbBuf, uint32_t fFlags)
1898{
1899 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1900 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1901
1902 AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); /** @todo fFlags are currently not implemented. */
1903
1904 if (!cbBuf)
1905 return VINF_SUCCESS;
1906
1907 AssertReturn(RTFileIsValid(pFile->hFile), VERR_WRONG_ORDER);
1908
1909 int rc;
1910
1911 if (pFile->enmType == PDMAUDIOFILETYPE_RAW)
1912 {
1913 rc = RTFileWrite(pFile->hFile, pvBuf, cbBuf, NULL);
1914 }
1915 else if (pFile->enmType == PDMAUDIOFILETYPE_WAV)
1916 {
1917 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1918 AssertPtr(pData);
1919
1920 rc = RTFileWrite(pFile->hFile, pvBuf, cbBuf, NULL);
1921 if (RT_SUCCESS(rc))
1922 {
1923 pData->Hdr.u32Size += (uint32_t)cbBuf;
1924 pData->Hdr.u32Size2 += (uint32_t)cbBuf;
1925 }
1926 }
1927 else
1928 rc = VERR_NOT_SUPPORTED;
1929
1930 return rc;
1931}
1932
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