OpenSL ES音频库学习
OpenSL ES音频库学习
简介
什么是OpenSL ES? openSL ES是一个专用于嵌入式系统的音频库,可以提供对音频的播放和录制等相关功能,在Android上Aduio Recoder都是基于此库实现的,同时,我们也可以在Android的JNI里面使用此库进行音频开发,官方介绍请点击
使用方式
OpenSL ES几乎都是通过一个Object一个Interface成对来获取一项功能,比如OpenSL ES的全局引擎engineObject和engineInterface:
SLObjectItf engineObject; //引擎始祖对象SLEngineItf engineInterface; //引擎接口slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);(*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);(*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineInterface);
内部大致原理
内部状态机制
从上一步得知所有的功能都是先创建Object,在获取其interface,其内部大致有这么一个状态机制
- 创建Object时,其处于未实现UNREALIZED 状态,内部也没有为其分配任何资源
- Realize激活后,才能通过Object获取其功能Interface,此时处于REALIZED状态
- 当音频被抢占或出现错误,Object处于SUSPENDED暂停状态,如果想恢复REALIZED需要调用Object的Resume方法
- 当我们把Object销毁后,就进入UNREALIZED状态了,重新使用需要在REALIZED才行
下图是参考网图:
IO回调机制
使用OpenSL ES进行音频播放时,当缓冲区数据没有填充满,会一直触发回调函数,直到缓冲区填满后停止,当缓冲区数据消耗完后,再近些下一轮的回调触发
部分功能介绍
录音Recorder
录音重点主要集中在初始化配置方面:
- 配置输入和输出
- 配置缓冲区IO回调
- 创建RecorderObject和RecorderInterface
输入和输出
SLDataSource和SLDataSink
typedef struct SLDataSource_ {void *pLocator; //输入数据类别void *pFormat; //输入数据格式
} SLDataSource;typedef struct SLDataSink_ {void *pLocator;void *pFormat;
} SLDataSink;
其中pLocator的类型可以是:
SLDataLocator_Address
SLDataLocator_BufferQueue
SLDataLocator_IODevice
SLDataLocator_MIDIBufferQueue
SLDataLocator_URI
针对录音的数据来源我们可以选择IODevice,以下是对IODevice的配置:
//录音源source
slHelper->ioDevice.locatorType = SL_DATALOCATOR_IODEVICE; //源头是io设备
slHelper->ioDevice.deviceType = SL_IODEVICE_AUDIOINPUT; //音频输入
slHelper->ioDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
slHelper->ioDevice.device = NULL; //device ID生效的前提必须device NULL
配置录音的输出,我们选择SLDataLocator_BufferQueue类型,输出到缓冲区,还需要配置输出的数据给format
//输出
queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; //输出到缓冲队列
queue.numBuffers = 2; //2个缓冲队列pcmFormat.formatType = SL_DATAFORMAT_PCM; //录音数据格式
pcmFormat.numChannels = channels;
pcmFormat.samplesPerSec = sampleRate;
pcmFormat.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; //每个采样点bit
pcmFormat.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
pcmFormat.channelMask = getChannelMask(channels); //根据声道数确定掩码
pcmFormat.endianness = SL_BYTEORDER_LITTLEENDIAN; //字节小端模式sink.pLocator = &(queue);
sink.pFormat = &(pcmFormat);
最后,就可以创建Recoder开始播放了:
///Recorder/
SLInterfaceID id[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
SLboolean required[] = {SL_BOOLEAN_TRUE};(*engineInterface)->CreateAudioRecorder(engineInterface, &recorderObject, &source, &sink, 1, id, required);
(*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);//Register
(*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,&(androidBufferQueueItf));(*androidBufferQueueItf)->RegisterCallback(androidBufferQueueItf, openSLCallBack, NULL);//start recorder
(*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderInterface);
(*recorderInterface)->SetRecordState(recorderInterface, SL_RECORDSTATE_RECORDING);
ok,以上就是楼主学习过程中的一点小小心得,如有不正,请指正;
以下是我的学习demo
音量调节
也是调用openSL ES的音量接口,设置分贝值即可,重要的是分贝值的计算。
SLmillibel level = getAmplificationLevel((leftVolume + rightVolume) / 2);
SLresult lresult = (*slVolumeItf)->SetVolumeLevel(slVolumeItf, level);
if (lresult != SL_RESULT_SUCCESS) {LOGE("slVolumeItf->SetVolumeLevel failed %d\n", (int)lresult);
}SLmillibel getAmplificationLevel(float volumeLevel) {if(volumeLevel < 0.0000001){return SL_MILLIBEL_MIN;}//分贝计算公式SLmillibel mb = lround(20f * log10f(volumeLevel));if(mb < SL_MILLIBEL_MIN){mb = SL_MILLIBEL_MIN;}return mb;
}
主要理解分贝(声压级)计算公式:
上诉表达式中,推导过程不做解释,Prms表示实际测的声压级,Pref是参考的声压级,由于人耳能听到最微小的声压级为0.00002Pa,所以默认是Pref一般采用这个值作为参考值计算出分贝,而不同手机厂商精度可能不一样,一个粗糙的计算方法是,环境静音时手机测量出来的值(MediaRecorder.getMaxAmplitude)作为Pref,然后在计算
部分API理解
- SLresult (*CreateAudioPlayer) (
SLEngineItf self,
SLObjectItf * pPlayer,
SLDataSource *pAudioSrc,
SLDataSink *pAudioSnk,
SLuint32 numInterfaces,
const SLInterfaceID * pInterfaceIds,
const SLboolean * pInterfaceRequired
); - SLresult (*CreateAudioRecorder) (
SLEngineItf self,
SLObjectItf * pRecorder,
SLDataSource *pAudioSrc,
SLDataSink *pAudioSnk,
SLuint32 numInterfaces,
const SLInterfaceID * pInterfaceIds,
const SLboolean * pInterfaceRequired
);
以上两个函数用法类似,前4个参数不多做解释,主要是后三个参数,标明创建播放器和录音器所具有的属性,
numInterfaces:属性的数量
pInterfaceIds:属性特性数组,数组长度和属性数量相同
pInterfaceRequired:上面的属性启动或者不启动,数组长度和属性数量相同
以播放器为例,播放器来源有bufferQueue、混音器还有音量调节,那么你应该做如下配置:
const SLInterfaceID ids[3] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME, SL_IID_PLAY};
const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
result = (*slEngine)->CreateAudioPlayer(slEngine, &slPlayerObject, &slDataSource,&audioSink, 3, ids, req);
参考的链接:
https://juejin.im/post/5bda5ed85188257f3e09d6f5
https://zhuanlan.zhihu.com/p/20865418
OpenSL ES音频库学习相关推荐
- 浅聊OpenSL ES音频开发
浅聊OpenSL ES音频开发 导语 开发Android上的音频应用,一般是使用Android提供的AudioRecord采集音频,使用AudioTrack播放音频,使用MediaCodec来编解码, ...
- 音视频学习 AudioTrack、OpenSL ES 音频渲染
前言 在讲解音频渲染之前,需要对音频的基础知识有所了解,所以该篇分为基础概念和AudioTrack 以及 OpenSL ES Demo 实例讲解,这样有助于更好的理解 Android 中音频渲染. 音 ...
- OpenHarmony OpenSl ES音频播放开发
1.OpenHarmony OpenSl ES音频播放简介 开发者可以通过本博文了解在OpenHarmony中如何使用OpenSL ES接口进行音频播放相关操作:当前仅实现了部分OpenSL ES接口 ...
- OpenHarmony OpenSl ES音频录制
1.OpenHarmony OpenSl ES音频录制开发简介 开发者可以通过本博文了解在OpenHarmony中如何使用OpenSL ES进行录音相关操作:当前仅实现了部分OpenSL ES接口,因 ...
- 【Android音视频】OpenSL ES音频播放示例一
本文将实现一个使用OpenSL ES来播放assets目录下mp3歌曲的demo(实际推荐大家使用oboe库). Android NDK之高性能音频https://developer.android. ...
- 播放器基础--OpenSL ES音频播放
介绍 官网 OpenSL ES (Open Sound Library for Embedded Systems)是针对嵌入式系统的一套无授权费,跨平台, 硬件加速的音频API.它提供了一套标准化,高 ...
- Android音视频学习系列(五) — 掌握音频基础知识并使用AudioTrack、OpenSL ES渲染PCM数据
系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...
- 音视频开发之旅(36) -FFmpeg +OpenSL ES实现音频解码和播放
目录 OpenSL ES基本介绍 OpenSL ES播放音频流程 代码实现 遇到的问题 资料 收获 上一篇我们通过AudioTrack实现了FFmpeg解码后的PCM音频数据的播放,在Android上 ...
- Android 音频 OpenSL ES 录音 采集
1,创建引擎 2,创建AudioRecorder并开始录音 3,暂停录音 4,释放资源 5,数据是通过回调函数处理的. 好处:缓冲区不用通过AudioRecord.getMinBufferSize获取 ...
最新文章
- 给分类添加缓存并解释StringRedisTemplate
- SPOJ - NSUBSTR Substrings(后缀自动机)
- Java 集合系列(一)
- flutter调用api_如何在Flutter(REST API)中进行API调用
- Python Unittest参数化parameterized之数据驱动
- 从零开始搭建vue移动端项目到上线的步骤
- 【跃迁之路】【456天】程序员高效学习方法论探索系列(实验阶段213-2018.05.07)...
- git学习笔记-(3-linux基本命令)
- solr配置oracle数据源,Solr索引Oracle数据库的基本配置
- 一个朋友的精彩BLOG
- ILdasm 的使用方法
- 运放的电压比较器电路
- typecho插件,typechoSEO插件,typecho程序插件
- css样式之导航条(nav)
- win10安装无法创建新分区也找不到现有分区问题
- Android的发布与更新
- 618投影仪怎么选?看看极米NEW Z6X、极米Z6X Pro与极米H3S
- 第46周收录128起融资,国内硬件新三板扎堆,大额融资激增,国内资本出海活跃 | 潜在周报
- Excel-宏、VBA
- Android 防止PC端第三方流氓软件强制安装apk至android设备
热门文章
- ubuntu 安装 TFTP server
- jar包的作用是什么?
- 算法:加油站的良好出发点问题
- windows7查看mysql是否启动_mysql5.7在windows不能启动的方法及查看数据库大小命令...
- ADC-JESD204B接口调试记录
- 内存管理——程序运行过程中内存的作用以及如何与cpu、os交互
- HTTP request response
- Java基础知识第二讲:Java开发手册/JVM/集合框架/异常体系/Java反射/语法知识/Java IO
- TabIndex 属性
- java中case when用法_CASE WHEN 的简单用法