Files
clawdbot/apps/macos/Sources/Clawdis/TalkAudioPlayer.swift
2025-12-30 00:17:10 +01:00

55 lines
1.8 KiB
Swift

import AVFoundation
import Foundation
import OSLog
@MainActor
final class TalkAudioPlayer: NSObject, @preconcurrency AVAudioPlayerDelegate {
static let shared = TalkAudioPlayer()
private let logger = Logger(subsystem: "com.steipete.clawdis", category: "talk.tts")
private var player: AVAudioPlayer?
private var continuation: CheckedContinuation<TalkPlaybackResult, Never>?
func play(data: Data) async -> TalkPlaybackResult {
self.stopInternal(interrupted: true)
do {
let player = try AVAudioPlayer(data: data)
self.player = player
player.delegate = self
player.prepareToPlay()
player.play()
return await withCheckedContinuation { continuation in
self.continuation = continuation
}
} catch {
self.logger.error("talk audio player failed: \(error.localizedDescription, privacy: .public)")
return TalkPlaybackResult(finished: false, interruptedAt: nil)
}
}
func stop() -> Double? {
guard let player else { return nil }
let time = player.currentTime
self.stopInternal(interrupted: true, interruptedAt: time)
return time
}
func audioPlayerDidFinishPlaying(_: AVAudioPlayer, successfully flag: Bool) {
self.stopInternal(interrupted: !flag)
}
private func stopInternal(interrupted: Bool, interruptedAt: Double? = nil) {
self.player?.stop()
self.player = nil
if let continuation {
self.continuation = nil
continuation.resume(returning: TalkPlaybackResult(finished: !interrupted, interruptedAt: interruptedAt))
}
}
}
struct TalkPlaybackResult: Sendable {
let finished: Bool
let interruptedAt: Double?
}