Browse Source

feat(azure_speech): 添加iOS项目基础文件和音频接收器HFP支持

修复音频会话配置逻辑,优化蓝牙耳机兼容性
添加iOS Runner项目文件和测试用例
更新音频接收器以支持HFP模式
newdev_shunjiawei
liwei1dao 9 months ago
parent
commit
7ee76a9248
  1. 81
      local_plugins/azure_speech/ios/azure_speech/Sources/tools/SimpleAudioReceiver.swift
  2. 2
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCommandSender.kt
  3. 2
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt

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

@ -55,6 +55,7 @@ public class SimpleAudioReceiver: NSObject {
//public var onAudioData: ((Data) -> Void)? //public var onAudioData: ((Data) -> Void)?
public var recordfile: RecordFile? public var recordfile: RecordFile?
public var isHeadphones = true public var isHeadphones = true
public var isReceiverHFP = false // 是否是接收HFP音频
/** /**
* 初始化 * 初始化
@ -119,6 +120,7 @@ public class SimpleAudioReceiver: NSObject {
print("AudioStream音频配置设置完成") print("AudioStream音频配置设置完成")
} }
/** /**
* 开始音频输入 * 开始音频输入
*/ */
@ -129,9 +131,11 @@ public class SimpleAudioReceiver: NSObject {
print("startAudioRecord=audioSourceType\(audioSourceType)") print("startAudioRecord=audioSourceType\(audioSourceType)")
switch audioSourceType { switch audioSourceType {
case .microphone: case .microphone:
runMicrophoneCapture() isReceiverHFP = true
runMicrophoneCapture()
case .external: case .external:
runExternalCapture() isReceiverHFP = false
runExternalCapture()
} }
} }
@ -225,20 +229,14 @@ public class SimpleAudioReceiver: NSObject {
*/ */
private func runExternalCapture() { private func runExternalCapture() {
if audioSourceType == .external { if audioSourceType == .external {
// do { do {
// try audioSession.setCategory(.playback, // 根据通话状态进行安全的音频会话配置
// mode: .videoChat, try safeConfigureAudioSessionForMic()
// options: [ .mixWithOthers,.allowBluetoothA2DP // 允许蓝牙耳机,不占用hfp链路 } catch {
// ]) // 添加音频优先级控制 print("设置音频会话失败: \(error.localizedDescription)")
// try audioSession.overrideOutputAudioPort(.none) }
// try audioSession.setActive(true)
// } catch {
// print("设置音频会话失败: \(error.localizedDescription)")
// }
//pushAudioData(data: Data()) //pushAudioData(data: Data())
micCapture.stopCapture() micCapture.stopCapture()
} }
} }
/** /**
@ -254,25 +252,46 @@ public class SimpleAudioReceiver: NSObject {
let desiredCategory: AVAudioSession.Category let desiredCategory: AVAudioSession.Category
let desiredMode: AVAudioSession.Mode let desiredMode: AVAudioSession.Mode
let desiredOptions: AVAudioSession.CategoryOptions let desiredOptions: AVAudioSession.CategoryOptions
if isReceiverHFP {
if isTelephonyActive() { if isTelephonyActive() {
// 通话中:录音优先,避免 A2DP(仅播放),改用 HFP(.allowBluetooth) // 通话中:录音优先,避免 A2DP(仅播放),改用 HFP(.allowBluetooth)
desiredCategory = .record desiredCategory = .record
desiredMode = .voiceChat desiredMode = .voiceChat
desiredOptions = [.allowBluetooth] desiredOptions = [.allowBluetooth]
} else { } else {
if(isHeadphones){ if(isHeadphones){
// 非通话:保持原有逻辑,但使用 .allowBluetooth 以支持录音(而不是 A2DP) // 非通话:保持原有逻辑,但使用 .allowBluetooth 以支持录音(而不是 A2DP)
desiredCategory = .playAndRecord desiredCategory = .playAndRecord
desiredMode = .videoChat desiredMode = .videoChat
desiredOptions = [.allowBluetooth, .mixWithOthers, .defaultToSpeaker] desiredOptions = [.allowBluetooth, .mixWithOthers, .defaultToSpeaker]
//desiredOptions = [.mixWithOthers, .defaultToSpeaker] //desiredOptions = [.mixWithOthers, .defaultToSpeaker]
}else{ }else{
desiredCategory = .playAndRecord desiredCategory = .playAndRecord
desiredMode = .videoChat desiredMode = .videoChat
desiredOptions = [.mixWithOthers, .defaultToSpeaker] desiredOptions = [.mixWithOthers, .defaultToSpeaker]
}
}
}else{
if isTelephonyActive() {
// 通话中:录音优先,避免 A2DP(仅播放),改用 HFP(.allowBluetooth)
desiredCategory = .record
desiredMode = .voiceChat
desiredOptions = [.allowBluetoothA2DP]
} else {
if(isHeadphones){
// 非通话:保持原有逻辑,但使用 .allowBluetooth 以支持录音(而不是 A2DP)
desiredCategory = .playAndRecord
desiredMode = .videoChat
desiredOptions = [.allowBluetoothA2DP, .mixWithOthers, .defaultToSpeaker]
//desiredOptions = [.mixWithOthers, .defaultToSpeaker]
}else{
desiredCategory = .playAndRecord
desiredMode = .videoChat
desiredOptions = [.mixWithOthers, .defaultToSpeaker]
}
} }
} }
// 仅在类别或模式变化时才调用 setCategory,避免触发不必要的 categoryChange // 仅在类别或模式变化时才调用 setCategory,避免触发不必要的 categoryChange
let needsSetCategory = (audioSession.category != desiredCategory) || (audioSession.mode != desiredMode) let needsSetCategory = (audioSession.category != desiredCategory) || (audioSession.mode != desiredMode)

2
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCommandSender.kt

@ -456,7 +456,7 @@ class BleCommandSender {
fun processDeviceNotification(data: ByteArray) { fun processDeviceNotification(data: ByteArray) {
Log.d( Log.d(
TAG, TAG,
"收到设备主动上报: ${data.joinToString(", ") { "0x${(it.toInt() and 0xFF).toString(16)}" }}" "ble指令----------- 收到设备主动上报: ${data.joinToString(", ") { "0x${(it.toInt() and 0xFF).toString(16)}" }}"
) )
if (data.size < 3) { if (data.size < 3) {
Log.e(TAG, "设备主动上报数据格式错误:数据长度过短") Log.e(TAG, "设备主动上报数据格式错误:数据长度过短")

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

@ -973,7 +973,7 @@ object BleService {
} }
// 通知特征数据(命令和控制) // 通知特征数据(命令和控制)
BleConst.NOTIFY_CHAR_UUID -> { BleConst.NOTIFY_CHAR_UUID -> {
Log.i(TAG, "收到通知特征数据(命令和控制)") Log.i(TAG, "ble指令----------- 收到通知特征数据(命令和控制)")
// 判断数据类型 // 判断数据类型
when { when {
// 设备响应 (0xBB开头) // 设备响应 (0xBB开头)

Loading…
Cancel
Save