VirtualBox

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

Last change on this file since 89480 was 89379, checked in by vboxsync, 4 years ago

Audio: Removed PDMAUDIOSTREAMCFG::enmLayout and PDMAUDIOSTREAMLAYOUT. bugref:9890

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

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