summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Audio/AudioMixer.h
blob: cb701dace17417607e9f6dc086615be6ef665423 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/* $Id: AudioMixer.h $ */
/** @file
 * VBox audio - Mixing routines.
 *
 * The mixing routines are mainly used by the various audio device emulations
 * to achieve proper multiplexing from/to attached devices LUNs.
 */

/*
 * Copyright (C) 2014-2023 Oracle and/or its affiliates.
 *
 * This file is part of VirtualBox base platform packages, as
 * available from https://www.virtualbox.org.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, in version 3 of the
 * License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <https://www.gnu.org/licenses>.
 *
 * SPDX-License-Identifier: GPL-3.0-only
 */

#ifndef VBOX_INCLUDED_SRC_Audio_AudioMixer_h
#define VBOX_INCLUDED_SRC_Audio_AudioMixer_h
#ifndef RT_WITHOUT_PRAGMA_ONCE
# pragma once
#endif

#include <iprt/cdefs.h>
#include <iprt/critsect.h>

#include <VBox/vmm/pdmaudioifs.h>
#include "AudioMixBuffer.h"
#include "AudioHlp.h"


/** @defgroup grp_pdm_ifs_audio_mixing  Audio Mixing
 * @ingroup grp_pdm_ifs_audio
 *
 * @note This is currently placed under PDM Audio Interface as that seemed like
 *       the best place for it.
 *
 * @{
 */

/** Pointer to an audio mixer sink. */
typedef struct AUDMIXSINK *PAUDMIXSINK;
/** Pointer to a const audio mixer sink. */
typedef struct AUDMIXSINK const *PCAUDMIXSINK;


/**
 * Audio mixer instance.
 */
typedef struct AUDIOMIXER
{
    /** Magic value (AUDIOMIXER_MAGIC). */
    uintptr_t               uMagic;
    /** The mixer's name (allocated after this structure). */
    char const             *pszName;
    /** The master volume of this mixer. */
    PDMAUDIOVOLUME          VolMaster;
    /** List of audio mixer sinks (AUDMIXSINK). */
    RTLISTANCHOR            lstSinks;
    /** Number of used audio sinks. */
    uint8_t                 cSinks;
    /** Mixer flags. See AUDMIXER_FLAGS_XXX. */
    uint32_t                fFlags;
    /** The mixer's critical section. */
    RTCRITSECT              CritSect;
} AUDIOMIXER;
/** Pointer to an audio mixer instance. */
typedef AUDIOMIXER *PAUDIOMIXER;

/** Value for AUDIOMIXER::uMagic. (Attilio Joseph "Teo" Macero)  */
#define AUDIOMIXER_MAGIC                UINT32_C(0x19251030)
/** Value for AUDIOMIXER::uMagic after destruction. */
#define AUDIOMIXER_MAGIC_DEAD           UINT32_C(0x20080219)

/** @name AUDMIXER_FLAGS_XXX - For AudioMixerCreate().
 * @{ */
/** No mixer flags specified. */
#define AUDMIXER_FLAGS_NONE             0
/** Debug mode enabled.
 *  This writes .WAV file to the host, usually to the temporary directory. */
#define AUDMIXER_FLAGS_DEBUG            RT_BIT(0)
/** Validation mask. */
#define AUDMIXER_FLAGS_VALID_MASK       UINT32_C(0x00000001)
/** @} */


/**
 * Audio mixer stream.
 */
