Tabula Sonora 0.1.0
A native C++20 implementation of the Roland Sound Canvas VA synth voice
Loading...
Searching...
No Matches
lfo_engine.hpp
Go to the documentation of this file.
1#pragma once
2
7
8#include <cstdint>
9#include <span>
10#include <utility>
11#include <vector>
12
13namespace ts {
14
16enum class LfoDestination {
23};
24
26struct LfoConfig {
28 int waveform = 0;
32 int increment = 0;
34 int delay_rate = 0;
36 int fade_rate = 0;
38 int pitch_depth = 0;
40 int tvf_depth = 0;
42 int tva_depth = 0;
43
45 [[nodiscard]] constexpr int depth(LfoDestination destination) const noexcept
46 {
47 switch (destination) {
49 return pitch_depth;
51 return tvf_depth;
53 break;
54 }
55 return tva_depth;
56 }
57};
58
59class LfoRunner;
60
76class LfoEngine {
77public:
79 static constexpr double waveform_scale = 32640.0;
80
82 [[nodiscard]] static constexpr bool is_random(int waveform) noexcept
83 {
84 return waveform >= 1 && waveform <= 3;
85 }
86
88 static constexpr int slew_step = 0x50;
89
94 struct Limits {
95 int clamp = 0;
96 int rounding = 0;
97 };
98
99 [[nodiscard]] static constexpr Limits destination_limits(LfoDestination destination) noexcept
100 {
101 switch (destination) {
103 return Limits{0x7F00, 0xFFFF};
105 return Limits{0x1800, 0x8000};
107 break;
108 }
109 return Limits{6000, 0x8000};
110 }
111
118 explicit LfoEngine(const TableSet& tables, EngineNoise* noise = nullptr);
119
121 [[nodiscard]] EngineNoise& noise() const noexcept { return *noise_; }
122
127 [[nodiscard]] int waveform(int phase, int waveform_index) const noexcept;
128
134 [[nodiscard]] std::pair<LfoConfig, LfoConfig>
135 configure(int tone_number,
136 const PartialParameters& partial,
137 const PartModifiers& modifiers = {}) const;
138
140 [[nodiscard]] LfoRunner create_runner(const LfoConfig& config) const;
141
143 [[nodiscard]] std::pair<LfoRunner, LfoRunner>
144 create_runners(int tone_number,
145 const PartialParameters& partial,
146 const PartModifiers& modifiers = {}) const;
147
149 [[nodiscard]] std::vector<double>
150 run(const LfoConfig& config, int tick_count, LfoDestination destination) const;
151
157 [[nodiscard]] std::vector<double> modulation(int tone_number,
158 const PartialParameters& partial,
159 int tick_count,
160 LfoDestination destination) const;
161
166 [[nodiscard]] std::vector<double>
168 const PartialParameters& partial,
169 int tick_count,
170 std::span<const double> mod_depth_per_tick) const;
171
176 [[nodiscard]] static int
177 mod_wheel_depth(int controller, int depth = 0x0A, int offset = 0) noexcept;
178
179private:
180 [[nodiscard]] std::vector<double> run_pitch_with_wheel(
181 const LfoConfig& config, int tick_count, std::span<const double> mod_depth_per_tick) const;
182
183 std::span<const std::uint8_t> half_sine_;
184 std::span<const std::uint16_t> rate_table_;
185 std::span<const std::uint16_t> cents_table_;
186 std::span<const std::uint16_t> delay_table_;
187 std::span<const std::uint8_t> wave_map_;
188 std::span<const std::int16_t> wave_bank_;
189 std::span<const std::uint8_t> tone_;
190
191 EngineNoise* noise_;
192 EngineNoise owned_noise_;
193};
194
206public:
207 LfoRunner(const LfoEngine& engine, const LfoConfig& config)
208 : engine_(&engine), config_(config), phase_(config.initial_phase & 0xFFFF)
209 {
210 }
211
213 [[nodiscard]] const LfoConfig& config() const noexcept { return config_; }
214
220 [[nodiscard]] bool is_applied() const noexcept { return applied_; }
221
232 void tick(int rate_offset = 0) noexcept;
233
241 [[nodiscard]] double value(LfoDestination destination, int matrix_depth = 0) const noexcept;
242
248 [[nodiscard]] double pitch_value(double wheel_depth) const noexcept;
249
250private:
252 void advance_waveform(bool wrapped) noexcept;
253
254 [[nodiscard]] int faded(int depth) const noexcept;
255 [[nodiscard]] double apply(int effective, int rounding) const noexcept;
256
257 const LfoEngine* engine_;
258 LfoConfig config_;
259
260 int phase_ = 0;
261 int delay_ = 0;
262 int fade_ = 0;
263 bool applied_ = false;
264
271 int output_ = 0;
272
279 int held_ = 0;
280 int slewed_ = 0;
281};
282
283} // namespace ts
The engine's shared pseudo-random generator.
Definition engine_noise.hpp:18
The two LFO engines that modulate every partial.
Definition lfo_engine.hpp:76
static constexpr double waveform_scale
Scale that maps a raw waveform value to [-1, 1].
Definition lfo_engine.hpp:79
std::pair< LfoConfig, LfoConfig > configure(int tone_number, const PartialParameters &partial, const PartModifiers &modifiers={}) const
Reads both LFO configurations for one partial: the tone-common LFO1 and the per-partial LFO2.
LfoEngine(const TableSet &tables, EngineNoise *noise=nullptr)
Creates the engine over a loaded table set, which must outlive it.
EngineNoise & noise() const noexcept
The generator the random shapes draw from.
Definition lfo_engine.hpp:121
static int mod_wheel_depth(int controller, int depth=0x0A, int offset=0) noexcept
The mod wheel's contribution to LFO1 pitch depth, in milli-semitones.
int waveform(int phase, int waveform_index) const noexcept
Evaluates a waveform at a 16-bit phase.
LfoRunner create_runner(const LfoConfig &config) const
Creates a runner that steps one LFO a control tick at a time.
static constexpr bool is_random(int waveform) noexcept
Whether a waveform is one of the three random shapes, which the runner drives.
Definition lfo_engine.hpp:82
std::vector< double > run(const LfoConfig &config, int tick_count, LfoDestination destination) const
Runs one LFO for a number of control ticks.
std::vector< double > pitch_modulation_with_wheel(int tone_number, const PartialParameters &partial, int tick_count, std::span< const double > mod_depth_per_tick) const
Combined pitch modulation with a per-tick mod-wheel depth folded into LFO1.
static constexpr int slew_step
How far a slewed random shape moves toward its target in one control tick.
Definition lfo_engine.hpp:88
std::vector< double > modulation(int tone_number, const PartialParameters &partial, int tick_count, LfoDestination destination) const
The combined LFO1 and LFO2 modulation for a partial, per control tick.
std::pair< LfoRunner, LfoRunner > create_runners(int tone_number, const PartialParameters &partial, const PartModifiers &modifiers={}) const
Creates runners for both of a partial's LFOs.
static constexpr Limits destination_limits(LfoDestination destination) noexcept
Definition lfo_engine.hpp:99
One LFO's running state: phase, delay and fade-in accumulators, stepped a control tick at a time.
Definition lfo_engine.hpp:205
const LfoConfig & config() const noexcept
The configuration this runner steps.
Definition lfo_engine.hpp:213
double pitch_value(double wheel_depth) const noexcept
The pitch modulation with a mod-wheel depth folded in, in milli-semitones.
void tick(int rate_offset=0) noexcept
Advances one control tick.
double value(LfoDestination destination, int matrix_depth=0) const noexcept
The modulation for one destination, after the last tick.
LfoRunner(const LfoEngine &engine, const LfoConfig &config)
Definition lfo_engine.hpp:207
bool is_applied() const noexcept
Whether the last tick ran an update at all, so the LFO has an output to give.
Definition lfo_engine.hpp:220
A zero-copy typed view over one 0x6e-byte partial parameter block inside a tone record.
Definition partial_parameters.hpp:20
The static tables the engine needs, loaded from SCCore.dll or from a directory of extracted ....
Definition table_set.hpp:30
Definition control_decode.hpp:7
LfoDestination
Which parameter an LFO's depth applies to.
Definition lfo_engine.hpp:16
@ tvf
Filter cutoff, in the same units as the runtime cutoff.
Definition lfo_engine.hpp:20
@ pitch
Pitch, in milli-semitones.
Definition lfo_engine.hpp:18
@ tva
Amplitude, as a fraction of 0x7f00 applied multiplicatively.
Definition lfo_engine.hpp:22
One LFO's static configuration.
Definition lfo_engine.hpp:26
constexpr int depth(LfoDestination destination) const noexcept
The depth for one destination.
Definition lfo_engine.hpp:45
int fade_rate
Per-tick increment of the fade-in accumulator.
Definition lfo_engine.hpp:36
int tva_depth
Depth applied to amplitude.
Definition lfo_engine.hpp:42
int pitch_depth
Depth applied to pitch, in milli-semitones.
Definition lfo_engine.hpp:38
int initial_phase
Starting phase, from the top two bits of the waveform byte.
Definition lfo_engine.hpp:30
int tvf_depth
Depth applied to the filter cutoff.
Definition lfo_engine.hpp:40
int waveform
Waveform index, after the selector nibble is mapped.
Definition lfo_engine.hpp:28
int delay_rate
Per-tick increment of the delay accumulator; zero means never start.
Definition lfo_engine.hpp:34
int increment
Phase increment per control tick.
Definition lfo_engine.hpp:32
Depth clamp and rounding constant for a destination.
Definition lfo_engine.hpp:94
int rounding
Definition lfo_engine.hpp:96
int clamp
Definition lfo_engine.hpp:95
The GS part modify offsets, as the synthesis chains consume them.
Definition part_modifiers.hpp:21