使用场景
一般在车载应用设置中,有一个 按键提示音 的功能开关,此开关的作用类似手机上的按键音,当用户点击某个按钮时,会发出 BB 声。但由于车载系统的特殊性,当用户从多媒体切换到收音机时,arm 端(多媒体,按键音)的声音是发不出来的。具体原因根据硬件的原理图来解释:

DSP 音频芯片主要用来处理声音的合成与混音,通常为四选一或者六选一,就是四路通道经过 DSP 处理只有一路的声音能够输出。通常多媒体,蓝牙电话,导航报点,按键音都是属于 arm 通道,而收音机则属于 Radio 通道,两者经过 DSP 的处理只能有一路的声音到达功放发出声音。进一步解释就是,当用户点击收音机图标进入收音机应用时,arm 告诉 MCU 此时用户切到了 Radio 通道,此时 MCU 会控制 DSP 只输出 Radio 的声音。如果此时用户再点击多媒体,而 arm 却没有通知 MCU 切换到 arm 通道,此时多媒体的声音仍然是听不到的,听到的仍然是收音机的声音,因为此时 DSP 只输出 Radio 通道的声音。同理,当用户在收音机界面时,DSP 只输出 Radio 的声音。此时用户点击按钮,按钮发出的声音由于属于 arm 端,是不会被 DSP 处理的,也就是用户听不到 BB 声,此时给用户的体验是不好的。所以如果遵从 Android 原生设计,声音只由 arm 发出,而在某些应用按键音无效,这就是一个很大的漏洞。但车载硬件在设计之初就考虑到了这个问题,beep 声由 MCU 发出,通过原理图可以看出,MCU 能通过发声电路发出一个 PWM 的脉冲信号到功放继而发出声音,所以,无论我们在那路通道,只要将按键事件告诉 MCU 即可。所以,我们想要实现 Beep 声,只需要将点击事件告诉 MCU 即可。而 Android 原生就有一套发声机制,为何不用?

Android 按键音原理
在 Android 系统中,所有的控件,只要监听了点击事件,当点击事件发生时,此时会有 beep 声。基于此,声音的发出应该是在控件的基类 View 中,先从 View 的 onTouchEvent 看起:

{// Use a Runnable and post this rather than calling// performClick directly. This lets other visual state// of the view update before click actions start.if (mPerformClick == null) {mPerformClick = new PerformClick();}if (!post(mPerformClick)) {performClick();}}  

performClick 大家都比较熟悉,主要用来处理点击事件,代码如下:

 public boolean performClick() {sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);ListenerInfo li = mListenerInfo;if (li != null && li.mOnClickListener != null) {playSoundEffect(SoundEffectConstants.CLICK);li.mOnClickListener.onClick(this);return true;}return false;}

playSoundEffect 从命名上猜测为按键音的发音函数,代码如下:

/*** Play a sound effect for this view.** <p>The framework will play sound effects for some built in actions, such as* clicking, but you may wish to play these effects in your widget,* for instance, for internal navigation.** <p>The sound effect will only be played if sound effects are enabled by the user, and* {@link #isSoundEffectsEnabled()} is true.** @param soundConstant One of the constants defined in {@link SoundEffectConstants}*/public void playSoundEffect(int soundConstant) {if (mAttachInfo == null || mAttachInfo.mRootCallbacks == null || !isSoundEffectsEnabled()) {return;}mAttachInfo.mRootCallbacks.playSoundEffect(soundConstant);}

