VAE - Virtual Audio Engine 1
Small Data Driven Audio Engine
vae_device.hpp
Go to the documentation of this file.
1#ifndef _VAE_DEVICE
2#define _VAE_DEVICE
3
4#include "../../../external/tklb/src/types/audio/resampler/TResampler.hpp"
5#include "../../../external/tklb/src/types/TDelegate.hpp"
6
7#include "../../../include/vae/vae.hpp"
8#include "../vae_types.hpp"
9#include "../vae_config.hpp"
10#include "../vae_util.hpp"
11#include "./vae_backend.hpp"
12
13namespace vae { namespace core {
14 /**
15 * @brief Interface for audio devices.
16 * Default implementation for resampling already provided.
17 */
18 class Device {
19 public:
21
22 protected:
23 Backend& mBackend; // Can't be const because rt audio isn't doing any const
26 Size mSampleRate = 0; // Samplerate after resampling
27 Size mRealSampleRate = 0; // Device sample rate
28 Size mOverruns = 0; // Happens when too many frames are pushed to the deivce
29 Size mUnderruns = 0; // Happens when too many frames are popper from the device
30
33
34 /**
35 * @brief Data shared with audio thread
36 */
38 Callback callback; // Optional callback to process synchronously in the audio callback
39 Device* device; // Device passed to callback function
40 RingBuffer queueToDevice; // Deinterleaved data in device samplerate
41 RingBuffer queueFromDevice; // Deinterleaved data in device samplerate
42 ScratchBuffer convertBuffer; // convert from interleaved, and Sample type
43
44 Resampler resamplerFromDevice; // Resample audio before putting it in queueFromDevice
46
47 size_t streamTime = 0; // device time without regard of underruns and overruns
48 Size underruns = 0; // Happens when the sound card needs more data than queueToDevice provides
49 Size overruns = 0; // Happens when the sound card emites more data than queueFromDevice fits
50 VAE_PROFILER_MUTEX(Mutex, mutex, "Device mutex") // Lock the queues
51 Uchar channelsOut = 0;
53
54 /**
55 * @brief Called from audio backend to push in interleaved audio data
56 *
57 * @tparam T Sample type
58 * @tparam INTERLEAVED Whether to treat data as interleaved
59 * @param from
60 * @param to
61 * @param frames
62 */
63 template <typename T>
64 void swapBufferInterleaved(const T* from, T* to, Size frames) {
66 VEA_PROFILER_THREAD_NAME("Device Thread")
67 if (from != nullptr) {
71 Lock l(mutex);
74 } else {
75 Lock l(mutex);
76 auto pushed = queueFromDevice.push(convertBuffer);
77 overruns += (frames - pushed);
78 }
79 }
80
81 if (callback.valid()) {
83 }
84
85 if (to != nullptr) {
86 int popped;
87 {
88 Lock l(mutex);
89 popped = queueToDevice.pop(convertBuffer, frames);
90 underruns += (frames - popped);
91 }
92 convertBuffer.putInterleaved(to, popped);
93 }
94 streamTime += frames;
96 // VAE_PROFILER_PLOT(profiler::deviceOverruns, int64_t(overruns)); // don't track them since they're not getting drained for now
97 }
98 };
99
100 static constexpr int _VAE_WORKER_SIZE = sizeof(AudioThreadWorker);
101
103
104 /**
105 * @brief initializes buffers, queues and resamplers if needed
106 * Has to be called in openDevice once the samplerate
107 * and channel config is known
108 *
109 * @param sampleRate The actual samplerate the device has, might not match requested rate
110 * @param channelsIn The real channelcount, might not match requested
111 * @param channelsOut The real channelcount, might not match requested
112 * @param bufferSize The amount of frames a callback will provide/request
113 */
114 void init(Size sampleRate, Uchar channelsIn, Uchar channelsOut, Size bufferSize) {
115 VAE_PROFILER_SCOPE_NAMED("Init Device")
116 mWorker.channelsIn = channelsIn;
117 mWorker.channelsOut = channelsOut;
118 mRealSampleRate = sampleRate;
119
120 if (sampleRate != mConfig.internalSampleRate) {
121 if (0 < channelsIn) {
122 // we get full device buffersize for these buffers
124 sampleRate, mConfig.internalSampleRate, bufferSize
125 );
128 channelsIn
129 );
133 }
134 if (0 < channelsOut) {
135 // But only DSP max block size in this direction
138 );
141 channelsOut
142 );
145 }
146 } else {
148 }
149
150 if (0 < channelsIn) {
152 bufferSize * mConfig.bufferPeriods,
153 channelsIn
154 );
155 }
156 if (0 < channelsOut) {
158 bufferSize * mConfig.bufferPeriods,
159 channelsOut
160 );
162 }
163
164 mWorker.convertBuffer.sampleRate = sampleRate;
166 bufferSize,
167 std::max(channelsOut, channelsIn) // buffer is shared for in and out, so make sure it has space for both
168 );
169 }
170
171 void postInit() {
172 VAE_DEBUG("Opened Audio Device on %s with samplerate %i", mBackend.getName(), mRealSampleRate)
174 VAE_DEBUG("Audio Device resamples to %i", mSampleRate)
175 }
176 }
177
178 public:
180
181 /**
182 * @brief Only a Backend can construct a Device
183 */
185 Backend& backend, const EngineConfig& config
186 ) : mBackend(backend), mConfig(config) { }
187
188 virtual ~Device() {
189 VAE_DEBUG(
190 "Device destructed. Underruns: %i %i Overruns:%i %i",
193 )
194 }
195
196 void setCallback(Callback callback) {
197 mWorker.device = this;
198 mWorker.callback = callback;
199 VAE_DEBUG("Device uses sync callback")
200 }
201
202 /**
203 * @brief Opens a specific audio device.
204 * The device struct may be altered to match the actual hardware.
205 * (sampleRate, bufferSize and channel count)
206 */
207 virtual bool openDevice(DeviceInfo& device) = 0;
208
209 /**
210 * @brief Tries to open the default audio device whith desired in out channels
211 */
212 virtual bool openDevice(bool input = false) {
213 DeviceInfo device =
215 return openDevice(device);
216 };
217
218 /**
219 * @brief Closes the currently open device.
220 * Otherwise does nothing.
221 */
222 virtual bool closeDevice() = 0;
223
224 /**
225 * @brief Push samples to the audio device
226 * @param buffer Pushes the amount of valid samples
227 */
228 Size push(const ScratchBuffer& buffer) {
230 VAE_ASSERT(0 < mWorker.channelsOut)
231 const auto frames = buffer.validSize();
232 VAE_ASSERT(frames != 0) // need to have valid frames
235 Lock lock(mWorker.mutex);
238 return pushed;
239 } else {
240 Lock lock(mWorker.mutex);
241 const auto pushed = mWorker.queueToDevice.push(buffer);
242 mOverruns += (frames - pushed);
243 return pushed;
244 }
246 return 0;
247 }
248
249 /**
250 * @brief Return amount of audio frames which can be pushed in buffer
251 * ! this is an estimate when resampling !
252 * @return Size
253 */
254 Size canPush() const {
256 auto remaining = mWorker.queueToDevice.remaining();
258 return mResamplerToDevice.estimateNeed(remaining);
259 }
260 return remaining;
261 }
262
263 /**
264 * @brief Get samples form audio device
265 * @param buffer Gets the amount of valid samples, might actualy get less
266 */
267 void pop(ScratchBuffer& buffer) {
270 auto frames = buffer.validSize();
271 VAE_ASSERT(frames != 0) // need to have valid frames
272 Lock lock(mWorker.mutex);
273 const auto popped = mWorker.queueFromDevice.pop(buffer, frames);
274 mUnderruns += (frames - popped);
276 }
277
278 Size canPop() const {
280 }
281
282 Size getChannelsOut() const { return mWorker.channelsOut; }
283
285
286 /**
287 * @brief Get the sample rate
288 * @return samplerate after resampling
289 */
290 Size getSampleRate() const { return mSampleRate; }
291
292 /**
293 * @brief Get the Real Sample Rate before resampling
294 *
295 * @return Size
296 */
298
299 size_t getStreamTime() const { return mWorker.streamTime; }
300
301 Size getOverruns() const { return mWorker.overruns; }
302 Size getUnderruns() const { return mWorker.underruns; }
303 }; // class Device
304
305 // TODO VAE this seems a bit excessive
306 constexpr int _VAE_DEVICE_SIZE = sizeof(Device);
307} } // namespace vae::core
308
309#endif // _VAE_DEVICE
310
bool resize(const Size length, uchar channels)
! Will not keep the contents! Resizes the buffer to the desired length and channel count.
Size validSize() const
Returns the length of actually valid audio in the buffer.
void setFromInterleaved(const T2 *samples, Size length, const uchar channels, Size offsetSrc=0, const Size offsetDst=0)
Set multiple channels from an interleaved array.
ushort sampleRate
Only relevant for resampling and oversampling.
void set(const T2 *samples, Size length, const uchar channel=0, const Size offsetDst=0)
Set a single channel from an array.
Size putInterleaved(T2 *buffer, Size length=0, const Size offset=0) const
Puts the interleaved contents in the target buffer.
Size remaining() const
Returns how many more elements the buffer can hold.
Size push(const AudioBufferTpl< T2, STORAGE2 > &in, Size offsetSrc=0)
Adds validSize() amount of frames to the buffer.
Size pop(AudioBufferTpl< T2, STORAGE2 > &out, const Size elements, Size offsetSrc=0, Size offsetDst=0)
Pops a number of elements in the buffer provided.
Size filled() const
Returns how many elements are in the buffer.
Size estimateNeed(const Size out) const
Estimate how many samples need to be put in to get n samples out.
Size calculateBufferSize(Size initialSize)
Calculate a buffersize fit for the resampled result.
bool init(uint rateIn, uint rateOut, uint maxBlock=512, uchar maxChannels=2, uchar quality=5)
setup the resampler
Size process(const Buffer &in, Buffer &out)
Resample function Make sure the out buffer has enough space.
Backend interface used to query devices before creating a actual device object.
Definition: vae_backend.hpp:14
virtual const char * getName() const =0
Returns name of the api.
virtual DeviceInfo getDefaultOutputDevice()=0
virtual DeviceInfo getDefaultInputDevice()=0
Interface for audio devices.
Definition: vae_device.hpp:18
AudioThreadWorker mWorker
Definition: vae_device.hpp:102
ScratchBuffer mResamplerBufferToDevice
Definition: vae_device.hpp:32
Size canPop() const
Definition: vae_device.hpp:278
virtual bool openDevice(DeviceInfo &device)=0
Opens a specific audio device.
Size push(const ScratchBuffer &buffer)
Push samples to the audio device.
Definition: vae_device.hpp:228
Size getRealSampleRate() const
Get the Real Sample Rate before resampling.
Definition: vae_device.hpp:297
virtual bool openDevice(bool input=false)
Tries to open the default audio device whith desired in out channels.
Definition: vae_device.hpp:212
const EngineConfig & mConfig
Definition: vae_device.hpp:24
size_t getStreamTime() const
Definition: vae_device.hpp:299
Size getSampleRate() const
Get the sample rate.
Definition: vae_device.hpp:290
Size getChannelsOut() const
Definition: vae_device.hpp:282
static constexpr int _VAE_WORKER_SIZE
Definition: vae_device.hpp:100
Size getOverruns() const
Definition: vae_device.hpp:301
virtual bool closeDevice()=0
Closes the currently open device.
Resampler mResamplerToDevice
Definition: vae_device.hpp:31
void init(Size sampleRate, Uchar channelsIn, Uchar channelsOut, Size bufferSize)
initializes buffers, queues and resamplers if needed Has to be called in openDevice once the samplera...
Definition: vae_device.hpp:114
Size getUnderruns() const
Definition: vae_device.hpp:302
Size getChannelsIn() const
Definition: vae_device.hpp:284
void setCallback(Callback callback)
Definition: vae_device.hpp:196
Backend & mBackend
Definition: vae_device.hpp:23
void pop(ScratchBuffer &buffer)
Get samples form audio device.
Definition: vae_device.hpp:267
Size canPush() const
Return amount of audio frames which can be pushed in buffer ! this is an estimate when resampling !
Definition: vae_device.hpp:254
T max(const T &v1, const T &v2)
Definition: TMath.hpp:21
constexpr Size MaxBlock
Maximum block size.
Definition: vae.hpp:276
const char *const deviceUnderruns
const char *const engineUnderruns
const char *const engineOverruns
unsigned char Uchar
Definition: vae_types.hpp:45
std::mutex Mutex
Definition: vae_types.hpp:52
std::unique_lock< Mutex > Lock
Definition: vae_types.hpp:53
constexpr int _VAE_DEVICE_SIZE
Definition: vae_device.hpp:306
Contains Typedefinitions and basic structures use by the public API and internally.
Definition: vae.hpp:31
unsigned int Size
How the elements are addressed in the heapbuffer.
Definition: vae.hpp:33
Basic struct describing a audio device.
Definition: vae.hpp:102
Settings for the engine defined at EnginePimpl::init.
Definition: vae.hpp:157
Size internalSampleRate
Samplerate requested from device.
Definition: vae.hpp:168
Size bufferPeriods
Number of blocks/buffers to processed ahead.
Definition: vae.hpp:240
Data shared with audio thread.
Definition: vae_device.hpp:37
void swapBufferInterleaved(const T *from, T *to, Size frames)
Called from audio backend to push in interleaved audio data.
Definition: vae_device.hpp:64
VAE_PROFILER_MUTEX(Mutex, mutex, "Device mutex") Uchar channelsOut=0
#define VAE_DEBUG(msg,...)
Definition: vae_logger.hpp:83
#define VAE_PROFILER_SCOPE_NAMED(name)
Profiles a scope and names it.
#define VAE_PROFILER_SCOPE()
Profiles a scope.
#define VAE_PROFILER_PLOT(name, value)
Records a value.
#define VAE_PROFILER_OVERLOAD_NEW()
Overloads new and delete of class to be tracked.
#define VEA_PROFILER_THREAD_NAME(name)
Sets name for current thread.
#define VAE_ASSERT(condition)
Definition: vae_util.hpp:11