VirtualBox

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

Last change on this file since 99582 was 99559, checked in by vboxsync, 20 months ago

Audio: Improved range checks for audio channels (PDMAUDIO_MAX_CHANNELS).

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