VirtualBox

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

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

Audio/Mixer: No need to propagate playback/capturing errors for attached streams to the outside, as this does not affect the mixer sink's operation as such in audioMixerSinkUpdateInternal().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 62.3 KB
Line 
1/* $Id: AudioMixer.cpp 73567 2018-08-08 15:01:36Z 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 int rc;
1393
1394 if (pSink->In.pStreamRecSource) /* Disable old recording source, if any set. */
1395 {
1396 const PPDMIAUDIOCONNECTOR pConn = pSink->In.pStreamRecSource->pConn;
1397 AssertPtr(pConn);
1398 rc = pConn->pfnEnable(pConn, PDMAUDIODIR_IN, false /* Disable */);
1399 }
1400 else
1401 rc = VINF_SUCCESS;
1402
1403 if (RT_SUCCESS(rc))
1404 {
1405 if (pStream) /* Can be NULL if un-setting. */
1406 {
1407 AssertPtr(pStream->pStream);
1408 AssertMsg(pStream->pStream->enmDir == PDMAUDIODIR_IN, ("Specified stream is not an input stream\n"));
1409 }
1410
1411 pSink->In.pStreamRecSource = pStream;
1412
1413 if (pSink->In.pStreamRecSource)
1414 {
1415 const PPDMIAUDIOCONNECTOR pConn = pSink->In.pStreamRecSource->pConn;
1416 AssertPtr(pConn);
1417 rc = pConn->pfnEnable(pConn, PDMAUDIODIR_IN, true /* Enable */);
1418 }
1419 }
1420
1421 LogFunc(("[%s] Recording source is now '%s', rc=%Rrc\n",
1422 pSink->pszName, pSink->In.pStreamRecSource ? pSink->In.pStreamRecSource->pszName : "<None>", rc));
1423
1424 return rc;
1425}
1426
1427/**
1428 * Set the current recording source of an input mixer sink.
1429 *
1430 * @return IPRT status code.
1431 * @param pSink Input mixer sink to set recording source for.
1432 * @param pStream Mixer stream to set as current recording source. Must be an input stream.
1433 */
1434int AudioMixerSinkSetRecordingSource(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1435{
1436 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1437 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1438
1439 int rc = RTCritSectEnter(&pSink->CritSect);
1440 if (RT_FAILURE(rc))
1441 return rc;
1442
1443 rc = audioMixerSinkSetRecSourceInternal(pSink, pStream);
1444
1445 int rc2 = RTCritSectLeave(&pSink->CritSect);
1446 AssertRC(rc2);
1447
1448 return rc;
1449}
1450
1451/**
1452 * Sets the volume of an individual sink.
1453 *
1454 * @returns IPRT status code.
1455 * @param pSink Sink to set volume for.
1456 * @param pVol Volume to set.
1457 */
1458int AudioMixerSinkSetVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
1459{
1460 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1461 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
1462
1463 int rc = RTCritSectEnter(&pSink->CritSect);
1464 if (RT_FAILURE(rc))
1465 return rc;
1466
1467 memcpy(&pSink->Volume, pVol, sizeof(PDMAUDIOVOLUME));
1468
1469 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU8, rVol=%RU8\n",
1470 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1471
1472 LogRel2(("Mixer: Setting volume of sink '%s' to %RU8/%RU8 (%s)\n",
1473 pSink->pszName, pVol->uLeft, pVol->uRight, pVol->fMuted ? "Muted" : "Unmuted"));
1474
1475 AssertPtr(pSink->pParent);
1476 rc = audioMixerSinkUpdateVolume(pSink, &pSink->pParent->VolMaster);
1477
1478 int rc2 = RTCritSectLeave(&pSink->CritSect);
1479 AssertRC(rc2);
1480
1481 return rc;
1482}
1483
1484/**
1485 * Updates a mixer sink, internal version.
1486 *
1487 * @returns IPRT status code.
1488 * @param pSink Mixer sink to update.
1489 */
1490static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink)
1491{
1492 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1493
1494 int rc = VINF_SUCCESS;
1495
1496#ifdef LOG_ENABLED
1497 char *pszStatus = dbgAudioMixerSinkStatusToStr(pSink->fStatus);
1498 Log3Func(("[%s] fStatus=%s\n", pSink->pszName, pszStatus));
1499 RTStrFree(pszStatus);
1500#endif
1501
1502 /* Sink disabled? Take a shortcut. */
1503 if (!(pSink->fStatus & AUDMIXSINK_STS_RUNNING))
1504 return rc;
1505
1506 /* Number of detected disabled streams of this sink. */
1507 uint8_t cStreamsDisabled = 0;
1508
1509 PAUDMIXSTREAM pMixStream, pMixStreamNext;
1510 RTListForEachSafe(&pSink->lstStreams, pMixStream, pMixStreamNext, AUDMIXSTREAM, Node)
1511 {
1512 PPDMAUDIOSTREAM pStream = pMixStream->pStream;
1513 AssertPtr(pStream);
1514
1515 PPDMIAUDIOCONNECTOR pConn = pMixStream->pConn;
1516 AssertPtr(pConn);
1517
1518 uint32_t cfProc = 0;
1519
1520 if (!DrvAudioHlpStreamStatusIsReady(pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream)))
1521 continue;
1522
1523 int rc2 = pConn->pfnStreamIterate(pConn, pStream);
1524 if (RT_SUCCESS(rc2))
1525 {
1526 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1527 {
1528 rc2 = pConn->pfnStreamCapture(pConn, pStream, &cfProc);
1529 if (RT_FAILURE(rc2))
1530 {
1531 LogFunc(("%s: Failed capturing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1532 continue;
1533 }
1534
1535 if (cfProc)
1536 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1537 }
1538 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1539 {
1540 rc2 = pConn->pfnStreamPlay(pConn, pStream, &cfProc);
1541 if (RT_FAILURE(rc2))
1542 {
1543 LogFunc(("%s: Failed playing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1544 continue;
1545 }
1546 }
1547 else
1548 {
1549 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1550 continue;
1551 }
1552
1553 PDMAUDIOSTREAMSTS strmSts = pConn->pfnStreamGetStatus(pConn, pStream);
1554
1555 /* Is the stream not enabled and also is not in a pending disable state anymore? */
1556 if ( !(strmSts & PDMAUDIOSTREAMSTS_FLAG_ENABLED)
1557 && !(strmSts & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE))
1558 {
1559 cStreamsDisabled++;
1560 }
1561 }
1562
1563 Log3Func(("\t%s: cPlayed/cCaptured=%RU32, rc2=%Rrc\n", pStream->szName, cfProc, rc2));
1564 }
1565
1566 Log3Func(("[%s] fPendingDisable=%RTbool, %RU8/%RU8 streams disabled\n",
1567 pSink->pszName, RT_BOOL(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE), cStreamsDisabled, pSink->cStreams));
1568
1569 /* Update last updated timestamp. */
1570 pSink->tsLastUpdatedMs = RTTimeMilliTS();
1571
1572 /* All streams disabled and the sink is in pending disable mode? */
1573 if ( cStreamsDisabled == pSink->cStreams
1574 && (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
1575 {
1576 audioMixerSinkReset(pSink);
1577 }
1578
1579 return rc;
1580}
1581
1582/**
1583 * Updates (invalidates) a mixer sink.
1584 *
1585 * @returns IPRT status code.
1586 * @param pSink Mixer sink to update.
1587 */
1588int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
1589{
1590 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1591
1592 int rc = RTCritSectEnter(&pSink->CritSect);
1593 if (RT_FAILURE(rc))
1594 return rc;
1595
1596 rc = audioMixerSinkUpdateInternal(pSink);
1597
1598 int rc2 = RTCritSectLeave(&pSink->CritSect);
1599 AssertRC(rc2);
1600
1601 return rc;
1602}
1603
1604/**
1605 * Updates the (master) volume of a mixer sink.
1606 *
1607 * @returns IPRT status code.
1608 * @param pSink Mixer sink to update volume for.
1609 * @param pVolMaster Master volume to set.
1610 */
1611static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
1612{
1613 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1614 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
1615
1616 LogFlowFunc(("[%s] Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1617 pSink->pszName, pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
1618 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32 ",
1619 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1620
1621 /** @todo Very crude implementation for now -- needs more work! */
1622
1623 pSink->VolumeCombined.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
1624
1625 pSink->VolumeCombined.uLeft = ( (pSink->Volume.uLeft ? pSink->Volume.uLeft : 1)
1626 * (pVolMaster->uLeft ? pVolMaster->uLeft : 1)) / PDMAUDIO_VOLUME_MAX;
1627
1628 pSink->VolumeCombined.uRight = ( (pSink->Volume.uRight ? pSink->Volume.uRight : 1)
1629 * (pVolMaster->uRight ? pVolMaster->uRight : 1)) / PDMAUDIO_VOLUME_MAX;
1630
1631 LogFlow(("-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1632 pSink->VolumeCombined.fMuted, pSink->VolumeCombined.uLeft, pSink->VolumeCombined.uRight));
1633
1634 /* Propagate new sink volume to all streams in the sink. */
1635 PAUDMIXSTREAM pMixStream;
1636 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1637 {
1638 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &pSink->VolumeCombined);
1639 AssertRC(rc2);
1640 }
1641
1642 return VINF_SUCCESS;
1643}
1644
1645/**
1646 * Writes audio output data to all connected mixer streams (multiplex).
1647 *
1648 * @returns IPRT status code.
1649 * @param pSink Sink to write audio output to.
1650 * @param enmOp What mixing operation to use. Currently not implemented.
1651 * @param pvBuf Pointer to audio data to write.
1652 * @param cbBuf Size (in bytes) of audio data to write.
1653 * @param pcbWrittenMin Returns minimum size (in bytes) successfully written by all mixer streams. Optional.
1654 */
1655int audioMixerSinkWriteToStreams(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWrittenMin)
1656{
1657 RT_NOREF(enmOp);
1658
1659 int rc = VINF_SUCCESS;
1660
1661 uint32_t cbWrittenMin = UINT32_MAX;
1662
1663 PAUDMIXSTREAM pMixStream;
1664 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1665 {
1666 if (!DrvAudioHlpStreamStatusCanWrite(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream)))
1667 continue;
1668
1669 PRTCIRCBUF pCircBuf = pMixStream->pCircBuf;
1670 void *pvChunk;
1671 size_t cbChunk;
1672
1673 uint32_t cbWritten = 0;
1674 uint32_t cbToWrite = RT_MIN(cbBuf, (uint32_t)RTCircBufFree(pCircBuf));
1675 while (cbToWrite)
1676 {
1677 RTCircBufAcquireWriteBlock(pCircBuf, cbToWrite, &pvChunk, &cbChunk);
1678
1679 if (cbChunk)
1680 memcpy(pvChunk, (uint8_t *)pvBuf + cbWritten, cbChunk);
1681
1682 RTCircBufReleaseWriteBlock(pCircBuf, cbChunk);
1683
1684 cbWritten += (uint32_t)cbChunk;
1685 Assert(cbWritten <= cbBuf);
1686
1687 Assert(cbToWrite >= cbChunk);
1688 cbToWrite -= (uint32_t)cbChunk;
1689 }
1690
1691 if (cbWritten < cbBuf)
1692 {
1693 LogFunc(("[%s] Warning: Only written %RU32/%RU32 bytes for stream '%s'\n",
1694 pSink->pszName, cbWritten, cbBuf, pMixStream->pszName));
1695 LogRel2(("Mixer: Buffer overrun for mixer stream '%s' (sink '%s')\n", pMixStream->pszName, pSink->pszName));
1696#ifdef DEBUG_andy
1697 AssertFailed();
1698#endif
1699 }
1700
1701 cbWrittenMin = RT_MIN(cbWrittenMin, cbWritten);
1702
1703 if (cbWritten) /* Update the mixer stream's last written time stamp. */
1704 pMixStream->tsLastReadWrittenNs = RTTimeNanoTS();
1705
1706 cbToWrite = RT_MIN(pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream),
1707 (uint32_t)RTCircBufUsed(pCircBuf));
1708 cbToWrite = DrvAudioHlpBytesAlign(cbToWrite, &pSink->PCMProps);
1709
1710 int rc2 = VINF_SUCCESS;
1711
1712 while (cbToWrite)
1713 {
1714 RTCircBufAcquireReadBlock(pCircBuf, cbToWrite, &pvChunk, &cbChunk);
1715
1716 if (cbChunk)
1717 {
1718 rc2 = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, pvChunk, (uint32_t)cbChunk,
1719 &cbWritten);
1720 if (RT_FAILURE(rc2))
1721 LogFunc(("[%s] Failed writing to stream '%s': %Rrc\n", pSink->pszName, pMixStream->pszName, rc2));
1722 }
1723
1724 RTCircBufReleaseReadBlock(pCircBuf, cbWritten);
1725
1726 if ( !cbWritten
1727 || cbWritten < cbChunk)
1728 {
1729#ifdef DEBUG_andy
1730 AssertFailed();
1731#endif
1732 break;
1733 }
1734
1735 if (RT_FAILURE(rc2))
1736 break;
1737
1738 Assert(cbToWrite >= cbChunk);
1739 cbToWrite -= (uint32_t)cbChunk;
1740 }
1741 }
1742
1743 if (cbWrittenMin == UINT32_MAX) /* Nothing written? */
1744 cbWrittenMin = 0;
1745
1746 if (pcbWrittenMin)
1747 *pcbWrittenMin = cbWrittenMin;
1748
1749 return rc;
1750}
1751
1752/**
1753 * Writes data to a mixer sink.
1754 *
1755 * @returns IPRT status code.
1756 * @param pSink Sink to write data to.
1757 * @param enmOp Mixer operation to use when writing data to the sink.
1758 * @param pvBuf Buffer containing the audio data to write.
1759 * @param cbBuf Size (in bytes) of the buffer containing the audio data.
1760 * @param pcbWritten Number of bytes written. Optional.
1761 */
1762int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
1763{
1764 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1765 RT_NOREF(enmOp);
1766 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1767 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
1768 /* pcbWritten is optional. */
1769
1770 int rc = RTCritSectEnter(&pSink->CritSect);
1771 if (RT_FAILURE(rc))
1772 return rc;
1773
1774 AssertMsg(pSink->fStatus & AUDMIXSINK_STS_RUNNING,
1775 ("%s: Can't write to a sink which is not running (anymore) (status 0x%x)\n", pSink->pszName, pSink->fStatus));
1776 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
1777 ("%s: Can't write to a sink which is not an output sink\n", pSink->pszName));
1778
1779#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1780 uint32_t cbWritten = 0;
1781 uint32_t cbToWrite = cbBuf;
1782 while (cbToWrite)
1783 {
1784 /* First, write the data to the mixer sink's own mixing buffer.
1785 * Here the audio data can be transformed into the mixer sink's format. */
1786 uint32_t cfWritten = 0;
1787 rc = AudioMixBufWriteCirc(&pSink->MixBuf, (uint8_t *)pvBuf + cbWritten, cbToWrite, &cfWritten);
1788 if (RT_FAILURE(rc))
1789 break;
1790
1791 const uint32_t cbWrittenChunk = DrvAudioHlpFramesToBytes(cfWritten, &pSink->PCMProps);
1792
1793 Assert(cbToWrite >= cbWrittenChunk);
1794 cbToWrite -= cbWrittenChunk;
1795 cbWritten += cbWrittenChunk;
1796 }
1797
1798 Assert(cbWritten <= cbBuf);
1799
1800 if ( RT_FAILURE(rc)
1801 || cbWritten < cbBuf)
1802 {
1803 LogRel2(("Mixer: Buffer overrun for mixer sink '%s' (only %RU32/%RU32 bytes written)\n",
1804 pSink->pszName, cbWritten, cbBuf));
1805# ifdef DEBUG_andy
1806 AssertFailed();
1807# endif
1808 }
1809
1810 /* Next, try to write (multiplex) as much audio data as possible to all connected mixer streams. */
1811 uint8_t arrChunkBuf[_1K]; /** @todo Hm ... some zero copy / shared buffers would be nice! */
1812 while (cbWritten)
1813 {
1814 uint32_t cfChunk;
1815 rc = AudioMixBufAcquireReadBlock(&pSink->MixBuf, arrChunkBuf, sizeof(arrChunkBuf), &cfChunk);
1816 if (RT_FAILURE(rc))
1817 break;
1818
1819 const uint32_t cbChunk = DrvAudioHlpFramesToBytes(cfChunk, &pSink->PCMProps);
1820 Assert(cbChunk <= sizeof(arrChunkBuf));
1821 rc = audioMixerSinkWriteToStreams(pSink, enmOp, arrChunkBuf, cbChunk, NULL /* pcbWrittenMin */);
1822 AudioMixBufReleaseReadBlock(&pSink->MixBuf, cfChunk);
1823
1824 Assert(cbWritten >= cbChunk);
1825 cbWritten -= cbChunk;
1826 }
1827
1828 if ( !(pSink->fStatus & AUDMIXSINK_STS_DIRTY)
1829 && AudioMixBufUsed(&pSink->MixBuf)) /* Still audio output data left? Consider the sink as being "dirty" then. */
1830 {
1831 /* Set dirty bit. */
1832 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1833 }
1834#else
1835 rc = audioMixerSinkWriteToStreams(pSink, enmOp, pvBuf, cbBuf, NULL /* pcbWrittenMin */);
1836#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
1837
1838 /* Update the sink's last written time stamp. */
1839 pSink->tsLastReadWrittenNs = RTTimeNanoTS();
1840
1841 if (pcbWritten)
1842 *pcbWritten = cbBuf; /* Always report everything written, as the backends need to keep up themselves. */
1843
1844 int rc2 = RTCritSectLeave(&pSink->CritSect);
1845 AssertRC(rc2);
1846
1847 return rc;
1848}
1849
1850/*********************************************************************************************************************************
1851 * Mixer Stream implementation.
1852 ********************************************************************************************************************************/
1853
1854/**
1855 * Controls a mixer stream, internal version.
1856 *
1857 * @returns IPRT status code.
1858 * @param pMixStream Mixer stream to control.
1859 * @param enmCmd Mixer stream command to use.
1860 * @param fCtl Additional control flags. Pass 0.
1861 */
1862int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
1863{
1864 AssertPtr(pMixStream->pConn);
1865 AssertPtr(pMixStream->pStream);
1866
1867 RT_NOREF(fCtl);
1868
1869 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
1870
1871 LogFlowFunc(("[%s] enmCmd=%ld, rc=%Rrc\n", pMixStream->pszName, enmCmd, rc));
1872
1873 return rc;
1874}
1875
1876/**
1877 * Controls a mixer stream.
1878 *
1879 * @returns IPRT status code.
1880 * @param pMixStream Mixer stream to control.
1881 * @param enmCmd Mixer stream command to use.
1882 * @param fCtl Additional control flags. Pass 0.
1883 */
1884int AudioMixerStreamCtl(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
1885{
1886 RT_NOREF(fCtl);
1887 AssertPtrReturn(pMixStream, VERR_INVALID_POINTER);
1888 /** @todo Validate fCtl. */
1889
1890 int rc = RTCritSectEnter(&pMixStream->CritSect);
1891 if (RT_FAILURE(rc))
1892 return rc;
1893
1894 rc = audioMixerStreamCtlInternal(pMixStream, enmCmd, fCtl);
1895
1896 int rc2 = RTCritSectLeave(&pMixStream->CritSect);
1897 if (RT_SUCCESS(rc))
1898 rc = rc2;
1899
1900 return rc;
1901}
1902
1903/**
1904 * Destroys a mixer stream, internal version.
1905 *
1906 * @param pMixStream Mixer stream to destroy.
1907 */
1908static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream)
1909{
1910 AssertPtrReturnVoid(pMixStream);
1911
1912 LogFunc(("%s\n", pMixStream->pszName));
1913
1914 if (pMixStream->pConn) /* Stream has a connector interface present? */
1915 {
1916 if (pMixStream->pStream)
1917 {
1918 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
1919 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
1920
1921 pMixStream->pStream = NULL;
1922 }
1923
1924 pMixStream->pConn = NULL;
1925 }
1926
1927 if (pMixStream->pszName)
1928 {
1929 RTStrFree(pMixStream->pszName);
1930 pMixStream->pszName = NULL;
1931 }
1932
1933 if (pMixStream->pCircBuf)
1934 {
1935 RTCircBufDestroy(pMixStream->pCircBuf);
1936 pMixStream->pCircBuf = NULL;
1937 }
1938
1939 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
1940 AssertRC(rc2);
1941
1942 RTMemFree(pMixStream);
1943 pMixStream = NULL;
1944}
1945
1946/**
1947 * Destroys a mixer stream.
1948 *
1949 * @param pMixStream Mixer stream to destroy.
1950 */
1951void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream)
1952{
1953 if (!pMixStream)
1954 return;
1955
1956 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1957 AssertRC(rc2);
1958
1959 LogFunc(("%s\n", pMixStream->pszName));
1960
1961 if (pMixStream->pSink) /* Is the stream part of a sink? */
1962 {
1963 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
1964 * pointer will be gone from the stream. */
1965 PAUDMIXSINK pSink = pMixStream->pSink;
1966
1967 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
1968 if (RT_SUCCESS(rc2))
1969 {
1970 Assert(pSink->cStreams);
1971 pSink->cStreams--;
1972 }
1973 }
1974 else
1975 rc2 = VINF_SUCCESS;
1976
1977 int rc3 = RTCritSectLeave(&pMixStream->CritSect);
1978 AssertRC(rc3);
1979
1980 if (RT_SUCCESS(rc2))
1981 {
1982 audioMixerStreamDestroyInternal(pMixStream);
1983 pMixStream = NULL;
1984 }
1985
1986 LogFlowFunc(("Returning %Rrc\n", rc2));
1987}
1988
1989/**
1990 * Returns whether a mixer stream currently is active (playing/recording) or not.
1991 *
1992 * @returns @c true if playing/recording, @c false if not.
1993 * @param pMixStream Mixer stream to return status for.
1994 */
1995bool AudioMixerStreamIsActive(PAUDMIXSTREAM pMixStream)
1996{
1997 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1998 if (RT_FAILURE(rc2))
1999 return false;
2000
2001 AssertPtr(pMixStream->pConn);
2002 AssertPtr(pMixStream->pStream);
2003
2004 bool fIsActive;
2005
2006 if ( pMixStream->pConn
2007 && pMixStream->pStream
2008 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
2009 {
2010 fIsActive = true;
2011 }
2012 else
2013 fIsActive = false;
2014
2015 rc2 = RTCritSectLeave(&pMixStream->CritSect);
2016 AssertRC(rc2);
2017
2018 return fIsActive;
2019}
2020
2021/**
2022 * Returns whether a mixer stream is valid (e.g. initialized and in a working state) or not.
2023 *
2024 * @returns @c true if valid, @c false if not.
2025 * @param pMixStream Mixer stream to return status for.
2026 */
2027bool AudioMixerStreamIsValid(PAUDMIXSTREAM pMixStream)
2028{
2029 if (!pMixStream)
2030 return false;
2031
2032 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2033 if (RT_FAILURE(rc2))
2034 return false;
2035
2036 bool fIsValid;
2037
2038 if ( pMixStream->pConn
2039 && pMixStream->pStream
2040 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED))
2041 {
2042 fIsValid = true;
2043 }
2044 else
2045 fIsValid = false;
2046
2047 rc2 = RTCritSectLeave(&pMixStream->CritSect);
2048 AssertRC(rc2);
2049
2050 return fIsValid;
2051}
2052
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