LCOV - code coverage report
Current view: top level - src/audio/effects/utility - looper.h (source / functions) Coverage Total Hit
Test: merged.info Lines: 91.7 % 12 11
Test Date: 2026-06-07 15:51:50 Functions: 88.9 % 9 8

            Line data    Source code
       1              : #pragma once
       2              : 
       3              : #include <atomic>
       4              : #include <cmath>
       5              : #include <cstdint>
       6              : #include <ostream>
       7              : #include <vector>
       8              : 
       9              : #include "audio/effects/core/effect.h"
      10              : 
      11              : namespace Amplitron {
      12              : 
      13              : /**
      14              :  * A simple in-chain looper (record / play / overdub / clear).
      15              :  *
      16              :  * Design goals:
      17              :  * - No allocations inside process/process_stereo (buffer is preallocated).
      18              :  * - Thread-safe UI visibility for state and loop position via atomics.
      19              :  * - Minimal, predictable state machine for practice workflows.
      20              :  */
      21              : class Looper : public Effect {
      22              :    public:
      23              :     enum class State : uint32_t {
      24              :         Empty = 0,  // no loop in memory
      25              :         Idle,       // loop exists but not playing
      26              :         Recording,
      27              :         Playing,
      28              :         Overdubbing,
      29              :     };
      30              : 
      31              :     Looper();
      32              : 
      33              :     void process(float* buffer, int num_samples) override;
      34              :     void process_stereo(float* left, float* right, int num_samples) override;
      35              :     void set_sample_rate(int sample_rate) override;
      36              :     void reset() override;
      37           27 :     const char* name() const override { return "Looper"; }
      38            6 :     const char* type_id() const override { return "Looper"; }
      39          156 :     std::vector<EffectParam>& params() override { return params_; }
      40            0 :     const std::vector<EffectParam>& params() const override { return params_; }
      41              : 
      42              :     // --- UI control (thread-safe) ---
      43              :     void request_record_toggle();
      44              :     void request_play_toggle();
      45              :     void request_overdub_toggle();
      46              :     void request_clear();
      47              : 
      48              :     // --- UI status snapshot (thread-safe) ---
      49          208 :     State state() const { return static_cast<State>(ui_state_.load(std::memory_order_relaxed)); }
      50          156 :     bool has_loop() const { return ui_has_loop_.load(std::memory_order_relaxed) != 0; }
      51           93 :     int loop_length_samples() const {
      52          124 :         return ui_loop_length_samples_.load(std::memory_order_relaxed);
      53              :     }
      54          260 :     int playhead_samples() const { return ui_playhead_samples_.load(std::memory_order_relaxed); }
      55              : 
      56              :    private:
      57              :     static constexpr int kMaxSeconds = 60;
      58              :     static constexpr float kMinLoopSeconds = 0.10f;
      59              :     static constexpr float kLoopLevelSmoothingSeconds = 0.02f;
      60              : 
      61              :     enum CommandBits : uint32_t {
      62              :         CmdRecordToggle = 1u << 0,
      63              :         CmdPlayToggle = 1u << 1,
      64              :         CmdOverdubToggle = 1u << 2,
      65              :         CmdClear = 1u << 3,
      66              :     };
      67              : 
      68              :     // Params (saved in presets): loop playback level + crossfade length.
      69              :     std::vector<EffectParam> params_;
      70              : 
      71              :     // Preallocated buffers (full capacity, mono or stereo).
      72              :     std::vector<float> buffer_l_;
      73              :     std::vector<float> buffer_r_;
      74              :     int max_samples_ = 0;
      75              : 
      76              :     // Audio-thread state (not atomic; only touched in process/process_stereo).
      77              :     State state_rt_ = State::Empty;
      78              :     bool has_loop_rt_ = false;
      79              :     int record_pos_ = 0;
      80              :     int playhead_ = 0;
      81              :     int loop_length_ = 0;
      82              : 
      83              :     float loop_level_smoothed_ = 0.80f;
      84              :     float loop_level_alpha_ = 0.0f;
      85              :     float crossfade_ms_smoothed_ = 5.0f;
      86              :     float crossfade_alpha_ = 0.0f;
      87              :     // UI-visible atomics (written from audio thread, read by GUI thread).
      88              :     std::atomic<uint32_t> ui_state_{static_cast<uint32_t>(State::Empty)};
      89              :     std::atomic<int> ui_has_loop_{0};
      90              :     std::atomic<int> ui_loop_length_samples_{0};
      91              :     std::atomic<int> ui_playhead_samples_{0};
      92              : 
      93              :     // UI -> audio thread command mailbox (bitmask).
      94              :     std::atomic<uint32_t> pending_commands_{0};
      95              : 
      96              :     // Helpers
      97              :     void ensure_capacity();
      98              :     void apply_pending_commands();
      99              :     void publish_ui_snapshot();
     100              :     void clear_loop_rt();
     101              :     void start_recording_rt();
     102              :     void stop_recording_rt_and_play_rt();
     103              :     void toggle_play_rt();
     104              :     void toggle_overdub_rt();
     105              : 
     106       151572 :     static inline float soft_clip(float x) {
     107       131092 :         const float ax = std::fabs(x);
     108       131092 :         return x / (1.0f + ax);
     109              :     }
     110              : 
     111              :     inline int crossfade_samples_rt(float ms) const;
     112              :     inline void process_core(float* left, float* right, int num_samples, bool stereo);
     113              : };
     114              : 
     115              : std::ostream& operator<<(std::ostream& os, Looper::State s);
     116              : }  // namespace Amplitron
        

Generated by: LCOV version 2.0-1