VirtualBox

source: vbox/trunk/include/VBox/vmm/pdmaudioinline.h@ 88298

Last change on this file since 88298 was 88269, checked in by vboxsync, 4 years ago

Audio: Made sure PDMAUDIOPCMPROPS is initialized using a helper function or the initializer macro, and that vital changes are made using setting helper functions. There are now two derived fields (frame size and shift count) that must be maintained, so this was the sanest way of doing it. Added a raw flag to PDMAUDIOPCMPROPS for VRDE/VRDP, since it wants the raw mixer content and we need a way of expressing this (PDMAUDIOSTREAMLAYOUT isn't the right place). The mixer buffers now uses PDMAUDIOPCMPROPS rather than the weird 32-bit format contraption for picking conversion functions. Simplify the drvAudioStreamPlay code by eliminating the PDMAUDIOSTREAMLAYOUT_RAW special case. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.4 KB
Line 
1/* $Id: pdmaudioinline.h 88269 2021-03-24 11:45:54Z vboxsync $ */
2/** @file
3 * PDM - Audio Helpers, Inlined Code. (DEV,++)
4 *
5 * This is all inlined because it's too tedious to create a couple libraries to
6 * contain it all (same bad excuse as for intnetinline.h & pdmnetinline.h).
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 * The contents of this file may alternatively be used under the terms
21 * of the Common Development and Distribution License Version 1.0
22 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
23 * VirtualBox OSE distribution, in which case the provisions of the
24 * CDDL are applicable instead of those of the GPL.
25 *
26 * You may elect to license modified versions of this file under the
27 * terms and conditions of either the GPL or the CDDL or both.
28 */
29
30#ifndef VBOX_INCLUDED_vmm_pdmaudioinline_h
31#define VBOX_INCLUDED_vmm_pdmaudioinline_h
32#ifndef RT_WITHOUT_PRAGMA_ONCE
33# pragma once
34#endif
35
36
37/*********************************************************************************************************************************
38* Header Files *
39*********************************************************************************************************************************/
40#include <VBox/err.h>
41#include <VBox/log.h>
42#include <VBox/vmm/pdmaudioifs.h>
43
44#include <iprt/asm.h>
45#include <iprt/asm-math.h>
46#include <iprt/assert.h>
47#include <iprt/mem.h>
48#include <iprt/string.h>
49
50
51/** @defgroup grp_pdm_audio_inline The PDM Audio Helper APIs
52 * @ingroup grp_pdm
53 * @{
54 */
55
56/* Fix later: */
57DECLINLINE(bool) PDMAudioPropsAreValid(PCPDMAUDIOPCMPROPS pProps);
58DECLINLINE(bool) PDMAudioPropsAreEqual(PCPDMAUDIOPCMPROPS pProps1, PCPDMAUDIOPCMPROPS pProps2);
59
60
61
62/**
63 * Gets the name of an audio direction enum value.
64 *
65 * @returns Pointer to read-only name string on success, "bad" if
66 * passed an invalid enum value.
67 * @param enmDir The audio direction value to name.
68 */
69DECLINLINE(const char *) PDMAudioDirGetName(PDMAUDIODIR enmDir)
70{
71 switch (enmDir)
72 {
73 case PDMAUDIODIR_UNKNOWN: return "Unknown";
74 case PDMAUDIODIR_IN: return "Input";
75 case PDMAUDIODIR_OUT: return "Output";
76 case PDMAUDIODIR_DUPLEX: return "Duplex";
77
78 /* no default */
79 case PDMAUDIODIR_END:
80 case PDMAUDIODIR_INVALID:
81 case PDMAUDIODIR_32BIT_HACK:
82 break;
83 }
84 AssertMsgFailedReturn(("Invalid audio direction %d\n", enmDir), "bad");
85}
86
87/**
88 * Gets the name of an audio mixer control enum value.
89 *
90 * @returns Pointer to read-only name, "bad" if invalid input.
91 * @param enmMixerCtl The audio mixer control value.
92 */
93DECLINLINE(const char *) PDMAudioMixerCtlGetName(PDMAUDIOMIXERCTL enmMixerCtl)
94{
95 switch (enmMixerCtl)
96 {
97 case PDMAUDIOMIXERCTL_UNKNOWN: return "Unknown";
98 case PDMAUDIOMIXERCTL_VOLUME_MASTER: return "Master Volume";
99 case PDMAUDIOMIXERCTL_FRONT: return "Front";
100 case PDMAUDIOMIXERCTL_CENTER_LFE: return "Center / LFE";
101 case PDMAUDIOMIXERCTL_REAR: return "Rear";
102 case PDMAUDIOMIXERCTL_LINE_IN: return "Line-In";
103 case PDMAUDIOMIXERCTL_MIC_IN: return "Microphone-In";
104 /* no default */
105 case PDMAUDIOMIXERCTL_END:
106 case PDMAUDIOMIXERCTL_INVALID:
107 case PDMAUDIOMIXERCTL_32BIT_HACK:
108 break;
109 }
110 AssertMsgFailedReturn(("Invalid mixer control %ld\n", enmMixerCtl), "bad");
111}
112
113/**
114 * Gets the name of a playback destination enum value.
115 *
116 * @returns Pointer to read-only name, "bad" if invalid input.
117 * @param enmPlaybackDst The playback destination value.
118 */
119DECLINLINE(const char *) PDMAudioPlaybackDstGetName(PDMAUDIOPLAYBACKDST enmPlaybackDst)
120{
121 switch (enmPlaybackDst)
122 {
123 case PDMAUDIOPLAYBACKDST_UNKNOWN: return "Unknown";
124 case PDMAUDIOPLAYBACKDST_FRONT: return "Front";
125 case PDMAUDIOPLAYBACKDST_CENTER_LFE: return "Center / LFE";
126 case PDMAUDIOPLAYBACKDST_REAR: return "Rear";
127 /* no default */
128 case PDMAUDIOPLAYBACKDST_INVALID:
129 case PDMAUDIOPLAYBACKDST_END:
130 case PDMAUDIOPLAYBACKDST_32BIT_HACK:
131 break;
132 }
133 AssertMsgFailedReturn(("Invalid playback destination %ld\n", enmPlaybackDst), "bad");
134}
135
136/**
137 * Gets the name of a recording source enum value.
138 *
139 * @returns Pointer to read-only name, "bad" if invalid input.
140 * @param enmRecSrc The recording source value.
141 */
142DECLINLINE(const char *) PDMAudioRecSrcGetName(PDMAUDIORECSRC enmRecSrc)
143{
144 switch (enmRecSrc)
145 {
146 case PDMAUDIORECSRC_UNKNOWN: return "Unknown";
147 case PDMAUDIORECSRC_MIC: return "Microphone In";
148 case PDMAUDIORECSRC_CD: return "CD";
149 case PDMAUDIORECSRC_VIDEO: return "Video";
150 case PDMAUDIORECSRC_AUX: return "AUX";
151 case PDMAUDIORECSRC_LINE: return "Line In";
152 case PDMAUDIORECSRC_PHONE: return "Phone";
153 /* no default */
154 case PDMAUDIORECSRC_END:
155 case PDMAUDIORECSRC_32BIT_HACK:
156 break;
157 }
158 AssertMsgFailedReturn(("Invalid recording source %ld\n", enmRecSrc), "bad");
159}
160
161/**
162 * Checks whether the audio format is signed.
163 *
164 * @returns @c true for signed format, @c false for unsigned.
165 * @param enmFmt The audio format.
166 */
167DECLINLINE(bool) PDMAudioFormatIsSigned(PDMAUDIOFMT enmFmt)
168{
169 switch (enmFmt)
170 {
171 case PDMAUDIOFMT_S8:
172 case PDMAUDIOFMT_S16:
173 case PDMAUDIOFMT_S32:
174 return true;
175
176 case PDMAUDIOFMT_U8:
177 case PDMAUDIOFMT_U16:
178 case PDMAUDIOFMT_U32:
179 return false;
180
181 /* no default */
182 case PDMAUDIOFMT_INVALID:
183 case PDMAUDIOFMT_END:
184 case PDMAUDIOFMT_32BIT_HACK:
185 break;
186 }
187 AssertMsgFailedReturn(("Bogus audio format %ld\n", enmFmt), false);
188}
189
190/**
191 * Gets the encoding width in bits of the give audio format.
192 *
193 * @returns Bit count. 0 if invalid input.
194 * @param enmFmt The audio format.
195 */
196DECLINLINE(uint8_t) PDMAudioFormatGetBits(PDMAUDIOFMT enmFmt)
197{
198 switch (enmFmt)
199 {
200 case PDMAUDIOFMT_S8:
201 case PDMAUDIOFMT_U8:
202 return 8;
203
204 case PDMAUDIOFMT_U16:
205 case PDMAUDIOFMT_S16:
206 return 16;
207
208 case PDMAUDIOFMT_U32:
209 case PDMAUDIOFMT_S32:
210 return 32;
211
212 /* no default */
213 case PDMAUDIOFMT_INVALID:
214 case PDMAUDIOFMT_END:
215 case PDMAUDIOFMT_32BIT_HACK:
216 break;
217 }
218 AssertMsgFailedReturn(("Bogus audio format %ld\n", enmFmt), 0);
219}
220
221/**
222 * Gets the name of an audio format enum value.
223 *
224 * @returns Pointer to read-only name on success, returns "bad" on if
225 * invalid enum value.
226 * @param enmFmt The audio format to name.
227 */
228DECLINLINE(const char *) PDMAudioFormatGetName(PDMAUDIOFMT enmFmt)
229{
230 switch (enmFmt)
231 {
232 case PDMAUDIOFMT_U8: return "U8";
233 case PDMAUDIOFMT_U16: return "U16";
234 case PDMAUDIOFMT_U32: return "U32";
235 case PDMAUDIOFMT_S8: return "S8";
236 case PDMAUDIOFMT_S16: return "S16";
237 case PDMAUDIOFMT_S32: return "S32";
238 /* no default */
239 case PDMAUDIOFMT_INVALID:
240 case PDMAUDIOFMT_END:
241 case PDMAUDIOFMT_32BIT_HACK:
242 break;
243 }
244 AssertMsgFailedReturn(("Bogus audio format %d\n", enmFmt), "bad");
245}
246
247/**
248 * Initializes a stream configuration from PCM properties.
249 *
250 * @return IPRT status code.
251 * @param pCfg The stream configuration to initialize.
252 * @param pProps The PCM properties to use.
253 */
254DECLINLINE(int) PDMAudioStrmCfgInitWithProps(PPDMAUDIOSTREAMCFG pCfg, PCPDMAUDIOPCMPROPS pProps)
255{
256 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
257 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
258
259 RT_ZERO(*pCfg);
260 pCfg->Backend.cFramesPreBuffering = UINT32_MAX; /* Explicitly set to "undefined". */
261
262 memcpy(&pCfg->Props, pProps, sizeof(PDMAUDIOPCMPROPS));
263
264 return VINF_SUCCESS;
265}
266
267/**
268 * Checks whether stream configuration matches the given PCM properties.
269 *
270 * @returns @c true if equal, @c false if not.
271 * @param pCfg The stream configuration.
272 * @param pProps The PCM properties to match with.
273 */
274DECLINLINE(bool) PDMAudioStrmCfgMatchesProps(PCPDMAUDIOSTREAMCFG pCfg, PCPDMAUDIOPCMPROPS pProps)
275{
276 AssertPtrReturn(pCfg, false);
277 return PDMAudioPropsAreEqual(pProps, &pCfg->Props);
278}
279
280/**
281 * Frees an audio stream allocated by PDMAudioStrmCfgDup().
282 *
283 * @param pCfg The stream configuration to free.
284 */
285DECLINLINE(void) PDMAudioStrmCfgFree(PPDMAUDIOSTREAMCFG pCfg)
286{
287 if (pCfg)
288 RTMemFree(pCfg);
289}
290
291/**
292 * Checks whether the given stream configuration is valid or not.
293 *
294 * @returns true/false accordingly.
295 * @param pCfg Stream configuration to check.
296 *
297 * @remarks This just performs a generic check of value ranges. Further, it
298 * will assert if the input is invalid.
299 *
300 * @sa PDMAudioPropsAreValid
301 */
302DECLINLINE(bool) PDMAudioStrmCfgIsValid(PCPDMAUDIOSTREAMCFG pCfg)
303{
304 AssertPtrReturn(pCfg, false);
305 AssertMsgReturn(pCfg->enmDir >= PDMAUDIODIR_UNKNOWN && pCfg->enmDir < PDMAUDIODIR_END,
306 ("%d\n", pCfg->enmDir), false);
307 AssertMsgReturn(pCfg->enmLayout >= PDMAUDIOSTREAMLAYOUT_UNKNOWN && pCfg->enmLayout < PDMAUDIOSTREAMLAYOUT_END,
308 ("%d\n", pCfg->enmLayout), false);
309 return PDMAudioPropsAreValid(&pCfg->Props);
310}
311
312/**
313 * Copies one stream configuration to another.
314 *
315 * @returns IPRT status code.
316 * @param pDstCfg The destination stream configuration.
317 * @param pSrcCfg The source stream configuration.
318 */
319DECLINLINE(int) PDMAudioStrmCfgCopy(PPDMAUDIOSTREAMCFG pDstCfg, PCPDMAUDIOSTREAMCFG pSrcCfg)
320{
321 AssertPtrReturn(pDstCfg, VERR_INVALID_POINTER);
322 AssertPtrReturn(pSrcCfg, VERR_INVALID_POINTER);
323
324 /* This used to be VBOX_STRICT only and return VERR_INVALID_PARAMETER, but
325 that's making release builds work differently from debug & strict builds,
326 which is a terrible idea: */
327 Assert(PDMAudioStrmCfgIsValid(pSrcCfg));
328
329 memcpy(pDstCfg, pSrcCfg, sizeof(PDMAUDIOSTREAMCFG));
330
331 return VINF_SUCCESS;
332}
333
334/**
335 * Duplicates an audio stream configuration.
336 *
337 * @returns Pointer to duplicate on success, NULL on failure. Must be freed
338 * using PDMAudioStrmCfgFree().
339 *
340 * @param pCfg The audio stream configuration to duplicate.
341 */
342DECLINLINE(PPDMAUDIOSTREAMCFG) PDMAudioStrmCfgDup(PCPDMAUDIOSTREAMCFG pCfg)
343{
344 AssertPtrReturn(pCfg, NULL);
345
346 PPDMAUDIOSTREAMCFG pDst = (PPDMAUDIOSTREAMCFG)RTMemAllocZ(sizeof(PDMAUDIOSTREAMCFG));
347 if (pDst)
348 {
349 int rc = PDMAudioStrmCfgCopy(pDst, pCfg);
350 if (RT_SUCCESS(rc))
351 return pDst;
352
353 PDMAudioStrmCfgFree(pDst);
354 }
355 return NULL;
356}
357
358/**
359 * Logs an audio stream configuration.
360 *
361 * @param pCfg The stream configuration to log.
362 */
363DECLINLINE(void) PDMAudioStrmCfgLog(PCPDMAUDIOSTREAMCFG pCfg)
364{
365 if (pCfg)
366 LogFunc(("szName=%s enmDir=%RU32 uHz=%RU32 cBits=%RU8%s cChannels=%RU8\n", pCfg->szName, pCfg->enmDir,
367 pCfg->Props.uHz, pCfg->Props.cbSampleX * 8, pCfg->Props.fSigned ? "S" : "U", pCfg->Props.cChannelsX));
368}
369
370/**
371 * Converts a stream command enum value to a string.
372 *
373 * @returns Pointer to read-only stream command name on success,
374 * "bad" if invalid command value.
375 * @param enmCmd The stream command to name.
376 */
377DECLINLINE(const char *) PDMAudioStrmCmdGetName(PDMAUDIOSTREAMCMD enmCmd)
378{
379 switch (enmCmd)
380 {
381 case PDMAUDIOSTREAMCMD_INVALID: return "Invalid";
382 case PDMAUDIOSTREAMCMD_UNKNOWN: return "Unknown";
383 case PDMAUDIOSTREAMCMD_ENABLE: return "Enable";
384 case PDMAUDIOSTREAMCMD_DISABLE: return "Disable";
385 case PDMAUDIOSTREAMCMD_PAUSE: return "Pause";
386 case PDMAUDIOSTREAMCMD_RESUME: return "Resume";
387 case PDMAUDIOSTREAMCMD_DRAIN: return "Drain";
388 case PDMAUDIOSTREAMCMD_DROP: return "Drop";
389 case PDMAUDIOSTREAMCMD_END:
390 case PDMAUDIOSTREAMCMD_32BIT_HACK:
391 break;
392 /* no default! */
393 }
394 AssertMsgFailedReturn(("Invalid stream command %d\n", enmCmd), "bad");
395}
396
397/**
398 * Checks if the stream status is one that can be read from.
399 *
400 * @returns @c true if ready to be read from, @c false if not.
401 * @param fStatus Stream status to evaluate, PDMAUDIOSTREAMSTS_FLAGS_XXX.
402 */
403DECLINLINE(bool) PDMAudioStrmStatusCanRead(PDMAUDIOSTREAMSTS fStatus)
404{
405 AssertReturn(fStatus & PDMAUDIOSTREAMSTS_VALID_MASK, false);
406 /*
407 return fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED
408 && fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED
409 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PAUSED)
410 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT);*/
411 return (fStatus & ( PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED
412 | PDMAUDIOSTREAMSTS_FLAGS_ENABLED
413 | PDMAUDIOSTREAMSTS_FLAGS_PAUSED
414 | PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT ))
415 == ( PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED
416 | PDMAUDIOSTREAMSTS_FLAGS_ENABLED);
417}
418
419/**
420 * Checks if the stream status is one that can be written to.
421 *
422 * @returns @c true if ready to be written to, @c false if not.
423 * @param fStatus Stream status to evaluate, PDMAUDIOSTREAMSTS_FLAGS_XXX.
424 */
425DECLINLINE(bool) PDMAudioStrmStatusCanWrite(PDMAUDIOSTREAMSTS fStatus)
426{
427 AssertReturn(fStatus & PDMAUDIOSTREAMSTS_VALID_MASK, false);
428 /*
429 return fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED
430 && fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED
431 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PAUSED)
432 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)
433 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT);*/
434 return (fStatus & ( PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED
435 | PDMAUDIOSTREAMSTS_FLAGS_ENABLED
436 | PDMAUDIOSTREAMSTS_FLAGS_PAUSED
437 | PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE
438 | PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT))
439 == ( PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED
440 | PDMAUDIOSTREAMSTS_FLAGS_ENABLED);
441}
442
443/**
444 * Checks if the stream status is a read-to-operate one.
445 *
446 * @returns @c true if ready to operate, @c false if not.
447 * @param fStatus Stream status to evaluate, PDMAUDIOSTREAMSTS_FLAGS_XXX.
448 */
449DECLINLINE(bool) PDMAudioStrmStatusIsReady(PDMAUDIOSTREAMSTS fStatus)
450{
451 AssertReturn(fStatus & PDMAUDIOSTREAMSTS_VALID_MASK, false);
452 /*
453 return fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED
454 && fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED
455 && !(fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT);*/
456 return (fStatus & ( PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED
457 | PDMAUDIOSTREAMSTS_FLAGS_ENABLED
458 | PDMAUDIOSTREAMSTS_FLAGS_PENDING_REINIT))
459 == ( PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED
460 | PDMAUDIOSTREAMSTS_FLAGS_ENABLED);
461}
462
463
464/*********************************************************************************************************************************
465* PCM Property Helpers *
466*********************************************************************************************************************************/
467
468/**
469 * Initialize PCM audio properties.
470 */
471DECLINLINE(void) PDMAudioPropsInit(PPDMAUDIOPCMPROPS pProps, uint8_t cbSample, bool fSigned, uint8_t cChannels, uint32_t uHz)
472{
473 pProps->cbFrame = cbSample * cChannels;
474 pProps->cbSampleX = cbSample;
475 pProps->cChannelsX = cChannels;
476 pProps->cShiftX = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(cbSample, cChannels);
477 pProps->fSigned = fSigned;
478 pProps->fSwapEndian = false;
479 pProps->fRaw = false;
480 pProps->uHz = uHz;
481
482 Assert(pProps->cbFrame == (uint32_t)cbSample * cChannels);
483 Assert(pProps->cbSampleX == cbSample);
484 Assert(pProps->cChannelsX == cChannels);
485}
486
487/**
488 * Initialize PCM audio properties, extended version.
489 */
490DECLINLINE(void) PDMAudioPropsInitEx(PPDMAUDIOPCMPROPS pProps, uint8_t cbSample, bool fSigned, uint8_t cChannels, uint32_t uHz,
491 bool fLittleEndian, bool fRaw)
492{
493 Assert(!fRaw || cbSample == sizeof(int64_t));
494 pProps->cbFrame = cbSample * cChannels;
495 pProps->cbSampleX = cbSample;
496 pProps->cChannelsX = cChannels;
497 pProps->cShiftX = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(cbSample, cChannels);
498 pProps->fSigned = fSigned;
499#ifdef RT_LITTLE_ENDIAN
500 pProps->fSwapEndian = !fLittleEndian;
501#else
502 pProps->fSwapEndian = fLittleEndian;
503#endif
504 pProps->fRaw = fRaw;
505 pProps->uHz = uHz;
506
507 Assert(pProps->cbFrame == (uint32_t)cbSample * cChannels);
508 Assert(pProps->cbSampleX == cbSample);
509 Assert(pProps->cChannelsX == cChannels);
510}
511
512/**
513 * Modifies the channel count.
514 *
515 * @param pProps The PCM properties to update.
516 * @param cChannels The new channel count.
517 */
518DECLINLINE(void) PDMAudioPropsSetChannels(PPDMAUDIOPCMPROPS pProps, uint8_t cChannels)
519{
520 Assert(cChannels > 0); Assert(cChannels < 16);
521 pProps->cChannelsX = cChannels;
522 pProps->cbFrame = pProps->cbSampleX * cChannels;
523 pProps->cShiftX = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pProps->cbSampleX, cChannels);
524}
525
526/**
527 * Modifies the sample size.
528 *
529 * @param pProps The PCM properties to update.
530 * @param cbSample The new sample size (in bytes).
531 */
532DECLINLINE(void) PDMAudioPropsSetSampleSize(PPDMAUDIOPCMPROPS pProps, uint8_t cbSample)
533{
534 Assert(cbSample == 1 || cbSample == 2 || cbSample == 4 || cbSample == 8);
535 pProps->cbSampleX = cbSample;
536 pProps->cbFrame = cbSample * pProps->cChannelsX;
537 pProps->cShiftX = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(cbSample, pProps->cChannelsX);
538}
539
540/**
541 * Gets the bitrate.
542 *
543 * Divide the result by 8 to get the byte rate.
544 *
545 * @returns Bit rate.
546 * @param pProps PCM properties to calculate bitrate for.
547 */
548DECLINLINE(uint32_t) PDMAudioPropsGetBitrate(PCPDMAUDIOPCMPROPS pProps)
549{
550 Assert(pProps->cbFrame == pProps->cbSampleX * pProps->cChannelsX);
551 return pProps->cbFrame * pProps->uHz * 8;
552}
553
554/**
555 * Gets the number of channels.
556 * @returns The channel count.
557 * @param pProps The PCM properties.
558 */
559DECL_FORCE_INLINE(uint8_t) PDMAudioPropsChannels(PCPDMAUDIOPCMPROPS pProps)
560{
561 return pProps->cChannelsX;
562}
563
564/**
565 * Gets the sample size in bytes.
566 * @returns Number of bytes per sample.
567 * @param pProps The PCM properties.
568 */
569DECL_FORCE_INLINE(uint8_t) PDMAudioPropsSampleSize(PCPDMAUDIOPCMPROPS pProps)
570{
571 return pProps->cbSampleX;
572}
573
574/**
575 * Gets the sample size in bits.
576 * @returns Number of bits per sample.
577 * @param pProps The PCM properties.
578 */
579DECLINLINE(uint8_t) PDMAudioPropsSampleBits(PCPDMAUDIOPCMPROPS pProps)
580{
581 return pProps->cbSampleX * 8;
582}
583
584/**
585 * Gets the frame size in bytes.
586 * @returns Number of bytes per frame.
587 * @param pProps The PCM properties.
588 */
589DECL_FORCE_INLINE(uint8_t) PDMAudioPropsFrameSize(PCPDMAUDIOPCMPROPS pProps)
590{
591 return pProps->cbFrame;
592}
593
594/**
595 * Gets the frequency.
596 * @returns Frequency.
597 * @param pProps The PCM properties.
598 */
599DECL_FORCE_INLINE(uint32_t) PDMAudioPropsHz(PCPDMAUDIOPCMPROPS pProps)
600{
601 return pProps->uHz;
602}
603
604/**
605 * Checks if the format is signed or unsigned.
606 * @returns true if signed, false if unsigned.
607 * @param pProps The PCM properties.
608 */
609DECL_FORCE_INLINE(bool) PDMAudioPropsIsSigned(PCPDMAUDIOPCMPROPS pProps)
610{
611 return pProps->fSigned;
612}
613
614/**
615 * Checks if the format is little-endian or not.
616 * @returns true if little-endian (or if 8-bit), false if big-endian.
617 * @param pProps The PCM properties.
618 */
619DECL_FORCE_INLINE(bool) PDMAudioPropsIsLittleEndian(PCPDMAUDIOPCMPROPS pProps)
620{
621#ifdef RT_LITTLE_ENDIAN
622 return !pProps->fSwapEndian || pProps->cbSampleX < 2;
623#else
624 return pProps->fSwapEndian || pProps->cbSampleX < 2;
625#endif
626}
627
628/**
629 * Checks if the format is big-endian or not.
630 * @returns true if big-endian (or if 8-bit), false if little-endian.
631 * @param pProps The PCM properties.
632 */
633DECL_FORCE_INLINE(bool) PDMAudioPropsIsBigEndian(PCPDMAUDIOPCMPROPS pProps)
634{
635#ifdef RT_LITTLE_ENDIAN
636 return pProps->fSwapEndian || pProps->cbSampleX < 2;
637#else
638 return !pProps->fSwapEndian || pProps->cbSampleX < 2;
639#endif
640}
641
642/**
643 * Rounds down the given byte amount to the nearest frame boundrary.
644 *
645 * @returns Rounded byte amount.
646 * @param pProps PCM properties to use.
647 * @param cb The size (in bytes) to round.
648 */
649DECLINLINE(uint32_t) PDMAudioPropsFloorBytesToFrame(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
650{
651 AssertPtrReturn(pProps, 0);
652 return PDMAUDIOPCMPROPS_F2B(pProps, PDMAUDIOPCMPROPS_B2F(pProps, cb));
653}
654
655/**
656 * Rounds up the given byte amount to the nearest frame boundrary.
657 *
658 * @returns Rounded byte amount.
659 * @param pProps PCM properties to use.
660 * @param cb The size (in bytes) to round.
661 */
662DECLINLINE(uint32_t) PDMAudioPropsRoundUpBytesToFrame(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
663{
664 AssertPtrReturn(pProps, 0);
665 uint32_t const cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */);
666 AssertReturn(cbFrame, 0);
667 return PDMAUDIOPCMPROPS_F2B(pProps, PDMAUDIOPCMPROPS_B2F(pProps, cb + cbFrame - 1));
668}
669
670/**
671 * Checks if the given size is aligned on a frame boundrary.
672 *
673 * @returns @c true if properly aligned, @c false if not.
674 * @param pProps PCM properties to use.
675 * @param cb The size (in bytes) to check.
676 */
677DECLINLINE(bool) PDMAudioPropsIsSizeAligned(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
678{
679 AssertPtrReturn(pProps, false);
680 uint32_t const cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */);
681 AssertReturn(cbFrame, false);
682 return cb % cbFrame == 0;
683}
684
685/**
686 * Converts bytes to frames (rounding down of course).
687 *
688 * @returns Number of frames.
689 * @param pProps PCM properties to use.
690 * @param cb The number of bytes to convert.
691 */
692DECLINLINE(uint32_t) PDMAudioPropsBytesToFrames(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
693{
694 AssertPtrReturn(pProps, 0);
695 return PDMAUDIOPCMPROPS_B2F(pProps, cb);
696}
697
698/**
699 * Converts bytes to milliseconds.
700 *
701 * @return Number milliseconds @a cb takes to play or record.
702 * @param pProps PCM properties to use.
703 * @param cb The number of bytes to convert.
704 *
705 * @note Rounds up the result.
706 */
707DECLINLINE(uint64_t) PDMAudioPropsBytesToMilli(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
708{
709 AssertPtrReturn(pProps, 0);
710
711 /* Check parameters to prevent division by chainsaw: */
712 uint32_t const uHz = pProps->uHz;
713 if (uHz)
714 {
715 const unsigned cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */);
716 if (cbFrame)
717 {
718 /* Round cb up to closest frame size: */
719 cb = (cb + cbFrame - 1) / cbFrame;
720
721 /* Convert to milliseconds. */
722 return (cb * (uint64_t)RT_MS_1SEC + uHz - 1) / uHz;
723 }
724 }
725 return 0;
726}
727
728/**
729 * Converts bytes to microseconds.
730 *
731 * @return Number microseconds @a cb takes to play or record.
732 * @param pProps PCM properties to use.
733 * @param cb The number of bytes to convert.
734 *
735 * @note Rounds up the result.
736 */
737DECLINLINE(uint64_t) PDMAudioPropsBytesToMicro(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
738{
739 AssertPtrReturn(pProps, 0);
740
741 /* Check parameters to prevent division by chainsaw: */
742 uint32_t const uHz = pProps->uHz;
743 if (uHz)
744 {
745 const unsigned cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */);
746 if (cbFrame)
747 {
748 /* Round cb up to closest frame size: */
749 cb = (cb + cbFrame - 1) / cbFrame;
750
751 /* Convert to microseconds. */
752 return (cb * (uint64_t)RT_US_1SEC + uHz - 1) / uHz;
753 }
754 }
755 return 0;
756}
757
758/**
759 * Converts bytes to nanoseconds.
760 *
761 * @return Number nanoseconds @a cb takes to play or record.
762 * @param pProps PCM properties to use.
763 * @param cb The number of bytes to convert.
764 *
765 * @note Rounds up the result.
766 */
767DECLINLINE(uint64_t) PDMAudioPropsBytesToNano(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
768{
769 AssertPtrReturn(pProps, 0);
770
771 /* Check parameters to prevent division by chainsaw: */
772 uint32_t const uHz = pProps->uHz;
773 if (uHz)
774 {
775 const unsigned cbFrame = PDMAUDIOPCMPROPS_F2B(pProps, 1 /* Frame */);
776 if (cbFrame)
777 {
778 /* Round cb up to closest frame size: */
779 cb = (cb + cbFrame - 1) / cbFrame;
780
781 /* Convert to nanoseconds. */
782 return (cb * (uint64_t)RT_NS_1SEC + uHz - 1) / uHz;
783 }
784 }
785 return 0;
786}
787
788/**
789 * Converts frames to bytes.
790 *
791 * @returns Number of bytes.
792 * @param pProps The PCM properties to use.
793 * @param cFrames Number of audio frames to convert.
794 * @sa PDMAUDIOPCMPROPS_F2B
795 */
796DECLINLINE(uint32_t) PDMAudioPropsFramesToBytes(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
797{
798 AssertPtrReturn(pProps, 0);
799 return PDMAUDIOPCMPROPS_F2B(pProps, cFrames);
800}
801
802/**
803 * Converts frames to milliseconds.
804 *
805 * @returns milliseconds.
806 * @param pProps The PCM properties to use.
807 * @param cFrames Number of audio frames to convert.
808 * @note No rounding here, result is floored.
809 */
810DECLINLINE(uint64_t) PDMAudioPropsFramesToMilli(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
811{
812 AssertPtrReturn(pProps, 0);
813
814 /* Check input to prevent division by chainsaw: */
815 uint32_t const uHz = pProps->uHz;
816 if (uHz)
817 return ASMMultU32ByU32DivByU32(cFrames, RT_MS_1SEC, uHz);
818 return 0;
819}
820
821/**
822 * Converts frames to nanoseconds.
823 *
824 * @returns Nanoseconds.
825 * @param pProps The PCM properties to use.
826 * @param cFrames Number of audio frames to convert.
827 * @note No rounding here, result is floored.
828 */
829DECLINLINE(uint64_t) PDMAudioPropsFramesToNano(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
830{
831 AssertPtrReturn(pProps, 0);
832
833 /* Check input to prevent division by chainsaw: */
834 uint32_t const uHz = pProps->uHz;
835 if (uHz)
836 return ASMMultU32ByU32DivByU32(cFrames, RT_NS_1SEC, uHz);
837 return 0;
838}
839
840/**
841 * Converts milliseconds to frames.
842 *
843 * @returns Number of frames
844 * @param pProps The PCM properties to use.
845 * @param cMs The number of milliseconds to convert.
846 *
847 * @note The result is rounded rather than floored (hysterical raisins).
848 */
849DECLINLINE(uint32_t) PDMAudioPropsMilliToFrames(PCPDMAUDIOPCMPROPS pProps, uint64_t cMs)
850{
851 AssertPtrReturn(pProps, 0);
852
853 uint32_t const uHz = pProps->uHz;
854 uint32_t cFrames;
855 if (cMs < RT_MS_1SEC)
856 cFrames = 0;
857 else
858 {
859 cFrames = cMs / RT_MS_1SEC * uHz;
860 cMs %= RT_MS_1SEC;
861 }
862 cFrames += (ASMMult2xU32RetU64(uHz, (uint32_t)cMs) + RT_MS_1SEC - 1) / RT_MS_1SEC;
863 return cFrames;
864}
865
866/**
867 * Converts milliseconds to bytes.
868 *
869 * @returns Number of bytes (frame aligned).
870 * @param pProps The PCM properties to use.
871 * @param cMs The number of milliseconds to convert.
872 *
873 * @note The result is rounded rather than floored (hysterical raisins).
874 */
875DECLINLINE(uint32_t) PDMAudioPropsMilliToBytes(PCPDMAUDIOPCMPROPS pProps, uint64_t cMs)
876{
877 return PDMAUDIOPCMPROPS_F2B(pProps, PDMAudioPropsMilliToFrames(pProps, cMs));
878}
879
880/**
881 * Converts nanoseconds to frames.
882 *
883 * @returns Number of frames
884 * @param pProps The PCM properties to use.
885 * @param cNs The number of nanoseconds to convert.
886 *
887 * @note The result is rounded rather than floored (hysterical raisins).
888 */
889DECLINLINE(uint32_t) PDMAudioPropsNanoToFrames(PCPDMAUDIOPCMPROPS pProps, uint64_t cNs)
890{
891 AssertPtrReturn(pProps, 0);
892
893 uint32_t const uHz = pProps->uHz;
894 uint32_t cFrames;
895 if (cNs < RT_NS_1SEC)
896 cFrames = 0;
897 else
898 {
899 cFrames = cNs / RT_NS_1SEC * uHz;
900 cNs %= RT_NS_1SEC;
901 }
902 cFrames += (ASMMult2xU32RetU64(uHz, (uint32_t)cNs) + RT_NS_1SEC - 1) / RT_NS_1SEC;
903 return cFrames;
904}
905
906/**
907 * Converts nanoseconds to bytes.
908 *
909 * @returns Number of bytes (frame aligned).
910 * @param pProps The PCM properties to use.
911 * @param cNs The number of nanoseconds to convert.
912 *
913 * @note The result is rounded rather than floored (hysterical raisins).
914 */
915DECLINLINE(uint32_t) PDMAudioPropsNanoToBytes(PCPDMAUDIOPCMPROPS pProps, uint64_t cNs)
916{
917 return PDMAUDIOPCMPROPS_F2B(pProps, PDMAudioPropsNanoToFrames(pProps, cNs));
918}
919
920/**
921 * Clears a sample buffer by the given amount of audio frames with silence (according to the format
922 * given by the PCM properties).
923 *
924 * @param pProps The PCM properties to apply.
925 * @param pvBuf The buffer to clear.
926 * @param cbBuf The buffer size in bytes.
927 * @param cFrames The number of audio frames to clear. Capped at @a cbBuf
928 * if exceeding the buffer. If the size is an unaligned
929 * number of frames, the extra bytes may be left
930 * uninitialized in some configurations.
931 */
932DECLINLINE(void) PDMAudioPropsClearBuffer(PCPDMAUDIOPCMPROPS pProps, void *pvBuf, size_t cbBuf, uint32_t cFrames)
933{
934 /*
935 * Validate input
936 */
937 AssertPtrReturnVoid(pProps);
938 Assert(pProps->cbSampleX);
939 if (!cbBuf || !cFrames)
940 return;
941 AssertPtrReturnVoid(pvBuf);
942
943 Assert(pProps->fSwapEndian == false); /** @todo Swapping Endianness is not supported yet. */
944
945 /*
946 * Decide how much needs clearing.
947 */
948 size_t cbToClear = PDMAudioPropsFramesToBytes(pProps, cFrames);
949 AssertStmt(cbToClear <= cbBuf, cbToClear = cbBuf);
950
951 Log2Func(("pProps=%p, pvBuf=%p, cFrames=%RU32, fSigned=%RTbool, cbSample=%RU8\n",
952 pProps, pvBuf, cFrames, pProps->fSigned, pProps->cbSampleX));
953
954 /*
955 * Do the job.
956 */
957 if (pProps->fSigned)
958 RT_BZERO(pvBuf, cbToClear);
959 else /* Unsigned formats. */
960 {
961 switch (pProps->cbSampleX)
962 {
963 case 1: /* 8 bit */
964 memset(pvBuf, 0x80, cbToClear);
965 break;
966
967 case 2: /* 16 bit */
968 {
969 uint16_t *pu16Dst = (uint16_t *)pvBuf;
970 size_t cLeft = cbToClear / sizeof(uint16_t);
971 while (cLeft-- > 0)
972 *pu16Dst++ = 0x80;
973 break;
974 }
975
976 /** @todo Add 24 bit? */
977
978 case 4: /* 32 bit */
979 ASMMemFill32(pvBuf, cbToClear & ~(size_t)3, 0x80);
980 break;
981
982 default:
983 AssertMsgFailed(("Invalid bytes per sample: %RU8\n", pProps->cbSampleX));
984 }
985 }
986}
987
988/**
989 * Compares two sets of PCM properties.
990 *
991 * @returns @c true if the same, @c false if not.
992 * @param pProps1 The first set of properties to compare.
993 * @param pProps2 The second set of properties to compare.
994 */
995DECLINLINE(bool) PDMAudioPropsAreEqual(PCPDMAUDIOPCMPROPS pProps1, PCPDMAUDIOPCMPROPS pProps2)
996{
997 AssertPtrReturn(pProps1, false);
998 AssertPtrReturn(pProps2, false);
999
1000 if (pProps1 == pProps2) /* If the pointers match, take a shortcut. */
1001 return true;
1002
1003 return pProps1->uHz == pProps2->uHz
1004 && pProps1->cChannelsX == pProps2->cChannelsX
1005 && pProps1->cbSampleX == pProps2->cbSampleX
1006 && pProps1->fSigned == pProps2->fSigned
1007 && pProps1->fSwapEndian == pProps2->fSwapEndian;
1008}
1009
1010/**
1011 * Checks whether the given PCM properties are valid or not.
1012 *
1013 * @returns true/false accordingly.
1014 * @param pProps The PCM properties to check.
1015 *
1016 * @remarks This just performs a generic check of value ranges. Further, it
1017 * will assert if the input is invalid.
1018 *
1019 * @sa PDMAudioStrmCfgIsValid
1020 */
1021DECLINLINE(bool) PDMAudioPropsAreValid(PCPDMAUDIOPCMPROPS pProps)
1022{
1023 AssertPtrReturn(pProps, false);
1024
1025 AssertReturn(pProps->cChannelsX != 0, false);
1026 AssertMsgReturn( pProps->cbSampleX == 1 || pProps->cbSampleX == 2 || pProps->cbSampleX == 4 || (pProps->cbSampleX == 8 && pProps->fRaw),
1027 ("%u\n", pProps->cbSampleX), false);
1028 AssertMsgReturn(pProps->cbFrame == pProps->cbSampleX * pProps->cChannelsX,
1029 ("cbFrame=%u cbSample=%u cChannels=%u\n", pProps->cbFrame, pProps->cbSampleX, pProps->cChannelsX),
1030 false);
1031 AssertMsgReturn(pProps->uHz >= 1000 && pProps->uHz < 1000000, ("%u\n", pProps->uHz), false);
1032 AssertMsgReturn(pProps->cShiftX == PDMAUDIOPCMPROPS_MAKE_SHIFT(pProps),
1033 ("cShift=%u cbSample=%u cChannels=%u\n", pProps->cShiftX, pProps->cbSampleX, pProps->cChannelsX),
1034 false);
1035 AssertReturn(!pProps->fRaw || (pProps->fSigned && pProps->cbSampleX == sizeof(int64_t)), false);
1036 return true;
1037}
1038
1039/**
1040 * Get number of bytes per frame.
1041 *
1042 * @returns Number of bytes per audio frame.
1043 * @param pProps PCM properties to use.
1044 * @sa PDMAUDIOPCMPROPS_F2B
1045 */
1046DECLINLINE(uint32_t) PDMAudioPropsBytesPerFrame(PCPDMAUDIOPCMPROPS pProps)
1047{
1048 return PDMAUDIOPCMPROPS_F2B(pProps, 1 /*cFrames*/);
1049}
1050
1051/**
1052 * Prints PCM properties to the debug log.
1053 *
1054 * @param pProps PCM properties to use.
1055 */
1056DECLINLINE(void) PDMAudioPropsLog(PCPDMAUDIOPCMPROPS pProps)
1057{
1058 AssertPtrReturnVoid(pProps);
1059
1060 Log(("uHz=%RU32, cChannels=%RU8, cBits=%RU8%s",
1061 pProps->uHz, pProps->cChannelsX, pProps->cbSampleX * 8, pProps->fSigned ? "S" : "U"));
1062}
1063
1064/** Max necessary buffer space for PDMAudioPropsToString */
1065#define PDMAUDIOPROPSTOSTRING_MAX sizeof("16ch S64 4294967296Hz swap raw")
1066
1067/**
1068 * Formats the PCM audio properties into a string buffer.
1069 *
1070 * @returns pszDst
1071 * @param pProps PCM properties to use.
1072 * @param pszDst The destination buffer.
1073 * @param cchDst The size of the destination buffer. Recommended to be at
1074 * least PDMAUDIOPROPSTOSTRING_MAX bytes.
1075 */
1076DECLINLINE(char *) PDMAudioPropsToString(PCPDMAUDIOPCMPROPS pProps, char *pszDst, size_t cchDst)
1077{
1078 /* 2ch S64 44100Hz swap raw */
1079 RTStrPrintf(pszDst, cchDst, "%uch %c%u %RU32Hz%s%s",
1080 PDMAudioPropsChannels(pProps), PDMAudioPropsIsSigned(pProps) ? 'S' : 'U', PDMAudioPropsSampleBits(pProps),
1081 PDMAudioPropsHz(pProps), pProps->fSwapEndian ? " swap" : "", pProps->fRaw ? " raw" : "");
1082 return pszDst;
1083}
1084
1085
1086
1087/** @} */
1088
1089#endif /* !VBOX_INCLUDED_vmm_pdmaudioinline_h */
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