Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
stft.hpp
Go to the documentation of this file.
1#pragma once
2
6
7#include <pulp/signal/fft.hpp>
10#include <vector>
11#include <complex>
12#include <cmath>
13#include <algorithm>
14#include <cassert>
15#include <cstring>
16
17namespace pulp::signal {
18
20struct StftConfig {
21 int fft_size = 1024; // Must be power of 2, 256–8192
22 int hop_size = 256; // Samples between frames
24 float window_param = 0.0f; // For Kaiser alpha
25};
26
28template <typename SampleType = float>
29struct StftFrameT {
30 std::vector<SampleType> magnitude; // num_bins values (fft_size/2 + 1)
31 std::vector<SampleType> phase; // num_bins values
32 int num_bins = 0;
33};
34
37
52template <typename SampleType = float>
53class StftT {
54public:
55 StftT() = default;
56
57 explicit StftT(const StftConfig& config) { configure(config); }
58
64 void configure(const StftConfig& config) {
65 assert(config.fft_size >= 256 && config.fft_size <= 8192);
66 assert((config.fft_size & (config.fft_size - 1)) == 0); // power of 2
67
68 config_ = config;
69 fft_ = FftT<SampleType>(config.fft_size);
71 config.fft_size, config.window, static_cast<SampleType>(config.window_param));
72 window_sum_ = SampleType{0};
73 for (const auto coefficient : window_) window_sum_ += coefficient;
74 num_bins_ = config.fft_size / 2 + 1;
75
76 // The complete oldest-to-newest analysis history remains contiguous.
77 history_.prepare(static_cast<std::size_t>(config.fft_size));
78 hop_counter_ = 0;
79 samples_fed_ = 0;
80
81 // Working buffers
82 windowed_.resize(config.fft_size, SampleType{0.0f});
83 freq_.resize(config.fft_size);
84
85 frame_.magnitude.resize(num_bins_, SampleType{0.0f});
86 frame_.phase.resize(num_bins_, SampleType{0.0f});
87 frame_.num_bins = num_bins_;
88 frame_ready_ = false;
89 }
90
93 bool push_samples(const SampleType* samples, int count) {
94 bool computed = false;
95 for (int i = 0; i < count; ++i) {
96 history_.push(samples[i]);
97 ++samples_fed_;
98 ++hop_counter_;
99
100 if (hop_counter_ >= config_.hop_size && samples_fed_ >= config_.fft_size) {
101 compute_frame();
102 hop_counter_ = 0;
103 computed = true;
104 }
105 }
106 return computed;
107 }
108
110 const StftFrameT<SampleType>& latest_frame() const { return frame_; }
111
113 bool frame_ready() const { return frame_ready_; }
114
116 int num_bins() const { return num_bins_; }
117
121 SampleType window_sum() const { return window_sum_; }
122
124 int fft_size() const { return config_.fft_size; }
125
127 int hop_size() const { return config_.hop_size; }
128
130 static void to_db(SampleType* magnitudes,
131 int count,
132 SampleType floor_db = SampleType{-120.0f}) {
133 SampleType floor_linear =
134 std::pow(SampleType{10.0f}, floor_db / SampleType{20.0f});
135 for (int i = 0; i < count; ++i) {
136 magnitudes[i] = SampleType{20.0f} *
137 std::log10(std::max(magnitudes[i], floor_linear));
138 }
139 }
140
142 std::vector<SampleType> latest_magnitude_db(
143 SampleType floor_db = SampleType{-120.0f}) const {
144 auto db = frame_.magnitude;
145 to_db(db.data(), static_cast<int>(db.size()), floor_db);
146 return db;
147 }
148
149 void reset() {
150 history_.reset();
151 hop_counter_ = 0;
152 samples_fed_ = 0;
153 frame_ready_ = false;
154 std::fill(frame_.magnitude.begin(), frame_.magnitude.end(), SampleType{0.0f});
155 std::fill(frame_.phase.begin(), frame_.phase.end(), SampleType{0.0f});
156 }
157
158private:
159 void compute_frame() {
160 const auto history = history_.window();
161 for (int i = 0; i < config_.fft_size; ++i) {
162 windowed_[i] = history[static_cast<std::size_t>(i)];
163 }
164
165 // Apply window
166 WindowFunction::apply(windowed_.data(), window_);
167
168 // Forward FFT (DC..Nyquist is all the frame reads)
169 fft_.forward_real_half(windowed_.data(), freq_.data());
170
171 // Extract magnitude and phase for positive frequencies
172 for (int i = 0; i < num_bins_; ++i) {
173 frame_.magnitude[i] = std::abs(freq_[i]);
174 frame_.phase[i] = std::arg(freq_[i]);
175 }
176
177 frame_ready_ = true;
178 }
179
180 StftConfig config_;
181 FftT<SampleType> fft_{1024};
182 std::vector<SampleType> window_;
183 SampleType window_sum_ = SampleType{1};
184 int num_bins_ = 0;
185
186 // Single-thread sample history; not a producer/consumer queue.
187 MirroredHistoryBuffer<SampleType> history_;
188 int hop_counter_ = 0;
189 int samples_fed_ = 0;
190
191 // Working buffers (reused to avoid allocation)
192 std::vector<SampleType> windowed_;
193 std::vector<std::complex<SampleType>> freq_;
194
195 // Latest computed frame
196 StftFrameT<SampleType> frame_;
197 bool frame_ready_ = false;
198};
199
202
203} // namespace pulp::signal
Definition fft.hpp:176
void forward_real_half(const SampleType *input, std::complex< SampleType > *output, SampleType scale) const
Real-input forward transform.
Definition fft.hpp:433
Allocation-stable STFT processor with single-thread ownership.
Definition stft.hpp:53
bool frame_ready() const
Whether at least one frame has been computed.
Definition stft.hpp:113
int hop_size() const
Current hop size.
Definition stft.hpp:127
SampleType window_sum() const
Sum of the configured analysis-window coefficients.
Definition stft.hpp:121
int fft_size() const
Current FFT size.
Definition stft.hpp:124
std::vector< SampleType > latest_magnitude_db(SampleType floor_db=SampleType{-120.0f}) const
Get the latest frame magnitudes in dB. Not RT-safe; returns a vector.
Definition stft.hpp:142
int num_bins() const
Number of frequency bins (fft_size/2 + 1).
Definition stft.hpp:116
void configure(const StftConfig &config)
RT contract: configure() allocates FFT/window/ring/frame storage and is not audio-thread safe.
Definition stft.hpp:64
const StftFrameT< SampleType > & latest_frame() const
Access the latest computed frame.
Definition stft.hpp:110
static void to_db(SampleType *magnitudes, int count, SampleType floor_db=SampleType{-120.0f})
Convert linear magnitudes to dB in-place.
Definition stft.hpp:130
bool push_samples(const SampleType *samples, int count)
Feed audio samples.
Definition stft.hpp:93
void reset()
Definition stft.hpp:149
StftT(const StftConfig &config)
Definition stft.hpp:57
static std::vector< SampleType > generate(int size, Type type, SampleType param=SampleType{0.0f})
Definition windowing.hpp:56
static void apply(SampleType *buffer, const std::vector< SampleType > &window)
Definition windowing.hpp:130
Type
Definition windowing.hpp:43
@ hann
Definition windowing.hpp:45
Fixed-capacity contiguous sample history for single-thread DSP algorithms.
Definition additive_bank.hpp:218
StftT< double > Stft64
Definition stft.hpp:201
@ i
Definition formant_filter_bank.hpp:20
StftFrameT< double > StftFrame64
Definition stft.hpp:36
StftFrameT< float > StftFrame
Definition stft.hpp:35
StftT< float > Stft
Definition stft.hpp:200
Configuration for an STFT processor.
Definition stft.hpp:20
float window_param
Definition stft.hpp:24
WindowFunction::Type window
Definition stft.hpp:23
int fft_size
Definition stft.hpp:21
int hop_size
Definition stft.hpp:22
A single completed STFT frame: magnitude (linear or dB) and phase.
Definition stft.hpp:29
std::vector< float > phase
Definition stft.hpp:31
std::vector< float > magnitude
Definition stft.hpp:30
int num_bins
Definition stft.hpp:32