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


    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 ");  
    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 ");  
    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) ",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=== ");  
    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=== ");  
    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 ", 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 ",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 ");  
    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) ",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 ", 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 ", 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 ", 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 ",PCM_STREAM_PATH_NAME);  
    20.         return getchar();  
    21.     }  
    22.     else  
    23.     {  
    24.         printf("success open file : %s ",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

  • 相关阅读:
    jQuery(3)——DOM操作
    jQuery(2)——选择器
    jQuery(1)——了解jQuery
    JavaScript(10)——Ajax以及跨域处理
    JavaScript(9)——call与apply
    JavaScript(8)——JSON
    JavaScript(7)——事件2.0
    JavaScript(6)——事件1.0
    JavaScript(5)——DOM
    spring mvc+spring+mybatis搭建javaWeb项目时遇到的一些问题
  • 原文地址:https://www.cnblogs.com/lidabo/p/6839313.html
Copyright © 2020-2023  润新知