最近没有系统学习的计划,看了开源的YOYOPlayer(一个相当强大的播放器软件,基于java编写),心里痒痒,比较肤浅的学习下javasound。
javasound是比较小巧的底层api,引用网上的一幅 javasound体系结构图:
javasound实现: 目前使用最多的是 jdk中javax.sound组件 (包括javax.sound.sampled 和 javax.sound.midi包) 和 开源的 Tritonus(http://www.tritonus.org/)组件
SPI:是Service Provider Interface(服务提供接口),它的作用是以插件的形式提供音频扩展模块。比如,javax.sound组件中,只支持au,mid,wav等少许音频格式,其中不包括mp3。要想支持mp3格式,那么就需要相应的解码器和SPI。所幸的是,已经有相应的开源包。
javazoom(http://www.javazoom.net)下的jlayer,是mp3格式的解码包。
javazoom下的mp3spi就是spi。
只要添加了相应的开源组建包,可以在不修改任何一行代码的情况下,支持其他音频格式。这就是spi的魅力。
简单介绍了javasound的体系结构,下面就介绍一个简易的mp3播放器的实现(所谓简易,就是没有界面,除了能播放mp3等文件外,就啥都不能干了 :) )
几个步骤:
1)得到音频流:
AudioSystem.getAudioInputStream(input)
2)得到音频格式:
audioInputStream.getFormat()
3)初始化数据行信息
数据行信息包括(受数据行支持的音频格式;其内部缓冲区的最小和最大大小
)
DataLine.Info info = new DataLine.Info(SourceDataLine.class, decodedFormat);
4)从混频器获得源数据行
SourceDataLine
接口提供将音频数据写入数据行的缓冲区中的方法。播放或混合音频的应用程序应该以足够快的速度将数据写入源数据行,以防缓冲区下溢(排空),下溢可能导致单击时音频数据中出现可感知的间断
SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info);
5)将音频数据写入源数据行中
line.start();
int readLenth = 0;
while (readLenth != -1) {
readLenth = decodedAudioStream.read(AUDIO_BUFER, 0, AUDIO_BUFER.length);
if (readLenth != -1) {
line.write(AUDIO_BUFER, 0, readLenth);
}
}
line.drain();
line.stop();
line.close();
下面提供完整的简易mp3播放器代码:
其中
AudioPlayer仅仅是一个接口。
/**
* <pre>
* MP3播放器实现
* 线程不安全,没有必要多个线程去启动播放器
* </pre>
*
* @author Lee Jones
*/
public class Mp3Player implements AudioPlayer {
private static final Log log = LogFactory.getLog(Mp3Player.class);
private static final byte[] AUDIO_BUFER = new byte[4096];
private AudioInputStream audioStream;
private AudioFormat decodedFormat;
private AudioInputStream decodedAudioStream;
@Override
public void play(InputStream input) {
if (input == null) {
log.warn("input stream is null");
return;
}
try {
init(input);
play();
} catch (Exception e) {
log.error("play error:", e);
}
}
@Override
public void play(File file) {
if (file == null) {
log.warn("audio file is null");
return;
}
try {
play(new FileInputStream(file));
} catch (FileNotFoundException e) {
log.error("file to inputStream error:", e);
}
}
@Override
public void play(URL url) {
if (url == null) {
log.warn("url is null");
return;
}
try {
play(url.openStream());
} catch (IOException e) {
log.error("url open inputStream error:", e);
}
}
/**
* init
*
* @param input
* @throws UnsupportedAudioFileException
* @throws IOException
*/
protected void init(InputStream input) throws UnsupportedAudioFileException, IOException {
initAudioStream(input);
initDecodedFormat();
initDecodedAudioStream();
}
/**
* init audio input stream
*
* @param input:audio input stream
* @throws IOException
* @throws UnsupportedAudioFileException
*/
protected void initAudioStream(InputStream input) throws UnsupportedAudioFileException, IOException {
audioStream = AudioSystem.getAudioInputStream(input);
}
/**
* init decoded format
*
* @throws UnsupportedAudioFileException
* @throws IOException
*/
protected void initDecodedFormat() throws UnsupportedAudioFileException, IOException {
AudioFormat baseFormat = audioStream.getFormat();
decodedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, baseFormat.getSampleRate(), 16,
baseFormat.getChannels(), baseFormat.getChannels() * 2,
baseFormat.getSampleRate(), false);
}
/**
* init decoded audio stream
*/
protected void initDecodedAudioStream() {
decodedAudioStream = AudioSystem.getAudioInputStream(decodedFormat, audioStream);
}
/**
* get source data line
*
* @param input audio input stream
* @return
* @throws UnsupportedAudioFileException
* @throws IOException
* @throws LineUnavailableException
*/
protected SourceDataLine getSourceDataLine() throws UnsupportedAudioFileException, IOException,
LineUnavailableException {
DataLine.Info info = new DataLine.Info(SourceDataLine.class, decodedFormat);
SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info);
line.open(decodedFormat);
return line;
}
/**
* play audio
*
* @throws UnsupportedAudioFileException
* @throws IOException
* @throws LineUnavailableException
*/
protected void play() throws UnsupportedAudioFileException, IOException, LineUnavailableException {
SourceDataLine line = getSourceDataLine();
line.start();
int readLenth = 0;
while (readLenth != -1) {
readLenth = decodedAudioStream.read(AUDIO_BUFER, 0, AUDIO_BUFER.length);
if (readLenth != -1) {
line.write(AUDIO_BUFER, 0, readLenth);
}
}
line.drain();
line.stop();
line.close();
decodedAudioStream.close();
audioStream.close();
}
}
最后附上简易播放器:
easy mp3 player
启动方式:linux用户使用./mp3player.sh,windows用户使用mp3player.bat启动(需要说明的是,必须设置好JAVA_HOME和PATH,并且使用jdk5以上版本。因为启动脚本中的指定classpath的方法需要jdk5版本以上才支持。如果是jdk1.4下的用户,请修改启动脚本)
lib目录:是简易播放器的lib包支持
log目录:记录log,如果有错误等情况,可查看log。
conf目录:因为没有图形界面,所以需要把播放的mp3文件路径手工写入。支持两种形式,一个是本地mp3文件,另一个是网络mp3资源,分别写入到local.txt和net.txt文件中。
比如
local.txt
/home/jones/data/music/misc/xin_nian_hao.mp3
/home/jones/data/music/misc/guo_ge.mp3
net.txt
http://lubai.com.cn/CGI-BIN/shuanxing.mp3
http://lubai.com.cn/CGI-BIN/shuanxing.mp3
因多媒体方向非自己的工作方向,所以了解的相当肤浅。有兴趣的朋友请多多指教。