1use super::tick;
7use std::fmt;
8use std::sync::atomic::{AtomicU64, Ordering};
9
10#[derive(Debug, Clone, Copy)]
19#[allow(dead_code)]
20pub struct ClockTick {
21 pub sample_pos: u64,
23
24 pub samples_since_last: u32,
26
27 pub is_new_block: bool,
29
30 pub sample_rate: f32,
32
33 pub tempo: Option<f64>,
35}
36
37#[allow(dead_code)]
38impl ClockTick {
39 pub fn new(sample_pos: u64, samples_since_last: u32, sample_rate: f32) -> Self {
41 Self {
42 sample_pos,
43 samples_since_last,
44 is_new_block: true,
45 sample_rate,
46 tempo: None,
47 }
48 }
49
50 pub fn delta_seconds(&self) -> f32 {
52 self.samples_since_last as f32 / self.sample_rate
53 }
54
55 pub fn absolute_seconds(&self) -> f64 {
57 self.sample_pos as f64 / self.sample_rate as f64
58 }
59
60 pub fn advance(&mut self, samples: u32) {
62 self.sample_pos += samples as u64;
63 self.samples_since_last = samples;
64 self.is_new_block = true;
65 }
66}
67
68#[allow(dead_code)]
76pub trait ClockSource: Send + Sync {
77 fn next_tick(&mut self) -> ClockTick;
79
80 fn sample_rate(&self) -> f32;
82
83 fn start(&mut self) -> Result<(), ClockError>;
85
86 fn stop(&mut self) -> Result<(), ClockError>;
88}
89
90pub struct SystemClock {
99 pub sample_rate: f32,
100 position: AtomicU64,
101 bpm: AtomicU64, }
103
104impl SystemClock {
105 pub fn new(sample_rate: f32, initial_bpm: f64) -> Self {
107 Self {
108 sample_rate,
109 position: AtomicU64::new(0),
110 bpm: AtomicU64::new(initial_bpm.to_bits()),
111 }
112 }
113
114 pub fn with_sample_rate(sample_rate: f32) -> Self {
116 Self::new(sample_rate, 120.0)
117 }
118
119 pub fn next_tick(&mut self, block_size: usize) -> tick::ClockTick {
121 let samples = block_size as u32;
122 let pos = self.position.fetch_add(samples as u64, Ordering::Relaxed);
123
124 tick::ClockTick {
125 sample_pos: pos,
126 samples_since_last: samples,
127 is_new_block: true,
128 sample_rate: self.sample_rate,
129 tempo: Some(self.bpm() as f32),
130 }
131 }
132
133 pub fn bpm(&self) -> f64 {
135 f64::from_bits(self.bpm.load(Ordering::Relaxed))
136 }
137
138 pub fn set_bpm(&self, bpm: f64) {
140 self.bpm.store(bpm.to_bits(), Ordering::Relaxed);
141 }
142
143 pub fn position(&self) -> u64 {
145 self.position.load(Ordering::Relaxed)
146 }
147
148 pub fn reset(&self) {
150 self.position.store(0, Ordering::Relaxed);
151 }
152}
153
154impl ClockSource for SystemClock {
155 fn next_tick(&mut self) -> ClockTick {
156 let pos = self.position.fetch_add(1, Ordering::Relaxed);
157 ClockTick {
158 sample_pos: pos,
159 samples_since_last: 1,
160 is_new_block: true,
161 sample_rate: self.sample_rate,
162 tempo: Some(self.bpm()),
163 }
164 }
165
166 fn sample_rate(&self) -> f32 {
167 self.sample_rate
168 }
169
170 fn start(&mut self) -> Result<(), ClockError> {
171 Ok(())
172 }
173
174 fn stop(&mut self) -> Result<(), ClockError> {
175 Ok(())
176 }
177}
178
179impl fmt::Debug for SystemClock {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 f.debug_struct("SystemClock")
182 .field("sample_rate", &self.sample_rate)
183 .field("position", &self.position.load(Ordering::Relaxed))
184 .field("bpm", &self.bpm())
185 .finish()
186 }
187}
188
189#[derive(Debug, thiserror::Error)]
195#[allow(dead_code)]
196pub enum ClockError {
197 #[error("Hardware error: {0}")]
199 Hardware(String),
200
201 #[error("Invalid sample rate: {0}")]
203 InvalidSampleRate(f32),
204
205 #[error("Clock not started")]
207 NotStarted,
208
209 #[error("Clock already started")]
211 AlreadyStarted,
212}
213
214#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn test_system_clock() {
224 let mut clock = SystemClock::new(44100.0, 120.0);
225
226 let tick = clock.next_tick(64);
227 assert_eq!(tick.sample_pos, 0);
228 assert_eq!(tick.samples_since_last, 64);
229 assert_eq!(tick.sample_rate, 44100.0);
230 assert_eq!(tick.tempo, Some(120.0));
231
232 let tick = clock.next_tick(64);
233 assert_eq!(tick.sample_pos, 64);
234 }
235
236 #[test]
237 fn test_clock_tick_math() {
238 let tick = ClockTick::new(44100, 44100, 44100.0);
239 assert_eq!(tick.absolute_seconds(), 1.0);
240 assert_eq!(tick.delta_seconds(), 1.0);
241 }
242}