高效的Java音乐播放类

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方法请自行替换。)
package org.loon.framework.game.net;

import org.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
*/

public class URI ... {

//传输协议类型
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类,用以统一管理音频数据源。

package org.loon.framework.game.sound;

import org.loon.framework.game.helper.StreamHelper;
import org.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
*/

public class SoundData ... {

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完成相关操作。

package org.loon.framework.game.sound;

import java.io.ByteArrayInputStream;

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

import org.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
*/

public class SoundPlay implements MetaEventListener,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再打交道。

调用方法如下:

package org.test;

import org.loon.framework.game.helper.StreamHelper;
import org.loon.framework.game.net.URI;
import org.loon.framework.game.sound.SoundData;
import org.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
*/

public class SoundPlayTest ... {

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
= new ByteArrayOutputStream();
// 用于承接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)...{
}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值