Browse Source

ble命令队列发送,加入录音功能

weicu
fdp 1 year ago
parent
commit
16076adb2f
  1. 129
      local_plugins/amap_service/ios/Classes/AmapServicePlugin.swift
  2. 11
      local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift
  3. 3
      local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/RecordFile.swift
  4. 40
      local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift

129
local_plugins/amap_service/ios/Classes/AmapServicePlugin.swift

@ -2,46 +2,103 @@ import Flutter
import UIKit
import AMapNaviKit
public class AmapServicePlugin: NSObject, FlutterPlugin {
private static var channel: FlutterMethodChannel?
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "com.yunqiinnovation.amap_service", binaryMessenger: registrar.messenger())
let instance = AmapServicePlugin()
// 必须先设置隐私合规
DispatchQueue.main.async {
// 2. API Key
AMapServices.shared()?.apiKey = "aa60a03be9f6531bcc64029e60f95d03"
AMapServices.shared().enableHTTPS = true
// 导航SDK隐私合规(如果使用导航功能)
AMapNaviManagerConfig.shared().updatePrivacyShow(.didShow, privacyInfo: .didContain)
AMapNaviManagerConfig.shared().updatePrivacyAgree(.didAgree)
// 3. 打印验证(调试用)
print("高德SDK初始化完成")
public class AmapServicePlugin: NSObject, FlutterPlugin, FlutterStreamHandler {
private static var channel: FlutterMethodChannel?
private var eventSink: FlutterEventSink?
public static func register(with registrar: FlutterPluginRegistrar) {
let methodChannel = FlutterMethodChannel(
name: "com.yunqiinnovation.amap_service",
binaryMessenger: registrar.messenger()
)
let eventChannel = FlutterEventChannel(
name: "com.yunqiinnovation.amap_service/events",
binaryMessenger: registrar.messenger()
)
let instance = AmapServicePlugin()
eventChannel.setStreamHandler(instance)
// 高德SDK初始化
DispatchQueue.main.async {
AMapServices.shared()?.apiKey = "aa60a03be9f6531bcc64029e60f95d03"
AMapServices.shared()?.enableHTTPS = true
if #available(iOS 13.0, *) {
AMapNaviManagerConfig.shared().updatePrivacyShow(.didShow, privacyInfo: .didContain)
AMapNaviManagerConfig.shared().updatePrivacyAgree(.didAgree)
}
print("高德SDK初始化完成")
}
registrar.addMethodCallDelegate(instance, channel: methodChannel)
self.channel = methodChannel
}
registrar.addMethodCallDelegate(instance, channel: channel)
self.channel = channel
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "open":
print("liwei------------------ 打开导航页面")
if let args = call.arguments as? [String: Any],
let startpos = args["startpos"] as? String,
let endpos = args["endpos"] as? String,
let ntype = args["ntype"] as? String{
print("liwei------------------ 打开导航页面1 \(startpos) \(endpos)")
DispatchQueue.main.async {
let navVC = AmapViewController(startpos: startpos, endpos: endpos)
// MARK: - FlutterStreamHandler
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
self.eventSink = events
return nil
}
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
eventSink = nil
return nil
}
// MARK: - 事件发送方法
private func sendEvent(eventName: String, data: [String: Any]) {
guard let sink = eventSink else { return }
let eventData: [String: Any] = [
"event": eventName,
"data": data
]
sink(eventData)
}
// MARK: - 方法处理
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "open":
guard let args = call.arguments as? [String: Any],
let startpos = args["startpos"] as? String,
let endpos = args["endpos"] as? String,
let ntype = args["ntype"] as? String else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "Missing arguments", details: nil))
return
}
print("打开导航页面: \(startpos) → \(endpos)")
DispatchQueue.main.async {
let navVC = AmapViewController(startpos: startpos, endpos: endpos)
navVC.modalPresentationStyle = .fullScreen
UIApplication.shared.keyWindow?.rootViewController?.present(navVC, animated: true)
}
result(nil)
} else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "Missing 'open' map", details: nil))
case "start":
guard let args = call.arguments as? [String: Any],
let startpos = args["startpos"] as? String,
let endpos = args["endpos"] as? String,
let ntype = args["ntype"] as? String else {
result(FlutterError(code: "INVALID_ARGUMENT", message: "Missing arguments", details: nil))
return
}
// 这里添加实际导航启动逻辑
print("开始导航: \(startpos) → \(endpos), 类型: \(ntype)")
sendEvent(eventName: "navigation_started", data: ["status": "success"])
result(nil)
case "stop":
// 这里添加停止导航逻辑
print("停止导航")
sendEvent(eventName: "navigation_stopped", data: ["status": "success"])
result(nil)
default:
result(FlutterMethodNotImplemented)
}
default:
result(FlutterMethodNotImplemented)
}
}
}
}

