一、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文件的流程

  1. 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})
  1. 创建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());

      }

}
  1. 代码
#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);

}

PCM文件下载地址