27template <
typename SampleType =
float>
29 const auto n = dst.size();
31 if (n == 1) { dst[0] = velocity;
return; }
32 constexpr SampleType two_pi = SampleType(6.28318530717958647692);
33 const SampleType phase_step = two_pi /
static_cast<SampleType
>(n);
34 for (std::size_t
i = 0;
i < n; ++
i) {
35 dst[
i] = velocity * SampleType(0.5)
36 * (SampleType(1) - std::cos(phase_step *
static_cast<SampleType
>(
i)));
68 constexpr double pi = 3.14159265358979323846;
69 return static_cast<float>(std::sin(pi *
static_cast<double>(shape.half_waves)
70 *
static_cast<double>(position)
71 +
static_cast<double>(shape.end_phase)));
79 for (std::size_t
i = 0;
i < dst.size(); ++
i)
88template <
typename SampleType =
float>
90 std::span<const ModalShape> shapes,
float position) {
91 const std::size_t n = std::min(dst.size(), shapes.size());
92 for (std::size_t
i = 0;
i < n; ++
i)
94 for (std::size_t
i = n;
i < dst.size(); ++
i) dst[
i] = SampleType{0};
153template <
typename SampleType =
float>
181 pickup_.resize(padded_ *
static_cast<std::size_t
>(max_pickups_));
182 std::fill(pickup_.begin(), pickup_.end(), SampleType{1});
194 const int count =
static_cast<int>(
195 std::min<std::size_t>(modes.size(),
static_cast<std::size_t
>(max_modes_)));
196 const double fs = sample_rate_;
197 for (
int m = 0; m < count; ++m) {
198 const auto i =
static_cast<std::size_t
>(m);
199 const double freq = std::clamp(
static_cast<double>(modes[
i].freq_hz),
201 const double t60 = std::max(
static_cast<double>(modes[
i].t60_s), 1.0e-3);
202 const double r = std::pow(10.0, -3.0 / (t60 * fs));
203 const double w = 2.0 * 3.14159265358979323846 * freq / fs;
204 const double cos_w = std::cos(w);
205 const double sin_w = std::sin(w);
206 const double gain =
static_cast<double>(modes[
i].gain);
207 rc_[
i] =
static_cast<SampleType
>(r * cos_w);
208 rs_[
i] =
static_cast<SampleType
>(r * sin_w);
209 cu_[
i] =
static_cast<SampleType
>(gain * cos_w);
210 cv_[
i] =
static_cast<SampleType
>(gain * sin_w);
214 for (std::size_t m =
static_cast<std::size_t
>(count); m < padded_; ++m) {
215 cu_[m] = SampleType{0};
216 cv_[m] = SampleType{0};
217 rc_[m] = SampleType{0};
218 rs_[m] = SampleType{0};
227 if (channel < 0 || channel >= max_pickups_)
return;
228 const std::size_t base =
static_cast<std::size_t
>(channel) * padded_;
229 const std::size_t n = std::min(gains.size(), padded_);
230 for (std::size_t m = 0; m < n; ++m) pickup_[base + m] = gains[m];
237 void process_add(
const SampleType* input, SampleType* output,
int num_samples,
239 SampleType* outs[1] = {output};
240 const int c = std::clamp(channel, 0, max_pickups_ - 1);
241 run(input, outs, &
c, 1, num_samples);
249 void process_add(
const SampleType* input, SampleType*
const* outputs,
250 int num_channels,
int num_samples) {
252 const int n = std::clamp(num_channels, 0, max_pickups_);
253 for (
int c = 0;
c < n; ++
c) channels[
c] =
c;
254 run(input, outputs, channels, n, num_samples);
270 void run(
const SampleType* input, SampleType*
const* outputs,
271 const int* channels,
int num_channels,
int num_samples) {
272 if (num_channels <= 0)
return;
274 for (
int g = 0; g < groups; ++g) {
275 const std::size_t base =
static_cast<std::size_t
>(g) *
kLanes;
279 for (
int l = 0; l <
kLanes; ++l) {
280 const std::size_t
i = base +
static_cast<std::size_t
>(l);
288 for (
int c = 0;
c < num_channels; ++
c) {
289 const std::size_t pbase =
290 static_cast<std::size_t
>(channels[
c]) * padded_ + base;
291 for (
int l = 0; l <
kLanes; ++l) pick[
c][l] = pickup_[pbase + l];
293 for (
int i = 0;
i < num_samples; ++
i) {
294 const SampleType x = input[
i];
296 for (
int l = 0; l <
kLanes; ++l) {
297 const SampleType nu = rc[l] *
u[l] - rs[l] * v[l] + cu[l] * x;
298 const SampleType nv = rs[l] *
u[l] + rc[l] * v[l] + cv[l] * x;
303 for (
int c = 0;
c < num_channels; ++
c) {
305 for (
int l = 0;
l <
kLanes; ++
l) t[l] = pick[
c][l] * y[l];
308 for (
int step =
kLanes / 2; step > 0; step /= 2)
309 for (
int l = 0;
l < step; ++
l) t[l] += t[l + step];
310 outputs[
c][
i] +=
t[0];
314 const std::size_t
i = base +
static_cast<std::size_t
>(
l);
321 AlignedBufferT<SampleType> cu_, cv_, rc_, rs_, u_, v_, pickup_;
322 double sample_rate_ = 48000.0;
323 std::size_t padded_ = 0;
325 int max_pickups_ = 1;
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 set_pickup_gains(int channel, std::span< const SampleType > gains)
Set how loudly pickup channel hears each mode.
Definition modal_bank.hpp:226
void process_add(const SampleType *input, SampleType *const *outputs, int num_channels, int num_samples)
Excite and run the bank into num_channels outputs, one per pickup: pickup c accumulates into outputs[...
Definition modal_bank.hpp:249
void reset()
Zero all resonator state (call on discontinuities / re-strike from silence).
Definition modal_bank.hpp:259
static constexpr int kMaxPickups
Definition modal_bank.hpp:164
int max_modes() const
Definition modal_bank.hpp:265
static constexpr int kLanes
Definition modal_bank.hpp:158
double sample_rate() const
Definition modal_bank.hpp:267
void process_add(const SampleType *input, SampleType *output, int num_samples, int channel=0)
Excite and run the bank into one output: adds pickup channel's view of the summed mode outputs for nu...
Definition modal_bank.hpp:237
int max_pickups() const
Definition modal_bank.hpp:266
int mode_count() const
Definition modal_bank.hpp:264
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
void fill_ideal_string_shapes(std::span< ModalShape > dst)
Fill dst with the mode shapes of an ideal string fixed at both ends: dst[i] describes the (i+1)-th tr...
Definition modal_bank.hpp:78
@ c
Definition spectrum_trace.hpp:35
void fill_mode_weights(std::span< SampleType > dst, std::span< const ModalShape > shapes, float position)
Sample every shape in shapes at position, writing phi_m(position) to dst.
Definition modal_bank.hpp:89
float mode_shape_at(const ModalShape &shape, float position)
Evaluate a mode's shape at a normalized position along the body.
Definition modal_bank.hpp:67
ModalBankT< double > ModalBank64
Definition modal_bank.hpp:330
T snap_to_zero(T x)
Returns 0 if |x| < snap_threshold, else x.
Definition denormal.hpp:45
@ u
Definition formant_filter_bank.hpp:20
@ i
Definition formant_filter_bank.hpp:20
void fill_strike_pulse(std::span< SampleType > dst, SampleType velocity)
Fill dst with a raised-cosine contact pulse, peak-normalized to velocity.
Definition modal_bank.hpp:28
ModalBankT< float > ModalBank
Definition modal_bank.hpp:329
@ l
Definition input_events.hpp:40
@ t
Definition input_events.hpp:41
One resonant mode of a vibrating object: frequency, decay time, and impulse-response amplitude.
Definition modal_bank.hpp:16
float t60_s
time for this mode to decay by 60 dB
Definition modal_bank.hpp:18
float gain
amplitude of this mode in the impulse response
Definition modal_bank.hpp:19
float freq_hz
Definition modal_bank.hpp:17
The spatial eigenfunction of one mode along a body's normalized length coordinate x in [0,...
Definition modal_bank.hpp:56
float half_waves
number of half-wavelengths across the body
Definition modal_bank.hpp:57
float end_phase
0 puts a node at x = 0; pi/2 an antinode
Definition modal_bank.hpp:58