我在高中的入门课程中制作了这个音乐课程。您可以看到我从中获取代码的the source。
我最初使用的是Clip
,但发现它的缓冲区大小非常小,并且无法很好地播放长歌(至少在学校的Mac上没有)。新代码效果很好,据我所知,它正在获取歌曲的“块”,播放它们,然后获得更多的块。问题是,当音乐改变时,歌曲有时会重叠,当我退出游戏时,可能会发出可怕的声音(至少在Mac上),因为在游戏结束前数据流正在被切断。
除了每次延迟程序几秒钟之外,还有其他方法可以解决这个问题吗? (注意:我不想使用外部.jar或库,我想严格遵守JRE)
package mahaffeyfinalproject;
import java.io.File;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
public class Music implements Runnable{
//SOURCE: http://www.ntu.edu.sg/home/ehchua/programming/java/J8c_PlayingSound.html
//Note: I've modified it slightly
private String location;
private boolean play;
public void Music(){
}
public void playMusic(String loc) {
location = loc;
play = true;
try{
Thread t = new Thread(this);
t.start();
}catch(Exception e){
System.err.println("Could not start music thread");
}
}
public void run(){
SourceDataLine soundLine = null;
int BUFFER_SIZE = 64*1024;
try {
File soundFile = new File(location);
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(soundFile);
AudioFormat audioFormat = audioInputStream.getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
soundLine = (SourceDataLine) AudioSystem.getLine(info);
soundLine.open(audioFormat);
soundLine.start();
int nBytesRead = 0;
byte[] sampledData = new byte[BUFFER_SIZE];
while (nBytesRead != -1 && play == true) {
nBytesRead = audioInputStream.read(sampledData, 0, sampledData.length);
if (nBytesRead >= 0) {
soundLine.write(sampledData, 0, nBytesRead);
}
}
} catch (Exception e) {
System.err.println("Could not start music!");
}
soundLine.drain();
soundLine.close();
}
public void stop(){
play = false;
}
}
答案 0 :(得分:5)
我不确定你接近可怕的声音,但我想我已经知道如何解决你提到的重叠问题。看起来你对Sound API的使用很好,但我怀疑你可能遇到了并发问题。我注意到你为你演奏的每个声音开始一个新的主题。这是一个好主意,但如果你不希望你的歌曲混合,你需要小心。我的理论是你的代码看起来像这样:
Music songA = new Music();
Music songB = new Music();
//Start playing song A
songA.playMusic("path");
//Some stuff happens with your game...
//Some time later you want to change to song B
songA.stop();
songB.playMusic("other path");
乍一看,这看起来很好,毕竟你在开始歌曲B之前小心地停止了歌曲,对吧?不幸的是,当谈到并发时,很少有事情像我们希望的那样简单。让我们看看你的一小部分代码,特别是负责将数据输入音频流的while
循环......
//Inside run(), this is the interesting bit at the end
while(nBytesRead != -1 && play == true){
//Feed the audio stream while there is still data
}
stop()
将play
设置为false,导致while循环退出,完成当前迭代后。如果我没有弄错的话,while
循环的每次迭代都会将64k数据发送到音频流中。 64k是相当数量的音频数据,并且取决于音频属性将持续一段时间。你真正想要做的是通知线程退出(即设置播放为假),然后等待线程完成。我相信这将解决重叠问题,如果你没有正确地杀死你的线程,这个问题也可能导致戒掉令人难以置信的球拍。我谦卑地建议您对音乐课程进行以下更改(不保证正确性)......
public class Music implements Runnable{
private Thread t; //Make the thread object a class field
//Just about everything is the same as it was before...
public void stop(){
play = false;
try{
t.join()
}catch(InterruptedException e){
//Don't do anything, it doesn't matter
}
}
}
希望这会有所帮助,对于所有善良和纯洁的人来说,不要按原样使用我的代码,它会在大约2分钟内脱离我的头脑。我推荐The Java Tutorials' series on threading。