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
OpenAL播放pcm或wav数据流-windows/ios/android(一)的更多相关文章
-
js判断操作系统windows,ios,android(笔记)
使用JS判断用户使用的系统是利用浏览器的userAgent. navigator.userAgent:userAgent 获取了浏览器用于 HTTP 请求的用户代理头的值. navigator.pla ...
-
微软云平台媒体服务实践系列 2- 使用动态封装为iOS, Android , Windows 等多平台提供视频点播(VoD)方案
文章微软云平台媒体服务实践系列 1- 使用静态封装为iOS, Android 设备实现点播(VoD)方案 介绍了如何针对少数iOS, Android 客户端的场景,出于节约成本的目的使用媒体服务的静 ...
-
WIN32下使用DirectSound接口的简单音频播放器(支持wav和mp3)
刚好最近接触了一些DirectSound,就写了一个小程序练练手,可以用来添加播放基本的wav和mp3音频文件的播放器.界面只是简单的GDI,dxsdk只使用了DirectSound8相关的接口. D ...
-
DirectSound播放PCM(可播放实时采集的音频数据)
前言 该篇整理的原始来源为http://blog.csdn.net/leixiaohua1020/article/details/40540147.非常感谢该博主的无私奉献,写了不少关于不同多媒体库的 ...
-
Web Api 中使用 PCM TO WAV 的语音操作
/// <summary> /// 语音[文件.上传.解码.保存(WAV)] /// </summary> [DeveloperEx("Liwei:秘书语音需求单&q ...
-
最简单的视音频播放示例9:SDL2播放PCM
本文记录SDL播放音频的技术.在这里使用的版本是SDL2.实际上SDL本身并不提供视音频播放的功能,它只是封装了视音频播放的底层API.在Windows平台下,SDL封装了Direct3D这类的API ...
-
最简单的视音频播放示例8:DirectSound播放PCM
本文记录DirectSound播放音频的技术.DirectSound是Windows下最常见的音频播放技术.目前大部分的音频播放应用都是通过DirectSound来播放的.本文记录一个使用Direct ...
-
最简单的视音频播放演示样例8:DirectSound播放PCM
===================================================== 最简单的视音频播放演示样例系列文章列表: 最简单的视音频播放演示样例1:总述 最简单的视音频 ...
-
微信小程序语音与讯飞语音识别接口(Java),Kronopath/SILKCodec,ffmpeg处理silk,pcm,wav转换
项目需求,需要使用讯飞的语音识别接口,将微信小程序上传的录音文件识别成文字返回 首先去讯飞开放平台中申请开通语音识别功能 在这里面下载sdk,然后解压,注意appid与sdk是关联的,appid在初始 ...
随机推荐
-
Python发布包到Pypi
本地打包:python setup.py sdist 上传Pypi:python setup.py register sdist upload
-
C#开源系统大汇总(转)
一.AOP框架 Encase 是C#编写开发的为.NET平台提供的AOP框架.Encase 独特的提供了把方面(aspects)部署到运行时代码,而其它AOP框架依赖配置文件的方式.这种 ...
-
收集统计信息让SQL走正确的执行计划
数据库环境:SQL SERVER 2005 今天在生产库里抓到一条跑得慢的SQL,语句不是很复杂,返回的数据才有800多行, 却执行了34分钟,甚至更久. 先看一下执行结果 我贴一下SQL. SELE ...
-
JavaSE学习笔记
1.数据类型 boolean char byte short int long double float double array class interface 总结: 前9种基本类型,后3中引用类 ...
-
面向对象设计——抽象工厂(Abstract Factory)模式
定义 提供一个创建一系列相关或者相互依赖对象的接口,而无需指定它们具体的类.抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道或关心实际产出的具体产品是什么.这样一来,客户就能从具体的产 ...
-
Solve fatal error: helper_math.h: No such file or directory
When the 'fatal error: helper_math.h: No such file or directory' occurs, it means the 'helper_math.h ...
-
vue 学习笔记—路由篇
一.关于三种路由 动态路由 就是path:good/:ops 这种 用 $route.params接收 <router-link>是用来跳转 <router-view> ...
-
ES6,Array.find()和findIndex()函数的用法
ES6为Array增加了find(),findIndex函数. find()函数用来查找目标元素,找到就返回该元素,找不到返回undefined. findIndex()函数也是查找目标元素,找到就返 ...
-
Java基础-面向对象(08)
面向过程 完成一个需求的步骤:首先是搞清楚我们要做什么,然后在分析怎么做,最后我们再代码体现.一步一步去实现,而具体的每一步都需要我们去实现和操作.这些步骤相互调用和协作,完成我们的需求.面向过程开发 ...
-
pip简单配置
pip安装Python模块的工具,等价于Redhat中的yum! 01.下载 百度云盘:http://pan.baidu.com/s/1eRHGBfk ###相关的 Linux ...