VirtualBox

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

Last change on this file since 96238 was 94996, checked in by vboxsync, 3 years ago

Audio/Mixer: Also need to take the sink's critical section when audioMixerSinkRemoveStreamInternal() is being invoked from AudioMixerDestroy() or AudioMixerSinkDestroy(), without explicitly destroying attached mixer streams before via AudioMixerStreamDestroy() or AudioMixerSinkRemoveAllStreams().

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

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