什么是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,其内部大致有这么一个状态机制
使用OpenSL ES进行音频播放时,当缓冲区数据没有填充满,会一直触发回调函数,直到缓冲区填满后停止,当缓冲区数据消耗完后,再近些下一轮的回调触发
录音重点主要集中在初始化配置方面:
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,然后在计算
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