midilib 3.1.0 → 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +12 -12
  3. data/Credits +7 -0
  4. data/README.rdoc +12 -148
  5. data/Rakefile +5 -15
  6. data/TODO.rdoc +8 -7
  7. data/examples/from_scratch.rb +2 -7
  8. data/examples/measures_mbt.rb +1 -6
  9. data/examples/print_program_changes.rb +1 -3
  10. data/examples/reader2text.rb +2 -7
  11. data/examples/seq2text.rb +1 -6
  12. data/examples/split.rb +1 -6
  13. data/examples/strings.rb +2 -7
  14. data/examples/transpose.rb +3 -8
  15. data/lib/midilib/consts.rb +2 -0
  16. data/lib/midilib/event.rb +2 -2
  17. data/lib/midilib/info.rb +4 -4
  18. data/lib/midilib/io/seqreader.rb +11 -27
  19. data/lib/midilib/io/seqwriter.rb +6 -4
  20. data/lib/midilib/track.rb +38 -6
  21. data/test/test_event.rb +13 -6
  22. data/test/test_io.rb +144 -13
  23. data/test/test_mergesort.rb +1 -6
  24. data/test/test_midifile.rb +1 -6
  25. data/test/test_sequence.rb +1 -6
  26. data/test/test_track.rb +66 -7
  27. data/test/test_varlen.rb +1 -6
  28. metadata +3 -87
  29. data/html/MIDI/ActiveSense.html +0 -171
  30. data/html/MIDI/ChannelEvent.html +0 -190
  31. data/html/MIDI/ChannelPressure.html +0 -212
  32. data/html/MIDI/Clock.html +0 -171
  33. data/html/MIDI/Continue.html +0 -171
  34. data/html/MIDI/Controller.html +0 -224
  35. data/html/MIDI/Event.html +0 -349
  36. data/html/MIDI/IO/MIDIFile.html +0 -1392
  37. data/html/MIDI/IO/SeqReader.html +0 -642
  38. data/html/MIDI/IO/SeqWriter.html +0 -442
  39. data/html/MIDI/IO.html +0 -91
  40. data/html/MIDI/KeySig.html +0 -292
  41. data/html/MIDI/Marker.html +0 -138
  42. data/html/MIDI/Measure.html +0 -260
  43. data/html/MIDI/Measures.html +0 -222
  44. data/html/MIDI/MetaEvent.html +0 -353
  45. data/html/MIDI/NoteEvent.html +0 -255
  46. data/html/MIDI/NoteOff.html +0 -188
  47. data/html/MIDI/NoteOn.html +0 -188
  48. data/html/MIDI/PitchBend.html +0 -213
  49. data/html/MIDI/PolyPressure.html +0 -216
  50. data/html/MIDI/ProgramChange.html +0 -212
  51. data/html/MIDI/Realtime.html +0 -194
  52. data/html/MIDI/Sequence.html +0 -641
  53. data/html/MIDI/SongPointer.html +0 -213
  54. data/html/MIDI/SongSelect.html +0 -212
  55. data/html/MIDI/Start.html +0 -171
  56. data/html/MIDI/Stop.html +0 -171
  57. data/html/MIDI/SystemCommon.html +0 -138
  58. data/html/MIDI/SystemExclusive.html +0 -215
  59. data/html/MIDI/SystemReset.html +0 -171
  60. data/html/MIDI/Tempo.html +0 -292
  61. data/html/MIDI/TimeSig.html +0 -286
  62. data/html/MIDI/Track.html +0 -489
  63. data/html/MIDI/TuneRequest.html +0 -194
  64. data/html/MIDI/Utils.html +0 -178
  65. data/html/MIDI.html +0 -281
  66. data/html/Object.html +0 -197
  67. data/html/README_rdoc.html +0 -545
  68. data/html/TODO_rdoc.html +0 -123
  69. data/html/created.rid +0 -15
  70. data/html/css/fonts.css +0 -167
  71. data/html/css/rdoc.css +0 -639
  72. data/html/fonts/Lato-Light.ttf +0 -0
  73. data/html/fonts/Lato-LightItalic.ttf +0 -0
  74. data/html/fonts/Lato-Regular.ttf +0 -0
  75. data/html/fonts/Lato-RegularItalic.ttf +0 -0
  76. data/html/fonts/SourceCodePro-Bold.ttf +0 -0
  77. data/html/fonts/SourceCodePro-Regular.ttf +0 -0
  78. data/html/images/add.png +0 -0
  79. data/html/images/arrow_up.png +0 -0
  80. data/html/images/brick.png +0 -0
  81. data/html/images/brick_link.png +0 -0
  82. data/html/images/bug.png +0 -0
  83. data/html/images/bullet_black.png +0 -0
  84. data/html/images/bullet_toggle_minus.png +0 -0
  85. data/html/images/bullet_toggle_plus.png +0 -0
  86. data/html/images/date.png +0 -0
  87. data/html/images/delete.png +0 -0
  88. data/html/images/find.png +0 -0
  89. data/html/images/loadingAnimation.gif +0 -0
  90. data/html/images/macFFBgHack.png +0 -0
  91. data/html/images/package.png +0 -0
  92. data/html/images/page_green.png +0 -0
  93. data/html/images/page_white_text.png +0 -0
  94. data/html/images/page_white_width.png +0 -0
  95. data/html/images/plugin.png +0 -0
  96. data/html/images/ruby.png +0 -0
  97. data/html/images/tag_blue.png +0 -0
  98. data/html/images/tag_green.png +0 -0
  99. data/html/images/transparent.png +0 -0
  100. data/html/images/wrench.png +0 -0
  101. data/html/images/wrench_orange.png +0 -0
  102. data/html/images/zoom.png +0 -0
  103. data/html/index.html +0 -534
  104. data/html/js/darkfish.js +0 -84
  105. data/html/js/navigation.js +0 -105
  106. data/html/js/navigation.js.gz +0 -0
  107. data/html/js/search.js +0 -110
  108. data/html/js/search_index.js +0 -1
  109. data/html/js/search_index.js.gz +0 -0
  110. data/html/js/searcher.js +0 -229
  111. data/html/js/searcher.js.gz +0 -0
  112. data/html/table_of_contents.html +0 -1258
