AR 眼镜之-系统应用音效-实现方案

目录

📂 前言

AR 眼镜系统版本

系统应用音效

1. 🔱 技术方案

1.1 技术方案概述

1.2 实现方案

1)初始化

2)播放音效

3)释放资源

2. 💠 播放音效

2.1 静音不播放

2.2 获取音效默认音量

3. ⚛️ 单声道空间音效

3.1 实现方案

3.2 封装播放单声道空间音效

4. ✅ 小结

附录1:StreamType 值

附录2:音效播放工具源码


📂 前言

AR 眼镜系统版本

        W517 Android9。

系统应用音效

        系统应用音效主要包括:通知音效、蓝牙电话音效、点击音效等。

1. 🔱 技术方案

1.1 技术方案概述

        系统应用音效都是比较短的,一般采用 Android 推荐的 ogg 格式,直接使用 SoundPool 播放即可。

1.2 实现方案

1)初始化

        在适当位置初始化 SoundPool 工具类(比如:在 Activity 的 onCreate 方法中初始化),包括:初始化相应 SoundPool、load 相应音效资源、保存音效资源 ID。

        1、初始化相应 SoundPool

private val phoneSP by lazy { SoundPool(20, AudioManager.STREAM_VOICE_CALL, 0) }
private val notifySP by lazy { SoundPool(20, AudioManager.STREAM_NOTIFICATION, 0) }
private val clickSP by lazy { SoundPool(20, AudioManager.STREAM_SYSTEM, 0) }

        SoundPool 构造第一个参数为 maxStreams,一般设置为1或10,本文设置为20是有一定风险的(主要是为了规避接通蓝牙电话时,点击蓝牙电话接听的系统音效被蓝牙电话音效通道抢占,导致点击蓝牙电话接听的系统音效无法播放的问题)。

        SoundPool 构造第二个参数是 streamType 值,具体值的含义可查看附录1。比如:本文的通知音效使用 STREAM_NOTIFICATION,蓝牙电话使用的 STREAM_VOICE_CALL,点击音效使用的 STREAM_SYSTEM。

        2、load 相应音效资源,保存音效资源 ID

/*** 加载音效ID*/
private var hangupId: Int = -1
private var answerId: Int = -1
private var notifyComeId: Int = -1
private var notifyClearId: Int = -1
private var clickId: Int = -1fun init(context: Context) {hangupId = phoneSP.load(context, R.raw.phone_hang_up, 3)answerId = phoneSP.load(context, R.raw.phone_answer, 3)notifyComeId = notifySP.load(context, R.raw.notification_message, 1)notifyClearId = notifySP.load(context, R.raw.notification_clear, 1)clickId = clickSP.load(context, R.raw.click, 1)Log.i(TAG, "init: hangupId = $hangupId,answerId = $answerId,notifyComeId = $notifyComeId,notifyClearId = $notifyClearId,clickId = $clickId")
}

        load 方法的第三个参数为声音的优先级,源码注释为:目前不起作用,默认使用1即可。

2)播放音效

        在触发音效播放处调用播放音效,包括播放音效通用方法、播放音效特定封装方法。

        1、播放音效通用方法

/*** 播放音效*/
fun play(context: Context,soundPool: SoundPool,soundId: Int,resId: Int,leftVol: Float = -1f,rightVol: Float = -1f
) {...// soundId:加载的音频资源的 ID。// leftVolume和rightVolume:左右声道的音量,范围为 0.0(静音)到 1.0(最大音量)。// priority:播放优先级,一般设为 1。// loop:是否循环播放,0 表示不循环,-1 表示无限循环。// rate:播放速率,1.0 表示正常速率,更大的值表示更快的播放速率,0.5 表示慢速播放。soundPool.play(soundId, leftVol, rightVol, 1, 0, 1.0f)...
}

        2、播放音效特定封装方法

