VirtualBox

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

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

Audio: Worked over draining, starting with the internal DMA buffer (instead of just the pre-buffer and backend buffer) and using the async I/O thread to keep calling PDMIAUDIOCONNECTOR::pfnStreamIterate and PDMIHOSTAUDIO::pfnStreamPlay (NULL buffer) every so often till the draining is done. Also put a rough deadline on the draining. The PDMAUDIOSTREAMCMD_DISABLE is now defined to stop playback/capturing immediately, even when already draining (if possible). This gets rid of the timers in DrvAudio and windows backends. DrvAudio no longer issue an DISABLE command at the end of the drain, it assumes the backend does that internally itself. After issuing PDMAUDIOSTREAMCMD_DRAIN the client (be it mixer or drvaudio) will not provide any more data for the buffers via pfnStreamPlay. Only tested windows, needs to re-test all platforms. bugref:9890

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