VirtualBox

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

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

Audio: Bugfixes.

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