Browse Source

加入录音音波动画

newdev_shunjiawei
fdp 1 year ago
parent
commit
1d91aaaabb
  1. 3
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/SimpleAudioReceiver.kt
  2. 376
      local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift
  3. 6
      local_plugins/azure_speech/ios/azure_speech/Sources/tools/SimpleAudioReceiver.swift

3
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/SimpleAudioReceiver.kt

@ -145,8 +145,9 @@ class SimpleAudioReceiver(private val context: Context) {
} }
pushAudioStream?.write(finalData) pushAudioStream?.write(finalData)
if(recordfile!=null){
recordfile?.saveAudioDataToWav(finalData) recordfile?.saveAudioDataToWav(finalData)
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "写入失败: ${e.message}") Log.e(TAG, "写入失败: ${e.message}")

376
local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift

@ -20,16 +20,40 @@ import ble_service
// 日志对象 // 日志对象
private let log = OSLog(subsystem: "com.azure.speech", category: "AzureSpeechPlugin") private let log = OSLog(subsystem: "com.azure.speech", category: "AzureSpeechPlugin")
// 替换原有的 StreamHandler 类
private typealias AsrEventStreamHandler = BaseEventStreamHandler
private typealias TtsEventStreamHandler = BaseEventStreamHandler
private typealias AudioDataEventStreamHandler = BaseEventStreamHandler
private typealias AstEventStreamHandler = BaseEventStreamHandler
// 事件类型枚举
public enum EventType {
case asr
case tts
case audioData
case ast
}
/**
* 通道配置结构体
*/
private struct ChannelConfig {
let methodChannelName: String
let eventChannelName: String
let eventType: EventType
let methodHandler: (FlutterMethodCall, @escaping FlutterResult) -> Void
}
// ASR相关 // ASR相关
private var asrChannel: FlutterMethodChannel? private var asrChannel: FlutterMethodChannel?
private var asrEventChannel: FlutterEventChannel? private var asrEventChannel: FlutterEventChannel?
private var asrEventSink: FlutterEventSink? internal var asrEventSink: FlutterEventSink?
private let azureAsrHelper = AzureAsrHelper() private let azureAsrHelper = AzureAsrHelper()
// TTS相关 // TTS相关
private var ttsChannel: FlutterMethodChannel? private var ttsChannel: FlutterMethodChannel?
private var ttsEventChannel: FlutterEventChannel? private var ttsEventChannel: FlutterEventChannel?
private var ttsEventSink: FlutterEventSink? internal var ttsEventSink: FlutterEventSink?
private let azureTtsHelper = AzureTtsHelper() private let azureTtsHelper = AzureTtsHelper()
// 是否已添加TTS事件监听器 // 是否已添加TTS事件监听器
@ -37,98 +61,152 @@ import ble_service
// AST相关 // AST相关
private var astChannel: FlutterMethodChannel? private var astChannel: FlutterMethodChannel?
private var astEventChannel: FlutterEventChannel? private var astEventChannel: FlutterEventChannel?
internal var astEventSink: FlutterEventSink?
private let azureAstHelper = IntegratedSpeechTranslationService() private let azureAstHelper = IntegratedSpeechTranslationService()
// 音频数据相关
private var audioDataEventChannel: FlutterEventChannel?
internal var audioDataEventSink: FlutterEventSink?
// 创建事件回调 // 创建事件回调
private var astEventCallback: AstEventCallback? private var astEventCallback: AstEventCallback?
// 是否已添加AST事件监听器 // 是否已添加AST事件监听器
private var isAstListenerAdded = false private var isAstListenerAdded = false
// 当前的连续识别回调 // 当前的连续识别回调
private var currentAsrCallback: AsrCallbackWrapper? private var currentAsrCallback: AsrCallbackWrapper?
// 插件注册 // 添加音频数据回调实例作为类属性
public static func register(with registrar: FlutterPluginRegistrar) { private lazy var audioDataCallback: AudioDataCallbackImpl = {
let instance = AzureSpeechPlugin() return AudioDataCallbackImpl { [weak self] data in
// 2. 初始化BleService并注册回调 self?.handleAudioData(data)
BleService.shared.addCallback(instance)
// 初始化ASR通道
let asrChannel = FlutterMethodChannel(name: "azure_speech/asr", binaryMessenger: registrar.messenger())
registrar.addMethodCallDelegate(instance, channel: asrChannel)
instance.asrChannel = asrChannel
// 设置ASR通道处理器 - 使用实例方法
asrChannel.setMethodCallHandler { [weak instance] (call, result) in
instance?.handleAsrMethodCall(call, result: result)
} }
}()
// 初始化TTS通道
let ttsChannel = FlutterMethodChannel(name: "azure_speech/tts", binaryMessenger: registrar.messenger())
registrar.addMethodCallDelegate(instance, channel: ttsChannel) /**
instance.ttsChannel = ttsChannel * 初始化通道的通用方法
* @param registrar Flutter插件注册器
// 设置TTS通道处理器 - 使用实例方法 * @param config 通道配置
ttsChannel.setMethodCallHandler { [weak instance] (call, result) in */
instance?.handleTtsMethodCall(call, result: result) private func setupChannel(registrar: FlutterPluginRegistrar, config: ChannelConfig) {
// 初始化方法通道
let methodChannel = FlutterMethodChannel(name: config.methodChannelName, binaryMessenger: registrar.messenger())
methodChannel.setMethodCallHandler { [weak self] (call, result) in
config.methodHandler(call, result)
} }
// 初始化事件通道
let eventChannel = FlutterEventChannel(name: config.eventChannelName, binaryMessenger: registrar.messenger())
eventChannel.setStreamHandler(BaseEventStreamHandler(instance: self, eventType: config.eventType))
// 初始化AST通道 // 保存通道引用
let astChannel = FlutterMethodChannel(name: "azure_speech/ast", binaryMessenger: registrar.messenger()) switch config.eventType {
registrar.addMethodCallDelegate(instance, channel: astChannel) case .asr:
instance.astChannel = astChannel self.asrChannel = methodChannel
self.asrEventChannel = eventChannel
// 设置AST通道处理器 - 使用实例方法 case .tts:
astChannel.setMethodCallHandler { [weak instance] (call, result) in self.ttsChannel = methodChannel
instance?.handleAstMethodCall(call, result: result) self.ttsEventChannel = eventChannel
case .audioData:
self.audioDataEventChannel = eventChannel
case .ast:
self.astChannel = methodChannel
self.astEventChannel = eventChannel
} }
// 初始化ASR事件通道
let asrEventChannel = FlutterEventChannel(name: "azure_speech/asr_events", binaryMessenger: registrar.messenger())
asrEventChannel.setStreamHandler(instance)
instance.asrEventChannel = asrEventChannel
// 初始化TTS事件通道
let ttsEventChannel = FlutterEventChannel(name: "azure_speech/tts_events", binaryMessenger: registrar.messenger())
ttsEventChannel.setStreamHandler(instance)
instance.ttsEventChannel = ttsEventChannel
// 初始化TTS事件通道
let astEventChannel = FlutterEventChannel(name: "azure_speech/asr_events", binaryMessenger: registrar.messenger())
astEventChannel.setStreamHandler(instance)
instance.astEventChannel = astEventChannel
} }
// 发送ASR事件方法 /**
internal func sendAsrEvent(_ event: [String: Any]) { * 统一的事件发送方法
if asrEventSink == nil { * @param event 事件数据
os_log("无法发送ASR事件:事件通道未准备好", log: log, type: .error) * @param type 事件类型
return */
} private func sendEvent(_ event: [String: Any], type: EventType) {
let eventSink: FlutterEventSink?
DispatchQueue.main.async { [weak self] in let logMessage: String
guard let self = self else { return }
self.asrEventSink?(event) switch type {
} case .asr:
eventSink = asrEventSink
logMessage = "无法发送ASR事件:事件通道未准备好"
case .tts:
eventSink = ttsEventSink
logMessage = "无法发送TTS事件:事件通道未准备好"
case .audioData:
eventSink = audioDataEventSink
logMessage = "无法发送音频数据事件:事件通道未准备好"
case .ast:
eventSink = astEventSink
logMessage = "无法发送AST事件:事件通道未准备好"
} }
// 发送TTS事件方法 guard eventSink != nil else {
private func sendTtsEvent(_ event: [String: Any]) { os_log("%@", log: log, type: .error, logMessage)
if ttsEventSink == nil { return
os_log("无法发送TTS事件:事件通道未准备好", log: log, type: .error) }
return
} DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
eventSink?(event)
}
}
// 插件注册
public static func register(with registrar: FlutterPluginRegistrar) {
let instance = AzureSpeechPlugin()
BleService.shared.addCallback(instance)
DispatchQueue.main.async { [weak self] in // 配置所有通道
guard let self = self else { return } let channelConfigs = [
self.ttsEventSink?(event) ChannelConfig(
methodChannelName: "azure_speech/asr",
eventChannelName: "azure_speech/asr_events",
eventType: .asr,
methodHandler: instance.handleAsrMethodCall
),
ChannelConfig(
methodChannelName: "azure_speech/tts",
eventChannelName: "azure_speech/tts_events",
eventType: .tts,
methodHandler: instance.handleTtsMethodCall
),
ChannelConfig(
methodChannelName: "azure_speech/ast",
eventChannelName: "azure_speech/ast_events",
eventType: .ast,
methodHandler: instance.handleAstMethodCall
)
]
// 批量初始化通道
for config in channelConfigs {
instance.setupChannel(registrar: registrar, config: config)
} }
// 单独处理音频数据事件通道(只有事件通道)
let audioDataEventChannel = FlutterEventChannel(name: "azure_speech/audio_data_events", binaryMessenger: registrar.messenger())
audioDataEventChannel.setStreamHandler(BaseEventStreamHandler(instance: instance, eventType: .audioData))
instance.audioDataEventChannel = audioDataEventChannel
} }
// 简化的事件发送方法
internal func sendAsrEvent(_ event: [String: Any]) {
sendEvent(event, type: .asr)
}
private func sendTtsEvent(_ event: [String: Any]) {
sendEvent(event, type: .tts)
}
private func sendAudioDataEvent(_ event: [String: Any]) {
sendEvent(event, type: .audioData)
}
private func sendAstEvent(_ event: [String: Any]) {
sendEvent(event, type: .ast)
}
// 设置TTS事件监听器 // 设置TTS事件监听器
private func setupTtsEventListener() { internal func setupTtsEventListener() {
if !isTtsListenerAdded { if !isTtsListenerAdded {
azureTtsHelper.addListener(self) azureTtsHelper.addListener(self)
isTtsListenerAdded = true isTtsListenerAdded = true
@ -136,18 +214,25 @@ import ble_service
} }
// // 处理Flutter方法调用 /**
// public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { * 处理音频数据
// switch call.method { * @param data 音频数据
// case "azure_speech/asr": */
// handleAsrMethodCall(call, result) private func handleAudioData(_ data: Data) {
// case "azure_speech/tts": guard audioDataEventSink != nil else {
// handleTtsMethodCall(call, result) print("音频数据事件通道未准备好")
// default: return
// // 处理未识别的通道 }
// result(FlutterMethodNotImplemented)
// } let eventMap: [String: Any] = [
// } "type": "audioData",
"data": FlutterStandardTypedData(bytes: data),
"timestamp": Int(Date().timeIntervalSince1970 * 1000),
"size": data.count
]
print("音频数据大小: \(data.count)")
sendAudioDataEvent(eventMap)
}
// MARK: - ASR 方法处理 // MARK: - ASR 方法处理
@ -299,19 +384,8 @@ import ble_service
do { do {
if acceptAudioData { if acceptAudioData {
// 创建音频数据回调实现类 // 使用复用的音频数据回调实例
let audioCallback = AudioDataCallbackImpl { [weak self] audioData in try azureAsrHelper.enableRecord(filePath: filePath, audioDataCallback: audioDataCallback)
// 构建音频数据事件映射
let audioEvent: [String: Any] = [
"type": "audioData",
"data": audioData,
"timestamp": Date().timeIntervalSince1970 * 1000,
"size": audioData.count
]
// 发送音频数据事件到 Flutter 层
self?.sendAsrEvent(audioEvent)
}
try azureAsrHelper.enableRecord(filePath: filePath, audioDataCallback: audioCallback)
} else { } else {
try azureAsrHelper.enableRecord(filePath: filePath) try azureAsrHelper.enableRecord(filePath: filePath)
} }
@ -325,9 +399,10 @@ import ble_service
case "pauseRecord": case "pauseRecord":
azureAsrHelper.pauseRecord() azureAsrHelper.pauseRecord()
result(true) result(true)
// case "resumeRecord":
// azureAsrHelper.resumeRecord() case "resumeRecord":
// result(true) azureAsrHelper.resumeRecord()
result(true)
case "stopRecord": case "stopRecord":
guard let args = call.arguments as? [String: Any], guard let args = call.arguments as? [String: Any],
@ -851,57 +926,60 @@ extension AzureSpeechPlugin: BleService.Callback {
public func onDeviceInfoReceived(infoType: Int, infoData: [String: Any]) { public func onDeviceInfoReceived(infoType: Int, infoData: [String: Any]) {
} }
} }
// MARK: - 事件处理 /**
extension AzureSpeechPlugin: FlutterStreamHandler { * 通用事件流处理器基类
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { */
// 根据通道类型设置事件接收器 private class BaseEventStreamHandler: NSObject, FlutterStreamHandler {
if let args = arguments as? [String: Any], private weak var instance: AzureSpeechPlugin?
let channel = args["channel"] as? String { private let eventType: AzureSpeechPlugin.EventType
if channel == "asr" { /**
asrEventSink = events * 初始化事件流处理器
} else if channel == "tts" { * @param instance AzureSpeechPlugin实例
ttsEventSink = events * @param eventType 事件类型
setupTtsEventListener() */
} init(instance: AzureSpeechPlugin, eventType: AzureSpeechPlugin.EventType) {
} else if arguments == nil { self.instance = instance
// 如果没有指定通道,尝试弄清楚是哪个通道在监听 self.eventType = eventType
if asrEventSink == nil && ttsEventSink != nil { super.init()
asrEventSink = events }
} else if asrEventSink != nil && ttsEventSink == nil {
ttsEventSink = events func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
setupTtsEventListener() switch eventType {
} else { case .asr:
// 无法确定哪个通道,默认设置为ASR instance?.asrEventSink = events
asrEventSink = events case .tts:
} instance?.ttsEventSink = events
instance?.setupTtsEventListener()
case .audioData:
instance?.audioDataEventSink = events
print("音频数据事件通道已准备好")
case .ast:
instance?.astEventSink = events
} }
return nil return nil
} }
public func onCancel(withArguments arguments: Any?) -> FlutterError? { func onCancel(withArguments arguments: Any?) -> FlutterError? {
// 根据通道类型清除事件接收器 switch eventType {
if let args = arguments as? [String: Any], case .asr:
let channel = args["channel"] as? String { instance?.asrEventSink = nil
case .tts:
if channel == "asr" { instance?.ttsEventSink = nil
asrEventSink = nil case .audioData:
} else if channel == "tts" { instance?.audioDataEventSink = nil
ttsEventSink = nil print("音频数据事件通道已取消")
} case .ast:
} else { instance?.astEventSink = nil
// 如果没有指定通道,清除所有通道
asrEventSink = nil
ttsEventSink = nil
} }
return nil return nil
} }
} }
// MARK: - TTS 事件监听实现 // MARK: - TTS 事件监听实现
extension AzureSpeechPlugin: TtsEventListener { extension AzureSpeechPlugin: TtsEventListener {
// 使用@objc特性为方法提供一个不同的Objective-C选择器名称 // 使用@objc特性为方法提供一个不同的Objective-C选择器名称
@ -942,19 +1020,6 @@ extension AzureSpeechPlugin: TtsEventListener {
} }
} }
// MARK: - 音频数据监听实现
extension AzureSpeechPlugin: AudioDataListener {
public func onAudioData(_ data: Data) {
if ttsEventSink == nil { return }
let eventMap: [String: Any] = [
"type": "audio_data",
"data": FlutterStandardTypedData(bytes: data)
]
sendTtsEvent(eventMap)
}
}
// MARK: - 音频数据回调实现 // MARK: - 音频数据回调实现
@ -978,6 +1043,7 @@ private class AudioDataCallbackImpl: SimpleAudioReceiver.AudioDataCallback {
* @param data 音频数据 * @param data 音频数据
*/ */
func onAudio(_ data: Data) { func onAudio(_ data: Data) {
print("音频数据回调方法: \(data.count)")
callback(data) callback(data)
} }
} }

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

@ -140,8 +140,10 @@ public class SimpleAudioReceiver: NSObject {
guard let dataToWrite = self.writeQueue.take() else { continue } guard let dataToWrite = self.writeQueue.take() else { continue }
do { do {
if let callback = self.audioDataCallback {
callback.onAudio(dataToWrite) if self.audioDataCallback != nil {
print("写入数据长度: \(dataToWrite.count)")
self.audioDataCallback!.onAudio(dataToWrite)
} }
try self.pushAudioStream?.write(dataToWrite) try self.pushAudioStream?.write(dataToWrite)
if self.recordfile != nil { if self.recordfile != nil {

Loading…
Cancel
Save