LCOV - code coverage report
Current view: top level - src/audio/effects - looper.h (source / functions) Coverage Total Hit
Test: merged.info Lines: 100.0 % 10 10
Test Date: 2026-06-01 11:15:25 Functions: 100.0 % 8 8

            Line data    Source code
       1              : #pragma once
       2              : 
       3              : #include "audio/effects/effect.h"
       4              : 
       5              : #include<ostream>
       6              : #include <atomic>
       7              : #include <cmath>
       8              : #include <cstdint>
       9              : #include <vector>
      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              : 
      41              :     // --- UI control (thread-safe) ---
      42              :     void request_record_toggle();
      43              :     void request_play_toggle();
      44              :     void request_overdub_toggle();
      45              :     void request_clear();
      46              : 
      47              :     // --- UI status snapshot (thread-safe) ---
      48          208 :     State state() const { return static_cast<State>(ui_state_.load(std::memory_order_relaxed)); }
      49          156 :     bool has_loop() const { return ui_has_loop_.load(std::memory_order_relaxed) != 0; }
      50          124 :     int loop_length_samples() const { return ui_loop_length_samples_.load(std::memory_order_relaxed); }
      51          260 :     int playhead_samples() const { return ui_playhead_samples_.load(std::memory_order_relaxed); }
      52              : 
      53              : private:
      54              :     static constexpr int kMaxSeconds = 60;
      55              :     static constexpr float kMinLoopSeconds = 0.10f;
      56              :     static constexpr float kLoopLevelSmoothingSeconds = 0.02f;
      57              : 
      58              :     enum CommandBits : uint32_t {
      59              :         CmdRecordToggle  = 1u << 0,
      60              :         CmdPlayToggle    = 1u << 1,
      61              :         CmdOverdubToggle = 1u << 2,
      62              :         CmdClear         = 1u << 3,
      63              :     };
      64              : 
      65              :     // Params (saved in presets): loop playback level + crossfade length.
      66              :     std::vector<EffectParam> params_;
      67              : 
      68              :     // Preallocated buffers (full capacity, mono or stereo).
      69              :     std::vector<float> buffer_l_;
      70              :     std::vector<float> buffer_r_;
      71              :     int max_samples_ = 0;
      72              : 
      73              :     // Audio-thread state (not atomic; only touched in process/process_stereo).
      74              :     State state_rt_ = State::Empty;
      75              :     bool has_loop_rt_ = false;
      76              :     int record_pos_ = 0;
      77              :     int playhead_ = 0;
      78              :     int loop_length_ = 0;
      79              : 
      80              :     float loop_level_smoothed_ = 0.80f;
      81              :     float loop_level_alpha_ = 0.0f;
      82              :     float crossfade_ms_smoothed_ = 5.0f;
      83              :     float crossfade_alpha_ = 0.0f;
      84              :     // UI-visible atomics (written from audio thread, read by GUI thread).
      85              :     std::atomic<uint32_t> ui_state_{static_cast<uint32_t>(State::Empty)};
      86              :     std::atomic<int> ui_has_loop_{0};
      87              :     std::atomic<int> ui_loop_length_samples_{0};
      88              :     std::atomic<int> ui_playhead_samples_{0};
      89              : 
      90              :     // UI -> audio thread command mailbox (bitmask).
      91              :     std::atomic<uint32_t> pending_commands_{0};
      92              : 
      93              :     // Helpers
      94              :     void ensure_capacity();
      95              :     void apply_pending_commands();
      96              :     void publish_ui_snapshot();
      97              :     void clear_loop_rt();
      98              :     void start_recording_rt();
      99              :     void stop_recording_rt_and_play_rt();
     100              :     void toggle_play_rt();
     101              :     void toggle_overdub_rt();
     102              : 
     103       151572 :     static inline float soft_clip(float x) {
     104       131092 :         const float ax = std::fabs(x);
     105       131092 :         return x / (1.0f + ax);
     106              :     }
     107              : 
     108              :     inline int crossfade_samples_rt(float ms) const;
     109              :     inline void process_core(float* left, float* right, int num_samples, bool stereo);
     110              : };
     111              : 
     112              : std::ostream& operator<<(std::ostream& os, Looper::State s);
     113              : } // namespace Amplitron
     114              : 
        

Generated by: LCOV version 2.0-1