Deluge Firmware 1.3.0
Build date: 2025.04.16
Loading...
Searching...
No Matches
lfo.h
1/*
2 * Copyright © 2014-2023 Synthstrom Audible Limited
3 *
4 * This file is part of The Synthstrom Audible Deluge Firmware.
5 *
6 * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software Foundation,
8 * either version 3 of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 * See the GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along with this program.
15 * If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#pragma once
19
20#include "definitions_cxx.hpp"
21#include "io/debug/log.h"
22#include "model/sync.h"
23#include "util/fixedpoint.h"
24#include "util/waves.h"
25
26class LFOConfig {
27public:
28 LFOConfig() : waveType(LFOType::TRIANGLE), syncType(SYNC_TYPE_EVEN), syncLevel(SYNC_LEVEL_NONE) {}
29 LFOConfig(LFOType type) : waveType(type), syncType(SYNC_TYPE_EVEN), syncLevel(SYNC_LEVEL_NONE) {}
30 LFOType waveType;
31 SyncType syncType;
32 SyncLevel syncLevel;
33};
34
35class LFO {
36public:
37 LFO() = default;
38 uint32_t phase{0};
39 int32_t holdValue{0};
40 int32_t target{0};
41 int32_t speed{0};
42 void setLocalInitialPhase(const LFOConfig& config);
43 void setGlobalInitialPhase(const LFOConfig& config);
44 [[gnu::always_inline]] int32_t render(int32_t numSamples, LFOConfig& config, uint32_t phaseIncrement) {
45 return render(numSamples, config.waveType, phaseIncrement);
46 }
47 [[gnu::always_inline]] int32_t render(int32_t numSamples, LFOType waveType, uint32_t phaseIncrement) {
48 int32_t value;
49 switch (waveType) {
50 case LFOType::SAW:
51 value = static_cast<int32_t>(phase);
52 break;
53
54 case LFOType::SQUARE:
55 value = getSquare(phase);
56 break;
57
58 case LFOType::SINE:
59 value = getSine(phase);
60 break;
61
62 case LFOType::TRIANGLE:
63 value = getTriangle(phase);
64 break;
65
66 case LFOType::SAMPLE_AND_HOLD:
67 if ((phase == 0) || (phase + phaseIncrement * numSamples < phase)) {
68 value = CONG;
69 holdValue = value;
70 }
71 else {
72 value = holdValue;
73 }
74 break;
75
76 case LFOType::RANDOM_WALK: {
77 uint32_t range = 4294967295u / 20;
78 if (phase == 0) {
79 value = (range / 2) - CONG % range;
80 holdValue = value;
81 }
82 else if (phase + phaseIncrement * numSamples < phase) {
83 // (holdValue / -16) adds a slight bias to make the new value move
84 // back towards zero modulation the further holdValue has moved away
85 // from zero. This is probably best explained by showing the edge
86 // cases:
87 // holdValue == 8 * range => (holdValue / -16) + (range / 2) == 0 =>
88 // next holdValue <= current holdValue
89 // holdValue == 0 => (holdValue / -16) + (range / 2) == range / 2 =>
90 // equal chance for next holdValue smaller or lager than current
91 // holdValue == -8 * range => (holdValue / -16) + (range / 2) ==
92 // range => next holdValue >= current holdValuie
93 holdValue = add_saturate((holdValue / -16) + (range / 2) - CONG % range, holdValue);
94 value = holdValue;
95 }
96 else {
97 value = holdValue;
98 }
99 break;
100 }
101 case LFOType::WARBLER: {
102 phaseIncrement *= 2;
103 warble(numSamples, phaseIncrement);
104 value = holdValue;
105 }
106 }
107
108 phase += phaseIncrement * numSamples;
109 return value;
110 }
111 void warble(int32_t numSamples, uint32_t phaseIncrement) {
112 if (phase + phaseIncrement * numSamples < phase) {
113 target = CONG;
114 speed = 0;
115 }
116 // this is a second order filter - the rate that the held value approaches the target is filtered instead of
117 // the difference. It makes a nice smooth random curve since the derivative is 0 at the start and end
118 auto targetSpeed = target - holdValue;
119 speed = speed + numSamples * (multiply_32x32_rshift32(targetSpeed, phaseIncrement >> 8));
120 holdValue = add_saturate(holdValue, speed);
121 }
122
123 void tick(int32_t numSamples, uint32_t phaseIncrement) {
124 // Note: if this overflows and we're using S&H or RANDOM_WALK, the
125 // next render() won't know it has overflown. Probably not an issue.
126 phase += phaseIncrement * numSamples;
127 }
128};
Definition lfo.h:26