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以上版本,需要對泛型顯示地聲明出來,否則會報警告,對於有代碼潔癖的人來講實在是無法容忍。
上面的測試代碼除了修復錯誤與警告外,還引用了一些前面課程的內容,大多數沒有什麼修改,只是少了它們代碼是無法完整調試的,具體內容可以參考前面的課程。