VirtualBox

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

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

Audio/Mixer: Don't query a stream's readable size in AudioMixerSinkRead(), as the value might differ from a former call to AudioMixerSinkGetReadable(). Keep it simple.

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