midilib 1.2.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. data/ChangeLog +3 -1
  2. data/Credits +6 -1
  3. data/README.rdoc +41 -15
  4. data/Rakefile +3 -11
  5. data/TODO.rdoc +4 -7
  6. data/examples/from_scratch.rb +2 -2
  7. data/examples/measures_mbt.rb +1 -1
  8. data/examples/print_program_changes.rb +1 -1
  9. data/examples/strings.rb +1 -1
  10. data/examples/transpose.rb +1 -1
  11. data/html/Array.html +382 -0
  12. data/html/IO.html +277 -0
  13. data/html/MIDI.html +829 -0
  14. data/html/MIDI/ActiveSense.html +317 -0
  15. data/html/MIDI/ChannelEvent.html +347 -0
  16. data/html/MIDI/ChannelPressure.html +379 -0
  17. data/html/MIDI/Clock.html +317 -0
  18. data/html/MIDI/Continue.html +317 -0
  19. data/html/MIDI/Controller.html +398 -0
  20. data/html/MIDI/Event.html +659 -0
  21. data/html/MIDI/IO.html +238 -0
  22. data/html/MIDI/IO/MIDIFile.html +2269 -0
  23. data/html/MIDI/IO/SeqReader.html +1051 -0
  24. data/html/MIDI/IO/SeqWriter.html +706 -0
  25. data/html/MIDI/KeySig.html +487 -0
  26. data/html/MIDI/Marker.html +275 -0
  27. data/html/MIDI/Measure.html +479 -0
  28. data/html/MIDI/Measures.html +416 -0
  29. data/html/MIDI/MetaEvent.html +617 -0
  30. data/html/MIDI/NoteEvent.html +459 -0
  31. data/html/MIDI/NoteOff.html +341 -0
  32. data/html/MIDI/NoteOn.html +341 -0
  33. data/html/MIDI/PitchBend.html +380 -0
  34. data/html/MIDI/PolyPressure.html +390 -0
  35. data/html/MIDI/ProgramChange.html +379 -0
  36. data/html/MIDI/Realtime.html +354 -0
  37. data/html/MIDI/Sequence.html +1063 -0
  38. data/html/MIDI/SongPointer.html +380 -0
  39. data/html/MIDI/SongSelect.html +379 -0
  40. data/html/MIDI/Start.html +317 -0
  41. data/html/MIDI/Stop.html +317 -0
  42. data/html/MIDI/SystemCommon.html +275 -0
  43. data/html/MIDI/SystemExclusive.html +382 -0
  44. data/html/MIDI/SystemReset.html +317 -0
  45. data/html/MIDI/Tempo.html +519 -0
  46. data/html/MIDI/TimeSig.html +524 -0
  47. data/html/MIDI/Track.html +859 -0
  48. data/html/MIDI/TuneRequest.html +354 -0
  49. data/html/MIDI/Utils.html +350 -0
  50. data/html/README_rdoc.html +882 -0
  51. data/html/TODO_rdoc.html +215 -0
  52. data/html/created.rid +14 -0
  53. data/html/images/brick.png +0 -0
  54. data/html/images/brick_link.png +0 -0
  55. data/html/images/bug.png +0 -0
  56. data/html/images/bullet_black.png +0 -0
  57. data/html/images/bullet_toggle_minus.png +0 -0
  58. data/html/images/bullet_toggle_plus.png +0 -0
  59. data/html/images/date.png +0 -0
  60. data/html/images/find.png +0 -0
  61. data/html/images/loadingAnimation.gif +0 -0
  62. data/html/images/macFFBgHack.png +0 -0
  63. data/html/images/package.png +0 -0
  64. data/html/images/page_green.png +0 -0
  65. data/html/images/page_white_text.png +0 -0
  66. data/html/images/page_white_width.png +0 -0
  67. data/html/images/plugin.png +0 -0
  68. data/html/images/ruby.png +0 -0
  69. data/html/images/tag_green.png +0 -0
  70. data/html/images/wrench.png +0 -0
  71. data/html/images/wrench_orange.png +0 -0
  72. data/html/images/zoom.png +0 -0
  73. data/html/index.html +1266 -0
  74. data/html/js/darkfish.js +116 -0
  75. data/html/js/jquery.js +32 -0
  76. data/html/js/quicksearch.js +114 -0
  77. data/html/js/thickbox-compressed.js +10 -0
  78. data/html/lib/midilib/consts_rb.html +55 -0
  79. data/html/lib/midilib/event_rb.html +56 -0
  80. data/html/lib/midilib/info_rb.html +52 -0
  81. data/html/lib/midilib/io/midifile_rb.html +54 -0
  82. data/html/lib/midilib/io/seqreader_rb.html +58 -0
  83. data/html/lib/midilib/io/seqwriter_rb.html +59 -0
  84. data/html/lib/midilib/measure_rb.html +54 -0
  85. data/html/lib/midilib/sequence_rb.html +58 -0
  86. data/html/lib/midilib/track_rb.html +54 -0
  87. data/html/lib/midilib/utils_rb.html +52 -0
  88. data/html/lib/midilib_rb.html +71 -0
  89. data/html/rdoc.css +706 -0
  90. data/install.rb +1 -1
  91. data/lib/midilib/event.rb +11 -77
  92. data/lib/midilib/info.rb +4 -4
  93. data/lib/midilib/io/seqreader.rb +2 -2
  94. data/lib/midilib/io/seqwriter.rb +1 -1
  95. data/lib/midilib/sequence.rb +1 -1
  96. data/lib/midilib/track.rb +54 -8
  97. data/test/test_event.rb +4 -62
  98. data/test/test_sequence.rb +1 -1
  99. data/test/test_track.rb +39 -5
  100. metadata +102 -9
