1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};

pub struct ClockUpdater {
    clock: Arc<AtomicUsize>
}

impl ClockUpdater {
    pub fn increment(&mut self, frames: usize) {
        self.clock.store(self.clock.load(Ordering::Relaxed) + frames, Ordering::Release);
    }
}

pub struct ClockConsumer {
    clock: Arc<AtomicUsize>,
    rate: u32,
    tempo: f32
}

impl ClockConsumer {
    pub fn raw_frames(&self) -> usize {
        self.clock.load(Ordering::Acquire)
    }
    pub fn beat(&self) -> f32 {
        self.tempo / 60. * (self.raw_frames() as f32 / self.rate as f32)
    }
    pub fn beat_duration(&self) -> f32 {
        self.tempo / 60.
    }
}

impl Clone for ClockConsumer {
    fn clone(&self) -> ClockConsumer {
        ClockConsumer {
            rate: self.rate,
            tempo: self.tempo,
            clock: self.clock.clone()
        }
    }
}

pub fn audio_clock(tempo: f32, rate: u32) -> (ClockUpdater, ClockConsumer) {
    let c = Arc::new(AtomicUsize::new(0));
    (ClockUpdater { clock: c.clone() }, ClockConsumer { clock: c, tempo, rate })
}

#[cfg(test)]
mod tests {
    use audio_clock;

    #[test]
    fn it_works() {
        let (mut updater, consumer) = audio_clock(132.0, 44100);
        updater.increment(128);
        assert_eq!(consumer.raw_frames(), 128);
        assert_eq!(consumer.beat_duration(), 132.0 / 60.);
        assert_eq!(consumer.beat(), consumer.beat_duration() * (consumer.raw_frames() as f32 / 44100. ));
        updater.increment(64);
        assert_eq!(consumer.raw_frames(), 128 + 64);
        assert_eq!(consumer.beat_duration(), 132.0 / 60.);
        assert_eq!(consumer.beat(), consumer.beat_duration() * (consumer.raw_frames() as f32 / 44100. ));
        let second_consumer = consumer.clone();
        updater.increment(64);
        assert_eq!(consumer.raw_frames(), 128 + 64 + 64);
        assert_eq!(consumer.raw_frames(), second_consumer.raw_frames());
        assert_eq!(consumer.beat_duration(), second_consumer.beat_duration());
        assert_eq!(consumer.beat(), second_consumer.beat());
    }
}