VirtualBox

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

Last change on this file since 89769 was 89768, checked in by vboxsync, 4 years ago

Audio: Made PDMAUDIOVOLUME multichannel. bugref:9890

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