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 <random>
16namespace Nodes::LFO::v2
17{
18struct 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 MIDISync.hpp:126