VirtualBox

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

Last change on this file since 61609 was 61609, 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: 34.8 KB
Line 
1/* $Id: AudioMixer.cpp 61609 2016-06-09 10:22:39Z vboxsync $ */
2/** @file
3 * VBox audio: Mixing routines, mainly used by the various audio device
4 * emulations to achieve proper multiplexing from/to attached
5 * devices LUNs.
6 *
7 * This mixer acts as a layer between the audio connector interface and
8 * the actual device emulation, providing mechanisms for audio sources (input) and
9 * audio sinks (output).
10 *
11 * As audio driver instances are handled as LUNs on the device level, this
12 * audio mixer then can take care of e.g. mixing various inputs/outputs to/from
13 * a specific source/sink.
14 *
15 * How and which audio streams are connected to sinks/sources depends on how
16 * the audio mixer has been set up.
17 *
18 * A sink can connect multiple output streams together, whereas a source
19 * does this with input streams. Each sink / source consists of one or more
20 * so-called mixer streams, which then in turn have pointers to the actual
21 * PDM audio input/output streams.
22 */
23
24/*
25 * Copyright (C) 2014-2016 Oracle Corporation
26 *
27 * This file is part of VirtualBox Open Source Edition (OSE), as
28 * available from http://www.virtualbox.org. This file is free software;
29 * you can redistribute it and/or modify it under the terms of the GNU
30 * General Public License (GPL) as published by the Free Software
31 * Foundation, in version 2 as it comes in the "COPYING" file of the
32 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
33 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
34 */
35#define LOG_GROUP LOG_GROUP_AUDIO_MIXER
36#include <VBox/log.h>
37#include "AudioMixer.h"
38#include "AudioMixBuffer.h"
39#include "DrvAudio.h"
40
41#include <VBox/vmm/pdm.h>
42#include <VBox/err.h>
43#include <VBox/vmm/mm.h>
44#include <VBox/vmm/pdmaudioifs.h>
45
46#include <iprt/alloc.h>
47#include <iprt/asm-math.h>
48#include <iprt/assert.h>
49#include <iprt/string.h>
50
51static int audioMixerRemoveSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink);
52
53static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink);
54static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster);
55static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink);
56static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream);
57static void audioMixerSinkReset(PAUDMIXSINK pSink);
58static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink);
59
60static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pStream);
61
62
63int AudioMixerCreateSink(PAUDIOMIXER pMixer, const char *pszName, AUDMIXSINKDIR enmDir, PAUDMIXSINK *ppSink)
64{
65 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
66 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
67 /* ppSink is optional. */
68
69 int rc = VINF_SUCCESS;
70
71 PAUDMIXSINK pSink = (PAUDMIXSINK)RTMemAllocZ(sizeof(AUDMIXSINK));
72 if (pSink)
73 {
74 pSink->pszName = RTStrDup(pszName);
75 if (!pSink->pszName)
76 rc = VERR_NO_MEMORY;
77
78 if (RT_SUCCESS(rc))
79 {
80 pSink->pParent = pMixer;
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 /* Not running anymore? Reset. */
482 if (!(pSink->fStatus & AUDMIXSINK_STS_RUNNING))
483 audioMixerSinkReset(pSink);
484
485 LogFlowFunc(("[%s]: enmCmd=%ld, fStatus=0x%x, rc=%Rrc\n", pSink->pszName, enmSinkCmd, pSink->fStatus, rc));
486 return rc;
487}
488
489void AudioMixerSinkDestroy(PAUDMIXSINK pSink)
490{
491 if (!pSink)
492 return;
493
494 if (pSink->pParent)
495 {
496 /* Save sink pointer, as after audioMixerRemoveSinkInternal() the
497 * pointer will be gone from the stream. */
498 PAUDIOMIXER pMixer = pSink->pParent;
499
500 audioMixerRemoveSinkInternal(pMixer, pSink);
501
502 Assert(pMixer->cSinks);
503 pMixer->cSinks--;
504 }
505
506 audioMixerSinkDestroyInternal(pSink);
507}
508
509static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink)
510{
511 AssertPtrReturnVoid(pSink);
512
513 LogFunc(("%s\n", pSink->pszName));
514
515 PAUDMIXSTREAM pStream, pStreamNext;
516 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
517 {
518 /* Save a pointer to the stream to remove, as pStream
519 * will not be valid anymore after calling audioMixerSinkRemoveStreamInternal(). */
520 PAUDMIXSTREAM pStreamToRemove = pStream;
521
522 audioMixerSinkRemoveStreamInternal(pSink, pStreamToRemove);
523 audioMixerStreamDestroyInternal(pStreamToRemove);
524 }
525
526 if (pSink->pszName)
527 {
528 RTStrFree(pSink->pszName);
529 pSink->pszName = NULL;
530 }
531
532 RTMemFree(pSink);
533 pSink = NULL;
534}
535
536/**
537 * Returns the amount of bytes ready to be read from a sink since the last call
538 * to AudioMixerSinkUpdate().
539 *
540 * @returns Amount of bytes ready to be read from the sink.
541 * @param pSink Sink to return number of available samples for.
542 */
543uint32_t AudioMixerSinkGetReadable(PAUDMIXSINK pSink)
544{
545 AssertPtrReturn(pSink, 0);
546
547 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT, ("Can't read from a non-input sink\n"));
548
549#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
550# error "Implement me!"
551#else
552 LogFlowFunc(("[%s]: cbReadable=%RU32\n", pSink->pszName, pSink->In.cbReadable));
553 return pSink->In.cbReadable;
554#endif
555}
556
557/**
558 * Returns the amount of bytes ready to be written to a sink since the last call
559 * to AudioMixerSinkUpdate().
560 *
561 * @returns Amount of bytes ready to be written to the sink.
562 * @param pSink Sink to return number of available samples for.
563 */
564uint32_t AudioMixerSinkGetWritable(PAUDMIXSINK pSink)
565{
566 AssertPtrReturn(pSink, 0);
567
568 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT, ("Can't write to a non-output sink\n"));
569
570#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
571# error "Implement me!"
572#else
573 LogFlowFunc(("[%s]: cbWritable=%RU32\n", pSink->pszName, pSink->Out.cbWritable));
574 return pSink->Out.cbWritable;
575#endif
576}
577
578/**
579 * Returns the sink's mixing direction.
580 *
581 * @returns Mixing direction.
582 * @param pSink Sink to return direction for.
583 *
584 * @remark
585 */
586AUDMIXSINKDIR AudioMixerSinkGetDir(PAUDMIXSINK pSink)
587{
588 AssertPtrReturn(pSink, AUDMIXSINKDIR_UNKNOWN);
589 return pSink->enmDir;
590}
591
592PAUDMIXSTREAM AudioMixerSinkGetStream(PAUDMIXSINK pSink, uint8_t uIndex)
593{
594 AssertPtrReturn(pSink, NULL);
595 AssertMsgReturn(uIndex < pSink->cStreams,
596 ("Index %RU8 exceeds stream count (%RU8)", uIndex, pSink->cStreams), NULL);
597
598 /* Slow lookup, d'oh. */
599 PAUDMIXSTREAM pStream = RTListGetFirst(&pSink->lstStreams, AUDMIXSTREAM, Node);
600 while (uIndex)
601 {
602 pStream = RTListGetNext(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node);
603 uIndex--;
604 }
605
606 AssertPtr(pStream);
607 return pStream;
608}
609
610AUDMIXSINKSTS AudioMixerSinkGetStatus(PAUDMIXSINK pSink)
611{
612 if (!pSink)
613 return false;
614
615 LogFlowFunc(("[%s]: fStatus=0x%x\n", pSink->pszName, pSink->fStatus));
616
617 /* If the dirty flag is set, there is unprocessed data in the sink. */
618 return pSink->fStatus;
619}
620
621uint8_t AudioMixerSinkGetStreamCount(PAUDMIXSINK pSink)
622{
623 if (!pSink)
624 return 0;
625
626 return pSink->cStreams;
627}
628
629int AudioMixerSinkRead(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
630{
631 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
632 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
633 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
634 /* pcbRead is optional. */
635
636 /** @todo Handle mixing operation enmOp! */
637
638 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT,
639 ("Can't read from a sink which is not an input sink\n"));
640
641#ifndef VBOX_AUDIO_MIXER_WITH_MIXBUF
642 uint8_t *pvMixBuf = (uint8_t *)RTMemAlloc(cbBuf);
643 if (!pvMixBuf)
644 return VERR_NO_MEMORY;
645#endif
646
647 int rc = VERR_NOT_FOUND;
648 uint32_t cbRead = 0;
649
650 /* Flag indicating whether this sink is in a 'clean' state,
651 * e.g. there is no more data to read from. */
652 bool fClean = true;
653
654 PAUDMIXSTREAM pMixStream;
655 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
656 {
657 if (!(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTRMSTS_FLAG_ENABLED))
658 continue;
659
660 uint32_t cbTotalRead = 0;
661 uint32_t cbToRead = cbBuf;
662
663 while (cbToRead)
664 {
665 uint32_t cbReadStrm;
666 AssertPtr(pMixStream->pConn);
667#ifndef VBOX_AUDIO_MIXER_WITH_MIXBUF
668 rc = pMixStream->pConn->pfnStreamRead(pMixStream->pConn, pMixStream->pStream,
669 (uint8_t *)pvMixBuf + cbTotalRead, cbToRead, &cbReadStrm);
670#endif
671 if ( RT_FAILURE(rc)
672 || !cbReadStrm)
673 break;
674
675 /** @todo Right now we only handle one stream (the last one added in fact). */
676
677 AssertBreakStmt(cbReadStrm <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
678 cbToRead -= cbReadStrm;
679 cbTotalRead += cbReadStrm;
680 }
681
682 if (RT_FAILURE(rc))
683 continue;
684
685 cbRead = RT_MAX(cbRead, cbTotalRead);
686
687 PDMAUDIOSTRMSTS strmSts = pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream);
688 fClean &= !(strmSts & PDMAUDIOSTRMSTS_FLAG_DATA_READABLE);
689 }
690
691 if (RT_SUCCESS(rc))
692 {
693 if (fClean)
694 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
695
696#ifndef VBOX_AUDIO_MIXER_WITH_MIXBUF
697 if (cbRead)
698 memcpy(pvBuf, pvMixBuf, cbRead);
699#endif
700 if (pcbRead)
701 *pcbRead = cbRead;
702 }
703
704#ifndef VBOX_AUDIO_MIXER_WITH_MIXBUF
705 RTMemFree(pvMixBuf);
706#endif
707
708 Log3Func(("[%s]: cbRead=%RU32, fStatus=0x%x, rc=%Rrc\n", pSink->pszName, cbRead, pSink->fStatus, rc));
709 return rc;
710}
711
712static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
713{
714 AssertPtrReturn(pSink, VERR_INVALID_PARAMETER);
715 if ( !pStream
716 || !pStream->pSink) /* Not part of a sink anymore? */
717 {
718 return VERR_NOT_FOUND;
719 }
720
721 AssertMsgReturn(pStream->pSink == pSink, ("Stream '%s' is not part of sink '%s'\n",
722 pStream->pszName, pSink->pszName), VERR_NOT_FOUND);
723
724 LogFlowFunc(("[%s]: (Stream = %s), cStreams=%RU8\n",
725 pSink->pszName, pStream->pStream->szName, pSink->cStreams));
726
727#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
728 /* Unlink mixing buffer. */
729 AudioMixBufUnlink(&pStream->pStream->MixBuf);
730#endif
731
732 /* Remove stream from sink. */
733 RTListNodeRemove(&pStream->Node);
734
735 /* Set sink to NULL so that we know we're not part of any sink anymore. */
736 pStream->pSink = NULL;
737
738 return VINF_SUCCESS;
739}
740
741void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
742{
743 int rc = audioMixerSinkRemoveStreamInternal(pSink, pStream);
744 if (RT_SUCCESS(rc))
745 {
746 Assert(pSink->cStreams);
747 pSink->cStreams--;
748 }
749}
750
751/**
752 * Removes all attached streams from a given sink.
753 *
754 * @param pSink Sink to remove attached streams from.
755 */
756static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink)
757{
758 if (!pSink)
759 return;
760
761 LogFunc(("%s\n", pSink->pszName));
762
763 PAUDMIXSTREAM pStream, pStreamNext;
764 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
765 audioMixerSinkRemoveStreamInternal(pSink, pStream);
766}
767
768/**
769 * Resets the sink's state.
770 *
771 * @param pSink Sink to reset.
772 */
773static void audioMixerSinkReset(PAUDMIXSINK pSink)
774{
775 if (!pSink)
776 return;
777
778 LogFunc(("%s\n", pSink->pszName));
779
780 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
781 {
782#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
783 AudioMixBufReset(&pSink->MixBuf);
784#else
785 pSink->In.cbReadable = 0;
786#endif
787 }
788 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
789 {
790#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
791 AudioMixBufReset(&pSink->MixBuf);
792#else
793 pSink->Out.cbWritable = 0;
794#endif
795 }
796}
797
798/**
799 * Removes all attached streams from a given sink.
800 *
801 * @param pSink Sink to remove attached streams from.
802 */
803void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
804{
805 if (!pSink)
806 return;
807
808 audioMixerSinkRemoveAllStreamsInternal(pSink);
809
810 pSink->cStreams = 0;
811}
812
813int AudioMixerSinkSetFormat(PAUDMIXSINK pSink, PPDMPCMPROPS pPCMProps)
814{
815 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
816 AssertPtrReturn(pPCMProps, VERR_INVALID_POINTER);
817
818 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, pPCMProps)) /* Bail out early if PCM properties are equal. */
819 return VINF_SUCCESS;
820
821 if (pSink->PCMProps.uHz)
822 LogFlowFunc(("[%s]: Old format: %RU8 bit, %RU8 channels, %RU32Hz\n",
823 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
824
825 memcpy(&pSink->PCMProps, pPCMProps, sizeof(PDMPCMPROPS));
826
827 LogFlowFunc(("[%s]: New format %RU8 bit, %RU8 channels, %RU32Hz\n",
828 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
829
830 int rc = VINF_SUCCESS;
831
832#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
833 /* Also update the sink's mixing buffer format. */
834 AudioMixBufDestroy(&pSink->MixBuf);
835 rc = AudioMixBufInit(&pSink->MixBuf, pSink->pszName, pPCMProps, _4K /** @todo Make configurable? */);
836 if (RT_SUCCESS(rc))
837 {
838 PAUDMIXSTREAM pStream;
839 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
840 {
841 /** @todo Invalidate mix buffers! */
842 }
843 }
844#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
845
846 LogFlowFuncLeaveRC(rc);
847 return rc;
848}
849
850/**
851 * Set the volume of an individual sink.
852 *
853 * @returns IPRT status code.
854 * @param pSink Sink to set volume for.
855 * @param pVol Volume to set.
856 */
857int AudioMixerSinkSetVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
858{
859 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
860 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
861 AssertPtr(pSink->pParent);
862
863 LogFlowFunc(("[%s]: fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n", pSink->pszName, pVol->fMuted, pVol->uLeft, pVol->uRight));
864
865 pSink->Volume = *pVol;
866
867 return audioMixerSinkUpdateVolume(pSink, &pSink->pParent->VolMaster);
868}
869
870static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink)
871{
872 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
873
874 int rc = VINF_SUCCESS;
875
876 Log3Func(("[%s]\n", pSink->pszName));
877
878 /* Update last updated timestamp. */
879 pSink->tsLastUpdatedNS = RTTimeNanoTS();
880
881 PAUDMIXSTREAM pMixStream;
882 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
883 {
884 PPDMAUDIOSTREAM pStream = pMixStream->pStream;
885 AssertPtr(pStream);
886
887 PPDMIAUDIOCONNECTOR pConn = pMixStream->pConn;
888 AssertPtr(pConn);
889
890 uint32_t cPlayed = 0;
891 uint32_t cCaptured = 0;
892
893 int rc2 = pConn->pfnStreamIterate(pConn, pStream);
894 if (RT_SUCCESS(rc2))
895 {
896 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
897 {
898 rc = pConn->pfnStreamCapture(pConn, pMixStream->pStream, &cCaptured);
899 if (RT_FAILURE(rc2))
900 {
901 LogFlowFunc(("%s: Failed capturing stream '%s', rc=%Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc2));
902 if (RT_SUCCESS(rc))
903 rc = rc2;
904 continue;
905 }
906
907 if (cCaptured)
908 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
909 }
910 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
911 {
912 rc2 = pConn->pfnStreamPlay(pConn, pMixStream->pStream, NULL /* cPlayed */);
913 if (RT_FAILURE(rc2))
914 {
915 LogFlowFunc(("%s: Failed playing stream '%s', rc=%Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc2));
916 if (RT_SUCCESS(rc))
917 rc = rc2;
918 continue;
919 }
920 }
921 else
922 {
923 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
924 continue;
925 }
926
927 rc2 = pConn->pfnStreamIterate(pConn, pStream);
928 if (RT_FAILURE(rc2))
929 {
930 LogFlowFunc(("%s: Failed re-iterating stream '%s', rc=%Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc2));
931 if (RT_SUCCESS(rc))
932 rc = rc2;
933 continue;
934 }
935
936 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
937 {
938#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
939# error "Implement me!"
940#else
941 pSink->In.cbReadable = pConn->pfnStreamGetReadable(pConn, pMixStream->pStream);
942#endif
943 }
944 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
945 {
946#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
947# error "Implement me!"
948#else
949 pSink->Out.cbWritable = pConn->pfnStreamGetWritable(pConn, pMixStream->pStream);
950#endif
951 }
952 }
953
954 Log3Func(("\t%s: cPlayed=%RU32, cCaptured=%RU32\n", pMixStream->pStream->szName, cPlayed, cCaptured));
955 }
956
957 if (RT_FAILURE(rc))
958 LogFlowFunc(("Failed with rc=%Rrc\n", rc));
959
960 return rc;
961}
962
963int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
964{
965 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
966
967 uint64_t tsElapsed = RTTimeNanoTS() - pSink->tsLastUpdatedNS;
968
969 /*
970 * Note: Hz elapsed = cTicksElapsed / cTimerTicks
971 * Bytes / second = Sample rate (Hz) * Audio channels * Bytes per sample
972 */
973// uint32_t cSamples = (int)((pSink->PCMProps.cChannels * cTicksElapsed * pSink->PCMProps.uHz + cTimerTicks) / cTimerTicks / pSink->PCMProps.cChannels);
974
975// LogFlowFunc(("[%s]: cTimerTicks=%RU64, cTicksElapsed=%RU64\n", pSink->pszName, cTimerTicks, cTicksElapsed));
976// LogFlowFunc(("\t%zuHz elapsed, %RU32 samples (%RU32 bytes)\n", cTicksElapsed / cTimerTicks, cSamples, cSamples << 1));
977
978 return audioMixerSinkUpdateInternal(pSink);
979}
980
981static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
982{
983 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
984 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
985
986 LogFlowFunc(("Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
987 pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
988 LogFlowFunc(("[%s]: fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
989 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
990
991 /** @todo Very crude implementation for now -- needs more work! */
992
993 PDMAUDIOVOLUME volSink;
994 volSink.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
995 volSink.uLeft = (pSink->Volume.uLeft * pVolMaster->uLeft) / UINT8_MAX;
996 volSink.uRight = (pSink->Volume.uRight * pVolMaster->uRight) / UINT8_MAX;
997
998 LogFlowFunc(("\t-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
999 volSink.fMuted, volSink.uLeft, volSink.uRight));
1000
1001 bool fOut = pSink->enmDir == AUDMIXSINKDIR_OUTPUT;
1002
1003 /* Propagate new sink volume to all streams in the sink. */
1004 PAUDMIXSTREAM pMixStream;
1005 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1006 {
1007 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &volSink);
1008 AssertRC(rc2);
1009 }
1010
1011 return VINF_SUCCESS;
1012}
1013
1014int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
1015{
1016 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1017 /* pcbWritten is optional. */
1018
1019 if (!pvBuf || !cbBuf)
1020 {
1021 if (pcbWritten)
1022 *pcbWritten = 0;
1023 return VINF_SUCCESS;
1024 }
1025
1026 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
1027 ("Can't write to a sink which is not an output sink\n"));
1028
1029 LogFlowFunc(("%s: enmOp=%ld, cbBuf=%RU32\n", pSink->pszName, enmOp, cbBuf));
1030
1031 uint32_t cPlayed;
1032 uint32_t cbProcessed;
1033
1034 PAUDMIXSTREAM pMixStream;
1035 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1036 {
1037 if (!(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTRMSTS_FLAG_ENABLED))
1038 {
1039 LogFlowFunc(("%s: Disabled, skipping ...\n", pSink->pszName));
1040 continue;
1041 }
1042
1043 int rc2 = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, pvBuf, cbBuf, &cbProcessed);
1044 if (RT_FAILURE(rc2))
1045 LogFlowFunc(("%s: Failed writing to stream '%s': %Rrc\n", pSink->pszName, pMixStream->pStream->szName, rc2));
1046
1047 if (cbProcessed < cbBuf)
1048 {
1049 LogFlowFunc(("%s: Only written %RU32/%RU32 bytes for stream '%s'\n",
1050 pSink->pszName, cbProcessed, cbBuf, pMixStream->pStream->szName));
1051 }
1052 }
1053
1054 if (cbBuf)
1055 {
1056 /* Set dirty bit. */
1057 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1058 }
1059
1060 if (pcbWritten)
1061 *pcbWritten = cbBuf; /* Always report back a complete write for now. */
1062
1063 return VINF_SUCCESS;
1064}
1065
1066/*********************************************************************************************************************************
1067 * Mixer Stream implementation.
1068 ********************************************************************************************************************************/
1069
1070int AudioMixerStreamCtl(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
1071{
1072 AssertPtrReturn(pMixStream, VERR_INVALID_POINTER);
1073 /** @todo Validate fCtl. */
1074
1075 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
1076
1077 return rc;
1078}
1079
1080static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream)
1081{
1082 AssertPtrReturnVoid(pMixStream);
1083
1084 LogFunc(("%s\n", pMixStream->pszName));
1085
1086 if (pMixStream->pConn) /* Stream has a connector interface present? */
1087 {
1088 if (pMixStream->pStream)
1089 {
1090 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
1091 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
1092
1093 pMixStream->pStream = NULL;
1094 }
1095
1096 pMixStream->pConn = NULL;
1097 }
1098
1099 if (pMixStream->pszName)
1100 {
1101 RTStrFree(pMixStream->pszName);
1102 pMixStream->pszName = NULL;
1103 }
1104
1105 RTMemFree(pMixStream);
1106 pMixStream = NULL;
1107}
1108
1109void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream)
1110{
1111 if (!pMixStream)
1112 return;
1113
1114 LogFunc(("%s\n", pMixStream->pszName));
1115
1116 int rc;
1117
1118 if (pMixStream->pSink) /* Is the stream part of a sink? */
1119 {
1120 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
1121 * pointer will be gone from the stream. */
1122 PAUDMIXSINK pSink = pMixStream->pSink;
1123
1124 rc = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
1125 if (RT_SUCCESS(rc))
1126 {
1127 Assert(pSink->cStreams);
1128 pSink->cStreams--;
1129 }
1130 }
1131 else
1132 rc = VINF_SUCCESS;
1133
1134 if (RT_SUCCESS(rc))
1135 audioMixerStreamDestroyInternal(pMixStream);
1136
1137 LogFlowFunc(("Returning %Rrc\n", rc));
1138}
1139
1140bool AudioMixerStreamIsActive(PAUDMIXSTREAM pMixStream)
1141{
1142 if ( !pMixStream
1143 || !pMixStream->pConn)
1144 {
1145 return false;
1146 }
1147
1148 bool fIsActive =
1149 (pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTRMSTS_FLAG_ENABLED);
1150
1151 return fIsActive;
1152}
1153
1154bool AudioMixerStreamIsValid(PAUDMIXSTREAM pMixStream)
1155{
1156 if (!pMixStream)
1157 return false;
1158
1159 uint32_t fStatus = pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream);
1160
1161 return (fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED);
1162}
1163
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