git ssb

0+

dinoworm ๐Ÿ› / metronome



Tree: fca343292fb72c697c29294488b5b36397ac1dc5

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
4use std::time::{Duration, Instant};
5use std::thread::{sleep, spawn};
6use std::sync::mpsc::{channel, Sender, Receiver, TryRecvError};
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;
22static DEFAULT_BARS_PER_LOOP: u64 = 4;
23static DEFAULT_BEATS_PER_MINUTE: f64 = 60_f64;
24
25#[derive(Clone, Copy, Debug, Hash)]
26pub 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
33impl 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#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
85pub struct ClockTime {
86 pub nanos: Nanos,
87 pub ticks: Ticks,
88 pub beats: Beats,
89 pub bars: Bars
90}
91
92impl 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#[derive(Debug)]
106pub struct Clock {
107 start: Time,
108 tick: Time,
109 signature: ClockSignature
110}
111
112pub enum ClockMessage {
113 Reset,
114 NudgeTempo(f64),
115 Signature(ClockSignature)
116}
117
118impl 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
208fn duration_to_nanos (duration: Duration) -> Nanos {
209 duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as Nanos
210}
211
212/*
213pub fn nanos_from_ticks (ticks: Ticks, signature: ClockSignature) -> Nanos {
214 ticks * signature.nanos_per_beat
215}
216
217pub fn ticks_from_beats (beats: Beats, signature: ClockSignature) -> Ticks {
218 beats * signature.ticks_per_beat
219}
220
221pub 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