VirtualBox

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

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

AudioMixBuffer.cpp: scm fix bugref:9890

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

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