39#include "../Core/AudioBuffer.h"
40#include "../Core/AudioSpec.h"
41#include "../Core/DspMath.h"
42#include "../Core/SpectralProcessor.h"
43#include "../Core/StateBlob.h"
80 prepared_.store(
false, std::memory_order_relaxed);
82 stft_.prepare(spec, fftSize, fftSize / 4);
83 numBins_ = stft_.getNumBins();
85 profile_.assign(
static_cast<size_t>(numBins_), 0.0f);
87 cleanPower_.assign(
static_cast<size_t>(numChannels_),
88 std::vector<float>(
static_cast<size_t>(numBins_), 0.0f));
90 prepared_.store(
true, std::memory_order_relaxed);
97 if (!prepared_.load(std::memory_order_relaxed))
return;
99 for (
auto& c : cleanPower_)
100 std::fill(c.begin(), c.end(), 0.0f);
107 std::fill(profile_.begin(), profile_.end(), 0.0f);
116 learning_.store(learning, std::memory_order_relaxed);
123 if (!std::isfinite(db))
return;
124 reduction_.store(std::clamp(db, T(0), T(40)), std::memory_order_relaxed);
133 if (!std::isfinite(factor))
return;
134 threshold_.store(std::clamp(factor, T(1), T(8)), std::memory_order_relaxed);
137 [[nodiscard]]
bool getLearning() const noexcept {
return learning_.load(std::memory_order_relaxed); }
138 [[nodiscard]] T
getReduction() const noexcept {
return reduction_.load(std::memory_order_relaxed); }
139 [[nodiscard]] T
getThreshold() const noexcept {
return threshold_.load(std::memory_order_relaxed); }
142 [[nodiscard]]
int getLatency() const noexcept {
return stft_.getLatency(); }
146 [[nodiscard]] std::vector<uint8_t>
getState()
const
151 w.
write(
"reduction",
static_cast<float>(reduction_.load(std::memory_order_relaxed)));
152 w.
write(
"threshold",
static_cast<float>(threshold_.load(std::memory_order_relaxed)));
171 if (!prepared_.load(std::memory_order_relaxed))
return;
173 const bool learning = learning_.load(std::memory_order_relaxed);
174 const float floorGain = std::pow(
175 10.0f, -
static_cast<float>(reduction_.load(std::memory_order_relaxed)) / 20.0f);
176 const float thresh =
static_cast<float>(threshold_.load(std::memory_order_relaxed));
185 const int nChEff = std::max(1, std::min(buffer.getNumChannels(), numChannels_));
189 const float overSub = thresh * thresh;
191 stft_.processBlock(buffer, [
this, learning, floorGain, overSub, nChEff](T* bins,
int numBins)
193 auto& clean = cleanPower_[
static_cast<size_t>(callCounter_ % nChEff)];
199 float learnRate = 0.0f;
202 learnFrames_ = std::min(learnFrames_ + 1, kMaxLearnFrames);
203 learnRate = 1.0f /
static_cast<float>(learnFrames_);
206 for (
int k = 0; k < numBins; ++k)
208 const float re =
static_cast<float>(bins[2 * k]);
209 const float im =
static_cast<float>(bins[2 * k + 1]);
210 const float power = re * re + im * im;
212 auto& noise = profile_[
static_cast<size_t>(k)];
214 noise += learnRate * (power - noise);
222 const float lambda = overSub * noise;
223 auto& prevClean = clean[
static_cast<size_t>(k)];
226 const float post = power / lambda;
227 const float prio = std::max(kDDAlpha * prevClean / lambda
228 + (1.0f - kDDAlpha) * std::max(post - 1.0f, 0.0f),
230 gain = std::max(prio / (1.0f + prio), floorGain);
232 prevClean = gain * gain * power;
234 bins[2 * k] =
static_cast<T
>(re * gain);
235 bins[2 * k + 1] =
static_cast<T
>(im * gain);
242 int numChannels_ = 0;
244 std::atomic<bool> prepared_ {
false };
246 static constexpr float kDDAlpha = 0.98f;
247 static constexpr float kMinPrioriSnr = 0.003162f;
248 static constexpr int kMaxLearnFrames = 4096;
250 std::vector<float> profile_;
251 int learnFrames_ = 0;
252 std::vector<std::vector<float>> cleanPower_;
253 int callCounter_ = 0;
255 std::atomic<bool> learning_ {
false };
256 std::atomic<T> reduction_ { T(18) };
257 std::atomic<T> threshold_ { T(2) };
Non-owning view over audio channel data.
Learn-a-profile spectral noise reduction (hiss/hum/room-tone).
void setThreshold(T factor) noexcept
Noise over-subtraction factor over the learned profile, in magnitude [1, 8] (default 2): the gain rul...
void setReduction(T db) noexcept
Maximum attenuation of noise bins in dB [0, 40] (default 18): the floor of the per-bin gain....
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).