VirtualBox

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

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

DrvAudioCommon.cpp: Renamed DrvAudioHlp*FileName() -> DrvAudioHlpFileName*().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 58.0 KB
Line 
1/* $Id: AudioMixer.cpp 73381 2018-07-27 09:16:19Z 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-2018 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 rc = RTCircBufCreate(&pMixStream->pCircBuf, DrvAudioHlpMsToBytes(100 /* ms */, &pSink->PCMProps)); /** @todo Make this configurable. */
656 AssertRC(rc);
657 }
658
659 if (RT_SUCCESS(rc))
660 {
661 pMixStream->fFlags = fFlags;
662 pMixStream->pConn = pConn;
663
664 if (ppStream)
665 *ppStream = pMixStream;
666 }
667 else if (pMixStream)
668 {
669 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
670 AssertRC(rc2);
671
672 if (pMixStream->pszName)
673 {
674 RTStrFree(pMixStream->pszName);
675 pMixStream->pszName = NULL;
676 }
677
678 RTMemFree(pMixStream);
679 pMixStream = NULL;
680 }
681
682 int rc2 = RTCritSectLeave(&pSink->CritSect);
683 AssertRC(rc2);
684
685 return rc;
686}
687
688/**
689 * Static helper function to translate a sink command
690 * to a PDM audio stream command.
691 *
692 * @returns PDM audio stream command, or PDMAUDIOSTREAMCMD_UNKNOWN if not found.
693 * @param enmCmd Mixer sink command to translate.
694 */
695static PDMAUDIOSTREAMCMD audioMixerSinkToStreamCmd(AUDMIXSINKCMD enmCmd)
696{
697 switch (enmCmd)
698 {
699 case AUDMIXSINKCMD_ENABLE: return PDMAUDIOSTREAMCMD_ENABLE;
700 case AUDMIXSINKCMD_DISABLE: return PDMAUDIOSTREAMCMD_DISABLE;
701 case AUDMIXSINKCMD_PAUSE: return PDMAUDIOSTREAMCMD_PAUSE;
702 case AUDMIXSINKCMD_RESUME: return PDMAUDIOSTREAMCMD_RESUME;
703 default: break;
704 }
705
706 AssertMsgFailed(("Unsupported sink command %d\n", enmCmd));
707 return PDMAUDIOSTREAMCMD_UNKNOWN;
708}
709
710/**
711 * Controls a mixer sink.
712 *
713 * @returns IPRT status code.
714 * @param pSink Mixer sink to control.
715 * @param enmSinkCmd Sink command to set.
716 */
717int AudioMixerSinkCtl(PAUDMIXSINK pSink, AUDMIXSINKCMD enmSinkCmd)
718{
719 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
720
721 PDMAUDIOSTREAMCMD enmCmdStream = audioMixerSinkToStreamCmd(enmSinkCmd);
722 if (enmCmdStream == PDMAUDIOSTREAMCMD_UNKNOWN)
723 return VERR_NOT_SUPPORTED;
724
725 int rc = RTCritSectEnter(&pSink->CritSect);
726 if (RT_FAILURE(rc))
727 return rc;
728
729 PAUDMIXSTREAM pStream;
730 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
731 {
732 int rc2 = audioMixerStreamCtlInternal(pStream, enmCmdStream, AUDMIXSTRMCTL_FLAG_NONE);
733 if (RT_SUCCESS(rc))
734 rc = rc2;
735 /* Keep going. Flag? */
736 }
737
738 if (enmSinkCmd == AUDMIXSINKCMD_ENABLE)
739 {
740 /* Make sure to clear any other former flags again by assigning AUDMIXSINK_STS_RUNNING directly. */
741 pSink->fStatus = AUDMIXSINK_STS_RUNNING;
742 }
743 else if (enmSinkCmd == AUDMIXSINKCMD_DISABLE)
744 {
745 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
746 {
747 /* Set the sink in a pending disable state first.
748 * The final status (disabled) will be set in the sink's iteration. */
749 pSink->fStatus |= AUDMIXSINK_STS_PENDING_DISABLE;
750 }
751 }
752
753#ifdef LOG_ENABLED
754 char *pszStatus = dbgAudioMixerSinkStatusToStr(pSink->fStatus);
755 LogFlowFunc(("[%s] enmCmd=%d, fStatus=%s, rc=%Rrc\n", pSink->pszName, enmSinkCmd, pszStatus, rc));
756 RTStrFree(pszStatus);
757#endif
758
759 int rc2 = RTCritSectLeave(&pSink->CritSect);
760 AssertRC(rc2);
761
762 return rc;
763}
764
765/**
766 * Destroys a mixer sink and removes it from the attached mixer (if any).
767 *
768 * @param pSink Mixer sink to destroy.
769 */
770void AudioMixerSinkDestroy(PAUDMIXSINK pSink)
771{
772 if (!pSink)
773 return;
774
775 int rc2 = RTCritSectEnter(&pSink->CritSect);
776 AssertRC(rc2);
777
778 if (pSink->pParent)
779 {
780 /* Save mixer pointer, as after audioMixerRemoveSinkInternal() the
781 * pointer will be gone from the stream. */
782 PAUDIOMIXER pMixer = pSink->pParent;
783 AssertPtr(pMixer);
784
785 audioMixerRemoveSinkInternal(pMixer, pSink);
786
787 Assert(pMixer->cSinks);
788 pMixer->cSinks--;
789 }
790
791 rc2 = RTCritSectLeave(&pSink->CritSect);
792 AssertRC(rc2);
793
794 audioMixerSinkDestroyInternal(pSink);
795}
796
797/**
798 * Destroys a mixer sink.
799 *
800 * @param pSink Mixer sink to destroy.
801 */
802static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink)
803{
804 AssertPtrReturnVoid(pSink);
805
806 LogFunc(("%s\n", pSink->pszName));
807
808 PAUDMIXSTREAM pStream, pStreamNext;
809 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
810 {
811 /* Save a pointer to the stream to remove, as pStream
812 * will not be valid anymore after calling audioMixerSinkRemoveStreamInternal(). */
813 PAUDMIXSTREAM pStreamToRemove = pStream;
814
815 audioMixerSinkRemoveStreamInternal(pSink, pStreamToRemove);
816 audioMixerStreamDestroyInternal(pStreamToRemove);
817 }
818
819#ifdef VBOX_AUDIO_MIXER_DEBUG
820 DrvAudioHlpFileDestroy(pSink->Dbg.pFile);
821 pSink->Dbg.pFile = NULL;
822#endif
823
824 if (pSink->pszName)
825 {
826 RTStrFree(pSink->pszName);
827 pSink->pszName = NULL;
828 }
829
830 RTCritSectDelete(&pSink->CritSect);
831
832 RTMemFree(pSink);
833 pSink = NULL;
834}
835
836/**
837 * Returns the amount of bytes ready to be read from a sink since the last call
838 * to AudioMixerSinkUpdate().
839 *
840 * @returns Amount of bytes ready to be read from the sink.
841 * @param pSink Sink to return number of available bytes for.
842 */
843uint32_t AudioMixerSinkGetReadable(PAUDMIXSINK pSink)
844{
845 AssertPtrReturn(pSink, 0);
846
847 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT, ("%s: Can't read from a non-input sink\n", pSink->pszName));
848
849 int rc = RTCritSectEnter(&pSink->CritSect);
850 if (RT_FAILURE(rc))
851 return 0;
852
853 uint32_t cbReadable = 0;
854
855 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
856 {
857#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
858# error "Implement me!"
859#else
860 /* Return how much data we can deliver since the last read. */
861 cbReadable = DrvAudioHlpMsToBytes(RTTimeMilliTS() - pSink->tsLastReadWrittenMs, &pSink->PCMProps);
862#endif
863 }
864
865 Log3Func(("[%s] cbReadable=%RU32\n", pSink->pszName, cbReadable));
866
867 int rc2 = RTCritSectLeave(&pSink->CritSect);
868 AssertRC(rc2);
869
870 return cbReadable;
871}
872
873/**
874 * Returns the amount of bytes ready to be written to a sink since the last call
875 * to AudioMixerSinkUpdate().
876 *
877 * @returns Amount of bytes ready to be written to the sink.
878 * @param pSink Sink to return number of available bytes for.
879 */
880uint32_t AudioMixerSinkGetWritable(PAUDMIXSINK pSink)
881{
882 AssertPtrReturn(pSink, 0);
883
884 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT, ("%s: Can't write to a non-output sink\n", pSink->pszName));
885
886 int rc = RTCritSectEnter(&pSink->CritSect);
887 if (RT_FAILURE(rc))
888 return 0;
889
890 uint32_t cbWritable = 0;
891
892 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
893 && !(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
894 {
895#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
896# error "Implement me!"
897#else
898 /* Return how much data we expect since the last write. */
899 cbWritable = DrvAudioHlpMsToBytes(RTTimeMilliTS() - pSink->tsLastReadWrittenMs, &pSink->PCMProps);
900#endif
901 }
902
903 Log3Func(("[%s] cbWritable=%RU32\n", pSink->pszName, cbWritable));
904
905 int rc2 = RTCritSectLeave(&pSink->CritSect);
906 AssertRC(rc2);
907
908 return cbWritable;
909}
910
911/**
912 * Returns the sink's mixing direction.
913 *
914 * @returns Mixing direction.
915 * @param pSink Sink to return direction for.
916 */
917AUDMIXSINKDIR AudioMixerSinkGetDir(PAUDMIXSINK pSink)
918{
919 AssertPtrReturn(pSink, AUDMIXSINKDIR_UNKNOWN);
920
921 int rc = RTCritSectEnter(&pSink->CritSect);
922 if (RT_FAILURE(rc))
923 return AUDMIXSINKDIR_UNKNOWN;
924
925 AUDMIXSINKDIR enmDir = pSink->enmDir;
926
927 int rc2 = RTCritSectLeave(&pSink->CritSect);
928 AssertRC(rc2);
929
930 return enmDir;
931}
932
933/**
934 * Returns a specific mixer stream from a sink, based on its index.
935 *
936 * @returns Mixer stream if found, or NULL if not found.
937 * @param pSink Sink to retrieve mixer stream from.
938 * @param uIndex Index of the mixer stream to return.
939 */
940PAUDMIXSTREAM AudioMixerSinkGetStream(PAUDMIXSINK pSink, uint8_t uIndex)
941{
942 AssertPtrReturn(pSink, NULL);
943
944 int rc = RTCritSectEnter(&pSink->CritSect);
945 if (RT_FAILURE(rc))
946 return NULL;
947
948 AssertMsgReturn(uIndex < pSink->cStreams,
949 ("Index %RU8 exceeds stream count (%RU8)", uIndex, pSink->cStreams), NULL);
950
951 /* Slow lookup, d'oh. */
952 PAUDMIXSTREAM pStream = RTListGetFirst(&pSink->lstStreams, AUDMIXSTREAM, Node);
953 while (uIndex)
954 {
955 pStream = RTListGetNext(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node);
956 uIndex--;
957 }
958
959 /** @todo Do we need to raise the stream's reference count here? */
960
961 int rc2 = RTCritSectLeave(&pSink->CritSect);
962 AssertRC(rc2);
963
964 AssertPtr(pStream);
965 return pStream;
966}
967
968/**
969 * Returns the current status of a mixer sink.
970 *
971 * @returns The sink's current status.
972 * @param pSink Mixer sink to return status for.
973 */
974AUDMIXSINKSTS AudioMixerSinkGetStatus(PAUDMIXSINK pSink)
975{
976 if (!pSink)
977 return AUDMIXSINK_STS_NONE;
978
979 int rc2 = RTCritSectEnter(&pSink->CritSect);
980 if (RT_FAILURE(rc2))
981 return AUDMIXSINK_STS_NONE;
982
983 /* If the dirty flag is set, there is unprocessed data in the sink. */
984 AUDMIXSINKSTS stsSink = pSink->fStatus;
985
986 rc2 = RTCritSectLeave(&pSink->CritSect);
987 AssertRC(rc2);
988
989 return stsSink;
990}
991
992/**
993 * Returns the number of attached mixer streams to a mixer sink.
994 *
995 * @returns The number of attached mixer streams.
996 * @param pSink Mixer sink to return number for.
997 */
998uint8_t AudioMixerSinkGetStreamCount(PAUDMIXSINK pSink)
999{
1000 if (!pSink)
1001 return 0;
1002
1003 int rc2 = RTCritSectEnter(&pSink->CritSect);
1004 if (RT_FAILURE(rc2))
1005 return 0;
1006
1007 uint8_t cStreams = pSink->cStreams;
1008
1009 rc2 = RTCritSectLeave(&pSink->CritSect);
1010 AssertRC(rc2);
1011
1012 return cStreams;
1013}
1014
1015/**
1016 * Returns whether the sink is in an active state or not.
1017 * Note: The pending disable state also counts as active.
1018 *
1019 * @returns True if active, false if not.
1020 * @param pSink Sink to return active state for.
1021 */
1022bool AudioMixerSinkIsActive(PAUDMIXSINK pSink)
1023{
1024 if (!pSink)
1025 return false;
1026
1027 int rc2 = RTCritSectEnter(&pSink->CritSect);
1028 if (RT_FAILURE(rc2))
1029 return false;
1030
1031 bool fIsActive = pSink->fStatus & AUDMIXSINK_STS_RUNNING;
1032 /* Note: AUDMIXSINK_STS_PENDING_DISABLE implies AUDMIXSINK_STS_RUNNING. */
1033
1034 Log3Func(("[%s] fActive=%RTbool\n", pSink->pszName, fIsActive));
1035
1036 rc2 = RTCritSectLeave(&pSink->CritSect);
1037 AssertRC(rc2);
1038
1039 return fIsActive;
1040}
1041
1042/**
1043 * Reads audio data from a mixer sink.
1044 *
1045 * @returns IPRT status code.
1046 * @param pSink Mixer sink to read data from.
1047 * @param enmOp Mixer operation to use for reading the data.
1048 * @param pvBuf Buffer where to store the read data.
1049 * @param cbBuf Buffer size (in bytes) where to store the data.
1050 * @param pcbRead Number of bytes read. Optional.
1051 */
1052int AudioMixerSinkRead(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1053{
1054 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1055 RT_NOREF(enmOp);
1056 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1057 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1058 /* pcbRead is optional. */
1059
1060 /** @todo Handle mixing operation enmOp! */
1061
1062 int rc = RTCritSectEnter(&pSink->CritSect);
1063 if (RT_FAILURE(rc))
1064 return rc;
1065
1066 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT,
1067 ("Can't read from a sink which is not an input sink\n"));
1068
1069#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1070# error "Implement me!"
1071#else
1072 uint8_t *pvMixBuf = (uint8_t *)RTMemAlloc(cbBuf);
1073 if (!pvMixBuf)
1074 {
1075 int rc2 = RTCritSectLeave(&pSink->CritSect);
1076 AssertRC(rc2);
1077
1078 return VERR_NO_MEMORY;
1079 }
1080#endif
1081
1082 uint32_t cbRead = 0;
1083
1084 /* Flag indicating whether this sink is in a 'clean' state,
1085 * e.g. there is no more data to read from. */
1086 bool fClean = true;
1087
1088 PAUDMIXSTREAM pMixStream;
1089 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1090 {
1091 if (!(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
1092 {
1093 Log3Func(("[%s] Stream '%s' disabled, skipping ...\n", pSink->pszName, pMixStream->pszName));
1094 continue;
1095 }
1096
1097 uint32_t cbTotalRead = 0;
1098 uint32_t cbToRead = cbBuf;
1099
1100 int rc2 = VINF_SUCCESS;
1101
1102 while (cbToRead)
1103 {
1104 uint32_t cbReadStrm;
1105 AssertPtr(pMixStream->pConn);
1106#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1107# error "Implement me!"
1108#else
1109 rc2 = pMixStream->pConn->pfnStreamRead(pMixStream->pConn, pMixStream->pStream,
1110 (uint8_t *)pvMixBuf + cbTotalRead, cbToRead, &cbReadStrm);
1111#endif
1112 if (RT_FAILURE(rc2))
1113 LogFunc(("[%s] Failed reading from stream '%s': %Rrc\n", pSink->pszName, pMixStream->pszName, rc2));
1114
1115 Log3Func(("[%s] Stream '%s': Read %RU32 bytes\n", pSink->pszName, pMixStream->pszName, cbReadStrm));
1116
1117 if ( RT_FAILURE(rc2)
1118 || !cbReadStrm)
1119 break;
1120
1121 /** @todo Right now we only handle one stream (the last one added in fact). */
1122
1123 AssertBreakStmt(cbReadStrm <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
1124 cbToRead -= cbReadStrm;
1125 cbTotalRead += cbReadStrm;
1126 }
1127
1128 if (RT_FAILURE(rc2))
1129 continue;
1130
1131 cbRead = RT_MAX(cbRead, cbTotalRead);
1132
1133 uint32_t cbReadable = pMixStream->pConn->pfnStreamGetReadable(pMixStream->pConn, pMixStream->pStream);
1134
1135 /* Still some data available? Then sink is not clean (yet). */
1136 if (cbReadable)
1137 fClean = false;
1138 }
1139
1140 if (RT_SUCCESS(rc))
1141 {
1142 if (fClean)
1143 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1144
1145 /* Update our last read time stamp. */
1146 pSink->tsLastReadWrittenMs = RTTimeMilliTS();
1147
1148#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1149# error "Implement me!"
1150#else
1151 Assert(cbRead <= cbBuf);
1152 if (cbRead)
1153 memcpy(pvBuf, pvMixBuf, cbRead);
1154#endif
1155
1156#ifdef VBOX_AUDIO_MIXER_DEBUG
1157 int rc2 = DrvAudioHlpFileWrite(pSink->Dbg.pFile, pvBuf, cbRead, 0 /* fFlags */);
1158 AssertRC(rc2);
1159#endif
1160 if (pcbRead)
1161 *pcbRead = cbRead;
1162 }
1163
1164#ifndef VBOX_AUDIO_MIXER_WITH_MIXBUF
1165 RTMemFree(pvMixBuf);
1166#endif
1167
1168#ifdef LOG_ENABLED
1169 char *pszStatus = dbgAudioMixerSinkStatusToStr(pSink->fStatus);
1170 Log2Func(("[%s] cbRead=%RU32, fClean=%RTbool, fStatus=%s, rc=%Rrc\n", pSink->pszName, cbRead, fClean, pszStatus, rc));
1171 RTStrFree(pszStatus);
1172#endif
1173
1174 int rc2 = RTCritSectLeave(&pSink->CritSect);
1175 AssertRC(rc2);
1176
1177 return rc;
1178}
1179
1180/**
1181 * Removes a mixer stream from a mixer sink, internal version.
1182 *
1183 * @returns IPRT status code.
1184 * @param pSink Sink to remove mixer stream from.
1185 * @param pStream Stream to remove.
1186 */
1187static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1188{
1189 AssertPtrReturn(pSink, VERR_INVALID_PARAMETER);
1190 if ( !pStream
1191 || !pStream->pSink) /* Not part of a sink anymore? */
1192 {
1193 return VERR_NOT_FOUND;
1194 }
1195
1196 AssertMsgReturn(pStream->pSink == pSink, ("Stream '%s' is not part of sink '%s'\n",
1197 pStream->pszName, pSink->pszName), VERR_NOT_FOUND);
1198
1199 LogFlowFunc(("[%s] (Stream = %s), cStreams=%RU8\n",
1200 pSink->pszName, pStream->pStream->szName, pSink->cStreams));
1201
1202#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1203 /* Unlink mixing buffer. */
1204 AudioMixBufUnlink(&pStream->pStream->MixBuf);
1205#endif
1206
1207 /* Remove stream from sink. */
1208 RTListNodeRemove(&pStream->Node);
1209
1210 /* Set sink to NULL so that we know we're not part of any sink anymore. */
1211 pStream->pSink = NULL;
1212
1213 return VINF_SUCCESS;
1214}
1215
1216/**
1217 * Removes a mixer stream from a mixer sink.
1218 *
1219 * @param pSink Sink to remove mixer stream from.
1220 * @param pStream Stream to remove.
1221 */
1222void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1223{
1224 int rc2 = RTCritSectEnter(&pSink->CritSect);
1225 AssertRC(rc2);
1226
1227 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pStream);
1228 if (RT_SUCCESS(rc2))
1229 {
1230 Assert(pSink->cStreams);
1231 pSink->cStreams--;
1232 }
1233
1234 rc2 = RTCritSectLeave(&pSink->CritSect);
1235 AssertRC(rc2);
1236}
1237
1238/**
1239 * Removes all attached streams from a given sink.
1240 *
1241 * @param pSink Sink to remove attached streams from.
1242 */
1243static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink)
1244{
1245 if (!pSink)
1246 return;
1247
1248 LogFunc(("%s\n", pSink->pszName));
1249
1250 PAUDMIXSTREAM pStream, pStreamNext;
1251 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
1252 audioMixerSinkRemoveStreamInternal(pSink, pStream);
1253}
1254
1255/**
1256 * Resets the sink's state.
1257 *
1258 * @param pSink Sink to reset.
1259 */
1260static void audioMixerSinkReset(PAUDMIXSINK pSink)
1261{
1262 if (!pSink)
1263 return;
1264
1265 LogFunc(("[%s]\n", pSink->pszName));
1266
1267 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1268 {
1269#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1270 AudioMixBufReset(&pSink->MixBuf);
1271#else
1272 pSink->In.cbReadable = 0;
1273#endif
1274 }
1275 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1276 {
1277#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1278 AudioMixBufReset(&pSink->MixBuf);
1279#else
1280 pSink->Out.cbWritable = 0;
1281#endif
1282 }
1283
1284 /* Update last updated timestamp. */
1285 pSink->tsLastUpdatedMs = RTTimeMilliTS();
1286
1287 /* Reset status. */
1288 pSink->fStatus = AUDMIXSINK_STS_NONE;
1289}
1290
1291/**
1292 * Removes all attached streams from a given sink.
1293 *
1294 * @param pSink Sink to remove attached streams from.
1295 */
1296void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
1297{
1298 if (!pSink)
1299 return;
1300
1301 int rc2 = RTCritSectEnter(&pSink->CritSect);
1302 AssertRC(rc2);
1303
1304 audioMixerSinkRemoveAllStreamsInternal(pSink);
1305
1306 pSink->cStreams = 0;
1307
1308 rc2 = RTCritSectLeave(&pSink->CritSect);
1309 AssertRC(rc2);
1310}
1311
1312/**
1313 * Resets a sink. This will immediately stop all processing.
1314 *
1315 * @param pSink Sink to reset.
1316 */
1317void AudioMixerSinkReset(PAUDMIXSINK pSink)
1318{
1319 if (!pSink)
1320 return;
1321
1322 int rc2 = RTCritSectEnter(&pSink->CritSect);
1323 AssertRC(rc2);
1324
1325 LogFlowFunc(("[%s]\n", pSink->pszName));
1326
1327 audioMixerSinkReset(pSink);
1328
1329 rc2 = RTCritSectLeave(&pSink->CritSect);
1330 AssertRC(rc2);
1331}
1332
1333/**
1334 * Returns the audio format of a mixer sink.
1335 *
1336 * @param pSink Sink to retrieve audio format for.
1337 * @param pPCMProps Where to the returned audio format.
1338 */
1339void AudioMixerSinkGetFormat(PAUDMIXSINK pSink, PPDMAUDIOPCMPROPS pPCMProps)
1340{
1341 AssertPtrReturnVoid(pSink);
1342 AssertPtrReturnVoid(pPCMProps);
1343
1344 int rc2 = RTCritSectEnter(&pSink->CritSect);
1345 if (RT_FAILURE(rc2))
1346 return;
1347
1348 memcpy(pPCMProps, &pSink->PCMProps, sizeof(PDMAUDIOPCMPROPS));
1349
1350 rc2 = RTCritSectLeave(&pSink->CritSect);
1351 AssertRC(rc2);
1352}
1353
1354/**
1355 * Sets the audio format of a mixer sink.
1356 *
1357 * @returns IPRT status code.
1358 * @param pSink Sink to set audio format for.
1359 * @param pPCMProps Audio format (PCM properties) to set.
1360 */
1361int AudioMixerSinkSetFormat(PAUDMIXSINK pSink, PPDMAUDIOPCMPROPS pPCMProps)
1362{
1363 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1364 AssertPtrReturn(pPCMProps, VERR_INVALID_POINTER);
1365
1366 int rc = RTCritSectEnter(&pSink->CritSect);
1367 if (RT_FAILURE(rc))
1368 return rc;
1369
1370 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, pPCMProps)) /* Bail out early if PCM properties are equal. */
1371 {
1372 rc = RTCritSectLeave(&pSink->CritSect);
1373 AssertRC(rc);
1374
1375 return rc;
1376 }
1377
1378 if (pSink->PCMProps.uHz)
1379 LogFlowFunc(("[%s] Old format: %RU8 bit, %RU8 channels, %RU32Hz\n",
1380 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
1381
1382 memcpy(&pSink->PCMProps, pPCMProps, sizeof(PDMAUDIOPCMPROPS));
1383
1384 LogFlowFunc(("[%s] New format %RU8 bit, %RU8 channels, %RU32Hz\n",
1385 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
1386
1387#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1388 /* Also update the sink's mixing buffer format. */
1389 AudioMixBufDestroy(&pSink->MixBuf);
1390 rc = AudioMixBufInit(&pSink->MixBuf, pSink->pszName, pPCMProps, _4K /** @todo Make configurable? */);
1391 if (RT_SUCCESS(rc))
1392 {
1393 PAUDMIXSTREAM pStream;
1394 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
1395 {
1396 /** @todo Invalidate mix buffers! */
1397 }
1398 }
1399#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
1400
1401#ifdef VBOX_AUDIO_MIXER_DEBUG
1402 if (RT_SUCCESS(rc))
1403 {
1404 DrvAudioHlpFileClose(pSink->Dbg.pFile);
1405
1406 char szTemp[RTPATH_MAX];
1407 int rc2 = RTPathTemp(szTemp, sizeof(szTemp));
1408 if (RT_SUCCESS(rc2))
1409 {
1410 /** @todo Sanitize sink name. */
1411
1412 char szName[64];
1413 RTStrPrintf(szName, sizeof(szName), "MixerSink-%s", pSink->pszName);
1414
1415 char szFile[RTPATH_MAX + 1];
1416 rc2 = DrvAudioHlpFileNameGet(szFile, RT_ELEMENTS(szFile), szTemp, szName,
1417 0 /* Instance */, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
1418 if (RT_SUCCESS(rc2))
1419 {
1420 rc2 = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAG_NONE,
1421 &pSink->Dbg.pFile);
1422 if (RT_SUCCESS(rc2))
1423 rc2 = DrvAudioHlpFileOpen(pSink->Dbg.pFile, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS, &pSink->PCMProps);
1424 }
1425 }
1426 }
1427#endif
1428
1429 int rc2 = RTCritSectLeave(&pSink->CritSect);
1430 AssertRC(rc2);
1431
1432 LogFlowFuncLeaveRC(rc);
1433 return rc;
1434}
1435
1436/**
1437 * Set the volume of an individual sink.
1438 *
1439 * @returns IPRT status code.
1440 * @param pSink Sink to set volume for.
1441 * @param pVol Volume to set.
1442 */
1443int AudioMixerSinkSetVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
1444{
1445 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1446 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
1447
1448 int rc = RTCritSectEnter(&pSink->CritSect);
1449 if (RT_FAILURE(rc))
1450 return rc;
1451
1452 memcpy(&pSink->Volume, pVol, sizeof(PDMAUDIOVOLUME));
1453
1454 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1455 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1456
1457 AssertPtr(pSink->pParent);
1458 rc = audioMixerSinkUpdateVolume(pSink, &pSink->pParent->VolMaster);
1459
1460 int rc2 = RTCritSectLeave(&pSink->CritSect);
1461 AssertRC(rc2);
1462
1463 return rc;
1464}
1465
1466/**
1467 * Updates a mixer sink, internal version.
1468 *
1469 * @returns IPRT status code.
1470 * @param pSink Mixer sink to update.
1471 */
1472static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink)
1473{
1474 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1475
1476 int rc = VINF_SUCCESS;
1477
1478#ifdef LOG_ENABLED
1479 char *pszStatus = dbgAudioMixerSinkStatusToStr(pSink->fStatus);
1480 Log3Func(("[%s] fStatus=%s\n", pSink->pszName, pszStatus));
1481 RTStrFree(pszStatus);
1482#endif
1483
1484 /* Sink disabled? Take a shortcut. */
1485 if (!(pSink->fStatus & AUDMIXSINK_STS_RUNNING))
1486 return rc;
1487
1488 /* Number of detected disabled streams of this sink. */
1489 uint8_t cStreamsDisabled = 0;
1490
1491 PAUDMIXSTREAM pMixStream, pMixStreamNext;
1492 RTListForEachSafe(&pSink->lstStreams, pMixStream, pMixStreamNext, AUDMIXSTREAM, Node)
1493 {
1494 PPDMAUDIOSTREAM pStream = pMixStream->pStream;
1495 AssertPtr(pStream);
1496
1497 PPDMIAUDIOCONNECTOR pConn = pMixStream->pConn;
1498 AssertPtr(pConn);
1499
1500 uint32_t cfProc = 0;
1501
1502 int rc2 = pConn->pfnStreamIterate(pConn, pStream);
1503 if (RT_SUCCESS(rc2))
1504 {
1505 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1506 {
1507 rc = pConn->pfnStreamCapture(pConn, pStream, &cfProc);
1508 if (RT_FAILURE(rc2))
1509 {
1510 LogFunc(("%s: Failed capturing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1511 if (RT_SUCCESS(rc))
1512 rc = rc2;
1513 continue;
1514 }
1515
1516 if (cfProc)
1517 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1518 }
1519 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1520 {
1521 rc2 = pConn->pfnStreamPlay(pConn, pStream, &cfProc);
1522 if (RT_FAILURE(rc2))
1523 {
1524 LogFunc(("%s: Failed playing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1525 if (RT_SUCCESS(rc))
1526 rc = rc2;
1527 continue;
1528 }
1529 }
1530 else
1531 {
1532 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1533 continue;
1534 }
1535
1536 rc2 = pConn->pfnStreamIterate(pConn, pStream);
1537 if (RT_FAILURE(rc2))
1538 {
1539 LogFunc(("%s: Failed re-iterating stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1540 if (RT_SUCCESS(rc))
1541 rc = rc2;
1542 continue;
1543 }
1544
1545 PDMAUDIOSTREAMSTS strmSts = pConn->pfnStreamGetStatus(pConn, pStream);
1546
1547 /* Is the stream not enabled and also is not in a pending disable state anymore? */
1548 if ( !(strmSts & PDMAUDIOSTREAMSTS_FLAG_ENABLED)
1549 && !(strmSts & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE))
1550 {
1551 cStreamsDisabled++;
1552 }
1553 }
1554
1555 Log3Func(("\t%s: cPlayed/cCaptured=%RU32, rc2=%Rrc\n", pStream->szName, cfProc, rc2));
1556 }
1557
1558 Log3Func(("[%s] fPendingDisable=%RTbool, %RU8/%RU8 streams disabled\n",
1559 pSink->pszName, RT_BOOL(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE), cStreamsDisabled, pSink->cStreams));
1560
1561 /* Update last updated timestamp. */
1562 pSink->tsLastUpdatedMs = RTTimeMilliTS();
1563
1564 /* All streams disabled and the sink is in pending disable mode? */
1565 if ( cStreamsDisabled == pSink->cStreams
1566 && (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
1567 {
1568 audioMixerSinkReset(pSink);
1569 }
1570
1571 Log3Func(("[%s] cbReadable=%RU32, cbWritable=%RU32, rc=%Rrc\n",
1572 pSink->pszName, pSink->In.cbReadable, pSink->Out.cbWritable, rc));
1573
1574 return rc;
1575}
1576
1577/**
1578 * Updates (invalidates) a mixer sink.
1579 *
1580 * @returns IPRT status code.
1581 * @param pSink Mixer sink to update.
1582 */
1583int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
1584{
1585 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1586
1587 int rc = RTCritSectEnter(&pSink->CritSect);
1588 if (RT_FAILURE(rc))
1589 return rc;
1590
1591 rc = audioMixerSinkUpdateInternal(pSink);
1592
1593 int rc2 = RTCritSectLeave(&pSink->CritSect);
1594 AssertRC(rc2);
1595
1596 return rc;
1597}
1598
1599/**
1600 * Updates the (master) volume of a mixer sink.
1601 *
1602 * @returns IPRT status code.
1603 * @param pSink Mixer sink to update volume for.
1604 * @param pVolMaster Master volume to set.
1605 */
1606static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
1607{
1608 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1609 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
1610
1611 LogFlowFunc(("[%s] Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1612 pSink->pszName, pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
1613 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32 ",
1614 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1615
1616 /** @todo Very crude implementation for now -- needs more work! */
1617
1618 pSink->VolumeCombined.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
1619
1620 pSink->VolumeCombined.uLeft = ( (pSink->Volume.uLeft ? pSink->Volume.uLeft : 1)
1621 * (pVolMaster->uLeft ? pVolMaster->uLeft : 1)) / PDMAUDIO_VOLUME_MAX;
1622
1623 pSink->VolumeCombined.uRight = ( (pSink->Volume.uRight ? pSink->Volume.uRight : 1)
1624 * (pVolMaster->uRight ? pVolMaster->uRight : 1)) / PDMAUDIO_VOLUME_MAX;
1625
1626 LogFlow(("-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1627 pSink->VolumeCombined.fMuted, pSink->VolumeCombined.uLeft, pSink->VolumeCombined.uRight));
1628
1629 /* Propagate new sink volume to all streams in the sink. */
1630 PAUDMIXSTREAM pMixStream;
1631 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1632 {
1633 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &pSink->VolumeCombined);
1634 AssertRC(rc2);
1635 }
1636
1637 return VINF_SUCCESS;
1638}
1639
1640/**
1641 * Writes data to a mixer sink.
1642 *
1643 * @returns IPRT status code.
1644 * @param pSink Sink to write data to.
1645 * @param enmOp Mixer operation to use when writing data to the sink.
1646 * @param pvBuf Buffer containing the audio data to write.
1647 * @param cbBuf Size (in bytes) of the buffer containing the audio data.
1648 * @param pcbWritten Number of bytes written. Optional.
1649 */
1650int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
1651{
1652 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1653 RT_NOREF(enmOp);
1654 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1655 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
1656 /* pcbWritten is optional. */
1657
1658 int rc = RTCritSectEnter(&pSink->CritSect);
1659 if (RT_FAILURE(rc))
1660 return rc;
1661
1662 AssertMsg(pSink->fStatus & AUDMIXSINK_STS_RUNNING,
1663 ("%s: Can't write to a sink which is not running (anymore) (status 0x%x)\n", pSink->pszName, pSink->fStatus));
1664 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
1665 ("%s: Can't write to a sink which is not an output sink\n", pSink->pszName));
1666
1667 Log3Func(("[%s] enmOp=%d, cbBuf=%RU32\n", pSink->pszName, enmOp, cbBuf));
1668
1669 PAUDMIXSTREAM pMixStream;
1670 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1671 {
1672 PRTCIRCBUF pCircBuf = pMixStream->pCircBuf;
1673 void *pvChunk;
1674 size_t cbChunk;
1675
1676 uint32_t cbWritten = 0;
1677 uint32_t cbToWrite = RT_MIN(cbBuf, (uint32_t)RTCircBufFree(pCircBuf));
1678 while (cbToWrite)
1679 {
1680 RTCircBufAcquireWriteBlock(pCircBuf, cbToWrite, &pvChunk, &cbChunk);
1681
1682 if (cbChunk)
1683 memcpy(pvChunk, (uint8_t *)pvBuf + cbWritten, cbChunk);
1684
1685 RTCircBufReleaseWriteBlock(pCircBuf, cbChunk);
1686
1687 cbWritten += (uint32_t)cbChunk;
1688
1689 Assert(cbToWrite >= cbChunk);
1690 cbToWrite -= (uint32_t)cbChunk;
1691 }
1692
1693 if (cbWritten < cbBuf)
1694 LogFunc(("[%s] Warning: Only written %RU32/%RU32 bytes for stream '%s'\n",
1695 pSink->pszName, cbWritten, cbBuf, pMixStream->pszName));
1696
1697 if (!pMixStream->tsLastReadWrittenMs)
1698 pMixStream->tsLastReadWrittenMs = RTTimeMilliTS();
1699
1700 pMixStream->tsLastReadWrittenMs = RTTimeMilliTS();
1701
1702 const uint32_t cbWritableStream = pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream);
1703 cbToWrite = RT_MIN(cbWritableStream, (uint32_t)RTCircBufUsed(pCircBuf));
1704
1705 int rc2 = VINF_SUCCESS;
1706
1707 while (cbToWrite)
1708 {
1709 RTCircBufAcquireReadBlock(pCircBuf, cbToWrite, &pvChunk, &cbChunk);
1710
1711 if (cbChunk)
1712 {
1713 rc2 = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, pvChunk, (uint32_t)cbChunk,
1714 &cbWritten);
1715 if (RT_FAILURE(rc2))
1716 LogFunc(("[%s] Failed writing to stream '%s': %Rrc\n", pSink->pszName, pMixStream->pszName, rc2));
1717 }
1718
1719 RTCircBufReleaseReadBlock(pCircBuf, cbWritten);
1720
1721 if ( !cbWritten
1722 || cbWritten < cbChunk)
1723 break;
1724
1725 if (RT_FAILURE(rc2))
1726 break;
1727
1728 Assert(cbToWrite >= cbChunk);
1729 cbToWrite -= (uint32_t)cbChunk;
1730 }
1731
1732 if (RTCircBufUsed(pCircBuf))
1733 {
1734 /* Set dirty bit. */
1735 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1736 }
1737 }
1738
1739 /* Update our last written time stamp. */
1740 pSink->tsLastReadWrittenMs = RTTimeMilliTS();
1741
1742 if (pcbWritten)
1743 *pcbWritten = cbBuf; /* Always report everything written, as the backends need to keep up themselves. */
1744
1745 int rc2 = RTCritSectLeave(&pSink->CritSect);
1746 AssertRC(rc2);
1747
1748 return rc;
1749}
1750
1751/*********************************************************************************************************************************
1752 * Mixer Stream implementation.
1753 ********************************************************************************************************************************/
1754
1755/**
1756 * Controls a mixer stream, internal version.
1757 *
1758 * @returns IPRT status code.
1759 * @param pMixStream Mixer stream to control.
1760 * @param enmCmd Mixer stream command to use.
1761 * @param fCtl Additional control flags. Pass 0.
1762 */
1763int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
1764{
1765 AssertPtr(pMixStream->pConn);
1766 AssertPtr(pMixStream->pStream);
1767
1768 RT_NOREF(fCtl);
1769
1770 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
1771
1772 LogFlowFunc(("[%s] enmCmd=%ld, rc=%Rrc\n", pMixStream->pszName, enmCmd, rc));
1773
1774 return rc;
1775}
1776
1777/**
1778 * Controls a mixer stream.
1779 *
1780 * @returns IPRT status code.
1781 * @param pMixStream Mixer stream to control.
1782 * @param enmCmd Mixer stream command to use.
1783 * @param fCtl Additional control flags. Pass 0.
1784 */
1785int AudioMixerStreamCtl(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
1786{
1787 RT_NOREF(fCtl);
1788 AssertPtrReturn(pMixStream, VERR_INVALID_POINTER);
1789 /** @todo Validate fCtl. */
1790
1791 int rc = RTCritSectEnter(&pMixStream->CritSect);
1792 if (RT_FAILURE(rc))
1793 return rc;
1794
1795 rc = audioMixerStreamCtlInternal(pMixStream, enmCmd, fCtl);
1796
1797 int rc2 = RTCritSectLeave(&pMixStream->CritSect);
1798 if (RT_SUCCESS(rc))
1799 rc = rc2;
1800
1801 return rc;
1802}
1803
1804/**
1805 * Destroys a mixer stream, internal version.
1806 *
1807 * @param pMixStream Mixer stream to destroy.
1808 */
1809static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream)
1810{
1811 AssertPtrReturnVoid(pMixStream);
1812
1813 LogFunc(("%s\n", pMixStream->pszName));
1814
1815 if (pMixStream->pConn) /* Stream has a connector interface present? */
1816 {
1817 if (pMixStream->pStream)
1818 {
1819 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
1820 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
1821
1822 pMixStream->pStream = NULL;
1823 }
1824
1825 pMixStream->pConn = NULL;
1826 }
1827
1828 if (pMixStream->pszName)
1829 {
1830 RTStrFree(pMixStream->pszName);
1831 pMixStream->pszName = NULL;
1832 }
1833
1834 if (pMixStream->pCircBuf)
1835 {
1836 RTCircBufDestroy(pMixStream->pCircBuf);
1837 pMixStream->pCircBuf = NULL;
1838 }
1839
1840 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
1841 AssertRC(rc2);
1842
1843 RTMemFree(pMixStream);
1844 pMixStream = NULL;
1845}
1846
1847/**
1848 * Destroys a mixer stream.
1849 *
1850 * @param pMixStream Mixer stream to destroy.
1851 */
1852void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream)
1853{
1854 if (!pMixStream)
1855 return;
1856
1857 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1858 AssertRC(rc2);
1859
1860 LogFunc(("%s\n", pMixStream->pszName));
1861
1862 if (pMixStream->pSink) /* Is the stream part of a sink? */
1863 {
1864 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
1865 * pointer will be gone from the stream. */
1866 PAUDMIXSINK pSink = pMixStream->pSink;
1867
1868 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
1869 if (RT_SUCCESS(rc2))
1870 {
1871 Assert(pSink->cStreams);
1872 pSink->cStreams--;
1873 }
1874 }
1875 else
1876 rc2 = VINF_SUCCESS;
1877
1878 int rc3 = RTCritSectLeave(&pMixStream->CritSect);
1879 AssertRC(rc3);
1880
1881 if (RT_SUCCESS(rc2))
1882 {
1883 audioMixerStreamDestroyInternal(pMixStream);
1884 pMixStream = NULL;
1885 }
1886
1887 LogFlowFunc(("Returning %Rrc\n", rc2));
1888}
1889
1890/**
1891 * Returns whether a mixer stream currently is active (playing/recording) or not.
1892 *
1893 * @returns @c true if playing/recording, @c false if not.
1894 * @param pMixStream Mixer stream to return status for.
1895 */
1896bool AudioMixerStreamIsActive(PAUDMIXSTREAM pMixStream)
1897{
1898 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1899 if (RT_FAILURE(rc2))
1900 return false;
1901
1902 AssertPtr(pMixStream->pConn);
1903 AssertPtr(pMixStream->pStream);
1904
1905 bool fIsActive;
1906
1907 if ( pMixStream->pConn
1908 && pMixStream->pStream
1909 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
1910 {
1911 fIsActive = true;
1912 }
1913 else
1914 fIsActive = false;
1915
1916 rc2 = RTCritSectLeave(&pMixStream->CritSect);
1917 AssertRC(rc2);
1918
1919 return fIsActive;
1920}
1921
1922/**
1923 * Returns whether a mixer stream is valid (e.g. initialized and in a working state) or not.
1924 *
1925 * @returns @c true if valid, @c false if not.
1926 * @param pMixStream Mixer stream to return status for.
1927 */
1928bool AudioMixerStreamIsValid(PAUDMIXSTREAM pMixStream)
1929{
1930 if (!pMixStream)
1931 return false;
1932
1933 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1934 if (RT_FAILURE(rc2))
1935 return false;
1936
1937 bool fIsValid;
1938
1939 if ( pMixStream->pConn
1940 && pMixStream->pStream
1941 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED))
1942 {
1943 fIsValid = true;
1944 }
1945 else
1946 fIsValid = false;
1947
1948 rc2 = RTCritSectLeave(&pMixStream->CritSect);
1949 AssertRC(rc2);
1950
1951 return fIsValid;
1952}
1953
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