JOAL学习笔记
先是例行的连续代码页,修复了原文中的错误并增加了必要的修改。
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.Vector;
import com.jogamp.openal.AL;
import com.jogamp.openal.ALC;
import com.jogamp.openal.ALCcontext;
import com.jogamp.openal.ALCdevice;
import com.jogamp.openal.ALFactory;
import com.jogamp.openal.util.ALut;
public class AdvALTest {
private static AL al;
private static ALC alc;
private static Vector<String> loadedFiles = new Vector<>(); // 临时储存加载的文件路径
private static Vector<Integer> buffers = new Vector<>(); // 储存所有被加载的缓冲区
private static Vector<Integer> sources = new Vector<>(); // 储存所有合法的声源
// 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 };
public static String getALErrorString(int err) {
switch (err) {
case AL.AL_NO_ERROR:
return "AL_NO_ERROR";
case AL.AL_INVALID_NAME:
return "AL_INVALID_NAME";
case AL.AL_INVALID_ENUM:
return "AL_INVALID_ENUM";
case AL.AL_INVALID_VALUE:
return "AL_INVALID_VALUE";
case AL.AL_INVALID_OPERATION:
return "AL_INVALID_OPERATION";
case AL.AL_OUT_OF_MEMORY:
return "AL_OUT_OF_MEMORY";
default:
return null;
}
}
public String getALCErrorString(int err) {
switch (err) {
case ALC.ALC_NO_ERROR:
return "ALC_NO_ERROR";
case ALC.ALC_INVALID_DEVICE:
return "ALC_INVALID_DEVICE";
case ALC.ALC_INVALID_CONTEXT:
return "ALC_INVALID_CONTEXT";
case ALC.ALC_INVALID_ENUM:
return "ALC_INVALID_ENUM";
case ALC.ALC_INVALID_VALUE:
return "ALC_INVALID_VALUE";
case ALC.ALC_OUT_OF_MEMORY:
return "ALC_OUT_OF_MEMORY";
default:
return null;
}
}
public static int loadALBuffer(String path) throws IOException {
// 定义缓冲区的数据变量
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];
// 缓冲区id及错误检测变量
int[] buffer = new int[1];
int result;
// 创建一个缓冲区,并检测是否出错
al.alGenBuffers(1, buffer, 0);
if ((result = al.alGetError()) != AL.AL_NO_ERROR)
throw new IOException(getALErrorString(result));
// 从文件中读入音频数据,检测加载是否正确
ALut.alutLoadWAVFile(path, format, data, size, freq, loop);
if ((result = al.alGetError()) != AL.AL_NO_ERROR)
throw new IOException(getALErrorString(result));
// 将音频数据装入缓冲区,检测缓冲区是否收到
al.alBufferData(buffer[0], format[0], data[0], size[0], freq[0]);
if ((result = al.alGetError()) != AL.AL_NO_ERROR)
throw new IOException(getALErrorString(result));
if ((result = al.alGetError()) != AL.AL_NO_ERROR)
throw new IOException(getALErrorString(result));
// 返回缓冲区id
return buffer[0];
}
public static int getLoadedALBuffer(String path) throws IOException {
int buffer; // 装在完成的缓冲区id
// 迭代器负责遍历文件列表中的所有路径。
Iterator<String> iter = loadedFiles.iterator();
int i = 0;
while (iter.hasNext()) {
String str = (String) iter.next();
if (str.equals(path)) {
return ((Integer) buffers.get(i)).intValue();
}
i++;
}
// 如果执行到了这里,说明文件是新的,我们必须对其创建新的缓冲区
buffer = loadALBuffer(path);
// 之后将其加入列表中,这样就将其加载依据注册给系统。
buffers.add(new Integer(buffer));
loadedFiles.add(path);
return buffer;
}
public static int loadALSample(String path, boolean loop)
throws IOException {
int[] source = new int[1];
int buffer;
int result;
// 获得文件对应的缓冲区id (加载它如果必要的话).
buffer = getLoadedALBuffer(path);
// 创建一个声源
al.alGenSources(1, source, 0);
if ((result = al.alGetError()) != AL.AL_NO_ERROR)
throw new IOException(getALErrorString(result));
// 设置生源属性
al.alSourcei(source[0], AL.AL_BUFFER, buffer);
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, loop ? AL.AL_TRUE : AL.AL_FALSE);
// 保存声源
sources.add(new Integer(source[0]));
// 返回声源id
return source[0];
}
public static void killALLoadedData() {
loadedFiles.clear();
}
static int phaser1;
static int phaser2;
public static void loadALData() throws IOException {
// 你应用程序需要做的全部内容,无需担心缓冲区。
phaser1 = loadALSample("wavdata/phaser.wav", false);
phaser2 = loadALSample("wavdata/phaser.wav", true);
killLoadedALData();
}
public static void killLoadedALData() {
// 释放所有缓冲区数据
Iterator<Integer> iter = buffers.iterator();
while (iter.hasNext()) {
al.alDeleteBuffers(1,
new int[] { ((Integer) iter.next()).intValue() }, 0);
}
// 释放所有声源
iter = sources.iterator();
while (iter.hasNext()) {
al.alDeleteSources(1,
new int[] { ((Integer) iter.next()).intValue() }, 0);
}
// 销毁列表项
buffers.clear();
sources.clear();
}
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);
}
public static void main(String[] args) {
try {
initOpenAL();
loadALData();
killLoadedALData();
exitOpenAL();
} catch (IOException err) {
err.printStackTrace();
}
}
}
之后是一些值得注意的问题
本次调试结果可谓历尽千辛,原文代码片中的错误非常多,例如数组越界、大括号缺失、调用参数不正确等。除此之外,由于现在普遍使用JDK1.5以上版本,需要对泛型显示地声明出来,否则会报警告,对于有代码洁癖的人来讲实在是无法容忍。
上面的测试代码除了修复错误与警告外,还引用了一些前面课程的内容,大多数没有什么修改,只是少了它们代码是无法完整调试的,具体内容可以参考前面的课程。