Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
spectral_stack.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <complex>
4#include <cstdint>
5#include <memory>
6#include <string>
7#include <vector>
8
10#include <pulp/signal/fft.hpp>
12
13namespace pulp::gpu_audio {
14
15namespace detail {
23void circular_box_blur(const std::vector<float>& m, std::vector<float>& out,
24 std::uint32_t radius, float inv_kernel, std::uint32_t n);
25} // namespace detail
26
27
36public:
37 virtual ~SpectralStack() = default;
38
39 virtual uint32_t fft_size() const = 0;
40 virtual uint32_t num_layers() const = 0;
41
44 virtual bool available() const = 0;
45
48 virtual bool capture(uint32_t layer, const float* frame_in) = 0;
49
51 virtual void clear(uint32_t layer) = 0;
52 virtual bool layer_active(uint32_t layer) const = 0;
53
58 virtual bool render(float* frame_out, const float* weights, float smear,
59 float jitter) = 0;
60};
61
67public:
68 bool prepare(uint32_t fft_size, uint32_t hop, uint32_t num_layers);
69
70 uint32_t fft_size() const override { return n_; }
71 uint32_t num_layers() const override {
72 return static_cast<uint32_t>(layers_.size());
73 }
74 bool available() const override { return n_ > 0; }
75 bool capture(uint32_t layer, const float* frame_in) override;
76 void clear(uint32_t layer) override;
77 bool layer_active(uint32_t layer) const override;
78 bool render(float* frame_out, const float* weights, float smear,
79 float jitter) override;
80
81private:
82 struct Layer {
83 std::vector<float> mag; // n
84 std::vector<float> phase; // n, persists + advances across renders
85 bool active = false;
86 };
87 uint32_t n_ = 0;
88 uint32_t hop_ = 0;
89 float hop_ratio_ = 0.0f; // hop / n: per-bin phase advance factor
90 std::vector<Layer> layers_;
91 std::unique_ptr<signal::Fft> fft_;
92 std::vector<std::complex<float>> scratch_; // n: capture + combine workspace
93 std::vector<float> smear_scratch_; // n: per-layer blurred magnitude (running-sum)
94 std::vector<float> weights_; // local copy (null weights => all 1)
95 uint32_t seed_ = 0x9E3779B9u; // jitter RNG seed, advanced each render
96};
97
111public:
114 bool prepare(uint32_t fft_size, uint32_t hop, uint32_t num_layers,
115 render::GpuCompute* shared_device = nullptr);
116
117 uint32_t fft_size() const override { return n_; }
118 uint32_t num_layers() const override { return num_layers_; }
119 bool available() const override;
120 bool capture(uint32_t layer, const float* frame_in) override;
121 void clear(uint32_t layer) override;
122 bool layer_active(uint32_t layer) const override;
123 bool render(float* frame_out, const float* weights, float smear,
124 float jitter) override;
125
127 std::string backend() const;
128
129private:
130 uint32_t n_ = 0;
131 uint32_t hop_ = 0;
132 uint32_t num_layers_ = 0;
133 render::GpuCompute* gpu_ = nullptr; // borrowed or == owned_
134 std::unique_ptr<render::GpuCompute> owned_; // when not shared
135 std::unique_ptr<signal::Fft> fft_; // CPU forward for capture
136 std::vector<std::complex<float>> cap_scratch_; // n
137 std::vector<float> mag_up_, phase_up_; // n upload scratch
138 std::vector<std::uint8_t> active_; // per layer
139 std::vector<float> weights_; // num_layers (null => 1)
140 uint32_t seed_ = 0x9E3779B9u; // jitter seed, advanced
141 std::uint64_t device_generation_ = 0; // at prepare
142};
143
148 const float* weights = nullptr; // length num_layers (null = all 1)
149 float smear = 0.0f;
150 float jitter = 0.0f;
151 bool freeze = false; // capture-trigger LEVEL (rising edge captures)
152 bool active = true; // false = emit silence (stack not primed)
153};
154
176public:
179 bool prepare(SpectralStack* stack, uint32_t fft_size, uint32_t hop,
181
185 void process(const float* in, float* out, uint32_t n,
186 const SpectralFreezeControls& ctl);
187
188 uint32_t fft_size() const { return fft_size_; }
189 uint32_t hop() const { return hop_; }
190 uint32_t captured_layers() const { return captured_; }
191
192 void reset();
193
194private:
195 void run_hop(const SpectralFreezeControls& ctl);
196
197 SpectralStack* stack_ = nullptr;
198 uint32_t fft_size_ = 0;
199 uint32_t hop_ = 0;
200 std::vector<float> window_; // analysis/synthesis window (hann)
201 float ola_norm_ = 1.0f; // steady-state window-overlap normalization
202
203 std::vector<float> in_hist_; // ring of the latest fft_size input samples
204 uint32_t in_wr_ = 0; // ring write index (oldest sample sits here)
205 uint32_t in_fill_ = 0; // samples accumulated toward the next hop
206 std::vector<float> frame_; // analysis frame scratch (fft_size)
207 std::vector<float> rendered_; // stack render output (fft_size)
208 std::vector<float> ola_; // overlap-add accumulator (fft_size)
209 std::vector<float> out_fifo_; // produced output waiting to be emitted
210 uint32_t out_head_ = 0, out_tail_ = 0;
211
212 bool prev_freeze_ = false;
213 uint32_t next_layer_ = 0; // round-robin capture target
214 uint32_t captured_ = 0;
215};
216
217} // namespace pulp::gpu_audio
CPU reference + always-available fallback.
Definition spectral_stack.hpp:66
uint32_t fft_size() const override
Definition spectral_stack.hpp:70
bool prepare(uint32_t fft_size, uint32_t hop, uint32_t num_layers)
bool render(float *frame_out, const float *weights, float smear, float jitter) override
Advance + smear + weighted-sum the stack into frame_out (fft_size real samples).
bool available() const override
True if this stack's compute backend is live.
Definition spectral_stack.hpp:74
uint32_t num_layers() const override
Definition spectral_stack.hpp:71
bool layer_active(uint32_t layer) const override
void clear(uint32_t layer) override
Deactivate a layer (its magnitude goes silent; phase keeps advancing).
bool capture(uint32_t layer, const float *frame_in) override
Analyze frame_in (fft_size real samples) with the analysis window and freeze its magnitude + phase in...
GPU-resident engine: the captured layer spectra live on the GPU and every hop runs advance + smear + ...
Definition spectral_stack.hpp:110
bool available() const override
True if this stack's compute backend is live.
uint32_t num_layers() const override
Definition spectral_stack.hpp:118
bool prepare(uint32_t fft_size, uint32_t hop, uint32_t num_layers, render::GpuCompute *shared_device=nullptr)
shared_device (optional) reuses an existing GpuCompute so the stack and a sibling don't each spin up ...
bool render(float *frame_out, const float *weights, float smear, float jitter) override
Advance + smear + weighted-sum the stack into frame_out (fft_size real samples).
bool layer_active(uint32_t layer) const override
uint32_t fft_size() const override
Definition spectral_stack.hpp:117
void clear(uint32_t layer) override
Deactivate a layer (its magnitude goes silent; phase keeps advancing).
bool capture(uint32_t layer, const float *frame_in) override
Analyze frame_in (fft_size real samples) with the analysis window and freeze its magnitude + phase in...
std::string backend() const
GPU backend name ("Metal"/"D3D12"/"Vulkan"), or "" if no device.
STFT freeze framer: turns an arbitrary-length input stream into hop-spaced analysis frames,...
Definition spectral_stack.hpp:175
void process(const float *in, float *out, uint32_t n, const SpectralFreezeControls &ctl)
Process n input samples → n output samples (mono).
bool prepare(SpectralStack *stack, uint32_t fft_size, uint32_t hop, signal::WindowFunction::Type window=signal::WindowFunction::Type::hann)
stack must outlive the framer and already be prepared with a matching fft_size.
uint32_t fft_size() const
Definition spectral_stack.hpp:188
uint32_t captured_layers() const
Definition spectral_stack.hpp:190
uint32_t hop() const
Definition spectral_stack.hpp:189
A stack of N captured spectral "moments" (layers) that can be sustained and morphed.
Definition spectral_stack.hpp:35
virtual uint32_t num_layers() const =0
virtual bool layer_active(uint32_t layer) const =0
virtual bool capture(uint32_t layer, const float *frame_in)=0
Analyze frame_in (fft_size real samples) with the analysis window and freeze its magnitude + phase in...
virtual bool render(float *frame_out, const float *weights, float smear, float jitter)=0
Advance + smear + weighted-sum the stack into frame_out (fft_size real samples).
virtual void clear(uint32_t layer)=0
Deactivate a layer (its magnitude goes silent; phase keeps advancing).
virtual ~SpectralStack()=default
virtual bool available() const =0
True if this stack's compute backend is live.
virtual uint32_t fft_size() const =0
GPU compute pipeline for non-rendering workloads (e.g.
Definition gpu_compute.hpp:48
Type
Definition windowing.hpp:43
@ hann
Definition windowing.hpp:45
Definition gpu_audio_node.hpp:78
void circular_box_blur(const std::vector< float > &m, std::vector< float > &out, std::uint32_t radius, float inv_kernel, std::uint32_t n)
Circular box blur of width (2*radius+1) over m[0..n) into out[0..n), in O(n) via a sliding running su...
Definition flow_pans.hpp:19
Per-hop control values handed to the framer (read on whatever thread drives the framer — the audio th...
Definition spectral_stack.hpp:147
bool active
Definition spectral_stack.hpp:152
const float * weights
Definition spectral_stack.hpp:148
bool freeze
Definition spectral_stack.hpp:151
float smear
Definition spectral_stack.hpp:149
float jitter
Definition spectral_stack.hpp:150