如何等待直到音频剪辑被加载?
我是JAVA的新手,我正在尝试阅读一个剪辑。 这是我的代码:
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
public class TestClipBis {
protected static AudioFormat audioFormat;
public static void main(String[] args) throws Exception {
// Specification of the sound to play
// No control. We assume that the sound can be played on audio system
//File soundFile = new File("chimes.wav");
File soundFile = new File("test.wav");
InputStream is = new FileInputStream(soundFile);
InputStream bufferedIn = new BufferedInputStream(is);
//AudioInputStream sound = AudioSystem.getAudioInputStream(soundFile);
AudioInputStream sound = AudioSystem.getAudioInputStream(bufferedIn);
audioFormat = sound.getFormat();
System.out.println(audioFormat);
// Loading the sound into the memory (a Clip)
DataLine.Info info = new DataLine.Info(Clip.class, sound.getFormat());
System.out.println(info);
//Clip clip = (Clip) AudioSystem.getClip();
Clip clip = (Clip) AudioSystem.getLine(info);
System.out.println("Sound frame lenght : "+sound.getFrameLength());
System.out.println("Clip FrameLength before opening : "+clip.getFrameLength());
System.out.println("Clip will open - "+info);
System.out.println("Info format : "+info.getLineClass());
// Check before this line that everything is in memory
// Yes, but how ?
clip.open(sound);
System.out.println("Clip is open");
System.out.println("Clip FrameLength after opening : "+clip.getFrameLength());
// Due to a bug in Java Sound,
// we explicitly out of the VM when the sounds stop
clip.addLineListener(new LineListener() {
public void update(LineEvent event) {
if (event.getType() == LineEvent.Type.STOP) {
System.out.println("Methode de sortie");
event.getLine().close();
System.exit(0);
}
}
});
// Playing the clip
clip.start();
System.out.println("IsActive : "+clip.isActive());
//clip.close();
}
}
我的问题是如何确保剪辑在打开和播放之前加载到内存中? 有了上面的代码,当我打开并播放声音文件时,我有几秒钟的播放时间,但是从来没有相同的长度,随机播放,也没有完整的歌曲。
或者我应该使用别的而不是剪辑来播放歌曲? 但我想要“移动”到歌曲中,而不是从开始到结束流动。
编辑:
好的,我尝试了几件事情。 首先,我试着看看是否写了“ByteArrayOuptputStream”。 我在循环中有一个“println”,是的,所有的都写了,但它不能解决问题。
然后,当我打开剪辑时,我尝试添加参数:audioformat,bytearray,startpoint,bufferlength。 没有什么比......好。
然后,我注意到,当声音停止时,使用退出方法。 所以,我试图“静音”该方法(带有评论标志)。 结果是不同的:它读取文件,但声音不稳定。 当我检查CPU使用率时,它大约为100%。 这是猜测什么问题的第一个线索吗?
我试着做一个循环,指出开始后的帧位置:所有的帧都被读取,但声音仍然不稳定。 我也在启动方法之前和之后尝试了thead.sleep:没有什么更好的了。
所以,这里是我使用的代码。 许多代码部分在“评论引用”之间,因为它们是尝试,不成功......
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
public class TestBufferedClip {
protected static AudioFormat audioFormat;
public static ByteBuffer buffer;
public static void main(String[] args) throws Exception {
// Specification of the sound to play
// No control. We assume that the sound can be played on audio system
//File soundFile = new File("chimes.wav");
File soundFile = new File("test.wav");
FileInputStream fis = new FileInputStream(soundFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream((int)soundFile.length());
byte[] buf = new byte[1024];
int n = 0;
int loop = 0;
while ((n=fis.read(buf))>=0) {
baos.write(buf);
buf=new byte[1024];
System.out.println("Loop = "+loop);
loop+=1;
}
byte[] ba = baos.toByteArray();
System.out.println("ByteArray size : "+ba.length);
ByteArrayInputStream bais = new ByteArrayInputStream(ba);
//AudioInputStream sound = AudioSystem.getAudioInputStream(soundFile);
AudioInputStream sound = AudioSystem.getAudioInputStream(bais);
audioFormat = sound.getFormat();
System.out.println(audioFormat);
// Loading the sound into the memory (a Clip)
DataLine.Info info = new DataLine.Info(Clip.class, sound.getFormat());
System.out.println("Info :"+info);
//Clip clip = (Clip) AudioSystem.getClip();
Clip clip = (Clip) AudioSystem.getLine(info);
System.out.println("Sound frame lenght : "+sound.getFrameLength());
System.out.println("Info format : "+info.getLineClass());
// Check before this line that everything is in memory
// Yes, but how ?
clip.open(audioFormat, ba, 0, ba.length);
//clip.open(sound);
System.out.println("Clip is open");
System.out.println("Clip FrameLength after opening : "+clip.getFrameLength());
// Due to a bug in Java Sound,
// we explicitly out of the VM when the sounds stop
/*
clip.addLineListener(new LineListener() {
public void update(LineEvent event) {
if (event.getType() == LineEvent.Type.STOP) {
System.out.println("Methode de sortie");
event.getLine().close();
System.exit(0);
}
}
});
*/
// Playing the clip
System.out.println("Before thread sleep");
try {
Thread.sleep(31000);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("After thread sleep");
clip.start();
System.out.println("IsActive : "+clip.isActive());
/*
while (clip.isActive()==true) {
System.out.println("Position = "+clip.getFramePosition());
}
*/
//clip.close();
}
}
@Phil Freihofner:
我想到你的解决方案来读取和丢弃数据,直到我达到我的“开始”点。 你写道:“为了从音频文件中的某一点开始,使用SourceDataLine,你必须从音频输入线读取和丢弃数据,直到你到达想要的起始点”。 你是怎样做的 ? 当我使用“SourceDataLine”方法时,我的start方法是一个带有line.write(bytes,0,bytesreads)的循环; 将声音指向扬声器。
那么,你如何阅读并丢弃? 该行没有找到任何“读取”方法。
javax.sound.sampled支持两个用于播放音频的对象。 您正在使用的剪辑必须在播放之前完全加载到内存中。 从好的一面来看,也可以很容易地将回放定位在剪辑内,或者使用微秒或帧位置。
我没有看到首先将声音加载到字节缓冲区中的好处。 这是剪辑缓冲的冗余级别。 如果你想要做DSP或者其他需要获取数据的东西,那么我只会这样做,除了Clip内置的设置起点的能力外。
如果您可以预先选择可能的音频选项作为剪辑,然后再选择它们,这可能是最佳解决方案,只要您没有耗尽RAM。
另一个播放选项是SourceDataLine。 它以每个缓冲区为基础读取并回放文件。 因此,它可以比卸载的Clip更快地启动(无需首先将整个文件加载到内存中)。 但是,一旦剪辑被预加载,剪辑将播放而不必重复文件加载。
为了从音频文件中的某一点开始,使用SourceDataLine,您必须从音频输入行读取并丢弃数据,直到您到达所需的起始点。 您可以通过计算帧数来完成此操作(格式会告诉您每帧的字节数)。 这种读取和丢弃会中断一点时间,但我的经验是,读取和丢弃音频数据的速度比播放快几个数量级,因为播放涉及阻塞队列以保持输出为所需的帧速率。
查看Java Sound Tutorials了解更多信息,其中包括Clip和SourceDataLine API的链接。
这是一个加载剪辑的例子:
File soundFile = new File("test.wav");
AudioInputStream sound = AudioSystem.getAudioInputStream(soundFile);
DataLine.Info info = new DataLine.Info(Clip.class, sound.getFormat());
Clip clip = (Clip) AudioSystem.getLine(info);
clip.open(sound);
test.wav的数据现在应该放在RAM中。 所有其他你使用字节缓冲区和缓冲行的东西都是不必要的。
准备好播放时,使用clip.setMicrosecondPosition(长毫秒)将声音设置为从所需位置开始(如果您从头开始播放,则不需要),除非您已播放剪辑,在这种情况下,位置将会是你停下来的地方)。 然后使用clip.start()开始播放。
重要提示:如果运行它的程序退出,播放将提前结束。 测试这种方法的一种方法是在clip.start()之后放置一个Thread.sleep(long milliseconds)命令,其中毫秒的值比剪辑的长度更长。 但这不是真正的解决方案,只是一种防止程序关闭剪辑回放线程的诊断。 您应该处理让程序从主线程运行,而不是线程与音频回放。
首先读取整个字节缓冲区。 通过将文件中的所有内容复制到ByteArrayOutputStream
。 这样你就可以将整个媒体内容存储在内存中。 现在,您可以将ByteArrayOutputStream.toByteArray()
数组包装到ByteArrayInputStream
并将该纯内存流作为音频输入流提供。