VirtualBox

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

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

Copyright year updates by scm.

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

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