VirtualBox

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

Last change on this file since 82401 was 82255, checked in by vboxsync, 5 years ago

vmm/pdmaudioifs.h: More of the same. bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 71.0 KB
Line 
1/* $Id: AudioMixer.cpp 82255 2019-11-27 23:20:26Z 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, %u bits, %RU8 channels, %RU32Hz)\n",
590 pSink->pszName, fFlags, pCfg->enmDir, pCfg->Props.cbSample * 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.enmDir = PDMAUDIODIR_IN;
608 CfgHost.u.enmSrc = pCfg->u.enmSrc;
609 CfgHost.enmLayout = pCfg->enmLayout;
610 }
611 else
612 {
613 CfgHost.enmDir = PDMAUDIODIR_OUT;
614 CfgHost.u.enmDst = pCfg->u.enmDst;
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: %u bit, %RU8 channels, %RU32Hz\n",
1413 pSink->pszName, pSink->PCMProps.cbSample * 8, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
1414
1415 memcpy(&pSink->PCMProps, pPCMProps, sizeof(PDMAUDIOPCMPROPS));
1416
1417 LogFlowFunc(("[%s] New format %u bit, %RU8 channels, %RU32Hz\n",
1418 pSink->pszName, pSink->PCMProps.cbSample * 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_FLAGS_NONE);
1450 if (RT_SUCCESS(rc2))
1451 {
1452 rc2 = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAGS_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 fStreamStatusNew = 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 (fStreamStatusNew & (PDMAUDIOSTREAMSTS_FLAGS_ENABLED | PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE))
1718 cStreamsDisabled--;
1719 /* Note: The mixer stream's internal status will be updated in the next iteration of this function. */
1720
1721 Log3Func(("\t%s: cPlayed/cCaptured=%RU32, rc2=%Rrc\n", pStream->szName, cfProc, rc2));
1722 }
1723
1724 Log3Func(("[%s] fPendingDisable=%RTbool, %RU8/%RU8 streams disabled\n",
1725 pSink->pszName, RT_BOOL(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE), cStreamsDisabled, pSink->cStreams));
1726
1727 /* Update last updated timestamp. */
1728 pSink->tsLastUpdatedMs = RTTimeMilliTS();
1729
1730 /* All streams disabled and the sink is in pending disable mode? */
1731 if ( cStreamsDisabled == pSink->cStreams
1732 && (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
1733 {
1734 audioMixerSinkReset(pSink);
1735 }
1736
1737 return rc;
1738}
1739
1740/**
1741 * Updates (invalidates) a mixer sink.
1742 *
1743 * @returns IPRT status code.
1744 * @param pSink Mixer sink to update.
1745 */
1746int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
1747{
1748 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1749
1750 int rc = RTCritSectEnter(&pSink->CritSect);
1751 if (RT_FAILURE(rc))
1752 return rc;
1753
1754 rc = audioMixerSinkUpdateInternal(pSink);
1755
1756 int rc2 = RTCritSectLeave(&pSink->CritSect);
1757 AssertRC(rc2);
1758
1759 return rc;
1760}
1761
1762/**
1763 * Updates the (master) volume of a mixer sink.
1764 *
1765 * @returns IPRT status code.
1766 * @param pSink Mixer sink to update volume for.
1767 * @param pVolMaster Master volume to set.
1768 */
1769static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
1770{
1771 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1772 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
1773
1774 LogFlowFunc(("[%s] Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1775 pSink->pszName, pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
1776 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32 ",
1777 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1778
1779 /** @todo Very crude implementation for now -- needs more work! */
1780
1781 pSink->VolumeCombined.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
1782
1783 pSink->VolumeCombined.uLeft = ( (pSink->Volume.uLeft ? pSink->Volume.uLeft : 1)
1784 * (pVolMaster->uLeft ? pVolMaster->uLeft : 1)) / PDMAUDIO_VOLUME_MAX;
1785
1786 pSink->VolumeCombined.uRight = ( (pSink->Volume.uRight ? pSink->Volume.uRight : 1)
1787 * (pVolMaster->uRight ? pVolMaster->uRight : 1)) / PDMAUDIO_VOLUME_MAX;
1788
1789 LogFlow(("-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1790 pSink->VolumeCombined.fMuted, pSink->VolumeCombined.uLeft, pSink->VolumeCombined.uRight));
1791
1792 /* Propagate new sink volume to all streams in the sink. */
1793 PAUDMIXSTREAM pMixStream;
1794 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1795 {
1796 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &pSink->VolumeCombined);
1797 AssertRC(rc2);
1798 }
1799
1800 return VINF_SUCCESS;
1801}
1802
1803/**
1804 * Writes (buffered) output data of a sink's stream to the bound audio connector stream.
1805 *
1806 * @returns IPRT status code.
1807 * @param pSink Sink of stream that contains the mixer stream.
1808 * @param pMixStream Mixer stream to write output data for.
1809 */
1810static int audioMixerSinkWriteToStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pMixStream)
1811{
1812 if (!pMixStream->pCircBuf)
1813 return VINF_SUCCESS;
1814
1815 return audioMixerSinkWriteToStreamEx(pSink, pMixStream, (uint32_t)RTCircBufUsed(pMixStream->pCircBuf), NULL /* pcbWritten */);
1816}
1817
1818/**
1819 * Writes (buffered) output data of a sink's stream to the bound audio connector stream, extended version.
1820 *
1821 * @returns IPRT status code.
1822 * @param pSink Sink of stream that contains the mixer stream.
1823 * @param pMixStream Mixer stream to write output data for.
1824 * @param cbToWrite Size (in bytes) to write.
1825 * @param pcbWritten Size (in bytes) written on success. Optional.
1826 */
1827static int audioMixerSinkWriteToStreamEx(PAUDMIXSINK pSink, PAUDMIXSTREAM pMixStream, uint32_t cbToWrite, uint32_t *pcbWritten)
1828{
1829 /* pcbWritten is optional. */
1830
1831 if ( !cbToWrite
1832 || !(pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED))
1833 {
1834 if (pcbWritten)
1835 *pcbWritten = 0;
1836
1837 return VINF_SUCCESS;
1838 }
1839
1840 PRTCIRCBUF pCircBuf = pMixStream->pCircBuf;
1841
1842 const uint32_t cbWritableStream = pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream);
1843 cbToWrite = RT_MIN(cbToWrite, RT_MIN((uint32_t)RTCircBufUsed(pCircBuf), cbWritableStream));
1844
1845 Log3Func(("[%s] cbWritableStream=%RU32, cbToWrite=%RU32\n",
1846 pMixStream->pszName, cbWritableStream, cbToWrite));
1847
1848 uint32_t cbWritten = 0;
1849
1850 int rc = VINF_SUCCESS;
1851
1852 while (cbToWrite)
1853 {
1854 void *pvChunk;
1855 size_t cbChunk;
1856 RTCircBufAcquireReadBlock(pCircBuf, cbToWrite, &pvChunk, &cbChunk);
1857
1858 Log3Func(("[%s] cbChunk=%RU32\n", pMixStream->pszName, cbChunk));
1859
1860 uint32_t cbChunkWritten = 0;
1861 if (cbChunk)
1862 {
1863 rc = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, pvChunk, (uint32_t)cbChunk,
1864 &cbChunkWritten);
1865 if (RT_FAILURE(rc))
1866 {
1867 if (rc == VERR_BUFFER_OVERFLOW)
1868 {
1869 LogRel2(("Mixer: Buffer overrun for mixer stream '%s' (sink '%s')\n", pMixStream->pszName, pSink->pszName));
1870 break;
1871 }
1872 else if (rc == VERR_AUDIO_STREAM_NOT_READY)
1873 {
1874 /* Stream is not enabled, just skip. */
1875 rc = VINF_SUCCESS;
1876 }
1877 else
1878 LogRel2(("Mixer: Writing to mixer stream '%s' (sink '%s') failed, rc=%Rrc\n",
1879 pMixStream->pszName, pSink->pszName, rc));
1880
1881 if (RT_FAILURE(rc))
1882 LogFunc(("[%s] Failed writing to stream '%s': %Rrc\n", pSink->pszName, pMixStream->pszName, rc));
1883 }
1884 }
1885
1886 RTCircBufReleaseReadBlock(pCircBuf, cbChunkWritten);
1887
1888 if ( RT_FAILURE(rc)
1889 || !cbChunkWritten)
1890 break;
1891
1892 Assert(cbToWrite >= cbChunkWritten);
1893 cbToWrite -= (uint32_t)cbChunkWritten;
1894
1895 cbWritten += (uint32_t)cbChunkWritten;
1896 }
1897
1898 Log3Func(("[%s] cbWritten=%RU32\n", pMixStream->pszName, cbWritten));
1899
1900 if (pcbWritten)
1901 *pcbWritten = cbWritten;
1902
1903#ifdef DEBUG_andy
1904 AssertRC(rc);
1905#endif
1906
1907 return rc;
1908}
1909
1910/**
1911 * Multiplexes audio output data to all connected mixer streams in a synchronized fashion, e.g.
1912 * only multiplex as much data as all streams can handle at this time.
1913 *
1914 * @returns IPRT status code.
1915 * @param pSink Sink to write audio output to.
1916 * @param enmOp What mixing operation to use. Currently not implemented.
1917 * @param pvBuf Pointer to audio data to write.
1918 * @param cbBuf Size (in bytes) of audio data to write.
1919 * @param pcbWrittenMin Returns minimum size (in bytes) successfully written to all mixer streams. Optional.
1920 */
1921static int audioMixerSinkMultiplexSync(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf,
1922 uint32_t *pcbWrittenMin)
1923{
1924 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1925 RT_NOREF(enmOp);
1926
1927 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
1928 ("%s: Can't multiplex to a sink which is not an output sink\n", pSink->pszName));
1929
1930 int rc = VINF_SUCCESS;
1931
1932 uint32_t cbToWriteMin = UINT32_MAX;
1933
1934 Log3Func(("[%s] cbBuf=%RU32\n", pSink->pszName, cbBuf));
1935
1936 PAUDMIXSTREAM pMixStream;
1937 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1938 {
1939 if (!(pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)) /* Mixing stream not enabled? Skip handling. */
1940 {
1941 Log3Func(("[%s] Stream '%s' disabled, skipping ...\n", pSink->pszName, pMixStream->pszName));
1942 continue;
1943 }
1944
1945 cbToWriteMin = RT_MIN(cbBuf, RT_MIN(cbToWriteMin, (uint32_t)RTCircBufFree(pMixStream->pCircBuf)));
1946 }
1947
1948 if (cbToWriteMin == UINT32_MAX) /* No space at all? */
1949 cbToWriteMin = 0;
1950
1951 if (cbToWriteMin)
1952 {
1953 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1954 {
1955 if (!(pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)) /* Mixing stream not enabled? Skip handling. */
1956 continue;
1957
1958 PRTCIRCBUF pCircBuf = pMixStream->pCircBuf;
1959 void *pvChunk;
1960 size_t cbChunk;
1961
1962 uint32_t cbWrittenBuf = 0;
1963 uint32_t cbToWriteBuf = cbToWriteMin;
1964
1965 while (cbToWriteBuf)
1966 {
1967 RTCircBufAcquireWriteBlock(pCircBuf, cbToWriteBuf, &pvChunk, &cbChunk);
1968
1969 if (cbChunk)
1970 memcpy(pvChunk, (uint8_t *)pvBuf + cbWrittenBuf, cbChunk);
1971
1972 RTCircBufReleaseWriteBlock(pCircBuf, cbChunk);
1973
1974 cbWrittenBuf += (uint32_t)cbChunk;
1975 Assert(cbWrittenBuf <= cbBuf);
1976
1977 Assert(cbToWriteBuf >= cbChunk);
1978 cbToWriteBuf -= (uint32_t)cbChunk;
1979 }
1980
1981 if (cbWrittenBuf) /* Update the mixer stream's last written time stamp. */
1982 pMixStream->tsLastReadWrittenNs = RTTimeNanoTS();
1983
1984 Log3Func(("[%s] Mixer stream '%s' -> cbWrittenBuf=%RU32\n", pSink->pszName, pMixStream->pszName, cbWrittenBuf));
1985 }
1986 }
1987
1988 Log3Func(("[%s] cbBuf=%RU32, cbToWriteMin=%RU32\n", pSink->pszName, cbBuf, cbToWriteMin));
1989
1990 if (pcbWrittenMin)
1991 *pcbWrittenMin = cbToWriteMin;
1992
1993 return rc;
1994}
1995
1996/**
1997 * Writes data to a mixer sink.
1998 *
1999 * @returns IPRT status code.
2000 * @param pSink Sink to write data to.
2001 * @param enmOp Mixer operation to use when writing data to the sink.
2002 * @param pvBuf Buffer containing the audio data to write.
2003 * @param cbBuf Size (in bytes) of the buffer containing the audio data.
2004 * @param pcbWritten Number of bytes written. Optional.
2005 */
2006int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
2007{
2008 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2009 RT_NOREF(enmOp);
2010 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2011 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
2012 /* pcbWritten is optional. */
2013
2014 int rc = RTCritSectEnter(&pSink->CritSect);
2015 if (RT_FAILURE(rc))
2016 return rc;
2017
2018 AssertMsg(pSink->fStatus & AUDMIXSINK_STS_RUNNING,
2019 ("%s: Can't write to a sink which is not running (anymore) (status 0x%x)\n", pSink->pszName, pSink->fStatus));
2020 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
2021 ("%s: Can't write to a sink which is not an output sink\n", pSink->pszName));
2022
2023 uint32_t cbWritten = 0;
2024 uint32_t cbToWrite = RT_MIN(AudioMixBufFreeBytes(&pSink->MixBuf), cbBuf);
2025 while (cbToWrite)
2026 {
2027 /* First, write the data to the mixer sink's own mixing buffer.
2028 * Here the audio data can be transformed into the mixer sink's format. */
2029 uint32_t cfWritten = 0;
2030 rc = AudioMixBufWriteCirc(&pSink->MixBuf, (uint8_t *)pvBuf + cbWritten, cbToWrite, &cfWritten);
2031 if (RT_FAILURE(rc))
2032 break;
2033
2034 const uint32_t cbWrittenChunk = DrvAudioHlpFramesToBytes(cfWritten, &pSink->PCMProps);
2035
2036 Assert(cbToWrite >= cbWrittenChunk);
2037 cbToWrite -= cbWrittenChunk;
2038 cbWritten += cbWrittenChunk;
2039 }
2040
2041 Log3Func(("[%s] cbBuf=%RU32 -> cbWritten=%RU32\n", pSink->pszName, cbBuf, cbWritten));
2042
2043 /* Update the sink's last written time stamp. */
2044 pSink->tsLastReadWrittenNs = RTTimeNanoTS();
2045
2046 if (pcbWritten)
2047 *pcbWritten = cbWritten;
2048
2049 int rc2 = RTCritSectLeave(&pSink->CritSect);
2050 AssertRC(rc2);
2051
2052 return rc;
2053}
2054
2055/*********************************************************************************************************************************
2056 * Mixer Stream implementation.
2057 ********************************************************************************************************************************/
2058
2059/**
2060 * Controls a mixer stream, internal version.
2061 *
2062 * @returns IPRT status code.
2063 * @param pMixStream Mixer stream to control.
2064 * @param enmCmd Mixer stream command to use.
2065 * @param fCtl Additional control flags. Pass 0.
2066 */
2067static int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
2068{
2069 AssertPtr(pMixStream->pConn);
2070 AssertPtr(pMixStream->pStream);
2071
2072 RT_NOREF(fCtl);
2073
2074 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
2075
2076 LogFlowFunc(("[%s] enmCmd=%ld, rc=%Rrc\n", pMixStream->pszName, enmCmd, rc));
2077
2078 return rc;
2079}
2080
2081/**
2082 * Updates a mixer stream's internal status.
2083 *
2084 * @returns VBox status code.
2085 * @param pMixStream Mixer stream to to update internal status for.
2086 */
2087static int audioMixerStreamUpdateStatus(PAUDMIXSTREAM pMixStream)
2088{
2089 pMixStream->fStatus = AUDMIXSTREAM_STATUS_NONE;
2090
2091 if (pMixStream->pConn) /* Audio connector available? */
2092 {
2093 const uint32_t fStreamStatus = pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream);
2094
2095 if (DrvAudioHlpStreamStatusIsReady(fStreamStatus))
2096 pMixStream->fStatus |= AUDMIXSTREAM_STATUS_ENABLED;
2097
2098 AssertPtr(pMixStream->pSink);
2099 switch (pMixStream->pSink->enmDir)
2100 {
2101 case AUDMIXSINKDIR_INPUT:
2102 if (DrvAudioHlpStreamStatusCanRead(fStreamStatus))
2103 pMixStream->fStatus |= AUDMIXSTREAM_STATUS_CAN_READ;
2104 break;
2105
2106 case AUDMIXSINKDIR_OUTPUT:
2107 if (DrvAudioHlpStreamStatusCanWrite(fStreamStatus))
2108 pMixStream->fStatus |= AUDMIXSTREAM_STATUS_CAN_WRITE;
2109 break;
2110
2111 default:
2112 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
2113 break;
2114 }
2115 }
2116
2117 LogFlowFunc(("[%s] -> 0x%x\n", pMixStream->pszName, pMixStream->fStatus));
2118 return VINF_SUCCESS;
2119}
2120
2121/**
2122 * Controls a mixer stream.
2123 *
2124 * @returns IPRT status code.
2125 * @param pMixStream Mixer stream to control.
2126 * @param enmCmd Mixer stream command to use.
2127 * @param fCtl Additional control flags. Pass 0.
2128 */
2129int AudioMixerStreamCtl(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
2130{
2131 RT_NOREF(fCtl);
2132 AssertPtrReturn(pMixStream, VERR_INVALID_POINTER);
2133 /** @todo Validate fCtl. */
2134
2135 int rc = RTCritSectEnter(&pMixStream->CritSect);
2136 if (RT_FAILURE(rc))
2137 return rc;
2138
2139 rc = audioMixerStreamCtlInternal(pMixStream, enmCmd, fCtl);
2140
2141 int rc2 = RTCritSectLeave(&pMixStream->CritSect);
2142 if (RT_SUCCESS(rc))
2143 rc = rc2;
2144
2145 return rc;
2146}
2147
2148/**
2149 * Destroys a mixer stream, internal version.
2150 *
2151 * @param pMixStream Mixer stream to destroy.
2152 */
2153static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream)
2154{
2155 AssertPtrReturnVoid(pMixStream);
2156
2157 LogFunc(("%s\n", pMixStream->pszName));
2158
2159 if (pMixStream->pConn) /* Stream has a connector interface present? */
2160 {
2161 if (pMixStream->pStream)
2162 {
2163 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
2164 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
2165
2166 pMixStream->pStream = NULL;
2167 }
2168
2169 pMixStream->pConn = NULL;
2170 }
2171
2172 if (pMixStream->pszName)
2173 {
2174 RTStrFree(pMixStream->pszName);
2175 pMixStream->pszName = NULL;
2176 }
2177
2178 if (pMixStream->pCircBuf)
2179 {
2180 RTCircBufDestroy(pMixStream->pCircBuf);
2181 pMixStream->pCircBuf = NULL;
2182 }
2183
2184 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
2185 AssertRC(rc2);
2186
2187 RTMemFree(pMixStream);
2188 pMixStream = NULL;
2189}
2190
2191/**
2192 * Destroys a mixer stream.
2193 *
2194 * @param pMixStream Mixer stream to destroy.
2195 */
2196void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream)
2197{
2198 if (!pMixStream)
2199 return;
2200
2201 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2202 AssertRC(rc2);
2203
2204 LogFunc(("%s\n", pMixStream->pszName));
2205
2206 if (pMixStream->pSink) /* Is the stream part of a sink? */
2207 {
2208 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
2209 * pointer will be gone from the stream. */
2210 PAUDMIXSINK pSink = pMixStream->pSink;
2211
2212 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
2213 if (RT_SUCCESS(rc2))
2214 {
2215 Assert(pSink->cStreams);
2216 pSink->cStreams--;
2217 }
2218 }
2219 else
2220 rc2 = VINF_SUCCESS;
2221
2222 int rc3 = RTCritSectLeave(&pMixStream->CritSect);
2223 AssertRC(rc3);
2224
2225 if (RT_SUCCESS(rc2))
2226 {
2227 audioMixerStreamDestroyInternal(pMixStream);
2228 pMixStream = NULL;
2229 }
2230
2231 LogFlowFunc(("Returning %Rrc\n", rc2));
2232}
2233
2234/**
2235 * Returns whether a mixer stream currently is active (playing/recording) or not.
2236 *
2237 * @returns @c true if playing/recording, @c false if not.
2238 * @param pMixStream Mixer stream to return status for.
2239 */
2240bool AudioMixerStreamIsActive(PAUDMIXSTREAM pMixStream)
2241{
2242 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2243 if (RT_FAILURE(rc2))
2244 return false;
2245
2246 AssertPtr(pMixStream->pConn);
2247 AssertPtr(pMixStream->pStream);
2248
2249 bool fIsActive;
2250
2251 if ( pMixStream->pConn
2252 && pMixStream->pStream
2253 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAGS_ENABLED))
2254 {
2255 fIsActive = true;
2256 }
2257 else
2258 fIsActive = false;
2259
2260 rc2 = RTCritSectLeave(&pMixStream->CritSect);
2261 AssertRC(rc2);
2262
2263 return fIsActive;
2264}
2265
2266/**
2267 * Returns whether a mixer stream is valid (e.g. initialized and in a working state) or not.
2268 *
2269 * @returns @c true if valid, @c false if not.
2270 * @param pMixStream Mixer stream to return status for.
2271 */
2272bool AudioMixerStreamIsValid(PAUDMIXSTREAM pMixStream)
2273{
2274 if (!pMixStream)
2275 return false;
2276
2277 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2278 if (RT_FAILURE(rc2))
2279 return false;
2280
2281 bool fIsValid;
2282
2283 if ( pMixStream->pConn
2284 && pMixStream->pStream
2285 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED))
2286 {
2287 fIsValid = true;
2288 }
2289 else
2290 fIsValid = false;
2291
2292 rc2 = RTCritSectLeave(&pMixStream->CritSect);
2293 AssertRC(rc2);
2294
2295 return fIsValid;
2296}
2297
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