VirtualBox

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

Last change on this file since 88429 was 88374, checked in by vboxsync, 4 years ago

AudioMixer: Fixed status printing. Added todo about possibly calling pfnStreamIterate again after pfnStreamPlay. bugref:9890

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

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