VirtualBox

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

最後變更 在這個檔案從89810是 89810,由 vboxsync 提交於 4 年 前

Audio: Eliminated some unnecessary stream config copying in the device code. bugref:9890

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

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