Harmony Native开发-我的OpenSL ES录音机

article/2025/7/4 2:21:04

零、写在前面

最早我是在Android上开发的OpenSL ES。但最近看了下鸿蒙的文档,发现它的底层库也支持OpenSL ES,这我的兴致就来了。简单了解了一下鸿蒙的Native开发,就着手开发起来。移植过程中发现其实对Android程序员还是相当友好的,从Android上移植到鸿蒙并没有太大的改动。这篇文章主要讲的是通以OpenSL ES开发为切入点,鸿蒙怎么进行基本的Native开发。Demo是实现一个录音机,完成录音和播放录音功能。

一、创建我的鸿蒙Native APP

鸿蒙的开发工具和AndroidStudio及其类似,毕竟来自同一家公司的开源,所以使用上也大致差不多,入门成本不高。创建一个native app和AndroidStuio的也类似,在创建项目模板的界面选择Native C++,完成一系列的的项目类型、名称、路径等等配置后就创建了我们的第一个Native C++
在这里插入图片描述
如果你是第一次创建Native项目可能会要你下载开发工具包,那么请你去设置里检查你的这三个选项是否都勾选上了并安装了,主要是第三个选项Native
在这里插入图片描述
项目结构和Android类似,在main路径下比普通的项目多出一个cpp,里面有一个CMakeList和一个返回helloworld的CPP文件,这里就涉及到JNI的内容,如果对JNI不熟悉的可以先去看看我的上一篇博文JNI常用开发技巧,在这我就不再讲CMakeList怎么编写和JNI的流程规范了。

二、开始OpenSL开发

2.1、引入底层库

我在这里主要引入一个OpenSL ES库和一个鸿蒙的日志库,日志库默认已经引入了,我们新建一个C++类OpenSLESRecord,功能全在里面实现,通过它默认创建的那个hello.CPP进行调用.
CMakeList申明OpenSLESRecord库并将它OpenSL ES链接起来,不然找不到OpenSL ES库。

add_library( # Sets the name of the library.OpenSLESRecord# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).OpenSLESRecord.cpp)target_link_libraries( # Specifies the target library.OpenSLESRecord# Links the target library to the log library# included in the NDK.OpenSLESlibhilog_ndk.z.so)

2.2、音频录制背景

讲几个音频的简单概念

  • PCM
    中文叫脉冲编码调制,说人话就是音频模拟量转成数字量,把麦克风采集到的电信号转化为byte数据。我们的录音文件就是xxx.pcm。但是有一点一般的播放器是无法播放PCM的,因为PCM是纯音频数据,没有音频格式信息,无法进行解码,所以只能我们自己的写一个,或者用Adobe Audition播放,但是也要指定音频格式才能播放。
  • 采样格式
    采样格式常见的有8bit,16bit,24bit,32bit。就是数字可以表达的音频信息的范围,比如我们的音频模拟量最高电压为5V,采样格式为8位,那我们最多把模拟信号分成255等分,那每等分中间的电压就是误差。所以采样格式位数越大,误差越小精度越高。
  • 声道
    常见的就是我们平常理解的单声道和双声道还有什么3.1、5.1
  • 声道布局
    就是声道输出的方向,常见的有左耳,右耳,左右耳
  • 采样率
    采样率是设备每秒采集音频信号的速度,常见采样率有8000,16000,44100,96000等,如采样率8000就是每秒采样音频信号8000次
  • 比特率
    是指单位时间内传送的比特(bit)数,单位是bit/s,计算公式是采样格式* 声道数 * 采样率
    那么从上面可以得出,一个PCM录音文件的大小就是
    (采样格式* 声道数 * 采样率 * 时间 / 8)bytes
    录音和播放过程中我们都需要设置这些东西,下面是我录音的PCM格式,SLDataFormat_PCM 结构体就是用于保存这些信息。
    SLDataFormat_PCM slDataFormatPcm = {SL_DATAFORMAT_PCM,                             //输出PCM格式的数据(SLuint32) 2,                                  //输出的声道数量SL_SAMPLINGRATE_44_1,                          //输出的采样频率,这里是44100HzSL_PCMSAMPLEFORMAT_FIXED_16,                   //输出的采样格式,这里是16bitSL_PCMSAMPLEFORMAT_FIXED_16,                   //一般来说,跟随上一个参数SL_SPEAKER_FRONT_LEFT |SL_SPEAKER_FRONT_RIGHT,  //双声道配置,如果单声道可以用 SL_SPEAKER_FRONT_CENTERSL_BYTEORDER_LITTLEENDIAN                      //PCM数据的大小端排列
    };
    

2.2、录音开发流程

在这里插入图片描述
其实把上面的流程再放大了看,其实可以把创建对象->实例化对象->获取接口看做一个流程。下面是前三步的代码

SLresult sLresult= slCreateEngine(&engineObject,0,NULL,0,NULL,0);LOGE("slCreateEngine...");if (SL_RESULT_SUCCESS != sLresult){return;}sLresult=(*engineObject)->Realize(engineObject, 0);LOGE("slCreateEngine Realize...");if (SL_RESULT_SUCCESS != sLresult){return;}sLresult=(*engineObject)->GetInterface(engineObject,SL_IID_ENGINE,&audioEngine);LOGE("slCreateEngine GetInterface...");if (SL_RESULT_SUCCESS != sLresult){return;}

如果说我们Android开发从C语言角度难以理解,那就从Java角度看一看,(当然只是为了让Java开发的有个大致的概念,实际并非完全如此此)。我们把C的结构体Java的对象的概念做一个替换。其实这就像是一个建造者模式代码,创建对象像是把需要构建的参数传进去,实例化对象像是调用build,我们到这一步才算是拿到了对象。而这个对象的属性里又有其它子模块对象,分别有各自的功能,我们这里是根据ID拿到这个对象的。我们前三步的目的就是为了拿到引擎接口,因为它有实际的方法。看看它的方法结构体构成,以本文用到的三个方法为例,来自OpenSL ES的头文件:

......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);SLresult (*CreateOutputMix) (SLEngineItf self,SLObjectItf * pMix,SLuint32 numInterfaces,const SLInterfaceID * pInterfaceIds,const SLboolean * pInterfaceRequired);
......

