OSSIA
Open Scenario System for Interactive Application
executor.hpp
1 #pragma once
2 #include <ossia/dataflow/fx_node.hpp>
3 #include <ossia/dataflow/graph_node.hpp>
4 #include <ossia/dataflow/node_process.hpp>
5 #include <ossia/dataflow/port.hpp>
6 #include <ossia/dataflow/safe_nodes/node.hpp>
7 #include <ossia/dataflow/safe_nodes/tick_policies.hpp>
9 #include <ossia/detail/apply_type.hpp>
10 #include <ossia/detail/for_each_in_tuple.hpp>
11 #include <ossia/detail/lockfree_queue.hpp>
13 
14 #include <bitset>
15 namespace ossia
16 {
17 
18 template <typename T, std::size_t N>
19 struct ebo_array
20 {
21  T arr[N];
22  constexpr T& operator[](std::size_t i) noexcept { return arr[i]; }
23  constexpr const T& operator[](std::size_t i) const noexcept { return arr[i]; }
24 };
25 template <typename T>
26 struct ebo_array<T, 0>
27 {
28  // constexpr const T operator[](std::size_t i) const noexcept { return {}; }
29 };
30 
31 template <typename T, std::size_t N>
32 constexpr const T* begin(const ebo_array<T, N>& e)
33 {
34  return e.arr;
35 }
36 template <typename T, std::size_t N>
37 constexpr const T* end(const ebo_array<T, N>& e)
38 {
39  return e.arr + N;
40 }
41 template <typename T, std::size_t N>
42 constexpr T* begin(ebo_array<T, N>& e)
43 {
44  return e.arr;
45 }
46 template <typename T, std::size_t N>
47 constexpr T* end(ebo_array<T, N>& e)
48 {
49  return e.arr + N;
50 }
51 }
52 namespace ossia::safe_nodes
53 {
54 
55 template <typename T, typename = void>
56 struct has_event_policy : std::false_type
57 {
58 };
59 template <typename T>
60 struct has_event_policy<T, std::void_t<typename T::event_policy>> : std::true_type
61 {
62 };
63 
64 template <typename T, typename = void>
65 struct has_audio_policy : std::false_type
66 {
67 };
68 template <typename T>
69 struct has_audio_policy<T, std::void_t<typename T::audio_policy>> : std::true_type
70 {
71 };
72 
73 template <typename T, typename = void>
74 struct has_control_policy : std::false_type
75 {
76 };
77 template <typename T>
78 struct has_control_policy<T, std::void_t<typename T::control_policy>> : std::true_type
79 {
80 };
81 
82 template <typename info, std::size_t N>
83 static constexpr auto& get_inlet_accessor(const ossia::inlets& inl) noexcept
84 {
85  constexpr auto cat = info::categorize_inlet(N);
86  if constexpr(cat == ossia::safe_nodes::inlet_kind::audio_in)
87  return *inl[N]->target<ossia::audio_port>();
88  else if constexpr(cat == ossia::safe_nodes::inlet_kind::midi_in)
89  return *inl[N]->target<ossia::midi_port>();
90  else if constexpr(cat == ossia::safe_nodes::inlet_kind::value_in)
91  return *inl[N]->target<ossia::value_port>();
92  else if constexpr(cat == ossia::safe_nodes::inlet_kind::address_in)
93  return inl[N]->address;
94  else
95  throw;
96 }
97 
98 template <typename info, std::size_t N>
99 static constexpr auto& get_outlet_accessor(const ossia::outlets& outl) noexcept
100 {
101  constexpr auto cat = info::categorize_outlet(N);
102  if constexpr(cat == ossia::safe_nodes::outlet_kind::audio_out)
103  return *outl[N]->target<ossia::audio_port>();
104  else if constexpr(cat == ossia::safe_nodes::outlet_kind::midi_out)
105  return *outl[N]->target<ossia::midi_port>();
106  else if constexpr(cat == ossia::safe_nodes::outlet_kind::value_out)
107  return *outl[N]->target<ossia::value_port>();
108  else
109  throw;
110 }
111 
112 template <typename Node_T>
113 class safe_node final
114  : public ossia::nonowning_graph_node
115  , public get_state<Node_T>::type
116 {
117 public:
118  using info = info_functions<Node_T>;
119  static const constexpr bool has_state = has_state_t<Node_T>::value;
120  using state_type = typename get_state<Node_T>::type;
121 
122  using controls_changed_list = std::bitset<info_functions<Node_T>::control_count>;
123  using controls_type = typename info_functions<Node_T>::controls_type;
124  using controls_values_type = typename info_functions<Node_T>::controls_values_type;
125  using control_tuple_t =
126  typename ossia::apply_type<controls_values_type, timed_vec>::type;
127 
128  // std::tuple<float, int...> : current running values of the controls
129  controls_values_type controls;
130 
131  // bitset : 1 if the control has changed since the last tick, 0 else
132  controls_changed_list controls_changed;
133 
134  // used to communicate control changes from the ui
135  ossia::spsc_queue<controls_values_type> cqueue;
136 
137  // holds the std::tuple<timed_vec<float>, ...>
138  control_tuple_t control_tuple;
139 
140  using control_outs_changed_list
141  = std::bitset<info_functions<Node_T>::control_out_count>;
142  using control_outs_type = typename info_functions<Node_T>::control_outs_type;
143  using control_outs_values_type =
144  typename info_functions<Node_T>::control_outs_values_type;
145  using control_out_tuple_t =
146  typename ossia::apply_type<control_outs_values_type, timed_vec>::type;
147 
148  control_outs_values_type control_outs;
149  control_outs_changed_list control_outs_changed;
150  ossia::spsc_queue<control_outs_values_type> control_outs_queue;
151  control_out_tuple_t control_outs_tuple;
152 
153  ebo_array<ossia::audio_inlet, info::audio_in_count> audio_in_ports;
154  ebo_array<ossia::midi_inlet, info::midi_in_count> midi_in_ports;
155  ebo_array<ossia::value_inlet, info::value_in_count> value_in_ports;
156  ebo_array<ossia::value_inlet, info::address_in_count> address_in_ports;
157  ebo_array<ossia::value_inlet, info::control_count> control_in_ports;
158 
159  ebo_array<ossia::audio_outlet, info::audio_out_count> audio_out_ports;
160  ebo_array<ossia::midi_outlet, info::midi_out_count> midi_out_ports;
161  ebo_array<ossia::value_outlet, info::value_out_count> value_out_ports;
162  ebo_array<ossia::value_outlet, info::control_out_count> control_out_ports;
163 
164  safe_node() noexcept
165  {
166  m_inlets.reserve(info_functions<Node_T>::inlet_size);
167  m_outlets.reserve(info_functions<Node_T>::outlet_size);
168  if constexpr(info::audio_in_count > 0)
169  for(auto& port : this->audio_in_ports)
170  m_inlets.push_back(std::addressof(port));
171 
172  if constexpr(info::midi_in_count > 0)
173  for(auto& port : this->midi_in_ports)
174  m_inlets.push_back(std::addressof(port));
175 
176  if constexpr(info::value_in_count > 0)
177  for(std::size_t i = 0; i < info::value_in_count; i++)
178  {
179  (*value_in_ports[i]).is_event = Node_T::Metadata::value_ins[i].is_event;
180  m_inlets.push_back(std::addressof(value_in_ports[i]));
181  }
182 
183  if constexpr(info::address_in_count > 0)
184  for(auto& port : this->address_in_ports)
185  m_inlets.push_back(std::addressof(port));
186 
187  if constexpr(info::control_count > 0)
188  {
189  for(auto& port : this->control_in_ports)
190  {
191  (*port).is_event = true;
192  m_inlets.push_back(std::addressof(port));
193  }
194 
195  {
196  int ctrl_i = 0;
197  for_each_in_tuple(Node_T::Metadata::controls, [&](auto& ctrl_info) {
198  ctrl_info.setup_exec(this->control_in_ports[ctrl_i]);
199  ctrl_i++;
200  });
201  }
202  }
203 
204  if constexpr(info::audio_out_count > 0)
205  for(auto& port : this->audio_out_ports)
206  m_outlets.push_back(std::addressof(port));
207 
208  if constexpr(info::midi_out_count > 0)
209  for(auto& port : this->midi_out_ports)
210  m_outlets.push_back(std::addressof(port));
211 
212  if constexpr(info::value_out_count > 0)
213  for(std::size_t i = 0; i < info::value_out_count; i++)
214  {
215  auto& port = value_out_ports[i];
216  if(auto& type = Node_T::Metadata::value_outs[i].type; !type.empty())
217  {
218  port->type = ossia::parse_dataspace(type);
219  }
220  m_outlets.push_back(std::addressof(port));
221  }
222 
223  if constexpr(info::control_out_count > 0)
224  for(auto& port : this->control_out_ports)
225  m_outlets.push_back(std::addressof(port));
226  }
227 
228  template <std::size_t N>
229  constexpr const auto& get_control_accessor(const ossia::inlets& inl) noexcept
230  {
231  constexpr const auto idx = info::control_start + N;
232  static_assert(info::control_count > 0);
233  static_assert(N < info::control_count);
234 
235  using control_type =
236  typename std::tuple_element<N, decltype(Node_T::Metadata::controls)>::type;
237  using val_type = typename control_type::type;
238 
239  ossia::timed_vec<val_type>& vec = get<N>(this->control_tuple);
240 
241  vec.clear();
242  const auto& vp = static_cast<ossia::value_inlet*>(inl[idx])->data.get_data();
243  vec.reserve(vp.size() + 1);
244 
245  // in all cases, set the current value at t=0
246  vec.insert(std::make_pair(int64_t{0}, get<N>(this->controls)));
247 
248  // copy all the values... values arrived later replace previous ones
249  apply_control<control_type::must_validate, N>(vec, vp);
250 
251  // the last value will be the first for the next tick
252  get<N>(this->controls) = vec.rbegin()->second;
253  return vec;
254  }
255 
256  template <std::size_t N>
257  constexpr auto& get_control_outlet_accessor(const ossia::outlets& outl) noexcept
258  {
259  static_assert(info::control_out_count > 0);
260  static_assert(N < info::control_out_count);
261 
262  return get<N>(this->control_outs_tuple);
263  }
264 
265  template <bool Validate, std::size_t N, typename Vec, typename Vp>
266  void apply_control(Vec& vec, const Vp& vp) noexcept
267  {
268  constexpr const auto ctrl = get<N>(Node_T::Metadata::controls);
269  if constexpr(Validate)
270  {
271  for(auto& v : vp)
272  {
273  if(auto res = ctrl.fromValue(v.value))
274  {
275  vec[int64_t{v.timestamp}] = *std::move(res);
276  this->controls_changed.set(N);
277  }
278  }
279  }
280  else
281  {
282  for(auto& v : vp)
283  {
284  vec[int64_t{v.timestamp}] = ctrl.fromValue(v.value);
285  this->controls_changed.set(N);
286  }
287  }
288  }
290 
291  template <std::size_t... I, std::size_t... CI, std::size_t... O, std::size_t... CO>
292  void apply_all_impl(
293  const std::index_sequence<I...>&, const std::index_sequence<CI...>&,
294  const std::index_sequence<O...>&, const std::index_sequence<CO...>&,
295  ossia::token_request tk, ossia::exec_state_facade st) noexcept
296  {
297  ossia::inlets& inlets = this->root_inputs();
298  ossia::outlets& outlets = this->root_outputs();
299 
300  if constexpr(has_state)
301  {
302  if constexpr(info::control_count > 0)
303  {
304  using policy = typename Node_T::control_policy;
305  policy{}(
306  [&](const ossia::token_request& sub_tk, auto&&... ctls) {
307  Node_T::run(
308  get_inlet_accessor<info, I>(inlets)...,
309  std::forward<decltype(ctls)>(ctls)...,
310  get_outlet_accessor<info, O>(outlets)...,
311  get_control_outlet_accessor<CO>(outlets)..., sub_tk, st,
312  static_cast<state_type&>(*this));
313  },
314  tk, get_control_accessor<CI>(inlets)...);
315  }
316  else
317  {
318  Node_T::run(
319  get_inlet_accessor<info, I>(inlets)...,
320  get_outlet_accessor<info, O>(outlets)...,
321  get_control_outlet_accessor<CO>(outlets)..., tk, st,
322  static_cast<state_type&>(*this));
323  }
324  }
325  else
326  {
327  if constexpr(info::control_count > 0)
328  {
329  using policy = typename Node_T::control_policy;
330  policy{}(
331  [&](const ossia::token_request& sub_tk, auto&&... ctls) {
332  Node_T::run(
333  get_inlet_accessor<info, I>(inlets)...,
334  std::forward<decltype(ctls)>(ctls)...,
335  get_outlet_accessor<info, O>(outlets)...,
336  get_control_outlet_accessor<CO>(outlets)..., sub_tk, st);
337  },
338  tk, get_control_accessor<CI>(inlets)...);
339  }
340  else
341  {
342  Node_T::run(
343  get_inlet_accessor<info, I>(inlets)...,
344  get_outlet_accessor<info, O>(outlets)...,
345  get_control_outlet_accessor<CO>(outlets)..., tk, st);
346  }
347  }
348  }
349 
350  void run(const ossia::token_request& tk, ossia::exec_state_facade st) noexcept override
351  {
352  using inlets_indices = std::make_index_sequence<info::control_start>;
353  using controls_indices = std::make_index_sequence<info::control_count>;
354  using outlets_indices = std::make_index_sequence<info::control_out_start>;
355  using control_outs_indices = std::make_index_sequence<info::control_out_count>;
356 
357  apply_all_impl(
358  inlets_indices{}, controls_indices{}, outlets_indices{}, control_outs_indices{},
359  tk, st);
360 
361  if constexpr(info::control_count > 0)
362  {
363  if(cqueue.size_approx() < 1 && controls_changed.any())
364  {
365  cqueue.try_enqueue(controls);
366  controls_changed.reset();
367  }
368  }
369 
370  if constexpr(info::control_out_count > 0)
371  {
372  std::size_t i = 0;
373  bool ok = false;
374  ossia::for_each_in_tuples_ref(
375  this->control_outs_tuple, this->control_outs,
376  [&](auto&& current_ctrls, auto&& control_out) {
377  if(!current_ctrls.empty())
378  {
379  ok = true;
380  control_out = std::move(current_ctrls.tree().get_sequence_cref().back().second);
381  current_ctrls.clear();
382  }
383 
384  i++;
385  });
386 
387  if(ok)
388  {
389  this->control_outs_queue.enqueue(this->control_outs);
390  }
391  }
392  }
393 
394  void all_notes_off() noexcept override
395  {
396  if constexpr(info::midi_in_count > 0)
397  {
398  // TODO
399  }
400  }
401 
402  [[nodiscard]] std::string label() const noexcept override { return "Control"; }
403 };
404 
405 template <typename T>
406 struct controls_feedback_type
407 {
408  using type = T;
409 };
410 
411 template <>
412 struct controls_feedback_type<std::string>
413 {
414  using type = std::string_view;
415 };
416 template <typename T>
417 using controls_feedback_type_t = typename controls_feedback_type<T>::type;
418 
419 template <typename Node_T>
420  requires std::is_same_v<typename Node_T::control_policy, ossia::safe_nodes::last_tick>
421 class safe_node<Node_T> final
422  : public ossia::nonowning_graph_node
423  , public get_state<Node_T>::type
424 {
425 public:
426  using info = info_functions<Node_T>;
427  static const constexpr bool has_state = has_state_t<Node_T>::value;
428  using state_type = typename get_state<Node_T>::type;
429 
430  using controls_changed_list = std::bitset<info_functions<Node_T>::control_count>;
431  using controls_type = typename info_functions<Node_T>::controls_type;
432  using controls_values_type = typename info_functions<Node_T>::controls_values_type;
433  using controls_values_feedback = boost::mp11::mp_transform<
434  controls_feedback_type_t, typename info_functions<Node_T>::controls_values_type>;
435 
436  // std::tuple<float, int...> : current running values of the controls
437  controls_values_type controls;
438 
439  // bitset : 1 if the control has changed since the last tick, 0 else
440  controls_changed_list controls_changed;
441 
442  // used to communicate control changes from the ui
443  ossia::spsc_queue<controls_values_feedback> cqueue;
444 
445  using control_outs_changed_list
446  = std::bitset<info_functions<Node_T>::control_out_count>;
447  using control_outs_type = typename info_functions<Node_T>::control_outs_type;
448  using control_outs_values_type =
449  typename info_functions<Node_T>::control_outs_values_type;
450 
451  control_outs_values_type control_outs;
452  control_outs_changed_list control_outs_changed;
453  ossia::spsc_queue<control_outs_values_type> control_outs_queue;
454 
455  ebo_array<ossia::audio_inlet, info::audio_in_count> audio_in_ports;
456  ebo_array<ossia::midi_inlet, info::midi_in_count> midi_in_ports;
457  ebo_array<ossia::value_inlet, info::value_in_count> value_in_ports;
458  ebo_array<ossia::value_inlet, info::address_in_count> address_in_ports;
459  ebo_array<ossia::value_inlet, info::control_count> control_in_ports;
460 
461  ebo_array<ossia::audio_outlet, info::audio_out_count> audio_out_ports;
462  ebo_array<ossia::midi_outlet, info::midi_out_count> midi_out_ports;
463  ebo_array<ossia::value_outlet, info::value_out_count> value_out_ports;
464  ebo_array<ossia::value_outlet, info::control_out_count> control_out_ports;
465 
466  safe_node() noexcept
467  {
468  m_inlets.reserve(info_functions<Node_T>::inlet_size);
469  m_outlets.reserve(info_functions<Node_T>::outlet_size);
470  if constexpr(info::audio_in_count > 0)
471  for(auto& port : this->audio_in_ports)
472  m_inlets.push_back(std::addressof(port));
473 
474  if constexpr(info::midi_in_count > 0)
475  for(auto& port : this->midi_in_ports)
476  m_inlets.push_back(std::addressof(port));
477 
478  if constexpr(info::value_in_count > 0)
479  for(std::size_t i = 0; i < info::value_in_count; i++)
480  {
481  (*value_in_ports[i]).is_event = Node_T::Metadata::value_ins[i].is_event;
482  m_inlets.push_back(std::addressof(value_in_ports[i]));
483  }
484 
485  if constexpr(info::address_in_count > 0)
486  for(auto& port : this->address_in_ports)
487  m_inlets.push_back(std::addressof(port));
488 
489  if constexpr(info::control_count > 0)
490  {
491  for(auto& port : this->control_in_ports)
492  {
493  (*port).is_event = true;
494  m_inlets.push_back(std::addressof(port));
495  }
496 
497  {
498  int ctrl_i = 0;
499  for_each_in_tuple(Node_T::Metadata::controls, [&](auto& ctrl_info) {
500  ctrl_info.setup_exec(this->control_in_ports[ctrl_i]);
501  ctrl_i++;
502  });
503  }
504  }
505 
506  if constexpr(info::audio_out_count > 0)
507  for(auto& port : this->audio_out_ports)
508  m_outlets.push_back(std::addressof(port));
509 
510  if constexpr(info::midi_out_count > 0)
511  for(auto& port : this->midi_out_ports)
512  m_outlets.push_back(std::addressof(port));
513 
514  if constexpr(info::value_out_count > 0)
515  for(std::size_t i = 0; i < info::value_out_count; i++)
516  {
517  auto& port = value_out_ports[i];
518  if(auto& type = Node_T::Metadata::value_outs[i].type; !type.empty())
519  {
520  port->type = ossia::parse_dataspace(type);
521  }
522  m_outlets.push_back(std::addressof(port));
523  }
524 
525  if constexpr(info::control_out_count > 0)
526  for(auto& port : this->control_out_ports)
527  m_outlets.push_back(std::addressof(port));
528  }
529 
530  template <std::size_t N>
531  constexpr const auto& get_control_accessor(const ossia::inlets& inl) noexcept
532  {
533  constexpr const auto idx = info::control_start + N;
534  static_assert(info::control_count > 0);
535  static_assert(N < info::control_count);
536 
537  using control_type =
538  typename std::tuple_element<N, decltype(Node_T::Metadata::controls)>::type;
539 
540  const auto& vp = static_cast<ossia::value_inlet*>(inl[idx])->data.get_data();
541 
542  if(!vp.empty())
543  {
544  // copy the last value
545  apply_control<control_type::must_validate, N>(
546  get<N>(this->controls), vp.back().value);
547  }
548  return get<N>(this->controls);
549  }
550 
551  template <std::size_t N>
552  constexpr auto& get_control_outlet_accessor(const ossia::outlets& outl) noexcept
553  {
554  static_assert(info::control_out_count > 0);
555  static_assert(N < info::control_out_count);
556 
557  return get<N>(this->control_outs_tuple);
558  }
559 
560  template <bool Validate, std::size_t N, typename Ctl>
561  void apply_control(Ctl& ctl, const ossia::value& v) noexcept
562  {
563  constexpr const auto ctrl = get<N>(Node_T::Metadata::controls);
564  if constexpr(Validate)
565  {
566  if(auto res = ctrl.fromValue(v))
567  {
568  ctl = *std::move(res);
569  this->controls_changed.set(N);
570  }
571  }
572  else
573  {
574  ctl = ctrl.fromValue(v);
575  this->controls_changed.set(N);
576  }
577  }
579 
580  controls_values_feedback controls_feedback(const controls_values_type& t)
581  {
582  return tuplet::apply(
583  []<typename... Args>(const Args&... args) {
584  return controls_values_feedback{args...};
585  },
586  t);
587  }
588 
589  template <std::size_t... I, std::size_t... CI, std::size_t... O, std::size_t... CO>
590  void apply_all_impl(
591  const std::index_sequence<I...>&, const std::index_sequence<CI...>&,
592  const std::index_sequence<O...>&, const std::index_sequence<CO...>&,
593  ossia::token_request tk, ossia::exec_state_facade st) noexcept
594  {
595  ossia::inlets& inlets = this->root_inputs();
596  ossia::outlets& outlets = this->root_outputs();
597 
598  if constexpr(has_state)
599  {
600  if constexpr(info::control_count > 0)
601  {
602  using policy = last_tick_values;
603  policy{}(
604  [&](const ossia::token_request& sub_tk, auto&&... ctls) {
605  Node_T::run(
606  get_inlet_accessor<info, I>(inlets)...,
607  std::forward<decltype(ctls)>(ctls)...,
608  get_outlet_accessor<info, O>(outlets)...,
609  get_control_outlet_accessor<CO>(outlets)..., sub_tk, st,
610  static_cast<state_type&>(*this));
611  },
612  tk, get_control_accessor<CI>(inlets)...);
613  }
614  else
615  {
616  Node_T::run(
617  get_inlet_accessor<info, I>(inlets)...,
618  get_outlet_accessor<info, O>(outlets)...,
619  get_control_outlet_accessor<CO>(outlets)..., tk, st,
620  static_cast<state_type&>(*this));
621  }
622  }
623  else
624  {
625  if constexpr(info::control_count > 0)
626  {
627  using policy = last_tick_values;
628  policy{}(
629  [&](const ossia::token_request& sub_tk, auto&&... ctls) {
630  Node_T::run(
631  get_inlet_accessor<info, I>(inlets)...,
632  std::forward<decltype(ctls)>(ctls)...,
633  get_outlet_accessor<info, O>(outlets)...,
634  get_control_outlet_accessor<CO>(outlets)..., sub_tk, st);
635  },
636  tk, get_control_accessor<CI>(inlets)...);
637  }
638  else
639  {
640  Node_T::run(
641  get_inlet_accessor<info, I>(inlets)...,
642  get_outlet_accessor<info, O>(outlets)...,
643  get_control_outlet_accessor<CO>(outlets)..., tk, st);
644  }
645  }
646  }
647 
648  void run(const ossia::token_request& tk, ossia::exec_state_facade st) noexcept override
649  {
650  using inlets_indices = std::make_index_sequence<info::control_start>;
651  using controls_indices = std::make_index_sequence<info::control_count>;
652  using outlets_indices = std::make_index_sequence<info::control_out_start>;
653  using control_outs_indices = std::make_index_sequence<info::control_out_count>;
654 
655  apply_all_impl(
656  inlets_indices{}, controls_indices{}, outlets_indices{}, control_outs_indices{},
657  tk, st);
658 
659  if constexpr(info::control_count > 0)
660  {
661  if(cqueue.size_approx() < 1 && controls_changed.any())
662  {
663  cqueue.try_enqueue(controls_feedback(controls));
664  controls_changed.reset();
665  }
666  }
667 
668  if constexpr(info::control_out_count > 0)
669  {
670  std::size_t i = 0;
671  bool ok = false;
672  ossia::for_each_in_tuples_ref(
673  this->control_outs_tuple, this->control_outs,
674  [&](auto&& current_ctrls, auto&& control_out) {
675  if(!current_ctrls.empty())
676  {
677  ok = true;
678  control_out = std::move(current_ctrls.container.back().second);
679  current_ctrls.clear();
680  }
681 
682  i++;
683  });
684 
685  if(ok)
686  {
687  this->control_outs_queue.enqueue(this->control_outs);
688  }
689  }
690  }
691 
692  void all_notes_off() noexcept override
693  {
694  if constexpr(info::midi_in_count > 0)
695  {
696  // TODO
697  }
698  }
699 
700  [[nodiscard]] std::string label() const noexcept override { return "Control"; }
701 };
702 template <typename Node_T>
703  requires std::is_same_v<
704  typename Node_T::control_policy, ossia::safe_nodes::default_tick>
705 class safe_node<Node_T> final
706  : public ossia::nonowning_graph_node
707  , public get_state<Node_T>::type
708 {
709 public:
710  using info = info_functions<Node_T>;
711  static_assert(info::control_count == 0);
712  static_assert(info::control_out_count == 0);
713  static const constexpr bool has_state = has_state_t<Node_T>::value;
714  using state_type = typename get_state<Node_T>::type;
715 
716  ebo_array<ossia::audio_inlet, info::audio_in_count> audio_in_ports;
717  ebo_array<ossia::midi_inlet, info::midi_in_count> midi_in_ports;
718  ebo_array<ossia::value_inlet, info::value_in_count> value_in_ports;
719  ebo_array<ossia::value_inlet, info::address_in_count> address_in_ports;
720 
721  ebo_array<ossia::audio_outlet, info::audio_out_count> audio_out_ports;
722  ebo_array<ossia::midi_outlet, info::midi_out_count> midi_out_ports;
723  ebo_array<ossia::value_outlet, info::value_out_count> value_out_ports;
724 
725  safe_node() noexcept
726  {
727  m_inlets.reserve(info_functions<Node_T>::inlet_size);
728  m_outlets.reserve(info_functions<Node_T>::outlet_size);
729  if constexpr(info::audio_in_count > 0)
730  for(auto& port : this->audio_in_ports)
731  m_inlets.push_back(std::addressof(port));
732 
733  if constexpr(info::midi_in_count > 0)
734  for(auto& port : this->midi_in_ports)
735  m_inlets.push_back(std::addressof(port));
736 
737  if constexpr(info::value_in_count > 0)
738  for(std::size_t i = 0; i < info::value_in_count; i++)
739  {
740  (*value_in_ports[i]).is_event = Node_T::Metadata::value_ins[i].is_event;
741  m_inlets.push_back(std::addressof(value_in_ports[i]));
742  }
743 
744  if constexpr(info::address_in_count > 0)
745  for(auto& port : this->address_in_ports)
746  m_inlets.push_back(std::addressof(port));
747 
748  if constexpr(info::audio_out_count > 0)
749  for(auto& port : this->audio_out_ports)
750  m_outlets.push_back(std::addressof(port));
751 
752  if constexpr(info::midi_out_count > 0)
753  for(auto& port : this->midi_out_ports)
754  m_outlets.push_back(std::addressof(port));
755 
756  if constexpr(info::value_out_count > 0)
757  for(std::size_t i = 0; i < info::value_out_count; i++)
758  {
759  auto& port = value_out_ports[i];
760  if(auto& type = Node_T::Metadata::value_outs[i].type; !type.empty())
761  {
762  port->type = ossia::parse_dataspace(type);
763  }
764  m_outlets.push_back(std::addressof(port));
765  }
766  }
767 
768  template <std::size_t... I, std::size_t... O>
769  void apply_all_impl(
770  const std::index_sequence<I...>&, const std::index_sequence<O...>&,
771  ossia::token_request tk, ossia::exec_state_facade st) noexcept
772  {
773  ossia::inlets& inlets = this->root_inputs();
774  ossia::outlets& outlets = this->root_outputs();
775 
776  if constexpr(has_state)
777  {
778  Node_T::run(
779  get_inlet_accessor<info, I>(inlets)...,
780  get_outlet_accessor<info, O>(outlets)..., tk, st,
781  static_cast<state_type&>(*this));
782  }
783  else
784  {
785  Node_T::run(
786  get_inlet_accessor<info, I>(inlets)...,
787  get_outlet_accessor<info, O>(outlets)..., tk, st);
788  }
789  }
790 
791  void run(const ossia::token_request& tk, ossia::exec_state_facade st) noexcept override
792  {
793  using inlets_indices = std::make_index_sequence<info::control_start>;
794  using outlets_indices = std::make_index_sequence<info::control_out_start>;
795 
796  apply_all_impl(inlets_indices{}, outlets_indices{}, tk, st);
797  }
798 
799  void all_notes_off() noexcept override
800  {
801  if constexpr(info::midi_in_count > 0)
802  {
803  // TODO
804  }
805  }
806 
807  [[nodiscard]] std::string label() const noexcept override { return "Control"; }
808 };
809 
810 struct value_adder
811 {
812  ossia::value_port& port;
813  ossia::value v;
814  void operator()()
815  {
816  // timestamp should be > all others so that it is always active ?
817  port.write_value(std::move(v), 0);
818  }
819 };
820 
821 template <typename T>
822 struct control_updater
823 {
824  T& control;
825  T v;
826  void operator()() { control = std::move(v); }
827 };
828 }
The value class.
Definition: value.hpp:173
Definition: git_info.h:7
val_type
Enum to represent the types that a value can take.
Definition: parameter_properties.hpp:16
unit_t parse_dataspace(std::string_view text)
parse_dataspace
Definition: dataspace_visitors.cpp:89