我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android TTS播报音频并且配合AudioManager压低其他音频声音

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android TTS播报音频并且配合AudioManager压低其他音频声音

文章目录

使用场景

1. 拦截三方导航返回的即将播放的文本信息,并且加以处理然后播报语音。2. 音频焦点管理,协调与其他音频的冲突,如后台播放音乐时导航播报3. 提示固定提示音,如超速等滴滴声音4. 控制导航语音音量大小

通过 TTS 播放文本信息

  • 什么是 TTS

TTS 就是 TextToSpeech Google 提供的将文字转换为自然语言流的技术,就是通过接收一段文本,转换为声音。具体看百度百科

我这使用场景是在第三方返回语音信息时拦截,然后自己经过处理后播报出去。具体实现的核心简化版代码如下

创建 TTS

const val GOOGLE_TTS_ENGINE = "com.google.android.tts"        private fun createTts() {    // contextRef 的弱引用 防止内存泄漏        contextRef.get()?.let { context ->            tts = if (!isFollowSystem) {            // 我们这里项目做的不是国内的项目所以如果不跟随系统选择的默认的Google引擎                TextToSpeech(                        context,                        { status -> this@TtsManager.onInit(status) },                        GOOGLE_TTS_ENGINE)            } else {            // 手机默认引擎,这个和手机厂商定制应该有关系                 TextToSpeech(context) { p0 -> this@TtsManager.onInit(p0) }            }            // 设置语言播报速度            tts?.setSpeechRate(1f)            // setOnUtteranceProgressListener 设置的是语音播报的开始、结束、异常 等监听事件            processListenerRef?.get()?.let {                tts?.setOnUtteranceProgressListener(it)            }        }    }

TextToSpeech 第一个参数大家都知道,第二个是初始化成功还是失败的回调,第三个是可以指定引擎,一般手机厂商很多会自己内置自己的引擎,默认的话大多应该就是 Google 引擎了。this 里面第四个参数是 packagename 如果本地有其他的引擎可以指定包名,默认null。this 里面第五个参数是如果指定的引擎失败会走默认的引擎初始化。

 public TextToSpeech(Context context, OnInitListener listener, String engine) {        this(context, listener, engine, null, true);    }

我用的手机不是国行的手机,所以默认是 Google 的 tts 引擎。

下载 TTS

由于目标用户特性,我这里如果没有 Google 引擎,会提示下载 Google tts 引擎

  • 先判断是否有 Google 引擎方法
fun isInstallGoogleTts():Boolean{        tts?.engines?.forEach{            if(it.name ==  "com.google.android.tts") {                return true            }        }        return false    }
  • 下载 TTS
private const val GOOGLE_TTS_URI = "market://details?id=com.google.android.tts"val intent = Intent(Intent.ACTION_VIEW)            intent.data = Uri.parse(GOOGLE_TTS_URI)            applicationContext.packageManager?.let {                if(intent.resolveActivity(it) != null) {                    startActivity(intent)                }            }

TTS 有 Voice 语音包

  • 根据需要筛选
            tts?.voices?.forEach {                // 筛选美式英语                if (it.locale.language == English                    && (it.locale.country.toUpperCase(Locale.ROOT) == US|| it.locale.country.toUpperCase(Locale.ROOT) == UNITED_STATES)) {                    enVoices.add(it)                    //刷选美式西班牙语                } else if (it.locale.language == Spanish                    && (it.locale.country.toUpperCase(Locale.ROOT) == US|| it.locale.country.toUpperCase(Locale.ROOT) == UNITED_STATES)) {                    spVoices.add(it)                }            }

TTS 播放语音

fun speak(text: String, queueModel: Int) {        Log.d(TAG, "Voice message: $text")        utteranceId = TAG + messageId++        // QUEUE_FLUSH interrupts already speaking messages.        val error: Int = tts?.speak(text, queueModel, null, utteranceId) ?: -1        if (error != 0) {            Log.e(TAG, "Error when speaking using Android's TextToSpeech: $error")        }}
  • speak 参数的含义
    public int speak(final CharSequence text,                     final int queueMode,                     final Bundle params,                     final String utteranceId) {        return runAction((ITextToSpeechService service) -> {        }, ERROR, "speak");    }
@param text :要讲的文本字符串,不超过 4000 个字符@param queueMode:要使用的队列策略,主要有两种- public static final int QUEUE_ADD = 1; 队列模式,在播放队列的末尾添加新条目。- public static final int QUEUE_FLUSH = 0; 主要意思就是前面的删除留下新的@param params:请求参数可以为null。就是内置的一些可配置的引擎参数。通过 bundle传入。@param utteranceId :此请求的唯一标识符。@ return :返回值是代表错误吗 0 成功 , -1 失败

音频焦点管理

音频焦点管理就是解决播报时是否有其他音频正在播报,比如导航中,后台放着音乐,前面右转播报时降低音乐音量保证用户可以听清楚导航声音。

通过 android,media 下的 AudioFocusRequest 的音频焦点管理类和 AudioManager 获取音频焦点。核心简化代码如下

  • 上面介绍了通过 TTS 播放导航信息播报,则播报之前要获取到音频焦点,结束之后要释放焦点,否则后台音乐的音量不会变回去。

  • 首先获取 AudioFocusRequest

    private var audioRequest: AudioFocusRequest? = null    private fun createFocusRequest(){        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {            audioRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK)                .setAudioAttributes(audioAttributes)                .setOnAudioFocusChangeListener {                    Log.d(TAG, "init Audio Focus: $it")                }                .setAcceptsDelayedFocusGain(false)                .build()        }}

通过 AudioFocusRequest 的 Builder 来构建对象,audioAttributes 设置一些属性如下:

        private val audioAttributes: AudioAttributes by lazy {        audioAttr?:AudioAttributes.Builder()            .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)            .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)            .build()    }
  • setUsage : 设置用途,因为我这里时导航,所以选择的 USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 导航用途
    • USAGE_MEDIA:表示音频的用途是多媒体。
      USAGE_VOICE_COMMUNICATION:表示音频的用途是语音通信。
      USAGE_NOTIFICATION:表示音频的用途是通知。
      USAGE_ALARM:表示音频的用途是闹钟。
      USAGE_ASSISTANCE_ACCESSIBILITY:表示音频的用途是辅助功能和无障碍功能。
  • setContentType: 设置描述音频信号(如语音或音乐)的内容类型的属性。以下是其中一些类型
    • CONTENT_TYPE_MUSIC:表示音频的内容类型是音乐。
      CONTENT_TYPE_SPEECH:表示音频的内容类型是语音。
      CONTENT_TYPE_MOVIE:表示音频的内容类型是电影。
      CONTENT_TYPE_SONIFICATION:表示音频的内容类型是提示音或系统声音。
  • setFlags(int flags):设置音频的标志。
  • AudioFocusRequest 的 .setAcceptsDelayedFocusGain(false) 属性