11
local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift

@ -232,7 +232,8 @@ public class AzureAsrHelper: NSObject {
// 停止音频处理
audioStream?.releaseAudioResources()
// 停止录音
recordfile?.closeFile(isSave: true)
// 释放资源
recognizer = nil
speechConfig = nil
@ -304,6 +305,9 @@ public class AzureAsrHelper: NSObject {
// 明确指定使用外部音频源(推流模式)
audioStream = AudioStream()
audioStream?.initAudioRecord()
audioStream?.onAudioData = { [weak self] data in
self?.recordfile?.saveAudioDataToWav(data)
}
}
// 创建音频配置
@ -508,7 +512,7 @@ private func detectLanguage(from result: SPXSpeechRecognitionResult) -> String {
private let bufferSize: Int = 4096
private var writeThread: DispatchQueue?
private var currentRoute: AudioOutputRoute?
public var onAudioData: ((Data) -> Void)?
/**
* 初始化
*/
@ -566,6 +570,7 @@ private func detectLanguage(from result: SPXSpeechRecognitionResult) -> String {
print("写入数据长度: \(dataToWrite.count)")
self.pushAudioStream?.write(dataToWrite)
self.onAudioData?(dataToWrite)
}
}
}
@ -739,7 +744,7 @@ private func detectLanguage(from result: SPXSpeechRecognitionResult) -> String {
try audioSession.setCategory(
.playAndRecord,
mode: .voiceChat,
options: [.allowBluetooth]
options: [.allowBluetooth, .mixWithOthers, .defaultToSpeaker]
)
try audioSession.overrideOutputAudioPort(.none)

3
local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/RecordFile.swift

@ -21,7 +21,7 @@ public class RecordFile {
let fileURL = URL(fileURLWithPath: path)
let directoryURL = fileURL.deletingLastPathComponent()
print("创建音频文件path: \(path)")
// 创建目录
do {
try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true)
@ -223,6 +223,7 @@ public class RecordFile {
try updateHandle.close()
lastSavedFile = fileURL
print("关闭文件path: \(fileURL)")
return true
} catch {
print("关闭文件失败: \(error.localizedDescription)")

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

@ -70,6 +70,19 @@ public class BleService: NSObject {
// MARK: - Opus音频处理器
private var opusProcessor: SwiftOpusAudioProcessor?
// MARK: - 新增命令队列相关属性
private var commandQueue = [Data]()
//是否回复
private var isReply = true
// 是否回复指令码
private var cmdReplyType: UInt8 = 0
//private var currentCommand: Command?
// 命令结构体
private struct Command {
let cmd: UInt8
let data: Data
}
// MARK: - 初始化
private override init() {
@ -650,8 +663,20 @@ public class BleService: NSObject {
os_log("发送命令: CMD=0x%02X, 长度=%d, 数据=[%@], CRC=0x%02X",
log: logger, type: .debug, cmd, cmdLength, dataHexString, crc)
// 加入队列并触发发送
if isReply{
peripheralDevice?.writeValue(cmdPacket, for: characteristic, type: .withoutResponse)
isReply = false;
return true
} else {
commandQueue.append(cmdPacket)
}
// 发送数据(使用无响应写入,与Android端保持一致)
peripheralDevice?.writeValue(cmdPacket, for: characteristic, type: .withoutResponse)
return true
} catch {
os_log("发送命令异常: %@", log: logger, type: .error, error.localizedDescription)
@ -691,6 +716,11 @@ public class BleService: NSObject {
/// 处理接收到的响应数据
private func handleResponseData(_ data: Data) {
guard let characteristic = writeCharacteristic,
connectionState == BleConst.STATE_CONNECTED else {
os_log(" 设备未连接", log: logger, type: .error)
return
}
guard data.count >= 4 else {
os_log("响应数据格式错误:数据长度过短", log: logger, type: .error)
return
@ -732,7 +762,13 @@ public class BleService: NSObject {
// 提取响应数据
let responseData = length > 0 ? data.subdata(in: 3..<(3 + Int(length))) : Data()
isReply = true;
cmdReplyType = cmd;
if !commandQueue.isEmpty {
let nextCmd = commandQueue.removeFirst()
peripheralDevice?.writeValue(nextCmd, for: characteristic, type: .withoutResponse)
}
// 根据命令类型处理设备响应
switch cmd {
case BleConst.CMD_GET_VERSION:

Loading…
Cancel
Save