Java opengl openal_项目里用到了openal,特分享一下openal全教程
// 存儲聲音數據
ALuint Buffer;
// 用于播放聲音
ALuint Source;
這是程序處理結構的初始化。在OPENAL中三種不同的結構,所有關于聲音播放和
聲音數據存儲在一個內存中,源(source)是指向放聲音的空間。明白源是非常
的重要。源只播放內存中的背景聲音數據。源也給出了特殊的屬性如位置和速度。
第三個對象是聽者,用戶就是那唯一的聽者。聽者屬性屬于源屬性,決定如何
聽聲音。例如,不同位置將決定聲音的速度。
// 源聲音的位置
ALfloat SourcePos[] = { 0.0, 0.0, 0.0 };
// 源聲音的速度
ALfloat SourceVel[] = { 0.0, 0.0, 0.0 };
// 聽者的位置
ALfloat ListenerPos[] = { 0.0, 0.0, 0.0 };
// 聽者的速度
ALfloat ListenerVel[] = { 0.0, 0.0, 0.0 };
// 聽者的方向 (first 3 elements are "at", second 3 are "up")
ALfloat ListenerOri[] = { 0.0, 0.0, -1.0,?0.0,
1.0, 0.0 };
在上面的代碼中,我們定義了源和聽者對象的位置和速度。這些數組是基于笛
卡兒坐標的矢量。你能很容易用結構或類做相同的事情。
ALboolean LoadALData()
{
// 載入變量.
ALenum
format;
ALsizei
size;
ALvoid*
data;
ALsizei
freq;
ALboolean
loop;
在這里我們建立一個函數用于從一個文件中載入聲音數據。變量用于存儲適合
我們的ALUT信息。
// 載入WAV數據
alGenBuffers(1, &Buffer);
if
(alGetError() != AL_NO_ERROR)
return AL_FALSE;
alutLoadWAVFile("wavdata/FancyPants.wav", &format,
&data, &size,
&freq, &loop);
alBufferData(Buffer, format, data, size, freq);
alutUnloadWAV(format, data, size, freq);
函數alGenBufers用于建立對象內存并把他們存儲在我們定義的變量中。然后判斷
數據是否存儲。
ALUT庫為我們打開文件,提供我們建立內存所需的信息,并且在我們歸屬所有
數據到內存后,她將處理這些數據。
// 捆綁源
alGenSources(1, &Source);
if
(alGetError() != AL_NO_ERROR)
return AL_FALSE;
alSourcei
(Source, AL_BUFFER,?Buffer?);
alSourcef
(Source,
AL_PITCH,?1.0f?);
alSourcef
(Source,
AL_GAIN,?1.0f?);
alSourcefv(Source, AL_POSITION, SourcePos);
alSourcefv(Source, AL_VELOCITY, SourceVel);
alSourcei
(Source, AL_LOOPING,?loop?);
我們用建立內存對象的方法建立了源對象。然后,我們定義源屬性用于錄放。
最重要的屬性是她用的內存。這告訴源用于錄放。因此,我們只有捆綁她。同時,
我們也告訴她我們定義的源位置和速度。
// 做錯誤檢測并返回
if
(alGetError() == AL_NO_ERROR)
return AL_TRUE;
return
AL_FALSE;
在函數的結尾,我們將做更多的檢測,以確定她的正確。
void SetListenervalues()
{
alListenerfv(AL_POSITION,?ListenerPos);
alListenerfv(AL_VELOCITY,?ListenerVel);
alListenerfv(AL_ORIENTATION, ListenerOri);
}
我們建立一個函數用于更新聽者速度。
void KillALData()
{
alDeleteBuffers(1, &Buffer);
alDeleteSources(1, &Source);
alutExit();
}
這是一個關閉函數,用于釋放內存和音頻設備。
int main(int argc, char *argv[])
{
//
初始OPENAL并清錯誤字節
alutInit(&argc, argv);
alGetError();
函數alutInit將安裝ALC需要的東西。ALUT通過ALC并設置她為當前建立OPENAL
環境描述。在WINDOWS平臺上初始DIRECTSOUND。然后用‘GLGETERROR’檢測錯誤。
// 載入WAV數據
if
(LoadALData() == AL_FALSE)
return -1;
SetListenervalues();
//
設置退出函數
atexit(KillALData);
我們將檢測WAV文件是否正確載入。如果沒有退出程序。
正確后,更新聽者參數,最后退出。
ALubyte c = ' ';
while (c
!= 'q')
{
c = getche();
switch (c)
{
// Pressing 'p' will begin playing the sample.
case 'p': alSourcePlay(Source); break;
// Pressing 's' will stop the sample from playing.
case 's': alSourceStop(Source); break;
// Pressing 'h' will pause (hold) the sample.
case 'h': alSourcePause(Source); break;
};
}
return
0;
}
This is the interesting part of the tutorial. It's a very basic
loop that lets us control the playback of the audio sample.
Pressing 'p' will replay the sample, pressing 's' will stop the
sample, and pressing 'h' will pause the sample. Pressing 'q' will
exit the program.
Well there it is. Your first delve into OpenAL. I hope it was
made simple enough for you. It may have been a little too simple
for the 1337 h4X0r, but we all got to start somewhere. Things will
get more advanced as we go along.
Download the Dev-C++ source and project file
OpenAl教程(二)
循環和消退
希望你覺得上一章有用,這一章將更容易。
#include
#include
#include
#include
#include
#include
#include
// 存儲聲音數據.
ALuint Buffer;
// 用于播放聲音.
ALuint Source;
// 源聲音的位置.
ALfloat SourcePos[] = { 0.0, 0.0, 0.0 };
// 源聲音的速度.
ALfloat SourceVel[] = { 0.0, 0.0, 0.1 };
// 聽者的位置.
ALfloat ListenerPos[] = { 0.0, 0.0, 0.0 };
// 聽者的速度
ALfloat ListenerVel[] = { 0.0, 0.0, 0.0 };
// 聽者的方向 (first 3 elements are "at", second 3 are "up")
ALfloat ListenerOri[] = { 0.0, 0.0, -1.0, 0.0, 1.0, 0.0 };
這一章與上一章唯一的不同是源速度的改變,他的‘Z’現在是0.1.
ALboolean LoadALData()
{
// 載入變量
ALenum format;
ALsizei size;
ALvoid* data;
ALsizei freq;
ALboolean loop;
// 載入WAV數據.
alGenBuffers(1, &Buffer);
if (alGetError() != AL_NO_ERROR)
return AL_FALSE;
alutLoadWAVFile("wavdata/Footsteps.wav", &format,
&data, &size,
&freq, &loop);
alBufferData(Buffer, format, data, size, freq);
alutUnloadWAV(format, data, size, freq);
// 捆綁源
alGenSources(1, &Source);
if (alGetError() != AL_NO_ERROR)
return AL_FALSE;
alSourcei (Source, AL_BUFFER, Buffer );
alSourcef (Source, AL_PITCH, 1.0f );
alSourcef (Source, AL_GAIN, 1.0f );
alSourcefv(Source, AL_POSITION, SourcePos);
alSourcefv(Source, AL_VELOCITY, SourceVel);
alSourcei (Source, AL_LOOPING, AL_TRUE );
// 做錯誤檢測并返回
if (alGetError() != AL_NO_ERROR)
return AL_FALSE;
return AL_TRUE;
}
在這一節中有兩處改變,首先是導入“FOOTSTES。WAV”,設置源‘AL_LOOPING’
為‘AL_TRUE’。這意味著源播放直到停止時結束。他將不斷的循環播放。
void SetListenervalues()
{
alListenerfv(AL_POSITION, ListenerPos);
alListenerfv(AL_VELOCITY, ListenerVel);
alListenerfv(AL_ORIENTATION, ListenerOri);
}
void KillALData()
{
alDeleteBuffers(1, &Buffer);
alDeleteSources(1, &Source);
alutExit();
}
這里沒有改變。
int main(int argc, char *argv[])
{
// 初始OPENAL并清錯誤字節
alutInit(NULL,0);
alGetError();
// 載入WAV數據.
if (LoadALData() == AL_FALSE)
return 0;
SetListenervalues();
// 設置退出函數.
atexit(KillALData);
// 開始源的播放.
alSourcePlay(Source);
//循環
ALint time = 0;
ALint elapse = 0;
while (!kbhit())
{
elapse += clock() - time;
time += elapse;
if (elapse > 50)
{
elapse = 0;
SourcePos[0] += SourceVel[0];
SourcePos[1] += SourceVel[1];
SourcePos[2] += SourceVel[2];
alSourcefv(Source, AL_POSITION, SourcePos);
}
}
return 0;
}
這里唯一的改變是增加了一個循環。他將代替播放和停止按鈕。
We do this by slowly incrementing the position by it's velocity
over
time. The time is sampled by checking the system clock which
gives
us a tick count. It shouldn't be necessary to change this, but if
the
audio clip fades too fast you might want to change 50 to some
higher n
umber. Pressing any key will end the loop
多源
你好,在這一章中,我們將在上一章的例程中加入一些元素,使他能同時播放超過一種的音樂。
通常在一個優秀的游戲中有各種不同的音夾(clip),這是怎樣實現的呢?
下面將介紹。
#include
#include
#include
#include
#include
#include
#include
// 我們需要的最大的數據緩沖.
#define NUM_BUFFERS 3
// 我們需要放三種聲音.
#define NUM_SOURCES 3
// 緩沖和源標志.
#define BATTLE 0
#define GUN1 1
#define GUN2 2
// 存儲聲音數據.
ALuint Buffers[NUM_BUFFERS];
// 用于播放聲音.
ALuint Sources[NUM_SOURCES];
// 源聲音的位置.
ALfloat SourcesPos[NUM_SOURCES][3];
// 源聲音的速度.
ALfloat SourcesVel[NUM_SOURCES][3];
// 聽者的位置.
ALfloat ListenerPos[] = { 0.0, 0.0, 0.0 };
// 聽者的速度.
ALfloat ListenerVel[] = { 0.0, 0.0, 0.0 };
// 聽者的方向 (first 3 elements are "at", second 3 are "up")
ALfloat ListenerOri[] = { 0.0, 0.0, -1.0, 0.0, 1.0, 0.0 };
在這一章中,唯一的不同是多了3種將導入Openal系統的不同的聲音效果 。
ALboolean LoadALData()
{
// 載入變量.
ALenum format;
ALsizei size;
ALvoid* data;
ALsizei freq;
ALboolean loop;
// 載入WAV數據.
alGenBuffers(NUM_BUFFERS, Buffers);
if (alGetError() != AL_NO_ERROR)
return AL_FALSE;
alutLoadWAVFile("wavdata/Battle.wav", &format,
&data, &size,
&freq, &loop);
alBufferData(Buffers[BATTLE], format, data, size, freq);
alutUnloadWAV(format, data, size, freq);
alutLoadWAVFile("wavdata/Gun1.wav", &format,
&data, &size,
&freq, &loop);
alBufferData(Buffers[GUN1], format, data, size, freq);
alutUnloadWAV(format, data, size, freq);
alutLoadWAVFile("wavdata/Gun2.wav", &format,
&data, &size,
&freq, &loop);
alBufferData(Buffers[GUN2], format, data, size, freq);
alutUnloadWAV(format, data, size, freq);
// 捆綁源.
alGenSources(NUM_SOURCES, Sources);
if (alGetError() != AL_NO_ERROR)
return AL_FALSE;
alSourcei (Sources[BATTLE], AL_BUFFER, Buffers[BATTLE] );
alSourcef (Sources[BATTLE], AL_PITCH, 1.0 );
alSourcef (Sources[BATTLE], AL_GAIN, 1.0 );
alSourcefv(Sources[BATTLE], AL_POSITION, SourcePos[BATTLE]);
alSourcefv(Sources[BATTLE], AL_VELOCITY, SourceVel[BATTLE]);
alSourcei (Sources[BATTLE], AL_LOOPING, AL_TRUE );
alSourcei (Sources[GUN1], AL_BUFFER, Buffers[GUN1] );
alSourcef (Sources[GUN1], AL_PITCH, 1.0 );
alSourcef (Sources[GUN1], AL_GAIN, 1.0 );
alSourcefv(Sources[GUN1], AL_POSITION, SourcePos[GUN1]);
alSourcefv(Sources[GUN1], AL_VELOCITY, SourceVel[GUN1]);
alSourcei (Sources[GUN1], AL_LOOPING, AL_FALSE );
alSourcei (Sources[GUN2], AL_BUFFER, Buffers[GUN2] );
alSourcef (Sources[GUN2], AL_PITCH, 1.0 );
alSourcef (Sources[GUN2], AL_GAIN, 1.0 );
alSourcefv(Sources[GUN2], AL_POSITION, SourcePos[GUN2]);
alSourcefv(Sources[GUN2], AL_VELOCITY, SourceVel[GUN2]);
alSourcei (Sources[GUN2], AL_LOOPING, AL_FALSE );
// 做錯誤檢測并返回
if( alGetError() != AL_NO_ERROR)
return AL_FALSE;
return AL_TRUE;
}
首先,我們導入文件數據到3個緩沖區,然后把3個緩沖區和3個源鎖在
一起。唯一的不同是文件“battle.wav”在不停止時循環。
void SetListenervalues()
{
alListenerfv(AL_POSITION, ListenerPos);
alListenerfv(AL_VELOCITY, ListenerVel);
alListenerfv(AL_ORIENTATION, ListenerOri);
}
void KillALData()
{
alDeleteBuffers(NUM_BUFFERS, &Buffers[0]);
alDeleteSources(NUM_SOURCES, &Sources[0]);
alutExit();
}
在這段代碼中,我們沒有改變。
int main(int argc, char *argv[])
{
// Initialize OpenAL and clear the error bit.
alutInit(NULL, 0);
alGetError();
// Load the wav data.
if (LoadALData() == AL_FALSE)
return 0;
SetListenervalues();
// Setup an exit procedure.
atexit(KillALData);
// Begin the battle sample to play.
alSourcePlay(Sources[BATTLE]);
// Go through all the sources and check that they are
playing.
// Skip the first source because it is looping anyway (will always
be playing).
ALint play;
while (!kbhit())
{
for (int i = 1; i < NUM_SOURCES; i++)
{
alGetSourcei(Sources[i], AL_SOURCE_STATE,
&play);
if (play != AL_PLAYING)
{
// Pick a random position around the listener to play the
source.
double theta = (double) (rand() % 360) * 3.14 / 180.0;
SourcePos[i][0] = -float(cos(theta));
SourcePos[i][1] = -float(rand()%2);
SourcePos[i][2] = -float(sin(theta));
alSourcefv(Sources[i], AL_POSITION, SourcePos[i] );
alSourcePlay(Sourcev[i]);
}
}
}
return 0;
}
這段是這篇文章最有趣的地方。我們在這里將播放。如果他不播放,我們將在3D空間中選一個點播放(點擊)。
And bang! We are done. As most of you have probably seen, you don't
have to do anything special to play more than one source at a time.
OpenAL will handle all the mixing features to get the sounds right
for their respective distances and velocities. And when it comes
right down to it, isn't that the beauty of OpenAL?
You know that was a lot easier than I thought. I don't know why I
waited so long to write it. Anyway, if anyone reading wants to see
something specific in future tutorials (not necessarily pertaining
to OpenAL, I have quite an extensive knowledge base) drop me a line
at lightonthewater@hotmail.com
I plan to do tutorials on sharing buffers and the Doppler effect in
some later tutorial unless there is request for something else.
Have fun with the code!
ALC
Alut一直為我們做著所有神奇的東西。例如處理音頻設備。ALUT庫為我們提供這些功能,但是一些機靈的程序員想知道他是怎樣工作的。
我們可以這樣想,在一些點上直接用ALC。
在這一章中,我們將講述ALC層,并看一下他是怎樣處理設備的。
ALCdevice* pDevice;
ALCubyte DeviceName[] = "DirectSound3D";
pDevice = alcOpenDevice(DeviceName);
當然,ALC設備是什么?可以這樣想,在共享整個系統下,OPENAL奪取了設備的句柄。在我們用DIRECTSOUND作為音頻設備時,設備能完成的很好。
程序從設備中奪取句柄并為程序準備著。
傳遞NULL給‘alcOpenDevice',他將使ALC用默認設備。
ALCcontext* pContext;
pContext = alcCreateContext(pDevice, NULL);
alcMakeContextCurrent(pContext);
ALC文本描述是什么?
OPENGL程序員能撤消通過不同窗口的狀態管理的控制的精簡文本描述。HGLRC能被挑調用建立多次,使多描述窗口成為可能。并且不同的文本描述狀態可以實現。ALC文本描述工作在相同的原理下。首先,我們告訴他我們用的設備,然后我們做當前的文本描述。理論上你能為不同的窗口建立多個表達文本描述,并且設置不同的狀態變量,以使他們能很好的工作。盡管“表達文本描述”用于可視表達。
你可能也注意到“alcCreateContext'中的第二個變量是NULL。
OPENAL中
下面的變量與他有關。
ALC_FREQUENCY
ALC_REFRESH
ALC_SYNC
你可以調用’alcMakeContextCurrent'替換你建立的多個文本描述。同樣在'alcMakeContextCurrent'中置NULL。他將防止處理其他聲音數據。要意識到當你有多個表達文本描述時,你只能在當前用一個。并且當你的程序要交換使用兩個描述時,必須確定當前使用的描述是在當前。當你想不通過大的檢查,知道用的哪個描述,必須用這些。
ALcontext* pCurContext;
pCurContext = alcGetCurrentContext();
通過文本描述,你能獲取設備。
ALdevice* pCurDevice;
pCurDevice = alcGetContextsDevice(pCurContext);
在我們用文本描述時,我們必須收回用的設備。在處理文本描述時,有更COOL的方法。
alcSuspendContext(pContext);
// 終止pContext.
alcProcessContext(pContext);
// 重置pContext.
當程序停止時,我們必須重置聲音數據到文本描述。當程序暫停時,文本描述中的數據不會產生聲音。在程序運行期間,源或緩沖區的‘lifetime'的有效是由源或緩沖器ID的合法性決定的。
alcMakeContextCurrent(NULL);
alcDestroyContext(pContext);
alcCloseDevice(pDevice);
最后,怎樣清出他呢?當前文本描述被初始化為’NULL‘,描述釋放并且設備句柄交還系統。在這里我們只講了一些ALC的基本功能。
ALenum alcGetError(ALvoid);
ALboolean alcIsExtensionPresent(ALCdevice* device, ALubyte*
extName);
ALvoid* alcGetProcAddress(ALCdevice* device, ALubyte*
funcName);
ALenum alcGetEnumvalue(ALCdevice* device, ALubyte* enumName);
ALubyte* alcGetString(ALCdevice* device, ALenum token);
ALvoid alcGetIntegerv(ALCdevice* device, ALenum token, ALsizei
size, ALint* dest);
這些做什么,我們肯定很清楚,首先,我們用'alcGetError' 檢測錯誤。
下面三個功能是詢問ALC的擴展。這在開始就應計劃。最后alcGetInteger'將返回ALC的版本'ALC_MAJOR_VERSION'or
'ALC_MINOR_VERSION'。
函數'alcGetString'返回下面信息:
ALC_DEFAULT_DEVICE_SPECIFIER
ALC_DEVICE_SPECIFIER
ALC_EXTENSIONS
首先是OPENAL完成的設備的信息。OPENAL 返回"DirectSound3D",
第二個返回"DirectSound" ;
最后一個返回NULL。
Well that's most of Alc for you. I hope it gave you a better
understanding of how OpenAL interacts with the operation system.
You might try writing your own initialization routines so you can
cast off Alut altogether. Either way have fun with it.
See the Java Bindings for OpenAL page for the Java version of this
tutorial - adapted by: Athomas Goldberg
源共享緩沖區
在這一章中,我們將講解如何在你的緩沖區中共享多個源。這是個非常合理,自然的步籌,非常的容易。你完全可以跳過這章。但對于愿意讀這一章的朋友,你將發現他非常有趣。我們將準備好ALC層以便我們能用第四章的知識。
讓我們開始吧,在這一章中,我們將用的矢量來自標準模板庫,因此,確定你是否安裝了他,最好還有一些關于他的知識。在這一章中,我不會講
STL。
// 表明緩沖區.
#define THUNDER 0
#define WATERDROP 1
#define STREAM 2
#define RAIN 3
#define CHIMES 4
#define OCEAN 5
#define NUM_BUFFERS 6
// 存貯聲音數據.
ALuint Buffers[NUM_BUFFERS];
// 播放多個聲音的源的矢量表
vector Sources;
首先,我寫出了我們用于表明緩沖區數組的一些指令。我們將用幾個WAV文件,因此我們需要幾個緩沖區。我們將用一個STL矢量代替用于存貯源的一個數組。我們能做這些是因為他讓我們能有一個源的動態數。我們能一直添加源到場景,直到OPENAL脫離他們運行。
ALboolean InitOpenAL()
{
ALCdevice* pDevice;
ALCcontext* pContext;
ALCubyte* deviceSpecifier;
ALCubyte deviceName[] = "DirectSound3D";
// 得到設備句柄
pDevice = alcOpenDevice(deviceName);
// 得到設備說明.
deviceSpecifier = alcGetString(pDevice, ALC_DEVICE_SPECIFIER);
printf("Using device '%s'.\n", szDeviceSpecifier);
// 建立聲音文本描述.
pContext = alcCreateContext(pDevice, NULL);
// 設置行為文本描述.
alcMakeContextCurrent(pContext);
// 檢查錯誤.
if (alcGetError() != ALC_NO_ERROR)
return AL_FALSE;
return AL_TRUE;
}
這是來自上一章的代碼。首先,我們得到
"DirectSound3D"設備的句柄,然后獲得用于程序的表明文本描述。這個文本描述設置當前,函數將檢查在我們返回成功前,是否出錯。
void ExitOpenAL()
{
ALCcontext* pCurContext;
ALCdevice* pCurDevice;
// 得到當前文本描述
pCurContext = alcGetCurrentContext();
// 得到用于當前文本描述的設備?
pCurDevice = alcGetContextsDevice(pCurContext);
// 重置當前文本描述為NULL
alcMakeContextCurrent(NULL);
//釋放文本描述和設備
alcDestroyContext(pCurContext);
alcCloseDevice(pCurDevice);
}
我們用和釋放的文本描述和設備將收回。另外,在OPENAL暫停程序時,設置當前文本描述為NULL。但是這些都不可預料。如果你用了多個文本描述,你也許需要更好的方法來做這些事。我將介紹一些比較好的方法。
ALboolean LoadALData()
{
// 導入的變量
ALenum format;
ALsizei size;
ALvoid* data;
ALsizei freq;
ALboolean loop;
// 裝載WAV文件到緩沖區
alGenBuffers(NUM_BUFFERS, Buffers);
if(alGetError() != AL_NO_ERROR)
return AL_FALSE;
alutLoadWAVFile("wavdata/thunder.wav", &format,
&data, &size,
&freq, &loop);
alBufferData(Buffers[THUNDER], format, data, size, freq);
alutUnloadWAV(format, data, size, freq);
alutLoadWAVFile("wavdata/waterdrop.wav", &format,
&data, &size,
&freq, &loop);
alBufferData(Buffers[WATERDROP], format, data, size, freq);
alutUnloadWAV(format, data, size, freq);
alutLoadWAVFile("wavdata/stream.wav", &format,
&data, &size,
&freq, &loop);
alBufferData(Buffers[STREAM], format, data, size, freq);
alutUnloadWAV(format, data, size, freq);
alutLoadWAVFile("wavdata/rain.wav", &format,
&data, &size,
&freq, &loop);
alBufferData(Buffers[RAIN], format, data, size, freq);
alutUnloadWAV(format, data, size, freq);
alutLoadWAVFile("wavdata/ocean.wav", &format,
&data, &size,
&freq, &loop);
alBufferData(Buffers[OCEAN], format, data, size, freq);
alutUnloadWAV(format, data, size, freq);
alutLoadWAVFile("wavdata/chimes.wav", &format,
&data, &size,
&freq, &loop);
alBufferData(Buffers[CHIMES], format, data, size, freq);
alutUnloadWAV(format, data, size, freq);
// 錯誤檢測
if (alGetError() != AL_NO_ERROR)
return AL_FALSE;
return AL_TRUE;
}
在這個函數中,我將移動產生源,這是因為我們將一個一個的初始化。
void AddSource(ALint type)
{
ALuint source;
alGenSources(1, &source);
if (alGetError() != AL_NO_ERROR)
{
printf("Error generating audio source.");
exit(-1);
}
alSourcei (source, AL_BUFFER, Buffers[type]);
alSourcef (source, AL_PITCH, 1.0 );
alSourcef (source, AL_GAIN, 1.0 );
alSourcefv(source, AL_POSITION, SourcePos );
alSourcefv(source, AL_VELOCITY, SourceVel );
alSourcei (source, AL_LOOPING, AL_TRUE );
alSourcePlay(source);
Sources.push_back(source);
}
這個函數將產生源,他將為我們載入的緩沖區中的一個產生一個源。用‘TYPE’表示緩沖區,我們文章開始建立的指令將做這些事情。然后做錯誤檢測,確定源能播放。如果源不能將退出。
void KillALData()
{
for (vector::iterator iter =
Sources.begin(); iter != Sources.end(); ++iter)
alDeleteSources(1, iter);
Sources.clear();
alDeleteBuffers(NUM_BUFFERS, Buffers);
ExitOpenAL();
}
這個函數將修改STL表。我們不得不刪除每個源并且清除表上的內容。
ALubyte c = ' ';
while (c != 'q')
{
c = getche();
switch (c)
{
case 'w': AddSource(WATERDROP); break;
case 't': AddSource(THUNDER); break;
case 's': AddSource(STREAM); break;
case 'r': AddSource(RAIN); break;
case 'o': AddSource(OCEAN); break;
case 'c': AddSource(CHIMES); break;
};
}
這是程序的主函數,他等待鍵盤輸入,建立源,播放聲音。
The program can be expanded for using more wav files, and have the
added feature of placing
the sources around the scene in arbitrary positions. You could even
allow for sources to
play with a given frequency rather than have them loop. However
this would require GUI routines
that go beyond the scope of the tutorial. A full featured
"Weathering Engine" would be a nifty
program to make though. ;)
高級導入和錯誤處理
雖然現在,我們能做出一些漂亮的東西,但是這些都沒有要求我們精確的處理他們。原因是我們寫的代碼是為了便于學習。因此,我們將移進一些高級的東西。最重要的是我們將學習更高級的處理錯誤的方法。我們也將重新改寫載入聲音數據的方法。我們首先考慮這些函數將做什么。
string GetALErrorString(ALenum err);
ALuint LoadALBuffer(string path);
ALuint GetLoadedALBuffer(string path);
ALuint LoadALSample(string path, bool loop);
void KillALLoadedData();
bool LoadALData();
void KillALData();
vector LoadedFiles; //
文件路徑
vector Buffers; // 緩沖區.
vector Sources; // 源.
看一下這個函數,想一下他做什么。我們試著建立一個關于緩沖區和源的系統。我們能調用來自文件的源并且系統能處理緩沖區的建立,因此,我們不用復制緩沖區。系統將處理緩沖區并且將有效的處理資源。
string GetALErrorString(ALenum err)
{
switch(err)
{
case AL_NO_ERROR:
return string("AL_NO_ERROR");
break;
case AL_INVALID_NAME:
return string("AL_INVALID_NAME");
break;
case AL_INVALID_ENUM:
return string("AL_INVALID_ENUM");
break;
case AL_INVALID_value:
return string("AL_INVALID_value");
break;
case AL_INVALID_OPERATION:
return string("AL_INVALID_OPERATION");
break;
case AL_OUT_OF_MEMORY:
return string("AL_OUT_OF_MEMORY");
break;
};
}
函數的功能是轉換錯誤代碼為字符。OPENAL SDK說返回'AL_OUT_OF_MEMORY'
是錯誤的,我們應認真對待所有錯誤,使我們的代碼用最新的版本處理數據。
string GetALCErrorString(ALenum err)
{
switch(err)
{
case ALC_NO_ERROR:
return string("AL_NO_ERROR");
break;
case ALC_INVALID_DEVICE:
return string("ALC_INVALID_DEVICE");
break;
case ALC_INVALID_CONTEXT:
return string("ALC_INVALID_CONTEXT");
break;
case ALC_INVALID_ENUM:
return string("ALC_INVALID_ENUM");
break;
case ALC_INVALID_value:
return string("ALC_INVALID_value");
break;
case ALC_OUT_OF_MEMORY:
return string("ALC_OUT_OF_MEMORY");
break;
};
}
這個函數的功能是說明ALC錯誤。OPENAL和ALC共享ID,但是他們在一些功能上不相等。函數
'alGetError'應注意:OPENAL
SDK定義一次只能得到一個錯誤。當函數被調用時,他返回他得到的第一個錯誤,并且清錯誤為'AL_NO_ERROR'
ALuint LoadALBuffer(string path)
{
// Variables to store data which defines the buffer.
ALenum format;
ALsizei size;
ALvoid* data;
ALsizei freq;
ALboolean loop;
// 緩沖區ID和錯誤檢測變量
ALuint buffer;
ALenum result;
// 產生緩沖區,看他是否成功建立.
alGenBuffers(1, &buffer);
if ((result = alGetError()) != AL_NO_ERROR)
throw GetALErrorString(result);
// 讀WAV數據,檢測是否成功。
alutLoadWAVFile(szFilePath, &format,
&data, &size,
&freq, &loop);
if ((result = alGetError()) != AL_NO_ERROR)
throw GetALErrorString(result);
// 裝載WAV數據,檢測是否成功
alBufferData(buffer, format, data, size, freq);
if ((result = alGetError()) != AL_NO_ERROR)
throw GetALErrorString(result);
// 出去臨時數據
alutUnloadWAV(format, data, size, freq);
if ((result = alGetError()) != AL_NO_ERROR)
throw GetALErrorString(result);
//返回緩沖區ID
return buffer;
}
在導入數據時,我們做了錯誤檢測。沒有任何錯誤將通過。當數據導入時,沒有足夠的內存,WAV文件可能不退出,或者OPENAL函數中的錯誤數據將產生錯誤。
ALuint GetLoadedALBuffer(string path)
{
int count = 0; // 'count' 表明緩沖區列表
ALuint buffer; // 用于導入緩沖區的緩沖區ID
// 重復列表中的每個文件
for(vector::iterator iter =
LoadedFiles.begin(); iter != LoadedFiles.end(); ++iter,
count++)
{
// 如果文件已經導入,返回他的緩沖區ID.
if(*iter == path)
return Buffers[count];
}
// 如果文件是新的,我們將為他建立緩沖區.
buffer = LoadALBuffer(path);
// 添加緩沖區到列表,記錄他已添加.
Buffers.push_back(buffer);
LoadedFiles.push_back(path);
return buffer;
}
人們的麻煩通常在這里,但他確實不是很復雜。我們通過包含文件路徑的列表來檢索。如果其中一個符合要求,我們直接返回他的ID到緩沖區。我們通過這個函數導入我們的文件,這樣就避免了復制時的浪費。每個文件應該保證在自己的列表中。'Buffers'表類士于'LoadedFiles'表。
ALuint LoadALSample(string path, bool loop)
{
ALuint source;
ALuint buffer;
ALenum result;
// 得到文件緩沖區ID
buffer = GetLoadedALBuffer(path);
// 產生源.
alGenSources(1 &source);
if ((result = alGetError()) != AL_NO_ERROR)
throw GetALErrorString(result);
// 設置源屬性.
alSourcei (source, AL_BUFFER, buffer );
alSourcef (source, AL_PITCH, 1.0 );
alSourcef (source, AL_GAIN, 1.0 );
alSourcefv(source, AL_POSITION, SourcePos);
alSourcefv(source, AL_VELOCITY, SourceVel);
alSourcei (source, AL_LOOPING, loop );
// 保存源ID.
Sources.push_back(source);
// 返回源ID.
return source;
}
現在我們已經建立處理緩沖區的系統,我們需要得到源的伸展。在這里,我們得到了導入文件的緩沖區ID。這個緩沖區是一個新源,我們保存他并返回。
void KillALLoadedData()
{
LoadedFiles.clear();
}
'gLoadedFilesv'存儲在導入緩沖區的WAV文件的路徑下,我們要處理他。
// 源ID's.
ALuint phaser1;
ALuint phaser2;
void LoadALData()
{
// 你的應用在這里,不用擔心緩沖區。
phaser1 = LoadALSample("wavdata/phaser.wav", false);
phaser2 = LoadALSample("wavdata/phaser.wav", true);
KillLoadedALData();
}
他表示用于程序的所有的WAV應用的程序。我們能調用導入相同的WAV文件到不同的源,'phaser.wav'
的緩沖區建立了一次,'gPhaser1' and 'gPhaser2'
用于背景音樂的緩沖區。不用處理緩沖區因為系統會自動處理。
void KillALData()
{
// 釋放所有的緩沖區數據.
for (vector::iterator iter =
Buffers.begin(); iter != Buffers.end(); ++iter)
alDeleteBuffers(1, iter);
// 釋放所有的源數據.
for (vector::iterator iter =
Sources.begin(); iter != Sources.end(); ++iter)
alDeleteBuffers(1, iter);
// 清除列表.
Buffers.clear();
Sources.clear();
}
我們已完成了前述的工作。然后就是釋放他們。
try
{
InitOpenAL();
LoadALData();
}
catch(string err)
{
cout << "OpenAL error: "
<< err.c_str()
<< endl;
}
如果導入源時出錯,我們將改正他。這將借助程序返回的報告。
That's it. A more advanced way of reporting errors, and a more
robust way of loading your wav files. We may find we need to do
some modifications in the future to allow for more flexibility, but
for now we will be using this source for basic file loading in
future tutorials. Expect future tutorials to expand on this
code.
總結
以上是生活随笔為你收集整理的Java opengl openal_项目里用到了openal,特分享一下openal全教程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 元音和辅音有哪些区别简答题(元音和辅音有
- 下一篇: java通过jxl处理execl空行_j