data/lib/midilib/track.rb CHANGED
@@ -59,11 +59,48 @@ module MIDI
59
59
  end
60
60
  end
61
61
 
62
+ # Iterates over events, yielding each one.
63
+ def each(&block) # :yields: event
64
+ @events.each(&block)
65
+ end
66
+
67
+ # If `event` exists in @events, deletes it, updates the delta time of
68
+ # the event after it, and calls `recalc_times` by default.
69
+ def delete_event(event, call_recalc_times = true)
70
+ i = @events.index(event)
71
+ return unless i
72
+
73
+ @events[i + 1].delta_time += @events[i].delta_time if i != (@events.length - 1)
74
+ @events.delete_at(i)
75
+ recalc_times if call_recalc_times
76
+ end
77
+
78
+ # Makes sure that we have one and only one end track meta event at the
79
+ # end of this track.
80
+ def ensure_track_end_meta_event
81
+ track_ends = @events.select { |e| e.is_a?(MetaEvent) && e.meta_type == META_TRACK_END }
82
+ has_end = !@events.empty? && track_ends[-1] == @events.last
83
+
84
+ # If we only have one end event and it's the last one, there's nothing
85
+ # to do.
86
+ return if track_ends.length == 1 && has_end
87
+
88
+ # If we have an end of track event already, leave it alone.
89
+ track_ends.pop if has_end
90
+ track_ends.each { |track_end| delete_event(track_end, false) }
91
+ return if has_end
92
+
93
+ mte = MetaEvent.new(META_TRACK_END, nil, 0)
94
+ mte.time_from_start = @events.last.time_from_start + mte.delta_time if @events.last
95
+ @events << mte
96
+ end
97
+
62
98
  # Merges an array of events into our event list. After merging, the
63
99
  # events' time_from_start values are correct so you don't need to worry
