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.

Ensures that each track ends with an end of track meta event, and that Track#recalc_times is called at the end of the track so it can update each event with its time from the track’s start (see end_track below).

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, #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, &block) ⇒ SeqReader

The optional &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.



19
20
21
22
23
24
25
# File 'lib/midilib/io/seqreader.rb', line 19

def initialize(seq, &block) # :yields: track, num_tracks, index
  super()
  @seq = seq
  @track = nil
  @chan_mask = 0
  @update_block = block
end

Instance Method Details

#chan_pressure(chan, press) ⇒ Object



127
128
129
130
# File 'lib/midilib/io/seqreader.rb', line 127

def chan_pressure(chan, press)
  @track.events << ChannelPressure.new(chan, press, @curr_ticks)
  track_uses_channel(chan)
end

#controller(chan, control, value) ⇒ Object



112
113
114
115
# File 'lib/midilib/io/seqreader.rb', line 112

def controller(chan, control, value)
  @track.events << Controller.new(chan, control, value, @curr_ticks)
  track_uses_channel(chan)
end

#end_trackObject



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/midilib/io/seqreader.rb', line 42

def end_track
  # Turn off any pending note on messages
  @pending.each { |on| make_note_off(on, 64) }
  @pending = nil

  # Make sure track has an end of track event and that all of the
  # `time_from_start` values are correct.
  @track.ensure_track_end_meta_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

#eotObject



136
137
138
# File 'lib/midilib/io/seqreader.rb', line 136

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

#header(format, ntrks, division) ⇒ Object



27
28
29
30
31
32
33
# File 'lib/midilib/io/seqreader.rb', line 27

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



179
180
181
# File 'lib/midilib/io/seqreader.rb', line 179

def key_signature(sharpflat, is_minor)
  @track.events << KeySig.new(sharpflat, is_minor, @curr_ticks)
end

#make_note_off(on, vel) ⇒ Object



100
101
102
103
104
105
# File 'lib/midilib/io/seqreader.rb', line 100

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



140
141
142
# File 'lib/midilib/io/seqreader.rb', line 140

def meta_misc(type, msg)
  @track.events << MetaEvent.new(type, msg, @curr_ticks)
end

#note_off(chan, note, vel) ⇒ Object



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

def note_off(chan, note, vel)
  # Find note on, create note off, connect the two, and remove
  # note on from pending list.

  corresp_note_on = nil

  @pending.each_with_index do |on, i|
    next unless on.note == note && on.channel == chan

    @pending.delete_at(i)
    corresp_note_on = on
    break
  end

  if corresp_note_on
    make_note_off(corresp_note_on, vel)
  else
    # When a corresponding note on is missing,
    # keep note off as input with lefting on/off attr to nil.
    off = NoteOff.new(chan, note, vel, @curr_ticks)
    @track.events << off

    if $DEBUG
      warn "note off with no earlier note on (ch #{chan}, note" +
           " #{note}, vel #{vel})"
    end
  end
end

#note_on(chan, note, vel) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
# File 'lib/midilib/io/seqreader.rb', line 59

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



117
118
119
120
# File 'lib/midilib/io/seqreader.rb', line 117

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



107
108
109
110
# File 'lib/midilib/io/seqreader.rb', line 107

def pressure(chan, note, press)
  @track.events << PolyPressure.new(chan, note, press, @curr_ticks)
  track_uses_channel(chan)
end

#program(chan, program) ⇒ Object



122
123
124
125
# File 'lib/midilib/io/seqreader.rb', line 122

def program(chan, program)
  @track.events << ProgramChange.new(chan, program, @curr_ticks)
  track_uses_channel(chan)
end

#start_trackObject



35
36
37
38
39
40
# File 'lib/midilib/io/seqreader.rb', line 35

def start_track
  @track = Track.new(@seq)
  @seq.tracks << @track

  @pending = []
end

#sysex(msg) ⇒ Object



132
133
134
# File 'lib/midilib/io/seqreader.rb', line 132

def sysex(msg)
  @track.events << SystemExclusive.new(msg, @curr_ticks)
end

#tempo(microsecs) ⇒ Object

def smpte(hour, min, sec, frame, fract)
end

++



175
176
177
# File 'lib/midilib/io/seqreader.rb', line 175

def tempo(microsecs)
  @track.events << Tempo.new(microsecs, @curr_ticks)
end

#text(type, msg) ⇒ Object

def sequence_number(num)

end

++



152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/midilib/io/seqreader.rb', line 152

def text(type, msg)
  case type
  when META_TEXT, META_LYRIC, META_CUE, META_SEQ_NAME, META_COPYRIGHT
    @track.events << MetaEvent.new(type, msg, @curr_ticks)
  when META_INSTRUMENT
    @track.instrument = msg
  when META_MARKER
    @track.events << Marker.new(msg, @curr_ticks)
  else
    warn "text = #{msg}, type = #{type}" if $DEBUG
  end
end

#time_signature(numer, denom, clocks, qnotes) ⇒ Object



165
166
167
168
# File 'lib/midilib/io/seqreader.rb', line 165

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.



189
190
191
# File 'lib/midilib/io/seqreader.rb', line 189

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