typedef struct AUDMIXSTREAM
{
    /** List entry on AUDMIXSINK::lstStreams. */
    RTLISTNODE              Node;
    /** Magic value (AUDMIXSTREAM_MAGIC). */
    uint32_t                uMagic;
    /** The backend buffer size in frames (for draining deadline calc). */
    uint32_t                cFramesBackendBuffer;
    /** Stream status of type AUDMIXSTREAM_STATUS_. */
    uint32_t                fStatus;
    /** Number of writable/readable frames the last time we checked. */
    uint32_t                cFramesLastAvail;
    /** Set if the stream has been found unreliable wrt. consuming/producing
     * samples, and that we shouldn't consider it when deciding how much to move
     * from the mixer buffer and to the drivers. */
    bool                    fUnreliable;
    /** Name of this stream. */
    char                   *pszName;
    /** The statistics prefix. */
    char                   *pszStatPrefix;
    /** Sink this stream is attached to. */
    PAUDMIXSINK             pSink;
    /** Pointer to audio connector being used. */
    PPDMIAUDIOCONNECTOR     pConn;
    /** Pointer to PDM audio stream this mixer stream handles. */
    PPDMAUDIOSTREAM         pStream;
    union
    {
        /** Output: Mixing buffer peeking state & config. */
        AUDIOMIXBUFPEEKSTATE    PeekState;
        /** Input:  Mixing buffer writing state & config. */
        AUDIOMIXBUFWRITESTATE   WriteState;
    };
    /** Last read (recording) / written (playback) timestamp (in ns). */
    uint64_t                tsLastReadWrittenNs;
    /** The streams's critical section. */
    RTCRITSECT              CritSect;
} AUDMIXSTREAM;
/** Pointer to an audio mixer stream. */
typedef AUDMIXSTREAM *PAUDMIXSTREAM;

/** Value for AUDMIXSTREAM::uMagic. (Jan Erik Kongshaug)  */
#define AUDMIXSTREAM_MAGIC                UINT32_C(0x19440704)
/** Value for AUDMIXSTREAM::uMagic after destruction. */
#define AUDMIXSTREAM_MAGIC_DEAD           UINT32_C(0x20191105)


/** @name AUDMIXSTREAM_STATUS_XXX - mixer stream status.
 * (This is a destilled version of PDMAUDIOSTREAM_STS_XXX.)
 * @{ */
/** No status set. */
#define AUDMIXSTREAM_STATUS_NONE                UINT32_C(0)
/** The mixing stream is enabled (active). */
#define AUDMIXSTREAM_STATUS_ENABLED             RT_BIT_32(0)
/** The mixing stream can be read from.
 * Always set together with AUDMIXSTREAM_STATUS_ENABLED. */
#define AUDMIXSTREAM_STATUS_CAN_READ            RT_BIT_32(1)
/** The mixing stream can be written to.
 * Always set together with AUDMIXSTREAM_STATUS_ENABLED. */
#define AUDMIXSTREAM_STATUS_CAN_WRITE           RT_BIT_32(2)
/** @} */


/** Callback for an asynchronous I/O update job.  */
typedef DECLCALLBACKTYPE(void, FNAUDMIXSINKUPDATE,(PPDMDEVINS pDevIns, PAUDMIXSINK pSink, void *pvUser));
/** Pointer to a callback for an asynchronous I/O update job.  */
typedef FNAUDMIXSINKUPDATE *PFNAUDMIXSINKUPDATE;

/**
 * Audio mixer sink.
 */
