git ssb

0+

dinoworm ๐Ÿ› / metronome



Tree: a2de68cc1a716fedcf010549ceed0b77aebe92f1

Files: a2de68cc1a716fedcf010549ceed0b77aebe92f1 / src / clock.rs

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

Built with git-ssb-web