初探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
沒有留言:
張貼留言