typedef struct AUDMIXSINK
{
    /** List entry on AUDIOMIXER::lstSinks. */
    RTLISTNODE              Node;
    /** Magic value (AUDMIXSINK_MAGIC). */
    uint32_t                uMagic;
    /** The sink direction (either PDMAUDIODIR_IN or PDMAUDIODIR_OUT). */
    PDMAUDIODIR             enmDir;
    /** Pointer to mixer object this sink is bound to. */
    PAUDIOMIXER             pParent;
    /** Name of this sink (allocated after this structure). */
    char const             *pszName;
    /** The sink's PCM format (i.e. the guest device side). */
    PDMAUDIOPCMPROPS        PCMProps;
    /** Sink status bits - AUDMIXSINK_STS_XXX. */
    uint32_t                fStatus;
    /** Number of bytes to be transferred from the device DMA buffer before the
     *  streams will be put into draining mode. */
    uint32_t                cbDmaLeftToDrain;
    /** The deadline for draining if it's pending. */
    uint64_t                nsDrainDeadline;
    /** When the draining startet (for logging). */
    uint64_t                nsDrainStarted;
    /** Number of streams assigned. */
    uint8_t                 cStreams;
    /** List of assigned streams (AUDMIXSTREAM).
     * @note All streams have the same PCM properties, so the mixer does not do
     *       any conversion.  bird: That is *NOT* true any more, the mixer has
     *       encoders/decoder states for each stream (well, input is still a todo).
     *
     * @todo Use something faster -- vector maybe?  bird: It won't be faster.  You
     *       will have a vector of stream pointers (because you cannot have a vector
     *       of full AUDMIXSTREAM structures since they'll move when the vector is
     *       reallocated and we need pointers to them to give out to devices), which
     *       is the same cost as going via Node.pNext/pPrev. */
    RTLISTANCHOR            lstStreams;
    /** The volume of this sink. The volume always will
     *  be combined with the mixer's master volume. */
    PDMAUDIOVOLUME          Volume;
    /** The volume of this sink, combined with the last set  master volume. */
    PDMAUDIOVOLUME          VolumeCombined;
    /** Timestamp since last update (in ms). */
    uint64_t                tsLastUpdatedMs;
    /** Last read (recording) / written (playback) timestamp (in ns). */
    uint64_t                tsLastReadWrittenNs;
    /** Union for input/output specifics. */
    union
    {
        struct
        {
            /** The sink's peek state. */
            AUDIOMIXBUFPEEKSTATE    State;
        } In;
        struct
        {
            /** The sink's write state. */
            AUDIOMIXBUFWRITESTATE   State;
        } Out;
    };
    struct
    {
        PAUDIOHLPFILE       pFile;
    } Dbg;
    /** This sink's mixing buffer. */
    AUDIOMIXBUF             MixBuf;
    /** Asynchronous I/O thread related stuff. */
    struct
    {
        /** The thread handle, NIL_RTTHREAD if not active. */
        RTTHREAD                hThread;
        /** Event for letting the thread know there is some data to process. */
        RTSEMEVENT              hEvent;
        /** The device instance (same for all update jobs). */
        PPDMDEVINS              pDevIns;
        /** Started indicator. */
        volatile bool           fStarted;
        /** Shutdown indicator. */
        volatile bool           fShutdown;
        /** Number of update jobs this sink has (usually zero or one). */
        uint8_t                 cUpdateJobs;
        /** The minimum typical interval for all jobs. */
        uint32_t                cMsMinTypicalInterval;
        /** Update jobs for this sink. */
        struct
        {
            /** User specific argument. */
            void               *pvUser;
            /** The callback. */
            PFNAUDMIXSINKUPDATE pfnUpdate;
            /** Typical interval in milliseconds. */
            uint32_t            cMsTypicalInterval;
        } aUpdateJobs[8];
    } AIO;
    /** The sink's critical section. */
    RTCRITSECT              CritSect;
} AUDMIXSINK;

/** Value for AUDMIXSINK::uMagic. (Sir George Martin)  */
#define AUDMIXSINK_MAGIC                UINT32_C(0x19260103)
/** Value for AUDMIXSINK::uMagic after destruction. */
#define AUDMIXSINK_MAGIC_DEAD           UINT32_C(0x20160308)


/** @name AUDMIXSINK_STS_XXX - Sink status bits.
 * @{ */
/** No status specified. */
#define AUDMIXSINK_STS_NONE                  0
/** The sink is active and running. */
#define AUDMIXSINK_STS_RUNNING               RT_BIT(0)
/** Draining the buffers and pending stop - output only. */
#define AUDMIXSINK_STS_DRAINING              RT_BIT(1)
/** Drained the DMA buffer. */
#define AUDMIXSINK_STS_DRAINED_DMA           RT_BIT(2)
/** Drained the mixer buffer, only waiting for streams (drivers) now. */
#define AUDMIXSINK_STS_DRAINED_MIXBUF        RT_BIT(3)
/** Dirty flag.
 * - For output sinks this means that there is data in the sink which has not
 *   been played yet.
 * - For input sinks this means that there is data in the sink which has been
 *   recorded but not transferred to the destination yet.
 * @todo This isn't used for *anything* at the moment. Remove? */
#define AUDMIXSINK_STS_DIRTY                 RT_BIT(4)
/** @} */


/** @name Audio mixer methods
 * @{ */
