VirtualBox

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

Last change on this file since 68272 was 68272, checked in by vboxsync, 8 years ago

Audio: Renamed PDMAUDIOSTRMSTS* -> PDMAUDIOSTREAMSTS*. No actual code changes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.0 KB
Line 
1/* $Id: AudioMixer.cpp 68272 2017-08-03 08:25:32Z vboxsync $ */
2/** @file
3 * VBox audio: Mixing routines, mainly used by the various audio device
4 * emulations to achieve proper multiplexing from/to attached
5 * devices LUNs.
6 *
7 * This mixer acts as a layer between the audio connector interface and
8 * the actual device emulation, providing mechanisms for audio sources (input) and
9 * audio sinks (output).
10 *
11 * Think of this mixer as kind of a high(er) level interface for the audio connector
12 * interface, abstracting common tasks such as creating and managing various audio
13 * sources and sinks. This mixer class is purely optional and can be left out when
14 * implementing a new device emulation, using only the audi connector interface
15 * instead. For example, the SB16 emulation does not use this mixer and does all its
16 * stream management on its own.
17 *
18 * As audio driver instances are handled as LUNs on the device level, this
19 * audio mixer then can take care of e.g. mixing various inputs/outputs to/from
20 * a specific source/sink.
21 *
22 * How and which audio streams are connected to sinks/sources depends on how
23 * the audio mixer has been set up.
24 *
25 * A sink can connect multiple output streams together, whereas a source
26 * does this with input streams. Each sink / source consists of one or more
27 * so-called mixer streams, which then in turn have pointers to the actual
28 * PDM audio input/output streams.
29 */
30
31/*
32 * Copyright (C) 2014-2017 Oracle Corporation
33 *
34 * This file is part of VirtualBox Open Source Edition (OSE), as
35 * available from http://www.virtualbox.org. This file is free software;
36 * you can redistribute it and/or modify it under the terms of the GNU
37 * General Public License (GPL) as published by the Free Software
38 * Foundation, in version 2 as it comes in the "COPYING" file of the
39 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
40 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
41 */
42#define LOG_GROUP LOG_GROUP_AUDIO_MIXER
43#include <VBox/log.h>
44#include "AudioMixer.h"
45#include "AudioMixBuffer.h"
46#include "DrvAudio.h"
47
48#include <VBox/vmm/pdm.h>
49#include <VBox/err.h>
50#include <VBox/vmm/mm.h>
51#include <VBox/vmm/pdmaudioifs.h>
52
53#include <iprt/alloc.h>
54#include <iprt/asm-math.h>
55#include <iprt/assert.h>
56#include <iprt/string.h>
57
58static int audioMixerRemoveSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink);
59
60static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink);
61static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster);
62static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink);
63static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream);
64static void audioMixerSinkReset(PAUDMIXSINK pSink);
65static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink);
66
67int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl);
68static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pStream);
69
70
71#ifdef LOG_ENABLED
72/**
73 * Converts a mixer sink status to a string.
74 *
75 * @returns Stringified mixer sink flags. Must be free'd with RTStrFree().
76 * "NONE" if no flags set.
77 * @param fFlags Mixer sink flags to convert.
78 */
79static char *dbgAudioMixerSinkStatusToStr(AUDMIXSINKSTS fStatus)
80{
81#define APPEND_FLAG_TO_STR(_aFlag) \
82 if (fStatus & AUDMIXSINK_STS_##_aFlag) \
83 { \
84 if (pszFlags) \
85 { \
86 rc2 = RTStrAAppend(&pszFlags, " "); \
87 if (RT_FAILURE(rc2)) \
88 break; \
89 } \
90 \
91 rc2 = RTStrAAppend(&pszFlags, #_aFlag); \
92 if (RT_FAILURE(rc2)) \
93 break; \
94 } \
95
96 char *pszFlags = NULL;
97 int rc2 = VINF_SUCCESS;
98
99 do
100 {
101 APPEND_FLAG_TO_STR(NONE);
102 APPEND_FLAG_TO_STR(RUNNING);
103 APPEND_FLAG_TO_STR(PENDING_DISABLE);
104 APPEND_FLAG_TO_STR(DIRTY);
105
106 } while (0);
107
108 if ( RT_FAILURE(rc2)
109 && pszFlags)
110 {
111 RTStrFree(pszFlags);
112 pszFlags = NULL;
113 }
114
115#undef APPEND_FLAG_TO_STR
116
117 return pszFlags;
118}
119#endif /* DEBUG */
120
121/**
122 * Creates an audio sink and attaches it to the given mixer.
123 *
124 * @returns IPRT status code.
125 * @param pMixer Mixer to attach created sink to.
126 * @param pszName Name of the sink to create.
127 * @param enmDir Direction of the sink to create.
128 * @param ppSink Pointer which returns the created sink on success.
129 */
130int AudioMixerCreateSink(PAUDIOMIXER pMixer, const char *pszName, AUDMIXSINKDIR enmDir, PAUDMIXSINK *ppSink)
131{
132 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
133 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
134 /* ppSink is optional. */
135
136 int rc = RTCritSectEnter(&pMixer->CritSect);
137 if (RT_FAILURE(rc))
138 return rc;
139
140 PAUDMIXSINK pSink = (PAUDMIXSINK)RTMemAllocZ(sizeof(AUDMIXSINK));
141 if (pSink)
142 {
143 pSink->pszName = RTStrDup(pszName);
144 if (!pSink->pszName)
145 rc = VERR_NO_MEMORY;
146
147 if (RT_SUCCESS(rc))
148 rc = RTCritSectInit(&pSink->CritSect);
149
150 if (RT_SUCCESS(rc))
151 {
152 pSink->pParent = pMixer;
153 pSink->enmDir = enmDir;
154 RTListInit(&pSink->lstStreams);
155
156 /* Set initial volume to max. */
157 pSink->Volume.fMuted = false;
158 pSink->Volume.uLeft = PDMAUDIO_VOLUME_MAX;
159 pSink->Volume.uRight = PDMAUDIO_VOLUME_MAX;
160
161 /* Ditto for the combined volume. */
162 pSink->VolumeCombined.fMuted = false;
163 pSink->VolumeCombined.uLeft = PDMAUDIO_VOLUME_MAX;
164 pSink->VolumeCombined.uRight = PDMAUDIO_VOLUME_MAX;
165
166 RTListAppend(&pMixer->lstSinks, &pSink->Node);
167 pMixer->cSinks++;
168
169 LogFlowFunc(("pMixer=%p, pSink=%p, cSinks=%RU8\n",
170 pMixer, pSink, pMixer->cSinks));
171
172 if (ppSink)
173 *ppSink = pSink;
174 }
175
176 if (RT_FAILURE(rc))
177 {
178 RTCritSectDelete(&pSink->CritSect);
179
180 if (pSink)
181 {
182 RTMemFree(pSink);
183 pSink = NULL;
184 }
185 }
186 }
187 else
188 rc = VERR_NO_MEMORY;
189
190 int rc2 = RTCritSectLeave(&pMixer->CritSect);
191 AssertRC(rc2);
192
193 return rc;
194}
195
196/**
197 * Creates an audio mixer.
198 *
199 * @returns IPRT status code.
200 * @param pszName Name of the audio mixer.
201 * @param fFlags Creation flags. Not used at the moment and must be 0.
202 * @param ppMixer Pointer which returns the created mixer object.
203 */
204int AudioMixerCreate(const char *pszName, uint32_t fFlags, PAUDIOMIXER *ppMixer)
205{
206 RT_NOREF(fFlags);
207 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
208 /** @todo Add fFlags validation. */
209 AssertPtrReturn(ppMixer, VERR_INVALID_POINTER);
210
211 int rc = VINF_SUCCESS;
212
213 PAUDIOMIXER pMixer = (PAUDIOMIXER)RTMemAllocZ(sizeof(AUDIOMIXER));
214 if (pMixer)
215 {
216 pMixer->pszName = RTStrDup(pszName);
217 if (!pMixer->pszName)
218 rc = VERR_NO_MEMORY;
219
220 if (RT_SUCCESS(rc))
221 rc = RTCritSectInit(&pMixer->CritSect);
222
223 if (RT_SUCCESS(rc))
224 {
225 pMixer->cSinks = 0;
226 RTListInit(&pMixer->lstSinks);
227
228 /* Set master volume to the max. */
229 pMixer->VolMaster.fMuted = false;
230 pMixer->VolMaster.uLeft = PDMAUDIO_VOLUME_MAX;
231 pMixer->VolMaster.uRight = PDMAUDIO_VOLUME_MAX;
232
233 LogFlowFunc(("Created mixer '%s'\n", pMixer->pszName));
234
235 *ppMixer = pMixer;
236 }
237 else
238 RTMemFree(pMixer);
239 }
240 else
241 rc = VERR_NO_MEMORY;
242
243 LogFlowFuncLeaveRC(rc);
244 return rc;
245}
246
247/**
248 * Helper function for the internal debugger to print the mixer's current
249 * state, along with the attached sinks.
250 *
251 * @param pMixer Mixer to print debug output for.
252 * @param pHlp Debug info helper to use.
253 * @param pszArgs Optional arguments. Not being used at the moment.
254 */
255void AudioMixerDebug(PAUDIOMIXER pMixer, PCDBGFINFOHLP pHlp, const char *pszArgs)
256{
257 RT_NOREF(pszArgs);
258 PAUDMIXSINK pSink;
259 unsigned iSink = 0;
260
261 int rc2 = RTCritSectEnter(&pMixer->CritSect);
262 if (RT_FAILURE(rc2))
263 return;
264
265 pHlp->pfnPrintf(pHlp, "[Master] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", pMixer->pszName,
266 pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight, pMixer->VolMaster.fMuted);
267
268 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
269 {
270 pHlp->pfnPrintf(pHlp, "[Sink %u] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", iSink, pSink->pszName,
271 pSink->Volume.uLeft, pSink->Volume.uRight, pSink->Volume.fMuted);
272 ++iSink;
273 }
274
275 rc2 = RTCritSectLeave(&pMixer->CritSect);
276 AssertRC(rc2);
277}
278
279/**
280 * Destroys an audio mixer.
281 *
282 * @param pMixer Audio mixer to destroy.
283 */
284void AudioMixerDestroy(PAUDIOMIXER pMixer)
285{
286 if (!pMixer)
287 return;
288
289 int rc2 = RTCritSectEnter(&pMixer->CritSect);
290 AssertRC(rc2);
291
292 LogFlowFunc(("Destroying %s ...\n", pMixer->pszName));
293
294 PAUDMIXSINK pSink, pSinkNext;
295 RTListForEachSafe(&pMixer->lstSinks, pSink, pSinkNext, AUDMIXSINK, Node)
296 {
297 /* Save a pointer to the sink to remove, as pSink
298 * will not be valid anymore after calling audioMixerRemoveSinkInternal(). */
299 PAUDMIXSINK pSinkToRemove = pSink;
300
301 audioMixerRemoveSinkInternal(pMixer, pSinkToRemove);
302 audioMixerSinkDestroyInternal(pSinkToRemove);
303 }
304
305 pMixer->cSinks = 0;
306
307 if (pMixer->pszName)
308 {
309 RTStrFree(pMixer->pszName);
310 pMixer->pszName = NULL;
311 }
312
313 rc2 = RTCritSectLeave(&pMixer->CritSect);
314 AssertRC(rc2);
315
316 RTCritSectDelete(&pMixer->CritSect);
317
318 RTMemFree(pMixer);
319 pMixer = NULL;
320}
321
322/**
323 * Invalidates all internal data, internal version.
324 *
325 * @returns IPRT status code.
326 * @param pMixer Mixer to invalidate data for.
327 */
328int audioMixerInvalidateInternal(PAUDIOMIXER pMixer)
329{
330 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
331
332 LogFlowFunc(("[%s]\n", pMixer->pszName));
333
334 /* Propagate new master volume to all connected sinks. */
335 PAUDMIXSINK pSink;
336 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
337 {
338 int rc2 = audioMixerSinkUpdateVolume(pSink, &pMixer->VolMaster);
339 AssertRC(rc2);
340 }
341
342 return VINF_SUCCESS;
343}
344
345/**
346 * Invalidates all internal data.
347 *
348 * @returns IPRT status code.
349 * @param pMixer Mixer to invalidate data for.
350 */
351void AudioMixerInvalidate(PAUDIOMIXER pMixer)
352{
353 AssertPtrReturnVoid(pMixer);
354
355 int rc2 = RTCritSectEnter(&pMixer->CritSect);
356 AssertRC(rc2);
357
358 LogFlowFunc(("[%s]\n", pMixer->pszName));
359
360 rc2 = audioMixerInvalidateInternal(pMixer);
361 AssertRC(rc2);
362
363 rc2 = RTCritSectLeave(&pMixer->CritSect);
364 AssertRC(rc2);
365}
366
367/**
368 * Removes a formerly attached audio sink for an audio mixer, internal version.
369 *
370 * @returns IPRT status code.
371 * @param pMixer Mixer to remove sink from.
372 * @param pSink Sink to remove.
373 */
374static int audioMixerRemoveSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
375{
376 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
377 if (!pSink)
378 return VERR_NOT_FOUND;
379
380 AssertMsgReturn(pSink->pParent == pMixer, ("%s: Is not part of mixer '%s'\n",
381 pSink->pszName, pMixer->pszName), VERR_NOT_FOUND);
382
383 LogFlowFunc(("[%s] pSink=%s, cSinks=%RU8\n",
384 pMixer->pszName, pSink->pszName, pMixer->cSinks));
385
386 /* Remove sink from mixer. */
387 RTListNodeRemove(&pSink->Node);
388 Assert(pMixer->cSinks);
389
390 /* Set mixer to NULL so that we know we're not part of any mixer anymore. */
391 pSink->pParent = NULL;
392
393 return VINF_SUCCESS;
394}
395
396/**
397 * Removes a formerly attached audio sink for an audio mixer.
398 *
399 * @returns IPRT status code.
400 * @param pMixer Mixer to remove sink from.
401 * @param pSink Sink to remove.
402 */
403void AudioMixerRemoveSink(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
404{
405 int rc2 = RTCritSectEnter(&pMixer->CritSect);
406 AssertRC(rc2);
407
408 audioMixerSinkRemoveAllStreamsInternal(pSink);
409 audioMixerRemoveSinkInternal(pMixer, pSink);
410
411 rc2 = RTCritSectLeave(&pMixer->CritSect);
412}
413
414/**
415 * Sets the mixer's master volume.
416 *
417 * @returns IPRT status code.
418 * @param pMixer Mixer to set master volume for.
419 * @param pVol Volume to set.
420 */
421int AudioMixerSetMasterVolume(PAUDIOMIXER pMixer, PPDMAUDIOVOLUME pVol)
422{
423 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
424 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
425
426 int rc = RTCritSectEnter(&pMixer->CritSect);
427 if (RT_FAILURE(rc))
428 return rc;
429
430 memcpy(&pMixer->VolMaster, pVol, sizeof(PDMAUDIOVOLUME));
431
432 LogFlowFunc(("[%s] lVol=%RU32, rVol=%RU32 => fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
433 pMixer->pszName, pVol->uLeft, pVol->uRight,
434 pMixer->VolMaster.fMuted, pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight));
435
436 rc = audioMixerInvalidateInternal(pMixer);
437
438 int rc2 = RTCritSectLeave(&pMixer->CritSect);
439 AssertRC(rc2);
440
441 return rc;
442}
443
444/*********************************************************************************************************************************
445 * Mixer Sink implementation.
446 ********************************************************************************************************************************/
447
448/**
449 * Adds an audio stream to a specific audio sink.
450 *
451 * @returns IPRT status code.
452 * @param pSink Sink to add audio stream to.
453 * @param pStream Stream to add.
454 */
455int AudioMixerSinkAddStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
456{
457 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
458 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
459
460 int rc = RTCritSectEnter(&pSink->CritSect);
461 if (RT_FAILURE(rc))
462 return rc;
463
464 if (pSink->cStreams == UINT8_MAX) /* 255 streams per sink max. */
465 {
466 int rc2 = RTCritSectLeave(&pSink->CritSect);
467 AssertRC(rc2);
468
469 return VERR_NO_MORE_HANDLES;
470 }
471
472 LogFlowFuncEnter();
473
474#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
475 /* Make sure only compatible streams are added. */
476 if (pStream->enmDir == PDMAUDIODIR_IN)
477 {
478 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, &pStream->InOut.pIn->Props))
479 {
480#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
481 /* Chain: Stream (Child) -> Sink (Child) -> Guest (Parent). */
482 PPDMAUDIOMIXBUF pHstIn = &pStream->InOut.pIn->pHstStrmIn->MixBuf;
483 PPDMAUDIOMIXBUF pGstIn = &pStream->InOut.pIn->MixBuf;
484
485 /* Unlink any former parent from host input. */
486 AudioMixBufUnlink(pHstIn);
487
488 /* Link host input to this sink as a parent. */
489 rc = AudioMixBufLinkTo(pHstIn, &pSink->MixBuf);
490 AssertRC(rc);
491
492 /* Unlink any former parent from this sink. */
493 AudioMixBufUnlink(&pSink->MixBuf);
494
495 /* Link guest input to this sink as a parent. */
496 rc = AudioMixBufLinkTo(&pSink->MixBuf, pGstIn);
497 AssertRC(rc);
498# ifdef DEBUG
499 AudioMixBufDbgPrintChain(&pStream->InOut.pIn->MixBuf);
500# endif
501#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
502 }
503 else
504 AssertFailedStmt(rc = VERR_WRONG_TYPE);
505 }
506 else if (pStream->enmDir == PDMAUDIODIR_OUT)
507 {
508 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, &pStream->InOut.pOut->Props))
509 {
510#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
511 /* Chain: Guest (Child) -> Sink (Child) -> Stream (Parent). */
512 rc = AudioMixBufLinkTo(&pStream->InOut.pOut->pHstStrmOut->MixBuf, &pSink->MixBuf);
513# ifdef DEBUG
514 AudioMixBufDbgPrintChain(&pSink->MixBuf);
515# endif
516#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
517 }
518 else
519 AssertFailedStmt(rc = VERR_WRONG_TYPE);
520 }
521 else
522 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
523#else
524 rc = VINF_SUCCESS;
525#endif
526
527 if (RT_SUCCESS(rc))
528 {
529 /** @todo Check if stream already is assigned to (another) sink. */
530
531 /* If the sink is running and not in pending disable mode,
532 * make sure that the added stream also is enabled. */
533 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
534 && !(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
535 {
536 rc = audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_ENABLE, AUDMIXSTRMCTL_FLAG_NONE);
537 }
538
539 if (RT_SUCCESS(rc))
540 {
541 /* Apply the sink's combined volume to the stream. */
542 rc = pStream->pConn->pfnStreamSetVolume(pStream->pConn, pStream->pStream, &pSink->VolumeCombined);
543 AssertRC(rc);
544 }
545
546 if (RT_SUCCESS(rc))
547 {
548 /* Save pointer to sink the stream is attached to. */
549 pStream->pSink = pSink;
550
551 /* Append stream to sink's list. */
552 RTListAppend(&pSink->lstStreams, &pStream->Node);
553 pSink->cStreams++;
554 }
555 }
556
557 LogFlowFunc(("[%s] cStreams=%RU8, rc=%Rrc\n", pSink->pszName, pSink->cStreams, rc));
558
559 int rc2 = RTCritSectLeave(&pSink->CritSect);
560 AssertRC(rc2);
561
562 return rc;
563}
564
565/**
566 * Creates an audio mixer stream.
567 *
568 * @returns IPRT status code.
569 * @param pSink Sink to use for creating the stream.
570 * @param pConn Audio connector interface to use.
571 * @param pCfg Audio stream configuration to use.
572 * @param fFlags Stream creation flags. Currently unused, set to 0.
573 * @param ppStream Pointer which receives the newly created audio stream.
574 */
575int AudioMixerSinkCreateStream(PAUDMIXSINK pSink,
576 PPDMIAUDIOCONNECTOR pConn, PPDMAUDIOSTREAMCFG pCfg, uint32_t fFlags, PAUDMIXSTREAM *ppStream)
577{
578 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
579 AssertPtrReturn(pConn, VERR_INVALID_POINTER);
580 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
581 /** @todo Validate fFlags. */
582 /* ppStream is optional. */
583
584 PAUDMIXSTREAM pMixStream = (PAUDMIXSTREAM)RTMemAllocZ(sizeof(AUDMIXSTREAM));
585 if (!pMixStream)
586 return VERR_NO_MEMORY;
587
588 pMixStream->pszName = RTStrDup(pCfg->szName);
589 if (!pMixStream->pszName)
590 {
591 RTMemFree(pMixStream);
592 return VERR_NO_MEMORY;
593 }
594
595 int rc = RTCritSectEnter(&pSink->CritSect);
596 if (RT_FAILURE(rc))
597 return rc;
598
599 LogFlowFunc(("[%s] fFlags=0x%x (enmDir=%ld, %RU8 bits, %RU8 channels, %RU32Hz)\n",
600 pSink->pszName, fFlags, pCfg->enmDir, pCfg->Props.cBits, pCfg->Props.cChannels, pCfg->Props.uHz));
601
602 /*
603 * Initialize the host-side configuration for the stream to be created.
604 * Always use the sink's PCM audio format as the host side when creating a stream for it.
605 */
606 PDMAUDIOSTREAMCFG CfgHost;
607 rc = DrvAudioHlpPCMPropsToStreamCfg(&pSink->PCMProps, &CfgHost);
608 AssertRCReturn(rc, rc);
609
610 /* Apply the sink's direction for the configuration to use to
611 * create the stream. */
612 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
613 {
614 CfgHost.DestSource.Source = pCfg->DestSource.Source;
615 CfgHost.enmDir = PDMAUDIODIR_IN;
616 CfgHost.enmLayout = pCfg->enmLayout;
617 }
618 else
619 {
620 CfgHost.DestSource.Dest = pCfg->DestSource.Dest;
621 CfgHost.enmDir = PDMAUDIODIR_OUT;
622 CfgHost.enmLayout = pCfg->enmLayout;
623 }
624
625 RTStrPrintf(CfgHost.szName, sizeof(CfgHost.szName), "%s", pCfg->szName);
626
627 rc = RTCritSectInit(&pMixStream->CritSect);
628 if (RT_SUCCESS(rc))
629 {
630 PPDMAUDIOSTREAM pStream;
631 rc = pConn->pfnStreamCreate(pConn, &CfgHost, pCfg, &pStream);
632 if (RT_SUCCESS(rc))
633 {
634 /* Save the audio stream pointer to this mixing stream. */
635 pMixStream->pStream = pStream;
636
637 /* Increase the stream's reference count to let others know
638 * we're reyling on it to be around now. */
639 pConn->pfnStreamRetain(pConn, pStream);
640 }
641 }
642
643 if (RT_SUCCESS(rc))
644 {
645 pMixStream->fFlags = fFlags;
646 pMixStream->pConn = pConn;
647
648 if (ppStream)
649 *ppStream = pMixStream;
650 }
651 else if (pMixStream)
652 {
653 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
654 AssertRC(rc2);
655
656 if (pMixStream->pszName)
657 {
658 RTStrFree(pMixStream->pszName);
659 pMixStream->pszName = NULL;
660 }
661
662 RTMemFree(pMixStream);
663 pMixStream = NULL;
664 }
665
666 int rc2 = RTCritSectLeave(&pSink->CritSect);
667 AssertRC(rc2);
668
669 return rc;
670}
671
672/**
673 * Static helper function to translate a sink command
674 * to a PDM audio stream command.
675 *
676 * @returns PDM audio stream command, or PDMAUDIOSTREAMCMD_UNKNOWN if not found.
677 * @param enmCmd Mixer sink command to translate.
678 */
679static PDMAUDIOSTREAMCMD audioMixerSinkToStreamCmd(AUDMIXSINKCMD enmCmd)
680{
681 switch (enmCmd)
682 {
683 case AUDMIXSINKCMD_ENABLE: return PDMAUDIOSTREAMCMD_ENABLE;
684 case AUDMIXSINKCMD_DISABLE: return PDMAUDIOSTREAMCMD_DISABLE;
685 case AUDMIXSINKCMD_PAUSE: return PDMAUDIOSTREAMCMD_PAUSE;
686 case AUDMIXSINKCMD_RESUME: return PDMAUDIOSTREAMCMD_RESUME;
687 default: break;
688 }
689
690 AssertMsgFailed(("Unsupported sink command %d\n", enmCmd));
691 return PDMAUDIOSTREAMCMD_UNKNOWN;
692}
693
694/**
695 * Controls a mixer sink.
696 *
697 * @returns IPRT status code.
698 * @param pSink Mixer sink to control.
699 * @param enmSinkCmd Sink command to set.
700 */
701int AudioMixerSinkCtl(PAUDMIXSINK pSink, AUDMIXSINKCMD enmSinkCmd)
702{
703 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
704
705 PDMAUDIOSTREAMCMD enmCmdStream = audioMixerSinkToStreamCmd(enmSinkCmd);
706 if (enmCmdStream == PDMAUDIOSTREAMCMD_UNKNOWN)
707 return VERR_NOT_SUPPORTED;
708
709 int rc = RTCritSectEnter(&pSink->CritSect);
710 if (RT_FAILURE(rc))
711 return rc;
712
713 PAUDMIXSTREAM pStream;
714 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
715 {
716 int rc2 = audioMixerStreamCtlInternal(pStream, enmCmdStream, AUDMIXSTRMCTL_FLAG_NONE);
717 if (RT_SUCCESS(rc))
718 rc = rc2;
719 /* Keep going. Flag? */
720 }
721
722 if (enmSinkCmd == AUDMIXSINKCMD_ENABLE)
723 {
724 /* Make sure to clear any other former flags again by assigning AUDMIXSINK_STS_RUNNING directly. */
725 pSink->fStatus = AUDMIXSINK_STS_RUNNING;
726 }
727 else if (enmSinkCmd == AUDMIXSINKCMD_DISABLE)
728 {
729 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
730 {
731 /* Set the sink in a pending disable state first.
732 * The final status (disabled) will be set in the sink's iteration. */
733 pSink->fStatus |= AUDMIXSINK_STS_PENDING_DISABLE;
734 }
735 }
736
737#ifdef LOG_ENABLED
738 char *pszStatus = dbgAudioMixerSinkStatusToStr(pSink->fStatus);
739 LogFlowFunc(("[%s] enmCmd=%d, fStatus=%s, rc=%Rrc\n", pSink->pszName, enmSinkCmd, pszStatus, rc));
740 RTStrFree(pszStatus);
741#endif
742
743 int rc2 = RTCritSectLeave(&pSink->CritSect);
744 AssertRC(rc2);
745
746 return rc;
747}
748
749/**
750 * Destroys a mixer sink and removes it from the attached mixer (if any).
751 *
752 * @param pSink Mixer sink to destroy.
753 */
754void AudioMixerSinkDestroy(PAUDMIXSINK pSink)
755{
756 if (!pSink)
757 return;
758
759 int rc2 = RTCritSectEnter(&pSink->CritSect);
760 AssertRC(rc2);
761
762 if (pSink->pParent)
763 {
764 /* Save mixer pointer, as after audioMixerRemoveSinkInternal() the
765 * pointer will be gone from the stream. */
766 PAUDIOMIXER pMixer = pSink->pParent;
767 AssertPtr(pMixer);
768
769 audioMixerRemoveSinkInternal(pMixer, pSink);
770
771 Assert(pMixer->cSinks);
772 pMixer->cSinks--;
773 }
774
775 rc2 = RTCritSectLeave(&pSink->CritSect);
776 AssertRC(rc2);
777
778 audioMixerSinkDestroyInternal(pSink);
779}
780
781/**
782 * Destroys a mixer sink.
783 *
784 * @param pSink Mixer sink to destroy.
785 */
786static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink)
787{
788 AssertPtrReturnVoid(pSink);
789
790 LogFunc(("%s\n", pSink->pszName));
791
792 PAUDMIXSTREAM pStream, pStreamNext;
793 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
794 {
795 /* Save a pointer to the stream to remove, as pStream
796 * will not be valid anymore after calling audioMixerSinkRemoveStreamInternal(). */
797 PAUDMIXSTREAM pStreamToRemove = pStream;
798
799 audioMixerSinkRemoveStreamInternal(pSink, pStreamToRemove);
800 audioMixerStreamDestroyInternal(pStreamToRemove);
801 }
802
803 if (pSink->pszName)
804 {
805 RTStrFree(pSink->pszName);
806 pSink->pszName = NULL;
807 }
808
809 RTCritSectDelete(&pSink->CritSect);
810
811 RTMemFree(pSink);
812 pSink = NULL;
813}
814
815/**
816 * Returns the amount of bytes ready to be read from a sink since the last call
817 * to AudioMixerSinkUpdate().
818 *
819 * @returns Amount of bytes ready to be read from the sink.
820 * @param pSink Sink to return number of available bytes for.
821 */
822uint32_t AudioMixerSinkGetReadable(PAUDMIXSINK pSink)
823{
824 AssertPtrReturn(pSink, 0);
825
826 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT, ("%s: Can't read from a non-input sink\n", pSink->pszName));
827
828 int rc = RTCritSectEnter(&pSink->CritSect);
829 if (RT_FAILURE(rc))
830 return 0;
831
832 uint32_t cbReadable = 0;
833
834#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
835# error "Implement me!"
836#else
837 /* The hosts sets the pace --
838 * so we try to find the maximum of readable data of all connected streams to this sink. */
839 PAUDMIXSTREAM pMixStream;
840 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
841 {
842 if (!(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
843 {
844 Log3Func(("[%s] Stream '%s' disabled, skipping ...\n", pSink->pszName, pMixStream->pszName));
845 continue;
846 }
847
848 cbReadable = RT_MAX(cbReadable,
849 pMixStream->pConn->pfnStreamGetReadable(pMixStream->pConn, pMixStream->pStream));
850
851 break; /** @todo For now we only support recording by the first stream added. */
852 }
853#endif
854
855 Log3Func(("[%s] cbReadable=%RU32\n", pSink->pszName, cbReadable));
856
857 int rc2 = RTCritSectLeave(&pSink->CritSect);
858 AssertRC(rc2);
859
860 return cbReadable;
861}
862
863/**
864 * Returns the amount of bytes ready to be written to a sink since the last call
865 * to AudioMixerSinkUpdate().
866 *
867 * @returns Amount of bytes ready to be written to the sink.
868 * @param pSink Sink to return number of available bytes for.
869 */
870uint32_t AudioMixerSinkGetWritable(PAUDMIXSINK pSink)
871{
872 AssertPtrReturn(pSink, 0);
873
874 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT, ("%s: Can't write to a non-output sink\n", pSink->pszName));
875
876 int rc = RTCritSectEnter(&pSink->CritSect);
877 if (RT_FAILURE(rc))
878 return 0;
879
880 uint32_t cbWritable = UINT32_MAX;
881
882 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
883 && !(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
884 {
885#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
886# error "Implement me!"
887#else
888 /* The hosts sets the pace --
889 * so we try to find the minimum of writable data to all connected streams to this sink. */
890 PAUDMIXSTREAM pMixStream;
891 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
892 {
893 if (!(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
894 {
895 Log3Func(("[%s] Stream '%s' disabled, skipping ...\n", pSink->pszName, pMixStream->pszName));
896 continue;
897 }
898
899 const uint32_t cbWritableStream = pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream);
900
901 if (cbWritableStream < cbWritable)
902 cbWritable = cbWritableStream;
903 }
904#endif
905 }
906
907 if (cbWritable == UINT32_MAX)
908 cbWritable = 0;
909
910 Log3Func(("[%s] cbWritable=%RU32\n", pSink->pszName, cbWritable));
911
912 int rc2 = RTCritSectLeave(&pSink->CritSect);
913 AssertRC(rc2);
914
915 return cbWritable;
916}
917
918/**
919 * Returns the sink's mixing direction.
920 *
921 * @returns Mixing direction.
922 * @param pSink Sink to return direction for.
923 */
924AUDMIXSINKDIR AudioMixerSinkGetDir(PAUDMIXSINK pSink)
925{
926 AssertPtrReturn(pSink, AUDMIXSINKDIR_UNKNOWN);
927
928 int rc = RTCritSectEnter(&pSink->CritSect);
929 if (RT_FAILURE(rc))
930 return AUDMIXSINKDIR_UNKNOWN;
931
932 AUDMIXSINKDIR enmDir = pSink->enmDir;
933
934 int rc2 = RTCritSectLeave(&pSink->CritSect);
935 AssertRC(rc2);
936
937 return enmDir;
938}
939
940/**
941 * Returns a specific mixer stream from a sink, based on its index.
942 *
943 * @returns Mixer stream if found, or NULL if not found.
944 * @param pSink Sink to retrieve mixer stream from.
945 * @param uIndex Index of the mixer stream to return.
946 */
947PAUDMIXSTREAM AudioMixerSinkGetStream(PAUDMIXSINK pSink, uint8_t uIndex)
948{
949 AssertPtrReturn(pSink, NULL);
950
951 int rc = RTCritSectEnter(&pSink->CritSect);
952 if (RT_FAILURE(rc))
953 return NULL;
954
955 AssertMsgReturn(uIndex < pSink->cStreams,
956 ("Index %RU8 exceeds stream count (%RU8)", uIndex, pSink->cStreams), NULL);
957
958 /* Slow lookup, d'oh. */
959 PAUDMIXSTREAM pStream = RTListGetFirst(&pSink->lstStreams, AUDMIXSTREAM, Node);
960 while (uIndex)
961 {
962 pStream = RTListGetNext(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node);
963 uIndex--;
964 }
965
966 /** @todo Do we need to raise the stream's reference count here? */
967
968 int rc2 = RTCritSectLeave(&pSink->CritSect);
969 AssertRC(rc2);
970
971 AssertPtr(pStream);
972 return pStream;
973}
974
975/**
976 * Returns the current status of a mixer sink.
977 *
978 * @returns The sink's current status.
979 * @param pSink Mixer sink to return status for.
980 */
981AUDMIXSINKSTS AudioMixerSinkGetStatus(PAUDMIXSINK pSink)
982{
983 if (!pSink)
984 return AUDMIXSINK_STS_NONE;
985
986 int rc2 = RTCritSectEnter(&pSink->CritSect);
987 if (RT_FAILURE(rc2))
988 return AUDMIXSINK_STS_NONE;
989
990 /* If the dirty flag is set, there is unprocessed data in the sink. */
991 AUDMIXSINKSTS stsSink = pSink->fStatus;
992
993 rc2 = RTCritSectLeave(&pSink->CritSect);
994 AssertRC(rc2);
995
996 return stsSink;
997}
998
999/**
1000 * Returns the number of attached mixer streams to a mixer sink.
1001 *
1002 * @returns The number of attached mixer streams.
1003 * @param pSink Mixer sink to return number for.
1004 */
1005uint8_t AudioMixerSinkGetStreamCount(PAUDMIXSINK pSink)
1006{
1007 if (!pSink)
1008 return 0;
1009
1010 int rc2 = RTCritSectEnter(&pSink->CritSect);
1011 if (RT_FAILURE(rc2))
1012 return 0;
1013
1014 uint8_t cStreams = pSink->cStreams;
1015
1016 rc2 = RTCritSectLeave(&pSink->CritSect);
1017 AssertRC(rc2);
1018
1019 return cStreams;
1020}
1021
1022/**
1023 * Returns whether the sink is in an active state or not.
1024 * Note: The pending disable state also counts as active.
1025 *
1026 * @returns True if active, false if not.
1027 * @param pSink Sink to return active state for.
1028 */
1029bool AudioMixerSinkIsActive(PAUDMIXSINK pSink)
1030{
1031 if (!pSink)
1032 return false;
1033
1034 int rc2 = RTCritSectEnter(&pSink->CritSect);
1035 if (RT_FAILURE(rc2))
1036 return false;
1037
1038 bool fIsActive = pSink->fStatus & AUDMIXSINK_STS_RUNNING;
1039 /* Note: AUDMIXSINK_STS_PENDING_DISABLE implies AUDMIXSINK_STS_RUNNING. */
1040
1041 Log3Func(("[%s] fActive=%RTbool\n", pSink->pszName, fIsActive));
1042
1043 rc2 = RTCritSectLeave(&pSink->CritSect);
1044 AssertRC(rc2);
1045
1046 return fIsActive;
1047}
1048
1049/**
1050 * Reads audio data from a mixer sink.
1051 *
1052 * @returns IPRT status code.
1053 * @param pSink Mixer sink to read data from.
1054 * @param enmOp Mixer operation to use for reading the data.
1055 * @param pvBuf Buffer where to store the read data.
1056 * @param cbBuf Buffer size (in bytes) where to store the data.
1057 * @param pcbRead Number of bytes read. Optional.
1058 */
1059int AudioMixerSinkRead(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1060{
1061 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1062 RT_NOREF(enmOp);
1063 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1064 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1065 /* pcbRead is optional. */
1066
1067 /** @todo Handle mixing operation enmOp! */
1068
1069 int rc = RTCritSectEnter(&pSink->CritSect);
1070 if (RT_FAILURE(rc))
1071 return rc;
1072
1073 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT,
1074 ("Can't read from a sink which is not an input sink\n"));
1075
1076#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1077# error "Implement me!"
1078#else
1079 uint8_t *pvMixBuf = (uint8_t *)RTMemAlloc(cbBuf);
1080 if (!pvMixBuf)
1081 {
1082 int rc2 = RTCritSectLeave(&pSink->CritSect);
1083 AssertRC(rc2);
1084
1085 return VERR_NO_MEMORY;
1086 }
1087#endif
1088
1089 uint32_t cbRead = 0;
1090
1091 /* Flag indicating whether this sink is in a 'clean' state,
1092 * e.g. there is no more data to read from. */
1093 bool fClean = true;
1094
1095 PAUDMIXSTREAM pMixStream;
1096 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1097 {
1098 if (!(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
1099 {
1100 Log3Func(("[%s] Stream '%s' disabled, skipping ...\n", pSink->pszName, pMixStream->pszName));
1101 continue;
1102 }
1103
1104 uint32_t cbTotalRead = 0;
1105 uint32_t cbToRead = cbBuf;
1106
1107 int rc2 = VINF_SUCCESS;
1108
1109 while (cbToRead)
1110 {
1111 uint32_t cbReadStrm;
1112 AssertPtr(pMixStream->pConn);
1113#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1114# error "Implement me!"
1115#else
1116 rc2 = pMixStream->pConn->pfnStreamRead(pMixStream->pConn, pMixStream->pStream,
1117 (uint8_t *)pvMixBuf + cbTotalRead, cbToRead, &cbReadStrm);
1118#endif
1119 if (RT_FAILURE(rc2))
1120 LogFunc(("[%s] Failed reading from stream '%s': %Rrc\n", pSink->pszName, pMixStream->pszName, rc2));
1121
1122 Log3Func(("[%s] Stream '%s': Read %RU32 bytes\n", pSink->pszName, pMixStream->pszName, cbReadStrm));
1123
1124 if ( RT_FAILURE(rc2)
1125 || !cbReadStrm)
1126 break;
1127
1128 /** @todo Right now we only handle one stream (the last one added in fact). */
1129
1130 AssertBreakStmt(cbReadStrm <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
1131 cbToRead -= cbReadStrm;
1132 cbTotalRead += cbReadStrm;
1133 }
1134
1135 if (RT_FAILURE(rc2))
1136 continue;
1137
1138 cbRead = RT_MAX(cbRead, cbTotalRead);
1139
1140 uint32_t cbReadable = pMixStream->pConn->pfnStreamGetReadable(pMixStream->pConn, pMixStream->pStream);
1141
1142 /* Still some data available? Then sink is not clean (yet). */
1143 if (cbReadable)
1144 fClean = false;
1145 }
1146
1147 if (RT_SUCCESS(rc))
1148 {
1149 if (fClean)
1150 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1151
1152#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1153# error "Implement me!"
1154#else
1155 if (cbRead)
1156 memcpy(pvBuf, pvMixBuf, cbRead);
1157#endif
1158 if (pcbRead)
1159 *pcbRead = cbRead;
1160 }
1161
1162#ifndef VBOX_AUDIO_MIXER_WITH_MIXBUF
1163 RTMemFree(pvMixBuf);
1164#endif
1165
1166
1167#ifdef LOG_ENABLED
1168 char *pszStatus = dbgAudioMixerSinkStatusToStr(pSink->fStatus);
1169 Log2Func(("[%s] cbRead=%RU32, fClean=%RTbool, fStatus=%s, rc=%Rrc\n", pSink->pszName, cbRead, fClean, pszStatus, rc));
1170 RTStrFree(pszStatus);
1171#endif
1172
1173 int rc2 = RTCritSectLeave(&pSink->CritSect);
1174 AssertRC(rc2);
1175
1176 return rc;
1177}
1178
1179/**
1180 * Removes a mixer stream from a mixer sink, internal version.
1181 *
1182 * @returns IPRT status code.
1183 * @param pSink Sink to remove mixer stream from.
1184 * @param pStream Stream to remove.
1185 */
1186static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1187{
1188 AssertPtrReturn(pSink, VERR_INVALID_PARAMETER);
1189 if ( !pStream
1190 || !pStream->pSink) /* Not part of a sink anymore? */
1191 {
1192 return VERR_NOT_FOUND;
1193 }
1194
1195 AssertMsgReturn(pStream->pSink == pSink, ("Stream '%s' is not part of sink '%s'\n",
1196 pStream->pszName, pSink->pszName), VERR_NOT_FOUND);
1197
1198 LogFlowFunc(("[%s] (Stream = %s), cStreams=%RU8\n",
1199 pSink->pszName, pStream->pStream->szName, pSink->cStreams));
1200
1201#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1202 /* Unlink mixing buffer. */
1203 AudioMixBufUnlink(&pStream->pStream->MixBuf);
1204#endif
1205
1206 /* Remove stream from sink. */
1207 RTListNodeRemove(&pStream->Node);
1208
1209 /* Set sink to NULL so that we know we're not part of any sink anymore. */
1210 pStream->pSink = NULL;
1211
1212 return VINF_SUCCESS;
1213}
1214
1215/**
1216 * Removes a mixer stream from a mixer sink.
1217 *
1218 * @param pSink Sink to remove mixer stream from.
1219 * @param pStream Stream to remove.
1220 */
1221void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1222{
1223 int rc2 = RTCritSectEnter(&pSink->CritSect);
1224 AssertRC(rc2);
1225
1226 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pStream);
1227 if (RT_SUCCESS(rc2))
1228 {
1229 Assert(pSink->cStreams);
1230 pSink->cStreams--;
1231 }
1232
1233 rc2 = RTCritSectLeave(&pSink->CritSect);
1234 AssertRC(rc2);
1235}
1236
1237/**
1238 * Removes all attached streams from a given sink.
1239 *
1240 * @param pSink Sink to remove attached streams from.
1241 */
1242static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink)
1243{
1244 if (!pSink)
1245 return;
1246
1247 LogFunc(("%s\n", pSink->pszName));
1248
1249 PAUDMIXSTREAM pStream, pStreamNext;
1250 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
1251 audioMixerSinkRemoveStreamInternal(pSink, pStream);
1252}
1253
1254/**
1255 * Resets the sink's state.
1256 *
1257 * @param pSink Sink to reset.
1258 */
1259static void audioMixerSinkReset(PAUDMIXSINK pSink)
1260{
1261 if (!pSink)
1262 return;
1263
1264 LogFunc(("[%s]\n", pSink->pszName));
1265
1266 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1267 {
1268#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1269 AudioMixBufReset(&pSink->MixBuf);
1270#else
1271 pSink->In.cbReadable = 0;
1272#endif
1273 }
1274 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1275 {
1276#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1277 AudioMixBufReset(&pSink->MixBuf);
1278#else
1279 pSink->Out.cbWritable = 0;
1280#endif
1281 }
1282
1283 /* Update last updated timestamp. */
1284 pSink->tsLastUpdatedMS = RTTimeMilliTS();
1285
1286 /* Reset status. */
1287 pSink->fStatus = AUDMIXSINK_STS_NONE;
1288}
1289
1290/**
1291 * Removes all attached streams from a given sink.
1292 *
1293 * @param pSink Sink to remove attached streams from.
1294 */
1295void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
1296{
1297 if (!pSink)
1298 return;
1299
1300 int rc2 = RTCritSectEnter(&pSink->CritSect);
1301 AssertRC(rc2);
1302
1303 audioMixerSinkRemoveAllStreamsInternal(pSink);
1304
1305 pSink->cStreams = 0;
1306
1307 rc2 = RTCritSectLeave(&pSink->CritSect);
1308 AssertRC(rc2);
1309}
1310
1311/**
1312 * Resets a sink. This will immediately stop all processing.
1313 *
1314 * @param pSink Sink to reset.
1315 */
1316void AudioMixerSinkReset(PAUDMIXSINK pSink)
1317{
1318 if (!pSink)
1319 return;
1320
1321 int rc2 = RTCritSectEnter(&pSink->CritSect);
1322 AssertRC(rc2);
1323
1324 LogFlowFunc(("[%s]\n", pSink->pszName));
1325
1326 audioMixerSinkReset(pSink);
1327
1328 rc2 = RTCritSectLeave(&pSink->CritSect);
1329 AssertRC(rc2);
1330}
1331
1332/**
1333 * Sets the audio format of a mixer sink.
1334 *
1335 * @returns IPRT status code.
1336 * @param pSink Sink to set audio format for.
1337 * @param pPCMProps Audio format (PCM properties) to set.
1338 */
1339int AudioMixerSinkSetFormat(PAUDMIXSINK pSink, PPDMAUDIOPCMPROPS pPCMProps)
1340{
1341 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1342 AssertPtrReturn(pPCMProps, VERR_INVALID_POINTER);
1343
1344 int rc = RTCritSectEnter(&pSink->CritSect);
1345 if (RT_FAILURE(rc))
1346 return rc;
1347
1348 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, pPCMProps)) /* Bail out early if PCM properties are equal. */
1349 {
1350 rc = RTCritSectLeave(&pSink->CritSect);
1351 AssertRC(rc);
1352
1353 return rc;
1354 }
1355
1356 if (pSink->PCMProps.uHz)
1357 LogFlowFunc(("[%s] Old format: %RU8 bit, %RU8 channels, %RU32Hz\n",
1358 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
1359
1360 memcpy(&pSink->PCMProps, pPCMProps, sizeof(PDMAUDIOPCMPROPS));
1361
1362 LogFlowFunc(("[%s] New format %RU8 bit, %RU8 channels, %RU32Hz\n",
1363 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
1364
1365#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1366 /* Also update the sink's mixing buffer format. */
1367 AudioMixBufDestroy(&pSink->MixBuf);
1368 rc = AudioMixBufInit(&pSink->MixBuf, pSink->pszName, pPCMProps, _4K /** @todo Make configurable? */);
1369 if (RT_SUCCESS(rc))
1370 {
1371 PAUDMIXSTREAM pStream;
1372 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
1373 {
1374 /** @todo Invalidate mix buffers! */
1375 }
1376 }
1377#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
1378
1379 int rc2 = RTCritSectLeave(&pSink->CritSect);
1380 AssertRC(rc2);
1381
1382 LogFlowFuncLeaveRC(rc);
1383 return rc;
1384}
1385
1386/**
1387 * Set the volume of an individual sink.
1388 *
1389 * @returns IPRT status code.
1390 * @param pSink Sink to set volume for.
1391 * @param pVol Volume to set.
1392 */
1393int AudioMixerSinkSetVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
1394{
1395 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1396 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
1397
1398 int rc = RTCritSectEnter(&pSink->CritSect);
1399 if (RT_FAILURE(rc))
1400 return rc;
1401
1402 memcpy(&pSink->Volume, pVol, sizeof(PDMAUDIOVOLUME));
1403
1404 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1405 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1406
1407 AssertPtr(pSink->pParent);
1408 rc = audioMixerSinkUpdateVolume(pSink, &pSink->pParent->VolMaster);
1409
1410 int rc2 = RTCritSectLeave(&pSink->CritSect);
1411 AssertRC(rc2);
1412
1413 return rc;
1414}
1415
1416/**
1417 * Updates a mixer sink, internal version.
1418 *
1419 * @returns IPRT status code.
1420 * @param pSink Mixer sink to update.
1421 */
1422static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink)
1423{
1424 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1425
1426 int rc = VINF_SUCCESS;
1427
1428#ifdef LOG_ENABLED
1429 char *pszStatus = dbgAudioMixerSinkStatusToStr(pSink->fStatus);
1430 Log3Func(("[%s] fStatus=%s\n", pSink->pszName, pszStatus));
1431 RTStrFree(pszStatus);
1432#endif
1433
1434 /* Sink disabled? Take a shortcut. */
1435 if (!(pSink->fStatus & AUDMIXSINK_STS_RUNNING))
1436 return rc;
1437
1438 /* Number of detected disabled streams of this sink. */
1439 uint8_t cStreamsDisabled = 0;
1440
1441 PAUDMIXSTREAM pMixStream, pMixStreamNext;
1442 RTListForEachSafe(&pSink->lstStreams, pMixStream, pMixStreamNext, AUDMIXSTREAM, Node)
1443 {
1444 PPDMAUDIOSTREAM pStream = pMixStream->pStream;
1445 AssertPtr(pStream);
1446
1447 PPDMIAUDIOCONNECTOR pConn = pMixStream->pConn;
1448 AssertPtr(pConn);
1449
1450 uint32_t cfProc = 0;
1451
1452 int rc2 = pConn->pfnStreamIterate(pConn, pStream);
1453 if (RT_SUCCESS(rc2))
1454 {
1455 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1456 {
1457 rc = pConn->pfnStreamCapture(pConn, pStream, &cfProc);
1458 if (RT_FAILURE(rc2))
1459 {
1460 LogFunc(("%s: Failed capturing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1461 if (RT_SUCCESS(rc))
1462 rc = rc2;
1463 continue;
1464 }
1465
1466 if (cfProc)
1467 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1468 }
1469 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1470 {
1471 rc2 = pConn->pfnStreamPlay(pConn, pStream, &cfProc);
1472 if (RT_FAILURE(rc2))
1473 {
1474 LogFunc(("%s: Failed playing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1475 if (RT_SUCCESS(rc))
1476 rc = rc2;
1477 continue;
1478 }
1479 }
1480 else
1481 {
1482 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1483 continue;
1484 }
1485
1486 rc2 = pConn->pfnStreamIterate(pConn, pStream);
1487 if (RT_FAILURE(rc2))
1488 {
1489 LogFunc(("%s: Failed re-iterating stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1490 if (RT_SUCCESS(rc))
1491 rc = rc2;
1492 continue;
1493 }
1494
1495 PDMAUDIOSTREAMSTS strmSts = pConn->pfnStreamGetStatus(pConn, pStream);
1496
1497 /* Is the stream not enabled and also is not in a pending disable state anymore? */
1498 if ( !(strmSts & PDMAUDIOSTREAMSTS_FLAG_ENABLED)
1499 && !(strmSts & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE))
1500 {
1501 cStreamsDisabled++;
1502 }
1503 }
1504
1505 Log3Func(("\t%s: cPlayed/cCaptured=%RU32, rc2=%Rrc\n", pStream->szName, cfProc, rc2));
1506 }
1507
1508 Log3Func(("[%s] fPendingDisable=%RTbool, %RU8/%RU8 streams disabled\n",
1509 pSink->pszName, RT_BOOL(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE), cStreamsDisabled, pSink->cStreams));
1510
1511 /* Update last updated timestamp. */
1512 pSink->tsLastUpdatedMS = RTTimeMilliTS();
1513
1514 /* All streams disabled and the sink is in pending disable mode? */
1515 if ( cStreamsDisabled == pSink->cStreams
1516 && (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
1517 {
1518 audioMixerSinkReset(pSink);
1519 }
1520
1521 Log3Func(("[%s] cbReadable=%RU32, cbWritable=%RU32, rc=%Rrc\n",
1522 pSink->pszName, pSink->In.cbReadable, pSink->Out.cbWritable, rc));
1523
1524 return rc;
1525}
1526
1527/**
1528 * Updates (invalidates) a mixer sink.
1529 *
1530 * @returns IPRT status code.
1531 * @param pSink Mixer sink to update.
1532 */
1533int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
1534{
1535 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1536
1537 int rc = RTCritSectEnter(&pSink->CritSect);
1538 if (RT_FAILURE(rc))
1539 return rc;
1540
1541 rc = audioMixerSinkUpdateInternal(pSink);
1542
1543 int rc2 = RTCritSectLeave(&pSink->CritSect);
1544 AssertRC(rc2);
1545
1546 return rc;
1547}
1548
1549/**
1550 * Updates the (master) volume of a mixer sink.
1551 *
1552 * @returns IPRT status code.
1553 * @param pSink Mixer sink to update volume for.
1554 * @param pVolMaster Master volume to set.
1555 */
1556static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
1557{
1558 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1559 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
1560
1561 LogFlowFunc(("[%s] Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1562 pSink->pszName, pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
1563 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32 ",
1564 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1565
1566 /** @todo Very crude implementation for now -- needs more work! */
1567
1568 pSink->VolumeCombined.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
1569
1570 pSink->VolumeCombined.uLeft = ( (pSink->Volume.uLeft ? pSink->Volume.uLeft : 1)
1571 * (pVolMaster->uLeft ? pVolMaster->uLeft : 1)) / PDMAUDIO_VOLUME_MAX;
1572
1573 pSink->VolumeCombined.uRight = ( (pSink->Volume.uRight ? pSink->Volume.uRight : 1)
1574 * (pVolMaster->uRight ? pVolMaster->uRight : 1)) / PDMAUDIO_VOLUME_MAX;
1575
1576 LogFlow(("-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1577 pSink->VolumeCombined.fMuted, pSink->VolumeCombined.uLeft, pSink->VolumeCombined.uRight));
1578
1579 /* Propagate new sink volume to all streams in the sink. */
1580 PAUDMIXSTREAM pMixStream;
1581 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1582 {
1583 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &pSink->VolumeCombined);
1584 AssertRC(rc2);
1585 }
1586
1587 return VINF_SUCCESS;
1588}
1589
1590/**
1591 * Writes data to a mixer sink.
1592 *
1593 * @returns IPRT status code.
1594 * @param pSink Sink to write data to.
1595 * @param enmOp Mixer operation to use when writing data to the sink.
1596 * @param pvBuf Buffer containing the audio data to write.
1597 * @param cbBuf Size (in bytes) of the buffer containing the audio data.
1598 * @param pcbWritten Number of bytes written. Optional.
1599 */
1600int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
1601{
1602 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1603 RT_NOREF(enmOp);
1604 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1605 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
1606 /* pcbWritten is optional. */
1607
1608 int rc = RTCritSectEnter(&pSink->CritSect);
1609 if (RT_FAILURE(rc))
1610 return rc;
1611
1612 AssertMsg(pSink->fStatus & AUDMIXSINK_STS_RUNNING,
1613 ("%s: Can't write to a sink which is not running (anymore) (status 0x%x)\n", pSink->pszName, pSink->fStatus));
1614 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
1615 ("%s: Can't write to a sink which is not an output sink\n", pSink->pszName));
1616
1617 Log3Func(("[%s] enmOp=%d, cbBuf=%RU32\n", pSink->pszName, enmOp, cbBuf));
1618
1619 uint32_t cbWritten = UINT32_MAX;
1620
1621 PAUDMIXSTREAM pMixStream;
1622 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1623 {
1624 if (!(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
1625 {
1626 Log3Func(("\t%s: Stream '%s' disabled, skipping ...\n", pMixStream->pszName, pMixStream->pszName));
1627 continue;
1628 }
1629
1630 uint32_t cbProcessed = 0;
1631 int rc2 = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, pvBuf, cbBuf, &cbProcessed);
1632 if (RT_FAILURE(rc2))
1633 LogFunc(("[%s] Failed writing to stream '%s': %Rrc\n", pSink->pszName, pMixStream->pszName, rc2));
1634
1635 Log3Func(("[%s] Written %RU32 to '%s'\n", pSink->pszName, cbProcessed, pMixStream->pszName));
1636
1637 if (cbProcessed)
1638 {
1639 /* Set dirty bit. */
1640 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1641
1642 /*
1643 * Return the minimum bytes processed by all connected streams.
1644 * The host sets the pace, so all backends have to behave accordingly.
1645 */
1646 if (cbWritten > cbProcessed)
1647 cbWritten = cbProcessed;
1648 }
1649 }
1650
1651 if (cbWritten == UINT32_MAX)
1652 cbWritten = 0;
1653
1654 Log3Func(("[%s] cbWritten=%RU32\n", pSink->pszName, cbWritten));
1655
1656 if (pcbWritten)
1657 *pcbWritten = cbWritten;
1658
1659 int rc2 = RTCritSectLeave(&pSink->CritSect);
1660 AssertRC(rc2);
1661
1662 return rc;
1663}
1664
1665/*********************************************************************************************************************************
1666 * Mixer Stream implementation.
1667 ********************************************************************************************************************************/
1668
1669/**
1670 * Controls a mixer stream, internal version.
1671 *
1672 * @returns IPRT status code.
1673 * @param pMixStream Mixer stream to control.
1674 * @param enmCmd Mixer stream command to use.
1675 * @param fCtl Additional control flags. Pass 0.
1676 */
1677int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
1678{
1679 AssertPtr(pMixStream->pConn);
1680 AssertPtr(pMixStream->pStream);
1681
1682 RT_NOREF(fCtl);
1683
1684 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
1685
1686 LogFlowFunc(("[%s] enmCmd=%ld, rc=%Rrc\n", pMixStream->pszName, enmCmd, rc));
1687
1688 return rc;
1689}
1690
1691/**
1692 * Controls a mixer stream.
1693 *
1694 * @returns IPRT status code.
1695 * @param pMixStream Mixer stream to control.
1696 * @param enmCmd Mixer stream command to use.
1697 * @param fCtl Additional control flags. Pass 0.
1698 */
1699int AudioMixerStreamCtl(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
1700{
1701 RT_NOREF(fCtl);
1702 AssertPtrReturn(pMixStream, VERR_INVALID_POINTER);
1703 /** @todo Validate fCtl. */
1704
1705 int rc = RTCritSectEnter(&pMixStream->CritSect);
1706 if (RT_FAILURE(rc))
1707 return rc;
1708
1709 rc = audioMixerStreamCtlInternal(pMixStream, enmCmd, fCtl);
1710
1711 int rc2 = RTCritSectLeave(&pMixStream->CritSect);
1712 if (RT_SUCCESS(rc))
1713 rc = rc2;
1714
1715 return rc;
1716}
1717
1718/**
1719 * Destroys a mixer stream, internal version.
1720 *
1721 * @param pMixStream Mixer stream to destroy.
1722 */
1723static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream)
1724{
1725 AssertPtrReturnVoid(pMixStream);
1726
1727 LogFunc(("%s\n", pMixStream->pszName));
1728
1729 if (pMixStream->pConn) /* Stream has a connector interface present? */
1730 {
1731 if (pMixStream->pStream)
1732 {
1733 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
1734 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
1735
1736 pMixStream->pStream = NULL;
1737 }
1738
1739 pMixStream->pConn = NULL;
1740 }
1741
1742 if (pMixStream->pszName)
1743 {
1744 RTStrFree(pMixStream->pszName);
1745 pMixStream->pszName = NULL;
1746 }
1747
1748 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
1749 AssertRC(rc2);
1750
1751 RTMemFree(pMixStream);
1752 pMixStream = NULL;
1753}
1754
1755/**
1756 * Destroys a mixer stream.
1757 *
1758 * @param pMixStream Mixer stream to destroy.
1759 */
1760void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream)
1761{
1762 if (!pMixStream)
1763 return;
1764
1765 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1766 AssertRC(rc2);
1767
1768 LogFunc(("%s\n", pMixStream->pszName));
1769
1770 if (pMixStream->pSink) /* Is the stream part of a sink? */
1771 {
1772 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
1773 * pointer will be gone from the stream. */
1774 PAUDMIXSINK pSink = pMixStream->pSink;
1775
1776 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
1777 if (RT_SUCCESS(rc2))
1778 {
1779 Assert(pSink->cStreams);
1780 pSink->cStreams--;
1781 }
1782 }
1783 else
1784 rc2 = VINF_SUCCESS;
1785
1786 int rc3 = RTCritSectLeave(&pMixStream->CritSect);
1787 AssertRC(rc3);
1788
1789 if (RT_SUCCESS(rc2))
1790 {
1791 audioMixerStreamDestroyInternal(pMixStream);
1792 pMixStream = NULL;
1793 }
1794
1795 LogFlowFunc(("Returning %Rrc\n", rc2));
1796}
1797
1798/**
1799 * Returns whether a mixer stream currently is active (playing/recording) or not.
1800 *
1801 * @returns @c true if playing/recording, @c false if not.
1802 * @param pMixStream Mixer stream to return status for.
1803 */
1804bool AudioMixerStreamIsActive(PAUDMIXSTREAM pMixStream)
1805{
1806 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1807 if (RT_FAILURE(rc2))
1808 return false;
1809
1810 AssertPtr(pMixStream->pConn);
1811 AssertPtr(pMixStream->pStream);
1812
1813 bool fIsActive;
1814
1815 if ( pMixStream->pConn
1816 && pMixStream->pStream
1817 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
1818 {
1819 fIsActive = true;
1820 }
1821 else
1822 fIsActive = false;
1823
1824 rc2 = RTCritSectLeave(&pMixStream->CritSect);
1825 AssertRC(rc2);
1826
1827 return fIsActive;
1828}
1829
1830/**
1831 * Returns whether a mixer stream is valid (e.g. initialized and in a working state) or not.
1832 *
1833 * @returns @c true if valid, @c false if not.
1834 * @param pMixStream Mixer stream to return status for.
1835 */
1836bool AudioMixerStreamIsValid(PAUDMIXSTREAM pMixStream)
1837{
1838 if (!pMixStream)
1839 return false;
1840
1841 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1842 if (RT_FAILURE(rc2))
1843 return false;
1844
1845 bool fIsValid;
1846
1847 if ( pMixStream->pConn
1848 && pMixStream->pStream
1849 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED))
1850 {
1851 fIsValid = true;
1852 }
1853 else
1854 fIsValid = false;
1855
1856 rc2 = RTCritSectLeave(&pMixStream->CritSect);
1857 AssertRC(rc2);
1858
1859 return fIsValid;
1860}
1861
Note: See TracBrowser for help on using the repository browser.

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