Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
spectral_gate_blur.hpp
Go to the documentation of this file.
1#pragma once
2
14
16
17#include <algorithm>
18#include <cmath>
19#include <complex>
20#include <cstddef>
21#include <cstdint>
22#include <limits>
23#include <type_traits>
24#include <vector>
25
26namespace pulp::signal {
27
28template <typename SampleType = float>
30public:
31 static_assert(std::is_floating_point_v<SampleType>);
32
35 bool set_threshold_magnitude(SampleType threshold) noexcept {
36 if (!std::isfinite(threshold) || threshold < SampleType{0}) return false;
37 threshold_magnitude_ = threshold;
38 return true;
39 }
40
41 SampleType threshold_magnitude() const noexcept { return threshold_magnitude_; }
42
48 bool process(std::complex<SampleType>* const* frames, int channels, int num_bins,
49 const SampleType* thresholds = nullptr) noexcept {
50 if (frames == nullptr || channels <= 0 || num_bins <= 0) return false;
51 for (int ch = 0; ch < channels; ++ch)
52 if (frames[ch] == nullptr) return false;
53 if (thresholds != nullptr) {
54 for (int bin = 0; bin < num_bins; ++bin) {
55 if (!std::isfinite(thresholds[bin])
56 || thresholds[bin] < SampleType{0})
57 return false;
58 }
59 }
60
61 for (int ch = 0; ch < channels; ++ch) {
62 for (int bin = 0; bin < num_bins; ++bin) {
63 auto& value = frames[ch][bin];
64 if (!std::isfinite(value.real()) || !std::isfinite(value.imag())) {
65 value = {};
66 continue;
67 }
68 const SampleType threshold =
69 thresholds != nullptr ? thresholds[bin] : threshold_magnitude_;
70 if (std::abs(value) < threshold) value = {};
71 }
72 }
73 return true;
74 }
75
76private:
77 SampleType threshold_magnitude_ = SampleType{0};
78};
79
80template <typename SampleType = float>
82public:
83 static_assert(std::is_floating_point_v<SampleType>);
84
85 static constexpr int maximum_channels = 64;
86 static constexpr int maximum_bins = 8193;
87 static constexpr int maximum_frames = 128;
88
90 std::uint64_t target_max_bytes,
91 std::uint64_t& bytes) noexcept {
95 return false;
96
97 std::uint64_t frame_elements = 0;
98 std::uint64_t history_elements = 0;
99 if (!checked_capacity_product(static_cast<std::uint64_t>(channels),
100 static_cast<std::uint64_t>(num_bins),
101 std::numeric_limits<std::uint64_t>::max(),
102 frame_elements)
103 || !checked_capacity_product(frame_elements,
104 static_cast<std::uint64_t>(blur_frames),
105 std::numeric_limits<std::uint64_t>::max(),
106 history_elements))
107 return false;
108
109 CheckedRetainedByteCharge charge(target_max_bytes);
110 if (!charge.add<SampleType>(history_elements)
111 || !charge.add<SampleType>(frame_elements)
112 || !charge.add<std::uint16_t>(frame_elements)
113 || !charge.add<std::complex<SampleType>>(frame_elements))
114 return false;
115 bytes = charge.total();
116 return true;
117 }
118
120 int blur_frames) noexcept {
121 std::uint64_t ignored = 0;
124 }
125
128 bool prepare(int channels, int num_bins, int blur_frames) {
129 std::uint64_t retained_bytes = 0;
132 return false;
133
134 const auto frame_elements = static_cast<std::size_t>(channels)
135 * static_cast<std::size_t>(num_bins);
136 std::vector<SampleType> new_history(
137 frame_elements * static_cast<std::size_t>(blur_frames), SampleType{0});
138 std::vector<SampleType> new_means(frame_elements, SampleType{0});
139 std::vector<std::uint16_t> new_nonzero_counts(frame_elements, 0);
140 std::vector<std::complex<SampleType>> new_phases(
141 frame_elements, std::complex<SampleType>{SampleType{1}, SampleType{0}});
142
143 history_.swap(new_history);
144 means_.swap(new_means);
145 nonzero_counts_.swap(new_nonzero_counts);
146 phases_.swap(new_phases);
147 channels_ = channels;
148 num_bins_ = num_bins;
149 blur_frames_ = blur_frames;
150 retained_bytes_ = retained_bytes;
151 write_frame_ = 0;
152 filled_frames_ = 0;
153 return true;
154 }
155
157 void reset() noexcept {
158 std::fill(history_.begin(), history_.end(), SampleType{0});
159 std::fill(means_.begin(), means_.end(), SampleType{0});
160 std::fill(nonzero_counts_.begin(), nonzero_counts_.end(), std::uint16_t{0});
161 std::fill(phases_.begin(), phases_.end(),
162 std::complex<SampleType>{SampleType{1}, SampleType{0}});
163 write_frame_ = 0;
164 filled_frames_ = 0;
165 }
166
171 bool process(std::complex<SampleType>* const* frames, int channels,
172 int num_bins) noexcept {
173 if (channels != channels_ || num_bins != num_bins_ || frames == nullptr
174 || blur_frames_ <= 0)
175 return false;
176 for (int ch = 0; ch < channels_; ++ch)
177 if (frames[ch] == nullptr) return false;
178
179 const auto frame_elements = static_cast<std::size_t>(channels_)
180 * static_cast<std::size_t>(num_bins_);
181 const auto history_offset = static_cast<std::size_t>(write_frame_)
182 * frame_elements;
183 const bool history_full = filled_frames_ >= blur_frames_;
184 const auto divisor = static_cast<SampleType>(
185 std::min(filled_frames_ + 1, blur_frames_));
186
187 for (int ch = 0; ch < channels_; ++ch) {
188 for (int bin = 0; bin < num_bins_; ++bin) {
189 const auto index = static_cast<std::size_t>(ch)
190 * static_cast<std::size_t>(num_bins_)
191 + static_cast<std::size_t>(bin);
192 auto& value = frames[ch][bin];
193 SampleType magnitude = SampleType{0};
194 if (std::isfinite(value.real()) && std::isfinite(value.imag())) {
195 const SampleType real = value.real();
196 const SampleType imag = value.imag();
197 const SampleType scale = std::max(std::abs(real), std::abs(imag));
198 if (scale > SampleType{0}) {
199 const SampleType scaled_real = real / scale;
200 const SampleType scaled_imag = imag / scale;
201 const SampleType scaled_magnitude =
202 std::hypot(scaled_real, scaled_imag);
203 phases_[index] = {scaled_real / scaled_magnitude,
204 scaled_imag / scaled_magnitude};
205 const SampleType maximum =
206 std::numeric_limits<SampleType>::max();
207 magnitude = scale > maximum / scaled_magnitude
208 ? maximum
209 : scale * scaled_magnitude;
210 }
211 }
212
213 auto& old_magnitude = history_[history_offset + index];
214 if (old_magnitude > SampleType{0}) --nonzero_counts_[index];
215 if (magnitude > SampleType{0}) ++nonzero_counts_[index];
216 auto& mean = means_[index];
217 if (history_full)
218 mean = std::fma(SampleType{1} / divisor,
219 magnitude - old_magnitude, mean);
220 else
221 mean += (magnitude - mean) / divisor;
222 if (nonzero_counts_[index] == 0)
223 mean = SampleType{0};
224 else if (!std::isfinite(mean))
225 mean = std::numeric_limits<SampleType>::max();
226 old_magnitude = magnitude;
227 value = phases_[index] * std::max(mean, SampleType{0});
228 }
229 }
230
231 write_frame_ = (write_frame_ + 1) % blur_frames_;
232 if (filled_frames_ < blur_frames_) ++filled_frames_;
233 return true;
234 }
235
236 int channels() const noexcept { return channels_; }
237 int num_bins() const noexcept { return num_bins_; }
238 int blur_frames() const noexcept { return blur_frames_; }
239 int filled_frames() const noexcept { return filled_frames_; }
240 std::uint64_t retained_bytes() const noexcept { return retained_bytes_; }
241
242private:
243 int channels_ = 0;
244 int num_bins_ = 0;
245 int blur_frames_ = 0;
246 int write_frame_ = 0;
247 int filled_frames_ = 0;
248 std::uint64_t retained_bytes_ = 0;
249 std::vector<SampleType> history_;
250 std::vector<SampleType> means_;
251 std::vector<std::uint16_t> nonzero_counts_;
252 std::vector<std::complex<SampleType>> phases_;
253};
254
259
260} // namespace pulp::signal
Definition checked_allocation.hpp:57
bool add(std::uint64_t elements) noexcept
Definition checked_allocation.hpp:63
std::uint64_t total() const noexcept
Definition checked_allocation.hpp:83
Definition spectral_gate_blur.hpp:81
static constexpr int maximum_channels
Definition spectral_gate_blur.hpp:85
bool prepare(int channels, int num_bins, int blur_frames)
Allocate fixed history off the audio thread.
Definition spectral_gate_blur.hpp:128
int filled_frames() const noexcept
Definition spectral_gate_blur.hpp:239
static bool checked_retained_bytes(int channels, int num_bins, int blur_frames, std::uint64_t target_max_bytes, std::uint64_t &bytes) noexcept
Definition spectral_gate_blur.hpp:89
void reset() noexcept
Clear the finite history without changing prepared capacity.
Definition spectral_gate_blur.hpp:157
int blur_frames() const noexcept
Definition spectral_gate_blur.hpp:238
std::uint64_t retained_bytes() const noexcept
Definition spectral_gate_blur.hpp:240
bool process(std::complex< SampleType > *const *frames, int channels, int num_bins) noexcept
Apply a causal box blur across at most blur_frames() analysis frames.
Definition spectral_gate_blur.hpp:171
static bool supports_configuration(int channels, int num_bins, int blur_frames) noexcept
Definition spectral_gate_blur.hpp:119
int num_bins() const noexcept
Definition spectral_gate_blur.hpp:237
static constexpr int maximum_frames
Definition spectral_gate_blur.hpp:87
int channels() const noexcept
Definition spectral_gate_blur.hpp:236
static constexpr int maximum_bins
Definition spectral_gate_blur.hpp:86
Definition spectral_gate_blur.hpp:29
bool set_threshold_magnitude(SampleType threshold) noexcept
Set the scalar linear-magnitude threshold.
Definition spectral_gate_blur.hpp:35
bool process(std::complex< SampleType > *const *frames, int channels, int num_bins, const SampleType *thresholds=nullptr) noexcept
Gate a coherent channel group in place.
Definition spectral_gate_blur.hpp:48
SampleType threshold_magnitude() const noexcept
Definition spectral_gate_blur.hpp:41
Definition additive_bank.hpp:218
SpectralFrameBlurT< double > SpectralFrameBlur64
Definition spectral_gate_blur.hpp:258
bool checked_capacity_product(std::uint64_t lhs, std::uint64_t rhs, std::uint64_t maximum, std::uint64_t &product) noexcept
Definition checked_allocation.hpp:12
SpectralFrameBlurT< float > SpectralFrameBlur
Definition spectral_gate_blur.hpp:257
@ maximum
Definition spectrum_trace.hpp:34
constexpr std::uint64_t kTargetAddressMaximumBytes
Definition checked_allocation.hpp:9
SpectralGateT< double > SpectralGate64
Definition spectral_gate_blur.hpp:256
SpectralGateT< float > SpectralGate
Definition spectral_gate_blur.hpp:255