Class: MIDI::IO::SeqReader

Inherits:
MIDIFile show all
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

MIDIFile::MThd_BYTE_ARRAY, MIDIFile::MTrk_BYTE_ARRAY, MIDIFile::NUM_DATA_BYTES

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

Methods inherited from MIDIFile

#arbitrary, #bad_byte, #chan_message, #eot, #error, #get_bytes, #getc, #handle_arbitrary, #handle_sysex, #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_trackObject


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

#key_signature(sharpflat, is_minor) ⇒ Object


185
186
187
# File 'lib/midilib/io/seqreader.rb', line 185

def key_signature(sharpflat, is_minor)
  @track.events << KeySig.new(sharpflat, is_minor, @curr_ticks)
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 = NoteOff.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 meta_misc(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 do |on, i|
    if on.note == note && on.channel == chan
      make_note_off(on, vel)
      @pending.delete_at(i)
      return
    end
	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 = NoteOn.new(chan, note, vel, @curr_ticks)
	@track.events << on
	@pending << on
	track_uses_channel(chan)
end

#pitch_bend(chan, lsb, msb) ⇒ Object


115
116
117
118
# File 'lib/midilib/io/seqreader.rb', line 115

def pitch_bend(chan, lsb, msb)
	@track.events << PitchBend.new(chan, (msb << 7) + 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_trackObject


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

++


181
182
183
# File 'lib/midilib/io/seqreader.rb', line 181

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
158
159
# File 'lib/midilib/io/seqreader.rb', line 146

def text(type, msg)
	case type
  when META_TEXT, META_LYRIC, META_CUE
    @track.events << MetaEvent.new(type, msg, @curr_ticks)
	when META_SEQ_NAME, META_COPYRIGHT
    @track.events << MetaEvent.new(type, 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

– Don't bother adding the META_TRACK_END event to the track. This way, we don't have to worry about always making sure the last event is always a track end event. We just have to make sure to write one when the track is output back to a file.

	def eot()
	    @track.events << MetaEvent.new(META_TRACK_END, nil, @curr_ticks)
	end

++


171
172
173
174
# File 'lib/midilib/io/seqreader.rb', line 171

def time_signature(numer, denom, clocks, qnotes)
	@seq.time_signature(numer, denom, clocks, qnotes)
  @track.events << TimeSig.new(numer, denom, clocks, qnotes, @curr_ticks)
end

#track_uses_channel(chan) ⇒ Object

Return true if the current track uses the specified channel.


195
196
197
# File 'lib/midilib/io/seqreader.rb', line 195

def track_uses_channel(chan)
	@chan_mask = @chan_mask | (1 << chan)
end