35template <
typename SampleType =
float>
57 std::uint64_t& bytes)
noexcept {
59 if (!charge.
add<SampleType>(
static_cast<std::uint64_t
>(fft_size / 2 + 1)))
61 bytes = charge.
total();
69 assert(config.fft_size >= 256 && (config.fft_size & (config.fft_size - 1)) == 0);
71 num_bins_ = config.fft_size / 2 + 1;
72 prev_mag_.assign(
static_cast<size_t>(num_bins_), SampleType{0});
77 std::fill(prev_mag_.begin(), prev_mag_.end(), SampleType{0});
78 std::memset(flux_history_, 0,
sizeof(flux_history_));
81 refractory_remaining_ = 0;
87 SampleType
analyze(
const std::complex<SampleType>*
const* frames,
int channels,
int num_bins) {
88 assert(num_bins == num_bins_);
91 for (
int k = 0; k < num_bins_; ++k) {
92 std::complex<SampleType> sum(SampleType{0}, SampleType{0});
93 for (
int ch = 0; ch < channels; ++ch) sum += frames[ch][k];
94 const SampleType mag = std::abs(sum);
95 const SampleType d = mag - prev_mag_[
static_cast<size_t>(k)];
96 if (d > SampleType{0}) flux +=
static_cast<double>(d);
97 total +=
static_cast<double>(mag);
98 prev_mag_[
static_cast<size_t>(k)] = mag;
101 flux_history_[history_pos_] =
static_cast<SampleType
>(flux);
102 history_pos_ = (history_pos_ + 1) % kHistory;
103 if (history_count_ < kHistory) ++history_count_;
105 if (history_count_ < 9 || total < 1
e-9)
106 return SampleType{0};
108 SampleType sorted[kHistory];
109 std::copy(flux_history_, flux_history_ + history_count_, sorted);
110 std::nth_element(sorted, sorted + history_count_ / 2, sorted + history_count_);
111 const double median =
static_cast<double>(sorted[history_count_ / 2]);
114 const bool transient =
115 flux *
static_cast<double>(config_.sensitivity) > 3.0 * median
116 && flux > 0.05 * total;
122 if (refractory_remaining_ > 0) --refractory_remaining_;
123 if (transient && refractory_remaining_ == 0) {
124 refractory_remaining_ = config_.refractory_frames;
125 return static_cast<SampleType
>(config_.strength);
127 return SampleType{0};
131 static constexpr int kHistory = 32;
135 std::vector<SampleType> prev_mag_;
136 SampleType flux_history_[kHistory] = {};
137 int history_count_ = 0;
138 int history_pos_ = 0;
139 int refractory_remaining_ = 0;
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 transient_phase_policy.hpp:36
void reset()
Definition transient_phase_policy.hpp:76
SampleType analyze(const std::complex< SampleType > *const *frames, int channels, int num_bins)
Analyze one frame group (all channels of the same time index) and return the phase-reset confidence: ...
Definition transient_phase_policy.hpp:87
void prepare(const Config &config)
RT contract: prepare() allocates the previous-magnitude buffer and is not audio-thread safe.
Definition transient_phase_policy.hpp:68
static bool checked_retained_bytes(int fft_size, std::uint64_t target_max_bytes, std::uint64_t &bytes) noexcept
Definition transient_phase_policy.hpp:56
Definition additive_bank.hpp:218
TransientPhasePolicyT< double > TransientPhasePolicy64
Definition transient_phase_policy.hpp:143
@ e
Definition formant_filter_bank.hpp:20
TransientPhasePolicyT< float > TransientPhasePolicy
Definition transient_phase_policy.hpp:142
Definition transient_phase_policy.hpp:38
int fft_size
Definition transient_phase_policy.hpp:39
int refractory_frames
Refractory period: after a reset fires, suppress further resets for this many analysis frames.
Definition transient_phase_policy.hpp:53
float sensitivity
Definition transient_phase_policy.hpp:41
float strength
Phase-reset amount handed back on detection (0..1).
Definition transient_phase_policy.hpp:43