64
- # about calling recalc_times.
100
+ # about calling #recalc_times.
65
101
  def merge(event_list)
66
102
  @events = merge_event_lists(@events, event_list)
103
+ ensure_track_end_meta_event
67
104
  end
68
105
 
69
106
  # Merges two event arrays together. Does not modify this track.
@@ -121,11 +158,6 @@ module MIDI
121
158
  end
122
159
  end
123
160
 
124
- # Iterate over events.
125
- def each(&block) # :yields: event
126
- @events.each(&block)
127
- end
128
-
129
161
  # Sort events by their time_from_start. After sorting,
130
162
  # recalc_delta_from_times is called to make sure that the delta times
131
163
  # reflect the possibly new event order.
data/test/test_event.rb CHANGED
@@ -1,10 +1,5 @@
1
- # Start looking for MIDI classes in the directory above this one.
2
- # This forces us to use the local copy of MIDI, even if there is
3
- # a previously installed version out there somewhere.
4
- $LOAD_PATH[0, 0] = File.join(File.dirname(__FILE__), '..', 'lib')
5
-
6
1
  require 'test/unit'
7
- require 'midilib'
2
+ require_relative '../lib/midilib'
8
3
 
9
4
  class EventTester < Test::Unit::TestCase
10
5
  def test_note_on
@@ -130,4 +125,16 @@ class EventTester < Test::Unit::TestCase
130
125
  assert_equal('foobar', e.data_as_str)
131
126
  assert_equal(foobar_as_array, e.data)
132
127
  end
128
+
129
+ def test_song_pointer
130
+ e = MIDI::SongPointer.new(1)
131
+ b = e.data_as_bytes
132
+ assert_equal(1, b[1]) # lsb, 7 bits
133
+ assert_equal(0, b[2]) # msb, 7 bits
134
+
135
+ e.pointer = (3 << 7) + 42
136
+ b = e.data_as_bytes
137
+ assert_equal(42, b[1]) # lsb, 7 bits
138
+ assert_equal(3, b[2]) # msb, 7 bits
139
+ end
133
140
  end
data/test/test_io.rb CHANGED
@@ -1,14 +1,7 @@
1
- # Start looking for MIDI classes in the directory above this one.
2
- # This forces us to use the local copy of MIDI, even if there is
3
- # a previously installed version out there somewhere.
4
- $LOAD_PATH[0, 0] = File.join(File.dirname(__FILE__), '..', 'lib')
5
- # Add current directory so we can find event_equality
6
- $LOAD_PATH[0, 0] = File.dirname(__FILE__)
7
-
8
1
  require 'test/unit'
9
- require 'midilib'
10
- require 'midilib/consts'
11
- require 'event_equality'
2
+ require_relative '../lib/midilib'
3
+ require_relative '../lib/midilib/consts'
4
+ require_relative 'event_equality'
12
5
 
13
6
  class IOTester < Test::Unit::TestCase
14
7
  SEQ_TEST_FILE = File.join(File.dirname(__FILE__), 'test.mid')
@@ -35,7 +28,12 @@ class IOTester < Test::Unit::TestCase
35
28
  assert_equal(1, format0_seq.tracks.length, 'number of tracks differ')
36
29
  format_1_count = multitrack_seq.tracks.map { |t| t.events.count }.reduce(:+)
37
30
  format_0_count = format0_seq.tracks.map { |t| t.events.count }.reduce(:+)
38
- assert_equal(format_1_count, format_0_count, 'same number of total events')
31
+
32
+ # The format 1 file will have one more event because there is an end of
33
+ # track meta event at the end of each track (the track 0 metadata track
34
+ # and track 1 with the notes), whereas the format 0 file only has one
35
+ # track, thus one end of track meta event.
36
+ assert_equal(format_1_count, format_0_count + 1, 'different number of total events')
39
37
  end
40
38
 
41
39
  def test_read_and_write
@@ -101,6 +99,22 @@ class IOTester < Test::Unit::TestCase
101
99
  assert_equal(MIDI::GM_PATCH_NAMES[0], seq.tracks[1].instrument)
