您现在的位置是:首页 > 文章详情

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

日期:2017-05-10点击:619

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

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. /** Copyright (c/c++) <2016.11.22> <zwg/> 
  2. * Function   
  3. * 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. 
  4. * 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. 
  5. * 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, 
  6. * next with property AL_BUFFERS_PROCESSED to query. This property obtains the number of buffers that have been processed, 
  7. * allows applications to use the alSourceUnqueueBuffers () function to delete the buffers that have been processed. 
  8. * 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. 
  9. * Opanal for audio rendering related implementation and definition, etc. 
  10. * OpenAL通过缓冲器排队机制支持声音的流式播放。缓冲器排队是多个缓冲器与单一音源相关联的一种机制。 
  11. * 当音源播放时,连续对各个缓冲器进行渲染,就好象这些缓冲器组成了一个连续的声音。这可以通过一些特殊函数来控制。 
  12. * 流音源的工作一般是这样的。音源里的一批缓冲器通过alSourceQueueBuffers()函数进行排队,然后播放音源, 
  13. * 接下来用属性AL_BUFFERS_PROCESSED来查询。该属性得出已经处理好的缓冲器的数量, 
  14. * 从而允许应用程序使用alSourceUnqueueBuffers()函数删除那些已经处理好的缓冲器。 
  15. * alSourceUnqueueBuffers()函数将从队列头部开始依次将处理好的缓冲器删除。最后,其余的缓冲器在音源上排队。 
  16. * OpanAl 用于音频渲染相关实现及定义,等 
  17. */  
  18.   
  19. #ifndef __LVS_OPENAL_INTERFACE_H__  
  20. #define __LVS_OPENAL_INTERFACE_H__  
  21.   
  22. #include <stdio.h>  
  23. #include <stdlib.h>  
  24. #include <string>  
  25.   
  26. //windows  
  27. #ifdef WIN32  
  28. #include <Windows.h>  
  29. //openAl库  
  30. #include "alut.h"  
  31. #pragma comment(lib,"alut.lib")  
  32. #pragma comment(lib,"OpenAL32.lib")  
  33. //ios  
  34. #elif __APPLE__  
  35. #include "alut.h"  
  36. //ANDROID平台    
  37. #elif __ANDROID__    
  38. #include "alut.h"  
  39. //linux  
  40. #else  
  41. #include "alut.h"  
  42. #endif  
  43.   
  44. //到处宏定义  
  45. //windows  
  46. #ifdef WIN32  
  47. #define LVS_DLLEXPORT __declspec(dllexport)  
  48. //ios  
  49. #elif __APPLE__  
  50. #define LVS_DLLEXPORT  
  51. //linux  
  52. #else  
  53. #define LVS_DLLEXPORT  
  54. #endif  
  55.   
  56. using namespace std;  
  57.   
  58. //接口初始化  
  59. int lvs_openal_interface_init();  
  60. //接口释放  
  61. void lvs_openal_interface_uninit();  
  62. //接口开始播放  
  63. void lvs_openal_interface_playsound();  
  64. //接口停止播放  
  65. void lvs_openal_interface_stopsound();  
  66. //接口设置音量  
  67. void lvs_openal_interface_setvolume(float volume);//volume取值范围(0~1)  
  68. //接口获取音量  
  69. float lvs_openal_interface_getvolume();  
  70. //接口传入pcm数据用于播放  
  71. int lvs_openal_interface_openaudiofromqueue(char* data,int dataSize,int aSampleRate,int aBit ,int aChannel);  
  72. //更新队列数据,删除已经播放的buffer,这个在队列满的时候用  
  73. int lvs_openal_interface_updataQueueBuffer();  
  74. //获取当前时间戳  
  75. long long lvs_openal_interface_getrealpts();  
  76. //获取已经播放了多少个数据块  
  77. long long lvs_openal_interface_getIsplayBufferSize();  
  78. //获取缓存队列长度  
  79. int lvs_openal_getnumqueuedsize();  
  80.   
  81. class cclass_openal_interface;  
  82.   
  83. class cclass_openal_interface  
  84. {  
  85. public:  
  86.     cclass_openal_interface();  
  87.     virtual ~cclass_openal_interface();  
  88.     //开始播放  
  89.     void playSound();  
  90.     //停止播放  
  91.     void stopSound();  
  92.     //设置音量  
  93.     void SetVolume(float volume);//volume取值范围(0~1)  
  94.     //获取音量  
  95.     float GetVolume();  
  96.     //传入pcm数据用于播放  
  97.     int openAudioFromQueue(char* data,int dataSize,int aSampleRate,int aBit ,int aChannel);  
  98.     //更新队列数据,删除已经播放的buffer  
  99.     int updataQueueBuffer();  
  100. private:  
  101.     //初始化openal  
  102.     int initOpenAL();  
  103.     //释放openal  
  104.     void cleanUpOpenAL();  
  105. public:  
  106.     int m_numprocessed;             //队列中已经播放过的数量  
  107.     int m_numqueued;                //队列中缓冲队列数量  
  108.     long long m_IsplayBufferSize;   //已经播放了多少个音频缓存数目  
  109.     double m_oneframeduration;      //一帧音频数据持续时间(ms)  
  110.     float m_volume;                 //当前音量volume取值范围(0~1)  
  111.     int m_samplerate;               //采样率  
  112.     int m_bit;                      //样本值  
  113.     int m_channel;                  //声道数  
  114.     int m_datasize;                 //一帧音频数据量  
  115. private:  
  116.     ALCdevice * m_Devicde;          //device句柄  
  117.     ALCcontext * m_Context;         //device context  
  118.     ALuint m_outSourceId;           //source id 负责播放  
  119. };  
  120.   
  121.   
  122. #endif  


