VirtualBox

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

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

scm --update-copyright-year

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

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