当前位置: 首页 > news >正文

简单OpenSL ES学习

初识OpenSL ES

  • OpenSL ES
    • Objects和Interfaces
  • 所有的Object在OpenSl里面我们拿到的都是一个SLObjectItf:
    • SLObjectItf_
    • 创建引擎
      • 创建过程要设计得这么麻烦?(object的生命周期)
      • 这么多参数,参数类型这么多学习障碍太大?
  • 创建混音器

OpenSL ES

简单来说OpenSL ES是一个嵌入式跨平台免费的音频处理库。 所以它不是Android特有的。它从PC端的整出来一个小一些的第三方库专门来给移动端使用,跨平台、无授权费,针对嵌入式系统精心优化的硬件音频加速 API。

最后一句话就是:我们商业应用用的就是它,所以要学习了解它。

据说人家是C语音,兼容C++,然后以面对对象的思想设计的。

可以播放PCM的一个库。

Objects和Interfaces

我们要想使用一个对象,必须创建这个对象,然后通过这个对象拿到它提供的接口,最后再通过接口提供的函数去执行。

设计的所有最顶层Object是音频引擎,之后其它所有的对象都需要音频引擎这个对象传入。

它的Objects是有生命周期概念的。
Objects ⼀般有三种状态,分别是:UNREALIZED (不可⽤),REALIZED(可⽤),SUSPENDED
(挂起)。

所有的Object在OpenSl里面我们拿到的都是一个SLObjectItf:

SLObjectItf_

//调用全局方法创建一个引擎对象(OpenSL ES唯一入口)

 SLresult result;result = slCreateEngine(&engineObject, 0, 0, 0, 0, 0);

我们从官方文档里面看到了,其它都是传入对象,只有第一个engineObject是丢进去赋值的,这也是C语音赋值正常流程。
这个engineObject的类型就是SLObjectItf,我们看看这个engineObject是什么东西。

SL_API SLresult SLAPIENTRY slCreateEngine(SLObjectItf             *pEngine,SLuint32                numOptions,const SLEngineOption    *pEngineOptions,SLuint32                numInterfaces,const SLInterfaceID     *pInterfaceIds,const SLboolean         * pInterfaceRequired
) SL_API_DEPRECATED(30);

这是函数定义,具体实现在cpp文件那里,我们看下这个SLObjectItf的定义:

typedef const struct SLObjectItf_ * const * SLObjectItf;

在C语言中,typedef关键字可以用来为已存在的类型定义一个新的名称。这里,typedef const struct SLObjectItf_ * const * SLObjectItf;是定义了一个新的类型名SLObjectItf,这个新类型是const struct SLObjectItf_ *类型的别名。

这个语句可以分为两部分来解释:

1、const struct SLObjectItf_ *:这是一个指向const struct SLObjectItf_类型的指针。struct SLObjectItf_是一个结构体类型,但是这里并没有给出这个结构体的具体定义,所以无法知道它包含哪些字段和数据。const关键字表示这个指针自身是一个常量,不能被修改,但指针所指向的内容是可以被修改的。

2、* SLObjectItf:这是一个指向上面定义的const struct SLObjectItf_ *类型的指针。也就是说,SLObjectItf是一个指向指向const struct SLObjectItf_类型的指针的指针。这样的数据结构通常被用于实现动态链接库(DLL)或者共享库,因为这样的设计可以让使用者在使用这些库的时候不直接操作原始的接口,而是通过这个指针的指针来操作。

简单来说:SLObjectItf是SLObjectItf_ 类型的别名,当调用调用全局方法创建一个引擎对象(唯一入口)的时候就会根据传入的参数类型来给这个SLObjectItf赋值。

我们简单看下它的函数定义:

struct SLObjectItf_ {SLresult (*Realize) (SLObjectItf self,SLboolean async);SLresult (*Resume) (SLObjectItf self,SLboolean async);SLresult (*GetState) (SLObjectItf self,SLuint32 * pState);SLresult (*GetInterface) (SLObjectItf self,const SLInterfaceID iid,void * pInterface);SLresult (*RegisterCallback) (SLObjectItf self,slObjectCallback callback,void * pContext);void (*AbortAsyncOperation) (SLObjectItf self);void (*Destroy) (SLObjectItf self);SLresult (*SetPriority) (SLObjectItf self,SLint32 priority,SLboolean preemptable);SLresult (*GetPriority) (SLObjectItf self,SLint32 *pPriority,SLboolean *pPreemptable);SLresult (*SetLossOfControlInterfaces) (SLObjectItf self,SLint16 numInterfaces,SLInterfaceID * pInterfaceIDs,SLboolean enabled);
};

