137 lines
3.6 KiB
Swift
137 lines
3.6 KiB
Swift
|
|
//
|
||
|
|
// 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
|
||
|
|
}
|