初探OpenAL
前言
在前篇初探XAudio2裡使用了XAudio2來播放聲音,這次來使用OpenAL來播放聲音,為什麼使用OpenAL呢?因為Linux裡沒有XAudio2可以使用,在Linux有一套名為ALSA的API,但查了一下授權條款是GPL,所以決定放棄這一套API,所以在Linux使用OpenAL來實現播放聲音,在此把研究過程做個紀錄。內容
在開始之前,請先到OpenAL官網下載SDK,請下載下圖所示選項
![]() |
| 下載OpenAL SDK |
OpenAL的範例可以在"(SDK directory)/samples"裡找到,但範例有用framework,如果需要教學網站的範例的可以參照OpenAL: Managing Playback Streams與OpenAL short example。在OpenAL的開始需要ALCdevice與ALCcontext,範例如下
#include <AL/al.h>
#include <AL/alc.h>
int main()
{
//
ALCdevice* pDevice=alcOpenDevice(NULL);
if(pDevice==NULL)
return 1;
ALCcontext* pContext=alcCreateContext(pDevice,NULL);
if(pContext==NULL)
{
alcCloseDevice(pDevice);
return 1;
}
if(!alcMakeContextCurrent(pContext) )
{
alcDestroyContext(pContext);
alcCloseDevice(pDevice);
return 1;
}
//OpenAL was ready...
//...
//program end...
alcDestroyContext(pContext);
alcCloseDevice(pDevice);
return 0;
}
alcMakeContextCurrent()非常重要,如果這個步驟沒成功,所有的OpenAL資源將無法製造。
接著來製造播放聲音,範例如下
#include <AL/al.h>
#include <AL/alc.h>
#include <windows.h>
ALenum GetChannelFormat(short channels,short samples)
{
ALenum val=AL_INVAILD_VALUE;
switch(samples)
{
case 16:
if(channels>1)
val=AL_FORMAT_STEREO16;
else
val=AL_FORMAT_MONO16;
break;
case 8:
if(channels>1)
val=AL_FORMAT_STEREO8;
else
val=AL_FORMAT_MONO8;
break;
}
return val;
}
int main()
{
//
ALCdevice* pDevice=alcOpenDevice(NULL);
if(pDevice==NULL)
return 1;
ALCcontext* pContext=alcCreateContext(pDevice,NULL);
if(pContext==NULL)
{
alcCloseDevice(pDevice);
return 1;
}
if(!alcMakeContextCurrent(pContext) )
{
alcDestroyContext(pContext);
alcCloseDevice(pDevice);
return 1;
}
//OpenAL was ready...
ALuint sourceID;
alGenSource(1,&sourceID);
if(alGetError()!=AL_NO_ERROR)
{
alcDestroyContext(pContext);
alcCloseDevice(pDevice);
return 1;
}
//Follow data load from WAV file
WAVEFORMATEX format;
unsigned char* pWAVRawData;
size_t wavRawDataSize;
//
ALunit bufID;
alGenBuffers(1,&bufID);
alBufferData(bufID,GetChannelFormat(format.nChannels,format.wBitsPerSample),pWAVRawData,format.nSamplesPerSec);
//Enqueue buffer
alSoruceQueueBuffers(sourceID,1,&bufID);
//Play
alSourcePlay(sourceID);
//wait play end
ALint sourceState;
alGetSourcei(sourceID, AL_SOURCE_STATE, &sourceState);
while(sourceState==AL_PLAYING)
::Sleep(1);//sleep 1ms
//
//program end...
alcDestroyContext(pContext);
alcCloseDevice(pDevice);
return 0;
}
流程的部分跟XAudio2很像,製造播放聲音的元件再推入Buffer來播放聲音,利用alGetSourcei()來確認是否播放完畢。
最後來說Stream play的部分,XAudio2的Dequeue是自動的,但OpenAL的Dequeue的流程要手動,範例如下
ALuint tempBufferID=0;
alSourceUnqueueBuffers(sourceID,1,tempBufferID);
if(tempBufferID!=0)
{
//Dequeue ok!
//...
}
過程不會太難,但什麼時候會Dequeue會失敗?當這個Buffer還在播放中的話,BufferID就會錶持為0,如果你看OpenAL: Managing Playback Streams裡的話,會發現它使用alGetSourcei()透過AL_BUFFERS_PROCESSED來得知Buffer是否播放完畢,但查到這篇AL_BUFFERS_PROCESSED bug?所以我不打算用範例的方法檢測是否播完。
整體來說XAudio2與OpenAL的用法很像,目前差異只有在Dequeue的部分,如果要整合的話相信並不困難,OpenAL可以用在Windows與Linux,但XAudio2卻只能跑在Windows,整合到底有沒有價值?只能日後再來檢驗了。
參考資料
ALSAOpenAL官網
OpenAL: Managing Playback Streams
OpenAL short example
AL_BUFFERS_PROCESSED bug?
相關文章
WAV檔案格式學習心得初探XAudio2

沒有留言:
張貼留言