Java音频播放,因为必须依赖到本地环境,所以JAVA在音频处理方面优势不大,或者说打从Java体系开发时就没太多的考虑音频播放因素,要知道最早的Java 1.1版本中,没有后来的javax.sound包,音频只能通过Applet包调取……
遗憾的是,在图形程序开发中,我们的程序却又难免要使用到背景音乐、效果音等配合图像操作,哎,这实在是Sun大神给我们开的一个不打不小的玩笑。万幸后来Sun大神开眼,提供了javax.sound包,才解救我们于水深火热当中~
但是继之而来的问题是,在javax.sound包的使用中,如同Java多媒体工具类的通病般,并没有提供十分完善的释放机制。如果我们做Windows开发,调用MediaPlayer反复N次可能没也什么大碍,但在Java中,如果音频程序反复运行的话,极容易出现内存累计损耗的情况,以至于最后抛出一个java.lang.OutOfMemoryError,然后……程序就挂了,用户就傻了,我们就疯了……
这已经是“是可忍孰不可忍”的问题了,有鉴于此,所以在本人的Loonframework框架开发中,二次整合了sound下的相关方法,力求以最简单的代码,做出最完善的音频控制类。在Loonframework-game还没有大成的现在,先摘录一部分方法,以供各位看官——拍砖!
对应网络资源调用,在Loonframework中建立了自己的uri用类,基本内容如下:
(其中StreamHelper为Loonframework自己的流媒体控制类,getHttpStream方法请自行替换。)
packageorg.loon.framework.game.net;

importorg.loon.framework.game.helper.StreamHelper;


/***//**
*<p>
*Title:LoonFramework
*</p>
*<p>
*Description:Loonframework专用uri(统一资源标识符)
*</p>
*<p>
*Copyright:Copyright(c)2007
*</p>
*<p>
*Company:LoonFramework
*</p>
*
*@authorchenpeng
*@email:ceponline@yahoo.com.cn
*@version0.1
*/

publicclassURI...{

//传输协议类型
publicstaticfinalint_L_URI_HTTP=1;

publicstaticfinalint_L_URI_UDP=2;

privateString_uri;

privateint_type;


/***//**
*析构函数,用于注入uri和type
*
*@paramuri
*@paramtype
*/

publicURI(Stringuri,inttype)...{
_uri=newString(uri);
_type=type;
}


/***//**
*析构函数,用于注入uri
*
*@paramuri
*/

publicURI(Stringuri)...{
_uri=newString(uri);
_type=URI._L_URI_HTTP;
}


/***//**
*返回uri所在位置资源的byte数组。
*
*@return
*/

publicbyte[]getData()...{

if(_uri==null)...{
returnnull;
}
returnStreamHelper.getHttpStream(_uri);
}


publicStringgetURI()...{
return_uri;
}


publicintgetType()...{
return_type;
}

}
在Loonframework框架中,定制了一个基础的SoundData类,用以统一管理音频数据源。
packageorg.loon.framework.game.sound;

importorg.loon.framework.game.helper.StreamHelper;
importorg.loon.framework.game.net.URI;


/***//**
*<p>
*Title:LoonFramework
*</p>
*<p>
*Description:用以获得并缓存声音文件数据(更进一步内容操作请见Loonframework-game框架)
*</p>
*<p>
*Copyright:Copyright(c)2007
*</p>
*<p>
*Company:LoonFramework
*</p>
*
*@authorchenpeng
*@email:ceponline@yahoo.com.cn
*@version0.1
*/

