JavaSound on Linux
ドハマリしたので記録。
事の発端はJavaSoundで音を出して遊ぼうとしたこと。
AudioFormat format = new AudioFormat(44100, 16, 2, true, false); SourceDataLine line = AudioSystem.getSourceDataLine(format); line.open()
こんな感じでSourceDataLineを取得して、lineに適当に波形流し込んで遊ぼうとしてたんですが、どうも時々line.open()でLineUnavailableExceptionが投げられる。で、色々調べた結果、JavaSoundはALSAではなくOSS向けの実装になってるぽくて、/dev/dspを排他的に開く様になっていて、
% lsof /dev/dsp COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME java 3544 osa 9w CHR 14,3 8592 /dev/dsp
こんな事になる。これがFlashのプラグインと衝突していて、先にfirefoxでFlashを見ていると(Flashプラグインは一度起動されるとFlashの実行が終わっても生き残り続け、サウンドデバイスを開きっぱなしにするため)LineUnavailableException: Audio Device Unavailableになる様です。
で、この現象について調べていたらこんなページが引っかかる。これによるとJavaSoundはplughw:0,0を掴もうとするので.asoundrcでplughwをdmix*1すればいいよ、と言うことですが、大嘘です。
実際にAudioSystem.getMixerInfoでMixerを取得すると、自分の環境では次の3つを得ます。
SB [plughw:0,0], version 1.0.18a:Direct Audio Device: HDA ATI SB, AD198x Analog, AD198x Analog Java Sound Audio Engine, version 1.0:Software mixer and synthesizer Port SB [hw:0], version 1.0.18a:HDA ATI SB, Analog Devices AD1981
このうち最後のものはよく分からんのですが、一番上と二番目はSourceDataLineを返せるMixerで、しかも一番上はいろいろなフォーマットに対応している様子を見せます(この辺は自力でプログラムを組むよりjsinfoを使った方が楽)。
が、しかし!コイツから得たSourceDataLineはopen()できない!どんなAudioFormatを渡しても(自分で対応してると言ってるフォーマットですら)「それはサポートしてないよ」と言われるだけ。結局使えるのは二番目のMixerが返すものだけで、これが前述のOSSで/dev/dspを掴んでしまうLineです。.asoundrcに色々書いて試してみるも、全然状況は変わらず。詰んだ。
で、ここから色々と探していると、こんなスレッドを発見。reply 2 of 4の人が、前のplughw:0,0をどうのと言っていたサイトについて、アレはたまたまサウンドカードの相性が合っていただけぽいよという旨の発言をしています。自分はあのページでは出来ているんだからこっちの環境が悪いんじゃないかと思って四苦八苦してたんだけど・・・。
で、ここで気分を変えて、調査途中で見かけたPulseAudioについて調べる。これもdmixとやることは同じみたいだけど、PulseAudioというドライバがALSAの前のレイヤーとして挟まるらしい。それくらいALSAなら自力で出来んじゃない?と思って「alsa dmix dsp」で検索すると・・・出た!aossというコマンドを使うといいらしい。ちなみにこれは単なるシェルスクリプトで、LD_PRELOADにlibaoss.soを入れるだけのもの。これを使ってaoss経由でjavaコマンドを実行したら共存した・・・!長かった・・・!
plughwの話がなければもっと早くたどり着けていた気がする。というかJavaSound on Linuxの情報少なすぎだよ・・・!英語でも「うまく動かん!どうすればいいの!→JavaSoundの実装がゴミなんだよ」位の流れしか無い。
ちなみに、aossはOSSにALSAの皮を被せて排他ロックしないようにするだけなので、dmixは別途設定が必要。自分の.asoundrcは最終的にこんなの。色々調べては導入して消してを繰り替えした割にはあっさりとまとまった。
pcm.!default { type plug slave.pcm "dmixer" } #pcm.!plughw { # type plug # slave.pcm "dmixer" #} #pcm.dsp { # type plug # slave.pcm "dmixer" #} #pcm.dsp0 { # type plug # slave.pcm "dmixer" #} ctl.mixer0 { type hw card 0 } pcm.dmixer { type dmix ipc_key 1024 slave { pcm "hw:0,0" period_time 0 buffer_time 0 period_size 1024 buffer_size 8192 format S32_LE channels 2 rate 44100 } bindings { 0 0 1 1 } } ctl.dmixer { type hw card 0 }