setAcceptsDelayedFocusGain() 是 AudioAttributes.Builder 类中的一个属性,用于指示音频源是否支持延迟获取焦点(delayed focus gain)。
在 Android 中,当多个应用程序同时请求使用音频焦点时,系统需要决定哪个应用程序具有焦点,以便控制音频的播放和暂停。默认情况下,如果一个应用程序请求获取音频焦点,系统会立即将焦点转移到该应用程序,但在某些情况下,这可能会引起不必要的中断和干扰。例如,如果用户正在使用语音助手(例如 Google Assistant)进行语音识别,那么在识别期间可能不希望其他应用程序突然播放声音并中断语音识别。为了避免这种情况,系统支持延迟获取焦点,即在请求获取焦点后,系统会等待一段时间以确定焦点是否真的需要转移,如果不需要,则会保留焦点。
如果一个应用程序支持延迟获取焦点,那么当它请求获取焦点时,系统会通知它是否已经获得了焦点,并告诉它何时将焦点转移到该应用程序。这使得应用程序可以在等待焦点时继续播放音频,而不必担心在焦点转移时被中断。
因此,setAcceptsDelayedFocusGain() 属性的意义是指示音频源是否支持延迟获取焦点。如果一个音频源支持延迟获取焦点,那么应该将该属性设置为 true,否则应该将其设置为 false。如果不确定是否需要支持延迟获取焦点,则可以使用默认值 false。

我这里因为要求实时性延迟的话会很危险,所以设置成了 false

  • 下一步获取 AudioManager
    private val audioManager: AudioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager

请求音频焦点

  • 我这里时在 TTS 开始播报之谦时候获取焦点,播放结束或者中断的时候移除,TTS回调如下:
private val utteranceProgressListener = object : UtteranceProgressListener() {        override fun onStart(utteranceId: String?) {        // 获取焦点            audioFocusManager.requestAudioFocus()            // 根据配置选择是否增加音量            increaseVolume()        }        override fun onDone(utteranceId: String?) {        // 释放焦点            audioFocusManager.abandonAudioFocus()            // 还原音量            recoverVolume()        }        @Deprecated("Deprecated in Java")        override fun onError(utteranceId: String?) {        }        override fun onError(utteranceId: String?, errorCode: Int) {            super.onError(utteranceId, errorCode)            // 释放焦点            audioFocusManager.abandonAudioFocus()             // 还原音量            recoverVolume()        }        override fun onStop(utteranceId: String?, interrupted: Boolean) {            super.onStop(utteranceId, interrupted)            //1.使用speak的QUEUE_FLUSH模式,打断上一个,上一个不会回调onDone,但是会回调onStop            //2.直接stop也会回调onStop            audioFocusManager.abandonAudioFocus()             // 还原音量            recoverVolume()        }    }
    fun requestAudioFocus(): Int {        try {            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {                audioRequest?.let {                    return audioManager.requestAudioFocus(it)                }                return AudioManager.AUDIOFOCUS_REQUEST_FAILED            } else {            // 这个已经弃用                return audioManager.requestAudioFocus(                    {                        Log.d(TAG, "requestAudioFocus Audio Focus: $it")                    },                    AudioManager.STREAM_ACCESSIBILITY,                    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK                )            }        } catch (e: IllegalArgumentException) {            e.printStackTrace()        }        return AudioManager.AUDIOFOCUS_REQUEST_FAILED    }

释放焦点

        fun abandonAudioFocus() {        audioRequest?.let {            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {                audioManager.abandonAudioFocusRequest(it)            } else {            // 弃用的方法                audioManager.abandonAudioFocus{                    Log.d(TAG, "abandonAudioFocus Audio Focus: $it")                }            }        }    }

通过 AudioManager 增加或者减少音量

    private fun increaseVolume() {    // 判断用户开关是否开启        if (isVolumeUp()) {            //记录原始音量            recordOriginVolume = getVolume()            //静音情况下直接返回            if (recordOriginVolume <= 0) {                return            }            // 设置音量,固定增加量,我们这里增加了原来的百分之三十            setVolume(recordOriginVolume + increaseVolumeNum)            //记录修改后的音量            recordTargetVolume = getVolume()            isVolumeUpping = true        }    }
  • 获取当前系统设置音量
    private fun getVolume(): Int {        return try {            audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)        } catch (e: Exception) {            TPLogUtils.e(TAG, "getVolume() error : message:${e.message}")            0        }    }

audioManager 的 getStreamVolume() 的类型有以下几种可以根据需求选择

STREAM_VOICE_CALL:语音通话音频流。STREAM_SYSTEM:系统提示音和声音效果音频流。STREAM_RING:电话铃声音频流。STREAM_NOTIFICATION:通知提示音和通知音效音频流。STREAM_ALARM:闹钟提示音频流。STREAM_DTMF:拨号按键音频流。STREAM_ACCESSIBILITY:辅助功能音频流
  • setVolume 设置音量
    private fun setVolume(volumeIndex: Int, flags: Int = 0) {        try {            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volumeIndex, flags)        } catch (e: Exception) {            TPLogUtils.e(                TAG,                "setVolume() error : message:${e.message},volumeIndex:$volumeIndex,flags:$flags"            )        }    }

音量设置完成,播放完成以后记得设置回原来的值,以免越来越大。

AudioManager 常用的 API

  • AudioManager 类的一些常用 API:
setStreamVolume(int streamType, int index, int flags):设置指定流类型(streamType)的音量值(index),其中标志(flags)通常设置为 0getStreamVolume(int streamType):获取指定流类型(streamType)的当前音量值。setMode(int mode):设置音频模式(mode),例如音乐播放或电话通话。setSpeakerphoneOn(boolean on):设置是否使用扬声器(speakerphone)进行音频输出。isSpeakerphoneOn():检查当前是否使用扬声器(speakerphone)进行音频输出。setMicrophoneMute(boolean on):设置麦克风静音状态。isMicrophoneMute():检查当前麦克风是否处于静音状态。setWiredHeadsetOn(boolean on):设置是否插入有线耳机。isWiredHeadsetOn():检查当前是否插入有线耳机。setBluetoothScoOn(boolean on):设置是否使用蓝牙 SCO 音频通道。startBluetoothSco():开始使用蓝牙 SCO 音频通道。stopBluetoothSco():停止使用蓝牙 SCO 音频通道。isBluetoothScoOn():检查当前是否使用蓝牙 SCO 音频通道。setBluetoothScoHeadset(AudioDevice bluetoothScoHeadset):设置当前蓝牙耳机设备。isBluetoothScoAvailableOffCall():检查当前是否支持在非通话状态下使用蓝牙 SCO 音频通道。

通过这些 API ,可以满足大多数场景下的语音播报需求。

来源地址:https://blog.csdn.net/ldxlz224/article/details/130532614

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android TTS播报音频并且配合AudioManager压低其他音频声音

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录