data/install.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  # This script installs midilib into the Ruby site-local library directory.
6
6
  #
7
7
  # Author:: Jim Menard (mailto:jimm@io.com)
8
- # Copyright:: Copyright (c) 2003-2009 by Jim Menard
8
+ # Copyright:: Copyright (c) 2003-2010 by Jim Menard
9
9
  # License:: Distributed under the same license as Ruby.
10
10
  #
11
11
 
@@ -30,16 +30,6 @@ class Event
30
30
  def initialize(status = 0, delta_time = 0)
31
31
  @status = status
32
32
  @delta_time = delta_time
33
- @is_channel = false # the majority of events are channel events
34
- @is_note = false # note on, note off, and poly press return true
35
- @is_note_on = false
36
- @is_note_off = false
37
- @is_meta = false
38
- @is_system = false
39
- @is_realtime = false
40
- @is_timesig = false
41
- @is_keysig = false
42
- @is_program_change = false
43
33
  @time_from_start = 0 # maintained by tracks
44
34
  end
45
35
  protected :initialize
@@ -51,57 +41,6 @@ class Event
51
41
  raise "subclass responsibility"
52
42
  end
53
43
 
54
- # Returns +true+ if this is a channel event.
55
- def channel?
56
- return @is_channel
57
- end
58
-
59
- # Returns +true+ if this is a note on, note off, or polyphonic pressure
60
- # event.
61
- def note?
62
- return @is_note
63
- end
64
-
65
- # Returns +true+ if this is a note on event.
66
- def note_on?
67
- @is_note_on
68
- end
69
-
70
- # Returns +true+ if this is a note off event.
71
- def note_off?
72
- @is_note_off
73
- end
74
-
75
- # Returns +true+ if this is a meta event.
76
- def meta?
77
- return @is_meta
78
- end
79
-
80
- # Returns +true+ if this is a system message event.
81
- def system?
82
- return @is_system
83
- end
84
-
85
- # Returns +true+ if this is a time signature event.
86
- def timesig?
87
- return @is_timesig
88
- end
89
-
90
- # Returns +true+ if this is a key signature event.
91
- def keysig?
92
- return @is_keysig
93
- end
94
-
95
- # Returns +true+ if this is a realtime status byte.
96
- def realtime?
97
- return @is_realtime
98
- end
99
-
100
- # Returns +true+ if this is a program change.
101
- def program_change?
102
- return @is_program_change
103
- end
104
-
105
44
  # Quantize this event's time_from_start by moving it to the nearest
