Browse Source

优化ios唤醒

weicu
fdp 1 year ago
parent
commit
4b954fd97f
  1. 2
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  2. 53
      local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift
  3. 4
      local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServicePlugin.swift
  4. 54
      local_plugins/azure_speech/ios/azure_speech/Sources/tools/SimpleAudioReceiver.swift
  5. 8
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt
  6. 20
      local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift

2
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt

@ -598,7 +598,7 @@ object AgentService : CoroutineScope {
val success = azureAsrHelper?.setupEventListeners(object :
AzureAsrHelper.ContinuousRecognizeCallback {
override fun onRecognizing(recognizing: String, detectedLanguage: String) {
if (recognizing.isNotEmpty()) {
if (recognizing.isNotEmpty()&&isKeepResult==true) {
// 检测到语音,更新状态
_hasSpeechDetected.set(true)

53
local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift

@ -69,7 +69,9 @@ class AgentServiceImpl: NSObject {
// 用途:解决 start 异步、stop 先于 onSessionStarted 到来时被忽略的问题
private var isStartingRecognition: Bool = false
private var stopRequestedDuringStart: Bool = false
//是否保留最终结果
private var isKeepResult = true;
var currsessionId: String = ""
var language: String = ""
private var serverurl = ""
@ -354,6 +356,8 @@ class AgentServiceImpl: NSObject {
if self.isRecognizing && !self.isSpeaking && !self.isAiStreaming {
os_log("空闲检测触发自动停止,模式: %{public}@", log: self.logger, type: .info, self.currentRecognitionMode)
// 关闭编码器 (设备侧)
BleService.shared.closeCodec()
self.stopRecognition()
self.sendEvent(name: "auto_stop", data: [
"reason": "idle_timeout",
@ -377,6 +381,14 @@ class AgentServiceImpl: NSObject {
}
}
func startRecognition(useBle: Bool = false, mode: String = "normal") -> Bool {
// 接收到唤醒信号,打开编码器 (设备侧)
print("ai启动语音\(useBle))")
var openResult = false // 添加openResult变量定义
if useBle{
isKeepResult = false
// 捕获openEncoder的返回值
openResult = BleService.shared.openEncoder() // 正确捕获返回值
}
if !isInitialized {
sendError("服务未初始化", code: "NOT_INITIALIZED")
return false
@ -387,14 +399,11 @@ class AgentServiceImpl: NSObject {
sendError("无麦克风权限", code: "PERMISSION_DENIED")
return false
}
}
stopTts();
if isRecognizing {
stopRecognition()
}
// 接收到唤醒信号,打开编码器 (设备侧)
let openResult = BleService.shared.openEncoder()
}
stopRecognition()
stopTts()
os_log("🎙️ 尝试打开编码器结果: %@", log: logger, type: .info, openResult ? "成功" : "失败")
wasMusicPlayingBeforeRecognition = MusicService.shared.isPlaying
@ -417,7 +426,7 @@ class AgentServiceImpl: NSObject {
// 新增:标记为"启动中",用于允许 stop 在启动未完成时也能生效
isStartingRecognition = true
print("ai启动语音=\(audioSourceType)=\(useBle)")
guard let success = azureAsrHelper?.startContinuousRecognition(
// callback: self,
@ -428,7 +437,9 @@ class AgentServiceImpl: NSObject {
sendError("启动语音识别失败", code: "RECOGNITION_START_ERROR")
return false
}
if(useBle){
isKeepResult = true
}
// 根据模式启动相应的空闲检测(此处 guard isRecognizing,会在 onSessionStarted 中再次启动)
startIdleCheckForMode(mode: mode)
@ -450,6 +461,7 @@ class AgentServiceImpl: NSObject {
/// 特性:
/// 1) 即便处于"启动中"(isStartingRecognition)也会打印日志并尝试停止
/// 2) 如果底层尚未完全进入可停止状态,记录一次待停止请求,待会话启动后立即停止
print("停止识别语音,isRecognizing=\(isRecognizing),isStartingRecognition=\(isStartingRecognition)")
if !(isRecognizing || isStartingRecognition) {
// 既不在识别中,也不在启动中,视为幂等处理
return true
@ -473,8 +485,6 @@ class AgentServiceImpl: NSObject {
MusicService.shared.resume()
}
// 关闭编码器 (设备侧)
BleService.shared.closeCodec()
// 重置识别模式
currentRecognitionMode = "normal"
@ -492,7 +502,7 @@ class AgentServiceImpl: NSObject {
return false
}
guard let audioStream = azureAsrHelper?.audioStream else {
os_log("音频流未初始化", log: logger,type: .error)
os_log("AI音频流未初始化", log: logger,type: .error)
return false
}
audioStream.saveAudioDataTo(data: audioData)
@ -1055,6 +1065,8 @@ private func jsonToString(_ json: [String: Any]) -> String? {
// 如果正在识别,需要先停止
let wasRecognizing = isRecognizing
if wasRecognizing {
// 关闭编码器 (设备侧)
BleService.shared.closeCodec()
_ = stopRecognition()
}
@ -1229,6 +1241,8 @@ private func jsonToString(_ json: [String: Any]) -> String? {
func dispose() -> Bool {
print("ai释放资源")
if isRecognizing {
// 关闭编码器 (设备侧)
BleService.shared.closeCodec()
stopRecognition()
}
@ -1390,6 +1404,8 @@ class ChatApiStreamCallback: StreamCallback {
print("onFunctionCall=functionCall\(functionCall)")
if (name == "exit_interaction") {
// 关闭编码器 (设备侧)
BleService.shared.closeCodec()
agentService.stopRecognition()
}
@ -1515,9 +1531,6 @@ extension AgentServiceImpl: BleService.Callback {
func onWakeupSignalReceived() {
stopTts()
stopRecognition()
startRecognition(useBle: true, mode: "ble_wakeup")
}
@ -1717,7 +1730,7 @@ class AudioPlayer {
extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback {
func onResult(_ text: String, _ detectedLanguage: String) {
if !text.isEmpty {
if !text.isEmpty && isKeepResult {
var data: [String: Any] = ["text": text]
if !detectedLanguage.isEmpty {
data["language"] = detectedLanguage
@ -1736,7 +1749,7 @@ extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback {
}
func onRecognizing(_ recognizing: String, _ detectedLanguage: String) {
if !recognizing.isEmpty {
if !recognizing.isEmpty && isKeepResult {
let previousHasSpeech = hasSpeechDetected
hasSpeechDetected = true
@ -1772,6 +1785,8 @@ extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback {
// 新增:若在启动期间已经收到停止请求,则立即执行停止
if stopRequestedDuringStart {
stopRequestedDuringStart = false
// 关闭编码器 (设备侧)
BleService.shared.closeCodec()
_ = stopRecognition()
}
}

4
local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServicePlugin.swift

@ -2,7 +2,7 @@ import Flutter
import UIKit
import Foundation
import os.log
import ble_service
/// 代理服务Flutter插件,负责将Flutter调用桥接到原生实现
public class AgentServicePlugin: NSObject, FlutterPlugin {
@ -90,6 +90,8 @@ public class AgentServicePlugin: NSObject, FlutterPlugin {
return
}
print("stopConversation")
// 关闭编码器 (设备侧)
BleService.shared.closeCodec()
impl.stopRecognition()
result(true)

54
local_plugins/azure_speech/ios/azure_speech/Sources/tools/SimpleAudioReceiver.swift

@ -144,11 +144,11 @@ public class SimpleAudioReceiver: NSObject {
while self.isRunning {
if !self._isWriting {
// print("startWriteThread 111")
usleep(10_000)
continue
}
// print("startWriteThread 222")
guard let dataToWrite = self.writeQueue.take() else { continue }
do {
@ -157,14 +157,12 @@ public class SimpleAudioReceiver: NSObject {
self.audioDataCallback!.onAudio(dataToWrite)
}
if self.pushAudioStream != nil && isContinuousRecognitionActive {
try self.pushAudioStream?.write(dataToWrite)
}
if self.recordfile != nil && isRecord{
// print("写入recordfile数据长度: \(dataToWrite.count)")
recordfile?.saveAudioDataToWav(dataToWrite)
}
// print("写入数据长度: \(dataToWrite.count)")
} catch {
print("推送音频数据失败: \(error.localizedDescription)")
}
@ -183,9 +181,8 @@ public class SimpleAudioReceiver: NSObject {
if audioSourceType != .external {
return
}
// print("外部data=\(data)")
// 放入队列,由写线程写入
writeQueue.put(data)
self.writeQueue.put(data)
}
/**
@ -220,18 +217,22 @@ public class SimpleAudioReceiver: NSObject {
}
}
/**
* 运行外部音频捕获
* 修复OSStatus error -50问题
*/
private func runExternalCapture() {
if audioSourceType == .external {
do {
try audioSession.setCategory(.playback,
mode: .videoChat,
options: [ .mixWithOthers,.allowBluetoothA2DP // 允许蓝牙耳机,不占用hfp链路
]) // 添加音频优先级控制
try audioSession.overrideOutputAudioPort(.none)
try audioSession.setActive(true)
} catch {
print("设置音频会话失败: \(error.localizedDescription)")
}
// do {
// try audioSession.setCategory(.playback,
// mode: .videoChat,
// options: [ .mixWithOthers,.allowBluetoothA2DP // 允许蓝牙耳机,不占用hfp链路
// ]) // 添加音频优先级控制
// try audioSession.overrideOutputAudioPort(.none)
// try audioSession.setActive(true)
// } catch {
// print("设置音频会话失败: \(error.localizedDescription)")
// }
//pushAudioData(data: Data())
micCapture.stopCapture()
@ -336,7 +337,7 @@ public func restoreOriginalAudioState() {
public func stopMicrophoneCapture() {
self.writeQueue.clear()
_isWriting=false
micCapture.stopCapture()
print("stopMicrophoneCapture")
@ -517,6 +518,23 @@ private class LinkedBlockingQueue<T> {
// 通知有新元素可用
availableItems.signal()
}
/**
* 清空队列
*/
func clear() {
queueLock.lock()
defer { queueLock.unlock() }
// 检查队列是否已关闭
if isClosed {
return
}
// 添加元素(无容量限制,与Android一致)
queue.removeAll()
}
/**

8
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt

@ -202,13 +202,7 @@ object BleService {
Log.e(TAG, "OpusManager初始化失败: ${e.message}", e)
// 根据需要决定是否因为Opus初始化失败而返回false
}
recordfile = RecordingFile(this.context)
recordfile!!.fileName = "不拆分"
recordfile1 = RecordingFile(this.context)
recordfile1!!.fileName = "重新压缩"
isInitialized = true
startOpusStreamDecoding(false, 1, 16000, 40)
return true
} catch (e: Exception) {
Log.e(TAG, "BLE服务初始化失败: ${e.message}", e)
@ -1701,7 +1695,7 @@ startBytesStatistics()
recordfile1!!.creatingFiles()
recordfile!!.closeFile()
recordfile!!.creatingFiles()
// startOpusStreamDecoding(false, 1, 16000, 40)
startOpusStreamDecoding(false, 1, 16000, 40)
// // 目前仅发送命令通知设备开始编码。
// return sendCommand(
// BleConst.CMD_CONTROL_CODEC.toByte(),

20
local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift

@ -586,22 +586,24 @@ private var cmdReplyType: UInt8 = 0
}
/// 打开编码器
func openEncoder() -> Bool {
/// - Returns: 操作是否成功
public func openEncoder() -> Bool {
os_log("🎙️ 准备打开编码(设备侧)...", log: logger, type: .info)
// 确保Opus解码器处于活跃状态(如果不是则启动)
// if opusProcessor?.isDecoding() != true {
startOpusDecoding()
//}
let paramData = Data([BleConst.CODEC_CONTROL_ENCODE_ON, BleConst.AUDIO_CHANNEL_RIGHT])
let result = sendCommand(BleConst.CMD_CONTROL_CODEC, data: paramData)
// let paramData = Data([BleConst.CODEC_CONTROL_ENCODE_ON, BleConst.AUDIO_CHANNEL_RIGHT])
// let result = sendCommand(BleConst.CMD_CONTROL_CODEC, data: paramData)
if result {
os_log("✅ 编码器命令发送成功", log: logger, type: .info)
} else {
os_log("❌ 编码器命令发送失败", log: logger, type: .error)
}
// if result {
// os_log("✅ 编码器命令发送成功", log: logger, type: .info)
// } else {
// os_log("❌ 编码器命令发送失败", log: logger, type: .error)
// }
return result
//return result
return true
}
/// 打开解码器

Loading…
Cancel
Save