JOAL学习笔记
先是例行的连续代码页
import java.nio.ByteBuffer;
import java.util.Random;
import com.jogamp.openal.AL;
import com.jogamp.openal.ALFactory;
import com.jogamp.openal.util.ALut;
public class MultipleSources {
static AL al;
// Maximum number of buffers we will need.
static final int NUM_BUFFERS = 3;
// Maximum emissions we will need.
static final int NUM_SOURCES = 3;
// These index the buffers and sources
static final int BATTLE = 0;
static final int GUN1 = 1;
static final int GUN2 = 2;
// Buffers hold sound data
static int[] buffers = new int[NUM_BUFFERS];
// Sources are points of emitting sound
static int[] sources = new int[NUM_SOURCES];
// Position of the source sounds.
static float[][] sourcePos = new float[NUM_SOURCES][3];
// Velocity of the source sounds
static float[][] sourceVel = new float[NUM_SOURCES][3];
// 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 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/battle.wav", format, data, size, freq,
loop);
al.alBufferData(buffers[BATTLE], format[0], data[0], size[0], freq[0]);
ALut.alutLoadWAVFile("wavdata/EXPLOSION.wav", format, data, size, freq, loop);
al.alBufferData(buffers[GUN1], format[0], data[0], size[0], freq[0]);
ALut.alutLoadWAVFile("wavdata/EXPLOSION.wav", format, data, size, freq, loop);
al.alBufferData(buffers[GUN2], format[0], data[0], size[0], freq[0]);
// bind buffers into audio sources
al.alGenSources(NUM_SOURCES, sources, 0);
al.alSourcei(sources[BATTLE], AL.AL_BUFFER, buffers[BATTLE]);
al.alSourcef(sources[BATTLE], AL.AL_PITCH, 1.0f);
al.alSourcef(sources[BATTLE], AL.AL_GAIN, 1.0f);
al.alSourcefv(sources[BATTLE], AL.AL_POSITION, sourcePos[BATTLE], 0);
al.alSourcefv(sources[BATTLE], AL.AL_VELOCITY, sourceVel[BATTLE], 0);
al.alSourcei(sources[BATTLE], AL.AL_LOOPING, AL.AL_TRUE);
al.alSourcei(sources[GUN1], AL.AL_BUFFER, buffers[GUN1]);
al.alSourcef(sources[GUN1], AL.AL_PITCH, 1.0f);
al.alSourcef(sources[GUN1], AL.AL_GAIN, 1.0f);
al.alSourcefv(sources[GUN1], AL.AL_POSITION, sourcePos[GUN1], 0);
al.alSourcefv(sources[GUN1], AL.AL_VELOCITY, sourceVel[GUN1], 0);
al.alSourcei(sources[GUN1], AL.AL_LOOPING, AL.AL_FALSE);
al.alSourcei(sources[GUN2], AL.AL_BUFFER, buffers[GUN2]);
al.alSourcef(sources[GUN2], AL.AL_PITCH, 1.0f);
al.alSourcef(sources[GUN2], AL.AL_GAIN, 1.0f);
al.alSourcefv(sources[GUN2], AL.AL_POSITION, sourcePos[GUN2], 0);
al.alSourcefv(sources[GUN2], AL.AL_VELOCITY, sourceVel[GUN2], 0);
al.alSourcei(sources[GUN2], AL.AL_LOOPING, AL.AL_FALSE);
// do another error check and return
if (al.alGetError() != AL.AL_NO_ERROR) {
return AL.AL_FALSE;
}
return AL.AL_TRUE;
}
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 killAllData() {
al.alDeleteBuffers(NUM_BUFFERS, buffers, 0);
al.alDeleteSources(NUM_SOURCES, sources, 0);
ALut.alutExit();
}
public static void main(String[] args) {
al = ALFactory.getAL();
// Initialize OpenAL and clear the error bit
ALut.alutInit();
al.alGetError();
// Load the wav data.
if (loadALData() == AL.AL_FALSE) {
System.exit(1);
}
setListenerValues();
// begin the battle sample to play
al.alSourcePlay(sources[BATTLE]);
long startTime = System.currentTimeMillis();
long elapsed = 0;
long totalElapsed = 0;
Random rand = new Random();
int[] state = new int[1];
while (totalElapsed < 500000) {
elapsed = System.currentTimeMillis() - startTime;
if (elapsed > 500) {
totalElapsed += elapsed;
startTime = System.currentTimeMillis();
// pick one of the sources at random and check to see if it is
// playing.
// Skip the first source because it is looping anyway (will
// always be playing).
int pick = Math.abs((rand.nextInt()) % 2) + 1;
al.alGetSourcei(sources[pick], AL.AL_SOURCE_STATE, state, 0);
if (state[0] != AL.AL_PLAYING) {
// pick a random position around the listener to play the
// source.
double theta = (rand.nextInt() % 360) * 3.14 / 180.0;
sourcePos[pick][0] = -((float) Math.cos(theta));
sourcePos[pick][1] = -((float) (rand.nextInt() % 2));
sourcePos[pick][2] = -((float) Math.sin(theta));
al.alSourcefv(sources[pick], AL.AL_POSITION,
sourcePos[pick], 0);
al.alSourcePlay(sources[pick]);
}
}
}
killAllData();
}
}
之后是一些值得注意的问题
本次的调试可以说是最为顺利的一次。
在音频的选择上,GUN1\2可以选择一些短暂的音频如游戏音效,笔者用爆炸音效测试的效果非常好(注意单声道!)。在实例程序中,只给出了10秒的播放时间,大家可以将这个数字调大点或干脆死循环。
教程的后面提到了一些混音的内容,笔者以前也做过基本的数字混音处理,将以前的笔记整理一下放到这里吧:
数字混音是按照采样大小计算的,使用Y表示结果采样、使用A\B表示待混合的两音,算法对应如下:
对于8位采样(正好是一个byte):
当A\B均为负时:
Y = A +B - (A * B / (-127))
否则,有:
Y = A + B - A * B / 128
同理推广到n位采样:
当A\B均为负时:
Y = A + B - (A * B / (-(2 pow(n-1) -1)))
否则,有:
Y = A + B - (A * B / (2 pow(n-1))
如果还记得高中学的那些不等式的话,可以看出Y无论如何也不会越界,因此对于多音混合,可以使用迭代计算。
这些只是基本的混合,不能满足更高级的需求(例如移动声源产生多普勒效应等),所以学习一下OpenAL还是很好的。