VirtualBox

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

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

Audio/Mixer: Use a mixer sink's mixing buffer for multiplexing.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 61.9 KB
Line 
1/* $Id: AudioMixer.cpp 73525 2018-08-06 12:33:08Z 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.cBits, 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 = RT_MIN(cbBuf,
1069 pStreamRecSource->pConn->pfnStreamGetReadable(pStreamRecSource->pConn,
1070 pStreamRecSource->pStream));
1071 while (cbToRead)
1072 {
1073 uint32_t cbReadStrm;
1074 AssertPtr(pStreamRecSource->pConn);
1075#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF_IN
1076# error "Implement me!"
1077#else
1078 rc = pStreamRecSource->pConn->pfnStreamRead(pStreamRecSource->pConn, pStreamRecSource->pStream,
1079 (uint8_t *)pvBuf + cbRead, cbToRead, &cbReadStrm);
1080#endif
1081 if (RT_FAILURE(rc))
1082 LogFunc(("[%s] Failed reading from stream '%s': %Rrc\n", pSink->pszName, pStreamRecSource->pszName, rc));
1083
1084 Log3Func(("[%s] Stream '%s': Read %RU32 bytes\n", pSink->pszName, pStreamRecSource->pszName, cbReadStrm));
1085
1086 if ( RT_FAILURE(rc)
1087 || !cbReadStrm)
1088 break;
1089
1090 AssertBreakStmt(cbReadStrm <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
1091 cbToRead -= cbReadStrm;
1092 cbRead += cbReadStrm;
1093 Assert(cbRead <= cbBuf);
1094 }
1095
1096 uint32_t cbReadable = pStreamRecSource->pConn->pfnStreamGetReadable(pStreamRecSource->pConn, pStreamRecSource->pStream);
1097
1098 /* Still some data available? Then sink is not clean (yet). */
1099 if (cbReadable)
1100 fClean = false;
1101
1102 if (RT_SUCCESS(rc))
1103 {
1104 if (fClean)
1105 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1106
1107 /* Update our last read time stamp. */
1108 pSink->tsLastReadWrittenNs = RTTimeNanoTS();
1109
1110#ifdef VBOX_AUDIO_MIXER_DEBUG
1111 int rc2 = DrvAudioHlpFileWrite(pSink->Dbg.pFile, pvBuf, cbRead, 0 /* fFlags */);
1112 AssertRC(rc2);
1113#endif
1114 if (pcbRead)
1115 *pcbRead = cbRead;
1116 }
1117 }
1118
1119#ifdef LOG_ENABLED
1120 char *pszStatus = dbgAudioMixerSinkStatusToStr(pSink->fStatus);
1121 Log2Func(("[%s] cbRead=%RU32, fClean=%RTbool, fStatus=%s, rc=%Rrc\n", pSink->pszName, cbRead, fClean, pszStatus, rc));
1122 RTStrFree(pszStatus);
1123#endif
1124
1125 int rc2 = RTCritSectLeave(&pSink->CritSect);
1126 AssertRC(rc2);
1127
1128 return rc;
1129}
1130
1131/**
1132 * Removes a mixer stream from a mixer sink, internal version.
1133 *
1134 * @returns IPRT status code.
1135 * @param pSink Sink to remove mixer stream from.
1136 * @param pStream Stream to remove.
1137 */
1138static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1139{
1140 AssertPtrReturn(pSink, VERR_INVALID_PARAMETER);
1141 if ( !pStream
1142 || !pStream->pSink) /* Not part of a sink anymore? */
1143 {
1144 return VERR_NOT_FOUND;
1145 }
1146
1147 AssertMsgReturn(pStream->pSink == pSink, ("Stream '%s' is not part of sink '%s'\n",
1148 pStream->pszName, pSink->pszName), VERR_NOT_FOUND);
1149
1150 LogFlowFunc(("[%s] (Stream = %s), cStreams=%RU8\n",
1151 pSink->pszName, pStream->pStream->szName, pSink->cStreams));
1152
1153 /* Remove stream from sink. */
1154 RTListNodeRemove(&pStream->Node);
1155
1156 int rc = VINF_SUCCESS;
1157
1158 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1159 {
1160 /* Make sure to also un-set the recording source if this stream was set
1161 * as the recording source before. */
1162 if (pStream == pSink->In.pStreamRecSource)
1163 rc = audioMixerSinkSetRecSourceInternal(pSink, NULL);
1164 }
1165
1166 /* Set sink to NULL so that we know we're not part of any sink anymore. */
1167 pStream->pSink = NULL;
1168
1169 return rc;
1170}
1171
1172/**
1173 * Removes a mixer stream from a mixer sink.
1174 *
1175 * @param pSink Sink to remove mixer stream from.
1176 * @param pStream Stream to remove.
1177 */
1178void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1179{
1180 int rc2 = RTCritSectEnter(&pSink->CritSect);
1181 AssertRC(rc2);
1182
1183 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pStream);
1184 if (RT_SUCCESS(rc2))
1185 {
1186 Assert(pSink->cStreams);
1187 pSink->cStreams--;
1188 }
1189
1190 rc2 = RTCritSectLeave(&pSink->CritSect);
1191 AssertRC(rc2);
1192}
1193
1194/**
1195 * Removes all attached streams from a given sink.
1196 *
1197 * @param pSink Sink to remove attached streams from.
1198 */
1199static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink)
1200{
1201 if (!pSink)
1202 return;
1203
1204 LogFunc(("%s\n", pSink->pszName));
1205
1206 PAUDMIXSTREAM pStream, pStreamNext;
1207 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
1208 audioMixerSinkRemoveStreamInternal(pSink, pStream);
1209}
1210
1211/**
1212 * Resets the sink's state.
1213 *
1214 * @param pSink Sink to reset.
1215 */
1216static void audioMixerSinkReset(PAUDMIXSINK pSink)
1217{
1218 if (!pSink)
1219 return;
1220
1221 LogFunc(("[%s]\n", pSink->pszName));
1222
1223#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1224 AudioMixBufReset(&pSink->MixBuf);
1225#endif
1226
1227 /* Update last updated timestamp. */
1228 pSink->tsLastUpdatedMs = 0;
1229
1230 /* Reset status. */
1231 pSink->fStatus = AUDMIXSINK_STS_NONE;
1232}
1233
1234/**
1235 * Removes all attached streams from a given sink.
1236 *
1237 * @param pSink Sink to remove attached streams from.
1238 */
1239void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
1240{
1241 if (!pSink)
1242 return;
1243
1244 int rc2 = RTCritSectEnter(&pSink->CritSect);
1245 AssertRC(rc2);
1246
1247 audioMixerSinkRemoveAllStreamsInternal(pSink);
1248
1249 pSink->cStreams = 0;
1250
1251 rc2 = RTCritSectLeave(&pSink->CritSect);
1252 AssertRC(rc2);
1253}
1254
1255/**
1256 * Resets a sink. This will immediately stop all processing.
1257 *
1258 * @param pSink Sink to reset.
1259 */
1260void AudioMixerSinkReset(PAUDMIXSINK pSink)
1261{
1262 if (!pSink)
1263 return;
1264
1265 int rc2 = RTCritSectEnter(&pSink->CritSect);
1266 AssertRC(rc2);
1267
1268 LogFlowFunc(("[%s]\n", pSink->pszName));
1269
1270 audioMixerSinkReset(pSink);
1271
1272 rc2 = RTCritSectLeave(&pSink->CritSect);
1273 AssertRC(rc2);
1274}
1275
1276/**
1277 * Returns the audio format of a mixer sink.
1278 *
1279 * @param pSink Sink to retrieve audio format for.
1280 * @param pPCMProps Where to the returned audio format.
1281 */
1282void AudioMixerSinkGetFormat(PAUDMIXSINK pSink, PPDMAUDIOPCMPROPS pPCMProps)
1283{
1284 AssertPtrReturnVoid(pSink);
1285 AssertPtrReturnVoid(pPCMProps);
1286
1287 int rc2 = RTCritSectEnter(&pSink->CritSect);
1288 if (RT_FAILURE(rc2))
1289 return;
1290
1291 memcpy(pPCMProps, &pSink->PCMProps, sizeof(PDMAUDIOPCMPROPS));
1292
1293 rc2 = RTCritSectLeave(&pSink->CritSect);
1294 AssertRC(rc2);
1295}
1296
1297/**
1298 * Sets the audio format of a mixer sink.
1299 *
1300 * @returns IPRT status code.
1301 * @param pSink Sink to set audio format for.
1302 * @param pPCMProps Audio format (PCM properties) to set.
1303 */
1304int AudioMixerSinkSetFormat(PAUDMIXSINK pSink, PPDMAUDIOPCMPROPS pPCMProps)
1305{
1306 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1307 AssertPtrReturn(pPCMProps, VERR_INVALID_POINTER);
1308 AssertReturn(DrvAudioHlpPCMPropsAreValid(pPCMProps), VERR_INVALID_PARAMETER);
1309
1310 int rc = RTCritSectEnter(&pSink->CritSect);
1311 if (RT_FAILURE(rc))
1312 return rc;
1313
1314 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, pPCMProps)) /* Bail out early if PCM properties are equal. */
1315 {
1316 rc = RTCritSectLeave(&pSink->CritSect);
1317 AssertRC(rc);
1318
1319 return rc;
1320 }
1321
1322 if (pSink->PCMProps.uHz)
1323 LogFlowFunc(("[%s] Old format: %RU8 bit, %RU8 channels, %RU32Hz\n",
1324 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
1325
1326 memcpy(&pSink->PCMProps, pPCMProps, sizeof(PDMAUDIOPCMPROPS));
1327
1328 LogFlowFunc(("[%s] New format %RU8 bit, %RU8 channels, %RU32Hz\n",
1329 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
1330
1331#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1332 /* Also update the sink's mixing buffer format. */
1333 AudioMixBufDestroy(&pSink->MixBuf);
1334 rc = AudioMixBufInit(&pSink->MixBuf, pSink->pszName, &pSink->PCMProps,
1335 DrvAudioHlpMilliToFrames(100 /* ms */, &pSink->PCMProps)); /** @todo Make this configurable? */
1336 if (RT_SUCCESS(rc))
1337 {
1338 PAUDMIXSTREAM pStream;
1339 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
1340 {
1341 /** @todo Invalidate mix buffers! */
1342 }
1343 }
1344#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
1345
1346#ifdef VBOX_AUDIO_MIXER_DEBUG
1347 if (RT_SUCCESS(rc))
1348 {
1349 DrvAudioHlpFileClose(pSink->Dbg.pFile);
1350
1351 char szTemp[RTPATH_MAX];
1352 int rc2 = RTPathTemp(szTemp, sizeof(szTemp));
1353 if (RT_SUCCESS(rc2))
1354 {
1355 /** @todo Sanitize sink name. */
1356
1357 char szName[64];
1358 RTStrPrintf(szName, sizeof(szName), "MixerSink-%s", pSink->pszName);
1359
1360 char szFile[RTPATH_MAX + 1];
1361 rc2 = DrvAudioHlpFileNameGet(szFile, RT_ELEMENTS(szFile), szTemp, szName,
1362 0 /* Instance */, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
1363 if (RT_SUCCESS(rc2))
1364 {
1365 rc2 = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAG_NONE,
1366 &pSink->Dbg.pFile);
1367 if (RT_SUCCESS(rc2))
1368 rc2 = DrvAudioHlpFileOpen(pSink->Dbg.pFile, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS, &pSink->PCMProps);
1369 }
1370 }
1371 }
1372#endif
1373
1374 int rc2 = RTCritSectLeave(&pSink->CritSect);
1375 AssertRC(rc2);
1376
1377 LogFlowFuncLeaveRC(rc);
1378 return rc;
1379}
1380
1381/**
1382 * Set the current recording source of an input mixer sink, internal version.
1383 *
1384 * @return IPRT status code.
1385 * @param pSink Input mixer sink to set recording source for.
1386 * @param pStream Mixer stream to set as current recording source. Must be an input stream.
1387 * Specify NULL to un-set the current recording source.
1388 */
1389static int audioMixerSinkSetRecSourceInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1390{
1391 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT, ("Specified sink is not an input sink\n"));
1392
1393 if (pStream) /* Can be NULL if un-setting. */
1394 {
1395 AssertPtr(pStream->pStream);
1396 AssertMsg(pStream->pStream->enmDir == PDMAUDIODIR_IN, ("Specified stream is not an input stream\n"));
1397 }
1398
1399 pSink->In.pStreamRecSource = pStream;
1400
1401 LogFunc(("[%s] Recording source is now '%s'\n",
1402 pSink->pszName, pSink->In.pStreamRecSource ? pSink->In.pStreamRecSource->pszName : "<None>"));
1403 return VINF_SUCCESS;
1404}
1405
1406/**
1407 * Set the current recording source of an input mixer sink.
1408 *
1409 * @return IPRT status code.
1410 * @param pSink Input mixer sink to set recording source for.
1411 * @param pStream Mixer stream to set as current recording source. Must be an input stream.
1412 */
1413int AudioMixerSinkSetRecordingSource(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1414{
1415 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1416 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1417
1418 int rc = RTCritSectEnter(&pSink->CritSect);
1419 if (RT_FAILURE(rc))
1420 return rc;
1421
1422 rc = audioMixerSinkSetRecSourceInternal(pSink, pStream);
1423
1424 int rc2 = RTCritSectLeave(&pSink->CritSect);
1425 AssertRC(rc2);
1426
1427 return rc;
1428}
1429
1430/**
1431 * Sets the volume of an individual sink.
1432 *
1433 * @returns IPRT status code.
1434 * @param pSink Sink to set volume for.
1435 * @param pVol Volume to set.
1436 */
1437int AudioMixerSinkSetVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
1438{
1439 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1440 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
1441
1442 int rc = RTCritSectEnter(&pSink->CritSect);
1443 if (RT_FAILURE(rc))
1444 return rc;
1445
1446 memcpy(&pSink->Volume, pVol, sizeof(PDMAUDIOVOLUME));
1447
1448 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1449 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1450
1451 AssertPtr(pSink->pParent);
1452 rc = audioMixerSinkUpdateVolume(pSink, &pSink->pParent->VolMaster);
1453
1454 int rc2 = RTCritSectLeave(&pSink->CritSect);
1455 AssertRC(rc2);
1456
1457 return rc;
1458}
1459
1460/**
1461 * Updates a mixer sink, internal version.
1462 *
1463 * @returns IPRT status code.
1464 * @param pSink Mixer sink to update.
1465 */
1466static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink)
1467{
1468 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1469
1470 int rc = VINF_SUCCESS;
1471
1472#ifdef LOG_ENABLED
1473 char *pszStatus = dbgAudioMixerSinkStatusToStr(pSink->fStatus);
1474 Log3Func(("[%s] fStatus=%s\n", pSink->pszName, pszStatus));
1475 RTStrFree(pszStatus);
1476#endif
1477
1478 /* Sink disabled? Take a shortcut. */
1479 if (!(pSink->fStatus & AUDMIXSINK_STS_RUNNING))
1480 return rc;
1481
1482 /* Number of detected disabled streams of this sink. */
1483 uint8_t cStreamsDisabled = 0;
1484
1485 PAUDMIXSTREAM pMixStream, pMixStreamNext;
1486 RTListForEachSafe(&pSink->lstStreams, pMixStream, pMixStreamNext, AUDMIXSTREAM, Node)
1487 {
1488 PPDMAUDIOSTREAM pStream = pMixStream->pStream;
1489 AssertPtr(pStream);
1490
1491 PPDMIAUDIOCONNECTOR pConn = pMixStream->pConn;
1492 AssertPtr(pConn);
1493
1494 uint32_t cfProc = 0;
1495
1496 if (!DrvAudioHlpStreamStatusIsReady(pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream)))
1497 continue;
1498
1499 int rc2 = pConn->pfnStreamIterate(pConn, pStream);
1500 if (RT_SUCCESS(rc2))
1501 {
1502 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1503 {
1504 rc = pConn->pfnStreamCapture(pConn, pStream, &cfProc);
1505 if (RT_FAILURE(rc2))
1506 {
1507 LogFunc(("%s: Failed capturing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1508 if (RT_SUCCESS(rc))
1509 rc = rc2;
1510 continue;
1511 }
1512
1513 if (cfProc)
1514 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1515 }
1516 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1517 {
1518 rc2 = pConn->pfnStreamPlay(pConn, pStream, &cfProc);
1519 if (RT_FAILURE(rc2))
1520 {
1521 LogFunc(("%s: Failed playing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1522 if (RT_SUCCESS(rc))
1523 rc = rc2;
1524 continue;
1525 }
1526 }
1527 else
1528 {
1529 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1530 continue;
1531 }
1532
1533 PDMAUDIOSTREAMSTS strmSts = pConn->pfnStreamGetStatus(pConn, pStream);
1534
1535 /* Is the stream not enabled and also is not in a pending disable state anymore? */
1536 if ( !(strmSts & PDMAUDIOSTREAMSTS_FLAG_ENABLED)
1537 && !(strmSts & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE))
1538 {
1539 cStreamsDisabled++;
1540 }
1541 }
1542
1543 Log3Func(("\t%s: cPlayed/cCaptured=%RU32, rc2=%Rrc\n", pStream->szName, cfProc, rc2));
1544 }
1545
1546 Log3Func(("[%s] fPendingDisable=%RTbool, %RU8/%RU8 streams disabled\n",
1547 pSink->pszName, RT_BOOL(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE), cStreamsDisabled, pSink->cStreams));
1548
1549 /* Update last updated timestamp. */
1550 pSink->tsLastUpdatedMs = RTTimeMilliTS();
1551
1552 /* All streams disabled and the sink is in pending disable mode? */
1553 if ( cStreamsDisabled == pSink->cStreams
1554 && (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
1555 {
1556 audioMixerSinkReset(pSink);
1557 }
1558
1559 return rc;
1560}
1561
1562/**
1563 * Updates (invalidates) a mixer sink.
1564 *
1565 * @returns IPRT status code.
1566 * @param pSink Mixer sink to update.
1567 */
1568int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
1569{
1570 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1571
1572 int rc = RTCritSectEnter(&pSink->CritSect);
1573 if (RT_FAILURE(rc))
1574 return rc;
1575
1576 rc = audioMixerSinkUpdateInternal(pSink);
1577
1578 int rc2 = RTCritSectLeave(&pSink->CritSect);
1579 AssertRC(rc2);
1580
1581 return rc;
1582}
1583
1584/**
1585 * Updates the (master) volume of a mixer sink.
1586 *
1587 * @returns IPRT status code.
1588 * @param pSink Mixer sink to update volume for.
1589 * @param pVolMaster Master volume to set.
1590 */
1591static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
1592{
1593 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1594 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
1595
1596 LogFlowFunc(("[%s] Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1597 pSink->pszName, pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
1598 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32 ",
1599 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1600
1601 /** @todo Very crude implementation for now -- needs more work! */
1602
1603 pSink->VolumeCombined.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
1604
1605 pSink->VolumeCombined.uLeft = ( (pSink->Volume.uLeft ? pSink->Volume.uLeft : 1)
1606 * (pVolMaster->uLeft ? pVolMaster->uLeft : 1)) / PDMAUDIO_VOLUME_MAX;
1607
1608 pSink->VolumeCombined.uRight = ( (pSink->Volume.uRight ? pSink->Volume.uRight : 1)
1609 * (pVolMaster->uRight ? pVolMaster->uRight : 1)) / PDMAUDIO_VOLUME_MAX;
1610
1611 LogFlow(("-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1612 pSink->VolumeCombined.fMuted, pSink->VolumeCombined.uLeft, pSink->VolumeCombined.uRight));
1613
1614 /* Propagate new sink volume to all streams in the sink. */
1615 PAUDMIXSTREAM pMixStream;
1616 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1617 {
1618 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &pSink->VolumeCombined);
1619 AssertRC(rc2);
1620 }
1621
1622 return VINF_SUCCESS;
1623}
1624
1625/**
1626 * Writes audio output data to all connected mixer streams (multiplex).
1627 *
1628 * @returns IPRT status code.
1629 * @param pSink Sink to write audio output to.
1630 * @param enmOp What mixing operation to use. Currently not implemented.
1631 * @param pvBuf Pointer to audio data to write.
1632 * @param cbBuf Size (in bytes) of audio data to write.
1633 * @param pcbWrittenMin Returns minimum size (in bytes) successfully written by all mixer streams. Optional.
1634 */
1635int audioMixerSinkWriteToStreams(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWrittenMin)
1636{
1637 RT_NOREF(enmOp);
1638
1639 int rc = VINF_SUCCESS;
1640
1641 uint32_t cbWrittenMin = UINT32_MAX;
1642
1643 PAUDMIXSTREAM pMixStream;
1644 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1645 {
1646 if (!DrvAudioHlpStreamStatusCanWrite(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream)))
1647 continue;
1648
1649 PRTCIRCBUF pCircBuf = pMixStream->pCircBuf;
1650 void *pvChunk;
1651 size_t cbChunk;
1652
1653 uint32_t cbWritten = 0;
1654 uint32_t cbToWrite = RT_MIN(cbBuf, (uint32_t)RTCircBufFree(pCircBuf));
1655 while (cbToWrite)
1656 {
1657 RTCircBufAcquireWriteBlock(pCircBuf, cbToWrite, &pvChunk, &cbChunk);
1658
1659 if (cbChunk)
1660 memcpy(pvChunk, (uint8_t *)pvBuf + cbWritten, cbChunk);
1661
1662 RTCircBufReleaseWriteBlock(pCircBuf, cbChunk);
1663
1664 cbWritten += (uint32_t)cbChunk;
1665 Assert(cbWritten <= cbBuf);
1666
1667 Assert(cbToWrite >= cbChunk);
1668 cbToWrite -= (uint32_t)cbChunk;
1669 }
1670
1671 if (cbWritten < cbBuf)
1672 {
1673 LogFunc(("[%s] Warning: Only written %RU32/%RU32 bytes for stream '%s'\n",
1674 pSink->pszName, cbWritten, cbBuf, pMixStream->pszName));
1675 LogRel2(("Audio: Buffer overrun for mixer stream '%s' (sink '%s')\n", pMixStream->pszName, pSink->pszName));
1676#ifdef DEBUG_andy
1677 AssertFailed();
1678#endif
1679 }
1680
1681 cbWrittenMin = RT_MIN(cbWrittenMin, cbWritten);
1682
1683 if (cbWritten) /* Update the mixer stream's last written time stamp. */
1684 pMixStream->tsLastReadWrittenNs = RTTimeNanoTS();
1685
1686 cbToWrite = RT_MIN(pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream),
1687 (uint32_t)RTCircBufUsed(pCircBuf));
1688 cbToWrite = DrvAudioHlpBytesAlign(cbToWrite, &pSink->PCMProps);
1689
1690 int rc2 = VINF_SUCCESS;
1691
1692 while (cbToWrite)
1693 {
1694 RTCircBufAcquireReadBlock(pCircBuf, cbToWrite, &pvChunk, &cbChunk);
1695
1696 if (cbChunk)
1697 {
1698 rc2 = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, pvChunk, (uint32_t)cbChunk,
1699 &cbWritten);
1700 if (RT_FAILURE(rc2))
1701 LogFunc(("[%s] Failed writing to stream '%s': %Rrc\n", pSink->pszName, pMixStream->pszName, rc2));
1702 }
1703
1704 RTCircBufReleaseReadBlock(pCircBuf, cbWritten);
1705
1706 if ( !cbWritten
1707 || cbWritten < cbChunk)
1708 {
1709#ifdef DEBUG_andy
1710 AssertFailed();
1711#endif
1712 break;
1713 }
1714
1715 if (RT_FAILURE(rc2))
1716 break;
1717
1718 Assert(cbToWrite >= cbChunk);
1719 cbToWrite -= (uint32_t)cbChunk;
1720 }
1721 }
1722
1723 if (cbWrittenMin == UINT32_MAX) /* Nothing written? */
1724 cbWrittenMin = 0;
1725
1726 if (pcbWrittenMin)
1727 *pcbWrittenMin = cbWrittenMin;
1728
1729 return rc;
1730}
1731
1732/**
1733 * Writes data to a mixer sink.
1734 *
1735 * @returns IPRT status code.
1736 * @param pSink Sink to write data to.
1737 * @param enmOp Mixer operation to use when writing data to the sink.
1738 * @param pvBuf Buffer containing the audio data to write.
1739 * @param cbBuf Size (in bytes) of the buffer containing the audio data.
1740 * @param pcbWritten Number of bytes written. Optional.
1741 */
1742int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
1743{
1744 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1745 RT_NOREF(enmOp);
1746 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1747 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
1748 /* pcbWritten is optional. */
1749
1750 int rc = RTCritSectEnter(&pSink->CritSect);
1751 if (RT_FAILURE(rc))
1752 return rc;
1753
1754 AssertMsg(pSink->fStatus & AUDMIXSINK_STS_RUNNING,
1755 ("%s: Can't write to a sink which is not running (anymore) (status 0x%x)\n", pSink->pszName, pSink->fStatus));
1756 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
1757 ("%s: Can't write to a sink which is not an output sink\n", pSink->pszName));
1758
1759#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1760 uint32_t cbWritten = 0;
1761 uint32_t cbToWrite = cbBuf;
1762 while (cbToWrite)
1763 {
1764 /* First, write the data to the mixer sink's own mixing buffer.
1765 * Here the audio data can be transformed into the mixer sink's format. */
1766 uint32_t cfWritten = 0;
1767 rc = AudioMixBufWriteCirc(&pSink->MixBuf, (uint8_t *)pvBuf + cbWritten, cbToWrite, &cfWritten);
1768 if (RT_FAILURE(rc))
1769 break;
1770
1771 const uint32_t cbWrittenChunk = DrvAudioHlpFramesToBytes(cfWritten, &pSink->PCMProps);
1772
1773 Assert(cbToWrite >= cbWrittenChunk);
1774 cbToWrite -= cbWrittenChunk;
1775 cbWritten += cbWrittenChunk;
1776 }
1777
1778 Assert(cbWritten <= cbBuf);
1779
1780 if ( RT_FAILURE(rc)
1781 || cbWritten < cbBuf)
1782 {
1783 LogRel2(("Audio: Buffer overrun for mixer sink '%s' (only %RU32/%RU32 bytes written)\n",
1784 pSink->pszName, cbWritten, cbBuf));
1785# ifdef DEBUG_andy
1786 AssertFailed();
1787# endif
1788 }
1789
1790 /* Next, try to write (multiplex) as much audio data as possible to all connected mixer streams. */
1791 uint8_t arrChunkBuf[_1K]; /** @todo Hm ... some zero copy / shared buffers would be nice! */
1792 while (cbWritten)
1793 {
1794 uint32_t cfChunk;
1795 rc = AudioMixBufAcquireReadBlock(&pSink->MixBuf, arrChunkBuf, sizeof(arrChunkBuf), &cfChunk);
1796 if (RT_FAILURE(rc))
1797 break;
1798
1799 const uint32_t cbChunk = DrvAudioHlpFramesToBytes(cfChunk, &pSink->PCMProps);
1800 Assert(cbChunk <= sizeof(arrChunkBuf));
1801 rc = audioMixerSinkWriteToStreams(pSink, enmOp, arrChunkBuf, cbChunk, NULL /* pcbWrittenMin */);
1802 AudioMixBufReleaseReadBlock(&pSink->MixBuf, cfChunk);
1803
1804 Assert(cbWritten >= cbChunk);
1805 cbWritten -= cbChunk;
1806 }
1807
1808 if ( !(pSink->fStatus & AUDMIXSINK_STS_DIRTY)
1809 && AudioMixBufUsed(&pSink->MixBuf)) /* Still audio output data left? Consider the sink as being "dirty" then. */
1810 {
1811 /* Set dirty bit. */
1812 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1813 }
1814#else
1815 rc = audioMixerSinkWriteToStreams(pSink, enmOp, pvBuf, cbBuf, NULL /* pcbWrittenMin */);
1816#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
1817
1818 /* Update the sink's last written time stamp. */
1819 pSink->tsLastReadWrittenNs = RTTimeNanoTS();
1820
1821 if (pcbWritten)
1822 *pcbWritten = cbBuf; /* Always report everything written, as the backends need to keep up themselves. */
1823
1824 int rc2 = RTCritSectLeave(&pSink->CritSect);
1825 AssertRC(rc2);
1826
1827 return rc;
1828}
1829
1830/*********************************************************************************************************************************
1831 * Mixer Stream implementation.
1832 ********************************************************************************************************************************/
1833
1834/**
1835 * Controls a mixer stream, internal version.
1836 *
1837 * @returns IPRT status code.
1838 * @param pMixStream Mixer stream to control.
1839 * @param enmCmd Mixer stream command to use.
1840 * @param fCtl Additional control flags. Pass 0.
1841 */
1842int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
1843{
1844 AssertPtr(pMixStream->pConn);
1845 AssertPtr(pMixStream->pStream);
1846
1847 RT_NOREF(fCtl);
1848
1849 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
1850
1851 LogFlowFunc(("[%s] enmCmd=%ld, rc=%Rrc\n", pMixStream->pszName, enmCmd, rc));
1852
1853 return rc;
1854}
1855
1856/**
1857 * Controls a mixer stream.
1858 *
1859 * @returns IPRT status code.
1860 * @param pMixStream Mixer stream to control.
1861 * @param enmCmd Mixer stream command to use.
1862 * @param fCtl Additional control flags. Pass 0.
1863 */
1864int AudioMixerStreamCtl(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
1865{
1866 RT_NOREF(fCtl);
1867 AssertPtrReturn(pMixStream, VERR_INVALID_POINTER);
1868 /** @todo Validate fCtl. */
1869
1870 int rc = RTCritSectEnter(&pMixStream->CritSect);
1871 if (RT_FAILURE(rc))
1872 return rc;
1873
1874 rc = audioMixerStreamCtlInternal(pMixStream, enmCmd, fCtl);
1875
1876 int rc2 = RTCritSectLeave(&pMixStream->CritSect);
1877 if (RT_SUCCESS(rc))
1878 rc = rc2;
1879
1880 return rc;
1881}
1882
1883/**
1884 * Destroys a mixer stream, internal version.
1885 *
1886 * @param pMixStream Mixer stream to destroy.
1887 */
1888static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream)
1889{
1890 AssertPtrReturnVoid(pMixStream);
1891
1892 LogFunc(("%s\n", pMixStream->pszName));
1893
1894 if (pMixStream->pConn) /* Stream has a connector interface present? */
1895 {
1896 if (pMixStream->pStream)
1897 {
1898 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
1899 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
1900
1901 pMixStream->pStream = NULL;
1902 }
1903
1904 pMixStream->pConn = NULL;
1905 }
1906
1907 if (pMixStream->pszName)
1908 {
1909 RTStrFree(pMixStream->pszName);
1910 pMixStream->pszName = NULL;
1911 }
1912
1913 if (pMixStream->pCircBuf)
1914 {
1915 RTCircBufDestroy(pMixStream->pCircBuf);
1916 pMixStream->pCircBuf = NULL;
1917 }
1918
1919 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
1920 AssertRC(rc2);
1921
1922 RTMemFree(pMixStream);
1923 pMixStream = NULL;
1924}
1925
1926/**
1927 * Destroys a mixer stream.
1928 *
1929 * @param pMixStream Mixer stream to destroy.
1930 */
1931void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream)
1932{
1933 if (!pMixStream)
1934 return;
1935
1936 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1937 AssertRC(rc2);
1938
1939 LogFunc(("%s\n", pMixStream->pszName));
1940
1941 if (pMixStream->pSink) /* Is the stream part of a sink? */
1942 {
1943 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
1944 * pointer will be gone from the stream. */
1945 PAUDMIXSINK pSink = pMixStream->pSink;
1946
1947 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
1948 if (RT_SUCCESS(rc2))
1949 {
1950 Assert(pSink->cStreams);
1951 pSink->cStreams--;
1952 }
1953 }
1954 else
1955 rc2 = VINF_SUCCESS;
1956
1957 int rc3 = RTCritSectLeave(&pMixStream->CritSect);
1958 AssertRC(rc3);
1959
1960 if (RT_SUCCESS(rc2))
1961 {
1962 audioMixerStreamDestroyInternal(pMixStream);
1963 pMixStream = NULL;
1964 }
1965
1966 LogFlowFunc(("Returning %Rrc\n", rc2));
1967}
1968
1969/**
1970 * Returns whether a mixer stream currently is active (playing/recording) or not.
1971 *
1972 * @returns @c true if playing/recording, @c false if not.
1973 * @param pMixStream Mixer stream to return status for.
1974 */
1975bool AudioMixerStreamIsActive(PAUDMIXSTREAM pMixStream)
1976{
1977 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1978 if (RT_FAILURE(rc2))
1979 return false;
1980
1981 AssertPtr(pMixStream->pConn);
1982 AssertPtr(pMixStream->pStream);
1983
1984 bool fIsActive;
1985
1986 if ( pMixStream->pConn
1987 && pMixStream->pStream
1988 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
1989 {
1990 fIsActive = true;
1991 }
1992 else
1993 fIsActive = false;
1994
1995 rc2 = RTCritSectLeave(&pMixStream->CritSect);
1996 AssertRC(rc2);
1997
1998 return fIsActive;
1999}
2000
2001/**
2002 * Returns whether a mixer stream is valid (e.g. initialized and in a working state) or not.
2003 *
2004 * @returns @c true if valid, @c false if not.
2005 * @param pMixStream Mixer stream to return status for.
2006 */
2007bool AudioMixerStreamIsValid(PAUDMIXSTREAM pMixStream)
2008{
2009 if (!pMixStream)
2010 return false;
2011
2012 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2013 if (RT_FAILURE(rc2))
2014 return false;
2015
2016 bool fIsValid;
2017
2018 if ( pMixStream->pConn
2019 && pMixStream->pStream
2020 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED))
2021 {
2022 fIsValid = true;
2023 }
2024 else
2025 fIsValid = false;
2026
2027 rc2 = RTCritSectLeave(&pMixStream->CritSect);
2028 AssertRC(rc2);
2029
2030 return fIsValid;
2031}
2032
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