前提・実現したいこと
javax.sound.sampled.Clipを用いて、2秒程度の音を0.2秒間隔程度で連続して鳴らしたいと考えています。
今までは、音を再生する度に異なるClipを用意していたのですが、Clipは自動でメモリ解放されないことを知り、現在は、音を再生し終えたClipを自動でメモリ解放close()
する方法を模索中です。
発生している問題・エラーメッセージ
下記のソースコードのコンストラクタにあるように、2つのデバッグ用コメント
System.out.println("timer stopped and clip closed");
System.out.println("timer and clip started");
を用意し、プログラムの動作を確認しました。期待していた結果は
- 音を再生した直後に
timer and clip started
と出力され - およそ2秒後に
timer stopped and clip closed
と出力される
でしたが、実際に実行すると、
- 8回ほど連続して
timer and clip started
が出力され - その後5秒ほどアプリがフリーズした後
- 8つの
timer stopped and clip closed
がまとめて出力される
となりました。
使い終えたClipのメモリ解放は自動で行われているようでしたが、アプリが時折フリーズしてしまうことに悩まされています。
該当のソースコード
MusicPlayer.java
Java
1import java.util.Timer; 2import java.util.TimerTask; 3import java.io.File; 4import javax.sound.sampled.AudioFormat; 5import javax.sound.sampled.AudioInputStream; 6import javax.sound.sampled.AudioSystem; 7import javax.sound.sampled.DataLine; 8import javax.sound.sampled.Clip; 9 10// Exceptions 11import java.io.IOException; 12import javax.sound.sampled.LineUnavailableException; 13import javax.sound.sampled.UnsupportedAudioFileException; 14 15public class MusicPlayer 16{ 17 private Clip p; 18 private Timer timer; 19 20 public MusicPlayer(String filePath) 21 { 22 p = LoadClipPlayer(filePath); 23 p.start(); 24 25 timer = new Timer(true); 26 TimerTask timerTask = new TimerTask() 27 { 28 public void run() 29 { 30 p.stop(); 31 p.close(); 32 System.out.println("timer stopped and clip closed"); 33 timer.cancel(); 34 } 35 }; 36 timer.schedule(timerTask, (int)(p.getMicrosecondLength() / 1000)); 37 38 System.out.println("timer and clip started"); 39 } 40 41 private static Clip LoadClipPlayer(String filePath) { 42 43 Clip clip = null; 44 45 try { 46 AudioInputStream stream = AudioSystem.getAudioInputStream(new File(filePath)); 47 AudioFormat format = stream.getFormat(); 48 DataLine.Info dataLine = new DataLine.Info(Clip.class, format); 49 clip = (Clip)AudioSystem.getLine(dataLine); 50 clip.open(stream); 51 } 52 catch(UnsupportedAudioFileException e) { 53 e.printStackTrace(); 54 } 55 catch(LineUnavailableException e) { 56 e.printStackTrace(); 57 } 58 catch(IOException e) { 59 e.printStackTrace(); 60 } 61 62 return clip; 63 } 64}
MusicPlayerのインスタンスはMusicPlayerManagerクラスで生成され、このクラスのArrayListに格納・保持されています。
MusicPlayerManager.java
Java
1import java.util.ArrayList; 2import java.util.Queue; 3import java.util.ArrayDeque; 4import javax.sound.sampled.Clip; 5 6public class MusicPlayerManager { 7 8 private static ArrayList<String> filePathList; 9 private static ArrayList<MusicPlayer> musicPlayers; 10 private static RhythmManager rhythmManager; 11 12 // 何番にPlay指示が出たかを一時保管し、リズムに合わせて処理するためのバッファ 13 private static Queue<Integer> orderBuffer; 14 15 public MusicPlayerManager() { 16 17 filePathList = new ArrayList<String>(); 18 musicPlayers = new ArrayList<MusicPlayer>(); 19 rhythmManager = new RhythmManager(150); 20 orderBuffer = new ArrayDeque<>(); 21 } 22 23 public MusicPlayerManager(String[] filePath) { 24 25 this(); 26 for(String filePath_ : filePath) { 27 filePathList.add(filePath_); 28 } 29 } 30 31 public MusicPlayerManager(ArrayList<String> filePath) { 32 33 this(); 34 for(String filePath_ : filePath) { 35 filePathList.add(filePath_); 36 } 37 } 38 39 public void Play(int fileNum) { 40 41 // 処理待機バッファ内のfileNumの重複を避ける 42 if(!orderBuffer.contains(fileNum)) 43 orderBuffer.add(fileNum); 44 } 45 46 public void AddFile(String filePath) { 47 48 filePathList.add(filePath); 49 } 50 51 public int GetFileTotalNum() { 52 53 return filePathList.size(); 54 } 55 56 // RhythmManagerに呼び出されるメソッド 57 // リズムに合わせて一定間隔で呼び出される 58 public static void PlayAllInOrderBuffer() 59 { 60 // キューの先頭を取得し削除しながらループする 61 while(orderBuffer.peek() != null) 62 { 63 int buf = orderBuffer.poll(); 64 musicPlayers.add(new MusicPlayer(filePathList.get(buf))); 65 } 66 } 67}
試したこと
timer.schedule(timerTask, (int)(p.getMicrosecondLength() / 1000))
の部分をtimer.scheduleAtFixedRate(timerTask, (int)(p.getMicrosecondLength() / 1000), 1000)
(第3引数の1000は適当)としてみましたが、動作は大差ありませんでした。- javax.swing.TimerとActionListenerを用いて同様のプログラムを作成しましたが、動作は大差ありませんでした。
- m-oguraさんの助言を受け、try-with-resourcesを用いてMusicPlayer.javaを下のように修正しましたが、タスクマネージャーでOpenJDK Platform binaryアプリ(起動中のアプリ)のメモリを監視したところ、音が鳴るたびにメモリ量は増え続けており、ストリームはclose出来ていないようでした。
Java
1import java.io.File; 2import javax.sound.sampled.AudioFormat; 3import javax.sound.sampled.AudioInputStream; 4import javax.sound.sampled.AudioSystem; 5import javax.sound.sampled.DataLine; 6import javax.sound.sampled.Clip; 7 8// Exceptions 9import java.io.IOException; 10import javax.sound.sampled.LineUnavailableException; 11import javax.sound.sampled.UnsupportedAudioFileException; 12 13public class MusicPlayer 14{ 15 public MusicPlayer(String filePath) 16 { 17 LoadClipPlayer(filePath); 18 } 19 20 private static void LoadClipPlayer(String filePath) { 21 22 try(AudioInputStream stream = AudioSystem.getAudioInputStream(new File(filePath));) 23 { 24 AudioFormat format = stream.getFormat(); 25 DataLine.Info dataLine = new DataLine.Info(Clip.class, format); 26 Clip clip = (Clip)AudioSystem.getLine(dataLine); 27 clip.open(stream); 28 clip.start(); 29 } 30 catch(UnsupportedAudioFileException e) { 31 e.printStackTrace(); 32 } 33 catch(LineUnavailableException e) { 34 e.printStackTrace(); 35 } 36 catch(IOException e) { 37 e.printStackTrace(); 38 } 39 } 40}
補足情報(FW/ツールのバージョンなど)
バージョンは以下の通りです。
>javac --version javac 15 >java --version openjdk 15 2020-09-15 OpenJDK Runtime Environment (build 15+36-1562) OpenJDK 64-Bit Server VM (build 15+36-1562, mixed mode, sharing)
どうぞよろしくお願いいたします。
回答5件
あなたの回答
tips
プレビュー