VirtualBox

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

Last change on this file since 61497 was 61386, 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: 30.7 KB
Line 
1/* $Id: AudioMixer.cpp 61386 2016-06-01 18:51:16Z 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 {
344 AssertMsgFailed(("Direction not implemented\n"));
345 rc = VERR_NOT_IMPLEMENTED;
346 }
347#else
348 rc = VINF_SUCCESS;
349#endif
350
351 if (RT_SUCCESS(rc))
352 {
353 /** @todo Check if stream already is assigned to (another) sink. */
354
355 /* Save pointer to sink the stream is attached to. */
356 pStream->pSink = pSink;
357
358 /* Append stream to sink's list. */
359 RTListAppend(&pSink->lstStreams, &pStream->Node);
360 pSink->cStreams++;
361 }
362
363 LogFlowFunc(("[%s]: cStreams=%RU8, rc=%Rrc\n", pSink->pszName, pSink->cStreams, rc));
364 return rc;
365}
366
367int AudioMixerSinkCreateStream(PAUDMIXSINK pSink,
368 PPDMIAUDIOCONNECTOR pConn, PPDMAUDIOSTREAMCFG pCfg, uint32_t fFlags, PAUDMIXSTREAM *ppStream)
369{
370 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
371 AssertPtrReturn(pConn, VERR_INVALID_POINTER);
372 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
373 /** @todo Validate fFlags. */
374 /* ppStream is optional. */
375
376 PAUDMIXSTREAM pMixStream = (PAUDMIXSTREAM)RTMemAllocZ(sizeof(AUDMIXSTREAM));
377 if (!pMixStream)
378 return VERR_NO_MEMORY;
379
380 pMixStream->pszName = RTStrDup(pCfg->szName);
381 if (!pMixStream->pszName)
382 {
383 RTMemFree(pMixStream);
384 return VERR_NO_MEMORY;
385 }
386
387 LogFlowFunc(("[%s]: fFlags=0x%x (enmDir=%ld, %s, %RU8 channels, %RU32Hz)\n",
388 pSink->pszName, fFlags, pCfg->enmDir, DrvAudioHlpAudFmtToStr(pCfg->enmFormat), pCfg->cChannels, pCfg->uHz));
389
390 PDMAUDIOSTREAMCFG CfgSink;
391 int rc = DrvAudioHlpPCMPropsToStreamCfg(&pSink->PCMProps, &CfgSink);
392 AssertRCReturn(rc, rc);
393
394 /* Apply the sink's direction for the configuration to use to
395 * create the stream. */
396 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
397 {
398 CfgSink.DestSource.Source = PDMAUDIORECSOURCE_UNKNOWN;
399 CfgSink.enmDir = PDMAUDIODIR_IN;
400 }
401 else
402 {
403 CfgSink.DestSource.Dest = PDMAUDIOPLAYBACKDEST_UNKNOWN;
404 CfgSink.enmDir = PDMAUDIODIR_OUT;
405 }
406
407 RTStrPrintf(CfgSink.szName, sizeof(CfgSink.szName), "%s", pCfg->szName);
408
409 /* Always use the sink's PCM audio format as the host side when creating a stream for it. */
410 PPDMAUDIOSTREAM pStream;
411 rc = pConn->pfnStreamCreate(pConn, &CfgSink, pCfg, &pStream);
412 if (RT_SUCCESS(rc))
413 {
414 /* Save the audio stream pointer to this mixing stream. */
415 pMixStream->pStream = pStream;
416
417 /* Increase the stream's reference count to let others know
418 * we're reyling on it to be around now. */
419 pConn->pfnStreamAddRef(pConn, pStream);
420 }
421
422 if (RT_SUCCESS(rc))
423 {
424 pMixStream->fFlags = fFlags;
425 pMixStream->pConn = pConn;
426
427 if (ppStream)
428 *ppStream = pMixStream;
429 }
430 else if (pMixStream)
431 {
432 RTStrFree(pMixStream->pszName);
433 pMixStream->pszName = NULL;
434
435 RTMemFree(pMixStream);
436 pMixStream = NULL;
437 }
438
439 return rc;
440}
441
442static PDMAUDIOSTREAMCMD audioMixerSinkToStreamCmd(AUDMIXSINKCMD enmCmd)
443{
444 switch (enmCmd)
445 {
446 case AUDMIXSINKCMD_ENABLE: return PDMAUDIOSTREAMCMD_ENABLE;
447 case AUDMIXSINKCMD_DISABLE: return PDMAUDIOSTREAMCMD_DISABLE;
448 case AUDMIXSINKCMD_PAUSE: return PDMAUDIOSTREAMCMD_PAUSE;
449 case AUDMIXSINKCMD_RESUME: return PDMAUDIOSTREAMCMD_RESUME;
450 default: break;
451 }
452
453 AssertMsgFailed(("Unsupported sink command %ld\n", enmCmd));
454 return PDMAUDIOSTREAMCMD_UNKNOWN;
455}
456
457int AudioMixerSinkCtl(PAUDMIXSINK pSink, AUDMIXSINKCMD enmCmd)
458{
459 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
460
461 PDMAUDIOSTREAMCMD enmCmdStream = audioMixerSinkToStreamCmd(enmCmd);
462 if (enmCmdStream == PDMAUDIOSTREAMCMD_UNKNOWN)
463 return VERR_NOT_SUPPORTED;
464
465 int rc = VINF_SUCCESS;
466
467 PAUDMIXSTREAM pStream;
468 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
469 {
470 int rc2 = AudioMixerStreamCtl(pStream, enmCmdStream, AUDMIXSTRMCTL_FLAG_NONE);
471 if (RT_SUCCESS(rc))
472 rc = rc2;
473 /* Keep going. Flag? */
474 }
475
476 LogFlowFunc(("[%s]: enmCmd=%ld, rc=%Rrc\n", pSink->pszName, enmCmd, rc));
477 return rc;
478}
479
480void AudioMixerSinkDestroy(PAUDMIXSINK pSink)
481{
482 if (!pSink)
483 return;
484
485 if (pSink->pParent)
486 {
487 /* Save sink pointer, as after audioMixerRemoveSinkInternal() the
488 * pointer will be gone from the stream. */
489 PAUDIOMIXER pMixer = pSink->pParent;
490
491 audioMixerRemoveSinkInternal(pMixer, pSink);
492
493 Assert(pMixer->cSinks);
494 pMixer->cSinks--;
495 }
496
497 audioMixerSinkDestroyInternal(pSink);
498}
499
500static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink)
501{
502 AssertPtrReturnVoid(pSink);
503
504 LogFunc(("%s\n", pSink->pszName));
505
506 PAUDMIXSTREAM pStream, pStreamNext;
507 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
508 {
509 /* Save a pointer to the stream to remove, as pStream
510 * will not be valid anymore after calling audioMixerSinkRemoveStreamInternal(). */
511 PAUDMIXSTREAM pStreamToRemove = pStream;
512
513 audioMixerSinkRemoveStreamInternal(pSink, pStreamToRemove);
514 audioMixerStreamDestroyInternal(pStreamToRemove);
515 }
516
517 if (pSink->pszName)
518 {
519 RTStrFree(pSink->pszName);
520 pSink->pszName = NULL;
521 }
522
523 RTMemFree(pSink);
524 pSink = NULL;
525}
526
527/**
528 * Returns the sink's mixing direction.
529 *
530 * @returns Mixing direction.
531 * @param pSink Sink to return direction for.
532 *
533 * @remark
534 */
535AUDMIXSINKDIR AudioMixerSinkGetDir(PAUDMIXSINK pSink)
536{
537 AssertPtrReturn(pSink, AUDMIXSINKDIR_UNKNOWN);
538 return pSink->enmDir;
539}
540
541PAUDMIXSTREAM AudioMixerSinkGetStream(PAUDMIXSINK pSink, uint8_t uIndex)
542{
543 AssertPtrReturn(pSink, NULL);
544 AssertMsgReturn(uIndex < pSink->cStreams,
545 ("Index %RU8 exceeds stream count (%RU8)", uIndex, pSink->cStreams), NULL);
546
547 /* Slow lookup, d'oh. */
548 PAUDMIXSTREAM pStream = RTListGetFirst(&pSink->lstStreams, AUDMIXSTREAM, Node);
549 while (uIndex)
550 {
551 pStream = RTListGetNext(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node);
552 uIndex--;
553 }
554
555 AssertPtr(pStream);
556 return pStream;
557}
558
559uint8_t AudioMixerSinkGetStreamCount(PAUDMIXSINK pSink)
560{
561 if (!pSink)
562 return 0;
563
564 return pSink->cStreams;
565}
566
567bool AudioMixerSinkHasData(PAUDMIXSINK pSink)
568{
569 if (!pSink)
570 return false;
571
572 LogFlowFunc(("[%s]: %RTbool\n", pSink->pszName, (pSink->fFlags & AUDMIXSINK_FLAG_DIRTY)));
573
574 /* If the dirty flag is set, there is unprocessed data in the sink. */
575 return (pSink->fFlags & AUDMIXSINK_FLAG_DIRTY);
576}
577
578int AudioMixerSinkRead(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
579{
580 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
581 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
582 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
583 /* pcbRead is optional. */
584
585 /** @todo Handle mixing operation enmOp! */
586
587 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT,
588 ("Can't read from a sink which is not an input sink\n"));
589
590 uint8_t *pvMixBuf = (uint8_t *)RTMemAlloc(cbBuf);
591 if (!pvMixBuf)
592 return VERR_NO_MEMORY;
593
594 int rc = VERR_NOT_FOUND;
595 uint32_t cbRead = 0;
596
597 PAUDMIXSTREAM pMixStream;
598 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
599 {
600 if (!(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTRMSTS_FLAG_ENABLED))
601 continue;
602
603 uint32_t cbTotalRead = 0;
604 uint32_t cbToRead = cbBuf;
605
606 while (cbToRead)
607 {
608 uint32_t cbReadStrm;
609 AssertPtr(pMixStream->pConn);
610 rc = pMixStream->pConn->pfnStreamRead(pMixStream->pConn, pMixStream->pStream,
611 (uint8_t *)pvMixBuf + cbTotalRead, cbToRead, &cbReadStrm);
612 if ( RT_FAILURE(rc)
613 || !cbReadStrm)
614 break;
615
616 /** @todo Right now we only handle one stream (the last one added in fact). */
617
618 AssertBreakStmt(cbReadStrm <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
619 cbToRead -= cbReadStrm;
620 cbTotalRead += cbReadStrm;
621 }
622
623 if (RT_FAILURE(rc))
624 continue;
625
626 cbRead = RT_MAX(cbRead, cbTotalRead);
627 }
628
629 if (RT_SUCCESS(rc))
630 {
631 memcpy(pvBuf, pvMixBuf, cbRead); /* @todo Use an intermediate mixing buffer per sink! */
632
633 if (pcbRead)
634 *pcbRead = cbRead;
635 }
636
637 RTMemFree(pvMixBuf);
638
639 Log3Func(("[%s]: cbRead=%RU32, rc=%Rrc\n", pSink->pszName, cbRead, rc));
640 return rc;
641}
642
643static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
644{
645 AssertPtrReturn(pSink, VERR_INVALID_PARAMETER);
646 if (!pStream)
647 return VERR_NOT_FOUND;
648
649 AssertMsgReturn(pStream->pSink == pSink, ("Stream '%s' is not part of sink '%s'\n",
650 pStream->pszName, pSink->pszName), VERR_NOT_FOUND);
651
652 LogFlowFunc(("[%s]: (Stream = %s), cStreams=%RU8\n",
653 pSink->pszName, pStream->pStream->szName, pSink->cStreams));
654
655#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
656 /* Unlink mixing buffer. */
657 AudioMixBufUnlink(&pStream->pStream->MixBuf);
658#endif
659
660 /* Remove stream from sink. */
661 RTListNodeRemove(&pStream->Node);
662 Assert(pSink->cStreams);
663 pSink->cStreams--;
664
665 /* Set sink to NULL so that we know we're not part of any sink anymore. */
666 pStream->pSink = NULL;
667
668 return VINF_SUCCESS;
669}
670
671void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
672{
673 audioMixerSinkRemoveStreamInternal(pSink, pStream);
674}
675
676/**
677 * Removes all attached streams from a given sink.
678 *
679 * @param pSink Sink to remove attached streams from.
680 */
681static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink)
682{
683 if (!pSink)
684 return;
685
686 LogFunc(("%s\n", pSink->pszName));
687
688 PAUDMIXSTREAM pStream, pStreamNext;
689 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
690 audioMixerSinkRemoveStreamInternal(pSink, pStream);
691}
692
693/**
694 * Removes all attached streams from a given sink.
695 *
696 * @param pSink Sink to remove attached streams from.
697 */
698void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
699{
700 if (!pSink)
701 return;
702
703 audioMixerSinkRemoveAllStreamsInternal(pSink);
704
705 pSink->cStreams = 0;
706}
707
708int AudioMixerSinkSetFormat(PAUDMIXSINK pSink, PPDMPCMPROPS pPCMProps)
709{
710 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
711 AssertPtrReturn(pPCMProps, VERR_INVALID_POINTER);
712
713 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, pPCMProps)) /* Bail out early if PCM properties are equal. */
714 return VINF_SUCCESS;
715
716 if (pSink->PCMProps.uHz)
717 LogFlowFunc(("[%s]: Old format: %RU8 bit, %RU8 channels, %RU32Hz\n",
718 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
719
720 memcpy(&pSink->PCMProps, pPCMProps, sizeof(PDMPCMPROPS));
721
722 LogFlowFunc(("[%s]: New format %RU8 bit, %RU8 channels, %RU32Hz\n",
723 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
724
725 int rc = VINF_SUCCESS;
726
727#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
728 /* Also update the sink's mixing buffer format. */
729 AudioMixBufDestroy(&pSink->MixBuf);
730 rc = AudioMixBufInit(&pSink->MixBuf, pSink->pszName, pPCMProps, _4K /** @todo Make configurable? */);
731 if (RT_SUCCESS(rc))
732 {
733 PAUDMIXSTREAM pStream;
734 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
735 {
736 /** @todo Invalidate mix buffers! */
737 }
738 }
739#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
740
741 LogFlowFuncLeaveRC(rc);
742 return rc;
743}
744
745/**
746 * Set the volume of an individual sink.
747 *
748 * @returns IPRT status code.
749 * @param pSink Sink to set volume for.
750 * @param pVol Volume to set.
751 */
752int AudioMixerSinkSetVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
753{
754 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
755 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
756 AssertPtr(pSink->pParent);
757
758 LogFlowFunc(("[%s]: fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n", pSink->pszName, pVol->fMuted, pVol->uLeft, pVol->uRight));
759
760 pSink->Volume = *pVol;
761
762 return audioMixerSinkUpdateVolume(pSink, &pSink->pParent->VolMaster);
763}
764
765void AudioMixerSinkTimerUpdate(PAUDMIXSINK pSink, uint64_t cTimerTicks, uint64_t cTicksElapsed, uint32_t *pcbData)
766{
767 AssertPtrReturnVoid(pSink);
768 /* pcbData is optional. */
769
770 /* Note: cTimerTicks / cTicksElapsed = Hz elapsed. */
771
772 LogFlowFunc(("[%s]: cTimerTicks=%RU64, cTicksElapsed=%RU64\n", pSink->pszName, cTimerTicks, cTicksElapsed));
773
774// uint32_t cSamplesMin = (uint32_t)((2 * cTicksElapsed * pSink->PCMProps.uHz + cTimerTicks) / cTimerTicks / 2);
775 uint32_t cSamplesMin = (cTicksElapsed / pSink->PCMProps.uHz) * pSink->PCMProps.cChannels;
776 // cSamplesMin = (uint32_t)((2 * cTicksElapsed * 44100 + cTimerTicks) / cTimerTicks / 2);
777 uint32_t cbSamplesMin = _4K; //cSamplesMin << pSink->PCMProps.cShift;
778
779 //Assert((cbSamplesMin % 2 == 0));
780
781//LogFlowFunc(("[%s]: cSamplesMin=%RU32 (%RU32 bytes, %RU32Hz)\n", pSink->pszName, cSamplesMin, cbSamplesMin, pSink->PCMProps.uHz));
782
783 uint32_t cbData = cbSamplesMin;
784
785 if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
786 {
787// uint32_t cSinkSamplesLive = AudioMixBufAvail(&pSink->MixBuf);
788// if (!cSinkSamplesLive)
789// cbData = AUDIOMIXBUF_S2B_RATIO(&pSink->MixBuf, AudioMixBufFree(&pSink->MixBufGuest));
790 }
791
792 audioMixerSinkUpdateInternal(pSink);
793
794 if (pcbData)
795 *pcbData = cbData;
796}
797
798static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink)
799{
800 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
801
802 int rc = VINF_SUCCESS;
803
804 Log3Func(("[%s]\n", pSink->pszName));
805
806 PAUDMIXSTREAM pMixStream;
807 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
808 {
809 PPDMAUDIOSTREAM pStream = pMixStream->pStream;
810 AssertPtr(pStream);
811
812 uint32_t cPlayed = 0;
813
814 rc = pMixStream->pConn->pfnStreamIterate(pMixStream->pConn, pStream);
815 if (RT_SUCCESS(rc))
816 {
817 if (pStream->enmDir == PDMAUDIODIR_IN)
818 {
819 /** @todo Implement this! */
820#if 0
821 rc = pStream->pConn->pfnStreamCapture(pStream->pConn, NULL /* pcSamplesCaptured */);
822#endif
823 }
824 else
825 {
826 rc = pMixStream->pConn->pfnStreamPlay(pMixStream->pConn, pMixStream->pStream, &cPlayed);
827 if (RT_FAILURE(rc))
828 LogFlowFunc(("%s: Failed playing stream '%s': %Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc));
829 }
830 }
831
832 Log3Func(("\t%s: cPlayed=%RU32, rc=%Rrc\n", pMixStream->pStream->szName, cPlayed, rc));
833 }
834
835 return rc;
836}
837
838int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
839{
840 return audioMixerSinkUpdateInternal(pSink);
841}
842
843static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
844{
845 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
846 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
847
848 LogFlowFunc(("Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
849 pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
850 LogFlowFunc(("[%s]: fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
851 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
852
853 /** @todo Very crude implementation for now -- needs more work! */
854
855 PDMAUDIOVOLUME volSink;
856 volSink.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
857 volSink.uLeft = (pSink->Volume.uLeft * pVolMaster->uLeft) / UINT8_MAX;
858 volSink.uRight = (pSink->Volume.uRight * pVolMaster->uRight) / UINT8_MAX;
859
860 LogFlowFunc(("\t-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
861 volSink.fMuted, volSink.uLeft, volSink.uRight));
862
863 bool fOut = pSink->enmDir == AUDMIXSINKDIR_OUTPUT;
864
865 /* Propagate new sink volume to all streams in the sink. */
866 PAUDMIXSTREAM pMixStream;
867 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
868 {
869 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &volSink);
870 AssertRC(rc2);
871 }
872
873 return VINF_SUCCESS;
874}
875
876int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
877{
878 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
879 /* pcbWritten is optional. */
880
881 if (!pvBuf || !cbBuf)
882 {
883 if (pcbWritten)
884 *pcbWritten = 0;
885 return VINF_SUCCESS;
886 }
887
888 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
889 ("Can't write to a sink which is not an output sink\n"));
890
891 LogFlowFunc(("%s: enmOp=%ld, cbBuf=%RU32\n", pSink->pszName, enmOp, cbBuf));
892
893 uint32_t cPlayed;
894 uint32_t cbProcessed;
895
896 PAUDMIXSTREAM pMixStream;
897 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
898 {
899 if (!(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTRMSTS_FLAG_ENABLED))
900 {
901 LogFlowFunc(("%s: Disabled, skipping ...\n", pSink->pszName));
902 continue;
903 }
904
905 int rc2 = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, pvBuf, cbBuf, &cbProcessed);
906 if (RT_FAILURE(rc2))
907 LogFlowFunc(("%s: Failed writing to stream '%s': %Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc2));
908
909 if (cbProcessed < cbBuf)
910 {
911 LogFlowFunc(("%s: Only written %RU32/%RU32 bytes for stream '%s'\n",
912 pSink->pszName, cbProcessed, cbBuf, pMixStream->pStream->szName));
913 }
914 }
915
916 /* Set dirty bit. */
917 pSink->fFlags |= AUDMIXSINK_FLAG_DIRTY;
918
919 if (pcbWritten)
920 *pcbWritten = cbBuf; /* Always report back a complete write for now. */
921
922 return VINF_SUCCESS;
923}
924
925/*********************************************************************************************************************************
926 * Mixer Stream implementation.
927 ********************************************************************************************************************************/
928
929int AudioMixerStreamCtl(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
930{
931 AssertPtrReturn(pMixStream, VERR_INVALID_POINTER);
932 /** @todo Validate fCtl. */
933
934 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
935
936 return rc;
937}
938
939static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream)
940{
941 AssertPtrReturnVoid(pMixStream);
942
943 LogFunc(("%s\n", pMixStream->pszName));
944
945 if (pMixStream->pConn) /* Stream has a connector interface present? */
946 {
947 if (pMixStream->pStream)
948 {
949 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
950 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
951
952 pMixStream->pStream = NULL;
953 }
954
955 pMixStream->pConn = NULL;
956 }
957
958 if (pMixStream->pszName)
959 {
960 RTStrFree(pMixStream->pszName);
961 pMixStream->pszName = NULL;
962 }
963
964 RTMemFree(pMixStream);
965 pMixStream = NULL;
966}
967
968void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream)
969{
970 if (!pMixStream)
971 return;
972
973 LogFunc(("%s\n", pMixStream->pszName));
974
975 int rc;
976
977 if (pMixStream->pSink)
978 {
979 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
980 * pointer will be gone from the stream. */
981 PAUDMIXSINK pSink = pMixStream->pSink;
982
983 rc = audioMixerSinkRemoveStreamInternal(pMixStream->pSink, pMixStream);
984 if (RT_SUCCESS(rc))
985 {
986 AssertPtr(pSink);
987 Assert(pSink->cStreams);
988 pSink->cStreams--;
989 }
990 }
991 else
992 rc = VINF_SUCCESS;
993
994 if (RT_SUCCESS(rc))
995 audioMixerStreamDestroyInternal(pMixStream);
996}
997
998bool AudioMixerStreamIsActive(PAUDMIXSTREAM pMixStream)
999{
1000 if ( !pMixStream
1001 || !pMixStream->pConn)
1002 {
1003 return false;
1004 }
1005
1006 bool fIsActive =
1007 (pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTRMSTS_FLAG_ENABLED);
1008
1009 return fIsActive;
1010}
1011
1012bool AudioMixerStreamIsValid(PAUDMIXSTREAM pMixStream)
1013{
1014 if (!pMixStream)
1015 return false;
1016
1017 uint32_t fStatus = pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream);
1018
1019 return (fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED);
1020}
1021
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