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プラグインと衝突していて、先にfirefoxFlashを見ていると(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の実装がゴミなんだよ」位の流れしか無い。
ちなみに、aossOSSALSAの皮を被せて排他ロックしないようにするだけなので、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
}

*1:複数のソフトウェアからのサウンド入力をALSAがmixする機能