LCOV - code coverage report
Current view: top level - src/audio/engine - metronome.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 96.2 % 106 102
Test Date: 2026-06-07 15:51:50 Functions: 91.7 % 12 11

            Line data    Source code
       1              : #include "audio/engine/metronome.h"
       2              : 
       3              : #include <algorithm>
       4              : #include <cmath>
       5              : 
       6              : namespace Amplitron {
       7              : 
       8              : static constexpr float kTwoPi = 6.28318530718f;
       9              : 
      10         1366 : Metronome::Metronome() { update_timing(); }
      11              : 
      12            0 : void Metronome::set_enabled(bool enabled) { enabled_.store(enabled, std::memory_order_relaxed); }
      13              : 
      14           18 : bool Metronome::is_enabled() const { return enabled_.load(std::memory_order_relaxed); }
      15              : 
      16           15 : void Metronome::toggle() {
      17           15 :     bool current = enabled_.load(std::memory_order_relaxed);
      18           15 :     while (!enabled_.compare_exchange_weak(current, !current, std::memory_order_relaxed,
      19              :                                            std::memory_order_relaxed)) {
      20              :     }
      21           15 : }
      22              : 
      23           18 : void Metronome::set_bpm(int bpm) {
      24           18 :     bpm_.store(std::max(40, std::min(bpm, 240)), std::memory_order_relaxed);
      25           18 : }
      26              : 
      27         5800 : int Metronome::get_bpm() const { return bpm_.load(std::memory_order_relaxed); }
      28              : 
      29           12 : void Metronome::set_volume(float volume) {
      30           12 :     volume_.store(clamp(volume, 0.0f, 1.0f), std::memory_order_relaxed);
      31           12 : }
      32              : 
      33           15 : float Metronome::get_volume() const { return volume_.load(std::memory_order_relaxed); }
      34              : 
      35          888 : void Metronome::set_sample_rate(int sample_rate) {
      36          888 :     sample_rate_.store(sample_rate, std::memory_order_relaxed);
      37          888 :     update_timing();
      38          888 : }
      39              : 
      40           74 : void Metronome::reset() {
      41           74 :     metronome_sample_counter_ = 0.0;
      42           74 :     metronome_click_samples_remaining_ = 0;
      43           74 :     metronome_click_env_ = 0.0f;
      44           74 :     metronome_click_phase_ = 0.0f;
      45           74 :     update_timing();
      46           74 : }
      47              : 
      48         1782 : void Metronome::update_timing() {
      49         1782 :     const int bpm = bpm_.load(std::memory_order_relaxed);
      50         1782 :     const int sample_rate = sample_rate_.load(std::memory_order_relaxed);
      51         1782 :     if (sample_rate <= 0) {
      52           10 :         metronome_samples_per_beat_ = 0.0;
      53           10 :         metronome_click_phase_inc_ = 0.0f;
      54           10 :         metronome_click_samples_total_ = 0;
      55           10 :         metronome_click_decay_ = 0.0f;
      56           10 :         return;
      57              :     }
      58              : 
      59         1772 :     metronome_samples_per_beat_ =
      60         1772 :         (static_cast<double>(sample_rate) * 60.0) / static_cast<double>(bpm);
      61         1772 :     if (metronome_samples_per_beat_ < 1.0) {
      62            0 :         metronome_samples_per_beat_ = 1.0;
      63            0 :     }
      64              : 
      65         1772 :     constexpr float kClickLengthSec = 0.01f;
      66         1772 :     const int click_samples = std::max(1, static_cast<int>(sample_rate * kClickLengthSec + 0.5f));
      67         1772 :     metronome_click_samples_total_ = click_samples;
      68              : 
      69         1772 :     constexpr float kClickFreq = 1000.0f;
      70         1772 :     metronome_click_phase_inc_ = (kTwoPi * kClickFreq) / static_cast<float>(sample_rate);
      71              : 
      72         1772 :     const float target = 0.001f;
      73         1772 :     metronome_click_decay_ = std::exp(std::log(target) / static_cast<float>(click_samples));
      74          618 : }
      75              : 
      76       301824 : float Metronome::next_sample() {
      77       301824 :     const bool metronome_target = enabled_.load(std::memory_order_relaxed);
      78       301824 :     if (metronome_target != metronome_enabled_) {
      79            9 :         metronome_enabled_ = metronome_target;
      80            9 :         metronome_sample_counter_ = 0.0;
      81            9 :         metronome_click_samples_remaining_ = 0;
      82            9 :         metronome_click_env_ = 0.0f;
      83            9 :         metronome_click_phase_ = 0.0f;
      84            3 :     }
      85              : 
      86       301824 :     const int bpm_state = bpm_.load(std::memory_order_relaxed);
      87       301824 :     const bool bpm_changed = (bpm_state != metronome_bpm_);
      88       301824 :     if (bpm_changed) {
      89            6 :         metronome_bpm_ = bpm_state;
      90            2 :     }
      91              : 
      92       301824 :     const float volume_state = volume_.load(std::memory_order_relaxed);
      93       301824 :     if (volume_state != metronome_volume_) {
      94            6 :         metronome_volume_ = volume_state;
      95            2 :     }
      96              : 
      97       301824 :     const bool timing_dirty = bpm_changed;
      98       301824 :     if (timing_dirty) {
      99            6 :         update_timing();
     100            6 :         if (metronome_enabled_) {
     101            6 :             if (metronome_sample_counter_ <= 0.0 ||
     102            0 :                 metronome_sample_counter_ > metronome_samples_per_beat_) {
     103            6 :                 metronome_sample_counter_ = metronome_samples_per_beat_;
     104            2 :             }
     105            2 :         }
     106            2 :     }
     107              : 
     108       301824 :     metronome_bpm_smoothed_ +=
     109       301824 :         metronome_bpm_smooth_alpha_ * (metronome_bpm_ - metronome_bpm_smoothed_);
     110       301824 :     metronome_volume_smoothed_ +=
     111       301824 :         metronome_volume_smooth_alpha_ * (metronome_volume_ - metronome_volume_smoothed_);
     112              : 
     113       301824 :     const int sample_rate = sample_rate_.load(std::memory_order_relaxed);
     114       301824 :     if (metronome_bpm_smoothed_ > 0.0f) {
     115       301824 :         metronome_samples_per_beat_ =
     116       301824 :             (static_cast<double>(sample_rate) * 60.0) / metronome_bpm_smoothed_;
     117       151104 :     }
     118              : 
     119       301824 :     if (!metronome_enabled_ || metronome_samples_per_beat_ <= 0.0) {
     120        82240 :         return 0.0f;
     121              :     }
     122              : 
     123       216384 :     metronome_sample_counter_ -= 1.0;
     124       216384 :     if (metronome_sample_counter_ <= 0.0) {
     125            3 :         metronome_sample_counter_ += metronome_samples_per_beat_;
     126            3 :         metronome_click_samples_remaining_ = metronome_click_samples_total_;
     127            3 :         metronome_click_env_ = 1.0f;
     128            3 :         metronome_click_phase_ = 0.0f;
     129            1 :     }
     130              : 
     131       216384 :     if (metronome_click_samples_remaining_ <= 0) {
     132       144126 :         return 0.0f;
     133              :     }
     134              : 
     135          130 :     float click =
     136          195 :         std::sin(metronome_click_phase_) * metronome_click_env_ * metronome_volume_smoothed_;
     137          195 :     metronome_click_phase_ += metronome_click_phase_inc_;
     138          195 :     if (metronome_click_phase_ >= kTwoPi) {
     139            3 :         metronome_click_phase_ -= kTwoPi;
     140            1 :     }
     141          195 :     metronome_click_env_ *= metronome_click_decay_;
     142          195 :     --metronome_click_samples_remaining_;
     143          195 :     return click;
     144       151104 : }
     145              : 
     146              : }  // namespace Amplitron
        

Generated by: LCOV version 2.0-1