|
|
@ -50,8 +50,17 @@ class AzureAsrHelper(private val context: Context) { |
|
|
// 网络状态监听 |
|
|
// 网络状态监听 |
|
|
private var networkMonitor: NetworkStateMonitor = NetworkStateMonitor(context) |
|
|
private var networkMonitor: NetworkStateMonitor = NetworkStateMonitor(context) |
|
|
private var isNetworkRecovering = false |
|
|
private var isNetworkRecovering = false |
|
|
|
|
|
|
|
|
|
|
|
// 网络丢失计数器 |
|
|
|
|
|
private var networkLostCount = 0 |
|
|
|
|
|
// 最大网络丢失次数(3秒检测) |
|
|
|
|
|
private val maxNetworkLostCount = 3 |
|
|
|
|
|
|
|
|
|
|
|
// 防抖机制相关变量 |
|
|
|
|
|
private var lastNetworkLostTime = 0L |
|
|
|
|
|
private val networkLostDebounceInterval = 3000L // 2秒防抖间隔 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 音频来源类型 |
|
|
* 音频来源类型 |
|
|
@ -221,61 +230,6 @@ class AzureAsrHelper(private val context: Context) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 执行一次性语音识别 |
|
|
|
|
|
* |
|
|
|
|
|
* @param callback 识别结果回调 |
|
|
|
|
|
*/ |
|
|
|
|
|
fun recognizeOnce( |
|
|
|
|
|
callback: RecognizeCallback, |
|
|
|
|
|
audioSourceType: AudioSourceType = AudioSourceType.MICROPHONE |
|
|
|
|
|
) { |
|
|
|
|
|
if (speechConfig == null) { |
|
|
|
|
|
callback.onError(1002,"语音服务未初始化") |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.audioSourceType = audioSourceType |
|
|
|
|
|
// 确保不在连续识别中 |
|
|
|
|
|
if (isContinuousRecognitionActive) { |
|
|
|
|
|
stopContinuousRecognition() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
// 启动音频处理(若已启动则跳过) |
|
|
|
|
|
if (!audioStream!!.isWriting()) { |
|
|
|
|
|
audioStream!!.startAudioRecord( |
|
|
|
|
|
when (audioSourceType) { |
|
|
|
|
|
AudioSourceType.MICROPHONE -> SimpleAudioReceiver.AudioSourceType.MICROPHONE |
|
|
|
|
|
AudioSourceType.EXTERNAL -> SimpleAudioReceiver.AudioSourceType.EXTERNAL |
|
|
|
|
|
}, |
|
|
|
|
|
null |
|
|
|
|
|
) |
|
|
|
|
|
} else { |
|
|
|
|
|
Log.d(tag, "音频已启动,跳过重复启动") |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 执行同步识别 |
|
|
|
|
|
val result = recognizer?.recognizeOnceAsync()?.get() |
|
|
|
|
|
|
|
|
|
|
|
// 停止音频处理 |
|
|
|
|
|
stopAudioProcessing() |
|
|
|
|
|
|
|
|
|
|
|
if (result != null && result.reason == ResultReason.RecognizedSpeech) { |
|
|
|
|
|
val detectedLanguage = AutoDetectSourceLanguageResult.fromResult(result)?.language |
|
|
|
|
|
?: supportedLanguages[0] |
|
|
|
|
|
callback.onResult(result.text, detectedLanguage) |
|
|
|
|
|
} else { |
|
|
|
|
|
callback.onError(1004,"未能识别语音") |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
stopAudioProcessing() |
|
|
|
|
|
callback.onError(1003,"识别异常: ${e.message}") |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 开始连续语音识别 |
|
|
* 开始连续语音识别 |
|
|
* @param audioSourceType 音频源类型 |
|
|
* @param audioSourceType 音频源类型 |
|
|
@ -300,11 +254,14 @@ class AzureAsrHelper(private val context: Context) { |
|
|
return true |
|
|
return true |
|
|
} |
|
|
} |
|
|
if (!isRecognizerValid()) { |
|
|
if (!isRecognizerValid()) { |
|
|
Log.w(tag, "识别器已失效,正在重新创建...") |
|
|
Log.d(tag, "重新启动连续识别") |
|
|
if (!setupRecognizer()) { |
|
|
val callback = continuousCallback |
|
|
Log.e(tag, "重新创建识别器失败") |
|
|
if (callback == null) { |
|
|
|
|
|
Log.e(tag, "无法重新启动连续识别:回调为空") |
|
|
|
|
|
continuousCallback?.onError(1001,"回调为空") |
|
|
return false |
|
|
return false |
|
|
} |
|
|
} |
|
|
|
|
|
setupEventListeners(callback) |
|
|
} |
|
|
} |
|
|
this.audioSourceType = audioSourceType |
|
|
this.audioSourceType = audioSourceType |
|
|
|
|
|
|
|
|
@ -330,7 +287,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
return true |
|
|
return true |
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
Log.e(tag, "开始连续识别失败: ${e.message}") |
|
|
Log.e(tag, "开始连续识别失败: ${e.message}") |
|
|
audioStream!!.stopMicrophoneCapture() |
|
|
stopContinuousRecognition() |
|
|
isContinuousRecognitionActive = false |
|
|
isContinuousRecognitionActive = false |
|
|
return false |
|
|
return false |
|
|
} |
|
|
} |
|
|
@ -399,10 +356,8 @@ class AzureAsrHelper(private val context: Context) { |
|
|
// 直接在当前线程调用回调 |
|
|
// 直接在当前线程调用回调 |
|
|
Log.d(tag, "会话结束事件") |
|
|
Log.d(tag, "会话结束事件") |
|
|
if (audioSourceType == AudioSourceType.EXTERNAL) { |
|
|
if (audioSourceType == AudioSourceType.EXTERNAL) { |
|
|
|
|
|
|
|
|
callback.onSessionStopped() |
|
|
callback.onSessionStopped() |
|
|
isContinuousRecognitionActive = false |
|
|
isContinuousRecognitionActive = false |
|
|
// stopAudioProcessing() |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
) |
|
|
) |
|
|
@ -414,20 +369,7 @@ class AzureAsrHelper(private val context: Context) { |
|
|
val reason = event.reason.toString() |
|
|
val reason = event.reason.toString() |
|
|
|
|
|
|
|
|
Log.d(tag, "识别被取消: reason=$reason, details=$errorDetails") |
|
|
Log.d(tag, "识别被取消: reason=$reason, details=$errorDetails") |
|
|
|
|
|
callback.onCanceled(reason,errorDetails) |
|
|
// 检查是否是网络相关错误 |
|
|
|
|
|
if (isNetworkRelatedError(reason, errorDetails)) { |
|
|
|
|
|
Log.d(tag, "检测到网络相关错误,停止音频处理") |
|
|
|
|
|
stopAudioProcessingImmediately() |
|
|
|
|
|
|
|
|
|
|
|
// 如果不是正在恢复网络,通知上层网络错误 |
|
|
|
|
|
if (!isNetworkRecovering) { |
|
|
|
|
|
callback.onError(1000,"启动连续识别失败,网络连接已断开") |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
// 非网络错误,正常处理 |
|
|
|
|
|
callback.onCanceled(reason, errorDetails) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
) |
|
|
) |
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
@ -448,41 +390,28 @@ class AzureAsrHelper(private val context: Context) { |
|
|
Log.e(tag, "语音服务未初始化") |
|
|
Log.e(tag, "语音服务未初始化") |
|
|
return false |
|
|
return false |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// if (!isContinuousRecognitionActive) { |
|
|
|
|
|
// return true |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
|
|
|
|
|
|
isContinuousRecognitionActive = false |
|
|
|
|
|
|
|
|
|
|
|
if (audioSourceType == AudioSourceType.EXTERNAL) { |
|
|
|
|
|
//pushAudioData(ByteArray(0)) |
|
|
|
|
|
} |
|
|
|
|
|
Log.d(tag, "停止连续语音识别: ") |
|
|
Log.d(tag, "停止连续语音识别: ") |
|
|
|
|
|
// 停止音频处理 |
|
|
|
|
|
audioStream!!.stopMicrophoneCapture() |
|
|
// 直接停止连续识别(SDK内部已是异步操作) |
|
|
// 直接停止连续识别(SDK内部已是异步操作) |
|
|
recognizer?.stopContinuousRecognitionAsync()?.get(1000, TimeUnit.MILLISECONDS) |
|
|
recognizer?.stopContinuousRecognitionAsync()?.get(1000, TimeUnit.MILLISECONDS) |
|
|
|
|
|
|
|
|
// 停止音频处理 |
|
|
recognizer?.close() |
|
|
audioStream!!.stopMicrophoneCapture() |
|
|
recognizer = null |
|
|
// 会话结束事件会设置isContinuousRecognitionActive = false |
|
|
isContinuousRecognitionActive = false |
|
|
return true |
|
|
return true |
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
// 强制重置状态 |
|
|
// 强制重置状态 |
|
|
isContinuousRecognitionActive = false |
|
|
isContinuousRecognitionActive = false |
|
|
Log.e(tag, "停止连续识别失败: ${e.message}") |
|
|
Log.e(tag, "停止连续识别失败: ${e.message}") |
|
|
recognizer?.stopContinuousRecognitionAsync() |
|
|
// 停止音频处理 |
|
|
// 停止音频处理 |
|
|
|
|
|
audioStream!!.stopMicrophoneCapture() |
|
|
audioStream!!.stopMicrophoneCapture() |
|
|
// 尝试强制关闭识别器 |
|
|
recognizer?.stopContinuousRecognitionAsync() |
|
|
// try { |
|
|
|
|
|
// recognizer?.close() |
|
|
recognizer?.close() |
|
|
// recognizer = null |
|
|
recognizer = null |
|
|
// } catch (ex: Exception) { |
|
|
isContinuousRecognitionActive = false |
|
|
// Log.e(tag, "关闭识别器失败: ${ex.message}") |
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
return false |
|
|
return false |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -503,12 +432,10 @@ class AzureAsrHelper(private val context: Context) { |
|
|
recognizer?.stopContinuousRecognitionAsync() |
|
|
recognizer?.stopContinuousRecognitionAsync() |
|
|
isContinuousRecognitionActive = false |
|
|
isContinuousRecognitionActive = false |
|
|
} |
|
|
} |
|
|
// recognizer?.stopContinuousRecognitionAsync()?.get(1000, TimeUnit.MILLISECONDS) |
|
|
|
|
|
// 停止音频处理 |
|
|
// 停止音频处理 |
|
|
stopAudioProcessing() |
|
|
stopAudioProcessing() |
|
|
// 清理网络监听 |
|
|
// 清理网络监听 |
|
|
networkMonitor.dispose() |
|
|
clearNetworkDetection() |
|
|
|
|
|
|
|
|
// 停止录音 |
|
|
// 停止录音 |
|
|
if (audioStream!!.recordfile != null) { |
|
|
if (audioStream!!.recordfile != null) { |
|
|
audioStream!!.recordfile!!.closeFile(true) |
|
|
audioStream!!.recordfile!!.closeFile(true) |
|
|
@ -521,24 +448,15 @@ class AzureAsrHelper(private val context: Context) { |
|
|
speechConfig?.close() |
|
|
speechConfig?.close() |
|
|
speechConfig = null |
|
|
speechConfig = null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 释放音频配置 |
|
|
// 释放音频配置 |
|
|
audioConfig?.close() |
|
|
audioConfig?.close() |
|
|
audioConfig = null |
|
|
audioConfig = null |
|
|
|
|
|
|
|
|
// 确保状态被重置 |
|
|
// 确保状态被重置 |
|
|
isContinuousRecognitionActive = false |
|
|
isContinuousRecognitionActive = false |
|
|
//microphoneStream = null |
|
|
|
|
|
|
|
|
|
|
|
audioConfig = null |
|
|
|
|
|
recognizer = null |
|
|
|
|
|
speechConfig = null |
|
|
|
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
// 确保状态被重置 |
|
|
// 确保状态被重置 |
|
|
|
|
|
|
|
|
isContinuousRecognitionActive = false |
|
|
isContinuousRecognitionActive = false |
|
|
//microphoneStream = null |
|
|
|
|
|
|
|
|
|
|
|
audioConfig = null |
|
|
audioConfig = null |
|
|
recognizer = null |
|
|
recognizer = null |
|
|
speechConfig = null |
|
|
speechConfig = null |
|
|
@ -743,23 +661,48 @@ class AzureAsrHelper(private val context: Context) { |
|
|
* 处理网络连接可用 |
|
|
* 处理网络连接可用 |
|
|
*/ |
|
|
*/ |
|
|
private fun handleNetworkAvailable() { |
|
|
private fun handleNetworkAvailable() { |
|
|
if (!isNetworkRecovering) { |
|
|
// 网络恢复时重置计数器 |
|
|
|
|
|
networkLostCount = 0 |
|
|
|
|
|
lastNetworkLostTime = 0L |
|
|
|
|
|
if (!isNetworkRecovering) { |
|
|
isNetworkRecovering = true |
|
|
isNetworkRecovering = true |
|
|
Log.d(tag, "网络恢复,准备重新启动识别") |
|
|
Log.d(tag, "网络恢复,准备重新启动识别") |
|
|
|
|
|
|
|
|
// 如果之前在进行连续识别,重新启动 |
|
|
|
|
|
Handler(Looper.getMainLooper()).postDelayed({ |
|
|
|
|
|
restartContinuousRecognition() |
|
|
|
|
|
}, 1000) // 延迟1秒确保网络稳定 |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 处理网络连接丢失 |
|
|
* 处理网络连接丢失(带防抖机制) |
|
|
*/ |
|
|
*/ |
|
|
private fun handleNetworkLost() { |
|
|
private fun handleNetworkLost() { |
|
|
Log.d(tag, "网络断开,停止音频处理") |
|
|
val currentTime = System.currentTimeMillis() |
|
|
|
|
|
|
|
|
|
|
|
// 防抖机制:如果距离上次触发时间小于防抖间隔,则忽略本次触发 |
|
|
|
|
|
if (currentTime - lastNetworkLostTime < networkLostDebounceInterval) { |
|
|
|
|
|
Log.d(tag, "网络丢失事件被防抖过滤,距离上次触发仅${currentTime - lastNetworkLostTime}ms") |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 更新最后触发时间 |
|
|
|
|
|
lastNetworkLostTime = currentTime |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//这里启动一个3秒定时器,3秒后检查网络是否恢复 |
|
|
|
|
|
// 启动3秒定时器,3秒后检查网络是否恢复 |
|
|
|
|
|
Handler(Looper.getMainLooper()).postDelayed({ |
|
|
|
|
|
Log.e(tag, "网络不可用") |
|
|
|
|
|
val isNetworkAvailable = checkNetworkStatus() |
|
|
|
|
|
if (isNetworkAvailable) { |
|
|
|
|
|
// 如果网络恢复,重置计数器和防抖时间 |
|
|
|
|
|
Log.d(tag, "网络已恢复,重置计数器") |
|
|
|
|
|
lastNetworkLostTime = 0L |
|
|
|
|
|
isNetworkRecovering = true |
|
|
|
|
|
} else { |
|
|
|
|
|
continuousCallback?.onError(1000,"网络连接不可用,请检查网络设置") |
|
|
|
|
|
Log.d(tag,"网络仍未恢复,继续检测") |
|
|
|
|
|
networkLostCount = 0 |
|
|
stopAudioProcessingImmediately() |
|
|
stopAudioProcessingImmediately() |
|
|
|
|
|
} |
|
|
|
|
|
}, 3000L) // 3秒延时 |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -774,30 +717,16 @@ class AzureAsrHelper(private val context: Context) { |
|
|
Log.e(tag, "立即停止音频处理失败: ${e.message}") |
|
|
Log.e(tag, "立即停止音频处理失败: ${e.message}") |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
/** |
|
|
* 清理网络检测相关资源 |
|
|
* 重新启动连续识别(网络恢复时使用) |
|
|
*/ |
|
|
*/ |
|
|
private fun clearNetworkDetection() { |
|
|
private fun restartContinuousRecognition() { |
|
|
|
|
|
try { |
|
|
networkLostCount = 0 |
|
|
Log.d(tag, "重新启动连续识别") |
|
|
lastNetworkLostTime = 0L |
|
|
// 检查continuousCallback是否为空 |
|
|
|
|
|
val callback = continuousCallback |
|
|
|
|
|
if (callback == null) { |
|
|
|
|
|
Log.e(tag, "无法重新启动连续识别:回调为空") |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
setupEventListeners(callback) |
|
|
|
|
|
isNetworkRecovering = false |
|
|
|
|
|
Log.d(tag, "连续识别重新启动成功") |
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
|
Log.e(tag, "重新启动连续识别失败: ${e.message}") |
|
|
|
|
|
isNetworkRecovering = false |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|