Loading...
Searching...
No Matches
GistState.hpp
1#pragma once
2#include <Audio/Settings/Model.hpp>
3
4#include <score/application/ApplicationContext.hpp>
5
6#include <ossia/dataflow/audio_port.hpp>
7#include <ossia/dataflow/graph_node.hpp>
8#include <ossia/dataflow/token_request.hpp>
9#include <ossia/dataflow/value_port.hpp>
10#include <ossia/detail/flat_map.hpp>
11#include <ossia/network/value/value.hpp>
12
13#include <Analysis/Helpers.hpp>
14
15#include <Gist.h>
16
17namespace ossia::safe_nodes
18{
19template <typename T>
20using timed_vec = ossia::flat_map<int64_t, T>;
21}
22
23namespace Analysis
24{
26{
27 explicit GistState(int bufferSize, int rate)
28 : bufferSize{bufferSize}
29 , rate{rate}
30 {
31 gist.reserve(2);
32 gist.emplace_back(bufferSize, rate);
33 gist.emplace_back(bufferSize, rate);
34 }
35
36 explicit GistState(Audio::Settings::Model& settings)
37 : GistState{settings.getBufferSize(), settings.getRate()}
38 {
39 }
40
41 explicit GistState()
42 : GistState{score::AppContext().settings<Audio::Settings::Model>()}
43 {
44 }
45
46 ~GistState() { gist.clear(); }
47
48 static int channels(auto& audio) noexcept { return audio.channels; }
49
50 static auto data(auto* channel) noexcept { return channel; }
51
52 static auto frames(auto& channel, int d) noexcept
53 {
54 if constexpr(requires { channel.size(); })
55 return channel.size();
56 else
57 return d;
58 }
59
60 // FIXME have it sample-accurate, for onset detection
61 template <typename V>
62 static auto write_value(auto& out_port, V&& ret)
63 {
64 if constexpr(std::is_same_v<V, ossia::impulse>)
65 out_port();
66 else
67 out_port.value = std::move(ret);
68 }
69
70 void preprocess(const auto& audio)
71 {
72 const auto N = channels(audio);
73 output.resize(N);
74 if(gist.size() < N)
75 {
76 gist.clear();
77 gist.reserve(N);
78 while(gist.size() < N)
79 gist.emplace_back(bufferSize, rate);
80 }
81 }
82
83 // No gain //
84 template <auto Func>
85 void process_mono(const auto& audio, auto& out_port, int d)
86 {
87 float ret = 0.f;
88 decltype(auto) c0 = audio.get()[0];
89 auto& g0 = gist[0];
90 {
91 const auto samples = frames(c0, d);
92 if(samples > 0)
93 {
94 if(g0.getAudioFrameSize() != samples)
95 g0.setAudioFrameSize(samples);
96
97 g0.processAudioFrame(data(c0), samples);
98 ret = (g0.*Func)();
99 }
100 }
101
102 write_value(out_port, ret);
103 }
104
105 template <auto Func>
106 void process_stereo(const auto& audio, auto& out_port, int d)
107 {
108 ossia::vec2f ret = {0.f, 0.f};
109 decltype(auto) c0 = audio.get()[0];
110 auto& g0 = gist[0];
111 {
112 const auto samples = frames(c0, d);
113 if(samples > 0)
114 {
115 if(g0.getAudioFrameSize() != samples)
116 g0.setAudioFrameSize(samples);
117
118 g0.processAudioFrame(data(c0), samples);
119 ret[0] = (g0.*Func)();
120 }
121 }
122 decltype(auto) c1 = audio.get()[1];
123 auto& g1 = gist[1];
124 {
125 const auto samples = frames(c1, d);
126 if(samples > 0)
127 {
128 if(g1.getAudioFrameSize() != samples)
129 g1.setAudioFrameSize(samples);
130
131 g1.processAudioFrame(data(c0), samples);
132 ret[1] = (g1.*Func)();
133 }
134 }
135
136 write_value(out_port, ret);
137 }
138
139 template <auto Func>
140 void process_multi(const auto& audio, auto& out_port, int d)
141 {
142 auto it = output.begin();
143 auto git = gist.begin();
144 for(auto& channel : audio.get())
145 {
146 const auto samples = frames(channel, d);
147 if(samples > 0)
148 {
149 if(git->getAudioFrameSize() != samples)
150 git->setAudioFrameSize(samples);
151
152 git->processAudioFrame(data(channel), samples);
153 *it = float(((*git).*Func)());
154 }
155 else
156 {
157 *it = 0.f;
158 }
159 ++it;
160 ++git;
161 }
162
163 write_value(out_port, output);
164 }
165
166 // Gain, gate
167 template <auto Func>
168 void process_mono(const auto& audio, float gain, float gate, auto& out_port, int d)
169 {
170 float ret = 0.f;
171 decltype(auto) c0 = audio.get()[0];
172 auto& g0 = gist[0];
173 {
174 const auto samples = frames(c0, d);
175 if(samples > 0)
176 {
177 if(g0.getAudioFrameSize() != samples)
178 g0.setAudioFrameSize(samples);
179
180 g0.processAudioFrame(data(c0), samples, gain, gate);
181 ret = (g0.*Func)();
182 }
183 }
184
185 write_value(out_port, ret);
186 }
187
188 template <auto Func>
189 void process_stereo(const auto& audio, float gain, float gate, auto& out_port, int d)
190 {
191 ossia::vec2f ret = {0.f, 0.f};
192 decltype(auto) c0 = audio.get()[0];
193 auto& g0 = gist[0];
194 {
195 const auto samples = frames(c0, d);
196 if(samples > 0)
197 {
198 if(g0.getAudioFrameSize() != samples)
199 g0.setAudioFrameSize(samples);
200
201 g0.processAudioFrame(data(c0), samples, gain, gate);
202 ret[0] = (g0.*Func)();
203 }
204 }
205 decltype(auto) c1 = audio.get()[1];
206 auto& g1 = gist[1];
207 {
208 const auto samples = frames(c1, d);
209 if(samples > 0)
210 {
211 if(g1.getAudioFrameSize() != samples)
212 g1.setAudioFrameSize(samples);
213
214 g1.processAudioFrame(data(c0), samples, gain, gate);
215 ret[1] = (g1.*Func)();
216 }
217 }
218
219 write_value(out_port, ret);
220 }
221
222 template <auto Func>
223 void process_multi(const auto& audio, float gain, float gate, auto& out_port, int d)
224 {
225 auto it = output.begin();
226 auto git = gist.begin();
227 for(auto& channel : audio.get())
228 {
229 const auto samples = frames(channel, d);
230 if(samples > 0)
231 {
232 if(git->getAudioFrameSize() != samples)
233 git->setAudioFrameSize(samples);
234
235 git->processAudioFrame(data(channel), samples, gain, gate);
236 float r{};
237 *it = r = float(((*git).*Func)());
238 }
239 else
240 {
241 *it = 0.f;
242 }
243 ++it;
244 ++git;
245 }
246
247 write_value(out_port, output);
248 }
249
250 // Gain, gate, pulse
251 template <auto Func>
252 void process_mono(
253 const auto& audio, float gain, float gate, auto& out_port, auto& pulse_port, int d)
254 {
255 float ret = 0.f;
256 decltype(auto) c0 = audio.get()[0];
257 auto& g0 = gist[0];
258 {
259 const auto samples = frames(c0, d);
260 if(samples > 0)
261 {
262 if(g0.getAudioFrameSize() != samples)
263 g0.setAudioFrameSize(samples);
264
265 g0.processAudioFrame(data(c0), samples, gain, gate);
266 ret = (g0.*Func)();
267 }
268 }
269
270 write_value(out_port, ret);
271
272 if(ret >= 1.f)
273 write_value(pulse_port, ossia::impulse{});
274 }
275
276 template <auto Func>
277 void process_stereo(
278 const auto& audio, float gain, float gate, auto& out_port, auto& pulse_port, int d)
279 {
280 ossia::vec2f ret = {0.f, 0.f};
281 decltype(auto) c0 = audio.get()[0];
282 auto& g0 = gist[0];
283 {
284 const auto samples = frames(c0, d);
285 if(samples > 0)
286 {
287 if(g0.getAudioFrameSize() != samples)
288 g0.setAudioFrameSize(samples);
289
290 g0.processAudioFrame(data(c0), samples, gain, gate);
291 ret[0] = (g0.*Func)();
292 }
293 }
294 decltype(auto) c1 = audio.get()[1];
295 auto& g1 = gist[1];
296 {
297 const auto samples = frames(c1, d);
298 if(samples > 0)
299 {
300 if(g1.getAudioFrameSize() != samples)
301 g1.setAudioFrameSize(samples);
302
303 g1.processAudioFrame(data(c0), samples, gain, gate);
304 ret[1] = (g1.*Func)();
305 }
306 }
307
308 write_value(out_port, ret);
309 if(ret[0] >= 1.f || ret[1] >= 1.f)
310 write_value(pulse_port, ossia::impulse{});
311 }
312
313 template <auto Func>
314 void process_multi(
315 const auto& audio, float gain, float gate, auto& out_port, auto& pulse_port, int d)
316 {
317 bool bang = false;
318 auto it = output.begin();
319 auto git = gist.begin();
320 for(auto& channel : audio.get())
321 {
322 const auto samples = frames(channel, d);
323 if(samples > 0)
324 {
325 if(git->getAudioFrameSize() != samples)
326 git->setAudioFrameSize(samples);
327
328 git->processAudioFrame(data(channel), samples, gain, gate);
329 float r{};
330 *it = r = float(((*git).*Func)());
331 bang |= (r >= 1.f);
332 }
333 else
334 {
335 *it = 0.f;
336 }
337 ++it;
338 ++git;
339 }
340
341 write_value(out_port, output);
342 if(bang)
343 {
344 write_value(pulse_port, ossia::impulse{});
345 }
346 }
347
348 template <auto Func, typename... Args>
349 void process(const auto& audio, Args&&... args)
350 {
351 preprocess(audio);
352
353 switch(channels(audio))
354 {
355 case 1:
356 return process_mono<Func>(audio, args...);
357 break;
358 case 2:
359 return process_stereo<Func>(audio, args...);
360 break;
361 default:
362 return process_multi<Func>(audio, args...);
363 break;
364 }
365 }
366
367 template <auto Func>
368 void processVector(const auto& audio, ossia::audio_port& mfcc, int d)
369 {
370 while(gist.size() < channels(audio))
371 gist.emplace_back(bufferSize, rate);
372
373 mfcc.set_channels(channels(audio));
374 auto it = mfcc.get().begin();
375 auto git = gist.begin();
376 for(auto& channel : audio.get())
377 {
378 const auto samples = frames(channel, d);
379 if(samples > 0)
380 {
381 if(git->getAudioFrameSize() != samples)
382 git->setAudioFrameSize(samples);
383
384 git->processAudioFrame(data(channel), samples);
385
386 auto& res = ((*git).*Func)();
387 it->assign(res.begin(), res.end());
388 }
389 else
390 {
391 it->clear();
392 }
393
394 ++it;
395 ++git;
396 }
397 }
398
399 template <auto Func>
400 void processVector(const auto& audio, float gain, float gate, auto& mfcc, int d)
401 {
402 while(gist.size() < channels(audio))
403 gist.emplace_back(bufferSize, rate);
404 auto git = gist.begin();
405
406 // mfcc.set_channels(channels(audio));
407 double** it = mfcc.get().begin();
408
409 for(auto& channel : audio.get())
410 {
411 const auto samples = frames(channel, d);
412 std::fill_n(*it, d, 0.f);
413 if(samples > 0)
414 {
415 if(git->getAudioFrameSize() != samples)
416 git->setAudioFrameSize(samples);
417
418 git->processAudioFrame(data(channel), samples, gain, gate);
419
420 decltype(auto) res = ((*git).*Func)();
421 SCORE_ASSERT(std::ssize(res) <= d);
422 std::copy_n(res.begin(), res.size(), *it);
423 }
424 ++it;
425 ++git;
426 }
427 }
428
429 ossia::small_vector<Gist<double>, 2> gist;
430 Analysis::analysis_vector output;
431 int bufferSize{};
432 int rate{};
433};
434}
Definition score-plugin-audio/Audio/Settings/Model.hpp:22
Definition GistState.hpp:26
T & settings() const
Access a specific Settings model instance.
Definition ApplicationContext.hpp:40