git ssb

0+

dinoworm ๐Ÿ› / metronome



Tree: f675cb27f2640ba304b1816b279b2ef7f8c19a59

Files: f675cb27f2640ba304b1816b279b2ef7f8c19a59 / src / clock.rs

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

Built with git-ssb-web