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

这篇具有很好参考价值的文章主要介绍了Android TTS播报音频并且配合AudioManager压低其他音频声音。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

使用场景

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

通过 TTS 播放文本信息

  • 什么是 TTS

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

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

创建 TTS

	/** Google引擎 */
	const val GOOGLE_TTS_ENGINE = "com.google.android.tts"
    /**
     * 初始化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

/** audioFocus请求参数*/
    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 设置一些属性如下:

    /** audioFocus属性 */
    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()
        }
    }
/**
     * 请求音频焦点,通过 audioManager 喝 audioFocusRequest来请求焦点
     *
     * @return 音频焦点请求结果
     */
    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
    }

释放焦点

    /**
     * 释放audio焦点
     */
    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://www.toymoban.com/news/detail-672857.html

到了这里,关于Android TTS播报音频并且配合AudioManager压低其他音频声音的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【TTS】uni-app语音播报app开发 / MT-TTS安卓原生自带语音合成插件(免费无限次)

    TTS 是 Text To Speech 的缩写,即“ 从文本到语音 ”,是人机对话的一部分,让机器能够说话。TTS是语音合成应用的一种。 在程序开发中,有很多TTS的插件,比如百度,讯飞,等等,但是大部分产品都不是完全免费无限制次数调用的且需在线使用 本篇文章介绍一种免费快捷且可

    2023年04月15日
    浏览(154)
  • 【ESP32S3 Sense接入语音识别+MiniMax模型+TTS模块语音播报】

    讲解视频: ESP32S3 AI助手使用MiniMax大模型生产工具1 大家好,今天的教程将围绕如何实现精准的语音播报功能展开,我们用到了ESP32S3 Sense接入语音识别+MiniMax模型对话+SNR9816TTS模块。 目前这是我使用的ESP32S3官方硬件👍👍👍(小小的身材有大大的力量)只需要35元加摄像头麦

    2024年04月12日
    浏览(63)
  • Java 音频播报(内置 + 第三方)

            jdk内置的可以使用,不过呢就是声音太原始了,这是同过输入文字然后识别读出来的。 1、导入JL的依赖 2、代码实现         这个是通过文件播报的,可以在网上下载自己需要的音频 最近在做项目,做外卖的,其中有一个功能就是接单,商家接单就需要用到这个功

    2024年02月02日
    浏览(51)
  • MS-TTS:免费微软TTS语音合成工具(一键合成导出MP3音频)

    声明 本工具是个免费工具,遇到问题,还请自行解决,下面有文字教程,B站有视频教程(链接在文章末尾); 其次,微软接口卡顿,连接超时等问题下方有详细说明,请仔细看一下; 再次,强制升级是为了统一版本方便问题的收集与解决,如果你能理解最好,不能理解的话

    2024年02月03日
    浏览(45)
  • 【专题速递】音频生成、TTS和AIGC在音乐上的运用

      //   AIGC的发展为音频带来了什么?AIGC如何赋能音乐创作?如何识别虚假音频?TTS可以在哪种场景下解决特定问题?7月29日LiveVideoStackCon2023上海站音频新体验专场,为您解答。 音频新体验 随着多媒体和通信网络技术的不断更新,以及新型音视频应用场景的不断涌现,音频

    2024年02月13日
    浏览(38)
  • OpenAI Whisper + FFmpeg + TTS:动态实现跨语言视频音频翻译

    本文作者系360奇舞团前端开发工程师 本文介绍了如何结合 OpenAI Whisper、FFmpeg 和 TTS(Text-to-Speech)技术,以实现将视频翻译为其他语言并更换声音的过程。我们将探讨如何使用 OpenAI Whisper 进行语音识别和翻译,然后使用 FFmpeg 提取视频音轨和处理视频,最后使用 TTS 技术生成新

    2024年02月09日
    浏览(51)
  • Unity 工具 之 Azure 微软SSML语音合成TTS流式获取音频数据的简单整理

    目录 Unity 工具 之 Azure 微软SSML语音合成TTS流式获取音频数据的简单整理 一、简单介绍 二、实现原理 三、实现步骤 四、关键代码 Unity 工具类,自己整理的一些游戏开发可能用到的模块,单独独立使用,方便游戏开发。 本节介绍,这里在使用微软的Azure 进行语音合成的两个方

    2024年02月12日
    浏览(57)
  • Android文字转语音播报

    Android文字转语音播报可以通过Android自带TTS和第三方语音框架实现。 代码如下(示例): 代码如下(示例): 1.包名、应用名: com.svox.pico 系统自带不支持中文语音 com.baidu.duersdk.opensdk 度秘语音引擎3.0 不支持5.0以下系统,大小11.95M com.google.android.tts 谷歌文字转语音引擎,不

    2023年04月08日
    浏览(40)
  • Unity C# 之 Azure 微软SSML语音合成TTS流式获取音频数据以及表情嘴型 Animation 的简单整理

    目录 Unity C# 之 Azure 微软SSML语音合成TTS流式获取音频数据以及表情嘴型 Animation 的简单整理 一、简单介绍 二、实现原理 三、注意事项 四、实现步骤 五、关键代码 Unity 工具类,自己整理的一些游戏开发可能用到的模块,单独独立使用,方便游戏开发。 本节介绍,这里在使用

    2024年02月12日
    浏览(57)
  • uniapp使用uni.createInnerAudioContext()播放指定音频并且切换

    uniapp API 中 uni.createInnerAudioContext()是无法多音频播放的 如果我们想同时播放 加上ambient(不中止其他声音播放,不能后台播放,静音后无声音)这个属性,我做的这个是用uniapp开发的一个安卓端的壳包,正常h5用MP3,WMA或者MPEG等格式的音频都是可以的,但在安卓端手机测试需

    2024年02月11日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包