midilib 2.0.2 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -20,201 +20,202 @@ require 'midilib/io/midifile'
20
20
  DEFAULT_MIDI_TEST_FILE = 'NoFences.mid'
21
21
 
22
22
  class TextTranslator < MIDI::IO::MIDIFile
23
-
24
- def initialize(seq, proc = nil)
25
- super()
26
- @seq = seq
27
- @track = nil
28
- @update_block = block_given?() ? Proc.new() : proc
29
- end
30
-
31
- # Generate a unique number for a channel/note combination. This is used
32
- # to remember pending note on events.
33
- def note_hash(chan, note)
34
- return (chan << 8) + note
35
- end
36
-
37
- # Print a delta time.
38
- def pdelta
39
- print "#{@curr_ticks}: "
40
- end
41
-
42
- # The remaining methods are overrides of methods in MIDI::IO::MIDIFile.
43
-
44
- def header(format, ntrks, division)
45
- puts "header: format = #{format}, ntrks = #{ntrks}," +
46
- " division = #{division}"
47
-
48
- @ntrks = ntrks
49
- @update_block.call(nil, @ntrks, 0) if @update_block
50
- end
51
-
52
- def start_track()
53
- pdelta()
54
- puts "track start"
55
-
56
- @pending = []
57
- @chan_mask = 0
58
- end
59
-
60
- def end_track()
61
- pdelta()
62
- puts "track end; chans used bitmask = #{@chan_mask}"
63
- # Write message for any pending note on messages
64
- @pending.each_with_index { | num, chan |
65
- puts "pending note off missing for chan #{num >> 8}," +
66
- " note #{num & 0xff}" if note_obj
67
- }
68
- @pending = nil
69
-
70
- # call update block
71
- @update_block.call(@track, @ntrks, @seq.tracks.length) if @update_block
72
- end
73
-
74
- def note_on(chan, note, vel)
75
- pdelta()
76
- if vel == 0
77
- print "(note on, vel 0) "
78
- note_off(chan, note, 64)
79
- return
80
- end
81
-
82
- puts "note on chan #{chan}, note #{note}, vel #{vel}"
83
- @pending << note_hash(chan, note)
84
- track_uses_channel(chan)
85
- end
86
-
87
- def note_off(chan, note, vel)
88
- pdelta()
89
- # Find note on, create note off, connect the two, and remove
90
- # note on from pending list.
91
- pnum = note_hash(chan, note)
92
- @pending.each_with_index { | num, i |
93
- if pnum == num
94
- puts "note off chan #{chan}, note #{note}, vel #{vel}"
95
- @pending.delete_at(i)
96
- return
97
- end
98
- }
99
- puts "note off with no earlier note on (ch #{chan}, note" +
100
- " #{note}, vel #{vel})"
101
- end
102
-
103
- def pressure(chan, note, press)
104
- pdelta()
105
- puts "pressure chan #{chan}, note #{note}, press #{press}"
106
- track_uses_channel(chan)
107
- end
108
-
109
- def controller(chan, control, value)
110
- pdelta()
111
- puts "controller chan #{chan}, control #{control}, value #{value}"
112
- track_uses_channel(chan)
113
- end
114
-
115
- def pitch_bend(chan, msb, lsb)
116
- pdelta()
117
- puts "pitch bend chan #{chan}, msb #{msb}, lsb #{lsb}"
118
- track_uses_channel(chan)
119
- end
120
-
121
- def program(chan, program)
122
- pdelta()
123
- puts "program chan #{chan}, program #{program}"
124
- track_uses_channel(chan)
125
- end
126
-
127
- def chan_pressure(chan, press)
128
- pdelta()
129
- puts "chan press chan #{chan}, press #{press}"
130
- track_uses_channel(chan)
131
- end
132
-
133
- def sysex(msg)
134
- pdelta()
135
- puts "sysex size #{msg.length}"
136
- end
137
-
138
- def meta_misc(type, msg)
139
- pdelta()
140
- puts "meta misc type #{type}, length #{msg.length}"
141
- end
142
-
143
- def sequencer_specific(type, msg)
144
- pdelta()
145
- puts "sequencer specific type #{type}, msg #{msg.length}"
146
- end
147
-
148
- def sequence_number(num)
149
- pdelta()
150
- puts "sequence number #{num}"
151
- end
152
-
153
- def text(type, msg)
154
- pdelta()
155
- case type
156
- when MIDI::META_SEQ_NAME
157
- puts "seq or track name #{msg}"
158
- when MIDI::META_INSTRUMENT
159
- puts "instrument name #{msg}"
160
- when MIDI::META_MARKER
161
- puts "marker #{msg}"
162
- else
163
- puts "text = #{msg}, type = #{type}"
164
- end
165
- end
166
-
167
- def eot()
168
- pdelta()
169
- puts "end of track event"
170
- end
171
-
172
- def time_signature(numer, denom, clocks, qnotes)
173
- pdelta()
174
- puts "time sig numer #{numer}, denom #{denom}, clocks #{clocks}," +
175
- " qnotes #{qnotes}"
176
- end
177
-
178
- def smpte(hour, min, sec, frame, fract)
179
- pdelta()
180
- puts "smpte #{hour}:#{min}.#{sec}, frame #{frame}, fract #{fract}"
181
- end
182
-
183
- def tempo(microsecs)
184
- pdelta()
185
- bpm = 1.0 / microsecs # quarter notes per microsecond
186
- bpm *= 1000000.0 # quarter notes per second
187
- bpm *= 60.0 # quarter notes per minute
188
- puts "tempo microsecs pqn = #{microsecs} (#{bpm} bpm)"
189
- end
190
-
191
- def key_signature(sharpflat, is_minor)
192
- pdelta()
193
- puts "key sig sharpflat #{sharpflat}, is_minor #{is_minor}"
194
- end
195
-
196
- def arbitrary(msg)
197
- pdelta()
198
- puts "arbitrary length = #{msg.length}"
199
- end
200
-
201
- def track_uses_channel(chan)
202
- @chan_mask = @chan_mask | (1 << chan)
203
- end
204
-
23
+ def initialize(seq, &block)
24
+ super()
25
+ @seq = seq
26
+ @track = nil
27
+ @update_block = block
28
+ end
29
+
30
+ # Generate a unique number for a channel/note combination. This is used
31
+ # to remember pending note on events.
32
+ def note_hash(chan, note)
33
+ (chan << 8) + note
34
+ end
35
+
36
+ # Print a delta time.
37
+ def pdelta
38
+ print "#{@curr_ticks}: "
39
+ end
40
+
41
+ # The remaining methods are overrides of methods in MIDI::IO::MIDIFile.
42
+
43
+ def header(format, ntrks, division)
44
+ puts "header: format = #{format}, ntrks = #{ntrks}," +
45
+ " division = #{division}"
46
+
47
+ @ntrks = ntrks
48
+ @update_block.call(nil, @ntrks, 0) if @update_block
49
+ end
50
+
51
+ def start_track
52
+ pdelta
53
+ puts 'track start'
54
+
55
+ @pending = []
56
+ @chan_mask = 0
57
+ end
58
+
59
+ def end_track
60
+ pdelta
61
+ puts "track end; chans used bitmask = #{@chan_mask}"
62
+ # Write message for any pending note on messages
63
+ @pending.each_with_index do |num, chan|
64
+ if note_obj
65
+ puts "pending note off missing for chan #{num >> 8}," +
66
+ " note #{num & 0xff}"
67
+ end
68
+ end
69
+ @pending = nil
70
+
71
+ # call update block
72
+ @update_block.call(@track, @ntrks, @seq.tracks.length) if @update_block
73
+ end
74
+
75
+ def note_on(chan, note, vel)
76
+ pdelta
77
+ if vel == 0
78
+ print '(note on, vel 0) '
79
+ note_off(chan, note, 64)
80
+ return
81
+ end
82
+
83
+ puts "note on chan #{chan}, note #{note}, vel #{vel}"
84
+ @pending << note_hash(chan, note)
85
+ track_uses_channel(chan)
86
+ end
87
+
88
+ def note_off(chan, note, vel)
89
+ pdelta
90
+ # Find note on, create note off, connect the two, and remove
91
+ # note on from pending list.
92
+ pnum = note_hash(chan, note)
93
+ @pending.each_with_index do |num, i|
94
+ next unless pnum == num
95
+
96
+ puts "note off chan #{chan}, note #{note}, vel #{vel}"
97
+ @pending.delete_at(i)
98
+ return
99
+ end
100
+ puts "note off with no earlier note on (ch #{chan}, note" +
101
+ " #{note}, vel #{vel})"
102
+ end
103
+
104
+ def pressure(chan, note, press)
105
+ pdelta
106
+ puts "pressure chan #{chan}, note #{note}, press #{press}"
107
+ track_uses_channel(chan)
108
+ end
109
+
110
+ def controller(chan, control, value)
111
+ pdelta
112
+ puts "controller chan #{chan}, control #{control}, value #{value}"
113
+ track_uses_channel(chan)
114
+ end
115
+
116
+ def pitch_bend(chan, msb, lsb)
117
+ pdelta
118
+ puts "pitch bend chan #{chan}, msb #{msb}, lsb #{lsb}"
119
+ track_uses_channel(chan)
120
+ end
121
+
122
+ def program(chan, program)
123
+ pdelta
124
+ puts "program chan #{chan}, program #{program}"
125
+ track_uses_channel(chan)
126
+ end
127
+
128
+ def chan_pressure(chan, press)
129
+ pdelta
130
+ puts "chan press chan #{chan}, press #{press}"
131
+ track_uses_channel(chan)
132
+ end
133
+
134
+ def sysex(msg)
135
+ pdelta
136
+ puts "sysex size #{msg.length}"
137
+ end
138
+
139
+ def meta_misc(type, msg)
140
+ pdelta
141
+ puts "meta misc type #{type}, length #{msg.length}"
142
+ end
143
+
144
+ def sequencer_specific(type, msg)
145
+ pdelta
146
+ puts "sequencer specific type #{type}, msg #{msg.length}"
147
+ end
148
+
149
+ def sequence_number(num)
150
+ pdelta
151
+ puts "sequence number #{num}"
152
+ end
153
+
154
+ def text(type, msg)
155
+ pdelta
156
+ msg = MIDI::MetaEvent.bytes_as_str(msg)
157
+ case type
158
+ when MIDI::META_SEQ_NAME
159
+ puts "seq or track name #{msg}"
160
+ when MIDI::META_INSTRUMENT
161
+ puts "instrument name #{msg}"
162
+ when MIDI::META_MARKER
163
+ puts "marker #{msg}"
164
+ else
165
+ puts "text = #{msg}, type = #{type}"
166
+ end
167
+ end
168
+
169
+ def eot
170
+ pdelta
171
+ puts 'end of track event'
172
+ end
173
+
174
+ def time_signature(numer, denom, clocks, qnotes)
175
+ pdelta
176
+ puts "time sig numer #{numer}, denom #{denom}, clocks #{clocks}," +
177
+ " qnotes #{qnotes}"
178
+ end
179
+
180
+ def smpte(hour, min, sec, frame, fract)
181
+ pdelta
182
+ puts "smpte #{hour}:#{min}.#{sec}, frame #{frame}, fract #{fract}"
183
+ end
184
+
185
+ def tempo(microsecs)
186
+ pdelta
187
+ bpm = 1.0 / microsecs # quarter notes per microsecond
188
+ bpm *= 1_000_000.0 # quarter notes per second
189
+ bpm *= 60.0 # quarter notes per minute
190
+ puts "tempo microsecs pqn = #{microsecs} (#{bpm} bpm)"
191
+ end
192
+
193
+ def key_signature(sharpflat, is_minor)
194
+ pdelta
195
+ puts "key sig sharpflat #{sharpflat}, is_minor #{is_minor}"
196
+ end
197
+
198
+ def arbitrary(msg)
199
+ pdelta
200
+ puts "arbitrary length = #{msg.length}"
201
+ end
202
+
203
+ def track_uses_channel(chan)
204
+ @chan_mask |= (1 << chan)
205
+ end
205
206
  end
