midilib 1.2.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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