Bauchschmerzen mit Ruby

Meine erste Produktion mit Jim Menards Midilib. Ich weiß nicht, wie es anderen damit geht, aber mir wird körperlich schlecht, wenn ich mir das anhöre. [Mehr Info, 16. Jan. 2008: Frau und Tochter reagieren auch mit Bauchschmerzen… Ich verkaufe den Sound an einen Apotheker!]

Ich spiele also jetzt wohl mit MIDI-Dateien, verwende dafür aber nicht Haskell sondern Ruby. Die Dokumentation von Midilib ist nicht toll und die Beispiele haben mir nicht ausgereicht, aber der Jim beantwortet Fragen im RubyforgeForum ganz schnell.

Die Quelltexten meiner Komposition:
#! /usr/bin/env ruby
#
# usage: smallpiece.rb
#
# This script, derived from from_scratch.rb by Jim Menard,
# shows you how to create a new sequence from scratch and save it
# to a MIDI file. It creates a file called 'first.mid'.

# the piece adds different tracks, handling standard instruments and percussion

# Start looking for MIDI module classes in the directory above this one.
# This forces us to use the local copy, even if there is a previously
# installed version out there somewhere.
$LOAD_PATH[0, 0] = File.join(File.dirname(__FILE__), ‚..‘, ‚lib‘)
BASE_TONE = 40
OCTAVE = 12
BASS_CHANNEL = 0
# 9 is the channel for drum events (Channel 10 0 indexed makes 9)
DRUMS_CHANNEL = 9
VOICE_CHANNEL = 0

require ‚midilib/sequence‘
require ‚midilib/consts‘
include MIDI

seq = Sequence.new()

# Create a first track for the sequence. This holds tempo events and stuff
# like that.
track = Track.new(seq)
seq.tracks << track
track.events << Tempo.new(Tempo.bpm_to_mpq(120))
track.events << MetaEvent.new(META_SEQ_NAME, ‚Sequence Name‘)
# now calculate the note lengths
# We use the new Sequence#note_to_delta method to get the
# delta time length of a single quarter note.
quarter_note_length = seq.note_to_delta(‚quarter‘)
eighth_note_length = quarter_note_length / 2
half_note_length = quarter_note_length * 2
note_length = quarter_note_length * 4
# and define the length of the lines, the lines per verses, and the verses in the piece
line_length = 4 # * note_length
verse_length = 3 # * line_length
song_length = 3 # * verse_length

# Create your track to hold the notes, give them a name and add them to the sequence.
bass = Track.new(seq)
bass.name = ‚Bass‘
seq.tracks << bass
drums = Track.new(seq)
drums.name = ‚Drums‘
seq.tracks << drums
voices = Track.new(seq)
voices.name = ‚Voices‘
seq.tracks << voices

# Add volume controller events (optional).
bass.events << Controller.new(BASS_CHANNEL, CC_VOLUME, 127)
drums.events << Controller.new(DRUMS_CHANNEL, CC_VOLUME, 127)
voices.events << Controller.new(VOICE_CHANNEL, CC_VOLUME, 127)
# Add breath controller events.
# I do not know yet whther this makes any sense
voices.events << Controller.new(VOICE_CHANNEL, CC_BREATH_CONTROLLER, 0)

# Add events to the track. Arguments for note on and note off
# constructors are channel, note, velocity, and delta_time. Channel numbers
# start at zero.
# here you set the instrument. 32 is the acoustic base
bass.events << ProgramChange.new(BASS_CHANNEL, 32, 0)
# 3 is the acoustic snare drum
drums.events << ProgramChange.new(DRUMS_CHANNEL, 3, 0)
# 52, Choir OOhs
voices.events << ProgramChange.new(VOICE_CHANNEL, 52, 0)

# fill the tracks.
# we’ll get back to the ugly notation later on

# the base
song_length.times do |time|
4.times do
[0, 4, 7, 9].each { | offset |
bass.events << NoteOnEvent.new(BASS_CHANNEL, BASE_TONE + offset, 127, 0)
bass.events << NoteOffEvent.new(BASS_CHANNEL, BASE_TONE + offset, 127, quarter_note_length)
}
end
2.times do
[5, 9, 12, 14].each { | offset |
bass.events << NoteOnEvent.new(BASS_CHANNEL, BASE_TONE + offset, 127, 0)
bass.events << NoteOffEvent.new(BASS_CHANNEL, BASE_TONE + offset, 127, quarter_note_length)
}
end
2.times do
[0, 4, 7, 9].each { | offset |
bass.events << NoteOnEvent.new(BASS_CHANNEL, BASE_TONE + offset, 127, 0)
bass.events << NoteOffEvent.new(BASS_CHANNEL, BASE_TONE + offset, 127, quarter_note_length)
}
end
[7, 11, 14, 11].each { | offset |
bass.events << NoteOnEvent.new(BASS_CHANNEL, BASE_TONE + offset, 127, 0)
bass.events << NoteOffEvent.new(BASS_CHANNEL, BASE_TONE + offset, 127, quarter_note_length)
}
[5, 9, 12, 9].each { | offset |
bass.events << NoteOnEvent.new(BASS_CHANNEL, BASE_TONE + offset, 127, 0)
bass.events << NoteOffEvent.new(BASS_CHANNEL, BASE_TONE + offset, 127, quarter_note_length)
}
2.times do
[0, 4, 7, 9].each { | offset |
bass.events << NoteOnEvent.new(BASS_CHANNEL, BASE_TONE + offset, 127, 0)
bass.events << NoteOffEvent.new(BASS_CHANNEL, BASE_TONE + offset, 127, quarter_note_length)
}
end
end