206
207
 
207
208
  # ================================================================
208
209
 
209
- seq = MIDI::Sequence.new()
210
+ seq = MIDI::Sequence.new
210
211
 
211
212
  # Specify what class to use when reading the MIDI file.
212
213
  seq.reader_class = TextTranslator
213
214
 
214
- File.open(ARGV[0] || DEFAULT_MIDI_TEST_FILE, 'rb') { | file |
215
- # The block we pass in to Sequence.read is called at the end of every
216
- # track read. It is optional, but is useful for progress reports.
217
- seq.read(file) { | track, num_tracks, i |
218
- puts "read track #{track ? track.name : ''} (#{i} of #{num_tracks})"
219
- }
220
- }
215
+ File.open(ARGV[0] || DEFAULT_MIDI_TEST_FILE, 'rb') do |file|
216
+ # The block we pass in to Sequence.read is called at the end of every
217
+ # track read. It is optional, but is useful for progress reports.
218
+ seq.read(file) do |track, num_tracks, i|
219
+ puts "read track #{track ? track.name : ''} (#{i} of #{num_tracks})"
220
+ end
221
+ end
data/examples/seq2text.rb CHANGED
@@ -19,23 +19,23 @@ require 'midilib/sequence'
19
19
  DEFAULT_MIDI_TEST_FILE = 'NoFences.mid'
