jmtk 0.0.3.3-java → 0.4-java
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.
- checksums.yaml +15 -0
- data/DEVELOPMENT_NOTES.md +20 -0
- data/INTRO.md +63 -31
- data/README.md +9 -3
- data/Rakefile +42 -42
- data/bin/jmtk +75 -32
- data/bin/mtk +75 -32
- data/examples/drum_pattern.rb +2 -2
- data/examples/dynamic_pattern.rb +1 -1
- data/examples/helpers/output_selector.rb +71 -0
- data/examples/notation.rb +5 -1
- data/examples/tone_row_melody.rb +1 -1
- data/lib/mtk.rb +1 -0
- data/lib/mtk/core/duration.rb +18 -3
- data/lib/mtk/core/intensity.rb +5 -3
- data/lib/mtk/core/interval.rb +21 -14
- data/lib/mtk/core/pitch.rb +2 -0
- data/lib/mtk/core/pitch_class.rb +6 -3
- data/lib/mtk/events/event.rb +2 -1
- data/lib/mtk/events/note.rb +1 -1
- data/lib/mtk/events/parameter.rb +1 -0
- data/lib/mtk/events/rest.rb +85 -0
- data/lib/mtk/events/timeline.rb +6 -2
- data/lib/mtk/io/jsound_input.rb +9 -3
- data/lib/mtk/io/midi_file.rb +38 -2
- data/lib/mtk/io/midi_input.rb +1 -1
- data/lib/mtk/io/midi_output.rb +95 -4
- data/lib/mtk/io/unimidi_input.rb +7 -3
- data/lib/mtk/lang/durations.rb +31 -26
- data/lib/mtk/lang/intensities.rb +29 -30
- data/lib/mtk/lang/intervals.rb +108 -41
- data/lib/mtk/lang/mtk_grammar.citrus +14 -4
- data/lib/mtk/lang/parser.rb +10 -5
- data/lib/mtk/lang/pitch_classes.rb +45 -17
- data/lib/mtk/lang/pitches.rb +169 -32
- data/lib/mtk/lang/tutorial.rb +279 -0
- data/lib/mtk/lang/tutorial_lesson.rb +87 -0
- data/lib/mtk/sequencers/event_builder.rb +29 -8
- data/spec/mtk/core/duration_spec.rb +14 -1
- data/spec/mtk/core/intensity_spec.rb +1 -1
- data/spec/mtk/events/event_spec.rb +10 -16
- data/spec/mtk/events/note_spec.rb +3 -3
- data/spec/mtk/events/rest_spec.rb +184 -0
- data/spec/mtk/events/timeline_spec.rb +5 -1
- data/spec/mtk/io/midi_file_spec.rb +13 -2
- data/spec/mtk/io/midi_output_spec.rb +42 -9
- data/spec/mtk/lang/durations_spec.rb +5 -5
- data/spec/mtk/lang/intensities_spec.rb +5 -5
- data/spec/mtk/lang/intervals_spec.rb +139 -13
- data/spec/mtk/lang/parser_spec.rb +65 -25
- data/spec/mtk/lang/pitch_classes_spec.rb +0 -11
- data/spec/mtk/lang/pitches_spec.rb +0 -15
- data/spec/mtk/patterns/chain_spec.rb +7 -7
- data/spec/mtk/patterns/for_each_spec.rb +2 -2
- data/spec/mtk/sequencers/event_builder_spec.rb +49 -17
- metadata +12 -22
data/lib/mtk/core/pitch.rb
CHANGED
data/lib/mtk/core/pitch_class.rb
CHANGED
@@ -4,10 +4,10 @@ module MTK
|
|
4
4
|
# A set of all pitches that are an integer number of octaves apart.
|
5
5
|
# A {Pitch} has the same PitchClass as the pitches one or more octaves away.
|
6
6
|
# @see https://en.wikipedia.org/wiki/Pitch_class
|
7
|
-
#
|
7
|
+
# @see Lang::PitchClasses
|
8
8
|
class PitchClass
|
9
9
|
|
10
|
-
# The
|
10
|
+
# The preferred names of the 12 pitch classes in the chromatic scale.
|
11
11
|
# The index of each {#name} is the pitch class's numeric {#value}.
|
12
12
|
NAMES = %w( C Db D Eb E F Gb G Ab A Bb B ).freeze
|
13
13
|
|
@@ -15,7 +15,7 @@ module MTK
|
|
15
15
|
# organized such that each index contains the allowed names of the pitch class with a {#value} equal to that index.
|
16
16
|
# @see VALID_NAMES
|
17
17
|
VALID_NAMES_BY_VALUE =
|
18
|
-
[ # (valid names ), # value #
|
18
|
+
[ # (valid names ), # value # preferred name
|
19
19
|
%w( B# C Dbb ), # 0 # C
|
20
20
|
%w( B## C# Db ), # 1 # Db
|
21
21
|
%w( C## D Ebb ), # 2 # D
|
@@ -48,6 +48,9 @@ module MTK
|
|
48
48
|
|
49
49
|
# The value of this pitch class.
|
50
50
|
# An integer from 0..11 that indexes this pitch class in {PITCH_CLASSES} and the {#name} in {NAMES}.
|
51
|
+
#
|
52
|
+
# This value is fairly arbitrary and just used for sorting purposes and mod 12 arithmetic when composing
|
53
|
+
# directly with pitch classes.
|
51
54
|
attr_reader :value
|
52
55
|
|
53
56
|
|
data/lib/mtk/events/event.rb
CHANGED
@@ -51,7 +51,7 @@ module MTK
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def to_h
|
54
|
-
hash = {:
|
54
|
+
hash = {type: @type}
|
55
55
|
hash[:value] = @value unless @value.nil?
|
56
56
|
hash[:duration] = @duration unless @duration.nil?
|
57
57
|
hash[:number] = @number unless @number.nil?
|
@@ -82,6 +82,7 @@ module MTK
|
|
82
82
|
@duration.length
|
83
83
|
end
|
84
84
|
|
85
|
+
# True if this event represents a rest, false otherwise.
|
85
86
|
# By convention, any events with negative durations are a rest
|
86
87
|
def rest?
|
87
88
|
@duration.rest?
|
data/lib/mtk/events/note.rb
CHANGED
data/lib/mtk/events/parameter.rb
CHANGED
@@ -0,0 +1,85 @@
|
|
1
|
+
module MTK
|
2
|
+
|
3
|
+
module Events
|
4
|
+
|
5
|
+
# An interval of silence.
|
6
|
+
#
|
7
|
+
# By convention, MTK uses {Core::Duration}s with negative values for rests. This class forces the {#duration}
|
8
|
+
# to always have a negative value.
|
9
|
+
#
|
10
|
+
# @note Because a negative durations indicate rests, other {Event} classes may represent rests too.
|
11
|
+
# Therefore, you should always determine if an {Event} is a rest via the {#rest?} method, instead
|
12
|
+
# of seeing if the class is an MTK::Events::Rest
|
13
|
+
class Rest < Event
|
14
|
+
|
15
|
+
def initialize(duration, channel=nil)
|
16
|
+
super :rest, duration:duration, channel:channel
|
17
|
+
self.duration = @duration # force to be a rest
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.from_h(hash)
|
21
|
+
new(hash[:duration], hash[:channel])
|
22
|
+
end
|
23
|
+
|
24
|
+
# Assign the duration, forcing to a negative value to indicate this is a rest.
|
25
|
+
def duration= duration
|
26
|
+
super
|
27
|
+
@duration = -@duration unless @duration.rest? # force to be a rest
|
28
|
+
end
|
29
|
+
|
30
|
+
# Rests don't have a corresponding value in MIDI, so this is nil
|
31
|
+
# @return nil
|
32
|
+
def midi_value
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# Rests don't have a corresponding value in MIDI, so this is a no-op
|
37
|
+
def midi_value= value
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
"Rest(#{@duration})"
|
42
|
+
end
|
43
|
+
|
44
|
+
def inspect
|
45
|
+
"#<#{self.class}:#{object_id} @duration=#{@duration.inspect}" +
|
46
|
+
if @channel then ", @channel=#{@channel}>" else '>' end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
# Construct a {Events::Rest} from a list of any supported type for the arguments: pitch, intensity, duration, channel
|
54
|
+
def Rest(*anything)
|
55
|
+
anything = anything.first if anything.size == 1
|
56
|
+
case anything
|
57
|
+
when MTK::Events::Rest then anything
|
58
|
+
when MTK::Events::Event then MTK::Events::Rest.new(anything.duration, anything.channel)
|
59
|
+
when Numeric then MTK::Events::Rest.new(anything)
|
60
|
+
when Duration then MTK::Events::Rest.new(anything)
|
61
|
+
|
62
|
+
when Array
|
63
|
+
duration = nil
|
64
|
+
channel = nil
|
65
|
+
unknowns = []
|
66
|
+
anything.each do |item|
|
67
|
+
case item
|
68
|
+
when MTK::Core::Duration then duration = item
|
69
|
+
else unknowns << item
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
duration = MTK.Duration(unknowns.shift) if duration.nil? and not unknowns.empty?
|
74
|
+
raise "MTK::Rest() couldn't find a duration in arguments: #{anything.inspect}" if duration.nil?
|
75
|
+
channel = unknowns.shift.to_i if channel.nil? and not unknowns.empty?
|
76
|
+
|
77
|
+
MTK::Events::Rest.new(duration, channel)
|
78
|
+
|
79
|
+
else
|
80
|
+
raise "MTK::Rest() doesn't understand #{anything.class}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
module_function :Rest
|
84
|
+
|
85
|
+
end
|
data/lib/mtk/events/timeline.rb
CHANGED
@@ -212,8 +212,12 @@ module MTK
|
|
212
212
|
def to_s
|
213
213
|
times = self.times
|
214
214
|
last = times.last
|
215
|
-
|
216
|
-
|
215
|
+
if last
|
216
|
+
width = sprintf("%d",last).length + 3 # nicely align the '=>' against the longest number
|
217
|
+
times.map{|t| sprintf("%#{width}.2f",t)+" => #{@timeline[t].join ', '}" }.join "\n"
|
218
|
+
else
|
219
|
+
''
|
220
|
+
end
|
217
221
|
end
|
218
222
|
|
219
223
|
def inspect
|
data/lib/mtk/io/jsound_input.rb
CHANGED
@@ -50,7 +50,7 @@ module MTK
|
|
50
50
|
def to_timeline(options={})
|
51
51
|
bpm = options.fetch :bmp, 120
|
52
52
|
beats_per_second = bpm.to_f/60
|
53
|
-
timeline = Timeline.new
|
53
|
+
timeline = MTK::Events::Timeline.new
|
54
54
|
note_ons = {}
|
55
55
|
start = nil
|
56
56
|
|
@@ -59,7 +59,13 @@ module MTK
|
|
59
59
|
time -= start
|
60
60
|
time /= beats_per_second
|
61
61
|
|
62
|
-
|
62
|
+
message_type = message.type
|
63
|
+
message_type = :note_off if message_type == :note_on and message.velocity == 0
|
64
|
+
# TODO: this will need to be made more robust when we support off velocities
|
65
|
+
|
66
|
+
next if message_type == :unknown # Ignore garbage messages
|
67
|
+
|
68
|
+
case message_type
|
63
69
|
when :note_on
|
64
70
|
note_ons[message.pitch] = [message,time]
|
65
71
|
|
@@ -71,7 +77,7 @@ module MTK
|
|
71
77
|
timeline.add time,note
|
72
78
|
end
|
73
79
|
|
74
|
-
else timeline.add time, MTK::Events::Parameter.from_midi([
|
80
|
+
else timeline.add time, MTK::Events::Parameter.from_midi([message_type, message.channel], message.data1, message.data2)
|
75
81
|
end
|
76
82
|
end
|
77
83
|
|
data/lib/mtk/io/midi_file.rb
CHANGED
@@ -41,6 +41,10 @@ module MTK
|
|
41
41
|
case event
|
42
42
|
when ::MIDI::NoteOn
|
43
43
|
note_ons[event.note] = [time,event]
|
44
|
+
# TODO: handle note ons with velocity 0 as a note off (use output from Logic Pro as a test case)
|
45
|
+
# This isn't actually necessary right now as midilibs seqreader#note_on automatically
|
46
|
+
# converts note ons with velocity 0 to note offs. In the future, for full off velocity support,
|
47
|
+
# I'll need to monkey patch midilib and update the code here
|
44
48
|
|
45
49
|
when ::MIDI::NoteOff
|
46
50
|
on_time,on_event = note_ons.delete(event.note)
|
@@ -99,7 +103,11 @@ module MTK
|
|
99
103
|
pitch, velocity = event.midi_pitch, event.velocity
|
100
104
|
add_event track, time => note_on(channel, pitch, velocity)
|
101
105
|
duration = event.duration_in_pulses(clock_rate)
|
102
|
-
|
106
|
+
# TODO: use note_off events when supporting off velocities
|
107
|
+
# add_event track, time+duration => note_off(channel, pitch, velocity)
|
108
|
+
# NOTE: cannot test the following line of code properly right now, because midilib automatically converts
|
109
|
+
# note ons with velocity 0 to note offs when reading files. See comments in #to_timelines in this file
|
110
|
+
add_event track, time+duration => note_on(channel, pitch, 0) # we use note ons with velocity 0 to indicate no off velocity
|
103
111
|
|
104
112
|
when :control
|
105
113
|
add_event track, time => cc(channel, event.number, event.midi_value)
|
@@ -182,8 +190,10 @@ module MTK
|
|
182
190
|
|
183
191
|
def add_track sequence, opts={}
|
184
192
|
track = ::MIDI::Track.new(sequence)
|
185
|
-
|
193
|
+
track_name = opts[:name]
|
194
|
+
track.name = track_name if track_name
|
186
195
|
sequence.tracks << track
|
196
|
+
sequence.format = if sequence.tracks.length > 1 then 1 else 0 end
|
187
197
|
track
|
188
198
|
end
|
189
199
|
|
@@ -207,3 +217,29 @@ module MTK
|
|
207
217
|
|
208
218
|
end
|
209
219
|
|
220
|
+
|
221
|
+
####################################################################################
|
222
|
+
# MONKEY PATCHING to prevent blank instrument name meta events from being generated
|
223
|
+
# This can be removed if my pull request https://github.com/jimm/midilib/pull/5
|
224
|
+
# is merged into midilib and a new gem is released with these changes.
|
225
|
+
|
226
|
+
# @private
|
227
|
+
module MIDI
|
228
|
+
module IO
|
229
|
+
class SeqWriter
|
230
|
+
alias original_write_instrument write_instrument
|
231
|
+
def write_instrument(instrument)
|
232
|
+
original_write_instrument(instrument) unless instrument.nil?
|
233
|
+
end
|
234
|
+
|
235
|
+
# Also monkey patching write_header to support alternate MIDI file formats
|
236
|
+
def write_header
|
237
|
+
@io.print 'MThd'
|
238
|
+
write32(6)
|
239
|
+
write16(@seq.format || 1)
|
240
|
+
write16(@seq.tracks.length)
|
241
|
+
write16(@seq.ppqn)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
data/lib/mtk/io/midi_input.rb
CHANGED
data/lib/mtk/io/midi_output.rb
CHANGED
@@ -105,8 +105,12 @@ module MTK
|
|
105
105
|
pitch = event.midi_pitch
|
106
106
|
velocity = event.velocity
|
107
107
|
duration = event.duration.to_f
|
108
|
-
|
109
|
-
|
108
|
+
# Set a lower priority (via level:1) for note-ons, so legato notes at the same pitch don't
|
109
|
+
# prematurely chop off the next note, by ensuring all note-offs at the same timepoint occur first.
|
110
|
+
@scheduler.at(time, level: 1) { note_on(pitch,velocity,channel) }
|
111
|
+
@scheduler.at(time + duration) { note_on(pitch,0,channel) }
|
112
|
+
# TODO: use a proper note off message whenever we support off velocities
|
113
|
+
#@scheduler.at(time + duration) { note_off(pitch,velocity,channel) }
|
110
114
|
|
111
115
|
when :control
|
112
116
|
@scheduler.at(time) { control(event.number, event.midi_value, channel) }
|
@@ -127,7 +131,10 @@ module MTK
|
|
127
131
|
end
|
128
132
|
end
|
129
133
|
|
130
|
-
end_time = timeline.times.last
|
134
|
+
end_time = timeline.times.last
|
135
|
+
final_events = timeline[end_time]
|
136
|
+
max_length = final_events.inject(0) {|max,event| len = event.length; max > len ? max : len } || 0
|
137
|
+
end_time += max_length + trailing_buffer
|
131
138
|
@scheduler.at(end_time) { @scheduler.stop }
|
132
139
|
|
133
140
|
thread = @scheduler.run
|
@@ -192,4 +199,88 @@ unless $__RUNNING_RSPEC_TESTS__ # I can't get this working on Travis-CI, problem
|
|
192
199
|
else
|
193
200
|
require 'mtk/io/unimidi_output'
|
194
201
|
end
|
195
|
-
end
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
|
206
|
+
####################################################################################
|
207
|
+
# MONKEY PATCHING gamelan to ensure note-offs come before note-ons
|
208
|
+
# when they occur at the same scheduler time.
|
209
|
+
# This patch applies https://github.com/jvoorhis/gamelan/commit/20d93f4e5d86517bd5c6f9212a0dcdbf371d1ea1
|
210
|
+
# to provide the priority/level feature for the scheduler (see @scheduler.at() calls for notes above).
|
211
|
+
# This patch should not be necessary when gamelan gem > 0.3 is released
|
212
|
+
|
213
|
+
# @private
|
214
|
+
module Gamelan
|
215
|
+
|
216
|
+
DEFAULT_PRIORITY = 0
|
217
|
+
NOTEON_PRIORITY = 0
|
218
|
+
CC_PRIORITY = -1
|
219
|
+
PC_PRIORITY = -2
|
220
|
+
NOTEOFF_PRIORITY = -3
|
221
|
+
|
222
|
+
class Priority < Struct.new(:time, :level)
|
223
|
+
include Comparable
|
224
|
+
def <=>(prio)
|
225
|
+
self.to_a <=> prio.to_a
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
if defined?(JRUBY_VERSION)
|
230
|
+
|
231
|
+
class Queue
|
232
|
+
def initialize(scheduler)
|
233
|
+
@scheduler = scheduler
|
234
|
+
@queue = PriorityQueue.new(10000) { |a,b|
|
235
|
+
a.priority <=> b.priority
|
236
|
+
}
|
237
|
+
end
|
238
|
+
|
239
|
+
def ready?
|
240
|
+
if top = @queue.peek
|
241
|
+
top.delay < @scheduler.phase
|
242
|
+
else
|
243
|
+
false
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
else
|
249
|
+
|
250
|
+
class Queue
|
251
|
+
def push(task)
|
252
|
+
@queue.push(task, task.priority)
|
253
|
+
end
|
254
|
+
alias << push
|
255
|
+
|
256
|
+
def ready?
|
257
|
+
if top = @queue.min
|
258
|
+
top[1].time < @scheduler.phase
|
259
|
+
else
|
260
|
+
false
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
|
267
|
+
class Scheduler < Timer
|
268
|
+
def at(delay, options = {}, &task)
|
269
|
+
options = { :delay => delay, :scheduler => self }.merge(options)
|
270
|
+
@queue << Task.new(options, &task)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
class Task
|
275
|
+
attr_reader :level, :priority
|
276
|
+
|
277
|
+
def initialize(options, &block)
|
278
|
+
@scheduler = options[:scheduler]
|
279
|
+
@delay = options[:delay].to_f
|
280
|
+
@level = options[:level] || DEFAULT_PRIORITY
|
281
|
+
@priority = Priority.new(@delay, @level)
|
282
|
+
@proc = block
|
283
|
+
@args = options[:args] || []
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
data/lib/mtk/io/unimidi_input.rb
CHANGED
@@ -59,7 +59,7 @@ module MTK
|
|
59
59
|
|
60
60
|
bpm = options.fetch :bmp, 120
|
61
61
|
beats_per_second = bpm.to_f/60
|
62
|
-
timeline = Timeline.new
|
62
|
+
timeline = MTK::Events::Timeline.new
|
63
63
|
note_ons = {}
|
64
64
|
start = nil
|
65
65
|
|
@@ -69,9 +69,13 @@ module MTK
|
|
69
69
|
time /= beats_per_second
|
70
70
|
|
71
71
|
if message.is_a? MTK::Events::Event
|
72
|
-
timeline.add time,message
|
72
|
+
timeline.add time,message unless message.type == :unknown
|
73
73
|
else
|
74
|
-
|
74
|
+
message_type = message.type
|
75
|
+
message_type = :note_off if message_type == :note_on and message.velocity == 0
|
76
|
+
# TODO: this will need to be made more robust when we support off velocities
|
77
|
+
|
78
|
+
case message_type
|
75
79
|
when :note_on
|
76
80
|
pitch = message.pitch
|
77
81
|
note_ons[pitch] = [message,time]
|
data/lib/mtk/lang/durations.rb
CHANGED
@@ -5,51 +5,56 @@ module MTK
|
|
5
5
|
|
6
6
|
# Defines duration constants using abbreviations for standard rhythm values ('w' for whole note, 'h' for half note, etc).
|
7
7
|
#
|
8
|
-
#
|
8
|
+
# These can be thought of like constants, but in order to support the lower case names,
|
9
|
+
# it was necessary to define them as "pseudo constant" methods.
|
10
|
+
# Like constants, these methods are available either through the module (MTK::Lang::Durations::q) or
|
11
|
+
# via mixin (include MTK::Lang::Durations; q). They are listed under the "Instance Attribute Summary" of this page.
|
9
12
|
#
|
10
|
-
# These
|
11
|
-
#
|
12
|
-
# The methods are available either through the module (MTK::Core::Durations::e) or via mixin (include MTK::Core::Durations; q)
|
13
|
-
#
|
14
|
-
# These values assume the quarter note is one beat (1.0), so they work best with 4/4 and other */4 time signatures.
|
13
|
+
# These values assume the quarter note is one beat, so you may find they work best with 4/4 and other */4 time signatures
|
14
|
+
# (although it's certainly possible to use them with less common time signatures like 5/8).
|
15
15
|
#
|
16
16
|
# @note Including this module defines a bunch of single-character variables, which may shadow existing variable names.
|
17
17
|
# Just be mindful of what is defined in this module when including it.
|
18
18
|
#
|
19
|
-
# @see
|
19
|
+
# @see Core::Duration
|
20
|
+
# @see Events::Note
|
20
21
|
module Durations
|
21
22
|
extend MTK::Lang::PseudoConstants
|
22
23
|
|
23
|
-
#
|
24
|
-
|
24
|
+
# @private
|
25
|
+
# @!macro [attach] define_duration
|
26
|
+
# $3: $4 beat(s)
|
27
|
+
# @!attribute [r]
|
28
|
+
# @return [MTK::Core::Duration] duration of $4 beat(s)
|
29
|
+
def self.define_duration name, value, description, beats
|
30
|
+
define_constant name, value
|
31
|
+
end
|
32
|
+
|
33
|
+
|
25
34
|
# whole note
|
26
|
-
# @macro [attach] durations.
|
35
|
+
# @macro [attach] durations.define_duration
|
27
36
|
# @attribute [r]
|
28
|
-
# @return [$2]
|
29
|
-
|
37
|
+
# @return [$2] duration for $1
|
38
|
+
define_duration 'w', MTK::Core::Duration[4], 'whole note', 4
|
39
|
+
|
40
|
+
define_duration 'h', MTK::Core::Duration[2], 'half note', 2
|
30
41
|
|
31
|
-
|
32
|
-
define_constant 'h', MTK::Core::Duration[2]
|
42
|
+
define_duration 'q', MTK::Core::Duration[1], 'quarter note', 1
|
33
43
|
|
34
|
-
|
35
|
-
define_constant 'q', MTK::Core::Duration[1]
|
44
|
+
define_duration 'e', MTK::Core::Duration[Rational(1,2)], 'eighth note', '1/2'
|
36
45
|
|
37
|
-
|
38
|
-
define_constant 'i', MTK::Core::Duration[Rational(1,2)]
|
46
|
+
define_duration 's', MTK::Core::Duration[Rational(1,4)], 'sixteenth note', '1/4'
|
39
47
|
|
40
|
-
|
41
|
-
define_constant 's', MTK::Core::Duration[Rational(1,4)]
|
48
|
+
define_duration 'r', MTK::Core::Duration[Rational(1,8)], 'thirty-second note', '1/8'
|
42
49
|
|
43
|
-
|
44
|
-
define_constant 'r', MTK::Core::Duration[Rational(1,8)]
|
50
|
+
define_duration 'x', MTK::Core::Duration[Rational(1,16)], 'sixty-fourth note', '1/16'
|
45
51
|
|
46
|
-
# sixty-fourth note
|
47
|
-
define_constant 'x', MTK::Core::Duration[Rational(1,16)]
|
48
52
|
|
49
|
-
#
|
50
|
-
DURATIONS = [w, h, q,
|
53
|
+
# All "psuedo constants" defined in this module
|
54
|
+
DURATIONS = [w, h, q, e, s, r, x].freeze
|
51
55
|
|
52
56
|
# The names of all "psuedo constants" defined in this module
|
57
|
+
# @see MTK::Core::Duration::NAMES
|
53
58
|
DURATION_NAMES = MTK::Core::Duration::NAMES
|
54
59
|
|
55
60
|
end
|