| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- package com.nova.brain.glass.helper
- import android.util.Log
- import com.nova.brain.glass.BuildConfig
- import com.nova.brain.glass.model.RecognizeAction
- import com.rokid.online.speech.AsrClient
- import com.rokid.online.speech.OnlineSpeechSdk
- 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.xuqm.base.extensions.showMessage
- object AsrHelper : OfflineCmdListener {
- private const val TAG = "AsrHelper"
- // 配置信息来自 BuildConfig(在 app/build.gradle 的 buildConfigField 中维护)
- private val DOMAIN get() = BuildConfig.SPEECH_DOMAIN
- private val AK get() = BuildConfig.SPEECH_AK
- private val SK get() = BuildConfig.SPEECH_SK
- private val UID get() = BuildConfig.SPEECH_UID
- private val DEVICE_ID get() = BuildConfig.SPEECH_DEVICE_ID
- private val ASR_PATH get() = BuildConfig.SPEECH_ASR_PATH
- private val TTS_PATH get() = BuildConfig.SPEECH_TTS_PATH
- // 唤醒词:Nova Nova
- private const val WAKE_WORD = "Nova Nova"
- private const val WAKE_WORD_PINYIN = "nou wa nou wa"
- private var sdk: OnlineSpeechSdk? = null
- private var asr: AsrClient? = null
- private var tts: TtsClient? = null
- private val audioSource = OpenSdkAudioSource()
- private val ttsPlayer = AndroidPcmTtsStreamPlayer()
- private var isConnected = false
- private var isMicRunning = false
- private var isTtsConnected = false
- private const val WAKE_RESPONSE = "在呢,您请说"
- // 拼接每次识别会话中的中间结果
- private var currentPartial = ""
- /** 当前页面的场景标识,由各 Activity 在 onResume/onPause 中维护 */
- var scene: String = "home"
- /** goToDecisionCenter 命中时的回调,由各 Activity 在 onResume/onPause 中注册/清空 */
- var onGoToDecisionCenter: ((action: RecognizeAction) -> Unit)? = null
- fun init() {
- val cfg = OnlineSpeechSdkConfig(
- domain = DOMAIN,
- ak = AK,
- sk = SK,
- uid = UID,
- deviceId = DEVICE_ID,
- asrPath = ASR_PATH,
- ttsPath = TTS_PATH,
- trustAllCerts = true,
- staticHttpHeaders = mapOf(
- "appCredential" to "userInfo",
- "messageId" to "android-asr-${System.currentTimeMillis()}",
- ),
- staticMessageHeaders = mapOf(
- "appCredential" to "userInfo",
- "messageId" to "android-asr-${System.currentTimeMillis()}",
- ),
- )
- sdk = OnlineSpeechSdk(cfg)
- asr = sdk!!.createAsrClient().attachAudioSource(audioSource).also { setupAsrCallbacks(it) }
- tts = sdk!!.createTtsClient().attachStreamPlayer(ttsPlayer).also { setupTtsCallbacks(it) }
- // 注册离线关键词 Nova Nova,GlassSdk 触发后启动 ASR
- OfflineCmdServiceHelper.registerAsrWakeWord()
- OfflineCmdServiceHelper.addOnLineListener(this)
- // 自动建立 ASR / TTS 连接
- asrConnect()
- tts?.connect()
- Log.d(TAG, "AsrHelper init done")
- }
- private fun asrConnect() {
- asr?.connect()
- Log.d(TAG, "ASR connect() called")
- }
- private fun asrStartMic() {
- if (!isConnected) {
- Log.w(TAG, "ASR startMic ignored: not connected")
- return
- }
- if (isMicRunning) {
- Log.w(TAG, "ASR startMic ignored: mic already running")
- return
- }
- runCatching { asr?.startAsrWithMic() }
- .onSuccess {
- isMicRunning = true
- Log.d(TAG, "ASR startAsrWithMic()")
- }
- .onFailure { Log.e(TAG, "ASR startAsrWithMic failed: ${it.message}") }
- }
- private fun setupAsrCallbacks(asrClient: AsrClient) {
- asrClient.setListener(object : AsrClient.Listener {
- override fun onOpen() {
- isConnected = true
- Log.d(TAG, "ASR websocket open")
- }
- override fun onStart(taskId: String) {
- currentPartial = ""
- Log.d(TAG, "ASR started: $taskId")
- }
- override fun onPartialResult(taskId: String, text: String) {
- // 滚动更新当前识别中间结果
- currentPartial += text
- Log.d(TAG, "ASR partial: $text")
- }
- override fun onFinalResult(taskId: String, text: String) {
- // 将最终结果追加拼接到会话字符串
- isMicRunning = false
- // 滚动更新当前识别中间结果
- currentPartial += text
- Log.d(TAG, "ASR final result: $currentPartial")
- IntentRecognizeHelper.recognize(
- text = currentPartial,
- scence = scene,
- onSuccess = { action ->
- if (action.name == "goToDecisionCenter") {
- onGoToDecisionCenter?.invoke(action)
- } else {
- "需要跳转任务列表".showMessage()
- }
- }
- )
- }
- override fun onFinished(taskId: String) {
- Log.d(TAG, "ASR ended: $taskId")
- }
- override fun onError(code: Int, message: String) {
- Log.e(TAG, "ASR error code=$code msg=$message")
- isMicRunning = false
- }
- override fun onClosed(code: Int, reason: String) {
- isConnected = false
- isMicRunning = false
- Log.d(TAG, "ASR closed code=$code reason=$reason")
- }
- })
- }
- 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) {
- if (cmd == WAKE_WORD) {
- Log.d(TAG, "Wake word triggered")
- if (isTtsConnected) {
- tts?.speak(WAKE_RESPONSE)
- } else {
- Log.w(TAG, "TTS not connected, starting mic directly")
- asrStartMic()
- }
- }
- }
- fun close() {
- OfflineCmdServiceHelper.removeOnLineListener(this)
- if (isMicRunning) {
- runCatching { asr?.stopAsrWithMic() }
- isMicRunning = false
- }
- asr?.close()
- tts?.close()
- sdk?.close()
- asr = null
- tts = null
- sdk = null
- isConnected = false
- isTtsConnected = false
- Log.d(TAG, "AsrHelper closed")
- }
- }
|