git ssb

0+

dinoworm ๐Ÿ› / metronome



Tree: de37e989827890621e4dbd2d5a5493b416130606

Files: de37e989827890621e4dbd2d5a5493b416130606 / src / clock.rs

4735 bytesRaw
1// inspired by https://github.com/mmckegg/rust-loop-drop/blob/master/src/midi_time.rs
2// http://www.deluge.co/?q=midi-tempo-bpm
3
4use std::time::{Duration, Instant};
5use std::thread::{sleep, spawn};
6use std::sync::mpsc::{channel, Sender, Receiver};
7
8use control;
9
10pub type Time = Instant;
11
12pub type Nanos = u64;
13pub type Ticks = u64;
14pub type Beats = u64;
15pub type Bars = u64;
16
17static SECONDS_PER_MINUTE: u64 = 60;
18static NANOS_PER_SECOND: u64 = 1_000_000_000;
19static BEATS_PER_MINUTE: u64 = 60;
20static DEFAULT_TICKS_PER_BEAT: u64 = 16;
21static DEFAULT_BEATS_PER_BAR: u64 = 4;
22
23#[derive(Clone, Copy, Debug, Hash)]
24pub struct ClockSignature {
25 pub nanos_per_beat: u64, // tempo
26 pub ticks_per_beat: u64, // meter
27 pub beats_per_bar: u64 // meter
28}
29
30impl ClockSignature {
31 pub fn new (beats_per_minute: f64) -> Self {
32 let minutes_per_beat = 1_f64 / beats_per_minute;
33 let seconds_per_beat = minutes_per_beat * SECONDS_PER_MINUTE as f64;
34 let nanos_per_beat = seconds_per_beat * NANOS_PER_SECOND as f64;
35
36 Self {
37 nanos_per_beat: nanos_per_beat as u64,
38 ticks_per_beat: DEFAULT_TICKS_PER_BEAT,
39 beats_per_bar: DEFAULT_BEATS_PER_BAR
40 }
41 }
42
43 pub fn to_beats_per_minute (&self) -> f64 {
44 let nanos_per_beat = self.nanos_per_beat;
45 let beats_per_nano = 1_f64 / self.nanos_per_beat as f64;
46 let beats_per_second = beats_per_nano * NANOS_PER_SECOND as f64;
47 let beats_per_minute = beats_per_second * SECONDS_PER_MINUTE as f64;
48 beats_per_minute
49 }
50
51 pub fn nanos_per_tick (&self) -> u64 {
52 (self.nanos_per_beat / self.ticks_per_beat) as u64
53 }
54
55 pub fn nanos_per_beat (&self) -> u64 {
56 self.nanos_per_beat
57 }
58
59 pub fn nanos_per_bar (&self) -> u64 {
60 self.nanos_per_beat() * self.beats_per_bar
61 }
62
63 pub fn nanos_to_ticks (&self, nanos: Nanos) -> u64 {
64 (nanos / self.nanos_per_tick()) as u64
65 }
66
67 pub fn nanos_to_beats (&self, nanos: Nanos) -> u64 {
68 (nanos / self.nanos_per_beat()) as u64
69 }
70
71 pub fn nanos_to_bars (&self, nanos: Nanos) -> u64 {
72 (nanos / self.nanos_per_bar()) as u64
73 }
74}
75
76#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
77pub struct ClockTime {
78 pub nanos: Nanos,
79 pub ticks: Ticks,
80 pub beats: Beats,
81 pub bars: Bars
82}
83
84impl ClockTime {
85 pub fn new (nanos: Nanos, signature: ClockSignature) -> Self {
86 Self {
87 nanos,
88 ticks: signature.nanos_to_ticks(nanos),
89 beats: signature.nanos_to_beats(nanos),
90 bars: signature.nanos_to_bars(nanos)
91 }
92 }
93}
94
95#[derive(Debug)]
96pub struct Clock {
97 start: Time,
98 tick: Time,
99 signature: ClockSignature
100}
101
102pub enum ClockMessage {
103 Time(ClockTime)
104}
105
106impl Clock {
107 pub fn new (signature: ClockSignature) -> Self {
108 let start = Time::now();
109
110 Self {
111 start,
112 tick: start,
113 signature
114 }
115 }
116
117 pub fn start (signature: ClockSignature, control_tx: Sender<control::ControlMessage>) {
118 let mut clock = Self::new(signature);
119
120 spawn(move|| {
121 loop {
122 clock.tick();
123
124 control_tx.send(control::ControlMessage::Time(clock.time())).unwrap();
125 }
126 });
127 }
128
129 pub fn time (&self) -> ClockTime {
130 ClockTime::new(self.nanos_since_start(), self.signature)
131 }
132
133 pub fn diff (&self) -> ClockTime {
134 let nanos_since_tick = self.nanos_since_tick();
135 let nanos_per_tick = self.signature.nanos_per_tick();
136 let diff = nanos_per_tick - nanos_since_tick;
137 ClockTime::new(diff, self.signature)
138 }
139
140 pub fn nanos_since_start (&self) -> Nanos {
141 duration_to_nanos(self.start.elapsed())
142 }
143
144 pub fn nanos_since_tick (&self) -> Nanos {
145 duration_to_nanos(self.tick.elapsed())
146 }
147
148 // https://github.com/BookOwl/fps_clock/blob/master/src/lib.rs
149 pub fn tick (&mut self) -> ClockTime {
150 let diff = self.diff();
151
152 if diff.nanos > 0 {
153 sleep(Duration::new(0, diff.nanos as u32))
154 };
155
156
157 self.tick = Time::now();
158
159 diff
160 }
161}
162
163fn duration_to_nanos (duration: Duration) -> Nanos {
164 duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as Nanos
165}
166
167/*
168pub fn nanos_from_ticks (ticks: Ticks, signature: ClockSignature) -> Nanos {
169 ticks * signature.nanos_per_beat
170}
171
172pub fn ticks_from_beats (beats: Beats, signature: ClockSignature) -> Ticks {
173 beats * signature.ticks_per_beat
174}
175
176pub fn ticks_from_measure (measures: Measures, signature: ClockSignature) -> Ticks {
177 measures * signature.beats_per_measure * signature.ticks_per_beat
178}
179*/
180

Built with git-ssb-web