LFO.hpp
1 #pragma once
2 #include <Fx/Types.hpp>
3 
4 #include <ossia/detail/flicks.hpp>
5 #include <ossia/detail/math.hpp>
6 
7 #include <halp/controls.hpp>
8 #include <halp/meta.hpp>
9 #include <halp/midi.hpp>
10 #include <rnd/random.hpp>
11 #include <tuplet/tuple.hpp>
12 
13 #include <random>
14 
15 namespace Nodes
16 {
17 
18 namespace LFO
19 {
20 static inline std::random_device& random_source()
21 {
22  static thread_local std::random_device d;
23  return d;
24 }
25 
26 namespace v1
27 {
28 struct Node
29 {
30  halp_meta(name, "LFO (old)")
31  halp_meta(c_name, "LFO")
32  halp_meta(category, "Control/Generators")
33  halp_meta(author, "ossia score")
34  halp_meta(manual_url, "https://ossia.io/score-docs/processes/lfo.html#lfo")
35  halp_meta(description, "Low-frequency oscillator")
36  halp_flag(deprecated);
37  halp_meta(recommended_height, 130.)
38  halp_meta(uuid, "0697b807-f588-49b5-926c-f97701edd0d8");
39 
40  struct Inputs
41  {
42  // FIXME knob
43  halp::log_hslider_f32<"Freq.", halp::range{0.01f, 100.f, 1.f}> freq;
44  halp::knob_f32<"Ampl.", halp::range{0., 1000., 0.}> ampl;
45  halp::knob_f32<"Fine", halp::range{0., 1., 0.5}> ampl_fine;
46  halp::knob_f32<"Offset", halp::range{-1000., 1000., 0.}> offset;
47  halp::knob_f32<"Fine", halp::range{-1., 1., 0.5}> offset_fine;
48  halp::knob_f32<"Jitter", halp::range{0., 1., 0}> jitter;
49  halp::knob_f32<"Phase", halp::range{-1., 1., 0.}> phase;
50  halp::enum_t<Control::Widgets::Waveform, "Waveform"> waveform;
51  quant_selector<"Quantification"> quant;
52  } inputs;
53  struct
54  {
55  halp::val_port<"Out", std::optional<float>> out;
56  } outputs;
57 
58  double phase{};
59  rnd::pcg rd;
60 
61  using tick = halp::tick_flicks;
62  void operator()(const halp::tick_flicks& tk)
63  {
64  constexpr const double sine_ratio = ossia::two_pi / ossia::flicks_per_second<double>;
65  const auto elapsed = tk.model_read_duration();
66 
67  const auto quantif = inputs.quant.value;
68  auto freq = inputs.freq.value;
69  auto ampl = inputs.ampl.value;
70  const auto ampl_fine = inputs.ampl_fine.value;
71  auto offset = inputs.offset.value;
72  const auto offset_fine = inputs.offset_fine.value;
73  const auto jitter = inputs.jitter.value;
74  auto custom_phase = inputs.phase.value;
75  const auto type = inputs.waveform.value;
76 
77  if(quantif)
78  {
79  // Determine the frequency with the quantification
80  if(tk.unexpected_bar_change())
81  {
82  this->phase = 0;
83  }
84 
85  // If quantif == 1, we quantize to the bar
86  // => f = 0.5 hz
87  // If quantif == 1/4, we quantize to the quarter
88  // => f = 2hz
89  // -> sin(elapsed * freq * 2 * pi / fps)
90  // -> sin(elapsed * 4 * 2 * pi / fps)
91  freq = 1. / (2. * quantif);
92  }
93 
94  const auto ph_delta = elapsed * freq * sine_ratio;
95 
96  {
97  auto ph = this->phase;
98  if(jitter > 0)
99  {
100  ph += std::normal_distribution<float>(0., 0.25)(this->rd) * jitter;
101  }
102 
103  ampl += ampl_fine;
104  offset += offset_fine;
105 
106  using namespace Control::Widgets;
107 
108  const auto add_val
109  = [&](auto new_val) { outputs.out.value = ampl * new_val + offset; };
110 
111  custom_phase = custom_phase * ossia::pi;
112  switch(type)
113  {
114  case Sin:
115  add_val(std::sin(custom_phase + ph));
116  break;
117  case Triangle:
118  add_val(std::asin(std::sin(custom_phase + ph)) / ossia::half_pi);
119  break;
120  case Saw:
121  add_val(std::atan(std::tan(custom_phase + ph)) / ossia::half_pi);
122  break;
123  case Square:
124  add_val((std::sin(custom_phase + ph) > 0.f) ? 1.f : -1.f);
125  break;
126  case SampleAndHold: {
127  const auto start_s = std::sin(custom_phase + ph);
128  const auto end_s = std::sin(custom_phase + ph + ph_delta);
129  if((start_s > 0 && end_s <= 0) || (start_s <= 0 && end_s > 0))
130  {
131  add_val(std::uniform_real_distribution<float>(-1.f, 1.f)(this->rd));
132  }
133  break;
134  }
135  case Noise1:
136  add_val(std::uniform_real_distribution<float>(-1.f, 1.f)(this->rd));
137  break;
138  case Noise2:
139  add_val(std::normal_distribution<float>(0.f, 1.f)(this->rd));
140  break;
141  case Noise3:
142  add_val(
143  std::clamp(std::cauchy_distribution<float>(0.f, 1.f)(this->rd), 0.f, 1.f));
144  break;
145  }
146  }
147 
148  this->phase += ph_delta;
149  }
150 };
151 }
152 }
153 }
Definition: LFO.hpp:41
Definition: LFO.hpp:29
Definition: Types.hpp:48