publicclassSoundData...{

privatebyte[]_data;

privateboolean_loop;

privateint_type;

publicstaticfinalint_L_SOUNDTYPE_MIDI=1;

publicstaticfinalint_L_SOUNDTYPE_WAV=2;


/***//**
*析构函数,用以注入uri,type,loop
*
*@paramuri
*@paramtype
*@paramloop
*/

publicSoundData(URIuri,inttype,booleanloop)...{

if(uri!=null)...{
_data=uri.getData();
}
_type=type;
_loop=loop;
}


/***//**
*析构函数,用以注入data,type,loop
*
*@paramdata
*@paramtype
*@paramloop
*/

publicSoundData(byte[]data,inttype,booleanloop)...{


if(data!=null&&data.length>0)...{
_data=newbyte[data.length];
//直接copybyte数组
System.arraycopy(data,0,_data,0,_data.length);
}
_type=type;
_loop=loop;
}


/***//**
*析构函数,用以注入限定位置的resName,type,loop
*@paramresName
*@paramtype
*@paramloop
*/

publicSoundData(StringresName,inttype,booleanloop)...{
this(StreamHelper.GetDataSource(resName),type,loop);
}


publicbyte[]getData()...{
return_data;
}


publicbooleangetLoop()...{
return_loop;
}


publicvoidsetLoop(booleanloop)...{
_loop=loop;
}


publicintgetType()...{
return_type;
}

}
Loonframework将音频播放相关方法,封装与SoundPlay之中,程序员可以不必理会javax.sound内部细节,而直接调用SoundPlay完成相关操作。
packageorg.loon.framework.game.sound;

importjava.io.ByteArrayInputStream;

importjavax.sound.midi.MetaEventListener;
importjavax.sound.midi.MetaMessage;
importjavax.sound.midi.MidiSystem;
importjavax.sound.midi.Sequence;
importjavax.sound.midi.Sequencer;
importjavax.sound.sampled.AudioFileFormat;
importjavax.sound.sampled.AudioSystem;
importjavax.sound.sampled.Clip;
importjavax.sound.sampled.DataLine;

importorg.loon.framework.game.net.URI;


/***//**
*<p>
*Title:LoonFramework
*</p>
*<p>
*Description:用以进行声音文件操作(仅为Loonframework中部分方法,更详细请参见Loonframework-game框架)
*</p>
*<p>
*Copyright:Copyright(c)2007
*</p>
*<p>
*Company:LoonFramework
*</p>
*
*@authorchenpeng
*@email:ceponline@yahoo.com.cn
*@version0.1
*/

