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