2015-03-02 55 views
0

我是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%。这是猜测什么问题的第一个线索吗?

我试着做了一个循环,指出开始后的frameposition:所有的帧被读取,但声音仍然是生涩的。 我也尝试了启动方法之前和之后的thead.sleep:没有比这更好的了。

所以,这里是我使用的代码。许多代码部分是“注释引号”之间,因为他们尝试,unsuccessfull ...

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)的循环;将声音指向扬声器。
那么,你如何阅读和丢弃?该行没有找到任何“读取”方法。

+0

声音数据可以逐渐从AudioInputStream读取?当你以这种方式读取数据时,只需将它重复读入一个变量(重写),直到达到所需的起点。然后才开始将数据从AIS传送到SourceDataLine写入命令。在这个链接的Java教程中有一个AudioInputStream - > SourceDataLine的代码示例。 http://docs.oracle.com/javase/tutorial/sound/converters.html搜索短语“做一些有用的事情”,它应该让你成为一个很好的代码示例(页面上的第一个完整的代码示例)的评论。 – 2015-03-05 22:59:22

+0

太棒了!你的解决方案是完美的我会放弃audioclip的解决方案。当我在Linux上使用openJDK时,这似乎太复杂了。你的解决方案运作得非常好。所以谢谢 ! – 2015-03-06 16:48:56

回答

0

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)命令,其中毫秒的值比剪辑的长度更长。但这不是真正的解决方案,只是一种防止程序关闭剪辑回放线程的诊断。您应该处理让程序从主线程运行,而不是线程与音频回放。

+0

我尝试使用该剪辑(请参阅我发布的firt代码),但似乎在开始播放之前不会预加载整个剪辑。因此,它开始播放声音,并在几秒钟后停止播放(通常为1到10秒,随机)。我在那个地方阅读(http://stackoverflow.com/questions/21874361/clip-plays-wav-file-with-bad-lag-in-java),我的问题可能是剪辑必须加载到内存在它开始之前。是的,好的...但是怎么样? – 2015-03-03 18:44:36

+0

我在主要答案中添加了有关正确打开剪辑的信息(这是预加载步骤)。 – 2015-03-05 23:03:41

0

首先读取整个字节缓冲区。通过将文件中的所有内容复制到ByteArrayOutputStream来完成此操作。这样你就可以将整个媒体内容存储在内存中。现在您可以将ByteArrayOutputStream.toByteArray()中的数组包装到ByteArrayInputStream中,并将纯内存流作为音频输入流提供。