javaee论坛

普通会员

225648

帖子

344

回复

358

积分

楼主
发表于 2017-06-08 11:01:03 | 查看: 808 | 回复: 1

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


最近在研究渲染问题,本文采用openal做pcm和wav数据流播放,并非本地文件,demo是windows的,ios通用。网上都是ios的,ios需要引用OpenAl.framework框架,

Android平台需要做openal的jni,android的openal库可以参考

http://blog..net/matrix_laboratory/article/details/53319735这篇文章,各个平台需要做稍微处理。

下面是代码:

//.h

< code_snippet_id="2019901" snippet_file_name="blog_20161201_1_7452789" code_snippet_id="2019901" snippet_file_name="blog_20161201_1_7452789" name="code" class="cpp">/** 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); //更新队列数据,删除已经播放的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

< code_snippet_id="2019901" snippet_file_name="blog_20161207_2_5415406" code_snippet_id="2019901" snippet_file_name="blog_20161207_2_5415406" name="code" class="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

< code_snippet_id="2019901" snippet_file_name="blog_20161201_3_91557" code_snippet_id="2019901" snippet_file_name="blog_20161201_3_91557" name="code" class="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还需完善。

如有错误请指正:

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





普通会员

0

帖子

240

回复

240

积分
沙发
发表于 2022-12-16 11:53:06

围观

您需要登录后才可以回帖 登录 | 立即注册

触屏版| 电脑版

技术支持 历史网 V2.0 © 2016-2017