VirtualBox

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

Last change on this file since 64816 was 64569, checked in by vboxsync, 8 years ago

Build fix.

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