63template <
typename SampleType =
float>
73template <
typename SampleType =
float>
77 std::uint64_t target_max_bytes,
78 std::uint64_t& bytes)
noexcept {
79 const auto bins =
static_cast<std::uint64_t
>(config.num_bins);
80 std::uint64_t history = 0;
85 if (!charge.
add<SampleType>(history)
86 || !charge.
add<SampleType>(bins) || !charge.
add<SampleType>(bins)
87 || !charge.
add<SampleType>(bins) || !charge.
add<SampleType>(bins)
88 || !charge.
add<SampleType>(bins)
89 || !charge.
add<SampleType>(
static_cast<std::uint64_t
>(config.time_median))
90 || !charge.
add<SampleType>(
static_cast<std::uint64_t
>(config.freq_median)))
92 bytes = charge.
total();
101 assert(config.num_bins > 0);
102 assert(config.time_median >= 1 && (config.time_median % 2) == 1);
103 assert(config.freq_median >= 1 && (config.freq_median % 2) == 1);
105 const auto bins =
static_cast<size_t>(config.num_bins);
107 history_.assign(
static_cast<size_t>(config.time_median) * bins, SampleType{0});
111 masks_.sines.assign(bins, SampleType{0});
112 masks_.transients.assign(bins, SampleType{0});
113 masks_.noise.assign(bins, SampleType{1});
115 harmonic_.assign(bins, SampleType{0});
116 percussive_.assign(bins, SampleType{0});
117 time_window_.assign(
static_cast<size_t>(config.time_median), SampleType{0});
118 freq_window_.assign(
static_cast<size_t>(config.freq_median), SampleType{0});
131 const int n = config_.num_bins;
132 const int th = config_.time_median, fh = config_.freq_median;
135 SampleType* slot = history_.data() +
static_cast<size_t>(history_pos_) * n;
136 std::copy(mag, mag + n, slot);
137 history_pos_ = (history_pos_ + 1) % th;
138 if (history_filled_ < th) ++history_filled_;
145 const int center = config_.causal
146 ? (history_pos_ - 1 + th) % th
147 : (history_pos_ + th / 2) % th;
148 const SampleType* center_mag = history_.data() +
static_cast<size_t>(
center) * n;
149 for (
int k = 0; k < n; ++k) {
151 for (
int t = 0; t < history_filled_; ++t)
152 time_window_[
static_cast<size_t>(count++)] =
153 history_[
static_cast<size_t>(t) * n + k];
154 harmonic_[
static_cast<size_t>(k)] = median(time_window_.data(), count);
158 for (
int k = 0; k < n; ++k) {
160 for (
int j = k - fh / 2; j <= k + fh / 2; ++j) {
161 const int kk = std::clamp(j, 0, n - 1);
162 freq_window_[
static_cast<size_t>(count++)] = center_mag[kk];
164 percussive_[
static_cast<size_t>(k)] = median(freq_window_.data(), count);
168 const SampleType beta =
static_cast<SampleType
>(config_.beta);
169 for (
int k = 0; k < n; ++k) {
170 const SampleType h = harmonic_[
static_cast<size_t>(k)];
171 const SampleType pc = percussive_[
static_cast<size_t>(k)];
172 const SampleType total = h + pc +
static_cast<SampleType
>(1
e-12);
175 const SampleType s_raw = saturate(h, beta * pc);
176 const SampleType t_raw = saturate(pc, beta * h);
177 const SampleType s = s_raw;
178 const SampleType t = t_raw * (SampleType{1} - s);
179 const SampleType nse = std::max(SampleType{0}, SampleType{1} - s - t);
181 masks_.sines[
static_cast<size_t>(k)] = s;
182 masks_.transients[
static_cast<size_t>(k)] = t;
183 masks_.noise[
static_cast<size_t>(k)] = nse;
191 const int th = config_.time_median;
192 const int center = config_.causal
193 ? (history_pos_ - 1 + th) % th
194 : (history_pos_ + th / 2) % th;
195 return history_.data() +
static_cast<size_t>(
center) * config_.num_bins;
199 return config_.causal ? 0 : (config_.time_median - 1) / 2;
203 std::fill(history_.begin(), history_.end(), SampleType{0});
211 static SampleType saturate(SampleType
a, SampleType b) {
212 const SampleType denom =
a + b +
static_cast<SampleType
>(1
e-12);
213 const SampleType r =
a / denom;
215 const SampleType x = std::clamp((r -
static_cast<SampleType
>(0.25))
216 /
static_cast<SampleType
>(0.5),
217 SampleType{0}, SampleType{1});
218 return x * x * (
static_cast<SampleType
>(3) -
static_cast<SampleType
>(2) * x);
221 static SampleType median(SampleType* v,
int count) {
222 const int mid =
count / 2;
223 std::nth_element(v, v + mid, v + count);
228 std::vector<SampleType> history_;
229 int history_pos_ = 0;
230 int history_filled_ = 0;
232 StnMasksT<SampleType> masks_;
233 std::vector<SampleType> harmonic_;
234 std::vector<SampleType> percussive_;
235 std::vector<SampleType> time_window_;
236 std::vector<SampleType> freq_window_;
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 stn_decomposer.hpp:74
int latency_frames() const
Definition stn_decomposer.hpp:198
const SampleType * center_magnitude() const
The center-frame magnitude the current masks apply to (delayed by (time_median-1)/2 frames vs the new...
Definition stn_decomposer.hpp:190
int num_bins() const
Definition stn_decomposer.hpp:121
const StnMasksT< SampleType > & process(const SampleType *mag)
Push one magnitude frame and compute its STN masks.
Definition stn_decomposer.hpp:130
void reset()
Definition stn_decomposer.hpp:202
void prepare(const StnConfig &config)
RT contract: prepare() allocates history, scratch, and mask storage and is not audio-thread safe.
Definition stn_decomposer.hpp:100
static bool checked_retained_bytes(const StnConfig &config, std::uint64_t target_max_bytes, std::uint64_t &bytes) noexcept
Definition stn_decomposer.hpp:76
@ count
sentinel: number of real capabilities (keep LAST)
Definition canvas_capability.hpp:38
Definition additive_bank.hpp:218
@ center
Definition multi_channel_meter.hpp:68
StnDecomposerT< double > StnDecomposer64
Definition stn_decomposer.hpp:240
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
@ a
Definition formant_filter_bank.hpp:20
@ e
Definition formant_filter_bank.hpp:20
StnDecomposerT< float > StnDecomposer
Definition stn_decomposer.hpp:239
StnMasksT< double > StnMasks64
Definition stn_decomposer.hpp:71
StnMasksT< float > StnMasks
Definition stn_decomposer.hpp:70
@ v
Definition input_events.hpp:41
Definition stn_decomposer.hpp:39
float beta
Soft-mask separation factor (Fitzgerald β): the bin is assigned to a class when that class's filtered...
Definition stn_decomposer.hpp:46
int freq_median
odd; bins for the vertical (transient) median
Definition stn_decomposer.hpp:42
bool causal
Align the masks to the NEWEST pushed frame instead of the ring's center frame.
Definition stn_decomposer.hpp:59
int num_bins
fft_size/2 + 1
Definition stn_decomposer.hpp:40
int time_median
odd; frames for the horizontal (tonal) median
Definition stn_decomposer.hpp:41
Per-frame soft masks. Each spans num_bins and sums to ~1 per bin.
Definition stn_decomposer.hpp:64
std::vector< float > noise
Definition stn_decomposer.hpp:67
std::vector< float > transients
Definition stn_decomposer.hpp:66
std::vector< float > sines
Definition stn_decomposer.hpp:65