VirtualBox

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

Last change on this file since 73639 was 73582, checked in by vboxsync, 6 years ago

Audio/Mixer: Skip handling input streams which currently are not marked as the recording source -- should save a few CPU cycles.

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