VirtualBox

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

Last change on this file since 62581 was 62579, checked in by vboxsync, 8 years ago

Audio/AudioMixer.cpp: Assertions.

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