# the drums
song_length.times do |verse|
verse_length.times do |line|
line_length.times do
drums.events << NoteOnEvent.new(DRUMS_CHANNEL, BASE_TONE + 2 * OCTAVE, 127, 0)
drums.events << NoteOffEvent.new(DRUMS_CHANNEL, BASE_TONE + 2 * OCTAVE + 7, 127, quarter_note_length)
drums.events << NoteOnEvent.new(DRUMS_CHANNEL, BASE_TONE + 3 * OCTAVE, 127, quarter_note_length)
drums.events << NoteOffEvent.new(DRUMS_CHANNEL, BASE_TONE + 3 * OCTAVE + 7, 127, eighth_note_length)
drums.events << NoteOnEvent.new(DRUMS_CHANNEL, 38, 127, eighth_note_length)
drums.events << NoteOffEvent.new(DRUMS_CHANNEL, 38, 127, quarter_note_length)
end
end
end

# the voices
song_length.times do |verse|
verse_length.times do |line|
line_length.times do |note|
# whe want to be in the same chord as the bass is
len = line_length * line + note + 1
case
when len == 5
tone = BASE_TONE + 5
when len == 6
tone = BASE_TONE + 5
when len == 10
tone = BASE_TONE + 5
when len == 9
tone = BASE_TONE + 7
else
tone = BASE_TONE
end
# now add the voice
voices.events << NoteOnEvent.new(VOICE_CHANNEL, tone + 2 * OCTAVE, 127, 0)
voices.events << NoteOnEvent.new(VOICE_CHANNEL, tone + 2 * OCTAVE + 4, 127, 0)
voices.events << NoteOffEvent.new(VOICE_CHANNEL, tone + 2 * OCTAVE + 4, 127, eighth_note_length)
voices.events << NoteOnEvent.new(VOICE_CHANNEL, tone + 2 * OCTAVE + 7, 127, 0)
voices.events << NoteOffEvent.new(VOICE_CHANNEL, tone + 2 * OCTAVE + 7, 127, eighth_note_length)
voices.events << NoteOnEvent.new(VOICE_CHANNEL, tone + 2 * OCTAVE + 9, 127, 0)
voices.events << NoteOffEvent.new(VOICE_CHANNEL, tone + 2 * OCTAVE + 9, 127, eighth_note_length)
# and sometime put on and off an effect
# I’m not sure yet that this works
voices.events << Controller.new(VOICE_CHANNEL, CC_SUSTAIN, 133)
voices.events << NoteOnEvent.new(VOICE_CHANNEL, tone + 2 * OCTAVE + 7, 127, half_note_length)
voices.events << NoteOnEvent.new(VOICE_CHANNEL, tone + 2 * OCTAVE + 4, 127, quarter_note_length)
voices.events << NoteOffEvent.new(VOICE_CHANNEL, tone + 2 * OCTAVE + 4, 127, quarter_note_length)
voices.events << NoteOffEvent.new(VOICE_CHANNEL, tone + 2 * OCTAVE + 7, 127, half_note_length)
voices.events << Controller.new(VOICE_CHANNEL, CC_SUSTAIN, 0)
voices.events << NoteOffEvent.new(VOICE_CHANNEL, tone + 2 * OCTAVE, 200, note_length)
end
end
end

# Calling recalc_times is not necessary, because that only sets the events‘
# start times, which are not written out to the MIDI file. The delta times are
# what get written out.
bass.recalc_times
drums.recalc_times
voices.recalc_times

File.open(‚first.mid‘, ‚wb‘) { | file |
seq.write(file)
}

Advertisements

One Trackback

  1. […] Meine erste Produktion mit Midilib bleibt schlecht, aber tut nicht mehr weh. Ich weiss sogar was in der ersten Version schief gegangen […]

Einen Kommentar schreiben

Required fields are marked *

*
*

%d Bloggern gefällt das: