VirtualBox

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

Last change on this file since 81328 was 80687, checked in by vboxsync, 5 years ago

Audio/Mixer: Logging.

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