106
45
  # multiple of +boundary+. See MIDI::Track#quantize. *Note*: does not
107
46
  # modify the event's delta_time, though MIDI::Track#quantize calls
@@ -148,7 +87,6 @@ class ChannelEvent < Event
148
87
  def initialize(status, channel, delta_time)
149
88
  super(status, delta_time)
150
89
  @channel = channel
151
- @is_channel = true
152
90
  end
153
91
  protected :initialize
154
92
 
@@ -166,7 +104,6 @@ class NoteEvent < ChannelEvent
166
104
  super(status, channel, delta_time)
167
105
  @note = note
168
106
  @velocity = velocity
169
- @is_note = true
170
107
  end
171
108
  protected :initialize
172
109
 
@@ -193,30 +130,36 @@ class NoteEvent < ChannelEvent
193
130
  end
194
131
  end
195
132
 
196
- class NoteOnEvent < NoteEvent
133
+ class NoteOn < NoteEvent
197
134
  attr_accessor :off
198
135
  def initialize(channel = 0, note = 64, velocity = 64, delta_time = 0)
199
136
  super(NOTE_ON, channel, note, velocity, delta_time)
200
- @is_note_on = true
201
137
  end
138
+
202
139
  def to_s
203
140
  return super <<
204
141
  "on #{note_to_s} #{number_to_s(@velocity)}"
205
142
  end
206
143
  end
207
144
 
208
- class NoteOffEvent < NoteEvent
145
+ # Old class name for compatability
146
+ NoteOnEvent = NoteOn
147
+
148
+ class NoteOff < NoteEvent
209
149
  attr_accessor :on
210
150
  def initialize(channel = 0, note = 64, velocity = 64, delta_time = 0)
211
151
  super(NOTE_OFF, channel, note, velocity, delta_time)
212
- @is_note_off = true
213
152
  end
153
+
214
154
  def to_s
215
155
  return super <<
216
156
  "off #{note_to_s} #{number_to_s(@velocity)}"
217
157
  end
218
158
  end
219
159
 
160
+ # Old class name for compatability
161
+ NoteOffEvent = NoteOff
162
+
220
163
  class PolyPressure < NoteEvent
221
164
  def initialize(channel = 0, note = 64, value = 0, delta_time = 0)
222
165
  super(POLY_PRESSURE, channel, note, value, delta_time)
@@ -241,7 +184,6 @@ class Controller < ChannelEvent
241
184
  super(CONTROLLER, channel, delta_time)
242
185
  @controller = controller
243
186
  @value = value
244
- @is_controller = true
245
187
  end
246
188
 
247
189
  def data_as_bytes
@@ -262,7 +204,6 @@ class ProgramChange < ChannelEvent
262
204
  def initialize(channel = 0, program = 0, delta_time = 0)
263
205
  super(PROGRAM_CHANGE, channel, delta_time)
264
206
  @program = program
265
- @is_program_change = true
266
207
  end
267
208
 
268
209
  def data_as_bytes
@@ -318,7 +259,6 @@ end
318
259
  class SystemCommon < Event
319
260
  def initialize(status, delta_time)
320
261
  super(status, delta_time)
321
- @is_system = true
322
262
  end
323
263
  end
324
264
 
@@ -401,7 +341,6 @@ end
401
341
  class Realtime < Event
402
342
  def initialize(status, delta_time)
403
343
  super(status, delta_time)
404
- @is_realtime = true
405
344
  end
406
345
 
407
346
  def data_as_bytes
