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