VirtualBox

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

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

Audio: nits. bugref:9890

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