Files
tinystt/Sources/TinyTTS/Speech/SpeechEngine.swift
T

63 lines
2.1 KiB
Swift

import Foundation
import Speech
@MainActor
enum SpeechEngine {
private static var warm: (any RecognitionSession)?
private static var warming: Task<Void, Never>?
private static var expiry: Task<Void, Never>?
private static var generation = UUID()
static var currentLocale: Locale { Locale(identifier: Prefs.shared.languageCode) }
static func warmUp() {
guard warming == nil, warm == nil else { return }
let token = generation
let locale = currentLocale
warming = Task {
let session = try? await make(locale: locale)
guard token == generation else { await session?.cancel(); return }
warming = nil
warm = session
expiry?.cancel()
expiry = Task {
try? await Task.sleep(for: .seconds(600))
guard !Task.isCancelled, token == generation else { return }
let old = warm
warm = nil
await old?.cancel()
}
}
}
static func acquire() async throws -> any RecognitionSession {
if let warming { await warming.value }
expiry?.cancel()
if let session = warm { warm = nil; return session }
return try await make(locale: currentLocale)
}
private static func make(locale: Locale) async throws -> any RecognitionSession {
if Prefs.shared.engineChoice == "legacy" { return try await LegacySession.make(locale: locale) }
do { return try await SpeechSession.make(locale: locale) }
catch {
guard Prefs.shared.engineChoice == "auto" else { throw error }
Log.error("primary analyzer unavailable; trying local dictation")
if let session = try? await SpeechSession.make(locale: locale, forceDictation: true) { return session }
return try await LegacySession.make(locale: locale)
}
}
static func replenish() { warmUp() }
static func resetWarm() {
generation = UUID()
warming?.cancel()
warming = nil
expiry?.cancel()
let old = warm
warm = nil
Task { await old?.cancel() }
}
}