ERLEDIGT
NEIN
NEIN
ANTWORTEN
0
0
ZUGRIFFE
763
763
EMPFEHLEN
-
Hallo,
hier meine Lösung in Ruby. Aus den ursprünglichen 60 Zeilen sind nach etwas OOP-Refactoring jetzt doch noch 175 geworden.
Code ruby:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 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 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
#!/usr/bin/env ruby require 'observer' class Instrument attr_reader :data def initialize(pcm_data) @data = pcm_data end def sample_count @data.size end def sample(n) @data[n] end def self.from_file(path) File.open(path, 'rb') do |f| f.seek(44) data = f.read.unpack("s*") self.new(data) end end end class Note def initialize(instrument, velocity) @instrument = instrument @velocity = velocity @sample_num = -1 end def next_sample! @sample_num += 1 (@instrument.sample(@sample_num) * @velocity).to_i end def done? @sample_num >= @instrument.sample_count - 1 end end class WaveWriter WAVE_HEADER = "\x52\x49\x46\x46%s\x57\x41\x56\x45\x66\x6d\x74\x20\x10\x00" + "\x00\x00\x01\x00\x01\x00\x44\xac\x00\x00\x88\x58\x01\x00\x02" + "\x00\x10\x00\x64\x61\x74\x61%s" WAVE_HEADER_SIZE = 44 def initialize(io, sample_count) @io = io write_header(sample_count) end def <<(sample) @io.write [sample].pack("s") end private def write_header(sample_count) data_size = sample_count * 2 header = WAVE_HEADER % [[data_size + WAVE_HEADER_SIZE - 8].pack("L"), [data_size].pack("L")] @io.write header end end class Sequencer include Observable DEFAULT_RENDER_OPTIONS = { :beats_per_minute => 80, :ticks_per_beat => 4, :sampling_rate => 44_100.0, } def initialize @instruments = [] end def add_instrument(instrument, roll) @instruments << [instrument, roll] end def render_wave(io, options = {}) options = DEFAULT_RENDER_OPTIONS.merge(options) ticks_per_sec = options[:beats_per_minute] * options[:ticks_per_beat] / 60.0 total_ticks = @instruments.map {|instrument, notes| notes.size}.max total_beats = (total_ticks.to_f / options[:ticks_per_beat]).ceil total_secs = total_beats.to_f / options[:beats_per_minute] * 60.0 sample_count = (total_secs * options[:sampling_rate]).to_i wave = WaveWriter.new(io, sample_count) last_tick_num = -1 notes = [] sample_count.times do |sample_num| time = sample_num / options[:sampling_rate] tick_num = (ticks_per_sec * time).to_i if tick_num > last_tick_num last_tick_num = tick_num notes += notes_for_tick(tick_num) changed(true) notify_observers(tick_num.to_f / total_ticks) end notes.reject! {|note| note.done?} wave << mix_notes(notes) end changed(true) notify_observers(1.0) end private def notes_for_tick(tick_num) notes = [] @instruments.each do |instrument, roll| symbol = roll[tick_num, 1] next if symbol.to_s.strip.empty? velocity = 0.5 if symbol =~ /\d/ velocity *= symbol.to_i * 0.1 end notes << Note.new(instrument, velocity) end notes end def mix_notes(notes) notes.inject(0) {|result, note| result + note.next_sample!} end end if __FILE__ == $0 DRUMKIT_DIR = "./drumkit" options = { :beats_per_minute => gets.to_i, :ticks_per_beat => gets.to_i } instruments = [] while !(line = gets.strip).empty? instruments << File.join(DRUMKIT_DIR, line) end rolls = [] while line = gets rolls << line end sequencer = Sequencer.new instruments.zip(rolls).each do |instrument, roll| sequencer.add_instrument(Instrument.from_file(instrument), roll) end $stderr.puts("Rendering...") progress_reporter = Object.new def progress_reporter.update(finished) $stderr.print("\r%d%%" % (finished*100)) end sequencer.add_observer(progress_reporter) sequencer.render_wave($stdout, options) end
Als Zugabe hier noch der Konverter, der Dateien im XML-Format von Hydrogen in unser Format umwandelt:
Code ruby:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
#!/usr/bin/env ruby require 'rubygems' require 'nokogiri' doc = Nokogiri::XML(File.new(ARGV[0])) instruments = [] doc.xpath("//instrumentList/instrument").each do |instrument| file = instrument.xpath("./layer/filename").first.text.sub(/\.flac$/, ".wav") instruments[(instrument/:id).text.to_i] = file end patterns = {} size = 0 doc.xpath("//patternList/pattern").each do |pattern| name = (pattern/:name).text size = (pattern/:size).text.to_i roll = Array.new(instruments.size) {" "*size} pattern.xpath(".//note").each do |note| instrument = (note/:instrument).text.to_i position = (note/:position).text.to_i velocity = ((note/:velocity).text.to_f*10).to_i roll[instrument][position] = velocity.to_s end patterns[name] = roll end rolls = [] doc.xpath("//patternSequence/group/patternID").each do |pattern_id| $stderr.puts(pattern_id.text) rolls << patterns[pattern_id.text] end roll = rolls.transpose.map {|a| a.join} puts doc.xpath("//song/bpm").text puts size/4 instruments, roll = instruments.zip(roll).reject {|i,r| r.strip.empty?}.transpose puts instruments puts puts roll
Grüße,
Matthias„Gib einem Menschen einen Fisch, und er wird für einen Tag satt. Lehre ihn Fischen, und er wird ein Leben lang satt.“
“For every complex problem, there is an answer that is short, simple and wrong.”
“Pessimism is safe, but optimism is a lot faster!”
Aktuelles Coding Quiz: #17 - Wörter kreuz und quer
Ähnliche Themen
-
[QUIZ#14] Matthias Reitinger (Ruby)
Von Matthias Reitinger im Forum ArchivAntworten: 3Letzter Beitrag: 28.03.10, 14:19 -
[QUIZ#13] Matthias Reitinger (Ruby)
Von Matthias Reitinger im Forum ArchivAntworten: 0Letzter Beitrag: 17.01.10, 17:02 -
[QUIZ#10] Matthias Reitinger (Ruby)
Von Matthias Reitinger im Forum ArchivAntworten: 1Letzter Beitrag: 10.10.09, 20:56 -
[QUIZ#1] Matthias Reitinger (C++)
Von Matthias Reitinger im Forum ArchivAntworten: 4Letzter Beitrag: 22.09.08, 22:02 -
[QUIZ#1] Matthias Reitinger (Ruby)
Von Matthias Reitinger im Forum ArchivAntworten: 0Letzter Beitrag: 21.09.08, 02:44






Login





