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
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- #ifndef __LVS_OPENAL_INTERFACE_H__
- #define __LVS_OPENAL_INTERFACE_H__
-
- #include
- #include
- #include
-
-
- #ifdef WIN32
- #include
-
- #include "alut.h"
- #pragma comment(lib,"alut.lib")
- #pragma comment(lib,"OpenAL32.lib")
-
- #elif __APPLE__
- #include "alut.h"
-
- #elif __ANDROID__
- #include "alut.h"
-
- #else
- #include "alut.h"
- #endif
-
-
-
- #ifdef WIN32
- #define LVS_DLLEXPORT __declspec(dllexport)
-
- #elif __APPLE__
- #define LVS_DLLEXPORT
-
- #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);
-
- float lvs_openal_interface_getvolume();
-
- int lvs_openal_interface_openaudiofromqueue(char* data,int dataSize,int aSampleRate,int aBit ,int aChannel);
-
- 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);
-
- float GetVolume();
-
- int openAudioFromQueue(char* data,int dataSize,int aSampleRate,int aBit ,int aChannel);
-
- int updataQueueBuffer();
- private:
-
- int initOpenAL();
-
- void cleanUpOpenAL();
- public:
- int m_numprocessed;
- int m_numqueued;
- long long m_IsplayBufferSize;
- double m_oneframeduration;
- float m_volume;
- int m_samplerate;
- int m_bit;
- int m_channel;
- int m_datasize;
- private:
- ALCdevice * m_Devicde;
- ALCcontext * m_Context;
- ALuint m_outSourceId;
- };
-
-
- #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)
- {
- 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;
-
-
- 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
-
- int zwg_argc=1;
-
- char* zwg_argv[]={"ZWG_ALUT"};
- ret= alutInit(&zwg_argc, zwg_argv);
- #else
-
- #endif
-
-
- m_Devicde = alcOpenDevice(NULL);
- if (m_Devicde)
- {
- #ifdef WIN32
-
- #else
-
- m_Context = alcCreateContext(m_Devicde, NULL);
-
-
- alcMakeContextCurrent(m_Context);
- #endif
- }
-
-
- 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)
- {
- 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);
-
-
-
- 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;
-
- alSourceUnqueueBuffers(m_outSourceId, 1, &buff);
-
- alDeleteBuffers(1, &buff);
-
-
- m_IsplayBufferSize ++;
- }
- long long time = (long long )((m_IsplayBufferSize * m_oneframeduration) + 0.5);
-
-
- return 1;
- }
-
- int cclass_openal_interface::openAudioFromQueue(char* data,int dataSize,int aSampleRate,int aBit ,int aChannel)
- {
- int ret = 0;
-
- ALenum format = 0;
-
- 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 ;
- }
- }
-
-
- alGenBuffers(1, &bufferID);
- if((ret = alGetError()) != AL_NO_ERROR)
- {
- printf("error alGenBuffers %x : %s\n", ret,alutGetErrorString (ret));
-
-
-
-
- }
-
- 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));
-
-
-
-
- }
-
- alSourceQueueBuffers(m_outSourceId, 1, &bufferID);
- if((ret = alGetError()) != AL_NO_ERROR)
- {
- printf("error alSourceQueueBuffers %x : %s\n", ret,alutGetErrorString (ret));
- }
-
-
- ret = updataQueueBuffer();
-
-
-
- bufferID = 0;
-
- return 1;
- }
//main.cpp
- #include "Lvs_OpenAl_Interface.h"
-
-
- #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;
-
-
- 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);
- }
-
-
- lvs_openal_interface_init();
-
-
- lvs_openal_interface_setvolume(1.0);
-
- for(;;)
- {
- Sleep(23);
-
- ret = fread(ndata, 1,ndatasize, pFile_pcm);
- if (ret != ndatasize)
- {
-
- 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();
- }
-
-
- lvs_openal_interface_uninit();
-
-
- if (pFile_pcm != NULL)
- {
- fclose(pFile_pcm);
- pFile_pcm = NULL;
- }
-
- return 1;
- }
程序运行效果并能听到声音:
本demo还需完善。
如有错误请指正:
交流请加QQ群:62054820
QQ:379969650.
原文转载自:http://blog.csdn.net/zhuweigangzwg/article/details/53286945
分界线:个人阅读和总结.
alSourceUnqueueBuffers <更新缓冲到buffer中>
是否需要将数据删除掉:alDeleteBuffers(1, &buff); <不应该删除,但是否会覆盖掉>
alDeleteBuffers <删除缓冲器>!删除的时候使用
别人的流程:
[这里进行排查!!!--这里也有问题] //buffer id 负责缓存,要用局部变量每次数据都是新的地址
ALuint bufferID = 0;
//创建一个buffer
alGenBuffers(1, &bufferID);
//指定要将数据复制到缓冲区中的数据
alBufferData(bufferID, format, data, dataSize,aSampleRate);
//附加一个或一组buffer到一个source上
alSourceQueueBuffers(m_outSourceId, 1, &bufferID);
//更新队列数据
ret = updataQueueBuffer(); -->这里是自定义函数.!!!,然后进行声音播放.
//删除一个缓冲 这里不应该删除缓冲,在source里面播放完毕删除 <不属于源代码,直接删除,这里指明了应该播放完删除>
//alDeleteBuffers(1, &bufferID);
bufferID = 0;
自定义处理函数:
先_获取状态,已经处理,总共的._播放,删除.
//用alGetSourcei(m_uiSource, AL_BUFFERS_PROCESSED,&m_iBuffersProcessed)取得缓冲队列中的空闲的buffer然后往里面添加数据即可
//这句话网上的,待判断
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 ++;
}
询问老大:
他播放实际上是安全的 你是判断他的播放状态才去 播放,并且清空缓冲区的.
确定:标识 和绑定的 数据是不是安全的<因为你要确保两个才能播放.仅仅只是delete buffer,恩恩,已经确定他是安全的了>
<思考,老大说的内存泄漏怎么解决\?>仅仅是强制标识两个容器的关联,最终才删除.
所以,根本就不需要判断数量和状态
,自动new 并且绑定数据,加到源中,在由源的内存进行内部自己的管理,也就是while中删除掉.