LFO_v2.hpp
1 #pragma once
2 #include <Fx/LFO.hpp>
3 #include <Fx/Types.hpp>
4 
5 #include <ossia/detail/flicks.hpp>
6 #include <ossia/detail/math.hpp>
7 
8 #include <halp/controls.hpp>
9 #include <halp/layout.hpp>
10 #include <halp/meta.hpp>
11 #include <halp/midi.hpp>
12 #include <rnd/random.hpp>
13 #include <tuplet/tuple.hpp>
14 
15 #include <random>
16 namespace Nodes::LFO::v2
17 {
18 struct Node
19 {
20  halp_meta(name, "LFO")
21  halp_meta(c_name, "LFO")
22  halp_meta(category, "Control/Generators")
23  halp_meta(author, "ossia score")
24  halp_meta(manual_url, "https://ossia.io/score-docs/processes/lfo.html#lfo")
25  halp_meta(description, "Low-frequency oscillator")
26  halp_meta(recommended_height, 130.)
27  halp_meta(uuid, "1e17e479-3513-44c8-a8a7-017be9f6ac8a");
28 
29  struct ins
30  {
31  halp::log_hslider_f32<"Freq.", halp::range{0.01f, 100.f, 1.f}> freq;
32  halp::knob_f32<"Ampl.", halp::range{0., 2., 0.5}> ampl;
33  halp::knob_f32<"Offset", halp::range{-1., 1., 0.5}> offset;
34  halp::knob_f32<"Jitter", halp::range{0., 1., 0}> jitter;
35  halp::knob_f32<"Phase", halp::range{-1., 1., 0.}> phase;
36  struct : halp::enum_t<Control::Widgets::Waveform, "Waveform">
37  {
38  static constexpr auto pixmaps()
39  {
40  return std::array<const char*, 16>{
41  ":/icons/wave_sin_off.png",
42  ":/icons/wave_sin_on.png",
43  ":/icons/wave_triangle_off.png",
44  ":/icons/wave_triangle_on.png",
45  ":/icons/wave_saw_off.png",
46  ":/icons/wave_saw_on.png",
47  ":/icons/wave_square_off.png",
48  ":/icons/wave_square_on.png",
49  ":/icons/wave_sample_and_hold_off.png",
50  ":/icons/wave_sample_and_hold_on.png",
51  ":/icons/wave_noise1_off.png",
52  ":/icons/wave_noise1_on.png",
53  ":/icons/wave_noise2_off.png",
54  ":/icons/wave_noise2_on.png",
55  ":/icons/wave_noise3_off.png",
56  ":/icons/wave_noise3_on.png"};
57  }
58  } waveform;
59  quant_selector<"Quantification"> quant;
60 
61  } inputs;
62  struct
63  {
64  // FIXME output range ???
65  struct : halp::val_port<"Out", std::optional<float>>
66  {
67  struct range
68  {
69  float min = 0.;
70  float max = 1.;
71  };
72  } out;
73  } outputs;
74 
75  double phase{};
76  rnd::pcg rd{random_source()};
77 
78  using tick = halp::tick_flicks;
79  void operator()(const halp::tick_flicks& tk)
80  {
81  constexpr const double sine_ratio = ossia::two_pi / ossia::flicks_per_second<double>;
82  const auto elapsed = tk.model_read_duration();
83 
84  const auto quantif = inputs.quant.value;
85  auto freq = inputs.freq.value;
86  auto ampl = inputs.ampl.value;
87  auto offset = inputs.offset.value;
88  const auto jitter = inputs.jitter.value;
89  auto custom_phase = inputs.phase.value;
90  const auto type = inputs.waveform.value;
91  if(quantif)
92  {
93  // Determine the frequency with the quantification
94  if(tk.unexpected_bar_change())
95  {
96  this->phase = 0;
97  }
98 
99  // If quantif == 1, we quantize to the bar
100  // => f = 0.5 hz
101  // If quantif == 1/4, we quantize to the quarter
102  // => f = 2hz
103  // -> sin(elapsed * freq * 2 * pi / fps)
104  // -> sin(elapsed * 4 * 2 * pi / fps)
105  freq = 1. / (2. * quantif);
106  }
107 
108  const auto ph_delta = elapsed * freq * sine_ratio;
109 
110  {
111  auto ph = this->phase;
112  if(jitter > 0)
113  {
114  ph += std::normal_distribution<float>(0., 0.25)(this->rd) * jitter;
115  }
116 
117  using namespace Control::Widgets;
118 
119  const auto add_val
120  = [&](auto new_val) { outputs.out.value = ampl * new_val + offset; };
121  switch(type)
122  {
123  case Sin:
124  add_val(std::sin(custom_phase + ph));
125  break;
126  case Triangle:
127  add_val(std::asin(std::sin(custom_phase + ph)) / ossia::half_pi);
128  break;
129  case Saw:
130  add_val(std::atan(std::tan(custom_phase + ph)) / ossia::half_pi);
131  break;
132  case Square:
133  add_val((std::sin(custom_phase + ph) > 0.f) ? 1.f : -1.f);
134  break;
135  case SampleAndHold: {
136  const auto start_s = std::sin(custom_phase + ph);
137  const auto end_s = std::sin(custom_phase + ph + ph_delta);
138  if((start_s > 0 && end_s <= 0) || (start_s <= 0 && end_s > 0))
139  {
140  add_val(std::uniform_real_distribution<float>(-1.f, 1.f)(this->rd));
141  }
142  break;
143  }
144  case Noise1:
145  add_val(std::uniform_real_distribution<float>(-1.f, 1.f)(this->rd));
146  break;
147  case Noise2:
148  add_val(std::normal_distribution<float>(0.f, 1.f)(this->rd));
149  break;
150  case Noise3:
151  add_val(
152  std::clamp(std::cauchy_distribution<float>(0.f, 1.f)(this->rd), 0.f, 1.f));
153  break;
154  }
155  }
156 
157  this->phase += ph_delta;
158  }
159 
160  struct ui
161  {
162  halp_meta(layout, halp::layouts::hbox)
163  struct
164  {
165  halp_meta(layout, halp::layouts::vbox)
166  halp_meta(background, halp::colors::mid)
167  struct
168  {
169  halp_meta(layout, halp::layouts::hbox)
170 
171  halp::control<&ins::freq> f;
172  halp::control<&ins::quant> q;
173  } timing;
174  halp::control<&ins::waveform> w;
175  } gen;
176 
177  struct
178  {
179  halp_meta(layout, halp::layouts::vbox)
180  halp_meta(background, halp::colors::mid)
181  halp::control<&ins::ampl> a;
182  halp::control<&ins::offset> o;
183  } ampl;
184  struct
185  {
186  halp_meta(layout, halp::layouts::vbox)
187  halp_meta(background, halp::colors::mid)
188  halp::control<&ins::jitter> j;
189  halp::control<&ins::phase> p;
190  } modulation;
191  };
192 };
193 }
Definition: LFO_v2.hpp:30
Definition: LFO_v2.hpp:161
Definition: LFO_v2.hpp:19
Definition: Types.hpp:48
Definition: LFO_v2.hpp:68