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