int         AudioMixerCreate(const char *pszName, uint32_t fFlags, PAUDIOMIXER *ppMixer);
void        AudioMixerDestroy(PAUDIOMIXER pMixer, PPDMDEVINS pDevIns);
void        AudioMixerDebug(PAUDIOMIXER pMixer, PCDBGFINFOHLP pHlp, const char *pszArgs);
int         AudioMixerSetMasterVolume(PAUDIOMIXER pMixer, PCPDMAUDIOVOLUME pVol);
int         AudioMixerCreateSink(PAUDIOMIXER pMixer, const char *pszName, PDMAUDIODIR enmDir, PPDMDEVINS pDevIns, PAUDMIXSINK *ppSink);
/** @} */

/** @name Audio mixer sink methods
 * @{ */
int         AudioMixerSinkStart(PAUDMIXSINK pSink);
int         AudioMixerSinkDrainAndStop(PAUDMIXSINK pSink, uint32_t cbComming);
void        AudioMixerSinkDestroy(PAUDMIXSINK pSink, PPDMDEVINS pDevIns);
uint32_t    AudioMixerSinkGetReadable(PAUDMIXSINK pSink);
uint32_t    AudioMixerSinkGetWritable(PAUDMIXSINK pSink);
PDMAUDIODIR AudioMixerSinkGetDir(PCAUDMIXSINK pSink);
uint32_t    AudioMixerSinkGetStatus(PAUDMIXSINK pSink);
bool        AudioMixerSinkIsActive(PAUDMIXSINK pSink);
void        AudioMixerSinkReset(PAUDMIXSINK pSink);
int         AudioMixerSinkSetFormat(PAUDMIXSINK pSink, PCPDMAUDIOPCMPROPS pPCMProps, uint32_t cMsSchedulingHint);
int         AudioMixerSinkSetVolume(PAUDMIXSINK pSink, PCPDMAUDIOVOLUME pVol);
int         AudioMixerSinkUpdate(PAUDMIXSINK pSink, uint32_t cbDmaUsed, uint32_t cbDmaPeriod);

int         AudioMixerSinkAddUpdateJob(PAUDMIXSINK pSink, PFNAUDMIXSINKUPDATE pfnUpdate, void *pvUser, uint32_t cMsTypicalInterval);
int         AudioMixerSinkRemoveUpdateJob(PAUDMIXSINK pSink, PFNAUDMIXSINKUPDATE pfnUpdate, void *pvUser);
int         AudioMixerSinkSignalUpdateJob(PAUDMIXSINK pSink);
uint64_t    AudioMixerSinkTransferFromCircBuf(PAUDMIXSINK pSink, PRTCIRCBUF pCircBuf, uint64_t offStream,
                                              uint32_t idStream, PAUDIOHLPFILE pDbgFile);
uint64_t    AudioMixerSinkTransferToCircBuf(PAUDMIXSINK pSink, PRTCIRCBUF pCircBuf, uint64_t offStream,
                                            uint32_t idStream, PAUDIOHLPFILE pDbgFile);
bool        AudioMixerSinkLockIsOwner(PAUDMIXSINK pSink);
int         AudioMixerSinkLock(PAUDMIXSINK pSink);
int         AudioMixerSinkTryLock(PAUDMIXSINK pSink);
int         AudioMixerSinkUnlock(PAUDMIXSINK pSink);

int         AudioMixerSinkCreateStream(PAUDMIXSINK pSink, PPDMIAUDIOCONNECTOR pConnector, PCPDMAUDIOSTREAMCFG pCfg,
                                       PPDMDEVINS pDevIns, PAUDMIXSTREAM *ppStream);
int         AudioMixerSinkAddStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream);
void        AudioMixerSinkRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream);
void        AudioMixerSinkRemoveAllStreams(PAUDMIXSINK pSink);
/** @} */

/** @name Audio mixer stream methods
 * @{ */
void        AudioMixerStreamDestroy(PAUDMIXSTREAM pStream, PPDMDEVINS pDevIns, bool fImmediate);
/** @} */

/** @} */

#endif /* !VBOX_INCLUDED_SRC_Audio_AudioMixer_h */