理解了前三步,那接下来的三步也是一样的了,最终目的是为了拿到我们的录制接口,调用它的方法,把缓存队列设置好,再给缓存队列设置一个回调,每次队列满了就把数据回调回来,数据处理就在这个回调方法里,我们这里把它写入文件。最好调用录制方法就万事大吉了。

2.3、播放开发流程

在这里插入图片描述
播放流程相对录制流程就多了一个创建混响器,其实也是因为我们的播放器需要这个东西。创建混响器的流程也一样理解就是啦。

2.4 小总结

这里讲的只是一个大致流程,这些大的流程中会需要传入一些参数,这些参数怎么设置基本上也就是一个固定的参数,比如输入设备Id、输出设备Id、PCM格式等等。所以这里不做详解,如果有兴趣可以去github上看我的demo,demo路径会放在文末。

三、上层应用开发

3.1、权限申请

  • 录音权限(麦克风权限)
  • 读写文件权限
    其中录音和写文件权限需要动态申请
    在config.json的里面申明需要的权限
"reqPermissions": [{"name": "ohos.permission.MICROPHONE"},{"name": "ohos.permission.WRITE_USER_STORAGE"},{"name": "ohos.permission.READ_USER_STORAGE"}]

Java层动态请求权限

private void requestPermission() {String[] permissions = {SystemPermission.WRITE_USER_STORAGE, SystemPermission.MICROPHONE};requestPermissionsFromUser(Arrays.stream(permissions).filter(permission -> verifySelfPermission(permission) != IBundleManager.PERMISSION_GRANTED).toArray(String[]::new), 0);}

3.2、界面编写

三个按钮分别 录音 暂停 和播放

<DirectionalLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_parent"ohos:width="match_parent"ohos:orientation="vertical"><Buttonohos:background_element="#0099cc"ohos:text_color="#ffffff"ohos:id="$+id:start_record_button"ohos:height="match_content"ohos:width="match_parent"ohos:start_padding="14vp"ohos:end_padding="0vp"ohos:padding="10vp"ohos:text="开始录音"ohos:text_size="16vp"ohos:top_margin="30vp"/><Buttonohos:background_element="#0099cc"ohos:text_color="#ffffff"ohos:id="$+id:stop_record_button"ohos:height="match_content"ohos:width="match_parent"ohos:start_padding="14vp"ohos:end_padding="0vp"ohos:padding="10vp"ohos:text="停止录音"ohos:text_size="16vp"ohos:top_margin="10vp"/><Buttonohos:background_element="#0099cc"ohos:text_color="#ffffff"ohos:id="$+id:play_record_button"ohos:height="match_content"ohos:width="match_parent"ohos:start_padding="14vp"ohos:end_padding="0vp"ohos:padding="10vp"ohos:text="播放录音"ohos:text_size="16vp"ohos:top_margin="10vp"/>
</DirectionalLayout>

Java层的就不继续凑字数了,看不懂的可以先看看鸿蒙的官方文档。

四、总结

