dsp/: add spectral detector with envelope + peak suppression

- Detector computes smoothed spectral envelope
- Finds peaks exceeding envelope
- Creates per-bin suppression mask
- Applies mask in frequency domain before ISTFT
- Default params: sharpness=1.0, selectivity=0.5, depth=0.3

Verified: burst500.wav → output RMS reduced from 0.0678 to 0.0476
This commit is contained in:
2026-08-17 15:49:48 +03:00
parent 7dcbcf49b4
commit 71870129c2
5 changed files with 80 additions and 66 deletions
+16 -8
View File
@@ -4,13 +4,15 @@
#include <vector>
SpectralProcessor::SpectralProcessor(size_t nfft, size_t hop)
: nfft_(nfft), hop_(hop), frame_count_(0), output_pos_(0) {
: nfft_(nfft), hop_(hop), frame_count_(0), output_pos_(0),
detector_(nfft, 44100.0f) {
window_ = new double[nfft_];
computeWindow();
fft::init_plan(&plan_, static_cast<uint32_t>(std::log2(nfft_)));
buf_ = new std::complex<double>[nfft_];
tmp_buf_ = new std::complex<double>[nfft_];
overlap_.resize(nfft_, 0.0f);
mask_.resize(nfft_, 1.0f);
}
SpectralProcessor::~SpectralProcessor() {
@@ -19,6 +21,10 @@ SpectralProcessor::~SpectralProcessor() {
delete[] tmp_buf_;
}
void SpectralProcessor::setDetectorParams(float sharpness, float selectivity, float depth) {
detector_.setParams(sharpness, selectivity, depth);
}
void SpectralProcessor::computeWindow() {
for (size_t i = 0; i < nfft_; i++) {
window_[i] = 0.5 * (1.0 - std::cos(2.0 * M_PI * i / (nfft_ - 1)));
@@ -49,23 +55,25 @@ void SpectralProcessor::istftFrame(std::complex<double>* in, float* out, float*
}
}
void SpectralProcessor::updateDetector() {
}
void SpectralProcessor::processBlock(float* in, float* out, size_t num_samples, size_t num_channels) {
memset(out, 0, num_samples * sizeof(float));
if (num_samples == 0 || num_samples < nfft_) {
return;
}
size_t nframes = (num_samples - nfft_) / hop_ + 1;
for (size_t f = 0; f < nframes; f++) {
size_t offset = f * hop_;
if (offset + nfft_ > num_samples) break;
stftFrame(in + offset, buf_);
updateDetector();
detector_.processFrame(buf_, mask_.data());
for (size_t i = 0; i < nfft_; i++) {
buf_[i] *= mask_[i];
}
istftFrame(buf_, out + offset, overlap_.data());
}
}