midilib 2.0.5 → 3.0.1
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 +5 -5
- data/ChangeLog +2 -1
- data/Credits +39 -2
- data/README.rdoc +5 -6
- data/Rakefile +36 -49
- data/TODO.rdoc +13 -2
- data/examples/from_scratch.rb +1 -1
- data/examples/measures_mbt.rb +11 -11
- data/examples/print_program_changes.rb +2 -2
- data/examples/reader2text.rb +47 -47
- data/examples/seq2text.rb +1 -1
- data/examples/split.rb +4 -3
- data/examples/strings.rb +4 -4
- data/examples/transpose.rb +19 -20
- data/install.rb +21 -13
- data/lib/midilib/consts.rb +237 -239
- data/lib/midilib/event.rb +128 -100
- data/lib/midilib/info.rb +3 -5
- data/lib/midilib/io/midifile.rb +155 -182
- data/lib/midilib/io/seqreader.rb +80 -85
- data/lib/midilib/io/seqwriter.rb +93 -88
- data/lib/midilib/measure.rb +78 -80
- data/lib/midilib/mergesort.rb +39 -0
- data/lib/midilib/sequence.rb +40 -32
- data/lib/midilib/track.rb +16 -57
- data/lib/midilib/utils.rb +4 -7
- data/lib/midilib.rb +5 -5
- data/test/event_equality.rb +28 -30
- data/test/test_event.rb +9 -11
- data/test/test_io.rb +83 -3
- data/test/test_mergesort.rb +37 -0
- data/test/test_midifile.rb +6 -19
- data/test/test_sequence.rb +5 -4
- data/test/test_track.rb +9 -38
- data/test/test_varlen.rb +1 -3
- metadata +8 -95
- data/html/IO.html +0 -155
- data/html/MIDI/ActiveSense.html +0 -206
- data/html/MIDI/ChannelEvent.html +0 -231
- data/html/MIDI/ChannelPressure.html +0 -265
- data/html/MIDI/Clock.html +0 -206
- data/html/MIDI/Continue.html +0 -206
- data/html/MIDI/Controller.html +0 -280
- data/html/MIDI/Event.html +0 -489
- data/html/MIDI/IO/MIDIFile.html +0 -2024
- data/html/MIDI/IO/SeqReader.html +0 -904
- data/html/MIDI/IO/SeqWriter.html +0 -572
- data/html/MIDI/IO.html +0 -95
- data/html/MIDI/KeySig.html +0 -353
- data/html/MIDI/MIDI/MIDI/Array.html +0 -255
- data/html/MIDI/MIDI/MIDI.html +0 -95
- data/html/MIDI/MIDI.html +0 -95
- data/html/MIDI/Marker.html +0 -158
- data/html/MIDI/Measure.html +0 -328
- data/html/MIDI/Measures.html +0 -285
- data/html/MIDI/MetaEvent.html +0 -461
- data/html/MIDI/NoteEvent.html +0 -331
- data/html/MIDI/NoteOff.html +0 -228
- data/html/MIDI/NoteOn.html +0 -228
- data/html/MIDI/PitchBend.html +0 -266
- data/html/MIDI/PolyPressure.html +0 -277
- data/html/MIDI/ProgramChange.html +0 -265
- data/html/MIDI/Realtime.html +0 -242
- data/html/MIDI/Sequence.html +0 -896
- data/html/MIDI/SongPointer.html +0 -266
- data/html/MIDI/SongSelect.html +0 -265
- data/html/MIDI/Start.html +0 -206
- data/html/MIDI/Stop.html +0 -206
- data/html/MIDI/SystemCommon.html +0 -158
- data/html/MIDI/SystemExclusive.html +0 -268
- data/html/MIDI/SystemReset.html +0 -206
- data/html/MIDI/Tempo.html +0 -396
- data/html/MIDI/TimeSig.html +0 -388
- data/html/MIDI/Track.html +0 -695
- data/html/MIDI/TuneRequest.html +0 -242
- data/html/MIDI/Utils.html +0 -220
- data/html/MIDI.html +0 -547
- data/html/README_rdoc.html +0 -731
- data/html/TODO_rdoc.html +0 -125
- data/html/created.rid +0 -14
- data/html/css/fonts.css +0 -167
- data/html/css/rdoc.css +0 -590
- data/html/fonts/Lato-Light.ttf +0 -0
- data/html/fonts/Lato-LightItalic.ttf +0 -0
- data/html/fonts/Lato-Regular.ttf +0 -0
- data/html/fonts/Lato-RegularItalic.ttf +0 -0
- data/html/fonts/SourceCodePro-Bold.ttf +0 -0
- data/html/fonts/SourceCodePro-Regular.ttf +0 -0
- data/html/images/add.png +0 -0
- data/html/images/arrow_up.png +0 -0
- data/html/images/brick.png +0 -0
- data/html/images/brick_link.png +0 -0
- data/html/images/bug.png +0 -0
- data/html/images/bullet_black.png +0 -0
- data/html/images/bullet_toggle_minus.png +0 -0
- data/html/images/bullet_toggle_plus.png +0 -0
- data/html/images/date.png +0 -0
- data/html/images/delete.png +0 -0
- data/html/images/find.png +0 -0
- data/html/images/loadingAnimation.gif +0 -0
- data/html/images/macFFBgHack.png +0 -0
- data/html/images/package.png +0 -0
- data/html/images/page_green.png +0 -0
- data/html/images/page_white_text.png +0 -0
- data/html/images/page_white_width.png +0 -0
- data/html/images/plugin.png +0 -0
- data/html/images/ruby.png +0 -0
- data/html/images/tag_blue.png +0 -0
- data/html/images/tag_green.png +0 -0
- data/html/images/transparent.png +0 -0
- data/html/images/wrench.png +0 -0
- data/html/images/wrench_orange.png +0 -0
- data/html/images/zoom.png +0 -0
- data/html/index.html +0 -768
- data/html/js/darkfish.js +0 -161
- data/html/js/jquery.js +0 -4
- data/html/js/navigation.js +0 -142
- data/html/js/navigation.js.gz +0 -0
- data/html/js/search.js +0 -109
- data/html/js/search_index.js +0 -1
- data/html/js/search_index.js.gz +0 -0
- data/html/js/searcher.js +0 -228
- data/html/js/searcher.js.gz +0 -0
- data/html/table_of_contents.html +0 -1265
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# This code was originally taken from
|
|
2
|
+
# http://github.com/adamjmurray/cosy/blob/master/lib/cosy/helper/midi_file_renderer_helper.rb
|
|
3
|
+
# with permission from Adam Murray, who originally suggested this fix.
|
|
4
|
+
# See http://wiki.github.com/adamjmurray/cosy/midilib-notes for details.
|
|
5
|
+
|
|
6
|
+
# A stable sorting algorithm that maintains the relative order of equal
|
|
7
|
+
# elements.
|
|
8
|
+
#
|
|
9
|
+
# This code used to be in a new subclass of Array, but that started causing
|
|
10
|
+
# problems in Ruby 3.0, apparently due to the return type of the `[]`
|
|
11
|
+
# operator which was the parent Array class.
|
|
12
|
+
#
|
|
13
|
+
# This code borrowed from 'Moser' http://codesnippets.joyent.com/posts/show/1699
|
|
14
|
+
def mergesort(arr, &cmp)
|
|
15
|
+
cmp = ->(a, b) { a <=> b } if cmp.nil?
|
|
16
|
+
if arr.size <= 1
|
|
17
|
+
arr.dup
|
|
18
|
+
else
|
|
19
|
+
halves = mergesort_split(arr).map { |half| mergesort(half, &cmp) }
|
|
20
|
+
mergesort_merge(*halves, &cmp)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def mergesort_split(arr)
|
|
25
|
+
n = (arr.length / 2).floor - 1
|
|
26
|
+
[arr[0..n], arr[n + 1..-1]]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def mergesort_merge(first, second, &predicate)
|
|
30
|
+
result = []
|
|
31
|
+
until first.empty? || second.empty?
|
|
32
|
+
result << if predicate.call(first.first, second.first) <= 0
|
|
33
|
+
first.shift
|
|
34
|
+
else
|
|
35
|
+
second.shift
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
result.concat(first).concat(second)
|
|
39
|
+
end
|
data/lib/midilib/sequence.rb
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
require_relative 'io/seqreader'
|
|
2
|
+
require_relative 'io/seqwriter'
|
|
3
|
+
require_relative 'measure'
|
|
4
4
|
|
|
5
5
|
module MIDI
|
|
6
|
-
|
|
7
6
|
# A MIDI::Sequence contains MIDI::Track objects.
|
|
8
7
|
class Sequence
|
|
9
|
-
|
|
10
8
|
include Enumerable
|
|
11
9
|
|
|
12
10
|
UNNAMED = 'Unnamed Sequence'
|
|
@@ -43,13 +41,13 @@ module MIDI
|
|
|
43
41
|
attr_accessor :writer_class
|
|
44
42
|
|
|
45
43
|
def initialize
|
|
46
|
-
@tracks =
|
|
44
|
+
@tracks = []
|
|
47
45
|
@ppqn = 480
|
|
48
46
|
|
|
49
47
|
# Time signature
|
|
50
|
-
@numer = 4
|
|
48
|
+
@numer = 4 # Numer + denom = 4/4 time default
|
|
51
49
|
@denom = 2
|
|
52
|
-
@clocks = 24
|
|
50
|
+
@clocks = 24 # Bug fix Nov 11, 2007 - this is not the same as ppqn!
|
|
53
51
|
@qnotes = 8
|
|
54
52
|
|
|
55
53
|
@reader_class = IO::SeqReader
|
|
@@ -67,24 +65,25 @@ module MIDI
|
|
|
67
65
|
# Returns the song tempo in beats per minute.
|
|
68
66
|
def beats_per_minute
|
|
69
67
|
return DEFAULT_TEMPO if @tracks.nil? || @tracks.empty?
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
|
|
69
|
+
event = @tracks.first.events.detect { |e| e.is_a?(MIDI::Tempo) }
|
|
70
|
+
event ? Tempo.mpq_to_bpm(event.tempo) : DEFAULT_TEMPO
|
|
72
71
|
end
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
alias bpm beats_per_minute
|
|
73
|
+
alias tempo beats_per_minute
|
|
75
74
|
|
|
76
75
|
# Pulses (also called ticks) are the units of delta times and event
|
|
77
76
|
# time_from_start values. This method converts a number of pulses to a
|
|
78
77
|
# float value that is a time in seconds.
|
|
79
78
|
def pulses_to_seconds(pulses)
|
|
80
|
-
(pulses.to_f / @ppqn.to_f / beats_per_minute
|
|
79
|
+
(pulses.to_f / @ppqn.to_f / beats_per_minute) * 60.0
|
|
81
80
|
end
|
|
82
81
|
|
|
83
82
|
# Given a note length name like "whole", "dotted quarter", or "8th
|
|
84
83
|
# triplet", return the length of that note in quarter notes as a delta
|
|
85
84
|
# time.
|
|
86
85
|
def note_to_delta(name)
|
|
87
|
-
|
|
86
|
+
length_to_delta(note_to_length(name))
|
|
88
87
|
end
|
|
89
88
|
|
|
90
89
|
# Given a note length name like "whole", "dotted quarter", or "8th
|
|
@@ -99,55 +98,61 @@ module MIDI
|
|
|
99
98
|
def note_to_length(name)
|
|
100
99
|
name.strip!
|
|
101
100
|
name =~ /^(dotted)?(.*?)(triplet)?$/
|
|
102
|
-
dotted
|
|
101
|
+
dotted = Regexp.last_match(1)
|
|
102
|
+
note_name = Regexp.last_match(2)
|
|
103
|
+
triplet = Regexp.last_match(3)
|
|
103
104
|
note_name.strip!
|
|
104
105
|
mult = 1.0
|
|
105
106
|
mult = 1.5 if dotted
|
|
106
107
|
mult /= 3.0 if triplet
|
|
107
108
|
len = NOTE_TO_LENGTH[note_name]
|
|
108
109
|
raise "Sequence.note_to_length: \"#{note_name}\" not understood in \"#{name}\"" unless len
|
|
109
|
-
|
|
110
|
+
|
|
111
|
+
len * mult
|
|
110
112
|
end
|
|
111
113
|
|
|
112
114
|
# Translates +length+ (a multiple of a quarter note) into a delta time.
|
|
113
115
|
# For example, 1 is a quarter note, 1.0/32.0 is a 32nd note, 1.5 is a
|
|
114
116
|
# dotted quarter, etc. Be aware when using division; 1/32 is zero due to
|
|
115
117
|
# integer mathematics and rounding. Use floating-point numbers like 1.0
|
|
116
|
-
# and 32.0. This method always returns an integer
|
|
118
|
+
# and 32.0. This method always returns an integer by calling `.round` on
|
|
119
|
+
# the floating-point result.
|
|
117
120
|
#
|
|
118
121
|
# See also note_to_delta and note_to_length.
|
|
119
122
|
def length_to_delta(length)
|
|
120
|
-
|
|
123
|
+
(@ppqn * length).round
|
|
121
124
|
end
|
|
122
125
|
|
|
123
126
|
# Returns the name of the first track (track zero). If there are no
|
|
124
127
|
# tracks, returns UNNAMED.
|
|
125
128
|
def name
|
|
126
129
|
return UNNAMED if @tracks.empty?
|
|
127
|
-
|
|
130
|
+
|
|
131
|
+
@tracks.first.name
|
|
128
132
|
end
|
|
129
133
|
|
|
130
134
|
# Hands the name to the first track. Does nothing if there are no tracks.
|
|
131
135
|
def name=(name)
|
|
132
136
|
return if @tracks.empty?
|
|
137
|
+
|
|
133
138
|
@tracks.first.name = name
|
|
134
139
|
end
|
|
135
140
|
|
|
136
141
|
# Reads a MIDI stream.
|
|
137
|
-
def read(io,
|
|
138
|
-
reader = @reader_class.new(self,
|
|
142
|
+
def read(io, &block) # :yields: track, num_tracks, index
|
|
143
|
+
reader = @reader_class.new(self, &block)
|
|
139
144
|
reader.read_from(io)
|
|
140
145
|
end
|
|
141
146
|
|
|
142
|
-
# Writes to a MIDI stream.
|
|
143
|
-
def write(io,
|
|
144
|
-
writer = @writer_class.new(self,
|
|
147
|
+
# Writes to a MIDI stream. +midi_format+ defaults to 1.
|
|
148
|
+
def write(io, midi_format = 1, &block) # :yields: track, num_tracks, index
|
|
149
|
+
writer = @writer_class.new(self, midi_format, &block)
|
|
145
150
|
writer.write_to(io)
|
|
146
151
|
end
|
|
147
152
|
|
|
148
153
|
# Iterates over the tracks.
|
|
149
|
-
def each
|
|
150
|
-
@tracks.each
|
|
154
|
+
def each(&block) # :yields: track
|
|
155
|
+
@tracks.each(&block)
|
|
151
156
|
end
|
|
152
157
|
|
|
153
158
|
# Returns a Measures object, which is an array container for all measures
|
|
@@ -156,13 +161,13 @@ module MIDI
|
|
|
156
161
|
# Collect time sig events and scan for last event time
|
|
157
162
|
time_sigs = []
|
|
158
163
|
max_pos = 0
|
|
159
|
-
@tracks.each
|
|
164
|
+
@tracks.each do |t|
|
|
160
165
|
t.each do |e|
|
|
161
|
-
time_sigs << e if e.
|
|
166
|
+
time_sigs << e if e.is_a?(MIDI::TimeSig)
|
|
162
167
|
max_pos = e.time_from_start if e.time_from_start > max_pos
|
|
163
168
|
end
|
|
164
169
|
end
|
|
165
|
-
time_sigs.sort { |x,y| x.time_from_start <=> y.time_from_start }
|
|
170
|
+
time_sigs.sort { |x, y| x.time_from_start <=> y.time_from_start }
|
|
166
171
|
|
|
167
172
|
# Add a "fake" time sig event at the very last position of the sequence,
|
|
168
173
|
# just to make sure the whole sequence is calculated.
|
|
@@ -172,7 +177,9 @@ module MIDI
|
|
|
172
177
|
|
|
173
178
|
# Default to 4/4
|
|
174
179
|
measure_length = @ppqn * 4
|
|
175
|
-
oldnumer
|
|
180
|
+
oldnumer = 4
|
|
181
|
+
olddenom = 2
|
|
182
|
+
oldbeats = 24
|
|
176
183
|
|
|
177
184
|
measures = MIDI::Measures.new(max_pos, @ppqn)
|
|
178
185
|
curr_pos = 0
|
|
@@ -186,11 +193,12 @@ module MIDI
|
|
|
186
193
|
curr_meas_no += 1
|
|
187
194
|
curr_pos += measure_length
|
|
188
195
|
end
|
|
189
|
-
oldnumer
|
|
196
|
+
oldnumer = te.numerator
|
|
197
|
+
olddenom = te.denominator
|
|
198
|
+
oldbeats = te.metronome_ticks
|
|
190
199
|
measure_length = te.measure_duration(@ppqn)
|
|
191
200
|
end
|
|
192
201
|
measures
|
|
193
202
|
end
|
|
194
|
-
|
|
195
203
|
end
|
|
196
204
|
end
|
data/lib/midilib/track.rb
CHANGED
|
@@ -1,47 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
require_relative 'event'
|
|
2
|
+
require_relative 'mergesort'
|
|
2
3
|
|
|
3
4
|
module MIDI
|
|
4
|
-
|
|
5
|
-
# This is taken from
|
|
6
|
-
# http://github.com/adamjmurray/cosy/blob/master/lib/cosy/helper/midi_file_renderer_helper.rb
|
|
7
|
-
# with permission from Adam Murray, who originally suggested this fix.
|
|
8
|
-
# See http://wiki.github.com/adamjmurray/cosy/midilib-notes for details.
|
|
9
|
-
# First we need to add some API infrastructure:
|
|
10
|
-
class MIDI::Array < ::Array
|
|
11
|
-
# This code borrowed from 'Moser' http://codesnippets.joyent.com/posts/show/1699
|
|
12
|
-
|
|
13
|
-
# A stable sorting algorithm that maintains the relative order of equal elements
|
|
14
|
-
def mergesort(&cmp)
|
|
15
|
-
if cmp == nil
|
|
16
|
-
cmp = lambda { |a, b| a <=> b }
|
|
17
|
-
end
|
|
18
|
-
if size <= 1
|
|
19
|
-
self.dup
|
|
20
|
-
else
|
|
21
|
-
halves = split.map { |half| half.mergesort(&cmp) }
|
|
22
|
-
merge(*halves, &cmp)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
protected
|
|
27
|
-
def split
|
|
28
|
-
n = (length / 2).floor - 1
|
|
29
|
-
[self[0..n], self[n+1..-1]]
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def merge(first, second, &predicate)
|
|
33
|
-
result = []
|
|
34
|
-
until first.empty? || second.empty?
|
|
35
|
-
if predicate.call(first.first, second.first) <= 0
|
|
36
|
-
result << first.shift
|
|
37
|
-
else
|
|
38
|
-
result << second.shift
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
result.concat(first).concat(second)
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
5
|
# A Track is a list of events.
|
|
46
6
|
#
|
|
47
7
|
# When you modify the +events+ array, make sure to call recalc_times so
|
|
@@ -52,7 +12,6 @@ module MIDI
|
|
|
52
12
|
# IO::SeqReader but is _not_ kept up to date by any other methods.
|
|
53
13
|
|
|
54
14
|
class Track
|
|
55
|
-
|
|
56
15
|
include Enumerable
|
|
57
16
|
|
|
58
17
|
UNNAMED = 'Unnamed'
|
|
@@ -62,22 +21,23 @@ module MIDI
|
|
|
62
21
|
|
|
63
22
|
def initialize(sequence)
|
|
64
23
|
@sequence = sequence
|
|
65
|
-
@events =
|
|
24
|
+
@events = []
|
|
66
25
|
|
|
67
26
|
# Bitmask of all channels used. Set when track is read in from
|
|
68
27
|
# a MIDI file.
|
|
69
28
|
@channels_used = 0
|
|
29
|
+
@instrument = nil
|
|
70
30
|
end
|
|
71
31
|
|
|
72
32
|
# Return track name. If there is no name, return UNNAMED.
|
|
73
33
|
def name
|
|
74
|
-
event = @events.detect { |e| e.
|
|
34
|
+
event = @events.detect { |e| e.is_a?(MetaEvent) && e.meta_type == META_SEQ_NAME }
|
|
75
35
|
event ? event.data_as_str : UNNAMED
|
|
76
36
|
end
|
|
77
37
|
|
|
78
38
|
# Set track name. Replaces or creates a name meta-event.
|
|
79
39
|
def name=(name)
|
|
80
|
-
event = @events.detect { |e| e.
|
|
40
|
+
event = @events.detect { |e| e.is_a?(MetaEvent) && e.meta_type == META_SEQ_NAME }
|
|
81
41
|
if event
|
|
82
42
|
event.data = name
|
|
83
43
|
else
|
|
@@ -112,7 +72,7 @@ module MIDI
|
|
|
112
72
|
recalc_times(0, list2)
|
|
113
73
|
list = list1 + list2
|
|
114
74
|
recalc_delta_from_times(0, list)
|
|
115
|
-
|
|
75
|
+
list
|
|
116
76
|
end
|
|
117
77
|
|
|
118
78
|
# Quantize every event. length_or_note is either a length (1 = quarter,
|
|
@@ -134,9 +94,9 @@ module MIDI
|
|
|
134
94
|
|
|
135
95
|
# Recalculate start times for all events in +list+ from starting_at to
|
|
136
96
|
# end.
|
|
137
|
-
def recalc_times(starting_at=0, list
|
|
138
|
-
t =
|
|
139
|
-
list[starting_at
|
|
97
|
+
def recalc_times(starting_at = 0, list = @events)
|
|
98
|
+
t = starting_at == 0 ? 0 : list[starting_at - 1].time_from_start
|
|
99
|
+
list[starting_at..-1].each do |e|
|
|
140
100
|
t += e.delta_time
|
|
141
101
|
e.time_from_start = t
|
|
142
102
|
end
|
|
@@ -146,24 +106,24 @@ module MIDI
|
|
|
146
106
|
# from each event's time_from_start. This is useful, for example, when
|
|
147
107
|
# merging two event lists. As a side-effect, elements from starting_at
|
|
148
108
|
# are sorted by time_from_start.
|
|
149
|
-
def recalc_delta_from_times(starting_at=0, list
|
|
109
|
+
def recalc_delta_from_times(starting_at = 0, list = @events)
|
|
150
110
|
prev_time_from_start = 0
|
|
151
111
|
# We need to sort the sublist. sublist.sort! does not do what we want.
|
|
152
112
|
# We call mergesort instead of Array.sort because sort is not stable
|
|
153
113
|
# (it can mix up the order of events that have the same start time).
|
|
154
114
|
# See http://wiki.github.com/adamjmurray/cosy/midilib-notes for details.
|
|
155
|
-
list[starting_at
|
|
115
|
+
list[starting_at..-1] = mergesort(list[starting_at..-1]) do |e1, e2|
|
|
156
116
|
e1.time_from_start <=> e2.time_from_start
|
|
157
117
|
end
|
|
158
|
-
list[starting_at
|
|
118
|
+
list[starting_at..-1].each do |e|
|
|
159
119
|
e.delta_time = e.time_from_start - prev_time_from_start
|
|
160
120
|
prev_time_from_start = e.time_from_start
|
|
161
121
|
end
|
|
162
122
|
end
|
|
163
123
|
|
|
164
124
|
# Iterate over events.
|
|
165
|
-
def each
|
|
166
|
-
@events.each
|
|
125
|
+
def each(&block) # :yields: event
|
|
126
|
+
@events.each(&block)
|
|
167
127
|
end
|
|
168
128
|
|
|
169
129
|
# Sort events by their time_from_start. After sorting,
|
|
@@ -173,7 +133,6 @@ module MIDI
|
|
|
173
133
|
# Note: this method is redundant, since recalc_delta_from_times sorts
|
|
174
134
|
# the events first. This method may go away in a future release, or at
|
|
175
135
|
# least be aliased to recalc_delta_from_times.
|
|
176
|
-
|
|
136
|
+
alias sort recalc_delta_from_times
|
|
177
137
|
end
|
|
178
|
-
|
|
179
138
|
end
|
data/lib/midilib/utils.rb
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
module MIDI
|
|
2
|
-
|
|
3
2
|
# Utility methods.
|
|
4
3
|
class Utils
|
|
5
|
-
|
|
6
4
|
# MIDI note names. NOTE_NAMES[0] is 'C', NOTE_NAMES[1] is 'C#', etc.
|
|
7
5
|
NOTE_NAMES = [
|
|
8
6
|
'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'
|
|
9
7
|
]
|
|
10
8
|
|
|
11
9
|
# Given a MIDI note number, return the name and octave as a string.
|
|
12
|
-
def
|
|
10
|
+
def self.note_to_s(num)
|
|
13
11
|
note = num % 12
|
|
14
12
|
octave = num / 12
|
|
15
|
-
|
|
13
|
+
"#{NOTE_NAMES[note]}#{octave - 1}"
|
|
16
14
|
end
|
|
17
15
|
|
|
18
16
|
# Given an integer, returns it as a variable length array of bytes (the
|
|
@@ -21,7 +19,7 @@ module MIDI
|
|
|
21
19
|
# The converse operation--converting a var len into a number--requires
|
|
22
20
|
# input from a stream of bytes. Therefore we don't supply it here. That is
|
|
23
21
|
# a part of the MIDIFile class.
|
|
24
|
-
def
|
|
22
|
+
def self.as_var_len(val)
|
|
25
23
|
buffer = []
|
|
26
24
|
buffer << (val & 0x7f)
|
|
27
25
|
val = (val >> 7)
|
|
@@ -29,8 +27,7 @@ module MIDI
|
|
|
29
27
|
buffer << (0x80 + (val & 0x7f))
|
|
30
28
|
val = (val >> 7)
|
|
31
29
|
end
|
|
32
|
-
|
|
30
|
+
buffer.reverse!
|
|
33
31
|
end
|
|
34
|
-
|
|
35
32
|
end
|
|
36
33
|
end
|
data/lib/midilib.rb
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
#
|
|
6
6
|
# See the README.rdoc file or http://midilib.rubyforge.org for details.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
require_relative 'midilib/info'
|
|
9
|
+
require_relative 'midilib/sequence'
|
|
10
|
+
require_relative 'midilib/track'
|
|
11
|
+
require_relative 'midilib/io/seqreader'
|
|
12
|
+
require_relative 'midilib/io/seqwriter'
|
|
13
13
|
|
|
14
14
|
# --
|
|
15
15
|
# consts.rb, utils.rb, and event.rb are included by these files.
|
data/test/event_equality.rb
CHANGED
|
@@ -5,77 +5,75 @@
|
|
|
5
5
|
# classes. If someone disagrees, it would be trivial to move them there.
|
|
6
6
|
|
|
7
7
|
module MIDI
|
|
8
|
-
|
|
9
8
|
class Event
|
|
10
|
-
def ==(
|
|
11
|
-
|
|
12
|
-
@status ==
|
|
13
|
-
@delta_time ==
|
|
14
|
-
@time_from_start ==
|
|
9
|
+
def ==(other)
|
|
10
|
+
other.instance_of?(self.class) &&
|
|
11
|
+
@status == other.status &&
|
|
12
|
+
@delta_time == other.delta_time &&
|
|
13
|
+
@time_from_start == other.time_from_start
|
|
15
14
|
end
|
|
16
15
|
end
|
|
17
16
|
|
|
18
17
|
class ChannelEvent
|
|
19
|
-
def ==(
|
|
20
|
-
|
|
18
|
+
def ==(other)
|
|
19
|
+
super(other) && @channel == other.channel
|
|
21
20
|
end
|
|
22
21
|
end
|
|
23
22
|
|
|
24
23
|
class NoteEvent < ChannelEvent
|
|
25
|
-
def ==(
|
|
26
|
-
|
|
27
|
-
@note ==
|
|
24
|
+
def ==(other)
|
|
25
|
+
super(other) &&
|
|
26
|
+
@note == other.note && @velocity == other.velocity
|
|
28
27
|
end
|
|
29
28
|
end
|
|
30
29
|
|
|
31
30
|
class Controller < ChannelEvent
|
|
32
|
-
def ==(
|
|
33
|
-
|
|
34
|
-
@controller ==
|
|
31
|
+
def ==(other)
|
|
32
|
+
super(other) &&
|
|
33
|
+
@controller == other.controller && @value == other.value
|
|
35
34
|
end
|
|
36
35
|
end
|
|
37
36
|
|
|
38
37
|
class ProgramChange < ChannelEvent
|
|
39
|
-
def ==(
|
|
40
|
-
|
|
38
|
+
def ==(other)
|
|
39
|
+
super(other) && @program == other.program
|
|
41
40
|
end
|
|
42
41
|
end
|
|
43
42
|
|
|
44
43
|
class ChannelPressure < ChannelEvent
|
|
45
|
-
def ==(
|
|
46
|
-
|
|
44
|
+
def ==(other)
|
|
45
|
+
super(other) && @pressure == other.pressure
|
|
47
46
|
end
|
|
48
47
|
end
|
|
49
48
|
|
|
50
49
|
class PitchBend < ChannelEvent
|
|
51
|
-
def ==(
|
|
52
|
-
|
|
50
|
+
def ==(other)
|
|
51
|
+
super(other) && @value == other.value
|
|
53
52
|
end
|
|
54
53
|
end
|
|
55
54
|
|
|
56
55
|
class SystemExclusive < SystemCommon
|
|
57
|
-
def ==(
|
|
58
|
-
|
|
56
|
+
def ==(other)
|
|
57
|
+
super(other) && @data == other.data
|
|
59
58
|
end
|
|
60
59
|
end
|
|
61
60
|
|
|
62
61
|
class SongPointer < SystemCommon
|
|
63
|
-
def ==(
|
|
64
|
-
|
|
62
|
+
def ==(other)
|
|
63
|
+
super(other) && @pointer == other.pointer
|
|
65
64
|
end
|
|
66
65
|
end
|
|
67
66
|
|
|
68
67
|
class SongSelect < SystemCommon
|
|
69
|
-
def ==(
|
|
70
|
-
|
|
68
|
+
def ==(other)
|
|
69
|
+
super(other) && @song == other.song
|
|
71
70
|
end
|
|
72
71
|
end
|
|
73
72
|
|
|
74
73
|
class MetaEvent < Event
|
|
75
|
-
def ==(
|
|
76
|
-
|
|
77
|
-
@data ==
|
|
74
|
+
def ==(other)
|
|
75
|
+
super(other) && @meta_type == other.meta_type &&
|
|
76
|
+
@data == other.data
|
|
78
77
|
end
|
|
79
78
|
end
|
|
80
|
-
|
|
81
79
|
end
|
data/test/test_event.rb
CHANGED
|
@@ -7,7 +7,6 @@ require 'test/unit'
|
|
|
7
7
|
require 'midilib'
|
|
8
8
|
|
|
9
9
|
class EventTester < Test::Unit::TestCase
|
|
10
|
-
|
|
11
10
|
def test_note_on
|
|
12
11
|
e = MIDI::NoteOn.new
|
|
13
12
|
assert_equal(MIDI::NOTE_ON, e.status)
|
|
@@ -20,25 +19,25 @@ class EventTester < Test::Unit::TestCase
|
|
|
20
19
|
|
|
21
20
|
def test_to_s
|
|
22
21
|
e = MIDI::NoteOn.new
|
|
23
|
-
assert_equal(
|
|
22
|
+
assert_equal('0: ch 00 on 40 40', e.to_s)
|
|
24
23
|
e.print_decimal_numbers = true
|
|
25
|
-
assert_equal(
|
|
24
|
+
assert_equal('0: ch 0 on 64 64', e.to_s)
|
|
26
25
|
e.print_note_names = true
|
|
27
|
-
assert_equal(
|
|
26
|
+
assert_equal('0: ch 0 on E4 64', e.to_s)
|
|
28
27
|
e.print_decimal_numbers = false
|
|
29
|
-
assert_equal(
|
|
28
|
+
assert_equal('0: ch 00 on E4 40', e.to_s)
|
|
30
29
|
end
|
|
31
30
|
|
|
32
31
|
def test_pitch_bend
|
|
33
32
|
e = MIDI::PitchBend.new(0, 128)
|
|
34
33
|
b = e.data_as_bytes
|
|
35
|
-
assert_equal(0, b[1])
|
|
36
|
-
assert_equal(1, b[2])
|
|
34
|
+
assert_equal(0, b[1]) # lsb, 7 bits
|
|
35
|
+
assert_equal(1, b[2]) # msb, 7 bits
|
|
37
36
|
|
|
38
37
|
e.value = (3 << 7) + 42
|
|
39
38
|
b = e.data_as_bytes
|
|
40
|
-
assert_equal(42, b[1])
|
|
41
|
-
assert_equal(3, b[2])
|
|
39
|
+
assert_equal(42, b[1]) # lsb, 7 bits
|
|
40
|
+
assert_equal(3, b[2]) # msb, 7 bits
|
|
42
41
|
end
|
|
43
42
|
|
|
44
43
|
def test_quantize_1
|
|
@@ -58,7 +57,7 @@ class EventTester < Test::Unit::TestCase
|
|
|
58
57
|
|
|
59
58
|
# Test with quantize_to(6)
|
|
60
59
|
[0, 0, 0, 6, 6, 6, 6, 6, 6, 12, 12, 12, 12, 12, 12,
|
|
61
|
-
|
|
60
|
+
18, 18, 18, 18, 18, 18, 24].each_with_index do |after, before|
|
|
62
61
|
e.time_from_start = before
|
|
63
62
|
e.quantize_to(6)
|
|
64
63
|
assert_equal(after, e.time_from_start)
|
|
@@ -131,5 +130,4 @@ class EventTester < Test::Unit::TestCase
|
|
|
131
130
|
assert_equal('foobar', e.data_as_str)
|
|
132
131
|
assert_equal(foobar_as_array, e.data)
|
|
133
132
|
end
|
|
134
|
-
|
|
135
133
|
end
|