VirtualBox

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

Last change on this file since 73770 was 73701, checked in by vboxsync, 7 years ago

Audio/DrvAudioCommon.cpp: The default is Little Endian in DrvAudioHlpClearBuf().

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

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