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#define MA_ENABLE_COREAUDIO 1
12#define MA_ENABLE_ALSA 1
13#define MA_NO_WAV 1
14#define MA_NO_FLAC 1
15#define MA_NO_MP3 1
16#define MA_NO_RESOURCE_MANAGER 1
17#define MA_NO_NODE_GRAPH 1
18#define MA_NO_GENERATION 1
19#define MA_API
20
21#include <ossia/audio/audio_engine.hpp>
22#include <ossia/detail/thread.hpp>
23
24#include <kfr/base/conversion.hpp>
25
26#include <miniaudio.h>
27
28#define OSSIA_AUDIO_MINIAUDIO 1
29
30namespace ossia
31{
32struct miniaudio_context
33{
34 ma_context context;
35};
36
37class miniaudio_engine final : public audio_engine
38{
39
40public:
41 std::vector<const ma_device_info*> devices;
42 bool is_duplex(const ma_device_id& card_in, const ma_device_id& card_out)
43 {
44#if defined(__APPLE__)
45 return true;
46 std::string_view i = card_in.coreaudio;
47 std::string_view o = card_out.coreaudio;
48 if(i.length() != o.length() || i.empty() || o.empty())
49 return false;
50 if(i.substr(0, i.size() - 1) == o.substr(0, o.size() - 1))
51 return true;
52
53#endif
54 return memcmp(&card_in, &card_out, sizeof(ma_device_id)) == 0;
55 }
56 miniaudio_engine(
57 std::shared_ptr<miniaudio_context> ctx, std::string name,
58 const ma_device_id& card_in, const ma_device_id& card_out, int inputs, int outputs,
59 int rate, int bs)
60 : m_ctx{std::move(ctx)}
61 {
62 ma_device_type dtype = ma_device_type_duplex;
63 if(inputs == 0)
64 dtype = ma_device_type_playback;
65 else if(outputs == 0)
66 dtype = ma_device_type_capture;
67
68 ma_device_config config = ma_device_config_init(dtype);
69
70 config.sampleRate = rate;
71 config.periodSizeInFrames = bs;
72
73 if(outputs > 0)
74 {
75 config.playback.pDeviceID = &card_out;
76 config.playback.channels = outputs;
77 config.playback.format = ma_format_f32;
78 // config.playback.shareMode = ma_share_mode_exclusive;
79 }
80
81 if(inputs > 0)
82 {
83 config.capture.pDeviceID = &card_in;
84 config.capture.channels = inputs;
85 config.capture.format = ma_format_f32;
86 // config.capture.shareMode = ma_share_mode_exclusive;
87 }
88
89 config.dataCallback = callback;
90
91 config.performanceProfile = ma_performance_profile_low_latency;
92 config.noFixedSizedCallback = false;
93 config.noClip = false;
94 config.noDisableDenormals = false;
95 config.noPreSilencedOutputBuffer = false;
96
97 config.pUserData = this;
98
99 if(ma_device_init(&m_ctx->context, &config, &m_stream) != MA_SUCCESS)
100 throw std::runtime_error("Cannot initialize miniaudio");
101
102 if(ma_device_start(&m_stream) != MA_SUCCESS)
103 throw std::runtime_error("Cannot start miniaudio");
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
111 bool running() const override
112 {
113 return ma_device_get_state(&m_stream) == ma_device_state_started;
114 }
115
116 ~miniaudio_engine() override
117 {
118 stop();
119
120 ma_device_stop(&m_stream);
121 ma_device_uninit(&m_stream);
122 }
123
124private:
125 static void
126 callback(ma_device* pDevice, void* output, const void* input, ma_uint32 nframes)
127 {
128 [[maybe_unused]]
129 static const thread_local auto _
130 = [] {
131 ossia::set_thread_name("ossia audio 0");
132 ossia::set_thread_pinned(thread_type::Audio, 0);
133 return 0;
134 }();
135
136 auto& self = *static_cast<miniaudio_engine*>(pDevice->pUserData);
137 self.tick_start();
138 if(!self.m_start)
139 self.m_start = std::chrono::steady_clock::now();
140
141 if(self.stop_processing)
142 {
143 self.tick_clear();
144 return;
145 }
146
147 float ins_data[self.effective_inputs * nframes + 16];
148 float* ins[self.effective_inputs + 2];
149 for(int i = 0; i < self.effective_inputs; i++)
150 ins[i] = ins_data + i * nframes;
151 kfr::deinterleave(ins, (float*)input, self.effective_inputs, nframes);
152
153 float outs_data[self.effective_outputs * nframes + 16];
154 std::memset(outs_data, 0, sizeof(float) * self.effective_outputs * nframes);
155 float* outs[self.effective_outputs + 2];
156 for(int i = 0; i < self.effective_outputs; i++)
157 outs[i] = outs_data + i * nframes;
158
159 auto now = std::chrono::steady_clock::now();
160 auto nsecs
161 = std::chrono::duration_cast<std::chrono::nanoseconds>(now - *self.m_start)
162 .count()
163 / 1e9;
164
165 ossia::audio_tick_state ts{(float* const*)ins, outs, self.effective_inputs,
166 self.effective_outputs, nframes, nsecs};
167 self.audio_tick(ts);
168
169 self.tick_end();
170
171 kfr::interleave(
172 (float*)output, (const float**)outs, self.effective_outputs, nframes);
173 }
174
175 std::shared_ptr<miniaudio_context> m_ctx;
176 ma_device m_stream;
177 std::optional<std::chrono::steady_clock::time_point> m_start;
178};
179}
180
181#endif
182// #endif
Definition git_info.h:7