最近沒有系統學習的計劃,看了開源的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
因多媒體方向非自己的工作方向,所以了解的相當膚淺。有興趣的朋友請多多指教。