VirtualBox

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

Last change on this file since 100444 was 99415, checked in by vboxsync, 18 months ago

Audio/Mixer: Renamed AudioMixerSinkLockIsOwned() -> AudioMixerSinkLockIsOwner(). bugref:10350

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