feat(speech): 集成TTS功能并优化语音配置管理
- 在gradle.properties中添加测试环境默认配置参数 - 通过BuildConfig统一管理语音服务相关配置信息 - 集成TtsClient实现文本转语音功能 - 添加TTS连接状态管理和回调处理 - 实现唤醒词触发后的TTS响应播报 - 优化ASR和TTS的连接与关闭流程 - 添加TTS播放完成后的麦克风自动启动逻辑
这个提交包含在:
父节点
b7f2405b7b
当前提交
e3bf95e4fe
@ -20,6 +20,14 @@ android {
|
|||||||
]
|
]
|
||||||
buildConfigField("String", "APP_Name", "\"" + apps.applicationName + "\"")
|
buildConfigField("String", "APP_Name", "\"" + apps.applicationName + "\"")
|
||||||
|
|
||||||
|
buildConfigField("String", "SPEECH_DOMAIN", "\"api-test.rokid.com\"")
|
||||||
|
buildConfigField("String", "SPEECH_AK", "\"\"")
|
||||||
|
buildConfigField("String", "SPEECH_SK", "\"\"")
|
||||||
|
buildConfigField("String", "SPEECH_UID", "\"demo-user\"")
|
||||||
|
buildConfigField("String", "SPEECH_DEVICE_ID", "\"demo-device\"")
|
||||||
|
buildConfigField("String", "SPEECH_ASR_PATH", "\"/ar/audio/api/ws/asr/streaming\"")
|
||||||
|
buildConfigField("String", "SPEECH_TTS_PATH", "\"/ar/audio/api/ws/tts\"")
|
||||||
|
|
||||||
flavorDimensions "versioncode"
|
flavorDimensions "versioncode"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
package com.nova.brain.glass.helper
|
package com.nova.brain.glass.helper
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.nova.brain.glass.BuildConfig
|
||||||
import com.nova.brain.glass.model.RecognizeAction
|
import com.nova.brain.glass.model.RecognizeAction
|
||||||
import com.rokid.online.speech.AsrClient
|
import com.rokid.online.speech.AsrClient
|
||||||
import com.rokid.online.speech.OnlineSpeechSdk
|
import com.rokid.online.speech.OnlineSpeechSdk
|
||||||
import com.rokid.online.speech.OnlineSpeechSdkConfig
|
import com.rokid.online.speech.OnlineSpeechSdkConfig
|
||||||
|
import com.rokid.online.speech.TtsClient
|
||||||
|
import com.rokid.online.speech.open.AndroidPcmTtsStreamPlayer
|
||||||
import com.rokid.online.speech.open.OpenSdkAudioSource
|
import com.rokid.online.speech.open.OpenSdkAudioSource
|
||||||
import com.xuqm.base.extensions.showMessage
|
import com.xuqm.base.extensions.showMessage
|
||||||
|
|
||||||
@ -12,14 +15,14 @@ object AsrHelper : OfflineCmdListener {
|
|||||||
|
|
||||||
private const val TAG = "AsrHelper"
|
private const val TAG = "AsrHelper"
|
||||||
|
|
||||||
// 配置信息,从 online-speech-sdk-demo 参考项目复制
|
// 配置信息来自 BuildConfig(在 app/build.gradle 的 buildConfigField 中维护)
|
||||||
private const val DOMAIN = "api-test.rokid.com"
|
private val DOMAIN get() = BuildConfig.SPEECH_DOMAIN
|
||||||
private const val ASR_PATH = "/ar/audio/api/ws/asr/streaming"
|
private val AK get() = BuildConfig.SPEECH_AK
|
||||||
private const val TTS_PATH = "/ar/audio/api/ws/tts"
|
private val SK get() = BuildConfig.SPEECH_SK
|
||||||
private const val AK = ""
|
private val UID get() = BuildConfig.SPEECH_UID
|
||||||
private const val SK = ""
|
private val DEVICE_ID get() = BuildConfig.SPEECH_DEVICE_ID
|
||||||
private const val UID = "demo-user"
|
private val ASR_PATH get() = BuildConfig.SPEECH_ASR_PATH
|
||||||
private const val DEVICE_ID = "demo-device"
|
private val TTS_PATH get() = BuildConfig.SPEECH_TTS_PATH
|
||||||
|
|
||||||
// 唤醒词:Nova Nova
|
// 唤醒词:Nova Nova
|
||||||
private const val WAKE_WORD = "Nova Nova"
|
private const val WAKE_WORD = "Nova Nova"
|
||||||
@ -27,10 +30,15 @@ object AsrHelper : OfflineCmdListener {
|
|||||||
|
|
||||||
private var sdk: OnlineSpeechSdk? = null
|
private var sdk: OnlineSpeechSdk? = null
|
||||||
private var asr: AsrClient? = null
|
private var asr: AsrClient? = null
|
||||||
|
private var tts: TtsClient? = null
|
||||||
private val audioSource = OpenSdkAudioSource()
|
private val audioSource = OpenSdkAudioSource()
|
||||||
|
private val ttsPlayer = AndroidPcmTtsStreamPlayer()
|
||||||
|
|
||||||
private var isConnected = false
|
private var isConnected = false
|
||||||
private var isMicRunning = false
|
private var isMicRunning = false
|
||||||
|
private var isTtsConnected = false
|
||||||
|
|
||||||
|
private const val WAKE_RESPONSE = "在呢,您请说"
|
||||||
|
|
||||||
// 拼接每次识别会话中的中间结果
|
// 拼接每次识别会话中的中间结果
|
||||||
private var currentPartial = ""
|
private var currentPartial = ""
|
||||||
@ -63,13 +71,15 @@ object AsrHelper : OfflineCmdListener {
|
|||||||
|
|
||||||
sdk = OnlineSpeechSdk(cfg)
|
sdk = OnlineSpeechSdk(cfg)
|
||||||
asr = sdk!!.createAsrClient().attachAudioSource(audioSource).also { setupAsrCallbacks(it) }
|
asr = sdk!!.createAsrClient().attachAudioSource(audioSource).also { setupAsrCallbacks(it) }
|
||||||
|
tts = sdk!!.createTtsClient().attachStreamPlayer(ttsPlayer).also { setupTtsCallbacks(it) }
|
||||||
|
|
||||||
// 注册离线关键词 Nova Nova,GlassSdk 触发后启动 ASR
|
// 注册离线关键词 Nova Nova,GlassSdk 触发后启动 ASR
|
||||||
OfflineCmdServiceHelper.registerAsrWakeWord()
|
OfflineCmdServiceHelper.registerAsrWakeWord()
|
||||||
OfflineCmdServiceHelper.addOnLineListener(this)
|
OfflineCmdServiceHelper.addOnLineListener(this)
|
||||||
|
|
||||||
// 自动建立 ASR 连接
|
// 自动建立 ASR / TTS 连接
|
||||||
asrConnect()
|
asrConnect()
|
||||||
|
tts?.connect()
|
||||||
|
|
||||||
Log.d(TAG, "AsrHelper init done")
|
Log.d(TAG, "AsrHelper init done")
|
||||||
}
|
}
|
||||||
@ -150,11 +160,40 @@ object AsrHelper : OfflineCmdListener {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 离线关键词回调:匹配唤醒词时启动麦克风
|
private fun setupTtsCallbacks(ttsClient: TtsClient) {
|
||||||
|
ttsClient.setListener(object : TtsClient.Listener {
|
||||||
|
override fun onOpen() {
|
||||||
|
isTtsConnected = true
|
||||||
|
Log.d(TAG, "TTS websocket open")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFinished(taskId: String) {
|
||||||
|
Log.d(TAG, "TTS ended: $taskId, starting mic")
|
||||||
|
asrStartMic()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(code: Int, message: String) {
|
||||||
|
Log.e(TAG, "TTS error code=$code msg=$message, fallback to mic")
|
||||||
|
asrStartMic()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClosed(code: Int, reason: String) {
|
||||||
|
isTtsConnected = false
|
||||||
|
Log.d(TAG, "TTS closed code=$code reason=$reason")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 离线关键词回调:唤醒词触发时先 TTS 播报,播报结束后启动麦克风
|
||||||
override fun onOfflineCmd(cmd: String) {
|
override fun onOfflineCmd(cmd: String) {
|
||||||
if (cmd == WAKE_WORD) {
|
if (cmd == WAKE_WORD) {
|
||||||
Log.d(TAG, "Wake word triggered, starting mic")
|
Log.d(TAG, "Wake word triggered")
|
||||||
asrStartMic()
|
if (isTtsConnected) {
|
||||||
|
tts?.speak(WAKE_RESPONSE)
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "TTS not connected, starting mic directly")
|
||||||
|
asrStartMic()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,10 +204,13 @@ object AsrHelper : OfflineCmdListener {
|
|||||||
isMicRunning = false
|
isMicRunning = false
|
||||||
}
|
}
|
||||||
asr?.close()
|
asr?.close()
|
||||||
|
tts?.close()
|
||||||
sdk?.close()
|
sdk?.close()
|
||||||
asr = null
|
asr = null
|
||||||
|
tts = null
|
||||||
sdk = null
|
sdk = null
|
||||||
isConnected = false
|
isConnected = false
|
||||||
|
isTtsConnected = false
|
||||||
Log.d(TAG, "AsrHelper closed")
|
Log.d(TAG, "AsrHelper closed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -19,3 +19,12 @@ android.useAndroidX=true
|
|||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
# Kotlin code style for this project: "official" or "obsolete":
|
# Kotlin code style for this project: "official" or "obsolete":
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
|
|
||||||
|
# Demo-Android defaults (test environment)
|
||||||
|
online.demo.domain=api-test.rokid.com
|
||||||
|
online.demo.ak=20bc97fd19ef47b38a8c63f3b22ff401
|
||||||
|
online.demo.sk=d2131994176d4e08a5815cad1e06da4d
|
||||||
|
online.demo.uid=demo-user
|
||||||
|
online.demo.deviceId=demo-device
|
||||||
|
online.demo.asrPath=/ar/audio/api/ws/asr/streaming
|
||||||
|
online.demo.ttsPath=/ar/audio/api/ws/tts
|
||||||
正在加载...
在新工单中引用
屏蔽一个用户