I have this sample code, which is essentially just a few basic classes for working with mdii files
class Array
def to_midi(file, note_length='eighth')
midi_max = 108.0
midi_min = 21.0
low, high = min, max
song = MIDI::Sequence.new
# Create a new track to hold the melody, running at 120 beats per minute.
song.tracks << (melody = MIDI::Track.new(song))
melody.events << MIDI::Tempo.new(MIDI::Tempo.bpm_to_mpq(120))
# Tell channel zero to use the "piano" sound.
melody.events << MIDI::ProgramChange.new(0, 0)
# Create a series of note events that play on channel zero.
each do |number|
midi_note = (midi_min + ((number-midi_min) * (midi_max-low)/high)).to_i
melody.events << MIDI::NoteOnEvent.new(0, midi_note, 127, 0)
melody.events << MIDI::NoteOffEvent.new(0, midi_note, 127,
song.note_to_delta(note_length))
end
open(file, 'w') { |f| song.write(f) }
end
end
class TimedTrack < MIDI::Track
MIDDLE_C = 60
@@channel_counter=0
def initialize(number, song)
super(number)
@sequence = song
@time = 0
@channel = @@channel_counter
@@channel_counter += 1
end
# Tell this track's channel to use the given instrument, and
# also set the track's instrument display name.
def instrument=(instrument)
@events << MIDI::ProgramChange.new(@channel, instrument)
super(MIDI::GM_PATCH_NAMES[instrument])
end
# Add one or more notes to sound simultaneously. Increments the per-track
# timer so that subsequent notes will sound after this one finishes.
def add_notes(offsets, velocity=127, duration='quarter')
offsets = [offsets] unless offsets.respond_to? :each
offsets.each do |offset|
event(MIDI::NoteOnEvent.new(@channel, MIDDLE_C + offset, velocity))
end
@time += @sequence.note_to_delta(duration)
offsets.each do |offset|
event(MIDI::NoteOffEvent.new(@channel, MIDDLE_C + offset, velocity))
end
recalc_delta_from_times
end
# Uses add_notes to sound a chord (a major triad in root position), using the
# given note as the low note. Like add_notes, increments the per-track timer.
def add_major_triad(low_note, velocity=127, duration='quarter')
add_notes([0, 4, 7].collect { |x| x + low_note }, velocity, duration)
end
private
def event(event)
@events << event
event.time_from_start = @time
end
end
most of it makes perfect sense to me except for the lines that use the <<
operator, from all of my research the only reason to use a <<
is when your defining a class that will be a singleton. So in what way specifically is the <<
being used in this code?
From https://github.com/jimm/midilib :
MIDI::Track
is a track that contains an array of events.
So with <<
you're adding events to your track. It is the same as melody.events.push(MIDI::NoteOnEvent.new(0, midi_note, 127, 0))
<<
could also be used for bit shifting operations
http://calleerlandsson.com/2014/02/06/rubys-bitwise-operators/
<<
operator may be used either for bitwise operations (quite unlikely here) or may be overloaded inside the class to match some behavior. In this case there is (probably) an Array
object and thus the event
is pushed into the @events
array via this operator. More info about this use case can be found here .
Take notice, that in future you can bump into other situations where this operator is used and not everytime it will mean same thing - it depends on library creator. The other use case that comes into mind right now can be ActiveRecord Relationships, as has_many
where also you can use this operator to immediately push an object to relationship and save. More info about this one can be found in api docs here . Quick sample:
class User < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :user
end
And then somewhere in code you can use:
@post = Post.find(10)
@user = User.find(1)
@user.posts << @post
# automatically save the @post object with updated foreign key
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.