VirtualBox

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

Last change on this file since 106165 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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