# OpenSL ES **Repository Path**: jackzhous/OpenSL-ES ## Basic Information - **Project Name**: OpenSL ES - **Description**: Android移动端使用OpenSL ES - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2019-07-23 - **Last Updated**: 2023-03-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # OpenSL ES音频库学习 ## 简介 __什么是OpenSL ES?__ openSL ES是一个专用于嵌入式系统的音频库,可以提供对音频的播放和录制等相关功能,在Android上Aduio Recoder都是基于此库实现的,同时,我们也可以在Android的JNI里面使用此库进行音频开发,官方介绍请[点击][1] ## 使用方式 OpenSL ES几乎都是通过一个Object一个Interface成对来获取一项功能,比如OpenSL ES的全局引擎engineObject和engineInterface: ```java 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,其内部大致有这么一个状态机制 1. 创建Object时,其处于未实现UNREALIZED 状态,内部也没有为其分配任何资源 2. Realize激活后,才能通过Object获取其功能Interface,此时处于REALIZED状态 3. 当音频被抢占或出现错误,Object处于SUSPENDED暂停状态,如果想恢复REALIZED需要调用Object的Resume方法 4. 当我们把Object销毁后,就进入UNREALIZED状态了,重新使用需要在REALIZED才行 下图是参考网图: ![状态机制](https://img-blog.csdnimg.cn/20190724095812600.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2phY2t6aG91eXU=,size_16,color_FFFFFF,t_70) ### IO回调机制 使用OpenSL ES进行音频录制和播放时,我们都是和一个缓冲区(自己创建)打交道,录音时,触发回调函数后,说明缓存区数据填满了,我们可以从里面取出录好的数据;播放时,触发回调函数后,说明缓冲区数据播完了,我们往缓冲区添加音频数据 ## 部分功能介绍 ### 录音Recorder 录音重点主要集中在初始化配置方面: + 配置输入和输出 + 配置缓冲区IO回调 + 创建RecorderObject和RecorderInterface #### 输入和输出 __SLDataSource和SLDataSink__ ```c typedef struct SLDataSource_ { void *pLocator; //输入数据类别 void *pFormat; //输入数据格式 } SLDataSource; typedef struct SLDataSink_ { void *pLocator; void *pFormat; } SLDataSink; ``` 其中pLocator的类型可以是: ```c SLDataLocator_Address SLDataLocator_BufferQueue SLDataLocator_IODevice SLDataLocator_MIDIBufferQueue SLDataLocator_URI ``` 针对录音的数据来源我们可以选择IODevice,以下是对IODevice的配置: ```java //录音源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 ```java //输出 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开始播放了: ```java ///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][2] 参考的链接: https://juejin.im/post/5bda5ed85188257f3e09d6f5 https://zhuanlan.zhihu.com/p/20865418 [1]:https://developer.android.com/ndk/guides/audio/opensl-for-android?hl=zh-CN [2]:https://gitee.com/jackzhous/OpenSL-ES