@@ -490,7 +429,6 @@ class MetaEvent < Event
490
429
  def initialize(meta_type, data = nil, delta_time = 0)
491
430
  super(META_EVENT, delta_time)
492
431
  @meta_type = meta_type
493
- @is_meta = true
494
432
  self.data=(data)
495
433
  end
496
434
 
@@ -573,9 +511,7 @@ class Tempo < MetaEvent
573
511
 
574
512
  # Translates microseconds per quarter note (beat) to beats per minute.
575
513
  def Tempo.mpq_to_bpm(mpq)
576
- # Nov 17 2007: Fixed integer rounding error issue:
577
- # 110 bpm (as 545455 msecs) becomes 109.999 (which became 109 bpm)
578
- return Integer(MICROSECS_PER_MINUTE.to_f / mpq + 0.49)
514
+ return MICROSECS_PER_MINUTE.to_f / mpq.to_f
579
515
  end
580
516
 
581
517
  def initialize(msecs_per_qnote, delta_time = 0)
@@ -611,7 +547,6 @@ class TimeSig < MetaEvent
611
547
  # Constructor
612
548
  def initialize(numer, denom, clocks, qnotes, delta_time = 0)
613
549
  super(META_TIME_SIG, [numer, denom, clocks, qnotes], delta_time)
614
- @is_timesig = true
615
550
  end
616
551
 
617
552
  # Returns the complete event as stored in the sequence
@@ -661,7 +596,6 @@ class KeySig < MetaEvent
661
596
  # Constructor
662
597
  def initialize(sharpflat, is_minor, delta_time = 0)
663
598
  super(META_KEY_SIG, [sharpflat, is_minor], delta_time)
664
- @is_keysig = true
665
599
  end
666
600
 
667
601
  # Returns the complete event as stored in the sequence
@@ -1,9 +1,9 @@
1
1
  module MIDI
2
2
 
3
- VERSION_MAJOR = 1
4
- VERSION_MINOR = 2
5
- VERSION_TWEAK = 1
3
+ VERSION_MAJOR = 2
4
+ VERSION_MINOR = 0
5
+ VERSION_TWEAK = 0
6
6
  Version = "#{VERSION_MAJOR}.#{VERSION_MINOR}.#{VERSION_TWEAK}"
7
- Copyright = 'Copyright (c) 2003-2009 by Jim Menard <jimm@io.com>'
7
+ Copyright = 'Copyright (c) 2003-2010 by Jim Menard <jimm@io.com>'
8
8
 
9
9
  end
@@ -75,7 +75,7 @@ class SeqReader < MIDIFile
75
75
  return
76
76
  end
77
77
 
78
- on = NoteOnEvent.new(chan, note, vel, @curr_ticks)
78
+ on = NoteOn.new(chan, note, vel, @curr_ticks)
79
79
  @track.events << on
80
80
  @pending << on
81
81
  track_uses_channel(chan)
@@ -96,7 +96,7 @@ class SeqReader < MIDIFile
96
96
  end
97
97
 
98
98
  def make_note_off(on, vel)
99
- off = NoteOffEvent.new(on.channel, on.note, vel, @curr_ticks)
99
+ off = NoteOff.new(on.channel, on.note, vel, @curr_ticks)
100
100
  @track.events << off
101
101
  on.off = off
102
102
  off.on = on
@@ -91,7 +91,7 @@ class SeqWriter
91
91
  # exactly the same, the rest is trivial. If it's note on/note off,
92
92
  # we can combine those further.
93
93
  if status == prev_status
94
- data[0,1] = nil # delete status byte from data
94
+ data[0,1] = [] # delete status byte from data
95
95
  return status + chan
96
96
  elsif status == NOTE_OFF && data[2] == 64
97
97
  # If we see a note off and the velocity is 64, we can store
@@ -153,7 +153,7 @@ class Sequence
153
153
  max_pos = 0