mAttachInfo 又是什么鬼?从类的定义可知,里面有个 Callbacks 的接口,里面有个 playSoundEffect 的方法,所以实现了此接口的类应该就能发出 Beep 声,从注释可知其真正的方法写在 parent window 中, parent window 就是ViewRoot ,实现该回调接口:

 /*** A set of information given to a view when it is attached to its parent* window.*/static class AttachInfo {interface Callbacks {void playSoundEffect(int effectId);boolean performHapticFeedback(int effectId, boolean always);}

接着看 ViewRoot 的 playSoundEffect 函数。

    public void playSoundEffect(int effectId) {checkThread();if (mMediaDisabled) {return;}try {final AudioManager audioManager = getAudioManager();switch (effectId) {case SoundEffectConstants.CLICK:audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);return;case SoundEffectConstants.NAVIGATION_DOWN:audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN);return;case SoundEffectConstants.NAVIGATION_LEFT:audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT);return;case SoundEffectConstants.NAVIGATION_RIGHT:audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT);return;case SoundEffectConstants.NAVIGATION_UP:audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP);return;default:throw new IllegalArgumentException("unknown effect id " + effectId +" not defined in " + SoundEffectConstants.class.getCanonicalName());}} catch (IllegalStateException e) {// Exception thrown by getAudioManager() when mView is nullLog.e(TAG, "FATAL EXCEPTION when attempting to play sound effect: " + e);e.printStackTrace();}}

在函数中主要通过 audioManager 调用 playSoundEffect ,实际是通过 AIDL 调用 AudioService 的 onPlaySoundEffect 方法。 `至此,Androd 原生的 beep 声原理弄清楚了。而我们需要做的是将 playSoundEffect 里的函数屏蔽掉,加上我们自己的逻辑,即通知 MCU 发声。

移植流程
在知道了 Android 原生的 View 按键音的最终实现是在 AudioService 的onPlaySoundEffect 方法中,那么我们可以在函数体里面屏蔽原功能,增加我们自己的处理逻辑。
第一步:参考 \frameworks\base\media\java\android\media 里面的 AIDL 的代码,创建文件

IPlaySoundEffectListener.aidl
package android.media;oneway interface IPlaySoundEffectListener{ void onPlaySoundEffect();
}

第二步:在接口 IAudioService 增加 setOnPlaySoundEffectListener(in IPlaySoundEffectListener l), 在 AudioService 中实现该方法 。

 private  IPlaySoundEffectListener mPlaySoundEffectListener;@Overridepublic void setOnPlaySoundEffectListener(IPlaySoundEffectListener l){mPlaySoundEffectListener = l;}private void onPlaySoundEffect(int effectType, int volume) {synchronized (mSoundEffectsLock) {try {if(mPlaySoundEffectListener!=null){mPlaySoundEffectListener.onPlaySoundEffect();}}catch (Exception ex) {Log.w(TAG, "onPlaySoundEffect: "+ex);} }

应用
在应用层,通过 getSystemService(Context.AUDIO_SERVICE) 获取 AudioService 来注册回调监听,当发生点击事件时,会回调到我们接口函数,继而处理我们自己的逻辑通知 MCU 发声。

车载设置--按键提示音相关推荐

  1. 苹果手机at系统_iOS14系统苹果手机如何设置充满电提示音?

    最近很多小伙伴问我,iOS14系统苹果手机能设置充电提示音,那么是不是能够设置充满电提示音呢?关于这个问题,我给到的答案是肯定的.接下来,小芳特地整理了图文教程和大家分享一下,希望能够帮助到大家. 需 ...

  2. 摩托罗拉v8对讲机驱动软件_摩托罗拉数字机如何设置“个性”提示音

    诺基亚手机的开机铃声是很多70后 80后的回忆,给心爱的"摩机"P8668i配上一段开机铃声"Hello MOTO",既俏皮又炫酷.如果设置个性提示音,今天就给 ...

  3. 安卓设置原生alert设置圆角_安卓手机设置充电提示音全新最全教程

    安卓版充电提示音教程_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili​m.bilibili.com 哎,自从前端时间IOS14发布后,他的其他所有功能没有活,唯一火了充电提示音.奈何大傻没有苹果手 ...

  4. iPad/iPhone如何设置充电提示音吖?

    其实iPad设置充电器提示音和iPhone是一样的原理,都是通过快捷指令播放一段声音,其实不是真正的替换了原来的提示音,只是在充电时播放了一段设置好的声音而已!由于优先级的问题,系统自带的提示音总是先 ...

  5. 安卓手机充电慢_安卓手机怎么设置充电提示音

    哎,自从前段时间IOS14发布后,他的其他所有功能没有活,唯一火了充电提示音.奈何大傻没有苹果手机,拉磨奢侈的东东,不舍得买,只能使用安卓手机喽.自那以后安卓各位大大也坐不住了,很多手机也增加了这个功 ...

  6. 安卓手机怎么设置充电提示音

    哎,自从前端时间IOS14发布后,他的其他所有功能没有活,唯一火了充电提示音.奈何大傻没有苹果手机,拉磨奢侈的东东,不舍得买,只能使用安卓手机喽.自那以后安卓各位大大也坐不住了,很多手机也增加了这个功 ...

  7. 有人问安卓手机怎么设置充电提示音?详细教程来了

    哎,自从前段时间IOS14发布后,他的其他所有功能没有活,唯一火了充电提示音.奈何大傻没有苹果手机,拉磨奢侈的东东,不舍得买,只能使用安卓手机喽.自那以后安卓各位大大也坐不住了,很多手机也增加了这个功 ...

  8. 安卓手机充电慢_安卓手机设置充电提示音全新最全教程

    安卓版充电提示音教程_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili​m.bilibili.com 哎,自从前端时间IOS14发布后,他的其他所有功能没有活,唯一火了充电提示音.奈何大傻没有苹果手 ...

  9. 让键盘发出老婆的声音,键盘按键提示音工具

    经常在电脑前工作的朋友,也许早就厌烦了键盘枯燥的敲击声 如何让敲键盘变得更社死有趣些呢,今天给大家分享一款很有意思的软件 它可以给键盘中的每个按键设置不同的声音 同时软件内置了多种音效包,可以让我们在 ...

最新文章

  1. 简单介绍python编程之文件读写
  2. 宝塔面板遇到No space left on device错误的解决方法
  3. 英特尔核芯显卡控制面板没有了_【有趣】第41期:被英特尔取消发售的CPU长啥样?...
  4. android数据回传多个页面_Android Day06四大组件之Activity多页面跳转和数据传递
  5. MySQL的几个character_set变量的说明
  6. php的limit分页,用php数组的array_slice分页和用limit查询分页哪个效率更高?
  7. php 随机码类,php随机类型验证码
  8. C++并发与多线程(三)单例设计模式与共享数据分析、call_once、condition_variable使用
  9. 在matlab中,简单地利用RS485协议对伺服电机进行控制
  10. linux ip被占用顶掉,记一次 Linux服务器被***后的排查思路
  11. 对两个等长升序的序列查找中位数
  12. vue项目运行npm install报错
  13. 【对数几率回归】matlab实现
  14. 《高等统计物理学》5:非平衡态统计物理初步
  15. Postgresql12+Pgpool-ii 4.1高可用集群
  16. 魁拔妖侠传 之 浮云骑士语录
  17. 这些年MAC下我常用的那些快捷键
  18. 谷安kali密码破解小结(crunch字典生成)
  19. AddressSanitizer使用介绍
  20. mediapipe KNN 基于mediapipe和KNN的引体向上计数/深蹲计数/俯卧撑计数【mediapipe】【KNN】【BlazePose】【K邻近算法】【python】

热门文章

  1. must appear in the GROUP BY clause or be used in an aggregate function
  2. 《写字练习》词库制作说明-让练习更有效
  3. 苹果也吹牛,M2处理器性能确实优秀,但是却与Intel存在很大差距
  4. 为什么说CPU频率远高于内存频率(CPU3Ghz、内存2666Mhz)
  5. 一些GitHub的镜像及资源
  6. POJ 3047 蔡勒公式
  7. 华为AI认证_图像处理实验(图像预处理)
  8. Redis lua教程
  9. WebVR开发教程——深度剖析
  10. Eclipse集成Hadoop插件详解(一)