Combiner.hpp
1 #pragma once
2 #include <AvndProcesses/AddressTools.hpp>
3 
4 namespace avnd_tools
5 {
6 
8 {
9  halp_meta(name, "Pattern combiner")
10  halp_meta(author, "ossia team")
11  halp_meta(category, "Control/Data processing")
12  halp_meta(description, "Apply an operation to all inputs matching a pattern")
13  halp_meta(c_name, "avnd_pattern_combine")
14  halp_meta(uuid, "18efe965-9acc-4703-9af3-3cef658b301a")
15  halp_meta(manual_url, "https://ossia.io/score-docs/processes/pattern-combiner.html#pattern-combiner")
16 
17  struct
18  {
19  PatternSelector pattern;
20 
21  struct
22  {
23  halp__enum("Mode", List, List, Average, Sum, Min, Max);
24  } mode{};
25 
26  } inputs;
27 
28  struct
29  {
30  halp::val_port<"Output", ossia::value> output;
31  } outputs;
32 
33  std::vector<ossia::value> current_values;
34 
35  struct value_size
36  {
37  int operator()(ossia::impulse) const noexcept { return 1; }
38  int operator()(int) const noexcept { return 1; }
39  int operator()(float) const noexcept { return 1; }
40  int operator()(bool) const noexcept { return 1; }
41  int operator()(const std::string&) const noexcept { return 1; }
42  int operator()(ossia::vec2f) const noexcept { return 2; }
43  int operator()(ossia::vec3f) const noexcept { return 3; }
44  int operator()(ossia::vec4f) const noexcept { return 4; }
45  int operator()(const std::vector<ossia::value>& v) const noexcept
46  {
47  return v.size();
48  }
49  int
50  operator()(const std::vector<std::pair<std::string, ossia::value>>& v) const noexcept
51  {
52  return v.size();
53  }
54  int operator()() const noexcept { return 0; }
55  };
56 
57  static std::optional<ossia::val_type>
58  all_have_same_type(const std::vector<ossia::value>& val) noexcept
59  {
60  if(val.empty())
61  return std::nullopt;
62 
63  auto t0 = val[0].get_type();
64  auto n0 = val[0].apply(value_size{});
65  for(auto& e : val)
66  {
67  if(e.get_type() != t0)
68  return std::nullopt;
69  if(e.apply(value_size{}) != n0)
70  return std::nullopt;
71  }
72 
73  return t0;
74  }
75 
76  struct average
77  {
78  PatternCombiner& self;
79  static double map(double res, double other) noexcept { return res = res + other; }
80  double reduce(double res) const noexcept
81  {
82  return res = res / self.current_values.size();
83  }
84  };
85  struct sum
86  {
87  PatternCombiner& self;
88  static double map(double res, double other) noexcept { return res = res + other; }
89  static double reduce(double res) noexcept { return res; }
90  };
91  struct minimum
92  {
93  PatternCombiner& self;
94  static double map(double res, double other) noexcept
95  {
96  return res = std::min(res, other);
97  }
98  static double reduce(double res) noexcept { return res; }
99  };
100  struct maximum
101  {
102  PatternCombiner& self;
103  static double map(double res, double other) noexcept
104  {
105  return res = std::max(res, other);
106  }
107  static double reduce(double res) noexcept { return res; }
108  };
109 
110  void process_various(auto func)
111  {
112  double res{};
113  for(const auto& val : current_values)
114  {
115  res = func.map(res, ossia::convert<double>(val));
116  }
117  res = func.reduce(res);
118  outputs.output.value = float(res);
119  }
120 
121  template <typename Op>
123  {
124  Op func;
125  PatternCombiner& self;
126  void operator()(ossia::impulse) const noexcept { }
127  void operator()(int) const noexcept
128  {
129  float res{};
130  for(const auto& val : self.current_values)
131  {
132  res = func.map(res, float(*val.template target<int>()));
133  }
134  res = func.reduce(res);
135  self.outputs.output.value = res;
136  }
137  void operator()(float) const noexcept
138  {
139  float res{};
140  for(const auto& val : self.current_values)
141  {
142  res = func.map(res, *val.template target<float>());
143  }
144  res = func.reduce(res);
145  self.outputs.output.value = res;
146  }
147  void operator()(bool) const noexcept
148  {
149  float res{};
150  for(const auto& val : self.current_values)
151  {
152  res = func.map(res, (*val.template target<bool>() ? 1.f : 0.f));
153  }
154  res = func.reduce(res);
155  self.outputs.output.value = res;
156  }
157  void operator()(const std::string&) const noexcept { }
158  void operator()(ossia::vec2f) const noexcept
159  {
160  ossia::vec2f res{};
161  for(const auto& val : self.current_values)
162  {
163  const auto& in = *val.template target<ossia::vec2f>();
164  res[0] = func.map(res[0], in[0]);
165  res[1] = func.map(res[1], in[1]);
166  }
167  res[0] = func.reduce(res[0]);
168  res[1] = func.reduce(res[1]);
169  self.outputs.output.value = res;
170  }
171  void operator()(ossia::vec3f) const noexcept
172  {
173  ossia::vec3f res{};
174  for(const auto& val : self.current_values)
175  {
176  const auto& in = *val.template target<ossia::vec3f>();
177  res[0] = func.map(res[0], in[0]);
178  res[1] = func.map(res[1], in[1]);
179  res[2] = func.map(res[2], in[2]);
180  }
181  res[0] = func.reduce(res[0]);
182  res[1] = func.reduce(res[1]);
183  res[2] = func.reduce(res[2]);
184  self.outputs.output.value = res;
185  }
186  void operator()(ossia::vec4f) const noexcept
187  {
188  ossia::vec4f res{};
189  for(const auto& val : self.current_values)
190  {
191  const auto& in = *val.template target<ossia::vec4f>();
192  res[0] = func.map(res[0], in[0]);
193  res[1] = func.map(res[1], in[1]);
194  res[2] = func.map(res[2], in[2]);
195  res[3] = func.map(res[3], in[3]);
196  }
197  res[0] = func.reduce(res[0]);
198  res[1] = func.reduce(res[1]);
199  res[2] = func.reduce(res[2]);
200  res[3] = func.reduce(res[3]);
201  self.outputs.output.value = res;
202  }
203  void operator()(const std::vector<ossia::value>& v) const noexcept
204  {
205  boost::container::small_vector<float, 250> res;
206  const int N = v.size();
207  res.resize(N);
208  for(const auto& val : self.current_values)
209  {
210  const auto& in = *val.template target<std::vector<ossia::value>>();
211  for(int i = 0; i < N; i++)
212  {
213  res[i] = func.map(res[i], ossia::convert<float>(in[i]));
214  }
215  }
216 
217  std::vector<ossia::value> rres;
218  for(int i = 0; i < N; i++)
219  rres[i] = (float)func.reduce(res[i]);
220 
221  self.outputs.output.value = std::move(rres);
222  }
223  void
224  operator()(const std::vector<std::pair<std::string, ossia::value>>& v) const noexcept
225  {
226  }
227  void operator()() const noexcept { }
228  };
229 
230  template <typename T>
231  void process_fixed(T func)
232  {
233  this->current_values[0].apply(do_process_fixed<T>{func, *this});
234  }
235 
236  void operator()()
237  {
238  if(!m_path)
239  return;
240 
241  current_values.clear();
242  if(inputs.pattern.devices_dirty)
243  inputs.pattern.reprocess();
244  for(auto in : this->roots)
245  {
246  if(auto p = in->get_parameter())
247  current_values.push_back(p->value());
248  }
249 
250  if(current_values.empty())
251  {
252  return;
253  }
254 
255  using mode = decltype(inputs.mode)::enum_type;
256  switch(inputs.mode)
257  {
258  case mode::List:
259  outputs.output.value = current_values;
260  break;
261  case mode::Average: {
262  if(auto t = all_have_same_type(current_values))
263  process_fixed(average{*this});
264  else
265  process_various(average{*this});
266  break;
267  }
268  case mode::Sum: {
269  if(auto t = all_have_same_type(current_values))
270  process_fixed(sum{*this});
271  else
272  process_various(sum{*this});
273  break;
274  }
275  case mode::Min: {
276  if(auto t = all_have_same_type(current_values))
277  process_fixed(minimum{*this});
278  else
279  process_various(minimum{*this});
280  break;
281  }
282  case mode::Max: {
283  if(auto t = all_have_same_type(current_values))
284  process_fixed(maximum{*this});
285  else
286  process_various(maximum{*this});
287  break;
288  }
289  }
290  }
291 };
292 
293 }
Definition: Combiner.hpp:77
Definition: Combiner.hpp:101
Definition: Combiner.hpp:92
Definition: Combiner.hpp:86
Definition: Combiner.hpp:36
Definition: Combiner.hpp:8
Definition: AddressTools.hpp:21
Definition: AddressTools.hpp:28