Files: fca343292fb72c697c29294488b5b36397ac1dc5 / src / clock.rs
6584 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 | |
4 | use std::time::{Duration, Instant}; |
5 | use std::thread::{sleep, spawn}; |
6 | use std::sync::mpsc::{channel, Sender, Receiver, TryRecvError}; |
7 | |
8 | use control; |
9 | |
10 | pub type Time = Instant; |
11 | |
12 | pub type Nanos = u64; |
13 | pub type Ticks = u64; |
14 | pub type Beats = u64; |
15 | pub type Bars = u64; |
16 | |
17 | static SECONDS_PER_MINUTE: u64 = 60; |
18 | static NANOS_PER_SECOND: u64 = 1_000_000_000; |
19 | static BEATS_PER_MINUTE: u64 = 60; |
20 | static DEFAULT_TICKS_PER_BEAT: u64 = 16; |
21 | static DEFAULT_BEATS_PER_BAR: u64 = 4; |
22 | static DEFAULT_BARS_PER_LOOP: u64 = 4; |
23 | static DEFAULT_BEATS_PER_MINUTE: f64 = 60_f64; |
24 | |
25 | |
26 | pub struct ClockSignature { |
27 | pub nanos_per_beat: u64, // tempo |
28 | pub ticks_per_beat: u64, // meter |
29 | pub beats_per_bar: u64, // meter |
30 | pub bars_per_loop: u64, |
31 | } |
32 | |
33 | impl ClockSignature { |
34 | pub fn new (beats_per_minute: f64) -> Self { |
35 | let minutes_per_beat = 1_f64 / beats_per_minute; |
36 | let seconds_per_beat = minutes_per_beat * SECONDS_PER_MINUTE as f64; |
37 | let nanos_per_beat = seconds_per_beat * NANOS_PER_SECOND as f64; |
38 | |
39 | Self { |
40 | nanos_per_beat: nanos_per_beat as u64, |
41 | ticks_per_beat: DEFAULT_TICKS_PER_BEAT, |
42 | beats_per_bar: DEFAULT_BEATS_PER_BAR, |
43 | bars_per_loop: DEFAULT_BARS_PER_LOOP, |
44 | } |
45 | } |
46 | |
47 | pub fn default () -> Self { |
48 | Self::new(DEFAULT_BEATS_PER_MINUTE) |
49 | } |
50 | |
51 | pub fn to_beats_per_minute (&self) -> f64 { |
52 | let nanos_per_beat = self.nanos_per_beat; |
53 | let beats_per_nano = 1_f64 / self.nanos_per_beat as f64; |
54 | let beats_per_second = beats_per_nano * NANOS_PER_SECOND as f64; |
55 | let beats_per_minute = beats_per_second * SECONDS_PER_MINUTE as f64; |
56 | beats_per_minute |
57 | } |
58 | |
59 | pub fn nanos_per_tick (&self) -> u64 { |
60 | (self.nanos_per_beat / self.ticks_per_beat) as u64 |
61 | } |
62 | |
63 | pub fn nanos_per_beat (&self) -> u64 { |
64 | self.nanos_per_beat |
65 | } |
66 | |
67 | pub fn nanos_per_bar (&self) -> u64 { |
68 | self.nanos_per_beat() * self.beats_per_bar |
69 | } |
70 | |
71 | pub fn nanos_to_ticks (&self, nanos: Nanos) -> u64 { |
72 | (nanos / self.nanos_per_tick()) % self.ticks_per_beat |
73 | } |
74 | |
75 | pub fn nanos_to_beats (&self, nanos: Nanos) -> u64 { |
76 | (nanos / self.nanos_per_beat()) % self.beats_per_bar |
77 | } |
78 | |
79 | pub fn nanos_to_bars (&self, nanos: Nanos) -> u64 { |
80 | nanos / self.nanos_per_bar() % self.bars_per_loop |
81 | } |
82 | } |
83 | |
84 | |
85 | pub struct ClockTime { |
86 | pub nanos: Nanos, |
87 | pub ticks: Ticks, |
88 | pub beats: Beats, |
89 | pub bars: Bars |
90 | } |
91 | |
92 | impl ClockTime { |
93 | pub fn new (nanos: Nanos, signature: ClockSignature) -> Self { |
94 | Self { |
95 | nanos, |
96 | ticks: signature.nanos_to_ticks(nanos), |
97 | beats: signature.nanos_to_beats(nanos), |
98 | bars: signature.nanos_to_bars(nanos), |
99 | // ticks_til_beat: signature.ticks_til_beat(nanos), |
100 | // beats_til_bar: signature.beats_til_bar(nanos) |
101 | } |
102 | } |
103 | } |
104 | |
105 | |
106 | pub struct Clock { |
107 | start: Time, |
108 | tick: Time, |
109 | signature: ClockSignature |
110 | } |
111 | |
112 | pub enum ClockMessage { |
113 | Reset, |
114 | NudgeTempo(f64), |
115 | Signature(ClockSignature) |
116 | } |
117 | |
118 | impl Clock { |
119 | pub fn new () -> Self { |
120 | let start = Time::now(); |
121 | let signature = ClockSignature::default(); |
122 | |
123 | Self { |
124 | start, |
125 | tick: start, |
126 | signature |
127 | } |
128 | } |
129 | |
130 | pub fn start (control_tx: Sender<control::ControlMessage>) -> Sender<ClockMessage> { |
131 | let mut clock = Self::new(); |
132 | |
133 | let (tx, rx) = channel(); |
134 | |
135 | control_tx.send(control::ControlMessage::Signature(ClockSignature::new(DEFAULT_BEATS_PER_MINUTE))).unwrap(); |
136 | |
137 | spawn(move|| { |
138 | loop { |
139 | // wait a tick |
140 | let diff = clock.tick(); |
141 | |
142 | // send clock time |
143 | control_tx.send(control::ControlMessage::Time(clock.time())).unwrap(); |
144 | |
145 | // handle any incoming messages |
146 | let message_result = rx.try_recv(); |
147 | match message_result { |
148 | Ok(ClockMessage::Reset) => { |
149 | clock.reset(); |
150 | }, |
151 | Ok(ClockMessage::Signature(signature)) => { |
152 | clock.signature = signature; |
153 | }, |
154 | Ok(ClockMessage::NudgeTempo(nudge)) => { |
155 | let old_beats_per_minute = clock.signature.to_beats_per_minute(); |
156 | let new_beats_per_minute = old_beats_per_minute - nudge; |
157 | let next_signature = ClockSignature::new(new_beats_per_minute); |
158 | control_tx.send(control::ControlMessage::Signature(next_signature)); |
159 | }, |
160 | Err(TryRecvError::Empty) => {}, |
161 | Err(TryRecvError::Disconnected) => { |
162 | panic!("{:?}", TryRecvError::Disconnected); |
163 | } |
164 | } |
165 | } |
166 | }); |
167 | |
168 | tx |
169 | } |
170 | |
171 | pub fn reset (&mut self) { |
172 | self.start = Time::now(); |
173 | } |
174 | |
175 | pub fn time (&self) -> ClockTime { |
176 | ClockTime::new(self.nanos_since_start(), self.signature) |
177 | } |
178 | |
179 | pub fn diff (&self) -> ClockTime { |
180 | let nanos_since_tick = self.nanos_since_tick(); |
181 | let nanos_per_tick = self.signature.nanos_per_tick(); |
182 | let diff = nanos_per_tick - nanos_since_tick; |
183 | ClockTime::new(diff, self.signature) |
184 | } |
185 | |
186 | pub fn nanos_since_start (&self) -> Nanos { |
187 | duration_to_nanos(self.start.elapsed()) |
188 | } |
189 | |
190 | pub fn nanos_since_tick (&self) -> Nanos { |
191 | duration_to_nanos(self.tick.elapsed()) |
192 | } |
193 | |
194 | // https://github.com/BookOwl/fps_clock/blob/master/src/lib.rs |
195 | pub fn tick (&mut self) -> ClockTime { |
196 | let diff = self.diff(); |
197 | |
198 | if diff.nanos > 0 { |
199 | sleep(Duration::new(0, diff.nanos as u32)) |
200 | }; |
201 | |
202 | self.tick = Time::now(); |
203 | |
204 | diff |
205 | } |
206 | } |
207 | |
208 | fn duration_to_nanos (duration: Duration) -> Nanos { |
209 | duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as Nanos |
210 | } |
211 | |
212 | /* |
213 | pub fn nanos_from_ticks (ticks: Ticks, signature: ClockSignature) -> Nanos { |
214 | ticks * signature.nanos_per_beat |
215 | } |
216 | |
217 | pub fn ticks_from_beats (beats: Beats, signature: ClockSignature) -> Ticks { |
218 | beats * signature.ticks_per_beat |
219 | } |
220 | |
221 | pub fn ticks_from_measure (measures: Measures, signature: ClockSignature) -> Ticks { |
222 | measures * signature.beats_per_measure * signature.ticks_per_beat |
223 | } |
224 | */ |
225 |
Built with git-ssb-web