publicclassSoundPlayimplementsMetaEventListener,Runnable...{

privateint_sleepTime;

privateClip_audio;

privateSequencer_midi;

privateboolean_loop;

privateint_soundType;

privateboolean_playing;

privateThread_thread=null;

privateboolean_isRun=false;


/***//**
*析构函数,初始化SoundPlay
*
*/

publicSoundPlay()...{

_loop=false;
_soundType=0;
_sleepTime=1000;
_playing=false;

}

//载入声音文件

publicbooleanload(SoundDatadata)...{
reset();

if(data==null||data.getData()==null)...{
returnfalse;
}
returninit(data.getData(),data.getType(),data.getLoop());
}


/***//**
*直接播放url文件
*
*@paramuri
*@paramftype
*@paramloop
*@return
*/

publicbooleanload(URIuri,intftype,booleanloop)...{

//刷新数据
reset();

if(uri==null)...{
returnfalse;
}
//获得SoundData
SoundDatadata=newSoundData(uri,ftype,loop);

if(data==null||data.getData()==null)...{
returnfalse;
}
returninit(data.getData(),data.getType(),data.getLoop());

}


/***//**
*初始化sound相关数据
*
*@paramdata
*@paramftype
*@paramloop
*@return
*/

privatebooleaninit(byte[]data,intftype,booleanloop)...{
booleanresult=false;

ByteArrayInputStreambis=null;


try...{
bis=newByteArrayInputStream(data);

}catch(Exceptione)...{
bis=null;
}


if(bis==null)...{
returnfalse;
}

//判断类型

switch(ftype)...{

//MIDI
caseSoundData._L_SOUNDTYPE_MIDI:

//当MIDI不存在时

if(_midi==null)...{


try...{
//获得Sequencer
_midi=MidiSystem.getSequencer();
_midi.open();


}catch(Exceptionex)...{
_midi=null;
}


if(_midi!=null)...{
_midi.addMetaEventListener(this);
}

}

//当MIDI依旧未获得时

if(_midi!=null)...{
//重新创建Sequence
Sequencesc=null;


try...{
sc=MidiSystem.getSequence(bis);

}catch(Exceptione)...{
sc=null;
}


if(sc!=null)...{


try...{

_midi.setSequence(sc);

//获得是否循环播放
_loop=loop;

//获得是否载入
result=true;


}catch(Exceptionee)...{
}

//获得声音类型
_soundType=SoundData._L_SOUNDTYPE_MIDI;

}

}


try...{
bis.close();

}catch(Exceptionee)...{
}

break;

//Wav
caseSoundData._L_SOUNDTYPE_WAV:

AudioFileFormattype=null;

//获得Audio

try...{
type=AudioSystem.getAudioFileFormat(bis);

}catch(Exceptione)...{
type=null;
}

//关闭流

try...{
bis.close();

}catch(Exceptionex)...{
}


if(type==null)...{
returnfalse;
}

//根据指定信息构造数据行的信息对象
DataLine.Infodi=newDataLine.Info(Clip.class,type.getFormat());

//转为Clip

try...{
_audio=(Clip)AudioSystem.getLine(di);

}catch(Exceptione)...{
}

//播放文件

try...{

_audio.open(type.getFormat(),data,0,data.length);

_loop=loop;

result=true;


}catch(Exceptione)...{
}

//获得文件类型
_soundType=SoundData._L_SOUNDTYPE_WAV;

break;

}

returnresult;
}


publicbooleanplay(SoundDatadata)...{


if(!load(data))...{
returnfalse;
}

returnplay();

}


publicbooleanplay()...{


switch(_soundType)...{

caseSoundData._L_SOUNDTYPE_MIDI:


try...{

_midi.start();

_playing=true;

_soundType=SoundData._L_SOUNDTYPE_MIDI;


}catch(Exceptionee)...{
}

break;

caseSoundData._L_SOUNDTYPE_WAV:


if(_audio!=null)...{


if(_loop)...{

//设定循环
_audio.setLoopPoints(0,-1);
_audio.setFramePosition(0);

_audio.loop(Clip.LOOP_CONTINUOUSLY);


}else...{

//强制设定播放位置至0
_audio.setFramePosition(0);

_audio.start();

}

_playing=true;

}

break;

}

return_playing;

}


/***//**
*自动播放,循环停止后结束。
*
*@paramdata
*@return
*/

publicbooleanAutoPlay(SoundDatadata)...{

if(!load(data))...{
returnfalse;
}
returnAutoPlay();
}


/***//**
*自动播放,循环停止后结束。
*
*@return
*/

publicbooleanAutoPlay()...{
_isRun=true;
_thread=newThread(this);
_thread.start();
return_playing;
}


/***//**
*停止播放
*/

publicvoidstop()...{


if(_audio!=null&&_audio.isActive())...{

try...{
_audio.stop();

}catch(Exceptione)...{
}
}


if(_midi!=null)...{
_midi.stop();
}
_playing=false;
_isRun=false;
}


/***//**
*释放数据
*
*/

publicvoidreset()...{

stop();

_loop=false;
_soundType=0;


if(_midi!=null)...{

_midi.close();

_midi=null;

}


if(_audio!=null&&_audio.isOpen())...{

_audio.close();

_audio=null;

}
_isRun=false;
_thread=null;
}


/***//**
*设定MetaMessage
*/

publicvoidmeta(MetaMessagemeta)...{
//判断是否循环播放MIDI
if(_loop&&_soundType==SoundData._L_SOUNDTYPE_MIDI

&&meta.getType()==47)...{


if(_midi!=null&&_midi.isOpen())...{
_midi.setMicrosecondPosition(0);
_midi.start();

}
}

}


publicvoidrun()...{

while(_isRun)...{
play();
//因为播放类型唯一,所以只会返回一个_playing结果,以此判定。

if(_midi!=null)...{
_playing=_midi.isRunning();
}

if(_audio!=null)...{
_playing=_audio.isRunning();
}
//当播放停止

if(!_playing)...{
//释放
reset();
}

try...{
Thread.sleep(_sleepTime);

}catch(InterruptedExceptione)...{
e.printStackTrace();
}
}
}


publicintgetSleepTime()...{
return_sleepTime;
}


/***//**
*设定AutoPlay线程循环时间。
*
*@paramtime
*/

publicvoidsetSleepTime(inttime)...{
_sleepTime=time;
}
}
这时我们需要面对的,仅是封装为实体的SoundData数据和SoundPlay操作,而不必和繁复的javax.sound再打交道。
调用方法如下:
packageorg.test;

importorg.loon.framework.game.helper.StreamHelper;
importorg.loon.framework.game.net.URI;
importorg.loon.framework.game.sound.SoundData;
importorg.loon.framework.game.sound.SoundPlay;


