From 18e98e2fdcd76d7ef304ebdf22f9605ebfd2f2b1 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Mon, 4 Aug 2025 10:27:39 +0800 Subject: [PATCH] =?UTF-8?q?Revert=20"perf(agent=5Fservice):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E9=9F=B3=E9=A2=91=E5=A4=84=E7=90=86=E5=92=8C=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E5=88=86=E5=8F=91=E6=80=A7=E8=83=BD"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 89e99cbe670206d6beb08b65ccb8a8088ac186c3. --- .../agent_service/AgentService.kt | 316 ++++-------------- .../agent_service/AgentServicePlugin.kt | 42 +-- .../agent_service/AgentServiceImpl.swift | 112 ++----- .../azure_speech/AzureAsrHelper.kt | 35 +- .../Sources/azure_speech/AzureAsrHelper.swift | 40 +-- 5 files changed, 121 insertions(+), 424 deletions(-) diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt index bb34a6d5c..a1921b405 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt @@ -15,7 +15,6 @@ import com.yunqiinnovation.user_portrait.UserPortrait import com.yunqiinnovation.location_service.LocationService import android.util.Log import kotlinx.coroutines.* -import kotlinx.coroutines.channels.Channel import java.util.Collections import kotlin.coroutines.CoroutineContext import com.yunqiinnovation.ble_service.BleService @@ -117,7 +116,7 @@ object AgentService : CoroutineScope { // 空闲检测相关 private var idleCheckJob: Job? = null - private val maxIdleSeconds = 10 // 最大空闲秒数(保持原有设置) + private val maxIdleSeconds = 10 // 最大空闲秒数 // 打断防抖相关 private var lastInterruptTime = 0L @@ -193,31 +192,27 @@ object AgentService : CoroutineScope { } // 初始化Azure ASR azureAsrHelper = AzureAsrHelper(context).apply { - // 使用AgentService的协程作用域,确保生命周期管理 - launch(Dispatchers.IO) { - try { - //获取语音识别的语种 - val supportedLanguagesConfig = config["supportedLanguages"] as? List - val supportedLanguages = supportedLanguagesConfig?.toTypedArray() - ?: arrayOf("zh-CN", "en-US") // 默认支持多语言 - - val asrInitSuccess = initialize( - subscriptionKey = config["azureSpeechKey"]?.toString() ?: "", - region = config["azureSpeechRegion"]?.toString() ?: "", - supportedLanguages = supportedLanguages, - audioSourceType = if (isExternalActive) AzureAsrHelper.AudioSourceType.EXTERNAL - else AzureAsrHelper.AudioSourceType.MICROPHONE - ) + // 使用协程处理异步初始化 + CoroutineScope(Dispatchers.IO).launch { + //获取语音识别的语种 + val supportedLanguagesConfig = config["supportedLanguages"] as? List + val supportedLanguages = supportedLanguagesConfig?.toTypedArray() + ?: arrayOf("zh-CN", "en-US") // 默认支持多语言 + + val asrInitSuccess = initialize( + subscriptionKey = config["azureSpeechKey"]?.toString() ?: "", + region = config["azureSpeechRegion"]?.toString() ?: "", + supportedLanguages = supportedLanguages, + audioSourceType = if (isExternalActive) AzureAsrHelper.AudioSourceType.EXTERNAL + else AzureAsrHelper.AudioSourceType.MICROPHONE + ) - if (asrInitSuccess) { - withContext(Dispatchers.Main) { - recognizeCallback() - } - } else { - Log.w(TAG, "Azure ASR初始化失败") + if (asrInitSuccess) { + withContext(Dispatchers.Main) { + recognizeCallback() } - } catch (e: Exception) { - Log.e(TAG, "Azure ASR初始化异常", e) + } else { + Log.w(TAG, "Azure ASR初始化失败") } } } @@ -249,71 +244,35 @@ object AgentService : CoroutineScope { } /** - * 释放所有资源 - 增强版本 + * 释放所有资源 */ fun dispose() { try { - Log.i(TAG, "开始释放AgentService资源...") - - // 1. 停止所有活动 stopRecognition() stopTts() stopAiStream() stopIdleCheck() - // 2. 停止事件处理器 - eventProcessorJob?.cancel() - eventProcessorJob = null - eventQueue.close() - - // 3. 释放音频播放器 audioPlayer?.release() audioPlayer = null - // 4. 释放Azure ASR(确保完全清理) - azureAsrHelper?.let { helper -> - try { - helper.dispose() - } catch (e: Exception) { - Log.e(TAG, "释放Azure ASR异常", e) - } - } - azureAsrHelper = null + azureAsrHelper?.dispose() - // 5. 释放ChatAPI服务 + // 释放ChatAPI服务 if (::chatApiService.isInitialized) { - try { - chatApiService.cancelCurrentStream() - chatApiService.dispose() - } catch (e: Exception) { - Log.e(TAG, "释放ChatAPI服务异常", e) - } + chatApiService.cancelCurrentStream() + chatApiService.dispose() } - // 6. 释放TTS服务 - ttsService?.let { tts -> - try { - tts.release() - } catch (e: Exception) { - Log.e(TAG, "释放TTS服务异常", e) - } - } + // 释放TTS服务 + ttsService?.release() ttsService = null - // 7. 取消所有协程并清理监听器 job.cancel() clearListeners() - - // 8. 重置状态 _isInitialized.set(false) - _isRecognitionActive.set(false) - _hasSpeechDetected.set(false) - _isTtsSpeaking.set(false) - _isAiStreaming.set(false) - - Log.i(TAG, "AgentService资源释放完成") } catch (e: Exception) { - Log.e(TAG, "释放资源异常: ${e.message}", e) + Log.e(TAG, "释放资源异常: ${e.message}") } } @@ -456,31 +415,19 @@ object AgentService : CoroutineScope { stopIdleCheck() // 先停止现有的检查 if (!isRecognitionActive) return - // BLE模式下使用稍长的超时时间,但不影响正常使用 - val timeoutSeconds = if (audioSourceType == AzureAsrHelper.AudioSourceType.EXTERNAL) { - maxIdleSeconds + 3 // BLE模式额外增加3秒,避免过度延长 - } else { - maxIdleSeconds - } - idleCheckJob = launch { try { - Log.d(TAG, "启动空闲检测,超时时间: ${timeoutSeconds}秒,音频源: $audioSourceType") - delay(timeoutSeconds * 1000L) + delay(maxIdleSeconds * 1000L) if (isActive && isRecognitionActive && !hasSpeechDetected && !isTtsSpeaking && !isAiStreaming) { - Log.i(TAG, "空闲超时,停止识别 - 超时时间: ${timeoutSeconds}秒") stopRecognition() sendEvent( "auto_stop", - mapOf("reason" to "idle_timeout", "seconds" to timeoutSeconds) + mapOf("reason" to "idle_timeout", "seconds" to maxIdleSeconds) ) - } else { - Log.d(TAG, "空闲检测结束,但条件不满足停止要求") } } catch (e: CancellationException) { // 协程被取消,正常行为 - Log.d(TAG, "空闲检测被取消") } catch (e: Exception) { Log.e(TAG, "空闲检测异常: ${e.message}") } @@ -513,12 +460,26 @@ object AgentService : CoroutineScope { Log.d(TAG, "服务未初始化") return false } - - Log.i(TAG, "开始语音识别 - 模式: ${if (isExternalActive) "BLE外部音频" else "设备麦克风"}") - - // 先停止当前识别 + Log.d("TAG", "开始语音识别startRecognition?") stopRecognition() - + BleService.openEncoder() + // if (isRecognitionActive&&audioSourceType==AzureAsrHelper.AudioSourceType.MICROPHONE){ + // Log.d(TAG, "开始语音识别stopRecognition") + // // BleService.closeCodec() + // stopRecognition() + + // } + // if((isExternalActive&&audioSourceType==AzureAsrHelper.AudioSourceType.MICROPHONE)||!isRecognitionActive) + // { + // Log.d(TAG, "开始语音识别BleService.openEncoder") + // BleService.openEncoder() + + // } + // if(isRecognitionActive) + // { + // Log.d(TAG, "开始语音识别azureAsrHelper?.startContinuousRecognition(audioSourceType)") + // return true + // } // 检查录音权限 if (!isExternalActive && !checkRecordAudioPermission()) { Log.e(TAG, "需要录音权限") @@ -526,45 +487,22 @@ object AgentService : CoroutineScope { return false } - // 设置音频源类型 - audioSourceType = if (isExternalActive) { - AzureAsrHelper.AudioSourceType.EXTERNAL - } else { - AzureAsrHelper.AudioSourceType.MICROPHONE - } - - Log.i(TAG, "音频源类型设置为: $audioSourceType") + _isRecognitionActive.set(true) + _hasSpeechDetected.set(false) try { - // 启动Azure ASR - val asrStarted = azureAsrHelper?.startContinuousRecognition(audioSourceType) ?: false - if (!asrStarted) { - Log.e(TAG, "Azure ASR启动失败") - return false - } - - // 如果是BLE模式,在ASR启动后再打开编码器 - if (isExternalActive) { - Log.i(TAG, "BLE模式:启动编码器") - // 使用协程延迟而不是阻塞主线程 - launch { - delay(50) // 给ASR一些启动时间 - val encoderOpened = BleService.openEncoder() - if (!encoderOpened) { - Log.w(TAG, "BLE编码器启动失败,但继续尝试识别") - } - } + audioSourceType = if (isExternalActive) { + AzureAsrHelper.AudioSourceType.EXTERNAL + } else { + AzureAsrHelper.AudioSourceType.MICROPHONE } + Log.d("TAG", "azureAsrHelper?.startContinuousRecognition") + azureAsrHelper?.startContinuousRecognition(audioSourceType) - _isRecognitionActive.set(true) - _hasSpeechDetected.set(false) - - Log.i(TAG, "语音识别启动成功") return true - } catch (e: Exception) { _isRecognitionActive.set(false) - Log.e(TAG, "启动语音识别失败: ${e.message}", e) + Log.e(TAG, "启动语音识别失败: ${e.message}") sendEvent( "error", mapOf( "code" to "RECOGNITION_START_ERROR", @@ -587,17 +525,13 @@ object AgentService : CoroutineScope { _hasSpeechDetected.set(true) if (!previousHasSpeech) { - Log.i(TAG, "首次检测到语音输入: $recognizing (音频源: $audioSourceType)") restartIdleCheck() } - // 实时发送识别结果,确保UI及时更新 - Log.v(TAG, "实时识别: $recognizing") sendEvent( "recognizing", mapOf( "text" to recognizing, - "language" to detectedLanguage, - "audioSource" to audioSourceType.toString() + "language" to detectedLanguage ) ) @@ -670,42 +604,28 @@ object AgentService : CoroutineScope { override fun onCanceled(reason: String, errorDetails: String) { _isRecognitionActive.set(false) stopIdleCheck() - Log.w(TAG, "语音识别被取消: $reason, 详情: $errorDetails") - - // 如果是BLE模式,关闭编码器 - if (audioSourceType == AzureAsrHelper.AudioSourceType.EXTERNAL) { - BleService.closeCodec() - } - + Log.d(TAG, "recognition_canceled:") + BleService.closeCodec() sendEvent( "recognition_canceled", mapOf( "reason" to reason, "details" to errorDetails ) ) - - // 重新设置回调,但不自动重启识别 recognizeCallback() } override fun onError(error: String) { _isRecognitionActive.set(false) stopIdleCheck() - Log.e(TAG, "语音识别错误: $error") - - // 如果是BLE模式,关闭编码器 - if (audioSourceType == AzureAsrHelper.AudioSourceType.EXTERNAL) { - BleService.closeCodec() - } - + BleService.closeCodec() + Log.d(TAG, "语音识别错误: $error") sendEvent( "error", mapOf( "code" to "RECOGNITION_ERROR", "message" to error ) ) - - // 重新设置回调,但不自动重启识别 recognizeCallback() } }) @@ -715,68 +635,10 @@ object AgentService : CoroutineScope { } } - // 音频数据推送统计 - private var audioDataCount = 0 - private var lastAudioDataTime = 0L - - // 性能监控 - private var lastMemoryCheckTime = 0L - private val MEMORY_CHECK_INTERVAL = 30000L // 30秒检查一次内存 - fun pushAudioData(data: ByteArray) { - val currentTime = System.currentTimeMillis() - audioDataCount++ - - // 每100个数据包或每5秒输出一次统计 - if (audioDataCount % 100 == 0 || (currentTime - lastAudioDataTime) > 5000) { - Log.i(TAG, "音频数据统计: 已推送${audioDataCount}包, 当前包大小: ${data.size} bytes, 音频源: $audioSourceType") - lastAudioDataTime = currentTime - - // 定期检查内存使用情况 - checkMemoryUsage(currentTime) - } - - // 添加调试日志,但保持原有逻辑的兼容性 - if (!isRecognitionActive) { - Log.w(TAG, "语音识别未激活,但仍尝试推送音频数据: ${data.size} bytes") - } - - if (audioSourceType != AzureAsrHelper.AudioSourceType.EXTERNAL) { - Log.w(TAG, "音频源类型不匹配: $audioSourceType,推送音频数据: ${data.size} bytes") - } - - // 保持原有逻辑,确保兼容性 azureAsrHelper?.audioStream?.saveAudioDataTo(data) } - /** - * 检查内存使用情况 - */ - private fun checkMemoryUsage(currentTime: Long) { - if (currentTime - lastMemoryCheckTime > MEMORY_CHECK_INTERVAL) { - lastMemoryCheckTime = currentTime - - val runtime = Runtime.getRuntime() - val usedMemory = runtime.totalMemory() - runtime.freeMemory() - val maxMemory = runtime.maxMemory() - val memoryUsagePercent = (usedMemory * 100 / maxMemory).toInt() - - Log.i(TAG, "内存使用情况: ${usedMemory / 1024 / 1024}MB / ${maxMemory / 1024 / 1024}MB (${memoryUsagePercent}%)") - - // 如果内存使用超过80%,发出警告 - if (memoryUsagePercent > 80) { - Log.w(TAG, "内存使用率过高: ${memoryUsagePercent}%,建议检查内存泄漏") - - // 发送内存警告事件 - sendEvent("memory_warning", mapOf( - "usedMemoryMB" to (usedMemory / 1024 / 1024), - "maxMemoryMB" to (maxMemory / 1024 / 1024), - "usagePercent" to memoryUsagePercent - )) - } - } - } - /** * 停止语音识别 */ @@ -1374,53 +1236,19 @@ object AgentService : CoroutineScope { } } - // 事件发送优化:使用单一协程处理事件队列 - private val eventQueue = Channel>>(capacity = Channel.UNLIMITED) - private var eventProcessorJob: Job? = null - - init { - // 启动事件处理协程 - startEventProcessor() - } - - private fun startEventProcessor() { - eventProcessorJob = launch(Dispatchers.Main) { - for ((eventName, data) in eventQueue) { - try { - // 创建监听器快照,避免并发修改 - val currentListeners = synchronized(listeners) { listeners.toList() } - - currentListeners.forEach { listener -> - try { - listener.onEvent(eventName, data) - } catch (e: Exception) { - Log.e(TAG, "向监听器发送事件失败: $eventName", e) - } - } - } catch (e: Exception) { - Log.e(TAG, "处理事件失败: $eventName", e) - } - } - } - } - /** - * 发送事件 - 优化版本,使用事件队列避免主线程阻塞 + * 发送事件 */ private fun sendEvent(eventName: String, data: Map) { - try { - // 对于高频事件,进行限流处理 - if (eventName == "recognizing" && eventQueue.trySend(Pair(eventName, data)).isFailure) { - Log.v(TAG, "识别事件队列已满,跳过当前事件") - return - } - - // 非阻塞发送到事件队列 - if (!eventQueue.trySend(Pair(eventName, data)).isSuccess) { - Log.w(TAG, "事件队列已满,丢弃事件: $eventName") + // 切换到主线程执行监听器回调,避免 UI 更新问题 + launch(Dispatchers.Main) { + listeners.forEach { listener -> + try { + listener.onEvent(eventName, data) + } catch (e: Exception) { + Log.e(TAG, "向监听器发送事件失败", e) + } } - } catch (e: Exception) { - Log.e(TAG, "发送事件异常: $eventName", e) } } diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt index dc5428345..60b919843 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt @@ -286,43 +286,19 @@ class AgentServicePlugin : FlutterPlugin, MethodCallHandler, EventChannel.Stream } /** - * 实现AgentServiceListener接口 - 优化版本 + * 实现AgentServiceListener接口 * 接收来自AgentService的事件,并转发到Flutter */ override fun onEvent(eventName: String, data: Map) { - try { - // 对于高频事件,进行优化处理 - val eventData = when (eventName) { - "recognizing" -> { - // 实时识别事件,简化数据结构 - JSONObject().apply { - put("event", eventName) - put("data", JSONObject().apply { - put("text", data["text"] ?: "") - put("language", data["language"] ?: "") - put("audioSource", data["audioSource"] ?: "") - }) - } - } - else -> { - // 其他事件,正常处理 - JSONObject().apply { - put("event", eventName) - put("data", JSONObject(data)) - } - } - } + // 将事件转换为JSON字符串 + val eventData = JSONObject().apply { + put("event", eventName) + put("data", JSONObject(data)) + } - // 使用协程在主线程发送事件 - MainScope().launch { - try { - eventSink?.success(eventData.toString()) - } catch (e: Exception) { - Log.e(TAG, "发送事件到Flutter失败: $eventName", e) - } - } - } catch (e: Exception) { - Log.e(TAG, "处理事件异常: $eventName", e) + // 在主线程发送事件到Flutter + MainScope().launch { + eventSink?.success(eventData.toString()) } } diff --git a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift index 6854e8974..684616bc8 100644 --- a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift +++ b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift @@ -104,34 +104,13 @@ class AgentServiceImpl: NSObject { listenersLock.unlock() } - // 事件发送优化:使用队列避免主线程阻塞 - private let eventQueue = DispatchQueue(label: "com.yunqiinnovation.agent_service.events", qos: .userInitiated) - internal func sendEvent(name eventName: String, data: [String: Any]) { - // 对于高频事件,使用异步处理 - if eventName == "recognizing" { - eventQueue.async { [weak self] in - self?.dispatchEventToListeners(eventName: eventName, data: data) - } - } else { - // 其他事件在主线程处理 - DispatchQueue.main.async { [weak self] in - self?.dispatchEventToListeners(eventName: eventName, data: data) - } - } - } - - private func dispatchEventToListeners(eventName: String, data: [String: Any]) { listenersLock.lock() let currentListeners = self.listeners listenersLock.unlock() - + for listener in currentListeners { - do { - listener.onEvent(eventName: eventName, data: data) - } catch { - os_log("发送事件失败: %{public}@", log: logger, type: .error, eventName) - } + listener.onEvent(eventName: eventName, data: data) } } @@ -296,39 +275,25 @@ class AgentServiceImpl: NSObject { private func startIdleCheck() { stopIdleCheck() - + guard isRecognizing else { return } - - // BLE模式下使用稍长的超时时间 - let timeoutSeconds: TimeInterval - if let azureHelper = azureAsrHelper, - azureHelper.audioSourceType == .external { - timeoutSeconds = maxIdleSeconds + 3 // BLE模式额外增加3秒 - } else { - timeoutSeconds = maxIdleSeconds - } - - os_log("启动空闲检测,超时时间: %.0f秒", log: logger, type: .debug, timeoutSeconds) - + // 使用全局队列而不是主队列,避免后台挂起问题 let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global(qos: .utility)) idleTimer = timer - - timer.schedule(deadline: .now() + timeoutSeconds) + + timer.schedule(deadline: .now() + maxIdleSeconds) timer.setEventHandler { [weak self] in // 使用更安全的方式检查self是否存在 DispatchQueue.main.async { [weak self] in guard let self = self else { return } - + if self.isRecognizing && !self.hasSpeechDetected && !self.isSpeaking && !self.isAiStreaming { - os_log("空闲超时,停止识别 - 超时时间: %.0f秒", log: self.logger, type: .info, timeoutSeconds) self.stopRecognition() self.sendEvent(name: "auto_stop", data: [ - "reason": "idle_timeout", - "seconds": timeoutSeconds + "reason": "idle_timeout", + "seconds": self.maxIdleSeconds ]) - } else { - os_log("空闲检测结束,但条件不满足停止要求", log: self.logger, type: .debug) } } } @@ -350,49 +315,34 @@ class AgentServiceImpl: NSObject { sendError("服务未初始化", code: "NOT_INITIALIZED") return false } - - os_log("开始语音识别 - 模式: %{public}@", log: logger, type: .info, useBle ? "BLE外部音频" : "设备麦克风") - + if !useBle { guard AVAudioSession.sharedInstance().recordPermission == .granted else { sendError("无麦克风权限", code: "PERMISSION_DENIED") return false } } - + if isRecognizing { stopRecognition() } - + let audioSourceType: AzureAsrHelper.AudioSourceType = useBle ? .external : .microphone - + if useBle { AudioSessionHub.shared.begin(.playback) } else { AudioSessionHub.shared.begin(.voice) } - - os_log("音频源类型设置为: %{public}@", log: logger, type: .info, audioSourceType == .external ? "EXTERNAL" : "MICROPHONE") - +print("ai启动语音=\(audioSourceType)=\(useBle)") guard let success = azureAsrHelper?.startContinuousRecognition( + // callback: self, audioSourceType: audioSourceType ), success else { sendError("启动语音识别失败", code: "RECOGNITION_START_ERROR") return false } - - // 如果是BLE模式,在ASR启动后再打开编码器 - if useBle { - os_log("BLE模式:启动编码器", log: logger, type: .info) - DispatchQueue.global().asyncAfter(deadline: .now() + 0.05) { - let encoderOpened = BleService.shared.openEncoder() - if !encoderOpened { - os_log("BLE编码器启动失败,但继续尝试识别", log: self.logger, type: .warning) - } - } - } - - os_log("语音识别启动成功", log: logger, type: .info) + return true } func recognizeCallback(useBle: Bool = false) -> Bool { @@ -413,15 +363,13 @@ class AgentServiceImpl: NSObject { } func pushAudioData(_ audioData: Data) -> Bool { - // 添加调试日志,但保持兼容性 if !isRecognizing || !isInitialized { - os_log("语音识别未激活或服务未初始化,但仍尝试推送音频数据: %d bytes", log: logger, type: .debug, audioData.count) - } - - guard let audioStream = azureAsrHelper?.audioStream else { - os_log("音频流未初始化", log: logger, type: .error) return false } + guard let audioStream = azureAsrHelper?.audioStream else { + os_log("音频流未初始化", log: logger,type: .error) + return false +} audioStream.saveAudioDataTo(data: audioData) return true @@ -1450,8 +1398,6 @@ extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback { } func onCanceled(_ reason: String, _ errorDetails: String) { - os_log("语音识别被取消: %{public}@, 详情: %{public}@", log: logger, type: .warning, reason, errorDetails) - var data: [String: Any] = [:] if !reason.isEmpty { data["reason"] = reason @@ -1459,27 +1405,13 @@ extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback { if !errorDetails.isEmpty { data["details"] = errorDetails } - - // 如果是BLE模式,关闭编码器 - if let azureHelper = azureAsrHelper, - azureHelper.audioSourceType == .external { - BleService.shared.closeCodec() - } - + sendEvent(name: "recognition_canceled", data: data) isRecognizing = false stopIdleCheck() } - + func onError(_ error: String) { - os_log("语音识别错误: %{public}@", log: logger, type: .error, error) - - // 如果是BLE模式,关闭编码器 - if let azureHelper = azureAsrHelper, - azureHelper.audioSourceType == .external { - BleService.shared.closeCodec() - } - let data: [String: Any] = ["message": error.isEmpty ? "未知错误" : error] sendEvent(name: "error", data: data) isRecognizing = false diff --git a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt index d5b8dae6c..02d909d4a 100644 --- a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt +++ b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt @@ -146,17 +146,6 @@ class AzureAsrHelper(private val context: Context) { // 设置分段策略为时间模式 setProperty("Speech_SegmentationStrategy", "Time") - - // 针对EXTERNAL音频源的实时性优化 - if (audioSourceType == AudioSourceType.EXTERNAL) { - // 减少缓冲延迟 - setProperty("SpeechServiceConnection_ReceiveTimeout", "1000") - setProperty("SpeechServiceConnection_SendTimeout", "1000") - // 启用实时模式 - setProperty("SpeechServiceConnection_EnableRealTimeRecognition", "true") - // 减少音频缓冲 - setProperty("SpeechServiceConnection_AudioBufferSizeMs", "100") - } } // 录音文件类 recordfile = RecordFile; @@ -717,9 +706,7 @@ class AzureAsrHelper(private val context: Context) { // 情况2:不在录制但队列有数据 -> 从队列获取 else if (writeQueue.isNotEmpty()) { data = writeQueue.poll() - if (data != null) { - Log.v("tag", "从队列写入数据: ${data.size} bytes, 队列剩余: ${writeQueue.size}") - } + Log.d("tag", "写入数据: ${data?.size}") bytesToWrite = data?.size ?: 0 } @@ -737,8 +724,7 @@ class AzureAsrHelper(private val context: Context) { Log.e("tag", "写入失败: ${e.message}") } } else { - // 使用更短的等待时间,提高响应性 - Thread.sleep(1) // 1ms等待,避免空转但保持响应性 + Thread.yield() // 避免空转消耗CPU } } } catch (e: Exception) { @@ -758,22 +744,11 @@ class AzureAsrHelper(private val context: Context) { * 外部音频输入 */ fun saveAudioDataTo(buffer: ByteArray) { - if (audioSourceType == AudioSourceType.MICROPHONE) return - - // 检查队列大小,避免积压 - if (writeQueue.size > 50) { // 限制队列大小 - Log.w("AudioStream", "音频队列积压严重(${writeQueue.size}),清理旧数据") - // 清理一半的旧数据,保持实时性 - repeat(writeQueue.size / 2) { - writeQueue.poll() - } - } + if (audioSourceType == AudioSourceType.MICROPHONE) return // 放入队列,由写线程写入 - val offered = writeQueue.offer(buffer.copyOf()) - if (!offered) { - Log.w("AudioStream", "音频队列已满,丢弃数据包: ${buffer.size} bytes") - } + writeQueue.offer(buffer.copyOf()) + } /** diff --git a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift index d8ee80e97..3106c1236 100644 --- a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift +++ b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift @@ -690,27 +690,22 @@ public class AzureAsrHelper: NSObject { guard let self = self else { return } while self.isRunning { + // print("是否写入: \(self.isWriting)") if !self.isWriting { - usleep(1_000) // 减少等待时间,提高响应性 + usleep(10_000) continue } + + guard let dataToWrite = self.writeQueue.take() else { continue } + + // print("写入数据长度: \(dataToWrite.count)") + do { + try self.pushAudioStream?.write(dataToWrite) + self.onAudioData?(dataToWrite) +} catch { + print("推送音频数据失败: \(error.localizedDescription)") +} - guard let dataToWrite = self.writeQueue.take() else { - usleep(1_000) // 短暂等待,避免空转 - continue - } - - // 添加队列状态日志 - if self.writeQueue.count > 10 { - print("音频写入队列积压: \(self.writeQueue.count)") - } - - do { - try self.pushAudioStream?.write(dataToWrite) - self.onAudioData?(dataToWrite) - } catch { - print("推送音频数据失败: \(error.localizedDescription)") - } } } } @@ -725,16 +720,7 @@ public class AzureAsrHelper: NSObject { if audioSourceType != .external { return } - - // 检查队列大小,避免积压 - if writeQueue.count > 50 { - os_log("音频队列积压严重(%d),清理旧数据", log: OSLog.default, type: .warning, writeQueue.count) - // 清理一半的旧数据,保持实时性 - for _ in 0..<(writeQueue.count / 2) { - _ = writeQueue.take() - } - } - + // print("外部data=\(data)") // 放入队列,由写线程写入 writeQueue.put(data) }