Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
path_latency_aligner.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "detail/audio_range.hpp"
4
5#include <algorithm>
6#include <array>
7#include <cstddef>
8#include <limits>
9#include <memory>
10#include <new>
11#include <span>
12#include <type_traits>
13#include <vector>
14
15namespace pulp::signal {
16
26template <typename SampleType = float, std::size_t MaxPaths = 16, std::size_t MaxChannels = 8,
27 typename Allocator = std::allocator<SampleType>>
29 public:
30 static_assert(MaxPaths > 0 && MaxChannels > 0);
31 static_assert(std::is_floating_point_v<SampleType>);
32 static constexpr std::size_t max_paths = MaxPaths;
33 static constexpr std::size_t max_channels = MaxChannels;
34
35 explicit PathLatencyAlignerT(const Allocator& allocator = Allocator{}) : rings_(allocator) {}
36
37 bool prepare(std::size_t paths, std::size_t channels, std::size_t max_latency_samples,
38 std::size_t max_block_size) {
39 if (paths == 0 || paths > MaxPaths || channels == 0 || channels > MaxChannels ||
40 max_block_size == 0 || max_latency_samples == std::numeric_limits<std::size_t>::max())
41 return false;
42 const auto stride = max_latency_samples + 1;
43 if (paths > std::numeric_limits<std::size_t>::max() / channels)
44 return false;
45 const auto streams = paths * channels;
46 if (streams > std::numeric_limits<std::size_t>::max() / stride)
47 return false;
48 const auto retained = streams * stride;
49 if (retained > rings_.max_size())
50 return false;
51 std::vector<SampleType, Allocator> replacement(rings_.get_allocator());
52#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
53 try {
54#endif
55 replacement.assign(retained, SampleType{0});
56#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
57 } catch (const std::bad_alloc&) {
58 return false;
59 }
60#endif
61 rings_.swap(replacement);
62 paths_ = paths;
63 channels_ = channels;
64 max_latency_ = max_latency_samples;
65 max_block_size_ = max_block_size;
66 stride_ = stride;
67 latencies_.fill(0);
68 write_positions_.fill(0);
69 reported_latency_ = 0;
70 return true;
71 }
72
73 bool configure_latencies(std::span<const std::size_t> latencies) noexcept {
74 if (latencies.size() != paths_ || rings_.empty())
75 return false;
76 for (const auto latency : latencies)
77 if (latency > max_latency_)
78 return false;
79 reported_latency_ = 0;
80 for (std::size_t path = 0; path < paths_; ++path) {
81 latencies_[path] = latencies[path];
82 reported_latency_ = std::max(reported_latency_, latencies[path]);
83 }
84 reset();
85 return true;
86 }
87
88 void reset() noexcept {
89 std::fill(rings_.begin(), rings_.end(), SampleType{0});
90 write_positions_.fill(0);
91 }
92
93 std::size_t reported_latency_samples() const noexcept {
94 return reported_latency_;
95 }
96 std::size_t path_latency_samples(std::size_t path) const noexcept {
97 return path < paths_ ? latencies_[path] : 0;
98 }
99 std::size_t compensation_delay_samples(std::size_t path) const noexcept {
100 return path < paths_ ? reported_latency_ - latencies_[path] : 0;
101 }
102 std::size_t path_count() const noexcept {
103 return paths_;
104 }
105 std::size_t channel_count() const noexcept {
106 return channels_;
107 }
108 std::size_t retained_samples() const noexcept {
109 return rings_.size();
110 }
111
112 bool process(const SampleType* const* inputs, std::size_t input_count,
113 SampleType* const* outputs, std::size_t output_count,
114 std::size_t frames) noexcept {
115 const auto stream_count = paths_ * channels_;
116 if (inputs == nullptr || outputs == nullptr || input_count != stream_count ||
117 output_count != stream_count || frames > max_block_size_ || rings_.empty())
118 return false;
119 for (std::size_t stream = 0; stream < stream_count; ++stream) {
120 if (inputs[stream] == nullptr || outputs[stream] == nullptr)
121 return false;
122 for (std::size_t other = 0; other < stream_count; ++other) {
123 if (other == stream) {
124 if (outputs[stream] != inputs[stream] &&
125 detail::audio_ranges_overlap(outputs[stream], inputs[stream], frames))
126 return false;
127 } else if (detail::audio_ranges_overlap(outputs[stream], inputs[other], frames)) {
128 return false;
129 }
130 if (other > stream &&
131 detail::audio_ranges_overlap(outputs[stream], outputs[other], frames))
132 return false;
133 }
134 }
135
136 for (std::size_t frame = 0; frame < frames; ++frame) {
137 for (std::size_t path = 0; path < paths_; ++path) {
138 const auto delay = compensation_delay_samples(path);
139 const auto position = write_positions_[path];
140 for (std::size_t channel = 0; channel < channels_; ++channel) {
141 const auto stream = path * channels_ + channel;
142 const auto input = inputs[stream][frame];
143 if (delay == 0) {
144 outputs[stream][frame] = input;
145 } else {
146 auto* ring = rings_.data() + stream * stride_;
147 outputs[stream][frame] = ring[position];
148 ring[position] = input;
149 }
150 }
151 if (delay > 0)
152 write_positions_[path] = (position + 1) % delay;
153 }
154 }
155 return true;
156 }
157
158 private:
159 std::size_t paths_ = 0;
160 std::size_t channels_ = 0;
161 std::size_t max_latency_ = 0;
162 std::size_t max_block_size_ = 0;
163 std::size_t stride_ = 0;
164 std::size_t reported_latency_ = 0;
165 std::array<std::size_t, MaxPaths> latencies_{};
166 std::array<std::size_t, MaxPaths> write_positions_{};
167 std::vector<SampleType, Allocator> rings_;
168};
169
172
173} // namespace pulp::signal
Aligns N already-processed paths to the largest declared intrinsic latency.
Definition path_latency_aligner.hpp:28
std::size_t channel_count() const noexcept
Definition path_latency_aligner.hpp:105
std::size_t retained_samples() const noexcept
Definition path_latency_aligner.hpp:108
std::size_t reported_latency_samples() const noexcept
Definition path_latency_aligner.hpp:93
void reset() noexcept
Definition path_latency_aligner.hpp:88
PathLatencyAlignerT(const Allocator &allocator=Allocator{})
Definition path_latency_aligner.hpp:35
bool process(const SampleType *const *inputs, std::size_t input_count, SampleType *const *outputs, std::size_t output_count, std::size_t frames) noexcept
Definition path_latency_aligner.hpp:112
bool prepare(std::size_t paths, std::size_t channels, std::size_t max_latency_samples, std::size_t max_block_size)
Definition path_latency_aligner.hpp:37
std::size_t path_count() const noexcept
Definition path_latency_aligner.hpp:102
std::size_t compensation_delay_samples(std::size_t path) const noexcept
Definition path_latency_aligner.hpp:99
static constexpr std::size_t max_paths
Definition path_latency_aligner.hpp:32
bool configure_latencies(std::span< const std::size_t > latencies) noexcept
Definition path_latency_aligner.hpp:73
static constexpr std::size_t max_channels
Definition path_latency_aligner.hpp:33
std::size_t path_latency_samples(std::size_t path) const noexcept
Definition path_latency_aligner.hpp:96
Definition additive_bank.hpp:218
@ delay
Definition envelope.hpp:57
PathLatencyAlignerT< double > PathLatencyAligner64
Definition path_latency_aligner.hpp:171
PathLatencyAlignerT< float > PathLatencyAligner
Definition path_latency_aligner.hpp:170