Android OpenSL ES 开发:Android OpenSL 介绍和开发流程说明

article/2025/7/4 8:49:09

一、Android OpenSL ES 介绍

OpenSL ES (Open Sound Library for Embedded Systems)是无授权费、跨平台、针对嵌入式系统精心优化的硬件音频加速API。它为嵌入式移动多媒体设备上的本地应用程序开发者提供标准化, 高性能,低响应时间的音频功能实现方法,并实现软/硬件音频性能的直接跨平台部署,降低执行难度,促进高级音频市场的发展。简单来说OpenSL ES是一个嵌入式跨平台免费的音频处理库。 

Android的OpenSL ES库是在NDK的platforms文件夹对应android平台先相应cpu类型里面,如:

二、Android OpenSL ES 开发流程

OpenSL ES 的开发流程主要有如下6个步骤:

1、 创建接口对象

2、设置混音器

3、创建播放器(录音器)

4、设置缓冲队列和回调函数

5、设置播放状态

6、启动回调函数

注明:其中第4步和第6步是OpenSL ES 播放PCM等数据格式的音频是需要用到的。

在使用OpenSL ES的API之前,需要引入OpenSL ES的头文件,代码如下:

#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>

由于是在Native层使用该特性,所需需要在Android.mk中增加链接选项,以便在链接阶段使用到系统系统的OpenSL ES的so库:

LOCAL_LDLIBS += -lOepnSLES

我们知道OpenSL ES提供的是基于C语言的API,但是它是基于对象和接口的方式提供的,会采用面向对象的思想开发API。因此我们先来了解一下OpenSL ES中对象和接口的概念:

  • 对象:对象是对一组资源及其状态的抽象,每个对象都有一个在其创建时指定的类型,类型决定了对象可以执行的任务集,对象有点类似于C++中类的概念。
  • 接口:接口是对象提供的一组特征的抽象,这些抽象会为开发者提供一组方法以及每个接口的类型功能,在代码中,接口的类型由接口ID来标识。

需要重点理解的是,一个对象在代码中其实是没有实际的表示形式的,可以通过接口来改变对象的状态以及使用对象提供的功能。对象有可以有一个或者多个接口的实例,但是接口实例肯定只属于一个对象。

如果明白了OpenSL ES 中对象和接口的概念,那么下面我们就继续看看,在代码中是如何使用它们的。

上面我们也提到过,对象是没有实际的代码表示形式的,对象的创建也是通过接口来完成的。通过获取对象的方法来获取出对象,进而可以访问对象的其他的接口方法或者改变对象的状态,下面是使用对象和接口的相关说明。

2.1 OpenSL ES 开发最重要的接口类 SLObjectItf

 通过SLObjectItf接口类我们可以创建所需要的各种类型的类接口,比如:

  • 创建引擎接口对象:SLObjectItf engineObject
  • 创建混音器接口对象:SLObjectItf outputMixObject
  • 创建播放器接口对象:SLObjectItf playerObject

以上等等都是通过SLObjectItf来创建的。

2.2 SLObjectItf 创建的具体的接口对象实例

OpenSL ES中也有具体的接口类,比如(引擎:SLEngineItf,播放器:SLPlayItf,声音控制器:SLVolumeItf等等)。

2.3 创建引擎并实现

OpenSL ES中开始的第一步都是声明SLObjectItf接口类型的引擎接口对象engineObject,然后用方法slCreateEngine创建一个引擎接口对象;创建好引擎接口对象后,需要用SLObjectItf的Realize方法来实现engineObject;最后用SLObjectItf的GetInterface方法来初始化SLEngnineItf对象实例。如:

SLObjectItf engineObject = NULL;//用SLObjectItf声明引擎接口对象
SLEngineItf engineEngine = NULL;//声明具体的引擎对象实例void createEngine()
{SLresult result;//返回结果result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);//第一步创建引擎result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);//实现(Realize)engineObject接口对象result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);//通过engineObject的GetInterface方法初始化engineEngine
}