fun playHangup(context: Context) {Log.i(TAG, "playHangup: resId = ${R.raw.phone_hang_up}")play(context, phoneSP, hangupId, R.raw.phone_hang_up)
}
fun playAnswer(context: Context) {Log.i(TAG, "playAnswer: resId = ${R.raw.phone_answer}")play(context, phoneSP, answerId, R.raw.phone_answer)
}
fun playNotifyCome(context: Context) {Log.i(TAG, "playNotifyCome: resId = ${R.raw.notification_message}")play(context, notifySP, notifyComeId, R.raw.notification_message)
}
fun playNotifyClear(context: Context) {Log.i(TAG, "playNotifyClear: resId = ${R.raw.notification_clear}")play(context, notifySP, notifyClearId, R.raw.notification_clear)
}
3)释放资源

        在适当位置释放 SoundPool 对象(比如:在 Activity 的 onDestroy 方法中释放资源)

/*** 释放资源*/
fun release() {phoneSP.release()notifySP.release()
}

2. 💠 播放音效

2.1 静音不播放

        判断当前系统对应音效是否静音,如果静音则不播放音效。

/*** 播放音效*/
fun play(context: Context,soundPool: SoundPool,soundId: Int,resId: Int,leftVol: Float = -1f,rightVol: Float = -1f
) {// 1.静音不播放if (isSilent(context)) {Log.i(TAG, "_play: AudioManager RINGER_MODE_SILENT!")return}...
}/*** @return 是否静音*/
private fun isSilent(context: Context) =(context.getSystemService(Context.AUDIO_SERVICE) as AudioManager).ringerMode == AudioManager.RINGER_MODE_SILENT

2.2 获取音效默认音量

        设定音效播放前,先获取到系统音效的默认音量大小,在播放时使用系统音效的默认音量值。

/*** 播放音效*/
fun play(context: Context,soundPool: SoundPool,soundId: Int,resId: Int,leftVol: Float = -1f,rightVol: Float = -1f
) {...// 2.获取音效默认音量val volFloat = getVol(context)...
}/*** @return 音效默认音量*/
private fun getVol(context: Context) = 10.0.pow((context.resources.getInteger(com.android.internal.R.integer.config_soundEffectVolumeDb).toFloat() / 20).toDouble()
).toFloat()

3. ⚛️ 单声道空间音效

3.1 实现方案

  1. 屏蔽原生 View 点击音效:android:soundEffectsEnabled="false"

  2. 使用 SoundPool 单独设置左右声道音量,假如只播放右声道,则把左声道音量置为0。

3.2 封装播放单声道空间音效

fun playClickLeft(context: Context) {Log.i(TAG, "playClickLeft: resId = ${R.raw.click}")play(context, phoneSP, hangupId, R.raw.click, rightVol = 0f)
}
fun playClickRight(context: Context) {Log.i(TAG, "playClickRight: resId = ${R.raw.click}")play(context, phoneSP, hangupId, R.raw.click, 0f)
}/*** 播放音效*/
fun play(context: Context,soundPool: SoundPool,soundId: Int,resId: Int,leftVol: Float = -1f,rightVol: Float = -1f
) {...// 2.获取音效默认音量val volFloat = getVol(context)val tempLeftVol = if (leftVol != -1f) leftVol else volFloatval tempRightVol = if (rightVol != -1f) rightVol else volFloatsoundPool.play(soundId, tempLeftVol, tempRightVol, 1, 0, 1.0f)...
}

4. ✅ 小结

        对于系统应用音效,本文只是一个基础实现方案,更多业务细节请参考产品逻辑去实现。

        另外,由于本人能力有限,如有错误,敬请批评指正,谢谢。


附录1:StreamType 值

