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