39 halp_meta(name,
"LFO")
40 halp_meta(c_name,
"LFO")
41 halp_meta(category,
"Control/Generators")
42 halp_meta(author,
"ossia score")
43 halp_meta(manual_url,
"https://ossia.io/score-docs/processes/lfo.html#lfo")
44 halp_meta(description,
"Low-frequency oscillator")
45 halp_meta(recommended_height, 130.)
46 halp_meta(uuid,
"0b1b1816-c33e-4796-a16d-5aab27fe600f");
51 halp::time_chooser<
"Period", halp::range{0.01, 60., 1.}>
period;
56 halp::knob_f32<
"Shape", halp::range{0., 1., 0.5}>
shape;
59 halp::knob_f32<
"Ampl.", halp::range{0., 2., 0.5}> ampl;
60 halp::knob_f32<
"Offset", halp::range{-1., 1., 0.5}> offset;
63 halp::knob_f32<
"Jitter", halp::range{0., 180., 0.}>
jitter;
67 halp::knob_f32<
"Phase", halp::range{0., 360., 0.}>
phase;
68 struct : halp::enum_t<Waveform,
"Waveform">
70 static constexpr auto pixmaps()
72 return std::array<const char*, 24>{
73 ":/icons/wave_sin_off.png",
74 ":/icons/wave_sin_on.png",
75 ":/icons/wave_triangle_off.png",
76 ":/icons/wave_triangle_on.png",
77 ":/icons/wave_saw_off.png",
78 ":/icons/wave_saw_on.png",
79 ":/icons/wave_ramp_down_off.png",
80 ":/icons/wave_ramp_down_on.png",
81 ":/icons/wave_square_ticks_off.png",
82 ":/icons/wave_square_ticks_on.png",
83 ":/icons/wave_square_changes_off.png",
84 ":/icons/wave_square_changes_on.png",
85 ":/icons/wave_sample_and_hold_ticks_off.png",
86 ":/icons/wave_sample_and_hold_ticks_on.png",
87 ":/icons/wave_sample_and_hold_changes_off.png",
88 ":/icons/wave_sample_and_hold_changes_on.png",
89 ":/icons/wave_noise1_off.png",
90 ":/icons/wave_noise1_on.png",
91 ":/icons/wave_noise2_off.png",
92 ":/icons/wave_noise2_on.png",
93 ":/icons/wave_noise3_off.png",
94 ":/icons/wave_noise3_on.png",
95 ":/icons/wave_drift_off.png",
96 ":/icons/wave_drift_on.png"};
107 struct : halp::val_port<
"Out", std::optional<float>>
125 rnd::pcg rd{random_source()};
126 uint32_t seed{uint32_t(rd())};
133 static double frac(
double x)
noexcept {
return x - std::floor(x); }
140 const double r = std::clamp(rise, 0.02, 0.98);
141 const double u = frac(x + 0.25);
142 return u < r ? 0.5 * u / r : 0.5 + 0.5 * (u - r) / (1. - r);
144 static double sine(
double x,
double rise)
noexcept
148 static double triangle(
double x,
double rise)
noexcept
151 return w < 0.5 ? -1. + 4. * w : 3. - 4. * w;
154 static double saw(
double x,
double shape)
noexcept
156 const double e = std::exp2((0.5 - std::clamp(shape, 0., 1.)) * 4.);
157 return 2. * std::pow(frac(x + 0.5), e) - 1.;
160 static int step(
double x,
double duty)
noexcept
162 return frac(x) < std::clamp(duty, 0., 1.) ? 1 : -1;
166 static float lattice(int64_t i, uint32_t seed)
noexcept
168 uint64_t h = uint64_t(i) * 0x9E3779B97F4A7C15ull ^ (uint64_t(seed) << 17);
170 h *= 0xBF58476D1CE4E5B9ull;
172 return float(h >> 40) / float(1 << 23) - 1.f;
176 static float drift(
double cycles,
double roughness, uint32_t seed)
noexcept
178 auto octave = [&](
double t, uint32_t sd) {
179 const double f = std::floor(t);
180 const double u = t - f;
181 const double k = u * u * (3. - 2. * u);
182 const float a =
lattice(int64_t(f), sd), b =
lattice(int64_t(f) + 1, sd);
183 return a + (b - a) *
float(k);
185 const double r = std::clamp(roughness, 0., 1.);
186 const float v = octave(cycles, seed) + float(r) * 0.5f * octave(cycles * 2.3, seed ^ 0x5bd1e995u)
187 + float(r * r) * 0.25f * octave(cycles * 5.1, seed ^ 0x27d4eb2du);
188 return std::clamp(v /
float(1. + 0.5 * r + 0.25 * r * r), -1.f, 1.f);
191 using tick = halp::tick_flicks;
192 void operator()(
const halp::tick_flicks& tk)
194 const auto ampl = inputs.ampl.value;
195 const auto offset = inputs.offset.value;
196 const auto jitter = inputs.
jitter.value;
197 const auto type = inputs.waveform.value;
198 const double shape = inputs.
shape.value;
199 const double period = std::max(1e-4, (
double)inputs.
period.value);
200 const bool sync = inputs.
period.sync;
210 seed = uint32_t(rd());
212 else if(sync && !locked && tk.unexpected_bar_change())
224 const double quarters_per_cycle = period * tk.tempo / 60.;
228 if(quarters_per_cycle > 0.)
230 c0 = (tk.start_position_in_quarters -
sync_origin) / quarters_per_cycle;
231 c1 = (tk.end_position_in_quarters -
sync_origin) / quarters_per_cycle;
236 c0 = double(cycles) +
phase;
239 + double(tk.model_read_duration())
240 / (period * ossia::flicks_per_second<double>);
241 else if(quarters_per_cycle > 0.)
243 + (tk.end_position_in_quarters - tk.start_position_in_quarters)
244 / quarters_per_cycle;
249 double shift = inputs.
phase.value;
251 shift += std::uniform_real_distribution<float>(-jitter, jitter)(this->rd);
255 const double x = frac(c0);
260 = [&](
auto new_val) { outputs.out.value = ampl * new_val + offset; };
263 const auto crossed = [&] {
264 return std::floor(c0) != std::floor(c1) ||
step(c0, shape) !=
step(c1, shape);
270 add_val(sine(x, shape));
273 add_val(triangle(x, shape));
276 add_val(
saw(x, shape));
279 add_val(-
saw(x, shape));
282 add_val(
float(
step(x, shape)));
284 case SquareOnChange: {
285 const int s =
step(x, shape);
292 if(!held_valid || crossed())
294 held = std::uniform_real_distribution<float>(-1.f, 1.f)(this->rd);
299 case SampleAndHoldEveryTick:
300 if(!held_valid || crossed())
302 held = std::uniform_real_distribution<float>(-1.f, 1.f)(this->rd);
308 add_val(std::uniform_real_distribution<float>(-1.f, 1.f)(this->rd));
311 add_val(std::clamp(std::normal_distribution<float>(0.f, 0.4f)(this->rd), -1.f, 1.f));
314 add_val(std::clamp(std::cauchy_distribution<float>(0.f, 0.1f)(this->rd), -1.f, 1.f));
317 add_val(
drift(c0, shape, seed));
325 const double end = locked ? c1 - shift :
phase + (c1 - c0);
326 const double whole = std::floor(end);
328 cycles = locked ? int64_t(whole) : cycles + int64_t(whole);
334 halp_meta(layout, halp::layouts::vbox)
337 halp_meta(layout, halp::layouts::hbox)
340 halp_meta(layout, halp::layouts::vbox)
341 halp_meta(background, halp::colors::background_mid)
342 halp::control<&ins::waveform> w;
347 halp_meta(layout, halp::layouts::vbox)
348 halp_meta(background, halp::colors::background_mid)
351 halp_meta(layout, halp::layouts::hbox)
352 halp::control<&ins::period> t;
353 halp::control<&ins::ampl> a;
354 halp::control<&ins::shape> s;
358 halp_meta(layout, halp::layouts::hbox)
359 halp::control<&ins::phase> p;
360 halp::control<&ins::offset> o;
361 halp::control<&ins::jitter> j;
368 halp_meta(layout, halp::layouts::hbox)
369 halp_meta(background, halp::colors::background_mid)
370 halp::control<&ins::retrigger> r;
371 halp::control<&ins::lock_to_bars> l;
halp::time_chooser<"Period", halp::range{0.01, 60., 1.}> period
One cycle: in seconds, or synced to a note value.
Definition LFO_v3.hpp:51
static float lattice(int64_t i, uint32_t seed) noexcept
A random value in [-1; 1] for each integer, the same every time.
Definition LFO_v3.hpp:166
static double saw(double x, double shape) noexcept
One ramp a cycle, 0 at x = 0; bent by the shape: linear at 0.5.
Definition LFO_v3.hpp:154
static int step(double x, double duty) noexcept
The two steps of a cycle: the first until duty, the second after.
Definition LFO_v3.hpp:160
static float drift(double cycles, double roughness, uint32_t seed) noexcept
Smooth value noise: one wander per cycle, finer detail by roughness.
Definition LFO_v3.hpp:176