37#include "../Core/AudioBuffer.h"
38#include "../Core/AudioSpec.h"
39#include "../Core/DspMath.h"
40#include "../Core/SpectralProcessor.h"
41#include "../Core/StateBlob.h"
78 prepared_.store(
false, std::memory_order_relaxed);
80 stft_.prepare(spec, fftSize, fftSize / 4);
81 numBins_ = stft_.getNumBins();
83 profile_.assign(
static_cast<size_t>(numBins_), 0.0f);
84 gains_.assign(
static_cast<size_t>(numChannels_),
85 std::vector<float>(
static_cast<size_t>(numBins_), 1.0f));
86 smooth_.assign(
static_cast<size_t>(numBins_), 1.0f);
88 prepared_.store(
true, std::memory_order_relaxed);
95 if (!prepared_.load(std::memory_order_relaxed))
return;
97 for (
auto& g : gains_)
98 std::fill(g.begin(), g.end(), 1.0f);
105 std::fill(profile_.begin(), profile_.end(), 0.0f);
113 learning_.store(learning, std::memory_order_relaxed);
120 if (!std::isfinite(db))
return;
121 reduction_.store(std::clamp(db, T(0), T(40)), std::memory_order_relaxed);
128 if (!std::isfinite(factor))
return;
129 threshold_.store(std::clamp(factor, T(1), T(8)), std::memory_order_relaxed);
132 [[nodiscard]]
bool getLearning() const noexcept {
return learning_.load(std::memory_order_relaxed); }
133 [[nodiscard]] T
getReduction() const noexcept {
return reduction_.load(std::memory_order_relaxed); }
134 [[nodiscard]] T
getThreshold() const noexcept {
return threshold_.load(std::memory_order_relaxed); }
137 [[nodiscard]]
int getLatency() const noexcept {
return stft_.getLatency(); }
141 [[nodiscard]] std::vector<uint8_t>
getState()
const
146 w.
write(
"reduction",
static_cast<float>(reduction_.load(std::memory_order_relaxed)));
147 w.
write(
"threshold",
static_cast<float>(threshold_.load(std::memory_order_relaxed)));
166 if (!prepared_.load(std::memory_order_relaxed))
return;
168 const bool learning = learning_.load(std::memory_order_relaxed);
169 const float floorGain = std::pow(
170 10.0f, -
static_cast<float>(reduction_.load(std::memory_order_relaxed)) / 20.0f);
171 const float thresh =
static_cast<float>(threshold_.load(std::memory_order_relaxed));
180 const int nChEff = std::max(1, std::min(buffer.getNumChannels(), numChannels_));
183 stft_.processBlock(buffer, [
this, learning, floorGain, thresh, nChEff](T* bins,
int numBins)
185 auto& chGain = gains_[
static_cast<size_t>(callCounter_ % nChEff)];
189 for (
int k = 0; k < numBins; ++k)
191 const float re =
static_cast<float>(bins[2 * k]);
192 const float im =
static_cast<float>(bins[2 * k + 1]);
193 const float mag = std::sqrt(re * re + im * im);
198 auto& p = profile_[
static_cast<size_t>(k)];
199 p = std::max(p * 0.995f + mag * 0.005f, std::max(p, mag * 0.8f));
202 const float open = profile_[
static_cast<size_t>(k)] * thresh;
203 smooth_[
static_cast<size_t>(k)] = (mag > open) ? 1.0f : floorGain;
208 for (
int k = 0; k < numBins; ++k)
210 const float a = smooth_[
static_cast<size_t>(std::max(k - 1, 0))];
211 const float b = smooth_[
static_cast<size_t>(k)];
212 const float c = smooth_[
static_cast<size_t>(std::min(k + 1, numBins - 1))];
213 float target = (a + b + c) * (1.0f / 3.0f);
216 auto& g = chGain[
static_cast<size_t>(k)];
217 g = (target > g) ? target : (g * 0.85f + target * 0.15f);
219 bins[2 * k] =
static_cast<T
>(
static_cast<float>(bins[2 * k]) * g);
220 bins[2 * k + 1] =
static_cast<T
>(
static_cast<float>(bins[2 * k + 1]) * g);
227 int numChannels_ = 0;
229 std::atomic<bool> prepared_ {
false };
231 std::vector<float> profile_;
232 std::vector<std::vector<float>> gains_;
233 std::vector<float> smooth_;
234 int callCounter_ = 0;
236 std::atomic<bool> learning_ {
false };
237 std::atomic<T> reduction_ { T(18) };
238 std::atomic<T> threshold_ { T(2) };
Non-owning view over audio channel data.
Learn-a-profile spectral gate (hiss/hum/room-tone reduction).
void setThreshold(T factor) noexcept
Gate threshold over the learned profile [1, 8] (default 2). Non-finite values are ignored.
void setReduction(T db) noexcept
Maximum attenuation of gated bins in dB [0, 40] (default 18). Non-finite values are ignored.
bool setState(const uint8_t *data, size_t size)
Restores parameters from a blob (tolerant; rejects foreign ids).
T getReduction() const noexcept
T getThreshold() const noexcept
std::vector< uint8_t > getState() const
Serializes the parameter state (the learned profile is material- dependent content,...
void processBlock(AudioBufferView< T > buffer) noexcept
Processes a block in-place. Pass-through until prepare() succeeds.
void clearProfile() noexcept
Forgets the learned noise profile (stream-owner thread).
int getLatency() const noexcept
Latency in samples (the STFT pipeline's).
bool getLearning() const noexcept
void prepare(const AudioSpec &spec, int fftSize=2048)
Prepares the STFT pipeline and the per-channel bin state.
void reset() noexcept
Clears signal state and per-bin gain memories (keeps profile).
void setLearning(bool learning) noexcept
While true, incoming audio trains the noise profile.
High-performance STFT analysis-modification-synthesis pipeline.
Tolerant reader: missing keys yield defaults, unknown keys are skipped.
float read(const char *key, float defaultValue) const
Reads a float, or defaultValue when the key is absent.
bool isValid() const noexcept
uint32_t processorId() const noexcept
Serializes key/value parameters into a versioned blob.
std::vector< uint8_t > blob() const
Finalizes and returns the blob.
void write(const char *key, float value)
Writes a float parameter.
Main namespace for the DSPark framework.
constexpr uint32_t stateId(const char(&tag)[5]) noexcept
Builds a FOURCC processor id, e.g. dspark::stateId("COMP").
Describes the audio environment for a DSP processor.
constexpr bool isValid() const noexcept
Checks if the specification contains valid, processable parameters.
int numChannels
Number of audio channels (e.g., 1 = mono, 2 = stereo).