上面说的Objects和Interfaces的关系在这里就是:SLObjectItf_ 是对象,它定义了很多的函数,实例化这个对象之后我们就可以
使用对象里面的函数,里面定义了接口(就是一个对象):Interfaces

如果是java这种面对对象语音,真正的接口的话必须实例化才行,这里显然就是名义上的接口,既然人家说是按照面对对象的思维来设计,我们就按照这种思维来理解,但是不能把它代入到具体的语音规则中。

创建引擎

这是在播放PCM数据的demo中的代码:

/***********  1 创建引擎 获取SLEngineItf***************/SLresult result;result = slCreateEngine(&engineObject, 0, 0, 0, 0, 0);if (result != SL_RESULT_SUCCESS)return;result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);if (result != SL_RESULT_SUCCESS)return;result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);if (result != SL_RESULT_SUCCESS)return;if (engineEngine) {LOGD("get SLEngineItf success");} else {LOGE("get SLEngineItf failed");}/***********         1 创建引擎       ***************/

这是在录音demo中的代码:

   SLEngineOption pEngineOptions[] = {(SLuint32) SL_ENGINEOPTION_THREADSAFE,(SLuint32) SL_BOOLEAN_TRUE};// 创建引擎对象,//调用全局方法创建一个引擎对象(OpenSL ES唯一入口)SLresult result;result = slCreateEngine(&engineObject, //对象地址,用于传出对象1, // 可选配置数组的大小pEngineOptions, // 选配置数组的参数 录音时候一般会这么配置,主要是为了兼容其它平台,避免出现不兼容情况。0,  //支持的接口数量nullptr, //具体的要支持的接口,是枚举的数组nullptr//具体的要支持的接口是开放的还是关闭的,也是一个数组,这三个参数长度是一致的);assert(SL_RESULT_SUCCESS == result);/* Realizing the SL Engine in synchronous mode. *///实例化这个对象result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);assert(SL_RESULT_SUCCESS == result);// get the engine interface, which is needed in order to create other objects//从这个对象里面获取引擎接口(*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);assert(SL_RESULT_SUCCESS == result);

创建过程要设计得这么麻烦?(object的生命周期)

OpenSL ES 的 Object 一般有三种状态,分别是: UNREALIZED (不可用),REALIZED (可用),SUSPENDED(挂起) 。
obiect 外干UNREALIZED(不用)状态时,系统不会为其分配资源: 调用 Realize 方法后便进入 REALIZED(可用)状态,此时对象的各个功能和资源可以正常访问;当系统音频相关的硬件设备被其他进程占用时,OpenSL ES Obiect 便会进入 SUSPENDED (挂起) 状态,随后调用 Resume 方法可使对象重回 REALIZED (可用)状态;当 0bject 使用结束后,调用 Destroy 方法释放资源,是对象重回 UNREALIZED (不可用)状态

因为设计到硬件,所以它的使用有一个过程,不像是存代码创建赋值直接在内存中生成某个对象。

这么多参数,参数类型这么多学习障碍太大?

android端在NDK中使用OpenSL ES的创建过程非常麻烦,但实际上在开发过程这些代码是很固定的,基本没人会手打,都是复制张贴,所以无需担心那么多。
比如创建混音器,参数选项那么多,实际上还真不太可能全研究透,而是什么需求配置什么参数,看具体场景需求之后再去查找,其它方面的大体都是固定式代码,所以有一定的了解即可。

创建混音器

 /***********  2 创建混音器 ***************/const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};// 环境回响const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);if (result != SL_RESULT_SUCCESS) {LOGE("CreateOutputMix failed");return;} else {LOGD("CreateOutputMix success");}//实例化混音器result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);if (result != SL_RESULT_SUCCESS) {LOGE("mixer init failed");} else {LOGD("mixer init success");}result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB,&outputMixEnvironmentalReverb);if (SL_RESULT_SUCCESS == result) {// 走廊效果SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR;result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(outputMixEnvironmentalReverb, &reverbSettings);(void) result;}/***********  2 创建混音器 ***************/

