VirtualBox

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

Last change on this file since 61541 was 61523, checked in by vboxsync, 9 years ago

Audio: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.5 KB
Line 
1/* $Id: AudioMixer.cpp 61523 2016-06-07 09:47:21Z 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 int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink);
58
59static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pStream);
60
61
62int AudioMixerCreateSink(PAUDIOMIXER pMixer, const char *pszName, AUDMIXSINKDIR enmDir, PAUDMIXSINK *ppSink)
63{
64 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
65 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
66 /* ppSink is optional. */
67
68 int rc = VINF_SUCCESS;
69
70 PAUDMIXSINK pSink = (PAUDMIXSINK)RTMemAllocZ(sizeof(AUDMIXSINK));
71 if (pSink)
72 {
73 pSink->pszName = RTStrDup(pszName);
74 if (!pSink->pszName)
75 rc = VERR_NO_MEMORY;
76
77 if (RT_SUCCESS(rc))
78 {
79 pSink->pParent = pMixer;
80 pSink->cStreams = 0;
81 pSink->enmDir = enmDir;
82 RTListInit(&pSink->lstStreams);
83
84 /* Set initial volume to max. */
85 pSink->Volume.fMuted = false;
86 pSink->Volume.uLeft = 0x7F;
87 pSink->Volume.uRight = 0x7F;
88
89 RTListAppend(&pMixer->lstSinks, &pSink->Node);
90 pMixer->cSinks++;
91
92 LogFlowFunc(("pMixer=%p, pSink=%p, cSinks=%RU8\n",
93 pMixer, pSink, pMixer->cSinks));
94
95 if (ppSink)
96 *ppSink = pSink;
97 }
98 else
99 RTMemFree(pSink);
100 }
101 else
102 rc = VERR_NO_MEMORY;
103
104 return rc;
105}
106
107int AudioMixerCreate(const char *pszName, uint32_t uFlags, PAUDIOMIXER *ppMixer)
108{
109 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
110 /** @todo Add flag validation. */
111 AssertPtrReturn(ppMixer, VERR_INVALID_POINTER);
112
113 int rc = VINF_SUCCESS;
114
115 PAUDIOMIXER pMixer = (PAUDIOMIXER)RTMemAllocZ(sizeof(AUDIOMIXER));
116 if (pMixer)
117 {
118 pMixer->pszName = RTStrDup(pszName);
119 if (!pMixer->pszName)
120 rc = VERR_NO_MEMORY;
121
122 if (RT_SUCCESS(rc))
123 {
124 pMixer->cSinks = 0;
125 RTListInit(&pMixer->lstSinks);
126
127 pMixer->VolMaster.fMuted = false;
128 pMixer->VolMaster.uLeft = UINT32_MAX;
129 pMixer->VolMaster.uRight = UINT32_MAX;
130
131 LogFlowFunc(("Created mixer '%s'\n", pMixer->pszName));
132
133 *ppMixer = pMixer;
134 }
135 else
136 RTMemFree(pMixer);
137 }
138 else
139 rc = VERR_NO_MEMORY;
140
141 LogFlowFuncLeaveRC(rc);
142 return rc;
143}
144
145void AudioMixerDebug(PAUDIOMIXER pMixer, PCDBGFINFOHLP pHlp, const char *pszArgs)
146{
147 PAUDMIXSINK pSink;
148 unsigned iSink = 0;
149
150 pHlp->pfnPrintf(pHlp, "[Master] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", pMixer->pszName,
151 pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight, pMixer->VolMaster.fMuted);
152
153 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
154 {
155 pHlp->pfnPrintf(pHlp, "[Sink %u] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", iSink, pSink->pszName,
156 pSink->Volume.uLeft, pSink->Volume.uRight, pSink->Volume.fMuted);
157 ++iSink;
158 }
159}
160
161void AudioMixerDestroy(PAUDIOMIXER pMixer)
162{
163 if (!pMixer)
164 return;
165
166 LogFlowFunc(("Destroying %s ...\n", pMixer->pszName));
167
168 PAUDMIXSINK pSink, pSinkNext;
169 RTListForEachSafe(&pMixer->lstSinks, pSink, pSinkNext, AUDMIXSINK, Node)
170 {
171 /* Save a pointer to the sink to remove, as pSink
172 * will not be valid anymore after calling audioMixerRemoveSinkInternal(). */
173 PAUDMIXSINK pSinkToRemove = pSink;
174
175 audioMixerRemoveSinkInternal(pMixer, pSinkToRemove);
176 audioMixerSinkDestroyInternal(pSinkToRemove);
177 }
178
179 pMixer->cSinks = 0;
180
181 if (pMixer->pszName)
182 {
183 RTStrFree(pMixer->pszName);
184 pMixer->pszName = NULL;
185 }
186
187 RTMemFree(pMixer);
188 pMixer = NULL;
189}
190
191int AudioMixerGetDeviceFormat(PAUDIOMIXER pMixer, PPDMAUDIOSTREAMCFG pCfg)
192{
193 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
194 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
195
196 /** @todo Perform a deep copy, if needed. */
197 *pCfg = pMixer->devFmt;
198
199 return VINF_SUCCESS;
200}
201
202void AudioMixerInvalidate(PAUDIOMIXER pMixer)
203{
204 AssertPtrReturnVoid(pMixer);
205
206 LogFlowFunc(("[%s]: Invalidating ...\n", pMixer->pszName));
207
208 /* Propagate new master volume to all connected sinks. */
209 PAUDMIXSINK pSink;
210 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
211 {
212 int rc2 = audioMixerSinkUpdateVolume(pSink, &pMixer->VolMaster);
213 AssertRC(rc2);
214 }
215}
216
217static int audioMixerRemoveSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
218{
219 AssertPtrReturn(pMixer, VERR_INVALID_PARAMETER);
220 if (!pSink)
221 return VERR_NOT_FOUND;
222
223 AssertMsgReturn(pSink->pParent == pMixer, ("Sink '%s' is not part of mixer '%s'\n",
224 pSink->pszName, pMixer->pszName), VERR_NOT_FOUND);
225
226 LogFlowFunc(("[%s]: pSink=%s, cSinks=%RU8\n",
227 pMixer->pszName, pSink->pszName, pMixer->cSinks));
228
229 /* Remove sink from mixer. */
230 RTListNodeRemove(&pSink->Node);
231 Assert(pMixer->cSinks);
232 pMixer->cSinks--;
233
234 /* Set mixer to NULL so that we know we're not part of any mixer anymore. */
235 pSink->pParent = NULL;
236
237 return VINF_SUCCESS;
238}
239
240void AudioMixerRemoveSink(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
241{
242 audioMixerSinkRemoveAllStreamsInternal(pSink);
243 audioMixerRemoveSinkInternal(pMixer, pSink);
244}
245
246int AudioMixerSetDeviceFormat(PAUDIOMIXER pMixer, PPDMAUDIOSTREAMCFG pCfg)
247{
248 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
249 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
250
251 /** @todo Perform a deep copy, if needed. */
252 pMixer->devFmt = *pCfg;
253
254 return VINF_SUCCESS;
255}
256
257/**
258 * Sets the mixer's master volume.
259 *
260 * @returns IPRT status code.
261 * @param pMixer Mixer to set master volume for.
262 * @param pVol Volume to set.
263 */
264int AudioMixerSetMasterVolume(PAUDIOMIXER pMixer, PPDMAUDIOVOLUME pVol)
265{
266 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
267 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
268
269 pMixer->VolMaster = *pVol;
270
271 LogFlowFunc(("[%s]: lVol=%RU32, rVol=%RU32 => fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
272 pMixer->pszName, pVol->uLeft, pVol->uRight,
273 pMixer->VolMaster.fMuted, pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight));
274
275 AudioMixerInvalidate(pMixer);
276 return VINF_SUCCESS;
277}
278
279/*********************************************************************************************************************************
280 * Mixer Sink implementation.
281 ********************************************************************************************************************************/
282
283int AudioMixerSinkAddStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
284{
285 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
286 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
287
288 if (pSink->cStreams == UINT8_MAX) /* 255 streams per sink max. */
289 return VERR_NO_MORE_HANDLES;
290
291 int rc;
292
293 LogFlowFuncEnter();
294
295#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
296 /* Make sure only compatible streams are added. */
297 if (pStream->enmDir == PDMAUDIODIR_IN)
298 {
299 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, &pStream->InOut.pIn->Props))
300 {
301#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
302 /* Chain: Stream (Child) -> Sink (Child) -> Guest (Parent). */
303 PPDMAUDIOMIXBUF pHstIn = &pStream->InOut.pIn->pHstStrmIn->MixBuf;
304 PPDMAUDIOMIXBUF pGstIn = &pStream->InOut.pIn->MixBuf;
305
306 /* Unlink any former parent from host input. */
307 AudioMixBufUnlink(pHstIn);
308
309 /* Link host input to this sink as a parent. */
310 rc = AudioMixBufLinkTo(pHstIn, &pSink->MixBuf);
311 AssertRC(rc);
312
313 /* Unlink any former parent from this sink. */
314 AudioMixBufUnlink(&pSink->MixBuf);
315
316 /* Link guest input to this sink as a parent. */
317 rc = AudioMixBufLinkTo(&pSink->MixBuf, pGstIn);
318 AssertRC(rc);
319# ifdef DEBUG
320 AudioMixBufDbgPrintChain(&pStream->InOut.pIn->MixBuf);
321# endif
322#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
323 }
324 else
325 rc = VERR_WRONG_TYPE;
326 }
327 else if (pStream->enmDir == PDMAUDIODIR_OUT)
328 {
329 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, &pStream->InOut.pOut->Props))
330 {
331#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
332 /* Chain: Guest (Child) -> Sink (Child) -> Stream (Parent). */
333 rc = AudioMixBufLinkTo(&pStream->InOut.pOut->pHstStrmOut->MixBuf, &pSink->MixBuf);
334# ifdef DEBUG
335 AudioMixBufDbgPrintChain(&pSink->MixBuf);
336# endif
337#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
338 }
339 else
340 rc = VERR_WRONG_TYPE;
341 }
342 else
343 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
344#else
345 rc = VINF_SUCCESS;
346#endif
347
348 if (RT_SUCCESS(rc))
349 {
350 /** @todo Check if stream already is assigned to (another) sink. */
351
352 /* Save pointer to sink the stream is attached to. */
353 pStream->pSink = pSink;
354
355 /* Append stream to sink's list. */
356 RTListAppend(&pSink->lstStreams, &pStream->Node);
357 pSink->cStreams++;
358 }
359
360 LogFlowFunc(("[%s]: cStreams=%RU8, rc=%Rrc\n", pSink->pszName, pSink->cStreams, rc));
361 return rc;
362}
363
364int AudioMixerSinkCreateStream(PAUDMIXSINK pSink,
365 PPDMIAUDIOCONNECTOR pConn, PPDMAUDIOSTREAMCFG pCfg, uint32_t fFlags, PAUDMIXSTREAM *ppStream)
366{
367 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
368 AssertPtrReturn(pConn, VERR_INVALID_POINTER);
369 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
370 /** @todo Validate fFlags. */
371 /* ppStream is optional. */
372
373 PAUDMIXSTREAM pMixStream = (PAUDMIXSTREAM)RTMemAllocZ(sizeof(AUDMIXSTREAM));
374 if (!pMixStream)
375 return VERR_NO_MEMORY;
376
377 pMixStream->pszName = RTStrDup(pCfg->szName);
378 if (!pMixStream->pszName)
379 {
380 RTMemFree(pMixStream);
381 return VERR_NO_MEMORY;
382 }
383
384 LogFlowFunc(("[%s]: fFlags=0x%x (enmDir=%ld, %s, %RU8 channels, %RU32Hz)\n",
385 pSink->pszName, fFlags, pCfg->enmDir, DrvAudioHlpAudFmtToStr(pCfg->enmFormat), pCfg->cChannels, pCfg->uHz));
386
387 PDMAUDIOSTREAMCFG CfgSink;
388 int rc = DrvAudioHlpPCMPropsToStreamCfg(&pSink->PCMProps, &CfgSink);
389 AssertRCReturn(rc, rc);
390
391 /* Apply the sink's direction for the configuration to use to
392 * create the stream. */
393 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
394 {
395 CfgSink.DestSource.Source = PDMAUDIORECSOURCE_UNKNOWN;
396 CfgSink.enmDir = PDMAUDIODIR_IN;
397 }
398 else
399 {
400 CfgSink.DestSource.Dest = PDMAUDIOPLAYBACKDEST_UNKNOWN;
401 CfgSink.enmDir = PDMAUDIODIR_OUT;
402 }
403
404 RTStrPrintf(CfgSink.szName, sizeof(CfgSink.szName), "%s", pCfg->szName);
405
406 /* Always use the sink's PCM audio format as the host side when creating a stream for it. */
407 PPDMAUDIOSTREAM pStream;
408 rc = pConn->pfnStreamCreate(pConn, &CfgSink, pCfg, &pStream);
409 if (RT_SUCCESS(rc))
410 {
411 /* Save the audio stream pointer to this mixing stream. */
412 pMixStream->pStream = pStream;
413
414 /* Increase the stream's reference count to let others know
415 * we're reyling on it to be around now. */
416 pConn->pfnStreamAddRef(pConn, pStream);
417 }
418
419 if (RT_SUCCESS(rc))
420 {
421 pMixStream->fFlags = fFlags;
422 pMixStream->pConn = pConn;
423
424 if (ppStream)
425 *ppStream = pMixStream;
426 }
427 else if (pMixStream)
428 {
429 RTStrFree(pMixStream->pszName);
430 pMixStream->pszName = NULL;
431
432 RTMemFree(pMixStream);
433 pMixStream = NULL;
434 }
435
436 return rc;
437}
438
439static PDMAUDIOSTREAMCMD audioMixerSinkToStreamCmd(AUDMIXSINKCMD enmCmd)
440{
441 switch (enmCmd)
442 {
443 case AUDMIXSINKCMD_ENABLE: return PDMAUDIOSTREAMCMD_ENABLE;
444 case AUDMIXSINKCMD_DISABLE: return PDMAUDIOSTREAMCMD_DISABLE;
445 case AUDMIXSINKCMD_PAUSE: return PDMAUDIOSTREAMCMD_PAUSE;
446 case AUDMIXSINKCMD_RESUME: return PDMAUDIOSTREAMCMD_RESUME;
447 default: break;
448 }
449
450 AssertMsgFailed(("Unsupported sink command %ld\n", enmCmd));
451 return PDMAUDIOSTREAMCMD_UNKNOWN;
452}
453
454int AudioMixerSinkCtl(PAUDMIXSINK pSink, AUDMIXSINKCMD enmSinkCmd)
455{
456 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
457
458 PDMAUDIOSTREAMCMD enmCmdStream = audioMixerSinkToStreamCmd(enmSinkCmd);
459 if (enmCmdStream == PDMAUDIOSTREAMCMD_UNKNOWN)
460 return VERR_NOT_SUPPORTED;
461
462 int rc = VINF_SUCCESS;
463
464 PAUDMIXSTREAM pStream;
465 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
466 {
467 int rc2 = AudioMixerStreamCtl(pStream, enmCmdStream, AUDMIXSTRMCTL_FLAG_NONE);
468 if (RT_SUCCESS(rc))
469 rc = rc2;
470 /* Keep going. Flag? */
471 }
472
473 /* Remove dirty bit in any case. */
474 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
475
476 if (enmSinkCmd == AUDMIXSINKCMD_ENABLE)
477 pSink->fStatus |= AUDMIXSINK_STS_RUNNING;
478 else if (enmSinkCmd == AUDMIXSINKCMD_DISABLE)
479 pSink->fStatus &= ~AUDMIXSINK_STS_RUNNING;
480
481 LogFlowFunc(("[%s]: enmCmd=%ld, fStatus=0x%x, rc=%Rrc\n", pSink->pszName, enmSinkCmd, pSink->fStatus, rc));
482 return rc;
483}
484
485void AudioMixerSinkDestroy(PAUDMIXSINK pSink)
486{
487 if (!pSink)
488 return;
489
490 if (pSink->pParent)
491 {
492 /* Save sink pointer, as after audioMixerRemoveSinkInternal() the
493 * pointer will be gone from the stream. */
494 PAUDIOMIXER pMixer = pSink->pParent;
495
496 audioMixerRemoveSinkInternal(pMixer, pSink);
497
498 Assert(pMixer->cSinks);
499 pMixer->cSinks--;
500 }
501
502 audioMixerSinkDestroyInternal(pSink);
503}
504
505static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink)
506{
507 AssertPtrReturnVoid(pSink);
508
509 LogFunc(("%s\n", pSink->pszName));
510
511 PAUDMIXSTREAM pStream, pStreamNext;
512 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
513 {
514 /* Save a pointer to the stream to remove, as pStream
515 * will not be valid anymore after calling audioMixerSinkRemoveStreamInternal(). */
516 PAUDMIXSTREAM pStreamToRemove = pStream;
517
518 audioMixerSinkRemoveStreamInternal(pSink, pStreamToRemove);
519 audioMixerStreamDestroyInternal(pStreamToRemove);
520 }
521
522 if (pSink->pszName)
523 {
524 RTStrFree(pSink->pszName);
525 pSink->pszName = NULL;
526 }
527
528 RTMemFree(pSink);
529 pSink = NULL;
530}
531
532/**
533 * Returns the sink's mixing direction.
534 *
535 * @returns Mixing direction.
536 * @param pSink Sink to return direction for.
537 *
538 * @remark
539 */
540AUDMIXSINKDIR AudioMixerSinkGetDir(PAUDMIXSINK pSink)
541{
542 AssertPtrReturn(pSink, AUDMIXSINKDIR_UNKNOWN);
543 return pSink->enmDir;
544}
545
546PAUDMIXSTREAM AudioMixerSinkGetStream(PAUDMIXSINK pSink, uint8_t uIndex)
547{
548 AssertPtrReturn(pSink, NULL);
549 AssertMsgReturn(uIndex < pSink->cStreams,
550 ("Index %RU8 exceeds stream count (%RU8)", uIndex, pSink->cStreams), NULL);
551
552 /* Slow lookup, d'oh. */
553 PAUDMIXSTREAM pStream = RTListGetFirst(&pSink->lstStreams, AUDMIXSTREAM, Node);
554 while (uIndex)
555 {
556 pStream = RTListGetNext(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node);
557 uIndex--;
558 }
559
560 AssertPtr(pStream);
561 return pStream;
562}
563
564AUDMIXSINKSTS AudioMixerSinkGetStatus(PAUDMIXSINK pSink)
565{
566 if (!pSink)
567 return false;
568
569 LogFlowFunc(("[%s]: fStatus=0x%x\n", pSink->pszName, pSink->fStatus));
570
571 /* If the dirty flag is set, there is unprocessed data in the sink. */
572 return pSink->fStatus;
573}
574
575uint8_t AudioMixerSinkGetStreamCount(PAUDMIXSINK pSink)
576{
577 if (!pSink)
578 return 0;
579
580 return pSink->cStreams;
581}
582
583int AudioMixerSinkRead(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
584{
585 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
586 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
587 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
588 /* pcbRead is optional. */
589
590 /** @todo Handle mixing operation enmOp! */
591
592 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT,
593 ("Can't read from a sink which is not an input sink\n"));
594
595#ifndef VBOX_AUDIO_MIXER_WITH_MIXBUF
596 uint8_t *pvMixBuf = (uint8_t *)RTMemAlloc(cbBuf);
597 if (!pvMixBuf)
598 return VERR_NO_MEMORY;
599#endif
600
601 int rc = VERR_NOT_FOUND;
602 uint32_t cbRead = 0;
603
604 /* Flag indicating whether this sink is in a 'clean' state,
605 * e.g. there is no more data to read from. */
606 bool fClean = true;
607
608 PAUDMIXSTREAM pMixStream;
609 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
610 {
611 if (!(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTRMSTS_FLAG_ENABLED))
612 continue;
613
614 uint32_t cbTotalRead = 0;
615 uint32_t cbToRead = cbBuf;
616
617 while (cbToRead)
618 {
619 uint32_t cbReadStrm;
620 AssertPtr(pMixStream->pConn);
621#ifndef VBOX_AUDIO_MIXER_WITH_MIXBUF
622 rc = pMixStream->pConn->pfnStreamRead(pMixStream->pConn, pMixStream->pStream,
623 (uint8_t *)pvMixBuf + cbTotalRead, cbToRead, &cbReadStrm);
624#endif
625 if ( RT_FAILURE(rc)
626 || !cbReadStrm)
627 break;
628
629 /** @todo Right now we only handle one stream (the last one added in fact). */
630
631 AssertBreakStmt(cbReadStrm <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
632 cbToRead -= cbReadStrm;
633 cbTotalRead += cbReadStrm;
634 }
635
636 if (RT_FAILURE(rc))
637 continue;
638
639 cbRead = RT_MAX(cbRead, cbTotalRead);
640
641 PDMAUDIOSTRMSTS strmSts = pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream);
642 fClean &= !(strmSts & PDMAUDIOSTRMSTS_FLAG_DATA_READABLE);
643 }
644
645 if (RT_SUCCESS(rc))
646 {
647 if (fClean)
648 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
649
650#ifndef VBOX_AUDIO_MIXER_WITH_MIXBUF
651 if (cbRead)
652 memcpy(pvBuf, pvMixBuf, cbRead);
653#endif
654 if (pcbRead)
655 *pcbRead = cbRead;
656 }
657
658#ifndef VBOX_AUDIO_MIXER_WITH_MIXBUF
659 RTMemFree(pvMixBuf);
660#endif
661
662 Log3Func(("[%s]: cbRead=%RU32, fStatus=0x%x, rc=%Rrc\n", pSink->pszName, cbRead, pSink->fStatus, rc));
663 return rc;
664}
665
666static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
667{
668 AssertPtrReturn(pSink, VERR_INVALID_PARAMETER);
669 if ( !pStream
670 || !pStream->pSink) /* Not part of a sink anymore? */
671 {
672 return VERR_NOT_FOUND;
673 }
674
675 AssertMsgReturn(pStream->pSink == pSink, ("Stream '%s' is not part of sink '%s'\n",
676 pStream->pszName, pSink->pszName), VERR_NOT_FOUND);
677
678 LogFlowFunc(("[%s]: (Stream = %s), cStreams=%RU8\n",
679 pSink->pszName, pStream->pStream->szName, pSink->cStreams));
680
681#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
682 /* Unlink mixing buffer. */
683 AudioMixBufUnlink(&pStream->pStream->MixBuf);
684#endif
685
686 /* Remove stream from sink. */
687 RTListNodeRemove(&pStream->Node);
688
689 /* Set sink to NULL so that we know we're not part of any sink anymore. */
690 pStream->pSink = NULL;
691
692 return VINF_SUCCESS;
693}
694
695void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
696{
697 int rc = audioMixerSinkRemoveStreamInternal(pSink, pStream);
698 if (RT_SUCCESS(rc))
699 {
700 Assert(pSink->cStreams);
701 pSink->cStreams--;
702 }
703}
704
705/**
706 * Removes all attached streams from a given sink.
707 *
708 * @param pSink Sink to remove attached streams from.
709 */
710static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink)
711{
712 if (!pSink)
713 return;
714
715 LogFunc(("%s\n", pSink->pszName));
716
717 PAUDMIXSTREAM pStream, pStreamNext;
718 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
719 audioMixerSinkRemoveStreamInternal(pSink, pStream);
720}
721
722/**
723 * Removes all attached streams from a given sink.
724 *
725 * @param pSink Sink to remove attached streams from.
726 */
727void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
728{
729 if (!pSink)
730 return;
731
732 audioMixerSinkRemoveAllStreamsInternal(pSink);
733
734 pSink->cStreams = 0;
735}
736
737int AudioMixerSinkSetFormat(PAUDMIXSINK pSink, PPDMPCMPROPS pPCMProps)
738{
739 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
740 AssertPtrReturn(pPCMProps, VERR_INVALID_POINTER);
741
742 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, pPCMProps)) /* Bail out early if PCM properties are equal. */
743 return VINF_SUCCESS;
744
745 if (pSink->PCMProps.uHz)
746 LogFlowFunc(("[%s]: Old format: %RU8 bit, %RU8 channels, %RU32Hz\n",
747 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
748
749 memcpy(&pSink->PCMProps, pPCMProps, sizeof(PDMPCMPROPS));
750
751 LogFlowFunc(("[%s]: New format %RU8 bit, %RU8 channels, %RU32Hz\n",
752 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
753
754 int rc = VINF_SUCCESS;
755
756#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
757 /* Also update the sink's mixing buffer format. */
758 AudioMixBufDestroy(&pSink->MixBuf);
759 rc = AudioMixBufInit(&pSink->MixBuf, pSink->pszName, pPCMProps, _4K /** @todo Make configurable? */);
760 if (RT_SUCCESS(rc))
761 {
762 PAUDMIXSTREAM pStream;
763 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
764 {
765 /** @todo Invalidate mix buffers! */
766 }
767 }
768#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
769
770 LogFlowFuncLeaveRC(rc);
771 return rc;
772}
773
774/**
775 * Set the volume of an individual sink.
776 *
777 * @returns IPRT status code.
778 * @param pSink Sink to set volume for.
779 * @param pVol Volume to set.
780 */
781int AudioMixerSinkSetVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
782{
783 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
784 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
785 AssertPtr(pSink->pParent);
786
787 LogFlowFunc(("[%s]: fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n", pSink->pszName, pVol->fMuted, pVol->uLeft, pVol->uRight));
788
789 pSink->Volume = *pVol;
790
791 return audioMixerSinkUpdateVolume(pSink, &pSink->pParent->VolMaster);
792}
793
794void AudioMixerSinkTimerUpdate(PAUDMIXSINK pSink, uint64_t cTimerTicks, uint64_t cTicksElapsed)
795{
796 AssertPtrReturnVoid(pSink);
797
798 /* Note: cTimerTicks / cTicksElapsed = Hz elapsed. */
799
800 LogFlowFunc(("[%s]: cTimerTicks=%RU64, cTicksElapsed=%RU64 -> %zuHz\n",
801 pSink->pszName, cTimerTicks, cTicksElapsed, cTimerTicks / cTicksElapsed));
802
803 audioMixerSinkUpdateInternal(pSink);
804}
805
806static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink)
807{
808 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
809
810 int rc = VINF_SUCCESS;
811
812 Log3Func(("[%s]\n", pSink->pszName));
813
814 PAUDMIXSTREAM pMixStream;
815 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
816 {
817 PPDMAUDIOSTREAM pStream = pMixStream->pStream;
818 AssertPtr(pStream);
819
820 uint32_t cPlayed = 0;
821 uint32_t cCaptured = 0;
822
823 int rc2 = pMixStream->pConn->pfnStreamIterate(pMixStream->pConn, pStream);
824 if (RT_SUCCESS(rc2))
825 {
826 if (pStream->enmDir == PDMAUDIODIR_IN)
827 {
828 rc = pMixStream->pConn->pfnStreamCapture(pMixStream->pConn, pMixStream->pStream, &cCaptured);
829 if (RT_FAILURE(rc2))
830 {
831 LogFlowFunc(("%s: Failed capture stream '%s': %Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc2));
832 if (RT_SUCCESS(rc))
833 rc = rc2;
834 }
835
836 if (cCaptured)
837 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
838 }
839 else if (pStream->enmDir == PDMAUDIODIR_OUT)
840 {
841 rc2 = pMixStream->pConn->pfnStreamPlay(pMixStream->pConn, pMixStream->pStream, &cPlayed);
842 if (RT_FAILURE(rc2))
843 {
844 LogFlowFunc(("%s: Failed playing stream '%s': %Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc2));
845 if (RT_SUCCESS(rc))
846 rc = rc2;
847 }
848 }
849 else
850 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
851 }
852
853 Log3Func(("\t%s: cPlayed=%RU32, cCaptured=%RU32\n", pMixStream->pStream->szName, cPlayed, cCaptured));
854 }
855
856 if (RT_FAILURE(rc))
857 LogFlowFunc(("Failed with rc=%Rrc\n", rc));
858
859 return rc;
860}
861
862int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
863{
864 return audioMixerSinkUpdateInternal(pSink);
865}
866
867static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
868{
869 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
870 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
871
872 LogFlowFunc(("Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
873 pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
874 LogFlowFunc(("[%s]: fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
875 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
876
877 /** @todo Very crude implementation for now -- needs more work! */
878
879 PDMAUDIOVOLUME volSink;
880 volSink.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
881 volSink.uLeft = (pSink->Volume.uLeft * pVolMaster->uLeft) / UINT8_MAX;
882 volSink.uRight = (pSink->Volume.uRight * pVolMaster->uRight) / UINT8_MAX;
883
884 LogFlowFunc(("\t-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
885 volSink.fMuted, volSink.uLeft, volSink.uRight));
886
887 bool fOut = pSink->enmDir == AUDMIXSINKDIR_OUTPUT;
888
889 /* Propagate new sink volume to all streams in the sink. */
890 PAUDMIXSTREAM pMixStream;
891 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
892 {
893 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &volSink);
894 AssertRC(rc2);
895 }
896
897 return VINF_SUCCESS;
898}
899
900int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
901{
902 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
903 /* pcbWritten is optional. */
904
905 if (!pvBuf || !cbBuf)
906 {
907 if (pcbWritten)
908 *pcbWritten = 0;
909 return VINF_SUCCESS;
910 }
911
912 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
913 ("Can't write to a sink which is not an output sink\n"));
914
915 LogFlowFunc(("%s: enmOp=%ld, cbBuf=%RU32\n", pSink->pszName, enmOp, cbBuf));
916
917 uint32_t cPlayed;
918 uint32_t cbProcessed;
919
920 PAUDMIXSTREAM pMixStream;
921 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
922 {
923 if (!(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTRMSTS_FLAG_ENABLED))
924 {
925 LogFlowFunc(("%s: Disabled, skipping ...\n", pSink->pszName));
926 continue;
927 }
928
929 int rc2 = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, pvBuf, cbBuf, &cbProcessed);
930 if (RT_FAILURE(rc2))
931 LogFlowFunc(("%s: Failed writing to stream '%s': %Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc2));
932
933 if (cbProcessed < cbBuf)
934 {
935 LogFlowFunc(("%s: Only written %RU32/%RU32 bytes for stream '%s'\n",
936 pSink->pszName, cbProcessed, cbBuf, pMixStream->pStream->szName));
937 }
938 }
939
940 if (cbBuf)
941 {
942 /* Set dirty bit. */
943 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
944 }
945
946 if (pcbWritten)
947 *pcbWritten = cbBuf; /* Always report back a complete write for now. */
948
949 return VINF_SUCCESS;
950}
951
952/*********************************************************************************************************************************
953 * Mixer Stream implementation.
954 ********************************************************************************************************************************/
955
956int AudioMixerStreamCtl(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
957{
958 AssertPtrReturn(pMixStream, VERR_INVALID_POINTER);
959 /** @todo Validate fCtl. */
960
961 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
962
963 return rc;
964}
965
966static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream)
967{
968 AssertPtrReturnVoid(pMixStream);
969
970 LogFunc(("%s\n", pMixStream->pszName));
971
972 if (pMixStream->pConn) /* Stream has a connector interface present? */
973 {
974 if (pMixStream->pStream)
975 {
976 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
977 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
978
979 pMixStream->pStream = NULL;
980 }
981
982 pMixStream->pConn = NULL;
983 }
984
985 if (pMixStream->pszName)
986 {
987 RTStrFree(pMixStream->pszName);
988 pMixStream->pszName = NULL;
989 }
990
991 RTMemFree(pMixStream);
992 pMixStream = NULL;
993}
994
995void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream)
996{
997 if (!pMixStream)
998 return;
999
1000 LogFunc(("%s\n", pMixStream->pszName));
1001
1002 int rc;
1003
1004 if (pMixStream->pSink) /* Is the stream part of a sink? */
1005 {
1006 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
1007 * pointer will be gone from the stream. */
1008 PAUDMIXSINK pSink = pMixStream->pSink;
1009
1010 rc = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
1011 if (RT_SUCCESS(rc))
1012 {
1013 Assert(pSink->cStreams);
1014 pSink->cStreams--;
1015 }
1016 }
1017 else
1018 rc = VINF_SUCCESS;
1019
1020 if (RT_SUCCESS(rc))
1021 audioMixerStreamDestroyInternal(pMixStream);
1022
1023 LogFlowFunc(("Returning %Rrc\n", rc));
1024}
1025
1026bool AudioMixerStreamIsActive(PAUDMIXSTREAM pMixStream)
1027{
1028 if ( !pMixStream
1029 || !pMixStream->pConn)
1030 {
1031 return false;
1032 }
1033
1034 bool fIsActive =
1035 (pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTRMSTS_FLAG_ENABLED);
1036
1037 return fIsActive;
1038}
1039
1040bool AudioMixerStreamIsValid(PAUDMIXSTREAM pMixStream)
1041{
1042 if (!pMixStream)
1043 return false;
1044
1045 uint32_t fStatus = pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream);
1046
1047 return (fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED);
1048}
1049
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