RubyとPerl比較: MIDIを読み込んで編集する
MIDIを編集したい。
MIDIをプログラム組んで編集したいなって思ったとき,これまでは過去に英華を極めたPerlでMIDI読み込みをやってました。時代的にPerlが一番強いときにMIDIもかなり流行ったと思うので。
それをRubyでやったら超楽にできちゃったやんっていう話。
とりあえずダイナミクスを可能な限り広げるスクリプトをPerlとRubyで書いてみました。 具体的には,MIDIの内容がベロシティ60-100の間の音しかない場合,それをベロシティ1-127の範囲に広げるって感じのスクリプト。
Perlから。
#!/usr/bin/perl use MIDI; use MIDI::Simple; use MIDI::Score; use Data::Dumper; use List::Util qw(max); use List::Util qw(min); local $Data::Dumper::Indent = 0; my $midi = shift; if( !defined($midi) ) { print "ABORTED!\nUsage:\n\$ perl midi.pl something.midi\ninput midi file."; die; } print $midi; print "\n"; my @s; my @channel; my @v_ch; my @mtrack; my $opus = MIDI::Opus->new({ "from_file" => $midi || die }); my $i = 0; foreach my $t ($opus->tracks) { if($t->events_r and @{$t->events_r}) { my($score_r, $ticks) = MIDI::Score::events_r_to_score_r( MIDI::Event::copy_structure( $t->events_r )); # event kara score he @notes = (); my $count = 0; while ($count < scalar(@$score_r)){ if(${$score_r}[$count]->[0]=~/note/){ print ${$score_r}[$count]->[5]; print "-"; print ${$score_r}[$count]->[4]; print "\n"; push(@notes,${$score_r}[$count]->[5]); } $count ++; } my $min_note = min @notes; my $max_note = max @notes; $count = 0; while ($count < scalar(@$score_r)){ if(${$score_r}[$count]->[0]=~/note/){ ${$score_r}[$count]->[5] = 1 + int ((${$score_r}[$count]->[5] - $min_note) * (126/($max_note-$min_note))); } $count ++; } my($events_r, $ticks2) = MIDI::Score::score_r_to_events_r(MIDI::Event::copy_structure( $score_r)); # score kara event he push @s,$events_r; my $x = MIDI::Track->new({ 'events' => $events_r }); push @v_ch , $x; $i++; push @ticks, $ticks; if(my $time_now) { unshift @{$t->events_r}, ['text_event', $time_now, "_"]; } } else { DEBUG > 1 and print " * Track is eventless. Skipping.\n"; } push @out_tracks, $t; } push @granularities, $opus->ticks; print $ticks[3]; my $o = MIDI::Opus->new( { 'format' => 1, 'ticks' => 480, 'tracks' => [ @v_ch ] } ); $o->write_to_file( 'output.mid' );
Perlだとこんな感じ。ざっと約80行。 正直Perlはわけわからないので酷い書き方してると思います。笑
これと同じ処理をRubyで書いたら。
require 'midilib' seq = MIDI::Sequence.new() File.open(ARGV[0], 'rb') do |file| seq.read(file) do |track, num_tracks, i| puts "read track #{i} of #{num_tracks}" end end seq.each do |track| notes = track.map{|event| event.velocity if MIDI::NoteEvent === event and event.note}.compact track.each do |event| if MIDI::NoteEvent === event and event.note puts event.velocity.to_s + "-" + event.note.to_s event.velocity = 1 + ((event.velocity - notes.min) * (126/(notes.max-notes.min))).to_i end end end File.open('output.midi', 'wb') { |file| seq.write(file) }
なんと20行!!びびる
Perlはよく知らないのでもっと行減らせるんだろうなーとは思ってますが,それでも4倍の差がでるのはすごいなーと思います。
まぁPerlやってる人が書いたら20行くらいに行減らせそうですけどね...
というかコードの行数とかどうでも良くて,自分にとっての分かりやすさが段違いです。
MIDIの読み込みでPerl以外の良い方法をこれまで知らなかったのでそのためだけにPerl使ってました。
が,ついにPerlともお別れの時が来たようです。