2.4 利用引擎对象创建其他接口对象

其他接口对象(SLObjectItf outputMixObject,SLObjectItf playerObject)等都是用引擎接口对象创建的(具体的接口对象需要的参数这里就说了,可参照ndk例子里面的),如:

//混音器
SLObjectItf outputMixObject = NULL;//用SLObjectItf创建混音器接口对象
SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;////创建具体的混音器对象实例
 
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);//利用引擎接口对象创建混音器接口对象
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);//实现(Realize)混音器接口对象
result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, &outputMixEnvironmentalReverb);//利用混音器接口对象初始化具体混音器实例//播放器
SLObjectItf playerObject = NULL;//用SLObjectItf创建播放器接口对象
SLPlayItf playerPlay = NULL;//创建具体的播放器对象实例
 
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, &audioSrc, &audioSnk, 3, ids, req);//利用引擎接口对象创建播放器接口对象
result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE);//实现(Realize)播放器接口对象
result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playerPlay);//初始化具体的播放器对象实例

最后就是使用创建好的具体对象实例来实现具体的功能。

三、OpenSL ES 使用示例

首先导入OpenSL ES和其他必须的库:

-lOpenSLES -landroid 

3.1 播放assets文件

创建引擎——>创建混音器——>创建播放器——>设置播放状态

JNIEXPORT void JNICALL
Java_com_renhui_openslaudio_MainActivity_playAudioByOpenSL_1assets(JNIEnv *env, jobject instance, jobject assetManager, jstring filename) {release();const char *utf8 = (*env)->GetStringUTFChars(env, filename, NULL);// use asset manager to open asset by filenameAAssetManager* mgr = AAssetManager_fromJava(env, assetManager);AAsset* asset = AAssetManager_open(mgr, utf8, AASSET_MODE_UNKNOWN);(*env)->ReleaseStringUTFChars(env, filename, utf8);// open asset as file descriptor
    off_t start, length;int fd = AAsset_openFileDescriptor(asset, &start, &length);AAsset_close(asset);SLresult result;//第一步,创建引擎
    createEngine();//第二步,创建混音器const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);(void)result;result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);(void)result;result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, &outputMixEnvironmentalReverb);if (SL_RESULT_SUCCESS == result) {result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(outputMixEnvironmentalReverb, &reverbSettings);(void)result;}//第三步,设置播放器参数和创建播放器// 1、 配置 audio sourceSLDataLocator_AndroidFD loc_fd = {SL_DATALOCATOR_ANDROIDFD, fd, start, length};SLDataFormat_MIME format_mime = {SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED};SLDataSource audioSrc = {&loc_fd, &format_mime};// 2、 配置 audio sinkSLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};SLDataSink audioSnk = {&loc_outmix, NULL};// 创建播放器const SLInterfaceID ids[3] = {SL_IID_SEEK, SL_IID_MUTESOLO, SL_IID_VOLUME};const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};result = (*engineEngine)->CreateAudioPlayer(engineEngine, &fdPlayerObject, &audioSrc, &audioSnk, 3, ids, req);(void)result;// 实现播放器result = (*fdPlayerObject)->Realize(fdPlayerObject, SL_BOOLEAN_FALSE);(void)result;// 得到播放器接口result = (*fdPlayerObject)->GetInterface(fdPlayerObject, SL_IID_PLAY, &fdPlayerPlay);(void)result;// 得到声音控制接口result = (*fdPlayerObject)->GetInterface(fdPlayerObject, SL_IID_VOLUME, &fdPlayerVolume);(void)result;//第四步,设置播放状态if (NULL != fdPlayerPlay) {result = (*fdPlayerPlay)->SetPlayState(fdPlayerPlay, SL_PLAYSTATE_PLAYING);(void)result;}//设置播放音量 (100 * -50:静音 )(*fdPlayerVolume)->SetVolumeLevel(fdPlayerVolume, 20 * -50);}

3.2 播放pcm文件

(集成到ffmpeg时,也是播放ffmpeg转换成的pcm格式的数据),这里为了模拟是直接读取的pcm格式的音频文件。

1. 创建播放器和混音器

//第一步,创建引擎
    createEngine();//第二步,创建混音器const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);(void)result;result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);(void)result;result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, &outputMixEnvironmentalReverb);if (SL_RESULT_SUCCESS == result) {result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(outputMixEnvironmentalReverb, &reverbSettings);(void)result;}SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};SLDataSink audioSnk = {&outputMix, NULL};

2. 设置pcm格式的频率位数等信息并创建播放器

// 第三步,配置PCM格式信息SLDataLocator_AndroidSimpleBufferQueue android_queue={SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2};SLDataFormat_PCM pcm={SL_DATAFORMAT_PCM,//播放pcm格式的数据2,//2个声道(立体声)SL_SAMPLINGRATE_44_1,//44100hz的频率SL_PCMSAMPLEFORMAT_FIXED_16,//位数 16位SL_PCMSAMPLEFORMAT_FIXED_16,//和位数一致就行SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立体声(前左前右)SL_BYTEORDER_LITTLEENDIAN//结束标志
    };SLDataSource slDataSource = {&android_queue, &pcm};const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND, SL_IID_VOLUME};const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};result = (*engineEngine)->CreateAudioPlayer(engineEngine, &pcmPlayerObject, &slDataSource, &audioSnk, 3, ids, req);//初始化播放器(*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);//    得到接口后调用  获取Player接口(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);

