41 static_assert(std::is_floating_point_v<SampleType>);
63 std::array<StageConfig, kMaxStages>
stages{{
85 static_cast<double>(std::numeric_limits<std::size_t>::max() / (2u *
kMaxStages)) ||
86 required >
static_cast<double>(std::numeric_limits<int>::max()))
88 const auto capacity =
static_cast<std::size_t
>(required);
90 DerivedConfig replacement_derived;
94 std::vector<SampleType> replacement;
95#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
98 replacement.assign(2u *
kMaxStages * capacity, SampleType{});
99#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
100 }
catch (
const std::bad_alloc&) {
102 }
catch (
const std::length_error&) {
107 storage_ = std::move(replacement);
110 capacity_ = capacity;
111 derived_ = replacement_derived;
114 derived_.latency_samples = calculate_latency_samples();
119 [[nodiscard]]
bool configure(
const Config& candidate)
noexcept {
120 if (!valid_config_shape(candidate))
126 DerivedConfig replacement;
127 if (!derive_config(candidate, sample_rate_, maximum_delay_ms_, capacity_, replacement))
130 derived_ = replacement;
132 derived_.latency_samples = calculate_latency_samples();
144 return maximum_delay_ms_;
151 if (stage >= config_.stage_count || channel >= 2u)
153 return channel == 0
u ? derived_.left_delay[stage] : derived_.right_delay[stage];
157 return storage_.size() *
sizeof(SampleType);
163 return prepared_ ? derived_.latency_samples : 0;
170 if (!prepared_ || config_.stage_count == 0
u)
173 for (std::size_t
i = 0;
i < config_.stage_count; ++
i) {
174 if (config_.stages[
i].gain != 0.0)
176 finite_tail += std::max(derived_.left_delay[
i], derived_.right_delay[
i]);
182 for (
auto& positions : write_positions_)
183 positions = {0
u, 0
u};
184 for (
auto& valid : valid_samples_)
188 void process_sample(SampleType input_left, SampleType input_right, SampleType& output_left,
189 SampleType& output_right)
noexcept {
190 output_left = SampleType{};
191 output_right = SampleType{};
194 if (!finite(input_left) || !finite(input_right)) {
199 SampleType
left = input_left;
200 SampleType
right = input_right;
201 for (std::size_t stage = 0; stage < config_.stage_count; ++stage) {
202 if (!process_stage(stage,
left,
right)) {
211 void process_block(
const SampleType* input_left,
const SampleType* input_right,
212 SampleType* output_left, SampleType* output_right,
213 std::size_t sample_count)
noexcept {
214 if (input_left ==
nullptr || input_right ==
nullptr || output_left ==
nullptr ||
215 output_right ==
nullptr)
217 for (std::size_t
i = 0;
i < sample_count; ++
i) {
218 const SampleType
left = input_left[
i];
219 const SampleType
right = input_right[
i];
225 struct DerivedConfig {
226 std::array<int, kMaxStages> left_delay{};
227 std::array<int, kMaxStages> right_delay{};
228 SampleType rotation_cos = SampleType{1};
229 SampleType rotation_sin = SampleType{};
230 int latency_samples = 0;
233 static constexpr std::array<int, kMaxStages> kRightOffsets{{3, 5, 7, 11, 13, 17, 19, 23}};
241 static bool valid_config_shape(
const Config& candidate)
noexcept {
247 for (std::size_t
i = 0;
i <
candidate.stage_count; ++
i) {
249 const double magnitude = std::abs(stage.gain);
250 if (!std::isfinite(stage.delay_ms) || stage.delay_ms <= 0.0 ||
260 std::size_t capacity, DerivedConfig& result)
noexcept {
261 if (!valid_config_shape(candidate))
263 const int maximum_delay =
static_cast<int>(capacity - 1u);
264 for (std::size_t
i = 0;
i <
candidate.stage_count; ++
i) {
266 if (!std::isfinite(stage.delay_ms) || stage.delay_ms < 1000.0 /
sample_rate ||
270 std::clamp(
static_cast<int>(std::llround(stage.delay_ms *
sample_rate / 1000.0)), 1,
272 const int offset = kRightOffsets[
i];
274 if (
left + offset <= maximum_delay)
276 else if (
left - offset >= 1)
278 result.left_delay[
i] =
left;
279 result.right_delay[
i] =
right;
281 const double angle =
candidate.stereo_width * std::numbers::pi / 4.0;
282 result.rotation_cos =
static_cast<SampleType
>(std::cos(angle));
283 result.rotation_sin =
static_cast<SampleType
>(std::sin(angle));
284 return finite(result.rotation_cos) && finite(result.rotation_sin);
287 static bool finite(SampleType value)
noexcept {
288 return std::isfinite(
static_cast<double>(value));
291 int calculate_latency_samples() noexcept {
292 int direct_path_horizon = 0;
293 for (std::size_t stage = 0; stage < config_.stage_count; ++stage) {
294 if (config_.stages[stage].gain == 0.0) {
295 direct_path_horizon +=
296 std::max(derived_.left_delay[stage], derived_.right_delay[stage]);
300 int earliest = std::numeric_limits<int>::max();
301 for (std::size_t input_channel = 0; input_channel < 2u; ++input_channel) {
303 for (
int sample = 0; sample <= direct_path_horizon; ++sample) {
306 const SampleType impulse = sample == 0 ? SampleType{1} : SampleType{};
308 input_channel == 1u ? impulse : SampleType{},
left,
right);
309 if (
left != SampleType{} ||
right != SampleType{}) {
310 earliest = std::min(earliest, sample);
317 return earliest == std::numeric_limits<int>::max() ? 0 : earliest;
320 std::size_t base_index(std::size_t stage, std::size_t channel)
const noexcept {
321 return (2u * stage + channel) * capacity_;
324 SampleType delayed(std::size_t stage, std::size_t channel,
int delay)
const noexcept {
325 if (
static_cast<std::size_t
>(
delay) > valid_samples_[stage][channel])
327 const auto position = write_positions_[stage][channel];
328 const auto read_position =
329 (position + capacity_ -
static_cast<std::size_t
>(
delay)) % capacity_;
330 return storage_[base_index(stage, channel) + read_position];
333 bool process_stage(std::size_t stage, SampleType&
left, SampleType&
right)
noexcept {
334 const SampleType delayed_left = delayed(stage, 0
u, derived_.left_delay[stage]);
335 const SampleType delayed_right = delayed(stage, 1u, derived_.right_delay[stage]);
336 const long double gain =
static_cast<long double>(config_.stages[stage].gain);
337 const long double write_left = detail::schroeder_allpass_write(
338 static_cast<long double>(
left),
static_cast<long double>(delayed_left), gain);
339 const long double write_right = detail::schroeder_allpass_write(
340 static_cast<long double>(
right),
static_cast<long double>(delayed_right), gain);
341 const long double allpass_left =
342 detail::schroeder_allpass_output(
static_cast<long double>(delayed_left), write_left,
344 const long double allpass_right =
345 detail::schroeder_allpass_output(
static_cast<long double>(delayed_right), write_right,
347 const long double limit =
static_cast<long double>(std::numeric_limits<SampleType>::max());
348 if (!std::isfinite(write_left) || !std::isfinite(write_right) ||
349 !std::isfinite(allpass_left) || !std::isfinite(allpass_right) ||
350 std::abs(write_left) > limit || std::abs(write_right) > limit ||
351 std::abs(allpass_left) > limit || std::abs(allpass_right) > limit)
354 const auto left_position = write_positions_[stage][0];
355 const auto right_position = write_positions_[stage][1];
356 storage_[base_index(stage, 0
u) + left_position] =
358 storage_[base_index(stage, 1u) + right_position] =
360 write_positions_[stage][0] = (left_position + 1u) % capacity_;
361 write_positions_[stage][1] = (right_position + 1u) % capacity_;
362 valid_samples_[stage][0] = std::min(valid_samples_[stage][0] + 1u, capacity_);
363 valid_samples_[stage][1] = std::min(valid_samples_[stage][1] + 1u, capacity_);
365 const long double c =
static_cast<long double>(derived_.rotation_cos);
366 const long double s =
static_cast<long double>(derived_.rotation_sin);
367 const long double sign = (stage & 1u) == 0
u ? 1.0L : -1.0L;
368 const long double rotated_left =
c * allpass_left - sign *
s * allpass_right;
369 const long double rotated_right = sign *
s * allpass_left +
c * allpass_right;
370 if (!std::isfinite(rotated_left) || !std::isfinite(rotated_right) ||
371 std::abs(rotated_left) > limit || std::abs(rotated_right) > limit)
373 left =
static_cast<SampleType
>(rotated_left);
374 right =
static_cast<SampleType
>(rotated_right);
379 DerivedConfig derived_{};
380 std::vector<SampleType> storage_{};
381 std::array<std::array<std::size_t, 2>,
kMaxStages> write_positions_{};
382 std::array<std::array<std::size_t, 2>,
kMaxStages> valid_samples_{};
383 double sample_rate_ = 48000.0;
384 double maximum_delay_ms_ = 50.0;
385 std::size_t capacity_ = 0
u;
386 bool prepared_ =
false;