20
20
 
21
21
  # Read from MIDI file
22
- seq = MIDI::Sequence.new()
22
+ seq = MIDI::Sequence.new
23
23
 
24
- File.open(ARGV[0] || DEFAULT_MIDI_TEST_FILE, 'rb') { | file |
25
- # The block we pass in to Sequence.read is called at the end of every
26
- # track read. It is optional, but is useful for progress reports.
27
- seq.read(file) { | track, num_tracks, i |
28
- puts "read track #{track ? track.name : ''} (#{i} of #{num_tracks})"
29
- }
30
- }
24
+ File.open(ARGV[0] || DEFAULT_MIDI_TEST_FILE, 'rb') do |file|
25
+ # The block we pass in to Sequence.read is called at the end of every
26
+ # track read. It is optional, but is useful for progress reports.
27
+ seq.read(file) do |track, num_tracks, i|
28
+ puts "read track #{track ? track.name : ''} (#{i} of #{num_tracks})"
29
+ end
30
+ end
31
31
 
32
- seq.each { | track |
33
- puts "*** track name \"#{track.name}\""
34
- puts "instrument name \"#{track.instrument}\""
35
- puts "#{track.events.length} events"
36
- track.each { | e |
37
- e.print_decimal_numbers = true # default = false (print hex)
38
- e.print_note_names = true # default = false (print note numbers)
39
- puts e
40
- }
41
- }
32
+ seq.each do |track|
33
+ puts "*** track name \"#{track.name}\""
34
+ puts "instrument name \"#{track.instrument}\""
35
+ puts "#{track.events.length} events"
36
+ track.each do |e|
37
+ e.print_decimal_numbers = true # default = false (print hex)
38
+ e.print_note_names = true # default = false (print note numbers)
39
+ puts e
40
+ end
41
+ end
data/examples/split.rb CHANGED
@@ -22,31 +22,32 @@ DEFAULT_MIDI_TEST_FILE = 'NoFences.mid'
22
22
  filename = ARGV[0]
