VirtualBox

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

Last change on this file since 104499 was 104499, checked in by vboxsync, 12 months ago

Audio/AudioMixer: Made the debug (.WAV) file of an output sink actually write something. Was missing before.

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

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