VirtualBox

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

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

AudioMixer: Build fix (draining revamp). bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 100.0 KB
Line 
1/* $Id: AudioMixer.cpp 88994 2021-05-12 00:54:09Z 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/semaphore.h>
91#include <iprt/string.h>
92#include <iprt/thread.h>
93
94#ifdef VBOX_WITH_DTRACE
95# include "dtrace/VBoxDD.h"
96#endif
97
98
99/*********************************************************************************************************************************
100* Internal Functions *
101*********************************************************************************************************************************/
102static int audioMixerAddSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink);
103static int audioMixerRemoveSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink);
104
105static int audioMixerSinkInit(PAUDMIXSINK pSink, PAUDIOMIXER pMixer, const char *pcszName, PDMAUDIODIR enmDir, PPDMDEVINS pDevIns);
106static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink, PPDMDEVINS pDevIns);
107static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, PCPDMAUDIOVOLUME pVolMaster);
108static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink);
109static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream);
110static void audioMixerSinkReset(PAUDMIXSINK pSink);
111static int audioMixerSinkSetRecSourceInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream);
112
113static int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd);
114static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pStream, PPDMDEVINS pDevIns);
115static int audioMixerStreamUpdateStatus(PAUDMIXSTREAM pMixStream);
116
117
118/** size of output buffer for dbgAudioMixerSinkStatusToStr. */
119#define AUDIOMIXERSINK_STATUS_STR_MAX sizeof("RUNNING DRAINING DRAINED_DMA DRAINED_MIXBUF DIRTY 0x12345678")
120
121/**
122 * Converts a mixer sink status to a string.
123 *
124 * @returns pszDst
125 * @param fStatus The mixer sink status.
126 * @param pszDst The output buffer. Must be at least
127 * AUDIOMIXERSINK_STATUS_STR_MAX in length.
128 */
129static const char *dbgAudioMixerSinkStatusToStr(uint32_t fStatus, char pszDst[AUDIOMIXERSINK_STATUS_STR_MAX])
130{
131 if (!fStatus)
132 return strcpy(pszDst, "NONE");
133 static const struct
134 {
135 const char *pszMnemonic;
136 uint32_t cchMnemonic;
137 uint32_t fStatus;
138 } s_aFlags[] =
139 {
140 { RT_STR_TUPLE("RUNNING "), AUDMIXSINK_STS_RUNNING },
141 { RT_STR_TUPLE("DRAINING "), AUDMIXSINK_STS_DRAINING },
142 { RT_STR_TUPLE("DRAINED_DMA"), AUDMIXSINK_STS_DRAINED_DMA },
143 { RT_STR_TUPLE("DRAINED_MIXBUF"), AUDMIXSINK_STS_DRAINED_MIXBUF },
144 { RT_STR_TUPLE("DIRTY "), AUDMIXSINK_STS_DIRTY },
145 };
146 char *psz = pszDst;
147 for (size_t i = 0; i < RT_ELEMENTS(s_aFlags); i++)
148 if (fStatus & s_aFlags[i].fStatus)
149 {
150 memcpy(psz, s_aFlags[i].pszMnemonic, s_aFlags[i].cchMnemonic);
151 psz += s_aFlags[i].cchMnemonic;
152 fStatus &= ~s_aFlags[i].fStatus;
153 if (!fStatus)
154 {
155 psz[-1] = '\0';
156 return pszDst;
157 }
158 }
159 RTStrPrintf(psz, AUDIOMIXERSINK_STATUS_STR_MAX - (psz - pszDst), "%#x", fStatus);
160 return pszDst;
161}
162
163/**
164 * Creates an audio sink and attaches it to the given mixer.
165 *
166 * @returns VBox status code.
167 * @param pMixer Mixer to attach created sink to.
168 * @param pszName Name of the sink to create.
169 * @param enmDir Direction of the sink to create.
170 * @param pDevIns The device instance to register statistics under.
171 * @param ppSink Pointer which returns the created sink on success.
172 */
173int AudioMixerCreateSink(PAUDIOMIXER pMixer, const char *pszName, PDMAUDIODIR enmDir, PPDMDEVINS pDevIns, PAUDMIXSINK *ppSink)
174{
175 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
176 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
177 /* ppSink is optional. */
178
179 int rc = RTCritSectEnter(&pMixer->CritSect);
180 AssertRCReturn(rc, rc);
181
182 PAUDMIXSINK pSink = (PAUDMIXSINK)RTMemAllocZ(sizeof(AUDMIXSINK));
183 if (pSink)
184 {
185 rc = audioMixerSinkInit(pSink, pMixer, pszName, enmDir, pDevIns);
186 if (RT_SUCCESS(rc))
187 {
188 rc = audioMixerAddSinkInternal(pMixer, pSink);
189 if (RT_SUCCESS(rc))
190 {
191 RTCritSectLeave(&pMixer->CritSect);
192
193 char szPrefix[128];
194 RTStrPrintf(szPrefix, sizeof(szPrefix), "MixerSink-%s/", pSink->pszName);
195 PDMDevHlpSTAMRegisterF(pDevIns, &pSink->MixBuf.cFrames, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
196 "Sink mixer buffer size in frames.", "%sMixBufSize", szPrefix);
197 PDMDevHlpSTAMRegisterF(pDevIns, &pSink->MixBuf.cUsed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
198 "Sink mixer buffer fill size in frames.", "%sMixBufUsed", szPrefix);
199 PDMDevHlpSTAMRegisterF(pDevIns, &pSink->cStreams, STAMTYPE_U8, STAMVISIBILITY_USED, STAMUNIT_NONE,
200 "Number of streams attached to the sink.", "%sStreams", szPrefix);
201
202 if (ppSink)
203 *ppSink = pSink;
204 return VINF_SUCCESS;
205 }
206 }
207
208 audioMixerSinkDestroyInternal(pSink, pDevIns);
209
210 RTMemFree(pSink);
211 pSink = NULL;
212 }
213 else
214 rc = VERR_NO_MEMORY;
215
216 RTCritSectLeave(&pMixer->CritSect);
217 return rc;
218}
219
220/**
221 * Creates an audio mixer.
222 *
223 * @returns VBox status code.
224 * @param pcszName Name of the audio mixer.
225 * @param fFlags Creation flags - AUDMIXER_FLAGS_XXX.
226 * @param ppMixer Pointer which returns the created mixer object.
227 */
228int AudioMixerCreate(const char *pcszName, uint32_t fFlags, PAUDIOMIXER *ppMixer)
229{
230 AssertPtrReturn(pcszName, VERR_INVALID_POINTER);
231 AssertReturn (!(fFlags & ~AUDMIXER_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
232 AssertPtrReturn(ppMixer, VERR_INVALID_POINTER);
233
234 int rc = VINF_SUCCESS;
235
236 PAUDIOMIXER pMixer = (PAUDIOMIXER)RTMemAllocZ(sizeof(AUDIOMIXER));
237 if (pMixer)
238 {
239 pMixer->pszName = RTStrDup(pcszName);
240 if (!pMixer->pszName)
241 rc = VERR_NO_MEMORY;
242
243 if (RT_SUCCESS(rc))
244 rc = RTCritSectInit(&pMixer->CritSect);
245
246 if (RT_SUCCESS(rc))
247 {
248 pMixer->cSinks = 0;
249 RTListInit(&pMixer->lstSinks);
250
251 pMixer->fFlags = fFlags;
252 pMixer->uMagic = AUDIOMIXER_MAGIC;
253
254 if (pMixer->fFlags & AUDMIXER_FLAGS_DEBUG)
255 LogRel(("Audio Mixer: Debug mode enabled\n"));
256
257 /* Set master volume to the max. */
258 pMixer->VolMaster.fMuted = false;
259 pMixer->VolMaster.uLeft = PDMAUDIO_VOLUME_MAX;
260 pMixer->VolMaster.uRight = PDMAUDIO_VOLUME_MAX;
261
262 LogFlowFunc(("Created mixer '%s'\n", pMixer->pszName));
263
264 *ppMixer = pMixer;
265 }
266 else
267 RTMemFree(pMixer); /** @todo leaks pszName due to badly structured code */
268 }
269 else
270 rc = VERR_NO_MEMORY;
271
272 LogFlowFuncLeaveRC(rc);
273 return rc;
274}
275
276/**
277 * Helper function for the internal debugger to print the mixer's current
278 * state, along with the attached sinks.
279 *
280 * @param pMixer Mixer to print debug output for.
281 * @param pHlp Debug info helper to use.
282 * @param pszArgs Optional arguments. Not being used at the moment.
283 */
284void AudioMixerDebug(PAUDIOMIXER pMixer, PCDBGFINFOHLP pHlp, const char *pszArgs)
285{
286 RT_NOREF(pszArgs);
287 Assert(pMixer->uMagic == AUDIOMIXER_MAGIC);
288
289 int rc2 = RTCritSectEnter(&pMixer->CritSect);
290 AssertRCReturnVoid(rc2);
291
292 pHlp->pfnPrintf(pHlp, "[Master] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", pMixer->pszName,
293 pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight, pMixer->VolMaster.fMuted);
294
295 PAUDMIXSINK pSink;
296 unsigned iSink = 0;
297 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
298 {
299 pHlp->pfnPrintf(pHlp, "[Sink %u] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", iSink, pSink->pszName,
300 pSink->Volume.uLeft, pSink->Volume.uRight, pSink->Volume.fMuted);
301 ++iSink;
302 }
303
304 rc2 = RTCritSectLeave(&pMixer->CritSect);
305 AssertRC(rc2);
306}
307
308/**
309 * Destroys an audio mixer.
310 *
311 * @param pMixer Audio mixer to destroy.
312 * @param pDevIns The device instance the statistics are associated with.
313 */
314void AudioMixerDestroy(PAUDIOMIXER pMixer, PPDMDEVINS pDevIns)
315{
316 if (!pMixer)
317 return;
318 AssertPtr(pMixer);
319 Assert(pMixer->uMagic == AUDIOMIXER_MAGIC);
320
321 int rc2 = RTCritSectEnter(&pMixer->CritSect);
322 AssertRC(rc2);
323
324 LogFlowFunc(("Destroying %s ...\n", pMixer->pszName));
325 pMixer->uMagic = AUDIOMIXER_MAGIC_DEAD;
326
327 PAUDMIXSINK pSink, pSinkNext;
328 RTListForEachSafe(&pMixer->lstSinks, pSink, pSinkNext, AUDMIXSINK, Node)
329 {
330 audioMixerSinkDestroyInternal(pSink, pDevIns);
331 audioMixerRemoveSinkInternal(pMixer, pSink);
332 RTMemFree(pSink);
333 }
334
335 Assert(pMixer->cSinks == 0);
336
337 RTStrFree(pMixer->pszName);
338 pMixer->pszName = NULL;
339
340 rc2 = RTCritSectLeave(&pMixer->CritSect);
341 AssertRC(rc2);
342
343 RTCritSectDelete(&pMixer->CritSect);
344
345 RTMemFree(pMixer);
346 pMixer = NULL;
347}
348
349/**
350 * Invalidates all internal data, internal version.
351 *
352 * @returns VBox status code.
353 * @param pMixer Mixer to invalidate data for.
354 */
355static int audioMixerInvalidateInternal(PAUDIOMIXER pMixer)
356{
357 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
358 LogFlowFunc(("[%s]\n", pMixer->pszName));
359
360 /* Propagate new master volume to all connected sinks. */
361 PAUDMIXSINK pSink;
362 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
363 {
364 int rc2 = audioMixerSinkUpdateVolume(pSink, &pMixer->VolMaster);
365 AssertRC(rc2);
366 }
367
368 return VINF_SUCCESS;
369}
370
371#if 0 /* unused */
372/**
373 * Invalidates all internal data.
374 *
375 * @returns VBox status code.
376 * @param pMixer Mixer to invalidate data for.
377 */
378void AudioMixerInvalidate(PAUDIOMIXER pMixer)
379{
380 AssertPtrReturnVoid(pMixer);
381 Assert(pMixer->uMagic == AUDIOMIXER_MAGIC);
382
383 int rc2 = RTCritSectEnter(&pMixer->CritSect);
384 AssertRC(rc2);
385
386 LogFlowFunc(("[%s]\n", pMixer->pszName));
387
388 rc2 = audioMixerInvalidateInternal(pMixer);
389 AssertRC(rc2);
390
391 rc2 = RTCritSectLeave(&pMixer->CritSect);
392 AssertRC(rc2);
393}
394#endif
395
396/**
397 * Adds sink to an existing mixer.
398 *
399 * @returns VBox status code.
400 * @param pMixer Mixer to add sink to.
401 * @param pSink Sink to add.
402 */
403static int audioMixerAddSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
404{
405 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
406 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
407
408 /** @todo Check upper sink limit? */
409 /** @todo Check for double-inserted sinks? */
410
411 RTListAppend(&pMixer->lstSinks, &pSink->Node);
412 pMixer->cSinks++;
413
414 LogFlowFunc(("pMixer=%p, pSink=%p, cSinks=%RU8\n",
415 pMixer, pSink, pMixer->cSinks));
416
417 return VINF_SUCCESS;
418}
419
420/**
421 * Removes a formerly attached audio sink for an audio mixer, internal version.
422 *
423 * @returns VBox status code.
424 * @param pMixer Mixer to remove sink from.
425 * @param pSink Sink to remove.
426 */
427static int audioMixerRemoveSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
428{
429 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
430 if (!pSink)
431 return VERR_NOT_FOUND;
432
433 AssertMsgReturn(pSink->pParent == pMixer, ("%s: Is not part of mixer '%s'\n",
434 pSink->pszName, pMixer->pszName), VERR_NOT_FOUND);
435
436 LogFlowFunc(("[%s] pSink=%s, cSinks=%RU8\n",
437 pMixer->pszName, pSink->pszName, pMixer->cSinks));
438
439 /* Remove sink from mixer. */
440 RTListNodeRemove(&pSink->Node);
441
442 Assert(pMixer->cSinks);
443 pMixer->cSinks--;
444
445 /* Set mixer to NULL so that we know we're not part of any mixer anymore. */
446 pSink->pParent = NULL;
447
448 return VINF_SUCCESS;
449}
450
451/**
452 * Sets the mixer's master volume.
453 *
454 * @returns VBox status code.
455 * @param pMixer Mixer to set master volume for.
456 * @param pVol Volume to set.
457 */
458int AudioMixerSetMasterVolume(PAUDIOMIXER pMixer, PPDMAUDIOVOLUME pVol)
459{
460 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
461 Assert(pMixer->uMagic == AUDIOMIXER_MAGIC);
462 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
463
464 int rc = RTCritSectEnter(&pMixer->CritSect);
465 AssertRCReturn(rc, rc);
466
467 memcpy(&pMixer->VolMaster, pVol, sizeof(PDMAUDIOVOLUME));
468
469 LogFlowFunc(("[%s] lVol=%RU32, rVol=%RU32 => fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
470 pMixer->pszName, pVol->uLeft, pVol->uRight,
471 pMixer->VolMaster.fMuted, pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight));
472
473 rc = audioMixerInvalidateInternal(pMixer);
474
475 int rc2 = RTCritSectLeave(&pMixer->CritSect);
476 AssertRC(rc2);
477
478 return rc;
479}
480
481
482/*********************************************************************************************************************************
483* Mixer Sink implementation. *
484*********************************************************************************************************************************/
485
486#ifdef VBOX_STRICT
487/**
488 * Checks if @a pNeedle is in the list of streams associated with @a pSink.
489 * @returns true / false.
490 */
491static bool audioMixerSinkIsStreamInList(PAUDMIXSINK pSink, PAUDMIXSTREAM pNeedle)
492{
493 PAUDMIXSTREAM pStream;
494 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
495 {
496 if (pStream == pNeedle)
497 return true;
498 }
499 return false;
500}
501#endif /* VBOX_STRICT */
502
503/**
504 * Adds an audio stream to a specific audio sink.
505 *
506 * @returns VBox status code.
507 * @param pSink Sink to add audio stream to.
508 * @param pStream Stream to add.
509 */
510int AudioMixerSinkAddStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
511{
512 LogFlowFuncEnter();
513 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
514 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
515 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
516 Assert(pStream->uMagic == AUDMIXSTREAM_MAGIC);
517 AssertPtrReturn(pStream->pConn, VERR_AUDIO_STREAM_NOT_READY);
518 AssertReturn(pStream->pSink == NULL, VERR_ALREADY_EXISTS);
519
520 int rc = RTCritSectEnter(&pSink->CritSect);
521 AssertRCReturn(rc, rc);
522
523 AssertLogRelMsgReturnStmt(pSink->cStreams < UINT8_MAX, ("too many streams!\n"), RTCritSectLeave(&pSink->CritSect),
524 VERR_TOO_MANY_OPEN_FILES);
525
526 /*
527 * If the sink is running and not in pending disable mode, make sure that
528 * the added stream also is enabled. Ignore any failure to enable it.
529 */
530 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
531 && !(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
532 {
533 audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_ENABLE);
534 }
535
536 if (pSink->enmDir != PDMAUDIODIR_OUT)
537 {
538 /* Apply the sink's combined volume to the stream. */
539 int rc2 = pStream->pConn->pfnStreamSetVolume(pStream->pConn, pStream->pStream, &pSink->VolumeCombined);
540 AssertRC(rc2);
541 }
542
543 /* Save pointer to sink the stream is attached to. */
544 pStream->pSink = pSink;
545
546 /* Append stream to sink's list. */
547 RTListAppend(&pSink->lstStreams, &pStream->Node);
548 pSink->cStreams++;
549
550 LogFlowFunc(("[%s] cStreams=%RU8, rc=%Rrc\n", pSink->pszName, pSink->cStreams, rc));
551 RTCritSectLeave(&pSink->CritSect);
552 return rc;
553}
554
555/**
556 * Creates an audio mixer stream.
557 *
558 * @returns VBox status code.
559 * @param pSink Sink to use for creating the stream.
560 * @param pConn Audio connector interface to use.
561 * @param pCfg Audio stream configuration to use. This may be modified
562 * in some unspecified way (see
563 * PDMIAUDIOCONNECTOR::pfnStreamCreate).
564 * @param pDevIns The device instance to register statistics with.
565 * @param ppStream Pointer which receives the newly created audio stream.
566 */
567int AudioMixerSinkCreateStream(PAUDMIXSINK pSink, PPDMIAUDIOCONNECTOR pConn, PPDMAUDIOSTREAMCFG pCfg,
568 PPDMDEVINS pDevIns, PAUDMIXSTREAM *ppStream)
569{
570 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
571 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
572 AssertPtrReturn(pConn, VERR_INVALID_POINTER);
573 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
574 AssertPtrNullReturn(ppStream, VERR_INVALID_POINTER);
575 Assert(pSink->AIO.pDevIns == pDevIns); RT_NOREF(pDevIns); /* we'll probably be adding more statistics */
576
577 /*
578 * Check status and get the host driver config.
579 */
580 if (pConn->pfnGetStatus(pConn, PDMAUDIODIR_DUPLEX) == PDMAUDIOBACKENDSTS_NOT_ATTACHED)
581 return VERR_AUDIO_BACKEND_NOT_ATTACHED;
582
583 PDMAUDIOBACKENDCFG BackendCfg;
584 int rc = pConn->pfnGetConfig(pConn, &BackendCfg);
585 AssertRCReturn(rc, rc);
586
587 /*
588 * Allocate the instance.
589 */
590 PAUDMIXSTREAM pMixStream = (PAUDMIXSTREAM)RTMemAllocZ(sizeof(AUDMIXSTREAM));
591 AssertReturn(pMixStream, VERR_NO_MEMORY);
592
593 /* Assign the backend's name to the mixer stream's name for easier identification in the (release) log. */
594 pMixStream->pszName = RTStrAPrintf2("[%s] %s", pCfg->szName, BackendCfg.szName);
595 pMixStream->pszStatPrefix = RTStrAPrintf2("MixerSink-%s/%s/", pSink->pszName, BackendCfg.szName);
596 if (pMixStream->pszName && pMixStream->pszStatPrefix)
597 {
598 rc = RTCritSectInit(&pMixStream->CritSect);
599 if (RT_SUCCESS(rc))
600 {
601 /*
602 * Lock the sink so we can safely get it's properties and call
603 * down into the audio driver to create that end of the stream.
604 */
605 rc = RTCritSectEnter(&pSink->CritSect);
606 AssertRC(rc);
607 if (RT_SUCCESS(rc))
608 {
609 LogFlowFunc(("[%s] (enmDir=%ld, %u bits, %RU8 channels, %RU32Hz)\n", pSink->pszName, pCfg->enmDir,
610 PDMAudioPropsSampleBits(&pCfg->Props), PDMAudioPropsChannels(&pCfg->Props), pCfg->Props.uHz));
611
612 /*
613 * Initialize the host-side configuration for the stream to be created.
614 * Always use the sink's PCM audio format as the host side when creating a stream for it.
615 */
616 AssertMsg(AudioHlpPcmPropsAreValid(&pSink->PCMProps),
617 ("%s: Does not (yet) have a format set when it must\n", pSink->pszName));
618
619 PDMAUDIOSTREAMCFG CfgHost;
620 rc = PDMAudioStrmCfgInitWithProps(&CfgHost, &pSink->PCMProps);
621 AssertRC(rc); /* cannot fail */
622
623 /* Apply the sink's direction for the configuration to use to create the stream. */
624 if (pSink->enmDir == PDMAUDIODIR_IN)
625 {
626 CfgHost.enmDir = PDMAUDIODIR_IN;
627 CfgHost.u.enmSrc = pCfg->u.enmSrc;
628 CfgHost.enmLayout = pCfg->enmLayout;
629 }
630 else
631 {
632 CfgHost.enmDir = PDMAUDIODIR_OUT;
633 CfgHost.u.enmDst = pCfg->u.enmDst;
634 CfgHost.enmLayout = pCfg->enmLayout;
635 }
636
637 RTStrCopy(CfgHost.szName, sizeof(CfgHost.szName), pCfg->szName);
638
639 /*
640 * Create the stream.
641 *
642 * Output streams are not using any mixing buffers in DrvAudio. This will
643 * become the norm after we move the input mixing here and convert DevSB16
644 * to use this mixer code too.
645 */
646 PPDMAUDIOSTREAM pStream;
647 rc = pConn->pfnStreamCreate(pConn, pSink->enmDir == PDMAUDIODIR_OUT ? PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF : 0,
648 &CfgHost, pCfg, &pStream);
649 if (RT_SUCCESS(rc))
650 {
651 pMixStream->cFramesBackendBuffer = CfgHost.Backend.cFramesBufferSize;
652
653 /* Set up the mixing buffer conversion state. */
654 if (pSink->enmDir == PDMAUDIODIR_OUT)
655 rc = AudioMixBufInitPeekState(&pSink->MixBuf, &pMixStream->PeekState, &pStream->Props);
656 if (RT_SUCCESS(rc))
657 {
658 /* Save the audio stream pointer to this mixing stream. */
659 pMixStream->pStream = pStream;
660
661 /* Increase the stream's reference count to let others know
662 * we're relying on it to be around now. */
663 pConn->pfnStreamRetain(pConn, pStream);
664 pMixStream->pConn = pConn;
665 pMixStream->uMagic = AUDMIXSTREAM_MAGIC;
666
667 RTCritSectLeave(&pSink->CritSect);
668
669 if (ppStream)
670 *ppStream = pMixStream;
671 return VINF_SUCCESS;
672 }
673
674 rc = pConn->pfnStreamDestroy(pConn, pStream);
675 }
676
677 /*
678 * Failed. Tear down the stream.
679 */
680 int rc2 = RTCritSectLeave(&pSink->CritSect);
681 AssertRC(rc2);
682 }
683 RTCritSectDelete(&pMixStream->CritSect);
684 }
685 }
686 else
687 rc = VERR_NO_STR_MEMORY;
688
689 RTStrFree(pMixStream->pszStatPrefix);
690 pMixStream->pszStatPrefix = NULL;
691 RTStrFree(pMixStream->pszName);
692 pMixStream->pszName = NULL;
693 RTMemFree(pMixStream);
694 return rc;
695}
696
697
698/**
699 * Starts playback/capturing on the mixer sink.
700 *
701 * @returns VBox status code. Generally always VINF_SUCCESS unless the input
702 * is invalid. Individual driver errors are suppressed and ignored.
703 * @param pSink Mixer sink to control.
704 */
705int AudioMixerSinkStart(PAUDMIXSINK pSink)
706{
707 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
708 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
709 int rc = RTCritSectEnter(&pSink->CritSect);
710 AssertRCReturn(rc, rc);
711 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
712 LogFunc(("Starting '%s'. Old status: %s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
713
714 /*
715 * Make sure the sink and its streams are all stopped.
716 */
717 if (!(pSink->fStatus & AUDMIXSINK_STS_RUNNING))
718 Assert(pSink->fStatus == AUDMIXSINK_STS_NONE);
719 else
720 {
721 LogFunc(("%s: This sink is still running!! Stop it before starting it again.\n", pSink->pszName));
722
723 PAUDMIXSTREAM pStream;
724 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
725 {
726 /** @todo PDMAUDIOSTREAMCMD_STOP_NOW */
727 audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_DISABLE);
728 }
729 audioMixerSinkReset(pSink);
730 }
731
732 /*
733 * Send the command to the streams.
734 */
735 if (pSink->enmDir == PDMAUDIODIR_OUT)
736 {
737 PAUDMIXSTREAM pStream;
738 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
739 {
740 audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_ENABLE);
741 }
742 }
743 else
744 {
745 AssertReturnStmt(pSink->enmDir == PDMAUDIODIR_IN, RTCritSectLeave(&pSink->CritSect), VERR_INTERNAL_ERROR_3);
746 if (pSink->In.pStreamRecSource) /* Any recording source set? */
747 {
748 Assert(audioMixerSinkIsStreamInList(pSink, pSink->In.pStreamRecSource));
749 audioMixerStreamCtlInternal(pSink->In.pStreamRecSource, PDMAUDIOSTREAMCMD_ENABLE);
750 }
751 }
752
753 /*
754 * Update the sink status.
755 */
756 pSink->fStatus = AUDMIXSINK_STS_RUNNING;
757
758 LogRel2(("Audio Mixer: Started sink '%s': %s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
759
760 RTCritSectLeave(&pSink->CritSect);
761 return VINF_SUCCESS;
762}
763
764
765/**
766 * Helper for AudioMixerSinkDrainAndStop that calculates the max length a drain
767 * operation should take.
768 *
769 * @returns The drain deadline (relative to RTTimeNanoTS).
770 * @param pSink The sink.
771 * @param cbDmaLeftToDrain The number of bytes in the DMA buffer left to
772 * transfer into the mixbuf.
773 */
774static uint64_t audioMixerSinkDrainDeadline(PAUDMIXSINK pSink, uint32_t cbDmaLeftToDrain)
775{
776 /*
777 * Calculate the max backend buffer size in mixbuf frames.
778 * (This is somewhat similar to audioMixerSinkUpdateOutputCalcFramesToRead.)
779 */
780 uint32_t cFramesStreamMax = 0;
781 PAUDMIXSTREAM pMixStream;
782 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
783 {
784 /*LogFunc(("Stream '%s': %#x (%u frames)\n", pMixStream->pszName, pMixStream->fStatus, pMixStream->cFramesBackendBuffer));*/
785 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_CAN_WRITE)
786 {
787 uint32_t cFrames = pMixStream->cFramesBackendBuffer;
788 if (PDMAudioPropsHz(&pMixStream->pStream->Props) == PDMAudioPropsHz(&pSink->MixBuf.Props))
789 { /* likely */ }
790 else
791 cFrames = cFrames * PDMAudioPropsHz(&pSink->MixBuf.Props) / PDMAudioPropsHz(&pMixStream->pStream->Props);
792 if (cFrames > cFramesStreamMax)
793 {
794 Log4Func(("%s: cFramesStreamMax %u -> %u; %s\n", pSink->pszName, cFramesStreamMax, cFrames, pMixStream->pszName));
795 cFramesStreamMax = cFrames;
796 }
797 }
798 }
799
800 /*
801 * Combine that with the pending DMA and mixbuf content, then convert
802 * to nanoseconds and apply a fudge factor to get a generous deadline.
803 */
804 uint32_t const cFramesDmaAndMixBuf = PDMAudioPropsBytesToFrames(&pSink->MixBuf.Props, cbDmaLeftToDrain)
805 + AudioMixBufLive(&pSink->MixBuf);
806 uint64_t const cNsToDrainMax = PDMAudioPropsFramesToNano(&pSink->MixBuf.Props, cFramesDmaAndMixBuf + cFramesStreamMax);
807 uint64_t const nsDeadline = cNsToDrainMax * 2;
808 LogFlowFunc(("%s: cFramesStreamMax=%#x cFramesDmaAndMixBuf=%#x -> cNsToDrainMax=%RU64 -> %RU64\n",
809 pSink->pszName, cFramesStreamMax, cFramesDmaAndMixBuf, cNsToDrainMax, nsDeadline));
810 return nsDeadline;
811}
812
813
814/**
815 * Kicks off the draining and stopping playback/capture on the mixer sink.
816 *
817 * For input streams this causes an immediate stop, as draining only makes sense
818 * to output stream in the VBox device context.
819 *
820 * @returns VBox status code. Generally always VINF_SUCCESS unless the input
821 * is invalid. Individual driver errors are suppressed and ignored.
822 * @param pSink Mixer sink to control.
823 * @param cbComming The number of bytes still left in the device's DMA
824 * buffers that the update job has yet to transfer. This
825 * is ignored for input streams.
826 */
827int AudioMixerSinkDrainAndStop(PAUDMIXSINK pSink, uint32_t cbComming)
828{
829 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
830 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
831
832 int rc = RTCritSectEnter(&pSink->CritSect);
833 AssertRCReturn(rc, rc);
834 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
835 LogFunc(("Draining '%s' with %#x bytes left. Old status: %s\n",
836 pSink->pszName, cbComming, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus) ));
837
838 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
839 {
840 /*
841 * Output streams will be drained then stopped (all by the AIO thread).
842 *
843 * For streams we define that they shouldn't not be written to after we start draining,
844 * so we have to hold back sending the command to them till we've processed all the
845 * cbComming remaining bytes in the DMA buffer.
846 */
847 if (pSink->enmDir == PDMAUDIODIR_OUT)
848 {
849 if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
850 {
851 Assert(!(pSink->fStatus & (AUDMIXSINK_STS_DRAINED_DMA | AUDMIXSINK_STS_DRAINED_MIXBUF)));
852
853 /* Update the status and draining member. */
854 pSink->cbDmaLeftToDrain = cbComming;
855 pSink->nsDrainDeadline = audioMixerSinkDrainDeadline(pSink, cbComming);
856 if (pSink->nsDrainDeadline > 0)
857 {
858 pSink->nsDrainStarted = RTTimeNanoTS();
859 pSink->nsDrainDeadline += pSink->nsDrainStarted;
860 pSink->fStatus |= AUDMIXSINK_STS_DRAINING;
861
862 /* Kick the AIO thread so it can keep pushing data till we're out of this
863 status. (The device's DMA timer won't kick it any more, so we must.) */
864 AudioMixerSinkSignalUpdateJob(pSink);
865 }
866 else
867 {
868 LogFunc(("%s: No active streams, doing an immediate stop.\n", pSink->pszName));
869 PAUDMIXSTREAM pStream;
870 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
871 {
872 audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_DISABLE);
873 }
874 audioMixerSinkReset(pSink);
875 }
876 }
877 else
878 AssertMsgFailed(("Already draining '%s': %s\n",
879 pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
880 }
881 /*
882 * Input sinks are stopped immediately.
883 *
884 * It's the guest giving order here and we can't force it to accept data that's
885 * already in the buffer pipeline or anything. So, there can be no draining here.
886 */
887 else
888 {
889 AssertReturnStmt(pSink->enmDir == PDMAUDIODIR_IN, RTCritSectLeave(&pSink->CritSect), VERR_INTERNAL_ERROR_3);
890 if (pSink->In.pStreamRecSource) /* Any recording source set? */
891 {
892 Assert(audioMixerSinkIsStreamInList(pSink, pSink->In.pStreamRecSource));
893 audioMixerStreamCtlInternal(pSink->In.pStreamRecSource, PDMAUDIOSTREAMCMD_DISABLE);
894 }
895 audioMixerSinkReset(pSink);
896 }
897 }
898 else
899 LogFunc(("%s: Not running\n", pSink->pszName));
900
901 LogRel2(("Audio Mixer: Started draining sink '%s': %s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
902 RTCritSectLeave(&pSink->CritSect);
903 return VINF_SUCCESS;
904}
905
906
907
908/**
909 * Initializes a sink.
910 *
911 * @returns VBox status code.
912 * @param pSink Sink to initialize.
913 * @param pMixer Mixer the sink is assigned to.
914 * @param pcszName Name of the sink.
915 * @param enmDir Direction of the sink.
916 * @param pDevIns The device instance.
917 */
918static int audioMixerSinkInit(PAUDMIXSINK pSink, PAUDIOMIXER pMixer, const char *pcszName, PDMAUDIODIR enmDir, PPDMDEVINS pDevIns)
919{
920 pSink->pszName = RTStrDup(pcszName);
921 if (!pSink->pszName)
922 return VERR_NO_MEMORY;
923
924 int rc = RTCritSectInit(&pSink->CritSect);
925 if (RT_SUCCESS(rc))
926 {
927 pSink->uMagic = AUDMIXSINK_MAGIC;
928 pSink->pParent = pMixer;
929 pSink->enmDir = enmDir;
930
931 RTListInit(&pSink->lstStreams);
932
933 /* Set initial volume to max. */
934 pSink->Volume.fMuted = false;
935 pSink->Volume.uLeft = PDMAUDIO_VOLUME_MAX;
936 pSink->Volume.uRight = PDMAUDIO_VOLUME_MAX;
937
938 /* Ditto for the combined volume. */
939 pSink->VolumeCombined.fMuted = false;
940 pSink->VolumeCombined.uLeft = PDMAUDIO_VOLUME_MAX;
941 pSink->VolumeCombined.uRight = PDMAUDIO_VOLUME_MAX;
942
943 /* AIO */
944 AssertPtr(pDevIns);
945 pSink->AIO.pDevIns = pDevIns;
946 pSink->AIO.hThread = NIL_RTTHREAD;
947 pSink->AIO.hEvent = NIL_RTSEMEVENT;
948 pSink->AIO.fStarted = false;
949 pSink->AIO.fShutdown = false;
950 pSink->AIO.cUpdateJobs = 0;
951 }
952
953 LogFlowFuncLeaveRC(rc);
954 return rc;
955}
956
957/**
958 * Destroys a mixer sink and removes it from the attached mixer (if any).
959 *
960 * @param pSink Mixer sink to destroy.
961 * @param pDevIns The device instance that statistics are registered with.
962 */
963void AudioMixerSinkDestroy(PAUDMIXSINK pSink, PPDMDEVINS pDevIns)
964{
965 if (!pSink)
966 return;
967
968 /** @todo wrong critsect for audioMixerRemoveSinkInternal... */
969 int rc2 = RTCritSectEnter(&pSink->CritSect);
970 AssertRC(rc2);
971
972 if (pSink->pParent)
973 {
974 /* Save mixer pointer, as after audioMixerRemoveSinkInternal() the
975 * pointer will be gone from the stream. */
976 PAUDIOMIXER pMixer = pSink->pParent;
977 AssertPtr(pMixer);
978 Assert(pMixer->uMagic == AUDIOMIXER_MAGIC);
979
980 audioMixerRemoveSinkInternal(pMixer, pSink);
981 }
982
983 rc2 = RTCritSectLeave(&pSink->CritSect);
984 AssertRC(rc2);
985
986 audioMixerSinkDestroyInternal(pSink, pDevIns);
987
988 RTMemFree(pSink);
989 pSink = NULL;
990}
991
992/**
993 * Destroys a mixer sink.
994 *
995 * @param pSink Mixer sink to destroy.
996 * @param pDevIns The device instance statistics are registered with.
997 */
998static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink, PPDMDEVINS pDevIns)
999{
1000 AssertPtrReturnVoid(pSink);
1001
1002 LogFunc(("%s\n", pSink->pszName));
1003
1004 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1005 pSink->uMagic = AUDMIXSINK_MAGIC_DEAD;
1006
1007 PAUDMIXSTREAM pStream, pStreamNext;
1008 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
1009 {
1010 audioMixerSinkRemoveStreamInternal(pSink, pStream);
1011 audioMixerStreamDestroyInternal(pStream, pDevIns); /* (Unlike the other two, this frees the stream structure.) */
1012 }
1013
1014 if ( pSink->pParent
1015 && pSink->pParent->fFlags & AUDMIXER_FLAGS_DEBUG)
1016 {
1017 AudioHlpFileDestroy(pSink->Dbg.pFile);
1018 pSink->Dbg.pFile = NULL;
1019 }
1020
1021 char szPrefix[128];
1022 RTStrPrintf(szPrefix, sizeof(szPrefix), "MixerSink-%s/", pSink->pszName);
1023 PDMDevHlpSTAMDeregisterByPrefix(pDevIns, szPrefix);
1024
1025 /* Shutdown the AIO thread if started: */
1026 ASMAtomicWriteBool(&pSink->AIO.fShutdown, true);
1027 if (pSink->AIO.hEvent != NIL_RTSEMEVENT)
1028 {
1029 int rc2 = RTSemEventSignal(pSink->AIO.hEvent);
1030 AssertRC(rc2);
1031 }
1032 if (pSink->AIO.hThread != NIL_RTTHREAD)
1033 {
1034 LogFlowFunc(("Waiting for AIO thread for %s...\n", pSink->pszName));
1035 int rc2 = RTThreadWait(pSink->AIO.hThread, RT_MS_30SEC, NULL);
1036 AssertRC(rc2);
1037 pSink->AIO.hThread = NIL_RTTHREAD;
1038 }
1039 if (pSink->AIO.hEvent != NIL_RTSEMEVENT)
1040 {
1041 int rc2 = RTSemEventDestroy(pSink->AIO.hEvent);
1042 AssertRC(rc2);
1043 pSink->AIO.hEvent = NIL_RTSEMEVENT;
1044 }
1045
1046 RTStrFree(pSink->pszName);
1047 pSink->pszName = NULL;
1048
1049 AudioMixBufDestroy(&pSink->MixBuf);
1050 RTCritSectDelete(&pSink->CritSect);
1051}
1052
1053/**
1054 * Returns the amount of bytes ready to be read from a sink since the last call
1055 * to AudioMixerSinkUpdate().
1056 *
1057 * @returns Amount of bytes ready to be read from the sink.
1058 * @param pSink Sink to return number of available bytes for.
1059 */
1060uint32_t AudioMixerSinkGetReadable(PAUDMIXSINK pSink)
1061{
1062 AssertPtrReturn(pSink, 0);
1063 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1064 AssertMsg(pSink->enmDir == PDMAUDIODIR_IN, ("%s: Can't read from a non-input sink\n", pSink->pszName));
1065
1066 int rc = RTCritSectEnter(&pSink->CritSect);
1067 AssertRCReturn(rc, 0);
1068
1069 uint32_t cbReadable = 0;
1070
1071 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
1072 {
1073#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF_IN
1074# error "Implement me!"
1075#else
1076 PAUDMIXSTREAM pStreamRecSource = pSink->In.pStreamRecSource;
1077 if (!pStreamRecSource)
1078 {
1079 Log3Func(("[%s] No recording source specified, skipping ...\n", pSink->pszName));
1080 }
1081 else
1082 {
1083 AssertPtr(pStreamRecSource->pConn);
1084 cbReadable = pStreamRecSource->pConn->pfnStreamGetReadable(pStreamRecSource->pConn, pStreamRecSource->pStream);
1085 }
1086#endif
1087 }
1088
1089 Log3Func(("[%s] cbReadable=%RU32\n", pSink->pszName, cbReadable));
1090
1091 int rc2 = RTCritSectLeave(&pSink->CritSect);
1092 AssertRC(rc2);
1093
1094 return cbReadable;
1095}
1096
1097/**
1098 * Returns the sink's current recording source.
1099 *
1100 * @return Mixer stream which currently is set as current recording source, NULL if none is set.
1101 * @param pSink Audio mixer sink to return current recording source for.
1102 */
1103PAUDMIXSTREAM AudioMixerSinkGetRecordingSource(PAUDMIXSINK pSink)
1104{
1105 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1106 int rc = RTCritSectEnter(&pSink->CritSect);
1107 AssertRCReturn(rc, NULL);
1108
1109 AssertMsg(pSink->enmDir == PDMAUDIODIR_IN, ("Specified sink is not an input sink\n"));
1110
1111 PAUDMIXSTREAM pStream = pSink->In.pStreamRecSource;
1112
1113 int rc2 = RTCritSectLeave(&pSink->CritSect);
1114 AssertRC(rc2);
1115
1116 return pStream;
1117}
1118
1119/**
1120 * Returns the amount of bytes ready to be written to a sink since the last call
1121 * to AudioMixerSinkUpdate().
1122 *
1123 * @returns Amount of bytes ready to be written to the sink.
1124 * @param pSink Sink to return number of available bytes for.
1125 */
1126uint32_t AudioMixerSinkGetWritable(PAUDMIXSINK pSink)
1127{
1128 AssertPtrReturn(pSink, 0);
1129 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1130 AssertMsg(pSink->enmDir == PDMAUDIODIR_OUT, ("%s: Can't write to a non-output sink\n", pSink->pszName));
1131
1132 int rc = RTCritSectEnter(&pSink->CritSect);
1133 AssertRCReturn(rc, 0);
1134
1135 uint32_t cbWritable = 0;
1136
1137 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
1138 && !(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
1139 {
1140 cbWritable = AudioMixBufFreeBytes(&pSink->MixBuf);
1141 }
1142
1143 Log3Func(("[%s] cbWritable=%RU32 (%RU64ms)\n",
1144 pSink->pszName, cbWritable, PDMAudioPropsBytesToMilli(&pSink->PCMProps, cbWritable)));
1145
1146 int rc2 = RTCritSectLeave(&pSink->CritSect);
1147 AssertRC(rc2);
1148
1149 return cbWritable;
1150}
1151
1152/**
1153 * Returns the sink's mixing direction.
1154 *
1155 * @returns Mixing direction.
1156 * @param pSink Sink to return direction for.
1157 */
1158PDMAUDIODIR AudioMixerSinkGetDir(PAUDMIXSINK pSink)
1159{
1160 AssertPtrReturn(pSink, PDMAUDIODIR_INVALID);
1161 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1162
1163 /** @todo the sink direction should be static... */
1164 int rc = RTCritSectEnter(&pSink->CritSect);
1165 AssertRCReturn(rc, PDMAUDIODIR_INVALID);
1166
1167 PDMAUDIODIR const enmDir = pSink->enmDir;
1168
1169 int rc2 = RTCritSectLeave(&pSink->CritSect);
1170 AssertRC(rc2);
1171
1172 return enmDir;
1173}
1174
1175/**
1176 * Returns the current status of a mixer sink.
1177 *
1178 * @returns The sink's current status (AUDMIXSINK_STS_XXX).
1179 * @param pSink Mixer sink to return status for.
1180 */
1181uint32_t AudioMixerSinkGetStatus(PAUDMIXSINK pSink)
1182{
1183 if (!pSink)
1184 return AUDMIXSINK_STS_NONE;
1185 AssertPtr(pSink);
1186 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1187
1188 int rc2 = RTCritSectEnter(&pSink->CritSect);
1189 AssertRCReturn(rc2, AUDMIXSINK_STS_NONE);
1190
1191 /* If the dirty flag is set, there is unprocessed data in the sink. */
1192 uint32_t const fStsSink = pSink->fStatus;
1193
1194 rc2 = RTCritSectLeave(&pSink->CritSect);
1195 AssertRC(rc2);
1196
1197 return fStsSink;
1198}
1199
1200/**
1201 * Returns whether the sink is in an active state or not.
1202 *
1203 * @note The pending disable state also counts as active.
1204 *
1205 * @returns True if active, false if not.
1206 * @param pSink Sink to return active state for.
1207 */
1208bool AudioMixerSinkIsActive(PAUDMIXSINK pSink)
1209{
1210 if (!pSink)
1211 return false;
1212 AssertPtr(pSink);
1213 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1214
1215 int rc2 = RTCritSectEnter(&pSink->CritSect);
1216 AssertRCReturn(rc2, false);
1217
1218 const bool fIsActive = pSink->fStatus & AUDMIXSINK_STS_RUNNING;
1219 /* Note: AUDMIXSINK_STS_PENDING_DISABLE implies AUDMIXSINK_STS_RUNNING. */
1220
1221 Log3Func(("[%s] fActive=%RTbool\n", pSink->pszName, fIsActive));
1222
1223 rc2 = RTCritSectLeave(&pSink->CritSect);
1224 AssertRC(rc2);
1225
1226 return fIsActive;
1227}
1228
1229/**
1230 * Reads audio data from a mixer sink.
1231 *
1232 * @returns VBox status code.
1233 * @param pSink Mixer sink to read data from.
1234 * @param pvBuf Buffer where to store the read data.
1235 * @param cbBuf Buffer size (in bytes) where to store the data.
1236 * @param pcbRead Number of bytes read.
1237 */
1238int AudioMixerSinkRead(PAUDMIXSINK pSink, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1239{
1240 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1241 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1242 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1243 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1244 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
1245
1246 int rc = RTCritSectEnter(&pSink->CritSect);
1247 AssertRCReturn(rc, rc);
1248
1249 AssertMsg(pSink->enmDir == PDMAUDIODIR_IN,
1250 ("Can't read from a sink which is not an input sink\n"));
1251
1252 uint32_t cbRead = 0;
1253
1254 /* Flag indicating whether this sink is in a 'clean' state,
1255 * e.g. there is no more data to read from. */
1256 bool fClean = true;
1257
1258 PAUDMIXSTREAM pStreamRecSource = pSink->In.pStreamRecSource;
1259 if (!pStreamRecSource)
1260 {
1261 Log3Func(("[%s] No recording source specified, skipping ...\n", pSink->pszName));
1262 }
1263 else if (!(pStreamRecSource->fStatus & AUDMIXSTREAM_STATUS_ENABLED)) /** @todo r=bird: AUDMIXSTREAM_STATUS_CAN_READ ?*/
1264 {
1265 Log3Func(("[%s] Stream '%s' disabled, skipping ...\n", pSink->pszName, pStreamRecSource->pszName));
1266 }
1267 else
1268 {
1269 uint32_t cbToRead = cbBuf;
1270 while (cbToRead)
1271 {
1272 uint32_t cbReadStrm;
1273 AssertPtr(pStreamRecSource->pConn);
1274#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF_IN
1275# error "Implement me!"
1276#else
1277 rc = pStreamRecSource->pConn->pfnStreamRead(pStreamRecSource->pConn, pStreamRecSource->pStream,
1278 (uint8_t *)pvBuf + cbRead, cbToRead, &cbReadStrm);
1279#endif
1280 if (RT_FAILURE(rc))
1281 LogFunc(("[%s] Failed reading from stream '%s': %Rrc\n", pSink->pszName, pStreamRecSource->pszName, rc));
1282
1283 Log3Func(("[%s] Stream '%s': Read %RU32 bytes\n", pSink->pszName, pStreamRecSource->pszName, cbReadStrm));
1284
1285 if ( RT_FAILURE(rc)
1286 || !cbReadStrm)
1287 break;
1288
1289 AssertBreakStmt(cbReadStrm <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
1290 cbToRead -= cbReadStrm;
1291 cbRead += cbReadStrm;
1292 Assert(cbRead <= cbBuf);
1293 }
1294
1295 uint32_t cbReadable = pStreamRecSource->pConn->pfnStreamGetReadable(pStreamRecSource->pConn, pStreamRecSource->pStream);
1296
1297 /* Still some data available? Then sink is not clean (yet). */
1298 if (cbReadable)
1299 fClean = false;
1300
1301 if (RT_SUCCESS(rc))
1302 {
1303 if (fClean)
1304 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1305
1306 /* Update our last read time stamp. */
1307 pSink->tsLastReadWrittenNs = RTTimeNanoTS();
1308
1309 if (pSink->pParent->fFlags & AUDMIXER_FLAGS_DEBUG)
1310 {
1311 int rc2 = AudioHlpFileWrite(pSink->Dbg.pFile, pvBuf, cbRead, 0 /* fFlags */);
1312 AssertRC(rc2);
1313 }
1314 }
1315 }
1316 *pcbRead = cbRead;
1317
1318#ifdef LOG_ENABLED
1319 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
1320#endif
1321 Log2Func(("[%s] cbRead=%RU32, fClean=%RTbool, fStatus=%s, rc=%Rrc\n",
1322 pSink->pszName, cbRead, fClean, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus), rc));
1323
1324 RTCritSectLeave(&pSink->CritSect);
1325 return rc;
1326}
1327
1328/**
1329 * Removes a mixer stream from a mixer sink, internal version.
1330 *
1331 * @returns VBox status code.
1332 * @param pSink Sink to remove mixer stream from.
1333 * @param pStream Stream to remove.
1334 */
1335static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1336{
1337 AssertPtrReturn(pSink, VERR_INVALID_PARAMETER);
1338 if ( !pStream
1339 || !pStream->pSink) /* Not part of a sink anymore? */
1340 {
1341 return VERR_NOT_FOUND;
1342 }
1343
1344 AssertMsgReturn(pStream->pSink == pSink, ("Stream '%s' is not part of sink '%s'\n",
1345 pStream->pszName, pSink->pszName), VERR_NOT_FOUND);
1346
1347 LogFlowFunc(("[%s] (Stream = %s), cStreams=%RU8\n",
1348 pSink->pszName, pStream->pStream->szName, pSink->cStreams));
1349
1350 /* Remove stream from sink. */
1351 RTListNodeRemove(&pStream->Node);
1352
1353 int rc = VINF_SUCCESS;
1354
1355 if (pSink->enmDir == PDMAUDIODIR_IN)
1356 {
1357 /* Make sure to also un-set the recording source if this stream was set
1358 * as the recording source before. */
1359 if (pStream == pSink->In.pStreamRecSource)
1360 rc = audioMixerSinkSetRecSourceInternal(pSink, NULL);
1361 }
1362
1363 /* Set sink to NULL so that we know we're not part of any sink anymore. */
1364 pStream->pSink = NULL;
1365
1366 return rc;
1367}
1368
1369/**
1370 * Removes a mixer stream from a mixer sink.
1371 *
1372 * @param pSink Sink to remove mixer stream from.
1373 * @param pStream Stream to remove.
1374 */
1375void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1376{
1377 AssertPtr(pSink);
1378 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1379 if (pStream)
1380 Assert(pStream->uMagic == AUDMIXSTREAM_MAGIC);
1381
1382 int rc2 = RTCritSectEnter(&pSink->CritSect);
1383 AssertRC(rc2);
1384
1385 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pStream);
1386 if (RT_SUCCESS(rc2))
1387 {
1388 Assert(pSink->cStreams);
1389 pSink->cStreams--;
1390 }
1391
1392 rc2 = RTCritSectLeave(&pSink->CritSect);
1393 AssertRC(rc2);
1394}
1395
1396/**
1397 * Removes all attached streams from a given sink.
1398 *
1399 * @param pSink Sink to remove attached streams from.
1400 */
1401static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink)
1402{
1403 if (!pSink)
1404 return;
1405
1406 LogFunc(("%s\n", pSink->pszName));
1407
1408 PAUDMIXSTREAM pStream, pStreamNext;
1409 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
1410 audioMixerSinkRemoveStreamInternal(pSink, pStream);
1411}
1412
1413/**
1414 * Removes all attached streams from a given sink.
1415 *
1416 * @param pSink Sink to remove attached streams from.
1417 */
1418void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
1419{
1420 if (!pSink)
1421 return;
1422
1423 int rc2 = RTCritSectEnter(&pSink->CritSect);
1424 AssertRC(rc2);
1425
1426 audioMixerSinkRemoveAllStreamsInternal(pSink);
1427
1428 pSink->cStreams = 0;
1429
1430 rc2 = RTCritSectLeave(&pSink->CritSect);
1431 AssertRC(rc2);
1432}
1433
1434/**
1435 * Resets the sink's state.
1436 *
1437 * @param pSink Sink to reset.
1438 */
1439static void audioMixerSinkReset(PAUDMIXSINK pSink)
1440{
1441 if (!pSink)
1442 return;
1443
1444 LogFunc(("[%s]\n", pSink->pszName));
1445
1446 AudioMixBufReset(&pSink->MixBuf);
1447
1448 /* Update last updated timestamp. */
1449 pSink->tsLastUpdatedMs = 0;
1450
1451 /* Reset status. */
1452 pSink->fStatus = AUDMIXSINK_STS_NONE;
1453}
1454
1455/**
1456 * Resets a sink. This will immediately stop all processing.
1457 *
1458 * @param pSink Sink to reset.
1459 */
1460void AudioMixerSinkReset(PAUDMIXSINK pSink)
1461{
1462 if (!pSink)
1463 return;
1464
1465 int rc2 = RTCritSectEnter(&pSink->CritSect);
1466 AssertRC(rc2);
1467
1468 LogFlowFunc(("[%s]\n", pSink->pszName));
1469
1470 audioMixerSinkReset(pSink);
1471
1472 rc2 = RTCritSectLeave(&pSink->CritSect);
1473 AssertRC(rc2);
1474}
1475
1476/**
1477 * Sets the audio format of a mixer sink.
1478 *
1479 * @returns VBox status code.
1480 * @param pSink The sink to set audio format for.
1481 * @param pPCMProps Audio format (PCM properties) to set.
1482 */
1483int AudioMixerSinkSetFormat(PAUDMIXSINK pSink, PCPDMAUDIOPCMPROPS pPCMProps)
1484{
1485 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1486 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1487 AssertPtrReturn(pPCMProps, VERR_INVALID_POINTER);
1488 AssertReturn(AudioHlpPcmPropsAreValid(pPCMProps), VERR_INVALID_PARAMETER);
1489
1490 int rc = RTCritSectEnter(&pSink->CritSect);
1491 if (RT_FAILURE(rc))
1492 return rc;
1493
1494 if (PDMAudioPropsAreEqual(&pSink->PCMProps, pPCMProps)) /* Bail out early if PCM properties are equal. */
1495 {
1496 rc = RTCritSectLeave(&pSink->CritSect);
1497 AssertRC(rc);
1498
1499 return rc;
1500 }
1501
1502 if (pSink->PCMProps.uHz)
1503 LogFlowFunc(("[%s] Old format: %u bit, %RU8 channels, %RU32Hz\n", pSink->pszName,
1504 PDMAudioPropsSampleBits(&pSink->PCMProps), PDMAudioPropsChannels(&pSink->PCMProps), pSink->PCMProps.uHz));
1505
1506 memcpy(&pSink->PCMProps, pPCMProps, sizeof(PDMAUDIOPCMPROPS));
1507
1508 LogFlowFunc(("[%s] New format %u bit, %RU8 channels, %RU32Hz\n", pSink->pszName, PDMAudioPropsSampleBits(&pSink->PCMProps),
1509 PDMAudioPropsChannels(&pSink->PCMProps), pSink->PCMProps.uHz));
1510
1511 /* Also update the sink's mixing buffer format. */
1512 AudioMixBufDestroy(&pSink->MixBuf);
1513 rc = AudioMixBufInit(&pSink->MixBuf, pSink->pszName, &pSink->PCMProps,
1514 PDMAudioPropsMilliToFrames(&pSink->PCMProps, 100 /*ms*/)); /** @todo Make this configurable? */
1515 if (RT_SUCCESS(rc))
1516 {
1517 PAUDMIXSTREAM pStream;
1518 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
1519 {
1520 /** @todo Invalidate mix buffers! */
1521 }
1522 }
1523
1524 if ( RT_SUCCESS(rc)
1525 && (pSink->pParent->fFlags & AUDMIXER_FLAGS_DEBUG))
1526 {
1527 AudioHlpFileClose(pSink->Dbg.pFile);
1528
1529 char szName[64];
1530 RTStrPrintf(szName, sizeof(szName), "MixerSink-%s", pSink->pszName);
1531
1532 char szFile[RTPATH_MAX];
1533 int rc2 = AudioHlpFileNameGet(szFile, RT_ELEMENTS(szFile), NULL /* Use temporary directory */, szName,
1534 0 /* Instance */, AUDIOHLPFILETYPE_WAV, AUDIOHLPFILENAME_FLAGS_NONE);
1535 if (RT_SUCCESS(rc2))
1536 {
1537 rc2 = AudioHlpFileCreate(AUDIOHLPFILETYPE_WAV, szFile, AUDIOHLPFILE_FLAGS_NONE, &pSink->Dbg.pFile);
1538 if (RT_SUCCESS(rc2))
1539 rc2 = AudioHlpFileOpen(pSink->Dbg.pFile, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS, &pSink->PCMProps);
1540 }
1541 }
1542
1543 int rc2 = RTCritSectLeave(&pSink->CritSect);
1544 AssertRC(rc2);
1545
1546 LogFlowFuncLeaveRC(rc);
1547 return rc;
1548}
1549
1550/**
1551 * Set the current recording source of an input mixer sink, internal version.
1552 *
1553 * @returns VBox status code.
1554 * @param pSink Input mixer sink to set recording source for.
1555 * @param pStream Mixer stream to set as current recording source. Must be an input stream.
1556 * Specify NULL to un-set the current recording source.
1557 */
1558static int audioMixerSinkSetRecSourceInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1559{
1560 AssertMsg(pSink->enmDir == PDMAUDIODIR_IN, ("Specified sink is not an input sink\n"));
1561
1562 int rc;
1563
1564 /*
1565 * Warning: Do *not* use pfnConn->pfnEnable() for enabling/disabling streams here, as this will unconditionally (re-)enable
1566 * streams, which would violate / run against the (global) VM settings. See @bugref{9882}.
1567 */
1568
1569 /* Get pointers of current recording source to make code easier to read below. */
1570 PAUDMIXSTREAM pCurRecSrc = pSink->In.pStreamRecSource; /* Can be NULL. */
1571 PPDMIAUDIOCONNECTOR pCurRecSrcConn = NULL;
1572 PPDMAUDIOSTREAM pCurRecSrcStream = NULL;
1573
1574 if (pCurRecSrc) /* First, disable old recording source, if any is set. */
1575 {
1576 pCurRecSrcConn = pSink->In.pStreamRecSource->pConn;
1577 AssertPtrReturn(pCurRecSrcConn, VERR_INVALID_POINTER);
1578 pCurRecSrcStream = pCurRecSrc->pStream;
1579 AssertPtrReturn(pCurRecSrcStream, VERR_INVALID_POINTER);
1580
1581 rc = pCurRecSrcConn->pfnStreamControl(pCurRecSrcConn, pCurRecSrcStream, PDMAUDIOSTREAMCMD_DISABLE);
1582 }
1583 else
1584 rc = VINF_SUCCESS;
1585
1586 if (RT_SUCCESS(rc))
1587 {
1588 if (pStream)
1589 {
1590 AssertPtr(pStream->pStream);
1591 AssertMsg(pStream->pStream->enmDir == PDMAUDIODIR_IN, ("Specified stream is not an input stream\n"));
1592 AssertPtr(pStream->pConn);
1593 rc = pStream->pConn->pfnStreamControl(pStream->pConn, pStream->pStream, PDMAUDIOSTREAMCMD_ENABLE);
1594 if (RT_SUCCESS(rc))
1595 {
1596 pCurRecSrc = pStream;
1597 }
1598 else if (pCurRecSrc) /* Stay with the current recording source (if any) and re-enable it. */
1599 {
1600 rc = pCurRecSrcConn->pfnStreamControl(pCurRecSrcConn, pCurRecSrcStream, PDMAUDIOSTREAMCMD_ENABLE);
1601 }
1602 }
1603 else
1604 pCurRecSrc = NULL; /* Unsetting, see audioMixerSinkRemoveStreamInternal. */
1605 }
1606
1607 /* Invalidate pointers. */
1608 pSink->In.pStreamRecSource = pCurRecSrc;
1609
1610 LogFunc(("[%s] Recording source is now '%s', rc=%Rrc\n",
1611 pSink->pszName, pSink->In.pStreamRecSource ? pSink->In.pStreamRecSource->pszName : "<None>", rc));
1612
1613 if (RT_SUCCESS(rc))
1614 LogRel(("Audio Mixer: Setting recording source of sink '%s' to '%s'\n",
1615 pSink->pszName, pSink->In.pStreamRecSource ? pSink->In.pStreamRecSource->pszName : "<None>"));
1616 else if (rc != VERR_AUDIO_STREAM_NOT_READY)
1617 LogRel(("Audio Mixer: Setting recording source of sink '%s' to '%s' failed with %Rrc\n",
1618 pSink->pszName, pSink->In.pStreamRecSource ? pSink->In.pStreamRecSource->pszName : "<None>", rc));
1619
1620 return rc;
1621}
1622
1623/**
1624 * Set the current recording source of an input mixer sink.
1625 *
1626 * @returns VBox status code.
1627 * @param pSink Input mixer sink to set recording source for.
1628 * @param pStream Mixer stream to set as current recording source. Must be an input stream.
1629 * Set to NULL to un-set the current recording source.
1630 */
1631int AudioMixerSinkSetRecordingSource(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1632{
1633 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1634 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1635 if (pStream)
1636 Assert(pStream->uMagic == AUDMIXSTREAM_MAGIC);
1637
1638 int rc = RTCritSectEnter(&pSink->CritSect);
1639 AssertRCReturn(rc, rc);
1640
1641 rc = audioMixerSinkSetRecSourceInternal(pSink, pStream);
1642
1643 int rc2 = RTCritSectLeave(&pSink->CritSect);
1644 AssertRC(rc2);
1645
1646 return rc;
1647}
1648
1649/**
1650 * Sets the volume of an individual sink.
1651 *
1652 * @returns VBox status code.
1653 * @param pSink Sink to set volume for.
1654 * @param pVol Volume to set.
1655 */
1656int AudioMixerSinkSetVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
1657{
1658 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1659 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
1660 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
1661
1662 int rc = RTCritSectEnter(&pSink->CritSect);
1663 AssertRCReturn(rc, rc);
1664
1665 memcpy(&pSink->Volume, pVol, sizeof(PDMAUDIOVOLUME));
1666
1667 LogRel2(("Audio Mixer: Setting volume of sink '%s' to %RU8/%RU8 (%s)\n",
1668 pSink->pszName, pVol->uLeft, pVol->uRight, pVol->fMuted ? "Muted" : "Unmuted"));
1669
1670 AssertPtr(pSink->pParent);
1671 rc = audioMixerSinkUpdateVolume(pSink, &pSink->pParent->VolMaster);
1672
1673 int rc2 = RTCritSectLeave(&pSink->CritSect);
1674 AssertRC(rc2);
1675
1676 return rc;
1677}
1678
1679/**
1680 * Updates an input mixer sink.
1681 *
1682 * @returns VBox status code.
1683 * @param pSink Mixer sink to update.
1684 */
1685static int audioMixerSinkUpdateInput(PAUDMIXSINK pSink)
1686{
1687 /*
1688 * Warning! We currently do _not_ use the mixing buffer for input streams!
1689 * Warning! We currently do _not_ use the mixing buffer for input streams!
1690 * Warning! We currently do _not_ use the mixing buffer for input streams!
1691 */
1692
1693 /*
1694 * Skip input sinks without a recoring source.
1695 */
1696 if (pSink->In.pStreamRecSource == NULL)
1697 return VINF_SUCCESS;
1698
1699 /*
1700 * Update each mixing sink stream's status.
1701 */
1702 PAUDMIXSTREAM pMixStream;
1703 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1704 {
1705 int rc2 = audioMixerStreamUpdateStatus(pMixStream);
1706 AssertRC(rc2);
1707 }
1708
1709 /*
1710 * Iterate and do capture on the recording source. We ignore all other streams.
1711 */
1712 int rc = VINF_SUCCESS; /* not sure if error propagation is worth it... */
1713#if 1
1714 pMixStream = pSink->In.pStreamRecSource;
1715#else
1716 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1717#endif
1718 {
1719 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1720 {
1721 uint32_t cFramesCaptured = 0;
1722 int rc2 = pMixStream->pConn->pfnStreamIterate(pMixStream->pConn, pMixStream->pStream);
1723 if (RT_SUCCESS(rc2))
1724 {
1725 /** @todo r=bird: Check for AUDMIXSTREAM_STATUS_CAN_READ? */
1726 rc2 = pMixStream->pConn->pfnStreamCapture(pMixStream->pConn, pMixStream->pStream, &cFramesCaptured);
1727 if (RT_SUCCESS(rc2))
1728 {
1729 if (cFramesCaptured)
1730 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1731 }
1732 else
1733 {
1734 LogFunc(("%s: Failed capturing stream '%s', rc=%Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc2));
1735 if (RT_SUCCESS(rc))
1736 rc = rc2;
1737 }
1738 }
1739 else if (RT_SUCCESS(rc))
1740 rc = rc2;
1741 Log3Func(("%s: cFramesCaptured=%RU32 (rc2=%Rrc)\n", pMixStream->pStream->szName, cFramesCaptured, rc2));
1742 }
1743 }
1744
1745 /* Update last updated timestamp. */
1746 pSink->tsLastUpdatedMs = RTTimeMilliTS();
1747
1748 Assert(!(pSink->fStatus & AUDMIXSINK_STS_DRAINING));
1749 return rc;
1750}
1751
1752
1753/**
1754 * Helper for audioMixerSinkUpdateOutput that determins now many frames it
1755 * can transfer from the sink's mixer buffer and to the drivers.
1756 *
1757 * This also updates the mixer stream status, which may involve stream re-inits.
1758 *
1759 * @returns Number of frames.
1760 * @param pSink The sink.
1761 * @param pcWritableStreams Where to return the number of writable streams.
1762 */
1763static uint32_t audioMixerSinkUpdateOutputCalcFramesToRead(PAUDMIXSINK pSink, uint32_t *pcWritableStreams)
1764{
1765 uint32_t cFramesToRead = AudioMixBufLive(&pSink->MixBuf); /* (to read from the mixing buffer) */
1766 uint32_t cWritableStreams = 0;
1767 PAUDMIXSTREAM pMixStream;
1768 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1769 {
1770#if 0 /** @todo this conceptually makes sense, but may mess up the pending-disable logic ... */
1771 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1772 pConn->pfnStreamIterate(pConn, pStream);
1773#endif
1774
1775 int rc2 = audioMixerStreamUpdateStatus(pMixStream);
1776 AssertRC(rc2);
1777
1778 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_CAN_WRITE)
1779 {
1780 uint32_t const cbWritable = pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream);
1781 uint32_t cFrames = PDMAudioPropsBytesToFrames(&pMixStream->pStream->Props, cbWritable);
1782 pMixStream->cFramesLastAvail = cFrames;
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 && !pMixStream->fUnreliable)
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
1800 *pcWritableStreams = cWritableStreams;
1801 return cFramesToRead;
1802}
1803
1804
1805/**
1806 * Updates an output mixer sink.
1807 *
1808 * @returns VBox status code.
1809 * @param pSink Mixer sink to update.
1810 */
1811static int audioMixerSinkUpdateOutput(PAUDMIXSINK pSink)
1812{
1813 PAUDMIXSTREAM pMixStream;
1814 Assert(!(pSink->fStatus & AUDMIXSINK_STS_DRAINED_MIXBUF) || AudioMixBufUsed(&pSink->MixBuf) == 0);
1815
1816 /*
1817 * Update each mixing sink stream's status and check how much we can
1818 * write into them.
1819 *
1820 * We're currently using the minimum size of all streams, however this
1821 * isn't a smart approach as it means one disfunctional stream can block
1822 * working ones. So, if we end up with zero frames and a full mixer
1823 * buffer we'll disregard the stream that accept the smallest amount and
1824 * try again.
1825 */
1826 uint32_t cReliableStreams = 0;
1827 uint32_t cMarkedUnreliable = 0;
1828 uint32_t cWritableStreams = 0;
1829 uint32_t cFramesToRead = audioMixerSinkUpdateOutputCalcFramesToRead(pSink, &cWritableStreams);
1830 if ( cFramesToRead != 0
1831 || cWritableStreams <= 1
1832 || AudioMixBufFree(&pSink->MixBuf) > 2)
1833 Log3Func(("%s: cLiveFrames=%#x cFramesToRead=%#x cWritableStreams=%#x\n", pSink->pszName,
1834 AudioMixBufLive(&pSink->MixBuf), cFramesToRead, cWritableStreams));
1835 else
1836 {
1837 Log3Func(("%s: MixBuf is full but one or more streams only want zero frames. Try disregarding those...\n", pSink->pszName));
1838 PAUDMIXSTREAM pMixStreamMin = NULL;
1839 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1840 {
1841 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_CAN_WRITE)
1842 {
1843 if (!pMixStream->fUnreliable)
1844 {
1845 if (pMixStream->cFramesLastAvail == 0)
1846 {
1847 cMarkedUnreliable++;
1848 pMixStream->fUnreliable = true;
1849 Log3Func(("%s: Marked '%s' as unreliable.\n", pSink->pszName, pMixStream->pszName));
1850 pMixStreamMin = pMixStream;
1851 }
1852 else
1853 {
1854 if (!pMixStreamMin || pMixStream->cFramesLastAvail < pMixStreamMin->cFramesLastAvail)
1855 pMixStreamMin = pMixStream;
1856 cReliableStreams++;
1857 }
1858 }
1859 }
1860 }
1861
1862 if (cMarkedUnreliable == 0 && cReliableStreams > 1 && pMixStreamMin != NULL)
1863 {
1864 cReliableStreams--;
1865 cMarkedUnreliable++;
1866 pMixStreamMin->fUnreliable = true;
1867 Log3Func(("%s: Marked '%s' as unreliable (%u frames).\n",
1868 pSink->pszName, pMixStreamMin->pszName, pMixStreamMin->cFramesLastAvail));
1869 }
1870
1871 if (cMarkedUnreliable > 0)
1872 {
1873 cWritableStreams = 0;
1874 cFramesToRead = audioMixerSinkUpdateOutputCalcFramesToRead(pSink, &cWritableStreams);
1875 }
1876
1877 Log3Func(("%s: cLiveFrames=%#x cFramesToRead=%#x cWritableStreams=%#x cMarkedUnreliable=%#x cReliableStreams=%#x\n",
1878 pSink->pszName, AudioMixBufLive(&pSink->MixBuf), cFramesToRead,
1879 cWritableStreams, cMarkedUnreliable, cReliableStreams));
1880 }
1881
1882 if (cWritableStreams > 0)
1883 {
1884 if (cFramesToRead > 0)
1885 {
1886 /*
1887 * For each of the enabled streams, convert cFramesToRead frames from
1888 * the mixing buffer and write that to the downstream driver.
1889 */
1890 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1891 {
1892 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_CAN_WRITE)
1893 {
1894 uint32_t offSrcFrame = 0;
1895 do
1896 {
1897 /* Convert a chunk from the mixer buffer. */
1898/*#define ELECTRIC_PEEK_BUFFER*/ /* if buffer code is misbehaving, enable this to catch overflows. */
1899#ifndef ELECTRIC_PEEK_BUFFER
1900 union
1901 {
1902 uint8_t ab[8192];
1903 uint64_t au64[8192 / sizeof(uint64_t)]; /* Use uint64_t to ensure good alignment. */
1904 } Buf;
1905 void * const pvBuf = &Buf;
1906 uint32_t const cbBuf = sizeof(Buf);
1907#else
1908 uint32_t const cbBuf = 0x2000 - 16;
1909 void * const pvBuf = RTMemEfAlloc(cbBuf, RTMEM_TAG, RT_SRC_POS);
1910#endif
1911 uint32_t cbDstPeeked = cbBuf;
1912 uint32_t cSrcFramesPeeked = cFramesToRead - offSrcFrame;
1913 AudioMixBufPeek(&pSink->MixBuf, offSrcFrame, cSrcFramesPeeked, &cSrcFramesPeeked,
1914 &pMixStream->PeekState, pvBuf, cbBuf, &cbDstPeeked);
1915 offSrcFrame += cSrcFramesPeeked;
1916
1917 /* Write it to the backend. Since've checked that there is buffer
1918 space available, this should always write the whole buffer unless
1919 it's an unreliable stream. */
1920 uint32_t cbDstWritten = 0;
1921 int rc2 = pMixStream->pConn->pfnStreamPlay(pMixStream->pConn, pMixStream->pStream,
1922 pvBuf, cbDstPeeked, &cbDstWritten);
1923 Log3Func(("%s: %#x L %#x => %#x bytes; wrote %#x rc2=%Rrc %s\n", pSink->pszName, offSrcFrame,
1924 cSrcFramesPeeked - cSrcFramesPeeked, cbDstPeeked, cbDstWritten, rc2, pMixStream->pszName));
1925#ifdef ELECTRIC_PEEK_BUFFER
1926 RTMemEfFree(pvBuf, RT_SRC_POS);
1927#endif
1928 if (RT_SUCCESS(rc2))
1929 AssertLogRelMsg(cbDstWritten == cbDstPeeked || pMixStream->fUnreliable,
1930 ("cbDstWritten=%#x cbDstPeeked=%#x - (sink '%s')\n",
1931 cbDstWritten, cbDstPeeked, pSink->pszName));
1932 else if (rc2 == VERR_AUDIO_STREAM_NOT_READY)
1933 {
1934 LogRel2(("Audio Mixer: '%s' (sink '%s'): Stream not ready - skipping.\n",
1935 pMixStream->pszName, pSink->pszName));
1936 break; /* must've changed status, stop processing */
1937 }
1938 else
1939 {
1940 Assert(rc2 != VERR_BUFFER_OVERFLOW);
1941 LogRel2(("Audio Mixer: Writing to mixer stream '%s' (sink '%s') failed, rc=%Rrc\n",
1942 pMixStream->pszName, pSink->pszName, rc2));
1943 break;
1944 }
1945 } while (offSrcFrame < cFramesToRead);
1946 }
1947 }
1948
1949 AudioMixBufAdvance(&pSink->MixBuf, cFramesToRead);
1950 }
1951
1952 /*
1953 * Update the dirty flag for what it's worth.
1954 */
1955 if (AudioMixBufUsed(&pSink->MixBuf) > 0)
1956 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1957 else
1958 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1959 }
1960 else
1961 {
1962 /*
1963 * If no writable streams, just drop the mixer buffer content.
1964 */
1965 AudioMixBufDrop(&pSink->MixBuf);
1966 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1967 }
1968
1969 /*
1970 * Iterate buffers.
1971 */
1972 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1973 {
1974 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
1975 pMixStream->pConn->pfnStreamIterate(pMixStream->pConn, pMixStream->pStream);
1976 }
1977
1978 /* Update last updated timestamp. */
1979 uint64_t const nsNow = RTTimeNanoTS();
1980 pSink->tsLastUpdatedMs = nsNow / RT_NS_1MS;
1981
1982 /*
1983 * Deal with pending disable.
1984 * We reset the sink when all streams have been disabled.
1985 */
1986 if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
1987 { /* likely, till we get to the end */ }
1988 else if (nsNow <= pSink->nsDrainDeadline)
1989 {
1990 /* Have we drained the mixbuf now? If so, update status and send drain
1991 command to streams. (As mentioned elsewhere we don't want to confuse
1992 driver code by sending drain command while there is still data to write.) */
1993 Assert((pSink->fStatus & AUDMIXSINK_STS_DIRTY) == (AudioMixBufUsed(&pSink->MixBuf) > 0 ? AUDMIXSINK_STS_DIRTY : 0));
1994 if ((pSink->fStatus & (AUDMIXSINK_STS_DRAINED_MIXBUF | AUDMIXSINK_STS_DIRTY)) == 0)
1995 {
1996 LogFunc(("Sink '%s': Setting AUDMIXSINK_STS_DRAINED_MIXBUF and sending drain command to streams (after %RU64 ns).\n",
1997 pSink->pszName, nsNow - pSink->nsDrainStarted));
1998 pSink->fStatus |= AUDMIXSINK_STS_DRAINED_MIXBUF;
1999
2000 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
2001 {
2002 pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, PDMAUDIOSTREAMCMD_DRAIN);
2003 }
2004 }
2005
2006 /* Check if all streams has stopped, and if so we stop the sink. */
2007 uint32_t const cStreams = pSink->cStreams;
2008 uint32_t cStreamsDisabled = pSink->cStreams;
2009 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
2010 {
2011 if (pMixStream->fStatus & AUDMIXSTREAM_STATUS_ENABLED)
2012 {
2013 PDMAUDIOSTREAMSTATE const enmState = pMixStream->pConn->pfnStreamGetState(pMixStream->pConn, pMixStream->pStream);
2014 if (enmState >= PDMAUDIOSTREAMSTATE_ENABLED)
2015 cStreamsDisabled--;
2016 }
2017 }
2018
2019 if (cStreamsDisabled != cStreams)
2020 Log3Func(("Sink '%s': %u out of %u streams disabled (after %RU64 ns).\n",
2021 pSink->pszName, cStreamsDisabled, cStreams, nsNow - pSink->nsDrainStarted));
2022 else
2023 {
2024 LogFunc(("Sink '%s': All %u streams disabled. Drain done after %RU64 ns.\n",
2025 pSink->pszName, cStreamsDisabled, nsNow - pSink->nsDrainStarted));
2026 audioMixerSinkReset(pSink); /* clears the status */
2027 }
2028 }
2029 else
2030 {
2031 /* Draining timed out. Just do an instant stop. */
2032 LogFunc(("Sink '%s': pending disable timed out after %RU64 ns!\n", pSink->pszName, nsNow - pSink->nsDrainStarted));
2033 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
2034 {
2035 pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, PDMAUDIOSTREAMCMD_DISABLE);
2036 }
2037 audioMixerSinkReset(pSink); /* clears the status */
2038 }
2039
2040 return VINF_SUCCESS;
2041}
2042
2043/**
2044 * Updates (invalidates) a mixer sink.
2045 *
2046 * @returns VBox status code.
2047 * @param pSink Mixer sink to update.
2048 */
2049int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
2050{
2051 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2052 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2053 int rc = RTCritSectEnter(&pSink->CritSect);
2054 AssertRCReturn(rc, rc);
2055
2056#ifdef LOG_ENABLED
2057 char szStatus[AUDIOMIXERSINK_STATUS_STR_MAX];
2058#endif
2059 Log3Func(("[%s] fStatus=%s\n", pSink->pszName, dbgAudioMixerSinkStatusToStr(pSink->fStatus, szStatus)));
2060
2061 /* Only process running sinks. */
2062 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
2063 {
2064 /* Do separate processing for input and output sinks. */
2065 if (pSink->enmDir == PDMAUDIODIR_OUT)
2066 rc = audioMixerSinkUpdateOutput(pSink);
2067 else if (pSink->enmDir == PDMAUDIODIR_IN)
2068 rc = audioMixerSinkUpdateInput(pSink);
2069 else
2070 AssertFailed();
2071 }
2072 else
2073 rc = VINF_SUCCESS; /* disabled */
2074
2075 RTCritSectLeave(&pSink->CritSect);
2076 return rc;
2077}
2078
2079
2080/**
2081 * @callback_method_impl{FNRTTHREAD, Audio Mixer Sink asynchronous I/O thread}
2082 */
2083static DECLCALLBACK(int) audioMixerSinkAsyncIoThread(RTTHREAD hThreadSelf, void *pvUser)
2084{
2085 PAUDMIXSINK pSink = (PAUDMIXSINK)pvUser;
2086 AssertPtr(pSink);
2087 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2088 RT_NOREF(hThreadSelf);
2089
2090 /*
2091 * The run loop.
2092 */
2093 LogFlowFunc(("%s: Entering run loop...\n", pSink->pszName));
2094 while (!pSink->AIO.fShutdown)
2095 {
2096 RTMSINTERVAL cMsSleep = RT_INDEFINITE_WAIT;
2097
2098 RTCritSectEnter(&pSink->CritSect);
2099 if (pSink->fStatus & (AUDMIXSINK_STS_RUNNING | AUDMIXSINK_STS_DRAINING))
2100 {
2101 /*
2102 * Before doing jobs, always update input sinks.
2103 */
2104 if (pSink->enmDir == PDMAUDIODIR_IN)
2105 audioMixerSinkUpdateInput(pSink);
2106
2107 /*
2108 * Do the device specific updating.
2109 */
2110 uintptr_t const cUpdateJobs = RT_MIN(pSink->AIO.cUpdateJobs, RT_ELEMENTS(pSink->AIO.aUpdateJobs));
2111 for (uintptr_t iJob = 0; iJob < cUpdateJobs; iJob++)
2112 pSink->AIO.aUpdateJobs[iJob].pfnUpdate(pSink->AIO.pDevIns, pSink, pSink->AIO.aUpdateJobs[iJob].pvUser);
2113
2114 /*
2115 * Update output sinks after the updating.
2116 */
2117 if (pSink->enmDir == PDMAUDIODIR_OUT)
2118 audioMixerSinkUpdateOutput(pSink);
2119
2120 /*
2121 * If we're in draining mode, we use the smallest typical interval of the
2122 * jobs for the next wait as we're unlikly to be woken up again by any
2123 * DMA timer as it has normally stopped running at this point.
2124 */
2125 if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
2126 { /* likely */ }
2127 else
2128 {
2129 /** @todo Also do some kind of timeout here and do a forced stream disable w/o
2130 * any draining if we exceed it. */
2131 cMsSleep = pSink->AIO.cMsMinTypicalInterval;
2132 }
2133
2134 }
2135 RTCritSectLeave(&pSink->CritSect);
2136
2137 /*
2138 * Now block till we're signalled or
2139 */
2140 if (!pSink->AIO.fShutdown)
2141 {
2142 int rc = RTSemEventWait(pSink->AIO.hEvent, cMsSleep);
2143 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%s: RTSemEventWait -> %Rrc\n", pSink->pszName, rc), rc);
2144 }
2145 }
2146
2147 LogFlowFunc(("%s: returnining normally.\n", pSink->pszName));
2148 return VINF_SUCCESS;
2149}
2150
2151
2152/**
2153 * Adds an AIO update job to the sink.
2154 *
2155 * @returns VBox status code.
2156 * @retval VERR_ALREADY_EXISTS if already registered job with same @a pvUser
2157 * and @a pfnUpdate.
2158 *
2159 * @param pSink The mixer sink to remove the AIO job from.
2160 * @param pfnUpdate The update callback for the job.
2161 * @param pvUser The user parameter to pass to @a pfnUpdate. This should
2162 * identify the job unique together with @a pfnUpdate.
2163 * @param cMsTypicalInterval A typical interval between jobs in milliseconds.
2164 * This is used when draining.
2165 */
2166int AudioMixerSinkAddUpdateJob(PAUDMIXSINK pSink, PFNAUDMIXSINKUPDATE pfnUpdate, void *pvUser, uint32_t cMsTypicalInterval)
2167{
2168 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2169 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2170 int rc = RTCritSectEnter(&pSink->CritSect);
2171 AssertRCReturn(rc, rc);
2172
2173 /*
2174 * Check that the job hasn't already been added.
2175 */
2176 uintptr_t const iEnd = pSink->AIO.cUpdateJobs;
2177 for (uintptr_t i = 0; i < iEnd; i++)
2178 AssertReturnStmt( pvUser != pSink->AIO.aUpdateJobs[i].pvUser
2179 || pfnUpdate != pSink->AIO.aUpdateJobs[i].pfnUpdate,
2180 RTCritSectLeave(&pSink->CritSect),
2181 VERR_ALREADY_EXISTS);
2182
2183 AssertReturnStmt(iEnd < RT_ELEMENTS(pSink->AIO.aUpdateJobs),
2184 RTCritSectLeave(&pSink->CritSect),
2185 VERR_ALREADY_EXISTS);
2186
2187 /*
2188 * Create the thread if not already running or if it stopped.
2189 */
2190/** @todo move this to the sink "enable" code */
2191 if (pSink->AIO.hThread != NIL_RTTHREAD)
2192 {
2193 int rcThread = VINF_SUCCESS;
2194 rc = RTThreadWait(pSink->AIO.hThread, 0, &rcThread);
2195 if (RT_FAILURE_NP(rc))
2196 { /* likely */ }
2197 else
2198 {
2199 LogRel(("Audio: AIO thread for '%s' died? rcThread=%Rrc\n", pSink->pszName, rcThread));
2200 pSink->AIO.hThread = NIL_RTTHREAD;
2201 }
2202 }
2203 if (pSink->AIO.hThread == NIL_RTTHREAD)
2204 {
2205 LogFlowFunc(("%s: Starting AIO thread...\n", pSink->pszName));
2206 if (pSink->AIO.hEvent == NIL_RTSEMEVENT)
2207 {
2208 rc = RTSemEventCreate(&pSink->AIO.hEvent);
2209 AssertRCReturnStmt(rc, RTCritSectLeave(&pSink->CritSect), rc);
2210 }
2211 static uint32_t volatile s_idxThread = 0;
2212 uint32_t idxThread = ASMAtomicIncU32(&s_idxThread);
2213 rc = RTThreadCreateF(&pSink->AIO.hThread, audioMixerSinkAsyncIoThread, pSink, 0 /*cbStack*/, RTTHREADTYPE_IO,
2214 RTTHREADFLAGS_WAITABLE | RTTHREADFLAGS_COM_MTA, "MixAIO-%u", idxThread);
2215 AssertRCReturnStmt(rc, RTCritSectLeave(&pSink->CritSect), rc);
2216 }
2217
2218 /*
2219 * Finally, actually add the job.
2220 */
2221 pSink->AIO.aUpdateJobs[iEnd].pfnUpdate = pfnUpdate;
2222 pSink->AIO.aUpdateJobs[iEnd].pvUser = pvUser;
2223 pSink->AIO.aUpdateJobs[iEnd].cMsTypicalInterval = cMsTypicalInterval;
2224 pSink->AIO.cUpdateJobs = (uint8_t)(iEnd + 1);
2225 if (cMsTypicalInterval < pSink->AIO.cMsMinTypicalInterval)
2226 pSink->AIO.cMsMinTypicalInterval = cMsTypicalInterval;
2227 LogFlowFunc(("%s: [#%zu]: Added pfnUpdate=%p pvUser=%p typically every %u ms (min %u ms)\n",
2228 pSink->pszName, iEnd, pfnUpdate, pvUser, cMsTypicalInterval, pSink->AIO.cMsMinTypicalInterval));
2229
2230 RTCritSectLeave(&pSink->CritSect);
2231 return VINF_SUCCESS;
2232
2233}
2234
2235
2236/**
2237 * Removes an update job previously registered via AudioMixerSinkAddUpdateJob().
2238 *
2239 * @returns VBox status code.
2240 * @retval VERR_NOT_FOUND if not found.
2241 *
2242 * @param pSink The mixer sink to remove the AIO job from.
2243 * @param pfnUpdate The update callback of the job.
2244 * @param pvUser The user parameter identifying the job.
2245 */
2246int AudioMixerSinkRemoveUpdateJob(PAUDMIXSINK pSink, PFNAUDMIXSINKUPDATE pfnUpdate, void *pvUser)
2247{
2248 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2249 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2250 int rc = RTCritSectEnter(&pSink->CritSect);
2251 AssertRCReturn(rc, rc);
2252
2253 rc = VERR_NOT_FOUND;
2254 for (uintptr_t iJob = 0; iJob < pSink->AIO.cUpdateJobs; iJob++)
2255 if ( pvUser == pSink->AIO.aUpdateJobs[iJob].pvUser
2256 && pfnUpdate == pSink->AIO.aUpdateJobs[iJob].pfnUpdate)
2257 {
2258 pSink->AIO.cUpdateJobs--;
2259 if (iJob != pSink->AIO.cUpdateJobs)
2260 memmove(&pSink->AIO.aUpdateJobs[iJob], &pSink->AIO.aUpdateJobs[iJob + 1],
2261 (pSink->AIO.cUpdateJobs - iJob) * sizeof(pSink->AIO.aUpdateJobs[0]));
2262 LogFlowFunc(("%s: [#%zu]: Removed pfnUpdate=%p pvUser=%p => cUpdateJobs=%u\n",
2263 pSink->pszName, iJob, pfnUpdate, pvUser, pSink->AIO.cUpdateJobs));
2264 rc = VINF_SUCCESS;
2265 break;
2266 }
2267 AssertRC(rc);
2268
2269 /* Recalc the minimum sleep interval (do it always). */
2270 pSink->AIO.cMsMinTypicalInterval = RT_MS_1SEC / 2;
2271 for (uintptr_t iJob = 0; iJob < pSink->AIO.cUpdateJobs; iJob++)
2272 if (pSink->AIO.aUpdateJobs[iJob].cMsTypicalInterval < pSink->AIO.cMsMinTypicalInterval)
2273 pSink->AIO.cMsMinTypicalInterval = pSink->AIO.aUpdateJobs[iJob].cMsTypicalInterval;
2274
2275
2276 RTCritSectLeave(&pSink->CritSect);
2277 return rc;
2278}
2279
2280
2281/**
2282 * Transfer data from the device's DMA buffer and into the sink.
2283 *
2284 * The caller is already holding the mixer sink's critical section, either by
2285 * way of being the AIO thread doing update jobs or by explicit locking calls.
2286 *
2287 * @returns The new stream offset.
2288 * @param pSink The mixer sink to transfer samples to.
2289 * @param pCircBuf The internal DMA buffer to move samples from.
2290 * @param offStream The stream current offset (logging, dtrace, return).
2291 * @param idStream Device specific audio stream identifier (logging, dtrace).
2292 * @param pDbgFile Debug file, NULL if disabled.
2293 */
2294uint64_t AudioMixerSinkTransferFromCircBuf(PAUDMIXSINK pSink, PRTCIRCBUF pCircBuf, uint64_t offStream,
2295 uint32_t idStream, PAUDIOHLPFILE pDbgFile)
2296{
2297 /*
2298 * Sanity.
2299 */
2300 AssertReturn(pSink, offStream);
2301 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2302 AssertReturn(pCircBuf, offStream);
2303 Assert(RTCritSectIsOwner(&pSink->CritSect));
2304 RT_NOREF(idStream);
2305
2306 /*
2307 * Figure how much that we can push down.
2308 */
2309 uint32_t const cbSinkWritable = AudioMixerSinkGetWritable(pSink);
2310 uint32_t const cbCircBufReadable = (uint32_t)RTCircBufUsed(pCircBuf);
2311 uint32_t cbToTransfer = RT_MIN(cbCircBufReadable, cbSinkWritable);
2312 /* Make sure that we always align the number of bytes when reading to the stream's PCM properties. */
2313 uint32_t const cbToTransfer2 = cbToTransfer = PDMAudioPropsFloorBytesToFrame(&pSink->PCMProps, cbToTransfer);
2314
2315 Log3Func(("idStream=%u: cbSinkWritable=%#RX32 cbCircBufReadable=%#RX32 -> cbToTransfer=%#RX32 @%#RX64\n",
2316 idStream, cbSinkWritable, cbCircBufReadable, cbToTransfer, offStream));
2317 AssertMsg(!(pSink->fStatus & AUDMIXSINK_STS_DRAINING) || cbCircBufReadable == pSink->cbDmaLeftToDrain,
2318 ("cbCircBufReadable=%#x cbDmaLeftToDrain=%#x\n", cbCircBufReadable, pSink->cbDmaLeftToDrain));
2319
2320 /*
2321 * Do the pushing.
2322 */
2323 while (cbToTransfer > 0)
2324 {
2325 void /*const*/ *pvSrcBuf;
2326 size_t cbSrcBuf;
2327 RTCircBufAcquireReadBlock(pCircBuf, cbToTransfer, &pvSrcBuf, &cbSrcBuf);
2328
2329 uint32_t cbWritten = 0;
2330 int rc = AudioMixerSinkWrite(pSink, AUDMIXOP_COPY, pvSrcBuf, (uint32_t)cbSrcBuf, &cbWritten);
2331 AssertRC(rc);
2332 Assert(cbWritten <= cbSrcBuf);
2333
2334 Log2Func(("idStream=%u: %#RX32/%#zx bytes read @%#RX64\n", idStream, cbWritten, cbSrcBuf, offStream));
2335#ifdef VBOX_WITH_DTRACE
2336 VBOXDD_AUDIO_MIXER_SINK_AIO_OUT(idStream, cbWritten, offStream);
2337#endif
2338 offStream += cbWritten;
2339
2340 if (!pDbgFile)
2341 { /* likely */ }
2342 else
2343 AudioHlpFileWrite(pDbgFile, pvSrcBuf, cbSrcBuf, 0 /* fFlags */);
2344
2345
2346 RTCircBufReleaseReadBlock(pCircBuf, cbWritten);
2347
2348 /* advance */
2349 cbToTransfer -= cbWritten;
2350 }
2351
2352 /*
2353 * Advance drain status.
2354 */
2355 if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINING))
2356 { /* likely for most of the playback time ... */ }
2357 else if (!(pSink->fStatus & AUDMIXSINK_STS_DRAINED_DMA))
2358 {
2359 if (cbToTransfer2 >= pSink->cbDmaLeftToDrain)
2360 {
2361 Assert(cbToTransfer2 == pSink->cbDmaLeftToDrain);
2362 Log3Func(("idStream=%u/'%s': Setting AUDMIXSINK_STS_DRAINED_DMA.\n", idStream, pSink->pszName));
2363 pSink->cbDmaLeftToDrain = 0;
2364 pSink->fStatus |= AUDMIXSINK_STS_DRAINED_DMA;
2365 }
2366 else
2367 {
2368 pSink->cbDmaLeftToDrain -= cbToTransfer2;
2369 Log3Func(("idStream=%u/'%s': still %#x bytes left in the DMA buffer\n",
2370 idStream, pSink->pszName, pSink->cbDmaLeftToDrain));
2371 }
2372 }
2373 else
2374 Assert(cbToTransfer2 == 0);
2375
2376 return offStream;
2377}
2378
2379
2380/**
2381 * Transfer data to the device's DMA buffer from the sink.
2382 *
2383 * The caller is already holding the mixer sink's critical section, either by
2384 * way of being the AIO thread doing update jobs or by explicit locking calls.
2385 *
2386 * @returns The new stream offset.
2387 * @param pSink The mixer sink to transfer samples from.
2388 * @param pCircBuf The internal DMA buffer to move samples to.
2389 * @param offStream The stream current offset (logging, dtrace, return).
2390 * @param idStream Device specific audio stream identifier (logging, dtrace).
2391 * @param pDbgFile Debug file, NULL if disabled.
2392 */
2393uint64_t AudioMixerSinkTransferToCircBuf(PAUDMIXSINK pSink, PRTCIRCBUF pCircBuf, uint64_t offStream,
2394 uint32_t idStream, PAUDIOHLPFILE pDbgFile)
2395{
2396 /*
2397 * Sanity.
2398 */
2399 AssertReturn(pSink, offStream);
2400 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2401 AssertReturn(pCircBuf, offStream);
2402 Assert(RTCritSectIsOwner(&pSink->CritSect));
2403
2404 /*
2405 * Figure out how much we can transfer.
2406 */
2407 const uint32_t cbSinkReadable = AudioMixerSinkGetReadable(pSink);
2408 const uint32_t cbCircBufWritable = (uint32_t)RTCircBufFree(pCircBuf);
2409 uint32_t cbToTransfer = RT_MIN(cbCircBufWritable, cbSinkReadable);
2410
2411 /* Make sure that we always align the number of bytes when reading to the stream's PCM properties. */
2412 cbToTransfer = PDMAudioPropsFloorBytesToFrame(&pSink->PCMProps, cbToTransfer);
2413
2414 Log3Func(("idStream=%u: cbSinkReadable=%#RX32 cbCircBufWritable=%#RX32 -> cbToTransfer=%#RX32 @%#RX64\n",
2415 idStream, cbSinkReadable, cbCircBufWritable, cbToTransfer, offStream));
2416 RT_NOREF(idStream);
2417
2418 /** @todo should we throttle (read less) this if we're far ahead? */
2419
2420 /*
2421 * Copy loop.
2422 */
2423 while (cbToTransfer > 0)
2424 {
2425/** @todo We should be able to read straight into the circular buffer here
2426 * as it should have a frame aligned size. */
2427
2428 /* Read a chunk of data. */
2429 uint8_t abBuf[4096];
2430 uint32_t cbRead = 0;
2431 int rc = AudioMixerSinkRead(pSink, abBuf, RT_MIN(cbToTransfer, sizeof(abBuf)), &cbRead);
2432 AssertRCBreak(rc);
2433 AssertMsg(cbRead > 0, ("Nothing read from sink, even if %#RX32 bytes were (still) announced\n", cbToTransfer));
2434
2435 /* Write it to the internal DMA buffer. */
2436 uint32_t off = 0;
2437 while (off < cbRead)
2438 {
2439 void *pvDstBuf;
2440 size_t cbDstBuf;
2441 RTCircBufAcquireWriteBlock(pCircBuf, cbRead - off, &pvDstBuf, &cbDstBuf);
2442
2443 memcpy(pvDstBuf, &abBuf[off], cbDstBuf);
2444
2445#ifdef VBOX_WITH_DTRACE
2446 VBOXDD_AUDIO_MIXER_SINK_AIO_IN(idStream, (uint32_t)cbDstBuf, offStream);
2447#endif
2448 offStream += cbDstBuf;
2449
2450 RTCircBufReleaseWriteBlock(pCircBuf, cbDstBuf);
2451
2452 off += (uint32_t)cbDstBuf;
2453 }
2454 Assert(off == cbRead);
2455
2456 /* Write to debug file? */
2457 if (RT_LIKELY(!pDbgFile))
2458 { /* likely */ }
2459 else
2460 AudioHlpFileWrite(pDbgFile, abBuf, cbRead, 0 /* fFlags */);
2461
2462 /* Advance. */
2463 Assert(cbRead <= cbToTransfer);
2464 cbToTransfer -= cbRead;
2465 }
2466
2467 return offStream;
2468}
2469
2470
2471/**
2472 * Signals the AIO thread to perform updates.
2473 *
2474 * @returns VBox status code.
2475 * @param pSink The mixer sink which AIO thread needs to do chores.
2476 */
2477int AudioMixerSinkSignalUpdateJob(PAUDMIXSINK pSink)
2478{
2479 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2480 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2481 return RTSemEventSignal(pSink->AIO.hEvent);
2482}
2483
2484
2485/**
2486 * Locks the mixer sink for purposes of serializing with the AIO thread.
2487 *
2488 * @returns VBox status code.
2489 * @param pSink The mixer sink to lock.
2490 */
2491int AudioMixerSinkLock(PAUDMIXSINK pSink)
2492{
2493 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2494 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2495 return RTCritSectEnter(&pSink->CritSect);
2496}
2497
2498
2499/**
2500 * Try to lock the mixer sink for purposes of serializing with the AIO thread.
2501 *
2502 * @returns VBox status code.
2503 * @param pSink The mixer sink to lock.
2504 */
2505int AudioMixerSinkTryLock(PAUDMIXSINK pSink)
2506{
2507 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2508 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2509 return RTCritSectTryEnter(&pSink->CritSect);
2510}
2511
2512
2513/**
2514 * Unlocks the sink.
2515 *
2516 * @returns VBox status code.
2517 * @param pSink The mixer sink to unlock.
2518 */
2519int AudioMixerSinkUnlock(PAUDMIXSINK pSink)
2520{
2521 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2522 return RTCritSectLeave(&pSink->CritSect);
2523}
2524
2525
2526/**
2527 * Updates the (master) volume of a mixer sink.
2528 *
2529 * @returns VBox status code.
2530 * @param pSink Mixer sink to update volume for.
2531 * @param pVolMaster Master volume to set.
2532 */
2533static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, PCPDMAUDIOVOLUME pVolMaster)
2534{
2535 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2536 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2537 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
2538
2539 LogFlowFunc(("[%s] Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
2540 pSink->pszName, pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
2541 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32 ",
2542 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
2543
2544 /** @todo Very crude implementation for now -- needs more work! */
2545
2546 pSink->VolumeCombined.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
2547
2548 pSink->VolumeCombined.uLeft = ( (pSink->Volume.uLeft ? pSink->Volume.uLeft : 1)
2549 * (pVolMaster->uLeft ? pVolMaster->uLeft : 1)) / PDMAUDIO_VOLUME_MAX;
2550
2551 pSink->VolumeCombined.uRight = ( (pSink->Volume.uRight ? pSink->Volume.uRight : 1)
2552 * (pVolMaster->uRight ? pVolMaster->uRight : 1)) / PDMAUDIO_VOLUME_MAX;
2553
2554 LogFlow(("-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
2555 pSink->VolumeCombined.fMuted, pSink->VolumeCombined.uLeft, pSink->VolumeCombined.uRight));
2556
2557 /*
2558 * Input sinks must currently propagate the new volume settings to
2559 * all the streams. (For output sinks we do the volume control here.)
2560 */
2561 if (pSink->enmDir != PDMAUDIODIR_OUT)
2562 {
2563 PAUDMIXSTREAM pMixStream;
2564 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
2565 {
2566 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &pSink->VolumeCombined);
2567 AssertRC(rc2);
2568 }
2569 }
2570
2571 return VINF_SUCCESS;
2572}
2573
2574/**
2575 * Writes data to a mixer output sink.
2576 *
2577 * @returns VBox status code.
2578 * @param pSink Sink to write data to.
2579 * @param enmOp Mixer operation to use when writing data to the sink.
2580 * @param pvBuf Buffer containing the audio data to write.
2581 * @param cbBuf Size (in bytes) of the buffer containing the audio data.
2582 * @param pcbWritten Number of bytes written. Optional.
2583 */
2584int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
2585{
2586 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
2587 Assert(pSink->uMagic == AUDMIXSINK_MAGIC);
2588 RT_NOREF(enmOp);
2589 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2590 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
2591 /* pcbWritten is optional. */
2592
2593 int rc = RTCritSectEnter(&pSink->CritSect);
2594 AssertRCReturn(rc, rc);
2595
2596 AssertMsg(pSink->fStatus & AUDMIXSINK_STS_RUNNING,
2597 ("%s: Can't write to a sink which is not running (anymore) (status 0x%x)\n", pSink->pszName, pSink->fStatus));
2598 AssertMsg(pSink->enmDir == PDMAUDIODIR_OUT,
2599 ("%s: Can't write to a sink which is not an output sink\n", pSink->pszName));
2600
2601 uint32_t cbWritten = 0;
2602 uint32_t cbToWrite = RT_MIN(AudioMixBufFreeBytes(&pSink->MixBuf), cbBuf);
2603 while (cbToWrite)
2604 {
2605 /* Write the data to the mixer sink's own mixing buffer.
2606 Here the audio data is transformed into the mixer sink's format. */
2607 uint32_t cFramesWritten = 0;
2608 rc = AudioMixBufWriteCirc(&pSink->MixBuf, (uint8_t const*)pvBuf + cbWritten, cbToWrite, &cFramesWritten);
2609 if (RT_SUCCESS(rc))
2610 {
2611 const uint32_t cbWrittenChunk = PDMAudioPropsFramesToBytes(&pSink->PCMProps, cFramesWritten);
2612 Assert(cbToWrite >= cbWrittenChunk);
2613 cbToWrite -= cbWrittenChunk;
2614 cbWritten += cbWrittenChunk;
2615 }
2616 else
2617 break;
2618 }
2619
2620 Log3Func(("[%s] cbBuf=%RU32 -> cbWritten=%RU32\n", pSink->pszName, cbBuf, cbWritten));
2621
2622 /* Update the sink's last written time stamp. */
2623 pSink->tsLastReadWrittenNs = RTTimeNanoTS();
2624
2625 if (pcbWritten)
2626 *pcbWritten = cbWritten;
2627
2628 RTCritSectLeave(&pSink->CritSect);
2629 return rc;
2630}
2631
2632
2633/*********************************************************************************************************************************
2634 * Mixer Stream implementation.
2635 ********************************************************************************************************************************/
2636
2637/**
2638 * Controls a mixer stream, internal version.
2639 *
2640 * @returns VBox status code (generally ignored).
2641 * @param pMixStream Mixer stream to control.
2642 * @param enmCmd Mixer stream command to use.
2643 */
2644static int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd)
2645{
2646 Assert(pMixStream->uMagic == AUDMIXSTREAM_MAGIC);
2647 AssertPtrReturn(pMixStream->pConn, VERR_AUDIO_STREAM_NOT_READY);
2648 AssertPtrReturn(pMixStream->pStream, VERR_AUDIO_STREAM_NOT_READY);
2649
2650 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
2651
2652 LogFlowFunc(("[%s] enmCmd=%d, rc=%Rrc\n", pMixStream->pszName, enmCmd, rc));
2653
2654 return rc;
2655}
2656
2657/**
2658 * Updates a mixer stream's internal status.
2659 *
2660 * This may perform a stream re-init if the driver requests it, in which case
2661 * this may take a little while longer than usual...
2662 *
2663 * @returns VBox status code.
2664 * @param pMixStream Mixer stream to to update internal status for.
2665 */
2666static int audioMixerStreamUpdateStatus(PAUDMIXSTREAM pMixStream)
2667{
2668 Assert(pMixStream->uMagic == AUDMIXSTREAM_MAGIC);
2669
2670 /*
2671 * Reset the mixer status to start with.
2672 */
2673 pMixStream->fStatus = AUDMIXSTREAM_STATUS_NONE;
2674
2675 PPDMIAUDIOCONNECTOR const pConn = pMixStream->pConn;
2676 if (pConn) /* Audio connector available? */
2677 {
2678 PPDMAUDIOSTREAM const pStream = pMixStream->pStream;
2679
2680 /*
2681 * Get the stream status.
2682 * Do re-init if needed and fetch the status again afterwards.
2683 */
2684 PDMAUDIOSTREAMSTATE enmState = pConn->pfnStreamGetState(pConn, pStream);
2685 if (enmState != PDMAUDIOSTREAMSTATE_NEED_REINIT)
2686 { /* likely */ }
2687 else
2688 {
2689 LogFunc(("[%s] needs re-init...\n", pMixStream->pszName));
2690 int rc = pConn->pfnStreamReInit(pConn, pStream);
2691 enmState = pConn->pfnStreamGetState(pConn, pStream);
2692 LogFunc(("[%s] re-init returns %Rrc and %s.\n", pMixStream->pszName, rc, PDMAudioStreamStateGetName(enmState)));
2693
2694 PAUDMIXSINK const pSink = pMixStream->pSink;
2695 AssertPtr(pSink);
2696 if (pSink->enmDir == PDMAUDIODIR_OUT)
2697 {
2698 rc = AudioMixBufInitPeekState(&pSink->MixBuf, &pMixStream->PeekState, &pStream->Props);
2699 /** @todo we need to remember this, don't we? */
2700 AssertRCReturn(rc, VINF_SUCCESS);
2701 }
2702 }
2703
2704 /*
2705 * Translate the status to mixer speak.
2706 */
2707 AssertMsg(enmState > PDMAUDIOSTREAMSTATE_INVALID && enmState < PDMAUDIOSTREAMSTATE_END, ("%d\n", enmState));
2708 switch (enmState)
2709 {
2710 case PDMAUDIOSTREAMSTATE_NOT_WORKING:
2711 case PDMAUDIOSTREAMSTATE_NEED_REINIT:
2712 case PDMAUDIOSTREAMSTATE_INACTIVE:
2713 pMixStream->fStatus = AUDMIXSTREAM_STATUS_NONE;
2714 break;
2715 case PDMAUDIOSTREAMSTATE_ENABLED:
2716 pMixStream->fStatus = AUDMIXSTREAM_STATUS_ENABLED;
2717 break;
2718 case PDMAUDIOSTREAMSTATE_ENABLED_READABLE:
2719 Assert(pMixStream->pSink->enmDir == PDMAUDIODIR_IN);
2720 pMixStream->fStatus = AUDMIXSTREAM_STATUS_ENABLED | AUDMIXSTREAM_STATUS_CAN_READ;
2721 break;
2722 case PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE:
2723 Assert(pMixStream->pSink->enmDir == PDMAUDIODIR_OUT);
2724 pMixStream->fStatus = AUDMIXSTREAM_STATUS_ENABLED | AUDMIXSTREAM_STATUS_CAN_WRITE;
2725 break;
2726 /* no default */
2727 case PDMAUDIOSTREAMSTATE_INVALID:
2728 case PDMAUDIOSTREAMSTATE_END:
2729 case PDMAUDIOSTREAMSTATE_32BIT_HACK:
2730 break;
2731 }
2732 }
2733
2734 LogFlowFunc(("[%s] -> 0x%x\n", pMixStream->pszName, pMixStream->fStatus));
2735 return VINF_SUCCESS;
2736}
2737
2738/**
2739 * Destroys & frees a mixer stream, internal version.
2740 *
2741 * Worker for audioMixerSinkDestroyInternal and AudioMixerStreamDestroy.
2742 *
2743 * @param pMixStream Mixer stream to destroy.
2744 * @param pDevIns The device instance the statistics are registered with.
2745 */
2746static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream, PPDMDEVINS pDevIns)
2747{
2748 AssertPtrReturnVoid(pMixStream);
2749
2750 LogFunc(("%s\n", pMixStream->pszName));
2751 Assert(pMixStream->uMagic == AUDMIXSTREAM_MAGIC);
2752
2753 if (pMixStream->pConn) /* Stream has a connector interface present? */
2754 {
2755 if (pMixStream->pStream)
2756 {
2757 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
2758 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
2759
2760 pMixStream->pStream = NULL;
2761 }
2762
2763 pMixStream->pConn = NULL;
2764 }
2765
2766 if (pMixStream->pszStatPrefix)
2767 {
2768 PDMDevHlpSTAMDeregisterByPrefix(pDevIns, pMixStream->pszStatPrefix);
2769 RTStrFree(pMixStream->pszStatPrefix);
2770 pMixStream->pszStatPrefix = NULL;
2771 }
2772
2773 RTStrFree(pMixStream->pszName);
2774 pMixStream->pszName = NULL;
2775
2776 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
2777 AssertRC(rc2);
2778
2779 RTMemFree(pMixStream);
2780 pMixStream = NULL;
2781}
2782
2783/**
2784 * Destroys a mixer stream.
2785 *
2786 * @param pMixStream Mixer stream to destroy.
2787 * @param pDevIns The device instance statistics are registered with.
2788 */
2789void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream, PPDMDEVINS pDevIns)
2790{
2791 if (!pMixStream)
2792 return;
2793
2794/** @todo r=bird: Wrng critsect for audioMixerSinkRemoveStreamInternal */
2795 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2796 AssertRC(rc2);
2797
2798 LogFunc(("%s\n", pMixStream->pszName));
2799
2800 if (pMixStream->pSink) /* Is the stream part of a sink? */
2801 {
2802 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
2803 * pointer will be gone from the stream. */
2804 PAUDMIXSINK pSink = pMixStream->pSink;
2805
2806 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
2807 if (RT_SUCCESS(rc2))
2808 {
2809 Assert(pSink->cStreams);
2810 pSink->cStreams--;
2811 }
2812 }
2813 else
2814 rc2 = VINF_SUCCESS;
2815
2816 int rc3 = RTCritSectLeave(&pMixStream->CritSect);
2817 AssertRC(rc3);
2818
2819 if (RT_SUCCESS(rc2))
2820 {
2821 audioMixerStreamDestroyInternal(pMixStream, pDevIns);
2822 pMixStream = NULL;
2823 }
2824
2825 LogFlowFunc(("Returning %Rrc\n", rc2));
2826}
2827
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