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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章