OpenAL播放pcm或wav数据流-windows/ios/android(一)

来源:互联网 发布:苹果 淘宝 编辑:程序博客网 时间:2024/06/04 20:14

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#endifusing 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);//更新队列数据,删除已经播放的bufferint updataQueueBuffer();private://初始化openalint initOpenAL();//释放openalvoid 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 contextALuint 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;//initinitOpenAL();}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//打开devicem_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 WIN32alcCloseDevice(m_Devicde);m_Devicde = NULL;alutExit();#elseALCcontext * 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 ;   //计算一帧数据持续时间}}//创建一个bufferalGenBuffers(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);}//initlvs_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();}//uinitlvs_openal_interface_uninit();//关闭pcm文件if (pFile_pcm != NULL){fclose(pFile_pcm);pFile_pcm = NULL;}return 1;}



程序运行效果并能听到声音:


本demo还需完善。

如有错误请指正:

交流请加QQ群:62054820
QQ:379969650.




0 0