/***//**
*<p>Title:LoonFramework</p>
*<p>Description:SoundPlay播放测试</p>
*<p>Copyright:Copyright(c)2007</p>
*<p>Company:LoonFramework</p>
*@authorchenpeng
*@email:ceponline@yahoo.com.cn
*@version0.1
*/

publicclassSoundPlayTest...{


staticvoidselectPlay(intftype)...{
SoundDatadata=null;


switch(ftype)...{
//通过loonframework下uri从网络播放音乐
case0:
data=newSoundData(newURI("http://looframework.sourceforge.net/midi/谁是大英雄.mid"),SoundData._L_SOUNDTYPE_MIDI,false);
break;
//通过本地资源下音乐文件的byte[]对象播放音乐
case1:
byte[]bytes=StreamHelper.GetResourceData("/midi/谁是大英雄.mid");
data=newSoundData(bytes,SoundData._L_SOUNDTYPE_MIDI,false);
break;
//通过音乐文件路径播放音乐
case2:
data=newSoundData("C:/谁是大英雄.mid",SoundData._L_SOUNDTYPE_MIDI,false);
break;
}
SoundPlayplay=newSoundPlay();
//AutoPlay与Play方法的区别在于,AutoPlay播放完毕会自动停止并释放资源,play需手动中止。
//play.play(data);
play.AutoPlay(data);
}


publicstaticvoidmain(String[]args)...{
selectPlay(2);
}

}

更详细方法,会待Loonframework-game完全公布后,再进行解释。
另:由于StreamHelper关联其他Loonframework中方法,暂不给出,inputStream转byte[]可用如下写法:
//is为获得的inputStream

ByteArrayOutputStreambyteArrayOutputStream=newByteArrayOutputStream();
//用于承接byte[]
byte[]arrayByte=null;

try...{
//每次传输大小为4096
byte[]bytes=newbyte[4096];
bytes=newbyte[is.available()];
intread;

while((read=is.read(bytes))>=0)...{
byteArrayOutputStream.write(bytes,0,read);
}
arrayByte=byteArrayOutputStream.toByteArray();

}catch(IOExceptione)...{
returnnull;

}finally...{

try...{

if(byteArrayOutputStream!=null)...{
byteArrayOutputStream.close();
byteArrayOutputStream=null;
}

if(is!=null)...{
is.close();
is=null;
}


}catch(IOExceptione)...{
}
}
遗憾的是,在图形程序开发中,我们的程序却又难免要使用到背景音乐、效果音等配合图像操作,哎,这实在是Sun大神给我们开的一个不打不小的玩笑。万幸后来Sun大神开眼,提供了javax.sound包,才解救我们于水深火热当中~
但是继之而来的问题是,在javax.sound包的使用中,如同Java多媒体工具类的通病般,并没有提供十分完善的释放机制。如果我们做Windows开发,调用MediaPlayer反复N次可能没也什么大碍,但在Java中,如果音频程序反复运行的话,极容易出现内存累计损耗的情况,以至于最后抛出一个java.lang.OutOfMemoryError,然后……程序就挂了,用户就傻了,我们就疯了……
这已经是“是可忍孰不可忍”的问题了,有鉴于此,所以在本人的Loonframework框架开发中,二次整合了sound下的相关方法,力求以最简单的代码,做出最完善的音频控制类。在Loonframework-game还没有大成的现在,先摘录一部分方法,以供各位看官——拍砖!
对应网络资源调用,在Loonframework中建立了自己的uri用类,基本内容如下:
(其中StreamHelper为Loonframework自己的流媒体控制类,getHttpStream方法请自行替换。)























































































在Loonframework框架中,定制了一个基础的SoundData类,用以统一管理音频数据源。














































































































Loonframework将音频播放相关方法,封装与SoundPlay之中,程序员可以不必理会javax.sound内部细节,而直接调用SoundPlay完成相关操作。























































































































































































































































































































































































































































































































这时我们需要面对的,仅是封装为实体的SoundData数据和SoundPlay操作,而不必和繁复的javax.sound再打交道。
调用方法如下:






















































更详细方法,会待Loonframework-game完全公布后,再进行解释。
另:由于StreamHelper关联其他Loonframework中方法,暂不给出,inputStream转byte[]可用如下写法:





































