一、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);
}PCM文件下载地址
















