Class: MTK::IO::MIDIFile
- Inherits:
-
Object
- Object
- MTK::IO::MIDIFile
- Defined in:
- lib/mtk/io/midi_file.rb
Overview
Note:
This class is optional and only available if you require ‘mtk/midi/file’. It depends on the ‘midilib’ gem.
MIDI file I/O: reads MIDI files into Events::Timelines and writes Events::Timelines to MIDI files.
Instance Method Summary collapse
-
#initialize(file) ⇒ MIDIFile
constructor
A new instance of MIDIFile.
-
#to_timelines ⇒ Timeline
Read a MIDI file into an Array of Events::Timelines.
- #write(anything) ⇒ Object
-
#write_timeline(timeline, parent_sequence = nil) ⇒ Object
Write the Timeline as a MIDI file.
- #write_timelines(timelines, parent_sequence = nil) ⇒ Object
Constructor Details
#initialize(file) ⇒ MIDIFile
Returns a new instance of MIDIFile.
10 11 12 13 14 15 16 |
# File 'lib/mtk/io/midi_file.rb', line 10 def initialize file if file.respond_to? :path @file = file.path else @file = file.to_s end end |
Instance Method Details
#to_timelines ⇒ Timeline
Read a MIDI file into an Array of Events::Timelines
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/mtk/io/midi_file.rb', line 22 def to_timelines timelines = [] ::File.open(@file, 'rb') do |f| sequence = ::MIDI::Sequence.new sequence.read(f) pulses_per_beat = sequence.ppqn.to_f track_idx = -1 sequence.each do |track| track_idx += 1 timeline = MTK::Events::Timeline.new note_ons = {} #puts "TRACK #{track_idx}" track.each do |event| #puts "#{event.class}: #{event} @#{event.time_from_start}" time = (event.time_from_start)/pulses_per_beat case event when ::MIDI::NoteOn note_ons[event.note] = [time,event] # TODO: handle note ons with velocity 0 as a note off (use output from Logic Pro as a test case) # This isn't actually necessary right now as midilibs seqreader#note_on automatically # converts note ons with velocity 0 to note offs. In the future, for full off velocity support, # I'll need to monkey patch midilib and update the code here when ::MIDI::NoteOff on_time,on_event = note_ons.delete(event.note) if on_event duration = time - on_time note = MTK::Events::Note.from_midi(event.note, on_event.velocity, duration, event.channel) timeline.add on_time, note end when ::MIDI::Controller, ::MIDI::PolyPressure, ::MIDI::ChannelPressure, ::MIDI::PitchBend, ::MIDI::ProgramChange timeline.add time, MTK::Events::Parameter.from_midi(*event.data_as_bytes) when ::MIDI::Tempo # Not sure if event.tempo needs to be converted? TODO: test! timeline.add time, MTK::Events::Parameter.new(:tempo, :value => event.tempo) end end timelines << timeline end end timelines end |
#write(anything) ⇒ Object
71 72 73 74 75 76 77 |
# File 'lib/mtk/io/midi_file.rb', line 71 def write(anything) case anything when MTK::Events::Timeline then write_timeline(anything) when Enumerable then write_timelines(anything) else raise "#{self.class}#write doesn't understand #{anything.class}" end end |
#write_timeline(timeline, parent_sequence = nil) ⇒ Object
Write the Timeline as a MIDI file
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/mtk/io/midi_file.rb', line 88 def write_timeline(timeline, parent_sequence=nil) sequence = parent_sequence || ::MIDI::Sequence.new clock_rate = sequence.ppqn track = add_track sequence timeline.each do |time,events| time *= clock_rate events.each do |event| next if event.rest? channel = (event.channel || 1) - 1 # midilib seems to count channels from 0, hence the -1 case event.type when :note pitch, velocity = event.midi_pitch, event.velocity add_event track, time => note_on(channel, pitch, velocity) duration = event.duration_in_pulses(clock_rate) # TODO: use note_off events when supporting off velocities # add_event track, time+duration => note_off(channel, pitch, velocity) # NOTE: cannot test the following line of code properly right now, because midilib automatically converts # note ons with velocity 0 to note offs when reading files. See comments in #to_timelines in this file add_event track, time+duration => note_on(channel, pitch, 0) # we use note ons with velocity 0 to indicate no off velocity when :control add_event track, time => cc(channel, event.number, event.midi_value) when :pressure if event.number add_event track, time => poly_pressure(channel, event.number, event.midi_value) else add_event track, time => channel_pressure(channel, event.midi_value) end when :bend add_event track, time => pitch_bend(channel, event.midi_value) when :program add_event track, time => program(channel, event.midi_value) when :tempo add_event track, time => tempo(event.value) end end end track.recalc_delta_from_times write_to_disk sequence unless parent_sequence end |
#write_timelines(timelines, parent_sequence = nil) ⇒ Object
79 80 81 82 83 |
# File 'lib/mtk/io/midi_file.rb', line 79 def write_timelines(timelines, parent_sequence=nil) sequence = parent_sequence || ::MIDI::Sequence.new timelines.each{|timeline| write_timeline(timeline, sequence) } write_to_disk sequence unless parent_sequence end |