VirtualBox

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

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

Audio: Use the DrvAudioHlpStreamStatusXXX() helpers where applicable.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 61.3 KB
Line 
1/* $Id: AudioMixer.cpp 73432 2018-08-01 15:03:03Z 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, DrvAudioHlpMilliToBytes(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 const uint64_t deltaLastReadWriteNs = RTTimeNanoTS() - pSink->tsLastReadWrittenNs;
925
926 uint32_t cbWritable = 0;
927
928 if ( (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
929 && !(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
930 {
931#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
932# error "Implement me!"
933#else
934 /* Return how much data we expect since the last write. */
935 cbWritable = DrvAudioHlpNanoToBytes(deltaLastReadWriteNs, &pSink->PCMProps);
936
937 /* Make sure to align the writable size to the stream's frame size. */
938 cbWritable = DrvAudioHlpBytesAlign(cbWritable, &pSink->PCMProps);
939#endif
940 }
941
942 Log3Func(("Mixer: [%s] cbWritable=%RU32 (%RU64ms)\n", pSink->pszName, cbWritable, deltaLastReadWriteNs / RT_NS_1MS_64));
943
944 int rc2 = RTCritSectLeave(&pSink->CritSect);
945 AssertRC(rc2);
946
947 return cbWritable;
948}
949
950/**
951 * Returns the sink's mixing direction.
952 *
953 * @returns Mixing direction.
954 * @param pSink Sink to return direction for.
955 */
956AUDMIXSINKDIR AudioMixerSinkGetDir(PAUDMIXSINK pSink)
957{
958 AssertPtrReturn(pSink, AUDMIXSINKDIR_UNKNOWN);
959
960 int rc = RTCritSectEnter(&pSink->CritSect);
961 if (RT_FAILURE(rc))
962 return AUDMIXSINKDIR_UNKNOWN;
963
964 AUDMIXSINKDIR enmDir = pSink->enmDir;
965
966 int rc2 = RTCritSectLeave(&pSink->CritSect);
967 AssertRC(rc2);
968
969 return enmDir;
970}
971
972/**
973 * Returns a specific mixer stream from a sink, based on its index.
974 *
975 * @returns Mixer stream if found, or NULL if not found.
976 * @param pSink Sink to retrieve mixer stream from.
977 * @param uIndex Index of the mixer stream to return.
978 */
979PAUDMIXSTREAM AudioMixerSinkGetStream(PAUDMIXSINK pSink, uint8_t uIndex)
980{
981 AssertPtrReturn(pSink, NULL);
982
983 int rc = RTCritSectEnter(&pSink->CritSect);
984 if (RT_FAILURE(rc))
985 return NULL;
986
987 AssertMsgReturn(uIndex < pSink->cStreams,
988 ("Index %RU8 exceeds stream count (%RU8)", uIndex, pSink->cStreams), NULL);
989
990 /* Slow lookup, d'oh. */
991 PAUDMIXSTREAM pStream = RTListGetFirst(&pSink->lstStreams, AUDMIXSTREAM, Node);
992 while (uIndex)
993 {
994 pStream = RTListGetNext(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node);
995 uIndex--;
996 }
997
998 /** @todo Do we need to raise the stream's reference count here? */
999
1000 int rc2 = RTCritSectLeave(&pSink->CritSect);
1001 AssertRC(rc2);
1002
1003 AssertPtr(pStream);
1004 return pStream;
1005}
1006
1007/**
1008 * Returns the current status of a mixer sink.
1009 *
1010 * @returns The sink's current status.
1011 * @param pSink Mixer sink to return status for.
1012 */
1013AUDMIXSINKSTS AudioMixerSinkGetStatus(PAUDMIXSINK pSink)
1014{
1015 if (!pSink)
1016 return AUDMIXSINK_STS_NONE;
1017
1018 int rc2 = RTCritSectEnter(&pSink->CritSect);
1019 if (RT_FAILURE(rc2))
1020 return AUDMIXSINK_STS_NONE;
1021
1022 /* If the dirty flag is set, there is unprocessed data in the sink. */
1023 AUDMIXSINKSTS stsSink = pSink->fStatus;
1024
1025 rc2 = RTCritSectLeave(&pSink->CritSect);
1026 AssertRC(rc2);
1027
1028 return stsSink;
1029}
1030
1031/**
1032 * Returns the number of attached mixer streams to a mixer sink.
1033 *
1034 * @returns The number of attached mixer streams.
1035 * @param pSink Mixer sink to return number for.
1036 */
1037uint8_t AudioMixerSinkGetStreamCount(PAUDMIXSINK pSink)
1038{
1039 if (!pSink)
1040 return 0;
1041
1042 int rc2 = RTCritSectEnter(&pSink->CritSect);
1043 if (RT_FAILURE(rc2))
1044 return 0;
1045
1046 uint8_t cStreams = pSink->cStreams;
1047
1048 rc2 = RTCritSectLeave(&pSink->CritSect);
1049 AssertRC(rc2);
1050
1051 return cStreams;
1052}
1053
1054/**
1055 * Returns whether the sink is in an active state or not.
1056 * Note: The pending disable state also counts as active.
1057 *
1058 * @returns True if active, false if not.
1059 * @param pSink Sink to return active state for.
1060 */
1061bool AudioMixerSinkIsActive(PAUDMIXSINK pSink)
1062{
1063 if (!pSink)
1064 return false;
1065
1066 int rc2 = RTCritSectEnter(&pSink->CritSect);
1067 if (RT_FAILURE(rc2))
1068 return false;
1069
1070 bool fIsActive = pSink->fStatus & AUDMIXSINK_STS_RUNNING;
1071 /* Note: AUDMIXSINK_STS_PENDING_DISABLE implies AUDMIXSINK_STS_RUNNING. */
1072
1073 Log3Func(("[%s] fActive=%RTbool\n", pSink->pszName, fIsActive));
1074
1075 rc2 = RTCritSectLeave(&pSink->CritSect);
1076 AssertRC(rc2);
1077
1078 return fIsActive;
1079}
1080
1081/**
1082 * Reads audio data from a mixer sink.
1083 *
1084 * @returns IPRT status code.
1085 * @param pSink Mixer sink to read data from.
1086 * @param enmOp Mixer operation to use for reading the data.
1087 * @param pvBuf Buffer where to store the read data.
1088 * @param cbBuf Buffer size (in bytes) where to store the data.
1089 * @param pcbRead Number of bytes read. Optional.
1090 */
1091int AudioMixerSinkRead(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1092{
1093 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1094 RT_NOREF(enmOp);
1095 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1096 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1097 /* pcbRead is optional. */
1098
1099 /** @todo Handle mixing operation enmOp! */
1100
1101 int rc = RTCritSectEnter(&pSink->CritSect);
1102 if (RT_FAILURE(rc))
1103 return rc;
1104
1105 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT,
1106 ("Can't read from a sink which is not an input sink\n"));
1107
1108 uint32_t cbRead = 0;
1109
1110 /* Flag indicating whether this sink is in a 'clean' state,
1111 * e.g. there is no more data to read from. */
1112 bool fClean = true;
1113
1114 PAUDMIXSTREAM pStreamRecSource = pSink->In.pStreamRecSource;
1115 if (!pStreamRecSource)
1116 {
1117 Log3Func(("[%s] No recording source specified, skipping ...\n", pSink->pszName));
1118 }
1119 else if (!DrvAudioHlpStreamStatusCanRead(
1120 pStreamRecSource->pConn->pfnStreamGetStatus(pStreamRecSource->pConn, pStreamRecSource->pStream)))
1121 {
1122 Log3Func(("[%s] Stream '%s' disabled, skipping ...\n", pSink->pszName, pStreamRecSource->pszName));
1123 }
1124 else
1125 {
1126 uint32_t cbToRead = RT_MIN(cbBuf,
1127 pStreamRecSource->pConn->pfnStreamGetReadable(pStreamRecSource->pConn,
1128 pStreamRecSource->pStream));
1129 while (cbToRead)
1130 {
1131 uint32_t cbReadStrm;
1132 AssertPtr(pStreamRecSource->pConn);
1133#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1134# error "Implement me!"
1135#else
1136 rc = pStreamRecSource->pConn->pfnStreamRead(pStreamRecSource->pConn, pStreamRecSource->pStream,
1137 (uint8_t *)pvBuf + cbRead, cbToRead, &cbReadStrm);
1138#endif
1139 if (RT_FAILURE(rc))
1140 LogFunc(("[%s] Failed reading from stream '%s': %Rrc\n", pSink->pszName, pStreamRecSource->pszName, rc));
1141
1142 Log3Func(("[%s] Stream '%s': Read %RU32 bytes\n", pSink->pszName, pStreamRecSource->pszName, cbReadStrm));
1143
1144 if ( RT_FAILURE(rc)
1145 || !cbReadStrm)
1146 break;
1147
1148 AssertBreakStmt(cbReadStrm <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
1149 cbToRead -= cbReadStrm;
1150 cbRead += cbReadStrm;
1151 Assert(cbRead <= cbBuf);
1152 }
1153
1154 uint32_t cbReadable = pStreamRecSource->pConn->pfnStreamGetReadable(pStreamRecSource->pConn, pStreamRecSource->pStream);
1155
1156 /* Still some data available? Then sink is not clean (yet). */
1157 if (cbReadable)
1158 fClean = false;
1159
1160 if (RT_SUCCESS(rc))
1161 {
1162 if (fClean)
1163 pSink->fStatus &= ~AUDMIXSINK_STS_DIRTY;
1164
1165 /* Update our last read time stamp. */
1166 pSink->tsLastReadWrittenNs = RTTimeNanoTS();
1167
1168#ifdef VBOX_AUDIO_MIXER_DEBUG
1169 int rc2 = DrvAudioHlpFileWrite(pSink->Dbg.pFile, pvBuf, cbRead, 0 /* fFlags */);
1170 AssertRC(rc2);
1171#endif
1172 if (pcbRead)
1173 *pcbRead = cbRead;
1174 }
1175 }
1176
1177#ifdef LOG_ENABLED
1178 char *pszStatus = dbgAudioMixerSinkStatusToStr(pSink->fStatus);
1179 Log2Func(("[%s] cbRead=%RU32, fClean=%RTbool, fStatus=%s, rc=%Rrc\n", pSink->pszName, cbRead, fClean, pszStatus, rc));
1180 RTStrFree(pszStatus);
1181#endif
1182
1183 int rc2 = RTCritSectLeave(&pSink->CritSect);
1184 AssertRC(rc2);
1185
1186 return rc;
1187}
1188
1189/**
1190 * Removes a mixer stream from a mixer sink, internal version.
1191 *
1192 * @returns IPRT status code.
1193 * @param pSink Sink to remove mixer stream from.
1194 * @param pStream Stream to remove.
1195 */
1196static int audioMixerSinkRemoveStreamInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1197{
1198 AssertPtrReturn(pSink, VERR_INVALID_PARAMETER);
1199 if ( !pStream
1200 || !pStream->pSink) /* Not part of a sink anymore? */
1201 {
1202 return VERR_NOT_FOUND;
1203 }
1204
1205 AssertMsgReturn(pStream->pSink == pSink, ("Stream '%s' is not part of sink '%s'\n",
1206 pStream->pszName, pSink->pszName), VERR_NOT_FOUND);
1207
1208 LogFlowFunc(("[%s] (Stream = %s), cStreams=%RU8\n",
1209 pSink->pszName, pStream->pStream->szName, pSink->cStreams));
1210
1211#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1212 /* Unlink mixing buffer. */
1213 AudioMixBufUnlink(&pStream->pStream->MixBuf);
1214#endif
1215
1216 /* Remove stream from sink. */
1217 RTListNodeRemove(&pStream->Node);
1218
1219 int rc = VINF_SUCCESS;
1220
1221 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1222 {
1223 /* Make sure to also un-set the recording source if this stream was set
1224 * as the recording source before. */
1225 if (pStream == pSink->In.pStreamRecSource)
1226 rc = audioMixerSinkSetRecSourceInternal(pSink, NULL);
1227 }
1228
1229 /* Set sink to NULL so that we know we're not part of any sink anymore. */
1230 pStream->pSink = NULL;
1231
1232 return rc;
1233}
1234
1235/**
1236 * Removes a mixer stream from a mixer sink.
1237 *
1238 * @param pSink Sink to remove mixer stream from.
1239 * @param pStream Stream to remove.
1240 */
1241void AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1242{
1243 int rc2 = RTCritSectEnter(&pSink->CritSect);
1244 AssertRC(rc2);
1245
1246 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pStream);
1247 if (RT_SUCCESS(rc2))
1248 {
1249 Assert(pSink->cStreams);
1250 pSink->cStreams--;
1251 }
1252
1253 rc2 = RTCritSectLeave(&pSink->CritSect);
1254 AssertRC(rc2);
1255}
1256
1257/**
1258 * Removes all attached streams from a given sink.
1259 *
1260 * @param pSink Sink to remove attached streams from.
1261 */
1262static void audioMixerSinkRemoveAllStreamsInternal(PAUDMIXSINK pSink)
1263{
1264 if (!pSink)
1265 return;
1266
1267 LogFunc(("%s\n", pSink->pszName));
1268
1269 PAUDMIXSTREAM pStream, pStreamNext;
1270 RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
1271 audioMixerSinkRemoveStreamInternal(pSink, pStream);
1272}
1273
1274/**
1275 * Resets the sink's state.
1276 *
1277 * @param pSink Sink to reset.
1278 */
1279static void audioMixerSinkReset(PAUDMIXSINK pSink)
1280{
1281 if (!pSink)
1282 return;
1283
1284 LogFunc(("[%s]\n", pSink->pszName));
1285
1286 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1287 {
1288#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1289 AudioMixBufReset(&pSink->MixBuf);
1290#else
1291 pSink->In.cbReadable = 0;
1292#endif
1293 }
1294 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1295 {
1296#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1297 AudioMixBufReset(&pSink->MixBuf);
1298#else
1299 pSink->Out.cbWritable = 0;
1300#endif
1301 }
1302
1303 /* Update last updated timestamp. */
1304 pSink->tsLastUpdatedMs = RTTimeMilliTS();
1305
1306 /* Reset status. */
1307 pSink->fStatus = AUDMIXSINK_STS_NONE;
1308}
1309
1310/**
1311 * Removes all attached streams from a given sink.
1312 *
1313 * @param pSink Sink to remove attached streams from.
1314 */
1315void AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink)
1316{
1317 if (!pSink)
1318 return;
1319
1320 int rc2 = RTCritSectEnter(&pSink->CritSect);
1321 AssertRC(rc2);
1322
1323 audioMixerSinkRemoveAllStreamsInternal(pSink);
1324
1325 pSink->cStreams = 0;
1326
1327 rc2 = RTCritSectLeave(&pSink->CritSect);
1328 AssertRC(rc2);
1329}
1330
1331/**
1332 * Resets a sink. This will immediately stop all processing.
1333 *
1334 * @param pSink Sink to reset.
1335 */
1336void AudioMixerSinkReset(PAUDMIXSINK pSink)
1337{
1338 if (!pSink)
1339 return;
1340
1341 int rc2 = RTCritSectEnter(&pSink->CritSect);
1342 AssertRC(rc2);
1343
1344 LogFlowFunc(("[%s]\n", pSink->pszName));
1345
1346 audioMixerSinkReset(pSink);
1347
1348 rc2 = RTCritSectLeave(&pSink->CritSect);
1349 AssertRC(rc2);
1350}
1351
1352/**
1353 * Returns the audio format of a mixer sink.
1354 *
1355 * @param pSink Sink to retrieve audio format for.
1356 * @param pPCMProps Where to the returned audio format.
1357 */
1358void AudioMixerSinkGetFormat(PAUDMIXSINK pSink, PPDMAUDIOPCMPROPS pPCMProps)
1359{
1360 AssertPtrReturnVoid(pSink);
1361 AssertPtrReturnVoid(pPCMProps);
1362
1363 int rc2 = RTCritSectEnter(&pSink->CritSect);
1364 if (RT_FAILURE(rc2))
1365 return;
1366
1367 memcpy(pPCMProps, &pSink->PCMProps, sizeof(PDMAUDIOPCMPROPS));
1368
1369 rc2 = RTCritSectLeave(&pSink->CritSect);
1370 AssertRC(rc2);
1371}
1372
1373/**
1374 * Sets the audio format of a mixer sink.
1375 *
1376 * @returns IPRT status code.
1377 * @param pSink Sink to set audio format for.
1378 * @param pPCMProps Audio format (PCM properties) to set.
1379 */
1380int AudioMixerSinkSetFormat(PAUDMIXSINK pSink, PPDMAUDIOPCMPROPS pPCMProps)
1381{
1382 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1383 AssertPtrReturn(pPCMProps, VERR_INVALID_POINTER);
1384
1385 int rc = RTCritSectEnter(&pSink->CritSect);
1386 if (RT_FAILURE(rc))
1387 return rc;
1388
1389 if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, pPCMProps)) /* Bail out early if PCM properties are equal. */
1390 {
1391 rc = RTCritSectLeave(&pSink->CritSect);
1392 AssertRC(rc);
1393
1394 return rc;
1395 }
1396
1397 if (pSink->PCMProps.uHz)
1398 LogFlowFunc(("[%s] Old format: %RU8 bit, %RU8 channels, %RU32Hz\n",
1399 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
1400
1401 memcpy(&pSink->PCMProps, pPCMProps, sizeof(PDMAUDIOPCMPROPS));
1402
1403 LogFlowFunc(("[%s] New format %RU8 bit, %RU8 channels, %RU32Hz\n",
1404 pSink->pszName, pSink->PCMProps.cBits, pSink->PCMProps.cChannels, pSink->PCMProps.uHz));
1405
1406#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
1407 /* Also update the sink's mixing buffer format. */
1408 AudioMixBufDestroy(&pSink->MixBuf);
1409 rc = AudioMixBufInit(&pSink->MixBuf, pSink->pszName, pPCMProps, _4K /** @todo Make configurable? */);
1410 if (RT_SUCCESS(rc))
1411 {
1412 PAUDMIXSTREAM pStream;
1413 RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
1414 {
1415 /** @todo Invalidate mix buffers! */
1416 }
1417 }
1418#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
1419
1420#ifdef VBOX_AUDIO_MIXER_DEBUG
1421 if (RT_SUCCESS(rc))
1422 {
1423 DrvAudioHlpFileClose(pSink->Dbg.pFile);
1424
1425 char szTemp[RTPATH_MAX];
1426 int rc2 = RTPathTemp(szTemp, sizeof(szTemp));
1427 if (RT_SUCCESS(rc2))
1428 {
1429 /** @todo Sanitize sink name. */
1430
1431 char szName[64];
1432 RTStrPrintf(szName, sizeof(szName), "MixerSink-%s", pSink->pszName);
1433
1434 char szFile[RTPATH_MAX + 1];
1435 rc2 = DrvAudioHlpFileNameGet(szFile, RT_ELEMENTS(szFile), szTemp, szName,
1436 0 /* Instance */, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
1437 if (RT_SUCCESS(rc2))
1438 {
1439 rc2 = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAG_NONE,
1440 &pSink->Dbg.pFile);
1441 if (RT_SUCCESS(rc2))
1442 rc2 = DrvAudioHlpFileOpen(pSink->Dbg.pFile, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS, &pSink->PCMProps);
1443 }
1444 }
1445 }
1446#endif
1447
1448 int rc2 = RTCritSectLeave(&pSink->CritSect);
1449 AssertRC(rc2);
1450
1451 LogFlowFuncLeaveRC(rc);
1452 return rc;
1453}
1454
1455/**
1456 * Set the current recording source of an input mixer sink, internal version.
1457 *
1458 * @return IPRT status code.
1459 * @param pSink Input mixer sink to set recording source for.
1460 * @param pStream Mixer stream to set as current recording source. Must be an input stream.
1461 * Specify NULL to un-set the current recording source.
1462 */
1463static int audioMixerSinkSetRecSourceInternal(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1464{
1465 AssertMsg(pSink->enmDir == AUDMIXSINKDIR_INPUT, ("Specified sink is not an input sink\n"));
1466
1467 if (pStream) /* Can be NULL if un-setting. */
1468 {
1469 AssertPtr(pStream->pStream);
1470 AssertMsg(pStream->pStream->enmDir == PDMAUDIODIR_IN, ("Specified stream is not an input stream\n"));
1471 }
1472
1473 pSink->In.pStreamRecSource = pStream;
1474
1475 LogFunc(("[%s] Recording source is now '%s'\n",
1476 pSink->pszName, pSink->In.pStreamRecSource ? pSink->In.pStreamRecSource->pszName : "<None>"));
1477 return VINF_SUCCESS;
1478}
1479
1480/**
1481 * Set the current recording source of an input mixer sink.
1482 *
1483 * @return IPRT status code.
1484 * @param pSink Input mixer sink to set recording source for.
1485 * @param pStream Mixer stream to set as current recording source. Must be an input stream.
1486 */
1487int AudioMixerSinkSetRecordingSource(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
1488{
1489 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1490 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1491
1492 int rc = RTCritSectEnter(&pSink->CritSect);
1493 if (RT_FAILURE(rc))
1494 return rc;
1495
1496 rc = audioMixerSinkSetRecSourceInternal(pSink, pStream);
1497
1498 int rc2 = RTCritSectLeave(&pSink->CritSect);
1499 AssertRC(rc2);
1500
1501 return rc;
1502}
1503
1504/**
1505 * Sets the volume of an individual sink.
1506 *
1507 * @returns IPRT status code.
1508 * @param pSink Sink to set volume for.
1509 * @param pVol Volume to set.
1510 */
1511int AudioMixerSinkSetVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
1512{
1513 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1514 AssertPtrReturn(pVol, VERR_INVALID_POINTER);
1515
1516 int rc = RTCritSectEnter(&pSink->CritSect);
1517 if (RT_FAILURE(rc))
1518 return rc;
1519
1520 memcpy(&pSink->Volume, pVol, sizeof(PDMAUDIOVOLUME));
1521
1522 LogFlowFunc(("[%s] fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
1523 pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
1524
1525 AssertPtr(pSink->pParent);
1526 rc = audioMixerSinkUpdateVolume(pSink, &pSink->pParent->VolMaster);
1527
1528 int rc2 = RTCritSectLeave(&pSink->CritSect);
1529 AssertRC(rc2);
1530
1531 return rc;
1532}
1533
1534/**
1535 * Updates a mixer sink, internal version.
1536 *
1537 * @returns IPRT status code.
1538 * @param pSink Mixer sink to update.
1539 */
1540static int audioMixerSinkUpdateInternal(PAUDMIXSINK pSink)
1541{
1542 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
1543
1544 int rc = VINF_SUCCESS;
1545
1546#ifdef LOG_ENABLED
1547 char *pszStatus = dbgAudioMixerSinkStatusToStr(pSink->fStatus);
1548 Log3Func(("[%s] fStatus=%s\n", pSink->pszName, pszStatus));
1549 RTStrFree(pszStatus);
1550#endif
1551
1552 /* Sink disabled? Take a shortcut. */
1553 if (!(pSink->fStatus & AUDMIXSINK_STS_RUNNING))
1554 return rc;
1555
1556 /* Number of detected disabled streams of this sink. */
1557 uint8_t cStreamsDisabled = 0;
1558
1559 PAUDMIXSTREAM pMixStream, pMixStreamNext;
1560 RTListForEachSafe(&pSink->lstStreams, pMixStream, pMixStreamNext, AUDMIXSTREAM, Node)
1561 {
1562 PPDMAUDIOSTREAM pStream = pMixStream->pStream;
1563 AssertPtr(pStream);
1564
1565 PPDMIAUDIOCONNECTOR pConn = pMixStream->pConn;
1566 AssertPtr(pConn);
1567
1568 uint32_t cfProc = 0;
1569
1570 if (!DrvAudioHlpStreamStatusIsReady(pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream)))
1571 continue;
1572
1573 int rc2 = pConn->pfnStreamIterate(pConn, pStream);
1574 if (RT_SUCCESS(rc2))
1575 {
1576 if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
1577 {
1578 rc = pConn->pfnStreamCapture(pConn, pStream, &cfProc);
1579 if (RT_FAILURE(rc2))
1580 {
1581 LogFunc(("%s: Failed capturing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1582 if (RT_SUCCESS(rc))
1583 rc = rc2;
1584 continue;
1585 }
1586
1587 if (cfProc)
1588 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1589 }
1590 else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
1591 {
1592 rc2 = pConn->pfnStreamPlay(pConn, pStream, &cfProc);
1593 if (RT_FAILURE(rc2))
1594 {
1595 LogFunc(("%s: Failed playing stream '%s', rc=%Rrc\n", pSink->pszName, pStream->szName, rc2));
1596 if (RT_SUCCESS(rc))
1597 rc = rc2;
1598 continue;
1599 }
1600 }
1601 else
1602 {
1603 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
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 PAUDMIXSTREAM pMixStream;
1730 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
1731 {
1732 if (!DrvAudioHlpStreamStatusCanWrite(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream)))
1733 continue;
1734
1735 PRTCIRCBUF pCircBuf = pMixStream->pCircBuf;
1736 void *pvChunk;
1737 size_t cbChunk;
1738
1739 uint32_t cbWritten = 0;
1740 uint32_t cbToWrite = RT_MIN(cbBuf, (uint32_t)RTCircBufFree(pCircBuf));
1741 while (cbToWrite)
1742 {
1743 RTCircBufAcquireWriteBlock(pCircBuf, cbToWrite, &pvChunk, &cbChunk);
1744
1745 if (cbChunk)
1746 memcpy(pvChunk, (uint8_t *)pvBuf + cbWritten, cbChunk);
1747
1748 RTCircBufReleaseWriteBlock(pCircBuf, cbChunk);
1749
1750 cbWritten += (uint32_t)cbChunk;
1751 Assert(cbWritten <= cbBuf);
1752
1753 Assert(cbToWrite >= cbChunk);
1754 cbToWrite -= (uint32_t)cbChunk;
1755 }
1756
1757 if (cbWritten < cbBuf)
1758 {
1759 LogFunc(("[%s] Warning: Only written %RU32/%RU32 bytes for stream '%s'\n",
1760 pSink->pszName, cbWritten, cbBuf, pMixStream->pszName));
1761 LogRel2(("Audio: Buffer overrun for mixer sink '%s', stream '%s'\n", pSink->pszName, pMixStream->pszName));
1762#ifdef DEBUG_andy
1763 AssertFailed();
1764#endif
1765 }
1766
1767 if (cbWritten) /* Update the mixer stream's last written time stamp. */
1768 pMixStream->tsLastReadWrittenNs = RTTimeNanoTS();
1769
1770 cbToWrite = RT_MIN(pMixStream->pConn->pfnStreamGetWritable(pMixStream->pConn, pMixStream->pStream),
1771 (uint32_t)RTCircBufUsed(pCircBuf));
1772 cbToWrite = DrvAudioHlpBytesAlign(cbToWrite, &pSink->PCMProps);
1773
1774 int rc2 = VINF_SUCCESS;
1775
1776 while (cbToWrite)
1777 {
1778 RTCircBufAcquireReadBlock(pCircBuf, cbToWrite, &pvChunk, &cbChunk);
1779
1780 if (cbChunk)
1781 {
1782 rc2 = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream, pvChunk, (uint32_t)cbChunk,
1783 &cbWritten);
1784 if (RT_FAILURE(rc2))
1785 LogFunc(("[%s] Failed writing to stream '%s': %Rrc\n", pSink->pszName, pMixStream->pszName, rc2));
1786 }
1787
1788 RTCircBufReleaseReadBlock(pCircBuf, cbWritten);
1789
1790 if ( !cbWritten
1791 || cbWritten < cbChunk)
1792 {
1793#ifdef DEBUG_andy
1794 AssertFailed();
1795#endif
1796 break;
1797 }
1798
1799 if (RT_FAILURE(rc2))
1800 break;
1801
1802 Assert(cbToWrite >= cbChunk);
1803 cbToWrite -= (uint32_t)cbChunk;
1804 }
1805
1806 if (RTCircBufUsed(pCircBuf))
1807 {
1808 /* Set dirty bit. */
1809 pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
1810 }
1811 }
1812
1813 /* Update the sink's last written time stamp. */
1814 pSink->tsLastReadWrittenNs = RTTimeNanoTS();
1815
1816 if (pcbWritten)
1817 *pcbWritten = cbBuf; /* Always report everything written, as the backends need to keep up themselves. */
1818
1819 int rc2 = RTCritSectLeave(&pSink->CritSect);
1820 AssertRC(rc2);
1821
1822 return rc;
1823}
1824
1825/*********************************************************************************************************************************
1826 * Mixer Stream implementation.
1827 ********************************************************************************************************************************/
1828
1829/**
1830 * Controls a mixer stream, internal version.
1831 *
1832 * @returns IPRT status code.
1833 * @param pMixStream Mixer stream to control.
1834 * @param enmCmd Mixer stream command to use.
1835 * @param fCtl Additional control flags. Pass 0.
1836 */
1837int audioMixerStreamCtlInternal(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
1838{
1839 AssertPtr(pMixStream->pConn);
1840 AssertPtr(pMixStream->pStream);
1841
1842 RT_NOREF(fCtl);
1843
1844 int rc = pMixStream->pConn->pfnStreamControl(pMixStream->pConn, pMixStream->pStream, enmCmd);
1845
1846 LogFlowFunc(("[%s] enmCmd=%ld, rc=%Rrc\n", pMixStream->pszName, enmCmd, rc));
1847
1848 return rc;
1849}
1850
1851/**
1852 * Controls a mixer stream.
1853 *
1854 * @returns IPRT status code.
1855 * @param pMixStream Mixer stream to control.
1856 * @param enmCmd Mixer stream command to use.
1857 * @param fCtl Additional control flags. Pass 0.
1858 */
1859int AudioMixerStreamCtl(PAUDMIXSTREAM pMixStream, PDMAUDIOSTREAMCMD enmCmd, uint32_t fCtl)
1860{
1861 RT_NOREF(fCtl);
1862 AssertPtrReturn(pMixStream, VERR_INVALID_POINTER);
1863 /** @todo Validate fCtl. */
1864
1865 int rc = RTCritSectEnter(&pMixStream->CritSect);
1866 if (RT_FAILURE(rc))
1867 return rc;
1868
1869 rc = audioMixerStreamCtlInternal(pMixStream, enmCmd, fCtl);
1870
1871 int rc2 = RTCritSectLeave(&pMixStream->CritSect);
1872 if (RT_SUCCESS(rc))
1873 rc = rc2;
1874
1875 return rc;
1876}
1877
1878/**
1879 * Destroys a mixer stream, internal version.
1880 *
1881 * @param pMixStream Mixer stream to destroy.
1882 */
1883static void audioMixerStreamDestroyInternal(PAUDMIXSTREAM pMixStream)
1884{
1885 AssertPtrReturnVoid(pMixStream);
1886
1887 LogFunc(("%s\n", pMixStream->pszName));
1888
1889 if (pMixStream->pConn) /* Stream has a connector interface present? */
1890 {
1891 if (pMixStream->pStream)
1892 {
1893 pMixStream->pConn->pfnStreamRelease(pMixStream->pConn, pMixStream->pStream);
1894 pMixStream->pConn->pfnStreamDestroy(pMixStream->pConn, pMixStream->pStream);
1895
1896 pMixStream->pStream = NULL;
1897 }
1898
1899 pMixStream->pConn = NULL;
1900 }
1901
1902 if (pMixStream->pszName)
1903 {
1904 RTStrFree(pMixStream->pszName);
1905 pMixStream->pszName = NULL;
1906 }
1907
1908 if (pMixStream->pCircBuf)
1909 {
1910 RTCircBufDestroy(pMixStream->pCircBuf);
1911 pMixStream->pCircBuf = NULL;
1912 }
1913
1914 int rc2 = RTCritSectDelete(&pMixStream->CritSect);
1915 AssertRC(rc2);
1916
1917 RTMemFree(pMixStream);
1918 pMixStream = NULL;
1919}
1920
1921/**
1922 * Destroys a mixer stream.
1923 *
1924 * @param pMixStream Mixer stream to destroy.
1925 */
1926void AudioMixerStreamDestroy(PAUDMIXSTREAM pMixStream)
1927{
1928 if (!pMixStream)
1929 return;
1930
1931 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1932 AssertRC(rc2);
1933
1934 LogFunc(("%s\n", pMixStream->pszName));
1935
1936 if (pMixStream->pSink) /* Is the stream part of a sink? */
1937 {
1938 /* Save sink pointer, as after audioMixerSinkRemoveStreamInternal() the
1939 * pointer will be gone from the stream. */
1940 PAUDMIXSINK pSink = pMixStream->pSink;
1941
1942 rc2 = audioMixerSinkRemoveStreamInternal(pSink, pMixStream);
1943 if (RT_SUCCESS(rc2))
1944 {
1945 Assert(pSink->cStreams);
1946 pSink->cStreams--;
1947 }
1948 }
1949 else
1950 rc2 = VINF_SUCCESS;
1951
1952 int rc3 = RTCritSectLeave(&pMixStream->CritSect);
1953 AssertRC(rc3);
1954
1955 if (RT_SUCCESS(rc2))
1956 {
1957 audioMixerStreamDestroyInternal(pMixStream);
1958 pMixStream = NULL;
1959 }
1960
1961 LogFlowFunc(("Returning %Rrc\n", rc2));
1962}
1963
1964/**
1965 * Returns whether a mixer stream currently is active (playing/recording) or not.
1966 *
1967 * @returns @c true if playing/recording, @c false if not.
1968 * @param pMixStream Mixer stream to return status for.
1969 */
1970bool AudioMixerStreamIsActive(PAUDMIXSTREAM pMixStream)
1971{
1972 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
1973 if (RT_FAILURE(rc2))
1974 return false;
1975
1976 AssertPtr(pMixStream->pConn);
1977 AssertPtr(pMixStream->pStream);
1978
1979 bool fIsActive;
1980
1981 if ( pMixStream->pConn
1982 && pMixStream->pStream
1983 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_ENABLED))
1984 {
1985 fIsActive = true;
1986 }
1987 else
1988 fIsActive = false;
1989
1990 rc2 = RTCritSectLeave(&pMixStream->CritSect);
1991 AssertRC(rc2);
1992
1993 return fIsActive;
1994}
1995
1996/**
1997 * Returns whether a mixer stream is valid (e.g. initialized and in a working state) or not.
1998 *
1999 * @returns @c true if valid, @c false if not.
2000 * @param pMixStream Mixer stream to return status for.
2001 */
2002bool AudioMixerStreamIsValid(PAUDMIXSTREAM pMixStream)
2003{
2004 if (!pMixStream)
2005 return false;
2006
2007 int rc2 = RTCritSectEnter(&pMixStream->CritSect);
2008 if (RT_FAILURE(rc2))
2009 return false;
2010
2011 bool fIsValid;
2012
2013 if ( pMixStream->pConn
2014 && pMixStream->pStream
2015 && RT_BOOL(pMixStream->pConn->pfnStreamGetStatus(pMixStream->pConn, pMixStream->pStream) & PDMAUDIOSTREAMSTS_FLAG_INITIALIZED))
2016 {
2017 fIsValid = true;
2018 }
2019 else
2020 fIsValid = false;
2021
2022 rc2 = RTCritSectLeave(&pMixStream->CritSect);
2023 AssertRC(rc2);
2024
2025 return fIsValid;
2026}
2027
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette