openal全教程

 openal教程(一)
簡介
   歡迎來到OPENAL的世界!OPENAL一直在不斷的創新,幾乎沒有一個API能達到
她的全部潛能。一個很大的原因是因爲hardware加速建立在特殊的版卡上。然而,
Creative Labs是APENAL的主要支持者,同時也是最大聲卡廠商之一。OPENAL的另
一個主要支持者LOKI。
   OPENAL不是商業產品,那樣做限制了她的發展。我只知道再PC遊戲中用OPENAL。
OPENAL有許多的潛能,有許多的聲音庫工作在最地層的hardware上。但OPENAL的
設計者經過無數的測試使她成爲一個高級的API。她的風格是自由的,不同的編碼
風格和hardware部件將充分運用她的功能。有OPENGL編程精練的人將很快掌握OPEN
AL。OPENAL有建立3D環境音效的能力。
   OPENAL是very cool,她是一個非常清晰的API並且能熔入你的代碼。你將做出
很COOL的音效,下面讓我們進入COOL COOL的OPENAL世界。
   #include <conio.h>
   #include <stdlib.h>
   #include <al/al.h>
   #include <al/alc.h>
   #include <al/alu.h>
   #include <al/alut.h>
  你會發現再OPENAL頭和OPENGL頭的定義上有許多的相似之處。除了“al.h","al
u.h","alut.h"與”gl.h","glu.h","glut.h"相似,還增加了一個"alc.h".ALC(
AUDIO LIBRARY CONTEXT)處理聲音通過不同的平臺,她也處理你在幾個窗口下共
享設備的環境。
  // 存儲聲音數據
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 [email protected] 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<ALuint> 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<ALuint>::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);
/*
* 1) 識別錯誤代碼
* 2)返回錯誤
*/


ALuint LoadALBuffer(string path);
/*
* 1) 建立緩衝區
* 2) 導入WAV文件
* 3) 返回緩衝區ID
*/

ALuint GetLoadedALBuffer(string path);
/*
* 1) 檢測文件是否載入
* 2) 如果載入,返回緩衝區ID
* 3) 如果失敗,重新載入並返回緩衝區ID
*/

ALuint LoadALSample(string path, bool loop);
/*
* 1) 建立源
* 2) 調用‘GtLoadedALBuffer' 中的’PATH‘加載文件?
* 3)返回源ID
*/

void KillALLoadedData();
/*
* 1)釋放數據?
*/

bool LoadALData();
/*
* 1) 載入所有的源和緩衝區
*/

void KillALData();
/*
* 1) 釋放所有的緩衝區
* 2) 釋放所有的源
*/

vector<string> LoadedFiles; // 文件路徑
vector<ALuint> Buffers; // 緩衝區.
vector<ALuint> 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<string>::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<ALuint>::iterator iter = Buffers.begin(); iter != Buffers.end(); ++iter)
alDeleteBuffers(1, iter);

// 釋放所有的源數據.
for (vector<ALuint>::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.

See the Java Bindings for OpenAL page for the Java version of this tutorial (adapted by: Athomas Goldberg)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章