实例解析使用Java实现基本的音频播放器的编写要点

网友投稿 188 2023-07-23


实例解析使用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;

/** *//**

*

* Title: LoonFramework

*

*

* Description:Loonframework专用uri(统一资源标识符)

*

*

* Copyright: Copyright (c) 2007

*

*

* Company: LoonFramework

*

*

* @author chenpeng

* @email:ceponline@yahoo.com.cn

* @version 0.1

*/

public class URI ...{

//传输协议类型

public static final int _L_URI_HTTP = 1;

public static final int _L_URI_UDP = 2;

private String _uri;

private int _type;

/** *//**

* 析构函数,用于注入uri和type

*

* @param uri

* @param type

*/

public URI(String uri, int type) ...{

_uri = new String(uri);

_type = type;

}

/** *//**

* 析构函数,用于注入uri

*

* @param uri

*/

public URI(String uri) ...{

_uri = new String(uri);

_type = URI._L_URI_HTTP;

}

/** *//**

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

*

* @return

*/

public byte[] getData() ...{

if (_uri == null) ...{

return null;

}

return StreamHelper.getHttpStream(_uri);

}

public String getURI() ...{

return _uri;

}

public int getType() ...{

return _type;

}

}

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

package org.loon.framework.game.sound;

import org.loon.framework.game.helper.StreamHelper;

import org.loon.framework.game.net.URI;

/** *//**

*

* Title: LoonFramework

*

*

* Description:用以获得并缓存声音文件数据(更进一步内容操作请见Loonframework-game框架)

*

*

* Copyright: Copyright (c) 2007

*

*

* Company: LoonFramework

*

*

* @author chenpeng

* @email:ceponline@yahoo.com.cn

* @version 0.1

*/

