繁体   English   中英

如何等待音频剪辑加载完毕?

[英]How to wait till an audio clip is loaded?

我是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”方法时,我的启动方法是一个带有line.write(bytes,0,bytesreads);的循环。 将声音指向扬声器。
那么,您如何阅读和丢弃? 我在该行中未找到任何“读取”方法。

javax.sound.sampled支持两个对象来播放音频。 您正在使用的剪辑必须先完全加载到内存中,然后才能播放。 从正面看,也可以使用微秒或帧位置将回放定位为从剪辑内部开始。

我认为先将声音加载到字节缓冲区中没有任何好处。 这是Clips的冗余缓冲级别。 仅当您尝试执行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,在这种情况下,位置将是您停止时的位置)。 然后使用clip.start()开始播放。

重要说明:如果运行播放的程序退出,播放将提前结束。 一种测试方法是在clip.start()后面放置一个Thread.sleep(long milliseconds)命令,其中毫秒的值比剪辑的长度长。 但这不是真正的解决方案,仅是一种诊断程序,可以防止程序关闭剪辑播放线程。 您应该处理使程序从主线程运行,而不是从音频播放线程运行。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM