VirtualBox

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

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

Audio: Enable/disable streams using PDMIAUDIOCONNECTOR::pfnStreamControl() instead of PDMIAUDIOCONNECTOR::pfnEnable(). bugref:9882

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