102
100
  end
103
101
 
102
+ # This is a regression test.
103
+ def test_read_eot_preserves_delta
104
+ seq = MIDI::Sequence.new
105
+ File.open(SEQ_TEST_FILE, 'rb') { |f| seq.read(f) }
106
+ track = seq.tracks.last
107
+ mte = MIDI::MetaEvent.new(MIDI::META_TRACK_END, nil, 123)
108
+ track.events << mte
109
+ track.recalc_times
110
+ File.open(OUTPUT_FILE, 'wb') { |f| seq.write(f) }
111
+ File.open(OUTPUT_FILE, 'rb') { |f| seq.read(f) }
112
+
113
+ assert_equal(mte, seq.tracks.last.events.last)
114
+ ensure
115
+ File.delete(OUTPUT_FILE) if File.exist?(OUTPUT_FILE)
116
+ end
117
+
104
118
  def test_preserve_deltas_in_some_situations
105
119
  out_seq = MIDI::Sequence.new
106
120
  out_track = MIDI::Track.new(out_seq)
@@ -125,7 +139,7 @@ class IOTester < Test::Unit::TestCase
125
139
  out_track.events << MIDI::NoteOff.new(0, 65, 127, 230)
126
140
  end
127
141
 
128
- File.open('/tmp/midilib_test.mid', 'wb') { |file| out_seq.write(file) }
142
+ File.open(TEMPFILE, 'wb') { |file| out_seq.write(file) }
129
143
 
130
144
  # Although start times are not written out to the MIDI file, we
131
145
  # calculate them because we are about to compare the out events with the
@@ -135,9 +149,126 @@ class IOTester < Test::Unit::TestCase
135
149
  in_seq = MIDI::Sequence.new
136
150
  File.open(TEMPFILE, 'rb') { |file| in_seq.read(file) }
137
151
  in_track = in_seq.tracks[0]
138
- assert_equal(out_track.events.length, in_track.events.length)
152
+ assert_equal(out_track.events.length + 1, in_track.events.length) # read added end of track meta event
139
153
  out_track.events.each_with_index do |event, i|
140
154
  assert_equal(event, in_track.events[i])
141
155
  end
