VirtualBox

source: vbox/trunk/include/VBox/vmm/pdmaudiohostenuminline.h@ 99896

Last change on this file since 99896 was 98103, checked in by vboxsync, 23 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: 16.5 KB
Line 
1/* $Id: pdmaudiohostenuminline.h 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * PDM - Audio Helpers for host audio device enumeration, Inlined Code. (DEV,++)
4 *
5 * This is all inlined because it's too tedious to create a couple libraries to
6 * contain it all (same bad excuse as for intnetinline.h & pdmnetinline.h).
7 */
8
9/*
10 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
11 *
12 * This file is part of VirtualBox base platform packages, as
13 * available from https://www.virtualbox.org.
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation, in version 3 of the
18 * License.
19 *
20 * This program is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, see <https://www.gnu.org/licenses>.
27 *
28 * The contents of this file may alternatively be used under the terms
29 * of the Common Development and Distribution License Version 1.0
30 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
31 * in the VirtualBox distribution, in which case the provisions of the
32 * CDDL are applicable instead of those of the GPL.
33 *
34 * You may elect to license modified versions of this file under the
35 * terms and conditions of either the GPL or the CDDL or both.
36 *
37 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
38 */
39
40#ifndef VBOX_INCLUDED_vmm_pdmaudiohostenuminline_h
41#define VBOX_INCLUDED_vmm_pdmaudiohostenuminline_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#include <VBox/vmm/pdmaudioinline.h>
54
55#include <iprt/assert.h>
56#include <iprt/mem.h>
57#include <iprt/string.h>
58
59
60/** @defgroup grp_pdm_audio_host_enum_inline The PDM Host Audio Enumeration Helper APIs
61 * @ingroup grp_pdm
62 * @{
63 */
64
65
66/**
67 * Allocates a host audio device for an enumeration result.
68 *
69 * @returns Newly allocated audio device, or NULL on failure.
70 * @param cb The total device structure size. This must be at least the
71 * size of PDMAUDIOHOSTDEV. The idea is that the caller extends
72 * the PDMAUDIOHOSTDEV structure and appends additional data
73 * after it in its private structure.
74 * @param cbName The number of bytes to allocate for the name field
75 * (including the terminator). Pass zero if RTStrAlloc and
76 * friends will be used.
77 * @param cbId The number of bytes to allocate for the ID field. Pass
78 * zero if RTStrAlloc and friends will be used.
79 */
80DECLINLINE(PPDMAUDIOHOSTDEV) PDMAudioHostDevAlloc(size_t cb, size_t cbName, size_t cbId)
81{
82 AssertReturn(cb >= sizeof(PDMAUDIOHOSTDEV), NULL);
83 AssertReturn(cb < _4M, NULL);
84 AssertReturn(cbName < _4K, NULL);
85 AssertReturn(cbId < _16K, NULL);
86
87 PPDMAUDIOHOSTDEV pDev = (PPDMAUDIOHOSTDEV)RTMemAllocZ(RT_ALIGN_Z(cb + cbName + cbId, 64));
88 if (pDev)
89 {
90 pDev->uMagic = PDMAUDIOHOSTDEV_MAGIC;
91 pDev->cbSelf = (uint32_t)cb;
92 RTListInit(&pDev->ListEntry);
93 if (cbName)
94 pDev->pszName = (char *)pDev + cb;
95 if (cbId)
96 pDev->pszId = (char *)pDev + cb + cbName;
97 }
98 return pDev;
99}
100
101/**
102 * Frees a host audio device allocated by PDMAudioHostDevAlloc.
103 *
104 * @param pDev The device to free. NULL is ignored.
105 */
106DECLINLINE(void) PDMAudioHostDevFree(PPDMAUDIOHOSTDEV pDev)
107{
108 if (pDev)
109 {
110 Assert(pDev->uMagic == PDMAUDIOHOSTDEV_MAGIC);
111 pDev->uMagic = ~PDMAUDIOHOSTDEV_MAGIC;
112 pDev->cbSelf = 0;
113
114 if (pDev->fFlags & PDMAUDIOHOSTDEV_F_NAME_ALLOC)
115 {
116 RTStrFree(pDev->pszName);
117 pDev->pszName = NULL;
118 }
119
120 if (pDev->fFlags & PDMAUDIOHOSTDEV_F_ID_ALLOC)
121 {
122 RTStrFree(pDev->pszId);
123 pDev->pszId = NULL;
124 }
125
126 RTMemFree(pDev);
127 }
128}
129
130/**
131 * Duplicates a host audio device enumeration entry.
132 *
133 * @returns Duplicated audio device entry on success, or NULL on failure.
134 * @param pDev The audio device enum entry to duplicate.
135 * @param fOnlyCoreData
136 */
137DECLINLINE(PPDMAUDIOHOSTDEV) PDMAudioHostDevDup(PCPDMAUDIOHOSTDEV pDev, bool fOnlyCoreData)
138{
139 AssertPtrReturn(pDev, NULL);
140 Assert(pDev->uMagic == PDMAUDIOHOSTDEV_MAGIC);
141 Assert(fOnlyCoreData || !(pDev->fFlags & PDMAUDIOHOSTDEV_F_NO_DUP));
142
143 uint32_t cbToDup = fOnlyCoreData ? sizeof(PDMAUDIOHOSTDEV) : pDev->cbSelf;
144 AssertReturn(cbToDup >= sizeof(*pDev), NULL);
145
146 PPDMAUDIOHOSTDEV pDevDup = PDMAudioHostDevAlloc(cbToDup, 0, 0);
147 if (pDevDup)
148 {
149 memcpy(pDevDup, pDev, cbToDup);
150 RTListInit(&pDevDup->ListEntry);
151 pDevDup->cbSelf = cbToDup;
152
153 if (pDev->pszName)
154 {
155 uintptr_t off;
156 if ( (pDevDup->fFlags & PDMAUDIOHOSTDEV_F_NAME_ALLOC)
157 || (off = (uintptr_t)pDev->pszName - (uintptr_t)pDev) >= pDevDup->cbSelf)
158 {
159 pDevDup->fFlags |= PDMAUDIOHOSTDEV_F_NAME_ALLOC;
160 pDevDup->pszName = RTStrDup(pDev->pszName);
161 AssertReturnStmt(pDevDup->pszName, PDMAudioHostDevFree(pDevDup), NULL);
162 }
163 else
164 pDevDup->pszName = (char *)pDevDup + off;
165 }
166
167 if (pDev->pszId)
168 {
169 uintptr_t off;
170 if ( (pDevDup->fFlags & PDMAUDIOHOSTDEV_F_ID_ALLOC)
171 || (off = (uintptr_t)pDev->pszId - (uintptr_t)pDev) >= pDevDup->cbSelf)
172 {
173 pDevDup->fFlags |= PDMAUDIOHOSTDEV_F_ID_ALLOC;
174 pDevDup->pszId = RTStrDup(pDev->pszId);
175 AssertReturnStmt(pDevDup->pszId, PDMAudioHostDevFree(pDevDup), NULL);
176 }
177 else
178 pDevDup->pszId = (char *)pDevDup + off;
179 }
180 }
181
182 return pDevDup;
183}
184
185/**
186 * Initializes a host audio device enumeration.
187 *
188 * @param pDevEnm The enumeration to initialize.
189 */
190DECLINLINE(void) PDMAudioHostEnumInit(PPDMAUDIOHOSTENUM pDevEnm)
191{
192 AssertPtr(pDevEnm);
193
194 pDevEnm->uMagic = PDMAUDIOHOSTENUM_MAGIC;
195 pDevEnm->cDevices = 0;
196 RTListInit(&pDevEnm->LstDevices);
197}
198
199/**
200 * Deletes the host audio device enumeration and frees all device entries
201 * associated with it.
202 *
203 * The user must call PDMAudioHostEnumInit again to use it again.
204 *
205 * @param pDevEnm The host audio device enumeration to delete.
206 */
207DECLINLINE(void) PDMAudioHostEnumDelete(PPDMAUDIOHOSTENUM pDevEnm)
208{
209 if (pDevEnm)
210 {
211 AssertPtr(pDevEnm);
212 AssertReturnVoid(pDevEnm->uMagic == PDMAUDIOHOSTENUM_MAGIC);
213
214 PPDMAUDIOHOSTDEV pDev, pDevNext;
215 RTListForEachSafe(&pDevEnm->LstDevices, pDev, pDevNext, PDMAUDIOHOSTDEV, ListEntry)
216 {
217 RTListNodeRemove(&pDev->ListEntry);
218
219 PDMAudioHostDevFree(pDev);
220
221 pDevEnm->cDevices--;
222 }
223
224 /* Sanity. */
225 Assert(RTListIsEmpty(&pDevEnm->LstDevices));
226 Assert(pDevEnm->cDevices == 0);
227
228 pDevEnm->uMagic = ~PDMAUDIOHOSTENUM_MAGIC;
229 }
230}
231
232/**
233 * Adds an audio device to a device enumeration.
234 *
235 * @param pDevEnm Device enumeration to add device to.
236 * @param pDev Device to add. The pointer will be owned by the device enumeration then.
237 */
238DECLINLINE(void) PDMAudioHostEnumAppend(PPDMAUDIOHOSTENUM pDevEnm, PPDMAUDIOHOSTDEV pDev)
239{
240 AssertPtr(pDevEnm);
241 AssertPtr(pDev);
242 Assert(pDevEnm->uMagic == PDMAUDIOHOSTENUM_MAGIC);
243
244 RTListAppend(&pDevEnm->LstDevices, &pDev->ListEntry);
245 pDevEnm->cDevices++;
246}
247
248/**
249 * Appends copies of matching host device entries from one to another enumeration.
250 *
251 * @returns VBox status code.
252 * @param pDstDevEnm The target to append copies of matching device to.
253 * @param pSrcDevEnm The source to copy matching devices from.
254 * @param enmUsage The usage to match for copying.
255 * Use PDMAUDIODIR_INVALID to match all entries.
256 * @param fOnlyCoreData Set this to only copy the PDMAUDIOHOSTDEV part.
257 * Careful with passing @c false here as not all
258 * backends have data that can be copied.
259 */
260DECLINLINE(int) PDMAudioHostEnumCopy(PPDMAUDIOHOSTENUM pDstDevEnm, PCPDMAUDIOHOSTENUM pSrcDevEnm,
261 PDMAUDIODIR enmUsage, bool fOnlyCoreData)
262{
263 AssertPtrReturn(pDstDevEnm, VERR_INVALID_POINTER);
264 AssertReturn(pDstDevEnm->uMagic == PDMAUDIOHOSTENUM_MAGIC, VERR_WRONG_ORDER);
265
266 AssertPtrReturn(pSrcDevEnm, VERR_INVALID_POINTER);
267 AssertReturn(pSrcDevEnm->uMagic == PDMAUDIOHOSTENUM_MAGIC, VERR_WRONG_ORDER);
268
269 PPDMAUDIOHOSTDEV pSrcDev;
270 RTListForEach(&pSrcDevEnm->LstDevices, pSrcDev, PDMAUDIOHOSTDEV, ListEntry)
271 {
272 if ( enmUsage == pSrcDev->enmUsage
273 || enmUsage == PDMAUDIODIR_INVALID /*all*/)
274 {
275 PPDMAUDIOHOSTDEV pDstDev = PDMAudioHostDevDup(pSrcDev, fOnlyCoreData);
276 AssertReturn(pDstDev, VERR_NO_MEMORY);
277
278 PDMAudioHostEnumAppend(pDstDevEnm, pDstDev);
279 }
280 }
281
282 return VINF_SUCCESS;
283}
284
285/**
286 * Moves all the device entries from one enumeration to another, destroying the
287 * former.
288 *
289 * @returns VBox status code.
290 * @param pDstDevEnm The target to put move @a pSrcDevEnm to. This
291 * does not need to be initialized, but if it is it
292 * must not have any device entries.
293 * @param pSrcDevEnm The source to move from. This will be empty
294 * upon successful return.
295 */
296DECLINLINE(int) PDMAudioHostEnumMove(PPDMAUDIOHOSTENUM pDstDevEnm, PPDMAUDIOHOSTENUM pSrcDevEnm)
297{
298 AssertPtrReturn(pDstDevEnm, VERR_INVALID_POINTER);
299 AssertReturn(pDstDevEnm->uMagic != PDMAUDIOHOSTENUM_MAGIC || pDstDevEnm->cDevices == 0, VERR_WRONG_ORDER);
300
301 AssertPtrReturn(pSrcDevEnm, VERR_INVALID_POINTER);
302 AssertReturn(pSrcDevEnm->uMagic == PDMAUDIOHOSTENUM_MAGIC, VERR_WRONG_ORDER);
303
304 pDstDevEnm->uMagic = PDMAUDIOHOSTENUM_MAGIC;
305 RTListInit(&pDstDevEnm->LstDevices);
306 pDstDevEnm->cDevices = pSrcDevEnm->cDevices;
307 if (pSrcDevEnm->cDevices)
308 {
309 PPDMAUDIOHOSTDEV pCur;
310 while ((pCur = RTListRemoveFirst(&pSrcDevEnm->LstDevices, PDMAUDIOHOSTDEV, ListEntry)) != NULL)
311 RTListAppend(&pDstDevEnm->LstDevices, &pCur->ListEntry);
312 }
313 return VINF_SUCCESS;
314}
315
316/**
317 * Get the default device with the given usage.
318 *
319 * This assumes that only one default device per usage is set, if there should
320 * be more than one, the first one is returned.
321 *
322 * @returns Default device if found, or NULL if not.
323 * @param pDevEnm Device enumeration to get default device for.
324 * @param enmUsage Usage to get default device for.
325 * Pass PDMAUDIODIR_INVALID to get the first device with
326 * either PDMAUDIOHOSTDEV_F_DEFAULT_OUT or
327 * PDMAUDIOHOSTDEV_F_DEFAULT_IN set.
328 */
329DECLINLINE(PPDMAUDIOHOSTDEV) PDMAudioHostEnumGetDefault(PCPDMAUDIOHOSTENUM pDevEnm, PDMAUDIODIR enmUsage)
330{
331 AssertPtrReturn(pDevEnm, NULL);
332 AssertReturn(pDevEnm->uMagic == PDMAUDIOHOSTENUM_MAGIC, NULL);
333
334 Assert(enmUsage == PDMAUDIODIR_IN || enmUsage == PDMAUDIODIR_OUT || enmUsage == PDMAUDIODIR_INVALID);
335 uint32_t const fFlags = enmUsage == PDMAUDIODIR_IN ? PDMAUDIOHOSTDEV_F_DEFAULT_IN
336 : enmUsage == PDMAUDIODIR_OUT ? PDMAUDIOHOSTDEV_F_DEFAULT_OUT
337 : enmUsage == PDMAUDIODIR_INVALID ? PDMAUDIOHOSTDEV_F_DEFAULT_IN | PDMAUDIOHOSTDEV_F_DEFAULT_OUT
338 : 0;
339
340 PPDMAUDIOHOSTDEV pDev;
341 RTListForEach(&pDevEnm->LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
342 {
343 if (pDev->fFlags & fFlags)
344 {
345 Assert(pDev->enmUsage == enmUsage || pDev->enmUsage == PDMAUDIODIR_DUPLEX || enmUsage == PDMAUDIODIR_INVALID);
346 return pDev;
347 }
348 }
349
350 return NULL;
351}
352
353/**
354 * Get the number of device with the given usage.
355 *
356 * @returns Number of matching devices.
357 * @param pDevEnm Device enumeration to get default device for.
358 * @param enmUsage Usage to count devices for.
359 * Pass PDMAUDIODIR_INVALID to get the total number of devices.
360 */
361DECLINLINE(uint32_t) PDMAudioHostEnumCountMatching(PCPDMAUDIOHOSTENUM pDevEnm, PDMAUDIODIR enmUsage)
362{
363 AssertPtrReturn(pDevEnm, 0);
364 AssertReturn(pDevEnm->uMagic == PDMAUDIOHOSTENUM_MAGIC, 0);
365
366 if (enmUsage == PDMAUDIODIR_INVALID)
367 return pDevEnm->cDevices;
368
369 uint32_t cDevs = 0;
370 PPDMAUDIOHOSTDEV pDev;
371 RTListForEach(&pDevEnm->LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
372 {
373 if (enmUsage == pDev->enmUsage)
374 cDevs++;
375 }
376
377 return cDevs;
378}
379
380/** The max string length for all PDMAUDIOHOSTDEV_F_XXX.
381 * @sa PDMAudioHostDevFlagsToString */
382#define PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN sizeof("DEFAULT_OUT DEFAULT_IN HOTPLUG BUGGY IGNORE LOCKED DEAD NAME_ALLOC ID_ALLOC NO_DUP ")
383
384/**
385 * Converts an audio device flags to a string.
386 *
387 * @returns
388 * @param pszDst Destination buffer with a size of at least
389 * PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN bytes (including
390 * the string terminator).
391 * @param fFlags Audio flags (PDMAUDIOHOSTDEV_F_XXX) to convert.
392 */
393DECLINLINE(const char *) PDMAudioHostDevFlagsToString(char pszDst[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN], uint32_t fFlags)
394{
395 static const struct { const char *pszMnemonic; uint32_t cchMnemonic; uint32_t fFlag; } s_aFlags[] =
396 {
397 { RT_STR_TUPLE("DEFAULT_OUT "), PDMAUDIOHOSTDEV_F_DEFAULT_OUT },
398 { RT_STR_TUPLE("DEFAULT_IN "), PDMAUDIOHOSTDEV_F_DEFAULT_IN },
399 { RT_STR_TUPLE("HOTPLUG "), PDMAUDIOHOSTDEV_F_HOTPLUG },
400 { RT_STR_TUPLE("BUGGY "), PDMAUDIOHOSTDEV_F_BUGGY },
401 { RT_STR_TUPLE("IGNORE "), PDMAUDIOHOSTDEV_F_IGNORE },
402 { RT_STR_TUPLE("LOCKED "), PDMAUDIOHOSTDEV_F_LOCKED },
403 { RT_STR_TUPLE("DEAD "), PDMAUDIOHOSTDEV_F_DEAD },
404 { RT_STR_TUPLE("NAME_ALLOC "), PDMAUDIOHOSTDEV_F_NAME_ALLOC },
405 { RT_STR_TUPLE("ID_ALLOC "), PDMAUDIOHOSTDEV_F_ID_ALLOC },
406 { RT_STR_TUPLE("NO_DUP "), PDMAUDIOHOSTDEV_F_NO_DUP },
407 };
408 size_t offDst = 0;
409 for (uint32_t i = 0; i < RT_ELEMENTS(s_aFlags); i++)
410 if (fFlags & s_aFlags[i].fFlag)
411 {
412 fFlags &= ~s_aFlags[i].fFlag;
413 memcpy(&pszDst[offDst], s_aFlags[i].pszMnemonic, s_aFlags[i].cchMnemonic);
414 offDst += s_aFlags[i].cchMnemonic;
415 }
416 Assert(fFlags == 0);
417 Assert(offDst < PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN);
418
419 if (offDst)
420 pszDst[offDst - 1] = '\0';
421 else
422 memcpy(pszDst, "NONE", sizeof("NONE"));
423 return pszDst;
424}
425
426/**
427 * Logs an audio device enumeration.
428 *
429 * @param pDevEnm Device enumeration to log.
430 * @param pszDesc Logging description (prefix).
431 */
432DECLINLINE(void) PDMAudioHostEnumLog(PCPDMAUDIOHOSTENUM pDevEnm, const char *pszDesc)
433{
434#ifdef LOG_ENABLED
435 AssertPtrReturnVoid(pDevEnm);
436 AssertPtrReturnVoid(pszDesc);
437 AssertReturnVoid(pDevEnm->uMagic == PDMAUDIOHOSTENUM_MAGIC);
438
439 if (LogIsEnabled())
440 {
441 LogFunc(("%s: %RU32 devices\n", pszDesc, pDevEnm->cDevices));
442
443 PPDMAUDIOHOSTDEV pDev;
444 RTListForEach(&pDevEnm->LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
445 {
446 char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];
447 LogFunc(("Device '%s':\n", pDev->pszName));
448 LogFunc((" ID = %s\n", pDev->pszId ? pDev->pszId : "<none>"));
449 LogFunc((" Usage = %s\n", PDMAudioDirGetName(pDev->enmUsage)));
450 LogFunc((" Flags = %s\n", PDMAudioHostDevFlagsToString(szFlags, pDev->fFlags)));
451 LogFunc((" Input channels = %RU8\n", pDev->cMaxInputChannels));
452 LogFunc((" Output channels = %RU8\n", pDev->cMaxOutputChannels));
453 LogFunc((" cbExtra = %RU32 bytes\n", pDev->cbSelf - sizeof(PDMAUDIOHOSTDEV)));
454 }
455 }
456#else
457 RT_NOREF(pDevEnm, pszDesc);
458#endif
459}
460
461/** @} */
462
463#endif /* !VBOX_INCLUDED_vmm_pdmaudiohostenuminline_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