VirtualBox

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

Last change on this file since 63016 was 62978, checked in by vboxsync, 9 years ago

enums defaults to int, so use '%d' and not '%ld' to prinft them!

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 44.3 KB
Line 
1/* $Id: AudioMixer.cpp 62978 2016-08-04 11:07:41Z vboxsync $ */
2/** @file
3 * VBox audio: Mixing routines, mainly used by the various audio device
4 * emulations to achieve proper multiplexing from/to attached
5 * devices LUNs.
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) and
9 * audio sinks (output).
10 *
11 * As audio driver instances are handled as LUNs on the device level, this
12 * audio mixer then can take care of e.g. mixing various inputs/outputs to/from
13 * a specific source/sink.
14 *
15 * How and which audio streams are connected to sinks/sources depends on how
16 * the audio mixer has been set up.
17 *
18 * A sink can connect multiple output streams together, whereas a source
19 * does this with input streams. Each sink / source consists of one or more
20 * so-called mixer streams, which then in turn have pointers to the actual
21 * PDM audio input/output streams.
22 */
23
24/*
25 * Copyright (C) 2014-2016 Oracle Corporation
26 *
27 * This file is part of VirtualBox Open Source Edition (OSE), as
28 * available from http://www.virtualbox.org. This file is free software;
29 * you can redistribute it and/or modify it under the terms of the GNU
30 * General Public License (GPL) as published by the Free Software
31 * Foundation, in version 2 as it comes in the "COPYING" file of the
32 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
33 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
34 */
35#define LOG_GROUP LOG_GROUP_AUDIO_MIXER
36#include <VBox/log.h>
37#include "AudioMixer.h"
38#include "AudioMixBuffer.h"
39#include "DrvAudio.h"
40
41#include <VBox/vmm/pdm.h>
42#include <VBox/err.h>
43#include <VBox/vmm/mm.h>
44#include <VBox/vmm/pdmaudioifs.h>
45
46#include <iprt/alloc.h>
47#include <iprt/asm-math.h>
48#include <iprt/assert.h>
49#include <iprt/string.h>
50
51static int audioMixerRemoveSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink);
52
53static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink);
54static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster);
55static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink);
56static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream);
57static void audioMixerSinkReset(PAUDMIXSINK pSink);
58static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink);
59
60static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pStream);
61
62
63/**
64 * Creates an audio sink and attaches it to the given mixer.
65 *
66 * @returns IPRT status code.
67 * @param pMixer Mixer to attach created sink to.
68 * @param pszName Name of the sink to create.
69 * @param enmDir Direction of the sink to create.
70 * @param ppSink Pointer which returns the created sink on success.
71 */
72int AudioMixerCreateSink(PAUDIOMIXER pMixer, const char *pszName, AUDMIXSINKDIR enmDir, PAUDMIXSINK *ppSink)
73{
74 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
75 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
76 /* ppSink is optional. */
77
78 int rc = VINF_SUCCESS;
79
80 PAUDMIXSINK pSink = (PAUDMIXSINK)RTMemAllocZ(sizeof(AUDMIXSINK));
81 if (pSink)
82 {
83 pSink->pszName = RTStrDup(pszName);
84 if (!pSink->pszName)
85 rc = VERR_NO_MEMORY;
86
87 if (RT_SUCCESS(rc))
88 {
89 pSink->pParent = pMixer;
90 pSink->enmDir = enmDir;
91 RTListInit(&pSink->lstStreams);
92
93 /* Set initial volume to max. */
94 pSink->Volume.fMuted = false;
95 pSink->Volume.uLeft = PDMAUDIO_VOLUME_MAX;
96 pSink->Volume.uRight = PDMAUDIO_VOLUME_MAX;
97
98 /* Ditto for the combined volume. */
99 pSink->VolumeCombined.fMuted = false;
100 pSink->VolumeCombined.uLeft = PDMAUDIO_VOLUME_MAX;
101 pSink->VolumeCombined.uRight = PDMAUDIO_VOLUME_MAX;
102
103 RTListAppend(&pMixer->lstSinks, &pSink->Node);
104 pMixer->cSinks++;
105
106 LogFlowFunc(("pMixer=%p, pSink=%p, cSinks=%RU8\n",
107 pMixer, pSink, pMixer->cSinks));
108
109 if (ppSink)
110 *ppSink = pSink;
111 }
112 else
113 RTMemFree(pSink);
114 }
115 else
116 rc = VERR_NO_MEMORY;
117
118 return rc;
119}
120
121/**
122 * Creates an audio mixer.
123 *
124 * @returns IPRT status code.
125 * @param pszName Name of the audio mixer.
126 * @param fFlags Creation flags. Not used at the moment and must be 0.
127 * @param ppMixer Pointer which returns the created mixer object.
128 */
129int AudioMixerCreate(const char *pszName, uint32_t fFlags, PAUDIOMIXER *ppMixer)
130{
131 RT_NOREF(fFlags);
132 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
133 /** @todo Add fFlags validation. */
134 AssertPtrReturn(ppMixer, VERR_INVALID_POINTER);
135
136 int rc = VINF_SUCCESS;
137
138 PAUDIOMIXER pMixer = (PAUDIOMIXER)RTMemAllocZ(sizeof(AUDIOMIXER));
139 if (pMixer)
140 {
141 pMixer->pszName = RTStrDup(pszName);
142 if (!pMixer->pszName)
143 rc = VERR_NO_MEMORY;
144
145 if (RT_SUCCESS(rc))
146 {
147 pMixer->cSinks = 0;
148 RTListInit(&pMixer->lstSinks);
149
150 /* Set master volume to the max. */
151 pMixer->VolMaster.fMuted = false;
152 pMixer->VolMaster.uLeft = PDMAUDIO_VOLUME_MAX;
153 pMixer->VolMaster.uRight = PDMAUDIO_VOLUME_MAX;
154
155 LogFlowFunc(("Created mixer '%s'\n", pMixer->pszName));
156
157 *ppMixer = pMixer;
158 }
159 else
160 RTMemFree(pMixer);
161 }
162 else
163 rc = VERR_NO_MEMORY;
164
165 LogFlowFuncLeaveRC(rc);
166 return rc;
167}
168
169/**
170 * Helper function for the internal debugger to print the mixer's current
171 * state, along with the attached sinks.
172 *
173 * @param pMixer Mixer to print debug output for.
174 * @param pHlp Debug info helper to use.
175 * @param pszArgs Optional arguments. Not being used at the moment.
176 */
177void AudioMixerDebug(PAUDIOMIXER pMixer, PCDBGFINFOHLP pHlp, const char *pszArgs)
178{
179 RT_NOREF(pszArgs);
180 PAUDMIXSINK pSink;
181 unsigned iSink = 0;
182
183 pHlp->pfnPrintf(pHlp, "[Master] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", pMixer->pszName,
184 pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight, pMixer->VolMaster.fMuted);
185
186 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
187 {
188 pHlp->pfnPrintf(pHlp, "[Sink %u] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", iSink, pSink->pszName,
189 pSink->Volume.uLeft, pSink->Volume.uRight, pSink->Volume.fMuted);
190 ++iSink;
191 }
192}
193
194/**
195 * Destroys an audio mixer.
196 *
197 * @param pMixer Audio mixer to destroy.
198 */
199void AudioMixerDestroy(PAUDIOMIXER pMixer)
200{
201 if (!pMixer)
202 return;
203
204 LogFlowFunc(("Destroying %s ...\n", pMixer->pszName));
205
206 PAUDMIXSINK pSink, pSinkNext;
207 RTListForEachSafe(&pMixer->lstSinks, pSink, pSinkNext, AUDMIXSINK, Node)
208 {
209 /* Save a pointer to the sink to remove, as pSink
210 * will not be valid anymore after calling audioMixerRemoveSinkInternal(). */
211 PAUDMIXSINK pSinkToRemove = pSink;
212
213 audioMixerRemoveSinkInternal(pMixer, pSinkToRemove);
214 audioMixerSinkDestroyInternal(pSinkToRemove);
215 }
216
217 pMixer->cSinks = 0;
218
219 if (pMixer->pszName)
220 {
221 RTStrFree(pMixer->pszName);
222 pMixer->pszName = NULL;
223 }
224
225 RTMemFree(pMixer);
226 pMixer = NULL;
227}
228
229/**
230 * Invalidates all internal data, internal version.
231 *
232 * @returns IPRT status code.
233 * @param pMixer Mixer to invalidate data for.
234 */
235int audioMixerInvalidateInternal(PAUDIOMIXER pMixer)
236{
237 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
238
239 LogFlowFunc(("[%s]\n", pMixer->pszName));
240
241 /* Propagate new master volume to all connected sinks. */
242 PAUDMIXSINK pSink;
243 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
244 {
245 int rc2 = audioMixerSinkUpdateVolume(pSink, &pMixer->VolMaster);
246 AssertRC(rc2);
247 }
248
249 return VINF_SUCCESS;
250}
251
252/**
253 * Invalidates all internal data.
254 *
255 * @returns IPRT status code.
256 * @param pMixer Mixer to invalidate data for.
257 */
258void AudioMixerInvalidate(PAUDIOMIXER pMixer)
259{
260 AssertPtrReturnVoid(pMixer);
261
262 LogFlowFunc(("[%s]\n", pMixer->pszName));
263
264 int rc2 = audioMixerInvalidateInternal(pMixer);
265 AssertRC(rc2);
266}
267
268/**
269 * Removes a formerly attached audio sink for an audio mixer, internal version.
270 *
271 * @returns IPRT status code.
272 * @param pMixer Mixer to remove sink from.
273 * @param pSink Sink to remove.
274 */
275static int audioMixerRemoveSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
276{
277 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
278 if (!pSink)
279 return VERR_NOT_FOUND;
280
281 AssertMsgReturn(pSink->pParent == pMixer, ("Sink '%s' is not part of mixer '%s'\n",
282 pSink->pszName, pMixer->pszName), VERR_NOT_FOUND);
283
284 LogFlowFunc(("[%s]: pSink=%s, cSinks=%RU8\n",
285 pMixer->pszName, pSink->pszName, pMixer->cSinks));
286
287 /* Remove sink from mixer. */
288 RTListNodeRemove(&pSink->Node);
289 Assert(pMixer->cSinks);
290 pMixer->cSinks--;
291
292 /* Set mixer to NULL so that we know we're not part of any mixer anymore. */
293 pSink->pParent = NULL;
294
295 return VINF_SUCCESS;
296}
297
298/**
299 * Removes a formerly attached audio sink for an audio mixer.
300 *
301 * @returns IPRT status code.
302 * @param pMixer Mixer to remove sink from.
303 * @param pSink Sink to remove.
304 */
305
306void AudioMixerRemoveSink(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
307{
308 audioMixerSinkRemoveAllStreamsInternal(pSink);
309 audioMixerRemoveSinkInternal(pMixer, pSink);
310}
311
312/**
313 * Sets the mixer's master volume.
314 *
315 * @returns IPRT status code.
316 * @param pMixer Mixer to set master volume for.
317 * @param pVol Volume to set.
318 */
319int AudioMixerSetMasterVolume(PAUDIOMIXER pMixer, PPDMAUDIOVOLUME pVol)
320{
321 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
322 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
323
324 memcpy(&pMixer->VolMaster, pVol, sizeof(PDMAUDIOVOLUME));
325
326 LogFlowFunc(("[%s]: lVol=%RU32, rVol=%RU32 => fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
327 pMixer->pszName, pVol->uLeft, pVol->uRight,
328 pMixer->VolMaster.fMuted, pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight));
329
330 return audioMixerInvalidateInternal(pMixer);
331}
332
333/*********************************************************************************************************************************
334 * Mixer Sink implementation.
335 ********************************************************************************************************************************/
336
337/**
338 * Adds an audio stream to a specific audio sink.
339 *
340 * @returns IPRT status code.
341 * @param pSink Sink to add audio stream to.
342 * @param pStream Stream to add.
343 */
344int AudioMixerSinkAddStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
345{
346 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
347 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
348
349 if (pSink->cStreams == UINT8_MAX) /* 255 streams per sink max. */
350 return VERR_NO_MORE_HANDLES;
351
352 int rc;
353
354 LogFlowFuncEnter();
355
356#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
357 /* Make sure only compatible streams are added. */
358 if (pStream->enmDir == PDMAUDIODIR_IN)
359 {
360 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, &pStream->InOut.pIn->Props))
361 {
362#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
363 /* Chain: Stream (Child) -> Sink (Child) -> Guest (Parent). */
364 PPDMAUDIOMIXBUF pHstIn = &pStream->InOut.pIn->pHstStrmIn->MixBuf;
365 PPDMAUDIOMIXBUF pGstIn = &pStream->InOut.pIn->MixBuf;
366
367 /* Unlink any former parent from host input. */
368 AudioMixBufUnlink(pHstIn);
369
370 /* Link host input to this sink as a parent. */
371 rc = AudioMixBufLinkTo(pHstIn, &pSink->MixBuf);
372 AssertRC(rc);
373
374 /* Unlink any former parent from this sink. */
375 AudioMixBufUnlink(&pSink->MixBuf);
376
377 /* Link guest input to this sink as a parent. */
378 rc = AudioMixBufLinkTo(&pSink->MixBuf, pGstIn);
379 AssertRC(rc);
380# ifdef DEBUG
381 AudioMixBufDbgPrintChain(&pStream->InOut.pIn->MixBuf);
382# endif
383#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
384 }
385 else
386 AssertFailedStmt(rc = VERR_WRONG_TYPE);
387 }
388 else if (pStream->enmDir == PDMAUDIODIR_OUT)
389 {
390 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, &pStream->InOut.pOut->Props))
391 {
392#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
393 /* Chain: Guest (Child) -> Sink (Child) -> Stream (Parent). */
394 rc = AudioMixBufLinkTo(&pStream->InOut.pOut->pHstStrmOut->MixBuf, &pSink->MixBuf);
395# ifdef DEBUG
396 AudioMixBufDbgPrintChain(&pSink->MixBuf);
397# endif
398#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
399 }
400 else
401 AssertFailedStmt(rc = VERR_WRONG_TYPE);
402 }
403 else
404 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
405#else
406 rc = VINF_SUCCESS;
407#endif
408
409 if (RT_SUCCESS(rc))
410 {
411 /** @todo Check if stream already is assigned to (another) sink. */
412
413 /* Apply the sink's combined volume to the stream. */
414 AssertPtr(pStream->pConn);
415 int rc2 = pStream->pConn->pfnStreamSetVolume(pStream->pConn, pStream->pStream, &pSink->VolumeCombined);
416 AssertRC(rc2);
417
418 /* Save pointer to sink the stream is attached to. */
419 pStream->pSink = pSink;
420
421 /* Append stream to sink's list. */
422 RTListAppend(&pSink->lstStreams, &pStream->Node);
423 pSink->cStreams++;
424 }
425
426 LogFlowFunc(("[%s]: cStreams=%RU8, rc=%Rrc\n", pSink->pszName, pSink->cStreams, rc));
427 return rc;
428}
429
430/**
431 * Creates an audio mixer stream.
432 *
433 * @returns IPRT status code.
434 * @param pSink Sink to use for creating the stream.
435 * @param pConn Audio connector interface to use.
436 * @param pCfg Audio stream configuration to use.
437 * @param fFlags Stream creation flags. Currently unused, set to 0.
438 * @param ppStream Pointer which receives the the newly created audio stream.
439 */
440int AudioMixerSinkCreateStream(PAUDMIXSINK pSink,
441 PPDMIAUDIOCONNECTOR pConn, PPDMAUDIOSTREAMCFG pCfg, uint32_t fFlags, PAUDMIXSTREAM *ppStream)
442{
443 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
444 AssertPtrReturn(pConn, VERR_INVALID_POINTER);
445 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
446 /** @todo Validate fFlags. */
447 /* ppStream is optional. */
448
449 PAUDMIXSTREAM pMixStream = (PAUDMIXSTREAM)RTMemAllocZ(sizeof(AUDMIXSTREAM));
450 if (!pMixStream)
451 return VERR_NO_MEMORY;
452
453 pMixStream->pszName = RTStrDup(pCfg->szName);
454 if (!pMixStream->pszName)
455 {
456 RTMemFree(pMixStream);
457 return VERR_NO_MEMORY;
458 }
459
460 LogFlowFunc(("[%s]: fFlags=0x%x (enmDir=%d, %s, %RU8 channels, %RU32Hz)\n",
461 pSink->pszName, fFlags, pCfg->enmDir, DrvAudioHlpAudFmtToStr(pCfg->enmFormat), pCfg->cChannels, pCfg->uHz));
462
463 /*
464 * Initialize the host-side configuration for the stream to be created.
465 * Always use the sink's PCM audio format as the host side when creating a stream for it.
466 */
467 PDMAUDIOSTREAMCFG CfgHost;
468 int rc = DrvAudioHlpPCMPropsToStreamCfg(&pSink->PCMProps, &CfgHost);
469 AssertRCReturn(rc, rc);
470
471 /* Apply the sink's direction for the configuration to use to
472 * create the stream. */
473 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
474 {
475 CfgHost.DestSource.Source = pCfg->DestSource.Source;
476 CfgHost.enmDir = PDMAUDIODIR_IN;
477 }
478 else
479 {
480 CfgHost.DestSource.Dest = pCfg->DestSource.Dest;
481 CfgHost.enmDir = PDMAUDIODIR_OUT;
482 }
483
484 RTStrPrintf(CfgHost.szName, sizeof(CfgHost.szName), "%s", pCfg->szName);
485
486 PPDMAUDIOSTREAM pStream;
487 rc = pConn->pfnStreamCreate(pConn, &CfgHost, pCfg, &pStream);
488 if (RT_SUCCESS(rc))
489 {
490 /* Save the audio stream pointer to this mixing stream. */
491 pMixStream->pStream = pStream;
492
493 /* Increase the stream's reference count to let others know
494 * we're reyling on it to be around now. */
495 pConn->pfnStreamRetain(pConn, pStream);
496 }
497
498 if (RT_SUCCESS(rc))
499 {
500 pMixStream->fFlags = fFlags;
501 pMixStream->pConn = pConn;
502
503 if (ppStream)
504 *ppStream = pMixStream;
505 }
506 else if (pMixStream)
507 {
508 if (pMixStream->pszName)
509 {
510 RTStrFree(pMixStream->pszName);
511 pMixStream->pszName = NULL;
512 }
513
514 RTMemFree(pMixStream);
515 pMixStream = NULL;
516 }
517
518 return rc;
519}
520
521/**
522 * Static helper function to translate a sink command
523 * to a PDM audio stream command.
524 *
525 * @returns PDM audio stream command, or PDMAUDIOSTREAMCMD_UNKNOWN if not found.
526 * @param enmCmd Mixer sink command to translate.
527 */
528static PDMAUDIOSTREAMCMD audioMixerSinkToStreamCmd(AUDMIXSINKCMD enmCmd)
529{
530 switch (enmCmd)
531 {
532 case AUDMIXSINKCMD_ENABLE: return PDMAUDIOSTREAMCMD_ENABLE;
533 case AUDMIXSINKCMD_DISABLE: return PDMAUDIOSTREAMCMD_DISABLE;
534 case AUDMIXSINKCMD_PAUSE: return PDMAUDIOSTREAMCMD_PAUSE;
535 case AUDMIXSINKCMD_RESUME: return PDMAUDIOSTREAMCMD_RESUME;
536 default: break;
537 }
538
539 AssertMsgFailed(("Unsupported sink command %d\n", enmCmd));
540 return PDMAUDIOSTREAMCMD_UNKNOWN;
541}
542
543/**
544 * Controls a mixer sink.
545 *
546 * @returns IPRT status code.
547 * @param pSink Mixer sink to control.
548 * @param enmSinkCmd Sink command to set.
549 */
550int AudioMixerSinkCtl(PAUDMIXSINK pSink, AUDMIXSINKCMD enmSinkCmd)
551{
552 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
553
554 PDMAUDIOSTREAMCMD enmCmdStream = audioMixerSinkToStreamCmd(enmSinkCmd);
555 if (enmCmdStream == PDMAUDIOSTREAMCMD_UNKNOWN)
556 return VERR_NOT_SUPPORTED;
557
558 int rc = VINF_SUCCESS;
559
560 PAUDMIXSTREAM pStream;
561 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
562 {
563 int rc2 = AudioMixerStreamCtl(pStream, enmCmdStream, AUDMIXSTRMCTL_FLAG_NONE);
564 if (RT_SUCCESS(rc))
565 rc = rc2;
566 /* Keep going. Flag? */
567 }
568
569 if (enmSinkCmd == AUDMIXSINKCMD_ENABLE)
570 {
571 pSink->fStatus |= AUDMIXSINK_STS_RUNNING;
572 }
573 else if (enmSinkCmd == AUDMIXSINKCMD_DISABLE)
574 {
575 /* Set the sink in a pending disable state first.
576 * The final status (disabled) will be set in the sink's iteration. */
577 pSink->fStatus |= AUDMIXSINK_STS_PENDING_DISABLE;
578 }
579
580 /* Not running anymore? Reset. */
581 if (!(pSink->fStatus & AUDMIXSINK_STS_RUNNING))
582 audioMixerSinkReset(pSink);
583
584 LogFlowFunc(("[%s]: enmCmd=%d, fStatus=0x%x, rc=%Rrc\n", pSink->pszName, enmSinkCmd, pSink->fStatus, rc));
585 return rc;
586}
587
588/**
589 * Destroys a mixer sink and removes it from the attached mixer (if any).
590 *
591 * @param pSink Mixer sink to destroy.
592 */
593void AudioMixerSinkDestroy(PAUDMIXSINK pSink)
594{
595 if (!pSink)
596 return;
597
598 if (pSink->pParent)
599 {
600 /* Save sink pointer, as after audioMixerRemoveSinkInternal() the
601 * pointer will be gone from the stream. */
602 PAUDIOMIXER pMixer = pSink->pParent;
603
604 audioMixerRemoveSinkInternal(pMixer, pSink);
605
606 Assert(pMixer->cSinks);
607 pMixer->cSinks--;
608 }
609
610 audioMixerSinkDestroyInternal(pSink);
611}
612
613/**
614 * Destroys a mixer sink.
615 *
616 * @param pSink Mixer sink to destroy.
617 */
618static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink)
619{
620 AssertPtrReturnVoid(pSink);
621
622 LogFunc(("%s\n", pSink->pszName));
623
624 PAUDMIXSTREAM pStream, pStreamNext;
625 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
626 {
627 /* Save a pointer to the stream to remove, as pStream
628 * will not be valid anymore after calling audioMixerSinkRemoveStreamInternal(). */
629 PAUDMIXSTREAM pStreamToRemove = pStream;
630
631 audioMixerSinkRemoveStreamInternal(pSink, pStreamToRemove);
632 audioMixerStreamDestroyInternal(pStreamToRemove);
633 }
634
635 if (pSink->pszName)
636 {
637 RTStrFree(pSink->pszName);
638 pSink->pszName = NULL;
639 }
640
641 RTMemFree(pSink);
642 pSink = NULL;
643}
644
645/**
646 * Returns the amount of bytes ready to be read from a sink since the last call
647 * to AudioMixerSinkUpdate().
648 *
649 * @returns Amount of bytes ready to be read from the sink.
650 * @param pSink Sink to return number of available samples for.
651 */
652uint32_t AudioMixerSinkGetReadable(PAUDMIXSINK pSink)
653{
654 AssertPtrReturn(pSink, 0);
655
656 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT, ("Can't read from a non-input sink\n"));
657
658#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
659# error "Implement me!"
660#else
661 Log3Func(("[%s]: cbReadable=%RU32\n", pSink->pszName, pSink->In.cbReadable));
662 return pSink->In.cbReadable;
663#endif
664}
665
666/**
667 * Returns the amount of bytes ready to be written to a sink since the last call
668 * to AudioMixerSinkUpdate().
669 *
670 * @returns Amount of bytes ready to be written to the sink.
671 * @param pSink Sink to return number of available samples for.
672 */
673uint32_t AudioMixerSinkGetWritable(PAUDMIXSINK pSink)
674{
675 AssertPtrReturn(pSink, 0);
676
677 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT, ("Can't write to a non-output sink\n"));
678
679#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
680# error "Implement me!"
681#else
682 Log3Func(("[%s]: cbWritable=%RU32\n", pSink->pszName, pSink->Out.cbWritable));
683 return pSink->Out.cbWritable;
684#endif
685}
686
687/**
688 * Returns the sink's mixing direction.
689 *
690 * @returns Mixing direction.
691 * @param pSink Sink to return direction for.
692 */
693AUDMIXSINKDIR AudioMixerSinkGetDir(PAUDMIXSINK pSink)
694{
695 AssertPtrReturn(pSink, AUDMIXSINKDIR_UNKNOWN);
696 return pSink->enmDir;
697}
698
699/**
700 * Returns a specific mixer stream from a sink, based on its index.
701 *
702 * @returns Mixer stream if found, or NULL if not found.
703 * @param pSink Sink to retrieve mixer stream from.
704 * @param uIndex Index of the mixer stream to return.
705 */
706PAUDMIXSTREAM AudioMixerSinkGetStream(PAUDMIXSINK pSink, uint8_t uIndex)
707{
708 AssertPtrReturn(pSink, NULL);
709 AssertMsgReturn(uIndex < pSink->cStreams,
710 ("Index %RU8 exceeds stream count (%RU8)", uIndex, pSink->cStreams), NULL);
711
712 /* Slow lookup, d'oh. */
713 PAUDMIXSTREAM pStream = RTListGetFirst(&pSink->lstStreams, AUDMIXSTREAM, Node);
714 while (uIndex)
715 {
716 pStream = RTListGetNext(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node);
717 uIndex--;
718 }
719
720 AssertPtr(pStream);
721 return pStream;
722}
723
724/**
725 * Returns the current status of a mixer sink.
726 *
727 * @returns IPRT status code.
728 * @param pSink Mixer sink to return status for.
729 */
730AUDMIXSINKSTS AudioMixerSinkGetStatus(PAUDMIXSINK pSink)
731{
732 if (!pSink)
733 return false;
734
735 LogFlowFunc(("[%s]: fStatus=0x%x\n", pSink->pszName, pSink->fStatus));
736
737 /* If the dirty flag is set, there is unprocessed data in the sink. */
738 return pSink->fStatus;
739}
740
741/**
742 * Returns the number of attached mixer streams to a mixer sink.
743 *
744 * @returns IPRT status code.
745 * @param pSink Mixer sink to return number for.
746 */
747uint8_t AudioMixerSinkGetStreamCount(PAUDMIXSINK pSink)
748{
749 if (!pSink)
750 return 0;
751
752 return pSink->cStreams;
753}
754
755/**
756 * Reads audio data from a mixer sink.
757 *
758 * @returns IPRT status code.
759 * @param pSink Mixer sink to read data from.
760 * @param enmOp Mixer operation to use for reading the data.
761 * @param pvBuf Buffer where to store the read data.
762 * @param cbBuf Buffer size (in bytes) where to store the data.
763 * @param pcbRead Number of bytes read. Optional.
764 */
765int AudioMixerSinkRead(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
766{
767 RT_NOREF(enmOp);
768 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
769 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
770 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
771 /* pcbRead is optional. */
772
773 /** @todo Handle mixing operation enmOp! */
774
775 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT,
776 ("Can't read from a sink which is not an input sink\n"));
777
778#ifndef VBOX_AUDIO_MIXER_WITH_MIXBUF
779 uint8_t *pvMixBuf = (uint8_t *)RTMemAlloc(cbBuf);
780 if (!pvMixBuf)
781 return VERR_NO_MEMORY;
782#endif
783
784 int rc = VERR_NOT_FOUND;
785 uint32_t cbRead = 0;
786
787 /* Flag indicating whether this sink is in a 'clean' state,
788 * e.g. there is no more data to read from. */
789 bool fClean = true;
790
791 PAUDMIXSTREAM pMixStream;
792 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
793 {
794 if (!(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTRMSTS_FLAG_ENABLED))
795 {
796 LogFlowFunc(("%s: Stream '%s' Disabled, skipping ...\n", pMixStream->pszName, pMixStream->pStream->szName));
797 continue;
798 }
799
800 uint32_t cbTotalRead = 0;
801 uint32_t cbToRead = cbBuf;
802
803 while (cbToRead)
804 {
805 uint32_t cbReadStrm;
806 AssertPtr(pMixStream->pConn);
807#ifndef VBOX_AUDIO_MIXER_WITH_MIXBUF
808 rc = pMixStream->pConn->pfnStreamRead(pMixStream->pConn, pMixStream->pStream,
809 (uint8_t *)pvMixBuf + cbTotalRead, cbToRead, &cbReadStrm);
810#endif
811 if ( RT_FAILURE(rc)
812 || !cbReadStrm)
813 break;
814
815 /** @todo Right now we only handle one stream (the last one added in fact). */
816
817 AssertBreakStmt(cbReadStrm <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
818 cbToRead -= cbReadStrm;
819 cbTotalRead += cbReadStrm;
820 }
821
822 if (RT_FAILURE(rc))
823 continue;
824
825 cbRead = RT_MAX(cbRead, cbTotalRead);
826
827 PDMAUDIOSTRMSTS strmSts = pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream);
828 fClean &= !(strmSts & PDMAUDIOSTRMSTS_FLAG_DATA_READABLE);
829 }
830
831 if (RT_SUCCESS(rc))
832 {
833 if (fClean)
834 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
835
836#ifndef VBOX_AUDIO_MIXER_WITH_MIXBUF
837 if (cbRead)
838 memcpy(pvBuf, pvMixBuf, cbRead);
839#endif
840 if (pcbRead)
841 *pcbRead = cbRead;
842 }
843
844#ifndef VBOX_AUDIO_MIXER_WITH_MIXBUF
845 RTMemFree(pvMixBuf);
846#endif
847
848 Log3Func(("[%s]: cbRead=%RU32, fStatus=0x%x, rc=%Rrc\n", pSink->pszName, cbRead, pSink->fStatus, rc));
849 return rc;
850}
851
852/**
853 * Removes a mixer stream from a mixer sink, internal version.
854 *
855 * @returns IPRT status code.
856 * @param pSink Sink to remove mixer stream from.
857 * @param pStream Stream to remove.
858 */
859static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
860{
861 AssertPtrReturn(pSink, VERR_INVALID_PARAMETER);
862 if ( !pStream
863 || !pStream->pSink) /* Not part of a sink anymore? */
864 {
865 return VERR_NOT_FOUND;
866 }
867
868 AssertMsgReturn(pStream->pSink == pSink, ("Stream '%s' is not part of sink '%s'\n",
869 pStream->pszName, pSink->pszName), VERR_NOT_FOUND);
870
871 LogFlowFunc(("[%s]: (Stream = %s), cStreams=%RU8\n",
872 pSink->pszName, pStream->pStream->szName, pSink->cStreams));
873
874#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
875 /* Unlink mixing buffer. */
876 AudioMixBufUnlink(&pStream->pStream->MixBuf);
877#endif
878
879 /* Remove stream from sink. */
880 RTListNodeRemove(&pStream->Node);
881
882 /* Set sink to NULL so that we know we're not part of any sink anymore. */
883 pStream->pSink = NULL;
884
885 return VINF_SUCCESS;
886}
887
888/**
889 * Removes a mixer stream from a mixer sink.
890 *
891 * @param pSink Sink to remove mixer stream from.
892 * @param pStream Stream to remove.
893 */
894void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
895{
896 int rc = audioMixerSinkRemoveStreamInternal(pSink, pStream);
897 if (RT_SUCCESS(rc))
898 {
899 Assert(pSink->cStreams);
900 pSink->cStreams--;
901 }
902}
903
904/**
905 * Removes all attached streams from a given sink.
906 *
907 * @param pSink Sink to remove attached streams from.
908 */
909static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink)
910{
911 if (!pSink)
912 return;
913
914 LogFunc(("%s\n", pSink->pszName));
915
916 PAUDMIXSTREAM pStream, pStreamNext;
917 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
918 audioMixerSinkRemoveStreamInternal(pSink, pStream);
919}
920
921/**
922 * Resets the sink's state.
923 *
924 * @param pSink Sink to reset.
925 */
926static void audioMixerSinkReset(PAUDMIXSINK pSink)
927{
928 if (!pSink)
929 return;
930
931 LogFunc(("%s\n", pSink->pszName));
932
933 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
934 {
935#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
936 AudioMixBufReset(&pSink->MixBuf);
937#else
938 pSink->In.cbReadable = 0;
939#endif
940 }
941 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
942 {
943#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
944 AudioMixBufReset(&pSink->MixBuf);
945#else
946 pSink->Out.cbWritable = 0;
947#endif
948 }
949
950 /* Update last updated timestamp. */
951 pSink->tsLastUpdatedMS = RTTimeMilliTS();
952
953 /* Reset status. */
954 pSink->fStatus = AUDMIXSINK_STS_NONE;
955}
956
957/**
958 * Removes all attached streams from a given sink.
959 *
960 * @param pSink Sink to remove attached streams from.
961 */
962void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
963{
964 if (!pSink)
965 return;
966
967 audioMixerSinkRemoveAllStreamsInternal(pSink);
968
969 pSink->cStreams = 0;
970}
971
972/**
973 * Sets the audio format of a mixer sink.
974 *
975 * @returns IPRT status code.
976 * @param pSink Sink to set audio format for.
977 * @param pPCMProps Audio format (PCM properties) to set.
978 */
979int AudioMixerSinkSetFormat(PAUDMIXSINK pSink, PPDMPCMPROPS pPCMProps)
980{
981 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
982 AssertPtrReturn(pPCMProps, VERR_INVALID_POINTER);
983
984 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, pPCMProps)) /* Bail out early if PCM properties are equal. */
985 return VINF_SUCCESS;
986
987 if (pSink->PCMProps.uHz)
988 LogFlowFunc(("[%s]: Old format: %RU8 bit, %RU8 channels, %RU32Hz\n",
989 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
990
991 memcpy(&pSink->PCMProps, pPCMProps, sizeof(PDMPCMPROPS));
992
993 LogFlowFunc(("[%s]: New format %RU8 bit, %RU8 channels, %RU32Hz\n",
994 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
995
996 int rc = VINF_SUCCESS;
997
998#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
999 /* Also update the sink's mixing buffer format. */
1000 AudioMixBufDestroy(&pSink->MixBuf);
1001 rc = AudioMixBufInit(&pSink->MixBuf, pSink->pszName, pPCMProps, _4K /** @todo Make configurable? */);
1002 if (RT_SUCCESS(rc))
1003 {
1004 PAUDMIXSTREAM pStream;
1005 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
1006 {
1007 /** @todo Invalidate mix buffers! */
1008 }
1009 }
1010#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
1011
1012 LogFlowFuncLeaveRC(rc);
1013 return rc;
1014}
1015
1016/**
1017 * Set the volume of an individual sink.
1018 *
1019 * @returns IPRT status code.
1020 * @param pSink Sink to set volume for.
1021 * @param pVol Volume to set.
1022 */
1023int AudioMixerSinkSetVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
1024{
1025 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1026 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
1027
1028 memcpy(&pSink->Volume, pVol, sizeof(PDMAUDIOVOLUME));
1029
1030 LogFlowFunc(("[%s]: fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1031 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1032
1033 AssertPtr(pSink->pParent);
1034 return audioMixerSinkUpdateVolume(pSink, &pSink->pParent->VolMaster);
1035}
1036
1037/**
1038 * Updates a mixer sink, internal version.
1039 *
1040 * @returns IPRT status code.
1041 * @param pSink Mixer sink to update.
1042 */
1043static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink)
1044{
1045 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1046
1047 int rc = VINF_SUCCESS;
1048
1049 Log3Func(("[%s] fStatus=0x%x\n", pSink->pszName, pSink->fStatus));
1050
1051 /* Sink disabled? Take a shortcut. */
1052 if (!(pSink->fStatus & AUDMIXSINK_STS_RUNNING))
1053 return rc;
1054
1055 /* Number of detected disabled streams of this sink. */
1056 uint8_t cStreamsDisabled = 0;
1057
1058 /* Get the time delta and calculate the bytes that need to be processed. */
1059 uint64_t tsDeltaMS = RTTimeMilliTS() - pSink->tsLastUpdatedMS;
1060 uint32_t cbDelta = (pSink->PCMProps.cbBitrate / 1000 /* s to ms */) * tsDeltaMS;
1061
1062 Log3Func(("[%s] Bitrate is %RU32 bytes/s -> %RU64ms / %RU32 bytes elapsed\n",
1063 pSink->pszName, pSink->PCMProps.cbBitrate, tsDeltaMS, cbDelta));
1064
1065 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1066 {
1067 pSink->In.cbReadable = cbDelta;
1068 }
1069 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1070 {
1071 pSink->Out.cbWritable = cbDelta;
1072 }
1073
1074 uint8_t uCurLUN = 0;
1075
1076 PAUDMIXSTREAM pMixStream, pMixStreamNext;
1077 RTListForEachSafe(&pSink->lstStreams, pMixStream, pMixStreamNext, AUDMIXSTREAM, Node)
1078 {
1079 PPDMAUDIOSTREAM pStream = pMixStream->pStream;
1080 AssertPtr(pStream);
1081
1082 PPDMIAUDIOCONNECTOR pConn = pMixStream->pConn;
1083 AssertPtr(pConn);
1084
1085 uint32_t cCaptured = 0;
1086
1087 int rc2 = pConn->pfnStreamIterate(pConn, pStream);
1088 if (RT_SUCCESS(rc2))
1089 {
1090 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1091 {
1092 rc = pConn->pfnStreamCapture(pConn, pMixStream->pStream, &cCaptured);
1093 if (RT_FAILURE(rc2))
1094 {
1095 LogFlowFunc(("%s: Failed capturing stream '%s', rc=%Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc2));
1096 if (RT_SUCCESS(rc))
1097 rc = rc2;
1098 continue;
1099 }
1100
1101 if (cCaptured)
1102 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1103 }
1104 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1105 {
1106 rc2 = pConn->pfnStreamPlay(pConn, pMixStream->pStream, NULL /* cPlayed */);
1107 if (RT_FAILURE(rc2))
1108 {
1109 LogFlowFunc(("%s: Failed playing stream '%s', rc=%Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc2));
1110 if (RT_SUCCESS(rc))
1111 rc = rc2;
1112 continue;
1113 }
1114 }
1115 else
1116 {
1117 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1118 continue;
1119 }
1120
1121 rc2 = pConn->pfnStreamIterate(pConn, pStream);
1122 if (RT_FAILURE(rc2))
1123 {
1124 LogFlowFunc(("%s: Failed re-iterating stream '%s', rc=%Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc2));
1125 if (RT_SUCCESS(rc))
1126 rc = rc2;
1127 continue;
1128 }
1129
1130 PDMAUDIOSTRMSTS strmSts = pConn->pfnStreamGetStatus(pConn, pMixStream->pStream);
1131
1132 /* Is the stream not enabled and also is not in a pending disable state anymore? */
1133 if ( !(strmSts & PDMAUDIOSTRMSTS_FLAG_ENABLED)
1134 && !(strmSts & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE))
1135 {
1136 cStreamsDisabled++;
1137 }
1138
1139 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1140 {
1141#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1142# error "Implement me!"
1143#else
1144 if (uCurLUN == 0)
1145 {
1146 pSink->In.cbReadable = pConn->pfnStreamGetReadable(pConn, pMixStream->pStream);
1147 Log3Func(("\t%s: cbReadable=%RU32\n", pMixStream->pStream->szName, pSink->In.cbReadable));
1148 uCurLUN++;
1149 }
1150#endif
1151 }
1152 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1153 {
1154#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1155# error "Implement me!"
1156#else
1157 if (uCurLUN == 0)
1158 {
1159 pSink->Out.cbWritable = pConn->pfnStreamGetWritable(pConn, pMixStream->pStream);
1160 Log3Func(("\t%s: cbWritable=%RU32\n", pMixStream->pStream->szName, pSink->Out.cbWritable));
1161 uCurLUN++;
1162 }
1163#endif
1164 }
1165 }
1166
1167 Log3Func(("\t%s: cCaptured=%RU32\n", pMixStream->pStream->szName, cCaptured));
1168 }
1169
1170 /* All streams disabled and the sink is in pending disable mode? */
1171 if ( cStreamsDisabled == pSink->cStreams
1172 && (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
1173 {
1174 audioMixerSinkReset(pSink);
1175 }
1176
1177 /* Update last updated timestamp. */
1178 pSink->tsLastUpdatedMS = RTTimeMilliTS();
1179
1180 Log3Func(("[%s] cbReadable=%RU32, cbWritable=%RU32, rc=%Rrc\n",
1181 pSink->pszName, pSink->In.cbReadable, pSink->Out.cbWritable, rc));
1182
1183 return rc;
1184}
1185
1186/**
1187 * Updates (invalidates) a mixer sink.
1188 *
1189 * @returns IPRT status code.
1190 * @param pSink Mixer sink to update.
1191 */
1192int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
1193{
1194 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1195
1196 /*
1197 * Note: Hz elapsed = cTicksElapsed / cTimerTicks
1198 * Bytes / second = Sample rate (Hz) * Audio channels * Bytes per sample
1199 */
1200// uint32_t cSamples = (int)((pSink->PCMProps.cChannels * cTicksElapsed * pSink->PCMProps.uHz + cTimerTicks) / cTimerTicks / pSink->PCMProps.cChannels);
1201
1202// LogFlowFunc(("[%s]: cTimerTicks=%RU64, cTicksElapsed=%RU64\n", pSink->pszName, cTimerTicks, cTicksElapsed));
1203// LogFlowFunc(("\t%zuHz elapsed, %RU32 samples (%RU32 bytes)\n", cTicksElapsed / cTimerTicks, cSamples, cSamples << 1));
1204
1205 return audioMixerSinkUpdateInternal(pSink);
1206}
1207
1208/**
1209 * Updates the (master) volume of a mixer sink.
1210 *
1211 * @returns IPRT status code.
1212 * @param pSink Mixer sink to update volume for.
1213 * @param pVolMaster Master volume to set.
1214 */
1215static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
1216{
1217 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1218 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
1219
1220 LogFlowFunc(("[%s]: Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1221 pSink->pszName, pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
1222 LogFlowFunc(("[%s]: fMuted=%RTbool, lVol=%RU32, rVol=%RU32 ",
1223 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1224
1225 /** @todo Very crude implementation for now -- needs more work! */
1226
1227 pSink->VolumeCombined.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
1228
1229 pSink->VolumeCombined.uLeft = ( (pSink->Volume.uLeft ? pSink->Volume.uLeft : 1)
1230 * (pVolMaster->uLeft ? pVolMaster->uLeft : 1)) / PDMAUDIO_VOLUME_MAX;
1231
1232 pSink->VolumeCombined.uRight = ( (pSink->Volume.uRight ? pSink->Volume.uRight : 1)
1233 * (pVolMaster->uRight ? pVolMaster->uRight : 1)) / PDMAUDIO_VOLUME_MAX;
1234
1235 LogFlow(("-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1236 pSink->VolumeCombined.fMuted, pSink->VolumeCombined.uLeft, pSink->VolumeCombined.uRight));
1237
1238 /* Propagate new sink volume to all streams in the sink. */
1239 PAUDMIXSTREAM pMixStream;
1240 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1241 {
1242 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &pSink->VolumeCombined);
1243 AssertRC(rc2);
1244 }
1245
1246 return VINF_SUCCESS;
1247}
1248
1249/**
1250 * Writes data to a mixer sink.
1251 *
1252 * @returns IPRT status code.
1253 * @param pSink Sink to write data to.
1254 * @param enmOp Mixer operation to use when writing data to the sink.
1255 * @param pvBuf Buffer containing the audio data to write.
1256 * @param cbBuf Size (in bytes) of the buffer containing the audio data.
1257 * @param pcbWritten Number of bytes written. Optional.
1258 */
1259int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
1260{
1261 RT_NOREF(enmOp);
1262 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1263 /* pcbWritten is optional. */
1264
1265 if (!pvBuf || !cbBuf)
1266 {
1267 if (pcbWritten)
1268 *pcbWritten = 0;
1269 return VINF_SUCCESS;
1270 }
1271
1272 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
1273 ("Can't write to a sink which is not an output sink\n"));
1274
1275 LogFlowFunc(("%s: enmOp=%d, cbBuf=%RU32\n", pSink->pszName, enmOp, cbBuf));
1276
1277 uint32_t cbProcessed;
1278
1279 PAUDMIXSTREAM pMixStream;
1280 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1281 {
1282 if (!(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTRMSTS_FLAG_ENABLED))
1283 {
1284 LogFlowFunc(("%s: Stream '%s' Disabled, skipping ...\n", pMixStream->pszName, pMixStream->pStream->szName));
1285 continue;
1286 }
1287
1288 int rc2 = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, pvBuf, cbBuf, &cbProcessed);
1289 if (RT_FAILURE(rc2))
1290 LogFlowFunc(("%s: Failed writing to stream '%s': %Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc2));
1291
1292 if (cbProcessed < cbBuf)
1293 {
1294 LogFlowFunc(("%s: Only written %RU32/%RU32 bytes for stream '%s'\n",
1295 pSink->pszName, cbProcessed, cbBuf, pMixStream->pStream->szName));
1296 }
1297 }
1298
1299 if (cbBuf)
1300 {
1301 /* Set dirty bit. */
1302 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1303 }
1304
1305 if (pcbWritten)
1306 *pcbWritten = cbBuf; /* Always report back a complete write for now. */
1307
1308 return VINF_SUCCESS;
1309}
1310
1311/*********************************************************************************************************************************
1312 * Mixer Stream implementation.
1313 ********************************************************************************************************************************/
1314
1315/**
1316 * Controls a mixer stream.
1317 *
1318 * @returns IPRT status code.
1319 * @param pMixStream Mixer stream to control.
1320 * @param enmCmd Mixer stream command to use.
1321 * @param fCtl Additional control flags. Pass 0.
1322 */
1323int AudioMixerStreamCtl(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
1324{
1325 RT_NOREF(fCtl);
1326 AssertPtrReturn(pMixStream, VERR_INVALID_POINTER);
1327 /** @todo Validate fCtl. */
1328
1329 LogFlowFunc(("[%s] enmCmd=%d\n", pMixStream->pszName, enmCmd));
1330
1331 return pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
1332}
1333
1334/**
1335 * Destroys a mixer stream, internal version.
1336 *
1337 * @param pMixStream Mixer stream to destroy.
1338 */
1339static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream)
1340{
1341 AssertPtrReturnVoid(pMixStream);
1342
1343 LogFunc(("%s\n", pMixStream->pszName));
1344
1345 if (pMixStream->pConn) /* Stream has a connector interface present? */
1346 {
1347 if (pMixStream->pStream)
1348 {
1349 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
1350 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
1351
1352 pMixStream->pStream = NULL;
1353 }
1354
1355 pMixStream->pConn = NULL;
1356 }
1357
1358 if (pMixStream->pszName)
1359 {
1360 RTStrFree(pMixStream->pszName);
1361 pMixStream->pszName = NULL;
1362 }
1363
1364 RTMemFree(pMixStream);
1365 pMixStream = NULL;
1366}
1367
1368/**
1369 * Destroys a mixer stream.
1370 *
1371 * @param pMixStream Mixer stream to destroy.
1372 */
1373void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream)
1374{
1375 if (!pMixStream)
1376 return;
1377
1378 LogFunc(("%s\n", pMixStream->pszName));
1379
1380 int rc;
1381
1382 if (pMixStream->pSink) /* Is the stream part of a sink? */
1383 {
1384 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
1385 * pointer will be gone from the stream. */
1386 PAUDMIXSINK pSink = pMixStream->pSink;
1387
1388 rc = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
1389 if (RT_SUCCESS(rc))
1390 {
1391 Assert(pSink->cStreams);
1392 pSink->cStreams--;
1393 }
1394 }
1395 else
1396 rc = VINF_SUCCESS;
1397
1398 if (RT_SUCCESS(rc))
1399 audioMixerStreamDestroyInternal(pMixStream);
1400
1401 LogFlowFunc(("Returning %Rrc\n", rc));
1402}
1403
1404/**
1405 * Returns whether a mixer stream currently is active (playing/recording) or not.
1406 *
1407 * @returns @true if playing/recording, @false if not.
1408 * @param pMixStream Mixer stream to return status for.
1409 */
1410bool AudioMixerStreamIsActive(PAUDMIXSTREAM pMixStream)
1411{
1412 bool fIsActive =
1413 pMixStream
1414 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTRMSTS_FLAG_ENABLED);
1415
1416 return fIsActive;
1417}
1418
1419/**
1420 * Returns whether a mixer stream is valid (e.g. initialized and in a working state) or not.
1421 *
1422 * @returns @true if valid, @false if not.
1423 * @param pMixStream Mixer stream to return status for.
1424 */
1425bool AudioMixerStreamIsValid(PAUDMIXSTREAM pMixStream)
1426{
1427 bool fIsValid =
1428 pMixStream
1429 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTRMSTS_FLAG_INITIALIZED);
1430
1431 return fIsValid;
1432}
1433
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