156
+
157
+ # Last event is a end of track meta event
158
+ e = in_track.events.last
159
+ assert(e.is_a?(MIDI::MetaEvent))
160
+ assert(e.meta_type == MIDI::META_TRACK_END)
161
+ end
162
+
163
+ def test_preserve_deltas_multiple_note_offs
164
+ out_seq = MIDI::Sequence.new
165
+ out_track = MIDI::Track.new(out_seq)
166
+ out_seq.tracks << out_track
167
+ out_track.events << MIDI::Tempo.new(MIDI::Tempo.bpm_to_mpq(120))
168
+
169
+ out_track = MIDI::Track.new(out_seq)
170
+ out_seq.tracks << out_track
171
+ out_track.events << MIDI::NoteOn.new(0, 65, 127, 100)
172
+ out_track.events << MIDI::NoteOff.new(0, 65, 127, 100)
173
+ out_track.events << MIDI::NoteOff.new(0, 65, 127, 100)
174
+
175
+ File.open(TEMPFILE, 'wb') { |file| out_seq.write(file) }
176
+
177
+ in_seq = MIDI::Sequence.new
178
+ File.open(TEMPFILE, 'rb') { |file| in_seq.read(file) }
179
+ in_track = in_seq.tracks[1]
180
+
181
+ out_track.recalc_times # so that start times are correct
182
+
183
+ assert_equal(out_track.events.length + 1, in_track.events.length)
184
+ out_track.events.each_with_index do |event, i|
185
+ assert_equal(event.data_as_bytes, in_track.events[i].data_as_bytes)
186
+ assert_equal(event.delta_time, in_track.events[i].delta_time)
187
+ assert_equal(event.time_from_start, in_track.events[i].time_from_start)
188
+ end
189
+
190
+ # Last event is a end of track meta event
191
+ e = in_track.events.last
192
+ assert(e.is_a?(MIDI::MetaEvent))
193
+ assert(e.meta_type == MIDI::META_TRACK_END)
194
+ end
195
+
196
+ def test_preserve_deltas_multiple_note_on_zero_velocity
197
+ out_seq = MIDI::Sequence.new
198
+ out_track = MIDI::Track.new(out_seq)
199
+ out_seq.tracks << out_track
200
+ out_track.events << MIDI::Tempo.new(MIDI::Tempo.bpm_to_mpq(120))
201
+
202
+ out_track = MIDI::Track.new(out_seq)
203
+ out_seq.tracks << out_track
204
+ out_track.events << MIDI::NoteOn.new(0, 65, 127, 100)
205
+ out_track.events << MIDI::NoteOn.new(0, 65, 0, 100)
206
+ out_track.events << MIDI::NoteOn.new(0, 65, 0, 100)
207
+
208
+ File.open(TEMPFILE, 'wb') { |file| out_seq.write(file) }
209
+
210
+ in_seq = MIDI::Sequence.new
211
+ File.open(TEMPFILE, 'rb') { |file| in_seq.read(file) }
212
+ in_track = in_seq.tracks[1]
213
+
214
+ # Turn the note ons with zero velocity into note offs, and recalc start
215
+ # times so that time_from_start is correct.
216
+ [1, 2].each do |i|
217
+ out_track.events[i] = MIDI::NoteOff.new(0, 65, 64, 100)
218
+ end
219
+ out_track.recalc_times # so that start times are correct
220
+
221
+ assert_equal(out_track.events.length + 1, in_track.events.length)
222
+ out_track.events.zip(in_track.events).each do |out_event, in_event|
223
+ assert_equal(out_event.data_as_bytes, in_event.data_as_bytes)
224
+ assert_equal(out_event.delta_time, in_event.delta_time)
225
+ assert_equal(out_event.time_from_start, in_event.time_from_start)
226
+ end
227
+
228
+ # Last event is a end of track meta event
229
+ e = in_track.events.last
230
+ assert(e.is_a?(MIDI::MetaEvent))
231
+ assert(e.meta_type == MIDI::META_TRACK_END)
232
+ end
233
+
234
+ # Regression test. Running status output when writing a track's events was
235
+ # broken.
236
+ def test_running_status_output
237
+ out_seq = MIDI::Sequence.new
238
+ out_track = MIDI::Track.new(out_seq)
239
+ out_seq.tracks << out_track
240
+ out_track.events << MIDI::Tempo.new(MIDI::Tempo.bpm_to_mpq(120))
241
+
242
+ out_track = MIDI::Track.new(out_seq)
243
+ out_seq.tracks << out_track
244
+ out_track.events << MIDI::NoteOn.new(0, 65, 127, 100)
245
+ out_track.events << MIDI::NoteOn.new(0, 65, 0, 100)
246
+ out_track.events << MIDI::NoteOn.new(0, 65, 0, 100)
247
+
248
+ File.open(TEMPFILE, 'wb') { |file| out_seq.write(file) }
249
+
250
+ in_seq = MIDI::Sequence.new
251
+ File.open(TEMPFILE, 'rb') { |file| in_seq.read(file) }
252
+ in_track = in_seq.tracks[1]
253
+
254
+ # Turn the note ons with zero velocity into note offs, and recalc start
255
+ # times so that time_from_start is correct.
256
+ [1, 2].each do |i|
257
+ out_track.events[i] = MIDI::NoteOff.new(0, 65, 64, 100)
258
+ end
259
+ out_track.recalc_times # so that start times are correct
260
+
261
+ assert_equal(out_track.events.length + 1, in_track.events.length)
262
+ out_track.events.each_with_index do |event, i|
263
+ in_event = in_track.events[i]
264
+ assert_equal(event.data_as_bytes, in_track.events[i].data_as_bytes)
265
+ assert_equal(event.delta_time, in_track.events[i].delta_time)
266
+ assert_equal(event.time_from_start, in_track.events[i].time_from_start)
267
+ end
268
+
269
+ # Last event is a end of track meta event
270
+ e = in_track.events.last
271
+ assert(e.is_a?(MIDI::MetaEvent))
272
+ assert(e.meta_type == MIDI::META_TRACK_END)
142
273
  end
143
274
  end
@@ -1,10 +1,5 @@
1
- # Start looking for MIDI classes in the directory above this one.
2
- # This forces us to use the local copy of MIDI, even if there is
3
- # a previously installed version out there somewhere.
4
- $LOAD_PATH[0, 0] = File.join(File.dirname(__FILE__), '..', 'lib')
5
-
6
1
  require 'test/unit'
7
- require 'midilib/mergesort'
2
+ require_relative '../lib/midilib/mergesort'
8
3
 
9
4
  class MergesortTester < Test::Unit::TestCase
10
5
  def test_mergesort
@@ -1,11 +1,6 @@
1
- # Start looking for MIDI classes in the directory above this one.
2
- # This forces us to use the local copy of MIDI, even if there is
3
- # a previously installed version out there somewhere.
4
- $LOAD_PATH[0, 0] = File.join(File.dirname(__FILE__), '..', 'lib')
5
-
6
1
  require 'test/unit'
7
2
  require 'stringio'
8
- require 'midilib'
3
+ require_relative '../lib/midilib'
9
4
 
10
5
  class MIDI::IO::MIDIFile
11
6
  attr_writer :io
@@ -1,10 +1,5 @@
1
- # Start looking for MIDI classes in the directory above this one.
2
- # This forces us to use the local copy of MIDI, even if there is
3
- # a previously installed version out there somewhere.
4
- $LOAD_PATH[0, 0] = File.join(File.dirname(__FILE__), '..', 'lib')
5
-
6
1
  require 'test/unit'
7
- require 'midilib'
2
+ require_relative '../lib/midilib'
8
3
 
9
4
  class SequenceTester < Test::Unit::TestCase
10
5
  def setup
data/test/test_track.rb CHANGED
@@ -1,10 +1,5 @@
1
- # Start looking for MIDI classes in the directory above this one.
2
- # This forces us to use the local copy of MIDI, even if there is
3
- # a previously installed version out there somewhere.
4
- $LOAD_PATH[0, 0] = File.join(File.dirname(__FILE__), '..', 'lib')
5
-
6
1
  require 'test/unit'
7
- require 'midilib'
2
+ require_relative '../lib/midilib'
8
3
 
9
4
  class TrackTester < Test::Unit::TestCase
10
5
  def setup
@@ -61,7 +56,8 @@ class TrackTester < Test::Unit::TestCase
61
56
  def test_merge
62
57
  list = (1..12).collect { |i| MIDI::NoteOn.new(0, 64, 64, 10) }
63
58
  @track.merge(list)
64
- assert_equal(15, @track.events.length)
59
+ # We merged 15 events, but an end of track meta event was added by merge
60
+ assert_equal(16, @track.events.length)
65
61
  assert_equal(10, @track.events[0].time_from_start)
66
62
  assert_equal(10, @track.events[0].delta_time)
67
63
  assert_equal(20, @track.events[1].time_from_start)
@@ -79,6 +75,7 @@ class TrackTester < Test::Unit::TestCase
79
75
  assert_equal(120, @track.events[12].time_from_start)
80
76
  assert_equal(200, @track.events[13].time_from_start)
81
77
  assert_equal(300, @track.events[14].time_from_start)
78
+ assert_equal(300, @track.events[15].time_from_start) # end of track meta event
82
79
  end
83
80
 
84
81
  def test_recalc_delta_from_times
@@ -140,4 +137,66 @@ class TrackTester < Test::Unit::TestCase
140
137
  x = MIDI::NoteOff.new(0, 64, 64, 10)
141
138
  assert(x.is_a?(MIDI::NoteOffEvent)) # old name
142
139
  end
140
+
141
+ def test_delete_event
142
+ # Event is not in the track; nothing happens
143
+ @track.delete_event(MIDI::Controller.new(0, 64, 64, 200))
144
+ assert_equal(3, @track.events.length)
145
+
146
+ # Make sure we update delta times and that start times are preserved
147
+ e = @track.events[1]
148
+ @track.delete_event(e)
149
+ assert_equal(2, @track.events.length)
150
+ assert(@track.events.index(e).nil?)
151
+ assert_equal([100, 200], @track.events.map(&:delta_time))
152
+ assert_equal([100, 300], @track.events.map(&:time_from_start))
153
+ end
154
+
155
+ def test_ensure_track_end_meta_event
156
+ @track.ensure_track_end_meta_event
157
+ assert_equal(4, @track.events.length)
158
+ e = @track.events.last
159
+ assert(e.is_a?(MIDI::MetaEvent))
160
+ assert_equal(MIDI::META_TRACK_END, e.meta_type)
161
+ assert_equal(0, e.delta_time)
162
+ assert_equal(@track.events[-2].time_from_start, e.time_from_start)
163
+ end
164
+
165
+ def test_ensure_track_end_meta_event_removes_duplicates
166
+ mte = MIDI::MetaEvent.new(MIDI::META_TRACK_END, nil, 0)
167
+ @track.events << mte
168
+ @track.events.unshift(mte.dup)
169
+ @track.events.unshift(mte.dup)
170
+
171
+ @track.ensure_track_end_meta_event
172
+ mtes = @track.events.select { |e| e.is_a?(MIDI::MetaEvent) && e.meta_type == MIDI::META_TRACK_END }
173
+ assert_equal(1, mtes.length)
174
+ assert(@track.events.last.is_a?(MIDI::MetaEvent) && @track.events.last.meta_type == MIDI::META_TRACK_END)
175
+ end
176
+
177
+ def test_ensure_track_end_with_dupes_does_not_shrink_track
178
+ mte = MIDI::MetaEvent.new(MIDI::META_TRACK_END, nil, 123)
179
+ @track.events.unshift(mte.dup)
180
+ @track.events << mte
181
+ @track.recalc_times
182
+ start_time = @track.events.last.time_from_start
183
+
184
+ @track.ensure_track_end_meta_event
185
+ mtes = @track.events.select { |e| e.is_a?(MIDI::MetaEvent) && e.meta_type == MIDI::META_TRACK_END }
186
+ assert_equal(1, mtes.length)
187
+ assert_equal(mte, mtes[0])
188
+
189
+ # As a side effect, ensure_track_end_meta_event calls recalc_times which
190
+ # in this case will modify the start time of mte.
191
+ assert_equal(start_time, mte.time_from_start)
192
+ end
193
+
194
+ def test_ensure_track_end_adds_to_empty_track
195
+ t = MIDI::Track.new(@seq)
196
+ t.ensure_track_end_meta_event
197
+
198
+ assert_equal(1, t.events.length)
199
+ mte = t.events.first
200
+ assert(mte.is_a?(MIDI::MetaEvent) && mte.meta_type == MIDI::META_TRACK_END)
201
+ end
143
202
  end
data/test/test_varlen.rb CHANGED
@@ -1,10 +1,5 @@
1
- # Start looking for MIDI classes in the directory above this one.
2
- # This forces us to use the local copy of MIDI, even if there is
3
- # a previously installed version out there somewhere.
4
- $LOAD_PATH[0, 0] = File.join(File.dirname(__FILE__), '..', 'lib')
5
-
6
1
  require 'test/unit'
7
- require 'midilib'
2
+ require_relative '../lib/midilib'
8
3
 
9
4
  class VarLenTester < Test::Unit::TestCase
10
5
  VAR_LEN_TEST_DATA = {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: midilib
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 4.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jim Menard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-09-04 00:00:00.000000000 Z
11
+ date: 2023-09-12 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  midilib is a pure Ruby MIDI library useful for reading and
@@ -35,90 +35,6 @@ files:
35
35
  - examples/split.rb
36
36
  - examples/strings.rb
37
37
  - examples/transpose.rb
38
- - html/MIDI.html
39
- - html/MIDI/ActiveSense.html
40
- - html/MIDI/ChannelEvent.html
41
- - html/MIDI/ChannelPressure.html
42
- - html/MIDI/Clock.html
43
- - html/MIDI/Continue.html
44
- - html/MIDI/Controller.html
45
- - html/MIDI/Event.html
46
- - html/MIDI/IO.html
47
- - html/MIDI/IO/MIDIFile.html
48
- - html/MIDI/IO/SeqReader.html
49
- - html/MIDI/IO/SeqWriter.html
50
- - html/MIDI/KeySig.html
51
- - html/MIDI/Marker.html
52
- - html/MIDI/Measure.html
53
- - html/MIDI/Measures.html
54
- - html/MIDI/MetaEvent.html
55
- - html/MIDI/NoteEvent.html
56
- - html/MIDI/NoteOff.html
57
- - html/MIDI/NoteOn.html
58
- - html/MIDI/PitchBend.html
59
- - html/MIDI/PolyPressure.html
60
- - html/MIDI/ProgramChange.html
61
- - html/MIDI/Realtime.html
62
- - html/MIDI/Sequence.html
63
- - html/MIDI/SongPointer.html
64
- - html/MIDI/SongSelect.html
65
- - html/MIDI/Start.html
66
- - html/MIDI/Stop.html
67
- - html/MIDI/SystemCommon.html
68
- - html/MIDI/SystemExclusive.html
69
- - html/MIDI/SystemReset.html
70
- - html/MIDI/Tempo.html
71
- - html/MIDI/TimeSig.html
72
- - html/MIDI/Track.html
73
- - html/MIDI/TuneRequest.html
74
- - html/MIDI/Utils.html
75
- - html/Object.html
76
- - html/README_rdoc.html
77
- - html/TODO_rdoc.html
78
- - html/created.rid
79
- - html/css/fonts.css
80
- - html/css/rdoc.css
81
- - html/fonts/Lato-Light.ttf
82
- - html/fonts/Lato-LightItalic.ttf
83
- - html/fonts/Lato-Regular.ttf
84
- - html/fonts/Lato-RegularItalic.ttf
85
- - html/fonts/SourceCodePro-Bold.ttf
86
- - html/fonts/SourceCodePro-Regular.ttf
87
- - html/images/add.png
88
- - html/images/arrow_up.png
89
- - html/images/brick.png
90
- - html/images/brick_link.png
91
- - html/images/bug.png
92
- - html/images/bullet_black.png
93
- - html/images/bullet_toggle_minus.png
94
- - html/images/bullet_toggle_plus.png
95
- - html/images/date.png
96
- - html/images/delete.png
97
- - html/images/find.png
98
- - html/images/loadingAnimation.gif
99
- - html/images/macFFBgHack.png
100
- - html/images/package.png
101
- - html/images/page_green.png
102
- - html/images/page_white_text.png
103
- - html/images/page_white_width.png
104
- - html/images/plugin.png
105
- - html/images/ruby.png
106
- - html/images/tag_blue.png
107
- - html/images/tag_green.png
108
- - html/images/transparent.png
109
- - html/images/wrench.png
110
- - html/images/wrench_orange.png
111
- - html/images/zoom.png
112
- - html/index.html
113
- - html/js/darkfish.js
114
- - html/js/navigation.js
115
- - html/js/navigation.js.gz
116
- - html/js/search.js
117
- - html/js/search_index.js
118
- - html/js/search_index.js.gz
119
- - html/js/searcher.js
120
- - html/js/searcher.js.gz
121
- - html/table_of_contents.html
122
38
  - install.rb
123
39
  - lib/midilib.rb
124
40
  - lib/midilib/consts.rb
@@ -163,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
163
79
  version: '0'
164
80
  requirements:
165
81
  - none
166
- rubygems_version: 3.3.7
82
+ rubygems_version: 3.4.17
167
83
  signing_key:
168
84
  specification_version: 4
169
85
  summary: MIDI file and event manipulation library