3. 设置缓冲队列和回调函数

//    注册回调缓冲区 获取缓冲队列接口(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);//缓冲接口回调(*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, NULL);

回调函数:

void * pcmBufferCallBack(SLAndroidBufferQueueItf bf, void * context)
{//assert(NULL == context);getPcmData(&buffer);// for streaming playback, replace this test by logic to find and fill the next bufferif (NULL != buffer) {SLresult result;// enqueue another bufferresult = (*pcmBufferQueue)->Enqueue(pcmBufferQueue, buffer, 44100 * 2 * 2);// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,// which for this code example would indicate a programming error
    }
}

读取pcm格式的文件:

void getPcmData(void **pcm)
{while(!feof(pcmFile)){fread(out_buffer, 44100 * 2 * 2, 1, pcmFile);if(out_buffer == NULL){LOGI("%s", "read end");break;} else{LOGI("%s", "reading");}*pcm = out_buffer;break;}
}

4. 设置播放状态并手动开始调用回调函数

//    获取播放状态接口(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);//    主动调用回调函数开始工作pcmBufferCallBack(pcmBufferQueue, NULL);

注意:

在回调函数中result = (*pcmBufferQueue)->Enqueue(pcmBufferQueue, buffer, 44100 * 2 * 2),最后的“44100*2*2”是buffer的大小,因为我这里是指定了没读取一次就从pcm文件中读取了“44100*2*2”个字节,所以可以正常播放,如果是利用ffmpeg来获取pcm数据源,那么实际大小要根据每个AVframe的具体大小来定,这样才能正常播放出声音!(44100 * 2 * 2 表示:44100是频率HZ,2是立体声双通道,2是采用的16位采样即2个字节,所以总的字节数就是:44100 * 2 * 2)


http://chatgpt.dhexx.cn/article/1Kty0Jvb.shtml

相关文章

Xcode7 网络请求报错:The resource could not be loaded because the App Transport Security policy requir...

Google后查证&#xff0c;iOS9引入了新特性App Transport Security (ATS)。详情&#xff1a;App Transport Security (ATS)新特性要求App内访问的网络必须使用HTTPS协议。但是现在公司的项目使用的是HTTP协议&#xff0c;使用私有加密方式保证数据安全。现在也不能马上改成HTTP…

【用户自定义气象站】

自定义气象站系统由数据采集器、传感器、总线模块、网络模块、供电模块等组成&#xff0c;可实现野外无人看守的情况下长期监测。用户可根据自己的需求&#xff0c;自定义测量指标&#xff0c;以达到观测目的。 自定义气象站测量指标 自定义气象站测量参数指标 总辐射、光合…

自动气象站设备的防雷要点

自动气象站设备&#xff0c;其工作场所几乎都是在户外&#xff0c;雨打风吹、电闪雷鸣、日晒雨淋&#xff0c;由于是雷雨天气&#xff0c;气象站也要连续工作。如何科学防雷&#xff0c;直接影响到气象站的运行与使用寿命。 自动气象站设备的工作要点是获取气象环境参数&#x…

12,桥接模式-露娜的召唤师技能

一,前言 7种结构型设计模式:桥接模式,适配器模式,装饰模式,组合模式,享元模式,外观模式,代理模式上篇我们说了装饰模式:动态地将责任附加到对象上,在不修改任何底层代码的情况下,为对象赋予新的职责开发中,我们经常会遇到一个类有两个或两个以上的维度经常在变化 如果我们使用…

浮标水质监测站是什么

浮标水质监测站是设立在河流、湖泊、水库、近岸海域等流域内进行现场水质自动监测的监测仪器&#xff0c;是以水质监测仪为核心&#xff0c;运用传感器技术&#xff0c;结合浮标体、电源供电系统、数据传输设备组成的放置于水域内的小型水质监测系统。用于连续自动监测被测水体…

【赛纳斯】EC Raman电化学拉曼光谱检测系统推动科研新突破

【前言】   近日,Angew在线发表了厦门大学李剑锋教授团队在设计用于氧还原反应的先进材料及改进催化剂的设计最 新综述文章。该论文综述了双金属纳米催化剂有序度对氧还原反应的影响。论文第 一作者为&#xff1a;Heng-Quan Chen,Huajie Ze,Mu-Fei Yue,论文共同通讯作者为&am…

Ethercat学习-从站源码移植

文章目录 简介移植源码1.源码结构2.GD32硬件接口准备1.SPI接口2.PDI中断配置3.Sync0中断配置4.Sync1中断配置5.定时器中断配置 3.移植准备4.源码移植1.修改头文件名2.ecatport.c文件修改1.SPI部分修改2.中断部分3.修改HW_Init()4.报错修改 3.myapp.c文件修改 5.其他 简介 移植…

小说php 站点源码下载,PTCMS小说站源码

必装环境&#xff1a;nginx(apache.iis 也可)&#xff0c;mysql,php5.6,memcached php5.6 安装扩展 memcache 新建站点&#xff0c;注意新建时&#xff0c;PHP 版本必须选择 PHP5.6&#xff0c;不然程序会报错 1.上传网站文件到网站目录&#xff0c;新建网站伪静态选择 thinkph…

PHP是什么

PHP 是服务器端脚本语言。 您应当具备的基础知识 在继续学习之前&#xff0c;您需要对以下知识有基本的了解&#xff1a; HTMLCSS 如果您希望首先学习这些项目&#xff0c;请在我们的 首页 访问这些教程。 PHP 是什么&#xff1f; PHP 代表 PHP: Hypertext PreprocessorPHP 是一…

国家地表水水质自动监测站坐标每四小时数据(共1952个监测站,含省份、城市、河流、流域、断面名称、监测时间、水温、pH、DO、CODMn、TP、TN、NH3-N、浊度等)

1.监测范围 国家地表水水质自动监测网1952 个水质自动监测站。2.监测项目 监测项目为国家水质自动监测站配备的监测指标&#xff0c;主要包括五参数(水温、pH、溶解氧、电导率和浊度)、氨氮、高锰酸盐指数、总氮、总磷&#xff0c;部分水站增测总有机碳、叶绿素a、藻密度、VOCs…

php 跨站脚本,Piwigo register.php页面多个跨站脚本漏洞

发布日期&#xff1a;2010-05-06 更新日期&#xff1a;2010-05-11 受影响系统&#xff1a; Piwigo project Piwigo 2.0.9 描述&#xff1a; -------------------------------------------------------------------------------- BUGTRAQ ID: 39958 CVE(CAN) ID: CVE-2010-1707…

