JOAL学习笔记 第五课 多声源共享缓冲区

来源:互联网 发布:java缺省包 编辑:程序博客网 时间:2024/06/06 02:55

JOAL学习笔记

 

先是例行的连续代码页

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.nio.ByteBuffer;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import com.jogamp.openal.AL;import com.jogamp.openal.ALC;import com.jogamp.openal.ALCcontext;import com.jogamp.openal.ALCdevice;import com.jogamp.openal.ALException;import com.jogamp.openal.ALFactory;import com.jogamp.openal.util.ALut;public class SourceSharingBuffers {static ALC alc;static AL al;// These index the buffers.public static final int THUNDER = 0;public static final int WATERDROP = 1;public static final int STREAM = 2;public static final int RAIN = 3;public static final int CHIMES = 4;public static final int OCEAN = 5;public static final int NUM_BUFFERS = 6;// Buffers hold sound data.static int[] buffers = new int[NUM_BUFFERS];// A list of sources for multiple emissions.static List<Integer> sources = new ArrayList<>();// Position of the source sounds.static float[] sourcePos = { 0.0f, 0.0f, 0.0f };// Velocity of the source sounds.static float[] sourceVel = { 0.0f, 0.0f, 0.0f };// Position of the listener.static float[] listenerPos = { 0.0f, 0.0f, 0.0f };// Velocity of the listener.static float[] listenerVel = { 0.0f, 0.0f, 0.0f };// Orientation of the listener. (first 3 elements are "at", second 3 are// "up")static float[] listenerOri = { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f };static int initOpenAL() {al = ALFactory.getAL();alc = ALFactory.getALC();ALCdevice device;ALCcontext context;String deviceSpecifier;String deviceName = "DirectSound3D"; // You may choose to open a// specific OpenAL device if you// know its name.deviceName = null; // Passing a null String to alcOpenDevice will open// the default device on your system!// Get handle to device.device = alc.alcOpenDevice(deviceName);// Get the device specifier.deviceSpecifier = alc.alcGetString(device, ALC.ALC_DEVICE_SPECIFIER);System.out.println("Using device " + deviceSpecifier);// Create audio context.context = alc.alcCreateContext(device, null);// Set active context.alc.alcMakeContextCurrent(context);// Check for an error.if (alc.alcGetError(device) != ALC.ALC_NO_ERROR)return AL.AL_FALSE;return AL.AL_TRUE;}static void exitOpenAL() {ALCcontext curContext;ALCdevice curDevice;// Get the current context.curContext = alc.alcGetCurrentContext();// Get the device used by that context.curDevice = alc.alcGetContextsDevice(curContext);// Reset the current context to NULL.alc.alcMakeContextCurrent(null);// Release the context and the device.alc.alcDestroyContext(curContext);alc.alcCloseDevice(curDevice);}static int loadALData() {// Variables to load into.int[] format = new int[1];int[] size = new int[1];ByteBuffer[] data = new ByteBuffer[1];int[] freq = new int[1];int[] loop = new int[1];// Load wav data into buffers.al.alGenBuffers(NUM_BUFFERS, buffers, 0);if (al.alGetError() != AL.AL_NO_ERROR)return AL.AL_FALSE;ALut.alutLoadWAVFile("wavdata/thunder.wav", format, data, size, freq,loop);al.alBufferData(buffers[THUNDER], format[0], data[0], size[0], freq[0]);ALut.alutLoadWAVFile("wavdata/waterdrop.wav", format, data, size, freq,loop);al.alBufferData(buffers[WATERDROP], format[0], data[0], size[0],freq[0]);ALut.alutLoadWAVFile("wavdata/stream.wav", format, data, size, freq,loop);al.alBufferData(buffers[STREAM], format[0], data[0], size[0], freq[0]);ALut.alutLoadWAVFile("wavdata/rain.wav", format, data, size, freq, loop);al.alBufferData(buffers[RAIN], format[0], data[0], size[0], freq[0]);ALut.alutLoadWAVFile("wavdata/ocean.wav", format, data, size, freq,loop);al.alBufferData(buffers[OCEAN], format[0], data[0], size[0], freq[0]);ALut.alutLoadWAVFile("wavdata/chimes.wav", format, data, size, freq,loop);al.alBufferData(buffers[CHIMES], format[0], data[0], size[0], freq[0]);// Do another error check and return.if (al.alGetError() != AL.AL_NO_ERROR)return AL.AL_FALSE;return AL.AL_TRUE;}static void addSource(int type) {int[] source = new int[1];al.alGenSources(1, source, 0);if (al.alGetError() != AL.AL_NO_ERROR) {System.err.println("Error generating audio source.");System.exit(1);}al.alSourcei(source[0], AL.AL_BUFFER, buffers[type]);al.alSourcef(source[0], AL.AL_PITCH, 1.0f);al.alSourcef(source[0], AL.AL_GAIN, 1.0f);al.alSourcefv(source[0], AL.AL_POSITION, sourcePos, 0);al.alSourcefv(source[0], AL.AL_VELOCITY, sourceVel, 0);al.alSourcei(source[0], AL.AL_LOOPING, AL.AL_TRUE);al.alSourcePlay(source[0]);sources.add(new Integer(source[0]));}static void setListenerValues() {al.alListenerfv(AL.AL_POSITION, listenerPos, 0);al.alListenerfv(AL.AL_VELOCITY, listenerVel, 0);al.alListenerfv(AL.AL_ORIENTATION, listenerOri, 0);}static void killALData() {Iterator<Integer> iter = sources.iterator();while (iter.hasNext()) {al.alDeleteSources(1,new int[] { ((Integer) iter.next()).intValue() }, 0);}sources.clear();al.alDeleteBuffers(NUM_BUFFERS, buffers, 0);exitOpenAL();}public static void main(String[] args) {try {initOpenAL();} catch (ALException e) {e.printStackTrace();System.exit(1);}if (loadALData() == AL.AL_FALSE)System.exit(1);setListenerValues();char[] c = new char[1];while (c[0] != 'q') {try {BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));System.out.println("Press a key and hit ENTER: \n"+ "\t'w' for Water Drop\n" + "\t't' for Thunder\n"+ "\t's' for Stream\n" + "\t'r' for Rain\n"+ "\t'o' for Ocean\n" + "\t'c' for Chimes\n"+ "\n'q' to Quit\n");buf.read(c);switch (c[0]) {case 'w':addSource(WATERDROP);break;case 't':addSource(THUNDER);break;case 's':addSource(STREAM);break;case 'r':addSource(RAIN);break;case 'o':addSource(OCEAN);break;case 'c':addSource(CHIMES);break;}} catch (IOException e) {System.exit(1);}}killALData();} // main}

之后是一些值得注意的问题

首先,本节课有较多音频,如果不想自己准备,可以在这里下载到原版:

http://jogamp.org/git/?p=joal-demos.git;a=tree;f=src/java/demos/data

 

不知道读者怎么想,这次的程序看起来就像某种机翻,如果C语言转Java能机翻的话……,可能使用的JDK非常老吧(不支持自动装拆箱、泛型类、foreach等),现在值得改进的地方很多,例如加载过程可以抽象为一个函数,我们的声源与缓冲区可以抽象为Java类等等,这类简单的不再细说。

 

源程序的大致思想是这样的,首先创建了有限个缓冲区,之后封装了一个addSource函数,当音频播放请求到来时,创建一个声源,把它加入线性表并让之播放。

当程序退出时,终止上下文并删除表中的全部声源。

 

假如我们的音频不需要循环(游戏中大部分音效都是这样的),播放完一次的声源就不再利用了,每次对播放请求创建新的声源的确有点浪费。

 

这里考虑一种多例模式的改进,我们把声源作为一种资源,创建一个固定数量的声源集合,当播放请求到来时,从该集合中找出一个当前可用的声源来为播放提供服务,这与之实现粒子特效的思想相同(见http://blog.csdn.net/shuzhe66/article/details/39523555)。

首先给出管理集合的工具类:

import java.util.HashSet;import java.util.Set;import java.util.concurrent.Semaphore;public class CrowdController<T extends Individual> {Semaphore ListSph = new Semaphore(1);Semaphore runSph = new Semaphore(1);Set<T> indList = new HashSet<T>();Set<T> runSet = new HashSet<T>();public int countAvailible() {int res = 0;ListSph.acquireUninterruptibly();for (Individual ind : indList) {if (ind.isAvalible()) {res++;}}ListSph.release();return res;}public int countUnAvailible() {int res = 0;ListSph.acquireUninterruptibly();for (Individual ind : indList) {if (!ind.isAvalible()) {res++;}}ListSph.release();return res;}public T getUnAvailible() {T result = null;ListSph.acquireUninterruptibly();for (T ind : indList) {if (!ind.isAvalible()) {result = ind;break;}}ListSph.release();if (result == null) {// System.out.println(this.toString()+" Report: not enough individuals!"+" TYPES:"+indList.iterator().next().getClass().toString());}return result;}public T getAvailible() {T result = null;ListSph.acquireUninterruptibly();for (T ind : indList) {if (ind.isAvalible()) {result = ind;break;}}ListSph.release();if (result == null) {// System.out.println(this.toString()+" Report: not enough individuals!");}return result;}public void addIndividual(T ind) {ListSph.acquireUninterruptibly();indList.add(ind);ListSph.release();}public void destroyAllInd() {ListSph.acquireUninterruptibly();for (Individual ind : indList) {ind.destroy();}ListSph.release();}public void finishAllInd(int src) {ListSph.acquireUninterruptibly();for (Individual ind : indList) {ind.finish(src);}ListSph.release();}public void finishAllInd() {ListSph.acquireUninterruptibly();for (Individual ind : indList) {ind.finish(0);}ListSph.release();}}
它容纳所有的个体,并提供一个方法返回一个集合内可用的个体。


之后是个体这个资源的接口:

public interface Individual {public boolean isAvalible();public void getUse(Object[] ARGS,float... FARGS);public void finish(int src);public void destroy();}
任何作为资源的个体必须实现该接口,它包含了一个返回资源是否可用的方法。

接着是作为资源出现的声源Java类,对声源句柄包装一下,并实现Individual接口。

public class SoundSource implements Individual{private int sourceId = -1;private int[] contenier = new int[1];private int[] state = new int[1];AL al;public SoundSource(AL al){this.al = al;al.alGenSources(1, contenier,0);sourceId = contenier[0];al.alSourcef(sourceId, AL.AL_PITCH, 1.0f);al.alSourcef(sourceId, AL.AL_GAIN, 1.0f);}@Overridepublic boolean isAvalible() {al.alGetSourcei(sourceId, AL.AL_SOURCE_STATE, state, 0);//这里使用了第三课中的内容,它返回一个声源的播放状态return state[0] != AL.AL_PLAYING;}@Overridepublic void getUse(Object[] ARGS, float... FARGS) {if(checkArg(ARGS)){int bufferId = (Integer)(ARGS[0]);float[] position = (float[])ARGS[1];float[] velocity = (float[])ARGS[2];int loop = (Integer)ARGS[3];al.alSourceStop(sourceId);al.alSourcei(sourceId, AL.AL_BUFFER, bufferId);al.alSourcefv(sourceId, AL.AL_POSITION, position, 0);al.alSourcefv(sourceId, AL.AL_VELOCITY, velocity, 0);al.alSourcei(sourceId, AL.AL_LOOPING, loop);al.alSourcePlay(sourceId);}}private boolean checkArg(Object[] ARGS){if(ARGS[0] instanceof Integer){if(ARGS[1] instanceof float[]){if(ARGS[2] instanceof float[]){if(ARGS[3] instanceof Integer){return true;}}}}return false;}@Overridepublic void finish(int src) {al.alSourceStop(sourceId);}@Overridepublic void destroy() {finish(0);al.alDeleteSources(1,contenier,0);}}


之后是一个可用于播放的包装类,它将CrowdController作为其成员。

public class SourceCrowd {CrowdController<SoundSource> crowdCt;public SourceCrowd(AL al,int maxSoundSource){crowdCt = new CrowdController<>();for(int i = 0;i < maxSoundSource;i++){crowdCt.addIndividual(new SoundSource(al));}}public synchronized boolean playSound(int bufferId,float[] posi,float velo[],int loop){SoundSource ss = crowdCt.getAvailible();if(ss != null){ss.getUse(new Object[]{bufferId,posi,velo,loop});return true;}else{return false;}}public void destroyAll(){crowdCt.destroyAllInd();}}
其构造方法指明了集合数量,本例中是指最大的音频并发数量。而且提供了播放与销毁的方法。


最后是对实例程序的改进:

public class SourceSharingBuffersChanged {static ALC alc;static AL al;// These index the buffers.public static final int THUNDER = 0;public static final int WATERDROP = 1;public static final int STREAM = 2;public static final int RAIN = 3;public static final int CHIMES = 4;public static final int OCEAN = 5;public static final int NUM_BUFFERS = 6;// Buffers hold sound data.static int[] buffers = new int[NUM_BUFFERS];// A list of sources for multiple emissions.static List<Integer> sources = new ArrayList<>();// Position of the source sounds.static float[] sourcePos = { 0.0f, 0.0f, 0.0f };// Velocity of the source sounds.static float[] sourceVel = { 0.0f, 0.0f, 0.0f };// Position of the listener.static float[] listenerPos = { 0.0f, 0.0f, 0.0f };// Velocity of the listener.static float[] listenerVel = { 0.0f, 0.0f, 0.0f };// Orientation of the listener. (first 3 elements are "at", second 3 are// "up")static float[] listenerOri = { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f };static SourceCrowd cc;static int initOpenAL() {al = ALFactory.getAL();alc = ALFactory.getALC();ALCdevice device;ALCcontext context;String deviceSpecifier;String deviceName = "DirectSound3D"; // You may choose to open a// specific OpenAL device if you// know its name.deviceName = null; // Passing a null String to alcOpenDevice will open// the default device on your system!// Get handle to device.device = alc.alcOpenDevice(deviceName);// Get the device specifier.deviceSpecifier = alc.alcGetString(device, ALC.ALC_DEVICE_SPECIFIER);System.out.println("Using device " + deviceSpecifier);// Create audio context.context = alc.alcCreateContext(device, null);// Set active context.alc.alcMakeContextCurrent(context);cc = new SourceCrowd(al,10);//注意这里!我们创建了一个拥有10个声源资源群体// Check for an error.if (alc.alcGetError(device) != ALC.ALC_NO_ERROR)return AL.AL_FALSE;return AL.AL_TRUE;}static void exitOpenAL() {ALCcontext curContext;ALCdevice curDevice;// Get the current context.curContext = alc.alcGetCurrentContext();// Get the device used by that context.curDevice = alc.alcGetContextsDevice(curContext);// Reset the current context to NULL.alc.alcMakeContextCurrent(null);// Release the context and the device.alc.alcDestroyContext(curContext);alc.alcCloseDevice(curDevice);}static int loadALData() {// Variables to load into.int[] format = new int[1];int[] size = new int[1];ByteBuffer[] data = new ByteBuffer[1];int[] freq = new int[1];int[] loop = new int[1];// Load wav data into buffers.al.alGenBuffers(NUM_BUFFERS, buffers, 0);if (al.alGetError() != AL.AL_NO_ERROR)return AL.AL_FALSE;ALut.alutLoadWAVFile("wavdata/thunder.wav", format, data, size, freq,loop);al.alBufferData(buffers[THUNDER], format[0], data[0], size[0], freq[0]);ALut.alutLoadWAVFile("wavdata/waterdrop.wav", format, data, size, freq,loop);al.alBufferData(buffers[WATERDROP], format[0], data[0], size[0],freq[0]);ALut.alutLoadWAVFile("wavdata/stream.wav", format, data, size, freq,loop);al.alBufferData(buffers[STREAM], format[0], data[0], size[0], freq[0]);ALut.alutLoadWAVFile("wavdata/rain.wav", format, data, size, freq, loop);al.alBufferData(buffers[RAIN], format[0], data[0], size[0], freq[0]);ALut.alutLoadWAVFile("wavdata/war.wav", format, data, size, freq,loop);al.alBufferData(buffers[OCEAN], format[0], data[0], size[0], freq[0]);ALut.alutLoadWAVFile("wavdata/chimes.wav", format, data, size, freq,loop);al.alBufferData(buffers[CHIMES], format[0], data[0], size[0], freq[0]);// Do another error check and return.if (al.alGetError() != AL.AL_NO_ERROR)return AL.AL_FALSE;return AL.AL_TRUE;}static void addSource(int type){cc.playSound(buffers[type], sourcePos,sourceVel,AL.AL_FALSE);//这里是改动最大的地方}static void setListenerValues() {al.alListenerfv(AL.AL_POSITION, listenerPos, 0);al.alListenerfv(AL.AL_VELOCITY, listenerVel, 0);al.alListenerfv(AL.AL_ORIENTATION, listenerOri, 0);}static void killALData() {cc.destroyAll();//不要忘记调用销毁方法al.alDeleteBuffers(NUM_BUFFERS, buffers, 0);exitOpenAL();}public static void main(String[] args) {try {initOpenAL();} catch (ALException e) {e.printStackTrace();System.exit(1);}if (loadALData() == AL.AL_FALSE)System.exit(1);setListenerValues();char[] c = new char[1];while (c[0] != 'q') {try {BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));System.out.println("Press a key and hit ENTER: \n"+ "\t'w' for Water Drop\n" + "\t't' for Thunder\n"+ "\t's' for Stream\n" + "\t'r' for Rain\n"+ "\t'o' for Ocean\n" + "\t'c' for Chimes\n"+ "\n'q' to Quit\n");buf.read(c);switch (c[0]) {case 'w':addSource(WATERDROP);break;case 't':addSource(THUNDER);break;case 's':addSource(STREAM);break;case 'r':addSource(RAIN);break;case 'o':addSource(OCEAN);break;case 'c':addSource(CHIMES);break;}} catch (IOException e) {System.exit(1);}}killALData();} // main}
这样程序中的声源便作为资源可以重复利用了。




0 0
原创粉丝点击