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.
@@ -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]