/** Used to identify the default audio stream volume */
public static final int STREAM_DEFAULT = -1;
/** Used to identify the volume of audio streams for phone calls */
public static final int STREAM_VOICE_CALL = 0;
/** Used to identify the volume of audio streams for system sounds */
public static final int STREAM_SYSTEM = 1;
/** Used to identify the volume of audio streams for the phone ring and message alerts */
public static final int STREAM_RING = 2;
/** Used to identify the volume of audio streams for music playback */
public static final int STREAM_MUSIC = 3;
/** Used to identify the volume of audio streams for alarms */
public static final int STREAM_ALARM = 4;
/** Used to identify the volume of audio streams for notifications */
public static final int STREAM_NOTIFICATION = 5;
/** Used to identify the volume of audio streams for phone calls when connected on bluetooth */
public static final int STREAM_BLUETOOTH_SCO = 6;
/** Used to identify the volume of audio streams for enforced system sounds in certain* countries (e.g camera in Japan) */
public static final int STREAM_SYSTEM_ENFORCED = 7;
/** Used to identify the volume of audio streams for DTMF tones */
public static final int STREAM_DTMF = 8;
/** Used to identify the volume of audio streams exclusively transmitted through the*  speaker (TTS) of the device */
public static final int STREAM_TTS = 9;
/** Used to identify the volume of audio streams for accessibility prompts */
public static final int STREAM_ACCESSIBILITY = 10;

附录2:音效播放工具源码

