VirtualBox

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

Last change on this file since 63848 was 63846, checked in by vboxsync, 8 years ago

Audio/AudioMixer.cpp: Logging.

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