Files
soothe2-re/dsp/spectral.cpp
T
Matiq 7dcbcf49b4 dsp/: C++ skeleton with working FFT/WOLA STFT
- Cooley-Tukey radix-2 FFT (forward + inverse with /N normalization)
- WOLA STFT/ISTFT with Hann window (nfft=2048, hop=512)
- WAV16 read + WAV24 write (fixed aliasing bug in read)
- Fixed in-place processing bug (separate input/output buffers)
- Biquad filter (peak/shelf/reject)
- Peak detector skeleton
- MS encode/decode (M8 stereo)
- Harness: WAV16 → STFT → WAV24 passthrough verified non-zero output

Verified: burst500.wav passthrough produces output RMS=0.0678
2026-08-17 11:25:35 +03:00

72 lines
2.1 KiB
C++

#include "spectral.hpp"
#include <cmath>
#include <cstring>
#include <vector>
SpectralProcessor::SpectralProcessor(size_t nfft, size_t hop)
: nfft_(nfft), hop_(hop), frame_count_(0), output_pos_(0) {
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);
}
SpectralProcessor::~SpectralProcessor() {
delete[] window_;
delete[] buf_;
delete[] tmp_buf_;
}
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)));
}
}
void SpectralProcessor::stftFrame(const float* in, std::complex<double>* out) {
for (size_t i = 0; i < nfft_; i++) {
out[i] = std::complex<double>(static_cast<double>(in[i]) * window_[i], 0.0);
}
fft::execute(&plan_, out);
}
void SpectralProcessor::istftFrame(std::complex<double>* in, float* out, float* overlap) {
memcpy(tmp_buf_, in, nfft_ * sizeof(std::complex<double>));
fft::execute_inverse(&plan_, tmp_buf_);
for (size_t i = 0; i < nfft_; i++) {
overlap[i] += static_cast<float>(tmp_buf_[i].real() * window_[i]);
}
for (size_t i = 0; i < hop_; i++) {
out[i] = overlap[i];
}
for (size_t i = 0; i < nfft_ - hop_; i++) {
overlap[i] = overlap[i + hop_];
}
for (size_t i = nfft_ - hop_; i < nfft_; i++) {
overlap[i] = 0.0f;
}
}
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();
istftFrame(buf_, out + offset, overlap_.data());
}
}