/*** Description:    音效播放工具* CreateDate:     2024/6/26 15:47* Author:         agg*/
object SoundPoolTools {private val TAG = SoundPoolTools::class.java.simpleNameprivate val phoneSP by lazy { SoundPool(20, AudioManager.STREAM_VOICE_CALL, 0) }private val notifySP by lazy { SoundPool(20, AudioManager.STREAM_NOTIFICATION, 0) }private val clickSP by lazy { SoundPool(20, AudioManager.STREAM_SYSTEM, 0) }/*** 加载音效ID*/private var hangupId: Int = -1private var answerId: Int = -1private var notifyComeId: Int = -1private var notifyClearId: Int = -1private var clickId: Int = -1fun init(context: Context) {hangupId = phoneSP.load(context, R.raw.phone_hang_up, 3)answerId = phoneSP.load(context, R.raw.phone_answer, 3)notifyComeId = notifySP.load(context, R.raw.notification_message, 1)notifyClearId = notifySP.load(context, R.raw.notification_clear, 1)clickId = clickSP.load(context, R.raw.click, 1)Log.i(TAG, "init: hangupId = $hangupId,answerId = $answerId,notifyComeId = $notifyComeId,notifyClearId = $notifyClearId,clickId = $clickId")}fun playClickLeft(context: Context) {Log.i(TAG, "playClickLeft: resId = ${R.raw.click}")play(context, phoneSP, hangupId, R.raw.click, rightVol = 0f)}fun playClickRight(context: Context) {Log.i(TAG, "playClickRight: resId = ${R.raw.click}")play(context, phoneSP, hangupId, R.raw.click, 0f)}fun playHangup(context: Context) {Log.i(TAG, "playHangup: resId = ${R.raw.phone_hang_up}")play(context, phoneSP, hangupId, R.raw.phone_hang_up)}fun playAnswer(context: Context) {Log.i(TAG, "playAnswer: resId = ${R.raw.phone_answer}")play(context, phoneSP, answerId, R.raw.phone_answer)}fun playNotifyCome(context: Context) {Log.i(TAG, "playNotifyCome: resId = ${R.raw.notification_message}")play(context, notifySP, notifyComeId, R.raw.notification_message)}fun playNotifyClear(context: Context) {Log.i(TAG, "playNotifyClear: resId = ${R.raw.notification_clear}")play(context, notifySP, notifyClearId, R.raw.notification_clear)}/*** 播放音效*/fun play(context: Context,soundPool: SoundPool,soundId: Int,resId: Int,leftVol: Float = -1f,rightVol: Float = -1f) {// 1.静音不播放if (isSilent(context)) {Log.i(TAG, "_play: AudioManager RINGER_MODE_SILENT!")return}// 2.获取音效默认音量val volFloat = getVol(context)val tempLeftVol = if (leftVol != -1f) leftVol else volFloatval tempRightVol = if (rightVol != -1f) rightVol else volFloat// 3.播放音效// soundId:加载的音频资源的 ID。// leftVolume和rightVolume:左右声道的音量,范围为 0.0(静音)到 1.0(最大音量)。// priority:播放优先级,一般设为 1。// loop:是否循环播放,0 表示不循环,-1 表示无限循环。// rate:播放速率,1.0 表示正常速率,更大的值表示更快的播放速率,0.5 表示慢速播放。if (soundId != -1) {Log.i(TAG, "_play: play [direct],soundId = $soundId,resId = $resId")soundPool.play(soundId, tempLeftVol, tempRightVol, 1, 0, 1.0f)} else {Log.i(TAG, "_play: play [load],soundId = $soundId,resId = $resId")soundPool.load(context, resId, 1)soundPool.setOnLoadCompleteListener { _, _, _ ->Log.i(TAG, "_play: play [load -> play],soundId = $soundId,resId = $resId")soundPool.play(soundId, tempLeftVol, tempRightVol, 1, 0, 1.0f)}}}/*** 释放资源*/fun release() {phoneSP.release()notifySP.release()}/*** @return 是否静音*/private fun isSilent(context: Context) =(context.getSystemService(Context.AUDIO_SERVICE) as AudioManager).ringerMode == AudioManager.RINGER_MODE_SILENT/*** @return 音效默认音量*/private fun getVol(context: Context) = 10.0.pow((context.resources.getInteger(com.android.internal.R.integer.config_soundEffectVolumeDb).toFloat() / 20).toDouble()).toFloat()}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/406299.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2.初识springcloud

文章目录 1.什么是SpringCloud1.1版本的介绍 2.Spring Cloud实现方案3.环境搭建4.服务拆分原则5.数据准备5.1订单服务5.2商品服务 大家好,我是晓星航。今天为大家带来的是 初识springcloud 相关的讲解!😀 1.什么是SpringCloud 简单来说&…

【算法基础实验】图论-最小生成树-Prim的即时实现

理论知识 Prim算法是一种用于计算加权无向图的最小生成树(MST, Minimum Spanning Tree)的贪心算法。最小生成树是一个连通的无向图的子图,它包含所有的顶点且总权重最小。Prim算法从一个起始顶点开始,不断将权重最小的边加入生成…

Excel表格添加趋势线_数据拟合

一个曲线通过补偿算法拟合为另一个曲线,通常可以通过多种数学和计算技术实现。这里也可以通过Excel表格添加趋势线,然后对趋势线进行拟合,得到趋势预测公式来达到数据补偿。 通过把你需要的数据导入到Excel表格中。 通过 “ 插入 ” --> “…

谷歌云AI新作:CROME,跨模态适配器高效多模态大语言模型

CROME: Cross-Modal Adapters for Efficient Multimodal LLM https://arxiv.org/pdf/2408.06610 Abstract 研究对象:Multimodal Large Language Models (MLLMs) demonstrate remarkable imagelanguage capabilities, but their widespread use faces challenges in…

论坛 推荐

畅议论坛:http://udbbs.top/http://udbbs.top/

查看U盘的具体信息,分区表格式、实际容量和分区状态

查看U盘的具体信息,分区表格式、实际容量和分区状态 前言: 利用windows自带的命令行窗口就可以 1、使用命令提示符查看MBR和GPT分区类型 (1)按“Windows R”键,在弹出的运行对话框中输入“diskpart”,并按…

游戏开发设计模式之工厂模式

目录 简单工厂模式(Simple Factory Pattern) 应用场景: 优缺点: 工厂方法模式(Factory Method Pattern) 应用场景: 优缺点: 抽象工厂模式(Abstract Factory Patte…

碰撞检测 | 基于ROS Rviz插件的多边形碰撞检测仿真平台

目录 0 专栏介绍1 基于多边形的碰撞检测2 碰撞检测仿真平台搭建2.1 多边形实例2.2 外部服务接口2.3 Rviz插件化 3 案例演示3.1 功能介绍3.2 绘制多边形 0 专栏介绍 🔥课设、毕设、创新竞赛必备!🔥本专栏涉及更高阶的运动规划算法轨迹优化实战…

Debian12安装tomcat8

jdk安装 安装Tomcat前需要先安装JDK,JDK安装参见: https://zhengshaoshaolin.blog.csdn.net/article/details/141407600 tomcat安装 1、下载安装 Apache Tomcat 访问官方 Apache Tomcat 下载页面,获取最新的二进制文件 或者使用如下的 wg…

Spring DI 数据类型—— set 方法注入

首先新建项目&#xff0c;可参考 初识IDEA、模拟三层--控制层、业务层和数据访问层 一、spring 环境搭建 &#xff08;一&#xff09;pom.xml 导相关坐标 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.or…

【Win开发环境搭建】Redis与可视化工具详细安装与配置过程

&#x1f3af;导读&#xff1a;本文档提供了Redis的简介、安装指南、配置教程及常见操作方法。包括了安装包的选择与配置环境变量的过程&#xff0c;详细说明了如何通过修改配置文件来设置密码和端口等内容。同时&#xff0c;文档还介绍了如何使用命令行工具连接Redis&#xff…

ArcGIS如何将投影坐标系转回为地理坐标系

有时候两个数据&#xff0c;一个为投影坐标系&#xff0c;另一个为地理坐标系时&#xff0c;在GIS软件中位置无法叠加到一起&#xff0c;这需要将两个或多个数据的坐标系统一&#xff0c;可以直接将地理坐标系的数据进行投影&#xff0c;或将投影坐标系转为地理坐标系。下面介绍…

在使用Simulink进行FOC(Field-Oriented Control,场向量控制)仿真时,如果遇到波形丢失精度的问题,该这么解决

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

【Qt】输入类控件QLineEdit

目录 输入类控件QLineEdit 例子&#xff1a;录入个人信息 例子&#xff1a;使用正则表达式验证输入框的数据 例子&#xff1a;验证俩次输入密码一致 例子&#xff1a;切换显示代码 输入类控件QLineEdit QLineEdit 用来表示单行输入框&#xff0c;可以输入一段文本&#xf…

网络安全入门教程(非常详细)从零基础入门到精通_网路安全 教程

前言 1.入行网络安全这是一条坚持的道路&#xff0c;三分钟的热情可以放弃往下看了。2.多练多想&#xff0c;不要离开了教程什么都不会了&#xff0c;最好看完教程自己独立完成技术方面的开发。3.有时多百度&#xff0c;我们往往都遇不到好心的大神&#xff0c;谁会无聊天天给…

QT-五子棋游戏

QT-五子棋游戏 一、演示效果二、核心代码三、下载链接 一、演示效果 二、核心代码 #include "GameModel.h" #include <time.h> #include <stdlib.h>GameModel::GameModel(){}void GameModel::startGame(GameType type){gameType type;//初始化棋盤game…

如何备份电脑数据到U盘?防止数据丢失从备份开始

在数字化时代&#xff0c;数据备份已经成为我们日常生活中不可或缺的一部分。电脑中的数据&#xff0c;无论是工作文件、学习资料&#xff0c;还是珍贵的照片和视频&#xff0c;都是我们生活中重要的资产。为了防止数据丢失&#xff0c;将数据备份到U盘是一个简单且实用的方法。…

【IEEE独立出版】第三届人工智能、物联网和云计算技术国际会议(AIoTC 2024,9月13-15)

第三届人工智能、物联网与云计算技术国际会议(AIoTC 2024)将于2024年9月13日-15日在中国武汉举行。 本次会议由华中师范大学伍伦贡联合研究院与南京大学联合主办、江苏省大数据区块链与智能信息专委会承办、江苏省概率统计学会、江苏省应用统计学会、Sir Forum、南京理工大学、…

《重温JavaScript五子棋小游戏》

目录 全部运行代码&#xff1a;五子棋游戏的基本步骤&#xff1a;代码剖析&#xff1a;1. 初始化游戏界面2. 管理游戏状态3. 玩家交互4. 电脑AI5. 胜负判定6. 游戏控制 本文通过实现一个基本的五子棋游戏&#xff0c;展示了如何使用HTML、CSS和JavaScript来构建一个简单的交互式…

GIS应用水平考试一级真题和答案分享~

2012年-2018年完整真题和答案 GIS应用水平考试资料分享https://docs.qq.com/doc/DRmxxaVhpbGJXSGho?u5295a88d71d8480d971da4e3334ee913