audio_processor_metronome/
playhead.rs

1// Augmented Audio: Audio libraries and applications
2// Copyright (c) 2022 Pedro Tacla Yamada
3//
4// The MIT License (MIT)
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22// THE SOFTWARE.
23
24use audio_processor_traits::AudioProcessorSettings;
25use augmented_playhead::{PlayHead, PlayHeadOptions};
26
27use crate::constants::{DEFAULT_SAMPLE_RATE, DEFAULT_TEMPO};
28
29/// This is so that consumers can control the play-head and metronome just follow
30///
31/// There are two types of methods here:
32///
33/// * Mutation methods: reset, set_tempo, prepare
34///   - These should be used by the metronome app only and noop if there's another master
35/// * Getter methods
36///   - These must be implemented
37pub trait MetronomePlayhead {
38    fn reset(&mut self) {}
39    fn set_tempo(&mut self, _tempo: f32) {}
40    fn prepare(&mut self, _settings: &AudioProcessorSettings, _tempo: f32) {}
41    fn accept_samples(&mut self, _samples: u32) {}
42    fn tempo(&self) -> Option<f32> {
43        None
44    }
45
46    fn position_beats(&self) -> f64;
47}
48
49pub struct DefaultMetronomePlayhead {
50    playhead: PlayHead,
51}
52
53impl DefaultMetronomePlayhead {
54    #[cfg(test)]
55    fn playhead(&self) -> &PlayHead {
56        &self.playhead
57    }
58}
59
60impl Default for DefaultMetronomePlayhead {
61    fn default() -> Self {
62        let sample_rate = DEFAULT_SAMPLE_RATE;
63        let tempo = DEFAULT_TEMPO;
64        let playhead = PlayHead::new(PlayHeadOptions::new(
65            Some(sample_rate),
66            Some(tempo),
67            Some(16),
68        ));
69        Self { playhead }
70    }
71}
72
73impl MetronomePlayhead for DefaultMetronomePlayhead {
74    fn reset(&mut self) {
75        self.playhead.set_position_seconds(0.0);
76    }
77
78    fn set_tempo(&mut self, tempo: f32) {
79        self.playhead.set_tempo(tempo);
80    }
81
82    fn prepare(&mut self, settings: &AudioProcessorSettings, tempo: f32) {
83        self.playhead = PlayHead::new(PlayHeadOptions::new(
84            Some(settings.sample_rate()),
85            Some(tempo),
86            self.playhead.options().ticks_per_quarter_note(),
87        ));
88    }
89
90    fn accept_samples(&mut self, samples: u32) {
91        self.playhead.accept_samples(samples)
92    }
93
94    fn tempo(&self) -> Option<f32> {
95        self.playhead.options().tempo()
96    }
97
98    fn position_beats(&self) -> f64 {
99        self.playhead.position_beats()
100    }
101}
102
103#[cfg(test)]
104mod test {
105    use super::{DefaultMetronomePlayhead, MetronomePlayhead};
106    use audio_processor_testing_helpers::assert_f_eq;
107    use audio_processor_traits::AudioProcessorSettings;
108
109    #[test]
110    fn test_default_metronome_playhead_has_expected_values() {
111        let playhead = DefaultMetronomePlayhead::default();
112        assert_f_eq!(playhead.position_beats(), 0.0);
113        assert_eq!(playhead.tempo(), Some(120.0));
114    }
115
116    #[test]
117    fn test_reset_sets_position_to_zero() {
118        let mut playhead = DefaultMetronomePlayhead::default();
119        playhead.accept_samples(100);
120        playhead.reset();
121        assert_f_eq!(playhead.position_beats(), 0.0);
122    }
123
124    #[test]
125    fn test_set_tempo_changes_tempo() {
126        let mut playhead = DefaultMetronomePlayhead::default();
127        playhead.set_tempo(80.0);
128        assert_eq!(playhead.tempo(), Some(80.0));
129    }
130
131    #[test]
132    fn test_prepare_sets_sample_rate_and_tempo() {
133        let mut playhead = DefaultMetronomePlayhead::default();
134        let settings = AudioProcessorSettings::new(22050.0, 2, 2, 512);
135        playhead.prepare(&settings, 100.0);
136
137        assert_eq!(playhead.tempo(), Some(100.0));
138        let sample_rate = playhead.playhead().options().sample_rate().unwrap();
139        assert_f_eq!(sample_rate, 22050.0);
140    }
141
142    #[test]
143    fn test_accept_samples_updates_position() {
144        let mut playhead = DefaultMetronomePlayhead::default();
145        let settings = AudioProcessorSettings::new(44100.0, 2, 2, 512);
146        playhead.prepare(&settings, 120.0);
147        playhead.accept_samples(4410);
148        assert_f_eq!(playhead.position_beats(), 0.2);
149    }
150}