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 
17 namespace ossia::safe_nodes
18 {
19 template <typename T>
20 using timed_vec = ossia::flat_map<int64_t, T>;
21 }
22 
23 namespace Analysis
24 {
25 struct GistState
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