154
154
  @tracks.each { |t|
155
155
  t.each { |e|
156
- time_sigs << e if e.timesig?
156
+ time_sigs << e if e.kind_of?(MIDI::TimeSig)
157
157
  max_pos = e.time_from_start if e.time_from_start > max_pos
158
158
  }
159
159
  }
@@ -1,5 +1,47 @@
1
1
  require 'midilib/event'
2
2
 
3
+ # This is taken from
4
+ # http://github.com/adamjmurray/cosy/blob/master/lib/cosy/helper/midi_file_renderer_helper.rb
5
+ # with permission from Adam Murray, who originally suggested this fix.
6
+ # See http://wiki.github.com/adamjmurray/cosy/midilib-notes for details.
7
+ # First we need to add some API infrastructure:
8
+ class Array
9
+ # This code borrowed from 'Moser' http://codesnippets.joyent.com/posts/show/1699
10
+
11
+ # A stable sorting algorithm that maintains the relative order of equal elements
12
+ def mergesort(&cmp)
13
+ if cmp == nil
14
+ cmp = lambda { |a, b| a <=> b }
15
+ end
16
+ if size <= 1
17
+ self.dup
18
+ else
19
+ halves = split.map{ |half|
20
+ half.mergesort(&cmp)
21
+ }
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
+
3
45
  module MIDI
4
46
 
5
47
  # A Track is a list of events.
@@ -32,7 +74,7 @@ class Track
32
74
  # Return track name. If there is no name, return UNNAMED.
33
75
  def name
34
76
  event = @events.detect { | e |
35
- e.meta? && e.meta_type == META_SEQ_NAME
77
+ e.kind_of?(MetaEvent) && e.meta_type == META_SEQ_NAME
36
78
  }
37
79
  return event ? event.data_as_str : UNNAMED
38
80
  end
@@ -40,7 +82,7 @@ class Track
40
82
  # Set track name. Replaces or creates a name meta-event.
41
83
  def name=(name)
42
84
  event = @events.detect { | e |
43
- e.meta? && e.meta_type == META_SEQ_NAME
85
+ e.kind_of?(MetaEvent) && e.meta_type == META_SEQ_NAME
44
86
  }
45
87
  if event
46
88
  event.data = name
@@ -74,7 +116,7 @@ class Track
74
116
  def merge_event_lists(list1, list2)
75
117
  recalc_times(0, list1)
76
118
  recalc_times(0, list2)
77
- list = (list1 + list2).sort_by { | e | e.time_from_start }
119
+ list = list1 + list2
78
120
  recalc_delta_from_times(0, list)
79
121
  return list
80
122
  end
@@ -113,7 +155,10 @@ class Track
113
155
  def recalc_delta_from_times(starting_at=0, list=@events)
114
156
  prev_time_from_start = 0
115
157
  # We need to sort the sublist. sublist.sort! does not do what we want.
