Java opengl openal_项目里用到了openal,特分享一下openal全教程
// 存儲(chǔ)聲音數(shù)據(jù)
ALuint Buffer;
// 用于播放聲音
ALuint Source;
這是程序處理結(jié)構(gòu)的初始化。在OPENAL中三種不同的結(jié)構(gòu),所有關(guān)于聲音播放和
聲音數(shù)據(jù)存儲(chǔ)在一個(gè)內(nèi)存中,源(source)是指向放聲音的空間。明白源是非常
的重要。源只播放內(nèi)存中的背景聲音數(shù)據(jù)。源也給出了特殊的屬性如位置和速度。
第三個(gè)對(duì)象是聽者,用戶就是那唯一的聽者。聽者屬性屬于源屬性,決定如何
聽聲音。例如,不同位置將決定聲音的速度。
// 源聲音的位置
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 };
在上面的代碼中,我們定義了源和聽者對(duì)象的位置和速度。這些數(shù)組是基于笛
卡兒坐標(biāo)的矢量。你能很容易用結(jié)構(gòu)或類做相同的事情。
ALboolean LoadALData()
{
// 載入變量.
ALenum
format;
ALsizei
size;
ALvoid*
data;
ALsizei
freq;
ALboolean
loop;
在這里我們建立一個(gè)函數(shù)用于從一個(gè)文件中載入聲音數(shù)據(jù)。變量用于存儲(chǔ)適合
我們的ALUT信息。
// 載入WAV數(shù)據(jù)
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);
函數(shù)alGenBufers用于建立對(duì)象內(nèi)存并把他們存儲(chǔ)在我們定義的變量中。然后判斷
數(shù)據(jù)是否存儲(chǔ)。
ALUT庫為我們打開文件,提供我們建立內(nèi)存所需的信息,并且在我們歸屬所有
數(shù)據(jù)到內(nèi)存后,她將處理這些數(shù)據(jù)。
// 捆綁源
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?);
我們用建立內(nèi)存對(duì)象的方法建立了源對(duì)象。然后,我們定義源屬性用于錄放。
最重要的屬性是她用的內(nèi)存。這告訴源用于錄放。因此,我們只有捆綁她。同時(shí),
我們也告訴她我們定義的源位置和速度。
// 做錯(cuò)誤檢測并返回
if
(alGetError() == AL_NO_ERROR)
return AL_TRUE;
return
AL_FALSE;
在函數(shù)的結(jié)尾,我們將做更多的檢測,以確定她的正確。
void SetListenervalues()
{
alListenerfv(AL_POSITION,?ListenerPos);
alListenerfv(AL_VELOCITY,?ListenerVel);
alListenerfv(AL_ORIENTATION, ListenerOri);
}
我們建立一個(gè)函數(shù)用于更新聽者速度。
void KillALData()
{
alDeleteBuffers(1, &Buffer);
alDeleteSources(1, &Source);
alutExit();
}
這是一個(gè)關(guān)閉函數(shù),用于釋放內(nèi)存和音頻設(shè)備。
int main(int argc, char *argv[])
{
//
初始OPENAL并清錯(cuò)誤字節(jié)
alutInit(&argc, argv);
alGetError();
函數(shù)alutInit將安裝ALC需要的東西。ALUT通過ALC并設(shè)置她為當(dāng)前建立OPENAL
環(huán)境描述。在WINDOWS平臺(tái)上初始DIRECTSOUND。然后用‘GLGETERROR’檢測錯(cuò)誤。
// 載入WAV數(shù)據(jù)
if
(LoadALData() == AL_FALSE)
return -1;
SetListenervalues();
//
設(shè)置退出函數(shù)
atexit(KillALData);
我們將檢測WAV文件是否正確載入。如果沒有退出程序。
正確后,更新聽者參數(shù),最后退出。
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教程(二)
循環(huán)和消退
希望你覺得上一章有用,這一章將更容易。
#include
#include
#include
#include
#include
#include
#include
// 存儲(chǔ)聲音數(shù)據(jù).
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’現(xiàn)在是0.1.
ALboolean LoadALData()
{
// 載入變量
ALenum format;
ALsizei size;
ALvoid* data;
ALsizei freq;
ALboolean loop;
// 載入WAV數(shù)據(jù).
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 );
// 做錯(cuò)誤檢測并返回
if (alGetError() != AL_NO_ERROR)
return AL_FALSE;
return AL_TRUE;
}
在這一節(jié)中有兩處改變,首先是導(dǎo)入“FOOTSTES。WAV”,設(shè)置源‘AL_LOOPING’
為‘AL_TRUE’。這意味著源播放直到停止時(shí)結(jié)束。他將不斷的循環(huán)播放。
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并清錯(cuò)誤字節(jié)
alutInit(NULL,0);
alGetError();
// 載入WAV數(shù)據(jù).
if (LoadALData() == AL_FALSE)
return 0;
SetListenervalues();
// 設(shè)置退出函數(shù).
atexit(KillALData);
// 開始源的播放.
alSourcePlay(Source);
//循環(huán)
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;
}
這里唯一的改變是增加了一個(gè)循環(huán)。他將代替播放和停止按鈕。
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
多源
你好,在這一章中,我們將在上一章的例程中加入一些元素,使他能同時(shí)播放超過一種的音樂。
通常在一個(gè)優(yōu)秀的游戲中有各種不同的音夾(clip),這是怎樣實(shí)現(xiàn)的呢?
下面將介紹。
#include
#include
#include
#include
#include
#include
#include
// 我們需要的最大的數(shù)據(jù)緩沖.
#define NUM_BUFFERS 3
// 我們需要放三種聲音.
#define NUM_SOURCES 3
// 緩沖和源標(biāo)志.
#define BATTLE 0
#define GUN1 1
#define GUN2 2
// 存儲(chǔ)聲音數(shù)據(jù).
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種將導(dǎo)入Openal系統(tǒng)的不同的聲音效果 。
ALboolean LoadALData()
{
// 載入變量.
ALenum format;
ALsizei size;
ALvoid* data;
ALsizei freq;
ALboolean loop;
// 載入WAV數(shù)據(jù).
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 );
// 做錯(cuò)誤檢測并返回
if( alGetError() != AL_NO_ERROR)
return AL_FALSE;
return AL_TRUE;
}
首先,我們導(dǎo)入文件數(shù)據(jù)到3個(gè)緩沖區(qū),然后把3個(gè)緩沖區(qū)和3個(gè)源鎖在
一起。唯一的不同是文件“battle.wav”在不停止時(shí)循環(huán)。
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空間中選一個(gè)點(diǎn)播放(點(diǎn)擊)。
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一直為我們做著所有神奇的東西。例如處理音頻設(shè)備。ALUT庫為我們提供這些功能,但是一些機(jī)靈的程序員想知道他是怎樣工作的。
我們可以這樣想,在一些點(diǎn)上直接用ALC。
在這一章中,我們將講述ALC層,并看一下他是怎樣處理設(shè)備的。
ALCdevice* pDevice;
ALCubyte DeviceName[] = "DirectSound3D";
pDevice = alcOpenDevice(DeviceName);
當(dāng)然,ALC設(shè)備是什么?可以這樣想,在共享整個(gè)系統(tǒng)下,OPENAL奪取了設(shè)備的句柄。在我們用DIRECTSOUND作為音頻設(shè)備時(shí),設(shè)備能完成的很好。
程序從設(shè)備中奪取句柄并為程序準(zhǔn)備著。
傳遞NULL給‘a(chǎn)lcOpenDevice',他將使ALC用默認(rèn)設(shè)備。
ALCcontext* pContext;
pContext = alcCreateContext(pDevice, NULL);
alcMakeContextCurrent(pContext);
ALC文本描述是什么?
OPENGL程序員能撤消通過不同窗口的狀態(tài)管理的控制的精簡文本描述。HGLRC能被挑調(diào)用建立多次,使多描述窗口成為可能。并且不同的文本描述狀態(tài)可以實(shí)現(xiàn)。ALC文本描述工作在相同的原理下。首先,我們告訴他我們用的設(shè)備,然后我們做當(dāng)前的文本描述。理論上你能為不同的窗口建立多個(gè)表達(dá)文本描述,并且設(shè)置不同的狀態(tài)變量,以使他們能很好的工作。盡管“表達(dá)文本描述”用于可視表達(dá)。
你可能也注意到“alcCreateContext'中的第二個(gè)變量是NULL。
OPENAL中
下面的變量與他有關(guān)。
ALC_FREQUENCY
ALC_REFRESH
ALC_SYNC
你可以調(diào)用’alcMakeContextCurrent'替換你建立的多個(gè)文本描述。同樣在'alcMakeContextCurrent'中置NULL。他將防止處理其他聲音數(shù)據(jù)。要意識(shí)到當(dāng)你有多個(gè)表達(dá)文本描述時(shí),你只能在當(dāng)前用一個(gè)。并且當(dāng)你的程序要交換使用兩個(gè)描述時(shí),必須確定當(dāng)前使用的描述是在當(dāng)前。當(dāng)你想不通過大的檢查,知道用的哪個(gè)描述,必須用這些。
ALcontext* pCurContext;
pCurContext = alcGetCurrentContext();
通過文本描述,你能獲取設(shè)備。
ALdevice* pCurDevice;
pCurDevice = alcGetContextsDevice(pCurContext);
在我們用文本描述時(shí),我們必須收回用的設(shè)備。在處理文本描述時(shí),有更COOL的方法。
alcSuspendContext(pContext);
// 終止pContext.
alcProcessContext(pContext);
// 重置pContext.
當(dāng)程序停止時(shí),我們必須重置聲音數(shù)據(jù)到文本描述。當(dāng)程序暫停時(shí),文本描述中的數(shù)據(jù)不會(huì)產(chǎn)生聲音。在程序運(yùn)行期間,源或緩沖區(qū)的‘lifetime'的有效是由源或緩沖器ID的合法性決定的。
alcMakeContextCurrent(NULL);
alcDestroyContext(pContext);
alcCloseDevice(pDevice);
最后,怎樣清出他呢?當(dāng)前文本描述被初始化為’NULL‘,描述釋放并且設(shè)備句柄交還系統(tǒng)。在這里我們只講了一些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' 檢測錯(cuò)誤。
下面三個(gè)功能是詢問ALC的擴(kuò)展。這在開始就應(yīng)計(jì)劃。最后alcGetInteger'將返回ALC的版本'ALC_MAJOR_VERSION'or
'ALC_MINOR_VERSION'。
函數(shù)'alcGetString'返回下面信息:
ALC_DEFAULT_DEVICE_SPECIFIER
ALC_DEVICE_SPECIFIER
ALC_EXTENSIONS
首先是OPENAL完成的設(shè)備的信息。OPENAL 返回"DirectSound3D",
第二個(gè)返回"DirectSound" ;
最后一個(gè)返回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
源共享緩沖區(qū)
在這一章中,我們將講解如何在你的緩沖區(qū)中共享多個(gè)源。這是個(gè)非常合理,自然的步籌,非常的容易。你完全可以跳過這章。但對(duì)于愿意讀這一章的朋友,你將發(fā)現(xiàn)他非常有趣。我們將準(zhǔn)備好ALC層以便我們能用第四章的知識(shí)。
讓我們開始吧,在這一章中,我們將用的矢量來自標(biāo)準(zhǔn)模板庫,因此,確定你是否安裝了他,最好還有一些關(guān)于他的知識(shí)。在這一章中,我不會(huì)講
STL。
// 表明緩沖區(qū).
#define THUNDER 0
#define WATERDROP 1
#define STREAM 2
#define RAIN 3
#define CHIMES 4
#define OCEAN 5
#define NUM_BUFFERS 6
// 存貯聲音數(shù)據(jù).
ALuint Buffers[NUM_BUFFERS];
// 播放多個(gè)聲音的源的矢量表
vector Sources;
首先,我寫出了我們用于表明緩沖區(qū)數(shù)組的一些指令。我們將用幾個(gè)WAV文件,因此我們需要幾個(gè)緩沖區(qū)。我們將用一個(gè)STL矢量代替用于存貯源的一個(gè)數(shù)組。我們能做這些是因?yàn)樗屛覀兡苡幸粋€(gè)源的動(dòng)態(tài)數(shù)。我們能一直添加源到場景,直到OPENAL脫離他們運(yùn)行。
ALboolean InitOpenAL()
{
ALCdevice* pDevice;
ALCcontext* pContext;
ALCubyte* deviceSpecifier;
ALCubyte deviceName[] = "DirectSound3D";
// 得到設(shè)備句柄
pDevice = alcOpenDevice(deviceName);
// 得到設(shè)備說明.
deviceSpecifier = alcGetString(pDevice, ALC_DEVICE_SPECIFIER);
printf("Using device '%s'.\n", szDeviceSpecifier);
// 建立聲音文本描述.
pContext = alcCreateContext(pDevice, NULL);
// 設(shè)置行為文本描述.
alcMakeContextCurrent(pContext);
// 檢查錯(cuò)誤.
if (alcGetError() != ALC_NO_ERROR)
return AL_FALSE;
return AL_TRUE;
}
這是來自上一章的代碼。首先,我們得到
"DirectSound3D"設(shè)備的句柄,然后獲得用于程序的表明文本描述。這個(gè)文本描述設(shè)置當(dāng)前,函數(shù)將檢查在我們返回成功前,是否出錯(cuò)。
void ExitOpenAL()
{
ALCcontext* pCurContext;
ALCdevice* pCurDevice;
// 得到當(dāng)前文本描述
pCurContext = alcGetCurrentContext();
// 得到用于當(dāng)前文本描述的設(shè)備?
pCurDevice = alcGetContextsDevice(pCurContext);
// 重置當(dāng)前文本描述為NULL
alcMakeContextCurrent(NULL);
//釋放文本描述和設(shè)備
alcDestroyContext(pCurContext);
alcCloseDevice(pCurDevice);
}
我們用和釋放的文本描述和設(shè)備將收回。另外,在OPENAL暫停程序時(shí),設(shè)置當(dāng)前文本描述為NULL。但是這些都不可預(yù)料。如果你用了多個(gè)文本描述,你也許需要更好的方法來做這些事。我將介紹一些比較好的方法。
ALboolean LoadALData()
{
// 導(dǎo)入的變量
ALenum format;
ALsizei size;
ALvoid* data;
ALsizei freq;
ALboolean loop;
// 裝載WAV文件到緩沖區(qū)
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);
// 錯(cuò)誤檢測
if (alGetError() != AL_NO_ERROR)
return AL_FALSE;
return AL_TRUE;
}
在這個(gè)函數(shù)中,我將移動(dòng)產(chǎn)生源,這是因?yàn)槲覀儗⒁粋€(gè)一個(gè)的初始化。
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);
}
這個(gè)函數(shù)將產(chǎn)生源,他將為我們載入的緩沖區(qū)中的一個(gè)產(chǎn)生一個(gè)源。用‘TYPE’表示緩沖區(qū),我們文章開始建立的指令將做這些事情。然后做錯(cuò)誤檢測,確定源能播放。如果源不能將退出。
void KillALData()
{
for (vector::iterator iter =
Sources.begin(); iter != Sources.end(); ++iter)
alDeleteSources(1, iter);
Sources.clear();
alDeleteBuffers(NUM_BUFFERS, Buffers);
ExitOpenAL();
}
這個(gè)函數(shù)將修改STL表。我們不得不刪除每個(gè)源并且清除表上的內(nèi)容。
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;
};
}
這是程序的主函數(shù),他等待鍵盤輸入,建立源,播放聲音。
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. ;)
高級(jí)導(dǎo)入和錯(cuò)誤處理
雖然現(xiàn)在,我們能做出一些漂亮的東西,但是這些都沒有要求我們精確的處理他們。原因是我們寫的代碼是為了便于學(xué)習(xí)。因此,我們將移進(jìn)一些高級(jí)的東西。最重要的是我們將學(xué)習(xí)更高級(jí)的處理錯(cuò)誤的方法。我們也將重新改寫載入聲音數(shù)據(jù)的方法。我們首先考慮這些函數(shù)將做什么。
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; // 緩沖區(qū).
vector Sources; // 源.
看一下這個(gè)函數(shù),想一下他做什么。我們試著建立一個(gè)關(guān)于緩沖區(qū)和源的系統(tǒng)。我們能調(diào)用來自文件的源并且系統(tǒng)能處理緩沖區(qū)的建立,因此,我們不用復(fù)制緩沖區(qū)。系統(tǒng)將處理緩沖區(qū)并且將有效的處理資源。
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;
};
}
函數(shù)的功能是轉(zhuǎn)換錯(cuò)誤代碼為字符。OPENAL SDK說返回'AL_OUT_OF_MEMORY'
是錯(cuò)誤的,我們應(yīng)認(rèn)真對(duì)待所有錯(cuò)誤,使我們的代碼用最新的版本處理數(shù)據(jù)。
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;
};
}
這個(gè)函數(shù)的功能是說明ALC錯(cuò)誤。OPENAL和ALC共享ID,但是他們在一些功能上不相等。函數(shù)
'alGetError'應(yīng)注意:OPENAL
SDK定義一次只能得到一個(gè)錯(cuò)誤。當(dāng)函數(shù)被調(diào)用時(shí),他返回他得到的第一個(gè)錯(cuò)誤,并且清錯(cuò)誤為'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;
// 緩沖區(qū)ID和錯(cuò)誤檢測變量
ALuint buffer;
ALenum result;
// 產(chǎn)生緩沖區(qū),看他是否成功建立.
alGenBuffers(1, &buffer);
if ((result = alGetError()) != AL_NO_ERROR)
throw GetALErrorString(result);
// 讀WAV數(shù)據(jù),檢測是否成功。
alutLoadWAVFile(szFilePath, &format,
&data, &size,
&freq, &loop);
if ((result = alGetError()) != AL_NO_ERROR)
throw GetALErrorString(result);
// 裝載WAV數(shù)據(jù),檢測是否成功
alBufferData(buffer, format, data, size, freq);
if ((result = alGetError()) != AL_NO_ERROR)
throw GetALErrorString(result);
// 出去臨時(shí)數(shù)據(jù)
alutUnloadWAV(format, data, size, freq);
if ((result = alGetError()) != AL_NO_ERROR)
throw GetALErrorString(result);
//返回緩沖區(qū)ID
return buffer;
}
在導(dǎo)入數(shù)據(jù)時(shí),我們做了錯(cuò)誤檢測。沒有任何錯(cuò)誤將通過。當(dāng)數(shù)據(jù)導(dǎo)入時(shí),沒有足夠的內(nèi)存,WAV文件可能不退出,或者OPENAL函數(shù)中的錯(cuò)誤數(shù)據(jù)將產(chǎn)生錯(cuò)誤。
ALuint GetLoadedALBuffer(string path)
{
int count = 0; // 'count' 表明緩沖區(qū)列表
ALuint buffer; // 用于導(dǎo)入緩沖區(qū)的緩沖區(qū)ID
// 重復(fù)列表中的每個(gè)文件
for(vector::iterator iter =
LoadedFiles.begin(); iter != LoadedFiles.end(); ++iter,
count++)
{
// 如果文件已經(jīng)導(dǎo)入,返回他的緩沖區(qū)ID.
if(*iter == path)
return Buffers[count];
}
// 如果文件是新的,我們將為他建立緩沖區(qū).
buffer = LoadALBuffer(path);
// 添加緩沖區(qū)到列表,記錄他已添加.
Buffers.push_back(buffer);
LoadedFiles.push_back(path);
return buffer;
}
人們的麻煩通常在這里,但他確實(shí)不是很復(fù)雜。我們通過包含文件路徑的列表來檢索。如果其中一個(gè)符合要求,我們直接返回他的ID到緩沖區(qū)。我們通過這個(gè)函數(shù)導(dǎo)入我們的文件,這樣就避免了復(fù)制時(shí)的浪費(fèi)。每個(gè)文件應(yīng)該保證在自己的列表中。'Buffers'表類士于'LoadedFiles'表。
ALuint LoadALSample(string path, bool loop)
{
ALuint source;
ALuint buffer;
ALenum result;
// 得到文件緩沖區(qū)ID
buffer = GetLoadedALBuffer(path);
// 產(chǎn)生源.
alGenSources(1 &source);
if ((result = alGetError()) != AL_NO_ERROR)
throw GetALErrorString(result);
// 設(shè)置源屬性.
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;
}
現(xiàn)在我們已經(jīng)建立處理緩沖區(qū)的系統(tǒng),我們需要得到源的伸展。在這里,我們得到了導(dǎo)入文件的緩沖區(qū)ID。這個(gè)緩沖區(qū)是一個(gè)新源,我們保存他并返回。
void KillALLoadedData()
{
LoadedFiles.clear();
}
'gLoadedFilesv'存儲(chǔ)在導(dǎo)入緩沖區(qū)的WAV文件的路徑下,我們要處理他。
// 源ID's.
ALuint phaser1;
ALuint phaser2;
void LoadALData()
{
// 你的應(yīng)用在這里,不用擔(dān)心緩沖區(qū)。
phaser1 = LoadALSample("wavdata/phaser.wav", false);
phaser2 = LoadALSample("wavdata/phaser.wav", true);
KillLoadedALData();
}
他表示用于程序的所有的WAV應(yīng)用的程序。我們能調(diào)用導(dǎo)入相同的WAV文件到不同的源,'phaser.wav'
的緩沖區(qū)建立了一次,'gPhaser1' and 'gPhaser2'
用于背景音樂的緩沖區(qū)。不用處理緩沖區(qū)因?yàn)橄到y(tǒng)會(huì)自動(dòng)處理。
void KillALData()
{
// 釋放所有的緩沖區(qū)數(shù)據(jù).
for (vector::iterator iter =
Buffers.begin(); iter != Buffers.end(); ++iter)
alDeleteBuffers(1, iter);
// 釋放所有的源數(shù)據(jù).
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;
}
如果導(dǎo)入源時(shí)出錯(cuò),我們將改正他。這將借助程序返回的報(bào)告。
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.
總結(jié)
以上是生活随笔為你收集整理的Java opengl openal_项目里用到了openal,特分享一下openal全教程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 元音和辅音有哪些区别简答题(元音和辅音有
- 下一篇: java通过jxl处理execl空行_j