VirtualBox

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

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

AudioMixer: Status logging fix. bugref:9890

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