Smooth.hpp
1 #pragma once
2 #include <Engine/Node/SimpleApi.hpp>
3 
4 #include <ossia/detail/logger.hpp>
5 #include <ossia/network/value/format_value.hpp>
6 
7 #include <libossia/3rdparty/dno/DeNoiser.hpp>
8 
9 namespace Nodes::ValueFilter
10 {
11 using namespace dno;
12 
14 {
15  NoiseFilter()
16  : dno_i{}
17  , dno_v{
18  DeNoiser<double>{}, DeNoiser<double>{}, DeNoiser<double>{},
19  DeNoiser<double>{}}
20  {
21  }
22 
23  DeNoiser<double> dno_i;
24  std::vector<DeNoiser<double>> dno_v;
25 
26  ossia::value filter(const ossia::value& val)
27  {
28  struct vis
29  {
30  ossia::value operator()() const { return {}; }
31  ossia::value operator()(const ossia::impulse&) const { return {}; }
32  ossia::value operator()(int i) const { return nFilt->dno_i(i); }
33  ossia::value operator()(float f) const { return nFilt->dno_v[0](f); }
34  ossia::value operator()(bool b) const { return b; }
35  ossia::value operator()(const std::string& s) const { return s; }
36  ossia::value operator()(const ossia::vec2f& t) const
37  {
38  return ossia::make_vec(nFilt->dno_v[0](t[0]), nFilt->dno_v[1](t[1]));
39  }
40  ossia::value operator()(const ossia::vec3f& t) const
41  {
42  return ossia::make_vec(
43  nFilt->dno_v[0](t[0]), nFilt->dno_v[1](t[1]), nFilt->dno_v[2](t[2]));
44  }
45  ossia::value operator()(const ossia::vec4f& t) const
46  {
47  return ossia::make_vec(
48  nFilt->dno_v[0](t[0]), nFilt->dno_v[1](t[1]), nFilt->dno_v[2](t[2]),
49  nFilt->dno_v[3](t[3]));
50  }
51 
52  ossia::value operator()(const std::vector<ossia::value>& t) const
53  {
54  std::vector<ossia::value> ret = t;
55  while(nFilt->dno_v.size() < ret.size())
56  nFilt->dno_v.push_back(nFilt->dno_v.front());
57 
58  for(std::size_t k = 0; k < ret.size(); k++)
59  ret[k] = (float)nFilt->dno_v[k](ossia::convert<float>(ret[k]));
60 
61  return ret;
62  }
63 
64  ossia::value operator()(const ossia::value_map_type& t) const
65  {
66  // FIXME
67  return t;
68  }
69 
70  NoiseFilter* nFilt{};
71 
72  vis(NoiseFilter* parent)
73  : nFilt{parent}
74  {
75  }
76  };
77 
78  try
79  {
80  return ossia::apply(vis{this}, val.v);
81  }
82  catch(std::exception& e)
83  {
84  ossia::logger().error("{}", e.what());
85  }
86  catch(...)
87  {
88  ossia::logger().error("error");
89  }
90 
91  return val;
92  }
93 
94  void set_amount(float amt)
95  {
96  dno_i.set_amount(amt);
97  for(auto& f : dno_v)
98  f.set_amount(amt);
99  }
100 
101  void set_type(const type& t = OneEuro)
102  {
103  dno_i.set_type(t);
104  for(auto& f : dno_v)
105  f.set_type(t);
106  }
107 
108  void set_freq(double freq) // 1e & LP
109  {
110  dno_i.set_freq(freq);
111  for(auto& f : dno_v)
112  f.set_freq(freq);
113  }
114 
115  void set_cutoff(double cutoff) // 1e & LP
116  {
117  dno_i.set_cutoff(cutoff);
118  for(auto& f : dno_v)
119  f.set_cutoff(cutoff);
120  }
121 
122  void set_beta(double beta) // 1e only
123  {
124  dno_i.set_1e_beta(beta);
125  for(auto& f : dno_v)
126  f.set_1e_beta(beta);
127  }
128 };
129 
131 {
132 public:
133  ossia::value filter(
134  const ossia::value& value, const std::string& type, float amount, float freq,
135  float cutoff, float beta)
136  {
137  if(type != prev_type)
138  {
139  nf.set_type(GetFilterType(type));
140  prev_type = type;
141  // Reset the values to make sure they get updated once the filter has been changed
142  prev_amount = INFINITY;
143  prev_freq = INFINITY;
144  prev_cutoff = INFINITY;
145  prev_beta = INFINITY;
146  }
147  if(amount != prev_amount)
148  {
149  nf.set_amount(amount);
150  prev_amount = amount;
151  }
152  if(freq != prev_freq)
153  {
154  nf.set_freq(freq);
155  prev_freq = freq;
156  }
157  if(cutoff != prev_cutoff)
158  {
159  nf.set_cutoff(cutoff);
160  prev_cutoff = cutoff;
161  }
162  if(beta != prev_beta)
163  {
164  nf.set_beta(beta);
165  prev_beta = beta;
166  }
167 
168  return nf.filter(value);
169  }
170 
171  ossia::value last;
172 
173 private:
174  std::string prev_type{"OneEuro"};
175  double prev_amount{1. / SCALED_AMOUNT}, prev_freq{INIT_FREQ}, prev_cutoff{INIT_CUTOFF},
176  prev_beta{INIT_BETA};
177 
178  NoiseFilter nf{};
179 
180  type GetFilterType(std::string_view str) noexcept
181  {
182  if(str == "LowPass")
183  return LowPass;
184  else if(str == "Average")
185  return Average;
186  else if(str == "Median")
187  return Median;
188  return OneEuro;
189  }
190 };
191 
192 namespace v1
193 {
194 struct Node
195 {
197  {
198  static const constexpr auto prettyName = "Smooth (old)";
199  static const constexpr auto objectKey = "ValueFilter";
200  static const constexpr auto category = "Control/Mappings";
201  static const constexpr auto author = "ossia score";
202  static const constexpr auto tags = std::array<const char*, 0>{};
203  static const constexpr auto kind
204  = Process::ProcessCategory::Mapping | Process::ProcessCategory::Deprecated;
205  static const constexpr auto description = "Filter noisy value stream";
206  static const uuid_constexpr auto uuid
207  = make_uuid("809c014d-7d02-45dc-8849-de7a7db5fe67");
208 
209  static const constexpr auto controls = tuplet::make_tuple(
210  Control::make_enum(
211  "Type", 0U, ossia::make_array("OneEuro", "LowPass", "Average", "Median")),
212  Control::FloatKnob{"Amount", 0., 1., 0.1},
213  Control::LogFloatSlider{"Freq (1e/LP)", 0.001, 300., 120.},
214  Control::LogFloatSlider{"Cutoff (1e/LP)", 0.001, 10., 1.},
215  Control::FloatSlider{"Beta (1e only)", 0.001, 10., 1.});
216 
217  static const constexpr value_in value_ins[]{"in"};
218  static const constexpr value_out value_outs[]{"out"};
219  };
220 
221  using State = NoiseState;
222  using control_policy = ossia::safe_nodes::last_tick;
223 
224  static void
225  run(const ossia::value_port& in, const std::string& type, float amount, float freq,
226  float cutoff, float beta, ossia::value_port& out, ossia::token_request t,
227  ossia::exec_state_facade st, State& self)
228  {
229  for(const ossia::timed_value& v : in.get_data())
230  {
231  auto filtered = self.filter(v.value, type, amount, freq, cutoff, beta);
232  out.write_value(filtered, v.timestamp); // TODO fix accuracy of timestamp
233  }
234 
235  if(!in.get_data().empty())
236  {
237  self.last = in.get_data().back().value;
238  }
239  }
240 
241  static void item(
242  Process::Enum& type, Process::FloatKnob& amount, Process::LogFloatSlider& freq,
243  Process::LogFloatSlider& cutoff, Process::FloatSlider& beta,
244  const Process::ProcessModel& process, QGraphicsItem& parent, QObject& context,
245  const Process::Context& doc)
246  {
247  using namespace Process;
248  const Process::PortFactoryList& portFactory
250  const auto tMarg = 5;
251  const auto cMarg = 15;
252  const auto h = 125;
253  const auto w = 220;
254 
255  auto c0_bg = new score::BackgroundItem{&parent};
256  c0_bg->setRect({0., 0., w, h});
257 
258  auto type_item = makeControl(
259  tuplet::get<0>(Metadata::controls), type, parent, context, doc, portFactory);
260  type_item.root.setPos(70, 0);
261  type_item.control.rows = 4;
262  type_item.control.columns = 1;
263  type_item.control.setRect(QRectF{0, 0, 60, 105});
264 
265  auto amount_item = makeControl(
266  tuplet::get<1>(Metadata::controls), amount, parent, context, doc, portFactory);
267  amount_item.root.setPos(tMarg, 30);
268  amount_item.control.setPos(0, cMarg);
269 
270  auto freq_item = makeControl(
271  tuplet::get<2>(Metadata::controls), freq, parent, context, doc, portFactory);
272  freq_item.root.setPos(140, 0);
273  freq_item.control.setPos(0, cMarg);
274 
275  auto cutoff_item = makeControl(
276  tuplet::get<3>(Metadata::controls), cutoff, parent, context, doc, portFactory);
277  cutoff_item.root.setPos(140, 40);
278  cutoff_item.control.setPos(0, cMarg);
279 
280  auto beta_item = makeControl(
281  tuplet::get<4>(Metadata::controls), beta, parent, context, doc, portFactory);
282  beta_item.root.setPos(140, 80);
283  beta_item.control.setPos(0, cMarg);
284  }
285 };
286 }
287 
288 namespace v2
289 {
290 struct Node
291 {
293  {
294  static const constexpr auto prettyName = "Smooth";
295  static const constexpr auto objectKey = "ValueFilter";
296  static const constexpr auto category = "Control/Mappings";
297  static const constexpr auto author = "ossia score";
298  static const constexpr auto tags = std::array<const char*, 0>{};
299  static const constexpr auto kind = Process::ProcessCategory::Mapping;
300  static const constexpr auto description = "Filter noisy value stream";
301  static const uuid_constexpr auto uuid
302  = make_uuid("bf603921-5a48-4aa5-9bc1-48a762be6467");
303 
304  static const constexpr auto controls = tuplet::make_tuple(
305  Control::make_enum(
306  "Type", 0U, ossia::make_array("OneEuro", "LowPass", "Average", "Median")),
307  Control::FloatKnob{"Amount", 0., 1., 0.1},
308  Control::LogFloatSlider{"Freq (1e/LP)", 0.001, 300., 120.},
309  Control::LogFloatSlider{"Cutoff (1e/LP)", 0.001, 10., 1.},
310  Control::FloatSlider{"Beta (1e only)", 0.001, 10., 1.},
311  Control::Toggle{"Continuous", false});
312 
313  static const constexpr value_in value_ins[]{"in"};
314  static const constexpr value_out value_outs[]{"out"};
315  };
316 
317  using State = NoiseState;
318  using control_policy = ossia::safe_nodes::last_tick;
319 
320  static void
321  run(const ossia::value_port& in, const std::string& type, float amount, float freq,
322  float cutoff, float beta, bool continuous, ossia::value_port& out,
323  ossia::token_request t, ossia::exec_state_facade st, State& self)
324  {
325  for(const ossia::timed_value& v : in.get_data())
326  {
327  auto filtered = self.filter(v.value, type, amount, freq, cutoff, beta);
328  out.write_value(filtered, v.timestamp); // TODO fix accuracy of timestamp
329  }
330 
331  if(!in.get_data().empty())
332  {
333  self.last = in.get_data().back().value;
334  }
335  else
336  {
337  if(continuous && self.last.valid())
338  {
339  auto filtered = self.filter(self.last, type, amount, freq, cutoff, beta);
340  out.write_value(filtered, st.timings(t).start_sample);
341  }
342  }
343  }
344 
345  static void item(
346  Process::Enum& type, Process::FloatKnob& amount, Process::LogFloatSlider& freq,
347  Process::LogFloatSlider& cutoff, Process::FloatSlider& beta, Process::Toggle& cont,
348  const Process::ProcessModel& process, QGraphicsItem& parent, QObject& context,
349  const Process::Context& doc)
350  {
351  using namespace Process;
352  const Process::PortFactoryList& portFactory
354  const auto tMarg = 5;
355  const auto cMarg = 15;
356  const auto h = 125;
357  const auto w = 220;
358 
359  auto c0_bg = new score::BackgroundItem{&parent};
360  c0_bg->setRect({0., 0., w, h});
361 
362  auto type_item = makeControl(
363  tuplet::get<0>(Metadata::controls), type, parent, context, doc, portFactory);
364  type_item.root.setPos(70, 0);
365  type_item.control.rows = 4;
366  type_item.control.columns = 1;
367  type_item.control.setRect(QRectF{0, 0, 60, 105});
368 
369  auto amount_item = makeControl(
370  tuplet::get<1>(Metadata::controls), amount, parent, context, doc, portFactory);
371  amount_item.root.setPos(tMarg, 30);
372  amount_item.control.setPos(0, cMarg);
373 
374  auto freq_item = makeControl(
375  tuplet::get<2>(Metadata::controls), freq, parent, context, doc, portFactory);
376  freq_item.root.setPos(140, 0);
377  freq_item.control.setPos(0, cMarg);
378 
379  auto cutoff_item = makeControl(
380  tuplet::get<3>(Metadata::controls), cutoff, parent, context, doc, portFactory);
381  cutoff_item.root.setPos(140, 40);
382  cutoff_item.control.setPos(0, cMarg);
383 
384  auto beta_item = makeControl(
385  tuplet::get<4>(Metadata::controls), beta, parent, context, doc, portFactory);
386  beta_item.root.setPos(140, 80);
387  beta_item.control.setPos(0, cMarg);
388 
389  auto cont_item = makeControl(
390  tuplet::get<5>(Metadata::controls), cont, parent, context, doc, portFactory);
391  cont_item.root.setPos(tMarg, 0);
392  //cont_item.control.setPos(10, cMarg);
393  }
394 };
395 }
396 }
Definition: Smooth.hpp:131
Definition: PortFactory.hpp:65
The Process class.
Definition: score-lib-process/Process/Process.hpp:61
Definition: RectItem.hpp:96
Base classes and tools to implement processes and layers.
Definition: JSONVisitor.hpp:1324
Utilities for OSSIA data structures.
Definition: DeviceInterface.hpp:33
Definition: score-lib-process/Control/Widgets.hpp:77
Definition: SimpleApi.hpp:32
Definition: score-lib-process/Control/Widgets.hpp:337
Definition: Smooth.hpp:14
Definition: Smooth.hpp:197
Definition: Smooth.hpp:195
Definition: Smooth.hpp:293
Definition: Smooth.hpp:291
Definition: ProcessContext.hpp:12
const T & interfaces() const
Access to a specific interface list.
Definition: ApplicationContext.hpp:67