可以看到在这里object是我们创建引擎拿到的interface对象,通过它实例化我们的outputMixObject对象
object和interface的说法其实我们根本不需要去管,一层嵌着一层,能大体知道它的设计思路即可,免得被它的对象和接口搞得自己混乱了。

其它更具体的代码这里就不再细讲了,网上的各种代码例子非常多,这里其实是为了加深我自己学习过程中的疑惑问题。

相关文章:

简单OpenSL ES学习

初识OpenSL ES OpenSL ESObjects和Interfaces 所有的Object在OpenSl里面我们拿到的都是一个SLObjectItf:SLObjectItf_创建引擎创建过程要设计得这么麻烦?(object的生命周期)这么多参数,参数类型这么多学习障碍太大&…...

Linux网络编程- struct packet_mreq setsockopt()

struct packet_mreq struct packet_mreq 是一个数据结构,用于 Linux 中的原始数据包套接字,当我们想改变套接字的行为以接收特定类型的数据包时,它与 setsockopt() 函数配合使用。 下面是 struct packet_mreq 的定义: struct p…...

C++学习day4

作业&#xff1a; 1> 思维导图 2> 整理代码 1. 拷贝赋值函数课上代码 //拷贝赋值函数课上代码 #include<iostream> using namespace std;//创建类 class Stu { private://私有的string name;int socer;int *age;//此处注意用到指针类型 public://共有的//无参构…...

从零学算法54

54.给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 螺旋遍历&#xff1a;从左上角开始&#xff0c;按照 向右、向下、向左、向上 的顺序 依次 提取元素&#xff0c;然后再进入内部一层重复相同的步骤&#xff0c;直到…...

Logback日志框架使用详解以及如何Springboot快速集成

Logback简介 日志系统是用于记录程序的运行过程中产生的运行信息、异常信息等&#xff0c;一般有8个级别&#xff0c;从低到高为All < Trace < Debug < Info < Warn < Error < Fatal < OFF off 最高等级&#xff0c;用于关闭所有日志记录fatal 指出每个…...

Nginx概念

Nginx概念 Nginx 是一款面向性能设计的 HTTP 服务器&#xff0c;相较于 Apache、lighttpd 具有占有内存少&#xff0c;稳定性高等优势&#xff0c;同时也是一个非常高效的反向代理、负载平衡服务器 nginx使用的是反应器模式&#xff0c;主事件循环等待操作系统发出准备事件的信…...

vim基础指令(自用)

这个是自己随便写的&#xff0c;类似于笔记 vim 多模式编辑器 查看指令&#xff1a; gg&#xff1a; 定位光标到最开始行 shift(按)g 定位到最结尾行 nshift(按)g 定位到任意行 shift&#xff04; 定位到本行结尾 0 定位到本行开头 w&#xff1a;跨单词移动 h.j.k,l: 左下上右 …...

【centos7安装ElasticSearch】

概述 最近工作中有用到ES &#xff0c;当然少不了自己装一个服务器捣鼓。本文的ElasticSearch 的版本&#xff1a; 7.17.3 一、下载 ElasticSearch 点此下载 下载完成后上传至 Linux 服务器&#xff0c;本文演示放在&#xff1a; /root/ 下&#xff0c;进行解压&#xff1…...

ElementPlus Switch 开关基础使用

昨天开发用到开关组件 后台返回字段是 can_write 默认是0 or 1 但是Switch 组件绑定的默认值默认是 true or false 直接绑定会导致默认是关闭状态 在页面一加载 值发生变化时 会自己调用 查了文档 需要使用 active-value 和 inactive-value 来指定绑定的数据类型 …...

Spring Boot:自定义注解--annotation

目录 自定义注解的定义和作用范围如何创建自定义注解创建注解接口 如何使用自定义注解进行数据验证创建注解处理器控制器中使用注解 如何为字段添加注解 自定义注解的定义和作用范围 自定义注解可以作用在类、方法、属性、参数、异常、字段或其他注解上。 如何创建自定义注解…...

