Brought-in footage plays at the project's rate

The new symbol gets project-rate frames covering the same wall-clock length,
and its root and sound map them back onto the source's frames.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Olive Vaughn 2026-09-30 01:11:32 -04:00
parent 4b8123d5e2
commit 1ac21fdcab
2 changed files with 26 additions and 6 deletions

View file

@ -71,6 +71,21 @@
[clip frozen label footage-id range]
(let [{c :clip ids :ids} (symbols clip frozen [:main] {:main (symbol-id label)})
sid (ids :main)
source-fps (:fps frozen)
project-fps (:fps clip)
;; The new symbol is played by the receiving project's clock. Keep its
;; wall-clock duration by giving it project-rate frames, while its root
;; maps those frames back onto the source timeline. A 30fps source in a
;; 12fps project therefore has 2.5 source frames per project frame.
source-rate (if (and (number? source-fps) (pos? source-fps)
(number? project-fps) (pos? project-fps))
(/ source-fps project-fps)
1)
output-frames (max 1 (js/Math.ceil (/ (clip/frames c sid) source-rate)))
c (-> c
(assoc-in [:symbols sid :frames] output-frames)
(update-in [:symbols sid :nodes :root :time]
#(assoc (or % {}) :mode :map :at 0 :rate source-rate)))
tracked? (and (nil? (:analysis clip))
(every? #(= % (ids %)) (keys (:subjects frozen))))]
{:sid sid
@ -81,7 +96,10 @@
{:id :sound :name "sound" :kind :audio :parent nil
:z "z-sound" :source {:footage footage-id}
;; Source frame `start` plays on the symbol's 0.
:span range :time {:mode :map :at (- (first range)) :rate 1}}))
:span range
:time {:mode :map
:at (/ (- (first range)) source-rate)
:rate source-rate}}))
tracked? (-> (assoc :analysis (:analysis frozen))
(update :subjects merge (:subjects frozen))
(update :features merge (:features frozen))

View file

@ -5,6 +5,7 @@
detector's identity comes from the server too, because it goes into the content
address of every block this produces."
(:require [arthur.domain.bring :as bring]
[arthur.domain.clip :as clip]
[arthur.events.edit :as edit]
[arthur.events.playback :as pb]
[arthur.flow.detect :as detect]
@ -432,6 +433,8 @@
{:keys [clip sid tracked?]}
(bring/take (:clip (store/entry (:clip/current db))) (:clip built)
name footage-id range)
imported-frames (clip/frames clip sid)
source-fps (get-in built [:clip :fps])
db (edit/edit-entry
db
#(cond-> (bring/placed % clip (:store built) sid host frame uuid point)
@ -442,10 +445,9 @@
(assoc-in [:ui :selection] [:node host uuid [uuid]])
(update :footage merge
{:loading? false
:status (str "made " name " · " (- (second range) (first range)) " frames"
:status (str "made " name " · " imported-frames " frames at " fps " fps"
(when (not= fps source-fps)
(str " · sampled from " source-fps " fps"))
(when-not tracked?
" · as drawings: this project already tracks other footage")
(when (not= fps (get-in built [:clip :fps]))
(str " · footage is " (get-in built [:clip :fps])
" fps, this project " fps)))}))
" · as drawings: this project already tracks other footage"))}))
:dispatch [::pb/refresh-clock]})))