midilib 2.0.2 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/ChangeLog +2 -1
- data/Credits +44 -2
- data/README.rdoc +42 -33
- data/Rakefile +36 -53
- data/TODO.rdoc +13 -2
- data/examples/from_scratch.rb +4 -6
- data/examples/measures_mbt.rb +11 -11
- data/examples/print_program_changes.rb +11 -11
- data/examples/reader2text.rb +191 -190
- data/examples/seq2text.rb +18 -18
- data/examples/split.rb +21 -20
- data/examples/strings.rb +15 -15
- data/examples/transpose.rb +41 -42
- data/install.rb +53 -34
- data/lib/midilib/consts.rb +406 -408
- data/lib/midilib/event.rb +335 -306
- data/lib/midilib/info.rb +5 -7
- data/lib/midilib/io/midifile.rb +424 -452
- data/lib/midilib/io/seqreader.rb +187 -192
- data/lib/midilib/io/seqwriter.rb +151 -147
- data/lib/midilib/measure.rb +78 -80
- data/lib/midilib/mergesort.rb +39 -0
- data/lib/midilib/sequence.rb +99 -86
- data/lib/midilib/track.rb +71 -118
- data/lib/midilib/utils.rb +17 -20
- data/lib/midilib.rb +5 -5
- data/test/event_equality.rb +50 -52
- data/test/test_event.rb +120 -124
- data/test/test_io.rb +107 -40
- data/test/test_mergesort.rb +37 -0
- data/test/test_midifile.rb +6 -19
- data/test/test_sequence.rb +64 -52
- data/test/test_track.rb +126 -155
- data/test/test_varlen.rb +23 -27
- metadata +20 -22
data/lib/midilib/io/seqreader.rb
CHANGED
@@ -1,202 +1,197 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require_relative 'midifile'
|
2
|
+
require_relative '../track'
|
3
|
+
require_relative '../event'
|
4
4
|
|
5
5
|
module MIDI
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
#
|
139
|
-
#
|
140
|
-
|
141
|
-
|
142
|
-
#
|
143
|
-
#
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
#
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
#
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
def time_signature(numer, denom, clocks, qnotes)
|
172
|
-
@seq.time_signature(numer, denom, clocks, qnotes)
|
6
|
+
module IO
|
7
|
+
# Reads MIDI files. As a subclass of MIDIFile, this class implements the
|
8
|
+
# callback methods for each MIDI event and use them to build Track and
|
9
|
+
# Event objects and give the tracks to a Sequence.
|
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
|
+
|
21
|
+
class SeqReader < MIDIFile
|
22
|
+
# The optional &block is called once at the start of the file and
|
23
|
+
# again at the end of each track. There are three arguments to the
|
24
|
+
# block: the track, the track number (1 through _n_), and the total
|
25
|
+
# number of tracks.
|
26
|
+
def initialize(seq, &block) # :yields: track, num_tracks, index
|
27
|
+
super()
|
28
|
+
@seq = seq
|
29
|
+
@track = nil
|
30
|
+
@chan_mask = 0
|
31
|
+
@update_block = block
|
32
|
+
end
|
33
|
+
|
34
|
+
def header(format, ntrks, division)
|
35
|
+
@seq.format = format
|
36
|
+
@seq.ppqn = division
|
37
|
+
|
38
|
+
@ntrks = ntrks
|
39
|
+
@update_block.call(nil, @ntrks, 0) if @update_block
|
40
|
+
end
|
41
|
+
|
42
|
+
def start_track
|
43
|
+
@track = Track.new(@seq)
|
44
|
+
@seq.tracks << @track
|
45
|
+
|
46
|
+
@pending = []
|
47
|
+
end
|
48
|
+
|
49
|
+
def end_track
|
50
|
+
# Turn off any pending note on messages
|
51
|
+
@pending.each { |on| make_note_off(on, 64) }
|
52
|
+
@pending = nil
|
53
|
+
|
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.
|
60
|
+
@track.recalc_times
|
61
|
+
|
62
|
+
# Store bitmask of all channels used into track
|
63
|
+
@track.channels_used = @chan_mask
|
64
|
+
|
65
|
+
# call update block
|
66
|
+
@update_block.call(@track, @ntrks, @seq.tracks.length) if @update_block
|
67
|
+
end
|
68
|
+
|
69
|
+
def note_on(chan, note, vel)
|
70
|
+
if vel == 0
|
71
|
+
note_off(chan, note, 64)
|
72
|
+
return
|
73
|
+
end
|
74
|
+
|
75
|
+
on = NoteOn.new(chan, note, vel, @curr_ticks)
|
76
|
+
@track.events << on
|
77
|
+
@pending << on
|
78
|
+
track_uses_channel(chan)
|
79
|
+
end
|
80
|
+
|
81
|
+
def note_off(chan, note, vel)
|
82
|
+
# Find note on, create note off, connect the two, and remove
|
83
|
+
# note on from pending list.
|
84
|
+
@pending.each_with_index do |on, i|
|
85
|
+
next unless on.note == note && on.channel == chan
|
86
|
+
|
87
|
+
make_note_off(on, vel)
|
88
|
+
@pending.delete_at(i)
|
89
|
+
return
|
90
|
+
end
|
91
|
+
if $DEBUG
|
92
|
+
warn "note off with no earlier note on (ch #{chan}, note" +
|
93
|
+
" #{note}, vel #{vel})"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def make_note_off(on, vel)
|
98
|
+
off = NoteOff.new(on.channel, on.note, vel, @curr_ticks)
|
99
|
+
@track.events << off
|
100
|
+
on.off = off
|
101
|
+
off.on = on
|
102
|
+
end
|
103
|
+
|
104
|
+
def pressure(chan, note, press)
|
105
|
+
@track.events << PolyPressure.new(chan, note, press, @curr_ticks)
|
106
|
+
track_uses_channel(chan)
|
107
|
+
end
|
108
|
+
|
109
|
+
def controller(chan, control, value)
|
110
|
+
@track.events << Controller.new(chan, control, value, @curr_ticks)
|
111
|
+
track_uses_channel(chan)
|
112
|
+
end
|
113
|
+
|
114
|
+
def pitch_bend(chan, lsb, msb)
|
115
|
+
@track.events << PitchBend.new(chan, (msb << 7) + lsb, @curr_ticks)
|
116
|
+
track_uses_channel(chan)
|
117
|
+
end
|
118
|
+
|
119
|
+
def program(chan, program)
|
120
|
+
@track.events << ProgramChange.new(chan, program, @curr_ticks)
|
121
|
+
track_uses_channel(chan)
|
122
|
+
end
|
123
|
+
|
124
|
+
def chan_pressure(chan, press)
|
125
|
+
@track.events << ChannelPressure.new(chan, press, @curr_ticks)
|
126
|
+
track_uses_channel(chan)
|
127
|
+
end
|
128
|
+
|
129
|
+
def sysex(msg)
|
130
|
+
@track.events << SystemExclusive.new(msg, @curr_ticks)
|
131
|
+
end
|
132
|
+
|
133
|
+
def meta_misc(type, msg)
|
134
|
+
@track.events << MetaEvent.new(type, msg, @curr_ticks)
|
135
|
+
end
|
136
|
+
|
137
|
+
# --
|
138
|
+
# def sequencer_specific(type, msg)
|
139
|
+
# end
|
140
|
+
|
141
|
+
# def sequence_number(num)
|
142
|
+
# end
|
143
|
+
# ++
|
144
|
+
|
145
|
+
def text(type, msg)
|
146
|
+
case type
|
147
|
+
when META_TEXT, META_LYRIC, META_CUE, META_SEQ_NAME, META_COPYRIGHT
|
148
|
+
@track.events << MetaEvent.new(type, msg, @curr_ticks)
|
149
|
+
when META_INSTRUMENT
|
150
|
+
@track.instrument = msg
|
151
|
+
when META_MARKER
|
152
|
+
@track.events << Marker.new(msg, @curr_ticks)
|
153
|
+
else
|
154
|
+
warn "text = #{msg}, type = #{type}" if $DEBUG
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# --
|
159
|
+
# Don't bother adding the META_TRACK_END event to the track. This way,
|
160
|
+
# we don't have to worry about always making sure the last event is
|
161
|
+
# always a track end event. We just have to make sure to write one when
|
162
|
+
# the track is output back to a file.
|
163
|
+
# def eot()
|
164
|
+
# @track.events << MetaEvent.new(META_TRACK_END, nil, @curr_ticks)
|
165
|
+
# end
|
166
|
+
# ++
|
167
|
+
|
168
|
+
def time_signature(numer, denom, clocks, qnotes)
|
169
|
+
@seq.time_signature(numer, denom, clocks, qnotes)
|
173
170
|
@track.events << TimeSig.new(numer, denom, clocks, qnotes, @curr_ticks)
|
174
|
-
|
171
|
+
end
|
175
172
|
|
176
|
-
# --
|
177
|
-
# def smpte(hour, min, sec, frame, fract)
|
178
|
-
# end
|
179
|
-
# ++
|
173
|
+
# --
|
174
|
+
# def smpte(hour, min, sec, frame, fract)
|
175
|
+
# end
|
176
|
+
# ++
|
180
177
|
|
181
|
-
|
182
|
-
|
183
|
-
|
178
|
+
def tempo(microsecs)
|
179
|
+
@track.events << Tempo.new(microsecs, @curr_ticks)
|
180
|
+
end
|
184
181
|
|
185
|
-
|
186
|
-
|
187
|
-
|
182
|
+
def key_signature(sharpflat, is_minor)
|
183
|
+
@track.events << KeySig.new(sharpflat, is_minor, @curr_ticks)
|
184
|
+
end
|
188
185
|
|
189
|
-
# --
|
190
|
-
# def arbitrary(msg)
|
191
|
-
# end
|
192
|
-
# ++
|
186
|
+
# --
|
187
|
+
# def arbitrary(msg)
|
188
|
+
# end
|
189
|
+
# ++
|
193
190
|
|
194
|
-
|
195
|
-
|
196
|
-
|
191
|
+
# Return true if the current track uses the specified channel.
|
192
|
+
def track_uses_channel(chan)
|
193
|
+
@chan_mask |= (1 << chan)
|
194
|
+
end
|
197
195
|
end
|
198
|
-
|
199
|
-
end
|
200
|
-
|
201
|
-
end
|
196
|
+
end
|
202
197
|
end
|