WIFI频段

问题&#xff1a; 5ghzwifi频段带宽怎么选 智造喵回答&#xff1a; 选择5GHz WiFi频段的带宽取决于你的网络需求和设备兼容性。通常有两种可选的带宽&#xff1a;20MHz和40MHz。 20MHz带宽&#xff1a;这是较为常见的 WiFi 频段带宽设置。它提供较好的穿墙能力和信号稳定性…...

Java的引用详解与示例

引用的作用 在Java中&#xff0c;引用&#xff08;Reference&#xff09;是一种重要的概念&#xff0c;它们用于管理对象的生命周期、内存分配和垃圾回收。引用的作用包括以下几个方面&#xff1a; 内存管理&#xff1a;引用帮助Java虚拟机&#xff08;JVM&#xff09;管理内存…...

c++视觉处理---霍夫变换

霍夫直线变换的函数 HoughLines 是OpenCV库中用于执行霍夫直线变换的函数。霍夫直线变换用于检测图像中的直线。下面是该函数的基本用法&#xff1a; cv::HoughLines(image, lines, rho, theta, threshold);image: 输入的二值图像&#xff0c;通常是通过边缘检测算法生成的。…...

el-table 边框颜色修改 简单有效!

废话不多说&#xff0c;直接上图 &#xff08;1&#xff09;修改前的图如下&#xff1a; 以上是elementUI原组件自带的样式 &#xff08;2&#xff09;下面是修改后的边框图如下&#xff1a; 源码如下&#xff1a; <el-table :data"jctableData" border size…...

Zabbix第二部分:基于Proxy分布式部署实现Web监控和Zabbix HA集群的搭建

代理和高可用 一、基于zabbix-proxy的分布式监控1.1 分布式监控的作用1.2 数据流向1.3 构成组件 二、部署zabbix代理服务器Step1 前置准备Step2 设置 zabbix 的下载源&#xff0c;安装 zabbix-proxyStep3 部署数据库并将zabbix相关文件导入Step4 修改zabbix-proxy的配置文件&am…...

JumpServer rce深入剖析

影响范围 JumpServer < v2.6.2 JumpServer < v2.5.4 JumpServer < v2.4.5 JumpServer v1.5.9 修复链接及参考 修改了一处代码&#xff1a; Git History 增加了一处鉴权 def connect(self):user self.scope["user"]if user.is_authenticated and …...

EasyExcel导入/导出Excel文件

EasyExcel导入/导出Excel文件简单写法 1、导入依赖 2、创建简单导入、导出demo 3、创建类 继承AnalysisEventListener&#xff08;导入Excel监听解析表格数据&#xff09; 4、创建类 基于注解 自定义Excel导出模版 1、导入EasyExcel依赖 <!--导入EasyExcel…...

力扣(LeetCode)2512. 奖励最顶尖的K名学生(C++)

优先队列哈希集合反向思维(或自定义排序) 模拟&#xff0c;请直接看算法思路&#xff1a; 两个哈希集合S1和S2, S1存正面词汇&#xff0c;S2存负面词汇&#xff1b;一个优先队列pq&#xff0c;pq存{score, id}键值对&#xff0c;即学生分数-学生id。 算法流程&#xff1a; 初…...

CubeMX+BabyOS 使用方法

MCU&#xff1a;STM32G030F 编译器&#xff1a;MDK 托管工具&#xff1a;Sourcetree CubeMX创建工程 BabyOS克隆 添加子模块 git submodule add https://gitee.com/notrynohigh/BabyOS.git BabyOS 切换dev 分支 查看当前分支 git branch -a 切换本地分支到dev git che…...

OpenResty安装-(基于Nginx的高性能Web平台,可在Nginx端编码业务)

文章目录 安装OpenResty1.安装1&#xff09;安装开发库2&#xff09;安装OpenResty仓库3&#xff09;安装OpenResty4&#xff09;安装opm工具5&#xff09;目录结构6&#xff09;配置nginx的环境变量 2.启动和运行3.备注 安装OpenResty 1.安装 首先你的Linux虚拟机必须联网 …...

