VirtualBox

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

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

Audio/Mixer: Added .WAV output support for mixer sinks (when VBOX_AUDIO_MIXER_DEBUG is set).

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