VirtualBox

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

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