57 lines
1.6 KiB
Swift
57 lines
1.6 KiB
Swift
import Foundation
|
|
|
|
public actor SilentEngine: OutputEngine {
|
|
public nonisolated let events: AsyncStream<OutputEvent>
|
|
private let continuation: AsyncStream<OutputEvent>.Continuation
|
|
private var item: QueueItem?
|
|
private var position: TimeInterval = 0
|
|
private var startedAt: ContinuousClock.Instant?
|
|
private var timer: Task<Void, Never>?
|
|
|
|
public init() {
|
|
(events, continuation) = AsyncStream<OutputEvent>.makeStream()
|
|
}
|
|
|
|
public func load(_ item: QueueItem, at position: TimeInterval) {
|
|
timer?.cancel()
|
|
self.item = item
|
|
self.position = position
|
|
startedAt = nil
|
|
}
|
|
|
|
public func play() {
|
|
guard let item, startedAt == nil else { return }
|
|
startedAt = .now
|
|
let remaining = max(item.track.duration - position, 0)
|
|
timer = Task { [continuation] in
|
|
try? await Task.sleep(for: .seconds(remaining))
|
|
guard !Task.isCancelled else { return }
|
|
continuation.yield(.finished(item.id))
|
|
}
|
|
}
|
|
|
|
public func pause() {
|
|
timer?.cancel()
|
|
if let startedAt {
|
|
let elapsed = ContinuousClock.now - startedAt
|
|
position += Double(elapsed.components.seconds) + Double(elapsed.components.attoseconds) / 1e18
|
|
}
|
|
startedAt = nil
|
|
}
|
|
|
|
public func seek(to position: TimeInterval) {
|
|
let wasPlaying = startedAt != nil
|
|
pause()
|
|
self.position = position
|
|
if wasPlaying {
|
|
play()
|
|
}
|
|
}
|
|
|
|
public func stop() {
|
|
timer?.cancel()
|
|
item = nil
|
|
startedAt = nil
|
|
}
|
|
}
|