23
23
  include_tempo_track = true
24
24
  if filename == '-x'
25
- include_tempo_track = false
26
- filename = ARGV[1]
25
+ include_tempo_track = false
26
+ filename = ARGV[1]
27
27
  end
28
28
 
29
29
  # Read from MIDI file
30
- seq = MIDI::Sequence.new()
30
+ seq = MIDI::Sequence.new
31
31
 
32
- File.open(filename || DEFAULT_MIDI_TEST_FILE, 'rb') { | file |
33
- # The block we pass in to Sequence.read is called at the end of every
34
- # track read. It is optional, but is useful for progress reports.
35
- seq.read(file) { | track, num_tracks, i |
36
- puts "read track #{track ? track.name : ''} (#{i} of #{num_tracks})"
37
- }
38
- }
32
+ File.open(filename || DEFAULT_MIDI_TEST_FILE, 'rb') do |file|
33
+ # The block we pass in to Sequence.read is called at the end of every
34
+ # track read. It is optional, but is useful for progress reports.
35
+ seq.read(file) do |track, num_tracks, i|
36
+ puts "read track #{track ? track.name : ''} (#{i} of #{num_tracks})"
37
+ end
38
+ end
39
39
 
40
40
  t0 = seq.tracks[0]
41
41
  unless include_tempo_track
