一、OpenSL ES是什么?
OpenSL ES ( 嵌入式音频加速标准), 它是无授权费、跨平台、针对嵌入式系统精心优化的硬件音频加速API。它为嵌入式移动多媒体设备上的本地应用程序开发者提供标准化, 高性能,低响应时间的音频功能实现方法,并实现软/硬件音频性能的直接跨平台部署,降低执行难度,促进高级音频市场的发展。
简单来说:OpenSL ES是一个嵌入式、跨平台、免费的、音频 处理库。
二、PCM文件是什么?
PCM(Pulse Code Modulation)脉冲编码调制是数字通信的编码方式之一。主要过程是将话音、图像等模拟信号每隔一定时间进行取样,使其离散化,同时将抽样值按分层单位四舍五入取整量化,同时将抽样值按一组二进制码来表示抽样脉冲的幅值。
什么是WAV和PCM?WAV:wav是一种无损的音频文件格式,WAV符合 PIFF(Resource Interchange File Format)规范。所有的WAV都有一个文件头,这个文件头音频流的编码参数。WAV对音频流的编码没有硬性规定,除了PCM之外,还有几乎所有支持ACM规范的编码都可以为WAV的音频流进行编码。
PCM:PCM(Pulse Code Modulation----脉码调制录音)。所谓PCM录音就是将声音等模拟信号变成符号化的脉冲列,再予以记录。PCM信号是由[1]、[0]等符号构成的数字信号,而未经过任何编码和压缩处理。与模拟信号比,它不易受传送系统的杂波及失真的影响。动态范围宽,可得到音质相当好的影响效果。
简单来说:wav是一种无损的音频文件格式,pcm是没有压缩的编码方式。
============================================================================================WAV和PCM的关系 :
WAV可以使用多种音频编码来压缩其音频流,不过我们常见的都是音频流被PCM编码处理的WAV,但这不表示WAV只能使用PCM编码,MP3编码同样也可以运用在WAV中,和AVI一样,只要安装好了相应的Decode,就可以欣赏这些WAV了。在Windows平台下,基于PCM编码的WAV是被支持得最好的音频格式,所有音频软件都能完美支持,由于本身可以达到较高的音质的要求,因此,WAV也是音乐编辑创作的首选格式,适合保存音乐素材。因此,基于PCM编码的WAV被作为了一种中介的格式,常常使用在其他编码的相互转换之中,例如MP3转换成WMA。
简单来说:pcm是无损wav文件中音频数据的一种编码方式,但wav还可以用其它方式编码。
三、使用OpenSL ES播放一个pcm文件的流程
- cmake配置
cmake_minimum_required(VERSION3.4.1) set(CMAKE_C_FLAGS"${CMAKE_C_FLAGS}-std=c99-Wall") add_library( native-lib SHARED native-lib.cpp) find_library( log-lib log) target_link_libraries( native-lib android OpenSLES ${log-lib})
- 创建native方法
public class MainActivity extends AppCompatActivity{ static{ System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public native void playpcm(String url); //点击事件 public void onButtonClick(View view){ File file = new File(Environment.getExternalStorageDirectory(),"mm.pcm"); playpcm(file.getAbsolutePath()); } }
- 代码
#include<jni.h> #include<string> #include<SLES/OpenSLES.h> #include<SLES/OpenSLES_Android.h> #include"AndroidLog.h" SLObjectItf engineObject = NULL; SLEngineItf engineEngin = NULL; SLObjectItf outputMixObject =NULL; SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL; SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR; SLObjectItf pcmPlayObject = NULL; SLPlayItf pclPlayerPlay = NULL; SLAndroidSimpleBufferQueueItf pcmBufferQueue; FILE *pcmFile; void *buffer; uint8_t *out_buffer; int getPcmData(void **pcm){ int size=0; while(!feof(pcmFile)){//是否读到结尾 size=fread(out_buffer,1,44100*2*2,pcmFile); if(out_buffer==NULL){ LOGD("%s","读取结束"); break;//读取完毕 }else{ LOGD("%s","读取ing"); } *pcm=out_buffer; break; } return size; } void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf,void *context){ int size = getPcmData(&buffer); if(buffer!=NULL){ (*pcmBufferQueue)->Enqueue(pcmBufferQueue,buffer,size); } } extern"C" JNIEXPORT void JNICALL Java_com_qy_ffmpeg_102_MainActivity_playpcm (JNIEnv *env,jobject instance,jstring url_){ const char *url = env->GetStringUTFChars(url_ , 0); pcmFile=fopen(url,"r");//打开文件 if(pcmFile==NULL){ return; } //读一秒钟的数据 out_buffer=(uint8_t*)malloc(44100*2*2); SLresult result; //1创建引擎接口(参数:) result=slCreateEngine(&engineObject,0,NULL,0,NULL,NULL); //判断是否成功 assert(SL_RESULT_SUCCESS==result); (void)result; //2实例化引擎 result=(*engineObject)->Realize(engineObject,SL_BOOLEAN_FALSE); assert(SL_RESULT_SUCCESS==result); (void)result; //3获取引擎接口(其他对象需要这个), result=(*engineObject) ->GetInterface(engineObject,SL_IID_ENGINE,&engineEngin); assert(SL_RESULT_SUCCESS==result); (void)result; //4创建混音器 const SLInterfaceID mids[1]={SL_IID_ENVIRONMENTALREVERB}; const SLboolean mreq[1]={SL_BOOLEAN_FALSE}; result=(*engineEngin) ->CreateOutputMix(engineEngin,&outputMixObject,1,mids,mreq); assert(SL_RESULT_SUCCESS==result); (void)result; //实现输出组合 result=(*outputMixObject) ->Realize(outputMixObject,SL_BOOLEAN_FALSE); assert(SL_RESULT_SUCCESS==result); (void)result; //获得环境混响接口 result=(*outputMixObject) ->GetInterface(outputMixObject,SL_IID_ENVIRONMENTALREVERB, &outputMixEnvironmentalReverb); 获取环境混响界面 ///如果环境混响效果不可用,则可能会失败, //原因是该功能不存在,CPU负载过大或未请求并授予所需的MODIFY_AUDIO_SETTINGS权限 if(SL_RESULT_SUCCESS==result){ result=(*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties( outputMixEnvironmentalReverb,&reverbSettings); (void)result; } //5创建缓冲队列(配置音频源) SLDataLocator_AndroidBufferQueue android_queue= {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE , 2}; //设置要播放的pcm格式的内容 SLDataFormat_PCM pcm={ SL_DATAFORMAT_PCM,//格式类型 2,//声道(单声道\双声道) SL_SAMPLINGRATE_44_1,//采样率 SL_PCMSAMPLEFORMAT_FIXED_16,//采样格式 SL_PCMSAMPLEFORMAT_FIXED_16,//采样容器大小 SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT,//声道相关 SL_BYTEORDER_LITTLEENDIAN//块的字节顺序从16--32位 }; //配置音频接收器 SLDataLocator_OutputMix outputMix={SL_DATALOCATOR_OUTPUTMIX,outputMixObject}; SLDataSource slDataSource={&android_queue,&pcm}; SLDataSink audioSnk={&outputMix,NULL,}; //创建音频播放器: const SLInterfaceIDids[2]={SL_IID_BUFFERQUEUE}; const SLbooleanreq[2]={SL_BOOLEAN_TRUE}; result=(*engineEngin)-> CreateAudioPlayer(engineEngin,&pcmPlayObject, &slDataSource,&audioSnk,1,ids,req); assert(SL_RESULT_SUCCESS==result); (void)result; //实例化播放器 result=(*pcmPlayObject)->Realize(pcmPlayObject,SL_BOOLEAN_FALSE); assert(SL_RESULT_SUCCESS==result); (void)result; //得到播放接口 result=(*pcmPlayObject)->GetInterface(pcmPlayObject,SL_IID_PLAY,&pclPlayerPlay); assert(SL_RESULT_SUCCESS==result); (void)result; //设置缓冲 result=(*pcmPlayObject)->GetInterface(pcmPlayObject,SL_IID_BUFFERQUEUE,&pcmBufferQueue); assert(SL_RESULT_SUCCESS==result); (void)result; //设置回调 (*pcmBufferQueue)->RegisterCallback(pcmBufferQueue,pcmBufferCallBack,NULL); //设置播放状态 (*pclPlayerPlay)->SetPlayState(pclPlayerPlay,SL_PLAYSTATE_PLAYING); // pcmBufferCallBack(pcmBufferQueue,NULL); //======== env->ReleaseStringUTFChars(url_,url); }