MathGenerator.hpp
1 #pragma once
2 #include <Engine/Node/SimpleApi.hpp>
3 
4 #include <ossia/math/math_expression.hpp>
5 
6 #include <numeric>
7 namespace Nodes
8 {
9 
10 template <typename State>
11 static void setMathExpressionTiming(
12  State& self, int64_t input_time, int64_t prev_time, int64_t parent_dur)
13 {
14  self.cur_time = input_time;
15  self.cur_deltatime = (input_time - prev_time);
16  self.cur_pos = parent_dur > 0 ? double(input_time) / parent_dur : 0;
17 }
18 
19 template <typename State>
20 static void setMathExpressionTiming(
21  State& self, ossia::time_value input_time, ossia::time_value prev_time,
22  ossia::time_value parent_dur, double modelToSamples)
23 {
24  setMathExpressionTiming(
25  self, input_time.impl * modelToSamples, prev_time.impl * modelToSamples,
26  parent_dur.impl * modelToSamples);
27 }
28 
29 template <typename State>
30 static void setMathExpressionTiming(
31  State& self, const ossia::token_request& tk, ossia::exec_state_facade st)
32 {
33  setMathExpressionTiming(
34  self, tk.date, tk.prev_date, tk.parent_duration, st.modelToSamples());
35 }
36 
37 static void miniMathItem(
38  const tuplet::tuple<Control::LineEdit>& controls, Process::LineEdit& edit,
39  const Process::ProcessModel& process, QGraphicsItem& parent, QObject& context,
40  const Process::Context& doc)
41 {
42  using namespace Process;
43  using namespace std;
44  using namespace tuplet;
45  const Process::PortFactoryList& portFactory
47 
48  auto edit_item
49  = makeControlNoText(get<0>(controls), edit, parent, context, doc, portFactory);
50  edit_item.control.setTextWidth(100);
51  edit_item.control.setPos(15, 0);
52 
53  if(auto obj = dynamic_cast<score::ResizeableItem*>(&parent))
54  {
55  QObject::connect(
56  &edit_item.control, &Process::LineEditItem::sizeChanged, obj,
57  &score::ResizeableItem::childrenSizeChanged);
58  }
59 }
60 
61 static void mathItem(
62  const tuplet::tuple<
64  Control::FloatSlider>& controls,
65  Process::LineEdit& edit, Process::FloatSlider& a, Process::FloatSlider& b,
66  Process::FloatSlider& c, const Process::ProcessModel& process, QGraphicsItem& parent,
67  QObject& context, const Process::Context& doc)
68 {
69  using namespace Process;
70  using namespace std;
71  using namespace tuplet;
72  const Process::PortFactoryList& portFactory
74 
75  const auto c0 = 10;
76 
77  auto c0_bg = new score::BackgroundItem{&parent};
78  c0_bg->setRect({0., 0., 300., 200.});
79 
80  auto edit_item
81  = makeControl(get<0>(controls), edit, parent, context, doc, portFactory);
82  edit_item.control.setTextWidth(280);
83  edit_item.root.setPos(c0, 40);
84  /*
85  ((QGraphicsProxyWidget&)edit_item.control).setMinimumWidth(200);
86  ((QGraphicsProxyWidget&)edit_item.control).setMaximumWidth(200);
87  ((QGraphicsProxyWidget&)edit_item.control).widget()->setMinimumWidth(200);
88  ((QGraphicsProxyWidget&)edit_item.control).widget()->setMaximumWidth(200);
89  */
90 
91  auto a_item = makeControl(get<1>(controls), a, parent, context, doc, portFactory);
92  a_item.root.setPos(c0, 5);
93  auto b_item = makeControl(get<2>(controls), b, parent, context, doc, portFactory);
94  b_item.root.setPos(c0 + 70, 5);
95  auto c_item = makeControl(get<3>(controls), c, parent, context, doc, portFactory);
96  c_item.root.setPos(c0 + 140, 5);
97 }
98 
99 namespace MathGenerator
100 {
101 struct Node
102 {
104  {
105  static const constexpr auto prettyName = "Expression Value Generator";
106  static const constexpr auto objectKey = "MathGenerator";
107  static const constexpr auto category = "Control/Generators";
108  static const constexpr auto author = "ossia score, ExprTK (Arash Partow)";
109  static const constexpr auto tags = std::array<const char*, 0>{};
110  static const constexpr auto kind = Process::ProcessCategory::Generator;
111  static const constexpr auto description
112  = "Generate a signal from a math expression.\n"
113  "Available variables: a,b,c, t (samples), dt (delta), pos (position "
114  "in parent)\n"
115  "See the documentation at http://www.partow.net/programming/exprtk";
116  static const uuid_constexpr auto uuid
117  = make_uuid("d757bd0d-c0a1-4aec-bf72-945b722ab85b");
118 
119  static const constexpr value_out value_outs[]{"out"};
120 
121  static const constexpr auto controls = tuplet::make_tuple(
122  Control::LineEdit("Expression (ExprTK)", "cos(t) + log(pos * 1 / dt)"),
123  Control::FloatSlider("Param (a)", 0., 1., 0.5),
124  Control::FloatSlider("Param (b)", 0., 1., 0.5),
125  Control::FloatSlider("Param (c)", 0., 1., 0.5));
126  };
127  struct State
128  {
129  State()
130  {
131  expr.add_variable("t", cur_time);
132  expr.add_variable("dt", cur_deltatime);
133  expr.add_variable("pos", cur_pos);
134  expr.add_variable("a", p1);
135  expr.add_variable("b", p2);
136  expr.add_variable("c", p3);
137  expr.add_variable("m1", m1);
138  expr.add_variable("m2", m2);
139  expr.add_variable("m3", m3);
140  expr.add_constants();
141  expr.register_symbol_table();
142  }
143  double cur_time{};
144  double cur_deltatime{};
145  double cur_pos{};
146  double p1{}, p2{}, p3{};
147  double m1{}, m2{}, m3{};
148  ossia::math_expression expr;
149  bool ok = false;
150  };
151 
152  using control_policy = ossia::safe_nodes::last_tick;
153  static void
154  run(const std::string& expr, float a, float b, float c, ossia::value_port& output,
155  ossia::token_request tk, ossia::exec_state_facade st, State& self)
156  {
157  if(!self.expr.set_expression(expr))
158  return;
159 
160  setMathExpressionTiming(self, tk, st);
161  self.p1 = a;
162  self.p2 = b;
163  self.p3 = c;
164 
165  auto res = self.expr.result();
166  const auto [tick_start, d] = st.timings(tk);
167  output.write_value(res, tick_start);
168  }
169 
170  template <typename... Args>
171  static void item(Args&&... args)
172  {
173  Nodes::mathItem(Metadata::controls, std::forward<Args>(args)...);
174  }
175 };
176 }
177 
178 namespace MathAudioGenerator
179 {
180 struct Node
181 {
183  {
184  static const constexpr auto prettyName = "Expression Audio Generator";
185  static const constexpr auto objectKey = "MathAudioGenerator";
186  static const constexpr auto category = "Audio/Utilities";
187  static const constexpr auto author = "ossia score, ExprTK (Arash Partow)";
188  static const constexpr auto tags = std::array<const char*, 0>{};
189  static const constexpr auto kind = Process::ProcessCategory::Generator;
190  static const constexpr auto description
191  = "Generate an audio signal from a math expression.\n"
192  "Available variables: a,b,c, t (samples), fs (sampling frequency)\n"
193  "See the documentation at http://www.partow.net/programming/exprtk";
194  static const uuid_constexpr auto uuid
195  = make_uuid("eae294b3-afeb-4fba-bbe4-337998d3748a");
196 
197  static const constexpr audio_out audio_outs[]{"out"};
198 
199  static const constexpr auto controls = tuplet::make_tuple(
201  "Expression (ExprTK)",
202  "var phi := 2 * pi * (20 + a * 500) / fs;\n"
203  "m1[0] += phi;\n"
204  "\n"
205  "out[0] := b * cos(m1[0]);\n"
206  "out[1] := b * cos(m1[0]);\n"),
207  Control::FloatSlider("Param (a)", 0., 1., 0.5),
208  Control::FloatSlider("Param (b)", 0., 1., 0.5),
209  Control::FloatSlider("Param (c)", 0., 1., 0.5));
210  };
211 
212  struct State
213  {
214  State()
215  {
216  cur_out.reserve(8);
217  m1.reserve(8);
218  m2.reserve(8);
219  m3.reserve(8);
220  cur_out.resize(2);
221  m1.resize(2);
222  m2.resize(2);
223  m3.resize(2);
224 
225  expr.add_vector("out", cur_out);
226  expr.add_variable("t", cur_time);
227  expr.add_variable("a", p1);
228  expr.add_variable("b", p2);
229  expr.add_variable("c", p3);
230  expr.add_vector("m1", m1);
231  expr.add_vector("m2", m2);
232  expr.add_vector("m3", m3);
233  expr.add_variable("fs", fs);
234 
235  expr.add_constants();
236  expr.register_symbol_table();
237  }
238 
239  void reset_symbols(std::size_t N)
240  {
241  // TODO allow to set how many channels
242  if(N == cur_out.size())
243  return;
244 
245  expr.remove_vector("out");
246  expr.remove_vector("m1");
247  expr.remove_vector("m2");
248  expr.remove_vector("m3");
249 
250  cur_out.resize(N);
251  m1.resize(N);
252  m2.resize(N);
253  m3.resize(N);
254 
255  expr.add_vector("out", cur_out);
256  expr.add_vector("m1", m1);
257  expr.add_vector("m2", m2);
258  expr.add_vector("m3", m3);
259 
260  expr.update_symbol_table();
261  }
262  std::vector<double> cur_out{};
263  double cur_time{};
264  double p1{}, p2{}, p3{};
265  std::vector<double> m1, m2, m3;
266  double fs{44100};
267  ossia::math_expression expr;
268  bool ok = false;
269  };
270 
271  using control_policy = ossia::safe_nodes::last_tick;
272  static void
273  run(const std::string& expr, float a, float b, float c, ossia::audio_port& output,
274  ossia::token_request tk, ossia::exec_state_facade st, State& self)
275  {
276  if(tk.forward())
277  {
278  self.fs = st.sampleRate();
279  if(!self.expr.set_expression(expr))
280  return;
281 
282  const auto samplesRatio = st.modelToSamples();
283  const auto [tick_start, count] = st.timings(tk);
284 
285  const int chans = 2;
286  self.reset_symbols(chans);
287  output.set_channels(chans);
288  for(int j = 0; j < chans; j++)
289  {
290  auto& out = output.channel(j);
291  out.resize(st.bufferSize(), boost::container::default_init);
292  }
293 
294  self.p1 = a;
295  self.p2 = b;
296  self.p3 = c;
297  const auto start_sample = (tk.prev_date * samplesRatio).impl;
298  for(int64_t i = 0; i < count; i++)
299  {
300  self.cur_time = start_sample + i;
301 
302  // Compute the value
303  self.expr.value();
304 
305  // Apply the output
306  for(int j = 0; j < chans; j++)
307  {
308  output.channel(j)[tick_start + i] = self.cur_out[j];
309  }
310  }
311  }
312  }
313 
314  template <typename... Args>
315  static void item(Args&&... args)
316  {
317  Nodes::mathItem(Metadata::controls, std::forward<Args>(args)...);
318  }
319 };
320 }
321 }
Definition: PortFactory.hpp:65
The Process class.
Definition: score-lib-process/Process/Process.hpp:61
Definition: RectItem.hpp:96
Definition: RectItem.hpp:12
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: score-lib-process/Control/Widgets.hpp:417
Definition: SimpleApi.hpp:32
Definition: MathGenerator.hpp:183
Definition: MathGenerator.hpp:181
Definition: MathGenerator.hpp:104
Definition: MathGenerator.hpp:102
Definition: ProcessContext.hpp:12
const T & interfaces() const
Access to a specific interface list.
Definition: ApplicationContext.hpp:67