VirtualBox

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

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

Audio: Merged pfnStreamWrite and pfnStreamPlay in PDMIAUDIOCONNECTOR. Moved pfnStreamRead down to pfnStreamCapture. 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 88720 2021-04-26 22:46:21Z 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 default: break;
711 }
712
713 AssertMsgFailed(("Unsupported sink command %d\n", enmCmd));
714 return PDMAUDIOSTREAMCMD_INVALID;
715}
716
717/**
718 * Controls a mixer sink.
719 *
720 * @returns VBox status code.
721 * @param pSink Mixer sink to control.
722 * @param enmSinkCmd Sink command to set.
723 */
724int AudioMixerSinkCtl(PAUDMIXSINK pSink, AUDMIXSINKCMD enmSinkCmd)
725{
726 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
727
728 PDMAUDIOSTREAMCMD enmCmdStream = audioMixerSinkToStreamCmd(enmSinkCmd);
729 if (enmCmdStream == PDMAUDIOSTREAMCMD_INVALID)
730 return VERR_NOT_SUPPORTED;
731
732 int rc = RTCritSectEnter(&pSink->CritSect);
733 if (RT_FAILURE(rc))
734 return rc;
735
736 /* Input sink and no recording source set? Bail out early. */
737 if ( pSink->enmDir == AUDMIXSINKDIR_INPUT
738 && pSink->In.pStreamRecSource == NULL)
739 {
740 int rc2 = RTCritSectLeave(&pSink->CritSect);
741 AssertRC(rc2);
742
743 return rc;
744 }
745
746 PAUDMIXSTREAM pStream;
747 if ( pSink->enmDir == AUDMIXSINKDIR_INPUT
748 && pSink->In.pStreamRecSource) /* Any recording source set? */
749 {
750 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
751 {
752 if (pStream == pSink->In.pStreamRecSource)
753 {
754 int rc2 = audioMixerStreamCtlInternal(pStream, enmCmdStream, AUDMIXSTRMCTL_F_NONE);
755 if (rc2 == VERR_NOT_SUPPORTED)
756 rc2 = VINF_SUCCESS;
757
758 if (RT_SUCCESS(rc))
759 rc = rc2;
760 /* Keep going. Flag? */
761 }
762 }
763 }
764 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
765 {
766 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
767 {
768 int rc2 = audioMixerStreamCtlInternal(pStream, enmCmdStream, AUDMIXSTRMCTL_F_NONE);
769 if (rc2 == VERR_NOT_SUPPORTED)
770 rc2 = VINF_SUCCESS;
771
772 if (RT_SUCCESS(rc))
773 rc = rc2;
774 /* Keep going. Flag? */
775 }
776 }
777
778 switch (enmSinkCmd)
779 {
780 case AUDMIXSINKCMD_ENABLE:
781 {
782 /* Make sure to clear any other former flags again by assigning AUDMIXSINK_STS_RUNNING directly. */
783 pSink->fStatus = AUDMIXSINK_STS_RUNNING;
784 break;
785 }
786
787 case AUDMIXSINKCMD_DISABLE:
788 {
789 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
790 {
791 /* Set the sink in a pending disable state first.
792 * The final status (disabled) will be set in the sink's iteration. */
793 pSink->fStatus |= AUDMIXSINK_STS_PENDING_DISABLE;
794 }
795 break;
796 }
797
798 default:
799 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
800 break;
801 }
802
803#if defined(RTLOG_REL_ENABLED) || defined(LOG_ENABLED)
804 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
805#endif
806 LogRel2(("Audio Mixer: Set new status of sink '%s': %s (enmCmd=%RU32 rc=%Rrc)\n",
807 pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus), enmSinkCmd, rc));
808
809 int rc2 = RTCritSectLeave(&pSink->CritSect);
810 AssertRC(rc2);
811
812 return rc;
813}
814
815/**
816 * Initializes a sink.
817 *
818 * @returns VBox status code.
819 * @param pSink Sink to initialize.
820 * @param pMixer Mixer the sink is assigned to.
821 * @param pcszName Name of the sink.
822 * @param enmDir Direction of the sink.
823 */
824static int audioMixerSinkInit(PAUDMIXSINK pSink, PAUDIOMIXER pMixer, const char *pcszName, AUDMIXSINKDIR enmDir)
825{
826 pSink->pszName = RTStrDup(pcszName);
827 if (!pSink->pszName)
828 return VERR_NO_MEMORY;
829
830 int rc = RTCritSectInit(&pSink->CritSect);
831 if (RT_SUCCESS(rc))
832 {
833 pSink->pParent = pMixer;
834 pSink->enmDir = enmDir;
835
836 RTListInit(&pSink->lstStreams);
837
838 /* Set initial volume to max. */
839 pSink->Volume.fMuted = false;
840 pSink->Volume.uLeft = PDMAUDIO_VOLUME_MAX;
841 pSink->Volume.uRight = PDMAUDIO_VOLUME_MAX;
842
843 /* Ditto for the combined volume. */
844 pSink->VolumeCombined.fMuted = false;
845 pSink->VolumeCombined.uLeft = PDMAUDIO_VOLUME_MAX;
846 pSink->VolumeCombined.uRight = PDMAUDIO_VOLUME_MAX;
847
848 const size_t cbScratchBuf = _1K; /** @todo Make this configurable? */
849
850 pSink->pabScratchBuf = (uint8_t *)RTMemAlloc(cbScratchBuf);
851 AssertPtrReturn(pSink->pabScratchBuf, VERR_NO_MEMORY);
852 pSink->cbScratchBuf = cbScratchBuf;
853 }
854
855 LogFlowFuncLeaveRC(rc);
856 return rc;
857}
858
859/**
860 * Destroys a mixer sink and removes it from the attached mixer (if any).
861 *
862 * @param pSink Mixer sink to destroy.
863 * @param pDevIns The device instance that statistics are registered with.
864 */
865void AudioMixerSinkDestroy(PAUDMIXSINK pSink, PPDMDEVINS pDevIns)
866{
867 if (!pSink)
868 return;
869
870 int rc2 = RTCritSectEnter(&pSink->CritSect);
871 AssertRC(rc2);
872
873 if (pSink->pParent)
874 {
875 /* Save mixer pointer, as after audioMixerRemoveSinkInternal() the
876 * pointer will be gone from the stream. */
877 PAUDIOMIXER pMixer = pSink->pParent;
878 AssertPtr(pMixer);
879
880 audioMixerRemoveSinkInternal(pMixer, pSink);
881 }
882
883 rc2 = RTCritSectLeave(&pSink->CritSect);
884 AssertRC(rc2);
885
886 audioMixerSinkDestroyInternal(pSink, pDevIns);
887
888 RTMemFree(pSink);
889 pSink = NULL;
890}
891
892/**
893 * Destroys a mixer sink.
894 *
895 * @param pSink Mixer sink to destroy.
896 * @param pDevIns The device instance statistics are registered with.
897 */
898static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink, PPDMDEVINS pDevIns)
899{
900 AssertPtrReturnVoid(pSink);
901
902 LogFunc(("%s\n", pSink->pszName));
903
904 PAUDMIXSTREAM pStream, pStreamNext;
905 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
906 {
907 audioMixerSinkRemoveStreamInternal(pSink, pStream);
908 audioMixerStreamDestroyInternal(pStream, pDevIns);
909 }
910
911 if ( pSink->pParent
912 && pSink->pParent->fFlags & AUDMIXER_FLAGS_DEBUG)
913 {
914 AudioHlpFileDestroy(pSink->Dbg.pFile);
915 pSink->Dbg.pFile = NULL;
916 }
917
918 char szPrefix[128];
919 RTStrPrintf(szPrefix, sizeof(szPrefix), "MixerSink-%s/", pSink->pszName);
920 PDMDevHlpSTAMDeregisterByPrefix(pDevIns, szPrefix);
921
922 RTStrFree(pSink->pszName);
923 pSink->pszName = NULL;
924
925 RTMemFree(pSink->pabScratchBuf);
926 pSink->pabScratchBuf = NULL;
927 pSink->cbScratchBuf = 0;
928
929 AudioMixBufDestroy(&pSink->MixBuf);
930 RTCritSectDelete(&pSink->CritSect);
931}
932
933/**
934 * Returns the amount of bytes ready to be read from a sink since the last call
935 * to AudioMixerSinkUpdate().
936 *
937 * @returns Amount of bytes ready to be read from the sink.
938 * @param pSink Sink to return number of available bytes for.
939 */
940uint32_t AudioMixerSinkGetReadable(PAUDMIXSINK pSink)
941{
942 AssertPtrReturn(pSink, 0);
943
944 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT, ("%s: Can't read from a non-input sink\n", pSink->pszName));
945
946 int rc = RTCritSectEnter(&pSink->CritSect);
947 if (RT_FAILURE(rc))
948 return 0;
949
950 uint32_t cbReadable = 0;
951
952 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
953 {
954#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF_IN
955# error "Implement me!"
956#else
957 PAUDMIXSTREAM pStreamRecSource = pSink->In.pStreamRecSource;
958 if (!pStreamRecSource)
959 {
960 Log3Func(("[%s] No recording source specified, skipping ...\n", pSink->pszName));
961 }
962 else
963 {
964 AssertPtr(pStreamRecSource->pConn);
965 cbReadable = pStreamRecSource->pConn->pfnStreamGetReadable(pStreamRecSource->pConn, pStreamRecSource->pStream);
966 }
967#endif
968 }
969
970 Log3Func(("[%s] cbReadable=%RU32\n", pSink->pszName, cbReadable));
971
972 int rc2 = RTCritSectLeave(&pSink->CritSect);
973 AssertRC(rc2);
974
975 return cbReadable;
976}
977
978/**
979 * Returns the sink's current recording source.
980 *
981 * @return Mixer stream which currently is set as current recording source, NULL if none is set.
982 * @param pSink Audio mixer sink to return current recording source for.
983 */
984PAUDMIXSTREAM AudioMixerSinkGetRecordingSource(PAUDMIXSINK pSink)
985{
986 int rc = RTCritSectEnter(&pSink->CritSect);
987 if (RT_FAILURE(rc))
988 return NULL;
989
990 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT, ("Specified sink is not an input sink\n"));
991
992 PAUDMIXSTREAM pStream = pSink->In.pStreamRecSource;
993
994 int rc2 = RTCritSectLeave(&pSink->CritSect);
995 AssertRC(rc2);
996
997 return pStream;
998}
999
1000/**
1001 * Returns the amount of bytes ready to be written to a sink since the last call
1002 * to AudioMixerSinkUpdate().
1003 *
1004 * @returns Amount of bytes ready to be written to the sink.
1005 * @param pSink Sink to return number of available bytes for.
1006 */
1007uint32_t AudioMixerSinkGetWritable(PAUDMIXSINK pSink)
1008{
1009 AssertPtrReturn(pSink, 0);
1010
1011 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT, ("%s: Can't write to a non-output sink\n", pSink->pszName));
1012
1013 int rc = RTCritSectEnter(&pSink->CritSect);
1014 if (RT_FAILURE(rc))
1015 return 0;
1016
1017 uint32_t cbWritable = 0;
1018
1019 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
1020 && !(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
1021 {
1022 cbWritable = AudioMixBufFreeBytes(&pSink->MixBuf);
1023 }
1024
1025 Log3Func(("[%s] cbWritable=%RU32 (%RU64ms)\n",
1026 pSink->pszName, cbWritable, PDMAudioPropsBytesToMilli(&pSink->PCMProps, cbWritable)));
1027
1028 int rc2 = RTCritSectLeave(&pSink->CritSect);
1029 AssertRC(rc2);
1030
1031 return cbWritable;
1032}
1033
1034/**
1035 * Returns the sink's mixing direction.
1036 *
1037 * @returns Mixing direction.
1038 * @param pSink Sink to return direction for.
1039 */
1040AUDMIXSINKDIR AudioMixerSinkGetDir(PAUDMIXSINK pSink)
1041{
1042 AssertPtrReturn(pSink, AUDMIXSINKDIR_UNKNOWN);
1043
1044 int rc = RTCritSectEnter(&pSink->CritSect);
1045 if (RT_FAILURE(rc))
1046 return AUDMIXSINKDIR_UNKNOWN;
1047
1048 const AUDMIXSINKDIR enmDir = pSink->enmDir;
1049
1050 int rc2 = RTCritSectLeave(&pSink->CritSect);
1051 AssertRC(rc2);
1052
1053 return enmDir;
1054}
1055
1056/**
1057 * Returns the sink's (friendly) name.
1058 *
1059 * @returns The sink's (friendly) name.
1060 */
1061const char *AudioMixerSinkGetName(const PAUDMIXSINK pSink)
1062{
1063 AssertPtrReturn(pSink, "<Unknown>");
1064
1065 return pSink->pszName;
1066}
1067
1068/**
1069 * Returns a specific mixer stream from a sink, based on its index.
1070 *
1071 * @returns Mixer stream if found, or NULL if not found.
1072 * @param pSink Sink to retrieve mixer stream from.
1073 * @param uIndex Index of the mixer stream to return.
1074 */
1075PAUDMIXSTREAM AudioMixerSinkGetStream(PAUDMIXSINK pSink, uint8_t uIndex)
1076{
1077 AssertPtrReturn(pSink, NULL);
1078
1079 int rc = RTCritSectEnter(&pSink->CritSect);
1080 if (RT_FAILURE(rc))
1081 return NULL;
1082
1083 AssertMsgReturn(uIndex < pSink->cStreams,
1084 ("Index %RU8 exceeds stream count (%RU8)", uIndex, pSink->cStreams), NULL);
1085
1086 /* Slow lookup, d'oh. */
1087 PAUDMIXSTREAM pStream = RTListGetFirst(&pSink->lstStreams, AUDMIXSTREAM, Node);
1088 while (uIndex)
1089 {
1090 pStream = RTListGetNext(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node);
1091 uIndex--;
1092 }
1093
1094 /** @todo Do we need to raise the stream's reference count here? */
1095
1096 int rc2 = RTCritSectLeave(&pSink->CritSect);
1097 AssertRC(rc2);
1098
1099 AssertPtr(pStream);
1100 return pStream;
1101}
1102
1103/**
1104 * Returns the current status of a mixer sink.
1105 *
1106 * @returns The sink's current status.
1107 * @param pSink Mixer sink to return status for.
1108 */
1109AUDMIXSINKSTS AudioMixerSinkGetStatus(PAUDMIXSINK pSink)
1110{
1111 if (!pSink)
1112 return AUDMIXSINK_STS_NONE;
1113
1114 int rc2 = RTCritSectEnter(&pSink->CritSect);
1115 if (RT_FAILURE(rc2))
1116 return AUDMIXSINK_STS_NONE;
1117
1118 /* If the dirty flag is set, there is unprocessed data in the sink. */
1119 AUDMIXSINKSTS stsSink = pSink->fStatus;
1120
1121 rc2 = RTCritSectLeave(&pSink->CritSect);
1122 AssertRC(rc2);
1123
1124 return stsSink;
1125}
1126
1127/**
1128 * Returns the number of attached mixer streams to a mixer sink.
1129 *
1130 * @returns The number of attached mixer streams.
1131 * @param pSink Mixer sink to return number for.
1132 */
1133uint8_t AudioMixerSinkGetStreamCount(PAUDMIXSINK pSink)
1134{
1135 if (!pSink)
1136 return 0;
1137
1138 int rc2 = RTCritSectEnter(&pSink->CritSect);
1139 if (RT_FAILURE(rc2))
1140 return 0;
1141
1142 const uint8_t cStreams = pSink->cStreams;
1143
1144 rc2 = RTCritSectLeave(&pSink->CritSect);
1145 AssertRC(rc2);
1146
1147 return cStreams;
1148}
1149
1150/**
1151 * Returns whether the sink is in an active state or not.
1152 * Note: The pending disable state also counts as active.
1153 *
1154 * @returns True if active, false if not.
1155 * @param pSink Sink to return active state for.
1156 */
1157bool AudioMixerSinkIsActive(PAUDMIXSINK pSink)
1158{
1159 if (!pSink)
1160 return false;
1161
1162 int rc2 = RTCritSectEnter(&pSink->CritSect);
1163 if (RT_FAILURE(rc2))
1164 return false;
1165
1166 const bool fIsActive = pSink->fStatus & AUDMIXSINK_STS_RUNNING;
1167 /* Note: AUDMIXSINK_STS_PENDING_DISABLE implies AUDMIXSINK_STS_RUNNING. */
1168
1169 Log3Func(("[%s] fActive=%RTbool\n", pSink->pszName, fIsActive));
1170
1171 rc2 = RTCritSectLeave(&pSink->CritSect);
1172 AssertRC(rc2);
1173
1174 return fIsActive;
1175}
1176
1177/**
1178 * Reads audio data from a mixer sink.
1179 *
1180 * @returns VBox status code.
1181 * @param pSink Mixer sink to read data from.
1182 * @param enmOp Mixer operation to use for reading the data.
1183 * @param pvBuf Buffer where to store the read data.
1184 * @param cbBuf Buffer size (in bytes) where to store the data.
1185 * @param pcbRead Number of bytes read. Optional.
1186 */
1187int AudioMixerSinkRead(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1188{
1189 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1190 RT_NOREF(enmOp);
1191 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1192 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1193 /* pcbRead is optional. */
1194
1195 /** @todo Handle mixing operation enmOp! */
1196
1197 int rc = RTCritSectEnter(&pSink->CritSect);
1198 if (RT_FAILURE(rc))
1199 return rc;
1200
1201 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT,
1202 ("Can't read from a sink which is not an input sink\n"));
1203
1204 uint32_t cbRead = 0;
1205
1206 /* Flag indicating whether this sink is in a 'clean' state,
1207 * e.g. there is no more data to read from. */
1208 bool fClean = true;
1209
1210 PAUDMIXSTREAM pStreamRecSource = pSink->In.pStreamRecSource;
1211 if (!pStreamRecSource)
1212 {
1213 Log3Func(("[%s] No recording source specified, skipping ...\n", pSink->pszName));
1214 }
1215 else if (!(pStreamRecSource->fStatus & AUDMIXSTREAM_STATUS_ENABLED))
1216 {
1217 Log3Func(("[%s] Stream '%s' disabled, skipping ...\n", pSink->pszName, pStreamRecSource->pszName));
1218 }
1219 else
1220 {
1221 uint32_t cbToRead = cbBuf;
1222 while (cbToRead)
1223 {
1224 uint32_t cbReadStrm;
1225 AssertPtr(pStreamRecSource->pConn);
1226#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF_IN
1227# error "Implement me!"
1228#else
1229 rc = pStreamRecSource->pConn->pfnStreamRead(pStreamRecSource->pConn, pStreamRecSource->pStream,
1230 (uint8_t *)pvBuf + cbRead, cbToRead, &cbReadStrm);
1231#endif
1232 if (RT_FAILURE(rc))
1233 LogFunc(("[%s] Failed reading from stream '%s': %Rrc\n", pSink->pszName, pStreamRecSource->pszName, rc));
1234
1235 Log3Func(("[%s] Stream '%s': Read %RU32 bytes\n", pSink->pszName, pStreamRecSource->pszName, cbReadStrm));
1236
1237 if ( RT_FAILURE(rc)
1238 || !cbReadStrm)
1239 break;
1240
1241 AssertBreakStmt(cbReadStrm <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
1242 cbToRead -= cbReadStrm;
1243 cbRead += cbReadStrm;
1244 Assert(cbRead <= cbBuf);
1245 }
1246
1247 uint32_t cbReadable = pStreamRecSource->pConn->pfnStreamGetReadable(pStreamRecSource->pConn, pStreamRecSource->pStream);
1248
1249 /* Still some data available? Then sink is not clean (yet). */
1250 if (cbReadable)
1251 fClean = false;
1252
1253 if (RT_SUCCESS(rc))
1254 {
1255 if (fClean)
1256 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1257
1258 /* Update our last read time stamp. */
1259 pSink->tsLastReadWrittenNs = RTTimeNanoTS();
1260
1261 if (pSink->pParent->fFlags & AUDMIXER_FLAGS_DEBUG)
1262 {
1263 int rc2 = AudioHlpFileWrite(pSink->Dbg.pFile, pvBuf, cbRead, 0 /* fFlags */);
1264 AssertRC(rc2);
1265 }
1266 }
1267 }
1268
1269#ifdef LOG_ENABLED
1270 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
1271#endif
1272 Log2Func(("[%s] cbRead=%RU32, fClean=%RTbool, fStatus=%s, rc=%Rrc\n",
1273 pSink->pszName, cbRead, fClean, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus), rc));
1274
1275 if (pcbRead)
1276 *pcbRead = cbRead;
1277
1278 int rc2 = RTCritSectLeave(&pSink->CritSect);
1279 AssertRC(rc2);
1280
1281 return rc;
1282}
1283
1284/**
1285 * Removes a mixer stream from a mixer sink, internal version.
1286 *
1287 * @returns VBox status code.
1288 * @param pSink Sink to remove mixer stream from.
1289 * @param pStream Stream to remove.
1290 */
1291static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1292{
1293 AssertPtrReturn(pSink, VERR_INVALID_PARAMETER);
1294 if ( !pStream
1295 || !pStream->pSink) /* Not part of a sink anymore? */
1296 {
1297 return VERR_NOT_FOUND;
1298 }
1299
1300 AssertMsgReturn(pStream->pSink == pSink, ("Stream '%s' is not part of sink '%s'\n",
1301 pStream->pszName, pSink->pszName), VERR_NOT_FOUND);
1302
1303 LogFlowFunc(("[%s] (Stream = %s), cStreams=%RU8\n",
1304 pSink->pszName, pStream->pStream->szName, pSink->cStreams));
1305
1306 /* Remove stream from sink. */
1307 RTListNodeRemove(&pStream->Node);
1308
1309 int rc = VINF_SUCCESS;
1310
1311 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1312 {
1313 /* Make sure to also un-set the recording source if this stream was set
1314 * as the recording source before. */
1315 if (pStream == pSink->In.pStreamRecSource)
1316 rc = audioMixerSinkSetRecSourceInternal(pSink, NULL);
1317 }
1318
1319 /* Set sink to NULL so that we know we're not part of any sink anymore. */
1320 pStream->pSink = NULL;
1321
1322 return rc;
1323}
1324
1325/**
1326 * Removes a mixer stream from a mixer sink.
1327 *
1328 * @param pSink Sink to remove mixer stream from.
1329 * @param pStream Stream to remove.
1330 */
1331void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1332{
1333 int rc2 = RTCritSectEnter(&pSink->CritSect);
1334 AssertRC(rc2);
1335
1336 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pStream);
1337 if (RT_SUCCESS(rc2))
1338 {
1339 Assert(pSink->cStreams);
1340 pSink->cStreams--;
1341 }
1342
1343 rc2 = RTCritSectLeave(&pSink->CritSect);
1344 AssertRC(rc2);
1345}
1346
1347/**
1348 * Removes all attached streams from a given sink.
1349 *
1350 * @param pSink Sink to remove attached streams from.
1351 */
1352static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink)
1353{
1354 if (!pSink)
1355 return;
1356
1357 LogFunc(("%s\n", pSink->pszName));
1358
1359 PAUDMIXSTREAM pStream, pStreamNext;
1360 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
1361 audioMixerSinkRemoveStreamInternal(pSink, pStream);
1362}
1363
1364/**
1365 * Resets the sink's state.
1366 *
1367 * @param pSink Sink to reset.
1368 */
1369static void audioMixerSinkReset(PAUDMIXSINK pSink)
1370{
1371 if (!pSink)
1372 return;
1373
1374 LogFunc(("[%s]\n", pSink->pszName));
1375
1376 AudioMixBufReset(&pSink->MixBuf);
1377
1378 /* Update last updated timestamp. */
1379 pSink->tsLastUpdatedMs = 0;
1380
1381 /* Reset status. */
1382 pSink->fStatus = AUDMIXSINK_STS_NONE;
1383}
1384
1385/**
1386 * Removes all attached streams from a given sink.
1387 *
1388 * @param pSink Sink to remove attached streams from.
1389 */
1390void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
1391{
1392 if (!pSink)
1393 return;
1394
1395 int rc2 = RTCritSectEnter(&pSink->CritSect);
1396 AssertRC(rc2);
1397
1398 audioMixerSinkRemoveAllStreamsInternal(pSink);
1399
1400 pSink->cStreams = 0;
1401
1402 rc2 = RTCritSectLeave(&pSink->CritSect);
1403 AssertRC(rc2);
1404}
1405
1406/**
1407 * Resets a sink. This will immediately stop all processing.
1408 *
1409 * @param pSink Sink to reset.
1410 */
1411void AudioMixerSinkReset(PAUDMIXSINK pSink)
1412{
1413 if (!pSink)
1414 return;
1415
1416 int rc2 = RTCritSectEnter(&pSink->CritSect);
1417 AssertRC(rc2);
1418
1419 LogFlowFunc(("[%s]\n", pSink->pszName));
1420
1421 audioMixerSinkReset(pSink);
1422
1423 rc2 = RTCritSectLeave(&pSink->CritSect);
1424 AssertRC(rc2);
1425}
1426
1427/**
1428 * Returns the audio format of a mixer sink.
1429 *
1430 * @param pSink Sink to retrieve audio format for.
1431 * @param pPCMProps Where to the returned audio format.
1432 */
1433void AudioMixerSinkGetFormat(PAUDMIXSINK pSink, PPDMAUDIOPCMPROPS pPCMProps)
1434{
1435 AssertPtrReturnVoid(pSink);
1436 AssertPtrReturnVoid(pPCMProps);
1437
1438 int rc2 = RTCritSectEnter(&pSink->CritSect);
1439 if (RT_FAILURE(rc2))
1440 return;
1441
1442 memcpy(pPCMProps, &pSink->PCMProps, sizeof(PDMAUDIOPCMPROPS));
1443
1444 rc2 = RTCritSectLeave(&pSink->CritSect);
1445 AssertRC(rc2);
1446}
1447
1448/**
1449 * Sets the audio format of a mixer sink.
1450 *
1451 * @returns VBox status code.
1452 * @param pSink The sink to set audio format for.
1453 * @param pPCMProps Audio format (PCM properties) to set.
1454 */
1455int AudioMixerSinkSetFormat(PAUDMIXSINK pSink, PCPDMAUDIOPCMPROPS pPCMProps)
1456{
1457 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1458 AssertPtrReturn(pPCMProps, VERR_INVALID_POINTER);
1459 AssertReturn(AudioHlpPcmPropsAreValid(pPCMProps), VERR_INVALID_PARAMETER);
1460
1461 int rc = RTCritSectEnter(&pSink->CritSect);
1462 if (RT_FAILURE(rc))
1463 return rc;
1464
1465 if (PDMAudioPropsAreEqual(&pSink->PCMProps, pPCMProps)) /* Bail out early if PCM properties are equal. */
1466 {
1467 rc = RTCritSectLeave(&pSink->CritSect);
1468 AssertRC(rc);
1469
1470 return rc;
1471 }
1472
1473 if (pSink->PCMProps.uHz)
1474 LogFlowFunc(("[%s] Old format: %u bit, %RU8 channels, %RU32Hz\n", pSink->pszName,
1475 PDMAudioPropsSampleBits(&pSink->PCMProps), PDMAudioPropsChannels(&pSink->PCMProps), pSink->PCMProps.uHz));
1476
1477 memcpy(&pSink->PCMProps, pPCMProps, sizeof(PDMAUDIOPCMPROPS));
1478
1479 LogFlowFunc(("[%s] New format %u bit, %RU8 channels, %RU32Hz\n", pSink->pszName, PDMAudioPropsSampleBits(&pSink->PCMProps),
1480 PDMAudioPropsChannels(&pSink->PCMProps), pSink->PCMProps.uHz));
1481
1482 /* Also update the sink's mixing buffer format. */
1483 AudioMixBufDestroy(&pSink->MixBuf);
1484 rc = AudioMixBufInit(&pSink->MixBuf, pSink->pszName, &pSink->PCMProps,
1485 PDMAudioPropsMilliToFrames(&pSink->PCMProps, 100 /*ms*/)); /** @todo Make this configurable? */
1486 if (RT_SUCCESS(rc))
1487 {
1488 PAUDMIXSTREAM pStream;
1489 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
1490 {
1491 /** @todo Invalidate mix buffers! */
1492 }
1493 }
1494
1495 if ( RT_SUCCESS(rc)
1496 && (pSink->pParent->fFlags & AUDMIXER_FLAGS_DEBUG))
1497 {
1498 AudioHlpFileClose(pSink->Dbg.pFile);
1499
1500 char szName[64];
1501 RTStrPrintf(szName, sizeof(szName), "MixerSink-%s", pSink->pszName);
1502
1503 char szFile[RTPATH_MAX];
1504 int rc2 = AudioHlpFileNameGet(szFile, RT_ELEMENTS(szFile), NULL /* Use temporary directory */, szName,
1505 0 /* Instance */, AUDIOHLPFILETYPE_WAV, AUDIOHLPFILENAME_FLAGS_NONE);
1506 if (RT_SUCCESS(rc2))
1507 {
1508 rc2 = AudioHlpFileCreate(AUDIOHLPFILETYPE_WAV, szFile, AUDIOHLPFILE_FLAGS_NONE, &pSink->Dbg.pFile);
1509 if (RT_SUCCESS(rc2))
1510 rc2 = AudioHlpFileOpen(pSink->Dbg.pFile, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS, &pSink->PCMProps);
1511 }
1512 }
1513
1514 int rc2 = RTCritSectLeave(&pSink->CritSect);
1515 AssertRC(rc2);
1516
1517 LogFlowFuncLeaveRC(rc);
1518 return rc;
1519}
1520
1521/**
1522 * Set the current recording source of an input mixer sink, internal version.
1523 *
1524 * @returns VBox status code.
1525 * @param pSink Input mixer sink to set recording source for.
1526 * @param pStream Mixer stream to set as current recording source. Must be an input stream.
1527 * Specify NULL to un-set the current recording source.
1528 */
1529static int audioMixerSinkSetRecSourceInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1530{
1531 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT, ("Specified sink is not an input sink\n"));
1532
1533 int rc;
1534
1535 /*
1536 * Warning: Do *not* use pfnConn->pfnEnable() for enabling/disabling streams here, as this will unconditionally (re-)enable
1537 * streams, which would violate / run against the (global) VM settings. See @bugref{9882}.
1538 */
1539
1540 /* Get pointers of current recording source to make code easier to read below. */
1541 PAUDMIXSTREAM pCurRecSrc = pSink->In.pStreamRecSource; /* Can be NULL. */
1542 PPDMIAUDIOCONNECTOR pCurRecSrcConn = NULL;
1543 PPDMAUDIOSTREAM pCurRecSrcStream = NULL;
1544
1545 if (pCurRecSrc) /* First, disable old recording source, if any is set. */
1546 {
1547 pCurRecSrcConn = pSink->In.pStreamRecSource->pConn;
1548 AssertPtrReturn(pCurRecSrcConn, VERR_INVALID_POINTER);
1549 pCurRecSrcStream = pCurRecSrc->pStream;
1550 AssertPtrReturn(pCurRecSrcStream, VERR_INVALID_POINTER);
1551
1552 rc = pCurRecSrcConn->pfnStreamControl(pCurRecSrcConn, pCurRecSrcStream, PDMAUDIOSTREAMCMD_DISABLE);
1553 }
1554 else
1555 rc = VINF_SUCCESS;
1556
1557 if (RT_SUCCESS(rc))
1558 {
1559 if (pStream)
1560 {
1561 AssertPtr(pStream->pStream);
1562 AssertMsg(pStream->pStream->enmDir == PDMAUDIODIR_IN, ("Specified stream is not an input stream\n"));
1563 AssertPtr(pStream->pConn);
1564 rc = pStream->pConn->pfnStreamControl(pStream->pConn, pStream->pStream, PDMAUDIOSTREAMCMD_ENABLE);
1565 if (RT_SUCCESS(rc))
1566 {
1567 pCurRecSrc = pStream;
1568 }
1569 else if (pCurRecSrc) /* Stay with the current recording source (if any) and re-enable it. */
1570 {
1571 rc = pCurRecSrcConn->pfnStreamControl(pCurRecSrcConn, pCurRecSrcStream, PDMAUDIOSTREAMCMD_ENABLE);
1572 }
1573 }
1574 else
1575 pCurRecSrc = NULL; /* Unsetting, see audioMixerSinkRemoveStreamInternal. */
1576 }
1577
1578 /* Invalidate pointers. */
1579 pSink->In.pStreamRecSource = pCurRecSrc;
1580
1581 LogFunc(("[%s] Recording source is now '%s', rc=%Rrc\n",
1582 pSink->pszName, pSink->In.pStreamRecSource ? pSink->In.pStreamRecSource->pszName : "<None>", rc));
1583
1584 if (RT_SUCCESS(rc))
1585 LogRel(("Audio Mixer: Setting recording source of sink '%s' to '%s'\n",
1586 pSink->pszName, pSink->In.pStreamRecSource ? pSink->In.pStreamRecSource->pszName : "<None>"));
1587 else if (rc != VERR_AUDIO_STREAM_NOT_READY)
1588 LogRel(("Audio Mixer: Setting recording source of sink '%s' to '%s' failed with %Rrc\n",
1589 pSink->pszName, pSink->In.pStreamRecSource ? pSink->In.pStreamRecSource->pszName : "<None>", rc));
1590
1591 return rc;
1592}
1593
1594/**
1595 * Set the current recording source of an input mixer sink.
1596 *
1597 * @returns VBox status code.
1598 * @param pSink Input mixer sink to set recording source for.
1599 * @param pStream Mixer stream to set as current recording source. Must be an input stream.
1600 * Set to NULL to un-set the current recording source.
1601 */
1602int AudioMixerSinkSetRecordingSource(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1603{
1604 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1605
1606 int rc = RTCritSectEnter(&pSink->CritSect);
1607 if (RT_FAILURE(rc))
1608 return rc;
1609
1610 rc = audioMixerSinkSetRecSourceInternal(pSink, pStream);
1611
1612 int rc2 = RTCritSectLeave(&pSink->CritSect);
1613 AssertRC(rc2);
1614
1615 return rc;
1616}
1617
1618/**
1619 * Sets the volume of an individual sink.
1620 *
1621 * @returns VBox status code.
1622 * @param pSink Sink to set volume for.
1623 * @param pVol Volume to set.
1624 */
1625int AudioMixerSinkSetVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
1626{
1627 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1628 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
1629
1630 int rc = RTCritSectEnter(&pSink->CritSect);
1631 if (RT_FAILURE(rc))
1632 return rc;
1633
1634 memcpy(&pSink->Volume, pVol, sizeof(PDMAUDIOVOLUME));
1635
1636 LogRel2(("Audio Mixer: Setting volume of sink '%s' to %RU8/%RU8 (%s)\n",
1637 pSink->pszName, pVol->uLeft, pVol->uRight, pVol->fMuted ? "Muted" : "Unmuted"));
1638
1639 AssertPtr(pSink->pParent);
1640 rc = audioMixerSinkUpdateVolume(pSink, &pSink->pParent->VolMaster);
1641
1642 int rc2 = RTCritSectLeave(&pSink->CritSect);
1643 AssertRC(rc2);
1644
1645 return rc;
1646}
1647
1648/**
1649 * Updates an input mixer sink.
1650 *
1651 * @returns VBox status code.
1652 * @param pSink Mixer sink to update.
1653 */
1654static int audioMixerSinkUpdateInput(PAUDMIXSINK pSink)
1655{
1656 /*
1657 * Warning! We currently do _not_ use the mixing buffer for input streams!
1658 * Warning! We currently do _not_ use the mixing buffer for input streams!
1659 * Warning! We currently do _not_ use the mixing buffer for input streams!
1660 */
1661
1662 /*
1663 * Skip input sinks without a recoring source.
1664 */
1665 if (pSink->In.pStreamRecSource == NULL)
1666 return VINF_SUCCESS;
1667
1668 /*
1669 * Update each mixing sink stream's status.
1670 */
1671 PAUDMIXSTREAM pMixStream;
1672 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1673 {
1674 int rc2 = audioMixerStreamUpdateStatus(pMixStream);
1675 AssertRC(rc2);
1676 }
1677
1678 /*
1679 * Iterate and do capture on the recording source. We ignore all other streams.
1680 */
1681 int rc = VINF_SUCCESS; /* not sure if error propagation is worth it... */
1682#if 1
1683 pMixStream = pSink->In.pStreamRecSource;
1684#else
1685 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1686#endif
1687 {
1688 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1689 {
1690 uint32_t cFramesCaptured = 0;
1691 int rc2 = pMixStream->pConn->pfnStreamIterate(pMixStream->pConn, pMixStream->pStream);
1692 if (RT_SUCCESS(rc2))
1693 {
1694 rc2 = pMixStream->pConn->pfnStreamCapture(pMixStream->pConn, pMixStream->pStream, &cFramesCaptured);
1695 if (RT_SUCCESS(rc2))
1696 {
1697 if (cFramesCaptured)
1698 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1699 }
1700 else
1701 {
1702 LogFunc(("%s: Failed capturing stream '%s', rc=%Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc2));
1703 if (RT_SUCCESS(rc))
1704 rc = rc2;
1705 }
1706 }
1707 else if (RT_SUCCESS(rc))
1708 rc = rc2;
1709 Log3Func(("%s: cFramesCaptured=%RU32 (rc2=%Rrc)\n", pMixStream->pStream->szName, cFramesCaptured, rc2));
1710 }
1711 }
1712
1713 /* Update last updated timestamp. */
1714 pSink->tsLastUpdatedMs = RTTimeMilliTS();
1715
1716 /*
1717 * Deal with pending disable. The general case is that we reset
1718 * the sink when all streams have been disabled, however input is
1719 * currently a special case where we only care about the one
1720 * recording source...
1721 */
1722 if (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE)
1723 {
1724#if 1
1725 uint32_t const cStreams = 1;
1726 uint32_t cStreamsDisabled = 1;
1727 pMixStream = pSink->In.pStreamRecSource;
1728#else
1729 uint32_t const cStreams = pSink->cStreams;
1730 uint32_t cStreamsDisabled = pSink->cStreams;
1731 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1732#endif
1733 {
1734 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1735 {
1736 PDMAUDIOSTREAMSTS const fSts = pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream);
1737 if (fSts & (PDMAUDIOSTREAMSTS_FLAGS_ENABLED | PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE))
1738 cStreamsDisabled--;
1739 }
1740 }
1741 Log3Func(("[%s] pending disable: %u of %u disabled\n", pSink->pszName, cStreamsDisabled, cStreams));
1742 if (cStreamsDisabled == cStreams)
1743 audioMixerSinkReset(pSink);
1744 }
1745
1746 return rc;
1747}
1748
1749/**
1750 * Updates an output mixer sink.
1751 *
1752 * @returns VBox status code.
1753 * @param pSink Mixer sink to update.
1754 */
1755static int audioMixerSinkUpdateOutput(PAUDMIXSINK pSink)
1756{
1757 /*
1758 * Update each mixing sink stream's status and check how much we can
1759 * write into them.
1760 *
1761 * We're currently using the minimum size of all streams, however this
1762 * isn't a smart approach as it means one disfunctional stream can block
1763 * working ones.
1764 */
1765 /** @todo rework this so a broken stream cannot hold up everyone. */
1766 uint32_t cFramesToRead = AudioMixBufLive(&pSink->MixBuf); /* (to read from the mixing buffer) */
1767 uint32_t cWritableStreams = 0;
1768 PAUDMIXSTREAM pMixStream;
1769 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1770 {
1771#if 0 /** @todo this conceptually makes sense, but may mess up the pending-disable logic ... */
1772 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1773 pConn->pfnStreamIterate(pConn, pStream);
1774#endif
1775
1776 int rc2 = audioMixerStreamUpdateStatus(pMixStream);
1777 AssertRC(rc2);
1778
1779 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1780 {
1781 uint32_t const cbWritable = pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream);
1782 uint32_t cFrames = PDMAudioPropsBytesToFrames(&pMixStream->pStream->Props, cbWritable);
1783 if (PDMAudioPropsHz(&pMixStream->pStream->Props) == PDMAudioPropsHz(&pSink->MixBuf.Props))
1784 { /* likely */ }
1785 else
1786 {
1787 cFrames = cFrames * PDMAudioPropsHz(&pSink->MixBuf.Props) / PDMAudioPropsHz(&pMixStream->pStream->Props);
1788 cFrames = cFrames > 2 ? cFrames - 2 : 0; /* rounding safety fudge */
1789 }
1790 if (cFramesToRead > cFrames)
1791 {
1792 Log4Func(("%s: cFramesToRead %u -> %u; %s (%u bytes writable)\n",
1793 pSink->pszName, cFramesToRead, cFrames, pMixStream->pszName, cbWritable));
1794 cFramesToRead = cFrames;
1795 }
1796 cWritableStreams++;
1797 }
1798 }
1799 Log3Func(("%s: cLiveFrames=%#x cFramesToRead=%#x cWritableStreams=%#x\n", pSink->pszName,
1800 AudioMixBufLive(&pSink->MixBuf), cFramesToRead, cWritableStreams));
1801
1802 if (cWritableStreams > 0)
1803 {
1804 if (cFramesToRead > 0)
1805 {
1806 /*
1807 * For each of the enabled streams, convert cFramesToRead frames from
1808 * the mixing buffer and write that to the downstream driver.
1809 */
1810 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1811 {
1812 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1813 {
1814 uint32_t offSrcFrame = 0;
1815 do
1816 {
1817 /* Convert a chunk from the mixer buffer. */
1818 union
1819 {
1820 uint8_t ab[8192];
1821 uint64_t au64[8192 / sizeof(uint64_t)]; /* Use uint64_t to ensure good alignment. */
1822 } Buf;
1823 uint32_t cbDstPeeked = sizeof(Buf);
1824 uint32_t cSrcFramesPeeked = cFramesToRead - offSrcFrame;
1825 AudioMixBufPeek(&pSink->MixBuf, offSrcFrame, cSrcFramesPeeked, &cSrcFramesPeeked,
1826 &pMixStream->PeekState, &Buf, sizeof(Buf), &cbDstPeeked);
1827 offSrcFrame += cSrcFramesPeeked;
1828
1829 /* Write it to the backend. Since've checked that there is buffer
1830 space available, this should always write the whole buffer. */
1831 uint32_t cbDstWritten = 0;
1832 int rc2 = pMixStream->pConn->pfnStreamPlay(pMixStream->pConn, pMixStream->pStream,
1833 &Buf, cbDstPeeked, &cbDstWritten);
1834 Log3Func(("%s: %#x L %#x => %#x bytes; wrote %#x rc2=%Rrc %s\n", pSink->pszName, offSrcFrame,
1835 cSrcFramesPeeked - cSrcFramesPeeked, cbDstPeeked, cbDstWritten, rc2, pMixStream->pszName));
1836 if (RT_SUCCESS(rc2))
1837 AssertLogRelMsg(cbDstWritten == cbDstPeeked,
1838 ("cbDstWritten=%#x cbDstPeeked=%#x - (sink '%s')\n",
1839 cbDstWritten, cbDstPeeked, pSink->pszName));
1840 else if (rc2 == VERR_AUDIO_STREAM_NOT_READY)
1841 {
1842 LogRel2(("Audio Mixer: '%s' (sink '%s'): Stream not ready - skipping.\n",
1843 pMixStream->pszName, pSink->pszName));
1844 break; /* must've changed status, stop processing */
1845 }
1846 else
1847 {
1848 Assert(rc2 != VERR_BUFFER_OVERFLOW);
1849 LogRel2(("Audio Mixer: Writing to mixer stream '%s' (sink '%s') failed, rc=%Rrc\n",
1850 pMixStream->pszName, pSink->pszName, rc2));
1851 break;
1852 }
1853 } while (offSrcFrame < cFramesToRead);
1854 }
1855 }
1856
1857 AudioMixBufAdvance(&pSink->MixBuf, cFramesToRead);
1858 }
1859
1860 /*
1861 * Update the dirty flag for what it's worth.
1862 */
1863 if (AudioMixBufUsed(&pSink->MixBuf))
1864 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1865 else
1866 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1867 }
1868 else
1869 {
1870 /*
1871 * If no writable streams, just drop the mixer buffer content.
1872 */
1873 AudioMixBufDrop(&pSink->MixBuf);
1874 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1875 }
1876
1877 /*
1878 * Iterate buffers.
1879 */
1880 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1881 {
1882 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1883 pMixStream->pConn->pfnStreamIterate(pMixStream->pConn, pMixStream->pStream);
1884 }
1885
1886 /* Update last updated timestamp. */
1887 pSink->tsLastUpdatedMs = RTTimeMilliTS();
1888
1889 /*
1890 * Deal with pending disable.
1891 * We reset the sink when all streams have been disabled.
1892 */
1893 if (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE)
1894 {
1895 uint32_t const cStreams = pSink->cStreams;
1896 uint32_t cStreamsDisabled = pSink->cStreams;
1897 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1898 {
1899 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1900 {
1901 PDMAUDIOSTREAMSTS const fSts = pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream);
1902 if (fSts & (PDMAUDIOSTREAMSTS_FLAGS_ENABLED | PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE))
1903 cStreamsDisabled--;
1904 }
1905 }
1906 Log3Func(("[%s] pending disable: %u of %u disabled\n", pSink->pszName, cStreamsDisabled, cStreams));
1907 if (cStreamsDisabled == cStreams)
1908 audioMixerSinkReset(pSink);
1909 }
1910
1911 return VINF_SUCCESS;
1912}
1913
1914/**
1915 * Updates (invalidates) a mixer sink.
1916 *
1917 * @returns VBox status code.
1918 * @param pSink Mixer sink to update.
1919 */
1920int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
1921{
1922 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1923 int rc = RTCritSectEnter(&pSink->CritSect);
1924 AssertRCReturn(rc, rc);
1925
1926#ifdef LOG_ENABLED
1927 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
1928#endif
1929 Log3Func(("[%s] fStatus=%s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
1930
1931 /* Only process running sinks. */
1932 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
1933 {
1934 /* Do separate processing for input and output sinks. */
1935 if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1936 rc = audioMixerSinkUpdateOutput(pSink);
1937 else if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1938 rc = audioMixerSinkUpdateInput(pSink);
1939 else
1940 AssertFailed();
1941 }
1942 else
1943 rc = VINF_SUCCESS; /* disabled */
1944
1945 RTCritSectLeave(&pSink->CritSect);
1946 return rc;
1947}
1948
1949/**
1950 * Updates the (master) volume of a mixer sink.
1951 *
1952 * @returns VBox status code.
1953 * @param pSink Mixer sink to update volume for.
1954 * @param pVolMaster Master volume to set.
1955 */
1956static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
1957{
1958 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1959 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
1960
1961 LogFlowFunc(("[%s] Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1962 pSink->pszName, pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
1963 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32 ",
1964 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1965
1966 /** @todo Very crude implementation for now -- needs more work! */
1967
1968 pSink->VolumeCombined.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
1969
1970 pSink->VolumeCombined.uLeft = ( (pSink->Volume.uLeft ? pSink->Volume.uLeft : 1)
1971 * (pVolMaster->uLeft ? pVolMaster->uLeft : 1)) / PDMAUDIO_VOLUME_MAX;
1972
1973 pSink->VolumeCombined.uRight = ( (pSink->Volume.uRight ? pSink->Volume.uRight : 1)
1974 * (pVolMaster->uRight ? pVolMaster->uRight : 1)) / PDMAUDIO_VOLUME_MAX;
1975
1976 LogFlow(("-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1977 pSink->VolumeCombined.fMuted, pSink->VolumeCombined.uLeft, pSink->VolumeCombined.uRight));
1978
1979 /*
1980 * Input sinks must currently propagate the new volume settings to
1981 * all the streams. (For output sinks we do the volume control here.)
1982 */
1983 if (pSink->enmDir != AUDMIXSINKDIR_OUTPUT)
1984 {
1985 PAUDMIXSTREAM pMixStream;
1986 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1987 {
1988 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &pSink->VolumeCombined);
1989 AssertRC(rc2);
1990 }
1991 }
1992
1993 return VINF_SUCCESS;
1994}
1995
1996/**
1997 * Writes data to a mixer output sink.
1998 *
1999 * @returns VBox status code.
2000 * @param pSink Sink to write data to.
2001 * @param enmOp Mixer operation to use when writing data to the sink.
2002 * @param pvBuf Buffer containing the audio data to write.
2003 * @param cbBuf Size (in bytes) of the buffer containing the audio data.
2004 * @param pcbWritten Number of bytes written. Optional.
2005 */
2006int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
2007{
2008 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2009 RT_NOREF(enmOp);
2010 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2011 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
2012 /* pcbWritten is optional. */
2013
2014 int rc = RTCritSectEnter(&pSink->CritSect);
2015 AssertRCReturn(rc, rc);
2016
2017 AssertMsg(pSink->fStatus & AUDMIXSINK_STS_RUNNING,
2018 ("%s: Can't write to a sink which is not running (anymore) (status 0x%x)\n", pSink->pszName, pSink->fStatus));
2019 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
2020 ("%s: Can't write to a sink which is not an output sink\n", pSink->pszName));
2021
2022 uint32_t cbWritten = 0;
2023 uint32_t cbToWrite = RT_MIN(AudioMixBufFreeBytes(&pSink->MixBuf), cbBuf);
2024 while (cbToWrite)
2025 {
2026 /* Write the data to the mixer sink's own mixing buffer.
2027 Here the audio data is transformed into the mixer sink's format. */
2028 uint32_t cFramesWritten = 0;
2029 rc = AudioMixBufWriteCirc(&pSink->MixBuf, (uint8_t const*)pvBuf + cbWritten, cbToWrite, &cFramesWritten);
2030 if (RT_SUCCESS(rc))
2031 {
2032 const uint32_t cbWrittenChunk = PDMAudioPropsFramesToBytes(&pSink->PCMProps, cFramesWritten);
2033 Assert(cbToWrite >= cbWrittenChunk);
2034 cbToWrite -= cbWrittenChunk;
2035 cbWritten += cbWrittenChunk;
2036 }
2037 else
2038 break;
2039 }
2040
2041 Log3Func(("[%s] cbBuf=%RU32 -> cbWritten=%RU32\n", pSink->pszName, cbBuf, cbWritten));
2042
2043 /* Update the sink's last written time stamp. */
2044 pSink->tsLastReadWrittenNs = RTTimeNanoTS();
2045
2046 if (pcbWritten)
2047 *pcbWritten = cbWritten;
2048
2049 RTCritSectLeave(&pSink->CritSect);
2050 return rc;
2051}
2052
2053
2054/*********************************************************************************************************************************
2055 * Mixer Stream implementation.
2056 ********************************************************************************************************************************/
2057
2058/**
2059 * Controls a mixer stream, internal version.
2060 *
2061 * @returns VBox status code.
2062 * @param pMixStream Mixer stream to control.
2063 * @param enmCmd Mixer stream command to use.
2064 * @param fCtl Additional control flags. Pass 0.
2065 */
2066static int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
2067{
2068 AssertPtr(pMixStream->pConn);
2069 AssertPtr(pMixStream->pStream);
2070
2071 RT_NOREF(fCtl);
2072
2073 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
2074
2075 LogFlowFunc(("[%s] enmCmd=%ld, rc=%Rrc\n", pMixStream->pszName, enmCmd, rc));
2076
2077 return rc;
2078}
2079
2080/**
2081 * Updates a mixer stream's internal status.
2082 *
2083 * @returns VBox status code.
2084 * @param pMixStream Mixer stream to to update internal status for.
2085 */
2086static int audioMixerStreamUpdateStatus(PAUDMIXSTREAM pMixStream)
2087{
2088 pMixStream->fStatus = AUDMIXSTREAM_STATUS_NONE;
2089
2090 if (pMixStream->pConn) /* Audio connector available? */
2091 {
2092 const uint32_t fStreamStatus = pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream);
2093
2094 if (PDMAudioStrmStatusIsReady(fStreamStatus))
2095 pMixStream->fStatus |= AUDMIXSTREAM_STATUS_ENABLED;
2096
2097 AssertPtr(pMixStream->pSink);
2098 switch (pMixStream->pSink->enmDir)
2099 {
2100 case AUDMIXSINKDIR_INPUT:
2101 if (PDMAudioStrmStatusCanRead(fStreamStatus))
2102 pMixStream->fStatus |= AUDMIXSTREAM_STATUS_CAN_READ;
2103 break;
2104
2105 case AUDMIXSINKDIR_OUTPUT:
2106 if (PDMAudioStrmStatusCanWrite(fStreamStatus))
2107 pMixStream->fStatus |= AUDMIXSTREAM_STATUS_CAN_WRITE;
2108 break;
2109
2110 default:
2111 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
2112 break;
2113 }
2114 }
2115
2116 LogFlowFunc(("[%s] -> 0x%x\n", pMixStream->pszName, pMixStream->fStatus));
2117 return VINF_SUCCESS;
2118}
2119
2120/**
2121 * Controls a mixer stream.
2122 *
2123 * @returns VBox status code.
2124 * @param pMixStream Mixer stream to control.
2125 * @param enmCmd Mixer stream command to use.
2126 * @param fCtl Additional control flags. Pass 0.
2127 */
2128int AudioMixerStreamCtl(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
2129{
2130 RT_NOREF(fCtl);
2131 AssertPtrReturn(pMixStream, VERR_INVALID_POINTER);
2132 /** @todo Validate fCtl. */
2133
2134 int rc = RTCritSectEnter(&pMixStream->CritSect);
2135 if (RT_FAILURE(rc))
2136 return rc;
2137
2138 rc = audioMixerStreamCtlInternal(pMixStream, enmCmd, fCtl);
2139
2140 int rc2 = RTCritSectLeave(&pMixStream->CritSect);
2141 if (RT_SUCCESS(rc))
2142 rc = rc2;
2143
2144 return rc;
2145}
2146
2147/**
2148 * Destroys a mixer stream, internal version.
2149 *
2150 * @param pMixStream Mixer stream to destroy.
2151 * @param pDevIns The device instance the statistics are registered with.
2152 */
2153static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream, PPDMDEVINS pDevIns)
2154{
2155 AssertPtrReturnVoid(pMixStream);
2156
2157 LogFunc(("%s\n", pMixStream->pszName));
2158
2159 if (pMixStream->pConn) /* Stream has a connector interface present? */
2160 {
2161 if (pMixStream->pStream)
2162 {
2163 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
2164 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
2165
2166 pMixStream->pStream = NULL;
2167 }
2168
2169 pMixStream->pConn = NULL;
2170 }
2171
2172 if (pMixStream->pszStatPrefix)
2173 {
2174 PDMDevHlpSTAMDeregisterByPrefix(pDevIns, pMixStream->pszStatPrefix);
2175 RTStrFree(pMixStream->pszStatPrefix);
2176 pMixStream->pszStatPrefix = NULL;
2177 }
2178
2179 RTStrFree(pMixStream->pszName);
2180 pMixStream->pszName = NULL;
2181
2182 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
2183 AssertRC(rc2);
2184
2185 RTMemFree(pMixStream);
2186 pMixStream = NULL;
2187}
2188
2189/**
2190 * Destroys a mixer stream.
2191 *
2192 * @param pMixStream Mixer stream to destroy.
2193 * @param pDevIns The device instance statistics are registered with.
2194 */
2195void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream, PPDMDEVINS pDevIns)
2196{
2197 if (!pMixStream)
2198 return;
2199
2200 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2201 AssertRC(rc2);
2202
2203 LogFunc(("%s\n", pMixStream->pszName));
2204
2205 if (pMixStream->pSink) /* Is the stream part of a sink? */
2206 {
2207 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
2208 * pointer will be gone from the stream. */
2209 PAUDMIXSINK pSink = pMixStream->pSink;
2210
2211 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
2212 if (RT_SUCCESS(rc2))
2213 {
2214 Assert(pSink->cStreams);
2215 pSink->cStreams--;
2216 }
2217 }
2218 else
2219 rc2 = VINF_SUCCESS;
2220
2221 int rc3 = RTCritSectLeave(&pMixStream->CritSect);
2222 AssertRC(rc3);
2223
2224 if (RT_SUCCESS(rc2))
2225 {
2226 audioMixerStreamDestroyInternal(pMixStream, pDevIns);
2227 pMixStream = NULL;
2228 }
2229
2230 LogFlowFunc(("Returning %Rrc\n", rc2));
2231}
2232
2233/**
2234 * Returns whether a mixer stream currently is active (playing/recording) or not.
2235 *
2236 * @returns @c true if playing/recording, @c false if not.
2237 * @param pMixStream Mixer stream to return status for.
2238 */
2239bool AudioMixerStreamIsActive(PAUDMIXSTREAM pMixStream)
2240{
2241 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2242 if (RT_FAILURE(rc2))
2243 return false;
2244
2245 AssertPtr(pMixStream->pConn);
2246 AssertPtr(pMixStream->pStream);
2247
2248 bool fIsActive;
2249
2250 if ( pMixStream->pConn
2251 && pMixStream->pStream
2252 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAGS_ENABLED))
2253 {
2254 fIsActive = true;
2255 }
2256 else
2257 fIsActive = false;
2258
2259 rc2 = RTCritSectLeave(&pMixStream->CritSect);
2260 AssertRC(rc2);
2261
2262 return fIsActive;
2263}
2264
2265/**
2266 * Returns whether a mixer stream is valid (e.g. initialized and in a working state) or not.
2267 *
2268 * @returns @c true if valid, @c false if not.
2269 * @param pMixStream Mixer stream to return status for.
2270 */
2271bool AudioMixerStreamIsValid(PAUDMIXSTREAM pMixStream)
2272{
2273 if (!pMixStream)
2274 return false;
2275
2276 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2277 if (RT_FAILURE(rc2))
2278 return false;
2279
2280 bool fIsValid;
2281
2282 if ( pMixStream->pConn
2283 && pMixStream->pStream
2284 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED))
2285 {
2286 fIsValid = true;
2287 }
2288 else
2289 fIsValid = false;
2290
2291 rc2 = RTCritSectLeave(&pMixStream->CritSect);
2292 AssertRC(rc2);
2293
2294 return fIsValid;
2295}
2296
Note: See TracBrowser for help on using the repository browser.

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