1use std::fmt;
2use std::sync::atomic::{AtomicU64, Ordering};
3
4use super::tick;
5
6pub struct SystemClock {
11 pub sample_rate: f32,
13 position: AtomicU64,
15 bpm: AtomicU64,
17}
18
19impl SystemClock {
20 pub fn new(sample_rate: f32, initial_bpm: f64) -> Self {
22 Self {
23 sample_rate,
24 position: AtomicU64::new(0),
25 bpm: AtomicU64::new(initial_bpm.to_bits()),
26 }
27 }
28
29 pub fn with_sample_rate(sample_rate: f32) -> Self {
31 Self::new(sample_rate, 120.0)
32 }
33
34 pub fn next_tick(&mut self, block_size: usize) -> tick::ClockTick {
36 let samples = block_size as u32;
37 let pos = self.position.fetch_add(samples as u64, Ordering::Relaxed);
38
39 tick::ClockTick {
40 sample_pos: pos,
41 samples_since_last: samples,
42 is_new_block: true,
43 sample_rate: self.sample_rate,
44 tempo: Some(self.bpm() as f32),
45 source: String::new(),
46 speed_ratio: 1.0,
47 is_final: true,
48 io_quantum: samples,
49 }
50 }
51
52 pub fn bpm(&self) -> f64 {
54 f64::from_bits(self.bpm.load(Ordering::Relaxed))
55 }
56
57 pub fn set_bpm(&self, bpm: f64) {
59 self.bpm.store(bpm.to_bits(), Ordering::Relaxed);
60 }
61
62 pub fn position(&self) -> u64 {
64 self.position.load(Ordering::Relaxed)
65 }
66
67 pub fn reset(&self) {
69 self.position.store(0, Ordering::Relaxed);
70 }
71}
72
73impl fmt::Debug for SystemClock {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 f.debug_struct("SystemClock")
76 .field("sample_rate", &self.sample_rate)
77 .field("position", &self.position.load(Ordering::Relaxed))
78 .field("bpm", &self.bpm())
79 .finish()
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn test_system_clock() {
89 let mut clock = SystemClock::new(44100.0, 120.0);
90
91 let tick = clock.next_tick(64);
92 assert_eq!(tick.sample_pos, 0);
93 assert_eq!(tick.samples_since_last, 64);
94 assert_eq!(tick.sample_rate, 44100.0);
95 assert_eq!(tick.tempo, Some(120.0));
96
97 let tick = clock.next_tick(64);
98 assert_eq!(tick.sample_pos, 64);
99 }
100}