VirtualBox

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

Last change on this file since 65469 was 65056, checked in by vboxsync, 8 years ago

Devices/Audio: doxygen fixes (s/@true/false / @c true/false/)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.8 KB
Line 
1/* $Id: DrvAudioCommon.cpp 65056 2017-01-03 10:16:46Z vboxsync $ */
2/** @file
3 * Intermedia audio driver, common routines. These are also used
4 * in the drivers which are bound to Main, e.g. the VRDE or the
5 * video audio recording drivers.
6 */
7
8/*
9 * Copyright (C) 2006-2016 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 * --------------------------------------------------------------------
19 *
20 * This code is based on: audio_template.h from QEMU AUDIO subsystem.
21 *
22 * QEMU Audio subsystem header
23 *
24 * Copyright (c) 2005 Vassili Karpov (malc)
25 *
26 * Permission is hereby granted, free of charge, to any person obtaining a copy
27 * of this software and associated documentation files (the "Software"), to deal
28 * in the Software without restriction, including without limitation the rights
29 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
30 * copies of the Software, and to permit persons to whom the Software is
31 * furnished to do so, subject to the following conditions:
32 *
33 * The above copyright notice and this permission notice shall be included in
34 * all copies or substantial portions of the Software.
35 *
36 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
39 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
40 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
41 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
42 * THE SOFTWARE.
43 */
44#include <iprt/alloc.h>
45#include <iprt/asm-math.h>
46#include <iprt/assert.h>
47#include <iprt/dir.h>
48#include <iprt/file.h>
49#include <iprt/string.h>
50#include <iprt/uuid.h>
51
52#define LOG_GROUP LOG_GROUP_DRV_AUDIO
53#include <VBox/log.h>
54
55#include <VBox/err.h>
56#include <VBox/vmm/pdmdev.h>
57#include <VBox/vmm/pdm.h>
58#include <VBox/vmm/mm.h>
59
60#include <ctype.h>
61#include <stdlib.h>
62
63#include "DrvAudio.h"
64#include "AudioMixBuffer.h"
65
66#pragma pack(1)
67/**
68 * Structure for building up a .WAV file header.
69 */
70typedef struct AUDIOWAVFILEHDR
71{
72 uint32_t u32RIFF;
73 uint32_t u32Size;
74 uint32_t u32WAVE;
75
76 uint32_t u32Fmt;
77 uint32_t u32Size1;
78 uint16_t u16AudioFormat;
79 uint16_t u16NumChannels;
80 uint32_t u32SampleRate;
81 uint32_t u32ByteRate;
82 uint16_t u16BlockAlign;
83 uint16_t u16BitsPerSample;
84
85 uint32_t u32ID2;
86 uint32_t u32Size2;
87} AUDIOWAVFILEHDR, *PAUDIOWAVFILEHDR;
88#pragma pack()
89
90/**
91 * Structure for keeeping the internal .WAV file data
92 */
93typedef struct AUDIOWAVFILEDATA
94{
95 /** The file header/footer. */
96 AUDIOWAVFILEHDR Hdr;
97} AUDIOWAVFILEDATA, *PAUDIOWAVFILEDATA;
98
99/**
100 * Retrieves the matching PDMAUDIOFMT for given bits + signing flag.
101 *
102 * @return IPRT status code.
103 * @return PDMAUDIOFMT Resulting audio format or PDMAUDIOFMT_INVALID if invalid.
104 * @param cBits Bits to retrieve audio format for.
105 * @param fSigned Signed flag for bits to retrieve audio format for.
106 */
107PDMAUDIOFMT DrvAudioAudFmtBitsToAudFmt(uint8_t cBits, bool fSigned)
108{
109 if (fSigned)
110 {
111 switch (cBits)
112 {
113 case 8: return PDMAUDIOFMT_S8;
114 case 16: return PDMAUDIOFMT_S16;
115 case 32: return PDMAUDIOFMT_S32;
116 default: break;
117 }
118 }
119 else
120 {
121 switch (cBits)
122 {
123 case 8: return PDMAUDIOFMT_U8;
124 case 16: return PDMAUDIOFMT_U16;
125 case 32: return PDMAUDIOFMT_U32;
126 default: break;
127 }
128 }
129
130 AssertMsgFailed(("Bogus audio bits %RU8\n", cBits));
131 return PDMAUDIOFMT_INVALID;
132}
133
134/**
135 * Clears a sample buffer by the given amount of audio samples.
136 *
137 * @return IPRT status code.
138 * @param pPCMProps PCM properties to use for the buffer to clear.
139 * @param pvBuf Buffer to clear.
140 * @param cbBuf Size (in bytes) of the buffer.
141 * @param cSamples Number of audio samples to clear in the buffer.
142 */
143void DrvAudioHlpClearBuf(PPDMAUDIOPCMPROPS pPCMProps, void *pvBuf, size_t cbBuf, uint32_t cSamples)
144{
145 AssertPtrReturnVoid(pPCMProps);
146 AssertPtrReturnVoid(pvBuf);
147
148 if (!cbBuf || !cSamples)
149 return;
150
151 Assert(pPCMProps->cBits);
152 size_t cbToClear = cSamples * (pPCMProps->cBits / 8 /* Bytes */);
153 Assert(cbBuf >= cbToClear);
154
155 if (cbBuf < cbToClear)
156 cbToClear = cbBuf;
157
158 Log2Func(("pPCMInfo=%p, pvBuf=%p, cSamples=%RU32, fSigned=%RTbool, cBits=%RU8, cShift=%RU8\n",
159 pPCMProps, pvBuf, cSamples, pPCMProps->fSigned, pPCMProps->cBits, pPCMProps->cShift));
160
161 if (pPCMProps->fSigned)
162 {
163 RT_BZERO(pvBuf, cbToClear);
164 }
165 else
166 {
167 switch (pPCMProps->cBits)
168 {
169 case 8:
170 {
171 memset(pvBuf, 0x80, cbToClear);
172 break;
173 }
174
175 case 16:
176 {
177 uint16_t *p = (uint16_t *)pvBuf;
178 int16_t s = INT16_MAX;
179
180 if (pPCMProps->fSwapEndian)
181 s = RT_BSWAP_U16(s);
182
183 for (uint32_t i = 0; i < cSamples; i++)
184 p[i] = s;
185
186 break;
187 }
188
189 case 32:
190 {
191 uint32_t *p = (uint32_t *)pvBuf;
192 int32_t s = INT32_MAX;
193
194 if (pPCMProps->fSwapEndian)
195 s = RT_BSWAP_U32(s);
196
197 for (uint32_t i = 0; i < cSamples; i++)
198 p[i] = s;
199
200 break;
201 }
202
203 default:
204 {
205 AssertMsgFailed(("Invalid bits: %RU8\n", pPCMProps->cBits));
206 break;
207 }
208 }
209 }
210}
211
212/**
213 * Allocates an audio device.
214 *
215 * @returns Newly allocated audio device, or NULL if failed.
216 * @param cbData How much additional data (in bytes) should be allocated to provide
217 * a (backend) specific area to store additional data.
218 * Optional, can be 0.
219 */
220PPDMAUDIODEVICE DrvAudioHlpDeviceAlloc(size_t cbData)
221{
222 PPDMAUDIODEVICE pDev = (PPDMAUDIODEVICE)RTMemAllocZ(sizeof(PDMAUDIODEVICE));
223 if (!pDev)
224 return NULL;
225
226 if (cbData)
227 {
228 pDev->pvData = RTMemAllocZ(cbData);
229 if (!pDev->pvData)
230 {
231 RTMemFree(pDev);
232 return NULL;
233 }
234 }
235
236 pDev->cbData = cbData;
237
238 pDev->cMaxInputChannels = 0;
239 pDev->cMaxOutputChannels = 0;
240
241 return pDev;
242}
243
244/**
245 * Frees an audio device.
246 *
247 * @param pDev Device to free.
248 */
249void DrvAudioHlpDeviceFree(PPDMAUDIODEVICE pDev)
250{
251 if (!pDev)
252 return;
253
254 Assert(pDev->cRefCount == 0);
255
256 if (pDev->pvData)
257 {
258 Assert(pDev->cbData);
259
260 RTMemFree(pDev->pvData);
261 pDev->pvData = NULL;
262 }
263
264 RTMemFree(pDev);
265 pDev = NULL;
266}
267
268/**
269 * Duplicates an audio device entry.
270 *
271 * @returns Duplicated audio device entry on success, or NULL on failure.
272 * @param pDev Audio device entry to duplicate.
273 * @param fCopyUserData Whether to also copy the user data portion or not.
274 */
275PPDMAUDIODEVICE DrvAudioHlpDeviceDup(PPDMAUDIODEVICE pDev, bool fCopyUserData)
276{
277 AssertPtrReturn(pDev, NULL);
278
279 PPDMAUDIODEVICE pDevDup = DrvAudioHlpDeviceAlloc(fCopyUserData ? pDev->cbData : 0);
280 if (pDevDup)
281 {
282 memcpy(pDevDup, pDev, sizeof(PDMAUDIODEVICE));
283
284 if ( fCopyUserData
285 && pDevDup->cbData)
286 {
287 memcpy(pDevDup->pvData, pDev->pvData, pDevDup->cbData);
288 }
289 else
290 {
291 pDevDup->cbData = 0;
292 pDevDup->pvData = NULL;
293 }
294 }
295
296 return pDevDup;
297}
298
299/**
300 * Initializes an audio device enumeration structure.
301 *
302 * @returns IPRT status code.
303 * @param pDevEnm Device enumeration to initialize.
304 */
305int DrvAudioHlpDeviceEnumInit(PPDMAUDIODEVICEENUM pDevEnm)
306{
307 AssertPtrReturn(pDevEnm, VERR_INVALID_POINTER);
308
309 RTListInit(&pDevEnm->lstDevices);
310 pDevEnm->cDevices = 0;
311
312 return VINF_SUCCESS;
313}
314
315/**
316 * Frees audio device enumeration data.
317 *
318 * @param pDevEnm Device enumeration to destroy.
319 */
320void DrvAudioHlpDeviceEnumFree(PPDMAUDIODEVICEENUM pDevEnm)
321{
322 if (!pDevEnm)
323 return;
324
325 PPDMAUDIODEVICE pDev, pDevNext;
326 RTListForEachSafe(&pDevEnm->lstDevices, pDev, pDevNext, PDMAUDIODEVICE, Node)
327 {
328 RTListNodeRemove(&pDev->Node);
329
330 DrvAudioHlpDeviceFree(pDev);
331
332 pDevEnm->cDevices--;
333 }
334
335 /* Sanity. */
336 Assert(RTListIsEmpty(&pDevEnm->lstDevices));
337 Assert(pDevEnm->cDevices == 0);
338}
339
340/**
341 * Adds an audio device to a device enumeration.
342 *
343 * @return IPRT status code.
344 * @param pDevEnm Device enumeration to add device to.
345 * @param pDev Device to add. The pointer will be owned by the device enumeration then.
346 */
347int DrvAudioHlpDeviceEnumAdd(PPDMAUDIODEVICEENUM pDevEnm, PPDMAUDIODEVICE pDev)
348{
349 AssertPtrReturn(pDevEnm, VERR_INVALID_POINTER);
350 AssertPtrReturn(pDev, VERR_INVALID_POINTER);
351
352 RTListAppend(&pDevEnm->lstDevices, &pDev->Node);
353 pDevEnm->cDevices++;
354
355 return VINF_SUCCESS;
356}
357
358/**
359 * Duplicates a device enumeration.
360 *
361 * @returns Duplicated device enumeration, or NULL on failure.
362 * Must be free'd with DrvAudioHlpDeviceEnumFree().
363 * @param pDevEnm Device enumeration to duplicate.
364 */
365PPDMAUDIODEVICEENUM DrvAudioHlpDeviceEnumDup(PPDMAUDIODEVICEENUM pDevEnm)
366{
367 AssertPtrReturn(pDevEnm, NULL);
368
369 PPDMAUDIODEVICEENUM pDevEnmDup = (PPDMAUDIODEVICEENUM)RTMemAlloc(sizeof(PDMAUDIODEVICEENUM));
370 if (!pDevEnmDup)
371 return NULL;
372
373 int rc2 = DrvAudioHlpDeviceEnumInit(pDevEnmDup);
374 AssertRC(rc2);
375
376 PPDMAUDIODEVICE pDev;
377 RTListForEach(&pDevEnm->lstDevices, pDev, PDMAUDIODEVICE, Node)
378 {
379 PPDMAUDIODEVICE pDevDup = DrvAudioHlpDeviceDup(pDev, true /* fCopyUserData */);
380 if (!pDevDup)
381 {
382 rc2 = VERR_NO_MEMORY;
383 break;
384 }
385
386 rc2 = DrvAudioHlpDeviceEnumAdd(pDevEnmDup, pDevDup);
387 if (RT_FAILURE(rc2))
388 {
389 DrvAudioHlpDeviceFree(pDevDup);
390 break;
391 }
392 }
393
394 if (RT_FAILURE(rc2))
395 {
396 DrvAudioHlpDeviceEnumFree(pDevEnmDup);
397 pDevEnmDup = NULL;
398 }
399
400 return pDevEnmDup;
401}
402
403/**
404 * Copies device enumeration entries from the source to the destination enumeration.
405 *
406 * @returns IPRT status code.
407 * @param pDstDevEnm Destination enumeration to store enumeration entries into.
408 * @param pSrcDevEnm Source enumeration to use.
409 * @param enmUsage Which entries to copy. Specify PDMAUDIODIR_ANY to copy all entries.
410 * @param fCopyUserData Whether to also copy the user data portion or not.
411 */
412int DrvAudioHlpDeviceEnumCopyEx(PPDMAUDIODEVICEENUM pDstDevEnm, PPDMAUDIODEVICEENUM pSrcDevEnm,
413 PDMAUDIODIR enmUsage, bool fCopyUserData)
414{
415 AssertPtrReturn(pDstDevEnm, VERR_INVALID_POINTER);
416 AssertPtrReturn(pSrcDevEnm, VERR_INVALID_POINTER);
417
418 int rc = VINF_SUCCESS;
419
420 PPDMAUDIODEVICE pSrcDev;
421 RTListForEach(&pSrcDevEnm->lstDevices, pSrcDev, PDMAUDIODEVICE, Node)
422 {
423 if ( enmUsage != PDMAUDIODIR_ANY
424 && enmUsage != pSrcDev->enmUsage)
425 {
426 continue;
427 }
428
429 PPDMAUDIODEVICE pDstDev = DrvAudioHlpDeviceDup(pSrcDev, fCopyUserData);
430 if (!pDstDev)
431 {
432 rc = VERR_NO_MEMORY;
433 break;
434 }
435
436 rc = DrvAudioHlpDeviceEnumAdd(pDstDevEnm, pDstDev);
437 if (RT_FAILURE(rc))
438 break;
439 }
440
441 return rc;
442}
443
444/**
445 * Copies all device enumeration entries from the source to the destination enumeration.
446 *
447 * Note: Does *not* copy the user-specific data assigned to a device enumeration entry.
448 * To do so, use DrvAudioHlpDeviceEnumCopyEx().
449 *
450 * @returns IPRT status code.
451 * @param pDstDevEnm Destination enumeration to store enumeration entries into.
452 * @param pSrcDevEnm Source enumeration to use.
453 */
454int DrvAudioHlpDeviceEnumCopy(PPDMAUDIODEVICEENUM pDstDevEnm, PPDMAUDIODEVICEENUM pSrcDevEnm)
455{
456 return DrvAudioHlpDeviceEnumCopyEx(pDstDevEnm, pSrcDevEnm, PDMAUDIODIR_ANY, false /* fCopyUserData */);
457}
458
459/**
460 * Returns the default device of a given device enumeration.
461 * This assumes that only one default device per usage is set.
462 *
463 * @returns Default device if found, or NULL if none found.
464 * @param pDevEnm Device enumeration to get default device for.
465 * @param enmUsage Usage to get default device for.
466 */
467PPDMAUDIODEVICE DrvAudioHlpDeviceEnumGetDefaultDevice(PPDMAUDIODEVICEENUM pDevEnm, PDMAUDIODIR enmUsage)
468{
469 AssertPtrReturn(pDevEnm, NULL);
470
471 PPDMAUDIODEVICE pDev;
472 RTListForEach(&pDevEnm->lstDevices, pDev, PDMAUDIODEVICE, Node)
473 {
474 if (enmUsage != PDMAUDIODIR_ANY)
475 {
476 if (enmUsage != pDev->enmUsage) /* Wrong usage? Skip. */
477 continue;
478 }
479
480 if (pDev->fFlags & PDMAUDIODEV_FLAGS_DEFAULT)
481 return pDev;
482 }
483
484 return NULL;
485}
486
487/**
488 * Logs an audio device enumeration.
489 *
490 * @param pszDesc Logging description.
491 * @param pDevEnm Device enumeration to log.
492 */
493void DrvAudioHlpDeviceEnumPrint(const char *pszDesc, PPDMAUDIODEVICEENUM pDevEnm)
494{
495 AssertPtrReturnVoid(pszDesc);
496 AssertPtrReturnVoid(pDevEnm);
497
498 LogFunc(("%s: %RU16 devices\n", pszDesc, pDevEnm->cDevices));
499
500 PPDMAUDIODEVICE pDev;
501 RTListForEach(&pDevEnm->lstDevices, pDev, PDMAUDIODEVICE, Node)
502 {
503 char *pszFlags = DrvAudioHlpAudDevFlagsToStrA(pDev->fFlags);
504
505 LogFunc(("Device '%s':\n", pDev->szName));
506 LogFunc(("\tUsage = %s\n", DrvAudioHlpAudDirToStr(pDev->enmUsage)));
507 LogFunc(("\tFlags = %s\n", pszFlags ? pszFlags : "<NONE>"));
508 LogFunc(("\tInput channels = %RU8\n", pDev->cMaxInputChannels));
509 LogFunc(("\tOutput channels = %RU8\n", pDev->cMaxOutputChannels));
510 LogFunc(("\tData = %p (%zu bytes)\n", pDev->pvData, pDev->cbData));
511
512 if (pszFlags)
513 RTStrFree(pszFlags);
514 }
515}
516
517/**
518 * Converts an audio direction to a string.
519 *
520 * @returns Stringified audio direction, or "Unknown", if not found.
521 * @param enmDir Audio direction to convert.
522 */
523const char *DrvAudioHlpAudDirToStr(PDMAUDIODIR enmDir)
524{
525 switch (enmDir)
526 {
527 case PDMAUDIODIR_UNKNOWN: return "Unknown";
528 case PDMAUDIODIR_IN: return "Input";
529 case PDMAUDIODIR_OUT: return "Output";
530 case PDMAUDIODIR_ANY: return "Duplex";
531 default: break;
532 }
533
534 AssertMsgFailed(("Invalid audio direction %ld\n", enmDir));
535 return "Unknown";
536}
537
538/**
539 * Converts an audio mixer control to a string.
540 *
541 * @returns Stringified audio mixer control or "Unknown", if not found.
542 * @param enmMixerCtl Audio mixer control to convert.
543 */
544const char *DrvAudioHlpAudMixerCtlToStr(PDMAUDIOMIXERCTL enmMixerCtl)
545{
546 switch (enmMixerCtl)
547 {
548 case PDMAUDIOMIXERCTL_VOLUME_MASTER: return "Master Volume";
549 case PDMAUDIOMIXERCTL_FRONT: return "Front";
550 case PDMAUDIOMIXERCTL_CENTER_LFE: return "Center / LFE";
551 case PDMAUDIOMIXERCTL_REAR: return "Rear";
552 case PDMAUDIOMIXERCTL_LINE_IN: return "Line-In";
553 case PDMAUDIOMIXERCTL_MIC_IN: return "Microphone-In";
554 default: break;
555 }
556
557 AssertMsgFailed(("Invalid mixer control %ld\n", enmMixerCtl));
558 return "Unknown";
559}
560
561/**
562 * Converts an audio device flags to a string.
563 *
564 * @returns Stringified audio flags. Must be free'd with RTStrFree().
565 * NULL if no flags set.
566 * @param fFlags Audio flags to convert.
567 */
568char *DrvAudioHlpAudDevFlagsToStrA(PDMAUDIODEVFLAG fFlags)
569{
570#define APPEND_FLAG_TO_STR(_aFlag) \
571 if (fFlags & PDMAUDIODEV_FLAGS_##_aFlag) \
572 { \
573 if (pszFlags) \
574 { \
575 rc2 = RTStrAAppend(&pszFlags, " "); \
576 if (RT_FAILURE(rc2)) \
577 break; \
578 } \
579 \
580 rc2 = RTStrAAppend(&pszFlags, #_aFlag); \
581 if (RT_FAILURE(rc2)) \
582 break; \
583 } \
584
585 char *pszFlags = NULL;
586 int rc2 = VINF_SUCCESS;
587
588 do
589 {
590 APPEND_FLAG_TO_STR(DEFAULT);
591 APPEND_FLAG_TO_STR(HOTPLUG);
592 APPEND_FLAG_TO_STR(BUGGY);
593 APPEND_FLAG_TO_STR(IGNORE);
594 APPEND_FLAG_TO_STR(LOCKED);
595 APPEND_FLAG_TO_STR(DEAD);
596
597 } while (0);
598
599 if (!pszFlags)
600 rc2 = RTStrAAppend(&pszFlags, "NONE");
601
602 if ( RT_FAILURE(rc2)
603 && pszFlags)
604 {
605 RTStrFree(pszFlags);
606 pszFlags = NULL;
607 }
608
609#undef APPEND_FLAG_TO_STR
610
611 return pszFlags;
612}
613
614/**
615 * Converts a recording source enumeration to a string.
616 *
617 * @returns Stringified recording source, or "Unknown", if not found.
618 * @param enmRecSrc Recording source to convert.
619 */
620const char *DrvAudioHlpRecSrcToStr(PDMAUDIORECSOURCE enmRecSrc)
621{
622 switch (enmRecSrc)
623 {
624 case PDMAUDIORECSOURCE_UNKNOWN: return "Unknown";
625 case PDMAUDIORECSOURCE_MIC: return "Microphone In";
626 case PDMAUDIORECSOURCE_CD: return "CD";
627 case PDMAUDIORECSOURCE_VIDEO: return "Video";
628 case PDMAUDIORECSOURCE_AUX: return "AUX";
629 case PDMAUDIORECSOURCE_LINE: return "Line In";
630 case PDMAUDIORECSOURCE_PHONE: return "Phone";
631 default:
632 break;
633 }
634
635 AssertMsgFailed(("Invalid recording source %ld\n", enmRecSrc));
636 return "Unknown";
637}
638
639/**
640 * Returns wether the given audio format has signed bits or not.
641 *
642 * @return IPRT status code.
643 * @return bool @c true for signed bits, @c false for unsigned.
644 * @param enmFmt Audio format to retrieve value for.
645 */
646bool DrvAudioHlpAudFmtIsSigned(PDMAUDIOFMT enmFmt)
647{
648 switch (enmFmt)
649 {
650 case PDMAUDIOFMT_S8:
651 case PDMAUDIOFMT_S16:
652 case PDMAUDIOFMT_S32:
653 return true;
654
655 case PDMAUDIOFMT_U8:
656 case PDMAUDIOFMT_U16:
657 case PDMAUDIOFMT_U32:
658 return false;
659
660 default:
661 break;
662 }
663
664 AssertMsgFailed(("Bogus audio format %ld\n", enmFmt));
665 return false;
666}
667
668/**
669 * Returns the bits of a given audio format.
670 *
671 * @return IPRT status code.
672 * @return uint8_t Bits of audio format.
673 * @param enmFmt Audio format to retrieve value for.
674 */
675uint8_t DrvAudioHlpAudFmtToBits(PDMAUDIOFMT enmFmt)
676{
677 switch (enmFmt)
678 {
679 case PDMAUDIOFMT_S8:
680 case PDMAUDIOFMT_U8:
681 return 8;
682
683 case PDMAUDIOFMT_U16:
684 case PDMAUDIOFMT_S16:
685 return 16;
686
687 case PDMAUDIOFMT_U32:
688 case PDMAUDIOFMT_S32:
689 return 32;
690
691 default:
692 break;
693 }
694
695 AssertMsgFailed(("Bogus audio format %ld\n", enmFmt));
696 return 0;
697}
698
699/**
700 * Converts an audio format to a string.
701 *
702 * @returns Stringified audio format, or "Unknown", if not found.
703 * @param enmFmt Audio format to convert.
704 */
705const char *DrvAudioHlpAudFmtToStr(PDMAUDIOFMT enmFmt)
706{
707 switch (enmFmt)
708 {
709 case PDMAUDIOFMT_U8:
710 return "U8";
711
712 case PDMAUDIOFMT_U16:
713 return "U16";
714
715 case PDMAUDIOFMT_U32:
716 return "U32";
717
718 case PDMAUDIOFMT_S8:
719 return "S8";
720
721 case PDMAUDIOFMT_S16:
722 return "S16";
723
724 case PDMAUDIOFMT_S32:
725 return "S32";
726
727 default:
728 break;
729 }
730
731 AssertMsgFailed(("Bogus audio format %ld\n", enmFmt));
732 return "Unknown";
733}
734
735/**
736 * Converts a given string to an audio format.
737 *
738 * @returns Audio format for the given string, or PDMAUDIOFMT_INVALID if not found.
739 * @param pszFmt String to convert to an audio format.
740 */
741PDMAUDIOFMT DrvAudioHlpStrToAudFmt(const char *pszFmt)
742{
743 AssertPtrReturn(pszFmt, PDMAUDIOFMT_INVALID);
744
745 if (!RTStrICmp(pszFmt, "u8"))
746 return PDMAUDIOFMT_U8;
747 else if (!RTStrICmp(pszFmt, "u16"))
748 return PDMAUDIOFMT_U16;
749 else if (!RTStrICmp(pszFmt, "u32"))
750 return PDMAUDIOFMT_U32;
751 else if (!RTStrICmp(pszFmt, "s8"))
752 return PDMAUDIOFMT_S8;
753 else if (!RTStrICmp(pszFmt, "s16"))
754 return PDMAUDIOFMT_S16;
755 else if (!RTStrICmp(pszFmt, "s32"))
756 return PDMAUDIOFMT_S32;
757
758 AssertMsgFailed(("Invalid audio format '%s'\n", pszFmt));
759 return PDMAUDIOFMT_INVALID;
760}
761
762/**
763 * Checks whether the given PCM properties are equal with the given
764 * stream configuration.
765 *
766 * @returns @c true if equal, @c false if not.
767 * @param pProps PCM properties to compare.
768 * @param pCfg Stream configuration to compare.
769 */
770bool DrvAudioHlpPCMPropsAreEqual(PPDMAUDIOPCMPROPS pProps, PPDMAUDIOSTREAMCFG pCfg)
771{
772 AssertPtrReturn(pProps, false);
773 AssertPtrReturn(pCfg, false);
774
775 int cBits = 8;
776 bool fSigned = false;
777
778 switch (pCfg->enmFormat)
779 {
780 case PDMAUDIOFMT_S8:
781 fSigned = true;
782 case PDMAUDIOFMT_U8:
783 break;
784
785 case PDMAUDIOFMT_S16:
786 fSigned = true;
787 case PDMAUDIOFMT_U16:
788 cBits = 16;
789 break;
790
791 case PDMAUDIOFMT_S32:
792 fSigned = true;
793 case PDMAUDIOFMT_U32:
794 cBits = 32;
795 break;
796
797 default:
798 AssertMsgFailed(("Unknown format %ld\n", pCfg->enmFormat));
799 break;
800 }
801
802 bool fEqual = pProps->uHz == pCfg->uHz
803 && pProps->cChannels == pCfg->cChannels
804 && pProps->fSigned == fSigned
805 && pProps->cBits == cBits
806 && pProps->fSwapEndian == !(pCfg->enmEndianness == PDMAUDIOHOSTENDIANNESS);
807 return fEqual;
808}
809
810/**
811 * Checks whether two given PCM properties are equal.
812 *
813 * @returns @c true if equal, @c false if not.
814 * @param pProps1 First properties to compare.
815 * @param pProps2 Second properties to compare.
816 */
817bool DrvAudioHlpPCMPropsAreEqual(PPDMAUDIOPCMPROPS pProps1, PPDMAUDIOPCMPROPS pProps2)
818{
819 AssertPtrReturn(pProps1, false);
820 AssertPtrReturn(pProps2, false);
821
822 if (pProps1 == pProps2) /* If the pointers match, take a shortcut. */
823 return true;
824
825 return pProps1->uHz == pProps2->uHz
826 && pProps1->cChannels == pProps2->cChannels
827 && pProps1->fSigned == pProps2->fSigned
828 && pProps1->cBits == pProps2->cBits
829 && pProps1->fSwapEndian == pProps2->fSwapEndian;
830}
831
832/**
833 * Converts PCM properties to a audio stream configuration.
834 *
835 * @return IPRT status code.
836 * @param pPCMProps Pointer to PCM properties to convert.
837 * @param pCfg Pointer to audio stream configuration to store result into.
838 */
839int DrvAudioHlpPCMPropsToStreamCfg(PPDMAUDIOPCMPROPS pPCMProps, PPDMAUDIOSTREAMCFG pCfg)
840{
841 AssertPtrReturn(pPCMProps, VERR_INVALID_POINTER);
842 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
843
844 pCfg->uHz = pPCMProps->uHz;
845 pCfg->cChannels = pPCMProps->cChannels;
846 pCfg->enmFormat = DrvAudioAudFmtBitsToAudFmt(pPCMProps->cBits, pPCMProps->fSigned);
847
848 /** @todo We assume little endian is the default for now. */
849 pCfg->enmEndianness = pPCMProps->fSwapEndian == false ? PDMAUDIOENDIANNESS_LITTLE : PDMAUDIOENDIANNESS_BIG;
850 return VINF_SUCCESS;
851}
852
853/**
854 * Checks whether a given stream configuration is valid or not.
855 *
856 * Returns @c true if configuration is valid, @c false if not.
857 * @param pCfg Stream configuration to check.
858 */
859bool DrvAudioHlpStreamCfgIsValid(PPDMAUDIOSTREAMCFG pCfg)
860{
861 bool fValid = ( pCfg->cChannels == 1
862 || pCfg->cChannels == 2); /* Either stereo (2) or mono (1), per stream. */
863
864 fValid |= ( pCfg->enmEndianness == PDMAUDIOENDIANNESS_LITTLE
865 || pCfg->enmEndianness == PDMAUDIOENDIANNESS_BIG);
866
867 fValid |= ( pCfg->enmDir == PDMAUDIODIR_IN
868 || pCfg->enmDir == PDMAUDIODIR_OUT);
869
870 if (fValid)
871 {
872 switch (pCfg->enmFormat)
873 {
874 case PDMAUDIOFMT_S8:
875 case PDMAUDIOFMT_U8:
876 case PDMAUDIOFMT_S16:
877 case PDMAUDIOFMT_U16:
878 case PDMAUDIOFMT_S32:
879 case PDMAUDIOFMT_U32:
880 break;
881 default:
882 fValid = false;
883 break;
884 }
885 }
886
887 fValid |= pCfg->uHz > 0;
888 /** @todo Check for defined frequencies supported. */
889
890 return fValid;
891}
892
893/**
894 * Converts an audio stream configuration to matching PCM properties.
895 *
896 * @return IPRT status code.
897 * @param pCfg Audio stream configuration to convert.
898 * @param pProps PCM properties to save result to.
899 */
900int DrvAudioHlpStreamCfgToProps(PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOPCMPROPS pProps)
901{
902 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
903 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
904
905 int rc = VINF_SUCCESS;
906
907 int cBits = 8, cShift = 0;
908 bool fSigned = false;
909
910 switch (pCfg->enmFormat)
911 {
912 case PDMAUDIOFMT_S8:
913 fSigned = true;
914 case PDMAUDIOFMT_U8:
915 break;
916
917 case PDMAUDIOFMT_S16:
918 fSigned = true;
919 case PDMAUDIOFMT_U16:
920 cBits = 16;
921 cShift = 1;
922 break;
923
924 case PDMAUDIOFMT_S32:
925 fSigned = true;
926 case PDMAUDIOFMT_U32:
927 cBits = 32;
928 cShift = 2;
929 break;
930
931 default:
932 AssertMsgFailed(("Unknown format %ld\n", pCfg->enmFormat));
933 rc = VERR_NOT_SUPPORTED;
934 break;
935 }
936
937 if (RT_SUCCESS(rc))
938 {
939 pProps->uHz = pCfg->uHz;
940 pProps->cBits = cBits;
941 pProps->fSigned = fSigned;
942 pProps->cShift = (pCfg->cChannels == 2) + cShift;
943 pProps->cChannels = pCfg->cChannels;
944 pProps->cbBitrate = DrvAudioHlpCalcBitrate(pProps->cBits, pProps->uHz, pProps->cChannels) / 8 /* Convert to bytes */;
945 pProps->uAlign = (1 << pProps->cShift) - 1;
946 pProps->fSwapEndian = pCfg->enmEndianness != PDMAUDIOHOSTENDIANNESS;
947 }
948
949 return rc;
950}
951
952/**
953 * Prints an audio stream configuration to the debug log.
954 *
955 * @param pCfg Stream configuration to log.
956 */
957void DrvAudioHlpStreamCfgPrint(PPDMAUDIOSTREAMCFG pCfg)
958{
959 AssertPtrReturnVoid(pCfg);
960
961 LogFlowFunc(("uHz=%RU32, cChannels=%RU8, enmFormat=", pCfg->uHz, pCfg->cChannels));
962
963 switch (pCfg->enmFormat)
964 {
965 case PDMAUDIOFMT_S8:
966 LogFlow(("S8"));
967 break;
968 case PDMAUDIOFMT_U8:
969 LogFlow(("U8"));
970 break;
971 case PDMAUDIOFMT_S16:
972 LogFlow(("S16"));
973 break;
974 case PDMAUDIOFMT_U16:
975 LogFlow(("U16"));
976 break;
977 case PDMAUDIOFMT_S32:
978 LogFlow(("S32"));
979 break;
980 case PDMAUDIOFMT_U32:
981 LogFlow(("U32"));
982 break;
983 default:
984 LogFlow(("invalid(%d)", pCfg->enmFormat));
985 break;
986 }
987
988 LogFlow((", endianness="));
989 switch (pCfg->enmEndianness)
990 {
991 case PDMAUDIOENDIANNESS_LITTLE:
992 LogFlow(("little\n"));
993 break;
994 case PDMAUDIOENDIANNESS_BIG:
995 LogFlow(("big\n"));
996 break;
997 default:
998 LogFlow(("invalid\n"));
999 break;
1000 }
1001}
1002
1003/**
1004 * Converts a stream command to a string.
1005 *
1006 * @returns Stringified stream command, or "Unknown", if not found.
1007 * @param enmCmd Stream command to convert.
1008 */
1009const char *DrvAudioHlpStreamCmdToStr(PDMAUDIOSTREAMCMD enmCmd)
1010{
1011 switch (enmCmd)
1012 {
1013 case PDMAUDIOSTREAMCMD_UNKNOWN: return "Unknown";
1014 case PDMAUDIOSTREAMCMD_ENABLE: return "Enable";
1015 case PDMAUDIOSTREAMCMD_DISABLE: return "Disable";
1016 case PDMAUDIOSTREAMCMD_PAUSE: return "Pause";
1017 case PDMAUDIOSTREAMCMD_RESUME: return "Resume";
1018 default: break;
1019 }
1020
1021 AssertMsgFailed(("Invalid stream command %ld\n", enmCmd));
1022 return "Unknown";
1023}
1024
1025/**
1026 * Calculates the audio bit rate of the given bits per sample, the Hz and the number
1027 * of audio channels.
1028 *
1029 * Divide the result by 8 to get the byte rate.
1030 *
1031 * @returns The calculated bit rate.
1032 * @param cBits Number of bits per sample.
1033 * @param uHz Hz (Hertz) rate.
1034 * @param cChannels Number of audio channels.
1035 */
1036uint32_t DrvAudioHlpCalcBitrate(uint8_t cBits, uint32_t uHz, uint8_t cChannels)
1037{
1038 return (cBits * uHz * cChannels);
1039}
1040
1041/**
1042 * Calculates the audio bit rate out of a given audio stream configuration.
1043 *
1044 * Divide the result by 8 to get the byte rate.
1045 *
1046 * @returns The calculated bit rate.
1047 * @param pCfg Audio stream configuration to calculate bit rate for.
1048 *
1049 * @remark
1050 */
1051uint32_t DrvAudioHlpCalcBitrate(PPDMAUDIOSTREAMCFG pCfg)
1052{
1053 return DrvAudioHlpCalcBitrate(DrvAudioHlpAudFmtToBits(pCfg->enmFormat), pCfg->uHz, pCfg->cChannels);
1054}
1055
1056/**
1057 * Sanitizes the file name component so that unsupported characters
1058 * will be replaced by an underscore ("_").
1059 *
1060 * @return IPRT status code.
1061 * @param pszPath Path to sanitize.
1062 * @param cbPath Size (in bytes) of path to sanitize.
1063 */
1064int DrvAudioHlpSanitizeFileName(char *pszPath, size_t cbPath)
1065{
1066 RT_NOREF(cbPath);
1067 int rc = VINF_SUCCESS;
1068#ifdef RT_OS_WINDOWS
1069 /* Filter out characters not allowed on Windows platforms, put in by
1070 RTTimeSpecToString(). */
1071 /** @todo Use something like RTPathSanitize() if available later some time. */
1072 static RTUNICP const s_uszValidRangePairs[] =
1073 {
1074 ' ', ' ',
1075 '(', ')',
1076 '-', '.',
1077 '0', '9',
1078 'A', 'Z',
1079 'a', 'z',
1080 '_', '_',
1081 0xa0, 0xd7af,
1082 '\0'
1083 };
1084 ssize_t cReplaced = RTStrPurgeComplementSet(pszPath, s_uszValidRangePairs, '_' /* Replacement */);
1085 if (cReplaced < 0)
1086 rc = VERR_INVALID_UTF8_ENCODING;
1087#else
1088 RT_NOREF(pszPath);
1089#endif
1090 return rc;
1091}
1092
1093/**
1094 * Constructs an unique file name, based on the given path and the audio file type.
1095 *
1096 * @returns IPRT status code.
1097 * @param pszFile Where to store the constructed file name.
1098 * @param cchFile Size (in characters) of the file name buffer.
1099 * @param pszPath Base path to use.
1100 * @param pszName A name for better identifying the file. Optional.
1101 * @param enmType Audio file type to construct file name for.
1102 */
1103int DrvAudioHlpGetFileName(char *pszFile, size_t cchFile, const char *pszPath, const char *pszName, PDMAUDIOFILETYPE enmType)
1104{
1105 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
1106 AssertReturn(cchFile, VERR_INVALID_PARAMETER);
1107 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1108 /* pszName is optional. */
1109
1110 int rc;
1111
1112 do
1113 {
1114 char szFilePath[RTPATH_MAX];
1115 RTStrPrintf(szFilePath, sizeof(szFilePath), "%s", pszPath);
1116
1117 /* Create it when necessary. */
1118 if (!RTDirExists(szFilePath))
1119 {
1120 rc = RTDirCreateFullPath(szFilePath, RTFS_UNIX_IRWXU);
1121 if (RT_FAILURE(rc))
1122 break;
1123 }
1124
1125 /* The actually drop directory consist of the current time stamp and a
1126 * unique number when necessary. */
1127 char pszTime[64];
1128 RTTIMESPEC time;
1129 if (!RTTimeSpecToString(RTTimeNow(&time), pszTime, sizeof(pszTime)))
1130 {
1131 rc = VERR_BUFFER_OVERFLOW;
1132 break;
1133 }
1134
1135 rc = DrvAudioHlpSanitizeFileName(pszTime, sizeof(pszTime));
1136 if (RT_FAILURE(rc))
1137 break;
1138
1139 rc = RTPathAppend(szFilePath, sizeof(szFilePath), pszTime);
1140 if (RT_FAILURE(rc))
1141 break;
1142
1143 if (pszName) /* Optional name given? */
1144 {
1145 rc = RTStrCat(szFilePath, sizeof(szFilePath), "-");
1146 if (RT_FAILURE(rc))
1147 break;
1148
1149 rc = RTStrCat(szFilePath, sizeof(szFilePath), pszName);
1150 if (RT_FAILURE(rc))
1151 break;
1152 }
1153
1154 switch (enmType)
1155 {
1156 case PDMAUDIOFILETYPE_WAV:
1157 rc = RTStrCat(szFilePath, sizeof(szFilePath), ".wav");
1158 break;
1159
1160 default:
1161 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1162 }
1163
1164 if (RT_FAILURE(rc))
1165 break;
1166
1167 RTStrPrintf(pszFile, cchFile, "%s", szFilePath);
1168
1169 } while (0);
1170
1171 LogFlowFuncLeaveRC(rc);
1172 return rc;
1173}
1174
1175/**
1176 * Opens or creates a wave (.WAV) file.
1177 *
1178 * @returns IPRT status code.
1179 * @param pFile Pointer to audio file handle to use.
1180 * @param pszFile File path of file to open or create.
1181 * @param fOpen Open flags.
1182 * @param pProps PCM properties to use.
1183 * @param fFlags Audio file flags.
1184 */
1185int DrvAudioHlpWAVFileOpen(PPDMAUDIOFILE pFile, const char *pszFile, uint32_t fOpen, PPDMAUDIOPCMPROPS pProps,
1186 PDMAUDIOFILEFLAGS fFlags)
1187{
1188 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1189 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
1190 /** @todo Validate fOpen flags. */
1191 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
1192 RT_NOREF(fFlags); /** @todo Validate fFlags flags. */
1193
1194 Assert(pProps->cChannels);
1195 Assert(pProps->uHz);
1196 Assert(pProps->cBits);
1197
1198 pFile->pvData = (PAUDIOWAVFILEDATA)RTMemAllocZ(sizeof(AUDIOWAVFILEDATA));
1199 if (!pFile->pvData)
1200 return VERR_NO_MEMORY;
1201 pFile->cbData = sizeof(PAUDIOWAVFILEDATA);
1202
1203 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1204 AssertPtr(pData);
1205
1206 /* Header. */
1207 pData->Hdr.u32RIFF = AUDIO_MAKE_FOURCC('R','I','F','F');
1208 pData->Hdr.u32Size = 36;
1209 pData->Hdr.u32WAVE = AUDIO_MAKE_FOURCC('W','A','V','E');
1210
1211 pData->Hdr.u32Fmt = AUDIO_MAKE_FOURCC('f','m','t',' ');
1212 pData->Hdr.u32Size1 = 16; /* Means PCM. */
1213 pData->Hdr.u16AudioFormat = 1; /* PCM, linear quantization. */
1214 pData->Hdr.u16NumChannels = pProps->cChannels;
1215 pData->Hdr.u32SampleRate = pProps->uHz;
1216 pData->Hdr.u32ByteRate = DrvAudioHlpCalcBitrate(pProps->cBits, pProps->uHz, pProps->cChannels) / 8;
1217 pData->Hdr.u16BlockAlign = pProps->cChannels * pProps->cBits / 8;
1218 pData->Hdr.u16BitsPerSample = pProps->cBits;
1219
1220 /* Data chunk. */
1221 pData->Hdr.u32ID2 = AUDIO_MAKE_FOURCC('d','a','t','a');
1222 pData->Hdr.u32Size2 = 0;
1223
1224 int rc = RTFileOpen(&pFile->hFile, pszFile, fOpen);
1225 if (RT_SUCCESS(rc))
1226 {
1227 rc = RTFileWrite(pFile->hFile, &pData->Hdr, sizeof(pData->Hdr), NULL);
1228 if (RT_FAILURE(rc))
1229 {
1230 RTFileClose(pFile->hFile);
1231 pFile->hFile = NIL_RTFILE;
1232 }
1233 }
1234
1235 if (RT_SUCCESS(rc))
1236 {
1237 pFile->enmType = PDMAUDIOFILETYPE_WAV;
1238
1239 RTStrPrintf(pFile->szName, RT_ELEMENTS(pFile->szName), "%s", pszFile);
1240 }
1241 else
1242 {
1243 RTMemFree(pFile->pvData);
1244 pFile->pvData = NULL;
1245 pFile->cbData = 0;
1246 }
1247
1248 return rc;
1249}
1250
1251/**
1252 * Closes a wave (.WAV) audio file.
1253 *
1254 * @returns IPRT status code.
1255 * @param pFile Audio file handle to close.
1256 */
1257int DrvAudioHlpWAVFileClose(PPDMAUDIOFILE pFile)
1258{
1259 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1260
1261 Assert(pFile->enmType == PDMAUDIOFILETYPE_WAV);
1262
1263 if (pFile->hFile != NIL_RTFILE)
1264 {
1265 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1266 AssertPtr(pData);
1267
1268 /* Update the header with the current data size. */
1269 RTFileWriteAt(pFile->hFile, 0, &pData->Hdr, sizeof(pData->Hdr), NULL);
1270
1271 RTFileClose(pFile->hFile);
1272 pFile->hFile = NIL_RTFILE;
1273 }
1274
1275 if (pFile->pvData)
1276 {
1277 RTMemFree(pFile->pvData);
1278 pFile->pvData = NULL;
1279 }
1280
1281 pFile->cbData = 0;
1282 pFile->enmType = PDMAUDIOFILETYPE_UNKNOWN;
1283
1284 return VINF_SUCCESS;
1285}
1286
1287/**
1288 * Returns the raw PCM audio data size of a wave file.
1289 * This does *not* include file headers and other data which does
1290 * not belong to the actual PCM audio data.
1291 *
1292 * @returns Size (in bytes) of the raw PCM audio data.
1293 * @param pFile Audio file handle to retrieve the audio data size for.
1294 */
1295size_t DrvAudioHlpWAVFileGetDataSize(PPDMAUDIOFILE pFile)
1296{
1297 AssertPtrReturn(pFile, 0);
1298
1299 Assert(pFile->enmType == PDMAUDIOFILETYPE_WAV);
1300
1301 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1302 AssertPtr(pData);
1303
1304 return pData->Hdr.u32Size2;
1305}
1306
1307/**
1308 * Write PCM data to a wave (.WAV) file.
1309 *
1310 * @returns IPRT status code.
1311 * @param pFile Audio file handle to write PCM data to.
1312 * @param pvBuf Audio data to write.
1313 * @param cbBuf Size (in bytes) of audio data to write.
1314 * @param fFlags Additional write flags. Not being used at the moment and must be 0.
1315 */
1316int DrvAudioHlpWAVFileWrite(PPDMAUDIOFILE pFile, const void *pvBuf, size_t cbBuf, uint32_t fFlags)
1317{
1318 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1319 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1320
1321 AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); /** @todo fFlags are currently not implemented. */
1322
1323 Assert(pFile->enmType == PDMAUDIOFILETYPE_WAV);
1324
1325 if (!cbBuf)
1326 return VINF_SUCCESS;
1327
1328 PAUDIOWAVFILEDATA pData = (PAUDIOWAVFILEDATA)pFile->pvData;
1329 AssertPtr(pData);
1330
1331 int rc = RTFileWrite(pFile->hFile, pvBuf, cbBuf, NULL);
1332 if (RT_SUCCESS(rc))
1333 {
1334 pData->Hdr.u32Size += (uint32_t)cbBuf;
1335 pData->Hdr.u32Size2 += (uint32_t)cbBuf;
1336 }
1337
1338 return rc;
1339}
1340
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