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.
@@ -1,202 +1,197 @@
1
- require 'midilib/io/midifile'
2
- require 'midilib/track'
3
- require 'midilib/event'
1
+ require_relative 'midifile'
2
+ require_relative '../track'
3
+ require_relative '../event'
4
4
 
5
5
  module MIDI
6
-
7
- module IO
8
-
9
- # Reads MIDI files. As a subclass of MIDIFile, this class implements
10
- # the callback methods for each MIDI event and use them to build Track
11
- # and Event objects and give the tracks to a Sequence.
12
- #
13
- # We append new events to the end of a track's event list, bypassing a call
14
- # to Track.#add. This means that we must call Track.recalc_times at the end
15
- # of the track so it can update each event with its time from the track's
16
- # start (see end_track below).
17
- #
18
- # META_TRACK_END events are not added to tracks. This way, we don't have to
19
- # worry about making sure the last event is always a track end event. We
20
- # rely on the SeqWriter to append a META_TRACK_END event to each track when
21
- # it is output.
22
-
23
- class SeqReader < MIDIFile
24
-
25
- # The optional proc block is called once at the start of the file
26
- # and again at the end of each track. There are three arguments
27
- # to the block: the track, the track number (1 through _n_), and
28
- # the total number of tracks.
29
- def initialize(seq, proc = nil) # :yields: track, num_tracks, index
30
- super()
31
- @seq = seq
32
- @track = nil
33
- @chan_mask = 0
34
- @update_block = block_given?() ? Proc.new() : proc
35
- end
36
-
37
- def header(format, ntrks, division)
38
- @seq.format = format
39
- @seq.ppqn = division
40
-
41
- @ntrks = ntrks
42
- @update_block.call(nil, @ntrks, 0) if @update_block
43
- end
44
-
45
- def start_track()
46
- @track = Track.new(@seq)
47
- @seq.tracks << @track
48
-
49
- @pending = []
50
- end
51
-
52
- def end_track()
53
- # Turn off any pending note on messages
54
- @pending.each { | on | make_note_off(on, 64) }
55
- @pending = nil
56
-
57
- # Don't bother adding the META_TRACK_END event to the track.
58
- # This way, we don't have to worry about making sure the
59
- # last event is always a track end event.
60
-
61
- # Let the track calculate event times from start of track. This is
62
- # in lieu of calling Track.add for each event.
63
- @track.recalc_times()
64
-
65
- # Store bitmask of all channels used into track
66
- @track.channels_used = @chan_mask
67
-
68
- # call update block
69
- @update_block.call(@track, @ntrks, @seq.tracks.length) if @update_block
70
- end
71
-
72
- def note_on(chan, note, vel)
73
- if vel == 0
74
- note_off(chan, note, 64)
75
- return
76
- end
77
-
78
- on = NoteOn.new(chan, note, vel, @curr_ticks)
79
- @track.events << on
80
- @pending << on
81
- track_uses_channel(chan)
82
- end
83
-
84
- def note_off(chan, note, vel)
85
- # Find note on, create note off, connect the two, and remove
86
- # note on from pending list.
87
- @pending.each_with_index { | on, i |
88
- if on.note == note && on.channel == chan
89
- make_note_off(on, vel)
90
- @pending.delete_at(i)
91
- return
92
- end
93
- }
94
- $stderr.puts "note off with no earlier note on (ch #{chan}, note" +
95
- " #{note}, vel #{vel})" if $DEBUG
96
- end
97
-
98
- def make_note_off(on, vel)
99
- off = NoteOff.new(on.channel, on.note, vel, @curr_ticks)
100
- @track.events << off
101
- on.off = off
102
- off.on = on
103
- end
104
-
105
- def pressure(chan, note, press)
106
- @track.events << PolyPressure.new(chan, note, press, @curr_ticks)
107
- track_uses_channel(chan)
108
- end
109
-
110
- def controller(chan, control, value)
111
- @track.events << Controller.new(chan, control, value, @curr_ticks)
112
- track_uses_channel(chan)
113
- end
114
-
115
- def pitch_bend(chan, msb, lsb)
116
- @track.events << PitchBend.new(chan, (msb << 7) + lsb, @curr_ticks)
117
- track_uses_channel(chan)
118
- end
119
-
120
- def program(chan, program)
121
- @track.events << ProgramChange.new(chan, program, @curr_ticks)
122
- track_uses_channel(chan)
123
- end
124
-
125
- def chan_pressure(chan, press)
126
- @track.events << ChannelPressure.new(chan, press, @curr_ticks)
127
- track_uses_channel(chan)
128
- end
129
-
130
- def sysex(msg)
131
- @track.events << SystemExclusive.new(msg, @curr_ticks)
132
- end
133
-
134
- def meta_misc(type, msg)
135
- @track.events << MetaEvent.new(type, msg, @curr_ticks)
136
- end
137
-
138
- # --
139
- # def sequencer_specific(type, msg)
140
- # end
141
-
142
- # def sequence_number(num)
143
- # end
144
- # ++
145
-
146
- def text(type, msg)
147
- case type
148
- when META_TEXT, META_LYRIC, META_CUE
149
- @track.events << MetaEvent.new(type, msg, @curr_ticks)
150
- when META_SEQ_NAME, META_COPYRIGHT
151
- @track.events << MetaEvent.new(type, msg, 0)
152
- when META_INSTRUMENT
153
- @track.instrument = msg
154
- when META_MARKER
155
- @track.events << Marker.new(msg, @curr_ticks)
156
- else
157
- $stderr.puts "text = #{msg}, type = #{type}" if $DEBUG
158
- end
159
- end
160
-
161
- # --
162
- # Don't bother adding the META_TRACK_END event to the track. This way,
163
- # we don't have to worry about always making sure the last event is
164
- # always a track end event. We just have to make sure to write one when
165
- # the track is output back to a file.
166
- # def eot()
167
- # @track.events << MetaEvent.new(META_TRACK_END, nil, @curr_ticks)
168
- # end
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
- end
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
- def tempo(microsecs)
182
- @track.events << Tempo.new(microsecs, @curr_ticks)
183
- end
178
+ def tempo(microsecs)
179
+ @track.events << Tempo.new(microsecs, @curr_ticks)
180
+ end
184
181
 
185
- def key_signature(sharpflat, is_minor)
186
- @track.events << KeySig.new(sharpflat, is_minor, @curr_ticks)
187
- end
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
- # Return true if the current track uses the specified channel.
195
- def track_uses_channel(chan)
196
- @chan_mask = @chan_mask | (1 << chan)
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