使用場景
1. 攔截三方導(dǎo)航返回的即將播放的文本信息,并且加以處理然后播報語音。
2. 音頻焦點管理,協(xié)調(diào)與其他音頻的沖突,如后臺播放音樂時導(dǎo)航播報
3. 提示固定提示音,如超速等滴滴聲音
4. 控制導(dǎo)航語音音量大小
通過 TTS 播放文本信息
- 什么是 TTS
TTS 就是 TextToSpeech Google 提供的將文字轉(zhuǎn)換為自然語言流的技術(shù),就是通過接收一段文本,轉(zhuǎn)換為聲音。具體看百度百科
我這使用場景是在第三方返回語音信息時攔截,然后自己經(jīng)過處理后播報出去。具體實現(xiàn)的核心簡化版代碼如下
創(chuàng)建 TTS
/** Google引擎 */
const val GOOGLE_TTS_ENGINE = "com.google.android.tts"
/**
* 初始化TTS引擎
*/
private fun createTts() {
// contextRef 的弱引用 防止內(nèi)存泄漏
contextRef.get()?.let { context ->
tts = if (!isFollowSystem) {
// 我們這里項目做的不是國內(nèi)的項目所以如果不跟隨系統(tǒng)選擇的默認的Google引擎
TextToSpeech(
context,
{ status -> this@TtsManager.onInit(status) },
GOOGLE_TTS_ENGINE)
} else {
// 手機默認引擎,這個和手機廠商定制應(yīng)該有關(guān)系
TextToSpeech(context) { p0 -> this@TtsManager.onInit(p0) }
}
// 設(shè)置語言播報速度
tts?.setSpeechRate(1f)
// setOnUtteranceProgressListener 設(shè)置的是語音播報的開始、結(jié)束、異常 等監(jiān)聽事件
processListenerRef?.get()?.let {
tts?.setOnUtteranceProgressListener(it)
}
}
}
TextToSpeech 第一個參數(shù)大家都知道,第二個是初始化成功還是失敗的回調(diào),第三個是可以指定引擎,一般手機廠商很多會自己內(nèi)置自己的引擎,默認的話大多應(yīng)該就是 Google 引擎了。this 里面第四個參數(shù)是 packagename 如果本地有其他的引擎可以指定包名,默認null。this 里面第五個參數(shù)是如果指定的引擎失敗會走默認的引擎初始化。
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 語音包
- 根據(jù)需要篩選
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 參數(shù)的含義
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:請求參數(shù)可以為null。就是內(nèi)置的一些可配置的引擎參數(shù)。通過 bundle傳入。
@param utteranceId :此請求的唯一標識符。
@ return :返回值是代表錯誤嗎 0 成功 , -1 失敗
音頻焦點管理
音頻焦點管理就是解決播報時是否有其他音頻正在播報,比如導(dǎo)航中,后臺放著音樂,前面右轉(zhuǎn)播報時降低音樂音量保證用戶可以聽清楚導(dǎo)航聲音。
通過 android,media 下的 AudioFocusRequest 的音頻焦點管理類和 AudioManager 獲取音頻焦點。核心簡化代碼如下
-
上面介紹了通過 TTS 播放導(dǎo)航信息播報,則播報之前要獲取到音頻焦點,結(jié)束之后要釋放焦點,否則后臺音樂的音量不會變回去。
-
首先獲取 AudioFocusRequest
/** audioFocus請求參數(shù)*/
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 來構(gòu)建對象,audioAttributes 設(shè)置一些屬性如下:
/** audioFocus屬性 */
private val audioAttributes: AudioAttributes by lazy {
audioAttr?:AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build()
}
- setUsage : 設(shè)置用途,因為我這里時導(dǎo)航,所以選擇的 USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 導(dǎo)航用途
- USAGE_MEDIA:表示音頻的用途是多媒體。
USAGE_VOICE_COMMUNICATION:表示音頻的用途是語音通信。
USAGE_NOTIFICATION:表示音頻的用途是通知。
USAGE_ALARM:表示音頻的用途是鬧鐘。
USAGE_ASSISTANCE_ACCESSIBILITY:表示音頻的用途是輔助功能和無障礙功能。- setContentType: 設(shè)置描述音頻信號(如語音或音樂)的內(nèi)容類型的屬性。以下是其中一些類型
- CONTENT_TYPE_MUSIC:表示音頻的內(nèi)容類型是音樂。
CONTENT_TYPE_SPEECH:表示音頻的內(nèi)容類型是語音。
CONTENT_TYPE_MOVIE:表示音頻的內(nèi)容類型是電影。
CONTENT_TYPE_SONIFICATION:表示音頻的內(nèi)容類型是提示音或系統(tǒng)聲音。- setFlags(int flags):設(shè)置音頻的標志。
- AudioFocusRequest 的 .setAcceptsDelayedFocusGain(false) 屬性
setAcceptsDelayedFocusGain() 是 AudioAttributes.Builder 類中的一個屬性,用于指示音頻源是否支持延遲獲取焦點(delayed focus gain)。
在 Android 中,當多個應(yīng)用程序同時請求使用音頻焦點時,系統(tǒng)需要決定哪個應(yīng)用程序具有焦點,以便控制音頻的播放和暫停。默認情況下,如果一個應(yīng)用程序請求獲取音頻焦點,系統(tǒng)會立即將焦點轉(zhuǎn)移到該應(yīng)用程序,但在某些情況下,這可能會引起不必要的中斷和干擾。例如,如果用戶正在使用語音助手(例如 Google Assistant)進行語音識別,那么在識別期間可能不希望其他應(yīng)用程序突然播放聲音并中斷語音識別。為了避免這種情況,系統(tǒng)支持延遲獲取焦點,即在請求獲取焦點后,系統(tǒng)會等待一段時間以確定焦點是否真的需要轉(zhuǎn)移,如果不需要,則會保留焦點。
如果一個應(yīng)用程序支持延遲獲取焦點,那么當它請求獲取焦點時,系統(tǒng)會通知它是否已經(jīng)獲得了焦點,并告訴它何時將焦點轉(zhuǎn)移到該應(yīng)用程序。這使得應(yīng)用程序可以在等待焦點時繼續(xù)播放音頻,而不必擔心在焦點轉(zhuǎn)移時被中斷。
因此,setAcceptsDelayedFocusGain() 屬性的意義是指示音頻源是否支持延遲獲取焦點。如果一個音頻源支持延遲獲取焦點,那么應(yīng)該將該屬性設(shè)置為 true,否則應(yīng)該將其設(shè)置為 false。如果不確定是否需要支持延遲獲取焦點,則可以使用默認值 false。
我這里因為要求實時性延遲的話會很危險,所以設(shè)置成了 false
- 下一步獲取 AudioManager
private val audioManager: AudioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
請求音頻焦點
- 我這里時在 TTS 開始播報之謙時候獲取焦點,播放結(jié)束或者中斷的時候移除,TTS回調(diào)如下:
private val utteranceProgressListener = object : UtteranceProgressListener() {
override fun onStart(utteranceId: String?) {
// 獲取焦點
audioFocusManager.requestAudioFocus()
// 根據(jù)配置選擇是否增加音量
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模式,打斷上一個,上一個不會回調(diào)onDone,但是會回調(diào)onStop
//2.直接stop也會回調(diào)onStop
audioFocusManager.abandonAudioFocus()
// 還原音量
recoverVolume()
}
}
/**
* 請求音頻焦點,通過 audioManager 喝 audioFocusRequest來請求焦點
*
* @return 音頻焦點請求結(jié)果
*/
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 {
// 這個已經(jīng)棄用
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() {
// 判斷用戶開關(guān)是否開啟
if (isVolumeUp()) {
//記錄原始音量
recordOriginVolume = getVolume()
//靜音情況下直接返回
if (recordOriginVolume <= 0) {
return
}
// 設(shè)置音量,固定增加量,我們這里增加了原來的百分之三十
setVolume(recordOriginVolume + increaseVolumeNum)
//記錄修改后的音量
recordTargetVolume = getVolume()
isVolumeUpping = true
}
}
- 獲取當前系統(tǒng)設(shè)置音量
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() 的類型有以下幾種可以根據(jù)需求選擇
STREAM_VOICE_CALL:語音通話音頻流。
STREAM_SYSTEM:系統(tǒng)提示音和聲音效果音頻流。
STREAM_RING:電話鈴聲音頻流。
STREAM_NOTIFICATION:通知提示音和通知音效音頻流。
STREAM_ALARM:鬧鐘提示音頻流。
STREAM_DTMF:撥號按鍵音頻流。
STREAM_ACCESSIBILITY:輔助功能音頻流
- setVolume 設(shè)置音量
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"
)
}
}
音量設(shè)置完成,播放完成以后記得設(shè)置回原來的值,以免越來越大。文章來源:http://www.zghlxwxcb.cn/news/detail-672857.html
AudioManager 常用的 API
- AudioManager 類的一些常用 API:
setStreamVolume(int streamType, int index, int flags):設(shè)置指定流類型(streamType)的音量值(index),其中標志(flags)通常設(shè)置為 0。
getStreamVolume(int streamType):獲取指定流類型(streamType)的當前音量值。
setMode(int mode):設(shè)置音頻模式(mode),例如音樂播放或電話通話。
setSpeakerphoneOn(boolean on):設(shè)置是否使用揚聲器(speakerphone)進行音頻輸出。
isSpeakerphoneOn():檢查當前是否使用揚聲器(speakerphone)進行音頻輸出。
setMicrophoneMute(boolean on):設(shè)置麥克風(fēng)靜音狀態(tài)。
isMicrophoneMute():檢查當前麥克風(fēng)是否處于靜音狀態(tài)。
setWiredHeadsetOn(boolean on):設(shè)置是否插入有線耳機。
isWiredHeadsetOn():檢查當前是否插入有線耳機。
setBluetoothScoOn(boolean on):設(shè)置是否使用藍牙 SCO 音頻通道。
startBluetoothSco():開始使用藍牙 SCO 音頻通道。
stopBluetoothSco():停止使用藍牙 SCO 音頻通道。
isBluetoothScoOn():檢查當前是否使用藍牙 SCO 音頻通道。
setBluetoothScoHeadset(AudioDevice bluetoothScoHeadset):設(shè)置當前藍牙耳機設(shè)備。
isBluetoothScoAvailableOffCall():檢查當前是否支持在非通話狀態(tài)下使用藍牙 SCO 音頻通道。
通過這些 API ,可以滿足大多數(shù)場景下的語音播報需求。文章來源地址http://www.zghlxwxcb.cn/news/detail-672857.html
到了這里,關(guān)于Android TTS播報音頻并且配合AudioManager壓低其他音頻聲音的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!