VirtualBox

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

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

Audio: Made sure PDMAUDIOPCMPROPS is initialized using a helper function or the initializer macro, and that vital changes are made using setting helper functions. There are now two derived fields (frame size and shift count) that must be maintained, so this was the sanest way of doing it. Added a raw flag to PDMAUDIOPCMPROPS for VRDE/VRDP, since it wants the raw mixer content and we need a way of expressing this (PDMAUDIOSTREAMLAYOUT isn't the right place). The mixer buffers now uses PDMAUDIOPCMPROPS rather than the weird 32-bit format contraption for picking conversion functions. Simplify the drvAudioStreamPlay code by eliminating the PDMAUDIOSTREAMLAYOUT_RAW special case. bugref:9890

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