Class: MIDI::IO::SeqReader
- Defined in:
- lib/midilib/io/seqreader.rb
Overview
Reads MIDI files. As a subclass of MIDIFile, this class implements the callback methods for each MIDI event and use them to build Track and Event objects and give the tracks to a Sequence.
We append new events to the end of a track’s event list, bypassing a call to Track.#add. This means that we must call Track.recalc_times at the end of the track so it can update each event with its time from the track’s start (see end_track below).
META_TRACK_END events are not added to tracks. This way, we don’t have to worry about making sure the last event is always a track end event. We rely on the SeqWriter to append a META_TRACK_END event to each track when it is output.
Constant Summary
Constants inherited from MIDIFile
Instance Attribute Summary
Attributes inherited from MIDIFile
#bytes_to_be_read, #curr_ticks, #no_merge, #raw_data, #raw_time_stamp_data, #raw_var_num_data, #skip_init, #ticks_so_far
Instance Method Summary collapse
- #chan_pressure(chan, press) ⇒ Object
- #controller(chan, control, value) ⇒ Object
- #end_track ⇒ Object
- #header(format, ntrks, division) ⇒ Object
-
#initialize(seq, proc = nil) ⇒ SeqReader
constructor
The optional proc block is called once at the start of the file and again at the end of each track.
- #make_note_off(on, vel) ⇒ Object
- #meta_misc(type, msg) ⇒ Object
- #note_off(chan, note, vel) ⇒ Object
- #note_on(chan, note, vel) ⇒ Object
- #pitch_bend(chan, msb, lsb) ⇒ Object
- #pressure(chan, note, press) ⇒ Object
- #program(chan, program) ⇒ Object
- #start_track ⇒ Object
- #sysex(msg) ⇒ Object
-
#tempo(microsecs) ⇒ Object
– def smpte(hour, min, sec, frame, fract) end ++.
-
#text(type, msg) ⇒ Object
def sequence_number(num) end ++.
-
#time_signature(numer, denom, clocks, qnotes) ⇒ Object
def eot() @track.events << MetaEvent.new(META_TRACK_END, nil, @curr_ticks) end ++.
-
#track_uses_channel(chan) ⇒ Object
Return true if the current track uses the specified channel.
Methods inherited from MIDIFile
#arbitrary, #bad_byte, #chan_message, #eot, #error, #getc, #handle_arbitrary, #handle_sysex, #key_signature, #meta_event, #msg, #msg_add, #msg_init, #msg_read, #read16, #read32, #read_from, #read_header, #read_mt_header_string, #read_track, #read_var_len, #sequence_number, #sequencer_specific, #smpte, #write16, #write32, #write_var_len
Constructor Details
#initialize(seq, proc = nil) ⇒ SeqReader
The optional proc block is called once at the start of the file and again at the end of each track. There are three arguments to the block: the track, the track number (1 through n), and the total number of tracks.
29 30 31 32 33 34 35 |
# File 'lib/midilib/io/seqreader.rb', line 29 def initialize(seq, proc = nil) # :yields: track, num_tracks, index super() @seq = seq @track = nil @chan_mask = 0 @update_block = block_given?() ? Proc.new() : proc end |
Instance Method Details
#chan_pressure(chan, press) ⇒ Object
125 126 127 128 |
# File 'lib/midilib/io/seqreader.rb', line 125 def chan_pressure(chan, press) @track.events << ChannelPressure.new(chan, press, @curr_ticks) track_uses_channel(chan) end |
#controller(chan, control, value) ⇒ Object
110 111 112 113 |
# File 'lib/midilib/io/seqreader.rb', line 110 def controller(chan, control, value) @track.events << Controller.new(chan, control, value, @curr_ticks) track_uses_channel(chan) end |
#end_track ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/midilib/io/seqreader.rb', line 52 def end_track() # Turn off any pending note on messages @pending.each { | on | make_note_off(on, 64) } @pending = nil # Don't bother adding the META_TRACK_END event to the track. # This way, we don't have to worry about making sure the # last event is always a track end event. # Let the track calculate event times from start of track. This is # in lieu of calling Track.add for each event. @track.recalc_times() # Store bitmask of all channels used into track @track.channels_used = @chan_mask # call update block @update_block.call(@track, @ntrks, @seq.tracks.length) if @update_block end |
#header(format, ntrks, division) ⇒ Object
37 38 39 40 41 42 43 |
# File 'lib/midilib/io/seqreader.rb', line 37 def header(format, ntrks, division) @seq.format = format @seq.ppqn = division @ntrks = ntrks @update_block.call(nil, @ntrks, 0) if @update_block end |
#make_note_off(on, vel) ⇒ Object
98 99 100 101 102 103 |
# File 'lib/midilib/io/seqreader.rb', line 98 def make_note_off(on, vel) off = NoteOffEvent.new(on.channel, on.note, vel, @curr_ticks) @track.events << off on.off = off off.on = on end |
#meta_misc(type, msg) ⇒ Object
134 135 136 |
# File 'lib/midilib/io/seqreader.rb', line 134 def (type, msg) @track.events << MetaEvent.new(type, msg, @curr_ticks) end |
#note_off(chan, note, vel) ⇒ Object
84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/midilib/io/seqreader.rb', line 84 def note_off(chan, note, vel) # Find note on, create note off, connect the two, and remove # note on from pending list. @pending.each_with_index { | on, i | if on.note == note && on.channel == chan make_note_off(on, vel) @pending.delete_at(i) return end } $stderr.puts "note off with no earlier note on (ch #{chan}, note" + " #{note}, vel #{vel})" if $DEBUG end |
#note_on(chan, note, vel) ⇒ Object
72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/midilib/io/seqreader.rb', line 72 def note_on(chan, note, vel) if vel == 0 note_off(chan, note, 64) return end on = NoteOnEvent.new(chan, note, vel, @curr_ticks) @track.events << on @pending << on track_uses_channel(chan) end |
#pitch_bend(chan, msb, lsb) ⇒ Object
115 116 117 118 |
# File 'lib/midilib/io/seqreader.rb', line 115 def pitch_bend(chan, msb, lsb) @track.events << PitchBend.new(chan, (msb << 8) + lsb, @curr_ticks) track_uses_channel(chan) end |
#pressure(chan, note, press) ⇒ Object
105 106 107 108 |
# File 'lib/midilib/io/seqreader.rb', line 105 def pressure(chan, note, press) @track.events << PolyPressure.new(chan, note, press, @curr_ticks) track_uses_channel(chan) end |
#program(chan, program) ⇒ Object
120 121 122 123 |
# File 'lib/midilib/io/seqreader.rb', line 120 def program(chan, program) @track.events << ProgramChange.new(chan, program, @curr_ticks) track_uses_channel(chan) end |
#start_track ⇒ Object
45 46 47 48 49 50 |
# File 'lib/midilib/io/seqreader.rb', line 45 def start_track() @track = Track.new(@seq) @seq.tracks << @track @pending = [] end |
#sysex(msg) ⇒ Object
130 131 132 |
# File 'lib/midilib/io/seqreader.rb', line 130 def sysex(msg) @track.events << SystemExclusive.new(msg, @curr_ticks) end |
#tempo(microsecs) ⇒ Object
–
def smpte(hour, min, sec, frame, fract)
end
++
178 179 180 |
# File 'lib/midilib/io/seqreader.rb', line 178 def tempo(microsecs) @track.events << Tempo.new(microsecs, @curr_ticks) end |
#text(type, msg) ⇒ Object
def sequence_number(num)
end
++
146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/midilib/io/seqreader.rb', line 146 def text(type, msg) case type when META_SEQ_NAME @track.events << MetaEvent.new(META_SEQ_NAME, msg, 0) when META_INSTRUMENT @track.instrument = msg when META_MARKER @track.events << Marker.new(msg, @curr_ticks) else $stderr.puts "text = #{msg}, type = #{type}" if $DEBUG end end |
#time_signature(numer, denom, clocks, qnotes) ⇒ Object
def eot()
@track.events << MetaEvent.new(META_TRACK_END, nil, @curr_ticks)
end
++
169 170 171 |
# File 'lib/midilib/io/seqreader.rb', line 169 def time_signature(numer, denom, clocks, qnotes) @seq.time_signature(numer, denom, clocks, qnotes) end |
#track_uses_channel(chan) ⇒ Object
Return true if the current track uses the specified channel.
191 192 193 |
# File 'lib/midilib/io/seqreader.rb', line 191 def track_uses_channel(chan) @chan_mask = @chan_mask | (1 << chan) end |