OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
miniaudio_protocol.hpp
1#pragma once
2#include <ossia/detail/config.hpp>
3
4#if __has_include(<miniaudio.h>)
5#define OSSIA_ENABLE_MINIAUDIO 1
6#define MINIAUDIO_IMPLEMENTATION
7#if defined(__APPLE__)
8#define MA_NO_RUNTIME_LINKING 1
9#endif
10#define MA_ENABLE_ONLY_SPECIFIC_BACKENDS 1
11#if defined(__EMSCRIPTEN__)
12#define MA_ENABLE_WEBAUDIO 1
13#define MA_ENABLE_AUDIO_WORKLETS 1
14#else
15#define MA_ENABLE_COREAUDIO 1
16#define MA_ENABLE_ALSA 1
17#endif
18#define MA_NO_WAV 1
19#define MA_NO_FLAC 1
20#define MA_NO_MP3 1
21#define MA_NO_RESOURCE_MANAGER 1
22#define MA_NO_NODE_GRAPH 1
23#define MA_NO_GENERATION 1
24#define MA_MAX_CHANNELS 1024
25#define MA_API
26
27#include <ossia/audio/audio_engine.hpp>
28#include <ossia/detail/thread.hpp>
29
30//#include <kfr/base/conversion.hpp>
31
32#include <miniaudio.h>
33
34#define OSSIA_AUDIO_MINIAUDIO 1
35
36namespace ossia
37{
38struct miniaudio_context
39{
40 ma_context context;
41};
42
43class miniaudio_engine final : public audio_engine
44{
45
46public:
47 std::vector<const ma_device_info*> devices;
48 bool is_duplex(const ma_device_id& card_in, const ma_device_id& card_out)
49 {
50#if defined(__APPLE__)
51 return true;
52 std::string_view i = card_in.coreaudio;
53 std::string_view o = card_out.coreaudio;
54 if(i.length() != o.length() || i.empty() || o.empty())
55 return false;
56 if(i.substr(0, i.size() - 1) == o.substr(0, o.size() - 1))
57 return true;
58
59#endif
60 return memcmp(&card_in, &card_out, sizeof(ma_device_id)) == 0;
61 }
62 miniaudio_engine(
63 std::shared_ptr<miniaudio_context> ctx, std::string name,
64 const ma_device_id& card_in, const ma_device_id& card_out, int inputs, int outputs,
65 int rate, int bs)
66 : m_ctx{std::move(ctx)}
67 {
68 ma_device_type dtype = ma_device_type_duplex;
69 if(inputs == 0)
70 dtype = ma_device_type_playback;
71 else if(outputs == 0)
72 dtype = ma_device_type_capture;
73
74 ma_device_config config = ma_device_config_init(dtype);
75
76 config.sampleRate = rate;
77 config.periodSizeInFrames = bs;
78
79 if(outputs > 0)
80 {
81 config.playback.pDeviceID = &card_out;
82 config.playback.channels = outputs;
83 config.playback.format = ma_format_f32;
84 // config.playback.shareMode = ma_share_mode_exclusive;
85 }
86
87 if(inputs > 0)
88 {
89 config.capture.pDeviceID = &card_in;
90 config.capture.channels = inputs;
91 config.capture.format = ma_format_f32;
92 // config.capture.shareMode = ma_share_mode_exclusive;
93 }
94
95 config.dataCallback = callback;
96
97 config.performanceProfile = ma_performance_profile_low_latency;
98 config.noFixedSizedCallback = false;
99 config.noClip = false;
100 config.noDisableDenormals = false;
101 config.noPreSilencedOutputBuffer = false;
102
103 config.pUserData = this;
104
105 this->effective_buffer_size = bs;
106 this->effective_sample_rate = rate;
107 this->effective_inputs = inputs;
108 this->effective_outputs = outputs;
109
110 ins_data.resize(effective_inputs * bs + 16);
111 outs_data.resize(effective_outputs * bs + 16);
112 ins.resize(effective_inputs + 2);
113 outs.resize(effective_outputs + 2);
114
115 if(ma_device_init(&m_ctx->context, &config, &m_stream) != MA_SUCCESS)
116 {
117 config.performanceProfile = ma_performance_profile_conservative;
118 if(ma_device_init(&m_ctx->context, &config, &m_stream) != MA_SUCCESS)
119 {
120 throw std::runtime_error("Cannot initialize miniaudio");
121 }
122 }
123
124 if(ma_device_start(&m_stream) != MA_SUCCESS)
125 throw std::runtime_error("Cannot start miniaudio");
126 m_active = true;
127 }
128
129 bool running() const override
130 {
131 return m_active && ma_device_get_state(&m_stream) == ma_device_state_started;
132 }
133
134 void stop() override
135 {
136 audio_engine::stop();
137
138 if(m_active)
139 {
140 ma_device_stop(&m_stream);
141 ma_device_uninit(&m_stream);
142 }
143 m_active = false;
144 }
145
146 ~miniaudio_engine() override { stop(); }
147
148private:
149 static void
150 callback(ma_device* pDevice, void* output, const void* input, ma_uint32 nframes)
151 {
152#if !defined(__EMSCRIPTEN__)
153 [[maybe_unused]]
154 static const thread_local auto _
155 = [] {
156 ossia::set_thread_name("ossia audio 0");
157 ossia::set_thread_pinned(thread_type::Audio, 0);
158 return 0;
159 }();
160#endif
161
162 auto& self = *static_cast<miniaudio_engine*>(pDevice->pUserData);
163 self.tick_start();
164
165 if(self.stop_processing)
166 {
167 self.tick_clear();
168 return;
169 }
170
171 auto ins = self.ins.data();
172 auto ins_data = self.ins_data.data();
173 for(int i = 0; i < self.effective_inputs; i++)
174 ins[i] = ins_data + i * nframes;
175#if !defined(__EMSCRIPTEN__)
176 kfr::deinterleave(ins, (float*)input, self.effective_inputs, nframes);
177#endif
178
179 auto outs = self.outs.data();
180 auto outs_data = self.outs_data.data();
181 std::memset(outs_data, 0, sizeof(float) * self.effective_outputs * nframes);
182 for(int i = 0; i < self.effective_outputs; i++)
183 outs[i] = outs_data + i * nframes;
184
185#if defined(__EMSCRIPTEN__)
186 // On WASM audio worklets, std::chrono::steady_clock is unavailable.
187 // Use the sample count to derive time instead.
188 self.m_frames_elapsed += nframes;
189 double nsecs = (double)self.m_frames_elapsed / self.effective_sample_rate;
190#else
191 if(!self.m_start)
192 self.m_start = std::chrono::steady_clock::now();
193 auto now = std::chrono::steady_clock::now();
194 auto nsecs
195 = std::chrono::duration_cast<std::chrono::nanoseconds>(now - *self.m_start)
196 .count()
197 / 1e9;
198#endif
199
200 ossia::audio_tick_state ts{(float* const*)ins, outs, self.effective_inputs,
201 self.effective_outputs, nframes, nsecs};
202 self.audio_tick(ts);
203
204 self.tick_end();
205
206#if !defined(__EMSCRIPTEN__)
207 kfr::interleave(
208 (float*)output, (const float**)outs, self.effective_outputs, nframes);
209#endif
210 }
211
212 std::shared_ptr<miniaudio_context> m_ctx;
213 ma_device m_stream;
214#if defined(__EMSCRIPTEN__)
215 uint64_t m_frames_elapsed{};
216#else
217 std::optional<std::chrono::steady_clock::time_point> m_start;
218#endif
219
220 boost::container::vector<float> ins_data;
221 boost::container::vector<float*> ins;
222 boost::container::vector<float> outs_data;
223 boost::container::vector<float*> outs;
224
225 bool m_active{};
226};
227}
228
229#endif
230// #endif
Definition git_info.h:7