git ssb

0+

dinoworm ๐Ÿ› / metronome



Tree: 2431efbc25dadc7996fc6ca2d7748bc2f467b4e9

Files: 2431efbc25dadc7996fc6ca2d7748bc2f467b4e9 / src / clock.rs

8206 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, TryRecvError};
7
8use control;
9
10pub type Nanos = u64;
11pub type Ticks = u64;
12pub type Beats = u64;
13pub type Bars = u64;
14
15static SECONDS_PER_MINUTE: u64 = 60;
16static NANOS_PER_SECOND: u64 = 1_000_000_000;
17static BEATS_PER_MINUTE: u64 = 60;
18static DEFAULT_TICKS_PER_BEAT: u64 = 16;
19static DEFAULT_BEATS_PER_BAR: u64 = 4;
20static DEFAULT_BARS_PER_LOOP: u64 = 4;
21static DEFAULT_BEATS_PER_MINUTE: f64 = 60_f64;
22
23#[derive(Clone, Copy, Debug)]
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 pub bars_per_loop: u64,
29}
30
31impl ClockSignature {
32 pub fn new (nanos_per_beat: u64) -> Self {
33 Self {
34 nanos_per_beat: nanos_per_beat,
35 ticks_per_beat: DEFAULT_TICKS_PER_BEAT,
36 beats_per_bar: DEFAULT_BEATS_PER_BAR,
37 bars_per_loop: DEFAULT_BARS_PER_LOOP,
38 }
39 }
40
41 pub fn default () -> Self {
42 Self::from_beats_per_minute(DEFAULT_BEATS_PER_MINUTE)
43 }
44
45 pub fn from_beats_per_minute (beats_per_minute: f64) -> Self {
46 let minutes_per_beat = 1_f64 / beats_per_minute;
47 let seconds_per_beat = minutes_per_beat * SECONDS_PER_MINUTE as f64;
48 let nanos_per_beat = seconds_per_beat * NANOS_PER_SECOND as f64;
49 Self::new(nanos_per_beat as u64)
50 }
51
52 pub fn to_beats_per_minute (&self) -> f64 {
53 let nanos_per_beat = self.nanos_per_beat;
54 let beats_per_nano = 1_f64 / self.nanos_per_beat as f64;
55 let beats_per_second = beats_per_nano * NANOS_PER_SECOND as f64;
56 let beats_per_minute = beats_per_second * SECONDS_PER_MINUTE as f64;
57 beats_per_minute
58 }
59
60 pub fn nanos_per_tick (&self) -> u64 {
61 (self.nanos_per_beat / self.ticks_per_beat) as u64
62 }
63
64 pub fn nanos_per_beat (&self) -> u64 {
65 self.nanos_per_beat
66 }
67
68 pub fn nanos_per_bar (&self) -> u64 {
69 self.nanos_per_beat() * self.beats_per_bar
70 }
71
72 pub fn nanos_per_loop (&self) -> u64 {
73 self.nanos_per_bar() * self.bars_per_loop
74 }
75
76 pub fn nanos_to_ticks (&self, nanos: Nanos) -> u64 {
77 (nanos / self.nanos_per_tick()) % self.ticks_per_beat
78 }
79
80 pub fn nanos_to_beats (&self, nanos: Nanos) -> u64 {
81 (nanos / self.nanos_per_beat()) % self.beats_per_bar
82 }
83
84 pub fn nanos_to_bars (&self, nanos: Nanos) -> u64 {
85 nanos / self.nanos_per_bar() % self.bars_per_loop
86 }
87}
88
89#[derive(Clone, Copy, Debug)]
90pub struct ClockTime {
91 nanos: Nanos,
92 signature: ClockSignature
93}
94
95impl ClockTime {
96 pub fn new (nanos: Nanos, signature: ClockSignature) -> Self {
97 Self {
98 nanos,
99 signature
100 }
101 }
102
103 pub fn nanos (&self) -> Nanos {
104 self.nanos
105 }
106
107 pub fn ticks (&self) -> Ticks {
108 self.signature.nanos_to_ticks(self.nanos)
109 }
110
111 pub fn beats (&self) -> Beats {
112 self.signature.nanos_to_beats(self.nanos)
113 }
114
115 pub fn bars (&self) -> Bars {
116 self.signature.nanos_to_bars(self.nanos)
117 }
118
119 pub fn nanos_since_loop (&self) -> Nanos {
120 self.nanos % self.signature.nanos_per_loop()
121 }
122
123 pub fn nanos_since_tick (&self) -> Nanos {
124 self.nanos % self.signature.nanos_per_tick()
125 }
126
127 pub fn nanos_since_beat (&self) -> Nanos {
128 self.nanos % self.signature.nanos_per_beat()
129 }
130
131 pub fn nanos_since_bar (&self) -> Nanos {
132 self.nanos % self.signature.nanos_per_bar()
133 }
134}
135
136#[derive(Debug)]
137pub struct Clock {
138 nanos: Nanos,
139 instant: Instant,
140 signature: ClockSignature
141}
142
143pub enum ClockMessage {
144 NudgeTempo(f64),
145 Reset,
146 Signature(ClockSignature),
147 Tap,
148}
149
150impl Clock {
151 pub fn new () -> Self {
152 let instant = Instant::now();
153 let signature = ClockSignature::default();
154
155 Self {
156 nanos: 0,
157 instant,
158 signature
159 }
160 }
161
162 pub fn start (control_tx: Sender<control::ControlMessage>) -> Sender<ClockMessage> {
163 let mut clock = Self::new();
164
165 let (tx, rx) = channel();
166
167 control_tx.send(control::ControlMessage::Signature(ClockSignature::from_beats_per_minute(DEFAULT_BEATS_PER_MINUTE))).unwrap();
168
169 spawn(move|| {
170 loop {
171 // wait a tick
172 let diff = clock.tick();
173
174 // send clock time
175 control_tx.send(control::ControlMessage::Time(clock.time())).unwrap();
176
177 // handle any incoming messages
178 let message_result = rx.try_recv();
179 match message_result {
180 Ok(ClockMessage::Reset) => {
181 clock.reset();
182 },
183 Ok(ClockMessage::Signature(signature)) => {
184 clock.signature = signature;
185 },
186 Ok(ClockMessage::Tap) => {
187 // find how far off the beat we are
188 let time = clock.time();
189 let nanos_since_beat = time.nanos_since_beat();
190 let nanos_per_beat = time.signature.nanos_per_beat();
191 let nanos_per_half_beat = time.signature.nanos_per_beat() / 2;
192 // if the beat happened recently
193 if nanos_since_beat < nanos_per_half_beat {
194 // nudge back to the beat
195 clock.nanos = time.nanos - nanos_since_beat
196 } else {
197 // nudge to the next beat
198 clock.nanos = time.nanos + nanos_per_beat - nanos_since_beat
199 }
200 },
201 Ok(ClockMessage::NudgeTempo(nudge)) => {
202 let old_beats_per_minute = clock.signature.to_beats_per_minute();
203 let new_beats_per_minute = old_beats_per_minute + nudge;
204 let next_signature = ClockSignature::from_beats_per_minute(new_beats_per_minute);
205 control_tx.send(control::ControlMessage::Signature(next_signature));
206 },
207 Err(TryRecvError::Empty) => {},
208 Err(TryRecvError::Disconnected) => {
209 panic!("{:?}", TryRecvError::Disconnected);
210 }
211 }
212 }
213 });
214
215 tx
216 }
217
218 pub fn reset (&mut self) {
219 self.nanos = 0;
220 self.instant = Instant::now();
221 }
222
223 pub fn time (&self) -> ClockTime {
224 ClockTime::new(self.nanos_since_loop(), self.signature)
225 }
226
227 pub fn nanos_since_loop (&self) -> Nanos {
228 self.nanos % self.signature.nanos_per_loop()
229 }
230
231 pub fn nanos_since_tick (&self) -> Nanos {
232 duration_to_nanos(self.instant.elapsed()) % self.signature.nanos_per_tick()
233 }
234
235 pub fn nanos_until_tick (&self) -> Nanos {
236 let nanos_since_tick = self.nanos_since_tick();
237 let nanos_per_tick = self.signature.nanos_per_tick();
238 nanos_per_tick - nanos_since_tick
239 }
240
241 // https://github.com/BookOwl/fps_clock/blob/master/src/lib.rs
242 pub fn tick (&mut self) -> Nanos {
243 let nanos_until_tick = self.nanos_until_tick();
244
245 sleep(Duration::new(0, nanos_until_tick as u32));
246
247 self.nanos = self.nanos + nanos_until_tick;
248 self.instant = Instant::now();
249
250 nanos_until_tick
251 }
252}
253
254fn duration_to_nanos (duration: Duration) -> Nanos {
255 duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as Nanos
256}
257
258/*
259pub fn nanos_from_ticks (ticks: Ticks, signature: ClockSignature) -> Nanos {
260 ticks * signature.nanos_per_beat
261}
262
263pub fn ticks_from_beats (beats: Beats, signature: ClockSignature) -> Ticks {
264 beats * signature.ticks_per_beat
265}
266
267pub fn ticks_from_measure (measures: Measures, signature: ClockSignature) -> Ticks {
268 measures * signature.beats_per_measure * signature.ticks_per_beat
269}
270*/
271

Built with git-ssb-web