Loading...
Searching...
No Matches
Combiner.hpp
1#pragma once
2#include <AvndProcesses/AddressTools.hpp>
3
4namespace 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
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