音声管理
- Javaでサウンド(WAVE)を再生する [2012-12-12]
- 強制終了(Application hang)対策 [2014-01-10]
Javaでサウンド(WAVE)を再生する
Java標準APIを使用する場合、以下の再生方法があるようだ
- SourceDataLine
- バッファを読み込みながら再生するため、長い音声や、合成をかけながら再生する場合に使用する。
- Clip
- Clipは短い音声に適しており、データをメモリ上に読み込んでおいてから再生する。
Windows環境へリモート接続中にアプリケーション起動した状態で「切断」(ログオフではない)を行うと、
AudioSystem.getLineメソッドでIllegalArugmentExceptionが発生する場合があった。
上記リモート接続時に、「リモートコンピュータサウンド」設定を「このコンピュータで聞く」に設定していたため、
「切断」ではアプリケーションが起動したままセッションが残ってしまうため、対応する音声ラインが見つからずIllegalArugmentExceptionとなっているようだ。
というわけで、検証するために、n秒後に音声を再生するアプリ作成・実行後、即「切断」し、再接続すると期待通りIllegalArugmentExceptionが発生していた。
「リモートコンピュータサウンド」設定を「再生しない」にした場合も上記と同様のIllegalArugmentExceptionが発生していた。
リモート上でも「動作保障」し、常に起動&自動音声再生するアプリケーションを作成する場合は、注意が必要そうだ。
- SourceDataLineから再生するパターン
-
package fomsan.ne.jp.example.sound; import java.io.File; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.LineEvent; import javax.sound.sampled.LineListener; import javax.sound.sampled.SourceDataLine; public class Play { public static void main(final String[] args) { class MyLineListener implements LineListener { @Override public void update(final LineEvent le) { final LineEvent.Type type = le.getType(); System.out.println(type); } } try { final AudioInputStream fis = AudioSystem.getAudioInputStream(new File("Sound.wav")); System.out.println("File AudioFormat: " + fis.getFormat()); final AudioInputStream ais = AudioSystem.getAudioInputStream( AudioFormat.Encoding.PCM_SIGNED, fis); final AudioFormat af = ais.getFormat(); System.out.println("AudioFormat: " + af.toString()); final int frameRate = (int) af.getFrameRate(); System.out.println("Frame Rate: " + frameRate); final int frameSize = af.getFrameSize(); System.out.println("Frame Size: " + frameSize); final SourceDataLine line = AudioSystem.getSourceDataLine(af); line.addLineListener(new MyLineListener()); line.open(af); final int bufSize = line.getBufferSize(); System.out.println("Buffer Size: " + bufSize); line.start(); final byte[] data = new byte[bufSize]; int bytesRead; while ((bytesRead = ais.read(data, 0, data.length)) != -1) { line.write(data, 0, bytesRead); } line.drain(); line.stop(); line.close(); } catch (final Exception e) { System.out.println(e); } } }
- Clipから再生するパターン
-
package fomsan.ne.jp.example.sound; import java.io.File; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; public class PlayClip { public static void main(final String[] args) { try { System.out.println("Sound Start"); final AudioInputStream ais = AudioSystem.getAudioInputStream(new File("Sound.wav")); ais.getFormat(); final Clip line = AudioSystem.getClip(); line.open(ais); line.start(); System.out.println("Playing..."); Thread.sleep(1); line.drain(); line.stop(); line.close(); System.out.println("Sound End"); } catch (final Exception e) { System.out.println(e); } } }
強制終了(Application hang)対策
- SourceDataLine drain() でハングする場合
-
Windows環境で音声再生時にアプリケーションがハングする問題が発生した。
原因は、EventDispatchスレッドのデッドロック(Java既知バグ)。
解決方法は、Javaを最新へ(1.6.45または1.7)へアップデートするしかないため、
drain()等のEventDipatchスレッド排他処理を呼ばないようにする。// ライン開始 line.start(); final int avail = line.available(); int bytesRead = 0; while (bytesRead != -1) { bytesRead = ais.read(bufData, 0, bufData.length); if (bytesRead != -1) { line.write(bufData, 0, bytesRead); } } // line.drain()を使用しない(ブロッキングが発生する) while (line.available() < avail) { Thread.sleep(WAIT_TIME); } // ラインを閉じる line.close();