VirtualBox

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

Last change on this file since 74897 was 74877, checked in by vboxsync, 6 years ago

Audio/Mixer: Fixed triggering a harmless debug assertion if a certain stream is not ready in audioMixerSinkWriteToStreamEx().

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