Browse Source

修改讯飞语音识别

weicu
tanlongsheng 11 months ago
parent
commit
56349b4f13
  1. 91
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt
  2. 57
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/XunFeiAsrHelper.kt

91
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt

@ -34,6 +34,7 @@ class AzureAsrHelper(private val context: Context) {
// 添加前台服务管理标志
private var isForegroundServiceRunning = false
// 配置参数
private var currentLanguage = "zh-CN"
private var supportedLanguages = arrayOf("zh-CN")
@ -41,6 +42,9 @@ class AzureAsrHelper(private val context: Context) {
private var subscriptionKey = ""
private var region = ""
private var useXunfei = false
private var xunFeiAsrHelper: XunFeiAsrHelper? = null
// 音频源配置
var audioSourceType = AudioSourceType.MICROPHONE
@ -53,6 +57,7 @@ class AzureAsrHelper(private val context: Context) {
// 网络丢失计数器
private var networkLostCount = 0
// 最大网络丢失次数(3秒检测)
private val maxNetworkLostCount = 3
@ -109,6 +114,8 @@ class AzureAsrHelper(private val context: Context) {
this.supportedLanguages = supportedLanguages
}
this.useXunfei = supportedLanguages.any { it == "zh-CN" || it == "en-US" }
// 根据支持的语言数量决定是否启用自动语言检测
this.isAutoDetectLanguage = supportedLanguages.size >= 2
@ -264,6 +271,47 @@ class AzureAsrHelper(private val context: Context) {
}
}
if (useXunfei) {
Log.d(tag, "Using Xunfei Recognition")
val callback = continuousCallback
if (callback == null) {
Log.e(tag, "无法启动连续识别:回调为空")
continuousCallback?.onError(currsessionid, 1001, "回调为空")
return false
}
if (xunFeiAsrHelper == null) {
xunFeiAsrHelper = XunFeiAsrHelper(context)
}
try {
xunFeiAsrHelper?.start(callback)
this.audioSourceType = audioSourceType
if (!audioStream.isContinuousRecognitionActive) {
audioStream.startAudioRecord(
when (audioSourceType) {
AudioSourceType.MICROPHONE -> SimpleAudioReceiver.AudioSourceType.MICROPHONE
AudioSourceType.EXTERNAL -> SimpleAudioReceiver.AudioSourceType.EXTERNAL
},
object : SimpleAudioReceiver.AudioDataCallback {
override fun onAudio(data: ByteArray) {
audioDataCallback?.onAudio(data)
xunFeiAsrHelper?.sendAudio(data)
}
}
)
}
audioStream.isContinuousRecognitionActive = true
return true
} catch (e: Exception) {
Log.e(tag, "Start Xunfei failed: ${e.message}")
xunFeiAsrHelper?.stop()
audioStream.isContinuousRecognitionActive = false
return false
}
}
if (!isRecognizerValid()) {
Log.d(tag, "重新启动连续识别")
val callback = continuousCallback
@ -398,6 +446,27 @@ class AzureAsrHelper(private val context: Context) {
* @return 是否成功停止
*/
fun stopContinuousRecognition(): Boolean {
if (useXunfei) {
try {
xunFeiAsrHelper?.stop()
stopAudioProcessing()
audioStream.isContinuousRecognitionActive = false
if (isForegroundServiceRunning) {
try {
AudioRecordingForegroundService.stopService(context)
isForegroundServiceRunning = false
} catch (e: Exception) {
Log.e(tag, "停止前台服务失败: ${e.message}")
}
}
return true
} catch (e: Exception) {
Log.e(tag, "Stop Xunfei failed: ${e.message}")
return false
}
}
if (speechConfig == null) {
Log.e(tag, "语音服务未初始化")
return false
@ -462,8 +531,12 @@ class AzureAsrHelper(private val context: Context) {
try {
// 如果正在进行连续识别,先停止
if (audioStream.isContinuousRecognitionActive) {
if (useXunfei) {
xunFeiAsrHelper?.stop()
} else {
// 直接停止,不等待结果
recognizer?.stopContinuousRecognitionAsync()
}
audioStream.isContinuousRecognitionActive = false
}
// 停止音频处理
@ -477,6 +550,9 @@ class AzureAsrHelper(private val context: Context) {
recognizer?.close()
recognizer = null
xunFeiAsrHelper?.stop()
xunFeiAsrHelper = null
// 释放speechConfig
speechConfig?.close()
speechConfig = null
@ -526,14 +602,15 @@ class AzureAsrHelper(private val context: Context) {
}
//录音音频文件
/**
* 开启录音
*/
fun enableRecord(audioSourceType: AudioSourceType = AudioSourceType.MICROPHONE,filePath: String, audioDataCallback: SimpleAudioReceiver.AudioDataCallback? = null) {
fun enableRecord(
audioSourceType: AudioSourceType = AudioSourceType.MICROPHONE,
filePath: String,
audioDataCallback: SimpleAudioReceiver.AudioDataCallback? = null
) {
Log.i(tag, "开启录音:")
this.audioSourceType = audioSourceType
if (audioSourceType == AudioSourceType.EXTERNAL) {
@ -565,7 +642,8 @@ class AzureAsrHelper(private val context: Context) {
audioStream.setupStream()
audioStream.startAudioRecord( when (audioSourceType) {
audioStream.startAudioRecord(
when (audioSourceType) {
AudioSourceType.MICROPHONE -> SimpleAudioReceiver.AudioSourceType.MICROPHONE
AudioSourceType.EXTERNAL -> SimpleAudioReceiver.AudioSourceType.EXTERNAL
},
@ -661,7 +739,6 @@ class AzureAsrHelper(private val context: Context) {
}
/**
* 一次性识别回调接口
*/
@ -738,6 +815,7 @@ class AzureAsrHelper(private val context: Context) {
private fun checkNetworkStatus(): Boolean {
return networkMonitor.checkNetworkStatus()
}
/**
* 检查是否是网络相关错误
*/
@ -805,6 +883,7 @@ class AzureAsrHelper(private val context: Context) {
Log.e(tag, "立即停止音频处理失败: ${e.message}")
}
}
/**
* 清理网络检测相关资源
*/

57
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/XunFeiAsrHelper.kt

@ -4,11 +4,12 @@ import android.content.Context
import android.util.Base64
import android.util.Log
import okhttp3.*
import okio.ByteString
import okio.ByteString.Companion.toByteString
import org.json.JSONObject
import java.net.URLEncoder
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.util.UUID
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
@ -16,77 +17,62 @@ class XunFeiAsrHelper(private val context: Context) {
private val tag = "XunFeiAsrHelper"
private val appId = "43d87d22"
private val accessKeyId = "2bd01ce06ac35957fc0c082f9b6284ec"
private val accessKeyId = "83f063755e8d342670002f50a4471d42"
private var webSocket: WebSocket? = null
private val client = OkHttpClient()
// Status: "UNDEFINED", "CONNECTING", "OPEN", "CLOSING", "CLOSED"
private var btnStatus = "UNDEFINED"
private var currentSessionId = ""
private var resultText = ""
fun start(callback: AzureAsrHelper.ContinuousRecognizeCallback) {
currentSessionId = UUID.randomUUID().toString()
var onResult: ((String) -> Unit)? = null
var onError: ((String) -> Unit)? = null
var onStatusChange: ((String) -> Unit)? = null
fun connectWebSocket() {
val url = getWebSocketUrl()
val request = Request.Builder().url(url).build()
changeBtnStatus("CONNECTING")
webSocket = client.newWebSocket(request, object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) {
Log.d(tag, "WebSocket Opened")
changeBtnStatus("OPEN")
callback.onSessionStarted(currentSessionId)
}
override fun onMessage(webSocket: WebSocket, text: String) {
renderResult(text)
renderResult(text, callback)
}
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
Log.d(tag, "WebSocket Closing: $code / $reason")
changeBtnStatus("CLOSING")
webSocket.close(1000, null)
}
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
Log.d(tag, "WebSocket Closed: $code / $reason")
changeBtnStatus("CLOSED")
callback.onSessionStopped(currentSessionId)
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
Log.e(tag, "WebSocket Error", t)
changeBtnStatus("CLOSED")
onError?.invoke(t.message ?: "WebSocket connection failed")
callback.onError(currentSessionId, 1004, "Xunfei connection failed: ${t.message}")
}
})
}
fun sendAudio(frameBuffer: ByteArray) {
webSocket?.let { ws ->
ws.send(ByteString.of(frameBuffer, 0, frameBuffer.size))
webSocket?.let { ws: WebSocket ->
ws.send(frameBuffer.toByteString(0, frameBuffer.size))
}
}
fun close() {
webSocket?.let { ws ->
fun stop() {
webSocket?.let { ws: WebSocket ->
ws.send("{\"end\": true}")
changeBtnStatus("CLOSING")
}
}
private fun changeBtnStatus(status: String) {
btnStatus = status
if (status == "CONNECTING") {
resultText = ""
}
onStatusChange?.invoke(status)
}
private fun renderResult(resultData: String) {
private fun renderResult(
resultData: String,
callback: AzureAsrHelper.ContinuousRecognizeCallback
) {
try {
val jsonData = JSONObject(resultData)
val action = jsonData.optString("action")
@ -120,16 +106,15 @@ class XunFeiAsrHelper(private val context: Context) {
}
if (st.optInt("type") == 0) {
resultText += resultTextTemp
onResult?.invoke(resultText)
callback.onResult(currentSessionId, resultTextTemp, "zh-CN")
} else {
onResult?.invoke(resultText + resultTextTemp)
callback.onRecognizing(currentSessionId, resultTextTemp, "zh-CN")
}
}
"error" -> {
Log.e(tag, "Error: $resultData")
onError?.invoke(jsonData.toString())
callback.onError(currentSessionId, 1005, "Xunfei error: $resultData")
}
}
} catch (e: Exception) {

Loading…
Cancel
Save