116
- list[starting_at .. -1] = list[starting_at .. -1].sort { | e1, e2 |
158
+ # We call mergesort instead of Array.sort because sort is not stable
159
+ # (it can mix up the order of events that have the same start time).
160
+ # See http://wiki.github.com/adamjmurray/cosy/midilib-notes for details.
161
+ list[starting_at .. -1] = list[starting_at .. -1].mergesort { | e1, e2 |
117
162
  e1.time_from_start <=> e2.time_from_start
118
163
  }
119
164
  list[starting_at .. -1].each { | e |
@@ -130,10 +175,11 @@ class Track
130
175
  # Sort events by their time_from_start. After sorting,
131
176
  # recalc_delta_from_times is called to make sure that the delta times
132
177
  # reflect the possibly new event order.
133
- def sort
134
- @events = @events.sort_by { | e | e.time_from_start }
135
- recalc_delta_from_times()
136
- end
178
+ #
179
+ # Note: this method is redundant, since recalc_delta_from_times sorts
180
+ # the events first. This method may go away in a future release, or at
181
+ # least be aliased to recalc_delta_from_times.
182
+ alias_method :sort, :recalc_delta_from_times
137
183
  end
138
184
 
139
185
  end
@@ -9,7 +9,7 @@ require 'midilib'
9
9
  class EventTester < Test::Unit::TestCase
10
10
 
11
11
  def test_note_on
12
- e = MIDI::NoteOnEvent.new
12
+ e = MIDI::NoteOn.new
13
13
  assert_equal(MIDI::NOTE_ON, e.status)
14
14
  assert_equal(0, e.channel)
15
15
  assert_equal(64, e.note)
@@ -19,7 +19,7 @@ class EventTester < Test::Unit::TestCase
19
19
  end
20
20
 
21
21
  def test_quantize
22
- e = MIDI::NoteOnEvent.new
22
+ e = MIDI::NoteOn.new
23
23
  e.quantize_to(4)
24
24
  assert_equal(0, e.time_from_start)
25
25
 
@@ -45,7 +45,7 @@ class EventTester < Test::Unit::TestCase
45
45
  end
46
46
 
47
47
  def test_to_s
48
- e = MIDI::NoteOnEvent.new
48
+ e = MIDI::NoteOn.new
49
49
  assert_equal("0: ch 00 on 40 40", e.to_s)
50
50
  e.print_decimal_numbers = true
51
51
  assert_equal("0: ch 0 on 64 64", e.to_s)
@@ -55,64 +55,6 @@ class EventTester < Test::Unit::TestCase
55
55
  assert_equal("0: ch 00 on E4 40", e.to_s)
56
56
  end
57
57
 
58
- def test_bools_note_on
59
- e = MIDI::NoteOnEvent.new
60
- assert(e.channel?)
61
- assert(e.note?)
62
- assert(e.note_on?)
63
- assert(!e.note_off?)
64
- assert(!e.meta?)
65
- assert(!e.system?)
66
- assert(!e.realtime?)
67
- end
68
-
69
- def test_bools_note_off
70
- e = MIDI::NoteOffEvent.new
71
- assert(e.channel?)
72
- assert(e.note?)
73
- assert(!e.note_on?)
74
- assert(e.note_off?)
75
- assert(!e.meta?)
76
- assert(!e.system?)
77
- assert(!e.realtime?)
78
- end
79
-
80
- def test_bools_realtime
81
- e = MIDI::Clock.new
82
- assert(!e.channel?)
83
- assert(!e.note?)
84
- assert(!e.meta?)
85
- assert(!e.system?)
86
- assert(e.realtime?)
87
- end
88
-
89
- def test_bools_controller
90
- e = MIDI::Controller.new
91
- assert(e.channel?)
92
- assert(!e.note?)
93
- assert(!e.meta?)
94
- assert(!e.system?)
95
- assert(!e.realtime?)
96
- end
97
-
98
- def test_bools_meta
99
- e = MIDI::MetaEvent.new(MIDI::META_SEQ_NUM)
100
- assert(!e.channel?)
101
- assert(!e.note?)
102
- assert(e.meta?)
103
- assert(!e.system?)
104
- assert(!e.realtime?)
105
- end
106
-
107
- def test_bools_system
108
- e = MIDI::TuneRequest.new
109
- assert(!e.channel?)
110
- assert(!e.note?)
111
- assert(!e.meta?)
112
- assert(e.system?)
113
- assert(!e.realtime?)
114
- end
115
-
116
58
  def test_pitch_bend
117
59
  e = MIDI::PitchBend.new(0, 128)
118
60
  b = e.data_as_bytes
@@ -126,7 +68,7 @@ class EventTester < Test::Unit::TestCase
126
68
  end
127
69
 
128
70
  def test_quantize
129
- e = MIDI::NoteOnEvent.new(0, 64, 64, 0)
71
+ e = MIDI::NoteOn.new(0, 64, 64, 0)
130
72
  e.quantize_to(80)
131
73
  assert_equal(0, e.time_from_start)
132
74