//.cpp

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. #include "Lvs_OpenAl_Interface.h"  
  2.   
  3. static cclass_openal_interface * copenal_interface = NULL;  
  4.   
  5. int lvs_openal_interface_init()   
  6. {  
  7.     int ret = 0;  
  8.     printf("Device : lvs_openal_interface_init\n");  
  9.     if(copenal_interface == NULL)  
  10.     {  
  11.         copenal_interface = new cclass_openal_interface();  
  12.     }  
  13.     return ret;  
  14. }  
  15.   
  16. void lvs_openal_interface_uninit()  
  17. {  
  18.     printf("Device : lvs_openal_interface_uninit\n");  
  19.   
  20.     if(copenal_interface)  
  21.     {  
  22.         delete copenal_interface;  
  23.         copenal_interface = NULL;  
  24.     }  
  25.     return ;  
  26. }  
  27.   
  28. void lvs_openal_interface_playsound()  
  29. {  
  30.     copenal_interface->playSound();  
  31. }  
  32.   
  33. void lvs_openal_interface_stopsound()  
  34. {  
  35.     copenal_interface->stopSound();  
  36. }  
  37.   
  38. void lvs_openal_interface_setvolume(float volume)//volume取值范围(0~1)  
  39. {  
  40.     copenal_interface->SetVolume(volume);  
  41. }  
  42.   
  43. float lvs_openal_interface_getvolume()  
  44. {  
  45.     return copenal_interface->GetVolume();  
  46. }  
  47.   
  48. int lvs_openal_interface_openaudiofromqueue(char* data,int dataSize,int aSampleRate,int aBit ,int aChannel)  
  49. {  
  50.     return copenal_interface->openAudioFromQueue(data,dataSize,aSampleRate,aBit,aChannel);  
  51. }  
  52.   
  53. long long lvs_openal_interface_getrealpts()  
  54. {  
  55.     long long time = (long long )((copenal_interface->m_IsplayBufferSize * copenal_interface->m_oneframeduration) + 0.5);  
  56.     printf("*****m_IsplayBufferSize : %ld",copenal_interface->m_IsplayBufferSize);  
  57.     printf("****************time : %lld(ms)\n",time);  
  58.     return time;  
  59. }  
  60.   
  61. long long lvs_openal_interface_getIsplayBufferSize()  
  62. {  
  63.     return copenal_interface->m_IsplayBufferSize;  
  64. }  
  65.   
  66. int lvs_openal_getnumqueuedsize()  
  67. {  
  68.     return copenal_interface->m_numqueued;  
  69. }  
  70.   
  71. int lvs_openal_interface_updataQueueBuffer()  
  72. {  
  73.     return copenal_interface->updataQueueBuffer();  
  74. }  
  75.   
  76. cclass_openal_interface::cclass_openal_interface()  
  77. {  
  78.     m_Devicde = NULL;    
  79.     m_Context = NULL;        
  80.     m_outSourceId = 0;        
  81.     m_numprocessed = 0;            
  82.     m_numqueued = 0;  
  83.     m_IsplayBufferSize = 0;  
  84.     m_oneframeduration = 0.0;  
  85.     m_volume = 1.0;  
  86.     m_samplerate = 0;  
  87.     m_bit = 0;  
  88.     m_channel = 0;  
  89.     m_datasize = 0;  
  90.   
  91.     //init  
  92.     initOpenAL();  
  93. }  
  94.   
  95. cclass_openal_interface::~cclass_openal_interface()  
  96. {  
  97.     cleanUpOpenAL();  
  98.   
  99.     m_Devicde = NULL;    
  100.     m_Context = NULL;        
  101.     m_outSourceId = 0;        
  102.     m_numprocessed = 0;            
  103.     m_numqueued = 0;  
  104.     m_IsplayBufferSize = 0;  
  105.     m_oneframeduration = 0.0;  
  106.     m_volume = 1.0;  
  107.     m_samplerate = 0;  
  108.     m_bit = 0;  
  109.     m_channel = 0;  
  110.     m_datasize = 0;  
  111. }  
  112.   
  113. int cclass_openal_interface::initOpenAL()  
  114. {  
  115.     int ret = 0;  
  116.   
  117.     printf("=======initOpenAl===\n");  
  118.   
  119. #ifdef WIN32  
  120.     //初始化 ALUT openal函数库  
  121.     int zwg_argc=1;  
  122.     //添加函数库名称  
  123.     char* zwg_argv[]={"ZWG_ALUT"};    
  124.     ret= alutInit(&zwg_argc, zwg_argv);   
  125. #else  
  126.   
  127. #endif  
  128.   
  129.     //打开device  
  130.     m_Devicde = alcOpenDevice(NULL);  
  131.     if (m_Devicde)  
  132.     {  
  133. #ifdef WIN32  
  134.         //windows 用这个context 声音不正常,以后处理  
  135. #else  
  136.         //建立声音文本描述  
  137.         m_Context = alcCreateContext(m_Devicde, NULL);  
  138.   
  139.         //设置行为文本描述  
  140.         alcMakeContextCurrent(m_Context);  
  141. #endif  
  142.     }  
  143.   
  144.     //创建一个source并设置一些属性  
  145.     alGenSources(1, &m_outSourceId);  
  146.     alSpeedOfSound(1.0);  
  147.     alDopplerVelocity(1.0);  
  148.     alDopplerFactor(1.0);  
  149.     alSourcef(m_outSourceId, AL_PITCH, 1.0f);  
  150.     alSourcef(m_outSourceId, AL_GAIN, 1.0f);  
  151.     alSourcei(m_outSourceId, AL_LOOPING, AL_FALSE);  
  152.     alSourcef(m_outSourceId, AL_SOURCE_TYPE, AL_STREAMING);  
  153.   
  154.     return ret;  
  155. }  
  156.   
  157. void cclass_openal_interface::cleanUpOpenAL()  
  158. {  
  159.     printf("=======cleanUpOpenAL===\n");  
  160.   
  161.     alDeleteSources(1, &m_outSourceId);  
  162.   
  163. #ifdef WIN32  
  164.     alcCloseDevice(m_Devicde);  
  165.     m_Devicde = NULL;  
  166.     alutExit();  
  167. #else  
  168.     ALCcontext * Context = alcGetCurrentContext();  
  169.     ALCdevice * Devicde = alcGetContextsDevice(Context);  
  170.   
  171.     if (Context)  
  172.     {  
  173.         alcMakeContextCurrent(NULL);  
  174.         alcDestroyContext(Context);  
  175.         m_Context = NULL;  
  176.     }  
  177.     alcCloseDevice(m_Devicde);  
  178.     m_Devicde = NULL;  
  179. #endif  
  180. }  
  181.   
  182. void cclass_openal_interface::playSound()  
  183. {  
  184.     int ret = 0;  
  185.     alSourcePlay(m_outSourceId);  
  186.     if((ret = alGetError()) != AL_NO_ERROR)  
  187.     {  
  188.         printf("error alcMakeContextCurrent %x : %s\n", ret,alutGetErrorString (ret));  
  189.     }  
  190. }  
  191.   
  192. void cclass_openal_interface::stopSound()  
  193. {  
  194.     alSourceStop(m_outSourceId);  
  195. }  
  196.   
  197. void cclass_openal_interface::SetVolume(float volume)//volume取值范围(0~1)  
  198. {  
  199.     m_volume = volume;  
  200.     alSourcef(m_outSourceId,AL_GAIN,volume);  
  201. }  
  202.   
  203. float cclass_openal_interface::GetVolume()  
  204. {  
  205.     return m_volume;  
  206. }  
  207.   
  208. int cclass_openal_interface::updataQueueBuffer()  
  209. {  
  210.     //播放状态字段  
  211.     ALint stateVaue = 0;  
  212.   
  213.     //获取处理队列,得出已经播放过的缓冲器的数量  
  214.     alGetSourcei(m_outSourceId, AL_BUFFERS_PROCESSED, &m_numprocessed);  
  215.     //获取缓存队列,缓存的队列数量  
  216.     alGetSourcei(m_outSourceId, AL_BUFFERS_QUEUED, &m_numqueued);  
  217.   
  218.     //获取播放状态,是不是正在播放  
  219.     alGetSourcei(m_outSourceId, AL_SOURCE_STATE, &stateVaue);  
  220.   
  221.     //printf("===statevaue ========================%x\n",stateVaue);  
  222.   
  223.     if (stateVaue == AL_STOPPED ||  
  224.         stateVaue == AL_PAUSED ||   
  225.         stateVaue == AL_INITIAL)   
  226.     {  
  227.         //如果没有数据,或数据播放完了  
  228.         if (m_numqueued < m_numprocessed || m_numqueued == 0 ||(m_numqueued == 1 && m_numprocessed ==1))  
  229.         {  
  230.             //停止播放  
  231.             printf("...Audio Stop\n");  
  232.             stopSound();  
  233.             cleanUpOpenAL();  
  234.             return 0;  
  235.         }  
  236.   
  237.         if (stateVaue != AL_PLAYING)  
  238.         {  
  239.             playSound();  
  240.         }  
  241.     }  
  242.   
  243.     //将已经播放过的的数据删除掉  
  244.     while(m_numprocessed --)  
  245.     {  
  246.         ALuint buff;  
  247.         //更新缓存buffer中的数据到source中  
  248.         alSourceUnqueueBuffers(m_outSourceId, 1, &buff);  
  249.         //删除缓存buff中的数据  
  250.         alDeleteBuffers(1, &buff);  
  251.   
  252.         //得到已经播放的音频队列多少块  
  253.         m_IsplayBufferSize ++;  
  254.     }  
  255.     long long time = (long long )((m_IsplayBufferSize * m_oneframeduration) + 0.5);  
  256.     //printf("*****m_IsplayBufferSize : %ld",m_IsplayBufferSize);  
  257.     //printf("****************time : %ld(ms)\n",time);  
  258.     return 1;  
  259. }  
  260.   
  261. int cclass_openal_interface::openAudioFromQueue(char* data,int dataSize,int aSampleRate,int aBit ,int aChannel)  
  262. {  
  263.     int ret = 0;  
  264.     //样本数openal的表示方法  
  265.     ALenum format = 0;  
  266.     //buffer id 负责缓存,要用局部变量每次数据都是新的地址  
  267.     ALuint bufferID = 0;  
  268.   
  269.     if (m_datasize == 0 &&  
  270.         m_samplerate == 0 &&  
  271.         m_bit == 0 &&  
  272.         m_channel == 0)  
  273.     {  
  274.         if (dataSize != 0 &&  
  275.             aSampleRate != 0 &&  
  276.             aBit != 0 &&  
  277.             aChannel != 0)  
  278.         {  
  279.             m_datasize = dataSize;  
  280.             m_samplerate = aSampleRate;  
  281.             m_bit = aBit;  
  282.             m_channel = aChannel;  
  283.             m_oneframeduration = m_datasize * 1.0 /(m_bit/8) /m_channel /m_samplerate * 1000 ;   //计算一帧数据持续时间  
  284.         }  
  285.     }  
  286.   
  287.     //创建一个buffer  
  288.     alGenBuffers(1, &bufferID);  
  289.     if((ret = alGetError()) != AL_NO_ERROR)  
  290.     {  
  291.         printf("error alGenBuffers %x : %s\n", ret,alutGetErrorString (ret));  
  292.         //AL_ILLEGAL_ENUM   
  293.         //AL_INVALID_VALUE  
  294.         //#define AL_ILLEGAL_COMMAND                        0xA004  
  295.         //#define AL_INVALID_OPERATION                      0xA004  
  296.     }  
  297.   
  298.     if (aBit == 8)   
  299.     {  
  300.         if (aChannel == 1)   
  301.         {  
  302.             format = AL_FORMAT_MONO8;  
  303.         }  
  304.         else if(aChannel == 2)  
  305.         {  
  306.             format = AL_FORMAT_STEREO8;  
  307.         }  
  308.     }  
  309.   
  310.     if( aBit == 16 )  
  311.     {  
  312.         if( aChannel == 1 )   
  313.         {   
  314.             format = AL_FORMAT_MONO16;  
  315.         }  
  316.         if( aChannel == 2 )   
  317.         {  
  318.             format = AL_FORMAT_STEREO16;  
  319.         }  
  320.     }  
  321.     //指定要将数据复制到缓冲区中的数据  
  322.     alBufferData(bufferID, format, data, dataSize,aSampleRate);  
  323.     if((ret = alGetError()) != AL_NO_ERROR)  
  324.     {  
  325.         printf("error alBufferData %x : %s\n", ret,alutGetErrorString (ret));  
  326.         //AL_ILLEGAL_ENUM   
  327.         //AL_INVALID_VALUE  
  328.         //#define AL_ILLEGAL_COMMAND                        0xA004  
  329.         //#define AL_INVALID_OPERATION                      0xA004  
  330.     }  
  331.     //附加一个或一组buffer到一个source上  
  332.     alSourceQueueBuffers(m_outSourceId, 1, &bufferID);  
  333.     if((ret = alGetError()) != AL_NO_ERROR)  
  334.     {  
  335.         printf("error alSourceQueueBuffers %x : %s\n", ret,alutGetErrorString (ret));  
  336.     }  
  337.   
  338.     //更新队列数据  
  339.     ret = updataQueueBuffer();  
  340.   
  341.     //删除一个缓冲 这里不应该删除缓冲,在source里面播放完毕删除  
  342.     //alDeleteBuffers(1, &bufferID);  
  343.     bufferID = 0;  
  344.   
  345.     return 1;  
  346. }  



 