public class SoundData ...{

private byte[] _data;

private boolean _loop;

private int _type;

public static final int _L_SOUNDTYPE_MIDI = 1;

public static final int _L_SOUNDTYPE_WAV = 2;

/** *//**

* 析构函数,用以注入uri,type,loop

*

* @param uri

* @param type

* @param loop

*/

public SoundData(URI uri, int type, boolean loop) ...{

if (uri != null) ...{

_data = uri.getData();

}

_type = type;

_loop = loop;

}

/** *//**

* 析构函数,用以注入data,type,loop

*

* @param data

* @param type

* @param loop

*/

public SoundData(byte[] data, int type, boolean loop) ...{

if (data != null && data.length > 0) ...{

_data = new byte[data.length];

// 直接copy byte数组

System.arraycopy(data, 0, _data, 0, _data.length);

}

_type = type;

_loop = loop;

}

/** *//**

* 析构函数,用以注入限定位置的resName,type,loop

* @param resName

* @param type

* @param loop

*/

public SoundData(String resName, int type, boolean loop) ...{

this(StreamHelper.GetDataSource(resName),type,loop);

}

public byte[] getData() ...{

return _data;

}

public boolean getLoop() ...{

return _loop;

}

public void setLoop(boolean loop) ...{

_loop = loop;

}

public int getType() ...{

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.MeXyQExghgGxtaMessage;

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;

/** *//**

*

* Title: LoonFramework

*

*

* Description:用以进行声音文件操作(仅为Loonframework中部分方法,更详细请参见Loonframework-game框架)

*

*

* Copyright: Copyright (c) 2007

*

*

* Company: LoonFramework

*

*

* @author chenpeng

* @email:ceponline@yahoo.com.cn

* @version 0.1

*/

public class SoundPlay implements MetaEventListener, Runnable ...{

private int _sleepTime;

private Clip _audio;

private Sequencer _midi;

private boolean _loop;

private int _soundType;

private boolean _playing;

private Thread _thread = null;

private boolean _isRun = false;

/** *//**

* 析构函数,初始化SoundPlay

*

*/

public SoundPlay() ...{

_loop = false;

_soundType = 0;

_sleepTime = 1000;

_playing = false;

}

// 载入声音文件

public boolean load(SoundData data) ...{

reset();

if (data == null || data.getData() == null) ...{

return false;

}

return init(data.getData(), data.getType(), data.getLoop());

}

/** *//**

* 直接播放url文件

*

* @param uri

* @param ftype

* @param loop

* @return

*/

public boolean load(URI uri, int ftype, boolean loop) ...{

// 刷新数据

reset();

if (uri == null) ...{

return false;

}

// 获得SoundData

SoundData data = new SoundData(uri, ftype, loop);

if (data == null || data.getData() == null) ...{

return false;

}

return init(data.getData(), data.getType(), data.getLoop());

}

/** *//**

* 初始化sound相关数据

*

* @param data

* @param ftype

* @param loop

* @return

*/

private boolean init(byte[] data, int ftype, boolean loop) ...{

boolean result = false;

ByteArrayInputStream bis = null;

try ...{

bis = new ByteArrayInputStream(data);

} catch (Exception e) ...{

bis = null;

}

if (bis == null) ...{

return false;

}

// 判断类型

switch (ftype) ...{

// MIDI

case SoundData._L_SOUNDTYPE_MIDI:

// 当MIDI不存在时

if (_midi == null) ...{

try ...{

// 获得Sequencer

_midi = MidiSystem.getSequencer();

_midi.open();

} catch (Exception ex) ...{

_midi = null;

}

if (_midi != null) ...{

_midi.addMetaEventListener(this);

}

}

// 当MIDI依旧未获得时

if (_midi != null) ...{

// 重新创建Sequence

Sequence sc = null;

try ...{

sc = MidiSystem.getSequence(bis);

} catch (Exception e) ...{

sc = null;

}

if (sc != null) ...{

try ...{

_midi.setSequence(sc);

// 获得是否循环播放

_loop = loop;

// 获得是否载入

result = true;

} catch (Exception ee) ...{

}

// 获得声音类型

_soundType = SoundData._L_SOUNDTYPE_MIDI;

}

}

try ...{

bis.close();

} catch (Exception ee) ...{

}

break;

// Wav

case SoundData._L_SOUNDTYPE_WAV:

AudioFileFormat type = null;

// 获得Audio

try ...{

type = AudioSystem.getAudioFileFormat(bis);

} catch (Exception e) ...{

type = null;

}

// 关闭流

try ...{

bis.close();

} catch (Exception ex) ...{

}

if (type == null) ...{

return false;

}

// 根据指定信息构造数据行的信息对象

DataLine.Info di = new DataLine.Info(Clip.class, type.getFormat());

// 转为Clip

try ...{

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

} catch (Exception e) ...{

}

// 播放文件

try ...{

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

_loop = loop;

result = true;

} catch (Exception e) ...{

}

// 获得文件类型

_soundType = SoundData._L_SOUNDTYPE_WAV;

break;

}

return result;

}

public boolean play(SoundData data) ...{

if (!load(data)) ...{

return false;

}

return play();

}

public boolean play() ...{

switch (_soundType) ...{

case SoundData._L_SOUNDTYPE_MIDI:

try ...{

_midi.start();

_playing = true;

_soundType = SoundData._L_SOUNDTYPE_MIDI;

} catch (Exception ee) ...{

}

break;

case SoundData._L_SOUNDTYPXyQExghgGxE_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;

}

/** *//**

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

*

* @param data

* @return

*/

public boolean AutoPlay(SoundData data) ...{

if (!load(data)) ...{

return false;

}

return AutoPlay();

}

/** *//**

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

*

* @return

*/

public boolean AutoPlay() ...{

_isRun = true;

_thread = new Thread(this);

_thread.start();

return _playing;

}

/** *//**

* 停止播放

*/

public void stop() ...{

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

try ...{

_audio.stop();

} catch (Exception e) ...{

}

}

if (_midi != null) ...{

_midi.stop();

}

_playing = false;

_isRun = false;

}

/** *//**

* 释放数据

*

*/

public void reset() ...{

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

*/

public void meta(MetaMessage meta) ...{

// 判断是否循环播放MIDI

if (_loop && _soundType == SoundData._L_SOUNDTYPE_MIDI

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

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

_midi.setMicrosecondPosition(0);

_midi.start();

}

}

}

public void run() ...{

while (_isRun) ...{

play();

// 因为播放类型唯一,所以只会返回一个_playing结果,以此判定。

if (_midi != null) ...{

_playing = _midi.isRunning();

}

if (_audio != null) ...{

_playing = _audio.isRunning();

}

// 当播放停止

if (!_playing) ...{

// 释放

reset();

}

try ...{

Thread.sleep(_sleepTime);

} catch (InterruptedException e) ...{

e.printStackTrace();

}

}

}

public int getSleepTime() ...{

return _sleepTime;

}

/** *//**

* 设定AutoPlay线程循环时间。

*

* @param time

*/

public void setSleepTime(int time) ...{

_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;

/** *//**

*

Title: LoonFramework

*

Description:SoundPlay播放测试

*

Copyright: Copyright (c) 2007

*

Company: LoonFramework

* @author chenpeng

* @email:ceponline@yahoo.com.cn

* @version 0.1

*/

public class SoundPlayTest ...{

static void selectPlay(int ftype)...{

SoundData data=null;

switch(ftype)...{

//通过loonframework下uri从网络播放音乐

case 0:

data=new SoundData(new URI("http://looframework.sourceforge.net/midi/谁是大英雄.mid"),SoundData._L_SOUNDTYPE_MIDI,false);

break;

//通过本地资源下音乐文件的byte[]对象播放音乐

case 1:

byte[] bytes=StreamHelper.GetResourceData("/midi/谁是大英雄.mid");

data=new SoundData(bytes,SoundData._L_SOUNDTYPE_MIDI,false);

break;

//通过音乐文件路径播放音乐

case 2:

data=new SoundData("C:/谁是大英雄.mid",SoundData._L_SOUNDTYPE_MIDI,false);

break;

}

SoundPlay play=new SoundPlay();

//AutoPlay与Play方法的区别在于,AutoPlay播放完毕会自动停止并释放资源,play需手动中止。

//play.play(data);

play.AutoPlay(data);

}

public static void main(String[]args)...{

selectPlay(2);

}

}

更详细方法,会待Loonframework-game完全公布后,再进行解释。

另:由于StreamHelper关联其他Loonframework中方法,暂不给出,inputStream转byte[]可用如下写法:

//is为获得的inputStream

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

//用于承接byte[]

byte[] arrayByte = null;

try ...{

// 每次传输大小为4096

byte[] bytes = new byte[4096];

bytes = new byte[is.available()];

int read;

while ((read = is.read(bytes)) >= 0) ...{

byteArrayOutputStream.write(bytes, 0, read);

}

arrayByte = byteArrayOutputStream.toByteArray();

} catch (IOException e) ...{

return null;

} finally ...{

try ...{

if (byteArrayOutputStream != null) ...{

byteArrayOutputStream.close();

byteArrayOutputStream = null;

}

if (is != null) ...{

is.close();

is = null;

}

} catch (IOException e) ...{

}

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法
下一篇:Java实现验证码具体代码(图片、汉字)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~