VirtualBox

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

Last change on this file since 94425 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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