VirtualBox

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

Last change on this file since 86639 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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