VirtualBox

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

Last change on this file since 80596 was 78510, checked in by vboxsync, 6 years ago

Audio: Doxygen build fix.

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