VirtualBox

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

Last change on this file since 89797 was 89768, checked in by vboxsync, 4 years ago

Audio: Made PDMAUDIOVOLUME multichannel. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.3 KB
Line 
1/* $Id: pdmaudioinline.h 89768 2021-06-17 23:03:19Z 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
57/**
58 * Gets the name of an audio direction enum value.
59 *
60 * @returns Pointer to read-only name string on success, "bad" if passed an
61 * invalid enum value.
62 * @param enmDir The audio direction value to name.
63 */
64DECLINLINE(const char *) PDMAudioDirGetName(PDMAUDIODIR enmDir)
65{
66 switch (enmDir)
67 {
68 case PDMAUDIODIR_UNKNOWN: return "unknown";
69 case PDMAUDIODIR_IN: return "input";
70 case PDMAUDIODIR_OUT: return "output";
71 case PDMAUDIODIR_DUPLEX: return "duplex";
72
73 /* no default */
74 case PDMAUDIODIR_END:
75 case PDMAUDIODIR_INVALID:
76 case PDMAUDIODIR_32BIT_HACK:
77 break;
78 }
79 AssertMsgFailedReturn(("Invalid audio direction %d\n", enmDir), "bad");
80}
81
82/**
83 * Gets the name of an audio mixer control enum value.
84 *
85 * @returns Pointer to read-only name, "bad" if invalid input.
86 * @param enmMixerCtl The audio mixer control value.
87 */
88DECLINLINE(const char *) PDMAudioMixerCtlGetName(PDMAUDIOMIXERCTL enmMixerCtl)
89{
90 switch (enmMixerCtl)
91 {
92 case PDMAUDIOMIXERCTL_UNKNOWN: return "Unknown";
93 case PDMAUDIOMIXERCTL_VOLUME_MASTER: return "Master Volume";
94 case PDMAUDIOMIXERCTL_FRONT: return "Front";
95 case PDMAUDIOMIXERCTL_CENTER_LFE: return "Center / LFE";
96 case PDMAUDIOMIXERCTL_REAR: return "Rear";
97 case PDMAUDIOMIXERCTL_LINE_IN: return "Line-In";
98 case PDMAUDIOMIXERCTL_MIC_IN: return "Microphone-In";
99 /* no default */
100 case PDMAUDIOMIXERCTL_END:
101 case PDMAUDIOMIXERCTL_INVALID:
102 case PDMAUDIOMIXERCTL_32BIT_HACK:
103 break;
104 }
105 AssertMsgFailedReturn(("Invalid mixer control %ld\n", enmMixerCtl), "bad");
106}
107
108/**
109 * Gets the name of a path enum value.
110 *
111 * @returns Pointer to read-only name, "bad" if invalid input.
112 * @param enmPath The path value to name.
113 */
114DECLINLINE(const char *) PDMAudioPathGetName(PDMAUDIOPATH enmPath)
115{
116 switch (enmPath)
117 {
118 case PDMAUDIOPATH_UNKNOWN: return "unknown";
119
120 case PDMAUDIOPATH_OUT_FRONT: return "front";
121 case PDMAUDIOPATH_OUT_CENTER_LFE: return "center-lfe";
122 case PDMAUDIOPATH_OUT_REAR: return "rear";
123
124 case PDMAUDIOPATH_IN_MIC: return "mic";
125 case PDMAUDIOPATH_IN_CD: return "cd";
126 case PDMAUDIOPATH_IN_VIDEO: return "video-in";
127 case PDMAUDIOPATH_IN_AUX: return "aux-in";
128 case PDMAUDIOPATH_IN_LINE: return "line-in";
129 case PDMAUDIOPATH_IN_PHONE: return "phone";
130
131 /* no default */
132 case PDMAUDIOPATH_INVALID:
133 case PDMAUDIOPATH_END:
134 case PDMAUDIOPATH_32BIT_HACK:
135 break;
136 }
137 AssertMsgFailedReturn(("Unknown enmPath=%d\n", enmPath), "bad");
138}
139
140/**
141 * Gets the name of a channel.
142 *
143 * @returns Pointer to read-only name, "bad" if invalid input.
144 * @param enmChannelId The channel ID to name.
145 */
146DECLINLINE(const char *) PDMAudioChannelIdGetName(PDMAUDIOCHANNELID enmChannelId)
147{
148 switch (enmChannelId)
149 {
150 case PDMAUDIOCHANNELID_INVALID: return "invalid";
151 case PDMAUDIOCHANNELID_UNUSED_ZERO: return "unused-zero";
152 case PDMAUDIOCHANNELID_UNUSED_SILENCE: return "unused-silence";
153 case PDMAUDIOCHANNELID_UNKNOWN: return "unknown";
154
155 case PDMAUDIOCHANNELID_FRONT_LEFT: return "FL";
156 case PDMAUDIOCHANNELID_FRONT_RIGHT: return "FR";
157 case PDMAUDIOCHANNELID_FRONT_CENTER: return "FC";
158 case PDMAUDIOCHANNELID_LFE: return "LFE";
159 case PDMAUDIOCHANNELID_REAR_LEFT: return "BL";
160 case PDMAUDIOCHANNELID_REAR_RIGHT: return "BR";
161 case PDMAUDIOCHANNELID_FRONT_LEFT_OF_CENTER: return "FLC";
162 case PDMAUDIOCHANNELID_FRONT_RIGHT_OF_CENTER: return "FRC";
163 case PDMAUDIOCHANNELID_REAR_CENTER: return "BC";
164 case PDMAUDIOCHANNELID_SIDE_LEFT: return "SL";
165 case PDMAUDIOCHANNELID_SIDE_RIGHT: return "SR";
166 case PDMAUDIOCHANNELID_TOP_CENTER: return "TC";
167 case PDMAUDIOCHANNELID_FRONT_LEFT_HEIGHT: return "TFL";
168 case PDMAUDIOCHANNELID_FRONT_CENTER_HEIGHT: return "TFC";
169 case PDMAUDIOCHANNELID_FRONT_RIGHT_HEIGHT: return "TFR";
170 case PDMAUDIOCHANNELID_REAR_LEFT_HEIGHT: return "TBL";
171 case PDMAUDIOCHANNELID_REAR_CENTER_HEIGHT: return "TBC";
172 case PDMAUDIOCHANNELID_REAR_RIGHT_HEIGHT: return "TBR";
173
174 /* no default */
175 case PDMAUDIOCHANNELID_END:
176 case PDMAUDIOCHANNELID_32BIT_HACK:
177 break;
178 }
179 AssertMsgFailedReturn(("Unknown enmChannelId=%d\n", enmChannelId), "bad");
180}
181
182
183/*********************************************************************************************************************************
184* Volume Helpers *
185*********************************************************************************************************************************/
186
187/**
188 * Initializes a PDMAUDIOVOLUME structure to max.
189 *
190 * @param pVol The structure to initialize.
191 */
192DECLINLINE(void) PDMAudioVolumeInitMax(PPDMAUDIOVOLUME pVol)
193{
194 pVol->fMuted = false;
195 for (uintptr_t i = 0; i < RT_ELEMENTS(pVol->auChannels); i++)
196 pVol->auChannels[i] = PDMAUDIO_VOLUME_MAX;
197}
198
199
200/**
201 * Initializes a PDMAUDIOVOLUME structure from a simple stereo setting.
202 *
203 * The additional channels will simply be assigned the higer of the two.
204 *
205 * @param pVol The structure to initialize.
206 * @param fMuted Muted.
207 * @param bLeft The left channel volume.
208 * @param bRight The right channel volume.
209 */
210DECLINLINE(void) PDMAudioVolumeInitFromStereo(PPDMAUDIOVOLUME pVol, bool fMuted, uint8_t bLeft, uint8_t bRight)
211{
212 pVol->fMuted = fMuted;
213 pVol->auChannels[0] = bLeft;
214 pVol->auChannels[1] = bRight;
215
216 uint8_t const bOther = RT_MAX(bLeft, bRight);
217 for (uintptr_t i = 2; i < RT_ELEMENTS(pVol->auChannels); i++)
218 pVol->auChannels[i] = bOther;
219}
220
221
222/**
223 * Combines two volume settings (typically master and sink).
224 *
225 * @param pVol Where to return the combined volume
226 * @param pVol1 The first volume settings to combine.
227 * @param pVol2 The second volume settings.
228 */
229DECLINLINE(void) PDMAudioVolumeCombine(PPDMAUDIOVOLUME pVol, PCPDMAUDIOVOLUME pVol1, PCPDMAUDIOVOLUME pVol2)
230{
231 if (pVol1->fMuted || pVol2->fMuted)
232 {
233 pVol->fMuted = true;
234 for (uintptr_t i = 0; i < RT_ELEMENTS(pVol->auChannels); i++)
235 pVol->auChannels[i] = 0;
236 }
237 else
238 {
239 pVol->fMuted = false;
240 /** @todo Very crude implementation for now -- needs more work! (At least
241 * when used in audioMixerSinkUpdateVolume it was considered as such.) */
242 for (uintptr_t i = 0; i < RT_ELEMENTS(pVol->auChannels); i++)
243 {
244#if 0 /* bird: I think the shift variant should produce the exact same result, w/o two conditionals per iteration. */
245 /* 255 * 255 / 255 = 0xFF (255) */
246 /* 17 * 127 / 255 = 8 */
247 /* 39 * 39 / 255 = 5 */
248 pVol->auChannels[i] = (uint8_t)( (RT_MAX(pVol1->auChannels[i], 1U) * RT_MAX(pVol2->auChannels[i], 1U))
249 / PDMAUDIO_VOLUME_MAX);
250#else
251 /* (((255 + 1) * (255 + 1)) >> 8) - 1 = 0xFF (255) */
252 /* ((( 17 + 1) * (127 + 1)) >> 8) - 1 = 0x8 (8) */
253 /* ((( 39 + 1) * ( 39 + 1)) >> 8) - 1 = 0x5 (5) */
254 pVol->auChannels[i] = (uint8_t)((((1U + pVol1->auChannels[i]) * (1U + pVol2->auChannels[i])) >> 8) - 1U);
255#endif
256 }
257 }
258}
259
260
261/*********************************************************************************************************************************
262* PCM Property Helpers *
263*********************************************************************************************************************************/
264
265/**
266 * Assigns default channel IDs according to the channel count.
267 *
268 * The assignments are taken from the standard speaker channel layouts table
269 * in the wikipedia article on surround sound:
270 * https://en.wikipedia.org/wiki/Surround_sound#Standard_speaker_channels
271 */
272DECLINLINE(void) PDMAudioPropsSetDefaultChannelIds(PPDMAUDIOPCMPROPS pProps)
273{
274 unsigned cChannels = pProps->cChannelsX;
275 switch (cChannels)
276 {
277 case 1:
278 pProps->aidChannels[0] = PDMAUDIOCHANNELID_MONO;
279 break;
280 case 2:
281 pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
282 pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
283 break;
284 case 3: /* 2.1 */
285 pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
286 pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
287 pProps->aidChannels[2] = PDMAUDIOCHANNELID_LFE;
288 break;
289 case 4: /* 4.0 */
290 pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
291 pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
292 pProps->aidChannels[2] = PDMAUDIOCHANNELID_REAR_LEFT;
293 pProps->aidChannels[3] = PDMAUDIOCHANNELID_REAR_RIGHT;
294 break;
295 case 5: /* 4.1 */
296 pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
297 pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
298 pProps->aidChannels[2] = PDMAUDIOCHANNELID_FRONT_CENTER;
299 pProps->aidChannels[3] = PDMAUDIOCHANNELID_LFE;
300 pProps->aidChannels[4] = PDMAUDIOCHANNELID_REAR_CENTER;
301 break;
302 case 6: /* 5.1 */
303 pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
304 pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
305 pProps->aidChannels[2] = PDMAUDIOCHANNELID_FRONT_CENTER;
306 pProps->aidChannels[3] = PDMAUDIOCHANNELID_LFE;
307 pProps->aidChannels[4] = PDMAUDIOCHANNELID_REAR_LEFT;
308 pProps->aidChannels[5] = PDMAUDIOCHANNELID_REAR_RIGHT;
309 break;
310 case 7: /* 6.1 */
311 pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
312 pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
313 pProps->aidChannels[2] = PDMAUDIOCHANNELID_FRONT_CENTER;
314 pProps->aidChannels[3] = PDMAUDIOCHANNELID_LFE;
315 pProps->aidChannels[4] = PDMAUDIOCHANNELID_REAR_LEFT;
316 pProps->aidChannels[5] = PDMAUDIOCHANNELID_REAR_RIGHT;
317 pProps->aidChannels[6] = PDMAUDIOCHANNELID_REAR_CENTER;
318 break;
319 case 8: /* 7.1 */
320 pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
321 pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
322 pProps->aidChannels[2] = PDMAUDIOCHANNELID_FRONT_CENTER;
323 pProps->aidChannels[3] = PDMAUDIOCHANNELID_LFE;
324 pProps->aidChannels[4] = PDMAUDIOCHANNELID_REAR_LEFT;
325 pProps->aidChannels[5] = PDMAUDIOCHANNELID_REAR_RIGHT;
326 pProps->aidChannels[6] = PDMAUDIOCHANNELID_FRONT_LEFT_OF_CENTER;
327 pProps->aidChannels[7] = PDMAUDIOCHANNELID_FRONT_RIGHT_OF_CENTER;
328 break;
329 case 9: /* 9.0 */
330 pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
331 pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
332 pProps->aidChannels[2] = PDMAUDIOCHANNELID_FRONT_CENTER;
333 pProps->aidChannels[3] = PDMAUDIOCHANNELID_REAR_LEFT;
334 pProps->aidChannels[4] = PDMAUDIOCHANNELID_REAR_RIGHT;
335 pProps->aidChannels[5] = PDMAUDIOCHANNELID_SIDE_LEFT;
336 pProps->aidChannels[6] = PDMAUDIOCHANNELID_SIDE_RIGHT;
337 pProps->aidChannels[7] = PDMAUDIOCHANNELID_FRONT_LEFT_HEIGHT;
338 pProps->aidChannels[8] = PDMAUDIOCHANNELID_FRONT_RIGHT_HEIGHT;
339 break;
340 case 10: /* 9.1 */
341 pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
342 pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
343 pProps->aidChannels[2] = PDMAUDIOCHANNELID_FRONT_CENTER;
344 pProps->aidChannels[3] = PDMAUDIOCHANNELID_LFE;
345 pProps->aidChannels[4] = PDMAUDIOCHANNELID_REAR_LEFT;
346 pProps->aidChannels[5] = PDMAUDIOCHANNELID_REAR_RIGHT;
347 pProps->aidChannels[6] = PDMAUDIOCHANNELID_SIDE_LEFT;
348 pProps->aidChannels[7] = PDMAUDIOCHANNELID_SIDE_RIGHT;
349 pProps->aidChannels[8] = PDMAUDIOCHANNELID_FRONT_LEFT_HEIGHT;
350 pProps->aidChannels[9] = PDMAUDIOCHANNELID_FRONT_RIGHT_HEIGHT;
351 break;
352 case 11: /* 11.0 */
353 pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
354 pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
355 pProps->aidChannels[2] = PDMAUDIOCHANNELID_FRONT_CENTER;
356 pProps->aidChannels[3] = PDMAUDIOCHANNELID_REAR_LEFT;
357 pProps->aidChannels[4] = PDMAUDIOCHANNELID_REAR_RIGHT;
358 pProps->aidChannels[5] = PDMAUDIOCHANNELID_FRONT_LEFT_OF_CENTER;
359 pProps->aidChannels[6] = PDMAUDIOCHANNELID_FRONT_RIGHT_OF_CENTER;
360 pProps->aidChannels[7] = PDMAUDIOCHANNELID_SIDE_LEFT;
361 pProps->aidChannels[8] = PDMAUDIOCHANNELID_SIDE_RIGHT;
362 pProps->aidChannels[9] = PDMAUDIOCHANNELID_FRONT_LEFT_HEIGHT;
363 pProps->aidChannels[10]= PDMAUDIOCHANNELID_FRONT_RIGHT_HEIGHT;
364 break;
365 default:
366 AssertFailed();
367 cChannels = 12;
368 RT_FALL_THROUGH();
369 case 12: /* 11.1 */
370 pProps->aidChannels[0] = PDMAUDIOCHANNELID_FRONT_LEFT;
371 pProps->aidChannels[1] = PDMAUDIOCHANNELID_FRONT_RIGHT;
372 pProps->aidChannels[2] = PDMAUDIOCHANNELID_FRONT_CENTER;
373 pProps->aidChannels[3] = PDMAUDIOCHANNELID_LFE;
374 pProps->aidChannels[4] = PDMAUDIOCHANNELID_REAR_LEFT;
375 pProps->aidChannels[5] = PDMAUDIOCHANNELID_REAR_RIGHT;
376 pProps->aidChannels[6] = PDMAUDIOCHANNELID_FRONT_LEFT_OF_CENTER;
377 pProps->aidChannels[7] = PDMAUDIOCHANNELID_FRONT_RIGHT_OF_CENTER;
378 pProps->aidChannels[8] = PDMAUDIOCHANNELID_SIDE_LEFT;
379 pProps->aidChannels[9] = PDMAUDIOCHANNELID_SIDE_RIGHT;
380 pProps->aidChannels[10]= PDMAUDIOCHANNELID_FRONT_LEFT_HEIGHT;
381 pProps->aidChannels[11]= PDMAUDIOCHANNELID_FRONT_RIGHT_HEIGHT;
382 break;
383 case 0:
384 break;
385 }
386 AssertCompile(RT_ELEMENTS(pProps->aidChannels) >= 12);
387
388 while (cChannels < RT_ELEMENTS(pProps->aidChannels))
389 pProps->aidChannels[cChannels++] = PDMAUDIOCHANNELID_INVALID;
390}
391
392
393/**
394 * Initialize PCM audio properties.
395 */
396DECLINLINE(void) PDMAudioPropsInit(PPDMAUDIOPCMPROPS pProps, uint8_t cbSample, bool fSigned, uint8_t cChannels, uint32_t uHz)
397{
398 pProps->cbFrame = cbSample * cChannels;
399 pProps->cbSampleX = cbSample;
400 pProps->cChannelsX = cChannels;
401 pProps->cShiftX = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(cbSample, cChannels);
402 pProps->fSigned = fSigned;
403 pProps->fSwapEndian = false;
404 pProps->fRaw = false;
405 pProps->uHz = uHz;
406
407 Assert(pProps->cbFrame == (uint32_t)cbSample * cChannels);
408 Assert(pProps->cbSampleX == cbSample);
409 Assert(pProps->cChannelsX == cChannels);
410
411 PDMAudioPropsSetDefaultChannelIds(pProps);
412}
413
414/**
415 * Initialize PCM audio properties, extended version.
416 */
417DECLINLINE(void) PDMAudioPropsInitEx(PPDMAUDIOPCMPROPS pProps, uint8_t cbSample, bool fSigned, uint8_t cChannels, uint32_t uHz,
418 bool fLittleEndian, bool fRaw)
419{
420 Assert(!fRaw || cbSample == sizeof(int64_t));
421 pProps->cbFrame = cbSample * cChannels;
422 pProps->cbSampleX = cbSample;
423 pProps->cChannelsX = cChannels;
424 pProps->cShiftX = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(cbSample, cChannels);
425 pProps->fSigned = fSigned;
426#ifdef RT_LITTLE_ENDIAN
427 pProps->fSwapEndian = !fLittleEndian;
428#else
429 pProps->fSwapEndian = fLittleEndian;
430#endif
431 pProps->fRaw = fRaw;
432 pProps->uHz = uHz;
433
434 Assert(pProps->cbFrame == (uint32_t)cbSample * cChannels);
435 Assert(pProps->cbSampleX == cbSample);
436 Assert(pProps->cChannelsX == cChannels);
437
438 PDMAudioPropsSetDefaultChannelIds(pProps);
439}
440
441/**
442 * Modifies the channel count.
443 *
444 * @note This will reset the channel IDs to defaults.
445 *
446 * @param pProps The PCM properties to update.
447 * @param cChannels The new channel count.
448 */
449DECLINLINE(void) PDMAudioPropsSetChannels(PPDMAUDIOPCMPROPS pProps, uint8_t cChannels)
450{
451 Assert(cChannels > 0); Assert(cChannels < 16);
452 pProps->cChannelsX = cChannels;
453 pProps->cbFrame = pProps->cbSampleX * cChannels;
454 pProps->cShiftX = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pProps->cbSampleX, cChannels);
455
456 PDMAudioPropsSetDefaultChannelIds(pProps);
457}
458
459/**
460 * Modifies the sample size.
461 *
462 * @param pProps The PCM properties to update.
463 * @param cbSample The new sample size (in bytes).
464 */
465DECLINLINE(void) PDMAudioPropsSetSampleSize(PPDMAUDIOPCMPROPS pProps, uint8_t cbSample)
466{
467 Assert(cbSample == 1 || cbSample == 2 || cbSample == 4 || cbSample == 8);
468 pProps->cbSampleX = cbSample;
469 pProps->cbFrame = cbSample * pProps->cChannelsX;
470 pProps->cShiftX = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(cbSample, pProps->cChannelsX);
471}
472
473/**
474 * Gets the bitrate.
475 *
476 * Divide the result by 8 to get the byte rate.
477 *
478 * @returns Bit rate.
479 * @param pProps PCM properties to calculate bitrate for.
480 */
481DECLINLINE(uint32_t) PDMAudioPropsGetBitrate(PCPDMAUDIOPCMPROPS pProps)
482{
483 Assert(pProps->cbFrame == pProps->cbSampleX * pProps->cChannelsX);
484 return pProps->cbFrame * pProps->uHz * 8;
485}
486
487/**
488 * Gets the number of channels.
489 * @returns The channel count.
490 * @param pProps The PCM properties.
491 */
492DECL_FORCE_INLINE(uint8_t) PDMAudioPropsChannels(PCPDMAUDIOPCMPROPS pProps)
493{
494 return pProps->cChannelsX;
495}
496
497/**
498 * Gets the sample size in bytes.
499 * @returns Number of bytes per sample.
500 * @param pProps The PCM properties.
501 */
502DECL_FORCE_INLINE(uint8_t) PDMAudioPropsSampleSize(PCPDMAUDIOPCMPROPS pProps)
503{
504 return pProps->cbSampleX;
505}
506
507/**
508 * Gets the sample size in bits.
509 * @returns Number of bits per sample.
510 * @param pProps The PCM properties.
511 */
512DECLINLINE(uint8_t) PDMAudioPropsSampleBits(PCPDMAUDIOPCMPROPS pProps)
513{
514 return pProps->cbSampleX * 8;
515}
516
517/**
518 * Gets the frame size in bytes.
519 * @returns Number of bytes per frame.
520 * @param pProps The PCM properties.
521 */
522DECL_FORCE_INLINE(uint8_t) PDMAudioPropsFrameSize(PCPDMAUDIOPCMPROPS pProps)
523{
524 return pProps->cbFrame;
525}
526
527/**
528 * Gets the frequency.
529 * @returns Frequency.
530 * @param pProps The PCM properties.
531 */
532DECL_FORCE_INLINE(uint32_t) PDMAudioPropsHz(PCPDMAUDIOPCMPROPS pProps)
533{
534 return pProps->uHz;
535}
536
537/**
538 * Checks if the format is signed or unsigned.
539 * @returns true if signed, false if unsigned.
540 * @param pProps The PCM properties.
541 */
542DECL_FORCE_INLINE(bool) PDMAudioPropsIsSigned(PCPDMAUDIOPCMPROPS pProps)
543{
544 return pProps->fSigned;
545}
546
547/**
548 * Checks if the format is little-endian or not.
549 * @returns true if little-endian (or if 8-bit), false if big-endian.
550 * @param pProps The PCM properties.
551 */
552DECL_FORCE_INLINE(bool) PDMAudioPropsIsLittleEndian(PCPDMAUDIOPCMPROPS pProps)
553{
554#ifdef RT_LITTLE_ENDIAN
555 return !pProps->fSwapEndian || pProps->cbSampleX < 2;
556#else
557 return pProps->fSwapEndian || pProps->cbSampleX < 2;
558#endif
559}
560
561/**
562 * Checks if the format is big-endian or not.
563 * @returns true if big-endian (or if 8-bit), false if little-endian.
564 * @param pProps The PCM properties.
565 */
566DECL_FORCE_INLINE(bool) PDMAudioPropsIsBigEndian(PCPDMAUDIOPCMPROPS pProps)
567{
568#ifdef RT_LITTLE_ENDIAN
569 return pProps->fSwapEndian || pProps->cbSampleX < 2;
570#else
571 return !pProps->fSwapEndian || pProps->cbSampleX < 2;
572#endif
573}
574
575/**
576 * Rounds down the given byte amount to the nearest frame boundrary.
577 *
578 * @returns Rounded byte amount.
579 * @param pProps PCM properties to use.
580 * @param cb The size (in bytes) to round.
581 */
582DECLINLINE(uint32_t) PDMAudioPropsFloorBytesToFrame(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
583{
584 AssertPtrReturn(pProps, 0);
585 return PDMAUDIOPCMPROPS_F2B(pProps, PDMAUDIOPCMPROPS_B2F(pProps, cb));
586}
587
588/**
589 * Rounds up the given byte amount to the nearest frame boundrary.
590 *
591 * @returns Rounded byte amount.
592 * @param pProps PCM properties to use.
593 * @param cb The size (in bytes) to round.
594 */
595DECLINLINE(uint32_t) PDMAudioPropsRoundUpBytesToFrame(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
596{
597 AssertPtrReturn(pProps, 0);
598 uint32_t const cbFrame = PDMAudioPropsFrameSize(pProps);
599 AssertReturn(cbFrame, 0);
600 return PDMAUDIOPCMPROPS_F2B(pProps, PDMAUDIOPCMPROPS_B2F(pProps, cb + cbFrame - 1));
601}
602
603/**
604 * Checks if the given size is aligned on a frame boundrary.
605 *
606 * @returns @c true if properly aligned, @c false if not.
607 * @param pProps PCM properties to use.
608 * @param cb The size (in bytes) to check.
609 */
610DECLINLINE(bool) PDMAudioPropsIsSizeAligned(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
611{
612 AssertPtrReturn(pProps, false);
613 uint32_t const cbFrame = PDMAudioPropsFrameSize(pProps);
614 AssertReturn(cbFrame, false);
615 return cb % cbFrame == 0;
616}
617
618/**
619 * Converts bytes to frames (rounding down of course).
620 *
621 * @returns Number of frames.
622 * @param pProps PCM properties to use.
623 * @param cb The number of bytes to convert.
624 */
625DECLINLINE(uint32_t) PDMAudioPropsBytesToFrames(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
626{
627 AssertPtrReturn(pProps, 0);
628 return PDMAUDIOPCMPROPS_B2F(pProps, cb);
629}
630
631/**
632 * Converts bytes to milliseconds.
633 *
634 * @return Number milliseconds @a cb takes to play or record.
635 * @param pProps PCM properties to use.
636 * @param cb The number of bytes to convert.
637 *
638 * @note Rounds up the result.
639 */
640DECLINLINE(uint64_t) PDMAudioPropsBytesToMilli(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
641{
642 AssertPtrReturn(pProps, 0);
643
644 /* Check parameters to prevent division by chainsaw: */
645 uint32_t const uHz = pProps->uHz;
646 if (uHz)
647 {
648 const unsigned cbFrame = PDMAudioPropsFrameSize(pProps);
649 if (cbFrame)
650 {
651 /* Round cb up to closest frame size: */
652 cb = (cb + cbFrame - 1) / cbFrame;
653
654 /* Convert to milliseconds. */
655 return (cb * (uint64_t)RT_MS_1SEC + uHz - 1) / uHz;
656 }
657 }
658 return 0;
659}
660
661/**
662 * Converts bytes to microseconds.
663 *
664 * @return Number microseconds @a cb takes to play or record.
665 * @param pProps PCM properties to use.
666 * @param cb The number of bytes to convert.
667 *
668 * @note Rounds up the result.
669 */
670DECLINLINE(uint64_t) PDMAudioPropsBytesToMicro(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
671{
672 AssertPtrReturn(pProps, 0);
673
674 /* Check parameters to prevent division by chainsaw: */
675 uint32_t const uHz = pProps->uHz;
676 if (uHz)
677 {
678 const unsigned cbFrame = PDMAudioPropsFrameSize(pProps);
679 if (cbFrame)
680 {
681 /* Round cb up to closest frame size: */
682 cb = (cb + cbFrame - 1) / cbFrame;
683
684 /* Convert to microseconds. */
685 return (cb * (uint64_t)RT_US_1SEC + uHz - 1) / uHz;
686 }
687 }
688 return 0;
689}
690
691/**
692 * Converts bytes to nanoseconds.
693 *
694 * @return Number nanoseconds @a cb takes to play or record.
695 * @param pProps PCM properties to use.
696 * @param cb The number of bytes to convert.
697 *
698 * @note Rounds up the result.
699 */
700DECLINLINE(uint64_t) PDMAudioPropsBytesToNano(PCPDMAUDIOPCMPROPS pProps, uint32_t cb)
701{
702 AssertPtrReturn(pProps, 0);
703
704 /* Check parameters to prevent division by chainsaw: */
705 uint32_t const uHz = pProps->uHz;
706 if (uHz)
707 {
708 const unsigned cbFrame = PDMAudioPropsFrameSize(pProps);
709 if (cbFrame)
710 {
711 /* Round cb up to closest frame size: */
712 cb = (cb + cbFrame - 1) / cbFrame;
713
714 /* Convert to nanoseconds. */
715 return (cb * (uint64_t)RT_NS_1SEC + uHz - 1) / uHz;
716 }
717 }
718 return 0;
719}
720
721/**
722 * Converts bytes to nanoseconds, 64-bit version.
723 *
724 * @return Number nanoseconds @a cb takes to play or record.
725 * @param pProps PCM properties to use.
726 * @param cb The number of bytes to convert (64-bit).
727 *
728 * @note Rounds up the result.
729 */
730DECLINLINE(uint64_t) PDMAudioPropsBytesToNano64(PCPDMAUDIOPCMPROPS pProps, uint64_t cb)
731{
732 AssertPtrReturn(pProps, 0);
733
734 /* Check parameters to prevent division by chainsaw: */
735 uint32_t const uHz = pProps->uHz;
736 if (uHz)
737 {
738 const unsigned cbFrame = PDMAudioPropsFrameSize(pProps);
739 if (cbFrame)
740 {
741 /* Round cb up to closest frame size: */
742 cb = (cb + cbFrame - 1) / cbFrame;
743
744 /* Convert to nanoseconds. */
745 return (cb * RT_NS_1SEC + uHz - 1) / uHz;
746 }
747 }
748 return 0;
749}
750
751/**
752 * Converts frames to bytes.
753 *
754 * @returns Number of bytes.
755 * @param pProps The PCM properties to use.
756 * @param cFrames Number of audio frames to convert.
757 * @sa PDMAUDIOPCMPROPS_F2B
758 */
759DECLINLINE(uint32_t) PDMAudioPropsFramesToBytes(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
760{
761 AssertPtrReturn(pProps, 0);
762 return PDMAUDIOPCMPROPS_F2B(pProps, cFrames);
763}
764
765/**
766 * Converts frames to milliseconds.
767 *
768 * @returns milliseconds.
769 * @param pProps The PCM properties to use.
770 * @param cFrames Number of audio frames to convert.
771 * @note No rounding here, result is floored.
772 */
773DECLINLINE(uint64_t) PDMAudioPropsFramesToMilli(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
774{
775 AssertPtrReturn(pProps, 0);
776
777 /* Check input to prevent division by chainsaw: */
778 uint32_t const uHz = pProps->uHz;
779 if (uHz)
780 return ASMMultU32ByU32DivByU32(cFrames, RT_MS_1SEC, uHz);
781 return 0;
782}
783
784/**
785 * Converts frames to milliseconds, but not returning more than @a cMsMax
786 *
787 * This is a convenience for logging and such.
788 *
789 * @returns milliseconds (32-bit).
790 * @param pProps The PCM properties to use.
791 * @param cFrames Number of audio frames to convert.
792 * @param cMsMax Max return value (32-bit).
793 * @note No rounding here, result is floored.
794 */
795DECLINLINE(uint32_t) PDMAudioPropsFramesToMilliMax(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames, uint32_t cMsMax)
796{
797 AssertPtrReturn(pProps, 0);
798
799 /* Check input to prevent division by chainsaw: */
800 uint32_t const uHz = pProps->uHz;
801 if (uHz)
802 {
803 uint32_t const cMsResult = ASMMultU32ByU32DivByU32(cFrames, RT_MS_1SEC, uHz);
804 return RT_MIN(cMsResult, cMsMax);
805 }
806 return 0;
807}
808
809/**
810 * Converts frames to microseconds.
811 *
812 * @returns microseconds.
813 * @param pProps The PCM properties to use.
814 * @param cFrames Number of audio frames to convert.
815 * @note No rounding here, result is floored.
816 */
817DECLINLINE(uint64_t) PDMAudioPropsFramesToMicro(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
818{
819 AssertPtrReturn(pProps, 0);
820
821 /* Check input to prevent division by chainsaw: */
822 uint32_t const uHz = pProps->uHz;
823 if (uHz)
824 return ASMMultU32ByU32DivByU32(cFrames, RT_US_1SEC, uHz);
825 return 0;
826}
827
828/**
829 * Converts frames to nanoseconds.
830 *
831 * @returns Nanoseconds.
832 * @param pProps The PCM properties to use.
833 * @param cFrames Number of audio frames to convert.
834 * @note No rounding here, result is floored.
835 */
836DECLINLINE(uint64_t) PDMAudioPropsFramesToNano(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
837{
838 AssertPtrReturn(pProps, 0);
839
840 /* Check input to prevent division by chainsaw: */
841 uint32_t const uHz = pProps->uHz;
842 if (uHz)
843 return ASMMultU32ByU32DivByU32(cFrames, RT_NS_1SEC, uHz);
844 return 0;
845}
846
847/**
848 * Converts frames to NT ticks (100 ns units).
849 *
850 * @returns NT ticks.
851 * @param pProps The PCM properties to use.
852 * @param cFrames Number of audio frames to convert.
853 * @note No rounding here, result is floored.
854 */
855DECLINLINE(uint64_t) PDMAudioPropsFramesToNtTicks(PCPDMAUDIOPCMPROPS pProps, uint32_t cFrames)
856{
857 AssertPtrReturn(pProps, 0);
858
859 /* Check input to prevent division by chainsaw: */
860 uint32_t const uHz = pProps->uHz;
861 if (uHz)
862 return ASMMultU32ByU32DivByU32(cFrames, RT_NS_1SEC / 100, uHz);
863 return 0;
864}
865
866/**
867 * Converts milliseconds to frames.
868 *
869 * @returns Number of frames
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) PDMAudioPropsMilliToFrames(PCPDMAUDIOPCMPROPS pProps, uint64_t cMs)
876{
877 AssertPtrReturn(pProps, 0);
878
879 uint32_t const uHz = pProps->uHz;
880 uint32_t cFrames;
881 if (cMs < RT_MS_1SEC)
882 cFrames = 0;
883 else
884 {
885 cFrames = cMs / RT_MS_1SEC * uHz;
886 cMs %= RT_MS_1SEC;
887 }
888 cFrames += (ASMMult2xU32RetU64(uHz, (uint32_t)cMs) + RT_MS_1SEC - 1) / RT_MS_1SEC;
889 return cFrames;
890}
891
892/**
893 * Converts milliseconds to bytes.
894 *
895 * @returns Number of bytes (frame aligned).
896 * @param pProps The PCM properties to use.
897 * @param cMs The number of milliseconds to convert.
898 *
899 * @note The result is rounded rather than floored (hysterical raisins).
900 */
901DECLINLINE(uint32_t) PDMAudioPropsMilliToBytes(PCPDMAUDIOPCMPROPS pProps, uint64_t cMs)
902{
903 return PDMAUDIOPCMPROPS_F2B(pProps, PDMAudioPropsMilliToFrames(pProps, cMs));
904}
905
906/**
907 * Converts nanoseconds to frames.
908 *
909 * @returns Number of frames.
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) PDMAudioPropsNanoToFrames(PCPDMAUDIOPCMPROPS pProps, uint64_t cNs)
916{
917 AssertPtrReturn(pProps, 0);
918
919 uint32_t const uHz = pProps->uHz;
920 uint32_t cFrames;
921 if (cNs < RT_NS_1SEC)
922 cFrames = 0;
923 else
924 {
925 cFrames = cNs / RT_NS_1SEC * uHz;
926 cNs %= RT_NS_1SEC;
927 }
928 cFrames += (ASMMult2xU32RetU64(uHz, (uint32_t)cNs) + RT_NS_1SEC - 1) / RT_NS_1SEC;
929 return cFrames;
930}
931
932/**
933 * Converts nanoseconds to frames, 64-bit return.
934 *
935 * @returns Number of frames (64-bit).
936 * @param pProps The PCM properties to use.
937 * @param cNs The number of nanoseconds to convert.
938 *
939 * @note The result is floored!
940 */
941DECLINLINE(uint64_t) PDMAudioPropsNanoToFrames64(PCPDMAUDIOPCMPROPS pProps, uint64_t cNs)
942{
943 AssertPtrReturn(pProps, 0);
944
945 uint32_t const uHz = pProps->uHz;
946 uint64_t cFrames;
947 if (cNs < RT_NS_1SEC)
948 cFrames = 0;
949 else
950 {
951 cFrames = cNs / RT_NS_1SEC * uHz;
952 cNs %= RT_NS_1SEC;
953 }
954 cFrames += ASMMult2xU32RetU64(uHz, (uint32_t)cNs) / RT_NS_1SEC;
955 return cFrames;
956}
957
958/**
959 * Converts nanoseconds to bytes.
960 *
961 * @returns Number of bytes (frame aligned).
962 * @param pProps The PCM properties to use.
963 * @param cNs The number of nanoseconds to convert.
964 *
965 * @note The result is rounded rather than floored (hysterical raisins).
966 */
967DECLINLINE(uint32_t) PDMAudioPropsNanoToBytes(PCPDMAUDIOPCMPROPS pProps, uint64_t cNs)
968{
969 return PDMAUDIOPCMPROPS_F2B(pProps, PDMAudioPropsNanoToFrames(pProps, cNs));
970}
971
972/**
973 * Converts nanoseconds to bytes, 64-bit version.
974 *
975 * @returns Number of bytes (frame aligned), 64-bit.
976 * @param pProps The PCM properties to use.
977 * @param cNs The number of nanoseconds to convert.
978 *
979 * @note The result is floored.
980 */
981DECLINLINE(uint64_t) PDMAudioPropsNanoToBytes64(PCPDMAUDIOPCMPROPS pProps, uint64_t cNs)
982{
983 return PDMAUDIOPCMPROPS_F2B(pProps, PDMAudioPropsNanoToFrames(pProps, cNs));
984}
985
986/**
987 * Clears a sample buffer by the given amount of audio frames with silence (according to the format
988 * given by the PCM properties).
989 *
990 * @param pProps The PCM properties to apply.
991 * @param pvBuf The buffer to clear.
992 * @param cbBuf The buffer size in bytes.
993 * @param cFrames The number of audio frames to clear. Capped at @a cbBuf
994 * if exceeding the buffer. If the size is an unaligned
995 * number of frames, the extra bytes may be left
996 * uninitialized in some configurations.
997 */
998DECLINLINE(void) PDMAudioPropsClearBuffer(PCPDMAUDIOPCMPROPS pProps, void *pvBuf, size_t cbBuf, uint32_t cFrames)
999{
1000 /*
1001 * Validate input
1002 */
1003 AssertPtrReturnVoid(pProps);
1004 Assert(pProps->cbSampleX);
1005 if (!cbBuf || !cFrames)
1006 return;
1007 AssertPtrReturnVoid(pvBuf);
1008
1009 /*
1010 * Decide how much needs clearing.
1011 */
1012 size_t cbToClear = PDMAudioPropsFramesToBytes(pProps, cFrames);
1013 AssertStmt(cbToClear <= cbBuf, cbToClear = cbBuf);
1014
1015 Log2Func(("pProps=%p, pvBuf=%p, cFrames=%RU32, fSigned=%RTbool, cbSample=%RU8\n",
1016 pProps, pvBuf, cFrames, pProps->fSigned, pProps->cbSampleX));
1017
1018 /*
1019 * Do the job.
1020 */
1021 if (pProps->fSigned)
1022 RT_BZERO(pvBuf, cbToClear);
1023 else /* Unsigned formats. */
1024 {
1025 switch (pProps->cbSampleX)
1026 {
1027 case 1: /* 8 bit */
1028 memset(pvBuf, 0x80, cbToClear);
1029 break;
1030
1031 case 2: /* 16 bit */
1032 {
1033 uint16_t *pu16Dst = (uint16_t *)pvBuf;
1034 uint16_t const u16Offset = !pProps->fSwapEndian ? UINT16_C(0x8000) : UINT16_C(0x80);
1035 cbBuf /= sizeof(*pu16Dst);
1036 while (cbBuf-- > 0)
1037 *pu16Dst++ = u16Offset;
1038 break;
1039 }
1040
1041 case 4: /* 32 bit */
1042 ASMMemFill32(pvBuf, cbToClear & ~(size_t)(sizeof(uint32_t) - 1),
1043 !pProps->fSwapEndian ? UINT32_C(0x80000000) : UINT32_C(0x80));
1044 break;
1045
1046 default:
1047 AssertMsgFailed(("Invalid bytes per sample: %RU8\n", pProps->cbSampleX));
1048 }
1049 }
1050}
1051
1052/**
1053 * Checks if the given buffer is silence.
1054 *
1055 * @param pProps The PCM properties to use checking the buffer.
1056 * @param pvBuf The buffer to check.
1057 * @param cbBuf The number of bytes to check (must be frame aligned).
1058 */
1059DECLINLINE(bool) PDMAudioPropsIsBufferSilence(PCPDMAUDIOPCMPROPS pProps, void const *pvBuf, size_t cbBuf)
1060{
1061 /*
1062 * Validate input
1063 */
1064 AssertPtrReturn(pProps, false);
1065 if (!cbBuf)
1066 return false;
1067 AssertPtrReturn(pvBuf, false);
1068
1069 /*
1070 * Do the job.
1071 */
1072 if (pProps->fSigned)
1073 return ASMMemIsZero(pvBuf, cbBuf);
1074
1075 switch (pProps->cbSampleX)
1076 {
1077 case 1: /* 8 bit */
1078 return ASMMemIsAllU8(pvBuf, cbBuf, 0x80);
1079
1080 case 2: /* 16 bit */
1081 {
1082 uint16_t const *pu16 = (uint16_t const *)pvBuf;
1083 uint16_t const u16Offset = !pProps->fSwapEndian ? UINT16_C(0x8000) : UINT16_C(0x80);
1084 cbBuf /= sizeof(*pu16);
1085 while (cbBuf-- > 0)
1086 if (*pu16 != u16Offset)
1087 return false;
1088 return true;
1089 }
1090
1091 case 4: /* 32 bit */
1092 {
1093 uint32_t const *pu32 = (uint32_t const *)pvBuf;
1094 uint32_t const u32Offset = !pProps->fSwapEndian ? UINT32_C(0x80000000) : UINT32_C(0x80);
1095 cbBuf /= sizeof(*pu32);
1096 while (cbBuf-- > 0)
1097 if (*pu32 != u32Offset)
1098 return false;
1099 return true;
1100 }
1101
1102 default:
1103 AssertMsgFailed(("Invalid bytes per sample: %RU8\n", pProps->cbSampleX));
1104 return false;
1105 }
1106}
1107
1108/**
1109 * Compares two sets of PCM properties.
1110 *
1111 * @returns @c true if the same, @c false if not.
1112 * @param pProps1 The first set of properties to compare.
1113 * @param pProps2 The second set of properties to compare.
1114 */
1115DECLINLINE(bool) PDMAudioPropsAreEqual(PCPDMAUDIOPCMPROPS pProps1, PCPDMAUDIOPCMPROPS pProps2)
1116{
1117 uintptr_t idxCh;
1118 AssertPtrReturn(pProps1, false);
1119 AssertPtrReturn(pProps2, false);
1120
1121 if (pProps1 == pProps2) /* If the pointers match, take a shortcut. */
1122 return true;
1123
1124 if (pProps1->uHz != pProps2->uHz)
1125 return false;
1126 if (pProps1->cChannelsX != pProps2->cChannelsX)
1127 return false;
1128 if (pProps1->cbSampleX != pProps2->cbSampleX)
1129 return false;
1130 if (pProps1->fSigned != pProps2->fSigned)
1131 return false;
1132 if (pProps1->fSwapEndian != pProps2->fSwapEndian)
1133 return false;
1134 if (pProps1->fRaw != pProps2->fRaw)
1135 return false;
1136
1137 idxCh = pProps1->cChannelsX;
1138 while (idxCh-- > 0)
1139 if (pProps1->aidChannels[idxCh] != pProps2->aidChannels[idxCh])
1140 return false;
1141
1142 return true;
1143}
1144
1145/**
1146 * Checks whether the given PCM properties are valid or not.
1147 *
1148 * @returns true/false accordingly.
1149 * @param pProps The PCM properties to check.
1150 *
1151 * @remarks This just performs a generic check of value ranges. Further, it
1152 * will assert if the input is invalid.
1153 *
1154 * @sa PDMAudioStrmCfgIsValid
1155 */
1156DECLINLINE(bool) PDMAudioPropsAreValid(PCPDMAUDIOPCMPROPS pProps)
1157{
1158 AssertPtrReturn(pProps, false);
1159
1160 AssertReturn(pProps->cChannelsX != 0, false);
1161 AssertReturn(pProps->cChannelsX <= PDMAUDIO_MAX_CHANNELS, false);
1162 AssertMsgReturn( pProps->cbSampleX == 1 || pProps->cbSampleX == 2 || pProps->cbSampleX == 4 || (pProps->cbSampleX == 8 && pProps->fRaw),
1163 ("%u\n", pProps->cbSampleX), false);
1164 AssertMsgReturn(pProps->cbFrame == pProps->cbSampleX * pProps->cChannelsX,
1165 ("cbFrame=%u cbSample=%u cChannels=%u\n", pProps->cbFrame, pProps->cbSampleX, pProps->cChannelsX),
1166 false);
1167 AssertMsgReturn(pProps->uHz >= 1000 && pProps->uHz < 1000000, ("%u\n", pProps->uHz), false);
1168 AssertMsgReturn(pProps->cShiftX == PDMAUDIOPCMPROPS_MAKE_SHIFT(pProps),
1169 ("cShift=%u cbSample=%u cChannels=%u\n", pProps->cShiftX, pProps->cbSampleX, pProps->cChannelsX),
1170 false);
1171 AssertReturn(!pProps->fRaw || (pProps->fSigned && pProps->cbSampleX == sizeof(int64_t)), false);
1172 return true;
1173}
1174
1175/**
1176 * Get number of bytes per frame.
1177 *
1178 * @returns Number of bytes per audio frame.
1179 * @param pProps PCM properties to use.
1180 * @sa PDMAUDIOPCMPROPS_F2B
1181 */
1182DECLINLINE(uint32_t) PDMAudioPropsBytesPerFrame(PCPDMAUDIOPCMPROPS pProps)
1183{
1184 return PDMAUDIOPCMPROPS_F2B(pProps, 1 /*cFrames*/);
1185}
1186
1187/**
1188 * Prints PCM properties to the debug log.
1189 *
1190 * @param pProps PCM properties to use.
1191 */
1192DECLINLINE(void) PDMAudioPropsLog(PCPDMAUDIOPCMPROPS pProps)
1193{
1194 AssertPtrReturnVoid(pProps);
1195
1196 Log(("uHz=%RU32, cChannels=%RU8, cBits=%RU8%s",
1197 pProps->uHz, pProps->cChannelsX, pProps->cbSampleX * 8, pProps->fSigned ? "S" : "U"));
1198}
1199
1200/** Max necessary buffer space for PDMAudioPropsToString */
1201#define PDMAUDIOPROPSTOSTRING_MAX sizeof("16ch S64 4294967296Hz swap raw")
1202
1203/**
1204 * Formats the PCM audio properties into a string buffer.
1205 *
1206 * @returns pszDst
1207 * @param pProps PCM properties to use.
1208 * @param pszDst The destination buffer.
1209 * @param cchDst The size of the destination buffer. Recommended to be at
1210 * least PDMAUDIOPROPSTOSTRING_MAX bytes.
1211 */
1212DECLINLINE(char *) PDMAudioPropsToString(PCPDMAUDIOPCMPROPS pProps, char *pszDst, size_t cchDst)
1213{
1214 /* 2ch S64 44100Hz swap raw */
1215 RTStrPrintf(pszDst, cchDst, "%uch %c%u %RU32Hz%s%s",
1216 PDMAudioPropsChannels(pProps), PDMAudioPropsIsSigned(pProps) ? 'S' : 'U', PDMAudioPropsSampleBits(pProps),
1217 PDMAudioPropsHz(pProps), pProps->fSwapEndian ? " swap" : "", pProps->fRaw ? " raw" : "");
1218 return pszDst;
1219}
1220
1221
1222/*********************************************************************************************************************************
1223* Stream Configuration Helpers *
1224*********************************************************************************************************************************/
1225
1226/**
1227 * Initializes a stream configuration from PCM properties.
1228 *
1229 * @returns VBox status code.
1230 * @param pCfg The stream configuration to initialize.
1231 * @param pProps The PCM properties to use.
1232 */
1233DECLINLINE(int) PDMAudioStrmCfgInitWithProps(PPDMAUDIOSTREAMCFG pCfg, PCPDMAUDIOPCMPROPS pProps)
1234{
1235 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
1236 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1237
1238 RT_ZERO(*pCfg);
1239 pCfg->Backend.cFramesPreBuffering = UINT32_MAX; /* Explicitly set to "undefined". */
1240
1241 memcpy(&pCfg->Props, pProps, sizeof(PDMAUDIOPCMPROPS));
1242
1243 return VINF_SUCCESS;
1244}
1245
1246/**
1247 * Checks whether stream configuration matches the given PCM properties.
1248 *
1249 * @returns @c true if equal, @c false if not.
1250 * @param pCfg The stream configuration.
1251 * @param pProps The PCM properties to match with.
1252 */
1253DECLINLINE(bool) PDMAudioStrmCfgMatchesProps(PCPDMAUDIOSTREAMCFG pCfg, PCPDMAUDIOPCMPROPS pProps)
1254{
1255 AssertPtrReturn(pCfg, false);
1256 return PDMAudioPropsAreEqual(pProps, &pCfg->Props);
1257}
1258
1259/**
1260 * Checks whether two stream configuration matches.
1261 *
1262 * @returns @c true if equal, @c false if not.
1263 * @param pCfg1 The first stream configuration.
1264 * @param pCfg2 The second stream configuration.
1265 */
1266DECLINLINE(bool) PDMAudioStrmCfgEquals(PCPDMAUDIOSTREAMCFG pCfg1, PCPDMAUDIOSTREAMCFG pCfg2)
1267{
1268 if (!pCfg1 || !pCfg2)
1269 return false;
1270 if (pCfg1 == pCfg2)
1271 return pCfg1 != NULL;
1272 if (PDMAudioPropsAreEqual(&pCfg1->Props, &pCfg2->Props))
1273 return pCfg1->enmDir == pCfg2->enmDir
1274 && pCfg1->enmPath == pCfg2->enmPath
1275 && pCfg1->Device.cMsSchedulingHint == pCfg2->Device.cMsSchedulingHint
1276 && pCfg1->Backend.cFramesPeriod == pCfg2->Backend.cFramesPeriod
1277 && pCfg1->Backend.cFramesBufferSize == pCfg2->Backend.cFramesBufferSize
1278 && pCfg1->Backend.cFramesPreBuffering == pCfg2->Backend.cFramesPreBuffering
1279 && strcmp(pCfg1->szName, pCfg2->szName) == 0;
1280 return false;
1281}
1282
1283/**
1284 * Frees an audio stream allocated by PDMAudioStrmCfgDup().
1285 *
1286 * @param pCfg The stream configuration to free.
1287 */
1288DECLINLINE(void) PDMAudioStrmCfgFree(PPDMAUDIOSTREAMCFG pCfg)
1289{
1290 if (pCfg)
1291 RTMemFree(pCfg);
1292}
1293
1294/**
1295 * Checks whether the given stream configuration is valid or not.
1296 *
1297 * @returns true/false accordingly.
1298 * @param pCfg Stream configuration to check.
1299 *
1300 * @remarks This just performs a generic check of value ranges. Further, it
1301 * will assert if the input is invalid.
1302 *
1303 * @sa PDMAudioPropsAreValid
1304 */
1305DECLINLINE(bool) PDMAudioStrmCfgIsValid(PCPDMAUDIOSTREAMCFG pCfg)
1306{
1307 AssertPtrReturn(pCfg, false);
1308 AssertMsgReturn(pCfg->enmDir >= PDMAUDIODIR_UNKNOWN && pCfg->enmDir < PDMAUDIODIR_END, ("%d\n", pCfg->enmDir), false);
1309 return PDMAudioPropsAreValid(&pCfg->Props);
1310}
1311
1312/**
1313 * Copies one stream configuration to another.
1314 *
1315 * @returns VBox status code.
1316 * @param pDstCfg The destination stream configuration.
1317 * @param pSrcCfg The source stream configuration.
1318 */
1319DECLINLINE(int) PDMAudioStrmCfgCopy(PPDMAUDIOSTREAMCFG pDstCfg, PCPDMAUDIOSTREAMCFG pSrcCfg)
1320{
1321 AssertPtrReturn(pDstCfg, VERR_INVALID_POINTER);
1322 AssertPtrReturn(pSrcCfg, VERR_INVALID_POINTER);
1323
1324 /* This used to be VBOX_STRICT only and return VERR_INVALID_PARAMETER, but
1325 that's making release builds work differently from debug & strict builds,
1326 which is a terrible idea: */
1327 Assert(PDMAudioStrmCfgIsValid(pSrcCfg));
1328
1329 memcpy(pDstCfg, pSrcCfg, sizeof(PDMAUDIOSTREAMCFG));
1330
1331 return VINF_SUCCESS;
1332}
1333
1334/**
1335 * Duplicates an audio stream configuration.
1336 *
1337 * @returns Pointer to duplicate on success, NULL on failure. Must be freed
1338 * using PDMAudioStrmCfgFree().
1339 *
1340 * @param pCfg The audio stream configuration to duplicate.
1341 */
1342DECLINLINE(PPDMAUDIOSTREAMCFG) PDMAudioStrmCfgDup(PCPDMAUDIOSTREAMCFG pCfg)
1343{
1344 AssertPtrReturn(pCfg, NULL);
1345
1346 PPDMAUDIOSTREAMCFG pDst = (PPDMAUDIOSTREAMCFG)RTMemAllocZ(sizeof(PDMAUDIOSTREAMCFG));
1347 if (pDst)
1348 {
1349 int rc = PDMAudioStrmCfgCopy(pDst, pCfg);
1350 if (RT_SUCCESS(rc))
1351 return pDst;
1352
1353 PDMAudioStrmCfgFree(pDst);
1354 }
1355 return NULL;
1356}
1357
1358/**
1359 * Logs an audio stream configuration.
1360 *
1361 * @param pCfg The stream configuration to log.
1362 */
1363DECLINLINE(void) PDMAudioStrmCfgLog(PCPDMAUDIOSTREAMCFG pCfg)
1364{
1365 if (pCfg)
1366 LogFunc(("szName=%s enmDir=%RU32 uHz=%RU32 cBits=%RU8%s cChannels=%RU8\n", pCfg->szName, pCfg->enmDir,
1367 pCfg->Props.uHz, pCfg->Props.cbSampleX * 8, pCfg->Props.fSigned ? "S" : "U", pCfg->Props.cChannelsX));
1368}
1369
1370/**
1371 * Converts a stream command enum value to a string.
1372 *
1373 * @returns Pointer to read-only stream command name on success,
1374 * "bad" if invalid command value.
1375 * @param enmCmd The stream command to name.
1376 */
1377DECLINLINE(const char *) PDMAudioStrmCmdGetName(PDMAUDIOSTREAMCMD enmCmd)
1378{
1379 switch (enmCmd)
1380 {
1381 case PDMAUDIOSTREAMCMD_INVALID: return "Invalid";
1382 case PDMAUDIOSTREAMCMD_ENABLE: return "Enable";
1383 case PDMAUDIOSTREAMCMD_DISABLE: return "Disable";
1384 case PDMAUDIOSTREAMCMD_PAUSE: return "Pause";
1385 case PDMAUDIOSTREAMCMD_RESUME: return "Resume";
1386 case PDMAUDIOSTREAMCMD_DRAIN: return "Drain";
1387 case PDMAUDIOSTREAMCMD_END:
1388 case PDMAUDIOSTREAMCMD_32BIT_HACK:
1389 break;
1390 /* no default! */
1391 }
1392 AssertMsgFailedReturn(("Invalid stream command %d\n", enmCmd), "bad");
1393}
1394
1395/** Max necessary buffer space for PDMAudioStrmCfgToString */
1396#define PDMAUDIOSTRMCFGTOSTRING_MAX \
1397 sizeof("'01234567890123456789012345678901234567890123456789012345678901234' unknown 16ch S64 4294967295Hz swap raw, 9999999ms buffer, 9999999ms period, 9999999ms pre-buffer, 4294967295ms sched, center-lfe")
1398
1399/**
1400 * Formats an audio stream configuration.
1401 *
1402 * @param pCfg The stream configuration to stringify.
1403 * @param pszDst The destination buffer.
1404 * @param cbDst The size of the destination buffer. Recommend this be
1405 * at least PDMAUDIOSTRMCFGTOSTRING_MAX bytes.
1406 */
1407DECLINLINE(const char *) PDMAudioStrmCfgToString(PCPDMAUDIOSTREAMCFG pCfg, char *pszDst, size_t cbDst)
1408{
1409 /* 'front' output 2ch 44100Hz raw, 300ms buffer, 75ms period, 150ms pre-buffer, 10ms sched */
1410 RTStrPrintf(pszDst, cbDst,
1411 "'%s' %s %uch %c%u %RU32Hz%s%s, %RU32ms buffer, %RU32ms period, %RU32ms pre-buffer, %RU32ms sched%s%s",
1412 pCfg->szName, PDMAudioDirGetName(pCfg->enmDir), PDMAudioPropsChannels(&pCfg->Props),
1413 PDMAudioPropsIsSigned(&pCfg->Props) ? 'S' : 'U', PDMAudioPropsSampleBits(&pCfg->Props),
1414 PDMAudioPropsHz(&pCfg->Props), pCfg->Props.fSwapEndian ? " swap" : "", pCfg->Props.fRaw ? " raw" : "",
1415 PDMAudioPropsFramesToMilliMax(&pCfg->Props, pCfg->Backend.cFramesBufferSize, 9999999),
1416 PDMAudioPropsFramesToMilliMax(&pCfg->Props, pCfg->Backend.cFramesPeriod, 9999999),
1417 PDMAudioPropsFramesToMilliMax(&pCfg->Props, pCfg->Backend.cFramesPreBuffering, 9999999),
1418 pCfg->Device.cMsSchedulingHint,
1419 pCfg->enmPath == PDMAUDIOPATH_UNKNOWN ? ", " : "",
1420 pCfg->enmPath == PDMAUDIOPATH_UNKNOWN ? "" : PDMAudioPathGetName(pCfg->enmPath) );
1421 return pszDst;
1422}
1423
1424
1425/*********************************************************************************************************************************
1426* Stream Status Helpers *
1427*********************************************************************************************************************************/
1428
1429/**
1430 * Converts a audio stream state enum value to a string.
1431 *
1432 * @returns Pointer to read-only audio stream state string on success,
1433 * "illegal" if invalid command value.
1434 * @param enmStreamState The state to convert.
1435 */
1436DECLINLINE(const char *) PDMAudioStreamStateGetName(PDMAUDIOSTREAMSTATE enmStreamState)
1437{
1438 switch (enmStreamState)
1439 {
1440 case PDMAUDIOSTREAMSTATE_INVALID: return "invalid";
1441 case PDMAUDIOSTREAMSTATE_NOT_WORKING: return "not-working";
1442 case PDMAUDIOSTREAMSTATE_NEED_REINIT: return "need-reinit";
1443 case PDMAUDIOSTREAMSTATE_INACTIVE: return "inactive";
1444 case PDMAUDIOSTREAMSTATE_ENABLED: return "enabled";
1445 case PDMAUDIOSTREAMSTATE_ENABLED_READABLE: return "enabled-readable";
1446 case PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE: return "enabled-writable";
1447 /* no default: */
1448 case PDMAUDIOSTREAMSTATE_END:
1449 case PDMAUDIOSTREAMSTATE_32BIT_HACK:
1450 break;
1451 }
1452 AssertMsgFailedReturn(("Invalid audio stream state: %d\n", enmStreamState), "illegal");
1453}
1454
1455/**
1456 * Converts a host audio (backend) stream state enum value to a string.
1457 *
1458 * @returns Pointer to read-only host audio stream state string on success,
1459 * "illegal" if invalid command value.
1460 * @param enmHostAudioStreamState The state to convert.
1461 */
1462DECLINLINE(const char *) PDMHostAudioStreamStateGetName(PDMHOSTAUDIOSTREAMSTATE enmHostAudioStreamState)
1463{
1464 switch (enmHostAudioStreamState)
1465 {
1466 case PDMHOSTAUDIOSTREAMSTATE_INVALID: return "invalid";
1467 case PDMHOSTAUDIOSTREAMSTATE_INITIALIZING: return "initializing";
1468 case PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING: return "not-working";
1469 case PDMHOSTAUDIOSTREAMSTATE_OKAY: return "okay";
1470 case PDMHOSTAUDIOSTREAMSTATE_DRAINING: return "draining";
1471 case PDMHOSTAUDIOSTREAMSTATE_INACTIVE: return "inactive";
1472 /* no default: */
1473 case PDMHOSTAUDIOSTREAMSTATE_END:
1474 case PDMHOSTAUDIOSTREAMSTATE_32BIT_HACK:
1475 break;
1476 }
1477 AssertMsgFailedReturn(("Invalid host audio stream state: %d\n", enmHostAudioStreamState), "illegal");
1478}
1479
1480/** @} */
1481
1482#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