[perl][java] Inline::Java + Java Sound APIでPerlからサウンドファイルを制御
Perlでmidi音楽ファイルを扱おうと思った場合、ファイルの書き出しだけであればMIDI::Simpleで十分で、再生まで行おうと思うと MIDI::Music という選択になるかと思います。
ただし、MIDI::Music は XS で soundcard.h を使うので、Mac OS X ではかなりつらいことになります。この辺を CoreAudio で代替するコードを書くとモテるかも?
porting command line unix tools to Max OS X
http://developer.apple.com/jp/technotes/tn2071.html
再生部分の処理を自分で書こうにも、音声ファイルの再生はちょっと想像しただけでもかなり複雑です。そのマシンに複数あるサウンドでバイスを正しく認識し、そのどれかを選択、対象となるファイルがそのデバイスの対応フォーマットかどうかを判別してファイルから読み取ったデータを流し込むという手順になります。これらはそのマシンの環境に深く依存するため、アプリケーション側から見ると、それらの環境依存ファクターを吸収するレイヤーが欲しくなります。
Java の場合は Java Sound API を介してやや抽象的な操作が出来るので素敵です。かなりインチキっぽいですが、Inline::Java を用いてそれをPerlから使う事が出来ます。
inline_java.pl
#!/usr/bin/perl use strict; use warnings; sub java_sound_source { q{ import java.lang.*; import java.io.*; import javax.sound.sampled.*; public class JavaSound { public static final int STREAM_BUFFER_SIZE = 1024 * 64; private Mixer[] mixers = null; public JavaSound() { this.setup_mixer(); } private void setup_mixer () { Mixer.Info[] mixer_info = AudioSystem.getMixerInfo(); Mixer[] mixers = new Mixer[mixer_info.length]; for (int i = 0; i < mixer_info.length; i++) { Mixer.Info mixer = mixer_info[i]; mixers[i] = AudioSystem.getMixer(mixer_info[i]); } this.mixers = mixers; } public void play_wave_file(String filename) { // setup audio stream File file = new File(filename); AudioInputStream stream = null; try { stream = this.get_audio(file); } catch (IOException e) { System.err.println("cannot open file"); return; } catch (UnsupportedAudioFileException e) { System.err.println("unsupported format"); return; } // open first audio line of first mixer (lazy!) Mixer target_mixer = this.mixers[0]; Line.Info[] lines = target_mixer.getSourceLineInfo(); SourceDataLine outline = null; try { outline = (SourceDataLine)target_mixer.getLine(lines[0]); outline.open(stream.getFormat(), STREAM_BUFFER_SIZE); } catch (LineUnavailableException e) { System.err.println("line label unavailable " + e.getMessage()); return; } try { // play (read audio bytes from the file and put them to // output source data line) outline.start(); int bytes_done = 0; byte[] buffer = new byte[STREAM_BUFFER_SIZE]; while ((bytes_done = stream.read(buffer)) != -1) { outline.write(buffer, 0, STREAM_BUFFER_SIZE); } outline.stop(); } catch (Exception e) { System.err.println(e.getMessage()); return; } } public static AudioInputStream get_audio(File file) throws UnsupportedAudioFileException, IOException { return AudioSystem.getAudioInputStream(file); } } }} use Inline Java => \&java_sound_source; my $javasound = new JavaSound(); $javasound->play_wave_file(shift);
以下のコマンドで .wav ファイルを再生することが出来ます。
bonar$ perl inline_java.pl Survivalism_bonar_mix.wav
「それがありなら、なんでもありだろwww」って感じですが、J2SEはもはやほとんどのプラットフォームで標準で準備されているので、意外とありなのではと思っています。同じ要領でmidiファイルの再生やリアルタイムな制御も出来る事になります。
じゃあ全部Javaで書けよって話ですが。新年あけましておめでとうございます。