基于Modbus RTU 485通信协议实现对PH、溶解氧传感器的数据采集

modbus rtu 485协议采用的是一主多从方式通信&#xff0c;主机是普中的stm32f103zet6开发板&#xff0c;从机是传感器。代码已经在实物上测试通过&#xff0c;并且也用modbus精灵测试通过了。如果你没有stm32基础的话&#xff0c;建议先去B站搜索“正点原子”了解一下485串口通…

3、基于51单片机的智能水箱控制系统-温度-PH值-水位(仿真+程序+原理图)

目录 基于51单片机的智能水箱控制系统1、主要功能2、实验结果3、仿真工程4、原理图5、程序源码6、资源获取 基于51单片机的智能水箱控制系统 1、主要功能 51单片机检测水箱内温度&#xff0c;ph值&#xff1b;使用pid算法控制温度到设置值&#xff1b;普通控制ph值到设定值&a…

如何下载y站视频

今天看到了一篇B站视频的下载方法&#xff0c;学习了下&#xff0c;然后去看了下y站是不是也能下下来&#xff0c;居然被我试出来了&#xff0c;嘿嘿 B站文章链接&#xff1a;https://blog.csdn.net/Enderman_xiaohei/article/details/94718494 然后看一下y站的&#xff0c;打…

基于STM32(HAL库)的水质检测(浑浊度、PH值、温度、手机APP显示、wifi上云)

本系统由通过wifi将浑浊度、PH值、温度采集的数据发送到手机APP&#xff0c;超过设定的阈值报警。 一、硬件材料清单&#xff1a; 1、STM32C8T6&#xff1a;控制器 2、OLED显示屏&#xff1a;显示传感器采集的数据 3、PH传感器&#xff1a;检测PH值 4、TDS传感器&#xff1…

JEB动态调试与篡改攻防世界Ph0en1x-100

文章目录 题目APK静态分析jadx反编译IDA反汇编 JEB动态调试工具的使用操作内存值 AndroidKiller工具的使用篡改软件包 总结 题目 攻防世界 Mobile 新手区题目链接 Ph0en1x-100&#xff0c;如下&#xff1a; 下载附件得到一个 apk&#xff0c;安装后如下&#xff1a; 要求输入…

php 抓站,如何跨站抓取别的站点的页面的补充

如何跨站抓取别的站点的页面的补充 更新时间&#xff1a;2006年10月09日 00:00:00 作者&#xff1a; 在实际的应用中&#xff0c;经常会遇到一些特殊的情况&#xff0c;比如需要新闻&#xff0c;天气预报&#xff0c;等等&#xff0c;但是作为个人站点或者实力小的站点 我们不…

用python计算ph,用python下载PH上的学习视频

努力学习&#xff0c;天天向上 闲来无事&#xff0c;用python写个脚本下载PH上的学习视频 环境&#xff1a;python3 用法&#xff1a;python ph_downloader.py viewkey viewkey是PH上的一串字符 代码在 https://paste.ubuntu.com/p/jXVYD3NGP9 多线程下载&#xff0c;网络不好时…

浮标水质监测站--河流湖泊水库现场水质自动监测的解决方案

浮标水质监测站--河流湖泊水库现场水质自动监测的解决方案 什么是浮标水质监测站&#xff1f; 浮标水质监测站是设立在河流、湖泊、水库、近岸海域等流域内进行现场水质自动监测的监测仪器&#xff0c;是以水质监测仪为核心&#xff0c;运用传感器技术&#xff0c;结合浮标体…

html全屏背景视频特效,HTML5全屏背景视频特效插件Vidage.js源码

下面我们对HTML5全屏背景视频特效插件Vidage.js源码文件阐述相关使用资料和HTML5全屏背景视频特效插件Vidage.js源码文件的更新信息。 HTML5全屏背景视频特效插件Vidage.js源码 本特效是一款移动友好的基于HTML5插件Vidage.js实现全屏背景视频特效的代码。Vidage.js可以在手机或…