如果你会Android端的OpenSL ES开发,那么鸿蒙端的其实并不难。所以你也可以先从Android或iOS入手OpenSL ES,因为他们的相关文档更多。Native端主要稍微不同的点就是Android端有它自己的BufferQueue,而鸿蒙端是没有的,因为鸿蒙端没有SLES/OpenSLES_Android.h这个头文件,但是问题不大,用自带的BufferQueue-SLBufferQueueItf就行。而上层的话虽说和Android端有不同,但是流程上都是相同的,都有对应的实现方法。主要是在真机上运行那个签名麻烦一点,还要去开发者平台注册应用那些。鸿蒙的IDE正式版2.0的Native开发还有bug,莫名奇妙的爆红和警告,跑起来没任何问题,我换了beta版3.0后就没这个问题的。下面是我写的录音Demo,有兴趣的可以clone下来玩一玩,在它的基础上再发展一下,比如把PCM文件转为wav格式的文件,这样我们的手机就能直接播放了。如果觉得有帮到你也可以点个小星星。欢迎大家讨论和批评指正。
鸿蒙环境下的OpenSL ES开发 GitHub地址


http://chatgpt.dhexx.cn/article/OLR4MBnu.shtml

相关文章

Android中opensl架构,Android OpenSL ES详解

简介 NDK开发OpenSL ES跨平台高效音频解决方案.png OpenSL ES全称为Open Sound Library for Embedded Systems,即嵌入式音频加速标准。OpenSL ES是无授权费、跨平台、针对嵌入式系统精心优化的硬件音频加速 API。它为嵌入式移动多媒体设备上的本地 应用程序开发者提供了标准化…

音视频学习 AudioTrack、OpenSL ES 音频渲染

前言 在讲解音频渲染之前&#xff0c;需要对音频的基础知识有所了解&#xff0c;所以该篇分为基础概念和AudioTrack 以及 OpenSL ES Demo 实例讲解&#xff0c;这样有助于更好的理解 Android 中音频渲染。 音频的基础概念涉及的知识点比较多&#xff0c;该篇文章的上半部分会…

OpenSL ES技术分析

背景简介 OpenSL ES是一种针对嵌入式系统特别优化过的硬件音频加速API&#xff0c;无授权费并且可以跨平台使用。它提供的高性能、标准化、低延迟的特性实现为嵌入式媒体开发提供了标准&#xff0c;嵌入式开发者在开发本地音频应用也将变得更为简便&#xff0c;利用该API能够实…

使用MediaCodec+OpenSL编写简单的音频播放器

使用MediaCodecOpenSL编写简单的音频播放器 前言 通过MediaCodec Native API 和OpenSL编写一个简单的音频播放器。可以解码并播放一个mp3文件. 流程 初始化 使用AMediaExtractor解析Mp3文件&#xff0c;它可以得到音频文件的格式、以及帧&#xff08;未解码&#xff09;。…

使用Android高性能音频--OpenSL ES和AAudio

AAudio的概念介绍: AAudio 是作为 OpenSL ES 库的轻量级原生 Android 替代项而开发。 与 OpenSL ES 相比&#xff0c;AAudio API 不仅较小&#xff0c;而且容易使用。 AAudio 是在 Android O 版本中引入的全新 Android C API。 因此 API 是专为需要低延迟的高性能音频应用而设…

opensl学习笔记

这个是看opensl文档看到的一个简单的使用示例图&#xff0c;单纯看代码的话很难理解opensl播放和采集的原理&#xff0c;可以结合图来理解。使用Audio Player来播放音频&#xff0c;DataSource类型为SLDataSource&#xff0c;例如安卓设备就是SL_DATALOCATOR_ANDROIDSIMPLEBUFF…

使用OpenSL直接播放mp3

使用OpenSL直接播放mp3 前言 通过使用OpenSL来播放一个mp3文件来学习openSL的使用方式。 设计 在android平台播放mp3方式有多种方式入使用MediaPlayer、AudioTrack、OpenSL、oboe等。根据使用MediaPlayer,AudioTrack的经验一个播放器需要有的基础功能有加载数据、开始、暂停…

音视频开发之旅(36) -FFmpeg +OpenSL ES实现音频解码和播放

目录 OpenSL ES基本介绍OpenSL ES播放音频流程代码实现遇到的问题资料收获 上一篇我们通过AudioTrack实现了FFmpeg解码后的PCM音频数据的播放&#xff0c;在Android上还有一种播放音频的方式即OpenSL ES, 什么是OpenSL ES&#xff0c;这个我们平时接触的很少&#xff0c;原因…

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

一、Android OpenSL ES 介绍 OpenSL ES (Open Sound Library for Embedded Systems)是无授权费、跨平台、针对嵌入式系统精心优化的硬件音频加速API。它为嵌入式移动多媒体设备上的本地应用程序开发者提供标准化, 高性能,低响应时间的音频功能实现方法&#xff0c;并实现软/硬件…

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…