-
Notifications
You must be signed in to change notification settings - Fork 0
/
tap_tempo.rb
executable file
·96 lines (73 loc) · 1.68 KB
/
tap_tempo.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#!/usr/bin/ruby -w
require 'io/console'
class TapTempo
attr_accessor :precision, :reset_time, :sample, :close_char
def initialize(precision: 1, reset_time: 5, sample: 5, close_char: 'q')
self.close_char = close_char
self.precision = precision
self.reset_time = reset_time
self.sample = sample
self.tempo = Tempo.new
end
def play
rules
loop do
char = IO.console.raw(&:getc)
break if char == close_char
tap
display_bpm
end
bye
end
private
def tap
self.tempo = Tempo.new if tempo.any? && (Time.now - tempo.max) > reset_time
self.tempo = tempo.sub_tempo(1..) if tempo.count >= sample
tempo.tap
end
def rules
puts <<~HELP
Tap any key in rhythm to find the tempo.
- use the key '#{close_char}' to end the program
- wait #{reset_time} seconds to reset the tempo
- only last #{sample} taps are use to compute the beats per minute(bpm)
- bpm are truncated to use #{precision} decimal digit
HELP
end
def display_bpm
if tempo.bpm?
puts "Tempo: #{tempo.bpm.truncate(precision)} bpm"
else
puts '[Hit a key one more time to start bpm computation...]'
end
end
def bye
puts 'Bye Bye!'
end
attr_accessor :tempo
end
class Tempo
include Enumerable
def initialize(beats = [])
self.beats = beats
end
def bpm
return unless bpm?
(60 * (beats.size - 1)) / (beats.last - beats.first).to_f
end
def bpm?
beats.size >= 2
end
def tap
beats << Time.now
end
def each(...)
beats.each(...)
end
def sub_tempo(range)
self.class.new(beats[range])
end
private
attr_accessor :beats
end
TapTempo.new.play