VirtualBox

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

Last change on this file since 73403 was 73403, checked in by vboxsync, 7 years ago

Audio/Mixer: Added support for setting / getting an (input) sink's current recording source via AudioMixerSink[Get|Set]RecordingSource().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.8 KB
Line 
1/* $Id: AudioMixer.cpp 73403 2018-07-31 08:59:13Z vboxsync $ */
2/** @file
3 * Audio mixing routines for multiplexing audio sources in device emulations.
4 *
5 * This mixer acts as a layer between the audio connector interface and
6 * the actual device emulation, providing mechanisms for audio sources (input)
7 * and audio sinks (output).
8 *
9 * Think of this mixer as kind of a high(er) level interface for the audio
10 * connector interface, abstracting common tasks such as creating and managing
11 * various audio sources and sinks. This mixer class is purely optional and can
12 * be left out when implementing a new device emulation, using only the audi
13 * connector interface instead. For example, the SB16 emulation does not use
14 * this mixer and does all its stream management on its own.
15 *
16 * As audio driver instances are handled as LUNs on the device level, this
17 * audio mixer then can take care of e.g. mixing various inputs/outputs to/from
18 * a specific source/sink.
19 *
20 * How and which audio streams are connected to sinks/sources depends on how
21 * the audio mixer has been set up.
22 *
23 * A sink can connect multiple output streams together, whereas a source
24 * does this with input streams. Each sink / source consists of one or more
25 * so-called mixer streams, which then in turn have pointers to the actual
26 * PDM audio input/output streams.
27 */
28
29/*
30 * Copyright (C) 2014-2018 Oracle Corporation
31 *
32 * This file is part of VirtualBox Open Source Edition (OSE), as
33 * available from http://www.virtualbox.org. This file is free software;
34 * you can redistribute it and/or modify it under the terms of the GNU
35 * General Public License (GPL) as published by the Free Software
36 * Foundation, in version 2 as it comes in the "COPYING" file of the
37 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
38 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
39 */
40
41
42/*********************************************************************************************************************************
43* Header Files *
44*********************************************************************************************************************************/
45#define LOG_GROUP LOG_GROUP_AUDIO_MIXER
46#include <VBox/log.h>
47#include "AudioMixer.h"
48#include "AudioMixBuffer.h"
49#include "DrvAudio.h"
50
51#include <VBox/vmm/pdm.h>
52#include <VBox/err.h>
53#include <VBox/vmm/mm.h>
54#include <VBox/vmm/pdmaudioifs.h>
55
56#include <iprt/alloc.h>
57#include <iprt/asm-math.h>
58#include <iprt/assert.h>
59#include <iprt/string.h>
60
61
62/*********************************************************************************************************************************
63* Internal Functions *
64*********************************************************************************************************************************/
65static int audioMixerRemoveSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink);
66
67static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink);
68static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster);
69static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink);
70static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream);
71static void audioMixerSinkReset(PAUDMIXSINK pSink);
72static int audioMixerSinkSetRecSourceInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream);
73static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink);
74
75int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl);
76static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pStream);
77
78
79#ifdef LOG_ENABLED
80/**
81 * Converts a mixer sink status to a string.
82 *
83 * @returns Stringified mixer sink flags. Must be free'd with RTStrFree().
84 * "NONE" if no flags set.
85 * @param fFlags Mixer sink flags to convert.
86 */
87static char *dbgAudioMixerSinkStatusToStr(AUDMIXSINKSTS fStatus)
88{
89#define APPEND_FLAG_TO_STR(_aFlag) \
90 if (fStatus & AUDMIXSINK_STS_##_aFlag) \
91 { \
92 if (pszFlags) \
93 { \
94 rc2 = RTStrAAppend(&pszFlags, " "); \
95 if (RT_FAILURE(rc2)) \
96 break; \
97 } \
98 \
99 rc2 = RTStrAAppend(&pszFlags, #_aFlag); \
100 if (RT_FAILURE(rc2)) \
101 break; \
102 } \
103
104 char *pszFlags = NULL;
105 int rc2 = VINF_SUCCESS;
106
107 do
108 {
109 APPEND_FLAG_TO_STR(NONE);
110 APPEND_FLAG_TO_STR(RUNNING);
111 APPEND_FLAG_TO_STR(PENDING_DISABLE);
112 APPEND_FLAG_TO_STR(DIRTY);
113
114 } while (0);
115
116 if ( RT_FAILURE(rc2)
117 && pszFlags)
118 {
119 RTStrFree(pszFlags);
120 pszFlags = NULL;
121 }
122
123#undef APPEND_FLAG_TO_STR
124
125 return pszFlags;
126}
127#endif /* DEBUG */
128
129/**
130 * Creates an audio sink and attaches it to the given mixer.
131 *
132 * @returns IPRT status code.
133 * @param pMixer Mixer to attach created sink to.
134 * @param pszName Name of the sink to create.
135 * @param enmDir Direction of the sink to create.
136 * @param ppSink Pointer which returns the created sink on success.
137 */
138int AudioMixerCreateSink(PAUDIOMIXER pMixer, const char *pszName, AUDMIXSINKDIR enmDir, PAUDMIXSINK *ppSink)
139{
140 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
141 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
142 /* ppSink is optional. */
143
144 int rc = RTCritSectEnter(&pMixer->CritSect);
145 if (RT_FAILURE(rc))
146 return rc;
147
148 PAUDMIXSINK pSink = (PAUDMIXSINK)RTMemAllocZ(sizeof(AUDMIXSINK));
149 if (pSink)
150 {
151 pSink->pszName = RTStrDup(pszName);
152 if (!pSink->pszName)
153 rc = VERR_NO_MEMORY;
154
155 if (RT_SUCCESS(rc))
156 rc = RTCritSectInit(&pSink->CritSect);
157
158 if (RT_SUCCESS(rc))
159 {
160 pSink->pParent = pMixer;
161 pSink->enmDir = enmDir;
162 RTListInit(&pSink->lstStreams);
163
164 /* Set initial volume to max. */
165 pSink->Volume.fMuted = false;
166 pSink->Volume.uLeft = PDMAUDIO_VOLUME_MAX;
167 pSink->Volume.uRight = PDMAUDIO_VOLUME_MAX;
168
169 /* Ditto for the combined volume. */
170 pSink->VolumeCombined.fMuted = false;
171 pSink->VolumeCombined.uLeft = PDMAUDIO_VOLUME_MAX;
172 pSink->VolumeCombined.uRight = PDMAUDIO_VOLUME_MAX;
173
174 RTListAppend(&pMixer->lstSinks, &pSink->Node);
175 pMixer->cSinks++;
176
177 LogFlowFunc(("pMixer=%p, pSink=%p, cSinks=%RU8\n",
178 pMixer, pSink, pMixer->cSinks));
179
180 if (ppSink)
181 *ppSink = pSink;
182 }
183
184 if (RT_FAILURE(rc))
185 {
186 RTCritSectDelete(&pSink->CritSect);
187
188 if (pSink)
189 {
190 RTMemFree(pSink);
191 pSink = NULL;
192 }
193 }
194 }
195 else
196 rc = VERR_NO_MEMORY;
197
198 int rc2 = RTCritSectLeave(&pMixer->CritSect);
199 AssertRC(rc2);
200
201 return rc;
202}
203
204/**
205 * Creates an audio mixer.
206 *
207 * @returns IPRT status code.
208 * @param pszName Name of the audio mixer.
209 * @param fFlags Creation flags. Not used at the moment and must be 0.
210 * @param ppMixer Pointer which returns the created mixer object.
211 */
212int AudioMixerCreate(const char *pszName, uint32_t fFlags, PAUDIOMIXER *ppMixer)
213{
214 RT_NOREF(fFlags);
215 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
216 /** @todo Add fFlags validation. */
217 AssertPtrReturn(ppMixer, VERR_INVALID_POINTER);
218
219 int rc = VINF_SUCCESS;
220
221 PAUDIOMIXER pMixer = (PAUDIOMIXER)RTMemAllocZ(sizeof(AUDIOMIXER));
222 if (pMixer)
223 {
224 pMixer->pszName = RTStrDup(pszName);
225 if (!pMixer->pszName)
226 rc = VERR_NO_MEMORY;
227
228 if (RT_SUCCESS(rc))
229 rc = RTCritSectInit(&pMixer->CritSect);
230
231 if (RT_SUCCESS(rc))
232 {
233 pMixer->cSinks = 0;
234 RTListInit(&pMixer->lstSinks);
235
236 /* Set master volume to the max. */
237 pMixer->VolMaster.fMuted = false;
238 pMixer->VolMaster.uLeft = PDMAUDIO_VOLUME_MAX;
239 pMixer->VolMaster.uRight = PDMAUDIO_VOLUME_MAX;
240
241 LogFlowFunc(("Created mixer '%s'\n", pMixer->pszName));
242
243 *ppMixer = pMixer;
244 }
245 else
246 RTMemFree(pMixer);
247 }
248 else
249 rc = VERR_NO_MEMORY;
250
251 LogFlowFuncLeaveRC(rc);
252 return rc;
253}
254
255/**
256 * Helper function for the internal debugger to print the mixer's current
257 * state, along with the attached sinks.
258 *
259 * @param pMixer Mixer to print debug output for.
260 * @param pHlp Debug info helper to use.
261 * @param pszArgs Optional arguments. Not being used at the moment.
262 */
263void AudioMixerDebug(PAUDIOMIXER pMixer, PCDBGFINFOHLP pHlp, const char *pszArgs)
264{
265 RT_NOREF(pszArgs);
266 PAUDMIXSINK pSink;
267 unsigned iSink = 0;
268
269 int rc2 = RTCritSectEnter(&pMixer->CritSect);
270 if (RT_FAILURE(rc2))
271 return;
272
273 pHlp->pfnPrintf(pHlp, "[Master] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", pMixer->pszName,
274 pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight, pMixer->VolMaster.fMuted);
275
276 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
277 {
278 pHlp->pfnPrintf(pHlp, "[Sink %u] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", iSink, pSink->pszName,
279 pSink->Volume.uLeft, pSink->Volume.uRight, pSink->Volume.fMuted);
280 ++iSink;
281 }
282
283 rc2 = RTCritSectLeave(&pMixer->CritSect);
284 AssertRC(rc2);
285}
286
287/**
288 * Destroys an audio mixer.
289 *
290 * @param pMixer Audio mixer to destroy.
291 */
292void AudioMixerDestroy(PAUDIOMIXER pMixer)
293{
294 if (!pMixer)
295 return;
296
297 int rc2 = RTCritSectEnter(&pMixer->CritSect);
298 AssertRC(rc2);
299
300 LogFlowFunc(("Destroying %s ...\n", pMixer->pszName));
301
302 PAUDMIXSINK pSink, pSinkNext;
303 RTListForEachSafe(&pMixer->lstSinks, pSink, pSinkNext, AUDMIXSINK, Node)
304 {
305 /* Save a pointer to the sink to remove, as pSink
306 * will not be valid anymore after calling audioMixerRemoveSinkInternal(). */
307 PAUDMIXSINK pSinkToRemove = pSink;
308
309 audioMixerRemoveSinkInternal(pMixer, pSinkToRemove);
310 audioMixerSinkDestroyInternal(pSinkToRemove);
311 }
312
313 pMixer->cSinks = 0;
314
315 if (pMixer->pszName)
316 {
317 RTStrFree(pMixer->pszName);
318 pMixer->pszName = NULL;
319 }
320
321 rc2 = RTCritSectLeave(&pMixer->CritSect);
322 AssertRC(rc2);
323
324 RTCritSectDelete(&pMixer->CritSect);
325
326 RTMemFree(pMixer);
327 pMixer = NULL;
328}
329
330/**
331 * Invalidates all internal data, internal version.
332 *
333 * @returns IPRT status code.
334 * @param pMixer Mixer to invalidate data for.
335 */
336int audioMixerInvalidateInternal(PAUDIOMIXER pMixer)
337{
338 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
339
340 LogFlowFunc(("[%s]\n", pMixer->pszName));
341
342 /* Propagate new master volume to all connected sinks. */
343 PAUDMIXSINK pSink;
344 RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
345 {
346 int rc2 = audioMixerSinkUpdateVolume(pSink, &pMixer->VolMaster);
347 AssertRC(rc2);
348 }
349
350 return VINF_SUCCESS;
351}
352
353/**
354 * Invalidates all internal data.
355 *
356 * @returns IPRT status code.
357 * @param pMixer Mixer to invalidate data for.
358 */
359void AudioMixerInvalidate(PAUDIOMIXER pMixer)
360{
361 AssertPtrReturnVoid(pMixer);
362
363 int rc2 = RTCritSectEnter(&pMixer->CritSect);
364 AssertRC(rc2);
365
366 LogFlowFunc(("[%s]\n", pMixer->pszName));
367
368 rc2 = audioMixerInvalidateInternal(pMixer);
369 AssertRC(rc2);
370
371 rc2 = RTCritSectLeave(&pMixer->CritSect);
372 AssertRC(rc2);
373}
374
375/**
376 * Removes a formerly attached audio sink for an audio mixer, internal version.
377 *
378 * @returns IPRT status code.
379 * @param pMixer Mixer to remove sink from.
380 * @param pSink Sink to remove.
381 */
382static int audioMixerRemoveSinkInternal(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
383{
384 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
385 if (!pSink)
386 return VERR_NOT_FOUND;
387
388 AssertMsgReturn(pSink->pParent == pMixer, ("%s: Is not part of mixer '%s'\n",
389 pSink->pszName, pMixer->pszName), VERR_NOT_FOUND);
390
391 LogFlowFunc(("[%s] pSink=%s, cSinks=%RU8\n",
392 pMixer->pszName, pSink->pszName, pMixer->cSinks));
393
394 /* Remove sink from mixer. */
395 RTListNodeRemove(&pSink->Node);
396 Assert(pMixer->cSinks);
397
398 /* Set mixer to NULL so that we know we're not part of any mixer anymore. */
399 pSink->pParent = NULL;
400
401 return VINF_SUCCESS;
402}
403
404/**
405 * Removes a formerly attached audio sink for an audio mixer.
406 *
407 * @returns IPRT status code.
408 * @param pMixer Mixer to remove sink from.
409 * @param pSink Sink to remove.
410 */
411void AudioMixerRemoveSink(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
412{
413 int rc2 = RTCritSectEnter(&pMixer->CritSect);
414 AssertRC(rc2);
415
416 audioMixerSinkRemoveAllStreamsInternal(pSink);
417 audioMixerRemoveSinkInternal(pMixer, pSink);
418
419 rc2 = RTCritSectLeave(&pMixer->CritSect);
420}
421
422/**
423 * Sets the mixer's master volume.
424 *
425 * @returns IPRT status code.
426 * @param pMixer Mixer to set master volume for.
427 * @param pVol Volume to set.
428 */
429int AudioMixerSetMasterVolume(PAUDIOMIXER pMixer, PPDMAUDIOVOLUME pVol)
430{
431 AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
432 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
433
434 int rc = RTCritSectEnter(&pMixer->CritSect);
435 if (RT_FAILURE(rc))
436 return rc;
437
438 memcpy(&pMixer->VolMaster, pVol, sizeof(PDMAUDIOVOLUME));
439
440 LogFlowFunc(("[%s] lVol=%RU32, rVol=%RU32 => fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
441 pMixer->pszName, pVol->uLeft, pVol->uRight,
442 pMixer->VolMaster.fMuted, pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight));
443
444 rc = audioMixerInvalidateInternal(pMixer);
445
446 int rc2 = RTCritSectLeave(&pMixer->CritSect);
447 AssertRC(rc2);
448
449 return rc;
450}
451
452/*********************************************************************************************************************************
453 * Mixer Sink implementation.
454 ********************************************************************************************************************************/
455
456/**
457 * Adds an audio stream to a specific audio sink.
458 *
459 * @returns IPRT status code.
460 * @param pSink Sink to add audio stream to.
461 * @param pStream Stream to add.
462 */
463int AudioMixerSinkAddStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
464{
465 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
466 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
467
468 int rc = RTCritSectEnter(&pSink->CritSect);
469 if (RT_FAILURE(rc))
470 return rc;
471
472 if (pSink->cStreams == UINT8_MAX) /* 255 streams per sink max. */
473 {
474 int rc2 = RTCritSectLeave(&pSink->CritSect);
475 AssertRC(rc2);
476
477 return VERR_NO_MORE_HANDLES;
478 }
479
480 LogFlowFuncEnter();
481
482#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
483 /* Make sure only compatible streams are added. */
484 if (pStream->enmDir == PDMAUDIODIR_IN)
485 {
486 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, &pStream->InOut.pIn->Props))
487 {
488#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
489 /* Chain: Stream (Child) -> Sink (Child) -> Guest (Parent). */
490 PPDMAUDIOMIXBUF pHstIn = &pStream->InOut.pIn->pHstStrmIn->MixBuf;
491 PPDMAUDIOMIXBUF pGstIn = &pStream->InOut.pIn->MixBuf;
492
493 /* Unlink any former parent from host input. */
494 AudioMixBufUnlink(pHstIn);
495
496 /* Link host input to this sink as a parent. */
497 rc = AudioMixBufLinkTo(pHstIn, &pSink->MixBuf);
498 AssertRC(rc);
499
500 /* Unlink any former parent from this sink. */
501 AudioMixBufUnlink(&pSink->MixBuf);
502
503 /* Link guest input to this sink as a parent. */
504 rc = AudioMixBufLinkTo(&pSink->MixBuf, pGstIn);
505 AssertRC(rc);
506# ifdef DEBUG
507 AudioMixBufDbgPrintChain(&pStream->InOut.pIn->MixBuf);
508# endif
509#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
510 }
511 else
512 AssertFailedStmt(rc = VERR_WRONG_TYPE);
513 }
514 else if (pStream->enmDir == PDMAUDIODIR_OUT)
515 {
516 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, &pStream->InOut.pOut->Props))
517 {
518#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
519 /* Chain: Guest (Child) -> Sink (Child) -> Stream (Parent). */
520 rc = AudioMixBufLinkTo(&pStream->InOut.pOut->pHstStrmOut->MixBuf, &pSink->MixBuf);
521# ifdef DEBUG
522 AudioMixBufDbgPrintChain(&pSink->MixBuf);
523# endif
524#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
525 }
526 else
527 AssertFailedStmt(rc = VERR_WRONG_TYPE);
528 }
529 else
530 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
531#else
532 rc = VINF_SUCCESS;
533#endif
534
535 if (RT_SUCCESS(rc))
536 {
537 /** @todo Check if stream already is assigned to (another) sink. */
538
539 /* If the sink is running and not in pending disable mode,
540 * make sure that the added stream also is enabled. */
541 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
542 && !(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
543 {
544 rc = audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_ENABLE, AUDMIXSTRMCTL_FLAG_NONE);
545 }
546
547 if (RT_SUCCESS(rc))
548 {
549 /* Apply the sink's combined volume to the stream. */
550 rc = pStream->pConn->pfnStreamSetVolume(pStream->pConn, pStream->pStream, &pSink->VolumeCombined);
551 AssertRC(rc);
552 }
553
554 if (RT_SUCCESS(rc))
555 {
556 /* Save pointer to sink the stream is attached to. */
557 pStream->pSink = pSink;
558
559 /* Append stream to sink's list. */
560 RTListAppend(&pSink->lstStreams, &pStream->Node);
561 pSink->cStreams++;
562 }
563 }
564
565 LogFlowFunc(("[%s] cStreams=%RU8, rc=%Rrc\n", pSink->pszName, pSink->cStreams, rc));
566
567 int rc2 = RTCritSectLeave(&pSink->CritSect);
568 AssertRC(rc2);
569
570 return rc;
571}
572
573/**
574 * Creates an audio mixer stream.
575 *
576 * @returns IPRT status code.
577 * @param pSink Sink to use for creating the stream.
578 * @param pConn Audio connector interface to use.
579 * @param pCfg Audio stream configuration to use.
580 * @param fFlags Stream flags. Currently unused, set to 0.
581 * @param ppStream Pointer which receives the newly created audio stream.
582 */
583int AudioMixerSinkCreateStream(PAUDMIXSINK pSink,
584 PPDMIAUDIOCONNECTOR pConn, PPDMAUDIOSTREAMCFG pCfg, AUDMIXSTREAMFLAGS fFlags, PAUDMIXSTREAM *ppStream)
585{
586 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
587 AssertPtrReturn(pConn, VERR_INVALID_POINTER);
588 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
589 /** @todo Validate fFlags. */
590 /* ppStream is optional. */
591
592 if (pConn->pfnGetStatus(pConn, PDMAUDIODIR_ANY) == PDMAUDIOBACKENDSTS_NOT_ATTACHED)
593 return VERR_AUDIO_BACKEND_NOT_ATTACHED;
594
595 PAUDMIXSTREAM pMixStream = (PAUDMIXSTREAM)RTMemAllocZ(sizeof(AUDMIXSTREAM));
596 if (!pMixStream)
597 return VERR_NO_MEMORY;
598
599 pMixStream->pszName = RTStrDup(pCfg->szName);
600 if (!pMixStream->pszName)
601 {
602 RTMemFree(pMixStream);
603 return VERR_NO_MEMORY;
604 }
605
606 int rc = RTCritSectEnter(&pSink->CritSect);
607 if (RT_FAILURE(rc))
608 return rc;
609
610 LogFlowFunc(("[%s] fFlags=0x%x (enmDir=%ld, %RU8 bits, %RU8 channels, %RU32Hz)\n",
611 pSink->pszName, fFlags, pCfg->enmDir, pCfg->Props.cBits, pCfg->Props.cChannels, pCfg->Props.uHz));
612
613 /*
614 * Initialize the host-side configuration for the stream to be created.
615 * Always use the sink's PCM audio format as the host side when creating a stream for it.
616 */
617 AssertMsg(DrvAudioHlpPCMPropsAreValid(&pSink->PCMProps),
618 ("%s: Does not (yet) have a format set when it must\n", pSink->pszName));
619
620 PDMAUDIOSTREAMCFG CfgHost;
621 rc = DrvAudioHlpPCMPropsToStreamCfg(&pSink->PCMProps, &CfgHost);
622 AssertRCReturn(rc, rc);
623
624 /* Apply the sink's direction for the configuration to use to
625 * create the stream. */
626 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
627 {
628 CfgHost.DestSource.Source = pCfg->DestSource.Source;
629 CfgHost.enmDir = PDMAUDIODIR_IN;
630 CfgHost.enmLayout = pCfg->enmLayout;
631 }
632 else
633 {
634 CfgHost.DestSource.Dest = pCfg->DestSource.Dest;
635 CfgHost.enmDir = PDMAUDIODIR_OUT;
636 CfgHost.enmLayout = pCfg->enmLayout;
637 }
638
639 RTStrPrintf(CfgHost.szName, sizeof(CfgHost.szName), "%s", pCfg->szName);
640
641 rc = RTCritSectInit(&pMixStream->CritSect);
642 if (RT_SUCCESS(rc))
643 {
644 PPDMAUDIOSTREAM pStream;
645 rc = pConn->pfnStreamCreate(pConn, &CfgHost, pCfg, &pStream);
646 if (RT_SUCCESS(rc))
647 {
648 /* Save the audio stream pointer to this mixing stream. */
649 pMixStream->pStream = pStream;
650
651 /* Increase the stream's reference count to let others know
652 * we're reyling on it to be around now. */
653 pConn->pfnStreamRetain(pConn, pStream);
654 }
655 }
656
657 if (RT_SUCCESS(rc))
658 {
659 rc = RTCircBufCreate(&pMixStream->pCircBuf, DrvAudioHlpMsToBytes(100 /* ms */, &pSink->PCMProps)); /** @todo Make this configurable. */
660 AssertRC(rc);
661 }
662
663 if (RT_SUCCESS(rc))
664 {
665 pMixStream->fFlags = fFlags;
666 pMixStream->pConn = pConn;
667
668 if (ppStream)
669 *ppStream = pMixStream;
670 }
671 else if (pMixStream)
672 {
673 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
674 AssertRC(rc2);
675
676 if (pMixStream->pszName)
677 {
678 RTStrFree(pMixStream->pszName);
679 pMixStream->pszName = NULL;
680 }
681
682 RTMemFree(pMixStream);
683 pMixStream = NULL;
684 }
685
686 int rc2 = RTCritSectLeave(&pSink->CritSect);
687 AssertRC(rc2);
688
689 return rc;
690}
691
692/**
693 * Static helper function to translate a sink command
694 * to a PDM audio stream command.
695 *
696 * @returns PDM audio stream command, or PDMAUDIOSTREAMCMD_UNKNOWN if not found.
697 * @param enmCmd Mixer sink command to translate.
698 */
699static PDMAUDIOSTREAMCMD audioMixerSinkToStreamCmd(AUDMIXSINKCMD enmCmd)
700{
701 switch (enmCmd)
702 {
703 case AUDMIXSINKCMD_ENABLE: return PDMAUDIOSTREAMCMD_ENABLE;
704 case AUDMIXSINKCMD_DISABLE: return PDMAUDIOSTREAMCMD_DISABLE;
705 case AUDMIXSINKCMD_PAUSE: return PDMAUDIOSTREAMCMD_PAUSE;
706 case AUDMIXSINKCMD_RESUME: return PDMAUDIOSTREAMCMD_RESUME;
707 default: break;
708 }
709
710 AssertMsgFailed(("Unsupported sink command %d\n", enmCmd));
711 return PDMAUDIOSTREAMCMD_UNKNOWN;
712}
713
714/**
715 * Controls a mixer sink.
716 *
717 * @returns IPRT status code.
718 * @param pSink Mixer sink to control.
719 * @param enmSinkCmd Sink command to set.
720 */
721int AudioMixerSinkCtl(PAUDMIXSINK pSink, AUDMIXSINKCMD enmSinkCmd)
722{
723 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
724
725 PDMAUDIOSTREAMCMD enmCmdStream = audioMixerSinkToStreamCmd(enmSinkCmd);
726 if (enmCmdStream == PDMAUDIOSTREAMCMD_UNKNOWN)
727 return VERR_NOT_SUPPORTED;
728
729 int rc = RTCritSectEnter(&pSink->CritSect);
730 if (RT_FAILURE(rc))
731 return rc;
732
733 PAUDMIXSTREAM pStream;
734 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
735 {
736 int rc2 = audioMixerStreamCtlInternal(pStream, enmCmdStream, AUDMIXSTRMCTL_FLAG_NONE);
737 if (RT_SUCCESS(rc))
738 rc = rc2;
739 /* Keep going. Flag? */
740 }
741
742 if (enmSinkCmd == AUDMIXSINKCMD_ENABLE)
743 {
744 /* Make sure to clear any other former flags again by assigning AUDMIXSINK_STS_RUNNING directly. */
745 pSink->fStatus = AUDMIXSINK_STS_RUNNING;
746 }
747 else if (enmSinkCmd == AUDMIXSINKCMD_DISABLE)
748 {
749 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
750 {
751 /* Set the sink in a pending disable state first.
752 * The final status (disabled) will be set in the sink's iteration. */
753 pSink->fStatus |= AUDMIXSINK_STS_PENDING_DISABLE;
754 }
755 }
756
757#ifdef LOG_ENABLED
758 char *pszStatus = dbgAudioMixerSinkStatusToStr(pSink->fStatus);
759 LogFlowFunc(("[%s] enmCmd=%d, fStatus=%s, rc=%Rrc\n", pSink->pszName, enmSinkCmd, pszStatus, rc));
760 RTStrFree(pszStatus);
761#endif
762
763 int rc2 = RTCritSectLeave(&pSink->CritSect);
764 AssertRC(rc2);
765
766 return rc;
767}
768
769/**
770 * Destroys a mixer sink and removes it from the attached mixer (if any).
771 *
772 * @param pSink Mixer sink to destroy.
773 */
774void AudioMixerSinkDestroy(PAUDMIXSINK pSink)
775{
776 if (!pSink)
777 return;
778
779 int rc2 = RTCritSectEnter(&pSink->CritSect);
780 AssertRC(rc2);
781
782 if (pSink->pParent)
783 {
784 /* Save mixer pointer, as after audioMixerRemoveSinkInternal() the
785 * pointer will be gone from the stream. */
786 PAUDIOMIXER pMixer = pSink->pParent;
787 AssertPtr(pMixer);
788
789 audioMixerRemoveSinkInternal(pMixer, pSink);
790
791 Assert(pMixer->cSinks);
792 pMixer->cSinks--;
793 }
794
795 rc2 = RTCritSectLeave(&pSink->CritSect);
796 AssertRC(rc2);
797
798 audioMixerSinkDestroyInternal(pSink);
799}
800
801/**
802 * Destroys a mixer sink.
803 *
804 * @param pSink Mixer sink to destroy.
805 */
806static void audioMixerSinkDestroyInternal(PAUDMIXSINK pSink)
807{
808 AssertPtrReturnVoid(pSink);
809
810 LogFunc(("%s\n", pSink->pszName));
811
812 PAUDMIXSTREAM pStream, pStreamNext;
813 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
814 {
815 /* Save a pointer to the stream to remove, as pStream
816 * will not be valid anymore after calling audioMixerSinkRemoveStreamInternal(). */
817 PAUDMIXSTREAM pStreamToRemove = pStream;
818
819 audioMixerSinkRemoveStreamInternal(pSink, pStreamToRemove);
820 audioMixerStreamDestroyInternal(pStreamToRemove);
821 }
822
823#ifdef VBOX_AUDIO_MIXER_DEBUG
824 DrvAudioHlpFileDestroy(pSink->Dbg.pFile);
825 pSink->Dbg.pFile = NULL;
826#endif
827
828 if (pSink->pszName)
829 {
830 RTStrFree(pSink->pszName);
831 pSink->pszName = NULL;
832 }
833
834 RTCritSectDelete(&pSink->CritSect);
835
836 RTMemFree(pSink);
837 pSink = NULL;
838}
839
840/**
841 * Returns the amount of bytes ready to be read from a sink since the last call
842 * to AudioMixerSinkUpdate().
843 *
844 * @returns Amount of bytes ready to be read from the sink.
845 * @param pSink Sink to return number of available bytes for.
846 */
847uint32_t AudioMixerSinkGetReadable(PAUDMIXSINK pSink)
848{
849 AssertPtrReturn(pSink, 0);
850
851 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT, ("%s: Can't read from a non-input sink\n", pSink->pszName));
852
853 int rc = RTCritSectEnter(&pSink->CritSect);
854 if (RT_FAILURE(rc))
855 return 0;
856
857 uint32_t cbReadable = 0;
858
859 if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
860 {
861#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
862# error "Implement me!"
863#else
864 PAUDMIXSTREAM pStreamRecSource = pSink->In.pStreamRecSource;
865 if (!pStreamRecSource)
866 {
867 Log3Func(("[%s] No recording source specified, skipping ...\n", pSink->pszName));
868 }
869 else
870 {
871 AssertPtr(pStreamRecSource->pConn);
872 cbReadable = pStreamRecSource->pConn->pfnStreamGetReadable(pStreamRecSource->pConn, pStreamRecSource->pStream);
873 }
874#endif
875 }
876
877 Log3Func(("[%s] cbReadable=%RU32\n", pSink->pszName, cbReadable));
878
879 int rc2 = RTCritSectLeave(&pSink->CritSect);
880 AssertRC(rc2);
881
882 return cbReadable;
883}
884
885/**
886 * Returns the sink's current recording source.
887 *
888 * @return Mixer stream which currently is set as current recording source, NULL if none is set.
889 * @param pSink Audio mixer sink to return current recording source for.
890 */
891PAUDMIXSTREAM AudioMixerSinkGetRecordingSource(PAUDMIXSINK pSink)
892{
893 int rc = RTCritSectEnter(&pSink->CritSect);
894 if (RT_FAILURE(rc))
895 return NULL;
896
897 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT, ("Specified sink is not an input sink\n"));
898
899 PAUDMIXSTREAM pStream = pSink->In.pStreamRecSource;
900
901 int rc2 = RTCritSectLeave(&pSink->CritSect);
902 AssertRC(rc2);
903
904 return pStream;
905}
906
907/**
908 * Returns the amount of bytes ready to be written to a sink since the last call
909 * to AudioMixerSinkUpdate().
910 *
911 * @returns Amount of bytes ready to be written to the sink.
912 * @param pSink Sink to return number of available bytes for.
913 */
914uint32_t AudioMixerSinkGetWritable(PAUDMIXSINK pSink)
915{
916 AssertPtrReturn(pSink, 0);
917
918 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT, ("%s: Can't write to a non-output sink\n", pSink->pszName));
919
920 int rc = RTCritSectEnter(&pSink->CritSect);
921 if (RT_FAILURE(rc))
922 return 0;
923
924 uint32_t cbWritable = 0;
925
926 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
927 && !(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
928 {
929#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
930# error "Implement me!"
931#else
932 /* Return how much data we expect since the last write. */
933 cbWritable = DrvAudioHlpMsToBytes(RTTimeMilliTS() - pSink->tsLastReadWrittenMs, &pSink->PCMProps);
934#endif
935 }
936
937 Log3Func(("[%s] cbWritable=%RU32\n", pSink->pszName, cbWritable));
938
939 int rc2 = RTCritSectLeave(&pSink->CritSect);
940 AssertRC(rc2);
941
942 return cbWritable;
943}
944
945/**
946 * Returns the sink's mixing direction.
947 *
948 * @returns Mixing direction.
949 * @param pSink Sink to return direction for.
950 */
951AUDMIXSINKDIR AudioMixerSinkGetDir(PAUDMIXSINK pSink)
952{
953 AssertPtrReturn(pSink, AUDMIXSINKDIR_UNKNOWN);
954
955 int rc = RTCritSectEnter(&pSink->CritSect);
956 if (RT_FAILURE(rc))
957 return AUDMIXSINKDIR_UNKNOWN;
958
959 AUDMIXSINKDIR enmDir = pSink->enmDir;
960
961 int rc2 = RTCritSectLeave(&pSink->CritSect);
962 AssertRC(rc2);
963
964 return enmDir;
965}
966
967/**
968 * Returns a specific mixer stream from a sink, based on its index.
969 *
970 * @returns Mixer stream if found, or NULL if not found.
971 * @param pSink Sink to retrieve mixer stream from.
972 * @param uIndex Index of the mixer stream to return.
973 */
974PAUDMIXSTREAM AudioMixerSinkGetStream(PAUDMIXSINK pSink, uint8_t uIndex)
975{
976 AssertPtrReturn(pSink, NULL);
977
978 int rc = RTCritSectEnter(&pSink->CritSect);
979 if (RT_FAILURE(rc))
980 return NULL;
981
982 AssertMsgReturn(uIndex < pSink->cStreams,
983 ("Index %RU8 exceeds stream count (%RU8)", uIndex, pSink->cStreams), NULL);
984
985 /* Slow lookup, d'oh. */
986 PAUDMIXSTREAM pStream = RTListGetFirst(&pSink->lstStreams, AUDMIXSTREAM, Node);
987 while (uIndex)
988 {
989 pStream = RTListGetNext(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node);
990 uIndex--;
991 }
992
993 /** @todo Do we need to raise the stream's reference count here? */
994
995 int rc2 = RTCritSectLeave(&pSink->CritSect);
996 AssertRC(rc2);
997
998 AssertPtr(pStream);
999 return pStream;
1000}
1001
1002/**
1003 * Returns the current status of a mixer sink.
1004 *
1005 * @returns The sink's current status.
1006 * @param pSink Mixer sink to return status for.
1007 */
1008AUDMIXSINKSTS AudioMixerSinkGetStatus(PAUDMIXSINK pSink)
1009{
1010 if (!pSink)
1011 return AUDMIXSINK_STS_NONE;
1012
1013 int rc2 = RTCritSectEnter(&pSink->CritSect);
1014 if (RT_FAILURE(rc2))
1015 return AUDMIXSINK_STS_NONE;
1016
1017 /* If the dirty flag is set, there is unprocessed data in the sink. */
1018 AUDMIXSINKSTS stsSink = pSink->fStatus;
1019
1020 rc2 = RTCritSectLeave(&pSink->CritSect);
1021 AssertRC(rc2);
1022
1023 return stsSink;
1024}
1025
1026/**
1027 * Returns the number of attached mixer streams to a mixer sink.
1028 *
1029 * @returns The number of attached mixer streams.
1030 * @param pSink Mixer sink to return number for.
1031 */
1032uint8_t AudioMixerSinkGetStreamCount(PAUDMIXSINK pSink)
1033{
1034 if (!pSink)
1035 return 0;
1036
1037 int rc2 = RTCritSectEnter(&pSink->CritSect);
1038 if (RT_FAILURE(rc2))
1039 return 0;
1040
1041 uint8_t cStreams = pSink->cStreams;
1042
1043 rc2 = RTCritSectLeave(&pSink->CritSect);
1044 AssertRC(rc2);
1045
1046 return cStreams;
1047}
1048
1049/**
1050 * Returns whether the sink is in an active state or not.
1051 * Note: The pending disable state also counts as active.
1052 *
1053 * @returns True if active, false if not.
1054 * @param pSink Sink to return active state for.
1055 */
1056bool AudioMixerSinkIsActive(PAUDMIXSINK pSink)
1057{
1058 if (!pSink)
1059 return false;
1060
1061 int rc2 = RTCritSectEnter(&pSink->CritSect);
1062 if (RT_FAILURE(rc2))
1063 return false;
1064
1065 bool fIsActive = pSink->fStatus & AUDMIXSINK_STS_RUNNING;
1066 /* Note: AUDMIXSINK_STS_PENDING_DISABLE implies AUDMIXSINK_STS_RUNNING. */
1067
1068 Log3Func(("[%s] fActive=%RTbool\n", pSink->pszName, fIsActive));
1069
1070 rc2 = RTCritSectLeave(&pSink->CritSect);
1071 AssertRC(rc2);
1072
1073 return fIsActive;
1074}
1075
1076/**
1077 * Reads audio data from a mixer sink.
1078 *
1079 * @returns IPRT status code.
1080 * @param pSink Mixer sink to read data from.
1081 * @param enmOp Mixer operation to use for reading the data.
1082 * @param pvBuf Buffer where to store the read data.
1083 * @param cbBuf Buffer size (in bytes) where to store the data.
1084 * @param pcbRead Number of bytes read. Optional.
1085 */
1086int AudioMixerSinkRead(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1087{
1088 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1089 RT_NOREF(enmOp);
1090 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1091 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1092 /* pcbRead is optional. */
1093
1094 /** @todo Handle mixing operation enmOp! */
1095
1096 int rc = RTCritSectEnter(&pSink->CritSect);
1097 if (RT_FAILURE(rc))
1098 return rc;
1099
1100 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT,
1101 ("Can't read from a sink which is not an input sink\n"));
1102
1103 uint32_t cbRead = 0;
1104
1105 /* Flag indicating whether this sink is in a 'clean' state,
1106 * e.g. there is no more data to read from. */
1107 bool fClean = true;
1108
1109 PAUDMIXSTREAM pStreamRecSource = pSink->In.pStreamRecSource;
1110 if (!pStreamRecSource)
1111 {
1112 Log3Func(("[%s] No recording source specified, skipping ...\n", pSink->pszName));
1113 }
1114 else if (!(pStreamRecSource->pConn->pfnStreamGetStatus(pStreamRecSource->pConn, pStreamRecSource->pStream) & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
1115 {
1116 Log3Func(("[%s] Stream '%s' disabled, skipping ...\n", pSink->pszName, pStreamRecSource->pszName));
1117 }
1118 else
1119 {
1120 uint32_t cbToRead = RT_MIN(cbBuf,
1121 pStreamRecSource->pConn->pfnStreamGetReadable(pStreamRecSource->pConn,
1122 pStreamRecSource->pStream));
1123 while (cbToRead)
1124 {
1125 uint32_t cbReadStrm;
1126 AssertPtr(pStreamRecSource->pConn);
1127#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1128# error "Implement me!"
1129#else
1130 rc = pStreamRecSource->pConn->pfnStreamRead(pStreamRecSource->pConn, pStreamRecSource->pStream,
1131 (uint8_t *)pvBuf + cbRead, cbToRead, &cbReadStrm);
1132#endif
1133 if (RT_FAILURE(rc))
1134 LogFunc(("[%s] Failed reading from stream '%s': %Rrc\n", pSink->pszName, pStreamRecSource->pszName, rc));
1135
1136 Log3Func(("[%s] Stream '%s': Read %RU32 bytes\n", pSink->pszName, pStreamRecSource->pszName, cbReadStrm));
1137
1138 if ( RT_FAILURE(rc)
1139 || !cbReadStrm)
1140 break;
1141
1142 AssertBreakStmt(cbReadStrm <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
1143 cbToRead -= cbReadStrm;
1144 cbRead += cbReadStrm;
1145 Assert(cbRead <= cbBuf);
1146 }
1147
1148 uint32_t cbReadable = pStreamRecSource->pConn->pfnStreamGetReadable(pStreamRecSource->pConn, pStreamRecSource->pStream);
1149
1150 /* Still some data available? Then sink is not clean (yet). */
1151 if (cbReadable)
1152 fClean = false;
1153
1154 if (RT_SUCCESS(rc))
1155 {
1156 if (fClean)
1157 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1158
1159 /* Update our last read time stamp. */
1160 pSink->tsLastReadWrittenMs = RTTimeMilliTS();
1161
1162#ifdef VBOX_AUDIO_MIXER_DEBUG
1163 int rc2 = DrvAudioHlpFileWrite(pSink->Dbg.pFile, pvBuf, cbRead, 0 /* fFlags */);
1164 AssertRC(rc2);
1165#endif
1166 if (pcbRead)
1167 *pcbRead = cbRead;
1168 }
1169 }
1170
1171#ifdef LOG_ENABLED
1172 char *pszStatus = dbgAudioMixerSinkStatusToStr(pSink->fStatus);
1173 Log2Func(("[%s] cbRead=%RU32, fClean=%RTbool, fStatus=%s, rc=%Rrc\n", pSink->pszName, cbRead, fClean, pszStatus, rc));
1174 RTStrFree(pszStatus);
1175#endif
1176
1177 int rc2 = RTCritSectLeave(&pSink->CritSect);
1178 AssertRC(rc2);
1179
1180 return rc;
1181}
1182
1183/**
1184 * Removes a mixer stream from a mixer sink, internal version.
1185 *
1186 * @returns IPRT status code.
1187 * @param pSink Sink to remove mixer stream from.
1188 * @param pStream Stream to remove.
1189 */
1190static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1191{
1192 AssertPtrReturn(pSink, VERR_INVALID_PARAMETER);
1193 if ( !pStream
1194 || !pStream->pSink) /* Not part of a sink anymore? */
1195 {
1196 return VERR_NOT_FOUND;
1197 }
1198
1199 AssertMsgReturn(pStream->pSink == pSink, ("Stream '%s' is not part of sink '%s'\n",
1200 pStream->pszName, pSink->pszName), VERR_NOT_FOUND);
1201
1202 LogFlowFunc(("[%s] (Stream = %s), cStreams=%RU8\n",
1203 pSink->pszName, pStream->pStream->szName, pSink->cStreams));
1204
1205#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1206 /* Unlink mixing buffer. */
1207 AudioMixBufUnlink(&pStream->pStream->MixBuf);
1208#endif
1209
1210 /* Remove stream from sink. */
1211 RTListNodeRemove(&pStream->Node);
1212
1213 int rc = VINF_SUCCESS;
1214
1215 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1216 {
1217 /* Make sure to also un-set the recording source if this stream was set
1218 * as the recording source before. */
1219 if (pStream == pSink->In.pStreamRecSource)
1220 rc = audioMixerSinkSetRecSourceInternal(pSink, NULL);
1221 }
1222
1223 /* Set sink to NULL so that we know we're not part of any sink anymore. */
1224 pStream->pSink = NULL;
1225
1226 return rc;
1227}
1228
1229/**
1230 * Removes a mixer stream from a mixer sink.
1231 *
1232 * @param pSink Sink to remove mixer stream from.
1233 * @param pStream Stream to remove.
1234 */
1235void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1236{
1237 int rc2 = RTCritSectEnter(&pSink->CritSect);
1238 AssertRC(rc2);
1239
1240 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pStream);
1241 if (RT_SUCCESS(rc2))
1242 {
1243 Assert(pSink->cStreams);
1244 pSink->cStreams--;
1245 }
1246
1247 rc2 = RTCritSectLeave(&pSink->CritSect);
1248 AssertRC(rc2);
1249}
1250
1251/**
1252 * Removes all attached streams from a given sink.
1253 *
1254 * @param pSink Sink to remove attached streams from.
1255 */
1256static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink)
1257{
1258 if (!pSink)
1259 return;
1260
1261 LogFunc(("%s\n", pSink->pszName));
1262
1263 PAUDMIXSTREAM pStream, pStreamNext;
1264 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
1265 audioMixerSinkRemoveStreamInternal(pSink, pStream);
1266}
1267
1268/**
1269 * Resets the sink's state.
1270 *
1271 * @param pSink Sink to reset.
1272 */
1273static void audioMixerSinkReset(PAUDMIXSINK pSink)
1274{
1275 if (!pSink)
1276 return;
1277
1278 LogFunc(("[%s]\n", pSink->pszName));
1279
1280 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1281 {
1282#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1283 AudioMixBufReset(&pSink->MixBuf);
1284#else
1285 pSink->In.cbReadable = 0;
1286#endif
1287 }
1288 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1289 {
1290#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1291 AudioMixBufReset(&pSink->MixBuf);
1292#else
1293 pSink->Out.cbWritable = 0;
1294#endif
1295 }
1296
1297 /* Update last updated timestamp. */
1298 pSink->tsLastUpdatedMs = RTTimeMilliTS();
1299
1300 /* Reset status. */
1301 pSink->fStatus = AUDMIXSINK_STS_NONE;
1302}
1303
1304/**
1305 * Removes all attached streams from a given sink.
1306 *
1307 * @param pSink Sink to remove attached streams from.
1308 */
1309void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
1310{
1311 if (!pSink)
1312 return;
1313
1314 int rc2 = RTCritSectEnter(&pSink->CritSect);
1315 AssertRC(rc2);
1316
1317 audioMixerSinkRemoveAllStreamsInternal(pSink);
1318
1319 pSink->cStreams = 0;
1320
1321 rc2 = RTCritSectLeave(&pSink->CritSect);
1322 AssertRC(rc2);
1323}
1324
1325/**
1326 * Resets a sink. This will immediately stop all processing.
1327 *
1328 * @param pSink Sink to reset.
1329 */
1330void AudioMixerSinkReset(PAUDMIXSINK pSink)
1331{
1332 if (!pSink)
1333 return;
1334
1335 int rc2 = RTCritSectEnter(&pSink->CritSect);
1336 AssertRC(rc2);
1337
1338 LogFlowFunc(("[%s]\n", pSink->pszName));
1339
1340 audioMixerSinkReset(pSink);
1341
1342 rc2 = RTCritSectLeave(&pSink->CritSect);
1343 AssertRC(rc2);
1344}
1345
1346/**
1347 * Returns the audio format of a mixer sink.
1348 *
1349 * @param pSink Sink to retrieve audio format for.
1350 * @param pPCMProps Where to the returned audio format.
1351 */
1352void AudioMixerSinkGetFormat(PAUDMIXSINK pSink, PPDMAUDIOPCMPROPS pPCMProps)
1353{
1354 AssertPtrReturnVoid(pSink);
1355 AssertPtrReturnVoid(pPCMProps);
1356
1357 int rc2 = RTCritSectEnter(&pSink->CritSect);
1358 if (RT_FAILURE(rc2))
1359 return;
1360
1361 memcpy(pPCMProps, &pSink->PCMProps, sizeof(PDMAUDIOPCMPROPS));
1362
1363 rc2 = RTCritSectLeave(&pSink->CritSect);
1364 AssertRC(rc2);
1365}
1366
1367/**
1368 * Sets the audio format of a mixer sink.
1369 *
1370 * @returns IPRT status code.
1371 * @param pSink Sink to set audio format for.
1372 * @param pPCMProps Audio format (PCM properties) to set.
1373 */
1374int AudioMixerSinkSetFormat(PAUDMIXSINK pSink, PPDMAUDIOPCMPROPS pPCMProps)
1375{
1376 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1377 AssertPtrReturn(pPCMProps, VERR_INVALID_POINTER);
1378
1379 int rc = RTCritSectEnter(&pSink->CritSect);
1380 if (RT_FAILURE(rc))
1381 return rc;
1382
1383 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, pPCMProps)) /* Bail out early if PCM properties are equal. */
1384 {
1385 rc = RTCritSectLeave(&pSink->CritSect);
1386 AssertRC(rc);
1387
1388 return rc;
1389 }
1390
1391 if (pSink->PCMProps.uHz)
1392 LogFlowFunc(("[%s] Old format: %RU8 bit, %RU8 channels, %RU32Hz\n",
1393 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
1394
1395 memcpy(&pSink->PCMProps, pPCMProps, sizeof(PDMAUDIOPCMPROPS));
1396
1397 LogFlowFunc(("[%s] New format %RU8 bit, %RU8 channels, %RU32Hz\n",
1398 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
1399
1400#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1401 /* Also update the sink's mixing buffer format. */
1402 AudioMixBufDestroy(&pSink->MixBuf);
1403 rc = AudioMixBufInit(&pSink->MixBuf, pSink->pszName, pPCMProps, _4K /** @todo Make configurable? */);
1404 if (RT_SUCCESS(rc))
1405 {
1406 PAUDMIXSTREAM pStream;
1407 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
1408 {
1409 /** @todo Invalidate mix buffers! */
1410 }
1411 }
1412#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
1413
1414#ifdef VBOX_AUDIO_MIXER_DEBUG
1415 if (RT_SUCCESS(rc))
1416 {
1417 DrvAudioHlpFileClose(pSink->Dbg.pFile);
1418
1419 char szTemp[RTPATH_MAX];
1420 int rc2 = RTPathTemp(szTemp, sizeof(szTemp));
1421 if (RT_SUCCESS(rc2))
1422 {
1423 /** @todo Sanitize sink name. */
1424
1425 char szName[64];
1426 RTStrPrintf(szName, sizeof(szName), "MixerSink-%s", pSink->pszName);
1427
1428 char szFile[RTPATH_MAX + 1];
1429 rc2 = DrvAudioHlpFileNameGet(szFile, RT_ELEMENTS(szFile), szTemp, szName,
1430 0 /* Instance */, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
1431 if (RT_SUCCESS(rc2))
1432 {
1433 rc2 = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAG_NONE,
1434 &pSink->Dbg.pFile);
1435 if (RT_SUCCESS(rc2))
1436 rc2 = DrvAudioHlpFileOpen(pSink->Dbg.pFile, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS, &pSink->PCMProps);
1437 }
1438 }
1439 }
1440#endif
1441
1442 int rc2 = RTCritSectLeave(&pSink->CritSect);
1443 AssertRC(rc2);
1444
1445 LogFlowFuncLeaveRC(rc);
1446 return rc;
1447}
1448
1449/**
1450 * Set the current recording source of an input mixer sink, internal version.
1451 *
1452 * @return IPRT status code.
1453 * @param pSink Input mixer sink to set recording source for.
1454 * @param pStream Mixer stream to set as current recording source. Must be an input stream.
1455 * Specify NULL to un-set the current recording source.
1456 */
1457static int audioMixerSinkSetRecSourceInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1458{
1459 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT, ("Specified sink is not an input sink\n"));
1460
1461 if (pStream) /* Can be NULL if un-setting. */
1462 {
1463 AssertPtr(pStream->pStream);
1464 AssertMsg(pStream->pStream->enmDir == PDMAUDIODIR_IN, ("Specified stream is not an input stream\n"));
1465 }
1466
1467 pSink->In.pStreamRecSource = pStream;
1468
1469 LogFunc(("[%s] Recording source is now '%s'\n",
1470 pSink->pszName, pSink->In.pStreamRecSource ? pSink->In.pStreamRecSource->pszName : "<None>"));
1471 return VINF_SUCCESS;
1472}
1473
1474/**
1475 * Set the current recording source of an input mixer sink.
1476 *
1477 * @return IPRT status code.
1478 * @param pSink Input mixer sink to set recording source for.
1479 * @param pStream Mixer stream to set as current recording source. Must be an input stream.
1480 */
1481int AudioMixerSinkSetRecordingSource(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1482{
1483 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1484 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1485
1486 int rc = RTCritSectEnter(&pSink->CritSect);
1487 if (RT_FAILURE(rc))
1488 return rc;
1489
1490 rc = audioMixerSinkSetRecSourceInternal(pSink, pStream);
1491
1492 int rc2 = RTCritSectLeave(&pSink->CritSect);
1493 AssertRC(rc2);
1494
1495 return rc;
1496}
1497
1498/**
1499 * Sets the volume of an individual sink.
1500 *
1501 * @returns IPRT status code.
1502 * @param pSink Sink to set volume for.
1503 * @param pVol Volume to set.
1504 */
1505int AudioMixerSinkSetVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
1506{
1507 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1508 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
1509
1510 int rc = RTCritSectEnter(&pSink->CritSect);
1511 if (RT_FAILURE(rc))
1512 return rc;
1513
1514 memcpy(&pSink->Volume, pVol, sizeof(PDMAUDIOVOLUME));
1515
1516 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1517 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1518
1519 AssertPtr(pSink->pParent);
1520 rc = audioMixerSinkUpdateVolume(pSink, &pSink->pParent->VolMaster);
1521
1522 int rc2 = RTCritSectLeave(&pSink->CritSect);
1523 AssertRC(rc2);
1524
1525 return rc;
1526}
1527
1528/**
1529 * Updates a mixer sink, internal version.
1530 *
1531 * @returns IPRT status code.
1532 * @param pSink Mixer sink to update.
1533 */
1534static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink)
1535{
1536 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1537
1538 int rc = VINF_SUCCESS;
1539
1540#ifdef LOG_ENABLED
1541 char *pszStatus = dbgAudioMixerSinkStatusToStr(pSink->fStatus);
1542 Log3Func(("[%s] fStatus=%s\n", pSink->pszName, pszStatus));
1543 RTStrFree(pszStatus);
1544#endif
1545
1546 /* Sink disabled? Take a shortcut. */
1547 if (!(pSink->fStatus & AUDMIXSINK_STS_RUNNING))
1548 return rc;
1549
1550 /* Number of detected disabled streams of this sink. */
1551 uint8_t cStreamsDisabled = 0;
1552
1553 PAUDMIXSTREAM pMixStream, pMixStreamNext;
1554 RTListForEachSafe(&pSink->lstStreams, pMixStream, pMixStreamNext, AUDMIXSTREAM, Node)
1555 {
1556 PPDMAUDIOSTREAM pStream = pMixStream->pStream;
1557 AssertPtr(pStream);
1558
1559 PPDMIAUDIOCONNECTOR pConn = pMixStream->pConn;
1560 AssertPtr(pConn);
1561
1562 uint32_t cfProc = 0;
1563
1564 int rc2 = pConn->pfnStreamIterate(pConn, pStream);
1565 if (RT_SUCCESS(rc2))
1566 {
1567 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1568 {
1569 rc = pConn->pfnStreamCapture(pConn, pStream, &cfProc);
1570 if (RT_FAILURE(rc2))
1571 {
1572 LogFunc(("%s: Failed capturing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1573 if (RT_SUCCESS(rc))
1574 rc = rc2;
1575 continue;
1576 }
1577
1578 if (cfProc)
1579 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1580 }
1581 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1582 {
1583 rc2 = pConn->pfnStreamPlay(pConn, pStream, &cfProc);
1584 if (RT_FAILURE(rc2))
1585 {
1586 LogFunc(("%s: Failed playing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1587 if (RT_SUCCESS(rc))
1588 rc = rc2;
1589 continue;
1590 }
1591 }
1592 else
1593 {
1594 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1595 continue;
1596 }
1597
1598 rc2 = pConn->pfnStreamIterate(pConn, pStream);
1599 if (RT_FAILURE(rc2))
1600 {
1601 LogFunc(("%s: Failed re-iterating stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1602 if (RT_SUCCESS(rc))
1603 rc = rc2;
1604 continue;
1605 }
1606
1607 PDMAUDIOSTREAMSTS strmSts = pConn->pfnStreamGetStatus(pConn, pStream);
1608
1609 /* Is the stream not enabled and also is not in a pending disable state anymore? */
1610 if ( !(strmSts & PDMAUDIOSTREAMSTS_FLAG_ENABLED)
1611 && !(strmSts & PDMAUDIOSTREAMSTS_FLAG_PENDING_DISABLE))
1612 {
1613 cStreamsDisabled++;
1614 }
1615 }
1616
1617 Log3Func(("\t%s: cPlayed/cCaptured=%RU32, rc2=%Rrc\n", pStream->szName, cfProc, rc2));
1618 }
1619
1620 Log3Func(("[%s] fPendingDisable=%RTbool, %RU8/%RU8 streams disabled\n",
1621 pSink->pszName, RT_BOOL(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE), cStreamsDisabled, pSink->cStreams));
1622
1623 /* Update last updated timestamp. */
1624 pSink->tsLastUpdatedMs = RTTimeMilliTS();
1625
1626 /* All streams disabled and the sink is in pending disable mode? */
1627 if ( cStreamsDisabled == pSink->cStreams
1628 && (pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
1629 {
1630 audioMixerSinkReset(pSink);
1631 }
1632
1633 Log3Func(("[%s] cbReadable=%RU32, cbWritable=%RU32, rc=%Rrc\n",
1634 pSink->pszName, pSink->In.cbReadable, pSink->Out.cbWritable, rc));
1635
1636 return rc;
1637}
1638
1639/**
1640 * Updates (invalidates) a mixer sink.
1641 *
1642 * @returns IPRT status code.
1643 * @param pSink Mixer sink to update.
1644 */
1645int AudioMixerSinkUpdate(PAUDMIXSINK pSink)
1646{
1647 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1648
1649 int rc = RTCritSectEnter(&pSink->CritSect);
1650 if (RT_FAILURE(rc))
1651 return rc;
1652
1653 rc = audioMixerSinkUpdateInternal(pSink);
1654
1655 int rc2 = RTCritSectLeave(&pSink->CritSect);
1656 AssertRC(rc2);
1657
1658 return rc;
1659}
1660
1661/**
1662 * Updates the (master) volume of a mixer sink.
1663 *
1664 * @returns IPRT status code.
1665 * @param pSink Mixer sink to update volume for.
1666 * @param pVolMaster Master volume to set.
1667 */
1668static int audioMixerSinkUpdateVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
1669{
1670 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1671 AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
1672
1673 LogFlowFunc(("[%s] Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1674 pSink->pszName, pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
1675 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32 ",
1676 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1677
1678 /** @todo Very crude implementation for now -- needs more work! */
1679
1680 pSink->VolumeCombined.fMuted = pVolMaster->fMuted || pSink->Volume.fMuted;
1681
1682 pSink->VolumeCombined.uLeft = ( (pSink->Volume.uLeft ? pSink->Volume.uLeft : 1)
1683 * (pVolMaster->uLeft ? pVolMaster->uLeft : 1)) / PDMAUDIO_VOLUME_MAX;
1684
1685 pSink->VolumeCombined.uRight = ( (pSink->Volume.uRight ? pSink->Volume.uRight : 1)
1686 * (pVolMaster->uRight ? pVolMaster->uRight : 1)) / PDMAUDIO_VOLUME_MAX;
1687
1688 LogFlow(("-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1689 pSink->VolumeCombined.fMuted, pSink->VolumeCombined.uLeft, pSink->VolumeCombined.uRight));
1690
1691 /* Propagate new sink volume to all streams in the sink. */
1692 PAUDMIXSTREAM pMixStream;
1693 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1694 {
1695 int rc2 = pMixStream->pConn->pfnStreamSetVolume(pMixStream->pConn, pMixStream->pStream, &pSink->VolumeCombined);
1696 AssertRC(rc2);
1697 }
1698
1699 return VINF_SUCCESS;
1700}
1701
1702/**
1703 * Writes data to a mixer sink.
1704 *
1705 * @returns IPRT status code.
1706 * @param pSink Sink to write data to.
1707 * @param enmOp Mixer operation to use when writing data to the sink.
1708 * @param pvBuf Buffer containing the audio data to write.
1709 * @param cbBuf Size (in bytes) of the buffer containing the audio data.
1710 * @param pcbWritten Number of bytes written. Optional.
1711 */
1712int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
1713{
1714 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1715 RT_NOREF(enmOp);
1716 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1717 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
1718 /* pcbWritten is optional. */
1719
1720 int rc = RTCritSectEnter(&pSink->CritSect);
1721 if (RT_FAILURE(rc))
1722 return rc;
1723
1724 AssertMsg(pSink->fStatus & AUDMIXSINK_STS_RUNNING,
1725 ("%s: Can't write to a sink which is not running (anymore) (status 0x%x)\n", pSink->pszName, pSink->fStatus));
1726 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
1727 ("%s: Can't write to a sink which is not an output sink\n", pSink->pszName));
1728
1729 Log3Func(("[%s] enmOp=%d, cbBuf=%RU32\n", pSink->pszName, enmOp, cbBuf));
1730
1731 PAUDMIXSTREAM pMixStream;
1732 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1733 {
1734 PRTCIRCBUF pCircBuf = pMixStream->pCircBuf;
1735 void *pvChunk;
1736 size_t cbChunk;
1737
1738 uint32_t cbWritten = 0;
1739 uint32_t cbToWrite = RT_MIN(cbBuf, (uint32_t)RTCircBufFree(pCircBuf));
1740 while (cbToWrite)
1741 {
1742 RTCircBufAcquireWriteBlock(pCircBuf, cbToWrite, &pvChunk, &cbChunk);
1743
1744 if (cbChunk)
1745 memcpy(pvChunk, (uint8_t *)pvBuf + cbWritten, cbChunk);
1746
1747 RTCircBufReleaseWriteBlock(pCircBuf, cbChunk);
1748
1749 cbWritten += (uint32_t)cbChunk;
1750
1751 Assert(cbToWrite >= cbChunk);
1752 cbToWrite -= (uint32_t)cbChunk;
1753 }
1754
1755 if (cbWritten < cbBuf)
1756 LogFunc(("[%s] Warning: Only written %RU32/%RU32 bytes for stream '%s'\n",
1757 pSink->pszName, cbWritten, cbBuf, pMixStream->pszName));
1758
1759 if (!pMixStream->tsLastReadWrittenMs)
1760 pMixStream->tsLastReadWrittenMs = RTTimeMilliTS();
1761
1762 pMixStream->tsLastReadWrittenMs = RTTimeMilliTS();
1763
1764 const uint32_t cbWritableStream = pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream);
1765 cbToWrite = RT_MIN(cbWritableStream, (uint32_t)RTCircBufUsed(pCircBuf));
1766
1767 int rc2 = VINF_SUCCESS;
1768
1769 while (cbToWrite)
1770 {
1771 RTCircBufAcquireReadBlock(pCircBuf, cbToWrite, &pvChunk, &cbChunk);
1772
1773 if (cbChunk)
1774 {
1775 rc2 = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, pvChunk, (uint32_t)cbChunk,
1776 &cbWritten);
1777 if (RT_FAILURE(rc2))
1778 LogFunc(("[%s] Failed writing to stream '%s': %Rrc\n", pSink->pszName, pMixStream->pszName, rc2));
1779 }
1780
1781 RTCircBufReleaseReadBlock(pCircBuf, cbWritten);
1782
1783 if ( !cbWritten
1784 || cbWritten < cbChunk)
1785 break;
1786
1787 if (RT_FAILURE(rc2))
1788 break;
1789
1790 Assert(cbToWrite >= cbChunk);
1791 cbToWrite -= (uint32_t)cbChunk;
1792 }
1793
1794 if (RTCircBufUsed(pCircBuf))
1795 {
1796 /* Set dirty bit. */
1797 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1798 }
1799 }
1800
1801 /* Update our last written time stamp. */
1802 pSink->tsLastReadWrittenMs = RTTimeMilliTS();
1803
1804 if (pcbWritten)
1805 *pcbWritten = cbBuf; /* Always report everything written, as the backends need to keep up themselves. */
1806
1807 int rc2 = RTCritSectLeave(&pSink->CritSect);
1808 AssertRC(rc2);
1809
1810 return rc;
1811}
1812
1813/*********************************************************************************************************************************
1814 * Mixer Stream implementation.
1815 ********************************************************************************************************************************/
1816
1817/**
1818 * Controls a mixer stream, internal version.
1819 *
1820 * @returns IPRT status code.
1821 * @param pMixStream Mixer stream to control.
1822 * @param enmCmd Mixer stream command to use.
1823 * @param fCtl Additional control flags. Pass 0.
1824 */
1825int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
1826{
1827 AssertPtr(pMixStream->pConn);
1828 AssertPtr(pMixStream->pStream);
1829
1830 RT_NOREF(fCtl);
1831
1832 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
1833
1834 LogFlowFunc(("[%s] enmCmd=%ld, rc=%Rrc\n", pMixStream->pszName, enmCmd, rc));
1835
1836 return rc;
1837}
1838
1839/**
1840 * Controls a mixer stream.
1841 *
1842 * @returns IPRT status code.
1843 * @param pMixStream Mixer stream to control.
1844 * @param enmCmd Mixer stream command to use.
1845 * @param fCtl Additional control flags. Pass 0.
1846 */
1847int AudioMixerStreamCtl(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
1848{
1849 RT_NOREF(fCtl);
1850 AssertPtrReturn(pMixStream, VERR_INVALID_POINTER);
1851 /** @todo Validate fCtl. */
1852
1853 int rc = RTCritSectEnter(&pMixStream->CritSect);
1854 if (RT_FAILURE(rc))
1855 return rc;
1856
1857 rc = audioMixerStreamCtlInternal(pMixStream, enmCmd, fCtl);
1858
1859 int rc2 = RTCritSectLeave(&pMixStream->CritSect);
1860 if (RT_SUCCESS(rc))
1861 rc = rc2;
1862
1863 return rc;
1864}
1865
1866/**
1867 * Destroys a mixer stream, internal version.
1868 *
1869 * @param pMixStream Mixer stream to destroy.
1870 */
1871static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream)
1872{
1873 AssertPtrReturnVoid(pMixStream);
1874
1875 LogFunc(("%s\n", pMixStream->pszName));
1876
1877 if (pMixStream->pConn) /* Stream has a connector interface present? */
1878 {
1879 if (pMixStream->pStream)
1880 {
1881 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
1882 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
1883
1884 pMixStream->pStream = NULL;
1885 }
1886
1887 pMixStream->pConn = NULL;
1888 }
1889
1890 if (pMixStream->pszName)
1891 {
1892 RTStrFree(pMixStream->pszName);
1893 pMixStream->pszName = NULL;
1894 }
1895
1896 if (pMixStream->pCircBuf)
1897 {
1898 RTCircBufDestroy(pMixStream->pCircBuf);
1899 pMixStream->pCircBuf = NULL;
1900 }
1901
1902 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
1903 AssertRC(rc2);
1904
1905 RTMemFree(pMixStream);
1906 pMixStream = NULL;
1907}
1908
1909/**
1910 * Destroys a mixer stream.
1911 *
1912 * @param pMixStream Mixer stream to destroy.
1913 */
1914void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream)
1915{
1916 if (!pMixStream)
1917 return;
1918
1919 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1920 AssertRC(rc2);
1921
1922 LogFunc(("%s\n", pMixStream->pszName));
1923
1924 if (pMixStream->pSink) /* Is the stream part of a sink? */
1925 {
1926 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
1927 * pointer will be gone from the stream. */
1928 PAUDMIXSINK pSink = pMixStream->pSink;
1929
1930 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
1931 if (RT_SUCCESS(rc2))
1932 {
1933 Assert(pSink->cStreams);
1934 pSink->cStreams--;
1935 }
1936 }
1937 else
1938 rc2 = VINF_SUCCESS;
1939
1940 int rc3 = RTCritSectLeave(&pMixStream->CritSect);
1941 AssertRC(rc3);
1942
1943 if (RT_SUCCESS(rc2))
1944 {
1945 audioMixerStreamDestroyInternal(pMixStream);
1946 pMixStream = NULL;
1947 }
1948
1949 LogFlowFunc(("Returning %Rrc\n", rc2));
1950}
1951
1952/**
1953 * Returns whether a mixer stream currently is active (playing/recording) or not.
1954 *
1955 * @returns @c true if playing/recording, @c false if not.
1956 * @param pMixStream Mixer stream to return status for.
1957 */
1958bool AudioMixerStreamIsActive(PAUDMIXSTREAM pMixStream)
1959{
1960 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1961 if (RT_FAILURE(rc2))
1962 return false;
1963
1964 AssertPtr(pMixStream->pConn);
1965 AssertPtr(pMixStream->pStream);
1966
1967 bool fIsActive;
1968
1969 if ( pMixStream->pConn
1970 && pMixStream->pStream
1971 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
1972 {
1973 fIsActive = true;
1974 }
1975 else
1976 fIsActive = false;
1977
1978 rc2 = RTCritSectLeave(&pMixStream->CritSect);
1979 AssertRC(rc2);
1980
1981 return fIsActive;
1982}
1983
1984/**
1985 * Returns whether a mixer stream is valid (e.g. initialized and in a working state) or not.
1986 *
1987 * @returns @c true if valid, @c false if not.
1988 * @param pMixStream Mixer stream to return status for.
1989 */
1990bool AudioMixerStreamIsValid(PAUDMIXSTREAM pMixStream)
1991{
1992 if (!pMixStream)
1993 return false;
1994
1995 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1996 if (RT_FAILURE(rc2))
1997 return false;
1998
1999 bool fIsValid;
2000
2001 if ( pMixStream->pConn
2002 && pMixStream->pStream
2003 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED))
2004 {
2005 fIsValid = true;
2006 }
2007 else
2008 fIsValid = false;
2009
2010 rc2 = RTCritSectLeave(&pMixStream->CritSect);
2011 AssertRC(rc2);
2012
2013 return fIsValid;
2014}
2015
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