Tabula Sonora 0.1.0
A native C++20 implementation of the Roland Sound Canvas VA synth voice
Loading...
Searching...
No Matches
tvf_chain.hpp
Go to the documentation of this file.
1#pragma once
2
9
10#include <array>
11#include <cstdint>
12#include <span>
13#include <vector>
14
15namespace ts {
16
28class TvfChain {
29public:
31 static constexpr int control_block_samples = 320;
32
34 struct Offsets {
35 int peak = 0;
36 std::array<int, 4> segments{};
37 int release = 0;
38 };
39
45
47 struct Coefficients {
48 double frequency = 0.0;
49 double damping = 0.0;
50 };
51
54 TvfChain(const TableSet& tables, const EnvelopeMachine& envelope);
55
61 [[nodiscard]] FilterTap tap(int filter_type) const noexcept;
62
64 [[nodiscard]] static int resonance_byte(const PartialParameters& partial,
65 int part_resonance = 0x40,
66 int part_resonance_default = 0x40) noexcept;
67
72 [[nodiscard]] int cutoff_units(double cutoff15, int resonance_byte) const noexcept;
73
81 [[nodiscard]] double frequency_coefficient(int units) const noexcept;
82
87 [[nodiscard]] double
88 damping_coefficient(int units, int resonance_byte, int filter_type) const noexcept;
89
102 [[nodiscard]] Coefficients
103 coefficients(int units, int resonance_byte, int filter_type) const noexcept;
104
107 [[nodiscard]] std::span<const std::int32_t> ramp_exp() const noexcept { return ramp_exp_; }
108
110 [[nodiscard]] double
111 cutoff_hz(double cutoff15, int resonance_byte, int sample_rate = 32000) const noexcept;
112
121 [[nodiscard]] int effective_velocity(const PartialParameters& partial,
122 int velocity) const noexcept;
123
125 [[nodiscard]] Offsets
126 envelope_offsets(const PartialParameters& partial, int key, int velocity) const;
127
136 [[nodiscard]] Envelope create_envelope(const PartialParameters& partial,
137 int velocity,
138 int key,
139 int sample_rate = 32000,
140 const PartModifiers& modifiers = {}) const;
141
147 [[nodiscard]] static bool responds_to_env_modifiers(const PartialParameters& partial) noexcept
148 {
149 return (partial.raw()[0x0E] & 0x10) != 0;
150 }
151
153 [[nodiscard]] std::vector<double> envelope(const PartialParameters& partial,
154 int velocity,
155 int key,
156 double hold_seconds,
157 double tail_seconds,
158 int sample_rate = 32000) const;
159
168 void apply(std::span<float> signal,
169 std::span<const double> cutoff15,
170 int filter_type,
171 int resonance_byte,
172 int block_samples = control_block_samples) const noexcept;
173
174private:
175 // The env-depth region is addressed by absolute VA in the decompile. Relative to the start of
176 // the cached slice (VA 0x1819a2f00) the two sub-tables sit here:
177 // 0x1819a3028 - 0x1819a2f00 = 0x128 (depth below neutral)
178 // 0x1819a2fa8 - 0x1819a2f00 = 0x0a8 (depth at or above neutral)
179 static constexpr int env_depth_low_branch = 0x128;
180 static constexpr int env_depth_high_branch = 0x0A8;
181
182 // g_pitch_bias lives inside the pitch-envelope export: 0x1819a2890 - 0x1819a2578 = 0x318.
183 static constexpr int pitch_bias_offset = 0x318;
184
185 [[nodiscard]] int env_depth_word(int offset) const;
186 [[nodiscard]] int pitch_bias(int magnitude) const;
187
189 [[nodiscard]] int damping_raw(int units, int resonance_byte, int filter_type) const noexcept;
190
191 const EnvelopeMachine* envelope_machine_;
192 std::span<const std::uint8_t> env_depth_;
193 std::span<const std::uint8_t> pitch_env_;
194 std::span<const std::int16_t> env_depth_key_follow_;
195 std::span<const std::int16_t> reso_curve_;
196 std::span<const std::uint8_t> rate_key_follow_;
197 std::span<const std::uint32_t> filter_type_coef_;
198 std::span<const std::uint16_t> warp_;
199 std::span<const std::uint16_t> ceiling_;
200 std::span<const std::uint16_t> q_low_pass_;
201 std::span<const std::uint16_t> q_type6_;
202 std::span<const std::int32_t> ramp_exp_;
203 std::span<const float> f_ceiling_;
204 std::span<const std::uint8_t> velocity_sensitivity_;
205};
206
207} // namespace ts
The segment-rate and segment-shape machine shared by the TVA, TVF and pitch envelopes.
Definition envelope_machine.hpp:22
A zero-copy typed view over one 0x6e-byte partial parameter block inside a tone record.
Definition partial_parameters.hpp:20
A four-segment envelope with a release, evaluated at any sample position.
Definition segment_envelope.hpp:21
The static tables the engine needs, loaded from SCCore.dll or from a directory of extracted ....
Definition table_set.hpp:30
void apply(std::span< float > signal, std::span< const double > cutoff15, int filter_type, int resonance_byte, int block_samples=control_block_samples) const noexcept
Filters a signal in place with a cutoff that moves per control block.
int effective_velocity(const PartialParameters &partial, int velocity) const noexcept
The velocity the filter envelope's depth actually responds to.
double cutoff_hz(double cutoff15, int resonance_byte, int sample_rate=32000) const noexcept
The cutoff in Hz. Diagnostic only — the filter never needs it.
int cutoff_units(double cutoff15, int resonance_byte) const noexcept
Warps a 15-bit cutoff sum and clamps it to the resonance-dependent ceiling — stage C.
static constexpr int control_block_samples
Samples between coefficient refreshes — the 100 Hz control tick.
Definition tvf_chain.hpp:31
static int resonance_byte(const PartialParameters &partial, int part_resonance=0x40, int part_resonance_default=0x40) noexcept
The resonance byte — stage A. Floored at 4.
FilterTap tap(int filter_type) const noexcept
Which filter response a partial takes, or FilterTap::bypass.
Coefficients coefficients(int units, int resonance_byte, int filter_type) const noexcept
Both coefficients at once, with f clamped to the stability ceiling q selects.
std::vector< double > envelope(const PartialParameters &partial, int velocity, int key, double hold_seconds, double tail_seconds, int sample_rate=32000) const
The 15-bit cutoff trajectory over a note, clamped to 15 bits.
static bool responds_to_env_modifiers(const PartialParameters &partial) noexcept
Whether this partial's filter envelope follows the part's envelope modify offsets.
Definition tvf_chain.hpp:147
double frequency_coefficient(int units) const noexcept
The filter's frequency coefficient f.
Offsets envelope_offsets(const PartialParameters &partial, int key, int velocity) const
The envelope's peak and its five stage offsets.
std::span< const std::int32_t > ramp_exp() const noexcept
The shared g_ramp_exp_tbl.
Definition tvf_chain.hpp:107
double damping_coefficient(int units, int resonance_byte, int filter_type) const noexcept
The filter's damping coefficient q — stage D.
Envelope create_envelope(const PartialParameters &partial, int velocity, int key, int sample_rate=32000, const PartModifiers &modifiers={}) const
Builds the cutoff envelope for one note, ready to be evaluated at any sample position.
TvfChain(const TableSet &tables, const EnvelopeMachine &envelope)
Creates the chain over a loaded table set and the shared machine, both of which must outlive it.
Definition control_decode.hpp:7
FilterTap
Which output of the state-variable filter a partial takes.
Definition state_variable_filter.hpp:6
The GS part modify offsets, as the synthesis chains consume them.
Definition part_modifiers.hpp:21
The pair of coefficients the filter runs on for one control block.
Definition tvf_chain.hpp:47
double damping
Definition tvf_chain.hpp:49
double frequency
Definition tvf_chain.hpp:48
A cutoff envelope and the base cutoff its offsets are added to.
Definition tvf_chain.hpp:41
SegmentEnvelope offsets
Definition tvf_chain.hpp:42
int base_cutoff
Definition tvf_chain.hpp:43
The envelope's peak and its five stage offsets, in cutoff units relative to that peak.
Definition tvf_chain.hpp:34
int peak
Definition tvf_chain.hpp:35
int release
Definition tvf_chain.hpp:37
std::array< int, 4 > segments
Definition tvf_chain.hpp:36