VirtualBox

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

最後變更 在這個檔案從88884是 88884,由 vboxsync 提交於 4 年 前

DrvAudio,Mixer: Changed PDMIAUDIOCONNECTOR::pfnStreamGetStatus into pfnStreamGetState and defined a simpler state enum (PDMAUDIOSTREAMSTATE) that fits the Mixer's need and nothing more. PDMAUDIOSTREAM_STS_XXX will soon be DrvAudio internal. Changed some state checks in the mixer from ENABLED and ENABLED+WRITABLE/READABLE (probably more that needs adjusting). bugref:9890

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 75.5 KB
 
1/* $Id: AudioMixer.cpp 88884 2021-05-05 18:27:18Z 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.alldomusa.eu.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)) /** @todo r=bird: AUDMIXSTREAM_STATUS_CAN_READ ?*/
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 /** @todo r=bird: Check for AUDMIXSTREAM_STATUS_CAN_READ? */
1695 rc2 = pMixStream->pConn->pfnStreamCapture(pMixStream->pConn, pMixStream->pStream, &cFramesCaptured);
1696 if (RT_SUCCESS(rc2))
1697 {
1698 if (cFramesCaptured)
1699 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1700 }
1701 else
1702 {
1703 LogFunc(("%s: Failed capturing stream '%s', rc=%Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc2));
1704 if (RT_SUCCESS(rc))
1705 rc = rc2;
1706 }
1707 }
1708 else if (RT_SUCCESS(rc))
1709 rc = rc2;
1710 Log3Func(("%s: cFramesCaptured=%RU32 (rc2=%Rrc)\n", pMixStream->pStream->szName, cFramesCaptured, rc2));
1711 }
1712 }
1713
1714 /* Update last updated timestamp. */
1715 pSink->tsLastUpdatedMs = RTTimeMilliTS();
1716
1717 /*
1718 * Deal with pending disable. The general case is that we reset
1719 * the sink when all streams have been disabled, however input is
1720 * currently a special case where we only care about the one
1721 * recording source...
1722 */
1723 if (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE)
1724 {
1725#if 1
1726 uint32_t const cStreams = 1;
1727 uint32_t cStreamsDisabled = 1;
1728 pMixStream = pSink->In.pStreamRecSource;
1729#else
1730 uint32_t const cStreams = pSink->cStreams;
1731 uint32_t cStreamsDisabled = pSink->cStreams;
1732 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1733#endif
1734 {
1735 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1736 {
1737 PDMAUDIOSTREAMSTATE const enmState = pMixStream->pConn->pfnStreamGetState(pMixStream->pConn, pMixStream->pStream);
1738 if (enmState >= PDMAUDIOSTREAMSTATE_ENABLED)
1739 cStreamsDisabled--;
1740 }
1741 }
1742 Log3Func(("[%s] pending disable: %u of %u disabled\n", pSink->pszName, cStreamsDisabled, cStreams));
1743 if (cStreamsDisabled == cStreams)
1744 audioMixerSinkReset(pSink);
1745 }
1746
1747 return rc;
1748}
1749
1750/**
1751 * Updates an output mixer sink.
1752 *
1753 * @returns VBox status code.
1754 * @param pSink Mixer sink to update.
1755 */
1756static int audioMixerSinkUpdateOutput(PAUDMIXSINK pSink)
1757{
1758 /*
1759 * Update each mixing sink stream's status and check how much we can
1760 * write into them.
1761 *
1762 * We're currently using the minimum size of all streams, however this
1763 * isn't a smart approach as it means one disfunctional stream can block
1764 * working ones.
1765 */
1766 /** @todo rework this so a broken stream cannot hold up everyone. */
1767 uint32_t cFramesToRead = AudioMixBufLive(&pSink->MixBuf); /* (to read from the mixing buffer) */
1768 uint32_t cWritableStreams = 0;
1769 PAUDMIXSTREAM pMixStream;
1770 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1771 {
1772#if 0 /** @todo this conceptually makes sense, but may mess up the pending-disable logic ... */
1773 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1774 pConn->pfnStreamIterate(pConn, pStream);
1775#endif
1776
1777 int rc2 = audioMixerStreamUpdateStatus(pMixStream);
1778 AssertRC(rc2);
1779
1780 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_CAN_WRITE)
1781 {
1782 uint32_t const cbWritable = pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream);
1783 uint32_t cFrames = PDMAudioPropsBytesToFrames(&pMixStream->pStream->Props, cbWritable);
1784 if (PDMAudioPropsHz(&pMixStream->pStream->Props) == PDMAudioPropsHz(&pSink->MixBuf.Props))
1785 { /* likely */ }
1786 else
1787 {
1788 cFrames = cFrames * PDMAudioPropsHz(&pSink->MixBuf.Props) / PDMAudioPropsHz(&pMixStream->pStream->Props);
1789 cFrames = cFrames > 2 ? cFrames - 2 : 0; /* rounding safety fudge */
1790 }
1791 if (cFramesToRead > cFrames)
1792 {
1793 Log4Func(("%s: cFramesToRead %u -> %u; %s (%u bytes writable)\n",
1794 pSink->pszName, cFramesToRead, cFrames, pMixStream->pszName, cbWritable));
1795 cFramesToRead = cFrames;
1796 }
1797 cWritableStreams++;
1798 }
1799 }
1800 Log3Func(("%s: cLiveFrames=%#x cFramesToRead=%#x cWritableStreams=%#x\n", pSink->pszName,
1801 AudioMixBufLive(&pSink->MixBuf), cFramesToRead, cWritableStreams));
1802
1803 if (cWritableStreams > 0)
1804 {
1805 if (cFramesToRead > 0)
1806 {
1807 /*
1808 * For each of the enabled streams, convert cFramesToRead frames from
1809 * the mixing buffer and write that to the downstream driver.
1810 */
1811 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1812 {
1813 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_CAN_WRITE)
1814 {
1815 uint32_t offSrcFrame = 0;
1816 do
1817 {
1818 /* Convert a chunk from the mixer buffer. */
1819 union
1820 {
1821 uint8_t ab[8192];
1822 uint64_t au64[8192 / sizeof(uint64_t)]; /* Use uint64_t to ensure good alignment. */
1823 } Buf;
1824 uint32_t cbDstPeeked = sizeof(Buf);
1825 uint32_t cSrcFramesPeeked = cFramesToRead - offSrcFrame;
1826 AudioMixBufPeek(&pSink->MixBuf, offSrcFrame, cSrcFramesPeeked, &cSrcFramesPeeked,
1827 &pMixStream->PeekState, &Buf, sizeof(Buf), &cbDstPeeked);
1828 offSrcFrame += cSrcFramesPeeked;
1829
1830 /* Write it to the backend. Since've checked that there is buffer
1831 space available, this should always write the whole buffer. */
1832 uint32_t cbDstWritten = 0;
1833 int rc2 = pMixStream->pConn->pfnStreamPlay(pMixStream->pConn, pMixStream->pStream,
1834 &Buf, cbDstPeeked, &cbDstWritten);
1835 Log3Func(("%s: %#x L %#x => %#x bytes; wrote %#x rc2=%Rrc %s\n", pSink->pszName, offSrcFrame,
1836 cSrcFramesPeeked - cSrcFramesPeeked, cbDstPeeked, cbDstWritten, rc2, pMixStream->pszName));
1837 if (RT_SUCCESS(rc2))
1838 AssertLogRelMsg(cbDstWritten == cbDstPeeked,
1839 ("cbDstWritten=%#x cbDstPeeked=%#x - (sink '%s')\n",
1840 cbDstWritten, cbDstPeeked, pSink->pszName));
1841 else if (rc2 == VERR_AUDIO_STREAM_NOT_READY)
1842 {
1843 LogRel2(("Audio Mixer: '%s' (sink '%s'): Stream not ready - skipping.\n",
1844 pMixStream->pszName, pSink->pszName));
1845 break; /* must've changed status, stop processing */
1846 }
1847 else
1848 {
1849 Assert(rc2 != VERR_BUFFER_OVERFLOW);
1850 LogRel2(("Audio Mixer: Writing to mixer stream '%s' (sink '%s') failed, rc=%Rrc\n",
1851 pMixStream->pszName, pSink->pszName, rc2));
1852 break;
1853 }
1854 } while (offSrcFrame < cFramesToRead);
1855 }
1856 }
1857
1858 AudioMixBufAdvance(&pSink->MixBuf, cFramesToRead);
1859 }
1860
1861 /*
1862 * Update the dirty flag for what it's worth.
1863 */
1864 if (AudioMixBufUsed(&pSink->MixBuf))
1865 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1866 else
1867 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1868 }
1869 else
1870 {
1871 /*
1872 * If no writable streams, just drop the mixer buffer content.
1873 */
1874 AudioMixBufDrop(&pSink->MixBuf);
1875 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1876 }
1877
1878 /*
1879 * Iterate buffers.
1880 */
1881 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1882 {
1883 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1884 pMixStream->pConn->pfnStreamIterate(pMixStream->pConn, pMixStream->pStream);
1885 }
1886
1887 /* Update last updated timestamp. */
1888 pSink->tsLastUpdatedMs = RTTimeMilliTS();
1889
1890 /*
1891 * Deal with pending disable.
1892 * We reset the sink when all streams have been disabled.
1893 */
1894 if (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE)
1895 {
1896 uint32_t const cStreams = pSink->cStreams;
1897 uint32_t cStreamsDisabled = pSink->cStreams;
1898 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1899 {
1900 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1901 {
1902 PDMAUDIOSTREAMSTATE const enmState = pMixStream->pConn->pfnStreamGetState(pMixStream->pConn, pMixStream->pStream);
1903 if (enmState >= PDMAUDIOSTREAMSTATE_ENABLED)
1904 cStreamsDisabled--;
1905 }
1906 }
1907 Log3Func(("[%s] pending disable: %u of %u disabled\n", pSink->pszName, cStreamsDisabled, cStreams));
1908 if (cStreamsDisabled == cStreams)
1909 audioMixerSinkReset(pSink);
1910 }
1911
1912 return VINF_SUCCESS;
1913}
1914
1915/**
1916 * Updates (invalidates) a mixer sink.
1917 *
1918 * @returns VBox status code.
1919 * @param pSink Mixer sink to update.
1920 */
1921int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
1922{
1923 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1924 int rc = RTCritSectEnter(&pSink->CritSect);
1925 AssertRCReturn(rc, rc);
1926
1927#ifdef LOG_ENABLED
1928 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
1929#endif
1930 Log3Func(("[%s] fStatus=%s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
1931
1932 /* Only process running sinks. */
1933 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
1934 {
1935 /* Do separate processing for input and output sinks. */
1936 if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1937 rc = audioMixerSinkUpdateOutput(pSink);
1938 else if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1939 rc = audioMixerSinkUpdateInput(pSink);
1940 else
1941 AssertFailed();
1942 }
1943 else
1944 rc = VINF_SUCCESS; /* disabled */
1945
1946 RTCritSectLeave(&pSink->CritSect);
1947 return rc;
1948}
1949
1950/**
1951 * Updates the (master) volume of a mixer sink.
1952 *
1953 * @returns VBox status code.
1954 * @param pSink Mixer sink to update volume for.
1955 * @param pVolMaster Master volume to set.
1956 */
1957static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
1958{
1959 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1960 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
1961
1962 LogFlowFunc(("[%s] Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1963 pSink->pszName, pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
1964 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32 ",
1965 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1966
1967 /** @todo Very crude implementation for now -- needs more work! */
1968
1969 pSink->VolumeCombined.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
1970
1971 pSink->VolumeCombined.uLeft = ( (pSink->Volume.uLeft ? pSink->Volume.uLeft : 1)
1972 * (pVolMaster->uLeft ? pVolMaster->uLeft : 1)) / PDMAUDIO_VOLUME_MAX;
1973
1974 pSink->VolumeCombined.uRight = ( (pSink->Volume.uRight ? pSink->Volume.uRight : 1)
1975 * (pVolMaster->uRight ? pVolMaster->uRight : 1)) / PDMAUDIO_VOLUME_MAX;
1976
1977 LogFlow(("-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1978 pSink->VolumeCombined.fMuted, pSink->VolumeCombined.uLeft, pSink->VolumeCombined.uRight));
1979
1980 /*
1981 * Input sinks must currently propagate the new volume settings to
1982 * all the streams. (For output sinks we do the volume control here.)
1983 */
1984 if (pSink->enmDir != AUDMIXSINKDIR_OUTPUT)
1985 {
1986 PAUDMIXSTREAM pMixStream;
1987 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1988 {
1989 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &pSink->VolumeCombined);
1990 AssertRC(rc2);
1991 }
1992 }
1993
1994 return VINF_SUCCESS;
1995}
1996
1997/**
1998 * Writes data to a mixer output sink.
1999 *
2000 * @returns VBox status code.
2001 * @param pSink Sink to write data to.
2002 * @param enmOp Mixer operation to use when writing data to the sink.
2003 * @param pvBuf Buffer containing the audio data to write.
2004 * @param cbBuf Size (in bytes) of the buffer containing the audio data.
2005 * @param pcbWritten Number of bytes written. Optional.
2006 */
2007int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
2008{
2009 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2010 RT_NOREF(enmOp);
2011 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2012 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
2013 /* pcbWritten is optional. */
2014
2015 int rc = RTCritSectEnter(&pSink->CritSect);
2016 AssertRCReturn(rc, rc);
2017
2018 AssertMsg(pSink->fStatus & AUDMIXSINK_STS_RUNNING,
2019 ("%s: Can't write to a sink which is not running (anymore) (status 0x%x)\n", pSink->pszName, pSink->fStatus));
2020 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
2021 ("%s: Can't write to a sink which is not an output sink\n", pSink->pszName));
2022
2023 uint32_t cbWritten = 0;
2024 uint32_t cbToWrite = RT_MIN(AudioMixBufFreeBytes(&pSink->MixBuf), cbBuf);
2025 while (cbToWrite)
2026 {
2027 /* Write the data to the mixer sink's own mixing buffer.
2028 Here the audio data is transformed into the mixer sink's format. */
2029 uint32_t cFramesWritten = 0;
2030 rc = AudioMixBufWriteCirc(&pSink->MixBuf, (uint8_t const*)pvBuf + cbWritten, cbToWrite, &cFramesWritten);
2031 if (RT_SUCCESS(rc))
2032 {
2033 const uint32_t cbWrittenChunk = PDMAudioPropsFramesToBytes(&pSink->PCMProps, cFramesWritten);
2034 Assert(cbToWrite >= cbWrittenChunk);
2035 cbToWrite -= cbWrittenChunk;
2036 cbWritten += cbWrittenChunk;
2037 }
2038 else
2039 break;
2040 }
2041
2042 Log3Func(("[%s] cbBuf=%RU32 -> cbWritten=%RU32\n", pSink->pszName, cbBuf, cbWritten));
2043
2044 /* Update the sink's last written time stamp. */
2045 pSink->tsLastReadWrittenNs = RTTimeNanoTS();
2046
2047 if (pcbWritten)
2048 *pcbWritten = cbWritten;
2049
2050 RTCritSectLeave(&pSink->CritSect);
2051 return rc;
2052}
2053
2054
2055/*********************************************************************************************************************************
2056 * Mixer Stream implementation.
2057 ********************************************************************************************************************************/
2058
2059/**
2060 * Controls a mixer stream, internal version.
2061 *
2062 * @returns VBox status code.
2063 * @param pMixStream Mixer stream to control.
2064 * @param enmCmd Mixer stream command to use.
2065 * @param fCtl Additional control flags. Pass 0.
2066 */
2067static int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
2068{
2069 AssertPtr(pMixStream->pConn);
2070 AssertPtr(pMixStream->pStream);
2071
2072 RT_NOREF(fCtl);
2073
2074 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
2075
2076 LogFlowFunc(("[%s] enmCmd=%ld, rc=%Rrc\n", pMixStream->pszName, enmCmd, rc));
2077
2078 return rc;
2079}
2080
2081/**
2082 * Updates a mixer stream's internal status.
2083 *
2084 * This may perform a stream re-init if the driver requests it, in which case
2085 * this may take a little while longer than usual...
2086 *
2087 * @returns VBox status code.
2088 * @param pMixStream Mixer stream to to update internal status for.
2089 */
2090static int audioMixerStreamUpdateStatus(PAUDMIXSTREAM pMixStream)
2091{
2092 /*
2093 * Reset the mixer status to start with.
2094 */
2095 pMixStream->fStatus = AUDMIXSTREAM_STATUS_NONE;
2096
2097 PPDMIAUDIOCONNECTOR const pConn = pMixStream->pConn;
2098 if (pConn) /* Audio connector available? */
2099 {
2100 PPDMAUDIOSTREAM const pStream = pMixStream->pStream;
2101
2102 /*
2103 * Get the stream status.
2104 * Do re-init if needed and fetch the status again afterwards.
2105 */
2106 PDMAUDIOSTREAMSTATE enmState = pConn->pfnStreamGetState(pConn, pStream);
2107 if (enmState != PDMAUDIOSTREAMSTATE_NEED_REINIT)
2108 { /* likely */ }
2109 else
2110 {
2111 LogFunc(("[%s] needs re-init...\n", pMixStream->pszName));
2112 int rc = pConn->pfnStreamReInit(pConn, pStream);
2113 enmState = pConn->pfnStreamGetState(pConn, pStream);
2114 LogFunc(("[%s] re-init returns %Rrc and %d.\n", pMixStream->pszName, rc, enmState));
2115
2116 PAUDMIXSINK const pSink = pMixStream->pSink;
2117 AssertPtr(pSink);
2118 if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
2119 {
2120 rc = AudioMixBufInitPeekState(&pSink->MixBuf, &pMixStream->PeekState, &pStream->Props);
2121 /** @todo we need to remember this, don't we? */
2122 AssertRCReturn(rc, VINF_SUCCESS);
2123 }
2124 }
2125
2126 /*
2127 * Translate the status to mixer speak.
2128 */
2129 AssertMsg(enmState > PDMAUDIOSTREAMSTATE_INVALID && enmState < PDMAUDIOSTREAMSTATE_END, ("%d\n", enmState));
2130 switch (enmState)
2131 {
2132 case PDMAUDIOSTREAMSTATE_NOT_WORKING:
2133 case PDMAUDIOSTREAMSTATE_NEED_REINIT:
2134 case PDMAUDIOSTREAMSTATE_INACTIVE:
2135 pMixStream->fStatus = AUDMIXSTREAM_STATUS_NONE;
2136 break;
2137 case PDMAUDIOSTREAMSTATE_ENABLED:
2138 pMixStream->fStatus = AUDMIXSTREAM_STATUS_ENABLED;
2139 break;
2140 case PDMAUDIOSTREAMSTATE_ENABLED_READABLE:
2141 Assert(pMixStream->pSink->enmDir == AUDMIXSINKDIR_INPUT);
2142 pMixStream->fStatus = AUDMIXSTREAM_STATUS_ENABLED | AUDMIXSTREAM_STATUS_CAN_READ;
2143 break;
2144 case PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE:
2145 Assert(pMixStream->pSink->enmDir == AUDMIXSINKDIR_OUTPUT);
2146 pMixStream->fStatus = AUDMIXSTREAM_STATUS_ENABLED | AUDMIXSTREAM_STATUS_CAN_WRITE;
2147 break;
2148 /* no default */
2149 case PDMAUDIOSTREAMSTATE_INVALID:
2150 case PDMAUDIOSTREAMSTATE_END:
2151 case PDMAUDIOSTREAMSTATE_32BIT_HACK:
2152 break;
2153 }
2154 }
2155
2156 LogFlowFunc(("[%s] -> 0x%x\n", pMixStream->pszName, pMixStream->fStatus));
2157 return VINF_SUCCESS;
2158}
2159
2160/**
2161 * Controls a mixer stream.
2162 *
2163 * @returns VBox status code.
2164 * @param pMixStream Mixer stream to control.
2165 * @param enmCmd Mixer stream command to use.
2166 * @param fCtl Additional control flags. Pass 0.
2167 */
2168int AudioMixerStreamCtl(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
2169{
2170 RT_NOREF(fCtl);
2171 AssertPtrReturn(pMixStream, VERR_INVALID_POINTER);
2172 /** @todo Validate fCtl. */
2173
2174 int rc = RTCritSectEnter(&pMixStream->CritSect);
2175 if (RT_FAILURE(rc))
2176 return rc;
2177
2178 rc = audioMixerStreamCtlInternal(pMixStream, enmCmd, fCtl);
2179
2180 int rc2 = RTCritSectLeave(&pMixStream->CritSect);
2181 if (RT_SUCCESS(rc))
2182 rc = rc2;
2183
2184 return rc;
2185}
2186
2187/**
2188 * Destroys a mixer stream, internal version.
2189 *
2190 * @param pMixStream Mixer stream to destroy.
2191 * @param pDevIns The device instance the statistics are registered with.
2192 */
2193static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream, PPDMDEVINS pDevIns)
2194{
2195 AssertPtrReturnVoid(pMixStream);
2196
2197 LogFunc(("%s\n", pMixStream->pszName));
2198
2199 if (pMixStream->pConn) /* Stream has a connector interface present? */
2200 {
2201 if (pMixStream->pStream)
2202 {
2203 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
2204 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
2205
2206 pMixStream->pStream = NULL;
2207 }
2208
2209 pMixStream->pConn = NULL;
2210 }
2211
2212 if (pMixStream->pszStatPrefix)
2213 {
2214 PDMDevHlpSTAMDeregisterByPrefix(pDevIns, pMixStream->pszStatPrefix);
2215 RTStrFree(pMixStream->pszStatPrefix);
2216 pMixStream->pszStatPrefix = NULL;
2217 }
2218
2219 RTStrFree(pMixStream->pszName);
2220 pMixStream->pszName = NULL;
2221
2222 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
2223 AssertRC(rc2);
2224
2225 RTMemFree(pMixStream);
2226 pMixStream = NULL;
2227}
2228
2229/**
2230 * Destroys a mixer stream.
2231 *
2232 * @param pMixStream Mixer stream to destroy.
2233 * @param pDevIns The device instance statistics are registered with.
2234 */
2235void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream, PPDMDEVINS pDevIns)
2236{
2237 if (!pMixStream)
2238 return;
2239
2240 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2241 AssertRC(rc2);
2242
2243 LogFunc(("%s\n", pMixStream->pszName));
2244
2245 if (pMixStream->pSink) /* Is the stream part of a sink? */
2246 {
2247 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
2248 * pointer will be gone from the stream. */
2249 PAUDMIXSINK pSink = pMixStream->pSink;
2250
2251 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
2252 if (RT_SUCCESS(rc2))
2253 {
2254 Assert(pSink->cStreams);
2255 pSink->cStreams--;
2256 }
2257 }
2258 else
2259 rc2 = VINF_SUCCESS;
2260
2261 int rc3 = RTCritSectLeave(&pMixStream->CritSect);
2262 AssertRC(rc3);
2263
2264 if (RT_SUCCESS(rc2))
2265 {
2266 audioMixerStreamDestroyInternal(pMixStream, pDevIns);
2267 pMixStream = NULL;
2268 }
2269
2270 LogFlowFunc(("Returning %Rrc\n", rc2));
2271}
2272
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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