VirtualBox

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

Last change on this file since 90828 was 89846, checked in by vboxsync, 3 years ago

pdmaudioinline.h: The _INVALID values are technically valid, so must return value for them w/o asserting. (Hit PDMAUDIOPATH_INVALID guruing with HDA.) bugref:9890

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