VirtualBox

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

Last change on this file since 88440 was 88433, checked in by vboxsync, 4 years ago

Audio: Eliminated the DrvAudio mixing buffers for output streams on devices without their own mixer. Changed the prebuffering to not default to the whole backend buffer size, but only 2/3 of it, so that there is room for a bit of incoming data from the device once we start playing. We don't want to have that gather in the device mixer or internal DMA buffers. This code needs some more testing and work, only tested on linux against ALSA. bugref:9890

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette