// // SpeechManager.swift // Crush // // Created by Leon on 2025/8/1. // import Foundation class SpeechManager { // MARK: - Properties static let shared = SpeechManager() private weak var currentModel: SpeechModel? private var timer: TimerModel? private var timerCount: Int = 0 private init() {} // MARK: - Private Methods private func model(withPath path: String) -> SpeechModel { let model = SpeechModel() model.path = path return model } private func timerAction() { timerCount -= 1 DispatchQueue.main.async {[weak self] in guard let `self` = self else { return } currentModel?.timerChangedBlock?(TimeInterval(timerCount/10)) } if let model = currentModel, model.isPlaying { if timerCount <= 0 { stopPlay(with: model) } } else { timer?.pauseTimer() } if timerCount < -10000 { timerCount = 0 } //dlog("SpeechManager timerAction___\(timerCount)") } // MARK: - Lazy Timer private var timerInstance: TimerModel { if timer == nil { timer = TimerModel() timer?.timerBlock = { [weak self] in self?.timerAction() } } return timer! } // MARK: - Public Methods static func checkVoiceState() -> Bool { // 🔥 Assuming PhoneManager and ChatRoomRoute are external dependencies if PhoneManager.isInPhoneChannel() || ChatRoomRoute.isChatRoomOpen() { UIWindow.key?.makeToast("toast_voice_channel_is_occupied") dlog("toast_voice_channel_is_occupied") return false } return true } func modelWithFilePath(_ path: String) -> SpeechModel { let model = model(withPath: path) model.fileType = .file return model } func modelWithFileUrl(_ url: String) -> SpeechModel { let model = model(withPath: url) model.fileType = .url return model } func modelWithBase64String(_ base64String: String) -> SpeechModel { let model = model(withPath: base64String) model.fileType = .base64 return model } func startPlay(with model: SpeechModel) { guard model.loadState != .loading else { return } if currentModel === model { if currentModel?.isPlaying == true { currentModel?.stopPlay() return } } if let current = currentModel { stopPlay(with: current) } model.canAutoPlay = true if model.needLoadData() { model.loadSpeechFile() currentModel = model } else { currentModel = model timerCount = model.audioTime model.startPlay() timerInstance.startTimer() } } func stopPlay(with model: SpeechModel) { model.canAutoPlay = false // model.stateChangedBlock = nil model.stopPlay() if model === currentModel { timerInstance.pauseTimer() } } func stopPlayCurrent() { if let model = currentModel { stopPlay(with: model) } } } // 🔥 Placeholder for external dependencies class PhoneManager { static func isInPhoneChannel() -> Bool { return false } // 🔥 Placeholder } class ChatRoomRoute { static func isChatRoomOpen() -> Bool { return false } // 🔥 Placeholder }