VirtualBox

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

Last change on this file since 78268 was 77603, checked in by vboxsync, 6 years ago

Audio/Mixer: audioMixerStreamCtlInternal() is static.

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