Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
modal_spec.hpp
Go to the documentation of this file.
1#pragma once
2
43
45
46#include <algorithm>
47#include <cmath>
48#include <cstddef>
49#include <optional>
50#include <string>
51#include <string_view>
52#include <vector>
53
54namespace pulp::signal {
55
62inline constexpr int kModalSpecSchemaVersion = 1;
63
68 double contact_s = 0.001;
69 double velocity = 1.0;
70};
71
82 int grid_points = 0;
84 std::vector<float> weights;
85
86 bool empty() const { return grid_points <= 0 || weights.empty(); }
87
90 float weight_at(int mode, float position) const {
91 if (empty()) return 1.0f;
92 const int last = grid_points - 1;
93 const std::size_t base = static_cast<std::size_t>(mode) *
94 static_cast<std::size_t>(grid_points);
95 if (base + static_cast<std::size_t>(grid_points) > weights.size()) return 1.0f;
96 if (last == 0) return weights[base];
97 const float x = std::clamp(position, 0.0f, 1.0f) * static_cast<float>(last);
98 const int i0 = std::min(static_cast<int>(x), last - 1);
99 const float frac = x - static_cast<float>(i0);
100 const float w0 = weights[base + static_cast<std::size_t>(i0)];
101 const float w1 = weights[base + static_cast<std::size_t>(i0) + 1];
102 return w0 + (w1 - w0) * frac;
103 }
104};
105
122 double freq_cents = 10.0;
124 double t60_rel = 0.08;
126 double gain_rel = 0.10;
130 double verify_seconds = 2.0;
131};
132
134struct ModalSpec {
136 std::string name;
137 std::string description;
138 std::vector<ModalMode> modes;
144
148 int contact_samples(double sample_rate) const {
149 const double n = excitation.contact_s * sample_rate;
150 return n <= 0.0 ? 0 : static_cast<int>(n + 0.5);
151 }
152};
153
161inline void resolve_modes(const ModalSpec& spec, float strike_position,
162 float pickup_position, std::vector<ModalMode>& out) {
163 out.resize(spec.modes.size());
164 for (std::size_t m = 0; m < spec.modes.size(); ++m) {
165 ModalMode mode = spec.modes[m];
166 const int mi = static_cast<int>(m);
167 mode.gain *= spec.strike_map.weight_at(mi, strike_position) *
168 spec.pickup_map.weight_at(mi, pickup_position);
169 out[m] = mode;
170 }
171}
172
174inline std::vector<ModalMode> resolve_modes(const ModalSpec& spec,
175 float strike_position = 0.5f,
176 float pickup_position = 0.5f) {
177 std::vector<ModalMode> out;
178 resolve_modes(spec, strike_position, pickup_position, out);
179 return out;
180}
181
187template <typename SampleType>
189 std::vector<ModalMode>& scratch, float strike_position = 0.5f,
190 float pickup_position = 0.5f) {
191 resolve_modes(spec, strike_position, pickup_position, scratch);
192 bank.set_modes(scratch);
193}
194
197template <typename SampleType>
199 double sample_rate, float strike_position = 0.5f,
200 float pickup_position = 0.5f) {
201 bank.prepare(sample_rate, static_cast<int>(spec.modes.size()));
202 std::vector<ModalMode> scratch;
203 load_modes(spec, bank, scratch, strike_position, pickup_position);
204}
205
210bool validate_modal_spec(const ModalSpec& spec, std::string& error);
211
216 std::string error;
220 std::vector<std::string> unknown_fields;
221};
222
244std::optional<ModalSpec> parse_modal_spec(std::string_view json,
245 ModalSpecDiagnostics* diagnostics = nullptr);
246
256std::string to_json(const ModalSpec& spec, bool pretty = true);
257
260bool operator==(const ModalSpec& a, const ModalSpec& b);
261inline bool operator!=(const ModalSpec& a, const ModalSpec& b) { return !(a == b); }
262
263} // namespace pulp::signal
Bank of two-pole modal resonators, stored structure-of-arrays in lane groups so the per-sample state ...
Definition modal_bank.hpp:154
void set_modes(std::span< const ModalMode > modes)
Load a mode set (up to the prepared max; excess modes are ignored).
Definition modal_bank.hpp:193
void prepare(double sample_rate, int max_modes, int max_pickups=1)
Allocate storage for up to max_modes modes and max_pickups listening positions at sample_rate.
Definition modal_bank.hpp:170
Definition additive_bank.hpp:218
bool validate_modal_spec(const ModalSpec &spec, std::string &error)
Check a spec's internal consistency.
bool operator!=(const ModalSpec &a, const ModalSpec &b)
Definition modal_spec.hpp:261
std::string to_json(const ModalSpec &spec, bool pretty=true)
Serialize a spec to JSON.
constexpr int kModalSpecSchemaVersion
Schema version this build writes and is the highest it can read.
Definition modal_spec.hpp:62
std::optional< ModalSpec > parse_modal_spec(std::string_view json, ModalSpecDiagnostics *diagnostics=nullptr)
Parse a ModalSpec from JSON.
void load_modes(const ModalSpec &spec, ModalBankT< SampleType > &bank, std::vector< ModalMode > &scratch, float strike_position=0.5f, float pickup_position=0.5f)
Resolve the spec at a strike/pickup position and load the result into an already-prepared bank.
Definition modal_spec.hpp:188
@ a
Definition formant_filter_bank.hpp:20
bool operator==(const ModalSpec &a, const ModalSpec &b)
Exact equality over every known field.
void resolve_modes(const ModalSpec &spec, float strike_position, float pickup_position, std::vector< ModalMode > &out)
Resolve the spec's modes at a strike/pickup position into out.
Definition modal_spec.hpp:161
void prepare_and_load(const ModalSpec &spec, ModalBankT< SampleType > &bank, double sample_rate, float strike_position=0.5f, float pickup_position=0.5f)
Prepare bank to hold this spec's modes at sample_rate and load them.
Definition modal_spec.hpp:198
One resonant mode of a vibrating object: frequency, decay time, and impulse-response amplitude.
Definition modal_bank.hpp:16
float gain
amplitude of this mode in the impulse response
Definition modal_bank.hpp:19
A mode shape sampled on a uniform grid across the object's normalized extent [0, 1],...
Definition modal_spec.hpp:80
std::vector< float > weights
grid_points weights per mode, laid out [mode * grid_points + g].
Definition modal_spec.hpp:84
int grid_points
Samples per mode across [0, 1]. Zero means "no map".
Definition modal_spec.hpp:82
bool empty() const
Definition modal_spec.hpp:86
float weight_at(int mode, float position) const
Linearly interpolated weight of mode at normalized position.
Definition modal_spec.hpp:90
What a parse found beyond the spec itself. Always populated; read it.
Definition modal_spec.hpp:213
std::vector< std::string > unknown_fields
Dotted paths of fields that were present, unrecognized, and ignored (see the unknown-field policy on ...
Definition modal_spec.hpp:220
std::string error
Empty iff the parse succeeded.
Definition modal_spec.hpp:216
How the resonator is struck.
Definition modal_spec.hpp:67
double velocity
peak of the contact pulse
Definition modal_spec.hpp:69
double contact_s
contact duration; 0 means a unit impulse
Definition modal_spec.hpp:68
The PASS criteria a render of this spec must meet.
Definition modal_spec.hpp:120
double freq_cents
Frequency error budget per mode, in cents.
Definition modal_spec.hpp:122
double verify_seconds
Impulse response length a verifier should render to check the above.
Definition modal_spec.hpp:130
double gain_rel
Amplitude error budget per mode, relative to the resolved gain.
Definition modal_spec.hpp:126
double t60_rel
T60 error budget per mode, relative (0.08 = 8%).
Definition modal_spec.hpp:124
A modal instrument, complete.
Definition modal_spec.hpp:134
std::vector< ModalMode > modes
Definition modal_spec.hpp:138
ModalSpecTolerances tolerances
Definition modal_spec.hpp:143
int schema_version
Definition modal_spec.hpp:135
ModalShapeMap strike_map
Optional. When present, must hold grid_points weights per mode.
Definition modal_spec.hpp:141
std::string name
Definition modal_spec.hpp:136
int contact_samples(double sample_rate) const
Contact duration in samples at sample_rate, for fill_strike_pulse.
Definition modal_spec.hpp:148
ModalShapeMap pickup_map
Definition modal_spec.hpp:142
ModalSpecExcitation excitation
Definition modal_spec.hpp:139
std::string description
Definition modal_spec.hpp:137