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
- /** 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在初始 ...
随机推荐
- 笔试真题解析 ALBB-2015 算法project师实习生机试
1.用十进制计算30!(30的阶乘),将结果转换成3进制进行表示的话,该进制下的结果末尾会有____个0. [解析] 计算N.下3进制结果末尾有多少个0,事实上就是计算3进制中的3被进位多少次,仅仅要 ...
- codevs 必做:2776、1222
2776 寻找代表元 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 广州二中苏元实验学校一共有n个社团,分 ...
- c# 怎么更改DataTable 中某列的值?
DataColumns dc = td.Columns["你的列"]; int inx = dc.Ordinal;td.Columns.Remove(dc);dc.DefaultV ...
- iostat命令简单使用
1.iostat使用范围 iostat命令可以生成3种类型的报告: (1)CPU使用情况的报告 (2)设备使用情况的报告 (3)网络文件系统(NFS)使用情况的报告 2.每种报告的格式说明 关于CPU ...
- php自定义函数: 计算两个时间日期相隔的天数,时,分,秒
function timediff( $begin_time, $end_time ) { if ( $begin_time < $end_time ) { $starttime = $begi ...
- cookie的坑
HTTP Cookie 设置了secure , 该cookie只能在HTTPS通道下被写入浏览器. HTTPS Cookie 设置了secure , 该cookie只能 ...
- Java语言实现简单FTP软件------>源码放送(十三)
Java语言实现简单FTP软件------>FTP协议分析(一) Java语言实现简单FTP软件------>FTP软件效果图预览之下载功能(二) Java语言实现简单FTP软件----- ...
- 关于中国省市的一份js代码
下面是一份关于中国省市的js代码,搜藏起来,非常有用. var arrCity = [ { name:"请选择", sub:[{name:"请选择"}], ty ...
- 正则表达式 匹配符合A表达式切不符合B表达式的字符串
有一道这样的面试题 写一个Java方法,利用正则表达式判断输入str中包含字符串”ios“或”apple“(大小写不敏感),但不包括”mediaplayer“.如果满足条件,返回所包含的字符串”ios ...
- SAP初始账号
方法1:有其中某Client的登录帐号1. 用已有帐号登录某个Client2. 运行Tcode SE303. 单击“tips and tricks“按钮4. 在Performance Tips an ...