VirtualBox

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

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

Logging.

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