Line data Source code
1 : #pragma once
2 :
3 : // Lightweight speaker cabinet filtering for guitar amp output shaping.
4 : // Approximates speaker response as H(z)=H_hp(z) * H_peak(z) * H_lp(z): a low
5 : // cut removes rumble, a resonant biquad models cabinet/body emphasis, and a
6 : // low-pass rolloff attenuates harsh high-frequency content.
7 :
8 : #include "audio/effects/effect.h"
9 : #include "audio/dsp/biquad.h"
10 : #include "audio/dsp/convolution_engine.h"
11 : #include <atomic>
12 : #include <string>
13 : #include <vector>
14 : #include <memory>
15 :
16 : namespace Amplitron {
17 :
18 : class CabinetSim : public Effect {
19 : public:
20 : CabinetSim();
21 : ~CabinetSim() override;
22 : void process(float* buffer, int num_samples) override;
23 : void set_sample_rate(int sample_rate) override;
24 : void reset() override;
25 95 : const char* name() const override { return "Cabinet"; }
26 3 : const char* type_id() const override { return "Cabinet"; }
27 27 : std::vector<EffectParam>& params() override { return params_; }
28 :
29 : // --- IR management (called from GUI thread) ---
30 : bool load_ir(const std::string& filepath);
31 : void clear_ir();
32 : bool has_ir() const;
33 19 : const std::string& ir_path() const { return ir_path_; }
34 13 : const std::string& ir_name() const { return ir_name_; }
35 23 : float ir_duration_ms() const { return ir_duration_ms_; }
36 :
37 : private:
38 : std::vector<EffectParam> params_;
39 :
40 : Biquad lp_; // speaker rolloff
41 : Biquad hp_; // low cut
42 : Biquad peak_; // resonance bump
43 :
44 : // --- Optional IR-based cabinet convolution ---
45 :
46 : // Atomic kernel swap: GUI thread stores, audio thread consumes
47 : std::atomic<ConvolutionKernel*> pending_kernel_{nullptr};
48 : const ConvolutionKernel* active_kernel_ = nullptr;
49 : mutable std::atomic<const ConvolutionKernel*> old_kernel_to_delete_{nullptr};
50 : std::atomic<bool> clear_pending_{false};
51 :
52 : ConvolutionEngine conv_engine_;
53 :
54 : // Dry signal buffer for process() to avoid per-call allocations in audio callback
55 : std::vector<float> dry_buffer_;
56 :
57 : // Raw IR samples for rebuilding kernel on sample rate / block size changes
58 : std::vector<float> raw_ir_samples_;
59 :
60 : // IR file metadata
61 : std::string ir_path_;
62 : std::string ir_name_;
63 : float ir_duration_ms_ = 0.0f;
64 :
65 : // Brightness one-pole smoother
66 : float bright_smooth_ = 0.5f;
67 : float bright_alpha_ = 0.0f;
68 :
69 : // Expected block size for the current kernel (atomic: read by audio thread, written by both)
70 : std::atomic<int> expected_block_size_{0};
71 :
72 : // Pending block size when audio callback detects a mismatch
73 : std::atomic<int> pending_block_size_{0};
74 :
75 : // Max IR length: 500ms worth of samples at current sample rate
76 : int max_ir_samples() const;
77 :
78 : // Check and consume pending kernel (called at start of process())
79 : void check_pending_kernel();
80 :
81 : // Build kernel from raw_ir_samples_ for a given block size
82 : void build_kernel(int block_size);
83 : };
84 :
85 : } // namespace Amplitron
|