Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
waveguide_line.hpp
Go to the documentation of this file.
1#pragma once
2
5
8
9#include <algorithm>
10#include <cmath>
11#include <cstddef>
12#include <limits>
13#include <type_traits>
14#include <utility>
15
16namespace pulp::signal {
17
33template <typename SampleType = float> class WaveguideLineT {
34 static_assert(std::is_floating_point_v<SampleType>);
35
36 public:
37 static constexpr double minimum_length_samples = 3.0;
38 static constexpr double minimum_max_length_seconds = 0.001;
39 static constexpr double maximum_max_length_seconds = 1.0;
40 static constexpr double default_max_length_seconds = 0.05;
41 static constexpr double retune_seconds = 0.005;
42
43 [[nodiscard]] bool prepare(double sample_rate,
44 double max_length_seconds = default_max_length_seconds) {
45 if (!std::isfinite(sample_rate) || !(sample_rate > 0.0) ||
46 !std::isfinite(max_length_seconds) ||
47 max_length_seconds < minimum_max_length_seconds ||
48 max_length_seconds > maximum_max_length_seconds)
49 return false;
50
51 const auto requested = std::ceil(sample_rate * max_length_seconds);
52 if (!std::isfinite(requested) || requested < minimum_length_samples ||
53 requested >= static_cast<double>(std::numeric_limits<std::size_t>::max()))
54 return false;
55
56 const auto maximum_delay = static_cast<std::size_t>(requested);
57 FractionalDelayHistoryT<double> new_right_going;
59 if (!new_right_going.prepare(maximum_delay) || !new_left_going.prepare(maximum_delay))
60 return false;
61
62 right_going_ = std::move(new_right_going);
63 left_going_ = std::move(new_left_going);
64 sample_rate_ = sample_rate;
65 maximum_length_samples_ = static_cast<double>(maximum_delay);
66 target_length_samples_ = minimum_length_samples;
67 length_.set_ramp_time(retune_seconds, sample_rate_);
68 length_.set_immediate(target_length_samples_);
69 length_configured_ = false;
70 prepared_ = true;
71 return true;
72 }
73
74 void set_length_samples(SampleType length) noexcept {
75 if (!prepared_ || !std::isfinite(static_cast<double>(length)))
76 return;
77 target_length_samples_ = std::clamp(static_cast<double>(length), minimum_length_samples,
78 maximum_length_samples_);
79 if (!length_configured_) {
80 length_.set_immediate(target_length_samples_);
81 length_configured_ = true;
82 } else {
83 length_.set_target(target_length_samples_);
84 }
85 }
86
87 [[nodiscard]] SampleType length_samples() const noexcept {
88 return static_cast<SampleType>(target_length_samples_);
89 }
90
93 [[nodiscard]] SampleType current_length_samples() const noexcept {
94 return static_cast<SampleType>(prepared_ ? length_.current()
95 : target_length_samples_);
96 }
97
98 [[nodiscard]] double round_trip_seconds() const noexcept {
99 return prepared_ ? 2.0 * length_.current() / sample_rate_ : 0.0;
100 }
101
102 [[nodiscard]] bool prepared() const noexcept { return prepared_; }
103
110 void read_outputs(SampleType& left_out, SampleType& right_out) const noexcept {
111 left_out = SampleType{};
112 right_out = SampleType{};
113 if (!prepared_)
114 return;
115
116 const auto delay = length_.current();
117 const auto at_right = right_going_.read_lagrange5_at(delay);
118 const auto at_left = left_going_.read_lagrange5_at(delay);
119 if (at_right)
120 right_out = finite_cast(at_right.sample);
121 if (at_left)
122 left_out = finite_cast(at_left.sample);
123 }
124
126 void push_inputs(SampleType left_in, SampleType right_in) noexcept {
127 if (!prepared_)
128 return;
129 (void)right_going_.push(static_cast<double>(left_in));
130 (void)left_going_.push(static_cast<double>(right_in));
131 (void)length_.next();
132 }
133
139 void process(SampleType left_in, SampleType right_in, SampleType& left_out,
140 SampleType& right_out) noexcept {
141 read_outputs(left_out, right_out);
142 push_inputs(left_in, right_in);
143 }
144
145 void reset() noexcept {
146 right_going_.reset();
147 left_going_.reset();
148 length_.set_immediate(target_length_samples_);
149 }
150
151 [[nodiscard]] std::size_t retained_bytes() const noexcept {
152 return right_going_.retained_bytes() + left_going_.retained_bytes();
153 }
154
155 private:
156 [[nodiscard]] static SampleType finite_cast(double value) noexcept {
157 const auto limit = static_cast<double>(std::numeric_limits<SampleType>::max());
158 if (!std::isfinite(value) || value > limit || value < -limit)
159 return SampleType{};
160 return static_cast<SampleType>(value);
161 }
162
163 FractionalDelayHistoryT<double> right_going_{};
164 FractionalDelayHistoryT<double> left_going_{};
165 SmoothedValue<double> length_{minimum_length_samples};
166 double sample_rate_ = 0.0;
167 double maximum_length_samples_ = 0.0;
168 double target_length_samples_ = minimum_length_samples;
169 bool length_configured_ = false;
170 bool prepared_ = false;
171};
172
175
176} // namespace pulp::signal
Prepared history shared by any number of stateless Lagrange read heads.
Definition fractional_delay.hpp:161
bool prepare(std::size_t maximum_delay_samples)
Definition fractional_delay.hpp:179
A passive bidirectional waveguide segment.
Definition waveguide_line.hpp:33
static constexpr double minimum_max_length_seconds
Definition waveguide_line.hpp:38
void set_length_samples(SampleType length) noexcept
Definition waveguide_line.hpp:74
bool prepared() const noexcept
Definition waveguide_line.hpp:102
void push_inputs(SampleType left_in, SampleType right_in) noexcept
Injects both boundary results and advances the retuning glide once.
Definition waveguide_line.hpp:126
void process(SampleType left_in, SampleType right_in, SampleType &left_out, SampleType &right_out) noexcept
Convenience step for feed-forward use: read, then push.
Definition waveguide_line.hpp:139
static constexpr double minimum_length_samples
Definition waveguide_line.hpp:37
static constexpr double maximum_max_length_seconds
Definition waveguide_line.hpp:39
bool prepare(double sample_rate, double max_length_seconds=default_max_length_seconds)
Definition waveguide_line.hpp:43
SampleType current_length_samples() const noexcept
Returns the delay head used by the next sample.
Definition waveguide_line.hpp:93
void reset() noexcept
Definition waveguide_line.hpp:145
static constexpr double default_max_length_seconds
Definition waveguide_line.hpp:40
static constexpr double retune_seconds
Definition waveguide_line.hpp:41
SampleType length_samples() const noexcept
Definition waveguide_line.hpp:87
std::size_t retained_bytes() const noexcept
Definition waveguide_line.hpp:151
double round_trip_seconds() const noexcept
Definition waveguide_line.hpp:98
void read_outputs(SampleType &left_out, SampleType &right_out) const noexcept
Reads both boundary arrivals without advancing either rail.
Definition waveguide_line.hpp:110
Prepared fractional-delay storage with Thiran-1 and Lagrange-3/5 reads.
Definition additive_bank.hpp:218
WaveguideLineT< float > WaveguideLine
Definition waveguide_line.hpp:173
@ delay
Definition envelope.hpp:57
WaveguideLineT< double > WaveguideLine64
Definition waveguide_line.hpp:174