Browse Source

Merge branch 'new_dev' of github.com:deepcloud2048/deep_voice into new_dev

newdev_shunjiawei
lxm 8 months ago
parent
commit
8ec6fd30f9
  1. 19
      local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift
  2. 29
      local_plugins/floating_ui_plugin/ios/Classes/NativePlugin.swift
  3. 9
      local_plugins/music_service/ios/music_service/Sources/music_service/MusicService.swift
  4. 2
      pubspec.yaml

19
local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift

@ -1413,6 +1413,7 @@ private let audioSessionQueueKey = DispatchSpecificKey<UInt8>()
private var audioSessionQueueKeySet = false private var audioSessionQueueKeySet = false
private var audioInterrupted = false private var audioInterrupted = false
private var audioSessionObserversAdded = false private var audioSessionObserversAdded = false
private var shouldDeactivateAudioSessionWhenIdle = false
// 新增:路由变化的去抖与配置重入保护标记 // 新增:路由变化的去抖与配置重入保护标记
private var routeChangeDebounceWorkItem: DispatchWorkItem? private var routeChangeDebounceWorkItem: DispatchWorkItem?
private var isApplyingAudioSessionConfig = false private var isApplyingAudioSessionConfig = false
@ -1471,6 +1472,7 @@ private func applyAudioSessionForTTS() -> Bool {
let otherAudioPlaying = audioSession.isOtherAudioPlaying let otherAudioPlaying = audioSession.isOtherAudioPlaying
let isRecording = (self.micCapture?.isCapturing == true) let isRecording = (self.micCapture?.isCapturing == true)
self.isHeadphones = hasHeadphones self.isHeadphones = hasHeadphones
self.shouldDeactivateAudioSessionWhenIdle = otherAudioPlaying
let desiredCategory: AVAudioSession.Category let desiredCategory: AVAudioSession.Category
let desiredMode: AVAudioSession.Mode let desiredMode: AVAudioSession.Mode
@ -1490,7 +1492,7 @@ private func applyAudioSessionForTTS() -> Bool {
desiredOptions = [.allowBluetooth, .defaultToSpeaker] desiredOptions = [.allowBluetooth, .defaultToSpeaker]
} }
if otherAudioPlaying { if otherAudioPlaying {
desiredOptions.insert(.duckOthers) desiredOptions.insert(.mixWithOthers)
} }
} else if self.currentRecognitionMode == "push_to_talk" || self.currentRecognitionMode == "normal" || self.currentRecognitionMode == "ble_wakeup" { } else if self.currentRecognitionMode == "push_to_talk" || self.currentRecognitionMode == "normal" || self.currentRecognitionMode == "ble_wakeup" {
if isRecording { if isRecording {
@ -1500,7 +1502,7 @@ private func applyAudioSessionForTTS() -> Bool {
desiredCategory = .playback desiredCategory = .playback
desiredMode = .spokenAudio desiredMode = .spokenAudio
if otherAudioPlaying { if otherAudioPlaying {
desiredOptions.insert(.duckOthers) desiredOptions.insert(.mixWithOthers)
} }
} else if isRecording { } else if isRecording {
desiredCategory = .playAndRecord desiredCategory = .playAndRecord
@ -1515,13 +1517,13 @@ private func applyAudioSessionForTTS() -> Bool {
desiredOptions.insert(.defaultToSpeaker) desiredOptions.insert(.defaultToSpeaker)
} }
if otherAudioPlaying { if otherAudioPlaying {
desiredOptions.insert(.duckOthers) desiredOptions.insert(.mixWithOthers)
} }
} else { } else {
desiredCategory = .playback desiredCategory = .playback
desiredMode = .spokenAudio desiredMode = .spokenAudio
if otherAudioPlaying { if otherAudioPlaying {
desiredOptions.insert(.duckOthers) desiredOptions.insert(.mixWithOthers)
} }
} }
@ -1551,7 +1553,7 @@ private func applyAudioSessionForTTS() -> Bool {
try audioSession.setActive(true) try audioSession.setActive(true)
} catch { } catch {
do { do {
try audioSession.setCategory(.playback, mode: .default, options: []) try audioSession.setCategory(.playback, mode: .default, options: [.mixWithOthers])
try audioSession.setActive(true) try audioSession.setActive(true)
} catch { } catch {
success = false success = false
@ -1645,6 +1647,13 @@ private func deactivateAudioSessionAfterTTSIfIdle() {
self.audioEngine = nil self.audioEngine = nil
self.playerNode = nil self.playerNode = nil
self.pcmFormat = nil self.pcmFormat = nil
if self.shouldDeactivateAudioSessionWhenIdle {
do {
try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
} catch {
}
self.shouldDeactivateAudioSessionWhenIdle = false
}
} }
} }
if DispatchQueue.getSpecific(key: audioSessionQueueKey) != nil { if DispatchQueue.getSpecific(key: audioSessionQueueKey) != nil {

29
local_plugins/floating_ui_plugin/ios/Classes/NativePlugin.swift

@ -6,11 +6,19 @@ import UIKit
public class FloatingUiPlugin: NSObject, FlutterPlugin { public class FloatingUiPlugin: NSObject, FlutterPlugin {
private var pipContentViewArray: [PlayerView] = [] private var pipContentViewArray: [PlayerView] = []
/**
* 更新音频会话配置,保证 PiP 播放场景的音频行为符合预期
* - Parameters: 无
* - Returns: 无
* - Throws: 无(内部捕获并记录 setCategory/setActive 可能抛出的异常)
*/
private static func updateAudioSession() { private static func updateAudioSession() {
let audioSession = AVAudioSession.sharedInstance() let audioSession = AVAudioSession.sharedInstance()
do { do {
if #available(iOS 14.5, *) { if #available(iOS 14.5, *) {
try audioSession.setCategory(.playback, mode: .moviePlayback, options: [.overrideMutedMicrophoneInterruption]) try audioSession.setCategory(.playback, mode: .moviePlayback, options: [.overrideMutedMicrophoneInterruption, .mixWithOthers])
} else {
try audioSession.setCategory(.playback, mode: .moviePlayback, options: [.mixWithOthers])
} }
try audioSession.setActive(true) try audioSession.setActive(true)
} catch { } catch {
@ -18,10 +26,14 @@ public class FloatingUiPlugin: NSObject, FlutterPlugin {
} }
} }
/**
* Flutter 插件注册入口
* - Parameters:
* - registrar: FlutterPluginRegistrar 实例,用于注册通道与原生视图
* - Returns: 无
* - Throws: 无(注册流程不抛出异常)
*/
public static func register(with registrar: FlutterPluginRegistrar) { public static func register(with registrar: FlutterPluginRegistrar) {
// 保证 PiP 能正常工作
Self.updateAudioSession()
let channel = FlutterMethodChannel(name: "native_plugin", binaryMessenger: registrar.messenger()) let channel = FlutterMethodChannel(name: "native_plugin", binaryMessenger: registrar.messenger())
let instance = FloatingUiPlugin() let instance = FloatingUiPlugin()
registrar.addMethodCallDelegate(instance, channel: channel) registrar.addMethodCallDelegate(instance, channel: channel)
@ -30,12 +42,21 @@ public class FloatingUiPlugin: NSObject, FlutterPlugin {
registrar.register(factory, withId: "native_view") registrar.register(factory, withId: "native_view")
} }
/**
* Flutter MethodChannel 调用处理入口
* - Parameters:
* - call: Flutter 发起的方法调用(方法名与参数)
* - result: 用于向 Flutter 回传结果的回调
* - Returns: 无
* - Throws: 无(内部逻辑不抛出异常,异常通过 result 或日志处理)
*/
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method { switch call.method {
case "getPlatformVersion": case "getPlatformVersion":
result("iOS " + UIDevice.current.systemVersion) result("iOS " + UIDevice.current.systemVersion)
case "createPipContentView": case "createPipContentView":
Self.updateAudioSession()
let playerView = PlayerView() let playerView = PlayerView()
// 这里未来可以根据 PiP 的事件来控制播放状态 // 这里未来可以根据 PiP 的事件来控制播放状态
playerView.play() playerView.play()

9
local_plugins/music_service/ios/music_service/Sources/music_service/MusicService.swift

@ -57,11 +57,11 @@ public class MusicService: NSObject {
// MARK: - 音频会话设置 // MARK: - 音频会话设置
private func setupAudioSession() { private func setupAudioSession() {
do { do {
// 设置音频会话类别,添加 mixWithOthers 选项以确保通知正常显示 // 设置音频会话类别:不使用 mixWithOthers,让本应用音乐播放时打断外部音乐
try AVAudioSession.sharedInstance().setCategory( try AVAudioSession.sharedInstance().setCategory(
.playback, .playback,
mode: .default, mode: .default,
options: [.allowAirPlay, .allowBluetooth, .mixWithOthers] options: [.allowAirPlay, .allowBluetooth]
) )
try AVAudioSession.sharedInstance().setActive(true) try AVAudioSession.sharedInstance().setActive(true)
print("🎵 [MusicService] ✅ 音频会话设置成功") print("🎵 [MusicService] ✅ 音频会话设置成功")
@ -407,6 +407,8 @@ public class MusicService: NSObject {
print("开始播放: \(url), ID: \(currentIdInternal ?? 0)") print("开始播放: \(url), ID: \(currentIdInternal ?? 0)")
setupAudioSession()
// 检查是否已在播放列表中 // 检查是否已在播放列表中
let existingIndex = playlist.firstIndex { existingSong in let existingIndex = playlist.firstIndex { existingSong in
if let existingId = existingSong["id"] as? NSNumber, if let existingId = existingSong["id"] as? NSNumber,
@ -450,6 +452,7 @@ public class MusicService: NSObject {
} }
public func resume() { public func resume() {
setupAudioSession()
player?.play() player?.play()
isPlaying = true isPlaying = true
updatePlaybackState() updatePlaybackState()
@ -495,6 +498,7 @@ public class MusicService: NSObject {
updatePlaybackState() updatePlaybackState()
updateMediaSessionPlaybackState(isPlaying: false) updateMediaSessionPlaybackState(isPlaying: false)
stopProgressTimer() stopProgressTimer()
_ = try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
print("已停止") print("已停止")
} }
@ -969,6 +973,7 @@ public class MusicService: NSObject {
} }
// 开始播放 // 开始播放
setupAudioSession()
player?.play() player?.play()
isPlaying = true isPlaying = true
updateMediaSessionPlaybackState(isPlaying: true) updateMediaSessionPlaybackState(isPlaying: true)

2
pubspec.yaml

@ -1,7 +1,7 @@
name: voitrans name: voitrans
description: "Voitrans - AI Voice Assistant." description: "Voitrans - AI Voice Assistant."
publish_to: "none" publish_to: "none"
version: 1.0.21+72 version: 1.0.22+72
environment: environment:
sdk: ">=3.3.0 <4.0.0" sdk: ">=3.3.0 <4.0.0"

Loading…
Cancel
Save