42
- s = MIDI::Sequence.new
43
- s.tracks << t0
44
- File.open("tempo_track.mid", 'wb') { | file | s.write(file) }
42
+ s = MIDI::Sequence.new
43
+ s.tracks << t0
44
+ File.open('tempo_track.mid', 'wb') { |file| s.write(file) }
45
+ end
46
+ seq.each_with_index do |track, i|
47
+ next unless i > 0
48
+
49
+ s = MIDI::Sequence.new
50
+ s.tracks << t0 if include_tempo_track
51
+ s.tracks << track
52
+ File.open("#{track.name}.mid", 'wb') { |file| s.write(file) }
45
53
  end
46
- seq.each_with_index { | track, i |
47
- next unless i > 0
48
- s = MIDI::Sequence.new
49
- s.tracks << t0 if include_tempo_track
50
- s.tracks << track
51
- File.open("#{track.name}.mid", 'wb') { | file | s.write(file) }
52
- }
data/examples/strings.rb CHANGED
@@ -15,20 +15,20 @@ require 'midilib/consts'
15
15
 
16
16
  DEFAULT_MIDI_TEST_FILE = 'NoFences.mid'
17
17
 
18
- seq = MIDI::Sequence.new()
19
- File.open(ARGV[0] || DEFAULT_MIDI_TEST_FILE, 'rb') { | file |
20
- # The block we pass in to Sequence.read is called at the end of every
21
- # track read. It is optional, but is useful for progress reports.
22
- seq.read(file) { | track, num_tracks, i |
23
- puts "read track #{track ? track.name : ''} (#{i} of #{num_tracks})"
24
- }
25
- }
18
+ seq = MIDI::Sequence.new
19
+ File.open(ARGV[0] || DEFAULT_MIDI_TEST_FILE, 'rb') do |file|
20
+ # The block we pass in to Sequence.read is called at the end of every
21
+ # track read. It is optional, but is useful for progress reports.
22
+ seq.read(file) do |track, num_tracks, i|
23
+ puts "read track #{track ? track.name : ''} (#{i} of #{num_tracks})"
24
+ end
25
+ end
26
26
 
27
27
  include MIDI
28
- seq.each { | track |
29
- track.each { | event |
30
- puts event.data if event.kind_of?(MIDI::MetaEvent) &&
31
- [META_TEXT, META_COPYRIGHT, META_SEQ_NAME, META_INSTRUMENT,
32
- META_LYRIC, META_CUE, META_MARKER].include?(event.meta_type)
33
- }
34
- }
28
+ seq.each do |track|
29
+ track.each do |event|
30
+ puts event.data if event.is_a?(MIDI::MetaEvent) &&
31
+ [META_TEXT, META_COPYRIGHT, META_SEQ_NAME, META_INSTRUMENT,
32
+ META_LYRIC, META_CUE, META_MARKER].include?(event.meta_type)
33
+ end
34
+ end
@@ -17,59 +17,58 @@ require 'midilib/sequence'
17
17
  require 'midilib/io/seqreader'
