VirtualBox

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

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

Audio/Mixer: Disable the old recording source (if any) in audioMixerSinkSetRecSourceInternal() before setting a new one -- should save a few CPU cycles.

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