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 isForegroundServiceRunning = false
// 配置参数 // 配置参数
private var currentLanguage = "zh-CN" private var currentLanguage = "zh-CN"
private var supportedLanguages = arrayOf("zh-CN") private var supportedLanguages = arrayOf("zh-CN")
@ -41,6 +42,9 @@ class AzureAsrHelper(private val context: Context) {
private var subscriptionKey = "" private var subscriptionKey = ""
private var region = "" private var region = ""
private var useXunfei = false
private var xunFeiAsrHelper: XunFeiAsrHelper? = null
// 音频源配置 // 音频源配置
var audioSourceType = AudioSourceType.MICROPHONE var audioSourceType = AudioSourceType.MICROPHONE
@ -53,6 +57,7 @@ class AzureAsrHelper(private val context: Context) {
// 网络丢失计数器 // 网络丢失计数器
private var networkLostCount = 0 private var networkLostCount = 0
// 最大网络丢失次数(3秒检测) // 最大网络丢失次数(3秒检测)
private val maxNetworkLostCount = 3 private val maxNetworkLostCount = 3
@ -109,6 +114,8 @@ class AzureAsrHelper(private val context: Context) {
this.supportedLanguages = supportedLanguages this.supportedLanguages = supportedLanguages
} }
this.useXunfei = supportedLanguages.any { it == "zh-CN" || it == "en-US" }
// 根据支持的语言数量决定是否启用自动语言检测 // 根据支持的语言数量决定是否启用自动语言检测
this.isAutoDetectLanguage = supportedLanguages.size >= 2 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()) { if (!isRecognizerValid()) {
Log.d(tag, "重新启动连续识别") Log.d(tag, "重新启动连续识别")
val callback = continuousCallback val callback = continuousCallback
@ -398,6 +446,27 @@ class AzureAsrHelper(private val context: Context) {
* @return 是否成功停止 * @return 是否成功停止
*/ */
fun stopContinuousRecognition(): Boolean { 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) { if (speechConfig == null) {
Log.e(tag, "语音服务未初始化") Log.e(tag, "语音服务未初始化")
return false return false
@ -462,8 +531,12 @@ class AzureAsrHelper(private val context: Context) {
try { try {
// 如果正在进行连续识别,先停止 // 如果正在进行连续识别,先停止
if (audioStream.isContinuousRecognitionActive) { if (audioStream.isContinuousRecognitionActive) {
if (useXunfei) {
xunFeiAsrHelper?.stop()
} else {
// 直接停止,不等待结果 // 直接停止,不等待结果
recognizer?.stopContinuousRecognitionAsync() recognizer?.stopContinuousRecognitionAsync()
}
audioStream.isContinuousRecognitionActive = false audioStream.isContinuousRecognitionActive = false
} }
// 停止音频处理 // 停止音频处理
@ -477,6 +550,9 @@ class AzureAsrHelper(private val context: Context) {
recognizer?.close() recognizer?.close()
recognizer = null recognizer = null
xunFeiAsrHelper?.stop()
xunFeiAsrHelper = null
// 释放speechConfig // 释放speechConfig
speechConfig?.close() speechConfig?.close()
speechConfig = null 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, "开启录音:") Log.i(tag, "开启录音:")
this.audioSourceType = audioSourceType this.audioSourceType = audioSourceType
if (audioSourceType == AudioSourceType.EXTERNAL) { if (audioSourceType == AudioSourceType.EXTERNAL) {
@ -565,7 +642,8 @@ class AzureAsrHelper(private val context: Context) {
audioStream.setupStream() audioStream.setupStream()
audioStream.startAudioRecord( when (audioSourceType) { audioStream.startAudioRecord(
when (audioSourceType) {
AudioSourceType.MICROPHONE -> SimpleAudioReceiver.AudioSourceType.MICROPHONE AudioSourceType.MICROPHONE -> SimpleAudioReceiver.AudioSourceType.MICROPHONE
AudioSourceType.EXTERNAL -> SimpleAudioReceiver.AudioSourceType.EXTERNAL 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 { private fun checkNetworkStatus(): Boolean {
return networkMonitor.checkNetworkStatus() return networkMonitor.checkNetworkStatus()
} }
/** /**
* 检查是否是网络相关错误 * 检查是否是网络相关错误
*/ */
@ -805,6 +883,7 @@ class AzureAsrHelper(private val context: Context) {
Log.e(tag, "立即停止音频处理失败: ${e.message}") 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.Base64
import android.util.Log import android.util.Log
import okhttp3.* import okhttp3.*
import okio.ByteString import okio.ByteString.Companion.toByteString
import org.json.JSONObject import org.json.JSONObject
import java.net.URLEncoder import java.net.URLEncoder
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.security.MessageDigest import java.security.MessageDigest
import java.util.UUID
import javax.crypto.Mac import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec import javax.crypto.spec.SecretKeySpec
@ -16,77 +17,62 @@ class XunFeiAsrHelper(private val context: Context) {
private val tag = "XunFeiAsrHelper" private val tag = "XunFeiAsrHelper"
private val appId = "43d87d22" private val appId = "43d87d22"
private val accessKeyId = "2bd01ce06ac35957fc0c082f9b6284ec" private val accessKeyId = "83f063755e8d342670002f50a4471d42"
private var webSocket: WebSocket? = null private var webSocket: WebSocket? = null
private val client = OkHttpClient() private val client = OkHttpClient()
// Status: "UNDEFINED", "CONNECTING", "OPEN", "CLOSING", "CLOSED" private var currentSessionId = ""
private var btnStatus = "UNDEFINED"
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 url = getWebSocketUrl()
val request = Request.Builder().url(url).build() val request = Request.Builder().url(url).build()
changeBtnStatus("CONNECTING")
webSocket = client.newWebSocket(request, object : WebSocketListener() { webSocket = client.newWebSocket(request, object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) { override fun onOpen(webSocket: WebSocket, response: Response) {
Log.d(tag, "WebSocket Opened") Log.d(tag, "WebSocket Opened")
changeBtnStatus("OPEN") callback.onSessionStarted(currentSessionId)
} }
override fun onMessage(webSocket: WebSocket, text: String) { override fun onMessage(webSocket: WebSocket, text: String) {
renderResult(text) renderResult(text, callback)
} }
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
Log.d(tag, "WebSocket Closing: $code / $reason") Log.d(tag, "WebSocket Closing: $code / $reason")
changeBtnStatus("CLOSING")
webSocket.close(1000, null) webSocket.close(1000, null)
} }
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
Log.d(tag, "WebSocket Closed: $code / $reason") Log.d(tag, "WebSocket Closed: $code / $reason")
changeBtnStatus("CLOSED") callback.onSessionStopped(currentSessionId)
} }
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
Log.e(tag, "WebSocket Error", t) Log.e(tag, "WebSocket Error", t)
changeBtnStatus("CLOSED") callback.onError(currentSessionId, 1004, "Xunfei connection failed: ${t.message}")
onError?.invoke(t.message ?: "WebSocket connection failed")
} }
}) })
} }
fun sendAudio(frameBuffer: ByteArray) { fun sendAudio(frameBuffer: ByteArray) {
webSocket?.let { ws -> webSocket?.let { ws: WebSocket ->
ws.send(ByteString.of(frameBuffer, 0, frameBuffer.size)) ws.send(frameBuffer.toByteString(0, frameBuffer.size))
} }
} }
fun close() { fun stop() {
webSocket?.let { ws -> webSocket?.let { ws: WebSocket ->
ws.send("{\"end\": true}") 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 { try {
val jsonData = JSONObject(resultData) val jsonData = JSONObject(resultData)
val action = jsonData.optString("action") val action = jsonData.optString("action")
@ -120,16 +106,15 @@ class XunFeiAsrHelper(private val context: Context) {
} }
if (st.optInt("type") == 0) { if (st.optInt("type") == 0) {
resultText += resultTextTemp callback.onResult(currentSessionId, resultTextTemp, "zh-CN")
onResult?.invoke(resultText)
} else { } else {
onResult?.invoke(resultText + resultTextTemp) callback.onRecognizing(currentSessionId, resultTextTemp, "zh-CN")
} }
} }
"error" -> { "error" -> {
Log.e(tag, "Error: $resultData") Log.e(tag, "Error: $resultData")
onError?.invoke(jsonData.toString()) callback.onError(currentSessionId, 1005, "Xunfei error: $resultData")
} }
} }
} catch (e: Exception) { } catch (e: Exception) {

Loading…
Cancel
Save