18
18
  require 'midilib/io/seqwriter'
19
19
 
20
-
21
20
  def usage
22
- $stderr.print <<EOF
23
- usage: #{$0} [--channel|-c channel] [--transpose|-t half_steps]
24
- input_midi_file output_midi_file
25
-
26
- --channel|-c channel 1-16; default is 1
27
- --transpose|-t half_steps default = 12 (one octave up)
28
- EOF
29
- exit(1)
21
+ $stderr.print <<~EOF
22
+ usage: #{$0} [--channel|-c channel] [--transpose|-t half_steps]
23
+ input_midi_file output_midi_file
24
+ #{' '}
25
+ --channel|-c channel 1-16; default is 1
26
+ --transpose|-t half_steps default = 12 (one octave up)
27
+ EOF
28
+ exit(1)
30
29
  end
31
30
 
32
31
  transpose = 12
33
32
  channel = 0
34
33
 
35
34
  g = GetoptLong.new(['--transpose', '-t', GetoptLong::REQUIRED_ARGUMENT],
36
- ['--channel', '-c', GetoptLong::REQUIRED_ARGUMENT])
37
- g.each { | name, arg |
38
- case name
39
- when '--transpose'
40
- transpose = arg.to_i
41
- when '--channel'
42
- channel = arg.to_i - 1
43
- else
44
- usage()
45
- end
46
- }
35
+ ['--channel', '-c', GetoptLong::REQUIRED_ARGUMENT])
36
+ g.each do |name, arg|
37
+ case name
38
+ when '--transpose'
39
+ transpose = arg.to_i
40
+ when '--channel'
41
+ channel = arg.to_i - 1
42
+ else
43
+ usage
44
+ end
45
+ end
47
46
 
48
- usage() unless ARGV.length >= 2
47
+ usage unless ARGV.length >= 2
49
48
 
50
- seq = MIDI::Sequence.new()
51
- File.open(ARGV[0], 'rb') { | file |
52
- # The block we pass in to Sequence.read is called at the end of every
53
- # track read. It is optional, but is useful for progress reports.
54
- seq.read(file) { | num_tracks, i |
55
- puts "read track #{i} of #{num_tracks}"
56
- }
57
- }
49
+ seq = MIDI::Sequence.new
50
+ File.open(ARGV[0], 'rb') do |file|
51
+ # The block we pass in to Sequence.read is called at the end of every
52
+ # track read. It is optional, but is useful for progress reports.
53
+ seq.read(file) do |num_tracks, i|
54
+ puts "read track #{i} of #{num_tracks}"
55
+ end
56
+ end
57
+
58
+ seq.each do |track|
59
+ track.each do |event|
60
+ next unless event.is_a?(MIDI::NoteEvent) && event.channel == channel
58
61
 
59
- seq.each { | track |
60
- track.each { | event |
61
- if event.kind_of?(MIDI::NoteEvent) && event.channel == channel
62
- val = event.note + transpose
63
- if val < 0 || val > 127
64
- $stderr.puts "transposition out of range; ignored"
65
- else
66
- event.note = val
67
- end
68
- end
69
- }
70
- }
62
+ val = event.note + transpose
63
+ if val < 0 || val > 127
64
+ warn 'transposition out of range; ignored'
65
+ else
66
+ event.note = val
67
+ end
68
+ end
69
+ end
71
70
 
72
71
  # Output to named file or stdout.
73
72
  file = ARGV[1] ? File.open(ARGV[1], 'wb') : $stdout
74
73
  seq.write(file)
75
- file.close() if ARGV[1]
74
+ file.close if ARGV[1]