VirtualBox

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

Last change on this file since 68898 was 68898, checked in by vboxsync, 7 years ago

Audio/Mixer: Added AudioMixerSinkGetFormat().

  • 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 68898 2017-09-28 08:41:40Z 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 const uint32_t cbWritableStream = pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream);
894
895 if (cbWritableStream < cbWritable)
896 cbWritable = cbWritableStream;
897 }
898#endif
899 }
900
901 if (cbWritable == UINT32_MAX)
902 cbWritable = 0;
903
904 Log3Func(("[%s] cbWritable=%RU32\n", pSink->pszName, cbWritable));
905
906 int rc2 = RTCritSectLeave(&pSink->CritSect);
907 AssertRC(rc2);
908
909 return cbWritable;
910}
911
912/**
913 * Returns the sink's mixing direction.
914 *
915 * @returns Mixing direction.
916 * @param pSink Sink to return direction for.
917 */
918AUDMIXSINKDIR AudioMixerSinkGetDir(PAUDMIXSINK pSink)
919{
920 AssertPtrReturn(pSink, AUDMIXSINKDIR_UNKNOWN);
921
922 int rc = RTCritSectEnter(&pSink->CritSect);
923 if (RT_FAILURE(rc))
924 return AUDMIXSINKDIR_UNKNOWN;
925
926 AUDMIXSINKDIR enmDir = pSink->enmDir;
927
928 int rc2 = RTCritSectLeave(&pSink->CritSect);
929 AssertRC(rc2);
930
931 return enmDir;
932}
933
934/**
935 * Returns a specific mixer stream from a sink, based on its index.
936 *
937 * @returns Mixer stream if found, or NULL if not found.
938 * @param pSink Sink to retrieve mixer stream from.
939 * @param uIndex Index of the mixer stream to return.
940 */
941PAUDMIXSTREAM AudioMixerSinkGetStream(PAUDMIXSINK pSink, uint8_t uIndex)
942{
943 AssertPtrReturn(pSink, NULL);
944
945 int rc = RTCritSectEnter(&pSink->CritSect);
946 if (RT_FAILURE(rc))
947 return NULL;
948
949 AssertMsgReturn(uIndex < pSink->cStreams,
950 ("Index %RU8 exceeds stream count (%RU8)", uIndex, pSink->cStreams), NULL);
951
952 /* Slow lookup, d'oh. */
953 PAUDMIXSTREAM pStream = RTListGetFirst(&pSink->lstStreams, AUDMIXSTREAM, Node);
954 while (uIndex)
955 {
956 pStream = RTListGetNext(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node);
957 uIndex--;
958 }
959
960 /** @todo Do we need to raise the stream's reference count here? */
961
962 int rc2 = RTCritSectLeave(&pSink->CritSect);
963 AssertRC(rc2);
964
965 AssertPtr(pStream);
966 return pStream;
967}
968
969/**
970 * Returns the current status of a mixer sink.
971 *
972 * @returns The sink's current status.
973 * @param pSink Mixer sink to return status for.
974 */
975AUDMIXSINKSTS AudioMixerSinkGetStatus(PAUDMIXSINK pSink)
976{
977 if (!pSink)
978 return AUDMIXSINK_STS_NONE;
979
980 int rc2 = RTCritSectEnter(&pSink->CritSect);
981 if (RT_FAILURE(rc2))
982 return AUDMIXSINK_STS_NONE;
983
984 /* If the dirty flag is set, there is unprocessed data in the sink. */
985 AUDMIXSINKSTS stsSink = pSink->fStatus;
986
987 rc2 = RTCritSectLeave(&pSink->CritSect);
988 AssertRC(rc2);
989
990 return stsSink;
991}
992
993/**
994 * Returns the number of attached mixer streams to a mixer sink.
995 *
996 * @returns The number of attached mixer streams.
997 * @param pSink Mixer sink to return number for.
998 */
999uint8_t AudioMixerSinkGetStreamCount(PAUDMIXSINK pSink)
1000{
1001 if (!pSink)
1002 return 0;
1003
1004 int rc2 = RTCritSectEnter(&pSink->CritSect);
1005 if (RT_FAILURE(rc2))
1006 return 0;
1007
1008 uint8_t cStreams = pSink->cStreams;
1009
1010 rc2 = RTCritSectLeave(&pSink->CritSect);
1011 AssertRC(rc2);
1012
1013 return cStreams;
1014}
1015
1016/**
1017 * Returns whether the sink is in an active state or not.
1018 * Note: The pending disable state also counts as active.
1019 *
1020 * @returns True if active, false if not.
1021 * @param pSink Sink to return active state for.
1022 */
1023bool AudioMixerSinkIsActive(PAUDMIXSINK pSink)
1024{
1025 if (!pSink)
1026 return false;
1027
1028 int rc2 = RTCritSectEnter(&pSink->CritSect);
1029 if (RT_FAILURE(rc2))
1030 return false;
1031
1032 bool fIsActive = pSink->fStatus & AUDMIXSINK_STS_RUNNING;
1033 /* Note: AUDMIXSINK_STS_PENDING_DISABLE implies AUDMIXSINK_STS_RUNNING. */
1034
1035 Log3Func(("[%s] fActive=%RTbool\n", pSink->pszName, fIsActive));
1036
1037 rc2 = RTCritSectLeave(&pSink->CritSect);
1038 AssertRC(rc2);
1039
1040 return fIsActive;
1041}
1042
1043/**
1044 * Reads audio data from a mixer sink.
1045 *
1046 * @returns IPRT status code.
1047 * @param pSink Mixer sink to read data from.
1048 * @param enmOp Mixer operation to use for reading the data.
1049 * @param pvBuf Buffer where to store the read data.
1050 * @param cbBuf Buffer size (in bytes) where to store the data.
1051 * @param pcbRead Number of bytes read. Optional.
1052 */
1053int AudioMixerSinkRead(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1054{
1055 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1056 RT_NOREF(enmOp);
1057 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1058 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1059 /* pcbRead is optional. */
1060
1061 /** @todo Handle mixing operation enmOp! */
1062
1063 int rc = RTCritSectEnter(&pSink->CritSect);
1064 if (RT_FAILURE(rc))
1065 return rc;
1066
1067 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT,
1068 ("Can't read from a sink which is not an input sink\n"));
1069
1070#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1071# error "Implement me!"
1072#else
1073 uint8_t *pvMixBuf = (uint8_t *)RTMemAlloc(cbBuf);
1074 if (!pvMixBuf)
1075 {
1076 int rc2 = RTCritSectLeave(&pSink->CritSect);
1077 AssertRC(rc2);
1078
1079 return VERR_NO_MEMORY;
1080 }
1081#endif
1082
1083 uint32_t cbRead = 0;
1084
1085 /* Flag indicating whether this sink is in a 'clean' state,
1086 * e.g. there is no more data to read from. */
1087 bool fClean = true;
1088
1089 PAUDMIXSTREAM pMixStream;
1090 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1091 {
1092 if (!(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
1093 {
1094 Log3Func(("[%s] Stream '%s' disabled, skipping ...\n", pSink->pszName, pMixStream->pszName));
1095 continue;
1096 }
1097
1098 uint32_t cbTotalRead = 0;
1099 uint32_t cbToRead = cbBuf;
1100
1101 int rc2 = VINF_SUCCESS;
1102
1103 while (cbToRead)
1104 {
1105 uint32_t cbReadStrm;
1106 AssertPtr(pMixStream->pConn);
1107#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1108# error "Implement me!"
1109#else
1110 rc2 = pMixStream->pConn->pfnStreamRead(pMixStream->pConn, pMixStream->pStream,
1111 (uint8_t *)pvMixBuf + cbTotalRead, cbToRead, &cbReadStrm);
1112#endif
1113 if (RT_FAILURE(rc2))
1114 LogFunc(("[%s] Failed reading from stream '%s': %Rrc\n", pSink->pszName, pMixStream->pszName, rc2));
1115
1116 Log3Func(("[%s] Stream '%s': Read %RU32 bytes\n", pSink->pszName, pMixStream->pszName, cbReadStrm));
1117
1118 if ( RT_FAILURE(rc2)
1119 || !cbReadStrm)
1120 break;
1121
1122 /** @todo Right now we only handle one stream (the last one added in fact). */
1123
1124 AssertBreakStmt(cbReadStrm <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
1125 cbToRead -= cbReadStrm;
1126 cbTotalRead += cbReadStrm;
1127 }
1128
1129 if (RT_FAILURE(rc2))
1130 continue;
1131
1132 cbRead = RT_MAX(cbRead, cbTotalRead);
1133
1134 uint32_t cbReadable = pMixStream->pConn->pfnStreamGetReadable(pMixStream->pConn, pMixStream->pStream);
1135
1136 /* Still some data available? Then sink is not clean (yet). */
1137 if (cbReadable)
1138 fClean = false;
1139 }
1140
1141 if (RT_SUCCESS(rc))
1142 {
1143 if (fClean)
1144 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1145
1146#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1147# error "Implement me!"
1148#else
1149 if (cbRead)
1150 memcpy(pvBuf, pvMixBuf, cbRead);
1151#endif
1152 if (pcbRead)
1153 *pcbRead = cbRead;
1154 }
1155
1156#ifndef VBOX_AUDIO_MIXER_WITH_MIXBUF
1157 RTMemFree(pvMixBuf);
1158#endif
1159
1160
1161#ifdef LOG_ENABLED
1162 char *pszStatus = dbgAudioMixerSinkStatusToStr(pSink->fStatus);
1163 Log2Func(("[%s] cbRead=%RU32, fClean=%RTbool, fStatus=%s, rc=%Rrc\n", pSink->pszName, cbRead, fClean, pszStatus, rc));
1164 RTStrFree(pszStatus);
1165#endif
1166
1167 int rc2 = RTCritSectLeave(&pSink->CritSect);
1168 AssertRC(rc2);
1169
1170 return rc;
1171}
1172
1173/**
1174 * Removes a mixer stream from a mixer sink, internal version.
1175 *
1176 * @returns IPRT status code.
1177 * @param pSink Sink to remove mixer stream from.
1178 * @param pStream Stream to remove.
1179 */
1180static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1181{
1182 AssertPtrReturn(pSink, VERR_INVALID_PARAMETER);
1183 if ( !pStream
1184 || !pStream->pSink) /* Not part of a sink anymore? */
1185 {
1186 return VERR_NOT_FOUND;
1187 }
1188
1189 AssertMsgReturn(pStream->pSink == pSink, ("Stream '%s' is not part of sink '%s'\n",
1190 pStream->pszName, pSink->pszName), VERR_NOT_FOUND);
1191
1192 LogFlowFunc(("[%s] (Stream = %s), cStreams=%RU8\n",
1193 pSink->pszName, pStream->pStream->szName, pSink->cStreams));
1194
1195#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1196 /* Unlink mixing buffer. */
1197 AudioMixBufUnlink(&pStream->pStream->MixBuf);
1198#endif
1199
1200 /* Remove stream from sink. */
1201 RTListNodeRemove(&pStream->Node);
1202
1203 /* Set sink to NULL so that we know we're not part of any sink anymore. */
1204 pStream->pSink = NULL;
1205
1206 return VINF_SUCCESS;
1207}
1208
1209/**
1210 * Removes a mixer stream from a mixer sink.
1211 *
1212 * @param pSink Sink to remove mixer stream from.
1213 * @param pStream Stream to remove.
1214 */
1215void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1216{
1217 int rc2 = RTCritSectEnter(&pSink->CritSect);
1218 AssertRC(rc2);
1219
1220 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pStream);
1221 if (RT_SUCCESS(rc2))
1222 {
1223 Assert(pSink->cStreams);
1224 pSink->cStreams--;
1225 }
1226
1227 rc2 = RTCritSectLeave(&pSink->CritSect);
1228 AssertRC(rc2);
1229}
1230
1231/**
1232 * Removes all attached streams from a given sink.
1233 *
1234 * @param pSink Sink to remove attached streams from.
1235 */
1236static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink)
1237{
1238 if (!pSink)
1239 return;
1240
1241 LogFunc(("%s\n", pSink->pszName));
1242
1243 PAUDMIXSTREAM pStream, pStreamNext;
1244 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
1245 audioMixerSinkRemoveStreamInternal(pSink, pStream);
1246}
1247
1248/**
1249 * Resets the sink's state.
1250 *
1251 * @param pSink Sink to reset.
1252 */
1253static void audioMixerSinkReset(PAUDMIXSINK pSink)
1254{
1255 if (!pSink)
1256 return;
1257
1258 LogFunc(("[%s]\n", pSink->pszName));
1259
1260 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1261 {
1262#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1263 AudioMixBufReset(&pSink->MixBuf);
1264#else
1265 pSink->In.cbReadable = 0;
1266#endif
1267 }
1268 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1269 {
1270#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1271 AudioMixBufReset(&pSink->MixBuf);
1272#else
1273 pSink->Out.cbWritable = 0;
1274#endif
1275 }
1276
1277 /* Update last updated timestamp. */
1278 pSink->tsLastUpdatedMS = RTTimeMilliTS();
1279
1280 /* Reset status. */
1281 pSink->fStatus = AUDMIXSINK_STS_NONE;
1282}
1283
1284/**
1285 * Removes all attached streams from a given sink.
1286 *
1287 * @param pSink Sink to remove attached streams from.
1288 */
1289void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
1290{
1291 if (!pSink)
1292 return;
1293
1294 int rc2 = RTCritSectEnter(&pSink->CritSect);
1295 AssertRC(rc2);
1296
1297 audioMixerSinkRemoveAllStreamsInternal(pSink);
1298
1299 pSink->cStreams = 0;
1300
1301 rc2 = RTCritSectLeave(&pSink->CritSect);
1302 AssertRC(rc2);
1303}
1304
1305/**
1306 * Resets a sink. This will immediately stop all processing.
1307 *
1308 * @param pSink Sink to reset.
1309 */
1310void AudioMixerSinkReset(PAUDMIXSINK pSink)
1311{
1312 if (!pSink)
1313 return;
1314
1315 int rc2 = RTCritSectEnter(&pSink->CritSect);
1316 AssertRC(rc2);
1317
1318 LogFlowFunc(("[%s]\n", pSink->pszName));
1319
1320 audioMixerSinkReset(pSink);
1321
1322 rc2 = RTCritSectLeave(&pSink->CritSect);
1323 AssertRC(rc2);
1324}
1325
1326/**
1327 * Returns the audio format of a mixer sink.
1328 *
1329 * @returns IPRT status code.
1330 * @param pSink Sink to retrieve audio format for.
1331 * @param pPCMProps Where to the returned audio format.
1332 */
1333void AudioMixerSinkGetFormat(PAUDMIXSINK pSink, PPDMAUDIOPCMPROPS pPCMProps)
1334{
1335 AssertPtrReturnVoid(pSink);
1336 AssertPtrReturnVoid(pPCMProps);
1337
1338 int rc2 = RTCritSectEnter(&pSink->CritSect);
1339 if (RT_FAILURE(rc2))
1340 return;
1341
1342 memcpy(pPCMProps, &pSink->PCMProps, sizeof(PDMAUDIOPCMPROPS));
1343
1344 rc2 = RTCritSectLeave(&pSink->CritSect);
1345 AssertRC(rc2);
1346}
1347
1348/**
1349 * Sets the audio format of a mixer sink.
1350 *
1351 * @returns IPRT status code.
1352 * @param pSink Sink to set audio format for.
1353 * @param pPCMProps Audio format (PCM properties) to set.
1354 */
1355int AudioMixerSinkSetFormat(PAUDMIXSINK pSink, PPDMAUDIOPCMPROPS pPCMProps)
1356{
1357 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1358 AssertPtrReturn(pPCMProps, VERR_INVALID_POINTER);
1359
1360 int rc = RTCritSectEnter(&pSink->CritSect);
1361 if (RT_FAILURE(rc))
1362 return rc;
1363
1364 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, pPCMProps)) /* Bail out early if PCM properties are equal. */
1365 {
1366 rc = RTCritSectLeave(&pSink->CritSect);
1367 AssertRC(rc);
1368
1369 return rc;
1370 }
1371
1372 if (pSink->PCMProps.uHz)
1373 LogFlowFunc(("[%s] Old format: %RU8 bit, %RU8 channels, %RU32Hz\n",
1374 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
1375
1376 memcpy(&pSink->PCMProps, pPCMProps, sizeof(PDMAUDIOPCMPROPS));
1377
1378 LogFlowFunc(("[%s] New format %RU8 bit, %RU8 channels, %RU32Hz\n",
1379 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
1380
1381#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1382 /* Also update the sink's mixing buffer format. */
1383 AudioMixBufDestroy(&pSink->MixBuf);
1384 rc = AudioMixBufInit(&pSink->MixBuf, pSink->pszName, pPCMProps, _4K /** @todo Make configurable? */);
1385 if (RT_SUCCESS(rc))
1386 {
1387 PAUDMIXSTREAM pStream;
1388 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
1389 {
1390 /** @todo Invalidate mix buffers! */
1391 }
1392 }
1393#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
1394
1395 int rc2 = RTCritSectLeave(&pSink->CritSect);
1396 AssertRC(rc2);
1397
1398 LogFlowFuncLeaveRC(rc);
1399 return rc;
1400}
1401
1402/**
1403 * Set the volume of an individual sink.
1404 *
1405 * @returns IPRT status code.
1406 * @param pSink Sink to set volume for.
1407 * @param pVol Volume to set.
1408 */
1409int AudioMixerSinkSetVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
1410{
1411 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1412 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
1413
1414 int rc = RTCritSectEnter(&pSink->CritSect);
1415 if (RT_FAILURE(rc))
1416 return rc;
1417
1418 memcpy(&pSink->Volume, pVol, sizeof(PDMAUDIOVOLUME));
1419
1420 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1421 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1422
1423 AssertPtr(pSink->pParent);
1424 rc = audioMixerSinkUpdateVolume(pSink, &pSink->pParent->VolMaster);
1425
1426 int rc2 = RTCritSectLeave(&pSink->CritSect);
1427 AssertRC(rc2);
1428
1429 return rc;
1430}
1431
1432/**
1433 * Updates a mixer sink, internal version.
1434 *
1435 * @returns IPRT status code.
1436 * @param pSink Mixer sink to update.
1437 */
1438static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink)
1439{
1440 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1441
1442 int rc = VINF_SUCCESS;
1443
1444#ifdef LOG_ENABLED
1445 char *pszStatus = dbgAudioMixerSinkStatusToStr(pSink->fStatus);
1446 Log3Func(("[%s] fStatus=%s\n", pSink->pszName, pszStatus));
1447 RTStrFree(pszStatus);
1448#endif
1449
1450 /* Sink disabled? Take a shortcut. */
1451 if (!(pSink->fStatus & AUDMIXSINK_STS_RUNNING))
1452 return rc;
1453
1454 /* Number of detected disabled streams of this sink. */
1455 uint8_t cStreamsDisabled = 0;
1456
1457 PAUDMIXSTREAM pMixStream, pMixStreamNext;
1458 RTListForEachSafe(&pSink->lstStreams, pMixStream, pMixStreamNext, AUDMIXSTREAM, Node)
1459 {
1460 PPDMAUDIOSTREAM pStream = pMixStream->pStream;
1461 AssertPtr(pStream);
1462
1463 PPDMIAUDIOCONNECTOR pConn = pMixStream->pConn;
1464 AssertPtr(pConn);
1465
1466 uint32_t cfProc = 0;
1467
1468 int rc2 = pConn->pfnStreamIterate(pConn, pStream);
1469 if (RT_SUCCESS(rc2))
1470 {
1471 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1472 {
1473 rc = pConn->pfnStreamCapture(pConn, pStream, &cfProc);
1474 if (RT_FAILURE(rc2))
1475 {
1476 LogFunc(("%s: Failed capturing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1477 if (RT_SUCCESS(rc))
1478 rc = rc2;
1479 continue;
1480 }
1481
1482 if (cfProc)
1483 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1484 }
1485 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1486 {
1487 rc2 = pConn->pfnStreamPlay(pConn, pStream, &cfProc);
1488 if (RT_FAILURE(rc2))
1489 {
1490 LogFunc(("%s: Failed playing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1491 if (RT_SUCCESS(rc))
1492 rc = rc2;
1493 continue;
1494 }
1495 }
1496 else
1497 {
1498 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1499 continue;
1500 }
1501
1502 rc2 = pConn->pfnStreamIterate(pConn, pStream);
1503 if (RT_FAILURE(rc2))
1504 {
1505 LogFunc(("%s: Failed re-iterating stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1506 if (RT_SUCCESS(rc))
1507 rc = rc2;
1508 continue;
1509 }
1510
1511 PDMAUDIOSTREAMSTS strmSts = pConn->pfnStreamGetStatus(pConn, pStream);
1512
1513 /* Is the stream not enabled and also is not in a pending disable state anymore? */
1514 if ( !(strmSts & PDMAUDIOSTREAMSTS_FLAG_ENABLED)
1515 && !(strmSts & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE))
1516 {
1517 cStreamsDisabled++;
1518 }
1519 }
1520
1521 Log3Func(("\t%s: cPlayed/cCaptured=%RU32, rc2=%Rrc\n", pStream->szName, cfProc, rc2));
1522 }
1523
1524 Log3Func(("[%s] fPendingDisable=%RTbool, %RU8/%RU8 streams disabled\n",
1525 pSink->pszName, RT_BOOL(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE), cStreamsDisabled, pSink->cStreams));
1526
1527 /* Update last updated timestamp. */
1528 pSink->tsLastUpdatedMS = RTTimeMilliTS();
1529
1530 /* All streams disabled and the sink is in pending disable mode? */
1531 if ( cStreamsDisabled == pSink->cStreams
1532 && (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
1533 {
1534 audioMixerSinkReset(pSink);
1535 }
1536
1537 Log3Func(("[%s] cbReadable=%RU32, cbWritable=%RU32, rc=%Rrc\n",
1538 pSink->pszName, pSink->In.cbReadable, pSink->Out.cbWritable, rc));
1539
1540 return rc;
1541}
1542
1543/**
1544 * Updates (invalidates) a mixer sink.
1545 *
1546 * @returns IPRT status code.
1547 * @param pSink Mixer sink to update.
1548 */
1549int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
1550{
1551 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1552
1553 int rc = RTCritSectEnter(&pSink->CritSect);
1554 if (RT_FAILURE(rc))
1555 return rc;
1556
1557 rc = audioMixerSinkUpdateInternal(pSink);
1558
1559 int rc2 = RTCritSectLeave(&pSink->CritSect);
1560 AssertRC(rc2);
1561
1562 return rc;
1563}
1564
1565/**
1566 * Updates the (master) volume of a mixer sink.
1567 *
1568 * @returns IPRT status code.
1569 * @param pSink Mixer sink to update volume for.
1570 * @param pVolMaster Master volume to set.
1571 */
1572static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
1573{
1574 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1575 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
1576
1577 LogFlowFunc(("[%s] Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1578 pSink->pszName, pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
1579 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32 ",
1580 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1581
1582 /** @todo Very crude implementation for now -- needs more work! */
1583
1584 pSink->VolumeCombined.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
1585
1586 pSink->VolumeCombined.uLeft = ( (pSink->Volume.uLeft ? pSink->Volume.uLeft : 1)
1587 * (pVolMaster->uLeft ? pVolMaster->uLeft : 1)) / PDMAUDIO_VOLUME_MAX;
1588
1589 pSink->VolumeCombined.uRight = ( (pSink->Volume.uRight ? pSink->Volume.uRight : 1)
1590 * (pVolMaster->uRight ? pVolMaster->uRight : 1)) / PDMAUDIO_VOLUME_MAX;
1591
1592 LogFlow(("-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1593 pSink->VolumeCombined.fMuted, pSink->VolumeCombined.uLeft, pSink->VolumeCombined.uRight));
1594
1595 /* Propagate new sink volume to all streams in the sink. */
1596 PAUDMIXSTREAM pMixStream;
1597 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1598 {
1599 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &pSink->VolumeCombined);
1600 AssertRC(rc2);
1601 }
1602
1603 return VINF_SUCCESS;
1604}
1605
1606/**
1607 * Writes data to a mixer sink.
1608 *
1609 * @returns IPRT status code.
1610 * @param pSink Sink to write data to.
1611 * @param enmOp Mixer operation to use when writing data to the sink.
1612 * @param pvBuf Buffer containing the audio data to write.
1613 * @param cbBuf Size (in bytes) of the buffer containing the audio data.
1614 * @param pcbWritten Number of bytes written. Optional.
1615 */
1616int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
1617{
1618 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1619 RT_NOREF(enmOp);
1620 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1621 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
1622 /* pcbWritten is optional. */
1623
1624 int rc = RTCritSectEnter(&pSink->CritSect);
1625 if (RT_FAILURE(rc))
1626 return rc;
1627
1628 AssertMsg(pSink->fStatus & AUDMIXSINK_STS_RUNNING,
1629 ("%s: Can't write to a sink which is not running (anymore) (status 0x%x)\n", pSink->pszName, pSink->fStatus));
1630 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
1631 ("%s: Can't write to a sink which is not an output sink\n", pSink->pszName));
1632
1633 Log3Func(("[%s] enmOp=%d, cbBuf=%RU32\n", pSink->pszName, enmOp, cbBuf));
1634
1635 uint32_t cbWritten = UINT32_MAX;
1636
1637 PAUDMIXSTREAM pMixStream;
1638 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1639 {
1640 uint32_t cbProcessed = 0;
1641 int rc2 = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, pvBuf, cbBuf, &cbProcessed);
1642 if (RT_FAILURE(rc2))
1643 LogFunc(("[%s] Failed writing to stream '%s': %Rrc\n", pSink->pszName, pMixStream->pszName, rc2));
1644
1645 Log3Func(("[%s] Written %RU32 to '%s'\n", pSink->pszName, cbProcessed, pMixStream->pszName));
1646
1647 if (cbProcessed)
1648 {
1649 /* Set dirty bit. */
1650 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1651
1652 /*
1653 * Return the minimum bytes processed by all connected streams.
1654 * The host sets the pace, so all backends have to behave accordingly.
1655 */
1656 if (cbWritten > cbProcessed)
1657 cbWritten = cbProcessed;
1658 }
1659 }
1660
1661 if (cbWritten == UINT32_MAX)
1662 cbWritten = 0;
1663
1664 Log3Func(("[%s] cbWritten=%RU32\n", pSink->pszName, cbWritten));
1665
1666 if (pcbWritten)
1667 *pcbWritten = cbWritten;
1668
1669 int rc2 = RTCritSectLeave(&pSink->CritSect);
1670 AssertRC(rc2);
1671
1672 return rc;
1673}
1674
1675/*********************************************************************************************************************************
1676 * Mixer Stream implementation.
1677 ********************************************************************************************************************************/
1678
1679/**
1680 * Controls a mixer stream, internal version.
1681 *
1682 * @returns IPRT status code.
1683 * @param pMixStream Mixer stream to control.
1684 * @param enmCmd Mixer stream command to use.
1685 * @param fCtl Additional control flags. Pass 0.
1686 */
1687int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
1688{
1689 AssertPtr(pMixStream->pConn);
1690 AssertPtr(pMixStream->pStream);
1691
1692 RT_NOREF(fCtl);
1693
1694 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
1695
1696 LogFlowFunc(("[%s] enmCmd=%ld, rc=%Rrc\n", pMixStream->pszName, enmCmd, rc));
1697
1698 return rc;
1699}
1700
1701/**
1702 * Controls a mixer stream.
1703 *
1704 * @returns IPRT status code.
1705 * @param pMixStream Mixer stream to control.
1706 * @param enmCmd Mixer stream command to use.
1707 * @param fCtl Additional control flags. Pass 0.
1708 */
1709int AudioMixerStreamCtl(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
1710{
1711 RT_NOREF(fCtl);
1712 AssertPtrReturn(pMixStream, VERR_INVALID_POINTER);
1713 /** @todo Validate fCtl. */
1714
1715 int rc = RTCritSectEnter(&pMixStream->CritSect);
1716 if (RT_FAILURE(rc))
1717 return rc;
1718
1719 rc = audioMixerStreamCtlInternal(pMixStream, enmCmd, fCtl);
1720
1721 int rc2 = RTCritSectLeave(&pMixStream->CritSect);
1722 if (RT_SUCCESS(rc))
1723 rc = rc2;
1724
1725 return rc;
1726}
1727
1728/**
1729 * Destroys a mixer stream, internal version.
1730 *
1731 * @param pMixStream Mixer stream to destroy.
1732 */
1733static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream)
1734{
1735 AssertPtrReturnVoid(pMixStream);
1736
1737 LogFunc(("%s\n", pMixStream->pszName));
1738
1739 if (pMixStream->pConn) /* Stream has a connector interface present? */
1740 {
1741 if (pMixStream->pStream)
1742 {
1743 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
1744 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
1745
1746 pMixStream->pStream = NULL;
1747 }
1748
1749 pMixStream->pConn = NULL;
1750 }
1751
1752 if (pMixStream->pszName)
1753 {
1754 RTStrFree(pMixStream->pszName);
1755 pMixStream->pszName = NULL;
1756 }
1757
1758 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
1759 AssertRC(rc2);
1760
1761 RTMemFree(pMixStream);
1762 pMixStream = NULL;
1763}
1764
1765/**
1766 * Destroys a mixer stream.
1767 *
1768 * @param pMixStream Mixer stream to destroy.
1769 */
1770void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream)
1771{
1772 if (!pMixStream)
1773 return;
1774
1775 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1776 AssertRC(rc2);
1777
1778 LogFunc(("%s\n", pMixStream->pszName));
1779
1780 if (pMixStream->pSink) /* Is the stream part of a sink? */
1781 {
1782 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
1783 * pointer will be gone from the stream. */
1784 PAUDMIXSINK pSink = pMixStream->pSink;
1785
1786 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
1787 if (RT_SUCCESS(rc2))
1788 {
1789 Assert(pSink->cStreams);
1790 pSink->cStreams--;
1791 }
1792 }
1793 else
1794 rc2 = VINF_SUCCESS;
1795
1796 int rc3 = RTCritSectLeave(&pMixStream->CritSect);
1797 AssertRC(rc3);
1798
1799 if (RT_SUCCESS(rc2))
1800 {
1801 audioMixerStreamDestroyInternal(pMixStream);
1802 pMixStream = NULL;
1803 }
1804
1805 LogFlowFunc(("Returning %Rrc\n", rc2));
1806}
1807
1808/**
1809 * Returns whether a mixer stream currently is active (playing/recording) or not.
1810 *
1811 * @returns @c true if playing/recording, @c false if not.
1812 * @param pMixStream Mixer stream to return status for.
1813 */
1814bool AudioMixerStreamIsActive(PAUDMIXSTREAM pMixStream)
1815{
1816 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1817 if (RT_FAILURE(rc2))
1818 return false;
1819
1820 AssertPtr(pMixStream->pConn);
1821 AssertPtr(pMixStream->pStream);
1822
1823 bool fIsActive;
1824
1825 if ( pMixStream->pConn
1826 && pMixStream->pStream
1827 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
1828 {
1829 fIsActive = true;
1830 }
1831 else
1832 fIsActive = false;
1833
1834 rc2 = RTCritSectLeave(&pMixStream->CritSect);
1835 AssertRC(rc2);
1836
1837 return fIsActive;
1838}
1839
1840/**
1841 * Returns whether a mixer stream is valid (e.g. initialized and in a working state) or not.
1842 *
1843 * @returns @c true if valid, @c false if not.
1844 * @param pMixStream Mixer stream to return status for.
1845 */
1846bool AudioMixerStreamIsValid(PAUDMIXSTREAM pMixStream)
1847{
1848 if (!pMixStream)
1849 return false;
1850
1851 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1852 if (RT_FAILURE(rc2))
1853 return false;
1854
1855 bool fIsValid;
1856
1857 if ( pMixStream->pConn
1858 && pMixStream->pStream
1859 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED))
1860 {
1861 fIsValid = true;
1862 }
1863 else
1864 fIsValid = false;
1865
1866 rc2 = RTCritSectLeave(&pMixStream->CritSect);
1867 AssertRC(rc2);
1868
1869 return fIsValid;
1870}
1871
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