VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/AudioMixer.cpp@ 89331

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

Audio: Reworking the capture (recording) code path, part 4: Combine PDMIAUDIOCONNECTOR::pfnStreamCapture and PDMIAUDIOCONNECTOR::pfnStreamRead, remove PDMIAUDIOCONNECTOR::pfnStreamSetVoplume, eliminate mixer buffers in DrvAudio. Added pre-buffering of input streams (delay fetching samples from the backend till we've reached the desired buffer fill there). bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 102.4 KB
Line 
1/* $Id: AudioMixer.cpp 89327 2021-05-28 00:22:27Z vboxsync $ */
2/** @file
3 * Audio mixing routines for multiplexing audio sources in device emulations.
4 *
5 * Overview
6 * ========
7 *
8 * This mixer acts as a layer between the audio connector interface and
9 * the actual device emulation, providing mechanisms for audio sources (input)
10 * and audio sinks (output).
11 *
12 * Think of this mixer as kind of a high(er) level interface for the audio
13 * connector interface, abstracting common tasks such as creating and managing
14 * various audio sources and sinks. This mixer class is purely optional and can
15 * be left out when implementing a new device emulation, using only the audi
16 * connector interface instead. For example, the SB16 emulation does not use
17 * this mixer and does all its stream management on its own.
18 *
19 * As audio driver instances are handled as LUNs on the device level, this
20 * audio mixer then can take care of e.g. mixing various inputs/outputs to/from
21 * a specific source/sink.
22 *
23 * How and which audio streams are connected to sinks/sources depends on how
24 * the audio mixer has been set up.
25 *
26 * A sink can connect multiple output streams together, whereas a source
27 * does this with input streams. Each sink / source consists of one or more
28 * so-called mixer streams, which then in turn have pointers to the actual
29 * PDM audio input/output streams.
30 *
31 * Playback
32 * ========
33 *
34 * For output sinks there can be one or more mixing stream attached.
35 * As the host sets the overall pace for the device emulation (virtual time
36 * in the guest OS vs. real time on the host OS), an output mixing sink
37 * needs to make sure that all connected output streams are able to accept
38 * all the same amount of data at a time.
39 *
40 * This is called synchronous multiplexing.
41 *
42 * A mixing sink employs an own audio mixing buffer, which in turn can convert
43 * the audio (output) data supplied from the device emulation into the sink's
44 * audio format. As all connected mixing streams in theory could have the same
45 * audio format as the mixing sink (parent), this can save processing time when
46 * it comes to serving a lot of mixing streams at once. That way only one
47 * conversion must be done, instead of each stream having to iterate over the
48 * data.
49 *
50 * Recording
51 * =========
52 *
53 * For input sinks only one mixing stream at a time can be the recording
54 * source currently. A recording source is optional, e.g. it is possible to
55 * have no current recording source set. Switching to a different recording
56 * source at runtime is possible.
57 */
58
59/*
60 * Copyright (C) 2014-2020 Oracle Corporation
61 *
62 * This file is part of VirtualBox Open Source Edition (OSE), as
63 * available from http://www.virtualbox.org. This file is free software;
64 * you can redistribute it and/or modify it under the terms of the GNU
65 * General Public License (GPL) as published by the Free Software
66 * Foundation, in version 2 as it comes in the "COPYING" file of the
67 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
68 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
69 */
70
71
72/*********************************************************************************************************************************
73* Header Files *
74*********************************************************************************************************************************/
75#define LOG_GROUP LOG_GROUP_AUDIO_MIXER
76#include <VBox/log.h>
77#include "AudioMixer.h"
78#include "AudioMixBuffer.h"
79#include "AudioHlp.h"
80
81#include <VBox/vmm/pdm.h>
82#include <VBox/err.h>
83#include <VBox/vmm/mm.h>
84#include <VBox/vmm/pdmaudioifs.h>
85#include <VBox/vmm/pdmaudioinline.h>
86
87#include <iprt/alloc.h>
88#include <iprt/asm-math.h>
89#include <iprt/assert.h>
90#include <iprt/semaphore.h>
91#include <iprt/string.h>
92#include <iprt/thread.h>
93
94#ifdef VBOX_WITH_DTRACE
95# include "dtrace/VBoxDD.h"
96#endif
97
98
99/*********************************************************************************************************************************
100* Internal Functions *
101*********************************************************************************************************************************/
102static int audioMixerAddSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink);
103static int audioMixerRemoveSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink);
104
105static int audioMixerSinkInit(PAUDMIXSINK pSink, PAUDIOMIXER pMixer, const char *pcszName, PDMAUDIODIR enmDir, PPDMDEVINS pDevIns);
106static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink, PPDMDEVINS pDevIns);
107static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, PCPDMAUDIOVOLUME pVolMaster);
108static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink);
109static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream);
110static void audioMixerSinkReset(PAUDMIXSINK pSink);
111
112static int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd);
113static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pStream, PPDMDEVINS pDevIns, bool fImmediate);
114static int audioMixerStreamUpdateStatus(PAUDMIXSTREAM pMixStream);
115
116
117/** size of output buffer for dbgAudioMixerSinkStatusToStr. */
118#define AUDIOMIXERSINK_STATUS_STR_MAX sizeof("RUNNING DRAINING DRAINED_DMA DRAINED_MIXBUF DIRTY 0x12345678")
119
120/**
121 * Converts a mixer sink status to a string.
122 *
123 * @returns pszDst
124 * @param fStatus The mixer sink status.
125 * @param pszDst The output buffer. Must be at least
126 * AUDIOMIXERSINK_STATUS_STR_MAX in length.
127 */
128static const char *dbgAudioMixerSinkStatusToStr(uint32_t fStatus, char pszDst[AUDIOMIXERSINK_STATUS_STR_MAX])
129{
130 if (!fStatus)
131 return strcpy(pszDst, "NONE");
132 static const struct
133 {
134 const char *pszMnemonic;
135 uint32_t cchMnemonic;
136 uint32_t fStatus;
137 } s_aFlags[] =
138 {
139 { RT_STR_TUPLE("RUNNING "), AUDMIXSINK_STS_RUNNING },
140 { RT_STR_TUPLE("DRAINING "), AUDMIXSINK_STS_DRAINING },
141 { RT_STR_TUPLE("DRAINED_DMA "), AUDMIXSINK_STS_DRAINED_DMA },
142 { RT_STR_TUPLE("DRAINED_MIXBUF "), AUDMIXSINK_STS_DRAINED_MIXBUF },
143 { RT_STR_TUPLE("DIRTY "), AUDMIXSINK_STS_DIRTY },
144 };
145 char *psz = pszDst;
146 for (size_t i = 0; i < RT_ELEMENTS(s_aFlags); i++)
147 if (fStatus & s_aFlags[i].fStatus)
148 {
149 memcpy(psz, s_aFlags[i].pszMnemonic, s_aFlags[i].cchMnemonic);
150 psz += s_aFlags[i].cchMnemonic;
151 fStatus &= ~s_aFlags[i].fStatus;
152 if (!fStatus)
153 {
154 psz[-1] = '\0';
155 return pszDst;
156 }
157 }
158 RTStrPrintf(psz, AUDIOMIXERSINK_STATUS_STR_MAX - (psz - pszDst), "%#x", fStatus);
159 return pszDst;
160}
161
162/**
163 * Creates an audio sink and attaches it to the given mixer.
164 *
165 * @returns VBox status code.
166 * @param pMixer Mixer to attach created sink to.
167 * @param pszName Name of the sink to create.
168 * @param enmDir Direction of the sink to create.
169 * @param pDevIns The device instance to register statistics under.
170 * @param ppSink Pointer which returns the created sink on success.
171 */
172int AudioMixerCreateSink(PAUDIOMIXER pMixer, const char *pszName, PDMAUDIODIR enmDir, PPDMDEVINS pDevIns, PAUDMIXSINK *ppSink)
173{
174 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
175 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
176 /* ppSink is optional. */
177
178 int rc = RTCritSectEnter(&pMixer->CritSect);
179 AssertRCReturn(rc, rc);
180
181 PAUDMIXSINK pSink = (PAUDMIXSINK)RTMemAllocZ(sizeof(AUDMIXSINK));
182 if (pSink)
183 {
184 rc = audioMixerSinkInit(pSink, pMixer, pszName, enmDir, pDevIns);
185 if (RT_SUCCESS(rc))
186 {
187 rc = audioMixerAddSinkInternal(pMixer, pSink);
188 if (RT_SUCCESS(rc))
189 {
190 RTCritSectLeave(&pMixer->CritSect);
191
192 char szPrefix[128];
193 RTStrPrintf(szPrefix, sizeof(szPrefix), "MixerSink-%s/", pSink->pszName);
194 PDMDevHlpSTAMRegisterF(pDevIns, &pSink->MixBuf.cFrames, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
195 "Sink mixer buffer size in frames.", "%sMixBufSize", szPrefix);
196 PDMDevHlpSTAMRegisterF(pDevIns, &pSink->MixBuf.cUsed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
197 "Sink mixer buffer fill size in frames.", "%sMixBufUsed", szPrefix);
198 PDMDevHlpSTAMRegisterF(pDevIns, &pSink->cStreams, STAMTYPE_U8, STAMVISIBILITY_USED, STAMUNIT_NONE,
199 "Number of streams attached to the sink.", "%sStreams", szPrefix);
200
201 if (ppSink)
202 *ppSink = pSink;
203 return VINF_SUCCESS;
204 }
205 }
206
207 audioMixerSinkDestroyInternal(pSink, pDevIns);
208
209 RTMemFree(pSink);
210 pSink = NULL;
211 }
212 else
213 rc = VERR_NO_MEMORY;
214
215 RTCritSectLeave(&pMixer->CritSect);
216 return rc;
217}
218
219/**
220 * Creates an audio mixer.
221 *
222 * @returns VBox status code.
223 * @param pcszName Name of the audio mixer.
224 * @param fFlags Creation flags - AUDMIXER_FLAGS_XXX.
225 * @param ppMixer Pointer which returns the created mixer object.
226 */
227int AudioMixerCreate(const char *pcszName, uint32_t fFlags, PAUDIOMIXER *ppMixer)
228{
229 AssertPtrReturn(pcszName, VERR_INVALID_POINTER);
230 AssertReturn (!(fFlags & ~AUDMIXER_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
231 AssertPtrReturn(ppMixer, VERR_INVALID_POINTER);
232
233 int rc = VINF_SUCCESS;
234
235 PAUDIOMIXER pMixer = (PAUDIOMIXER)RTMemAllocZ(sizeof(AUDIOMIXER));
236 if (pMixer)
237 {
238 pMixer->pszName = RTStrDup(pcszName);
239 if (!pMixer->pszName)
240 rc = VERR_NO_MEMORY;
241
242 if (RT_SUCCESS(rc))
243 rc = RTCritSectInit(&pMixer->CritSect);
244
245 if (RT_SUCCESS(rc))
246 {
247 pMixer->cSinks = 0;
248 RTListInit(&pMixer->lstSinks);
249
250 pMixer->fFlags = fFlags;
251 pMixer->uMagic = AUDIOMIXER_MAGIC;
252
253 if (pMixer->fFlags & AUDMIXER_FLAGS_DEBUG)
254 LogRel(("Audio Mixer: Debug mode enabled\n"));
255
256 /* Set master volume to the max. */
257 pMixer->VolMaster.fMuted = false;
258 pMixer->VolMaster.uLeft = PDMAUDIO_VOLUME_MAX;
259 pMixer->VolMaster.uRight = PDMAUDIO_VOLUME_MAX;
260
261 LogFlowFunc(("Created mixer '%s'\n", pMixer->pszName));
262
263 *ppMixer = pMixer;
264 }
265 else
266 RTMemFree(pMixer); /** @todo leaks pszName due to badly structured code */
267 }
268 else
269 rc = VERR_NO_MEMORY;
270
271 LogFlowFuncLeaveRC(rc);
272 return rc;
273}
274
275/**
276 * Helper function for the internal debugger to print the mixer's current
277 * state, along with the attached sinks.
278 *
279 * @param pMixer Mixer to print debug output for.
280 * @param pHlp Debug info helper to use.
281 * @param pszArgs Optional arguments. Not being used at the moment.
282 */
283void AudioMixerDebug(PAUDIOMIXER pMixer, PCDBGFINFOHLP pHlp, const char *pszArgs)
284{
285 RT_NOREF(pszArgs);
286 Assert(pMixer->uMagic == AUDIOMIXER_MAGIC);
287
288 int rc2 = RTCritSectEnter(&pMixer->CritSect);
289 AssertRCReturnVoid(rc2);
290
291 pHlp->pfnPrintf(pHlp, "[Master] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", pMixer->pszName,
292 pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight, pMixer->VolMaster.fMuted);
293
294 PAUDMIXSINK pSink;
295 unsigned iSink = 0;
296 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
297 {
298 pHlp->pfnPrintf(pHlp, "[Sink %u] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", iSink, pSink->pszName,
299 pSink->Volume.uLeft, pSink->Volume.uRight, pSink->Volume.fMuted);
300 ++iSink;
301 }
302
303 rc2 = RTCritSectLeave(&pMixer->CritSect);
304 AssertRC(rc2);
305}
306
307/**
308 * Destroys an audio mixer.
309 *
310 * @param pMixer Audio mixer to destroy.
311 * @param pDevIns The device instance the statistics are associated with.
312 */
313void AudioMixerDestroy(PAUDIOMIXER pMixer, PPDMDEVINS pDevIns)
314{
315 if (!pMixer)
316 return;
317 AssertPtr(pMixer);
318 Assert(pMixer->uMagic == AUDIOMIXER_MAGIC);
319
320 int rc2 = RTCritSectEnter(&pMixer->CritSect);
321 AssertRC(rc2);
322
323 LogFlowFunc(("Destroying %s ...\n", pMixer->pszName));
324 pMixer->uMagic = AUDIOMIXER_MAGIC_DEAD;
325
326 PAUDMIXSINK pSink, pSinkNext;
327 RTListForEachSafe(&pMixer->lstSinks, pSink, pSinkNext, AUDMIXSINK, Node)
328 {
329 audioMixerSinkDestroyInternal(pSink, pDevIns);
330 audioMixerRemoveSinkInternal(pMixer, pSink);
331 RTMemFree(pSink);
332 }
333
334 Assert(pMixer->cSinks == 0);
335
336 RTStrFree(pMixer->pszName);
337 pMixer->pszName = NULL;
338
339 rc2 = RTCritSectLeave(&pMixer->CritSect);
340 AssertRC(rc2);
341
342 RTCritSectDelete(&pMixer->CritSect);
343
344 RTMemFree(pMixer);
345 pMixer = NULL;
346}
347
348/**
349 * Invalidates all internal data, internal version.
350 *
351 * @returns VBox status code.
352 * @param pMixer Mixer to invalidate data for.
353 */
354static int audioMixerInvalidateInternal(PAUDIOMIXER pMixer)
355{
356 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
357 LogFlowFunc(("[%s]\n", pMixer->pszName));
358
359 /* Propagate new master volume to all connected sinks. */
360 PAUDMIXSINK pSink;
361 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
362 {
363 int rc2 = audioMixerSinkUpdateVolume(pSink, &pMixer->VolMaster);
364 AssertRC(rc2);
365 }
366
367 return VINF_SUCCESS;
368}
369
370#if 0 /* unused */
371/**
372 * Invalidates all internal data.
373 *
374 * @returns VBox status code.
375 * @param pMixer Mixer to invalidate data for.
376 */
377void AudioMixerInvalidate(PAUDIOMIXER pMixer)
378{
379 AssertPtrReturnVoid(pMixer);
380 Assert(pMixer->uMagic == AUDIOMIXER_MAGIC);
381
382 int rc2 = RTCritSectEnter(&pMixer->CritSect);
383 AssertRC(rc2);
384
385 LogFlowFunc(("[%s]\n", pMixer->pszName));
386
387 rc2 = audioMixerInvalidateInternal(pMixer);
388 AssertRC(rc2);
389
390 rc2 = RTCritSectLeave(&pMixer->CritSect);
391 AssertRC(rc2);
392}
393#endif
394
395/**
396 * Adds sink to an existing mixer.
397 *
398 * @returns VBox status code.
399 * @param pMixer Mixer to add sink to.
400 * @param pSink Sink to add.
401 */
402static int audioMixerAddSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
403{
404 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
405 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
406
407 /** @todo Check upper sink limit? */
408 /** @todo Check for double-inserted sinks? */
409
410 RTListAppend(&pMixer->lstSinks, &pSink->Node);
411 pMixer->cSinks++;
412
413 LogFlowFunc(("pMixer=%p, pSink=%p, cSinks=%RU8\n",
414 pMixer, pSink, pMixer->cSinks));
415
416 return VINF_SUCCESS;
417}
418
419/**
420 * Removes a formerly attached audio sink for an audio mixer, internal version.
421 *
422 * @returns VBox status code.
423 * @param pMixer Mixer to remove sink from.
424 * @param pSink Sink to remove.
425 */
426static int audioMixerRemoveSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
427{
428 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
429 if (!pSink)
430 return VERR_NOT_FOUND;
431
432 AssertMsgReturn(pSink->pParent == pMixer, ("%s: Is not part of mixer '%s'\n",
433 pSink->pszName, pMixer->pszName), VERR_NOT_FOUND);
434
435 LogFlowFunc(("[%s] pSink=%s, cSinks=%RU8\n",
436 pMixer->pszName, pSink->pszName, pMixer->cSinks));
437
438 /* Remove sink from mixer. */
439 RTListNodeRemove(&pSink->Node);
440
441 Assert(pMixer->cSinks);
442 pMixer->cSinks--;
443
444 /* Set mixer to NULL so that we know we're not part of any mixer anymore. */
445 pSink->pParent = NULL;
446
447 return VINF_SUCCESS;
448}
449
450/**
451 * Sets the mixer's master volume.
452 *
453 * @returns VBox status code.
454 * @param pMixer Mixer to set master volume for.
455 * @param pVol Volume to set.
456 */
457int AudioMixerSetMasterVolume(PAUDIOMIXER pMixer, PPDMAUDIOVOLUME pVol)
458{
459 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
460 Assert(pMixer->uMagic == AUDIOMIXER_MAGIC);
461 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
462
463 int rc = RTCritSectEnter(&pMixer->CritSect);
464 AssertRCReturn(rc, rc);
465
466 memcpy(&pMixer->VolMaster, pVol, sizeof(PDMAUDIOVOLUME));
467
468 LogFlowFunc(("[%s] lVol=%RU32, rVol=%RU32 => fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
469 pMixer->pszName, pVol->uLeft, pVol->uRight,
470 pMixer->VolMaster.fMuted, pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight));
471
472 rc = audioMixerInvalidateInternal(pMixer);
473
474 int rc2 = RTCritSectLeave(&pMixer->CritSect);
475 AssertRC(rc2);
476
477 return rc;
478}
479
480
481/*********************************************************************************************************************************
482* Mixer Sink implementation. *
483*********************************************************************************************************************************/
484
485/**
486 * Adds an audio stream to a specific audio sink.
487 *
488 * @returns VBox status code.
489 * @param pSink Sink to add audio stream to.
490 * @param pStream Stream to add.
491 */
492int AudioMixerSinkAddStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
493{
494 LogFlowFuncEnter();
495 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
496 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
497 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
498 Assert(pStream->uMagic == AUDMIXSTREAM_MAGIC);
499 AssertPtrReturn(pStream->pConn, VERR_AUDIO_STREAM_NOT_READY);
500 AssertReturn(pStream->pSink == NULL, VERR_ALREADY_EXISTS);
501
502 int rc = RTCritSectEnter(&pSink->CritSect);
503 AssertRCReturn(rc, rc);
504
505 AssertLogRelMsgReturnStmt(pSink->cStreams < UINT8_MAX, ("too many streams!\n"), RTCritSectLeave(&pSink->CritSect),
506 VERR_TOO_MANY_OPEN_FILES);
507
508 /*
509 * If the sink is running and not in pending disable mode, make sure that
510 * the added stream also is enabled. Ignore any failure to enable it.
511 */
512 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
513 && !(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
514 {
515 audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_ENABLE);
516 }
517
518 /* Save pointer to sink the stream is attached to. */
519 pStream->pSink = pSink;
520
521 /* Append stream to sink's list. */
522 RTListAppend(&pSink->lstStreams, &pStream->Node);
523 pSink->cStreams++;
524
525 LogFlowFunc(("[%s] cStreams=%RU8, rc=%Rrc\n", pSink->pszName, pSink->cStreams, rc));
526 RTCritSectLeave(&pSink->CritSect);
527 return rc;
528}
529
530/**
531 * Creates an audio mixer stream.
532 *
533 * @returns VBox status code.
534 * @param pSink Sink to use for creating the stream.
535 * @param pConn Audio connector interface to use.
536 * @param pCfg Audio stream configuration to use. This may be modified
537 * in some unspecified way (see
538 * PDMIAUDIOCONNECTOR::pfnStreamCreate).
539 * @param pDevIns The device instance to register statistics with.
540 * @param ppStream Pointer which receives the newly created audio stream.
541 */
542int AudioMixerSinkCreateStream(PAUDMIXSINK pSink, PPDMIAUDIOCONNECTOR pConn, PPDMAUDIOSTREAMCFG pCfg,
543 PPDMDEVINS pDevIns, PAUDMIXSTREAM *ppStream)
544{
545 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
546 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
547 AssertPtrReturn(pConn, VERR_INVALID_POINTER);
548 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
549 AssertPtrNullReturn(ppStream, VERR_INVALID_POINTER);
550 Assert(pSink->AIO.pDevIns == pDevIns); RT_NOREF(pDevIns); /* we'll probably be adding more statistics */
551 AssertReturn(pCfg->enmDir == pSink->enmDir, VERR_MISMATCH);
552
553 /*
554 * Check status and get the host driver config.
555 */
556 if (pConn->pfnGetStatus(pConn, PDMAUDIODIR_DUPLEX) == PDMAUDIOBACKENDSTS_NOT_ATTACHED)
557 return VERR_AUDIO_BACKEND_NOT_ATTACHED;
558
559 PDMAUDIOBACKENDCFG BackendCfg;
560 int rc = pConn->pfnGetConfig(pConn, &BackendCfg);
561 AssertRCReturn(rc, rc);
562
563 /*
564 * Allocate the instance.
565 */
566 PAUDMIXSTREAM pMixStream = (PAUDMIXSTREAM)RTMemAllocZ(sizeof(AUDMIXSTREAM));
567 AssertReturn(pMixStream, VERR_NO_MEMORY);
568
569 /* Assign the backend's name to the mixer stream's name for easier identification in the (release) log. */
570 pMixStream->pszName = RTStrAPrintf2("[%s] %s", pCfg->szName, BackendCfg.szName);
571 pMixStream->pszStatPrefix = RTStrAPrintf2("MixerSink-%s/%s/", pSink->pszName, BackendCfg.szName);
572 if (pMixStream->pszName && pMixStream->pszStatPrefix)
573 {
574 rc = RTCritSectInit(&pMixStream->CritSect);
575 if (RT_SUCCESS(rc))
576 {
577 /*
578 * Lock the sink so we can safely get it's properties and call
579 * down into the audio driver to create that end of the stream.
580 */
581 rc = RTCritSectEnter(&pSink->CritSect);
582 AssertRC(rc);
583 if (RT_SUCCESS(rc))
584 {
585 LogFlowFunc(("[%s] (enmDir=%ld, %u bits, %RU8 channels, %RU32Hz)\n", pSink->pszName, pCfg->enmDir,
586 PDMAudioPropsSampleBits(&pCfg->Props), PDMAudioPropsChannels(&pCfg->Props), pCfg->Props.uHz));
587
588 /*
589 * Initialize the host-side configuration for the stream to be created,
590 * this is the sink format & direction with the src/dir, layout, name
591 * and device specific config copied from the guest side config (pCfg).
592 */
593 AssertMsg(AudioHlpPcmPropsAreValid(&pSink->PCMProps),
594 ("%s: Does not (yet) have a format set when it must\n", pSink->pszName));
595
596 PDMAUDIOSTREAMCFG CfgHost;
597 rc = PDMAudioStrmCfgInitWithProps(&CfgHost, &pSink->PCMProps);
598 AssertRC(rc); /* cannot fail */
599 CfgHost.enmDir = pSink->enmDir;
600 CfgHost.enmPath = pCfg->enmPath;
601 CfgHost.enmLayout = pCfg->enmLayout;
602 CfgHost.Device = pCfg->Device;
603 RTStrCopy(CfgHost.szName, sizeof(CfgHost.szName), pCfg->szName);
604
605 /*
606 * Create the stream.
607 *
608 * Output streams are not using any mixing buffers in DrvAudio. This will
609 * become the norm after we move the input mixing here and convert DevSB16
610 * to use this mixer code too.
611 */
612 PPDMAUDIOSTREAM pStream;
613 rc = pConn->pfnStreamCreate(pConn, 0 /*fFlags*/, &CfgHost, pCfg, &pStream);
614 if (RT_SUCCESS(rc))
615 {
616 pMixStream->cFramesBackendBuffer = CfgHost.Backend.cFramesBufferSize;
617
618 /* Set up the mixing buffer conversion state. */
619 if (pSink->enmDir == PDMAUDIODIR_IN)
620 rc = AudioMixBufInitWriteState(&pSink->MixBuf, &pMixStream->WriteState, &pStream->Props);
621 else
622 rc = AudioMixBufInitPeekState(&pSink->MixBuf, &pMixStream->PeekState, &pStream->Props);
623 if (RT_SUCCESS(rc))
624 {
625 /* Save the audio stream pointer to this mixing stream. */
626 pMixStream->pStream = pStream;
627
628 /* Increase the stream's reference count to let others know
629 * we're relying on it to be around now. */
630 pConn->pfnStreamRetain(pConn, pStream);
631 pMixStream->pConn = pConn;
632 pMixStream->uMagic = AUDMIXSTREAM_MAGIC;
633
634 RTCritSectLeave(&pSink->CritSect);
635
636 if (ppStream)
637 *ppStream = pMixStream;
638 return VINF_SUCCESS;
639 }
640
641 rc = pConn->pfnStreamDestroy(pConn, pStream, true /*fImmediate*/);
642 }
643
644 /*
645 * Failed. Tear down the stream.
646 */
647 int rc2 = RTCritSectLeave(&pSink->CritSect);
648 AssertRC(rc2);
649 }
650 RTCritSectDelete(&pMixStream->CritSect);
651 }
652 }
653 else
654 rc = VERR_NO_STR_MEMORY;
655
656 RTStrFree(pMixStream->pszStatPrefix);
657 pMixStream->pszStatPrefix = NULL;
658 RTStrFree(pMixStream->pszName);
659 pMixStream->pszName = NULL;
660 RTMemFree(pMixStream);
661 return rc;
662}
663
664
665/**
666 * Starts playback/capturing on the mixer sink.
667 *
668 * @returns VBox status code. Generally always VINF_SUCCESS unless the input
669 * is invalid. Individual driver errors are suppressed and ignored.
670 * @param pSink Mixer sink to control.
671 */
672int AudioMixerSinkStart(PAUDMIXSINK pSink)
673{
674 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
675 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
676 int rc = RTCritSectEnter(&pSink->CritSect);
677 AssertRCReturn(rc, rc);
678 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
679 LogFunc(("Starting '%s'. Old status: %s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
680
681 AssertReturnStmt(pSink->enmDir == PDMAUDIODIR_IN || pSink->enmDir == PDMAUDIODIR_OUT,
682 RTCritSectLeave(&pSink->CritSect), VERR_INTERNAL_ERROR_3);
683
684 /*
685 * Make sure the sink and its streams are all stopped.
686 */
687 if (!(pSink->fStatus & AUDMIXSINK_STS_RUNNING))
688 Assert(pSink->fStatus == AUDMIXSINK_STS_NONE);
689 else
690 {
691 LogFunc(("%s: This sink is still running!! Stop it before starting it again.\n", pSink->pszName));
692
693 PAUDMIXSTREAM pStream;
694 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
695 {
696 /** @todo PDMAUDIOSTREAMCMD_STOP_NOW */
697 audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_DISABLE);
698 }
699 audioMixerSinkReset(pSink);
700 }
701
702 /*
703 * Send the command to the streams.
704 */
705 PAUDMIXSTREAM pStream;
706 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
707 {
708 audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_ENABLE);
709 }
710
711 /*
712 * Update the sink status.
713 */
714 pSink->fStatus = AUDMIXSINK_STS_RUNNING;
715
716 LogRel2(("Audio Mixer: Started sink '%s': %s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
717
718 RTCritSectLeave(&pSink->CritSect);
719 return VINF_SUCCESS;
720}
721
722
723/**
724 * Helper for AudioMixerSinkDrainAndStop that calculates the max length a drain
725 * operation should take.
726 *
727 * @returns The drain deadline (relative to RTTimeNanoTS).
728 * @param pSink The sink.
729 * @param cbDmaLeftToDrain The number of bytes in the DMA buffer left to
730 * transfer into the mixbuf.
731 */
732static uint64_t audioMixerSinkDrainDeadline(PAUDMIXSINK pSink, uint32_t cbDmaLeftToDrain)
733{
734 /*
735 * Calculate the max backend buffer size in mixbuf frames.
736 * (This is somewhat similar to audioMixerSinkUpdateOutputCalcFramesToRead.)
737 */
738 uint32_t cFramesStreamMax = 0;
739 PAUDMIXSTREAM pMixStream;
740 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
741 {
742 /*LogFunc(("Stream '%s': %#x (%u frames)\n", pMixStream->pszName, pMixStream->fStatus, pMixStream->cFramesBackendBuffer));*/
743 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_CAN_WRITE)
744 {
745 uint32_t cFrames = pMixStream->cFramesBackendBuffer;
746 if (PDMAudioPropsHz(&pMixStream->pStream->Props) == PDMAudioPropsHz(&pSink->MixBuf.Props))
747 { /* likely */ }
748 else
749 cFrames = cFrames * PDMAudioPropsHz(&pSink->MixBuf.Props) / PDMAudioPropsHz(&pMixStream->pStream->Props);
750 if (cFrames > cFramesStreamMax)
751 {
752 Log4Func(("%s: cFramesStreamMax %u -> %u; %s\n", pSink->pszName, cFramesStreamMax, cFrames, pMixStream->pszName));
753 cFramesStreamMax = cFrames;
754 }
755 }
756 }
757
758 /*
759 * Combine that with the pending DMA and mixbuf content, then convert
760 * to nanoseconds and apply a fudge factor to get a generous deadline.
761 */
762 uint32_t const cFramesDmaAndMixBuf = PDMAudioPropsBytesToFrames(&pSink->MixBuf.Props, cbDmaLeftToDrain)
763 + AudioMixBufLive(&pSink->MixBuf);
764 uint64_t const cNsToDrainMax = PDMAudioPropsFramesToNano(&pSink->MixBuf.Props, cFramesDmaAndMixBuf + cFramesStreamMax);
765 uint64_t const nsDeadline = cNsToDrainMax * 2;
766 LogFlowFunc(("%s: cFramesStreamMax=%#x cFramesDmaAndMixBuf=%#x -> cNsToDrainMax=%RU64 -> %RU64\n",
767 pSink->pszName, cFramesStreamMax, cFramesDmaAndMixBuf, cNsToDrainMax, nsDeadline));
768 return nsDeadline;
769}
770
771
772/**
773 * Kicks off the draining and stopping playback/capture on the mixer sink.
774 *
775 * For input streams this causes an immediate stop, as draining only makes sense
776 * to output stream in the VBox device context.
777 *
778 * @returns VBox status code. Generally always VINF_SUCCESS unless the input
779 * is invalid. Individual driver errors are suppressed and ignored.
780 * @param pSink Mixer sink to control.
781 * @param cbComming The number of bytes still left in the device's DMA
782 * buffers that the update job has yet to transfer. This
783 * is ignored for input streams.
784 */
785int AudioMixerSinkDrainAndStop(PAUDMIXSINK pSink, uint32_t cbComming)
786{
787 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
788 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
789
790 int rc = RTCritSectEnter(&pSink->CritSect);
791 AssertRCReturn(rc, rc);
792 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
793 LogFunc(("Draining '%s' with %#x bytes left. Old status: %s\n",
794 pSink->pszName, cbComming, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus) ));
795
796 AssertReturnStmt(pSink->enmDir == PDMAUDIODIR_IN || pSink->enmDir == PDMAUDIODIR_OUT,
797 RTCritSectLeave(&pSink->CritSect), VERR_INTERNAL_ERROR_3);
798
799 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
800 {
801 /*
802 * Output streams will be drained then stopped (all by the AIO thread).
803 *
804 * For streams we define that they shouldn't not be written to after we start draining,
805 * so we have to hold back sending the command to them till we've processed all the
806 * cbComming remaining bytes in the DMA buffer.
807 */
808 if (pSink->enmDir == PDMAUDIODIR_OUT)
809 {
810 if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
811 {
812 Assert(!(pSink->fStatus & (AUDMIXSINK_STS_DRAINED_DMA | AUDMIXSINK_STS_DRAINED_MIXBUF)));
813
814 /* Update the status and draining member. */
815 pSink->cbDmaLeftToDrain = cbComming;
816 pSink->nsDrainDeadline = audioMixerSinkDrainDeadline(pSink, cbComming);
817 if (pSink->nsDrainDeadline > 0)
818 {
819 pSink->nsDrainStarted = RTTimeNanoTS();
820 pSink->nsDrainDeadline += pSink->nsDrainStarted;
821 pSink->fStatus |= AUDMIXSINK_STS_DRAINING;
822
823 /* Kick the AIO thread so it can keep pushing data till we're out of this
824 status. (The device's DMA timer won't kick it any more, so we must.) */
825 AudioMixerSinkSignalUpdateJob(pSink);
826 }
827 else
828 {
829 LogFunc(("%s: No active streams, doing an immediate stop.\n", pSink->pszName));
830 PAUDMIXSTREAM pStream;
831 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
832 {
833 audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_DISABLE);
834 }
835 audioMixerSinkReset(pSink);
836 }
837 }
838 else
839 AssertMsgFailed(("Already draining '%s': %s\n",
840 pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
841 }
842 /*
843 * Input sinks are stopped immediately.
844 *
845 * It's the guest giving order here and we can't force it to accept data that's
846 * already in the buffer pipeline or anything. So, there can be no draining here.
847 */
848 else
849 {
850 PAUDMIXSTREAM pStream;
851 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
852 {
853 audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_DISABLE);
854 }
855 audioMixerSinkReset(pSink);
856 }
857 }
858 else
859 LogFunc(("%s: Not running\n", pSink->pszName));
860
861 LogRel2(("Audio Mixer: Started draining sink '%s': %s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
862 RTCritSectLeave(&pSink->CritSect);
863 return VINF_SUCCESS;
864}
865
866
867
868/**
869 * Initializes a sink.
870 *
871 * @returns VBox status code.
872 * @param pSink Sink to initialize.
873 * @param pMixer Mixer the sink is assigned to.
874 * @param pcszName Name of the sink.
875 * @param enmDir Direction of the sink.
876 * @param pDevIns The device instance.
877 */
878static int audioMixerSinkInit(PAUDMIXSINK pSink, PAUDIOMIXER pMixer, const char *pcszName, PDMAUDIODIR enmDir, PPDMDEVINS pDevIns)
879{
880 pSink->pszName = RTStrDup(pcszName);
881 if (!pSink->pszName)
882 return VERR_NO_MEMORY;
883
884 int rc = RTCritSectInit(&pSink->CritSect);
885 if (RT_SUCCESS(rc))
886 {
887 pSink->uMagic = AUDMIXSINK_MAGIC;
888 pSink->pParent = pMixer;
889 pSink->enmDir = enmDir;
890
891 RTListInit(&pSink->lstStreams);
892
893 /* Set initial volume to max. */
894 pSink->Volume.fMuted = false;
895 pSink->Volume.uLeft = PDMAUDIO_VOLUME_MAX;
896 pSink->Volume.uRight = PDMAUDIO_VOLUME_MAX;
897
898 /* Ditto for the combined volume. */
899 pSink->VolumeCombined.fMuted = false;
900 pSink->VolumeCombined.uLeft = PDMAUDIO_VOLUME_MAX;
901 pSink->VolumeCombined.uRight = PDMAUDIO_VOLUME_MAX;
902
903 /* AIO */
904 AssertPtr(pDevIns);
905 pSink->AIO.pDevIns = pDevIns;
906 pSink->AIO.hThread = NIL_RTTHREAD;
907 pSink->AIO.hEvent = NIL_RTSEMEVENT;
908 pSink->AIO.fStarted = false;
909 pSink->AIO.fShutdown = false;
910 pSink->AIO.cUpdateJobs = 0;
911 }
912
913 LogFlowFuncLeaveRC(rc);
914 return rc;
915}
916
917/**
918 * Destroys a mixer sink and removes it from the attached mixer (if any).
919 *
920 * @param pSink Mixer sink to destroy.
921 * @param pDevIns The device instance that statistics are registered with.
922 */
923void AudioMixerSinkDestroy(PAUDMIXSINK pSink, PPDMDEVINS pDevIns)
924{
925 if (!pSink)
926 return;
927
928 /** @todo wrong critsect for audioMixerRemoveSinkInternal... */
929 int rc2 = RTCritSectEnter(&pSink->CritSect);
930 AssertRC(rc2);
931
932 if (pSink->pParent)
933 {
934 /* Save mixer pointer, as after audioMixerRemoveSinkInternal() the
935 * pointer will be gone from the stream. */
936 PAUDIOMIXER pMixer = pSink->pParent;
937 AssertPtr(pMixer);
938 Assert(pMixer->uMagic == AUDIOMIXER_MAGIC);
939
940 audioMixerRemoveSinkInternal(pMixer, pSink);
941 }
942
943 rc2 = RTCritSectLeave(&pSink->CritSect);
944 AssertRC(rc2);
945
946 audioMixerSinkDestroyInternal(pSink, pDevIns);
947
948 RTMemFree(pSink);
949 pSink = NULL;
950}
951
952/**
953 * Destroys a mixer sink.
954 *
955 * @param pSink Mixer sink to destroy.
956 * @param pDevIns The device instance statistics are registered with.
957 */
958static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink, PPDMDEVINS pDevIns)
959{
960 AssertPtrReturnVoid(pSink);
961
962 LogFunc(("%s\n", pSink->pszName));
963
964 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
965 pSink->uMagic = AUDMIXSINK_MAGIC_DEAD;
966
967 PAUDMIXSTREAM pStream, pStreamNext;
968 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
969 {
970 audioMixerSinkRemoveStreamInternal(pSink, pStream);
971 audioMixerStreamDestroyInternal(pStream, pDevIns, true /*fImmediate*/); /* (Unlike the other two, this frees the stream structure.) */
972 }
973
974 if ( pSink->pParent
975 && pSink->pParent->fFlags & AUDMIXER_FLAGS_DEBUG)
976 {
977 AudioHlpFileDestroy(pSink->Dbg.pFile);
978 pSink->Dbg.pFile = NULL;
979 }
980
981 char szPrefix[128];
982 RTStrPrintf(szPrefix, sizeof(szPrefix), "MixerSink-%s/", pSink->pszName);
983 PDMDevHlpSTAMDeregisterByPrefix(pDevIns, szPrefix);
984
985 /* Shutdown the AIO thread if started: */
986 ASMAtomicWriteBool(&pSink->AIO.fShutdown, true);
987 if (pSink->AIO.hEvent != NIL_RTSEMEVENT)
988 {
989 int rc2 = RTSemEventSignal(pSink->AIO.hEvent);
990 AssertRC(rc2);
991 }
992 if (pSink->AIO.hThread != NIL_RTTHREAD)
993 {
994 LogFlowFunc(("Waiting for AIO thread for %s...\n", pSink->pszName));
995 int rc2 = RTThreadWait(pSink->AIO.hThread, RT_MS_30SEC, NULL);
996 AssertRC(rc2);
997 pSink->AIO.hThread = NIL_RTTHREAD;
998 }
999 if (pSink->AIO.hEvent != NIL_RTSEMEVENT)
1000 {
1001 int rc2 = RTSemEventDestroy(pSink->AIO.hEvent);
1002 AssertRC(rc2);
1003 pSink->AIO.hEvent = NIL_RTSEMEVENT;
1004 }
1005
1006 RTStrFree(pSink->pszName);
1007 pSink->pszName = NULL;
1008
1009 AudioMixBufDestroy(&pSink->MixBuf);
1010 RTCritSectDelete(&pSink->CritSect);
1011}
1012
1013/**
1014 * Returns the amount of bytes ready to be read from a sink since the last call
1015 * to AudioMixerSinkUpdate().
1016 *
1017 * @returns Amount of bytes ready to be read from the sink.
1018 * @param pSink Sink to return number of available bytes for.
1019 */
1020uint32_t AudioMixerSinkGetReadable(PAUDMIXSINK pSink)
1021{
1022 AssertPtrReturn(pSink, 0);
1023 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1024 AssertMsg(pSink->enmDir == PDMAUDIODIR_IN, ("%s: Can't read from a non-input sink\n", pSink->pszName));
1025
1026 int rc = RTCritSectEnter(&pSink->CritSect);
1027 AssertRCReturn(rc, 0);
1028
1029 uint32_t cbReadable = 0;
1030
1031 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
1032 {
1033 uint32_t const cFrames = AudioMixBufLive(&pSink->MixBuf);
1034 cbReadable = PDMAudioPropsFramesToBytes(&pSink->PCMProps, cFrames);
1035 }
1036
1037 Log3Func(("[%s] cbReadable=%RU32\n", pSink->pszName, cbReadable));
1038
1039 int rc2 = RTCritSectLeave(&pSink->CritSect);
1040 AssertRC(rc2);
1041
1042 return cbReadable;
1043}
1044
1045/**
1046 * Returns the amount of bytes ready to be written to a sink since the last call
1047 * to AudioMixerSinkUpdate().
1048 *
1049 * @returns Amount of bytes ready to be written to the sink.
1050 * @param pSink Sink to return number of available bytes for.
1051 */
1052uint32_t AudioMixerSinkGetWritable(PAUDMIXSINK pSink)
1053{
1054 AssertPtrReturn(pSink, 0);
1055 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1056 AssertMsg(pSink->enmDir == PDMAUDIODIR_OUT, ("%s: Can't write to a non-output sink\n", pSink->pszName));
1057
1058 int rc = RTCritSectEnter(&pSink->CritSect);
1059 AssertRCReturn(rc, 0);
1060
1061 uint32_t cbWritable = 0;
1062
1063 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
1064 && !(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
1065 {
1066 cbWritable = AudioMixBufFreeBytes(&pSink->MixBuf);
1067 }
1068
1069 Log3Func(("[%s] cbWritable=%RU32 (%RU64ms)\n",
1070 pSink->pszName, cbWritable, PDMAudioPropsBytesToMilli(&pSink->PCMProps, cbWritable)));
1071
1072 int rc2 = RTCritSectLeave(&pSink->CritSect);
1073 AssertRC(rc2);
1074
1075 return cbWritable;
1076}
1077
1078/**
1079 * Returns the sink's mixing direction.
1080 *
1081 * @returns Mixing direction.
1082 * @param pSink Sink to return direction for.
1083 */
1084PDMAUDIODIR AudioMixerSinkGetDir(PAUDMIXSINK pSink)
1085{
1086 AssertPtrReturn(pSink, PDMAUDIODIR_INVALID);
1087 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1088
1089 /** @todo the sink direction should be static... */
1090 int rc = RTCritSectEnter(&pSink->CritSect);
1091 AssertRCReturn(rc, PDMAUDIODIR_INVALID);
1092
1093 PDMAUDIODIR const enmDir = pSink->enmDir;
1094
1095 int rc2 = RTCritSectLeave(&pSink->CritSect);
1096 AssertRC(rc2);
1097
1098 return enmDir;
1099}
1100
1101/**
1102 * Returns the current status of a mixer sink.
1103 *
1104 * @returns The sink's current status (AUDMIXSINK_STS_XXX).
1105 * @param pSink Mixer sink to return status for.
1106 */
1107uint32_t AudioMixerSinkGetStatus(PAUDMIXSINK pSink)
1108{
1109 if (!pSink)
1110 return AUDMIXSINK_STS_NONE;
1111 AssertPtr(pSink);
1112 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1113
1114 int rc2 = RTCritSectEnter(&pSink->CritSect);
1115 AssertRCReturn(rc2, AUDMIXSINK_STS_NONE);
1116
1117 /* If the dirty flag is set, there is unprocessed data in the sink. */
1118 uint32_t const fStsSink = pSink->fStatus;
1119
1120 rc2 = RTCritSectLeave(&pSink->CritSect);
1121 AssertRC(rc2);
1122
1123 return fStsSink;
1124}
1125
1126/**
1127 * Returns whether the sink is in an active state or not.
1128 *
1129 * @note The pending disable state also counts as active.
1130 *
1131 * @returns True if active, false if not.
1132 * @param pSink Sink to return active state for.
1133 */
1134bool AudioMixerSinkIsActive(PAUDMIXSINK pSink)
1135{
1136 if (!pSink)
1137 return false;
1138 AssertPtr(pSink);
1139 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1140
1141 int rc2 = RTCritSectEnter(&pSink->CritSect);
1142 AssertRCReturn(rc2, false);
1143
1144 const bool fIsActive = pSink->fStatus & AUDMIXSINK_STS_RUNNING;
1145 /* Note: AUDMIXSINK_STS_PENDING_DISABLE implies AUDMIXSINK_STS_RUNNING. */
1146
1147 Log3Func(("[%s] fActive=%RTbool\n", pSink->pszName, fIsActive));
1148
1149 rc2 = RTCritSectLeave(&pSink->CritSect);
1150 AssertRC(rc2);
1151
1152 return fIsActive;
1153}
1154
1155/**
1156 * Removes a mixer stream from a mixer sink, internal version.
1157 *
1158 * @returns VBox status code.
1159 * @param pSink Sink to remove mixer stream from.
1160 * @param pStream Stream to remove.
1161 */
1162static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1163{
1164 AssertPtrReturn(pSink, VERR_INVALID_PARAMETER);
1165 if (pStream)
1166 { /* likely */ }
1167 else
1168 return VERR_NOT_FOUND;
1169 AssertMsgReturn(pStream->pSink == pSink, ("Stream '%s' is not part of sink '%s'\n",
1170 pStream->pszName, pSink->pszName), VERR_NOT_FOUND);
1171
1172 LogFlowFunc(("[%s] (Stream = %s), cStreams=%RU8\n",
1173 pSink->pszName, pStream->pStream->szName, pSink->cStreams));
1174
1175 /* Remove stream from sink. */
1176 RTListNodeRemove(&pStream->Node);
1177
1178 /* Set sink to NULL so that we know we're not part of any sink anymore. */
1179 pStream->pSink = NULL;
1180
1181 return VINF_SUCCESS;
1182}
1183
1184/**
1185 * Removes a mixer stream from a mixer sink.
1186 *
1187 * @param pSink Sink to remove mixer stream from.
1188 * @param pStream Stream to remove.
1189 */
1190void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1191{
1192 AssertPtr(pSink);
1193 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1194 if (pStream)
1195 Assert(pStream->uMagic == AUDMIXSTREAM_MAGIC);
1196
1197 int rc2 = RTCritSectEnter(&pSink->CritSect);
1198 AssertRC(rc2);
1199
1200 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pStream);
1201 if (RT_SUCCESS(rc2))
1202 {
1203 Assert(pSink->cStreams);
1204 pSink->cStreams--;
1205 }
1206
1207 rc2 = RTCritSectLeave(&pSink->CritSect);
1208 AssertRC(rc2);
1209}
1210
1211/**
1212 * Removes all attached streams from a given sink.
1213 *
1214 * @param pSink Sink to remove attached streams from.
1215 */
1216static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink)
1217{
1218 if (!pSink)
1219 return;
1220
1221 LogFunc(("%s\n", pSink->pszName));
1222
1223 PAUDMIXSTREAM pStream, pStreamNext;
1224 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
1225 audioMixerSinkRemoveStreamInternal(pSink, pStream);
1226}
1227
1228/**
1229 * Removes all attached streams from a given sink.
1230 *
1231 * @param pSink Sink to remove attached streams from.
1232 */
1233void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
1234{
1235 if (!pSink)
1236 return;
1237
1238 int rc2 = RTCritSectEnter(&pSink->CritSect);
1239 AssertRC(rc2);
1240
1241 audioMixerSinkRemoveAllStreamsInternal(pSink);
1242
1243 pSink->cStreams = 0;
1244
1245 rc2 = RTCritSectLeave(&pSink->CritSect);
1246 AssertRC(rc2);
1247}
1248
1249/**
1250 * Resets the sink's state.
1251 *
1252 * @param pSink Sink to reset.
1253 */
1254static void audioMixerSinkReset(PAUDMIXSINK pSink)
1255{
1256 if (!pSink)
1257 return;
1258
1259 LogFunc(("[%s]\n", pSink->pszName));
1260
1261 AudioMixBufReset(&pSink->MixBuf);
1262
1263 /* Update last updated timestamp. */
1264 pSink->tsLastUpdatedMs = 0;
1265
1266 /* Reset status. */
1267 pSink->fStatus = AUDMIXSINK_STS_NONE;
1268}
1269
1270/**
1271 * Resets a sink. This will immediately stop all processing.
1272 *
1273 * @param pSink Sink to reset.
1274 */
1275void AudioMixerSinkReset(PAUDMIXSINK pSink)
1276{
1277 if (!pSink)
1278 return;
1279
1280 int rc2 = RTCritSectEnter(&pSink->CritSect);
1281 AssertRC(rc2);
1282
1283 LogFlowFunc(("[%s]\n", pSink->pszName));
1284
1285 audioMixerSinkReset(pSink);
1286
1287 rc2 = RTCritSectLeave(&pSink->CritSect);
1288 AssertRC(rc2);
1289}
1290
1291/**
1292 * Sets the audio format of a mixer sink.
1293 *
1294 * @returns VBox status code.
1295 * @param pSink The sink to set audio format for.
1296 * @param pProps The properties of the new audio format.
1297 */
1298int AudioMixerSinkSetFormat(PAUDMIXSINK pSink, PCPDMAUDIOPCMPROPS pProps)
1299{
1300 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1301 AssertReturn(pSink->uMagic == AUDMIXSINK_MAGIC, VERR_INVALID_MAGIC);
1302 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
1303 AssertReturn(AudioHlpPcmPropsAreValid(pProps), VERR_INVALID_PARAMETER);
1304
1305 int rc = RTCritSectEnter(&pSink->CritSect);
1306 AssertRCReturn(rc, rc);
1307
1308 /*
1309 * Nothing to do here unless the format changed.
1310 */
1311 if (!PDMAudioPropsAreEqual(&pSink->PCMProps, pProps))
1312 {
1313#ifdef LOG_ENABLED
1314 char szTmp[PDMAUDIOPROPSTOSTRING_MAX];
1315#endif
1316 if (PDMAudioPropsHz(&pSink->PCMProps) != 0)
1317 LogFlowFunc(("[%s] Old format: %s\n", pSink->pszName, PDMAudioPropsToString(&pSink->PCMProps, szTmp, sizeof(szTmp)) ));
1318
1319 pSink->PCMProps = *pProps;
1320 LogFlowFunc(("[%s] New format: %s\n", pSink->pszName, PDMAudioPropsToString(&pSink->PCMProps, szTmp, sizeof(szTmp)) ));
1321
1322 /*
1323 * Also update the sink's mixing buffer format.
1324 */
1325 AudioMixBufDestroy(&pSink->MixBuf);
1326
1327 /** @todo r=bird: Make sure we've got more room here than what's expected to
1328 * be moved in one guest DMA period. */
1329 rc = AudioMixBufInit(&pSink->MixBuf, pSink->pszName, &pSink->PCMProps,
1330 PDMAudioPropsMilliToFrames(&pSink->PCMProps, 100 /*ms*/)); /** @todo Make this configurable? */
1331 if (RT_SUCCESS(rc))
1332 {
1333 /*
1334 * Input sinks must init their (mostly dummy) peek state.
1335 */
1336 if (pSink->enmDir == PDMAUDIODIR_IN)
1337 rc = AudioMixBufInitPeekState(&pSink->MixBuf, &pSink->In.State, &pSink->PCMProps);
1338 if (RT_SUCCESS(rc))
1339 {
1340 /*
1341 * Re-initialize the peek/write states as the frequency, channel count
1342 * and other things may have changed now.
1343 */
1344 PAUDMIXSTREAM pMixStream;
1345 if (pSink->enmDir == PDMAUDIODIR_IN)
1346 {
1347 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1348 {
1349 int rc2 = AudioMixBufInitWriteState(&pSink->MixBuf, &pMixStream->WriteState, &pMixStream->pStream->Props);
1350 /** @todo remember this. */
1351 AssertLogRelRC(rc2);
1352 }
1353 }
1354 else
1355 {
1356 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1357 {
1358 int rc2 = AudioMixBufInitPeekState(&pSink->MixBuf, &pMixStream->PeekState, &pMixStream->pStream->Props);
1359 /** @todo remember this. */
1360 AssertLogRelRC(rc2);
1361 }
1362 }
1363
1364 /*
1365 * Debug.
1366 */
1367 if (!(pSink->pParent->fFlags & AUDMIXER_FLAGS_DEBUG))
1368 { /* likely */ }
1369 else
1370 {
1371 AudioHlpFileClose(pSink->Dbg.pFile);
1372
1373 char szName[64];
1374 RTStrPrintf(szName, sizeof(szName), "MixerSink-%s", pSink->pszName);
1375 AudioHlpFileCreateAndOpen(&pSink->Dbg.pFile, NULL /*pszDir - use temp dir*/, szName,
1376 0 /*iInstance*/, &pSink->PCMProps);
1377 }
1378 }
1379 else
1380 LogFunc(("AudioMixBufInitPeekState failed: %Rrc\n", rc));
1381 }
1382 else
1383 LogFunc(("AudioMixBufInit failed: %Rrc\n", rc));
1384 }
1385
1386 RTCritSectLeave(&pSink->CritSect);
1387 LogFlowFuncLeaveRC(rc);
1388 return rc;
1389}
1390
1391/**
1392 * Sets the volume of an individual sink.
1393 *
1394 * @returns VBox status code.
1395 * @param pSink Sink to set volume for.
1396 * @param pVol Volume to set.
1397 */
1398int AudioMixerSinkSetVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
1399{
1400 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1401 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1402 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
1403
1404 int rc = RTCritSectEnter(&pSink->CritSect);
1405 AssertRCReturn(rc, rc);
1406
1407 memcpy(&pSink->Volume, pVol, sizeof(PDMAUDIOVOLUME));
1408
1409 LogRel2(("Audio Mixer: Setting volume of sink '%s' to %RU8/%RU8 (%s)\n",
1410 pSink->pszName, pVol->uLeft, pVol->uRight, pVol->fMuted ? "Muted" : "Unmuted"));
1411
1412 AssertPtr(pSink->pParent);
1413 rc = audioMixerSinkUpdateVolume(pSink, &pSink->pParent->VolMaster);
1414
1415 int rc2 = RTCritSectLeave(&pSink->CritSect);
1416 AssertRC(rc2);
1417
1418 return rc;
1419}
1420
1421
1422/**
1423 * Helper for audioMixerSinkUpdateInput that determins now many frames it can
1424 * transfer from the drivers and into the sink's mixer buffer.
1425 *
1426 * This also updates the mixer stream status, which may involve stream re-inits.
1427 *
1428 * @returns Number of frames.
1429 * @param pSink The sink.
1430 * @param pcReadableStreams Where to return the number of readable streams.
1431 */
1432static uint32_t audioMixerSinkUpdateInputCalcFramesToTransfer(PAUDMIXSINK pSink, uint32_t *pcReadableStreams)
1433{
1434 uint32_t cFramesToRead = AudioMixBufFree(&pSink->MixBuf);
1435 uint32_t cReadableStreams = 0;
1436 PAUDMIXSTREAM pMixStream;
1437 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1438 {
1439 int rc2 = audioMixerStreamUpdateStatus(pMixStream);
1440 AssertRC(rc2);
1441
1442 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_CAN_READ)
1443 {
1444 PPDMIAUDIOCONNECTOR const pIConnector = pMixStream->pConn;
1445 PPDMAUDIOSTREAM const pStream = pMixStream->pStream;
1446 pIConnector->pfnStreamIterate(pIConnector, pStream);
1447
1448 uint32_t const cbReadable = pIConnector->pfnStreamGetReadable(pIConnector, pStream);
1449 uint32_t cFrames = PDMAudioPropsBytesToFrames(&pStream->Props, cbReadable);
1450 pMixStream->cFramesLastAvail = cFrames;
1451 if (PDMAudioPropsHz(&pStream->Props) == PDMAudioPropsHz(&pSink->MixBuf.Props))
1452 { /* likely */ }
1453 else
1454 {
1455 cFrames = cFrames * PDMAudioPropsHz(&pSink->MixBuf.Props) / PDMAudioPropsHz(&pStream->Props);
1456 cFrames = cFrames > 2 ? cFrames - 2 : 0; /* rounding safety fudge */
1457 }
1458 if (cFramesToRead > cFrames && !pMixStream->fUnreliable)
1459 {
1460 Log4Func(("%s: cFramesToRead %u -> %u; %s (%u bytes readable)\n",
1461 pSink->pszName, cFramesToRead, cFrames, pMixStream->pszName, cbReadable));
1462 cFramesToRead = cFrames;
1463 }
1464 cReadableStreams++;
1465 }
1466 }
1467
1468 *pcReadableStreams = cReadableStreams;
1469 return cFramesToRead;
1470}
1471
1472
1473/**
1474 * Updates an input mixer sink.
1475 *
1476 * @returns VBox status code.
1477 * @param pSink Mixer sink to update.
1478 * @param cbDmaBuf The number of bytes in the DMA buffer. For detecting
1479 * underruns. Zero if we don't know.
1480 * @param cbDmaPeriod The minimum number of bytes required for reliable DMA
1481 * operation. Zero if we don't know.
1482 */
1483static int audioMixerSinkUpdateInput(PAUDMIXSINK pSink, uint32_t cbDmaBuf, uint32_t cbDmaPeriod)
1484{
1485 PAUDMIXSTREAM pMixStream;
1486 Assert(!(pSink->fStatus & AUDMIXSINK_STS_DRAINED_MIXBUF)); /* (can't drain input sink) */
1487
1488 /*
1489 * Iterate, update status and check each mixing sink stream for how much
1490 * we can transfer.
1491 *
1492 * We're currently using the minimum size of all streams, however this
1493 * isn't a smart approach as it means one disfunctional stream can block
1494 * working ones. So, if we end up with zero frames and a full mixer
1495 * buffer we'll disregard the stream that accept the smallest amount and
1496 * try again.
1497 */
1498 uint32_t cReadableStreams = 0;
1499 uint32_t cFramesToXfer = audioMixerSinkUpdateInputCalcFramesToTransfer(pSink, &cReadableStreams);
1500 if ( cFramesToXfer != 0
1501 || cReadableStreams <= 1
1502 || cbDmaPeriod == 0 /* Insufficient info to decide. The update function will call us again, at least for HDA. */
1503 || cbDmaBuf + PDMAudioPropsFramesToBytes(&pSink->PCMProps, AudioMixBufUsed(&pSink->MixBuf)) >= cbDmaPeriod)
1504 Log3Func(("%s: cFreeFrames=%#x cFramesToXfer=%#x cReadableStreams=%#x\n", pSink->pszName,
1505 AudioMixBufFree(&pSink->MixBuf), cFramesToXfer, cReadableStreams));
1506 else
1507 {
1508 Log3Func(("%s: MixBuf is underrunning but one or more streams only provides zero frames. Try disregarding those...\n", pSink->pszName));
1509 uint32_t cReliableStreams = 0;
1510 uint32_t cMarkedUnreliable = 0;
1511 PAUDMIXSTREAM pMixStreamMin = NULL;
1512 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1513 {
1514 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_CAN_READ)
1515 {
1516 if (!pMixStream->fUnreliable)
1517 {
1518 if (pMixStream->cFramesLastAvail == 0)
1519 {
1520 cMarkedUnreliable++;
1521 pMixStream->fUnreliable = true;
1522 Log3Func(("%s: Marked '%s' as unreliable.\n", pSink->pszName, pMixStream->pszName));
1523 pMixStreamMin = pMixStream;
1524 }
1525 else
1526 {
1527 if (!pMixStreamMin || pMixStream->cFramesLastAvail < pMixStreamMin->cFramesLastAvail)
1528 pMixStreamMin = pMixStream;
1529 cReliableStreams++;
1530 }
1531 }
1532 }
1533 }
1534
1535 if (cMarkedUnreliable == 0 && cReliableStreams > 1 && pMixStreamMin != NULL)
1536 {
1537 cReliableStreams--;
1538 cMarkedUnreliable++;
1539 pMixStreamMin->fUnreliable = true;
1540 Log3Func(("%s: Marked '%s' as unreliable (%u frames).\n",
1541 pSink->pszName, pMixStreamMin->pszName, pMixStreamMin->cFramesLastAvail));
1542 }
1543
1544 if (cMarkedUnreliable > 0)
1545 {
1546 cReadableStreams = 0;
1547 cFramesToXfer = audioMixerSinkUpdateInputCalcFramesToTransfer(pSink, &cReadableStreams);
1548 }
1549
1550 Log3Func(("%s: cFreeFrames=%#x cFramesToXfer=%#x cReadableStreams=%#x cMarkedUnreliable=%#x cReliableStreams=%#x\n",
1551 pSink->pszName, AudioMixBufFree(&pSink->MixBuf), cFramesToXfer,
1552 cReadableStreams, cMarkedUnreliable, cReliableStreams));
1553 }
1554
1555 if (cReadableStreams > 0)
1556 {
1557 if (cFramesToXfer > 0)
1558 {
1559/*#define ELECTRIC_INPUT_BUFFER*/ /* if buffer code is misbehaving, enable this to catch overflows. */
1560#ifndef ELECTRIC_INPUT_BUFFER
1561 union
1562 {
1563 uint8_t ab[8192];
1564 uint64_t au64[8192 / sizeof(uint64_t)]; /* Use uint64_t to ensure good alignment. */
1565 } Buf;
1566 void * const pvBuf = &Buf;
1567 uint32_t const cbBuf = sizeof(Buf);
1568#else
1569 uint32_t const cbBuf = 0x2000 - 16;
1570 void * const pvBuf = RTMemEfAlloc(cbBuf, RTMEM_TAG, RT_SRC_POS);
1571#endif
1572
1573 /*
1574 * For each of the enabled streams, read cFramesToXfer frames worth
1575 * of samples from them and merge that into the mixing buffer.
1576 */
1577 bool fAssign = true;
1578 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1579 {
1580 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_CAN_READ)
1581 {
1582 PPDMIAUDIOCONNECTOR const pIConnector = pMixStream->pConn;
1583 PPDMAUDIOSTREAM const pStream = pMixStream->pStream;
1584
1585 /* Calculate how many bytes we should read from this stream. */
1586 bool const fResampleSrc = PDMAudioPropsHz(&pStream->Props) != PDMAudioPropsHz(&pSink->MixBuf.Props);
1587 uint32_t const cbSrcToXfer = !fResampleSrc
1588 ? PDMAudioPropsFramesToBytes(&pStream->Props, cFramesToXfer)
1589 : PDMAudioPropsFramesToBytes(&pStream->Props, /** @todo check rounding errors here... */
1590 cFramesToXfer * PDMAudioPropsHz(&pSink->MixBuf.Props)
1591 / PDMAudioPropsHz(&pStream->Props));
1592
1593 /* Do the reading. */
1594 uint32_t offSrc = 0;
1595 uint32_t offDstFrame = 0;
1596 do
1597 {
1598 /*
1599 * Read a chunk from the backend.
1600 */
1601 uint32_t const cbSrcToRead = RT_MIN(cbBuf, cbSrcToXfer - offSrc);
1602 uint32_t cbSrcRead = 0;
1603 if (cbSrcToRead > 0)
1604 {
1605 int rc2 = pIConnector->pfnStreamCapture(pIConnector, pStream, pvBuf, cbSrcToRead, &cbSrcRead);
1606 Log3Func(("%s: %#x L %#x => %#x bytes; rc2=%Rrc %s\n",
1607 pSink->pszName, offSrc, cbSrcToRead, cbSrcRead, rc2, pMixStream->pszName));
1608
1609 if (RT_SUCCESS(rc2))
1610 AssertLogRelMsg(cbSrcRead == cbSrcToRead || pMixStream->fUnreliable,
1611 ("cbSrcRead=%#x cbSrcToRead=%#x - (sink '%s')\n",
1612 cbSrcRead, cbSrcToRead, pSink->pszName));
1613 else if (rc2 == VERR_AUDIO_STREAM_NOT_READY)
1614 {
1615 LogRel2(("Audio Mixer: '%s' (sink '%s'): Stream not ready - skipping.\n",
1616 pMixStream->pszName, pSink->pszName)); /* must've changed status, stop processing */
1617 break;
1618 }
1619 else
1620 {
1621 Assert(rc2 != VERR_BUFFER_OVERFLOW);
1622 LogRel2(("Audio Mixer: Reading from mixer stream '%s' (sink '%s') failed, rc=%Rrc\n",
1623 pMixStream->pszName, pSink->pszName, rc2));
1624 break;
1625 }
1626 offSrc += cbSrcRead;
1627 }
1628 else
1629 Assert(fResampleSrc); /** @todo test this case */
1630
1631 /*
1632 * Assign or blend it into the mixer buffer.
1633 */
1634 uint32_t cFramesDstTransferred = 0;
1635 if (fAssign)
1636 {
1637 /** @todo could complicate this by detecting silence here too and stay in
1638 * assign mode till we get a stream with non-silence... */
1639 AudioMixBufWrite(&pSink->MixBuf, &pMixStream->WriteState, pvBuf, cbSrcRead,
1640 offDstFrame, cFramesToXfer - offDstFrame, &cFramesDstTransferred);
1641 }
1642 /* We don't need to blend silence buffers. For simplicity, always blend
1643 when we're resampling (for rounding). */
1644 else if (fResampleSrc || !PDMAudioPropsIsBufferSilence(&pStream->Props, pvBuf, cbSrcRead))
1645 {
1646 AudioMixBufBlend(&pSink->MixBuf, &pMixStream->WriteState, pvBuf, cbSrcRead,
1647 offDstFrame, cFramesToXfer - offDstFrame, &cFramesDstTransferred);
1648 }
1649 else
1650 {
1651 cFramesDstTransferred = PDMAudioPropsBytesToFrames(&pStream->Props, cbSrcRead);
1652 AudioMixBufBlendGap(&pSink->MixBuf, &pMixStream->WriteState, cFramesDstTransferred);
1653 }
1654 AssertBreak(cFramesDstTransferred > 0);
1655
1656 /* Advance. */
1657 offDstFrame += cFramesDstTransferred;
1658 } while (offDstFrame < cFramesToXfer);
1659
1660 /*
1661 * In case the first stream is misbehaving, make sure we written the entire area.
1662 */
1663 if (offDstFrame >= cFramesToXfer)
1664 { /* likely */ }
1665 else if (fAssign)
1666 AudioMixBufSilence(&pSink->MixBuf, &pMixStream->WriteState, offDstFrame, cFramesToXfer - offDstFrame);
1667 else
1668 AudioMixBufBlendGap(&pSink->MixBuf, &pMixStream->WriteState, cFramesToXfer - offDstFrame);
1669 fAssign = false;
1670 }
1671 }
1672
1673 /*
1674 * Commit the buffer area we've written and blended into.
1675 */
1676 AudioMixBufCommit(&pSink->MixBuf, cFramesToXfer);
1677
1678#ifdef ELECTRIC_INPUT_BUFFER
1679 RTMemEfFree(pvBuf, RT_SRC_POS);
1680#endif
1681 }
1682
1683 /*
1684 * Set the dirty flag for what it's worth.
1685 */
1686 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1687 }
1688 else
1689 {
1690 /*
1691 * No readable stream. Clear the dirty flag if empty (pointless flag).
1692 */
1693 if (!AudioMixBufUsed(&pSink->MixBuf))
1694 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1695 }
1696
1697 /* Update last updated timestamp. */
1698 pSink->tsLastUpdatedMs = RTTimeMilliTS();
1699
1700 return VINF_SUCCESS;
1701}
1702
1703
1704/**
1705 * Helper for audioMixerSinkUpdateOutput that determins now many frames it
1706 * can transfer from the sink's mixer buffer and to the drivers.
1707 *
1708 * This also updates the mixer stream status, which may involve stream re-inits.
1709 *
1710 * @returns Number of frames.
1711 * @param pSink The sink.
1712 * @param pcWritableStreams Where to return the number of writable streams.
1713 */
1714static uint32_t audioMixerSinkUpdateOutputCalcFramesToRead(PAUDMIXSINK pSink, uint32_t *pcWritableStreams)
1715{
1716 uint32_t cFramesToRead = AudioMixBufLive(&pSink->MixBuf); /* (to read from the mixing buffer) */
1717 uint32_t cWritableStreams = 0;
1718 PAUDMIXSTREAM pMixStream;
1719 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1720 {
1721#if 0 /** @todo this conceptually makes sense, but may mess up the pending-disable logic ... */
1722 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1723 pConn->pfnStreamIterate(pConn, pStream);
1724#endif
1725
1726 int rc2 = audioMixerStreamUpdateStatus(pMixStream);
1727 AssertRC(rc2);
1728
1729 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_CAN_WRITE)
1730 {
1731 uint32_t const cbWritable = pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream);
1732 uint32_t cFrames = PDMAudioPropsBytesToFrames(&pMixStream->pStream->Props, cbWritable);
1733 pMixStream->cFramesLastAvail = cFrames;
1734 if (PDMAudioPropsHz(&pMixStream->pStream->Props) == PDMAudioPropsHz(&pSink->MixBuf.Props))
1735 { /* likely */ }
1736 else
1737 {
1738 cFrames = cFrames * PDMAudioPropsHz(&pSink->MixBuf.Props) / PDMAudioPropsHz(&pMixStream->pStream->Props);
1739 cFrames = cFrames > 2 ? cFrames - 2 : 0; /* rounding safety fudge */
1740 }
1741 if (cFramesToRead > cFrames && !pMixStream->fUnreliable)
1742 {
1743 Log4Func(("%s: cFramesToRead %u -> %u; %s (%u bytes writable)\n",
1744 pSink->pszName, cFramesToRead, cFrames, pMixStream->pszName, cbWritable));
1745 cFramesToRead = cFrames;
1746 }
1747 cWritableStreams++;
1748 }
1749 }
1750
1751 *pcWritableStreams = cWritableStreams;
1752 return cFramesToRead;
1753}
1754
1755
1756/**
1757 * Updates an output mixer sink.
1758 *
1759 * @returns VBox status code.
1760 * @param pSink Mixer sink to update.
1761 */
1762static int audioMixerSinkUpdateOutput(PAUDMIXSINK pSink)
1763{
1764 PAUDMIXSTREAM pMixStream;
1765 Assert(!(pSink->fStatus & AUDMIXSINK_STS_DRAINED_MIXBUF) || AudioMixBufUsed(&pSink->MixBuf) == 0);
1766
1767 /*
1768 * Update each mixing sink stream's status and check how much we can
1769 * write into them.
1770 *
1771 * We're currently using the minimum size of all streams, however this
1772 * isn't a smart approach as it means one disfunctional stream can block
1773 * working ones. So, if we end up with zero frames and a full mixer
1774 * buffer we'll disregard the stream that accept the smallest amount and
1775 * try again.
1776 */
1777 uint32_t cWritableStreams = 0;
1778 uint32_t cFramesToRead = audioMixerSinkUpdateOutputCalcFramesToRead(pSink, &cWritableStreams);
1779 if ( cFramesToRead != 0
1780 || cWritableStreams <= 1
1781 || AudioMixBufFree(&pSink->MixBuf) > 2)
1782 Log3Func(("%s: cLiveFrames=%#x cFramesToRead=%#x cWritableStreams=%#x\n", pSink->pszName,
1783 AudioMixBufLive(&pSink->MixBuf), cFramesToRead, cWritableStreams));
1784 else
1785 {
1786 Log3Func(("%s: MixBuf is full but one or more streams only want zero frames. Try disregarding those...\n", pSink->pszName));
1787 uint32_t cReliableStreams = 0;
1788 uint32_t cMarkedUnreliable = 0;
1789 PAUDMIXSTREAM pMixStreamMin = NULL;
1790 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1791 {
1792 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_CAN_WRITE)
1793 {
1794 if (!pMixStream->fUnreliable)
1795 {
1796 if (pMixStream->cFramesLastAvail == 0)
1797 {
1798 cMarkedUnreliable++;
1799 pMixStream->fUnreliable = true;
1800 Log3Func(("%s: Marked '%s' as unreliable.\n", pSink->pszName, pMixStream->pszName));
1801 pMixStreamMin = pMixStream;
1802 }
1803 else
1804 {
1805 if (!pMixStreamMin || pMixStream->cFramesLastAvail < pMixStreamMin->cFramesLastAvail)
1806 pMixStreamMin = pMixStream;
1807 cReliableStreams++;
1808 }
1809 }
1810 }
1811 }
1812
1813 if (cMarkedUnreliable == 0 && cReliableStreams > 1 && pMixStreamMin != NULL)
1814 {
1815 cReliableStreams--;
1816 cMarkedUnreliable++;
1817 pMixStreamMin->fUnreliable = true;
1818 Log3Func(("%s: Marked '%s' as unreliable (%u frames).\n",
1819 pSink->pszName, pMixStreamMin->pszName, pMixStreamMin->cFramesLastAvail));
1820 }
1821
1822 if (cMarkedUnreliable > 0)
1823 {
1824 cWritableStreams = 0;
1825 cFramesToRead = audioMixerSinkUpdateOutputCalcFramesToRead(pSink, &cWritableStreams);
1826 }
1827
1828 Log3Func(("%s: cLiveFrames=%#x cFramesToRead=%#x cWritableStreams=%#x cMarkedUnreliable=%#x cReliableStreams=%#x\n",
1829 pSink->pszName, AudioMixBufLive(&pSink->MixBuf), cFramesToRead,
1830 cWritableStreams, cMarkedUnreliable, cReliableStreams));
1831 }
1832
1833 if (cWritableStreams > 0)
1834 {
1835 if (cFramesToRead > 0)
1836 {
1837 /*
1838 * For each of the enabled streams, convert cFramesToRead frames from
1839 * the mixing buffer and write that to the downstream driver.
1840 */
1841 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1842 {
1843 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_CAN_WRITE)
1844 {
1845 uint32_t offSrcFrame = 0;
1846 do
1847 {
1848 /* Convert a chunk from the mixer buffer. */
1849/*#define ELECTRIC_PEEK_BUFFER*/ /* if buffer code is misbehaving, enable this to catch overflows. */
1850#ifndef ELECTRIC_PEEK_BUFFER
1851 union
1852 {
1853 uint8_t ab[8192];
1854 uint64_t au64[8192 / sizeof(uint64_t)]; /* Use uint64_t to ensure good alignment. */
1855 } Buf;
1856 void * const pvBuf = &Buf;
1857 uint32_t const cbBuf = sizeof(Buf);
1858#else
1859 uint32_t const cbBuf = 0x2000 - 16;
1860 void * const pvBuf = RTMemEfAlloc(cbBuf, RTMEM_TAG, RT_SRC_POS);
1861#endif
1862 uint32_t cbDstPeeked = cbBuf;
1863 uint32_t cSrcFramesPeeked = cFramesToRead - offSrcFrame;
1864 AudioMixBufPeek(&pSink->MixBuf, offSrcFrame, cSrcFramesPeeked, &cSrcFramesPeeked,
1865 &pMixStream->PeekState, pvBuf, cbBuf, &cbDstPeeked);
1866 offSrcFrame += cSrcFramesPeeked;
1867
1868 /* Write it to the backend. Since've checked that there is buffer
1869 space available, this should always write the whole buffer unless
1870 it's an unreliable stream. */
1871 uint32_t cbDstWritten = 0;
1872 int rc2 = pMixStream->pConn->pfnStreamPlay(pMixStream->pConn, pMixStream->pStream,
1873 pvBuf, cbDstPeeked, &cbDstWritten);
1874 Log3Func(("%s: %#x L %#x => %#x bytes; wrote %#x rc2=%Rrc %s\n", pSink->pszName, offSrcFrame,
1875 cSrcFramesPeeked - cSrcFramesPeeked, cbDstPeeked, cbDstWritten, rc2, pMixStream->pszName));
1876#ifdef ELECTRIC_PEEK_BUFFER
1877 RTMemEfFree(pvBuf, RT_SRC_POS);
1878#endif
1879 if (RT_SUCCESS(rc2))
1880 AssertLogRelMsg(cbDstWritten == cbDstPeeked || pMixStream->fUnreliable,
1881 ("cbDstWritten=%#x cbDstPeeked=%#x - (sink '%s')\n",
1882 cbDstWritten, cbDstPeeked, pSink->pszName));
1883 else if (rc2 == VERR_AUDIO_STREAM_NOT_READY)
1884 {
1885 LogRel2(("Audio Mixer: '%s' (sink '%s'): Stream not ready - skipping.\n",
1886 pMixStream->pszName, pSink->pszName));
1887 break; /* must've changed status, stop processing */
1888 }
1889 else
1890 {
1891 Assert(rc2 != VERR_BUFFER_OVERFLOW);
1892 LogRel2(("Audio Mixer: Writing to mixer stream '%s' (sink '%s') failed, rc=%Rrc\n",
1893 pMixStream->pszName, pSink->pszName, rc2));
1894 break;
1895 }
1896 } while (offSrcFrame < cFramesToRead);
1897 }
1898 }
1899
1900 AudioMixBufAdvance(&pSink->MixBuf, cFramesToRead);
1901 }
1902
1903 /*
1904 * Update the dirty flag for what it's worth.
1905 */
1906 if (AudioMixBufUsed(&pSink->MixBuf) > 0)
1907 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1908 else
1909 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1910 }
1911 else
1912 {
1913 /*
1914 * If no writable streams, just drop the mixer buffer content.
1915 */
1916 AudioMixBufDrop(&pSink->MixBuf);
1917 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1918 }
1919
1920 /*
1921 * Iterate buffers.
1922 */
1923 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1924 {
1925 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1926 pMixStream->pConn->pfnStreamIterate(pMixStream->pConn, pMixStream->pStream);
1927 }
1928
1929 /* Update last updated timestamp. */
1930 uint64_t const nsNow = RTTimeNanoTS();
1931 pSink->tsLastUpdatedMs = nsNow / RT_NS_1MS;
1932
1933 /*
1934 * Deal with pending disable.
1935 * We reset the sink when all streams have been disabled.
1936 */
1937 if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
1938 { /* likely, till we get to the end */ }
1939 else if (nsNow <= pSink->nsDrainDeadline)
1940 {
1941 /* Have we drained the mixbuf now? If so, update status and send drain
1942 command to streams. (As mentioned elsewhere we don't want to confuse
1943 driver code by sending drain command while there is still data to write.) */
1944 Assert((pSink->fStatus & AUDMIXSINK_STS_DIRTY) == (AudioMixBufUsed(&pSink->MixBuf) > 0 ? AUDMIXSINK_STS_DIRTY : 0));
1945 if ((pSink->fStatus & (AUDMIXSINK_STS_DRAINED_MIXBUF | AUDMIXSINK_STS_DIRTY)) == 0)
1946 {
1947 LogFunc(("Sink '%s': Setting AUDMIXSINK_STS_DRAINED_MIXBUF and sending drain command to streams (after %RU64 ns).\n",
1948 pSink->pszName, nsNow - pSink->nsDrainStarted));
1949 pSink->fStatus |= AUDMIXSINK_STS_DRAINED_MIXBUF;
1950
1951 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1952 {
1953 pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, PDMAUDIOSTREAMCMD_DRAIN);
1954 }
1955 }
1956
1957 /* Check if all streams has stopped, and if so we stop the sink. */
1958 uint32_t const cStreams = pSink->cStreams;
1959 uint32_t cStreamsDisabled = pSink->cStreams;
1960 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1961 {
1962 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1963 {
1964 PDMAUDIOSTREAMSTATE const enmState = pMixStream->pConn->pfnStreamGetState(pMixStream->pConn, pMixStream->pStream);
1965 if (enmState >= PDMAUDIOSTREAMSTATE_ENABLED)
1966 cStreamsDisabled--;
1967 }
1968 }
1969
1970 if (cStreamsDisabled != cStreams)
1971 Log3Func(("Sink '%s': %u out of %u streams disabled (after %RU64 ns).\n",
1972 pSink->pszName, cStreamsDisabled, cStreams, nsNow - pSink->nsDrainStarted));
1973 else
1974 {
1975 LogFunc(("Sink '%s': All %u streams disabled. Drain done after %RU64 ns.\n",
1976 pSink->pszName, cStreamsDisabled, nsNow - pSink->nsDrainStarted));
1977 audioMixerSinkReset(pSink); /* clears the status */
1978 }
1979 }
1980 else
1981 {
1982 /* Draining timed out. Just do an instant stop. */
1983 LogFunc(("Sink '%s': pending disable timed out after %RU64 ns!\n", pSink->pszName, nsNow - pSink->nsDrainStarted));
1984 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1985 {
1986 pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, PDMAUDIOSTREAMCMD_DISABLE);
1987 }
1988 audioMixerSinkReset(pSink); /* clears the status */
1989 }
1990
1991 return VINF_SUCCESS;
1992}
1993
1994/**
1995 * Updates (invalidates) a mixer sink.
1996 *
1997 * @returns VBox status code.
1998 * @param pSink Mixer sink to update.
1999 * @param cbDmaUsed The DMA buffer fill for input stream, ignored for
2000 * output sinks.
2001 * @param cbDmaPeriod The DMA period in bytes for input stream, ignored
2002 * for output sinks.
2003 */
2004int AudioMixerSinkUpdate(PAUDMIXSINK pSink, uint32_t cbDmaUsed, uint32_t cbDmaPeriod)
2005{
2006 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2007 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2008 int rc = RTCritSectEnter(&pSink->CritSect);
2009 AssertRCReturn(rc, rc);
2010
2011#ifdef LOG_ENABLED
2012 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
2013#endif
2014 Log3Func(("[%s] fStatus=%s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
2015
2016 /* Only process running sinks. */
2017 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
2018 {
2019 /* Do separate processing for input and output sinks. */
2020 if (pSink->enmDir == PDMAUDIODIR_OUT)
2021 rc = audioMixerSinkUpdateOutput(pSink);
2022 else if (pSink->enmDir == PDMAUDIODIR_IN)
2023 rc = audioMixerSinkUpdateInput(pSink, cbDmaUsed, cbDmaPeriod);
2024 else
2025 AssertFailedStmt(rc = VERR_INTERNAL_ERROR_3);
2026 }
2027 else
2028 rc = VINF_SUCCESS; /* disabled */
2029
2030 RTCritSectLeave(&pSink->CritSect);
2031 return rc;
2032}
2033
2034
2035/**
2036 * @callback_method_impl{FNRTTHREAD, Audio Mixer Sink asynchronous I/O thread}
2037 */
2038static DECLCALLBACK(int) audioMixerSinkAsyncIoThread(RTTHREAD hThreadSelf, void *pvUser)
2039{
2040 PAUDMIXSINK pSink = (PAUDMIXSINK)pvUser;
2041 AssertPtr(pSink);
2042 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2043 RT_NOREF(hThreadSelf);
2044
2045 /*
2046 * The run loop.
2047 */
2048 LogFlowFunc(("%s: Entering run loop...\n", pSink->pszName));
2049 while (!pSink->AIO.fShutdown)
2050 {
2051 RTMSINTERVAL cMsSleep = RT_INDEFINITE_WAIT;
2052
2053 RTCritSectEnter(&pSink->CritSect);
2054 if (pSink->fStatus & (AUDMIXSINK_STS_RUNNING | AUDMIXSINK_STS_DRAINING))
2055 {
2056 /*
2057 * Before doing jobs, always update input sinks.
2058 */
2059 if (pSink->enmDir == PDMAUDIODIR_IN)
2060 audioMixerSinkUpdateInput(pSink, 0 /*cbDmaUsed*/, 0 /*cbDmaPeriod*/);
2061
2062 /*
2063 * Do the device specific updating.
2064 */
2065 uintptr_t const cUpdateJobs = RT_MIN(pSink->AIO.cUpdateJobs, RT_ELEMENTS(pSink->AIO.aUpdateJobs));
2066 for (uintptr_t iJob = 0; iJob < cUpdateJobs; iJob++)
2067 pSink->AIO.aUpdateJobs[iJob].pfnUpdate(pSink->AIO.pDevIns, pSink, pSink->AIO.aUpdateJobs[iJob].pvUser);
2068
2069 /*
2070 * Update output sinks after the updating.
2071 */
2072 if (pSink->enmDir == PDMAUDIODIR_OUT)
2073 audioMixerSinkUpdateOutput(pSink);
2074
2075 /*
2076 * If we're in draining mode, we use the smallest typical interval of the
2077 * jobs for the next wait as we're unlikly to be woken up again by any
2078 * DMA timer as it has normally stopped running at this point.
2079 */
2080 if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
2081 { /* likely */ }
2082 else
2083 {
2084 /** @todo Also do some kind of timeout here and do a forced stream disable w/o
2085 * any draining if we exceed it. */
2086 cMsSleep = pSink->AIO.cMsMinTypicalInterval;
2087 }
2088
2089 }
2090 RTCritSectLeave(&pSink->CritSect);
2091
2092 /*
2093 * Now block till we're signalled or
2094 */
2095 if (!pSink->AIO.fShutdown)
2096 {
2097 int rc = RTSemEventWait(pSink->AIO.hEvent, cMsSleep);
2098 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%s: RTSemEventWait -> %Rrc\n", pSink->pszName, rc), rc);
2099 }
2100 }
2101
2102 LogFlowFunc(("%s: returnining normally.\n", pSink->pszName));
2103 return VINF_SUCCESS;
2104}
2105
2106
2107/**
2108 * Adds an AIO update job to the sink.
2109 *
2110 * @returns VBox status code.
2111 * @retval VERR_ALREADY_EXISTS if already registered job with same @a pvUser
2112 * and @a pfnUpdate.
2113 *
2114 * @param pSink The mixer sink to remove the AIO job from.
2115 * @param pfnUpdate The update callback for the job.
2116 * @param pvUser The user parameter to pass to @a pfnUpdate. This should
2117 * identify the job unique together with @a pfnUpdate.
2118 * @param cMsTypicalInterval A typical interval between jobs in milliseconds.
2119 * This is used when draining.
2120 */
2121int AudioMixerSinkAddUpdateJob(PAUDMIXSINK pSink, PFNAUDMIXSINKUPDATE pfnUpdate, void *pvUser, uint32_t cMsTypicalInterval)
2122{
2123 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2124 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2125 int rc = RTCritSectEnter(&pSink->CritSect);
2126 AssertRCReturn(rc, rc);
2127
2128 /*
2129 * Check that the job hasn't already been added.
2130 */
2131 uintptr_t const iEnd = pSink->AIO.cUpdateJobs;
2132 for (uintptr_t i = 0; i < iEnd; i++)
2133 AssertReturnStmt( pvUser != pSink->AIO.aUpdateJobs[i].pvUser
2134 || pfnUpdate != pSink->AIO.aUpdateJobs[i].pfnUpdate,
2135 RTCritSectLeave(&pSink->CritSect),
2136 VERR_ALREADY_EXISTS);
2137
2138 AssertReturnStmt(iEnd < RT_ELEMENTS(pSink->AIO.aUpdateJobs),
2139 RTCritSectLeave(&pSink->CritSect),
2140 VERR_ALREADY_EXISTS);
2141
2142 /*
2143 * Create the thread if not already running or if it stopped.
2144 */
2145/** @todo move this to the sink "enable" code */
2146 if (pSink->AIO.hThread != NIL_RTTHREAD)
2147 {
2148 int rcThread = VINF_SUCCESS;
2149 rc = RTThreadWait(pSink->AIO.hThread, 0, &rcThread);
2150 if (RT_FAILURE_NP(rc))
2151 { /* likely */ }
2152 else
2153 {
2154 LogRel(("Audio: AIO thread for '%s' died? rcThread=%Rrc\n", pSink->pszName, rcThread));
2155 pSink->AIO.hThread = NIL_RTTHREAD;
2156 }
2157 }
2158 if (pSink->AIO.hThread == NIL_RTTHREAD)
2159 {
2160 LogFlowFunc(("%s: Starting AIO thread...\n", pSink->pszName));
2161 if (pSink->AIO.hEvent == NIL_RTSEMEVENT)
2162 {
2163 rc = RTSemEventCreate(&pSink->AIO.hEvent);
2164 AssertRCReturnStmt(rc, RTCritSectLeave(&pSink->CritSect), rc);
2165 }
2166 static uint32_t volatile s_idxThread = 0;
2167 uint32_t idxThread = ASMAtomicIncU32(&s_idxThread);
2168 rc = RTThreadCreateF(&pSink->AIO.hThread, audioMixerSinkAsyncIoThread, pSink, 0 /*cbStack*/, RTTHREADTYPE_IO,
2169 RTTHREADFLAGS_WAITABLE | RTTHREADFLAGS_COM_MTA, "MixAIO-%u", idxThread);
2170 AssertRCReturnStmt(rc, RTCritSectLeave(&pSink->CritSect), rc);
2171 }
2172
2173 /*
2174 * Finally, actually add the job.
2175 */
2176 pSink->AIO.aUpdateJobs[iEnd].pfnUpdate = pfnUpdate;
2177 pSink->AIO.aUpdateJobs[iEnd].pvUser = pvUser;
2178 pSink->AIO.aUpdateJobs[iEnd].cMsTypicalInterval = cMsTypicalInterval;
2179 pSink->AIO.cUpdateJobs = (uint8_t)(iEnd + 1);
2180 if (cMsTypicalInterval < pSink->AIO.cMsMinTypicalInterval)
2181 pSink->AIO.cMsMinTypicalInterval = cMsTypicalInterval;
2182 LogFlowFunc(("%s: [#%zu]: Added pfnUpdate=%p pvUser=%p typically every %u ms (min %u ms)\n",
2183 pSink->pszName, iEnd, pfnUpdate, pvUser, cMsTypicalInterval, pSink->AIO.cMsMinTypicalInterval));
2184
2185 RTCritSectLeave(&pSink->CritSect);
2186 return VINF_SUCCESS;
2187
2188}
2189
2190
2191/**
2192 * Removes an update job previously registered via AudioMixerSinkAddUpdateJob().
2193 *
2194 * @returns VBox status code.
2195 * @retval VERR_NOT_FOUND if not found.
2196 *
2197 * @param pSink The mixer sink to remove the AIO job from.
2198 * @param pfnUpdate The update callback of the job.
2199 * @param pvUser The user parameter identifying the job.
2200 */
2201int AudioMixerSinkRemoveUpdateJob(PAUDMIXSINK pSink, PFNAUDMIXSINKUPDATE pfnUpdate, void *pvUser)
2202{
2203 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2204 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2205 int rc = RTCritSectEnter(&pSink->CritSect);
2206 AssertRCReturn(rc, rc);
2207
2208 rc = VERR_NOT_FOUND;
2209 for (uintptr_t iJob = 0; iJob < pSink->AIO.cUpdateJobs; iJob++)
2210 if ( pvUser == pSink->AIO.aUpdateJobs[iJob].pvUser
2211 && pfnUpdate == pSink->AIO.aUpdateJobs[iJob].pfnUpdate)
2212 {
2213 pSink->AIO.cUpdateJobs--;
2214 if (iJob != pSink->AIO.cUpdateJobs)
2215 memmove(&pSink->AIO.aUpdateJobs[iJob], &pSink->AIO.aUpdateJobs[iJob + 1],
2216 (pSink->AIO.cUpdateJobs - iJob) * sizeof(pSink->AIO.aUpdateJobs[0]));
2217 LogFlowFunc(("%s: [#%zu]: Removed pfnUpdate=%p pvUser=%p => cUpdateJobs=%u\n",
2218 pSink->pszName, iJob, pfnUpdate, pvUser, pSink->AIO.cUpdateJobs));
2219 rc = VINF_SUCCESS;
2220 break;
2221 }
2222 AssertRC(rc);
2223
2224 /* Recalc the minimum sleep interval (do it always). */
2225 pSink->AIO.cMsMinTypicalInterval = RT_MS_1SEC / 2;
2226 for (uintptr_t iJob = 0; iJob < pSink->AIO.cUpdateJobs; iJob++)
2227 if (pSink->AIO.aUpdateJobs[iJob].cMsTypicalInterval < pSink->AIO.cMsMinTypicalInterval)
2228 pSink->AIO.cMsMinTypicalInterval = pSink->AIO.aUpdateJobs[iJob].cMsTypicalInterval;
2229
2230
2231 RTCritSectLeave(&pSink->CritSect);
2232 return rc;
2233}
2234
2235
2236/**
2237 * Transfer data from the device's DMA buffer and into the sink.
2238 *
2239 * The caller is already holding the mixer sink's critical section, either by
2240 * way of being the AIO thread doing update jobs or by explicit locking calls.
2241 *
2242 * @returns The new stream offset.
2243 * @param pSink The mixer sink to transfer samples to.
2244 * @param pCircBuf The internal DMA buffer to move samples from.
2245 * @param offStream The stream current offset (logging, dtrace, return).
2246 * @param idStream Device specific audio stream identifier (logging, dtrace).
2247 * @param pDbgFile Debug file, NULL if disabled.
2248 */
2249uint64_t AudioMixerSinkTransferFromCircBuf(PAUDMIXSINK pSink, PRTCIRCBUF pCircBuf, uint64_t offStream,
2250 uint32_t idStream, PAUDIOHLPFILE pDbgFile)
2251{
2252 /*
2253 * Sanity.
2254 */
2255 AssertReturn(pSink, offStream);
2256 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2257 AssertReturn(pCircBuf, offStream);
2258 Assert(RTCritSectIsOwner(&pSink->CritSect));
2259 RT_NOREF(idStream);
2260
2261 /*
2262 * Figure how much that we can push down.
2263 */
2264 uint32_t const cbSinkWritable = AudioMixerSinkGetWritable(pSink);
2265 uint32_t const cbCircBufReadable = (uint32_t)RTCircBufUsed(pCircBuf);
2266 uint32_t cbToTransfer = RT_MIN(cbCircBufReadable, cbSinkWritable);
2267 /* Make sure that we always align the number of bytes when reading to the stream's PCM properties. */
2268 uint32_t const cbToTransfer2 = cbToTransfer = PDMAudioPropsFloorBytesToFrame(&pSink->PCMProps, cbToTransfer);
2269
2270 Log3Func(("idStream=%u: cbSinkWritable=%#RX32 cbCircBufReadable=%#RX32 -> cbToTransfer=%#RX32 @%#RX64\n",
2271 idStream, cbSinkWritable, cbCircBufReadable, cbToTransfer, offStream));
2272 AssertMsg(!(pSink->fStatus & AUDMIXSINK_STS_DRAINING) || cbCircBufReadable == pSink->cbDmaLeftToDrain,
2273 ("cbCircBufReadable=%#x cbDmaLeftToDrain=%#x\n", cbCircBufReadable, pSink->cbDmaLeftToDrain));
2274
2275 /*
2276 * Do the pushing.
2277 */
2278 while (cbToTransfer > 0)
2279 {
2280 void /*const*/ *pvSrcBuf;
2281 size_t cbSrcBuf;
2282 RTCircBufAcquireReadBlock(pCircBuf, cbToTransfer, &pvSrcBuf, &cbSrcBuf);
2283
2284 uint32_t cbWritten = 0;
2285 int rc = AudioMixerSinkWrite(pSink, AUDMIXOP_COPY, pvSrcBuf, (uint32_t)cbSrcBuf, &cbWritten);
2286 AssertRC(rc);
2287 Assert(cbWritten <= cbSrcBuf);
2288
2289 Log2Func(("idStream=%u: %#RX32/%#zx bytes read @%#RX64\n", idStream, cbWritten, cbSrcBuf, offStream));
2290#ifdef VBOX_WITH_DTRACE
2291 VBOXDD_AUDIO_MIXER_SINK_AIO_OUT(idStream, cbWritten, offStream);
2292#endif
2293 offStream += cbWritten;
2294
2295 if (!pDbgFile)
2296 { /* likely */ }
2297 else
2298 AudioHlpFileWrite(pDbgFile, pvSrcBuf, cbSrcBuf, 0 /* fFlags */);
2299
2300
2301 RTCircBufReleaseReadBlock(pCircBuf, cbWritten);
2302
2303 /* advance */
2304 cbToTransfer -= cbWritten;
2305 }
2306
2307 /*
2308 * Advance drain status.
2309 */
2310 if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
2311 { /* likely for most of the playback time ... */ }
2312 else if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINED_DMA))
2313 {
2314 if (cbToTransfer2 >= pSink->cbDmaLeftToDrain)
2315 {
2316 Assert(cbToTransfer2 == pSink->cbDmaLeftToDrain);
2317 Log3Func(("idStream=%u/'%s': Setting AUDMIXSINK_STS_DRAINED_DMA.\n", idStream, pSink->pszName));
2318 pSink->cbDmaLeftToDrain = 0;
2319 pSink->fStatus |= AUDMIXSINK_STS_DRAINED_DMA;
2320 }
2321 else
2322 {
2323 pSink->cbDmaLeftToDrain -= cbToTransfer2;
2324 Log3Func(("idStream=%u/'%s': still %#x bytes left in the DMA buffer\n",
2325 idStream, pSink->pszName, pSink->cbDmaLeftToDrain));
2326 }
2327 }
2328 else
2329 Assert(cbToTransfer2 == 0);
2330
2331 return offStream;
2332}
2333
2334
2335/**
2336 * Transfer data to the device's DMA buffer from the sink.
2337 *
2338 * The caller is already holding the mixer sink's critical section, either by
2339 * way of being the AIO thread doing update jobs or by explicit locking calls.
2340 *
2341 * @returns The new stream offset.
2342 * @param pSink The mixer sink to transfer samples from.
2343 * @param pCircBuf The internal DMA buffer to move samples to.
2344 * @param offStream The stream current offset (logging, dtrace, return).
2345 * @param idStream Device specific audio stream identifier (logging, dtrace).
2346 * @param pDbgFile Debug file, NULL if disabled.
2347 */
2348uint64_t AudioMixerSinkTransferToCircBuf(PAUDMIXSINK pSink, PRTCIRCBUF pCircBuf, uint64_t offStream,
2349 uint32_t idStream, PAUDIOHLPFILE pDbgFile)
2350{
2351 /*
2352 * Sanity.
2353 */
2354 AssertReturn(pSink, offStream);
2355 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2356 AssertReturn(pCircBuf, offStream);
2357 Assert(RTCritSectIsOwner(&pSink->CritSect));
2358
2359 /*
2360 * Figure out how much we can transfer.
2361 */
2362 const uint32_t cbSinkReadable = AudioMixerSinkGetReadable(pSink);
2363 const uint32_t cbCircBufWritable = (uint32_t)RTCircBufFree(pCircBuf);
2364 uint32_t cbToTransfer = RT_MIN(cbCircBufWritable, cbSinkReadable);
2365 uint32_t cFramesToTransfer = PDMAudioPropsBytesToFrames(&pSink->PCMProps, cbToTransfer);
2366 cbToTransfer = PDMAudioPropsFramesToBytes(&pSink->PCMProps, cFramesToTransfer);
2367
2368 Log3Func(("idStream=%u: cbSinkReadable=%#RX32 cbCircBufWritable=%#RX32 -> cbToTransfer=%#RX32 (%RU32 frames) @%#RX64\n",
2369 idStream, cbSinkReadable, cbCircBufWritable, cbToTransfer, cFramesToTransfer, offStream));
2370 RT_NOREF(idStream);
2371
2372 /** @todo should we throttle (read less) this if we're far ahead? */
2373
2374 /*
2375 * Copy loop.
2376 */
2377 while (cbToTransfer > 0)
2378 {
2379/** @todo We should be able to read straight into the circular buffer here
2380 * as it should have a frame aligned size. */
2381
2382 /* Read a chunk of data. */
2383 uint8_t abBuf[4096];
2384 uint32_t cbRead = 0;
2385 uint32_t cFramesRead = 0;
2386 AudioMixBufPeek(&pSink->MixBuf, 0, cFramesToTransfer, &cFramesRead,
2387 &pSink->In.State, abBuf, RT_MIN(cbToTransfer, sizeof(abBuf)), &cbRead);
2388 AssertBreak(cFramesRead > 0);
2389 Assert(cbRead > 0);
2390
2391 cFramesToTransfer -= cFramesRead;
2392 AudioMixBufAdvance(&pSink->MixBuf, cFramesRead);
2393
2394 /* Write it to the internal DMA buffer. */
2395 uint32_t off = 0;
2396 while (off < cbRead)
2397 {
2398 void *pvDstBuf;
2399 size_t cbDstBuf;
2400 RTCircBufAcquireWriteBlock(pCircBuf, cbRead - off, &pvDstBuf, &cbDstBuf);
2401
2402 memcpy(pvDstBuf, &abBuf[off], cbDstBuf);
2403
2404#ifdef VBOX_WITH_DTRACE
2405 VBOXDD_AUDIO_MIXER_SINK_AIO_IN(idStream, (uint32_t)cbDstBuf, offStream);
2406#endif
2407 offStream += cbDstBuf;
2408
2409 RTCircBufReleaseWriteBlock(pCircBuf, cbDstBuf);
2410
2411 off += (uint32_t)cbDstBuf;
2412 }
2413 Assert(off == cbRead);
2414
2415 /* Write to debug file? */
2416 if (RT_LIKELY(!pDbgFile))
2417 { /* likely */ }
2418 else
2419 AudioHlpFileWrite(pDbgFile, abBuf, cbRead, 0 /* fFlags */);
2420
2421 /* Advance. */
2422 Assert(cbRead <= cbToTransfer);
2423 cbToTransfer -= cbRead;
2424 }
2425
2426 return offStream;
2427}
2428
2429
2430/**
2431 * Signals the AIO thread to perform updates.
2432 *
2433 * @returns VBox status code.
2434 * @param pSink The mixer sink which AIO thread needs to do chores.
2435 */
2436int AudioMixerSinkSignalUpdateJob(PAUDMIXSINK pSink)
2437{
2438 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2439 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2440 return RTSemEventSignal(pSink->AIO.hEvent);
2441}
2442
2443
2444/**
2445 * Locks the mixer sink for purposes of serializing with the AIO thread.
2446 *
2447 * @returns VBox status code.
2448 * @param pSink The mixer sink to lock.
2449 */
2450int AudioMixerSinkLock(PAUDMIXSINK pSink)
2451{
2452 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2453 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2454 return RTCritSectEnter(&pSink->CritSect);
2455}
2456
2457
2458/**
2459 * Try to lock the mixer sink for purposes of serializing with the AIO thread.
2460 *
2461 * @returns VBox status code.
2462 * @param pSink The mixer sink to lock.
2463 */
2464int AudioMixerSinkTryLock(PAUDMIXSINK pSink)
2465{
2466 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2467 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2468 return RTCritSectTryEnter(&pSink->CritSect);
2469}
2470
2471
2472/**
2473 * Unlocks the sink.
2474 *
2475 * @returns VBox status code.
2476 * @param pSink The mixer sink to unlock.
2477 */
2478int AudioMixerSinkUnlock(PAUDMIXSINK pSink)
2479{
2480 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2481 return RTCritSectLeave(&pSink->CritSect);
2482}
2483
2484
2485/**
2486 * Updates the (master) volume of a mixer sink.
2487 *
2488 * @returns VBox status code.
2489 * @param pSink Mixer sink to update volume for.
2490 * @param pVolMaster Master volume to set.
2491 */
2492static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, PCPDMAUDIOVOLUME pVolMaster)
2493{
2494 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2495 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2496 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
2497
2498 LogFlowFunc(("[%s] Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
2499 pSink->pszName, pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
2500 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32 ",
2501 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
2502
2503 /** @todo Very crude implementation for now -- needs more work! */
2504
2505 pSink->VolumeCombined.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
2506
2507 pSink->VolumeCombined.uLeft = ( (pSink->Volume.uLeft ? pSink->Volume.uLeft : 1)
2508 * (pVolMaster->uLeft ? pVolMaster->uLeft : 1)) / PDMAUDIO_VOLUME_MAX;
2509
2510 pSink->VolumeCombined.uRight = ( (pSink->Volume.uRight ? pSink->Volume.uRight : 1)
2511 * (pVolMaster->uRight ? pVolMaster->uRight : 1)) / PDMAUDIO_VOLUME_MAX;
2512
2513 LogFlow(("-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
2514 pSink->VolumeCombined.fMuted, pSink->VolumeCombined.uLeft, pSink->VolumeCombined.uRight));
2515
2516 AudioMixBufSetVolume(&pSink->MixBuf, &pSink->VolumeCombined);
2517 return VINF_SUCCESS;
2518}
2519
2520/**
2521 * Writes data to a mixer output sink.
2522 *
2523 * @returns VBox status code.
2524 * @param pSink Sink to write data to.
2525 * @param enmOp Mixer operation to use when writing data to the sink.
2526 * @param pvBuf Buffer containing the audio data to write.
2527 * @param cbBuf Size (in bytes) of the buffer containing the audio data.
2528 * @param pcbWritten Number of bytes written. Optional.
2529 */
2530int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
2531{
2532 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2533 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2534 RT_NOREF(enmOp);
2535 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2536 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
2537 /* pcbWritten is optional. */
2538
2539 int rc = RTCritSectEnter(&pSink->CritSect);
2540 AssertRCReturn(rc, rc);
2541
2542 AssertMsg(pSink->fStatus & AUDMIXSINK_STS_RUNNING,
2543 ("%s: Can't write to a sink which is not running (anymore) (status 0x%x)\n", pSink->pszName, pSink->fStatus));
2544 AssertMsg(pSink->enmDir == PDMAUDIODIR_OUT,
2545 ("%s: Can't write to a sink which is not an output sink\n", pSink->pszName));
2546
2547 uint32_t cbWritten = 0;
2548 uint32_t cbToWrite = AudioMixBufFreeBytes(&pSink->MixBuf);
2549 cbToWrite = RT_MIN(cbToWrite, cbBuf);
2550 while (cbToWrite > 0)
2551 {
2552 /* Write the data to the mixer sink's own mixing buffer.
2553 Here the audio data is transformed into the mixer sink's format. */
2554 uint32_t cFramesWritten = 0;
2555 rc = AudioMixBufWriteCirc(&pSink->MixBuf, (uint8_t const*)pvBuf + cbWritten, cbToWrite, &cFramesWritten);
2556 if (RT_SUCCESS(rc))
2557 {
2558 const uint32_t cbWrittenChunk = PDMAudioPropsFramesToBytes(&pSink->PCMProps, cFramesWritten);
2559 Assert(cbToWrite >= cbWrittenChunk);
2560 cbToWrite -= cbWrittenChunk;
2561 cbWritten += cbWrittenChunk;
2562 }
2563 else
2564 break;
2565 }
2566
2567 Log3Func(("[%s] cbBuf=%RU32 -> cbWritten=%RU32\n", pSink->pszName, cbBuf, cbWritten));
2568
2569 /* Update the sink's last written time stamp. */
2570 pSink->tsLastReadWrittenNs = RTTimeNanoTS();
2571
2572 if (pcbWritten)
2573 *pcbWritten = cbWritten;
2574
2575 RTCritSectLeave(&pSink->CritSect);
2576 return rc;
2577}
2578
2579
2580/*********************************************************************************************************************************
2581 * Mixer Stream implementation.
2582 ********************************************************************************************************************************/
2583
2584/**
2585 * Controls a mixer stream, internal version.
2586 *
2587 * @returns VBox status code (generally ignored).
2588 * @param pMixStream Mixer stream to control.
2589 * @param enmCmd Mixer stream command to use.
2590 */
2591static int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd)
2592{
2593 Assert(pMixStream->uMagic == AUDMIXSTREAM_MAGIC);
2594 AssertPtrReturn(pMixStream->pConn, VERR_AUDIO_STREAM_NOT_READY);
2595 AssertPtrReturn(pMixStream->pStream, VERR_AUDIO_STREAM_NOT_READY);
2596
2597 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
2598
2599 LogFlowFunc(("[%s] enmCmd=%d, rc=%Rrc\n", pMixStream->pszName, enmCmd, rc));
2600
2601 return rc;
2602}
2603
2604/**
2605 * Updates a mixer stream's internal status.
2606 *
2607 * This may perform a stream re-init if the driver requests it, in which case
2608 * this may take a little while longer than usual...
2609 *
2610 * @returns VBox status code.
2611 * @param pMixStream Mixer stream to to update internal status for.
2612 */
2613static int audioMixerStreamUpdateStatus(PAUDMIXSTREAM pMixStream)
2614{
2615 Assert(pMixStream->uMagic == AUDMIXSTREAM_MAGIC);
2616
2617 /*
2618 * Reset the mixer status to start with.
2619 */
2620 pMixStream->fStatus = AUDMIXSTREAM_STATUS_NONE;
2621
2622 PPDMIAUDIOCONNECTOR const pConn = pMixStream->pConn;
2623 if (pConn) /* Audio connector available? */
2624 {
2625 PPDMAUDIOSTREAM const pStream = pMixStream->pStream;
2626
2627 /*
2628 * Get the stream status.
2629 * Do re-init if needed and fetch the status again afterwards.
2630 */
2631 PDMAUDIOSTREAMSTATE enmState = pConn->pfnStreamGetState(pConn, pStream);
2632 if (enmState != PDMAUDIOSTREAMSTATE_NEED_REINIT)
2633 { /* likely */ }
2634 else
2635 {
2636 LogFunc(("[%s] needs re-init...\n", pMixStream->pszName));
2637 int rc = pConn->pfnStreamReInit(pConn, pStream);
2638 enmState = pConn->pfnStreamGetState(pConn, pStream);
2639 LogFunc(("[%s] re-init returns %Rrc and %s.\n", pMixStream->pszName, rc, PDMAudioStreamStateGetName(enmState)));
2640
2641 PAUDMIXSINK const pSink = pMixStream->pSink;
2642 AssertPtr(pSink);
2643 if (pSink->enmDir == PDMAUDIODIR_OUT)
2644 {
2645 rc = AudioMixBufInitPeekState(&pSink->MixBuf, &pMixStream->PeekState, &pStream->Props);
2646 /** @todo we need to remember this, don't we? */
2647 AssertLogRelRCReturn(rc, VINF_SUCCESS);
2648 }
2649 else
2650 {
2651 rc = AudioMixBufInitWriteState(&pSink->MixBuf, &pMixStream->WriteState, &pStream->Props);
2652 /** @todo we need to remember this, don't we? */
2653 AssertLogRelRCReturn(rc, VINF_SUCCESS);
2654 }
2655 }
2656
2657 /*
2658 * Translate the status to mixer speak.
2659 */
2660 AssertMsg(enmState > PDMAUDIOSTREAMSTATE_INVALID && enmState < PDMAUDIOSTREAMSTATE_END, ("%d\n", enmState));
2661 switch (enmState)
2662 {
2663 case PDMAUDIOSTREAMSTATE_NOT_WORKING:
2664 case PDMAUDIOSTREAMSTATE_NEED_REINIT:
2665 case PDMAUDIOSTREAMSTATE_INACTIVE:
2666 pMixStream->fStatus = AUDMIXSTREAM_STATUS_NONE;
2667 break;
2668 case PDMAUDIOSTREAMSTATE_ENABLED:
2669 pMixStream->fStatus = AUDMIXSTREAM_STATUS_ENABLED;
2670 break;
2671 case PDMAUDIOSTREAMSTATE_ENABLED_READABLE:
2672 Assert(pMixStream->pSink->enmDir == PDMAUDIODIR_IN);
2673 pMixStream->fStatus = AUDMIXSTREAM_STATUS_ENABLED | AUDMIXSTREAM_STATUS_CAN_READ;
2674 break;
2675 case PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE:
2676 Assert(pMixStream->pSink->enmDir == PDMAUDIODIR_OUT);
2677 pMixStream->fStatus = AUDMIXSTREAM_STATUS_ENABLED | AUDMIXSTREAM_STATUS_CAN_WRITE;
2678 break;
2679 /* no default */
2680 case PDMAUDIOSTREAMSTATE_INVALID:
2681 case PDMAUDIOSTREAMSTATE_END:
2682 case PDMAUDIOSTREAMSTATE_32BIT_HACK:
2683 break;
2684 }
2685 }
2686
2687 LogFlowFunc(("[%s] -> 0x%x\n", pMixStream->pszName, pMixStream->fStatus));
2688 return VINF_SUCCESS;
2689}
2690
2691/**
2692 * Destroys & frees a mixer stream, internal version.
2693 *
2694 * Worker for audioMixerSinkDestroyInternal and AudioMixerStreamDestroy.
2695 *
2696 * @param pMixStream Mixer stream to destroy.
2697 * @param pDevIns The device instance the statistics are registered with.
2698 * @param fImmediate How to handle still draining streams, whether to let
2699 * them complete (@c false) or destroy them immediately (@c
2700 * true).
2701 */
2702static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream, PPDMDEVINS pDevIns, bool fImmediate)
2703{
2704 AssertPtrReturnVoid(pMixStream);
2705
2706 LogFunc(("%s\n", pMixStream->pszName));
2707 Assert(pMixStream->uMagic == AUDMIXSTREAM_MAGIC);
2708
2709 if (pMixStream->pConn) /* Stream has a connector interface present? */
2710 {
2711 if (pMixStream->pStream)
2712 {
2713 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
2714 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream, fImmediate);
2715
2716 pMixStream->pStream = NULL;
2717 }
2718
2719 pMixStream->pConn = NULL;
2720 }
2721
2722 if (pMixStream->pszStatPrefix)
2723 {
2724 PDMDevHlpSTAMDeregisterByPrefix(pDevIns, pMixStream->pszStatPrefix);
2725 RTStrFree(pMixStream->pszStatPrefix);
2726 pMixStream->pszStatPrefix = NULL;
2727 }
2728
2729 RTStrFree(pMixStream->pszName);
2730 pMixStream->pszName = NULL;
2731
2732 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
2733 AssertRC(rc2);
2734
2735 RTMemFree(pMixStream);
2736 pMixStream = NULL;
2737}
2738
2739/**
2740 * Destroys a mixer stream.
2741 *
2742 * @param pMixStream Mixer stream to destroy.
2743 * @param pDevIns The device instance statistics are registered with.
2744 * @param fImmediate How to handle still draining streams, whether to let
2745 * them complete (@c false) or destroy them immediately (@c
2746 * true).
2747 */
2748void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream, PPDMDEVINS pDevIns, bool fImmediate)
2749{
2750 if (!pMixStream)
2751 return;
2752
2753/** @todo r=bird: Wrng critsect for audioMixerSinkRemoveStreamInternal */
2754 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2755 AssertRC(rc2);
2756
2757 LogFunc(("%s\n", pMixStream->pszName));
2758
2759 if (pMixStream->pSink) /* Is the stream part of a sink? */
2760 {
2761 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
2762 * pointer will be gone from the stream. */
2763 PAUDMIXSINK pSink = pMixStream->pSink;
2764
2765 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
2766 if (RT_SUCCESS(rc2))
2767 {
2768 Assert(pSink->cStreams);
2769 pSink->cStreams--;
2770 }
2771 }
2772 else
2773 rc2 = VINF_SUCCESS;
2774
2775 int rc3 = RTCritSectLeave(&pMixStream->CritSect);
2776 AssertRC(rc3);
2777
2778 if (RT_SUCCESS(rc2))
2779 {
2780 audioMixerStreamDestroyInternal(pMixStream, pDevIns, fImmediate);
2781 pMixStream = NULL;
2782 }
2783
2784 LogFlowFunc(("Returning %Rrc\n", rc2));
2785}
2786
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette