midilib 3.1.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +12 -12
  3. data/Credits +7 -0
  4. data/README.rdoc +10 -1
  5. data/Rakefile +0 -1
  6. data/TODO.rdoc +8 -7
  7. data/lib/midilib/event.rb +2 -2
  8. data/lib/midilib/info.rb +3 -3
  9. data/lib/midilib/io/seqreader.rb +11 -27
  10. data/lib/midilib/io/seqwriter.rb +6 -4
  11. data/lib/midilib/track.rb +38 -6
  12. data/test/test_event.rb +12 -0
  13. data/test/test_io.rb +28 -2
  14. data/test/test_track.rb +65 -1
  15. metadata +3 -87
  16. data/html/MIDI/ActiveSense.html +0 -171
  17. data/html/MIDI/ChannelEvent.html +0 -190
  18. data/html/MIDI/ChannelPressure.html +0 -212
  19. data/html/MIDI/Clock.html +0 -171
  20. data/html/MIDI/Continue.html +0 -171
  21. data/html/MIDI/Controller.html +0 -224
  22. data/html/MIDI/Event.html +0 -349
  23. data/html/MIDI/IO/MIDIFile.html +0 -1392
  24. data/html/MIDI/IO/SeqReader.html +0 -642
  25. data/html/MIDI/IO/SeqWriter.html +0 -442
  26. data/html/MIDI/IO.html +0 -91
  27. data/html/MIDI/KeySig.html +0 -292
  28. data/html/MIDI/Marker.html +0 -138
  29. data/html/MIDI/Measure.html +0 -260
  30. data/html/MIDI/Measures.html +0 -222
  31. data/html/MIDI/MetaEvent.html +0 -353
  32. data/html/MIDI/NoteEvent.html +0 -255
  33. data/html/MIDI/NoteOff.html +0 -188
  34. data/html/MIDI/NoteOn.html +0 -188
  35. data/html/MIDI/PitchBend.html +0 -213
  36. data/html/MIDI/PolyPressure.html +0 -216
  37. data/html/MIDI/ProgramChange.html +0 -212
  38. data/html/MIDI/Realtime.html +0 -194
  39. data/html/MIDI/Sequence.html +0 -641
  40. data/html/MIDI/SongPointer.html +0 -213
  41. data/html/MIDI/SongSelect.html +0 -212
  42. data/html/MIDI/Start.html +0 -171
  43. data/html/MIDI/Stop.html +0 -171
  44. data/html/MIDI/SystemCommon.html +0 -138
  45. data/html/MIDI/SystemExclusive.html +0 -215
  46. data/html/MIDI/SystemReset.html +0 -171
  47. data/html/MIDI/Tempo.html +0 -292
  48. data/html/MIDI/TimeSig.html +0 -286
  49. data/html/MIDI/Track.html +0 -489
  50. data/html/MIDI/TuneRequest.html +0 -194
  51. data/html/MIDI/Utils.html +0 -178
  52. data/html/MIDI.html +0 -281
  53. data/html/Object.html +0 -197
  54. data/html/README_rdoc.html +0 -545
  55. data/html/TODO_rdoc.html +0 -123
  56. data/html/created.rid +0 -15
  57. data/html/css/fonts.css +0 -167
  58. data/html/css/rdoc.css +0 -639
  59. data/html/fonts/Lato-Light.ttf +0 -0
  60. data/html/fonts/Lato-LightItalic.ttf +0 -0
  61. data/html/fonts/Lato-Regular.ttf +0 -0
  62. data/html/fonts/Lato-RegularItalic.ttf +0 -0
  63. data/html/fonts/SourceCodePro-Bold.ttf +0 -0
  64. data/html/fonts/SourceCodePro-Regular.ttf +0 -0
  65. data/html/images/add.png +0 -0
  66. data/html/images/arrow_up.png +0 -0
  67. data/html/images/brick.png +0 -0
  68. data/html/images/brick_link.png +0 -0
  69. data/html/images/bug.png +0 -0
  70. data/html/images/bullet_black.png +0 -0
  71. data/html/images/bullet_toggle_minus.png +0 -0
  72. data/html/images/bullet_toggle_plus.png +0 -0
  73. data/html/images/date.png +0 -0
  74. data/html/images/delete.png +0 -0
  75. data/html/images/find.png +0 -0
  76. data/html/images/loadingAnimation.gif +0 -0
  77. data/html/images/macFFBgHack.png +0 -0
  78. data/html/images/package.png +0 -0
  79. data/html/images/page_green.png +0 -0
  80. data/html/images/page_white_text.png +0 -0
  81. data/html/images/page_white_width.png +0 -0
  82. data/html/images/plugin.png +0 -0
  83. data/html/images/ruby.png +0 -0
  84. data/html/images/tag_blue.png +0 -0
  85. data/html/images/tag_green.png +0 -0
  86. data/html/images/transparent.png +0 -0
  87. data/html/images/wrench.png +0 -0
  88. data/html/images/wrench_orange.png +0 -0
  89. data/html/images/zoom.png +0 -0
  90. data/html/index.html +0 -534
  91. data/html/js/darkfish.js +0 -84
  92. data/html/js/navigation.js +0 -105
  93. data/html/js/navigation.js.gz +0 -0
  94. data/html/js/search.js +0 -110
  95. data/html/js/search_index.js +0 -1
  96. data/html/js/search_index.js.gz +0 -0
  97. data/html/js/searcher.js +0 -229
  98. data/html/js/searcher.js.gz +0 -0
  99. data/html/table_of_contents.html +0 -1258
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9bef04b13853bd3e16488263bd4fdf5fe3f43bc9fa8f48f6967ff8cd78fc2a16
4
- data.tar.gz: fd3bf8e7ed497e1396a228b7f6506f25067b2673b381fd6b5312899369e490db
3
+ metadata.gz: f41957f8222283fa766d489f32dc603edf23fe904682107e216bb5646325fd27
4
+ data.tar.gz: 2c55d4cb9c3d2255b721725d622936a59456e87e70df4da77cfb1b82f230a338
5
5
  SHA512:
6
- metadata.gz: 1fabc2a6c78a0152d48f6139e644cea2ef67b90c66b38ec085d6eabf4e11ce0c0a6549d5a34ea7020e05b0b3cd069db000e80444daacb8681afe03d458965e15
7
- data.tar.gz: 10ce51a6d08c5181cafea51f1772d110cee3a7c244ad47ed06be76b8395f4dfcdf3cfb79a8cd15d9c5dd8c235094fc30013a5d25e0c396574452a1765f38bea1
6
+ metadata.gz: 884f3ab3488afa1c717ec571de2ebca279ce1e13caa69039b7e0dec2bcf265c3285169d5abe8500cf9cd60f55d818876774bcd3daa0782f2e76d6c4955b1511f
7
+ data.tar.gz: fce2b737b8c56017d35e27117bee86f2455a05286df63685b354c8d44666369a28353c4efc4e86a8fe01edbf803bc38a8d8549c8ea94003d66fd30eaea8eb3bd
data/ChangeLog CHANGED
@@ -1,16 +1,16 @@
1
1
  This change log is no longer maintaned. For further change descriptions,
2
2
  see the Git logs.
3
3
 
4
- 2007-12-11 Jim Menard <jim@iamplify.com>
4
+ 2007-12-11 Jim Menard <jim@jimmenard.com>
5
5
 
6
6
  * lib/midilib/io/seqreader.rb: Fixed text method and added default
7
7
  implementation of key_signature.
8
8
 
9
- 2007-12-09 Jim Menard <jim@iamplify.com>
9
+ 2007-12-09 Jim Menard <jim@jimmenard.com>
10
10
 
11
11
  * examples/measures_mbt.rb: Added.
12
12
 
13
- 2007-12-09 Jim Menard <jim@iamplify.com>
13
+ 2007-12-09 Jim Menard <jim@jimmenard.com>
14
14
 
15
15
  * lib/midilib/event.rb: Added code that fixes bpm calculation
16
16
  bugs, and his TimeSig and KeySig classes.
@@ -19,17 +19,17 @@ see the Git logs.
19
19
 
20
20
  * lib/midilib/sequence.rb: Fixed clock. Added get_measures method.
21
21
 
22
- 2007-12-09 Jim Menard <jim@iamplify.com>
22
+ 2007-12-09 Jim Menard <jim@jimmenard.com>
23
23
 
24
24
  * lib/midilib/event.rb: added program_change? and
25
25
  print_channel_numbers_from_one to Event.
26
26
 
27
- 2007-03-17 Jim Menard <jimm@io.com>
27
+ 2007-03-17 Jim Menard <jim@jimmenard.com>
28
28
 
29
29
  * Version 1.0.0 released. I will no longer be maintaining this
30
30
  change log; the Subversion comments should be sufficient.
31
31
 
32
- 2006-08-20 Jim Menard <jimm@io.com>
32
+ 2006-08-20 Jim Menard <jim@jimmenard.com>
33
33
 
34
34
  * lib/midilib/event.rb (PolyPressure::initialize): Fixed the
35
35
  misspelled POLY_PRESSURE cosntant, thanks to Mario Pehle
@@ -39,7 +39,7 @@ see the Git logs.
39
39
 
40
40
  * test/test.mid: created (copied examples/from_scratch.mid).
41
41
 
42
- 2005-03-21 Jim Menard <jimm@io.com>
42
+ 2005-03-21 Jim Menard <jim@jimmenard.com>
43
43
 
44
44
  * Version 0.8.4 released.
45
45
 
@@ -48,7 +48,7 @@ see the Git logs.
48
48
  (SystemCommon::initialize): moved @is_system = true to here
49
49
  (SystemExclusive::initialize): ...from here.
50
50
 
51
- 2005-03-20 Jim Menard <jimm@io.com>
51
+ 2005-03-20 Jim Menard <jim@jimmenard.com>
52
52
 
53
53
  * lib/midilib/sequence.rb (Sequence::note_to_delta): created.
54
54
  (Sequence::note_to_length): created.
@@ -56,11 +56,11 @@ see the Git logs.
56
56
 
57
57
  * examples/from_scratch.rb: created.
58
58
 
59
- 2004-07-16 Jim Menard <jimm@io.com>
59
+ 2004-07-16 Jim Menard <jim@jimmenard.com>
60
60
 
61
61
  * Version 0.8.3 released.
62
62
 
63
- 2004-07-10 Jim Menard <jimm@io.com>
63
+ 2004-07-10 Jim Menard <jim@jimmenard.com>
64
64
 
65
65
  * lib/midilib/event.rb (NoteEvent::note_to_s): created.
66
66
  (Event::number_to_s): created.
@@ -68,7 +68,7 @@ see the Git logs.
68
68
  attributes.
69
69
  (to_s all classes): use @print_note_names and @print_decimal_numbers
70
70
 
71
- 2004-06-30 Jim Menard <jimm@io.com>
71
+ 2004-06-30 Jim Menard <jim@jimmenard.com>
72
72
 
73
73
  * Version 0.8.2 released.
74
74
 
@@ -103,7 +103,7 @@ see the Git logs.
103
103
  * lib/midilib/io/seqreader.rb (SeqReader::initialize): added block
104
104
  rdoc comment.
105
105
 
106
- 2004-06-27 Jim Menard <jimm@io.com>
106
+ 2004-06-27 Jim Menard <jim@jimmenard.com>
107
107
 
108
108
  * Version 0.8.1 released.
109
109
 
data/Credits CHANGED
@@ -96,3 +96,10 @@ Miika Alonen (@amiika on Github)
96
96
 
97
97
  Found a bug caused by note offs coming right before note ons. See
98
98
  https://github.com/jimm/midilib/pull/21 for a detailed write-up.
99
+
100
+ @mike-bourgeous on Github
101
+
102
+ Opened an issue (https://github.com/jimm/midilib/issues/24) which led to
103
+ the addition of end of track meta events when reading and writing MIDI
104
+ files.
105
+ Found and fixed a bug in SongPointer#data_as_bytes.
data/README.rdoc CHANGED
@@ -91,6 +91,15 @@ This bit mask is set when the track is read from the MIDI file by a SeqReader
91
91
  but is _not_ kept up to date by any other methods. Specifically, if you add
92
92
  events to a track at any other time, the bit mask will not be updated.
93
93
 
94
+ When a Track is read from a MIDI file, a MIDI::META_TRACK_END event is added
95
+ to the end if there isn't one in the file already. When a Track is written
96
+ to a MIDI file, a MIDI::META_TRACK_END event is always output even if the
97
+ Track does not have one.
98
+
99
+ The Track#merge method ensures that there is only one MIDI::META_TRACK_END
100
+ event after the merge and that it's at its proper place at the end of the
101
+ list of events. It does so by calling Track#ensure_track_end_meta_event.
102
+
94
103
  === MIDI::Measure
95
104
 
96
105
  This class contains information about a measure from the sequence. Measure
@@ -133,7 +142,7 @@ string. When assigning to a meta event's data, if you pass in a string it will
133
142
  get converted to an array of bytes.
134
143
 
135
144
 
136
- == How To Use
145
+ == How To Use midilib
137
146
 
138
147
  The following examples show you how to use midilib to read, write, and
139
148
  manipulate MIDI files and modify track events. See also the files in the
data/Rakefile CHANGED
@@ -33,7 +33,6 @@ spec = Gem::Specification.new do |s|
33
33
  s.author = 'Jim Menard'
34
34
  s.email = 'jim@jimmenard.com'
35
35
  s.homepage = 'http://github.com/jimm/midilib'
36
- s.rubyforge_project = PROJECT_NAME
37
36
  s.license = 'Ruby'
38
37
 
39
38
  s.summary = 'MIDI file and event manipulation library'
data/TODO.rdoc CHANGED
@@ -1,13 +1,14 @@
1
1
  == Bugs
2
2
 
3
3
  midilib does not handle tempo changes when calculating +beats_per_minute+.
4
- The tempo events are correctly handled when reading/writing/moving them
5
- around, it's just the functions that answer questions about the current
6
- tempo that are wrong. See https://github.com/jimm/midilib/issues/8. The
7
- method +beats_per_minute+ and related methods like +pulses_to_seconds+ will
8
- have to take into account the possibility of more than one tempo event. They
9
- will probably have to take new arguments specifying where in the sequence
10
- the beats or pulses are being requested. For example we could have
4
+ See https://github.com/jimm/midilib/issues/8 which describes the issue. The
5
+ tempo events are correctly handled when reading/writing/moving them around,
6
+ it's just the functions that answer questions about the current tempo that
7
+ are wrong. See https://github.com/jimm/midilib/issues/8. The method
8
+ +beats_per_minute+ and related methods like +pulses_to_seconds+ will have to
9
+ take into account the possibility of more than one tempo event. They will
10
+ probably have to take new arguments specifying where in the sequence the
11
+ beats or pulses are being requested. For example we could have
11
12
  +beats_per_minute(at_seconds: 0.0, at_beat: 0.0)+ (where the two keyword
12
13
  args are mutually exclusive). Perhaps there should be separately named
13
14
  public methods like +beats_per_minute_at_seconds+ and
data/lib/midilib/event.rb CHANGED
@@ -299,8 +299,8 @@ module MIDI
299
299
  def data_as_bytes
300
300
  data = []
301
301
  data << @status
302
- data << ((@pointer >> 8) & 0xff)
303
- data << (@pointer & 0xff)
302
+ data << (@pointer & 0x7f)
303
+ data << ((@pointer >> 7) & 0x7f)
304
304
  end
305
305
 
306
306
  def to_s
data/lib/midilib/info.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module MIDI
2
- VERSION_MAJOR = 3
3
- VERSION_MINOR = 1
2
+ VERSION_MAJOR = 4
3
+ VERSION_MINOR = 0
4
4
  VERSION_TWEAK = 0
5
5
  Version = "#{VERSION_MAJOR}.#{VERSION_MINOR}.#{VERSION_TWEAK}"
6
- Copyright = 'Copyright (c) 2003-2022 by Jim Menard <jim@jimmenard.com>'
6
+ Copyright = 'Copyright (c) 2003-2023 by Jim Menard <jim@jimmenard.com>'
7
7
  end
@@ -8,16 +8,9 @@ module MIDI
8
8
  # callback methods for each MIDI event and use them to build Track and
9
9
  # Event objects and give the tracks to a Sequence.
10
10
  #
11
- # We append new events to the end of a track's event list, bypassing a
12
- # call to Track.#add. This means that we must call Track.recalc_times at
13
- # the end of the track so it can update each event with its time from
14
- # the track's start (see end_track below).
15
- #
16
- # META_TRACK_END events are not added to tracks. This way, we don't have
17
- # to worry about making sure the last event is always a track end event.
18
- # We rely on the SeqWriter to append a META_TRACK_END event to each
19
- # track when it is output.
20
-
11
+ # Ensures that each track ends with an end of track meta event, and that
12
+ # Track#recalc_times is called at the end of the track so it can update
13
+ # each event with its time from the track's start (see end_track below).
21
14
  class SeqReader < MIDIFile
22
15
  # The optional &block is called once at the start of the file and
23
16
  # again at the end of each track. There are three arguments to the
@@ -51,12 +44,9 @@ module MIDI
51
44
  @pending.each { |on| make_note_off(on, 64) }
52
45
  @pending = nil
53
46
 
54
- # Don't bother adding the META_TRACK_END event to the track.
55
- # This way, we don't have to worry about making sure the
56
- # last event is always a track end event.
57
-
58
- # Let the track calculate event times from start of track. This is
59
- # in lieu of calling Track.add for each event.
47
+ # Make sure track has an end of track event and that all of the
48
+ # `time_from_start` values are correct.
49
+ @track.ensure_track_end_meta_event
60
50
  @track.recalc_times
61
51
 
62
52
  # Store bitmask of all channels used into track
@@ -102,7 +92,7 @@ module MIDI
102
92
 
103
93
  if $DEBUG
104
94
  warn "note off with no earlier note on (ch #{chan}, note" +
105
- " #{note}, vel #{vel})"
95
+ " #{note}, vel #{vel})"
106
96
  end
107
97
  end
108
98
  end
@@ -143,6 +133,10 @@ module MIDI
143
133
  @track.events << SystemExclusive.new(msg, @curr_ticks)
144
134
  end
145
135
 
136
+ def eot
137
+ @track.events << MetaEvent.new(META_TRACK_END, nil, @curr_ticks)
138
+ end
139
+
146
140
  def meta_misc(type, msg)
147
141
  @track.events << MetaEvent.new(type, msg, @curr_ticks)
148
142
  end
@@ -168,16 +162,6 @@ module MIDI
168
162
  end
169
163
  end
170
164
 
171
- # --
172
- # Don't bother adding the META_TRACK_END event to the track. This way,
173
- # we don't have to worry about always making sure the last event is
174
- # always a track end event. We just have to make sure to write one when
175
- # the track is output back to a file.
176
- # def eot()
177
- # @track.events << MetaEvent.new(META_TRACK_END, nil, @curr_ticks)
178
- # end
179
- # ++
180
-
181
165
  def time_signature(numer, denom, clocks, qnotes)
182
166
  @seq.time_signature(numer, denom, clocks, qnotes)
183
167
  @track.events << TimeSig.new(numer, denom, clocks, qnotes, @curr_ticks)
@@ -66,10 +66,12 @@ module MIDI
66
66
  prev_status = status
67
67
  end
68
68
 
69
- # Write track end event.
70
- event = MetaEvent.new(META_TRACK_END)
71
- write_var_len(0)
72
- @bytes_written += write_bytes(event.data_as_bytes)
69
+ # Write track end event if the track doesn't have one.
70
+ unless track.events.last.is_a?(MetaEvent) && track.events.last.meta_type == META_TRACK_END
71
+ event = MetaEvent.new(META_TRACK_END)
72
+ write_var_len(0)
73
+ @bytes_written += write_bytes(event.data_as_bytes)
74
+ end
73
75
 
74
76
  # Go back to beginning of track data and write number of bytes,
75
77
  # then come back here to end of file.
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
@@ -130,4 +130,16 @@ class EventTester < Test::Unit::TestCase
130
130
  assert_equal('foobar', e.data_as_str)
131
131
  assert_equal(foobar_as_array, e.data)
132
132
  end
133
+
134
+ def test_song_pointer
135
+ e = MIDI::SongPointer.new(1)
136
+ b = e.data_as_bytes
137
+ assert_equal(1, b[1]) # lsb, 7 bits
138
+ assert_equal(0, b[2]) # msb, 7 bits
139
+
140
+ e.pointer = (3 << 7) + 42
141
+ b = e.data_as_bytes
142
+ assert_equal(42, b[1]) # lsb, 7 bits
143
+ assert_equal(3, b[2]) # msb, 7 bits
144
+ end
133
145
  end
data/test/test_io.rb CHANGED
@@ -35,7 +35,12 @@ class IOTester < Test::Unit::TestCase
35
35
  assert_equal(1, format0_seq.tracks.length, 'number of tracks differ')
36
36
  format_1_count = multitrack_seq.tracks.map { |t| t.events.count }.reduce(:+)
37
37
  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')
38
+
39
+ # The format 1 file will have one more event because there is an end of
40
+ # track meta event at the end of each track (the track 0 metadata track
41
+ # and track 1 with the notes), whereas the format 0 file only has one
42
+ # track, thus one end of track meta event.
43
+ assert_equal(format_1_count, format_0_count + 1, 'different number of total events')
39
44
  end
40
45
 
41
46
  def test_read_and_write
@@ -101,6 +106,22 @@ class IOTester < Test::Unit::TestCase
101
106
  assert_equal(MIDI::GM_PATCH_NAMES[0], seq.tracks[1].instrument)
102
107
  end
103
108
 
109
+ # This is a regression test.
110
+ def test_read_eot_preserves_delta
111
+ seq = MIDI::Sequence.new
112
+ File.open(SEQ_TEST_FILE, 'rb') { |f| seq.read(f) }
113
+ track = seq.tracks.last
114
+ mte = MIDI::MetaEvent.new(MIDI::META_TRACK_END, nil, 123)
115
+ track.events << mte
116
+ track.recalc_times
117
+ File.open(OUTPUT_FILE, 'wb') { |f| seq.write(f) }
118
+ File.open(OUTPUT_FILE, 'rb') { |f| seq.read(f) }
119
+
120
+ assert_equal(mte, seq.tracks.last.events.last)
121
+ ensure
122
+ File.delete(OUTPUT_FILE) if File.exist?(OUTPUT_FILE)
123
+ end
124
+
104
125
  def test_preserve_deltas_in_some_situations
105
126
  out_seq = MIDI::Sequence.new
106
127
  out_track = MIDI::Track.new(out_seq)
@@ -135,9 +156,14 @@ class IOTester < Test::Unit::TestCase
135
156
  in_seq = MIDI::Sequence.new
136
157
  File.open(TEMPFILE, 'rb') { |file| in_seq.read(file) }
137
158
  in_track = in_seq.tracks[0]
138
- assert_equal(out_track.events.length, in_track.events.length)
159
+ assert_equal(out_track.events.length + 1, in_track.events.length) # read added end of track meta event
139
160
  out_track.events.each_with_index do |event, i|
140
161
  assert_equal(event, in_track.events[i])
141
162
  end
163
+
164
+ # Last event is a end of track meta event
165
+ e = in_track.events.last
166
+ assert(e.is_a?(MIDI::MetaEvent))
167
+ assert(e.meta_type == MIDI::META_TRACK_END)
142
168
  end
143
169
  end
data/test/test_track.rb CHANGED
@@ -61,7 +61,8 @@ class TrackTester < Test::Unit::TestCase
61
61
  def test_merge
62
62
  list = (1..12).collect { |i| MIDI::NoteOn.new(0, 64, 64, 10) }
63
63
  @track.merge(list)
64
- assert_equal(15, @track.events.length)
64
+ # We merged 15 events, but an end of track meta event was added by merge
65
+ assert_equal(16, @track.events.length)
65
66
  assert_equal(10, @track.events[0].time_from_start)
66
67
  assert_equal(10, @track.events[0].delta_time)
67
68
  assert_equal(20, @track.events[1].time_from_start)
@@ -79,6 +80,7 @@ class TrackTester < Test::Unit::TestCase
79
80
  assert_equal(120, @track.events[12].time_from_start)
80
81
  assert_equal(200, @track.events[13].time_from_start)
81
82
  assert_equal(300, @track.events[14].time_from_start)
83
+ assert_equal(300, @track.events[15].time_from_start) # end of track meta event
82
84
  end
83
85
 
84
86
  def test_recalc_delta_from_times
@@ -140,4 +142,66 @@ class TrackTester < Test::Unit::TestCase
140
142
  x = MIDI::NoteOff.new(0, 64, 64, 10)
141
143
  assert(x.is_a?(MIDI::NoteOffEvent)) # old name
142
144
  end
145
+
146
+ def test_delete_event
147
+ # Event is not in the track; nothing happens
148
+ @track.delete_event(MIDI::Controller.new(0, 64, 64, 200))
149
+ assert_equal(3, @track.events.length)
150
+
151
+ # Make sure we update delta times and that start times are preserved
152
+ e = @track.events[1]
153
+ @track.delete_event(e)
154
+ assert_equal(2, @track.events.length)
155
+ assert(@track.events.index(e).nil?)
156
+ assert_equal([100, 200], @track.events.map(&:delta_time))
157
+ assert_equal([100, 300], @track.events.map(&:time_from_start))
158
+ end
159
+
160
+ def test_ensure_track_end_meta_event
161
+ @track.ensure_track_end_meta_event
162
+ assert_equal(4, @track.events.length)
163
+ e = @track.events.last
164
+ assert(e.is_a?(MIDI::MetaEvent))
165
+ assert_equal(MIDI::META_TRACK_END, e.meta_type)
166
+ assert_equal(0, e.delta_time)
167
+ assert_equal(@track.events[-2].time_from_start, e.time_from_start)
168
+ end
169
+
170
+ def test_ensure_track_end_meta_event_removes_duplicates
171
+ mte = MIDI::MetaEvent.new(MIDI::META_TRACK_END, nil, 0)
172
+ @track.events << mte
173
+ @track.events.unshift(mte.dup)
174
+ @track.events.unshift(mte.dup)
175
+
176
+ @track.ensure_track_end_meta_event
177
+ mtes = @track.events.select { |e| e.is_a?(MIDI::MetaEvent) && e.meta_type == MIDI::META_TRACK_END }
178
+ assert_equal(1, mtes.length)
179
+ assert(@track.events.last.is_a?(MIDI::MetaEvent) && @track.events.last.meta_type == MIDI::META_TRACK_END)
180
+ end
181
+
182
+ def test_ensure_track_end_with_dupes_does_not_shrink_track
183
+ mte = MIDI::MetaEvent.new(MIDI::META_TRACK_END, nil, 123)
184
+ @track.events.unshift(mte.dup)
185
+ @track.events << mte
186
+ @track.recalc_times
187
+ start_time = @track.events.last.time_from_start
188
+
189
+ @track.ensure_track_end_meta_event
190
+ mtes = @track.events.select { |e| e.is_a?(MIDI::MetaEvent) && e.meta_type == MIDI::META_TRACK_END }
191
+ assert_equal(1, mtes.length)
192
+ assert_equal(mte, mtes[0])
193
+
194
+ # As a side effect, ensure_track_end_meta_event calls recalc_times which
195
+ # in this case will modify the start time of mte.
196
+ assert_equal(start_time, mte.time_from_start)
197
+ end
198
+
199
+ def test_ensure_track_end_adds_to_empty_track
200
+ t = MIDI::Track.new(@seq)
201
+ t.ensure_track_end_meta_event
202
+
203
+ assert_equal(1, t.events.length)
204
+ mte = t.events.first
205
+ assert(mte.is_a?(MIDI::MetaEvent) && mte.meta_type == MIDI::META_TRACK_END)
206
+ end
143
207
  end
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.0
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-02-05 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.3
167
83
  signing_key:
168
84
  specification_version: 4
169
85
  summary: MIDI file and event manipulation library