VirtualBox

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

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

Audio: Buffer usage statistics so we can monitor whether they are emptied when they should be. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 76.5 KB
Line 
1/* $Id: AudioMixer.cpp 88307 2021-03-26 21:18:42Z vboxsync $ */
2/** @file
3 * Audio mixing routines for multiplexing audio sources in device emulations.
4 *
5 * Overview
6 * ========
7 *
8 * This mixer acts as a layer between the audio connector interface and
9 * the actual device emulation, providing mechanisms for audio sources (input)
10 * and audio sinks (output).
11 *
12 * Think of this mixer as kind of a high(er) level interface for the audio
13 * connector interface, abstracting common tasks such as creating and managing
14 * various audio sources and sinks. This mixer class is purely optional and can
15 * be left out when implementing a new device emulation, using only the audi
16 * connector interface instead. For example, the SB16 emulation does not use
17 * this mixer and does all its stream management on its own.
18 *
19 * As audio driver instances are handled as LUNs on the device level, this
20 * audio mixer then can take care of e.g. mixing various inputs/outputs to/from
21 * a specific source/sink.
22 *
23 * How and which audio streams are connected to sinks/sources depends on how
24 * the audio mixer has been set up.
25 *
26 * A sink can connect multiple output streams together, whereas a source
27 * does this with input streams. Each sink / source consists of one or more
28 * so-called mixer streams, which then in turn have pointers to the actual
29 * PDM audio input/output streams.
30 *
31 * Playback
32 * ========
33 *
34 * For output sinks there can be one or more mixing stream attached.
35 * As the host sets the overall pace for the device emulation (virtual time
36 * in the guest OS vs. real time on the host OS), an output mixing sink
37 * needs to make sure that all connected output streams are able to accept
38 * all the same amount of data at a time.
39 *
40 * This is called synchronous multiplexing.
41 *
42 * A mixing sink employs an own audio mixing buffer, which in turn can convert
43 * the audio (output) data supplied from the device emulation into the sink's
44 * audio format. As all connected mixing streams in theory could have the same
45 * audio format as the mixing sink (parent), this can save processing time when
46 * it comes to serving a lot of mixing streams at once. That way only one
47 * conversion must be done, instead of each stream having to iterate over the
48 * data.
49 *
50 * Recording
51 * =========
52 *
53 * For input sinks only one mixing stream at a time can be the recording
54 * source currently. A recording source is optional, e.g. it is possible to
55 * have no current recording source set. Switching to a different recording
56 * source at runtime is possible.
57 */
58
59/*
60 * Copyright (C) 2014-2020 Oracle Corporation
61 *
62 * This file is part of VirtualBox Open Source Edition (OSE), as
63 * available from http://www.virtualbox.org. This file is free software;
64 * you can redistribute it and/or modify it under the terms of the GNU
65 * General Public License (GPL) as published by the Free Software
66 * Foundation, in version 2 as it comes in the "COPYING" file of the
67 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
68 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
69 */
70
71
72/*********************************************************************************************************************************
73* Header Files *
74*********************************************************************************************************************************/
75#define LOG_GROUP LOG_GROUP_AUDIO_MIXER
76#include <VBox/log.h>
77#include "AudioMixer.h"
78#include "AudioMixBuffer.h"
79#include "AudioHlp.h"
80
81#include <VBox/vmm/pdm.h>
82#include <VBox/err.h>
83#include <VBox/vmm/mm.h>
84#include <VBox/vmm/pdmaudioifs.h>
85#include <VBox/vmm/pdmaudioinline.h>
86
87#include <iprt/alloc.h>
88#include <iprt/asm-math.h>
89#include <iprt/assert.h>
90#include <iprt/string.h>
91
92
93/*********************************************************************************************************************************
94* Internal Functions *
95*********************************************************************************************************************************/
96static int audioMixerAddSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink);
97static int audioMixerRemoveSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink);
98
99static int audioMixerSinkInit(PAUDMIXSINK pSink, PAUDIOMIXER pMixer, const char *pcszName, AUDMIXSINKDIR enmDir);
100static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink, PPDMDEVINS pDevIns);
101static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster);
102static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink);
103static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream);
104static void audioMixerSinkReset(PAUDMIXSINK pSink);
105static int audioMixerSinkSetRecSourceInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream);
106static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink);
107static int audioMixerSinkMultiplexSync(PAUDMIXSINK pSink, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten);
108static int audioMixerSinkWriteToStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pMixStream);
109static int audioMixerSinkWriteToStreamEx(PAUDMIXSINK pSink, PAUDMIXSTREAM pMixStream, uint32_t cbToWrite, uint32_t *pcbWritten);
110
111static int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl);
112static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pStream, PPDMDEVINS pDevIns);
113static int audioMixerStreamUpdateStatus(PAUDMIXSTREAM pMixStream);
114
115
116/** size of output buffer for dbgAudioMixerSinkStatusToStr. */
117#define AUDIOMIXERSINK_STATUS_STR_MAX sizeof("RUNNING PENDING_DISABLE DIRTY 0x12345678")
118
119/**
120 * Converts a mixer sink status to a string.
121 *
122 * @returns pszDst
123 * @param fStatus The mixer sink status.
124 * @param pszDst The output buffer. Must be at least
125 * AUDIOMIXERSINK_STATUS_STR_MAX in length.
126 */
127static const char *dbgAudioMixerSinkStatusToStr(AUDMIXSINKSTS fStatus, char pszDst[AUDIOMIXERSINK_STATUS_STR_MAX])
128{
129 if (!fStatus)
130 return strcpy(pszDst, "NONE");
131 static const struct
132 {
133 const char *pszMnemonic;
134 uint32_t cchMnemonic;
135 uint32_t fStatus;
136 } s_aFlags[] =
137 {
138 { RT_STR_TUPLE("RUNNING"), AUDMIXSINK_STS_RUNNING },
139 { RT_STR_TUPLE("PENDING_DISABLE"), AUDMIXSINK_STS_PENDING_DISABLE },
140 { RT_STR_TUPLE("DIRTY"), AUDMIXSINK_STS_DIRTY },
141 };
142 char *psz = pszDst;
143 for (size_t i = 0; i < RT_ELEMENTS(s_aFlags); i++)
144 if (fStatus & s_aFlags[i].fStatus)
145 {
146 memcpy(psz, s_aFlags[i].pszMnemonic, s_aFlags[i].cchMnemonic);
147 psz += s_aFlags[i].cchMnemonic;
148 fStatus &= ~s_aFlags[i].fStatus;
149 if (!fStatus)
150 {
151 psz[-1] = '\0';
152 return pszDst;
153 }
154 }
155 RTStrPrintf(psz, AUDIOMIXERSINK_STATUS_STR_MAX - (psz - pszDst), "%#x", fStatus);
156 return pszDst;
157}
158
159/**
160 * Creates an audio sink and attaches it to the given mixer.
161 *
162 * @returns VBox status code.
163 * @param pMixer Mixer to attach created sink to.
164 * @param pszName Name of the sink to create.
165 * @param enmDir Direction of the sink to create.
166 * @param pDevIns The device instance to register statistics under.
167 * @param ppSink Pointer which returns the created sink on success.
168 */
169int AudioMixerCreateSink(PAUDIOMIXER pMixer, const char *pszName, AUDMIXSINKDIR enmDir, PPDMDEVINS pDevIns, PAUDMIXSINK *ppSink)
170{
171 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
172 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
173 /* ppSink is optional. */
174
175 int rc = RTCritSectEnter(&pMixer->CritSect);
176 AssertRCReturn(rc, rc);
177
178 PAUDMIXSINK pSink = (PAUDMIXSINK)RTMemAllocZ(sizeof(AUDMIXSINK));
179 if (pSink)
180 {
181 rc = audioMixerSinkInit(pSink, pMixer, pszName, enmDir);
182 if (RT_SUCCESS(rc))
183 {
184 rc = audioMixerAddSinkInternal(pMixer, pSink);
185 if (RT_SUCCESS(rc))
186 {
187 RTCritSectLeave(&pMixer->CritSect);
188
189 if (ppSink)
190 *ppSink = pSink;
191 return VINF_SUCCESS;
192 }
193 }
194
195 audioMixerSinkDestroyInternal(pSink, pDevIns);
196
197 RTMemFree(pSink);
198 pSink = NULL;
199 }
200 else
201 rc = VERR_NO_MEMORY;
202
203 RTCritSectLeave(&pMixer->CritSect);
204 return rc;
205}
206
207/**
208 * Creates an audio mixer.
209 *
210 * @returns VBox status code.
211 * @param pcszName Name of the audio mixer.
212 * @param fFlags Creation flags.
213 * @param ppMixer Pointer which returns the created mixer object.
214 */
215int AudioMixerCreate(const char *pcszName, uint32_t fFlags, PAUDIOMIXER *ppMixer)
216{
217 AssertPtrReturn(pcszName, VERR_INVALID_POINTER);
218 AssertReturn (!(fFlags & ~AUDMIXER_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
219 AssertPtrReturn(ppMixer, VERR_INVALID_POINTER);
220
221 int rc = VINF_SUCCESS;
222
223 PAUDIOMIXER pMixer = (PAUDIOMIXER)RTMemAllocZ(sizeof(AUDIOMIXER));
224 if (pMixer)
225 {
226 pMixer->pszName = RTStrDup(pcszName);
227 if (!pMixer->pszName)
228 rc = VERR_NO_MEMORY;
229
230 if (RT_SUCCESS(rc))
231 rc = RTCritSectInit(&pMixer->CritSect);
232
233 if (RT_SUCCESS(rc))
234 {
235 pMixer->cSinks = 0;
236 RTListInit(&pMixer->lstSinks);
237
238 pMixer->fFlags = fFlags;
239
240 if (pMixer->fFlags & AUDMIXER_FLAGS_DEBUG)
241 LogRel(("Audio Mixer: Debug mode enabled\n"));
242
243 /* Set master volume to the max. */
244 pMixer->VolMaster.fMuted = false;
245 pMixer->VolMaster.uLeft = PDMAUDIO_VOLUME_MAX;
246 pMixer->VolMaster.uRight = PDMAUDIO_VOLUME_MAX;
247
248 LogFlowFunc(("Created mixer '%s'\n", pMixer->pszName));
249
250 *ppMixer = pMixer;
251 }
252 else
253 RTMemFree(pMixer); /** @todo leaks pszName due to badly structured code */
254 }
255 else
256 rc = VERR_NO_MEMORY;
257
258 LogFlowFuncLeaveRC(rc);
259 return rc;
260}
261
262/**
263 * Helper function for the internal debugger to print the mixer's current
264 * state, along with the attached sinks.
265 *
266 * @param pMixer Mixer to print debug output for.
267 * @param pHlp Debug info helper to use.
268 * @param pszArgs Optional arguments. Not being used at the moment.
269 */
270void AudioMixerDebug(PAUDIOMIXER pMixer, PCDBGFINFOHLP pHlp, const char *pszArgs)
271{
272 RT_NOREF(pszArgs);
273 PAUDMIXSINK pSink;
274 unsigned iSink = 0;
275
276 int rc2 = RTCritSectEnter(&pMixer->CritSect);
277 if (RT_FAILURE(rc2))
278 return;
279
280 pHlp->pfnPrintf(pHlp, "[Master] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", pMixer->pszName,
281 pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight, pMixer->VolMaster.fMuted);
282
283 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
284 {
285 pHlp->pfnPrintf(pHlp, "[Sink %u] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", iSink, pSink->pszName,
286 pSink->Volume.uLeft, pSink->Volume.uRight, pSink->Volume.fMuted);
287 ++iSink;
288 }
289
290 rc2 = RTCritSectLeave(&pMixer->CritSect);
291 AssertRC(rc2);
292}
293
294/**
295 * Destroys an audio mixer.
296 *
297 * @param pMixer Audio mixer to destroy.
298 * @param pDevIns The device instance the statistics are associated with.
299 */
300void AudioMixerDestroy(PAUDIOMIXER pMixer, PPDMDEVINS pDevIns)
301{
302 if (!pMixer)
303 return;
304
305 int rc2 = RTCritSectEnter(&pMixer->CritSect);
306 AssertRC(rc2);
307
308 LogFlowFunc(("Destroying %s ...\n", pMixer->pszName));
309
310 PAUDMIXSINK pSink, pSinkNext;
311 RTListForEachSafe(&pMixer->lstSinks, pSink, pSinkNext, AUDMIXSINK, Node)
312 {
313 audioMixerSinkDestroyInternal(pSink, pDevIns);
314 audioMixerRemoveSinkInternal(pMixer, pSink);
315 RTMemFree(pSink);
316 }
317
318 Assert(pMixer->cSinks == 0);
319
320 RTStrFree(pMixer->pszName);
321 pMixer->pszName = NULL;
322
323 rc2 = RTCritSectLeave(&pMixer->CritSect);
324 AssertRC(rc2);
325
326 RTCritSectDelete(&pMixer->CritSect);
327
328 RTMemFree(pMixer);
329 pMixer = NULL;
330}
331
332/**
333 * Invalidates all internal data, internal version.
334 *
335 * @returns VBox status code.
336 * @param pMixer Mixer to invalidate data for.
337 */
338int audioMixerInvalidateInternal(PAUDIOMIXER pMixer)
339{
340 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
341
342 LogFlowFunc(("[%s]\n", pMixer->pszName));
343
344 /* Propagate new master volume to all connected sinks. */
345 PAUDMIXSINK pSink;
346 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
347 {
348 int rc2 = audioMixerSinkUpdateVolume(pSink, &pMixer->VolMaster);
349 AssertRC(rc2);
350 }
351
352 return VINF_SUCCESS;
353}
354
355/**
356 * Invalidates all internal data.
357 *
358 * @returns VBox status code.
359 * @param pMixer Mixer to invalidate data for.
360 */
361void AudioMixerInvalidate(PAUDIOMIXER pMixer)
362{
363 AssertPtrReturnVoid(pMixer);
364
365 int rc2 = RTCritSectEnter(&pMixer->CritSect);
366 AssertRC(rc2);
367
368 LogFlowFunc(("[%s]\n", pMixer->pszName));
369
370 rc2 = audioMixerInvalidateInternal(pMixer);
371 AssertRC(rc2);
372
373 rc2 = RTCritSectLeave(&pMixer->CritSect);
374 AssertRC(rc2);
375}
376
377/**
378 * Adds sink to an existing mixer.
379 *
380 * @returns VBox status code.
381 * @param pMixer Mixer to add sink to.
382 * @param pSink Sink to add.
383 */
384static int audioMixerAddSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
385{
386 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
387 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
388
389 /** @todo Check upper sink limit? */
390 /** @todo Check for double-inserted sinks? */
391
392 RTListAppend(&pMixer->lstSinks, &pSink->Node);
393 pMixer->cSinks++;
394
395 LogFlowFunc(("pMixer=%p, pSink=%p, cSinks=%RU8\n",
396 pMixer, pSink, pMixer->cSinks));
397
398 return VINF_SUCCESS;
399}
400
401/**
402 * Removes a formerly attached audio sink for an audio mixer, internal version.
403 *
404 * @returns VBox status code.
405 * @param pMixer Mixer to remove sink from.
406 * @param pSink Sink to remove.
407 */
408static int audioMixerRemoveSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
409{
410 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
411 if (!pSink)
412 return VERR_NOT_FOUND;
413
414 AssertMsgReturn(pSink->pParent == pMixer, ("%s: Is not part of mixer '%s'\n",
415 pSink->pszName, pMixer->pszName), VERR_NOT_FOUND);
416
417 LogFlowFunc(("[%s] pSink=%s, cSinks=%RU8\n",
418 pMixer->pszName, pSink->pszName, pMixer->cSinks));
419
420 /* Remove sink from mixer. */
421 RTListNodeRemove(&pSink->Node);
422
423 Assert(pMixer->cSinks);
424 pMixer->cSinks--;
425
426 /* Set mixer to NULL so that we know we're not part of any mixer anymore. */
427 pSink->pParent = NULL;
428
429 return VINF_SUCCESS;
430}
431
432/**
433 * Removes a formerly attached audio sink for an audio mixer.
434 *
435 * @returns VBox status code.
436 * @param pMixer Mixer to remove sink from.
437 * @param pSink Sink to remove.
438 */
439void AudioMixerRemoveSink(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
440{
441 int rc2 = RTCritSectEnter(&pMixer->CritSect);
442 AssertRC(rc2);
443
444 audioMixerSinkRemoveAllStreamsInternal(pSink);
445 audioMixerRemoveSinkInternal(pMixer, pSink);
446
447 rc2 = RTCritSectLeave(&pMixer->CritSect);
448}
449
450/**
451 * Sets the mixer's master volume.
452 *
453 * @returns VBox status code.
454 * @param pMixer Mixer to set master volume for.
455 * @param pVol Volume to set.
456 */
457int AudioMixerSetMasterVolume(PAUDIOMIXER pMixer, PPDMAUDIOVOLUME pVol)
458{
459 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
460 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
461
462 int rc = RTCritSectEnter(&pMixer->CritSect);
463 if (RT_FAILURE(rc))
464 return rc;
465
466 memcpy(&pMixer->VolMaster, pVol, sizeof(PDMAUDIOVOLUME));
467
468 LogFlowFunc(("[%s] lVol=%RU32, rVol=%RU32 => fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
469 pMixer->pszName, pVol->uLeft, pVol->uRight,
470 pMixer->VolMaster.fMuted, pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight));
471
472 rc = audioMixerInvalidateInternal(pMixer);
473
474 int rc2 = RTCritSectLeave(&pMixer->CritSect);
475 AssertRC(rc2);
476
477 return rc;
478}
479
480/*********************************************************************************************************************************
481 * Mixer Sink implementation.
482 ********************************************************************************************************************************/
483
484/**
485 * Adds an audio stream to a specific audio sink.
486 *
487 * @returns VBox status code.
488 * @param pSink Sink to add audio stream to.
489 * @param pStream Stream to add.
490 */
491int AudioMixerSinkAddStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
492{
493 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
494 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
495
496 int rc = RTCritSectEnter(&pSink->CritSect);
497 if (RT_FAILURE(rc))
498 return rc;
499
500 if (pSink->cStreams == UINT8_MAX) /* 255 streams per sink max. */
501 {
502 int rc2 = RTCritSectLeave(&pSink->CritSect);
503 AssertRC(rc2);
504
505 return VERR_NO_MORE_HANDLES;
506 }
507
508 LogFlowFuncEnter();
509
510 /** @todo Check if stream already is assigned to (another) sink. */
511
512 /* If the sink is running and not in pending disable mode,
513 * make sure that the added stream also is enabled. */
514 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
515 && !(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
516 {
517 rc = audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_ENABLE, AUDMIXSTRMCTL_F_NONE);
518 if (rc == VERR_AUDIO_STREAM_NOT_READY)
519 rc = VINF_SUCCESS; /* Not fatal here, stream can become available at some later point in time. */
520 }
521
522 if (RT_SUCCESS(rc))
523 {
524 /* Apply the sink's combined volume to the stream. */
525 rc = pStream->pConn->pfnStreamSetVolume(pStream->pConn, pStream->pStream, &pSink->VolumeCombined);
526 AssertRC(rc);
527 }
528
529 if (RT_SUCCESS(rc))
530 {
531 /* Save pointer to sink the stream is attached to. */
532 pStream->pSink = pSink;
533
534 /* Append stream to sink's list. */
535 RTListAppend(&pSink->lstStreams, &pStream->Node);
536 pSink->cStreams++;
537 }
538
539 LogFlowFunc(("[%s] cStreams=%RU8, rc=%Rrc\n", pSink->pszName, pSink->cStreams, rc));
540
541 int rc2 = RTCritSectLeave(&pSink->CritSect);
542 AssertRC(rc2);
543
544 return rc;
545}
546
547/**
548 * Creates an audio mixer stream.
549 *
550 * @returns VBox status code.
551 * @param pSink Sink to use for creating the stream.
552 * @param pConn Audio connector interface to use.
553 * @param pCfg Audio stream configuration to use. This may be modified
554 * in some unspecified way (see
555 * PDMIAUDIOCONNECTOR::pfnStreamCreate).
556 * @param fFlags Stream flags. Currently unused, set to 0.
557 * @param pDevIns The device instance to register statistics with.
558 * @param ppStream Pointer which receives the newly created audio stream.
559 */
560int AudioMixerSinkCreateStream(PAUDMIXSINK pSink, PPDMIAUDIOCONNECTOR pConn, PPDMAUDIOSTREAMCFG pCfg,
561 AUDMIXSTREAMFLAGS fFlags, PPDMDEVINS pDevIns, PAUDMIXSTREAM *ppStream)
562{
563 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
564 AssertPtrReturn(pConn, VERR_INVALID_POINTER);
565 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
566 /** @todo Validate fFlags. */
567 /* ppStream is optional. */
568
569 /*
570 * Check status and get the host driver config.
571 */
572 if (pConn->pfnGetStatus(pConn, PDMAUDIODIR_DUPLEX) == PDMAUDIOBACKENDSTS_NOT_ATTACHED)
573 return VERR_AUDIO_BACKEND_NOT_ATTACHED;
574
575 PDMAUDIOBACKENDCFG BackendCfg;
576 int rc = pConn->pfnGetConfig(pConn, &BackendCfg);
577 if (RT_FAILURE(rc))
578 return rc;
579
580 /*
581 * Allocate the instance.
582 */
583 PAUDMIXSTREAM pMixStream = (PAUDMIXSTREAM)RTMemAllocZ(sizeof(AUDMIXSTREAM));
584 AssertReturn(pMixStream, VERR_NO_MEMORY);
585
586 pMixStream->fFlags = fFlags;
587
588 /* Assign the backend's name to the mixer stream's name for easier identification in the (release) log. */
589 pMixStream->pszName = RTStrAPrintf2("[%s] %s", pCfg->szName, BackendCfg.szName);
590 pMixStream->pszStatPrefix = RTStrAPrintf2("Mix-%s/[%s] %s/", pSink->pszName, pCfg->szName, BackendCfg.szName);
591 if (pMixStream->pszName && pMixStream->pszStatPrefix)
592 {
593 rc = RTCritSectInit(&pMixStream->CritSect);
594 if (RT_SUCCESS(rc))
595 {
596 rc = RTCircBufCreate(&pMixStream->pCircBuf, PDMAudioPropsMilliToBytes(&pSink->PCMProps, 100 /*ms*/)); /** @todo Make this configurable. */
597 if (RT_SUCCESS(rc))
598 {
599 pMixStream->StatsCircBufSize = RTCircBufSize(pMixStream->pCircBuf);
600 pMixStream->StatsCircBufUsed = RTCircBufUsed(pMixStream->pCircBuf);
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] fFlags=0x%x (enmDir=%ld, %u bits, %RU8 channels, %RU32Hz)\n", pSink->pszName, fFlags, 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 * Always use the sink's PCM audio format as the host side when creating a stream for it.
616 */
617 AssertMsg(AudioHlpPcmPropsAreValid(&pSink->PCMProps),
618 ("%s: Does not (yet) have a format set when it must\n", pSink->pszName));
619
620 PDMAUDIOSTREAMCFG CfgHost;
621 rc = PDMAudioStrmCfgInitWithProps(&CfgHost, &pSink->PCMProps);
622 AssertRC(rc); /* cannot fail */
623
624 /* Apply the sink's direction for the configuration to use to create the stream. */
625 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
626 {
627 CfgHost.enmDir = PDMAUDIODIR_IN;
628 CfgHost.u.enmSrc = pCfg->u.enmSrc;
629 CfgHost.enmLayout = pCfg->enmLayout;
630 }
631 else
632 {
633 CfgHost.enmDir = PDMAUDIODIR_OUT;
634 CfgHost.u.enmDst = pCfg->u.enmDst;
635 CfgHost.enmLayout = pCfg->enmLayout;
636 }
637
638 RTStrCopy(CfgHost.szName, sizeof(CfgHost.szName), pCfg->szName);
639
640 /*
641 * Create the stream.
642 */
643 PPDMAUDIOSTREAM pStream;
644 rc = pConn->pfnStreamCreate(pConn, &CfgHost, pCfg, &pStream);
645 if (RT_SUCCESS(rc))
646 {
647 /* Save the audio stream pointer to this mixing stream. */
648 pMixStream->pStream = pStream;
649
650 /* Increase the stream's reference count to let others know
651 * we're reyling on it to be around now. */
652 pConn->pfnStreamRetain(pConn, pStream);
653 pMixStream->pConn = pConn;
654
655 RTCritSectLeave(&pSink->CritSect);
656
657 /*
658 * Register statistics before we return.
659 */
660 PDMDevHlpSTAMRegisterF(pDevIns, &pMixStream->StatsCircBufSize, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_BYTES,
661 "Circular buffer size", "%sCircBufSize", pMixStream->pszStatPrefix);
662 PDMDevHlpSTAMRegisterF(pDevIns, &pMixStream->StatsCircBufUsed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_BYTES,
663 "Circular buffer fill size", "%sCircBufUsed", pMixStream->pszStatPrefix);
664
665 if (ppStream)
666 *ppStream = pMixStream;
667 return VINF_SUCCESS;
668 }
669
670 /*
671 * Failed. Tear down the stream.
672 */
673 int rc2 = RTCritSectLeave(&pSink->CritSect);
674 AssertRC(rc2);
675 }
676 RTCircBufDestroy(pMixStream->pCircBuf);
677 }
678 RTCritSectDelete(&pMixStream->CritSect);
679 }
680 }
681 else
682 rc = VERR_NO_STR_MEMORY;
683
684 RTStrFree(pMixStream->pszStatPrefix);
685 pMixStream->pszStatPrefix = NULL;
686 RTStrFree(pMixStream->pszName);
687 pMixStream->pszName = NULL;
688 RTMemFree(pMixStream);
689 return rc;
690}
691
692/**
693 * Static helper function to translate a sink command
694 * to a PDM audio stream command.
695 *
696 * @returns PDM audio stream command, or PDMAUDIOSTREAMCMD_UNKNOWN if not found.
697 * @param enmCmd Mixer sink command to translate.
698 */
699static PDMAUDIOSTREAMCMD audioMixerSinkToStreamCmd(AUDMIXSINKCMD enmCmd)
700{
701 switch (enmCmd)
702 {
703 case AUDMIXSINKCMD_ENABLE: return PDMAUDIOSTREAMCMD_ENABLE;
704 case AUDMIXSINKCMD_DISABLE: return PDMAUDIOSTREAMCMD_DISABLE;
705 case AUDMIXSINKCMD_PAUSE: return PDMAUDIOSTREAMCMD_PAUSE;
706 case AUDMIXSINKCMD_RESUME: return PDMAUDIOSTREAMCMD_RESUME;
707 case AUDMIXSINKCMD_DROP: return PDMAUDIOSTREAMCMD_DROP;
708 default: break;
709 }
710
711 AssertMsgFailed(("Unsupported sink command %d\n", enmCmd));
712 return PDMAUDIOSTREAMCMD_UNKNOWN;
713}
714
715/**
716 * Controls a mixer sink.
717 *
718 * @returns VBox status code.
719 * @param pSink Mixer sink to control.
720 * @param enmSinkCmd Sink command to set.
721 */
722int AudioMixerSinkCtl(PAUDMIXSINK pSink, AUDMIXSINKCMD enmSinkCmd)
723{
724 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
725
726 PDMAUDIOSTREAMCMD enmCmdStream = audioMixerSinkToStreamCmd(enmSinkCmd);
727 if (enmCmdStream == PDMAUDIOSTREAMCMD_UNKNOWN)
728 return VERR_NOT_SUPPORTED;
729
730 int rc = RTCritSectEnter(&pSink->CritSect);
731 if (RT_FAILURE(rc))
732 return rc;
733
734 /* Input sink and no recording source set? Bail out early. */
735 if ( pSink->enmDir == AUDMIXSINKDIR_INPUT
736 && pSink->In.pStreamRecSource == NULL)
737 {
738 int rc2 = RTCritSectLeave(&pSink->CritSect);
739 AssertRC(rc2);
740
741 return rc;
742 }
743
744 PAUDMIXSTREAM pStream;
745 if ( pSink->enmDir == AUDMIXSINKDIR_INPUT
746 && pSink->In.pStreamRecSource) /* Any recording source set? */
747 {
748 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
749 {
750 if (pStream == pSink->In.pStreamRecSource)
751 {
752 int rc2 = audioMixerStreamCtlInternal(pStream, enmCmdStream, AUDMIXSTRMCTL_F_NONE);
753 if (rc2 == VERR_NOT_SUPPORTED)
754 rc2 = VINF_SUCCESS;
755
756 if (RT_SUCCESS(rc))
757 rc = rc2;
758 /* Keep going. Flag? */
759 }
760 }
761 }
762 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
763 {
764 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
765 {
766 int rc2 = audioMixerStreamCtlInternal(pStream, enmCmdStream, AUDMIXSTRMCTL_F_NONE);
767 if (rc2 == VERR_NOT_SUPPORTED)
768 rc2 = VINF_SUCCESS;
769
770 if (RT_SUCCESS(rc))
771 rc = rc2;
772 /* Keep going. Flag? */
773 }
774 }
775
776 switch (enmSinkCmd)
777 {
778 case AUDMIXSINKCMD_ENABLE:
779 {
780 /* Make sure to clear any other former flags again by assigning AUDMIXSINK_STS_RUNNING directly. */
781 pSink->fStatus = AUDMIXSINK_STS_RUNNING;
782 break;
783 }
784
785 case AUDMIXSINKCMD_DISABLE:
786 {
787 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
788 {
789 /* Set the sink in a pending disable state first.
790 * The final status (disabled) will be set in the sink's iteration. */
791 pSink->fStatus |= AUDMIXSINK_STS_PENDING_DISABLE;
792 }
793 break;
794 }
795
796 case AUDMIXSINKCMD_DROP:
797 {
798 AudioMixBufReset(&pSink->MixBuf);
799
800 /* Clear dirty bit, keep others. */
801 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
802 break;
803 }
804
805 default:
806 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
807 break;
808 }
809
810#if defined(RTLOG_REL_ENABLED) || defined(LOG_ENABLED)
811 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
812#endif
813 LogRel2(("Audio Mixer: Set new status of sink '%s': %s (enmCmd=%RU32 rc=%Rrc)\n",
814 pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus), enmSinkCmd, rc));
815
816 int rc2 = RTCritSectLeave(&pSink->CritSect);
817 AssertRC(rc2);
818
819 return rc;
820}
821
822/**
823 * Initializes a sink.
824 *
825 * @returns VBox status code.
826 * @param pSink Sink to initialize.
827 * @param pMixer Mixer the sink is assigned to.
828 * @param pcszName Name of the sink.
829 * @param enmDir Direction of the sink.
830 */
831static int audioMixerSinkInit(PAUDMIXSINK pSink, PAUDIOMIXER pMixer, const char *pcszName, AUDMIXSINKDIR enmDir)
832{
833 pSink->pszName = RTStrDup(pcszName);
834 if (!pSink->pszName)
835 return VERR_NO_MEMORY;
836
837 int rc = RTCritSectInit(&pSink->CritSect);
838 if (RT_SUCCESS(rc))
839 {
840 pSink->pParent = pMixer;
841 pSink->enmDir = enmDir;
842
843 RTListInit(&pSink->lstStreams);
844
845 /* Set initial volume to max. */
846 pSink->Volume.fMuted = false;
847 pSink->Volume.uLeft = PDMAUDIO_VOLUME_MAX;
848 pSink->Volume.uRight = PDMAUDIO_VOLUME_MAX;
849
850 /* Ditto for the combined volume. */
851 pSink->VolumeCombined.fMuted = false;
852 pSink->VolumeCombined.uLeft = PDMAUDIO_VOLUME_MAX;
853 pSink->VolumeCombined.uRight = PDMAUDIO_VOLUME_MAX;
854
855 const size_t cbScratchBuf = _1K; /** @todo Make this configurable? */
856
857 pSink->pabScratchBuf = (uint8_t *)RTMemAlloc(cbScratchBuf);
858 AssertPtrReturn(pSink->pabScratchBuf, VERR_NO_MEMORY);
859 pSink->cbScratchBuf = cbScratchBuf;
860 }
861
862 LogFlowFuncLeaveRC(rc);
863 return rc;
864}
865
866/**
867 * Destroys a mixer sink and removes it from the attached mixer (if any).
868 *
869 * @param pSink Mixer sink to destroy.
870 * @param pDevIns The device instance that statistics are registered with.
871 */
872void AudioMixerSinkDestroy(PAUDMIXSINK pSink, PPDMDEVINS pDevIns)
873{
874 if (!pSink)
875 return;
876
877 int rc2 = RTCritSectEnter(&pSink->CritSect);
878 AssertRC(rc2);
879
880 if (pSink->pParent)
881 {
882 /* Save mixer pointer, as after audioMixerRemoveSinkInternal() the
883 * pointer will be gone from the stream. */
884 PAUDIOMIXER pMixer = pSink->pParent;
885 AssertPtr(pMixer);
886
887 audioMixerRemoveSinkInternal(pMixer, pSink);
888 }
889
890 rc2 = RTCritSectLeave(&pSink->CritSect);
891 AssertRC(rc2);
892
893 audioMixerSinkDestroyInternal(pSink, pDevIns);
894
895 RTMemFree(pSink);
896 pSink = NULL;
897}
898
899/**
900 * Destroys a mixer sink.
901 *
902 * @param pSink Mixer sink to destroy.
903 * @param pDevIns The device instance statistics are registered with.
904 */
905static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink, PPDMDEVINS pDevIns)
906{
907 AssertPtrReturnVoid(pSink);
908
909 LogFunc(("%s\n", pSink->pszName));
910
911 PAUDMIXSTREAM pStream, pStreamNext;
912 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
913 {
914 audioMixerSinkRemoveStreamInternal(pSink, pStream);
915 audioMixerStreamDestroyInternal(pStream, pDevIns);
916 }
917
918 if ( pSink->pParent
919 && pSink->pParent->fFlags & AUDMIXER_FLAGS_DEBUG)
920 {
921 AudioHlpFileDestroy(pSink->Dbg.pFile);
922 pSink->Dbg.pFile = NULL;
923 }
924
925 RTStrFree(pSink->pszName);
926 pSink->pszName = NULL;
927
928 RTMemFree(pSink->pabScratchBuf);
929 pSink->pabScratchBuf = NULL;
930 pSink->cbScratchBuf = 0;
931
932 AudioMixBufDestroy(&pSink->MixBuf);
933 RTCritSectDelete(&pSink->CritSect);
934}
935
936/**
937 * Returns the amount of bytes ready to be read from a sink since the last call
938 * to AudioMixerSinkUpdate().
939 *
940 * @returns Amount of bytes ready to be read from the sink.
941 * @param pSink Sink to return number of available bytes for.
942 */
943uint32_t AudioMixerSinkGetReadable(PAUDMIXSINK pSink)
944{
945 AssertPtrReturn(pSink, 0);
946
947 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT, ("%s: Can't read from a non-input sink\n", pSink->pszName));
948
949 int rc = RTCritSectEnter(&pSink->CritSect);
950 if (RT_FAILURE(rc))
951 return 0;
952
953 uint32_t cbReadable = 0;
954
955 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
956 {
957#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF_IN
958# error "Implement me!"
959#else
960 PAUDMIXSTREAM pStreamRecSource = pSink->In.pStreamRecSource;
961 if (!pStreamRecSource)
962 {
963 Log3Func(("[%s] No recording source specified, skipping ...\n", pSink->pszName));
964 }
965 else
966 {
967 AssertPtr(pStreamRecSource->pConn);
968 cbReadable = pStreamRecSource->pConn->pfnStreamGetReadable(pStreamRecSource->pConn, pStreamRecSource->pStream);
969 }
970#endif
971 }
972
973 Log3Func(("[%s] cbReadable=%RU32\n", pSink->pszName, cbReadable));
974
975 int rc2 = RTCritSectLeave(&pSink->CritSect);
976 AssertRC(rc2);
977
978 return cbReadable;
979}
980
981/**
982 * Returns the sink's current recording source.
983 *
984 * @return Mixer stream which currently is set as current recording source, NULL if none is set.
985 * @param pSink Audio mixer sink to return current recording source for.
986 */
987PAUDMIXSTREAM AudioMixerSinkGetRecordingSource(PAUDMIXSINK pSink)
988{
989 int rc = RTCritSectEnter(&pSink->CritSect);
990 if (RT_FAILURE(rc))
991 return NULL;
992
993 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT, ("Specified sink is not an input sink\n"));
994
995 PAUDMIXSTREAM pStream = pSink->In.pStreamRecSource;
996
997 int rc2 = RTCritSectLeave(&pSink->CritSect);
998 AssertRC(rc2);
999
1000 return pStream;
1001}
1002
1003/**
1004 * Returns the amount of bytes ready to be written to a sink since the last call
1005 * to AudioMixerSinkUpdate().
1006 *
1007 * @returns Amount of bytes ready to be written to the sink.
1008 * @param pSink Sink to return number of available bytes for.
1009 */
1010uint32_t AudioMixerSinkGetWritable(PAUDMIXSINK pSink)
1011{
1012 AssertPtrReturn(pSink, 0);
1013
1014 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT, ("%s: Can't write to a non-output sink\n", pSink->pszName));
1015
1016 int rc = RTCritSectEnter(&pSink->CritSect);
1017 if (RT_FAILURE(rc))
1018 return 0;
1019
1020 uint32_t cbWritable = 0;
1021
1022 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
1023 && !(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
1024 {
1025 cbWritable = AudioMixBufFreeBytes(&pSink->MixBuf);
1026 }
1027
1028 Log3Func(("[%s] cbWritable=%RU32 (%RU64ms)\n",
1029 pSink->pszName, cbWritable, PDMAudioPropsBytesToMilli(&pSink->PCMProps, cbWritable)));
1030
1031 int rc2 = RTCritSectLeave(&pSink->CritSect);
1032 AssertRC(rc2);
1033
1034 return cbWritable;
1035}
1036
1037/**
1038 * Returns the sink's mixing direction.
1039 *
1040 * @returns Mixing direction.
1041 * @param pSink Sink to return direction for.
1042 */
1043AUDMIXSINKDIR AudioMixerSinkGetDir(PAUDMIXSINK pSink)
1044{
1045 AssertPtrReturn(pSink, AUDMIXSINKDIR_UNKNOWN);
1046
1047 int rc = RTCritSectEnter(&pSink->CritSect);
1048 if (RT_FAILURE(rc))
1049 return AUDMIXSINKDIR_UNKNOWN;
1050
1051 const AUDMIXSINKDIR enmDir = pSink->enmDir;
1052
1053 int rc2 = RTCritSectLeave(&pSink->CritSect);
1054 AssertRC(rc2);
1055
1056 return enmDir;
1057}
1058
1059/**
1060 * Returns the sink's (friendly) name.
1061 *
1062 * @returns The sink's (friendly) name.
1063 */
1064const char *AudioMixerSinkGetName(const PAUDMIXSINK pSink)
1065{
1066 AssertPtrReturn(pSink, "<Unknown>");
1067
1068 return pSink->pszName;
1069}
1070
1071/**
1072 * Returns a specific mixer stream from a sink, based on its index.
1073 *
1074 * @returns Mixer stream if found, or NULL if not found.
1075 * @param pSink Sink to retrieve mixer stream from.
1076 * @param uIndex Index of the mixer stream to return.
1077 */
1078PAUDMIXSTREAM AudioMixerSinkGetStream(PAUDMIXSINK pSink, uint8_t uIndex)
1079{
1080 AssertPtrReturn(pSink, NULL);
1081
1082 int rc = RTCritSectEnter(&pSink->CritSect);
1083 if (RT_FAILURE(rc))
1084 return NULL;
1085
1086 AssertMsgReturn(uIndex < pSink->cStreams,
1087 ("Index %RU8 exceeds stream count (%RU8)", uIndex, pSink->cStreams), NULL);
1088
1089 /* Slow lookup, d'oh. */
1090 PAUDMIXSTREAM pStream = RTListGetFirst(&pSink->lstStreams, AUDMIXSTREAM, Node);
1091 while (uIndex)
1092 {
1093 pStream = RTListGetNext(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node);
1094 uIndex--;
1095 }
1096
1097 /** @todo Do we need to raise the stream's reference count here? */
1098
1099 int rc2 = RTCritSectLeave(&pSink->CritSect);
1100 AssertRC(rc2);
1101
1102 AssertPtr(pStream);
1103 return pStream;
1104}
1105
1106/**
1107 * Returns the current status of a mixer sink.
1108 *
1109 * @returns The sink's current status.
1110 * @param pSink Mixer sink to return status for.
1111 */
1112AUDMIXSINKSTS AudioMixerSinkGetStatus(PAUDMIXSINK pSink)
1113{
1114 if (!pSink)
1115 return AUDMIXSINK_STS_NONE;
1116
1117 int rc2 = RTCritSectEnter(&pSink->CritSect);
1118 if (RT_FAILURE(rc2))
1119 return AUDMIXSINK_STS_NONE;
1120
1121 /* If the dirty flag is set, there is unprocessed data in the sink. */
1122 AUDMIXSINKSTS stsSink = pSink->fStatus;
1123
1124 rc2 = RTCritSectLeave(&pSink->CritSect);
1125 AssertRC(rc2);
1126
1127 return stsSink;
1128}
1129
1130/**
1131 * Returns the number of attached mixer streams to a mixer sink.
1132 *
1133 * @returns The number of attached mixer streams.
1134 * @param pSink Mixer sink to return number for.
1135 */
1136uint8_t AudioMixerSinkGetStreamCount(PAUDMIXSINK pSink)
1137{
1138 if (!pSink)
1139 return 0;
1140
1141 int rc2 = RTCritSectEnter(&pSink->CritSect);
1142 if (RT_FAILURE(rc2))
1143 return 0;
1144
1145 const uint8_t cStreams = pSink->cStreams;
1146
1147 rc2 = RTCritSectLeave(&pSink->CritSect);
1148 AssertRC(rc2);
1149
1150 return cStreams;
1151}
1152
1153/**
1154 * Returns whether the sink is in an active state or not.
1155 * Note: The pending disable state also counts as active.
1156 *
1157 * @returns True if active, false if not.
1158 * @param pSink Sink to return active state for.
1159 */
1160bool AudioMixerSinkIsActive(PAUDMIXSINK pSink)
1161{
1162 if (!pSink)
1163 return false;
1164
1165 int rc2 = RTCritSectEnter(&pSink->CritSect);
1166 if (RT_FAILURE(rc2))
1167 return false;
1168
1169 const bool fIsActive = pSink->fStatus & AUDMIXSINK_STS_RUNNING;
1170 /* Note: AUDMIXSINK_STS_PENDING_DISABLE implies AUDMIXSINK_STS_RUNNING. */
1171
1172 Log3Func(("[%s] fActive=%RTbool\n", pSink->pszName, fIsActive));
1173
1174 rc2 = RTCritSectLeave(&pSink->CritSect);
1175 AssertRC(rc2);
1176
1177 return fIsActive;
1178}
1179
1180/**
1181 * Reads audio data from a mixer sink.
1182 *
1183 * @returns VBox status code.
1184 * @param pSink Mixer sink to read data from.
1185 * @param enmOp Mixer operation to use for reading the data.
1186 * @param pvBuf Buffer where to store the read data.
1187 * @param cbBuf Buffer size (in bytes) where to store the data.
1188 * @param pcbRead Number of bytes read. Optional.
1189 */
1190int AudioMixerSinkRead(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1191{
1192 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1193 RT_NOREF(enmOp);
1194 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1195 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1196 /* pcbRead is optional. */
1197
1198 /** @todo Handle mixing operation enmOp! */
1199
1200 int rc = RTCritSectEnter(&pSink->CritSect);
1201 if (RT_FAILURE(rc))
1202 return rc;
1203
1204 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT,
1205 ("Can't read from a sink which is not an input sink\n"));
1206
1207 uint32_t cbRead = 0;
1208
1209 /* Flag indicating whether this sink is in a 'clean' state,
1210 * e.g. there is no more data to read from. */
1211 bool fClean = true;
1212
1213 PAUDMIXSTREAM pStreamRecSource = pSink->In.pStreamRecSource;
1214 if (!pStreamRecSource)
1215 {
1216 Log3Func(("[%s] No recording source specified, skipping ...\n", pSink->pszName));
1217 }
1218 else if (!(pStreamRecSource->fStatus & AUDMIXSTREAM_STATUS_ENABLED))
1219 {
1220 Log3Func(("[%s] Stream '%s' disabled, skipping ...\n", pSink->pszName, pStreamRecSource->pszName));
1221 }
1222 else
1223 {
1224 uint32_t cbToRead = cbBuf;
1225 while (cbToRead)
1226 {
1227 uint32_t cbReadStrm;
1228 AssertPtr(pStreamRecSource->pConn);
1229#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF_IN
1230# error "Implement me!"
1231#else
1232 rc = pStreamRecSource->pConn->pfnStreamRead(pStreamRecSource->pConn, pStreamRecSource->pStream,
1233 (uint8_t *)pvBuf + cbRead, cbToRead, &cbReadStrm);
1234#endif
1235 if (RT_FAILURE(rc))
1236 LogFunc(("[%s] Failed reading from stream '%s': %Rrc\n", pSink->pszName, pStreamRecSource->pszName, rc));
1237
1238 Log3Func(("[%s] Stream '%s': Read %RU32 bytes\n", pSink->pszName, pStreamRecSource->pszName, cbReadStrm));
1239
1240 if ( RT_FAILURE(rc)
1241 || !cbReadStrm)
1242 break;
1243
1244 AssertBreakStmt(cbReadStrm <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
1245 cbToRead -= cbReadStrm;
1246 cbRead += cbReadStrm;
1247 Assert(cbRead <= cbBuf);
1248 }
1249
1250 uint32_t cbReadable = pStreamRecSource->pConn->pfnStreamGetReadable(pStreamRecSource->pConn, pStreamRecSource->pStream);
1251
1252 /* Still some data available? Then sink is not clean (yet). */
1253 if (cbReadable)
1254 fClean = false;
1255
1256 if (RT_SUCCESS(rc))
1257 {
1258 if (fClean)
1259 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1260
1261 /* Update our last read time stamp. */
1262 pSink->tsLastReadWrittenNs = RTTimeNanoTS();
1263
1264 if (pSink->pParent->fFlags & AUDMIXER_FLAGS_DEBUG)
1265 {
1266 int rc2 = AudioHlpFileWrite(pSink->Dbg.pFile, pvBuf, cbRead, 0 /* fFlags */);
1267 AssertRC(rc2);
1268 }
1269 }
1270 }
1271
1272#ifdef LOG_ENABLED
1273 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
1274#endif
1275 Log2Func(("[%s] cbRead=%RU32, fClean=%RTbool, fStatus=%s, rc=%Rrc\n",
1276 pSink->pszName, cbRead, fClean, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus), rc));
1277
1278 if (pcbRead)
1279 *pcbRead = cbRead;
1280
1281 int rc2 = RTCritSectLeave(&pSink->CritSect);
1282 AssertRC(rc2);
1283
1284 return rc;
1285}
1286
1287/**
1288 * Removes a mixer stream from a mixer sink, internal version.
1289 *
1290 * @returns VBox status code.
1291 * @param pSink Sink to remove mixer stream from.
1292 * @param pStream Stream to remove.
1293 */
1294static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1295{
1296 AssertPtrReturn(pSink, VERR_INVALID_PARAMETER);
1297 if ( !pStream
1298 || !pStream->pSink) /* Not part of a sink anymore? */
1299 {
1300 return VERR_NOT_FOUND;
1301 }
1302
1303 AssertMsgReturn(pStream->pSink == pSink, ("Stream '%s' is not part of sink '%s'\n",
1304 pStream->pszName, pSink->pszName), VERR_NOT_FOUND);
1305
1306 LogFlowFunc(("[%s] (Stream = %s), cStreams=%RU8\n",
1307 pSink->pszName, pStream->pStream->szName, pSink->cStreams));
1308
1309 /* Remove stream from sink. */
1310 RTListNodeRemove(&pStream->Node);
1311
1312 int rc = VINF_SUCCESS;
1313
1314 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1315 {
1316 /* Make sure to also un-set the recording source if this stream was set
1317 * as the recording source before. */
1318 if (pStream == pSink->In.pStreamRecSource)
1319 rc = audioMixerSinkSetRecSourceInternal(pSink, NULL);
1320 }
1321
1322 /* Set sink to NULL so that we know we're not part of any sink anymore. */
1323 pStream->pSink = NULL;
1324
1325 return rc;
1326}
1327
1328/**
1329 * Removes a mixer stream from a mixer sink.
1330 *
1331 * @param pSink Sink to remove mixer stream from.
1332 * @param pStream Stream to remove.
1333 */
1334void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1335{
1336 int rc2 = RTCritSectEnter(&pSink->CritSect);
1337 AssertRC(rc2);
1338
1339 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pStream);
1340 if (RT_SUCCESS(rc2))
1341 {
1342 Assert(pSink->cStreams);
1343 pSink->cStreams--;
1344 }
1345
1346 rc2 = RTCritSectLeave(&pSink->CritSect);
1347 AssertRC(rc2);
1348}
1349
1350/**
1351 * Removes all attached streams from a given sink.
1352 *
1353 * @param pSink Sink to remove attached streams from.
1354 */
1355static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink)
1356{
1357 if (!pSink)
1358 return;
1359
1360 LogFunc(("%s\n", pSink->pszName));
1361
1362 PAUDMIXSTREAM pStream, pStreamNext;
1363 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
1364 audioMixerSinkRemoveStreamInternal(pSink, pStream);
1365}
1366
1367/**
1368 * Resets the sink's state.
1369 *
1370 * @param pSink Sink to reset.
1371 */
1372static void audioMixerSinkReset(PAUDMIXSINK pSink)
1373{
1374 if (!pSink)
1375 return;
1376
1377 LogFunc(("[%s]\n", pSink->pszName));
1378
1379 AudioMixBufReset(&pSink->MixBuf);
1380
1381 /* Update last updated timestamp. */
1382 pSink->tsLastUpdatedMs = 0;
1383
1384 /* Reset status. */
1385 pSink->fStatus = AUDMIXSINK_STS_NONE;
1386}
1387
1388/**
1389 * Removes all attached streams from a given sink.
1390 *
1391 * @param pSink Sink to remove attached streams from.
1392 */
1393void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
1394{
1395 if (!pSink)
1396 return;
1397
1398 int rc2 = RTCritSectEnter(&pSink->CritSect);
1399 AssertRC(rc2);
1400
1401 audioMixerSinkRemoveAllStreamsInternal(pSink);
1402
1403 pSink->cStreams = 0;
1404
1405 rc2 = RTCritSectLeave(&pSink->CritSect);
1406 AssertRC(rc2);
1407}
1408
1409/**
1410 * Resets a sink. This will immediately stop all processing.
1411 *
1412 * @param pSink Sink to reset.
1413 */
1414void AudioMixerSinkReset(PAUDMIXSINK pSink)
1415{
1416 if (!pSink)
1417 return;
1418
1419 int rc2 = RTCritSectEnter(&pSink->CritSect);
1420 AssertRC(rc2);
1421
1422 LogFlowFunc(("[%s]\n", pSink->pszName));
1423
1424 audioMixerSinkReset(pSink);
1425
1426 rc2 = RTCritSectLeave(&pSink->CritSect);
1427 AssertRC(rc2);
1428}
1429
1430/**
1431 * Returns the audio format of a mixer sink.
1432 *
1433 * @param pSink Sink to retrieve audio format for.
1434 * @param pPCMProps Where to the returned audio format.
1435 */
1436void AudioMixerSinkGetFormat(PAUDMIXSINK pSink, PPDMAUDIOPCMPROPS pPCMProps)
1437{
1438 AssertPtrReturnVoid(pSink);
1439 AssertPtrReturnVoid(pPCMProps);
1440
1441 int rc2 = RTCritSectEnter(&pSink->CritSect);
1442 if (RT_FAILURE(rc2))
1443 return;
1444
1445 memcpy(pPCMProps, &pSink->PCMProps, sizeof(PDMAUDIOPCMPROPS));
1446
1447 rc2 = RTCritSectLeave(&pSink->CritSect);
1448 AssertRC(rc2);
1449}
1450
1451/**
1452 * Sets the audio format of a mixer sink.
1453 *
1454 * @returns VBox status code.
1455 * @param pSink Sink to set audio format for.
1456 * @param pPCMProps Audio format (PCM properties) to set.
1457 */
1458int AudioMixerSinkSetFormat(PAUDMIXSINK pSink, PPDMAUDIOPCMPROPS pPCMProps)
1459{
1460 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1461 AssertPtrReturn(pPCMProps, VERR_INVALID_POINTER);
1462 AssertReturn(AudioHlpPcmPropsAreValid(pPCMProps), VERR_INVALID_PARAMETER);
1463
1464 int rc = RTCritSectEnter(&pSink->CritSect);
1465 if (RT_FAILURE(rc))
1466 return rc;
1467
1468 if (PDMAudioPropsAreEqual(&pSink->PCMProps, pPCMProps)) /* Bail out early if PCM properties are equal. */
1469 {
1470 rc = RTCritSectLeave(&pSink->CritSect);
1471 AssertRC(rc);
1472
1473 return rc;
1474 }
1475
1476 if (pSink->PCMProps.uHz)
1477 LogFlowFunc(("[%s] Old format: %u bit, %RU8 channels, %RU32Hz\n", pSink->pszName,
1478 PDMAudioPropsSampleBits(&pSink->PCMProps), PDMAudioPropsChannels(&pSink->PCMProps), pSink->PCMProps.uHz));
1479
1480 memcpy(&pSink->PCMProps, pPCMProps, sizeof(PDMAUDIOPCMPROPS));
1481
1482 LogFlowFunc(("[%s] New format %u bit, %RU8 channels, %RU32Hz\n", pSink->pszName, PDMAudioPropsSampleBits(&pSink->PCMProps),
1483 PDMAudioPropsChannels(&pSink->PCMProps), pSink->PCMProps.uHz));
1484
1485 /* Also update the sink's mixing buffer format. */
1486 AudioMixBufDestroy(&pSink->MixBuf);
1487 rc = AudioMixBufInit(&pSink->MixBuf, pSink->pszName, &pSink->PCMProps,
1488 PDMAudioPropsMilliToFrames(&pSink->PCMProps, 100 /*ms*/)); /** @todo Make this configurable? */
1489 if (RT_SUCCESS(rc))
1490 {
1491 PAUDMIXSTREAM pStream;
1492 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
1493 {
1494 /** @todo Invalidate mix buffers! */
1495 }
1496 }
1497
1498 if ( RT_SUCCESS(rc)
1499 && (pSink->pParent->fFlags & AUDMIXER_FLAGS_DEBUG))
1500 {
1501 AudioHlpFileClose(pSink->Dbg.pFile);
1502
1503 char szName[64];
1504 RTStrPrintf(szName, sizeof(szName), "MixerSink-%s", pSink->pszName);
1505
1506 char szFile[RTPATH_MAX];
1507 int rc2 = AudioHlpFileNameGet(szFile, RT_ELEMENTS(szFile), NULL /* Use temporary directory */, szName,
1508 0 /* Instance */, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAGS_NONE);
1509 if (RT_SUCCESS(rc2))
1510 {
1511 rc2 = AudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAGS_NONE, &pSink->Dbg.pFile);
1512 if (RT_SUCCESS(rc2))
1513 rc2 = AudioHlpFileOpen(pSink->Dbg.pFile, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS, &pSink->PCMProps);
1514 }
1515 }
1516
1517 int rc2 = RTCritSectLeave(&pSink->CritSect);
1518 AssertRC(rc2);
1519
1520 LogFlowFuncLeaveRC(rc);
1521 return rc;
1522}
1523
1524/**
1525 * Set the current recording source of an input mixer sink, internal version.
1526 *
1527 * @returns VBox status code.
1528 * @param pSink Input mixer sink to set recording source for.
1529 * @param pStream Mixer stream to set as current recording source. Must be an input stream.
1530 * Specify NULL to un-set the current recording source.
1531 */
1532static int audioMixerSinkSetRecSourceInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1533{
1534 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT, ("Specified sink is not an input sink\n"));
1535
1536 int rc;
1537
1538 /*
1539 * Warning: Do *not* use pfnConn->pfnEnable() for enabling/disabling streams here, as this will unconditionally (re-)enable
1540 * streams, which would violate / run against the (global) VM settings. See @bugref{9882}.
1541 */
1542
1543 /* Get pointers of current recording source to make code easier to read below. */
1544 PAUDMIXSTREAM pCurRecSrc = pSink->In.pStreamRecSource; /* Can be NULL. */
1545 PPDMIAUDIOCONNECTOR pCurRecSrcConn = NULL;
1546 PPDMAUDIOSTREAM pCurRecSrcStream = NULL;
1547
1548 if (pCurRecSrc) /* First, disable old recording source, if any is set. */
1549 {
1550 pCurRecSrcConn = pSink->In.pStreamRecSource->pConn;
1551 AssertPtrReturn(pCurRecSrcConn, VERR_INVALID_POINTER);
1552 pCurRecSrcStream = pCurRecSrc->pStream;
1553 AssertPtrReturn(pCurRecSrcStream, VERR_INVALID_POINTER);
1554
1555 rc = pCurRecSrcConn->pfnStreamControl(pCurRecSrcConn, pCurRecSrcStream, PDMAUDIOSTREAMCMD_DISABLE);
1556 }
1557 else
1558 rc = VINF_SUCCESS;
1559
1560 if (RT_SUCCESS(rc))
1561 {
1562 if (pStream)
1563 {
1564 AssertPtr(pStream->pStream);
1565 AssertMsg(pStream->pStream->enmDir == PDMAUDIODIR_IN, ("Specified stream is not an input stream\n"));
1566 AssertPtr(pStream->pConn);
1567 rc = pStream->pConn->pfnStreamControl(pStream->pConn, pStream->pStream, PDMAUDIOSTREAMCMD_ENABLE);
1568 if (RT_SUCCESS(rc))
1569 {
1570 pCurRecSrc = pStream;
1571 }
1572 else if (pCurRecSrc) /* Stay with the current recording source (if any) and re-enable it. */
1573 {
1574 rc = pCurRecSrcConn->pfnStreamControl(pCurRecSrcConn, pCurRecSrcStream, PDMAUDIOSTREAMCMD_ENABLE);
1575 }
1576 }
1577 else
1578 pCurRecSrc = NULL; /* Unsetting, see audioMixerSinkRemoveStreamInternal. */
1579 }
1580
1581 /* Invalidate pointers. */
1582 pSink->In.pStreamRecSource = pCurRecSrc;
1583
1584 LogFunc(("[%s] Recording source is now '%s', rc=%Rrc\n",
1585 pSink->pszName, pSink->In.pStreamRecSource ? pSink->In.pStreamRecSource->pszName : "<None>", rc));
1586
1587 if (RT_SUCCESS(rc))
1588 LogRel(("Audio Mixer: Setting recording source of sink '%s' to '%s'\n",
1589 pSink->pszName, pSink->In.pStreamRecSource ? pSink->In.pStreamRecSource->pszName : "<None>"));
1590 else if (rc != VERR_AUDIO_STREAM_NOT_READY)
1591 LogRel(("Audio Mixer: Setting recording source of sink '%s' to '%s' failed with %Rrc\n",
1592 pSink->pszName, pSink->In.pStreamRecSource ? pSink->In.pStreamRecSource->pszName : "<None>", rc));
1593
1594 return rc;
1595}
1596
1597/**
1598 * Set the current recording source of an input mixer sink.
1599 *
1600 * @returns VBox status code.
1601 * @param pSink Input mixer sink to set recording source for.
1602 * @param pStream Mixer stream to set as current recording source. Must be an input stream.
1603 * Set to NULL to un-set the current recording source.
1604 */
1605int AudioMixerSinkSetRecordingSource(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1606{
1607 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1608
1609 int rc = RTCritSectEnter(&pSink->CritSect);
1610 if (RT_FAILURE(rc))
1611 return rc;
1612
1613 rc = audioMixerSinkSetRecSourceInternal(pSink, pStream);
1614
1615 int rc2 = RTCritSectLeave(&pSink->CritSect);
1616 AssertRC(rc2);
1617
1618 return rc;
1619}
1620
1621/**
1622 * Sets the volume of an individual sink.
1623 *
1624 * @returns VBox status code.
1625 * @param pSink Sink to set volume for.
1626 * @param pVol Volume to set.
1627 */
1628int AudioMixerSinkSetVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
1629{
1630 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1631 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
1632
1633 int rc = RTCritSectEnter(&pSink->CritSect);
1634 if (RT_FAILURE(rc))
1635 return rc;
1636
1637 memcpy(&pSink->Volume, pVol, sizeof(PDMAUDIOVOLUME));
1638
1639 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU8, rVol=%RU8\n",
1640 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1641
1642 LogRel2(("Audio Mixer: Setting volume of sink '%s' to %RU8/%RU8 (%s)\n",
1643 pSink->pszName, pVol->uLeft, pVol->uRight, pVol->fMuted ? "Muted" : "Unmuted"));
1644
1645 AssertPtr(pSink->pParent);
1646 rc = audioMixerSinkUpdateVolume(pSink, &pSink->pParent->VolMaster);
1647
1648 int rc2 = RTCritSectLeave(&pSink->CritSect);
1649 AssertRC(rc2);
1650
1651 return rc;
1652}
1653
1654/**
1655 * Updates a mixer sink, internal version.
1656 *
1657 * @returns VBox status code.
1658 * @param pSink Mixer sink to update.
1659 */
1660static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink)
1661{
1662 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1663
1664#ifdef LOG_ENABLED
1665 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
1666#endif
1667 Log3Func(("[%s] fStatus=%s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
1668
1669 /* Sink disabled? Take a shortcut. */
1670 if (!(pSink->fStatus & AUDMIXSINK_STS_RUNNING))
1671 return VINF_SUCCESS;
1672
1673 /* Input sink and no recording source set? Bail out early. */
1674 if ( pSink->enmDir == AUDMIXSINKDIR_INPUT
1675 && pSink->In.pStreamRecSource == NULL)
1676 return VINF_SUCCESS;
1677
1678 /* Sanity. */
1679 AssertPtr(pSink->pabScratchBuf);
1680 Assert(pSink->cbScratchBuf);
1681
1682 /* Update each mixing sink stream's status. */
1683 PAUDMIXSTREAM pMixStream;
1684 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1685 {
1686 int rc2 = audioMixerStreamUpdateStatus(pMixStream);
1687 AssertRC(rc2);
1688 }
1689
1690 /* Number of disabled streams of this sink. */
1691 uint8_t cStreamsDisabled = pSink->cStreams;
1692
1693 /* Next, try to write (multiplex) as much audio data as possible to all connected mixer streams. */
1694 uint32_t cbToWriteToStreams = AudioMixBufUsedBytes(&pSink->MixBuf);
1695
1696 int rc = VINF_SUCCESS;
1697 while (cbToWriteToStreams)
1698 {
1699 uint32_t cfChunk;
1700 rc = AudioMixBufAcquireReadBlock(&pSink->MixBuf, pSink->pabScratchBuf,
1701 RT_MIN(cbToWriteToStreams, (uint32_t)pSink->cbScratchBuf), &cfChunk);
1702 if (RT_FAILURE(rc))
1703 break;
1704
1705 const uint32_t cbChunk = PDMAudioPropsFramesToBytes(&pSink->PCMProps, cfChunk);
1706 Assert(cbChunk <= pSink->cbScratchBuf);
1707
1708 /* Multiplex the current chunk in a synchronized fashion to all connected streams. */
1709 uint32_t cbChunkWrittenMin = 0;
1710 rc = audioMixerSinkMultiplexSync(pSink, pSink->pabScratchBuf, cbChunk, &cbChunkWrittenMin);
1711 if (RT_SUCCESS(rc))
1712 {
1713 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1714 {
1715 int rc2 = audioMixerSinkWriteToStream(pSink, pMixStream);
1716 AssertRC(rc2);
1717 }
1718 }
1719
1720 Log3Func(("[%s] cbChunk=%RU32, cbChunkWrittenMin=%RU32\n", pSink->pszName, cbChunk, cbChunkWrittenMin));
1721
1722 AudioMixBufReleaseReadBlock(&pSink->MixBuf, AUDIOMIXBUF_B2F(&pSink->MixBuf, cbChunkWrittenMin));
1723
1724 if ( RT_FAILURE(rc)
1725 || cbChunkWrittenMin == 0)
1726 break;
1727
1728 Assert(cbToWriteToStreams >= cbChunkWrittenMin);
1729 cbToWriteToStreams -= cbChunkWrittenMin;
1730 }
1731
1732 if ( !(pSink->fStatus & AUDMIXSINK_STS_DIRTY)
1733 && AudioMixBufUsed(&pSink->MixBuf)) /* Still audio output data left? Consider the sink as being "dirty" then. */
1734 {
1735 /* Set dirty bit. */
1736 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1737 }
1738
1739 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1740 {
1741 /* Input sink and not the recording source? Skip. */
1742 if ( pSink->enmDir == AUDMIXSINKDIR_INPUT
1743 && pSink->In.pStreamRecSource != pMixStream)
1744 continue;
1745
1746 PPDMAUDIOSTREAM pStream = pMixStream->pStream;
1747 AssertPtr(pStream);
1748
1749 PPDMIAUDIOCONNECTOR pConn = pMixStream->pConn;
1750 AssertPtr(pConn);
1751
1752 uint32_t cfProc = 0;
1753
1754 if (!(pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED))
1755 continue;
1756
1757 int rc2 = pConn->pfnStreamIterate(pConn, pStream);
1758 if (RT_SUCCESS(rc2))
1759 {
1760 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1761 {
1762 rc2 = pConn->pfnStreamCapture(pConn, pStream, &cfProc);
1763 if (RT_FAILURE(rc2))
1764 {
1765 LogFunc(("%s: Failed capturing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1766 continue;
1767 }
1768
1769 if (cfProc)
1770 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1771 }
1772 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1773 {
1774 rc2 = pConn->pfnStreamPlay(pConn, pStream, &cfProc);
1775 if (RT_FAILURE(rc2))
1776 {
1777 LogFunc(("%s: Failed playing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1778 continue;
1779 }
1780 }
1781 else
1782 {
1783 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1784 continue;
1785 }
1786 }
1787
1788 const PDMAUDIOSTREAMSTS fStreamStatusNew = pConn->pfnStreamGetStatus(pConn, pStream);
1789
1790 /* Is the stream enabled or in pending disable state?
1791 * Don't consider this stream as being disabled then. */
1792 if (fStreamStatusNew & (PDMAUDIOSTREAMSTS_FLAGS_ENABLED | PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE))
1793 cStreamsDisabled--;
1794 /* Note: The mixer stream's internal status will be updated in the next iteration of this function. */
1795
1796 Log3Func(("\t%s: cPlayed/cCaptured=%RU32, rc2=%Rrc\n", pStream->szName, cfProc, rc2));
1797 }
1798
1799 Log3Func(("[%s] fPendingDisable=%RTbool, %RU8/%RU8 streams disabled\n",
1800 pSink->pszName, RT_BOOL(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE), cStreamsDisabled, pSink->cStreams));
1801
1802 /* Update last updated timestamp. */
1803 pSink->tsLastUpdatedMs = RTTimeMilliTS();
1804
1805 /* All streams disabled and the sink is in pending disable mode? */
1806 if ( cStreamsDisabled == pSink->cStreams
1807 && (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
1808 {
1809 audioMixerSinkReset(pSink);
1810 }
1811
1812 return rc;
1813}
1814
1815/**
1816 * Updates (invalidates) a mixer sink.
1817 *
1818 * @returns VBox status code.
1819 * @param pSink Mixer sink to update.
1820 */
1821int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
1822{
1823 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1824
1825 int rc = RTCritSectEnter(&pSink->CritSect);
1826 if (RT_FAILURE(rc))
1827 return rc;
1828
1829 rc = audioMixerSinkUpdateInternal(pSink);
1830
1831 int rc2 = RTCritSectLeave(&pSink->CritSect);
1832 AssertRC(rc2);
1833
1834 return rc;
1835}
1836
1837/**
1838 * Updates the (master) volume of a mixer sink.
1839 *
1840 * @returns VBox status code.
1841 * @param pSink Mixer sink to update volume for.
1842 * @param pVolMaster Master volume to set.
1843 */
1844static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
1845{
1846 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1847 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
1848
1849 LogFlowFunc(("[%s] Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1850 pSink->pszName, pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
1851 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32 ",
1852 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1853
1854 /** @todo Very crude implementation for now -- needs more work! */
1855
1856 pSink->VolumeCombined.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
1857
1858 pSink->VolumeCombined.uLeft = ( (pSink->Volume.uLeft ? pSink->Volume.uLeft : 1)
1859 * (pVolMaster->uLeft ? pVolMaster->uLeft : 1)) / PDMAUDIO_VOLUME_MAX;
1860
1861 pSink->VolumeCombined.uRight = ( (pSink->Volume.uRight ? pSink->Volume.uRight : 1)
1862 * (pVolMaster->uRight ? pVolMaster->uRight : 1)) / PDMAUDIO_VOLUME_MAX;
1863
1864 LogFlow(("-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1865 pSink->VolumeCombined.fMuted, pSink->VolumeCombined.uLeft, pSink->VolumeCombined.uRight));
1866
1867 /* Propagate new sink volume to all streams in the sink. */
1868 PAUDMIXSTREAM pMixStream;
1869 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1870 {
1871 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &pSink->VolumeCombined);
1872 AssertRC(rc2);
1873 }
1874
1875 return VINF_SUCCESS;
1876}
1877
1878/**
1879 * Writes (buffered) output data of a sink's stream to the bound audio connector stream.
1880 *
1881 * @returns VBox status code.
1882 * @param pSink Sink of stream that contains the mixer stream.
1883 * @param pMixStream Mixer stream to write output data for.
1884 */
1885static int audioMixerSinkWriteToStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pMixStream)
1886{
1887 if (!pMixStream->pCircBuf)
1888 return VINF_SUCCESS;
1889
1890 return audioMixerSinkWriteToStreamEx(pSink, pMixStream, (uint32_t)RTCircBufUsed(pMixStream->pCircBuf), NULL /* pcbWritten */);
1891}
1892
1893/**
1894 * Writes (buffered) output data of a sink's stream to the bound audio connector stream, extended version.
1895 *
1896 * @returns VBox status code.
1897 * @param pSink Sink of stream that contains the mixer stream.
1898 * @param pMixStream Mixer stream to write output data for.
1899 * @param cbToWrite Size (in bytes) to write.
1900 * @param pcbWritten Size (in bytes) written on success. Optional.
1901 */
1902static int audioMixerSinkWriteToStreamEx(PAUDMIXSINK pSink, PAUDMIXSTREAM pMixStream, uint32_t cbToWrite, uint32_t *pcbWritten)
1903{
1904 /* pcbWritten is optional. */
1905
1906 if ( !cbToWrite
1907 || !(pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED))
1908 {
1909 if (pcbWritten)
1910 *pcbWritten = 0;
1911
1912 return VINF_SUCCESS;
1913 }
1914
1915 PRTCIRCBUF pCircBuf = pMixStream->pCircBuf;
1916
1917 const uint32_t cbWritableStream = pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream);
1918 cbToWrite = RT_MIN(cbToWrite, RT_MIN((uint32_t)RTCircBufUsed(pCircBuf), cbWritableStream));
1919
1920 Log3Func(("[%s] cbWritableStream=%RU32, cbToWrite=%RU32\n", pMixStream->pszName, cbWritableStream, cbToWrite));
1921
1922 uint32_t cbWritten = 0;
1923
1924 int rc = VINF_SUCCESS;
1925
1926 while (cbToWrite)
1927 {
1928 void *pvChunk;
1929 size_t cbChunk;
1930 RTCircBufAcquireReadBlock(pCircBuf, cbToWrite, &pvChunk, &cbChunk);
1931
1932 Log3Func(("[%s] cbChunk=%RU32\n", pMixStream->pszName, cbChunk));
1933
1934 uint32_t cbChunkWritten = 0;
1935 if (cbChunk)
1936 {
1937 rc = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, pvChunk, (uint32_t)cbChunk,
1938 &cbChunkWritten);
1939 if (RT_FAILURE(rc))
1940 {
1941 if (rc == VERR_BUFFER_OVERFLOW)
1942 {
1943 LogRel2(("Audio Mixer: Buffer overrun for mixer stream '%s' (sink '%s')\n", pMixStream->pszName, pSink->pszName));
1944 break;
1945 }
1946 if (rc == VERR_AUDIO_STREAM_NOT_READY)
1947 {
1948 /* Stream is not enabled, just skip. */
1949 rc = VINF_SUCCESS;
1950 }
1951 else
1952 LogRel2(("Audio Mixer: Writing to mixer stream '%s' (sink '%s') failed, rc=%Rrc\n",
1953 pMixStream->pszName, pSink->pszName, rc));
1954
1955 if (RT_FAILURE(rc))
1956 LogFunc(("[%s] Failed writing to stream '%s': %Rrc\n", pSink->pszName, pMixStream->pszName, rc));
1957 }
1958 }
1959
1960 RTCircBufReleaseReadBlock(pCircBuf, cbChunkWritten);
1961
1962 if ( RT_FAILURE(rc)
1963 || !cbChunkWritten)
1964 break;
1965
1966 Assert(cbToWrite >= cbChunkWritten);
1967 cbToWrite -= (uint32_t)cbChunkWritten;
1968
1969 cbWritten += (uint32_t)cbChunkWritten;
1970 }
1971
1972 pMixStream->StatsCircBufUsed = RTCircBufUsed(pCircBuf);
1973 Log3Func(("[%s] cbWritten=%RU32\n", pMixStream->pszName, cbWritten));
1974
1975 if (pcbWritten)
1976 *pcbWritten = cbWritten;
1977
1978#ifdef DEBUG_andy
1979 AssertRC(rc);
1980#endif
1981
1982 return rc;
1983}
1984
1985/**
1986 * Multiplexes audio output data to all connected mixer streams in a synchronized fashion, e.g.
1987 * only multiplex as much data as all streams can handle at this time.
1988 *
1989 * @returns VBox status code.
1990 * @param pSink Sink to write audio output to.
1991 * @param pvBuf Pointer to audio data to write.
1992 * @param cbBuf Size (in bytes) of audio data to write.
1993 * @param pcbWritten Returns the number of bytes written to each of the
1994 * streams.
1995 */
1996static int audioMixerSinkMultiplexSync(PAUDMIXSINK pSink, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
1997{
1998 Log3Func(("[%s] cbBuf=%RU32\n", pSink->pszName, cbBuf));
1999 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
2000 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
2001 ("%s: Can't multiplex to a sink which is not an output sink\n", pSink->pszName));
2002
2003 /*
2004 * Check all enabled streems for buffer space.
2005 */
2006 uint32_t cbToWrite = UINT32_MAX;
2007 PAUDMIXSTREAM pMixStream;
2008 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
2009 {
2010 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
2011 {
2012 uint32_t const cbCircFree = (uint32_t)RTCircBufFree(pMixStream->pCircBuf);
2013 cbToWrite = RT_MIN(cbToWrite, cbCircFree);
2014 }
2015 else
2016 Log3Func(("[%s] Stream '%s' disabled, skipping ...\n", pSink->pszName, pMixStream->pszName));
2017 }
2018 if (cbToWrite != UINT32_MAX)
2019 cbToWrite = RT_MIN(cbBuf, cbToWrite);
2020 else
2021 cbToWrite = 0; /* No active streams at all. */
2022 if (cbToWrite)
2023 {
2024 /*
2025 * Do the copying.
2026 */
2027 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
2028 {
2029 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
2030 {
2031 PRTCIRCBUF pCircBuf = pMixStream->pCircBuf;
2032 size_t offWritten = 0;
2033 while (offWritten < cbToWrite)
2034 {
2035 void *pvChunk = NULL;
2036 size_t cbChunk = 0;
2037 RTCircBufAcquireWriteBlock(pCircBuf, cbToWrite - offWritten, &pvChunk, &cbChunk);
2038
2039 memcpy(pvChunk, (uint8_t const *)pvBuf + offWritten, cbChunk);
2040
2041 RTCircBufReleaseWriteBlock(pCircBuf, cbChunk);
2042
2043 offWritten += cbChunk;
2044 }
2045 Assert(offWritten == cbToWrite);
2046
2047 pMixStream->tsLastReadWrittenNs = RTTimeNanoTS();
2048 pMixStream->StatsCircBufUsed = RTCircBufUsed(pCircBuf);
2049 Log3Func(("[%s] Mixer stream '%s' -> cbWrittenBuf=%RU32\n", pSink->pszName, pMixStream->pszName, cbToWrite));
2050 }
2051 }
2052 }
2053
2054 Log3Func(("[%s] cbBuf=%RU32, cbToWrite=%RU32\n", pSink->pszName, cbBuf, cbToWrite));
2055
2056 *pcbWritten = cbToWrite;
2057 return VINF_SUCCESS;
2058}
2059
2060/**
2061 * Writes data to a mixer sink.
2062 *
2063 * @returns VBox status code.
2064 * @param pSink Sink to write data to.
2065 * @param enmOp Mixer operation to use when writing data to the sink.
2066 * @param pvBuf Buffer containing the audio data to write.
2067 * @param cbBuf Size (in bytes) of the buffer containing the audio data.
2068 * @param pcbWritten Number of bytes written. Optional.
2069 */
2070int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
2071{
2072 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2073 RT_NOREF(enmOp);
2074 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2075 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
2076 /* pcbWritten is optional. */
2077
2078 int rc = RTCritSectEnter(&pSink->CritSect);
2079 AssertRCReturn(rc, rc);
2080
2081 AssertMsg(pSink->fStatus & AUDMIXSINK_STS_RUNNING,
2082 ("%s: Can't write to a sink which is not running (anymore) (status 0x%x)\n", pSink->pszName, pSink->fStatus));
2083 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
2084 ("%s: Can't write to a sink which is not an output sink\n", pSink->pszName));
2085
2086 uint32_t cbWritten = 0;
2087 uint32_t cbToWrite = RT_MIN(AudioMixBufFreeBytes(&pSink->MixBuf), cbBuf);
2088 while (cbToWrite)
2089 {
2090 /* Write the data to the mixer sink's own mixing buffer.
2091 Here the audio data is transformed into the mixer sink's format. */
2092 uint32_t cFramesWritten = 0;
2093 rc = AudioMixBufWriteCirc(&pSink->MixBuf, (uint8_t const*)pvBuf + cbWritten, cbToWrite, &cFramesWritten);
2094 if (RT_SUCCESS(rc))
2095 {
2096 const uint32_t cbWrittenChunk = PDMAudioPropsFramesToBytes(&pSink->PCMProps, cFramesWritten);
2097 Assert(cbToWrite >= cbWrittenChunk);
2098 cbToWrite -= cbWrittenChunk;
2099 cbWritten += cbWrittenChunk;
2100 }
2101 else
2102 break;
2103 }
2104
2105 Log3Func(("[%s] cbBuf=%RU32 -> cbWritten=%RU32\n", pSink->pszName, cbBuf, cbWritten));
2106
2107 /* Update the sink's last written time stamp. */
2108 pSink->tsLastReadWrittenNs = RTTimeNanoTS();
2109
2110 if (pcbWritten)
2111 *pcbWritten = cbWritten;
2112
2113 RTCritSectLeave(&pSink->CritSect);
2114 return rc;
2115}
2116
2117/*********************************************************************************************************************************
2118 * Mixer Stream implementation.
2119 ********************************************************************************************************************************/
2120
2121/**
2122 * Controls a mixer stream, internal version.
2123 *
2124 * @returns VBox status code.
2125 * @param pMixStream Mixer stream to control.
2126 * @param enmCmd Mixer stream command to use.
2127 * @param fCtl Additional control flags. Pass 0.
2128 */
2129static int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
2130{
2131 AssertPtr(pMixStream->pConn);
2132 AssertPtr(pMixStream->pStream);
2133
2134 RT_NOREF(fCtl);
2135
2136 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
2137
2138 LogFlowFunc(("[%s] enmCmd=%ld, rc=%Rrc\n", pMixStream->pszName, enmCmd, rc));
2139
2140 return rc;
2141}
2142
2143/**
2144 * Updates a mixer stream's internal status.
2145 *
2146 * @returns VBox status code.
2147 * @param pMixStream Mixer stream to to update internal status for.
2148 */
2149static int audioMixerStreamUpdateStatus(PAUDMIXSTREAM pMixStream)
2150{
2151 pMixStream->fStatus = AUDMIXSTREAM_STATUS_NONE;
2152
2153 if (pMixStream->pConn) /* Audio connector available? */
2154 {
2155 const uint32_t fStreamStatus = pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream);
2156
2157 if (PDMAudioStrmStatusIsReady(fStreamStatus))
2158 pMixStream->fStatus |= AUDMIXSTREAM_STATUS_ENABLED;
2159
2160 AssertPtr(pMixStream->pSink);
2161 switch (pMixStream->pSink->enmDir)
2162 {
2163 case AUDMIXSINKDIR_INPUT:
2164 if (PDMAudioStrmStatusCanRead(fStreamStatus))
2165 pMixStream->fStatus |= AUDMIXSTREAM_STATUS_CAN_READ;
2166 break;
2167
2168 case AUDMIXSINKDIR_OUTPUT:
2169 if (PDMAudioStrmStatusCanWrite(fStreamStatus))
2170 pMixStream->fStatus |= AUDMIXSTREAM_STATUS_CAN_WRITE;
2171 break;
2172
2173 default:
2174 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
2175 break;
2176 }
2177 }
2178
2179 LogFlowFunc(("[%s] -> 0x%x\n", pMixStream->pszName, pMixStream->fStatus));
2180 return VINF_SUCCESS;
2181}
2182
2183/**
2184 * Controls a mixer stream.
2185 *
2186 * @returns VBox status code.
2187 * @param pMixStream Mixer stream to control.
2188 * @param enmCmd Mixer stream command to use.
2189 * @param fCtl Additional control flags. Pass 0.
2190 */
2191int AudioMixerStreamCtl(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
2192{
2193 RT_NOREF(fCtl);
2194 AssertPtrReturn(pMixStream, VERR_INVALID_POINTER);
2195 /** @todo Validate fCtl. */
2196
2197 int rc = RTCritSectEnter(&pMixStream->CritSect);
2198 if (RT_FAILURE(rc))
2199 return rc;
2200
2201 rc = audioMixerStreamCtlInternal(pMixStream, enmCmd, fCtl);
2202
2203 int rc2 = RTCritSectLeave(&pMixStream->CritSect);
2204 if (RT_SUCCESS(rc))
2205 rc = rc2;
2206
2207 return rc;
2208}
2209
2210/**
2211 * Destroys a mixer stream, internal version.
2212 *
2213 * @param pMixStream Mixer stream to destroy.
2214 * @param pDevIns The device instance the statistics are registered with.
2215 */
2216static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream, PPDMDEVINS pDevIns)
2217{
2218 AssertPtrReturnVoid(pMixStream);
2219
2220 LogFunc(("%s\n", pMixStream->pszName));
2221
2222 if (pMixStream->pConn) /* Stream has a connector interface present? */
2223 {
2224 if (pMixStream->pStream)
2225 {
2226 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
2227 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
2228
2229 pMixStream->pStream = NULL;
2230 }
2231
2232 pMixStream->pConn = NULL;
2233 }
2234
2235 if (pMixStream->pszStatPrefix)
2236 {
2237 PDMDevHlpSTAMDeregisterByPrefix(pDevIns, pMixStream->pszStatPrefix);
2238 RTStrFree(pMixStream->pszStatPrefix);
2239 pMixStream->pszStatPrefix = NULL;
2240 }
2241
2242 RTStrFree(pMixStream->pszName);
2243 pMixStream->pszName = NULL;
2244
2245 if (pMixStream->pCircBuf)
2246 {
2247 RTCircBufDestroy(pMixStream->pCircBuf);
2248 pMixStream->pCircBuf = NULL;
2249 }
2250
2251 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
2252 AssertRC(rc2);
2253
2254 RTMemFree(pMixStream);
2255 pMixStream = NULL;
2256}
2257
2258/**
2259 * Destroys a mixer stream.
2260 *
2261 * @param pMixStream Mixer stream to destroy.
2262 * @param pDevIns The device instance statistics are registered with.
2263 */
2264void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream, PPDMDEVINS pDevIns)
2265{
2266 if (!pMixStream)
2267 return;
2268
2269 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2270 AssertRC(rc2);
2271
2272 LogFunc(("%s\n", pMixStream->pszName));
2273
2274 if (pMixStream->pSink) /* Is the stream part of a sink? */
2275 {
2276 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
2277 * pointer will be gone from the stream. */
2278 PAUDMIXSINK pSink = pMixStream->pSink;
2279
2280 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
2281 if (RT_SUCCESS(rc2))
2282 {
2283 Assert(pSink->cStreams);
2284 pSink->cStreams--;
2285 }
2286 }
2287 else
2288 rc2 = VINF_SUCCESS;
2289
2290 int rc3 = RTCritSectLeave(&pMixStream->CritSect);
2291 AssertRC(rc3);
2292
2293 if (RT_SUCCESS(rc2))
2294 {
2295 audioMixerStreamDestroyInternal(pMixStream, pDevIns);
2296 pMixStream = NULL;
2297 }
2298
2299 LogFlowFunc(("Returning %Rrc\n", rc2));
2300}
2301
2302/**
2303 * Returns whether a mixer stream currently is active (playing/recording) or not.
2304 *
2305 * @returns @c true if playing/recording, @c false if not.
2306 * @param pMixStream Mixer stream to return status for.
2307 */
2308bool AudioMixerStreamIsActive(PAUDMIXSTREAM pMixStream)
2309{
2310 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2311 if (RT_FAILURE(rc2))
2312 return false;
2313
2314 AssertPtr(pMixStream->pConn);
2315 AssertPtr(pMixStream->pStream);
2316
2317 bool fIsActive;
2318
2319 if ( pMixStream->pConn
2320 && pMixStream->pStream
2321 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAGS_ENABLED))
2322 {
2323 fIsActive = true;
2324 }
2325 else
2326 fIsActive = false;
2327
2328 rc2 = RTCritSectLeave(&pMixStream->CritSect);
2329 AssertRC(rc2);
2330
2331 return fIsActive;
2332}
2333
2334/**
2335 * Returns whether a mixer stream is valid (e.g. initialized and in a working state) or not.
2336 *
2337 * @returns @c true if valid, @c false if not.
2338 * @param pMixStream Mixer stream to return status for.
2339 */
2340bool AudioMixerStreamIsValid(PAUDMIXSTREAM pMixStream)
2341{
2342 if (!pMixStream)
2343 return false;
2344
2345 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2346 if (RT_FAILURE(rc2))
2347 return false;
2348
2349 bool fIsValid;
2350
2351 if ( pMixStream->pConn
2352 && pMixStream->pStream
2353 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED))
2354 {
2355 fIsValid = true;
2356 }
2357 else
2358 fIsValid = false;
2359
2360 rc2 = RTCritSectLeave(&pMixStream->CritSect);
2361 AssertRC(rc2);
2362
2363 return fIsValid;
2364}
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