VirtualBox

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

Last change on this file since 88448 was 88443, checked in by vboxsync, 4 years ago

DrvAudio: Don't try set volume on output streams when adding them. Don't bother iterating streams when we've got zero bytes to push during mixer update. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.7 KB
Line 
1/* $Id: AudioMixer.cpp 88443 2021-04-09 17:21:32Z 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) && pSink->enmDir != AUDMIXSINKDIR_OUTPUT)
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 > 0)
1813 {
1814 if (cFramesToRead > 0)
1815 {
1816 /*
1817 * For each of the enabled streams, convert cFramesToRead frames from
1818 * the mixing buffer and write that to the downstream driver.
1819 */
1820 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1821 {
1822 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1823 {
1824 uint32_t offSrcFrame = 0;
1825 do
1826 {
1827 /* Convert a chunk from the mixer buffer. */
1828 union
1829 {
1830 uint8_t ab[8192];
1831 uint64_t au64[8192 / sizeof(uint64_t)]; /* Use uint64_t to ensure good alignment. */
1832 } Buf;
1833 uint32_t cbDstPeeked = sizeof(Buf);
1834 uint32_t cSrcFramesPeeked = cFramesToRead - offSrcFrame;
1835 AudioMixBufPeek(&pSink->MixBuf, offSrcFrame, cSrcFramesPeeked, &cSrcFramesPeeked,
1836 &pMixStream->PeekState, &Buf, sizeof(Buf), &cbDstPeeked);
1837 offSrcFrame += cSrcFramesPeeked;
1838
1839 /* Write it to the backend. Since've checked that there is buffer
1840 space available, this should always write the whole buffer. */
1841 uint32_t cbDstWritten = 0;
1842 int rc2 = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream,
1843 &Buf, cbDstPeeked, &cbDstWritten);
1844 Log3Func(("%s: %#x L %#x => %#x bytes; wrote %#x rc2=%Rrc %s\n", pSink->pszName, offSrcFrame,
1845 cSrcFramesPeeked - cSrcFramesPeeked, cbDstPeeked, cbDstWritten, rc2, pMixStream->pszName));
1846 if (RT_SUCCESS(rc2))
1847 AssertLogRelMsg(cbDstWritten == cbDstPeeked,
1848 ("cbDstWritten=%#x cbDstPeeked=%#x - (sink '%s')\n",
1849 cbDstWritten, cbDstPeeked, pSink->pszName));
1850 else if (rc2 == VERR_AUDIO_STREAM_NOT_READY)
1851 {
1852 LogRel2(("Audio Mixer: '%s' (sink '%s'): Stream not ready - skipping.\n",
1853 pMixStream->pszName, pSink->pszName));
1854 break; /* must've changed status, stop processing */
1855 }
1856 else
1857 {
1858 Assert(rc2 != VERR_BUFFER_OVERFLOW);
1859 LogRel2(("Audio Mixer: Writing to mixer stream '%s' (sink '%s') failed, rc=%Rrc\n",
1860 pMixStream->pszName, pSink->pszName, rc2));
1861 break;
1862 }
1863 } while (offSrcFrame < cFramesToRead);
1864 }
1865 }
1866
1867 AudioMixBufAdvance(&pSink->MixBuf, cFramesToRead);
1868 }
1869
1870 /*
1871 * Update the dirty flag for what it's worth.
1872 */
1873 if (AudioMixBufUsed(&pSink->MixBuf))
1874 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1875 else
1876 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1877 }
1878 else
1879 {
1880 /*
1881 * If no writable streams, just drop the mixer buffer content.
1882 */
1883 AudioMixBufDrop(&pSink->MixBuf);
1884 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1885 }
1886
1887 /*
1888 * Iterate buffers (pfnStreamPlay is not used any more).
1889 */
1890 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1891 {
1892 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1893 pMixStream->pConn->pfnStreamIterate(pMixStream->pConn, pMixStream->pStream);
1894 }
1895
1896 /* Update last updated timestamp. */
1897 pSink->tsLastUpdatedMs = RTTimeMilliTS();
1898
1899 /*
1900 * Deal with pending disable.
1901 * We reset the sink when all streams have been disabled.
1902 */
1903 if (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE)
1904 {
1905 uint32_t const cStreams = pSink->cStreams;
1906 uint32_t cStreamsDisabled = pSink->cStreams;
1907 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1908 {
1909 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1910 {
1911 PDMAUDIOSTREAMSTS const fSts = pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream);
1912 if (fSts & (PDMAUDIOSTREAMSTS_FLAGS_ENABLED | PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE))
1913 cStreamsDisabled--;
1914 }
1915 }
1916 Log3Func(("[%s] pending disable: %u of %u disabled\n", pSink->pszName, cStreamsDisabled, cStreams));
1917 if (cStreamsDisabled == cStreams)
1918 audioMixerSinkReset(pSink);
1919 }
1920
1921 return VINF_SUCCESS;
1922}
1923
1924/**
1925 * Updates (invalidates) a mixer sink.
1926 *
1927 * @returns VBox status code.
1928 * @param pSink Mixer sink to update.
1929 */
1930int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
1931{
1932 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1933 int rc = RTCritSectEnter(&pSink->CritSect);
1934 AssertRCReturn(rc, rc);
1935
1936#ifdef LOG_ENABLED
1937 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
1938#endif
1939 Log3Func(("[%s] fStatus=%s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
1940
1941 /* Only process running sinks. */
1942 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
1943 {
1944 /* Do separate processing for input and output sinks. */
1945 if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1946 rc = audioMixerSinkUpdateOutput(pSink);
1947 else if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1948 rc = audioMixerSinkUpdateInput(pSink);
1949 else
1950 AssertFailed();
1951 }
1952 else
1953 rc = VINF_SUCCESS; /* disabled */
1954
1955 RTCritSectLeave(&pSink->CritSect);
1956 return rc;
1957}
1958
1959/**
1960 * Updates the (master) volume of a mixer sink.
1961 *
1962 * @returns VBox status code.
1963 * @param pSink Mixer sink to update volume for.
1964 * @param pVolMaster Master volume to set.
1965 */
1966static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
1967{
1968 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1969 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
1970
1971 LogFlowFunc(("[%s] Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1972 pSink->pszName, pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
1973 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32 ",
1974 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1975
1976 /** @todo Very crude implementation for now -- needs more work! */
1977
1978 pSink->VolumeCombined.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
1979
1980 pSink->VolumeCombined.uLeft = ( (pSink->Volume.uLeft ? pSink->Volume.uLeft : 1)
1981 * (pVolMaster->uLeft ? pVolMaster->uLeft : 1)) / PDMAUDIO_VOLUME_MAX;
1982
1983 pSink->VolumeCombined.uRight = ( (pSink->Volume.uRight ? pSink->Volume.uRight : 1)
1984 * (pVolMaster->uRight ? pVolMaster->uRight : 1)) / PDMAUDIO_VOLUME_MAX;
1985
1986 LogFlow(("-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1987 pSink->VolumeCombined.fMuted, pSink->VolumeCombined.uLeft, pSink->VolumeCombined.uRight));
1988
1989 /*
1990 * Input sinks must currently propagate the new volume settings to
1991 * all the streams. (For output sinks we do the volume control here.)
1992 */
1993 if (pSink->enmDir != AUDMIXSINKDIR_OUTPUT)
1994 {
1995 PAUDMIXSTREAM pMixStream;
1996 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1997 {
1998 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &pSink->VolumeCombined);
1999 AssertRC(rc2);
2000 }
2001 }
2002
2003 return VINF_SUCCESS;
2004}
2005
2006/**
2007 * Writes data to a mixer output sink.
2008 *
2009 * @returns VBox status code.
2010 * @param pSink Sink to write data to.
2011 * @param enmOp Mixer operation to use when writing data to the sink.
2012 * @param pvBuf Buffer containing the audio data to write.
2013 * @param cbBuf Size (in bytes) of the buffer containing the audio data.
2014 * @param pcbWritten Number of bytes written. Optional.
2015 */
2016int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
2017{
2018 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2019 RT_NOREF(enmOp);
2020 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2021 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
2022 /* pcbWritten is optional. */
2023
2024 int rc = RTCritSectEnter(&pSink->CritSect);
2025 AssertRCReturn(rc, rc);
2026
2027 AssertMsg(pSink->fStatus & AUDMIXSINK_STS_RUNNING,
2028 ("%s: Can't write to a sink which is not running (anymore) (status 0x%x)\n", pSink->pszName, pSink->fStatus));
2029 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
2030 ("%s: Can't write to a sink which is not an output sink\n", pSink->pszName));
2031
2032 uint32_t cbWritten = 0;
2033 uint32_t cbToWrite = RT_MIN(AudioMixBufFreeBytes(&pSink->MixBuf), cbBuf);
2034 while (cbToWrite)
2035 {
2036 /* Write the data to the mixer sink's own mixing buffer.
2037 Here the audio data is transformed into the mixer sink's format. */
2038 uint32_t cFramesWritten = 0;
2039 rc = AudioMixBufWriteCirc(&pSink->MixBuf, (uint8_t const*)pvBuf + cbWritten, cbToWrite, &cFramesWritten);
2040 if (RT_SUCCESS(rc))
2041 {
2042 const uint32_t cbWrittenChunk = PDMAudioPropsFramesToBytes(&pSink->PCMProps, cFramesWritten);
2043 Assert(cbToWrite >= cbWrittenChunk);
2044 cbToWrite -= cbWrittenChunk;
2045 cbWritten += cbWrittenChunk;
2046 }
2047 else
2048 break;
2049 }
2050
2051 Log3Func(("[%s] cbBuf=%RU32 -> cbWritten=%RU32\n", pSink->pszName, cbBuf, cbWritten));
2052
2053 /* Update the sink's last written time stamp. */
2054 pSink->tsLastReadWrittenNs = RTTimeNanoTS();
2055
2056 if (pcbWritten)
2057 *pcbWritten = cbWritten;
2058
2059 RTCritSectLeave(&pSink->CritSect);
2060 return rc;
2061}
2062
2063
2064/*********************************************************************************************************************************
2065 * Mixer Stream implementation.
2066 ********************************************************************************************************************************/
2067
2068/**
2069 * Controls a mixer stream, internal version.
2070 *
2071 * @returns VBox status code.
2072 * @param pMixStream Mixer stream to control.
2073 * @param enmCmd Mixer stream command to use.
2074 * @param fCtl Additional control flags. Pass 0.
2075 */
2076static int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
2077{
2078 AssertPtr(pMixStream->pConn);
2079 AssertPtr(pMixStream->pStream);
2080
2081 RT_NOREF(fCtl);
2082
2083 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
2084
2085 LogFlowFunc(("[%s] enmCmd=%ld, rc=%Rrc\n", pMixStream->pszName, enmCmd, rc));
2086
2087 return rc;
2088}
2089
2090/**
2091 * Updates a mixer stream's internal status.
2092 *
2093 * @returns VBox status code.
2094 * @param pMixStream Mixer stream to to update internal status for.
2095 */
2096static int audioMixerStreamUpdateStatus(PAUDMIXSTREAM pMixStream)
2097{
2098 pMixStream->fStatus = AUDMIXSTREAM_STATUS_NONE;
2099
2100 if (pMixStream->pConn) /* Audio connector available? */
2101 {
2102 const uint32_t fStreamStatus = pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream);
2103
2104 if (PDMAudioStrmStatusIsReady(fStreamStatus))
2105 pMixStream->fStatus |= AUDMIXSTREAM_STATUS_ENABLED;
2106
2107 AssertPtr(pMixStream->pSink);
2108 switch (pMixStream->pSink->enmDir)
2109 {
2110 case AUDMIXSINKDIR_INPUT:
2111 if (PDMAudioStrmStatusCanRead(fStreamStatus))
2112 pMixStream->fStatus |= AUDMIXSTREAM_STATUS_CAN_READ;
2113 break;
2114
2115 case AUDMIXSINKDIR_OUTPUT:
2116 if (PDMAudioStrmStatusCanWrite(fStreamStatus))
2117 pMixStream->fStatus |= AUDMIXSTREAM_STATUS_CAN_WRITE;
2118 break;
2119
2120 default:
2121 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
2122 break;
2123 }
2124 }
2125
2126 LogFlowFunc(("[%s] -> 0x%x\n", pMixStream->pszName, pMixStream->fStatus));
2127 return VINF_SUCCESS;
2128}
2129
2130/**
2131 * Controls a mixer stream.
2132 *
2133 * @returns VBox status code.
2134 * @param pMixStream Mixer stream to control.
2135 * @param enmCmd Mixer stream command to use.
2136 * @param fCtl Additional control flags. Pass 0.
2137 */
2138int AudioMixerStreamCtl(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
2139{
2140 RT_NOREF(fCtl);
2141 AssertPtrReturn(pMixStream, VERR_INVALID_POINTER);
2142 /** @todo Validate fCtl. */
2143
2144 int rc = RTCritSectEnter(&pMixStream->CritSect);
2145 if (RT_FAILURE(rc))
2146 return rc;
2147
2148 rc = audioMixerStreamCtlInternal(pMixStream, enmCmd, fCtl);
2149
2150 int rc2 = RTCritSectLeave(&pMixStream->CritSect);
2151 if (RT_SUCCESS(rc))
2152 rc = rc2;
2153
2154 return rc;
2155}
2156
2157/**
2158 * Destroys a mixer stream, internal version.
2159 *
2160 * @param pMixStream Mixer stream to destroy.
2161 * @param pDevIns The device instance the statistics are registered with.
2162 */
2163static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream, PPDMDEVINS pDevIns)
2164{
2165 AssertPtrReturnVoid(pMixStream);
2166
2167 LogFunc(("%s\n", pMixStream->pszName));
2168
2169 if (pMixStream->pConn) /* Stream has a connector interface present? */
2170 {
2171 if (pMixStream->pStream)
2172 {
2173 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
2174 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
2175
2176 pMixStream->pStream = NULL;
2177 }
2178
2179 pMixStream->pConn = NULL;
2180 }
2181
2182 if (pMixStream->pszStatPrefix)
2183 {
2184 PDMDevHlpSTAMDeregisterByPrefix(pDevIns, pMixStream->pszStatPrefix);
2185 RTStrFree(pMixStream->pszStatPrefix);
2186 pMixStream->pszStatPrefix = NULL;
2187 }
2188
2189 RTStrFree(pMixStream->pszName);
2190 pMixStream->pszName = NULL;
2191
2192 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
2193 AssertRC(rc2);
2194
2195 RTMemFree(pMixStream);
2196 pMixStream = NULL;
2197}
2198
2199/**
2200 * Destroys a mixer stream.
2201 *
2202 * @param pMixStream Mixer stream to destroy.
2203 * @param pDevIns The device instance statistics are registered with.
2204 */
2205void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream, PPDMDEVINS pDevIns)
2206{
2207 if (!pMixStream)
2208 return;
2209
2210 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2211 AssertRC(rc2);
2212
2213 LogFunc(("%s\n", pMixStream->pszName));
2214
2215 if (pMixStream->pSink) /* Is the stream part of a sink? */
2216 {
2217 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
2218 * pointer will be gone from the stream. */
2219 PAUDMIXSINK pSink = pMixStream->pSink;
2220
2221 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
2222 if (RT_SUCCESS(rc2))
2223 {
2224 Assert(pSink->cStreams);
2225 pSink->cStreams--;
2226 }
2227 }
2228 else
2229 rc2 = VINF_SUCCESS;
2230
2231 int rc3 = RTCritSectLeave(&pMixStream->CritSect);
2232 AssertRC(rc3);
2233
2234 if (RT_SUCCESS(rc2))
2235 {
2236 audioMixerStreamDestroyInternal(pMixStream, pDevIns);
2237 pMixStream = NULL;
2238 }
2239
2240 LogFlowFunc(("Returning %Rrc\n", rc2));
2241}
2242
2243/**
2244 * Returns whether a mixer stream currently is active (playing/recording) or not.
2245 *
2246 * @returns @c true if playing/recording, @c false if not.
2247 * @param pMixStream Mixer stream to return status for.
2248 */
2249bool AudioMixerStreamIsActive(PAUDMIXSTREAM pMixStream)
2250{
2251 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2252 if (RT_FAILURE(rc2))
2253 return false;
2254
2255 AssertPtr(pMixStream->pConn);
2256 AssertPtr(pMixStream->pStream);
2257
2258 bool fIsActive;
2259
2260 if ( pMixStream->pConn
2261 && pMixStream->pStream
2262 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAGS_ENABLED))
2263 {
2264 fIsActive = true;
2265 }
2266 else
2267 fIsActive = false;
2268
2269 rc2 = RTCritSectLeave(&pMixStream->CritSect);
2270 AssertRC(rc2);
2271
2272 return fIsActive;
2273}
2274
2275/**
2276 * Returns whether a mixer stream is valid (e.g. initialized and in a working state) or not.
2277 *
2278 * @returns @c true if valid, @c false if not.
2279 * @param pMixStream Mixer stream to return status for.
2280 */
2281bool AudioMixerStreamIsValid(PAUDMIXSTREAM pMixStream)
2282{
2283 if (!pMixStream)
2284 return false;
2285
2286 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2287 if (RT_FAILURE(rc2))
2288 return false;
2289
2290 bool fIsValid;
2291
2292 if ( pMixStream->pConn
2293 && pMixStream->pStream
2294 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED))
2295 {
2296 fIsValid = true;
2297 }
2298 else
2299 fIsValid = false;
2300
2301 rc2 = RTCritSectLeave(&pMixStream->CritSect);
2302 AssertRC(rc2);
2303
2304 return fIsValid;
2305}
2306
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