JOAL學習筆記 第六課 高級加載方式與錯誤處理

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以上版本,需要對泛型顯示地聲明出來,否則會報警告,對於有代碼潔癖的人來講實在是無法容忍。

上面的測試代碼除了修復錯誤與警告外,還引用了一些前面課程的內容,大多數沒有什麼修改,只是少了它們代碼是無法完整調試的,具體內容可以參考前面的課程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章