VirtualBox

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

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

AudioMixer: Removed AudioMixBufWriteAt, eliminated last non-testcase AudioMixBufWriteCirc use. Fixed testcase buffer clearing failure. bugref:9890

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

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