OpenAL播放pcm或wav數據流-windows/iOS/Android(一)
最近在研究渲染問題,本文采用openal做pcm和wav數據流播放,並非本地文件,demo是windows的,ios通用。網上都是ios的,ios需要引用OpenAl.framework框架,
Android平台需要做openal的jni,android的openal庫可以參考
http://blog.csdn.NET/matrix_laboratory/article/details/53319735這篇文章,各個平台需要做稍微處理。
下面是代碼:
//.h
- /** Copyright (c/c++) <2016.11.22> <zwg/>
- * Function
- * OpenAL through the buffer queuing mechanism to support the streaming playback of sound. The buffer queue is a buffer associated with a single source contact mechanism.
- * when audio playback, continuous rendering of each buffer, as if the buffer is composed of a continuous sound. This can be controlled by some special functions.
- * flow is generally the source of the work. In a number of audio buffer by alSourceQueueBuffers () function to queue, and then play the sound source,
- * next with property AL_BUFFERS_PROCESSED to query. This property obtains the number of buffers that have been processed,
- * allows applications to use the alSourceUnqueueBuffers () function to delete the buffers that have been processed.
- * alSourceUnqueueBuffers () function will start from the queue header will be processed in order to remove the buffer. Finally, the rest of the buffer queue in gear.
- * Opanal for audio rendering related implementation and definition, etc.
- * OpenAL通過緩沖器排隊機制支持聲音的流式播放。緩沖器排隊是多個緩沖器與單一音源相關聯的一種機制。
- * 當音源播放時,連續對各個緩沖器進行渲染,就好象這些緩沖器組成了一個連續的聲音。這可以通過一些特殊函數來控制。
- * 流音源的工作一般是這樣的。音源里的一批緩沖器通過alSourceQueueBuffers()函數進行排隊,然后播放音源,
- * 接下來用屬性AL_BUFFERS_PROCESSED來查詢。該屬性得出已經處理好的緩沖器的數量,
- * 從而允許應用程序使用alSourceUnqueueBuffers()函數刪除那些已經處理好的緩沖器。
- * alSourceUnqueueBuffers()函數將從隊列頭部開始依次將處理好的緩沖器刪除。最后,其余的緩沖器在音源上排隊。
- * OpanAl 用於音頻渲染相關實現及定義,等
- */
- #ifndef __LVS_OPENAL_INTERFACE_H__
- #define __LVS_OPENAL_INTERFACE_H__
- #include <stdio.h>
- #include <stdlib.h>
- #include <string>
- //windows
- #ifdef WIN32
- #include <Windows.h>
- //openAl庫
- #include "alut.h"
- #pragma comment(lib,"alut.lib")
- #pragma comment(lib,"OpenAL32.lib")
- //ios
- #elif __APPLE__
- #include "alut.h"
- //ANDROID平台
- #elif __ANDROID__
- #include "alut.h"
- //linux
- #else
- #include "alut.h"
- #endif
- //到處宏定義
- //windows
- #ifdef WIN32
- #define LVS_DLLEXPORT __declspec(dllexport)
- //ios
- #elif __APPLE__
- #define LVS_DLLEXPORT
- //linux
- #else
- #define LVS_DLLEXPORT
- #endif
- using namespace std;
- //接口初始化
- int lvs_openal_interface_init();
- //接口釋放
- void lvs_openal_interface_uninit();
- //接口開始播放
- void lvs_openal_interface_playsound();
- //接口停止播放
- void lvs_openal_interface_stopsound();
- //接口設置音量
- void lvs_openal_interface_setvolume(float volume);//volume取值范圍(0~1)
- //接口獲取音量
- float lvs_openal_interface_getvolume();
- //接口傳入pcm數據用於播放
- int lvs_openal_interface_openaudiofromqueue(char* data,int dataSize,int aSampleRate,int aBit ,int aChannel);
- //更新隊列數據,刪除已經播放的buffer,這個在隊列滿的時候用
- int lvs_openal_interface_updataQueueBuffer();
- //獲取當前時間戳
- long long lvs_openal_interface_getrealpts();
- //獲取已經播放了多少個數據塊
- long long lvs_openal_interface_getIsplayBufferSize();
- //獲取緩存隊列長度
- int lvs_openal_getnumqueuedsize();
- class cclass_openal_interface;
- class cclass_openal_interface
- {
- public:
- cclass_openal_interface();
- virtual ~cclass_openal_interface();
- //開始播放
- void playSound();
- //停止播放
- void stopSound();
- //設置音量
- void SetVolume(float volume);//volume取值范圍(0~1)
- //獲取音量
- float GetVolume();
- //傳入pcm數據用於播放
- int openAudioFromQueue(char* data,int dataSize,int aSampleRate,int aBit ,int aChannel);
- //更新隊列數據,刪除已經播放的buffer
- int updataQueueBuffer();
- private:
- //初始化openal
- int initOpenAL();
- //釋放openal
- void cleanUpOpenAL();
- public:
- int m_numprocessed; //隊列中已經播放過的數量
- int m_numqueued; //隊列中緩沖隊列數量
- long long m_IsplayBufferSize; //已經播放了多少個音頻緩存數目
- double m_oneframeduration; //一幀音頻數據持續時間(ms)
- float m_volume; //當前音量volume取值范圍(0~1)
- int m_samplerate; //采樣率
- int m_bit; //樣本值
- int m_channel; //聲道數
- int m_datasize; //一幀音頻數據量
- private:
- ALCdevice * m_Devicde; //device句柄
- ALCcontext * m_Context; //device context
- ALuint m_outSourceId; //source id 負責播放
- };
- #endif
//.cpp
- #include "Lvs_OpenAl_Interface.h"
- static cclass_openal_interface * copenal_interface = NULL;
- int lvs_openal_interface_init()
- {
- int ret = 0;
- printf("Device : lvs_openal_interface_init\n");
- if(copenal_interface == NULL)
- {
- copenal_interface = new cclass_openal_interface();
- }
- return ret;
- }
- void lvs_openal_interface_uninit()
- {
- printf("Device : lvs_openal_interface_uninit\n");
- if(copenal_interface)
- {
- delete copenal_interface;
- copenal_interface = NULL;
- }
- return ;
- }
- void lvs_openal_interface_playsound()
- {
- copenal_interface->playSound();
- }
- void lvs_openal_interface_stopsound()
- {
- copenal_interface->stopSound();
- }
- void lvs_openal_interface_setvolume(float volume)//volume取值范圍(0~1)
- {
- copenal_interface->SetVolume(volume);
- }
- float lvs_openal_interface_getvolume()
- {
- return copenal_interface->GetVolume();
- }
- int lvs_openal_interface_openaudiofromqueue(char* data,int dataSize,int aSampleRate,int aBit ,int aChannel)
- {
- return copenal_interface->openAudioFromQueue(data,dataSize,aSampleRate,aBit,aChannel);
- }
- long long lvs_openal_interface_getrealpts()
- {
- long long time = (long long )((copenal_interface->m_IsplayBufferSize * copenal_interface->m_oneframeduration) + 0.5);
- printf("*****m_IsplayBufferSize : %ld",copenal_interface->m_IsplayBufferSize);
- printf("****************time : %lld(ms)\n",time);
- return time;
- }
- long long lvs_openal_interface_getIsplayBufferSize()
- {
- return copenal_interface->m_IsplayBufferSize;
- }
- int lvs_openal_getnumqueuedsize()
- {
- return copenal_interface->m_numqueued;
- }
- int lvs_openal_interface_updataQueueBuffer()
- {
- return copenal_interface->updataQueueBuffer();
- }
- cclass_openal_interface::cclass_openal_interface()
- {
- m_Devicde = NULL;
- m_Context = NULL;
- m_outSourceId = 0;
- m_numprocessed = 0;
- m_numqueued = 0;
- m_IsplayBufferSize = 0;
- m_oneframeduration = 0.0;
- m_volume = 1.0;
- m_samplerate = 0;
- m_bit = 0;
- m_channel = 0;
- m_datasize = 0;
- //init
- initOpenAL();
- }
- cclass_openal_interface::~cclass_openal_interface()
- {
- cleanUpOpenAL();
- m_Devicde = NULL;
- m_Context = NULL;
- m_outSourceId = 0;
- m_numprocessed = 0;
- m_numqueued = 0;
- m_IsplayBufferSize = 0;
- m_oneframeduration = 0.0;
- m_volume = 1.0;
- m_samplerate = 0;
- m_bit = 0;
- m_channel = 0;
- m_datasize = 0;
- }
- int cclass_openal_interface::initOpenAL()
- {
- int ret = 0;
- printf("=======initOpenAl===\n");
- #ifdef WIN32
- //初始化 ALUT openal函數庫
- int zwg_argc=1;
- //添加函數庫名稱
- char* zwg_argv[]={"ZWG_ALUT"};
- ret= alutInit(&zwg_argc, zwg_argv);
- #else
- #endif
- //打開device
- m_Devicde = alcOpenDevice(NULL);
- if (m_Devicde)
- {
- #ifdef WIN32
- //windows 用這個context 聲音不正常,以后處理
- #else
- //建立聲音文本描述
- m_Context = alcCreateContext(m_Devicde, NULL);
- //設置行為文本描述
- alcMakeContextCurrent(m_Context);
- #endif
- }
- //創建一個source並設置一些屬性
- alGenSources(1, &m_outSourceId);
- alSpeedOfSound(1.0);
- alDopplerVelocity(1.0);
- alDopplerFactor(1.0);
- alSourcef(m_outSourceId, AL_PITCH, 1.0f);
- alSourcef(m_outSourceId, AL_GAIN, 1.0f);
- alSourcei(m_outSourceId, AL_LOOPING, AL_FALSE);
- alSourcef(m_outSourceId, AL_SOURCE_TYPE, AL_STREAMING);
- return ret;
- }
- void cclass_openal_interface::cleanUpOpenAL()
- {
- printf("=======cleanUpOpenAL===\n");
- alDeleteSources(1, &m_outSourceId);
- #ifdef WIN32
- alcCloseDevice(m_Devicde);
- m_Devicde = NULL;
- alutExit();
- #else
- ALCcontext * Context = alcGetCurrentContext();
- ALCdevice * Devicde = alcGetContextsDevice(Context);
- if (Context)
- {
- alcMakeContextCurrent(NULL);
- alcDestroyContext(Context);
- m_Context = NULL;
- }
- alcCloseDevice(m_Devicde);
- m_Devicde = NULL;
- #endif
- }
- void cclass_openal_interface::playSound()
- {
- int ret = 0;
- alSourcePlay(m_outSourceId);
- if((ret = alGetError()) != AL_NO_ERROR)
- {
- printf("error alcMakeContextCurrent %x : %s\n", ret,alutGetErrorString (ret));
- }
- }
- void cclass_openal_interface::stopSound()
- {
- alSourceStop(m_outSourceId);
- }
- void cclass_openal_interface::SetVolume(float volume)//volume取值范圍(0~1)
- {
- m_volume = volume;
- alSourcef(m_outSourceId,AL_GAIN,volume);
- }
- float cclass_openal_interface::GetVolume()
- {
- return m_volume;
- }
- int cclass_openal_interface::updataQueueBuffer()
- {
- //播放狀態字段
- ALint stateVaue = 0;
- //獲取處理隊列,得出已經播放過的緩沖器的數量
- alGetSourcei(m_outSourceId, AL_BUFFERS_PROCESSED, &m_numprocessed);
- //獲取緩存隊列,緩存的隊列數量
- alGetSourcei(m_outSourceId, AL_BUFFERS_QUEUED, &m_numqueued);
- //獲取播放狀態,是不是正在播放
- alGetSourcei(m_outSourceId, AL_SOURCE_STATE, &stateVaue);
- //printf("===statevaue ========================%x\n",stateVaue);
- if (stateVaue == AL_STOPPED ||
- stateVaue == AL_PAUSED ||
- stateVaue == AL_INITIAL)
- {
- //如果沒有數據,或數據播放完了
- if (m_numqueued < m_numprocessed || m_numqueued == 0 ||(m_numqueued == 1 && m_numprocessed ==1))
- {
- //停止播放
- printf("...Audio Stop\n");
- stopSound();
- cleanUpOpenAL();
- return 0;
- }
- if (stateVaue != AL_PLAYING)
- {
- playSound();
- }
- }
- //將已經播放過的的數據刪除掉
- while(m_numprocessed --)
- {
- ALuint buff;
- //更新緩存buffer中的數據到source中
- alSourceUnqueueBuffers(m_outSourceId, 1, &buff);
- //刪除緩存buff中的數據
- alDeleteBuffers(1, &buff);
- //得到已經播放的音頻隊列多少塊
- m_IsplayBufferSize ++;
- }
- long long time = (long long )((m_IsplayBufferSize * m_oneframeduration) + 0.5);
- //printf("*****m_IsplayBufferSize : %ld",m_IsplayBufferSize);
- //printf("****************time : %ld(ms)\n",time);
- return 1;
- }
- int cclass_openal_interface::openAudioFromQueue(char* data,int dataSize,int aSampleRate,int aBit ,int aChannel)
- {
- int ret = 0;
- //樣本數openal的表示方法
- ALenum format = 0;
- //buffer id 負責緩存,要用局部變量每次數據都是新的地址
- ALuint bufferID = 0;
- if (m_datasize == 0 &&
- m_samplerate == 0 &&
- m_bit == 0 &&
- m_channel == 0)
- {
- if (dataSize != 0 &&
- aSampleRate != 0 &&
- aBit != 0 &&
- aChannel != 0)
- {
- m_datasize = dataSize;
- m_samplerate = aSampleRate;
- m_bit = aBit;
- m_channel = aChannel;
- m_oneframeduration = m_datasize * 1.0 /(m_bit/8) /m_channel /m_samplerate * 1000 ; //計算一幀數據持續時間
- }
- }
- //創建一個buffer
- alGenBuffers(1, &bufferID);
- if((ret = alGetError()) != AL_NO_ERROR)
- {
- printf("error alGenBuffers %x : %s\n", ret,alutGetErrorString (ret));
- //AL_ILLEGAL_ENUM
- //AL_INVALID_VALUE
- //#define AL_ILLEGAL_COMMAND 0xA004
- //#define AL_INVALID_OPERATION 0xA004
- }
- if (aBit == 8)
- {
- if (aChannel == 1)
- {
- format = AL_FORMAT_MONO8;
- }
- else if(aChannel == 2)
- {
- format = AL_FORMAT_STEREO8;
- }
- }
- if( aBit == 16 )
- {
- if( aChannel == 1 )
- {
- format = AL_FORMAT_MONO16;
- }
- if( aChannel == 2 )
- {
- format = AL_FORMAT_STEREO16;
- }
- }
- //指定要將數據復制到緩沖區中的數據
- alBufferData(bufferID, format, data, dataSize,aSampleRate);
- if((ret = alGetError()) != AL_NO_ERROR)
- {
- printf("error alBufferData %x : %s\n", ret,alutGetErrorString (ret));
- //AL_ILLEGAL_ENUM
- //AL_INVALID_VALUE
- //#define AL_ILLEGAL_COMMAND 0xA004
- //#define AL_INVALID_OPERATION 0xA004
- }
- //附加一個或一組buffer到一個source上
- alSourceQueueBuffers(m_outSourceId, 1, &bufferID);
- if((ret = alGetError()) != AL_NO_ERROR)
- {
- printf("error alSourceQueueBuffers %x : %s\n", ret,alutGetErrorString (ret));
- }
- //更新隊列數據
- ret = updataQueueBuffer();
- //刪除一個緩沖 這里不應該刪除緩沖,在source里面播放完畢刪除
- //alDeleteBuffers(1, &bufferID);
- bufferID = 0;
- return 1;
- }
//main.cpp
- #include "Lvs_OpenAl_Interface.h"
- //要顯示的pcm/wav文件路徑及名稱
- #define PCM_STREAM_PATH_NAME "../pcm_stream/44100_2_16.pcm"
- int main()
- {
- int ret = 0;
- int nSampleRate = 44100; //采樣率
- int nBit = 16; //樣本數
- int nChannel = 2; //聲道
- int ndatasize = 1024 * (nBit/8) *nChannel; //每次讀取的數據大小
- char ndata[4096 + 1] = {0}; //讀取的數據
- FILE * pFile_pcm = NULL; //讀取pcm數據的文件句柄
- //打開pcm文件
- if((pFile_pcm = fopen(PCM_STREAM_PATH_NAME, "rb")) == NULL)
- {
- printf("filed open file : %s\n",PCM_STREAM_PATH_NAME);
- return getchar();
- }
- else
- {
- printf("success open file : %s\n",PCM_STREAM_PATH_NAME);
- }
- //init
- lvs_openal_interface_init();
- //設置音量volume取值范圍(0~1)
- lvs_openal_interface_setvolume(1.0);
- for(;;)
- {
- Sleep(23);
- //循環讀取文件
- ret = fread(ndata, 1,ndatasize, pFile_pcm);
- if (ret != ndatasize)
- {
- //seek到文件開頭
- fseek(pFile_pcm, 0, SEEK_SET);
- fread(ndata, 1,ndatasize, pFile_pcm);
- }
- //具體的處理在這里
- ret = lvs_openal_interface_openaudiofromqueue((char *)ndata,ndatasize,nSampleRate,nBit,nChannel);
- long long time = lvs_openal_interface_getrealpts();
- }
- //uinit
- lvs_openal_interface_uninit();
- //關閉pcm文件
- if (pFile_pcm != NULL)
- {
- fclose(pFile_pcm);
- pFile_pcm = NULL;
- }
- return 1;
- }
程序運行效果並能聽到聲音:
本demo還需完善。
from:http://blog.csdn.net/zhuweigangzwg/article/details/53286945