metronome-odd 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3498f8b8652455ef065ce8876395adb9bfcb0e8139a5ce0938981224c51ddfb1
4
+ data.tar.gz: 67a174595dab000f0b193a51299f4bf0f73eb0b97f589633c00cee8f3c494713
5
+ SHA512:
6
+ metadata.gz: 4773f9a3f5a6abd7ae901dc819f139bda1ecc0d5a132abe9287a6e7401d39b177382818b47eed57f29a0064f56bce5db1800f1f97e6a8de727771ba193a48a6e
7
+ data.tar.gz: c3843cd332f0fc9d077541b136980145beea2b759db6e2aeb41b7b5bc69dde7d2bab50e8b29e1fce7016c8107f034bb7f7b14fad4ff55381c59f8d54b12b53e7
@@ -0,0 +1,406 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'metronome-odd'
4
+
5
+ ParseLine::Line.add_command(:play, /^play$/)
6
+ ParseLine::Line.add_command(:loop, /^loop$/)
7
+ ParseLine::Line.add_command(:loop_speed_up, /^loop:(?<first>\d+),(?<second>\d+)\((?<times>\d+)\)$/)
8
+ ParseLine::Line.add_command(:print_tempo, /^tempo$/)
9
+ ParseLine::Line.add_command(:next_tempo, /^next_tempo:(?<tempo>\d+)$/)
10
+ ParseLine::Line.add_command(:next_tempo_short, /^nt:(?<tempo>\d+)$/)
11
+ ParseLine::Line.add_command(:force_tempo, /^force_tempo:(?<tempo>\d+)$/)
12
+ ParseLine::Line.add_command(:force_tempo_short, /^ft:(?<tempo>\d+)$/)
13
+ ParseLine::Line.add_command(:signature, /^sign:(?<n>\d+)x(?<d>2|4|8|16)$/)
14
+ ParseLine::Line.add_command(:odd_signature, /^sign:(?<beats>(\d\.\d)(,(\d\.\d))*)$/)
15
+ ParseLine::Line.add_command(:add_bar, /^add:(\((?<tempo>\d+)\))?(?<n>\d+)x(?<d>2|4|8|16)(\((?<times>\d+)\))?$/)
16
+ ParseLine::Line.add_command(:odd_bar, /^add:(\((?<tempo>\d+)\))?(?<beats>(\d\.\d)(,(\d\.\d))*)(\((?<times>\d+)\))?$/)
17
+ ParseLine::Line.add_command(:add_1, /^add$/)
18
+ ParseLine::Line.add_command(:add, /^add:(?<times>\d+)$/)
19
+ ParseLine::Line.add_command(:save, /^save:(?<practice>\w+)?$/)
20
+ ParseLine::Line.add_command(:load, /^load:(?<practice>\w+)?$/)
21
+ ParseLine::Line.add_command(:list, /^list$/)
22
+ ParseLine::Line.add_command(:reset, /^reset$/)
23
+ ParseLine::Line.add_command(:help, /^help$/)
24
+ ParseLine::Line.add_command(:help_command, /^help:(?<command>\w+)$/)
25
+ ParseLine::Line.add_command(:print, /^print$/)
26
+ ParseLine::Line.add_command(:quit, /^quit$/)
27
+ ParseLine::Line.add_command(:empty, /^$/)
28
+
29
+ ParseLine::Line.add_command(:example, /^example$/)
30
+
31
+
32
+ puts
33
+ puts "Welcome to the Programmable Metronome, by Billy Regodón"
34
+
35
+ def help
36
+ puts
37
+ puts "Use the following syntax: add:(t)nxd(T), to add a nxd bar of tempo t, T times"
38
+ puts "e.g.: add:(100)4x4(8) adds 8 bars of 4x4 with tempo 100"
39
+ puts
40
+ puts """Type
41
+ \"play\" to play the programmed bars
42
+ \"loop\" to play them repeatedly, press \'crtl\'+C to stop
43
+ \"loop:xf(N)\" to play the practice session repeatedly, speeding up xf in N repetitions
44
+ \"loop:t1,t2(N)\" to play the practice session, changing tempo from t1 to t2 in N repetitions
45
+ \"next_tempo:t\" to set the tempo of the next added bar, in beats per minute or bpm
46
+ you may use \"nt\" instead of \"next_tempo:t\"
47
+ \"force_tempo:t\" to change the tempo of the current practice session
48
+ you may use \"ft\" instead of \"force_tempo:t\"
49
+ \"sign:nxd\" to set a standard time signature, with a multiple of 2, up to 16
50
+ \"sign:f,f,f...\" to set an arbitrary odd bar, with f the duration of the beats
51
+ \"add:nxd\" to add a standard time signature with the set tempo
52
+ \"add:f,f,f...\" to add an arbitrary odd bar with the set tempo
53
+ \"add:(t)nxd(N)\" to add N bars of a standard time signature with tempo t (t and N optional)
54
+ \"add:(t)f,f,f...(N)\" to add N bars of an arbitrary odd bar with tempo t (t and N optional)
55
+ \"add\" to add one bar of the previously set type
56
+ \"add:N\" to add N bars of the previously set type
57
+ \"load:practice\" to load a previously saved practice session
58
+ \"save:practice\" to save the current practice session
59
+ \"list\" to list the available saved practice sessions
60
+ \"reset\" to reset to default and dismiss current practice session
61
+ \"help\" to print this help
62
+ \"help:command\" to print the help for another command
63
+ \"quit\" to exit"""
64
+ puts """\nIt is recommended that your create a practice folder to save your practice sessions
65
+ and that you run \"metronome-odd\" in that folder."""
66
+ puts """\nType \"example\" to print some example commands."""
67
+ puts
68
+ end
69
+
70
+ def help_command(command)
71
+ puts
72
+ puts "Type"
73
+ case command
74
+ when "play"
75
+ puts " \"play\" to play the programmed bars"
76
+ when "loop"
77
+ puts """ \"loop\" to play the programmed bars repeatedly, press \'crtl\'+C to stop
78
+ \"loop:xf(N)\" to play the practice session repeatedly, speeding xf in N repetitions
79
+ \"loop:t(N)\" to play the practice session repeatedly, changing tempo to t in N repetitions"""
80
+ when "tempo"
81
+ puts " \"tempo:t\" to set the tempo of the next added bar, in beats per minute or bpm"
82
+ when "force_tempo"
83
+ puts " \"force_tempo:t\" to change the tempo of the current practice session"
84
+ when "sign"
85
+ puts """ \"sign:nxd\" to set a standard time signature, with a multiple of 2, up to 16
86
+ \"sign:f,f,f...\" to set an arbitrary odd bar, with f the duration of the beats"""
87
+ when "add"
88
+ puts """ \"add:nxd\" to add a standard time signature with the set tempo
89
+ \"add:f,f,f...\" to add an arbitrary odd bar with the set tempo
90
+ \"add:(t)nxd(N)\" to add N bars of a standard time signature with tempo t (t and N optional)
91
+ \"add:(t)f,f,f...(N)\" to add N bars of an arbitrary odd bar with tempo t (t and N optional)
92
+ \"add\" to add one bar of the previously set type
93
+ \"add:N\" to add N bars of the previously set type"""
94
+ when "print"
95
+ puts " \"print\" prints the current practice session on the screen"
96
+ when "help"
97
+ puts """ \"help\" to print this help
98
+ \"help:command\" to print the help for another command"""
99
+ when "quit"
100
+ puts " \"quit\" to exit"
101
+ else
102
+ puts " Unknown command"
103
+ end
104
+ puts
105
+ end
106
+
107
+ def help_uninitialized
108
+ puts
109
+ puts " The practice session has not been initialized"
110
+ end
111
+
112
+ def help_example
113
+ puts """\nType:
114
+ \"add\" to add a default 4x4 bar
115
+ \"play\" to play it
116
+ \"loop\" to play it repeatedly until you press \'crtl\'+C
117
+ \"add:3x4\" to add a 3x4 bar after the previous one
118
+ \"loop\" to play them in loop
119
+ \"print\" to print the session on the screen without playing it
120
+ \"reset\" to go back to default empty session
121
+ \"play\" or \"loop\" to verify that we have an empty session
122
+ \"add:1.0,1.0,0.5,1.5,0.5(3)\" to add three non-standard 9x8 bars
123
+ \"add:4x4\" to add a standard 4x4 after them
124
+ \"play\" to play the practice session
125
+ \"print\" to print the session on the screen without playing it
126
+ \"tempo\" to check that the tempo is 100
127
+ \"force_tempo:150\" or \"ft:150\" to change the tempo of the session to 150
128
+ \"play\" to play the practice session
129
+ \"reset\" to go back to default empty session
130
+ \"add\" to add a default 4x4 bar at the default 100 tempo
131
+ \"next_tempo:150\" or \"nt:150\" to set the tempo for the next bar
132
+ \"add\" to add a default 4x4 bar at 150 tempo
133
+ \"loop\" to play them repeatedly until you press \'crtl\'+C"
134
+ puts
135
+
136
+ end
137
+
138
+ help
139
+
140
+ def reset
141
+ metronome = Metronome::Practice.new
142
+ metronome.push(Metronome::Silence.new(0.01))
143
+ tempo = 100
144
+ n = 4
145
+ d = 4
146
+ times = 1
147
+ beats = [1.0, 1.0, 1.0, 1.0]
148
+ prev_bar = :bar
149
+ non_initialized = true
150
+ [metronome, tempo, n, d, times, beats, prev_bar, non_initialized]
151
+ end
152
+
153
+ metronome, tempo, n, d, times, beats, prev_bar, non_initialized = reset
154
+
155
+ loop do
156
+ print(">>>")
157
+ line = ParseLine::Line.new($stdin.gets.chomp)
158
+ symbol, match = line.parse
159
+
160
+ case symbol
161
+ when :quit
162
+ break
163
+
164
+
165
+ when :play
166
+ unless non_initialized
167
+ metronome.play
168
+ puts
169
+ else
170
+ puts help_uninitialized
171
+ end
172
+
173
+
174
+ when :loop
175
+ unless non_initialized
176
+ puts "\nPlaying loop, press \'crtl\'+C to stop\n"
177
+ loop do
178
+ if metronome.loop_stop?
179
+ break
180
+ end
181
+ end
182
+ puts
183
+ else
184
+ puts help_uninitialized
185
+ end
186
+
187
+
188
+ when :loop_speed_up
189
+ unless non_initialized
190
+ puts "\nPlaying loop, press \'crtl\'+C to stop\n"
191
+ first_tempo = Float(match[:first])
192
+ second_tempo = Float(match[:second])
193
+ tempo = first_tempo
194
+ tempo_inc = second_tempo - first_tempo
195
+ times = Integer(match[:times])
196
+
197
+ if tempo_inc < 0.0
198
+ first_tempo, second_tempo = second_tempo, first_tempo
199
+ tempo_inc *= -1
200
+ end
201
+
202
+ inc = tempo_inc/times
203
+ loop do
204
+ if (second_tempo > first_tempo) && (second_tempo > tempo)
205
+ metronome.force_tempo(Integer(tempo))
206
+ tempo += inc
207
+ end
208
+ if metronome.loop_stop?
209
+ break
210
+ end
211
+ end
212
+
213
+ puts
214
+ else
215
+ puts help_uninitialized
216
+ end
217
+
218
+
219
+ when :print_tempo
220
+ puts "\n Set tempo is #{tempo}"
221
+ puts
222
+
223
+
224
+ when :next_tempo, :next_tempo_short
225
+ tempo = Integer(match[:tempo])
226
+
227
+
228
+ when :force_tempo, :force_tempo_short
229
+ metronome.force_tempo(Float(match[:tempo]))
230
+
231
+
232
+ when :signature
233
+ n = Integer(match[:n])
234
+ d = Integer(match[:d])
235
+ prev_bar = :bar
236
+
237
+
238
+ when :odd_signature
239
+ beats = []
240
+ match[:beats].split(',').each do |d| beats << d.to_f end
241
+
242
+ prev_bar = :odd_bar
243
+
244
+
245
+ when :add_bar
246
+ tempo = match[:tempo] ? Integer(match[:tempo]) : tempo
247
+ n = Integer(match[:n])
248
+ d = Integer(match[:d])
249
+ times = match[:times] ? Integer(match[:times]) : 1
250
+
251
+ (1..times).each do
252
+ metronome.push(Metronome::Bar.new(tempo, n, d))
253
+ end
254
+ prev_bar = :bar
255
+ non_initialized = false
256
+
257
+
258
+ when :odd_bar
259
+ tempo = match[:tempo] ? Integer(match[:tempo]) : tempo
260
+ times = match[:times] ? Integer(match[:times]) : 1
261
+ beats = []
262
+ match[:beats].split(',').each do |d| beats << d.to_f end
263
+
264
+ (1..times).each do
265
+ metronome.push(Metronome::OddBar.new(tempo, beats))
266
+ end
267
+ prev_bar = :odd_bar
268
+ non_initialized = false
269
+
270
+
271
+ when :add_1
272
+ if prev_bar == :bar
273
+ metronome.push(Metronome::Bar.new(tempo, n, d))
274
+ elsif prev_bar == :odd_bar
275
+ metronome.push(Metronome::OddBar.new(tempo, beats))
276
+ else
277
+ puts 181283
278
+ return -1
279
+ end
280
+ non_initialized = false
281
+
282
+
283
+ when :add
284
+ times = Integer(match[:times])
285
+ (1..times).each do
286
+ if prev_bar == :bar
287
+ metronome.push(Metronome::Bar.new(tempo, n, d))
288
+ elsif prev_bar == :odd_bar
289
+ metronome.push(Metronome::OddBar.new(tempo, beats))
290
+ else
291
+ puts 181743
292
+ return -1
293
+ end
294
+ end
295
+ non_initialized = false
296
+
297
+
298
+ when :print
299
+ unless non_initialized
300
+ metronome.print_practice
301
+ puts
302
+ else
303
+ puts help_uninitialized
304
+ end
305
+
306
+
307
+ when :save
308
+ practice_name = match[:practice]
309
+
310
+ if metronome.length > 1
311
+ if Save.save_exist?(practice_name)
312
+ puts "\n\"#{practice_name}\" has been already used, press \"o\" to overwrite"
313
+ if Keypress.read_char == "o"
314
+ puts "\b\n Overwriting..."
315
+ else
316
+ practice_name = :no_overwrite
317
+ puts "\b\n Cancelling..."
318
+ end
319
+ else
320
+ puts "\n Saving..."
321
+ end
322
+ unless practice_name == :no_overwrite
323
+ save_obj = Array.new
324
+ save_obj << metronome
325
+ save_obj << tempo
326
+ save_obj << n
327
+ save_obj << d
328
+ save_obj << times
329
+ save_obj << beats
330
+ save_obj << prev_bar
331
+ save_obj << non_initialized
332
+ if Save.save_obj(practice_name, save_obj) == 0
333
+ puts " #{practice_name} successfully saved."
334
+ else
335
+ puts " Could no save #{practice_name}"
336
+ end
337
+ end
338
+ else
339
+ puts "\n There is no practice session to save."
340
+ end
341
+ puts
342
+
343
+
344
+ when :load
345
+ practice_name = match[:practice]
346
+
347
+ if Save.save_exist?(practice_name)
348
+ if metronome.length > 1
349
+ puts "\n Are you sure to load a new practice session? Press \"y\" to continue."
350
+ unless Keypress.read_char == "y"
351
+ practice_name = :no_overload
352
+ puts "\b Cancelling..."
353
+ end
354
+ else
355
+ puts
356
+ end
357
+ unless practice_name == :no_overload
358
+ puts " Loading #{practice_name}..."
359
+ metronome, tempo, n, d, times, beats, prev_bar, non_initialized = Save.load_obj(practice_name)
360
+ end
361
+ else
362
+ puts "\n File not found"
363
+ end
364
+ puts
365
+
366
+
367
+ when :list
368
+ puts "\nAvailable practice sessions:"
369
+ Save.list.each do |n|
370
+ puts " #{n}"
371
+ end
372
+ puts
373
+
374
+
375
+ when :reset
376
+ puts "\nAre you sure to continue? Press \"y\" to confirm"
377
+ if Keypress.read_char == "y"
378
+ puts "\n Resetting..."
379
+ metronome, tempo, n, d, times, beats, prev_bar, non_initialized = reset
380
+ end
381
+ puts
382
+
383
+
384
+ when :help
385
+ help
386
+
387
+
388
+ when :empty
389
+
390
+
391
+ when :help_command
392
+ help_command(match[:command])
393
+
394
+
395
+ when :example
396
+ help_example
397
+
398
+
399
+ else
400
+ puts
401
+ puts " Unknown command"
402
+ puts
403
+ end
404
+ end
405
+
406
+ puts "Leaving..."
@@ -0,0 +1,261 @@
1
+ require 'metronome-odd/interruptible_sleep'
2
+ require 'metronome-odd/parse_line.rb'
3
+ require 'metronome-odd/save.rb'
4
+ require 'metronome-odd/keypress.rb'
5
+
6
+
7
+ module Metronome
8
+ class Sound
9
+ def initialize(sound_file)
10
+ @sound_file = sound_file
11
+ self
12
+ end
13
+
14
+ def is_sound_file?
15
+ @sound_file.extension == "aiff"
16
+ end
17
+
18
+ def play
19
+ spawn("afplay #{@sound_file}")
20
+ end
21
+
22
+ def set_sound(sound_file)
23
+ @sound_file = sound_file
24
+ end
25
+
26
+ private
27
+ def extension
28
+ @sound_file.split('.').last
29
+ end
30
+ end
31
+
32
+ class Silence
33
+ include Stop
34
+ include InterruptibleSleep
35
+ attr_accessor :duration
36
+
37
+ def initialize(duration)
38
+ @duration = duration
39
+ self
40
+ end
41
+
42
+ def wait
43
+ Signal.trap('SIGINT') do print "\b\bstopped"
44
+ @@stop = true
45
+ end
46
+ interruptible_sleep @duration
47
+ end
48
+
49
+ def play
50
+ self.wait
51
+ end
52
+
53
+ def print_sign
54
+ end
55
+ end
56
+
57
+ class Beat
58
+ attr_accessor :silence
59
+ def initialize(tempo, sound_file)
60
+ @sound = Sound.new(sound_file)
61
+ @silence = Silence.new(60.0/tempo)
62
+ self
63
+ end
64
+
65
+ def play
66
+ @sound.play
67
+ @silence.wait
68
+ end
69
+ end
70
+
71
+ class Bar
72
+ attr_writer :upbeat_sound_file
73
+ attr_writer :downbeat_sound_file
74
+ attr_reader :n
75
+ attr_reader :d
76
+ attr_accessor :tempo
77
+ attr_accessor :beat_array
78
+
79
+ def initialize(tempo, n, d)
80
+ data_dir = Gem.datadir("metronome-odd")
81
+ data_dir = data_dir ? data_dir : ""
82
+ @upbeat_sound_file = data_dir + "/beat_upbeat.aiff"
83
+ @downbeat_sound_file = data_dir + "/beat_downbeat.aiff"
84
+
85
+ @beat_array = Array.new
86
+ @beat_array.push(Beat.new(tempo*d/4.0, @upbeat_sound_file))
87
+
88
+ @n = n
89
+ @d = d
90
+
91
+ @tempo = tempo
92
+
93
+ (2..n).each do
94
+ @beat_array.push(Beat.new(tempo*d/4.0, @downbeat_sound_file))
95
+ end
96
+ self
97
+ end
98
+
99
+ def play
100
+ @beat_array.each {|b| b.play}
101
+ end
102
+
103
+ def print_sign
104
+ print "|#{@n}x#{@d}|"
105
+ end
106
+
107
+ def set_tempo(tempo)
108
+ old_tempo = Float(@tempo)
109
+
110
+ @beat_array.each do |beat|
111
+ beat.silence.duration *= old_tempo/tempo
112
+ end
113
+
114
+ @tempo = tempo
115
+ end
116
+
117
+ end
118
+
119
+ class OddBar < Bar
120
+ attr_accessor :tempo
121
+ attr_accessor :beat_array
122
+
123
+ def initialize(tempo, in_beat)
124
+ data_dir = Gem.datadir("metronome-odd")
125
+ data_dir = data_dir ? data_dir : ""
126
+ @upbeat_sound_file = data_dir + "/beat_upbeat.aiff"
127
+ @downbeat_sound_file = data_dir + "/beat_downbeat.aiff"
128
+
129
+ @beat_array = Array.new
130
+ @in_beat = in_beat
131
+ @tempo = tempo
132
+
133
+ upbeat_b = true
134
+ time = 1
135
+ prev_beat = 1
136
+ in_beat.each do |t|
137
+ if upbeat_b
138
+ beat = Beat.new(tempo/t, @upbeat_sound_file)
139
+ upbeat_b = false
140
+ else
141
+ beat = Beat.new(tempo/t, @downbeat_sound_file)
142
+ end
143
+ @beat_array.push(beat)
144
+ end
145
+
146
+ end
147
+
148
+ def print_sign
149
+ # In order to print an odd bar, firstly we find the beat sound times
150
+ # in in_sum, starting from 1.
151
+ # Then we add the silences in the beats in in_clone
152
+ # Finally we print the bar, with numbers in the beats and
153
+ # semicolons in the silences that occur in a beat
154
+ # width may be used to change size
155
+ width = 8
156
+ max_beat = @in_beat.sum
157
+ in_sum = [1.0]
158
+ acum = 1.0
159
+ @in_beat.each do |e| acum=acum+e;in_sum.push(acum) end
160
+ in_clone = in_sum.clone
161
+ (1...max_beat.floor).each do |d|
162
+ if not(in_clone.include?(d))
163
+ in_clone.push(Float(d))
164
+ end
165
+ end
166
+ in_clone = in_clone.sort
167
+
168
+ print "|"
169
+ in_clone[0...-1].each_with_index do |d, i|
170
+ if in_sum.include?(d)
171
+ if d == Integer(d)
172
+ print(':')
173
+ end
174
+ print(d.floor)
175
+ else
176
+ print(':')
177
+ end
178
+ if d<in_clone.last
179
+ t = Integer((in_clone[i+1]-in_clone[i])*width-1)
180
+ t.times do print " " end
181
+ end
182
+ end
183
+ print "|"
184
+ end
185
+ end
186
+
187
+ class Practice < Array
188
+ include Stop
189
+ @next_bar_hash = nil
190
+ @prev_bar = nil
191
+
192
+ def initialize
193
+ @next_bar_hash = Hash.new(nil)
194
+ end
195
+
196
+ def sound(type)
197
+ @@stop = false
198
+ bar_counter = 0 # 0 accounts for the intial silence
199
+
200
+ self.each do |b|
201
+ unless b.class == Silence
202
+ print "\n#{bar_counter}:\t"
203
+ b.print_sign
204
+ if @next_bar_hash[b]
205
+ print " -> "
206
+ @next_bar_hash[b].print_sign
207
+ print "\t"
208
+ else
209
+ case type
210
+ when :play
211
+ print " - last bar! "
212
+ when :loop
213
+ print " - repeat! "
214
+ end
215
+ end
216
+ end
217
+ b.play
218
+ bar_counter += 1
219
+ if @@stop then break end
220
+ end
221
+ puts
222
+ end
223
+
224
+ def play
225
+ sound(:play)
226
+ end
227
+
228
+ def loop_stop?
229
+ sound(:loop)
230
+ @@stop
231
+ end
232
+
233
+ def force_tempo(tempo)
234
+ self.each do |bar|
235
+ unless bar.class == Silence
236
+ bar.set_tempo(tempo)
237
+ end
238
+ end
239
+ end
240
+
241
+ def push(b)
242
+ super(b)
243
+ if @prev_bar
244
+ @next_bar_hash[@prev_bar] = b
245
+ end
246
+ @prev_bar = b
247
+ end
248
+
249
+ def print_practice
250
+ bar_counter = 0 # 0 accounts for the intial silence
251
+ self.each do |b|
252
+ unless b.class == Silence
253
+ print "\n#{bar_counter}:\t"
254
+ b.print_sign
255
+ end
256
+ bar_counter += 1
257
+ end
258
+ puts
259
+ end
260
+ end
261
+ end
@@ -0,0 +1,14 @@
1
+ module InterruptibleSleep
2
+ def interruptible_sleep(seconds)
3
+ @_sleep_check, @_sleep_interrupt = IO.pipe
4
+ IO.select([@_sleep_check], nil, nil, seconds)
5
+ end
6
+
7
+ def interrupt_sleep
8
+ @_sleep_interrupt.close if @_sleep_interrupt
9
+ end
10
+ end
11
+
12
+ module Stop
13
+ @@stop = false
14
+ end
@@ -0,0 +1,66 @@
1
+ #This has since been folded into a much more robust gem called Remedy. https://rubygems.org/gems/remedy & https://github.com/acook/remedy
2
+
3
+ require 'io/console'
4
+
5
+ # Reads keypresses from the user including 2 and 3 escape character sequences.
6
+ module Keypress
7
+ def Keypress.read_char
8
+ STDIN.echo = false
9
+ STDIN.raw!
10
+
11
+ input = STDIN.getc.chr
12
+ if input == "\e" then
13
+ input << STDIN.read_nonblock(3) rescue nil
14
+ input << STDIN.read_nonblock(2) rescue nil
15
+ end
16
+ ensure
17
+ STDIN.echo = true
18
+ STDIN.cooked!
19
+
20
+ return input
21
+ end
22
+
23
+ # oringal case statement from:
24
+ # http://www.alecjacobson.com/weblog/?p=75
25
+ def Keypress.show_single_key
26
+ c = read_char
27
+
28
+ case c
29
+ when " "
30
+ puts "SPACE"
31
+ when "\t"
32
+ puts "TAB"
33
+ when "\r"
34
+ puts "RETURN"
35
+ when "\n"
36
+ puts "LINE FEED"
37
+ when "\e"
38
+ puts "ESCAPE"
39
+ when "\e[A"
40
+ puts "UP ARROW"
41
+ when "\e[B"
42
+ puts "DOWN ARROW"
43
+ when "\e[C"
44
+ puts "RIGHT ARROW"
45
+ when "\e[D"
46
+ puts "LEFT ARROW"
47
+ when "\177"
48
+ puts "BACKSPACE"
49
+ when "\004"
50
+ puts "DELETE"
51
+ when "\e[3~"
52
+ puts "ALTERNATE DELETE"
53
+ when "\u0003"
54
+ puts "CONTROL-C"
55
+ exit 0
56
+ when /^.$/
57
+ puts "SINGLE CHAR HIT: #{c.inspect}"
58
+ else
59
+ puts "SOMETHING ELSE: #{c.inspect}"
60
+ end
61
+ end
62
+
63
+ def Keypress.test
64
+ show_single_key while(true)
65
+ end
66
+ end
@@ -0,0 +1,40 @@
1
+ module ParseLine
2
+ class Line
3
+ @@line_regexp_hash = nil
4
+ @help = false
5
+
6
+ def remove_separators
7
+ @line = @line.gsub(/\s+/, "")
8
+ end
9
+
10
+ def initialize(line)
11
+ if @@line_regexp_hash.nil?
12
+ @@line_regexp_hash = Hash.new([/^$/,/^$/])
13
+ end
14
+ @line = line
15
+ end
16
+
17
+ def Line.add_command(symbol, command)
18
+ if @@line_regexp_hash.nil?
19
+ @@line_regexp_hash = Hash.new([/^$/,[]])
20
+ end
21
+ @@line_regexp_hash[symbol] = command
22
+ end
23
+
24
+ def parse
25
+ c = nil
26
+ s = nil
27
+ self.remove_separators
28
+ @@line_regexp_hash.each do |symbol, command_regex|
29
+ if @line.match(command_regex)
30
+ c = @line.match(command_regex)
31
+ s = symbol
32
+ break
33
+ end
34
+ end
35
+ [s, c]
36
+ end
37
+ end
38
+ end
39
+
40
+
@@ -0,0 +1,57 @@
1
+ module Save
2
+ def self.file_name_format(short_name)
3
+ "./sp." + short_name + ".marshal"
4
+ end
5
+
6
+ def self.save_exist?(short_name)
7
+ File.exist?(Save.file_name_format(short_name))
8
+ end
9
+
10
+ def self.save_obj(short_name, obj)
11
+ file_name = Save.file_name_format(short_name)
12
+ file_handler = File.open(file_name, "w") {|to_file| Marshal.dump(obj, to_file)}
13
+ file_handler.close
14
+ if File.exist?(file_name)
15
+ return 0
16
+ else
17
+ return -1
18
+ end
19
+ end
20
+
21
+ def self.load_obj(short_name)
22
+ file_name = Save.file_name_format(short_name)
23
+ Save.load(file_name)
24
+ end
25
+
26
+ def self.delete_obj(short_name)
27
+ file_name = Save.file_name_format(short_name)
28
+ Save.delete(file_name)
29
+ end
30
+
31
+ def self.list
32
+ list = []
33
+ ls = Dir.entries(".")
34
+ ls.each do |f|
35
+ m = f.match(/^sp\.(?<name>\w+)\.marshal$/)
36
+ if m
37
+ list.push(m[:name])
38
+ end
39
+ end
40
+ list
41
+ end
42
+
43
+ def self.load(file_name)
44
+ if File.exist?(file_name)
45
+ begin
46
+ file_handler = File.open(file_name, "r")
47
+ obj = Marshal.load(file_handler)
48
+ file_handler.close
49
+ rescue
50
+ obj = false
51
+ end
52
+ else
53
+ obj = nil
54
+ end
55
+ obj
56
+ end
57
+ end
@@ -0,0 +1,13 @@
1
+ require './lib/metronome-odd.rb'
2
+ require 'test/unit'
3
+
4
+ class TestNAME < Test::Unit::TestCase
5
+
6
+ def test_practice
7
+ t = Bar.new(100, 6, 8)
8
+ p = Practice.new
9
+ (1..5).each {p.push(t)}
10
+ p.play
11
+ end
12
+
13
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: metronome-odd
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ platform: ruby
6
+ authors:
7
+ - Guillermo Regodón
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-11-23 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Metronome that can be programmed to change de tempo or the time signature.
14
+ email:
15
+ - guillermoregodon@gmail.com
16
+ executables:
17
+ - metronome-odd
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - bin/metronome-odd
22
+ - data/metronome-odd/beat_downbeat.aiff
23
+ - data/metronome-odd/beat_upbeat.aiff
24
+ - lib/metronome-odd.rb
25
+ - lib/metronome-odd/interruptible_sleep.rb
26
+ - lib/metronome-odd/keypress.rb
27
+ - lib/metronome-odd/parse_line.rb
28
+ - lib/metronome-odd/save.rb
29
+ - tests/test_metronome-odd.rb
30
+ homepage: https://github.com/GuillermoRegodon/rubygem-metronome-odd
31
+ licenses:
32
+ - MIT
33
+ metadata: {}
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ - data
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubygems_version: 3.0.3
51
+ signing_key:
52
+ specification_version: 4
53
+ summary: Programmable metronome
54
+ test_files:
55
+ - tests/test_metronome-odd.rb