[譯文]JOAL教程
原文地址:http://jogamp.org/joal-demos/www/devmaster/lesson3.html
原文作者:Athomas Goldberg
譯文:三向板磚
轉載請保留以上信息。
本節對應的連續代碼頁及學習筆記:http://blog.csdn.net/shuzhe66/article/details/40260861
第三課 多聲源
本文是DevMaster.net(http://devmaster.net/)的OpenAL教程對應的JOAL版本。C語言版原文作者爲JesseMaurais
大家好,距離上次發佈教程也有一段時間了,但遲到總比沒有要好,我猜大家都渴望着閱讀新一期教程,於是我便着手去寫了。
本期教程將教會大家如何同時播放多個音頻。在很多激烈的遊戲當中包含有各種各樣的元素,在它們被觸發時往往伴隨着各類音效,這實現起來並不困難,處理多路音頻與處理單路音頻的方法極爲相似。
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;
// 所需緩衝區的最大數量.
static final int NUM_BUFFERS = 3;
// 所需聲源的最大數量
static final int NUM_SOURCES = 3;
// 下面三個變量標記了不同的聲源
static final int BATTLE = 0;
static final int GUN1 = 1;
static final int GUN2 = 2;
// 容納聲音數據的緩衝區
static int[] buffers = new int[NUM_BUFFERS];
// 播放聲音的點聲源
static int[] sources = new int[NUM_SOURCES];
// 各個聲源的位置
static float[][] sourcePos = new float[NUM_SOURCES][3];
// 各個聲源的速度
static float[][] sourceVel = new float[NUM_SOURCES][3];
// 聽衆的位置
static float[] listenerPos = { 0.0f, 0.0f, 0.0f };
// 聽衆的速度
static float[] listenerVel = { 0.0f, 0.0f, 0.0f };
// 聽衆的朝向
static float[] listenerOri = { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f };
我相信上面這段代碼對於看過前兩個章節的人來講會非常熟悉,唯一的不同在於我們需要將三種不同的音效裝入OpenAL系統中。
static int loadALData() {
//需要填裝的值
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];
// 將wav文件讀入緩衝區
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/Gun1.wav",
format,
data,
size,
freq,
loop);
al.alBufferData(
buffers[GUN1],
format[0],
data[0],
size[0],
freq[0]);
ALut.alutLoadWAVFile(
"wavdata/Gun2.wav",
format,
data,
size,
freq,
loop);
al.alBufferData(
buffers[GUN2],
format[0],
data[0],
size[0],
freq[0]);
//將緩衝區與聲源綁定
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);
// 檢查錯誤並返回結果
if (al.alGetError() != AL.AL_NO_ERROR) {
return AL.AL_FALSE;
}
return AL.AL_TRUE;
}
上面的代碼看起來與之前有些許不同,實際上並不是這樣。大體上講,我們只是將三個音頻文件裝載到緩衝區中,並將三個緩衝區與對應的聲源綁定起來。唯一一點不同是Battle.wav(對應source[0])被設定爲循環播放而其它兩個音頻則沒有。
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();
//初始化OpenAL,並將錯誤標記重置
ALut.alutInit();
al.alGetError();
//裝載音頻數據.
if(loadALData() == AL.AL_FALSE) {
System.exit(1);
}
setListenerValues();
//播放“戰鬥”的背景音樂
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 < 10000) {
elapsed = System.currentTimeMillis() - startTime;
if (elapsed > 50) {
totalElapsed += elapsed;
startTime = System.currentTimeMillis();
//隨機的選擇除背景樂外的兩個音效之一,判斷它是否在播放中
int pick = Math.abs((rand.nextInt()) % 2) + 1;
al.alGetSourcei(sources[pick], AL.AL_SOURCE_STATE, state, 0);
if (state[0] != AL.AL_PLAYING) {
//在聽衆周圍選擇一個隨機的位置播放聲音
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();
}
}
這裏便是本次教程中最有趣的部分,我們一次檢測各個聲源是否處於播放當中,對沒有播放的聲源下達播放命令,而且我們在播放前爲其設定了一個三維空間中的隨機新位置(這樣僅僅是爲了好玩~)。
最後我們運行它,伴隨着混合音效,我們成功了!大部分讀者可能已經意識到,爲了讓聲源一起播放,我們僅僅下達了命令而未做什麼特別的事情,OpenAL已經處理了混音部分,並讓實際播放的聲音與聲源、聽衆的位置、速度相符,這難道不是OpenAL的魅力所在嗎?
這是如此的簡單,以至於我自己都懷疑爲什麼本篇教程會拖這麼久。如果讀者朋友們想要一些其它特別的教程(不一定是OpenAL,我的知識量是很龐大的)可以通過[email protected]聯繫我,不出意外的話,我打算在未來的教程部分講解緩衝區共享與多普勒效應的有關內容,祝大家編碼愉快!