算法-DFS+记忆化/动态规划-不同路径 II

算法-DFS记忆化/动态规划-不同路径 II 1 题目概述 1.1 题目出处 https://leetcode.cn/problems/unique-paths-ii 1.2 题目描述 2 DFS记忆化 2.1 思路 注意题意&#xff0c;每次要么往右&#xff0c;要么往下走&#xff0c;也就是说不能走回头路。但是仍有可能走到之前已经…...

黑盒测试方法:原理+实战

目录 一、如何设计测试用例 二、黑盒测试常用方法 1、基于需求进行测试用例的设计 2、等价类 3、边界值 4、判定表分析法&#xff08;因果分析法&#xff09; 5、正交表 6、场景设计法 三、案例补充 1、使用Fiddler模拟弱网 2、针对一个接口该如何测试 一、如何设计测试…...

SQLite事务处理

语法 BEGIN TRANSACTION; COMMIT TRANSACTION; &#xff08;或END TRANSACTION;&#xff09; ROLLBACK TRANSACTION; 事务处理 除了一些PRAGMA语句以外&#xff0c;其它访问数据库的语句会自动启动事务处理&#xff0c;并且在结束时自动提交。 通过上一节的命令可以手动控制…...

Java中CountDownLatch使用场景

在Java的并发API中&#xff0c;CountDownLatch是一个同步器&#xff0c;它允许一个或多个线程等待一组操作完成。 如果您正在开发一个服务器应用程序&#xff0c;该应用程序在开始处理请求之前需要初始化各种资源。这些资源可能是这样的&#xff1a; 加载配置文件建立数据库连…...

漏刻有时数据可视化Echarts组件开发(41)svg格式地图应用

1.定义SVG文件 var svg ;2.注册地图函数 Echarts.registerMap是Echarts图表库中用于注册地图的函数。它可以将第三方地图或自定义地图数据与Echarts进行集成&#xff0c;使用Echarts的API进行绘制。使用方法如下&#xff1a; echarts.registerMap(mapName, geoJson) 参数map…...

firefox的主题文件位置在哪?记录以防遗忘

这篇文章写点轻松的 最近找到了一个自己喜欢的firefox主题,很想把主题的背景图片找到,所以找了下主题文件所在位置 我的firefox版本:版本: 118.0.1 (64 位)主题名称: Sora Kawai 我的位置在 C:\Users\mizuhokaga\AppData\Roaming\Mozilla\Firefox\Profiles\w0e4e24v.default…...

Vuex获取、修改参数值及异步数据处理

14天阅读挑战赛 学不可以已... 目录 一、Vuex简介 1.1 vuex介绍 1.2 vuex核心 二、Vuex使用 2.1 Vuex安装 2.2 创建store模块 2.3 创建vuex的store实例并注册上面引入的各大模块 三、使用Vuex获取、修改值案例 3.1 创建两个菜单组件 3.2 配置路由 3.3 模拟菜单数据 …...

【 OpenGauss源码学习 —— 列存储(autoanalyze)(二)】

列存储&#xff08;autoanalyze&#xff09;&#xff08;二&#xff09; 概述PgStat_StatTabEntry 结构体pgstat_count_heap_insert 与 pgstat_count_cu_insert 函数CStoreInsert::BatchInsertCommon 函数pgstat_count_cu_update 函数pgstat_count_cu_delete 函数pgstat_count_…...

使用postman 调用 Webservice 接口

1. 先在浏览器地址栏 访问你的webService地址 地址格式: http://127.0.0.1:8092/xxxx/ws(这个自己的决定)/xxxxXccv?wsdl 2. post man POST 访问wwebService接口 地址格式: http://127.0.0.1:8092/xxxx/ws(这个自己的决定)/xxxxXccv <soapenv:Envelope xmlns:soapenv…...

程序员Google插件推荐

文章目录 AdBlock (广告拦截插件)SuperCopy 超级复制Octotree (github增强工具)GitZip for github (github增强工具)JSON-handleSimpleExtManager(管理谷歌插件)OneTab (标签页合并)PostWoman(接口调试)篡改猴 (Tampermonkey)FeHelper(前端助手) AdBlock (广告拦截插件) ☆ 拦截…...