JOAL學習筆記 第三課 多聲源播放

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還是很好的。

發佈了42 篇原創文章 · 獲贊 50 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章