Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions MiniCut.playground/Sources/Model/Clip.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,25 @@ struct Clip: Identifiable {
var content: ClipContent

private var _start: TimeInterval
private var _length: TimeInterval
private var _originalLength: TimeInterval

/// Start offset within the clip (i.e. nonzero means that the clip is trimmed).
var start: TimeInterval {
get { _start }
set { _start = max(0, newValue) }
}
/// Original length of the clip's content.
var originalLength: TimeInterval {
get { _originalLength }
set { _originalLength = min(max(0, newValue), content.duration.map { $0 - start } ?? .infinity) }
}
/// Playback speed factor, 1 corresponds to normal playback rate.
/// Only applies to audiovisual content. Shall never be 0.
var speed: Double = 1
/// Playback length, i.e. original length divided by speed.
var length: TimeInterval {
get { _length }
set { _length = min(max(0, newValue), content.duration.map { $0 - start } ?? .infinity) }
get { originalLength / speed }
set { originalLength = newValue * speed }
}

var visualOffsetDx: Double = 0 // Normalized
Expand All @@ -41,7 +51,7 @@ struct Clip: Identifiable {
self.category = category
self.content = content
_start = max(0, start)
_length = max(0, length ?? content.duration ?? Self.defaultLength)
_originalLength = max(0, length ?? content.duration ?? Self.defaultLength)
}

init(url: URL) {
Expand Down
8 changes: 8 additions & 0 deletions MiniCut.playground/Sources/View/InspectorClipView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ final class InspectorClipView: SKNode {
}
})
]
case .audiovisual(_):
props += [
("Speed", { [weak self] in
Slider<Double>(value: self?.clip?.clip.speed ?? 1, range: 0.25..<4, width: $0) {
self?.clip?.clip.speed = $0
}
})
]
default:
break
}
Expand Down
13 changes: 9 additions & 4 deletions MiniCut.playground/Sources/View/VideoClipView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,29 @@ final class VideoClipView: SKNode {
switch clip.clip.content {
case .audiovisual(let content):
player = AVPlayer(playerItem: AVPlayerItem(asset: content.asset))

let video = SKVideoNode(avPlayer: player)
video.size = size
addChild(video)

let updatePlayer = { [weak self] in
guard let currentClip = state.timeline[trackId]?[id] else { return }
let relative = (state.cursor - currentClip.offset) + currentClip.clip.start
let originalRelative = (state.cursor - currentClip.offset) + currentClip.clip.start
let relative = originalRelative * currentClip.clip.speed
self?.player.rate = Float(currentClip.clip.speed)
self?.player.seek(to: CMTime(seconds: relative, preferredTimescale: 1000))
}

clipSubscription = state.timelineDidChange.subscribeFiring(state.timeline) { _ in updatePlayer() }
cursorSubscription = state.cursorDidChange.subscribeFiring(state.cursor) { _ in updatePlayer() }

isPlayingSubscription = state.isPlayingDidChange.subscribeFiring(state.isPlaying) {
isPlayingSubscription = state.isPlayingDidChange.subscribeFiring(state.isPlaying) { [weak self] in
guard let speed = state.timeline[trackId]?[id]?.clip.speed else { return }
if $0 {
video.play()
self?.player.play()
self?.player.rate = Float(speed)
} else {
video.pause()
self?.player.pause()
}
}
case .text(let text):
Expand Down