VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DevHdaStreamMap.cpp@ 88913

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

Audio: We don't return IPRT status codes, but VBox status codes. duh. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.2 KB
Line 
1/* $Id: DevHdaStreamMap.cpp 88300 2021-03-26 14:31:55Z vboxsync $ */
2/** @file
3 * Intel HD Audio Controller Emulation - Stream mapping (e.g. 5.1 -> stereo).
4 */
5
6/*
7 * Copyright (C) 2017-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DEV_HDA
23#include <VBox/log.h>
24
25#include <VBox/vmm/pdmdev.h>
26#include <VBox/vmm/pdmaudioifs.h>
27#include <VBox/vmm/pdmaudioinline.h>
28
29#include <iprt/mem.h>
30#include <iprt/string.h>
31
32#include "AudioHlp.h"
33
34#include "DevHdaStreamChannel.h"
35#include "DevHdaStreamMap.h"
36
37
38
39/** Convert host stereo to mono guest, signed 16-bit edition. */
40static DECLCALLBACK(void)
41hdaR3StreamMap_H2G_GenericS16_Stereo2Mono(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
42{
43 /** @todo this doesn't merge the two, it just picks the first one. */
44 uint16_t *pu16Dst = (uint16_t *)pvDst;
45 uint16_t const *pu16Src = (uint16_t const *)pvSrc;
46 size_t const cbDstFrame = pMap->cbGuestFrame;
47
48 if (cbDstFrame != sizeof(*pu16Dst))
49 RT_BZERO(pvDst, cbDstFrame * cFrames);
50
51 while (cFrames-- > 0)
52 {
53 *pu16Dst = *pu16Src;
54 pu16Src += 2;
55 pu16Dst = (uint16_t *)((uintptr_t)pu16Dst + cbDstFrame);
56 }
57}
58
59
60/** Convert mono guest channel to host stereo, signed 16-bit edition. */
61static DECLCALLBACK(void)
62hdaR3StreamMap_G2H_GenericS16_Mono2Stereo(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
63{
64 uint32_t *pu32Dst = (uint32_t *)pvDst;
65 uint16_t const *pu16Src = (uint16_t const *)pvSrc;
66 size_t const cbSrcFrame = pMap->cbGuestFrame;
67
68 while (cFrames-- > 0)
69 {
70 uint16_t uSample = *pu16Src;
71 *pu32Dst++ = RT_MAKE_U32(uSample, uSample);
72 pu16Src = (uint16_t const *)((uintptr_t)pu16Src + cbSrcFrame);
73 }
74}
75
76
77/** Convert host stereo to mono guest, signed 32-bit edition. */
78static DECLCALLBACK(void)
79hdaR3StreamMap_H2G_GenericS32_Stereo2Mono(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
80{
81 /** @todo this doesn't merge the two, it just picks the first one. */
82 uint32_t *pu32Dst = (uint32_t *)pvDst;
83 uint32_t const *pu32Src = (uint32_t const *)pvSrc;
84 size_t const cbDstFrame = pMap->cbGuestFrame;
85
86 if (cbDstFrame != sizeof(*pu32Dst))
87 RT_BZERO(pvDst, cbDstFrame * cFrames);
88
89 while (cFrames-- > 0)
90 {
91 *pu32Dst = *pu32Src;
92 pu32Src += 2;
93 pu32Dst = (uint32_t *)((uintptr_t)pu32Dst + cbDstFrame);
94 }
95}
96
97
98/** Convert mono guest channel to host stereo, signed 32-bit edition. */
99static DECLCALLBACK(void)
100hdaR3StreamMap_G2H_GenericS32_Mono2Stereo(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
101{
102 uint64_t *pu64Dst = (uint64_t *)pvDst;
103 uint32_t const *pu32Src = (uint32_t const *)pvSrc;
104 size_t const cbSrcFrame = pMap->cbGuestFrame;
105
106 while (cFrames-- > 0)
107 {
108 uint32_t uSample = *pu32Src;
109 *pu64Dst++ = RT_MAKE_U64(uSample, uSample);
110 pu32Src = (uint32_t const *)((uintptr_t)pu32Src + cbSrcFrame);
111 }
112}
113
114
115/** Convert stereo host to 2 or more guest channels, signed 16-bit edition. */
116static DECLCALLBACK(void)
117hdaR3StreamMap_H2G_GenericS16_Stereo2NonMono(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
118{
119 uint32_t *pu32Dst = (uint32_t *)pvDst;
120 uint32_t const *pu32Src = (uint32_t const *)pvSrc; /** @todo could be misaligned */
121 size_t const cbDstFrame = pMap->cbGuestFrame;
122
123 RT_BZERO(pvDst, cbDstFrame * cFrames);
124
125 while (cFrames-- > 0)
126 {
127 *pu32Dst = *pu32Src++;
128 pu32Dst = (uint32_t *)((uintptr_t)pu32Dst + cbDstFrame);
129 }
130}
131
132
133/** Convert 2 or more guest channels to host stereo, signed 16-bit edition. */
134static DECLCALLBACK(void)
135hdaR3StreamMap_G2H_GenericS16_NonMono2Stereo(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
136{
137 uint32_t *pu32Dst = (uint32_t *)pvDst;
138 uint32_t const *pu32Src = (uint32_t const *)pvSrc; /** @todo could be misaligned */
139 size_t const cbSrcFrame = pMap->cbGuestFrame;
140
141 while (cFrames-- > 0)
142 {
143 *pu32Dst++ = *pu32Src;
144 pu32Src = (uint32_t const *)((uintptr_t)pu32Src + cbSrcFrame);
145 }
146}
147
148
149/** Convert stereo host to 2 or more guest channels, signed 32-bit edition. */
150static DECLCALLBACK(void)
151hdaR3StreamMap_H2G_GenericS32_Stereo2NonMono(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
152{
153 uint64_t *pu64Dst = (uint64_t *)pvDst; /** @todo could be misaligned. */
154 uint64_t const *pu64Src = (uint64_t const *)pvSrc;
155 size_t const cbDstFrame = pMap->cbGuestFrame;
156
157 RT_BZERO(pvDst, cbDstFrame * cFrames);
158
159 while (cFrames-- > 0)
160 {
161 *pu64Dst = *pu64Src++;
162 pu64Dst = (uint64_t *)((uintptr_t)pu64Dst + cbDstFrame);
163 }
164}
165
166
167/** Convert 2 or more guest channels to host stereo, signed 32-bit edition. */
168static DECLCALLBACK(void)
169hdaR3StreamMap_G2H_GenericS32_NonMono2Stereo(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
170{
171 uint64_t *pu64Dst = (uint64_t *)pvDst;
172 uint64_t const *pu64Src = (uint64_t const *)pvSrc; /** @todo could be misaligned */
173 size_t const cbSrcFrame = pMap->cbGuestFrame;
174
175 while (cFrames-- > 0)
176 {
177 *pu64Dst++ = *pu64Src;
178 pu64Src = (uint64_t const *)((uintptr_t)pu64Src + cbSrcFrame);
179 }
180}
181
182
183/** No conversion needed, just copy the data. */
184static DECLCALLBACK(void)
185hdaR3StreamMap_GenericCopy(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
186{
187 memcpy(pvDst, pvSrc, cFrames * pMap->cbGuestFrame);
188}
189
190
191/**
192 * Sets up a stream mapping according to the given properties / configuration.
193 *
194 * @return VBox status code.
195 * @retval VERR_NOT_SUPPORTED if the channel setup is not supported.
196 *
197 * @param pMap Pointer to mapping to set up.
198 * @param pProps Input: The stream PCM properties from the guest.
199 * Output: Properties for the host stream.
200 * @param cHostChannels The number of host channels to map to.
201 */
202static int hdaR3StreamMapSetup(PHDASTREAMMAP pMap, PPDMAUDIOPCMPROPS pProps, uint8_t cHostChannels)
203{
204 int rc;
205
206 /** @todo We ASSUME that all channels in a stream ...
207 * - have the same format
208 * - are in a consecutive order with no gaps in between
209 * - have a simple (raw) data layout
210 * - work in a non-striped fashion, e.g. interleaved (only on one SDn, not spread over multiple SDns) */
211 uint8_t cChannels = PDMAudioPropsChannels(pProps);
212 if ( cChannels == 1 /* Mono */
213 || cChannels == 2 /* Stereo */
214 || cChannels == 4 /* Quadrophonic */
215 || cChannels == 6) /* Surround (5.1) */
216 {
217 /*
218 * Copy the guest stream properties and see if we need to change the host properties.
219 */
220 memcpy(&pMap->GuestProps, pProps, sizeof(PDMAUDIOPCMPROPS));
221 if (cChannels != cHostChannels)
222 {
223 if (cChannels == 1)
224 LogRelMax(32, ("HDA: Warning: Guest mono, host stereo.\n"));
225 else if (cHostChannels == 1 && cChannels == 2)
226 LogRelMax(32, ("HDA: Warning: Host mono, guest stereo.\n"));
227 else
228#ifndef VBOX_WITH_AUDIO_HDA_51_SURROUND
229 LogRelMax(32, ("HDA: Warning: Guest configured %u channels, host only supports %u. Ignoring additional channels.\n",
230 cChannels, cHostChannels));
231#else
232# error reconsider the above logic
233#endif
234 PDMAudioPropsSetChannels(pProps, cHostChannels);
235 }
236
237 /*
238 * Pick conversion functions.
239 */
240 Assert(PDMAudioPropsSampleSize(&pMap->GuestProps) == PDMAudioPropsSampleSize(pProps));
241
242 /* If the channel count matches, we can use the memcpy converters: */
243 if (PDMAudioPropsChannels(pProps) == PDMAudioPropsChannels(&pMap->GuestProps))
244 {
245 pMap->pfnGuestToHost = hdaR3StreamMap_GenericCopy;
246 pMap->pfnHostToGuest = hdaR3StreamMap_GenericCopy;
247 pMap->fMappingNeeded = false;
248 }
249 else
250 {
251 /* For multi-speaker guest configs, we currently just pick the
252 first two channels and map this onto the host stereo ones. */
253 AssertReturn(cHostChannels == 2, VERR_NOT_SUPPORTED);
254 switch (PDMAudioPropsSampleSize(&pMap->GuestProps))
255 {
256 case 2:
257 if (PDMAudioPropsChannels(&pMap->GuestProps) > 1)
258 {
259 pMap->pfnGuestToHost = hdaR3StreamMap_G2H_GenericS16_NonMono2Stereo;
260 pMap->pfnHostToGuest = hdaR3StreamMap_H2G_GenericS16_Stereo2NonMono;
261 }
262 else
263 {
264 pMap->pfnGuestToHost = hdaR3StreamMap_G2H_GenericS16_Mono2Stereo;
265 pMap->pfnHostToGuest = hdaR3StreamMap_H2G_GenericS16_Stereo2Mono;
266 }
267 break;
268
269 case 4:
270 if (PDMAudioPropsChannels(&pMap->GuestProps) > 1)
271 {
272 pMap->pfnGuestToHost = hdaR3StreamMap_G2H_GenericS32_NonMono2Stereo;
273 pMap->pfnHostToGuest = hdaR3StreamMap_H2G_GenericS32_Stereo2NonMono;
274 }
275 else
276 {
277 pMap->pfnGuestToHost = hdaR3StreamMap_G2H_GenericS32_Mono2Stereo;
278 pMap->pfnHostToGuest = hdaR3StreamMap_H2G_GenericS32_Stereo2Mono;
279 }
280 break;
281
282 default:
283 AssertMsgFailedReturn(("cbSample=%u\n", PDMAudioPropsChannels(&pMap->GuestProps)), VERR_NOT_SUPPORTED);
284 }
285 pMap->fMappingNeeded = true;
286 }
287
288 /** @todo bird: Not sure if this is really needed any more. */
289
290 /* For now we don't have anything other as mono / stereo channels being covered by the backends.
291 * So just set up one channel covering those and skipping the rest (like configured rear or center/LFE outputs). */
292 pMap->cMappings = 1;
293 pMap->paMappings = (PPDMAUDIOSTREAMMAP)RTMemAlloc(sizeof(PDMAUDIOSTREAMMAP) * pMap->cMappings);
294 if (pMap->paMappings)
295 {
296 PPDMAUDIOSTREAMMAP pMapLR = &pMap->paMappings[0];
297 pMapLR->aenmIDs[0] = PDMAUDIOSTREAMCHANNELID_FRONT_LEFT;
298 pMapLR->aenmIDs[1] = PDMAUDIOSTREAMCHANNELID_FRONT_RIGHT;
299 pMapLR->cbFrame = PDMAudioPropsFrameSize(pProps);
300 pMapLR->cbStep = PDMAudioPropsSampleSize(pProps) * 2 /* Front left + Front right channels */;
301 pMapLR->offFirst = 0;
302 pMapLR->offNext = pMapLR->offFirst;
303
304 rc = hdaR3StreamChannelDataInit(&pMapLR->Data, PDMAUDIOSTREAMCHANNELDATA_FLAGS_NONE);
305 AssertRC(rc);
306 }
307 else
308 rc = VERR_NO_MEMORY;
309 }
310 else
311 rc = VERR_NOT_SUPPORTED; /** @todo r=andy Support more setups. */
312
313 return rc;
314}
315
316
317/**
318 * Initializes a stream mapping structure according to the given PCM properties.
319 *
320 * @returns VBox status code.
321 * @param pMap Pointer to mapping to initialize.
322 * @param cHostChannels The number of host channels to map to.
323 * @param pProps Input: The stream PCM properties from the guest.
324 * Output: Properties for the host side.
325 */
326int hdaR3StreamMapInit(PHDASTREAMMAP pMap, uint8_t cHostChannels, PPDMAUDIOPCMPROPS pProps)
327{
328 AssertPtrReturn(pMap, VERR_INVALID_POINTER);
329 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
330
331 if (!AudioHlpPcmPropsAreValid(pProps))
332 return VERR_INVALID_PARAMETER;
333
334 hdaR3StreamMapReset(pMap);
335
336 pMap->cbGuestFrame = PDMAudioPropsFrameSize(pProps);
337 int rc = hdaR3StreamMapSetup(pMap, pProps, cHostChannels);
338 if (RT_SUCCESS(rc))
339 {
340#ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
341 /* Create the circular buffer if not created yet. */
342 if (!pMap->pCircBuf)
343 rc = RTCircBufCreate(&pMap->pCircBuf, _4K); /** @todo Make size configurable? */
344 if (RT_SUCCESS(rc))
345#endif
346 {
347 LogFunc(("cChannels=%RU8, cBytes=%RU8 -> cbGuestFrame=%RU32\n",
348 PDMAudioPropsChannels(pProps), PDMAudioPropsSampleSize(pProps), pMap->cbGuestFrame));
349
350 Assert(pMap->cbGuestFrame); /* Frame size must not be 0. */
351 pMap->enmLayout = PDMAUDIOSTREAMLAYOUT_INTERLEAVED;
352 return VINF_SUCCESS;
353 }
354 }
355
356 return rc;
357}
358
359
360/**
361 * Destroys a given stream mapping.
362 *
363 * @param pMap Pointer to mapping to destroy.
364 */
365void hdaR3StreamMapDestroy(PHDASTREAMMAP pMap)
366{
367 hdaR3StreamMapReset(pMap);
368
369#ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
370 if (pMap->pCircBuf)
371 {
372 RTCircBufDestroy(pMap->pCircBuf);
373 pMap->pCircBuf = NULL;
374 }
375#endif
376}
377
378
379/**
380 * Resets a given stream mapping.
381 *
382 * @param pMap Pointer to mapping to reset.
383 */
384void hdaR3StreamMapReset(PHDASTREAMMAP pMap)
385{
386 AssertPtrReturnVoid(pMap);
387
388 pMap->enmLayout = PDMAUDIOSTREAMLAYOUT_UNKNOWN;
389
390 if (pMap->paMappings)
391 {
392 for (uint8_t i = 0; i < pMap->cMappings; i++)
393 hdaR3StreamChannelDataDestroy(&pMap->paMappings[i].Data);
394
395 RTMemFree(pMap->paMappings);
396 pMap->paMappings = NULL;
397
398 pMap->cMappings = 0;
399 }
400
401 pMap->fMappingNeeded = false;
402 pMap->pfnGuestToHost = hdaR3StreamMap_GenericCopy;
403 pMap->pfnHostToGuest = hdaR3StreamMap_GenericCopy;
404 RT_ZERO(pMap->GuestProps);
405}
406
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