VirtualBox

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

Last change on this file was 103162, checked in by vboxsync, 3 months ago

Audio/Mixer: Fixed a warning found by Parfait. ​bugref:10354

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 101.6 KB
Line 
1/* $Id: AudioMixer.cpp 103162 2024-02-01 13:23:46Z 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 int rc = RTCritSectEnter(&pSink->CritSect);
2015 AssertRCReturn(rc, offStream);
2016
2017 /*
2018 * Figure how much that we can push down.
2019 */
2020 uint32_t const cbSinkWritable = AudioMixerSinkGetWritable(pSink);
2021 uint32_t const cbCircBufReadable = (uint32_t)RTCircBufUsed(pCircBuf);
2022 uint32_t cbToTransfer = RT_MIN(cbCircBufReadable, cbSinkWritable);
2023 /* Make sure that we always align the number of bytes when reading to the stream's PCM properties. */
2024 uint32_t const cbToTransfer2 = cbToTransfer = PDMAudioPropsFloorBytesToFrame(&pSink->PCMProps, cbToTransfer);
2025
2026 Log3Func(("idStream=%u: cbSinkWritable=%#RX32 cbCircBufReadable=%#RX32 -> cbToTransfer=%#RX32 @%#RX64\n",
2027 idStream, cbSinkWritable, cbCircBufReadable, cbToTransfer, offStream));
2028 /* Note: There now can be more data in the DMA buffer than initially announced. See @bugref{10354}. */
2029 AssertMsg(!(pSink->fStatus & AUDMIXSINK_STS_DRAINING) || cbCircBufReadable >= pSink->cbDmaLeftToDrain,
2030 ("cbCircBufReadable=%#x cbDmaLeftToDrain=%#x\n", cbCircBufReadable, pSink->cbDmaLeftToDrain));
2031
2032 /*
2033 * Do the pushing.
2034 */
2035 while (cbToTransfer > 0)
2036 {
2037 void /*const*/ *pvSrcBuf;
2038 size_t cbSrcBuf;
2039 RTCircBufAcquireReadBlock(pCircBuf, cbToTransfer, &pvSrcBuf, &cbSrcBuf);
2040
2041 uint32_t cbWritten = 0;
2042 audioMixerSinkWrite(pSink, pvSrcBuf, (uint32_t)cbSrcBuf, &cbWritten);
2043 Assert(cbWritten <= cbSrcBuf);
2044
2045 Log2Func(("idStream=%u: %#RX32/%#zx bytes read @%#RX64\n", idStream, cbWritten, cbSrcBuf, offStream));
2046#ifdef VBOX_WITH_DTRACE
2047 VBOXDD_AUDIO_MIXER_SINK_AIO_OUT(idStream, cbWritten, offStream);
2048#endif
2049 offStream += cbWritten;
2050
2051 if (!pDbgFile)
2052 { /* likely */ }
2053 else
2054 AudioHlpFileWrite(pDbgFile, pvSrcBuf, cbSrcBuf);
2055
2056
2057 RTCircBufReleaseReadBlock(pCircBuf, cbWritten);
2058
2059 /* advance */
2060 cbToTransfer -= cbWritten;
2061 }
2062
2063 /*
2064 * Advance drain status.
2065 */
2066 if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
2067 { /* likely for most of the playback time ... */ }
2068 else if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINED_DMA))
2069 {
2070 if (cbToTransfer2 >= pSink->cbDmaLeftToDrain)
2071 {
2072 Assert(cbToTransfer2 == pSink->cbDmaLeftToDrain);
2073 Log3Func(("idStream=%u/'%s': Setting AUDMIXSINK_STS_DRAINED_DMA.\n", idStream, pSink->pszName));
2074 pSink->cbDmaLeftToDrain = 0;
2075 pSink->fStatus |= AUDMIXSINK_STS_DRAINED_DMA;
2076 }
2077 else
2078 {
2079 pSink->cbDmaLeftToDrain -= cbToTransfer2;
2080 Log3Func(("idStream=%u/'%s': still %#x bytes left in the DMA buffer\n",
2081 idStream, pSink->pszName, pSink->cbDmaLeftToDrain));
2082 }
2083 }
2084 else
2085 Assert(cbToTransfer2 == 0);
2086
2087 Log3Func(("idStream=%u: cbCircBufUsed=%RX32 left\n", idStream, (uint32_t)RTCircBufUsed(pCircBuf)));
2088
2089 RTCritSectLeave(&pSink->CritSect);
2090
2091 LogFlowFuncLeave();
2092 return offStream;
2093}
2094
2095
2096/**
2097 * Transfer data to the device's DMA buffer from the sink.
2098 *
2099 * The caller is already holding the mixer sink's critical section, either by
2100 * way of being the AIO thread doing update jobs or by explicit locking calls.
2101 *
2102 * @returns The new stream offset.
2103 * @param pSink The mixer sink to transfer samples from.
2104 * @param pCircBuf The internal DMA buffer to move samples to.
2105 * @param offStream The stream current offset (logging, dtrace, return).
2106 * @param idStream Device specific audio stream identifier (logging, dtrace).
2107 * @param pDbgFile Debug file, NULL if disabled.
2108 */
2109uint64_t AudioMixerSinkTransferToCircBuf(PAUDMIXSINK pSink, PRTCIRCBUF pCircBuf, uint64_t offStream,
2110 uint32_t idStream, PAUDIOHLPFILE pDbgFile)
2111{
2112 /*
2113 * Sanity.
2114 */
2115 AssertReturn(pSink, offStream);
2116 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2117 AssertReturn(pCircBuf, offStream);
2118 Assert(RTCritSectIsOwner(&pSink->CritSect));
2119
2120 /*
2121 * Figure out how much we can transfer.
2122 */
2123 const uint32_t cbSinkReadable = AudioMixerSinkGetReadable(pSink);
2124 const uint32_t cbCircBufWritable = (uint32_t)RTCircBufFree(pCircBuf);
2125 uint32_t cbToTransfer = RT_MIN(cbCircBufWritable, cbSinkReadable);
2126 uint32_t cFramesToTransfer = PDMAudioPropsBytesToFrames(&pSink->PCMProps, cbToTransfer);
2127 cbToTransfer = PDMAudioPropsFramesToBytes(&pSink->PCMProps, cFramesToTransfer);
2128
2129 Log3Func(("idStream=%u: cbSinkReadable=%#RX32 cbCircBufWritable=%#RX32 -> cbToTransfer=%#RX32 (%RU32 frames) @%#RX64\n",
2130 idStream, cbSinkReadable, cbCircBufWritable, cbToTransfer, cFramesToTransfer, offStream));
2131 RT_NOREF(idStream);
2132
2133 /** @todo should we throttle (read less) this if we're far ahead? */
2134
2135 /*
2136 * Copy loop.
2137 */
2138 while (cbToTransfer > 0)
2139 {
2140/** @todo We should be able to read straight into the circular buffer here
2141 * as it should have a frame aligned size. */
2142
2143 /* Read a chunk of data. */
2144 uint8_t abBuf[4096];
2145 uint32_t cbRead = 0;
2146 uint32_t cFramesRead = 0;
2147 AudioMixBufPeek(&pSink->MixBuf, 0, cFramesToTransfer, &cFramesRead,
2148 &pSink->In.State, abBuf, RT_MIN(cbToTransfer, sizeof(abBuf)), &cbRead);
2149 AssertBreak(cFramesRead > 0);
2150 Assert(cbRead > 0);
2151
2152 cFramesToTransfer -= cFramesRead;
2153 AudioMixBufAdvance(&pSink->MixBuf, cFramesRead);
2154
2155 /* Write it to the internal DMA buffer. */
2156 uint32_t off = 0;
2157 while (off < cbRead)
2158 {
2159 void *pvDstBuf;
2160 size_t cbDstBuf;
2161 RTCircBufAcquireWriteBlock(pCircBuf, cbRead - off, &pvDstBuf, &cbDstBuf);
2162
2163 memcpy(pvDstBuf, &abBuf[off], cbDstBuf);
2164
2165#ifdef VBOX_WITH_DTRACE
2166 VBOXDD_AUDIO_MIXER_SINK_AIO_IN(idStream, (uint32_t)cbDstBuf, offStream);
2167#endif
2168 offStream += cbDstBuf;
2169
2170 RTCircBufReleaseWriteBlock(pCircBuf, cbDstBuf);
2171
2172 off += (uint32_t)cbDstBuf;
2173 }
2174 Assert(off == cbRead);
2175
2176 /* Write to debug file? */
2177 if (RT_LIKELY(!pDbgFile))
2178 { /* likely */ }
2179 else
2180 AudioHlpFileWrite(pDbgFile, abBuf, cbRead);
2181
2182 /* Advance. */
2183 Assert(cbRead <= cbToTransfer);
2184 cbToTransfer -= cbRead;
2185 }
2186
2187 return offStream;
2188}
2189
2190
2191/**
2192 * Signals the AIO thread to perform updates.
2193 *
2194 * @returns VBox status code.
2195 * @param pSink The mixer sink which AIO thread needs to do chores.
2196 */
2197int AudioMixerSinkSignalUpdateJob(PAUDMIXSINK pSink)
2198{
2199 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2200 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2201 return RTSemEventSignal(pSink->AIO.hEvent);
2202}
2203
2204
2205/**
2206 * Checks if the caller is the owner of the mixer sink's critical section.
2207 *
2208 * @returns \c true if the caller is the lock owner, \c false if not.
2209 * @param pSink The mixer sink to check.
2210 */
2211bool AudioMixerSinkLockIsOwner(PAUDMIXSINK pSink)
2212{
2213 return RTCritSectIsOwner(&pSink->CritSect);
2214}
2215
2216
2217/**
2218 * Locks the mixer sink for purposes of serializing with the AIO thread.
2219 *
2220 * @returns VBox status code.
2221 * @param pSink The mixer sink to lock.
2222 */
2223int AudioMixerSinkLock(PAUDMIXSINK pSink)
2224{
2225 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2226 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2227 return RTCritSectEnter(&pSink->CritSect);
2228}
2229
2230
2231/**
2232 * Try to lock the mixer sink for purposes of serializing with the AIO thread.
2233 *
2234 * @returns VBox status code.
2235 * @param pSink The mixer sink to lock.
2236 */
2237int AudioMixerSinkTryLock(PAUDMIXSINK pSink)
2238{
2239 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2240 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2241 return RTCritSectTryEnter(&pSink->CritSect);
2242}
2243
2244
2245/**
2246 * Unlocks the sink.
2247 *
2248 * @returns VBox status code.
2249 * @param pSink The mixer sink to unlock.
2250 */
2251int AudioMixerSinkUnlock(PAUDMIXSINK pSink)
2252{
2253 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2254 return RTCritSectLeave(&pSink->CritSect);
2255}
2256
2257
2258/**
2259 * Creates an audio mixer stream.
2260 *
2261 * @returns VBox status code.
2262 * @param pSink Sink to use for creating the stream.
2263 * @param pConn Audio connector interface to use.
2264 * @param pCfg Audio stream configuration to use. This may be modified
2265 * in some unspecified way (see
2266 * PDMIAUDIOCONNECTOR::pfnStreamCreate).
2267 * @param pDevIns The device instance to register statistics with.
2268 * @param ppStream Pointer which receives the newly created audio stream.
2269 */
2270int AudioMixerSinkCreateStream(PAUDMIXSINK pSink, PPDMIAUDIOCONNECTOR pConn, PCPDMAUDIOSTREAMCFG pCfg,
2271 PPDMDEVINS pDevIns, PAUDMIXSTREAM *ppStream)
2272{
2273 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2274 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2275 AssertPtrReturn(pConn, VERR_INVALID_POINTER);
2276 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2277 AssertPtrNullReturn(ppStream, VERR_INVALID_POINTER);
2278 Assert(pSink->AIO.pDevIns == pDevIns); RT_NOREF(pDevIns); /* we'll probably be adding more statistics */
2279 AssertReturn(pCfg->enmDir == pSink->enmDir, VERR_MISMATCH);
2280
2281 /*
2282 * Check status and get the host driver config.
2283 */
2284 if (pConn->pfnGetStatus(pConn, PDMAUDIODIR_DUPLEX) == PDMAUDIOBACKENDSTS_NOT_ATTACHED)
2285 return VERR_AUDIO_BACKEND_NOT_ATTACHED;
2286
2287 PDMAUDIOBACKENDCFG BackendCfg;
2288 int rc = pConn->pfnGetConfig(pConn, &BackendCfg);
2289 AssertRCReturn(rc, rc);
2290
2291 /*
2292 * Allocate the instance.
2293 */
2294 PAUDMIXSTREAM pMixStream = (PAUDMIXSTREAM)RTMemAllocZ(sizeof(AUDMIXSTREAM));
2295 AssertReturn(pMixStream, VERR_NO_MEMORY);
2296
2297 /* Assign the backend's name to the mixer stream's name for easier identification in the (release) log. */
2298 pMixStream->pszName = RTStrAPrintf2("[%s] %s", pCfg->szName, BackendCfg.szName);
2299 pMixStream->pszStatPrefix = RTStrAPrintf2("MixerSink-%s/%s/", pSink->pszName, BackendCfg.szName);
2300 if (pMixStream->pszName && pMixStream->pszStatPrefix)
2301 {
2302 rc = RTCritSectInit(&pMixStream->CritSect);
2303 if (RT_SUCCESS(rc))
2304 {
2305 /*
2306 * Lock the sink so we can safely get it's properties and call
2307 * down into the audio driver to create that end of the stream.
2308 */
2309 rc = RTCritSectEnter(&pSink->CritSect);
2310 AssertRC(rc);
2311 if (RT_SUCCESS(rc))
2312 {
2313 LogFlowFunc(("[%s] (enmDir=%ld, %u bits, %RU8 channels, %RU32Hz)\n", pSink->pszName, pCfg->enmDir,
2314 PDMAudioPropsSampleBits(&pCfg->Props), PDMAudioPropsChannels(&pCfg->Props), pCfg->Props.uHz));
2315
2316 /*
2317 * Initialize the host-side configuration for the stream to be created,
2318 * this is the sink format & direction with the src/dir, layout, name
2319 * and device specific config copied from the guest side config (pCfg).
2320 * We disregard any Backend settings here.
2321 *
2322 * (Note! pfnStreamCreate used to get both CfgHost and pCfg (aka pCfgGuest)
2323 * passed in, but that became unnecessary with DrvAudio stoppping
2324 * mixing. The mixing is done here and we bridge guest & host configs.)
2325 */
2326 AssertMsg(AudioHlpPcmPropsAreValidAndSupported(&pSink->PCMProps),
2327 ("%s: Does not (yet) have a (valid and supported) format set when it must\n", pSink->pszName));
2328
2329 PDMAUDIOSTREAMCFG CfgHost;
2330 rc = PDMAudioStrmCfgInitWithProps(&CfgHost, &pSink->PCMProps);
2331 AssertRC(rc); /* cannot fail */
2332 CfgHost.enmDir = pSink->enmDir;
2333 CfgHost.enmPath = pCfg->enmPath;
2334 CfgHost.Device = pCfg->Device;
2335 RTStrCopy(CfgHost.szName, sizeof(CfgHost.szName), pCfg->szName);
2336
2337 /*
2338 * Create the stream.
2339 *
2340 * Output streams are not using any mixing buffers in DrvAudio. This will
2341 * become the norm after we move the input mixing here and convert DevSB16
2342 * to use this mixer code too.
2343 */
2344 PPDMAUDIOSTREAM pStream;
2345 rc = pConn->pfnStreamCreate(pConn, 0 /*fFlags*/, &CfgHost, &pStream);
2346 if (RT_SUCCESS(rc))
2347 {
2348 pMixStream->cFramesBackendBuffer = pStream->Cfg.Backend.cFramesBufferSize;
2349
2350 /* Set up the mixing buffer conversion state. */
2351 if (pSink->enmDir == PDMAUDIODIR_IN)
2352 rc = AudioMixBufInitWriteState(&pSink->MixBuf, &pMixStream->WriteState, &pStream->Cfg.Props);
2353 else
2354 rc = AudioMixBufInitPeekState(&pSink->MixBuf, &pMixStream->PeekState, &pStream->Cfg.Props);
2355 if (RT_SUCCESS(rc))
2356 {
2357 /* Save the audio stream pointer to this mixing stream. */
2358 pMixStream->pStream = pStream;
2359
2360 /* Increase the stream's reference count to let others know
2361 * we're relying on it to be around now. */
2362 pConn->pfnStreamRetain(pConn, pStream);
2363 pMixStream->pConn = pConn;
2364 pMixStream->uMagic = AUDMIXSTREAM_MAGIC;
2365
2366 RTCritSectLeave(&pSink->CritSect);
2367
2368 if (ppStream)
2369 *ppStream = pMixStream;
2370 return VINF_SUCCESS;
2371 }
2372
2373 rc = pConn->pfnStreamDestroy(pConn, pStream, true /*fImmediate*/);
2374 }
2375
2376 /*
2377 * Failed. Tear down the stream.
2378 */
2379 int rc2 = RTCritSectLeave(&pSink->CritSect);
2380 AssertRC(rc2);
2381 }
2382 RTCritSectDelete(&pMixStream->CritSect);
2383 }
2384 }
2385 else
2386 rc = VERR_NO_STR_MEMORY;
2387
2388 RTStrFree(pMixStream->pszStatPrefix);
2389 pMixStream->pszStatPrefix = NULL;
2390 RTStrFree(pMixStream->pszName);
2391 pMixStream->pszName = NULL;
2392 RTMemFree(pMixStream);
2393 return rc;
2394}
2395
2396
2397/**
2398 * Adds an audio stream to a specific audio sink.
2399 *
2400 * @returns VBox status code.
2401 * @param pSink Sink to add audio stream to.
2402 * @param pStream Stream to add.
2403 */
2404int AudioMixerSinkAddStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
2405{
2406 LogFlowFuncEnter();
2407 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2408 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2409 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2410 Assert(pStream->uMagic == AUDMIXSTREAM_MAGIC);
2411 AssertPtrReturn(pStream->pConn, VERR_AUDIO_STREAM_NOT_READY);
2412 AssertReturn(pStream->pSink == NULL, VERR_ALREADY_EXISTS);
2413
2414 int rc = RTCritSectEnter(&pSink->CritSect);
2415 AssertRCReturn(rc, rc);
2416
2417 AssertLogRelMsgReturnStmt(pSink->cStreams < UINT8_MAX, ("too many streams!\n"), RTCritSectLeave(&pSink->CritSect),
2418 VERR_TOO_MANY_OPEN_FILES);
2419
2420 /*
2421 * If the sink is running and not in pending disable mode, make sure that
2422 * the added stream also is enabled. Ignore any failure to enable it.
2423 */
2424 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
2425 && !(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
2426 {
2427 audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_ENABLE);
2428 }
2429
2430 /* Save pointer to sink the stream is attached to. */
2431 pStream->pSink = pSink;
2432
2433 /* Append stream to sink's list. */
2434 RTListAppend(&pSink->lstStreams, &pStream->Node);
2435 pSink->cStreams++;
2436
2437 LogFlowFunc(("[%s] cStreams=%RU8, rc=%Rrc\n", pSink->pszName, pSink->cStreams, rc));
2438 RTCritSectLeave(&pSink->CritSect);
2439 return rc;
2440}
2441
2442
2443/**
2444 * Removes a mixer stream from a mixer sink, internal version.
2445 *
2446 * @returns VBox status code.
2447 * @param pSink The mixer sink (valid).
2448 * @param pStream The stream to remove (valid).
2449 *
2450 * @note Caller must own the sink lock.
2451 */
2452static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
2453{
2454 AssertPtr(pSink);
2455 AssertPtr(pStream);
2456 AssertMsgReturn(pStream->pSink == pSink, ("Stream '%s' is not part of sink '%s'\n",
2457 pStream->pszName, pSink->pszName), VERR_NOT_FOUND);
2458 Assert(RTCritSectIsOwner(&pSink->CritSect));
2459 LogFlowFunc(("[%s] (Stream = %s), cStreams=%RU8\n", pSink->pszName, pStream->pStream->Cfg.szName, pSink->cStreams));
2460
2461 /*
2462 * Remove stream from sink, update the count and set the pSink member to NULL.
2463 */
2464 RTListNodeRemove(&pStream->Node);
2465
2466 Assert(pSink->cStreams > 0);
2467 pSink->cStreams--;
2468
2469 pStream->pSink = NULL;
2470
2471 return VINF_SUCCESS;
2472}
2473
2474
2475/**
2476 * Removes a mixer stream from a mixer sink.
2477 *
2478 * @param pSink The mixer sink.
2479 * @param pStream The stream to remove.
2480 */
2481void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
2482{
2483 AssertPtrReturnVoid(pSink);
2484 AssertReturnVoid(pSink->uMagic == AUDMIXSINK_MAGIC);
2485 AssertPtrReturnVoid(pStream);
2486 AssertReturnVoid(pStream->uMagic == AUDMIXSTREAM_MAGIC);
2487
2488 int rc = RTCritSectEnter(&pSink->CritSect);
2489 AssertRCReturnVoid(rc);
2490
2491 audioMixerSinkRemoveStreamInternal(pSink, pStream);
2492
2493 RTCritSectLeave(&pSink->CritSect);
2494}
2495
2496
2497/**
2498 * Removes all streams from a given sink.
2499 *
2500 * @param pSink The mixer sink. NULL is ignored.
2501 */
2502void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
2503{
2504 if (!pSink)
2505 return;
2506 AssertReturnVoid(pSink->uMagic == AUDMIXSINK_MAGIC);
2507
2508 int rc = RTCritSectEnter(&pSink->CritSect);
2509 AssertRCReturnVoid(rc);
2510
2511 LogFunc(("%s\n", pSink->pszName));
2512
2513 PAUDMIXSTREAM pStream, pStreamNext;
2514 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
2515 {
2516 audioMixerSinkRemoveStreamInternal(pSink, pStream);
2517 }
2518 AssertStmt(pSink->cStreams == 0, pSink->cStreams = 0);
2519
2520 RTCritSectLeave(&pSink->CritSect);
2521}
2522
2523
2524
2525/*********************************************************************************************************************************
2526 * Mixer Stream implementation.
2527 ********************************************************************************************************************************/
2528
2529/**
2530 * Controls a mixer stream, internal version.
2531 *
2532 * @returns VBox status code (generally ignored).
2533 * @param pMixStream Mixer stream to control.
2534 * @param enmCmd Mixer stream command to use.
2535 */
2536static int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd)
2537{
2538 Assert(pMixStream->uMagic == AUDMIXSTREAM_MAGIC);
2539 AssertPtrReturn(pMixStream->pConn, VERR_AUDIO_STREAM_NOT_READY);
2540 AssertPtrReturn(pMixStream->pStream, VERR_AUDIO_STREAM_NOT_READY);
2541
2542 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
2543
2544 LogFlowFunc(("[%s] enmCmd=%d, rc=%Rrc\n", pMixStream->pszName, enmCmd, rc));
2545
2546 return rc;
2547}
2548
2549/**
2550 * Updates a mixer stream's internal status.
2551 *
2552 * This may perform a stream re-init if the driver requests it, in which case
2553 * this may take a little while longer than usual...
2554 *
2555 * @returns VBox status code.
2556 * @param pMixStream Mixer stream to to update internal status for.
2557 */
2558static int audioMixerStreamUpdateStatus(PAUDMIXSTREAM pMixStream)
2559{
2560 Assert(pMixStream->uMagic == AUDMIXSTREAM_MAGIC);
2561
2562 /*
2563 * Reset the mixer status to start with.
2564 */
2565 pMixStream->fStatus = AUDMIXSTREAM_STATUS_NONE;
2566
2567 PPDMIAUDIOCONNECTOR const pConn = pMixStream->pConn;
2568 if (pConn) /* Audio connector available? */
2569 {
2570 PPDMAUDIOSTREAM const pStream = pMixStream->pStream;
2571
2572 /*
2573 * Get the stream status.
2574 * Do re-init if needed and fetch the status again afterwards.
2575 */
2576 PDMAUDIOSTREAMSTATE enmState = pConn->pfnStreamGetState(pConn, pStream);
2577 if (enmState != PDMAUDIOSTREAMSTATE_NEED_REINIT)
2578 { /* likely */ }
2579 else
2580 {
2581 LogFunc(("[%s] needs re-init...\n", pMixStream->pszName));
2582 int rc = pConn->pfnStreamReInit(pConn, pStream);
2583 enmState = pConn->pfnStreamGetState(pConn, pStream);
2584 LogFunc(("[%s] re-init returns %Rrc and %s.\n", pMixStream->pszName, rc, PDMAudioStreamStateGetName(enmState)));
2585
2586 PAUDMIXSINK const pSink = pMixStream->pSink;
2587 AssertPtr(pSink);
2588 if (pSink->enmDir == PDMAUDIODIR_OUT)
2589 {
2590 rc = AudioMixBufInitPeekState(&pSink->MixBuf, &pMixStream->PeekState, &pStream->Cfg.Props);
2591 /** @todo we need to remember this, don't we? */
2592 AssertLogRelRCReturn(rc, VINF_SUCCESS);
2593 }
2594 else
2595 {
2596 rc = AudioMixBufInitWriteState(&pSink->MixBuf, &pMixStream->WriteState, &pStream->Cfg.Props);
2597 /** @todo we need to remember this, don't we? */
2598 AssertLogRelRCReturn(rc, VINF_SUCCESS);
2599 }
2600 }
2601
2602 /*
2603 * Translate the status to mixer speak.
2604 */
2605 AssertMsg(enmState > PDMAUDIOSTREAMSTATE_INVALID && enmState < PDMAUDIOSTREAMSTATE_END, ("%d\n", enmState));
2606 switch (enmState)
2607 {
2608 case PDMAUDIOSTREAMSTATE_NOT_WORKING:
2609 case PDMAUDIOSTREAMSTATE_NEED_REINIT:
2610 case PDMAUDIOSTREAMSTATE_INACTIVE:
2611 pMixStream->fStatus = AUDMIXSTREAM_STATUS_NONE;
2612 break;
2613 case PDMAUDIOSTREAMSTATE_ENABLED:
2614 pMixStream->fStatus = AUDMIXSTREAM_STATUS_ENABLED;
2615 break;
2616 case PDMAUDIOSTREAMSTATE_ENABLED_READABLE:
2617 Assert(pMixStream->pSink->enmDir == PDMAUDIODIR_IN);
2618 pMixStream->fStatus = AUDMIXSTREAM_STATUS_ENABLED | AUDMIXSTREAM_STATUS_CAN_READ;
2619 break;
2620 case PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE:
2621 Assert(pMixStream->pSink->enmDir == PDMAUDIODIR_OUT);
2622 pMixStream->fStatus = AUDMIXSTREAM_STATUS_ENABLED | AUDMIXSTREAM_STATUS_CAN_WRITE;
2623 break;
2624 /* no default */
2625 case PDMAUDIOSTREAMSTATE_INVALID:
2626 case PDMAUDIOSTREAMSTATE_END:
2627 case PDMAUDIOSTREAMSTATE_32BIT_HACK:
2628 break;
2629 }
2630 }
2631
2632 LogFlowFunc(("[%s] -> 0x%x\n", pMixStream->pszName, pMixStream->fStatus));
2633 return VINF_SUCCESS;
2634}
2635
2636
2637/**
2638 * Destroys & frees a mixer stream, internal version.
2639 *
2640 * Worker for audioMixerSinkDestroyInternal and AudioMixerStreamDestroy.
2641 *
2642 * @param pMixStream Mixer stream to destroy.
2643 * @param pDevIns The device instance the statistics are registered with.
2644 * @param fImmediate How to handle still draining streams, whether to let
2645 * them complete (@c false) or destroy them immediately (@c
2646 * true).
2647 */
2648static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream, PPDMDEVINS pDevIns, bool fImmediate)
2649{
2650 AssertPtr(pMixStream);
2651 LogFunc(("%s\n", pMixStream->pszName));
2652 Assert(pMixStream->uMagic == AUDMIXSTREAM_MAGIC);
2653
2654 /*
2655 * Invalidate it.
2656 */
2657 pMixStream->uMagic = AUDMIXSTREAM_MAGIC_DEAD;
2658
2659 /*
2660 * Destroy the driver stream (if any).
2661 */
2662 if (pMixStream->pConn) /* Stream has a connector interface present? */
2663 {
2664 if (pMixStream->pStream)
2665 {
2666 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
2667 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream, fImmediate);
2668
2669 pMixStream->pStream = NULL;
2670 }
2671
2672 pMixStream->pConn = NULL;
2673 }
2674
2675 /*
2676 * Stats. Doing it by prefix is soo much faster than individually, btw.
2677 */
2678 if (pMixStream->pszStatPrefix)
2679 {
2680 PDMDevHlpSTAMDeregisterByPrefix(pDevIns, pMixStream->pszStatPrefix);
2681 RTStrFree(pMixStream->pszStatPrefix);
2682 pMixStream->pszStatPrefix = NULL;
2683 }
2684
2685 /*
2686 * Delete the critsect and free the memory.
2687 */
2688 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
2689 AssertRC(rc2);
2690
2691 RTStrFree(pMixStream->pszName);
2692 pMixStream->pszName = NULL;
2693
2694 RTMemFree(pMixStream);
2695}
2696
2697
2698/**
2699 * Destroys a mixer stream.
2700 *
2701 * @param pMixStream Mixer stream to destroy.
2702 * @param pDevIns The device instance statistics are registered with.
2703 * @param fImmediate How to handle still draining streams, whether to let
2704 * them complete (@c false) or destroy them immediately (@c
2705 * true).
2706 */
2707void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream, PPDMDEVINS pDevIns, bool fImmediate)
2708{
2709 if (!pMixStream)
2710 return;
2711 AssertReturnVoid(pMixStream->uMagic == AUDMIXSTREAM_MAGIC);
2712 LogFunc(("%s\n", pMixStream->pszName));
2713
2714 /*
2715 * Serializing paranoia.
2716 */
2717 int rc = RTCritSectEnter(&pMixStream->CritSect);
2718 AssertRCReturnVoid(rc);
2719 RTCritSectLeave(&pMixStream->CritSect);
2720
2721 /*
2722 * Unlink from sink if associated with one.
2723 */
2724 PAUDMIXSINK pSink = pMixStream->pSink;
2725 if ( RT_VALID_PTR(pSink)
2726 && pSink->uMagic == AUDMIXSINK_MAGIC)
2727 {
2728 RTCritSectEnter(&pSink->CritSect);
2729 audioMixerSinkRemoveStreamInternal(pMixStream->pSink, pMixStream);
2730 RTCritSectLeave(&pSink->CritSect);
2731 }
2732 else if (pSink)
2733 AssertFailed();
2734
2735 /*
2736 * Do the actual stream destruction.
2737 */
2738 audioMixerStreamDestroyInternal(pMixStream, pDevIns, fImmediate);
2739 LogFlowFunc(("returns\n"));
2740}
2741
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use