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
|