//main.cpp

 

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. #include "Lvs_OpenAl_Interface.h"  
  2.   
  3. //要显示的pcm/wav文件路径及名称  
  4. #define PCM_STREAM_PATH_NAME  "../pcm_stream/44100_2_16.pcm"  
  5.   
  6. int main()  
  7. {  
  8.     int ret = 0;  
  9.     int nSampleRate = 44100;                   //采样率  
  10.     int nBit = 16;                             //样本数  
  11.     int nChannel = 2;                          //声道  
  12.     int ndatasize = 1024 * (nBit/8) *nChannel; //每次读取的数据大小   
  13.     char ndata[4096 + 1] = {0};                //读取的数据           
  14.     FILE * pFile_pcm = NULL;                   //读取pcm数据的文件句柄    
  15.   
  16.     //打开pcm文件  
  17.     if((pFile_pcm = fopen(PCM_STREAM_PATH_NAME, "rb")) == NULL)  
  18.     {  
  19.         printf("filed open file : %s\n",PCM_STREAM_PATH_NAME);  
  20.         return getchar();  
  21.     }  
  22.     else  
  23.     {  
  24.         printf("success open file : %s\n",PCM_STREAM_PATH_NAME);  
  25.     }  
  26.   
  27.     //init  
  28.     lvs_openal_interface_init();  
  29.   
  30.     //设置音量volume取值范围(0~1)  
  31.     lvs_openal_interface_setvolume(1.0);  
  32.   
  33.     for(;;)  
  34.     {  
  35.         Sleep(23);  
  36.         //循环读取文件  
  37.         ret = fread(ndata, 1,ndatasize, pFile_pcm);  
  38.         if (ret != ndatasize)  
  39.         {  
  40.             //seek到文件开头  
  41.             fseek(pFile_pcm, 0, SEEK_SET);  
  42.             fread(ndata, 1,ndatasize, pFile_pcm);  
  43.         }  
  44.         //具体的处理在这里  
  45.         ret = lvs_openal_interface_openaudiofromqueue((char *)ndata,ndatasize,nSampleRate,nBit,nChannel);  
  46.   
  47.         long long time = lvs_openal_interface_getrealpts();  
  48.     }  
  49.   
  50.     //uinit  
  51.     lvs_openal_interface_uninit();  
  52.   
  53.     //关闭pcm文件  
  54.     if (pFile_pcm != NULL)  
  55.     {  
  56.         fclose(pFile_pcm);  
  57.         pFile_pcm = NULL;  
  58.     }  
  59.   
  60.     return 1;  
  61. }  



 

 

 

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

 

本demo还需完善。

 

from:http://blog.csdn.net/zhuweigangzwg/article/details/53286945

原文链接:https://yq.aliyun.com/articles/243581
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章