Fix playback stopping when fallback audio ends
This commit is contained in:
parent
ee603a351b
commit
e5b61bfcdd
2 changed files with 50 additions and 10 deletions
|
|
@ -192,6 +192,23 @@
|
|||
(.arrayBuffer response)))
|
||||
(.then decode-bytes!)))
|
||||
|
||||
(defn fit-buffer
|
||||
"Fit fallback audio to the open timeline, padding with silence or trimming.
|
||||
The buffer and clock must share a duration so looping wraps at the timeline's
|
||||
end rather than repeating a short soundtrack underneath a longer animation."
|
||||
[^js buffer seconds]
|
||||
(let [rate (.-sampleRate buffer)
|
||||
frames (max 1 (js/Math.ceil (* seconds rate)))]
|
||||
(if (= frames (.-length buffer))
|
||||
buffer
|
||||
(let [channels (.-numberOfChannels buffer)
|
||||
fitted (.createBuffer (js/OfflineAudioContext. channels 1 rate)
|
||||
channels frames rate)]
|
||||
(dotimes [c channels]
|
||||
(.set (.getChannelData fitted c)
|
||||
(.subarray (.getChannelData buffer c) 0 (min frames (.-length buffer)))))
|
||||
fitted))))
|
||||
|
||||
(defn clock-source!
|
||||
"Promise of `{:buffer :seconds}` — the audio the transport runs its clock on
|
||||
while symbol `sid` is open, and how long that clock is.
|
||||
|
|
@ -216,7 +233,9 @@
|
|||
|
||||
(and fallback-url (= sid (clip/opens-on document)))
|
||||
(-> (decode! fallback-url)
|
||||
(.then (fn [^js b] {:buffer b :seconds (.-duration b)})))
|
||||
(.then (fn [b]
|
||||
(let [seconds (/ (clip/output-frames document sid) (:fps document))]
|
||||
{:buffer (fit-buffer b seconds) :seconds seconds}))))
|
||||
|
||||
:else
|
||||
{:buffer nil
|
||||
|
|
@ -236,12 +255,8 @@
|
|||
symbol played against the whole take's soundtrack would run ten frames and then
|
||||
keep the clock going for minutes."
|
||||
[document sid fallback-url store]
|
||||
(-> (buffer! document sid store)
|
||||
(.then (fn [buffer]
|
||||
(cond
|
||||
buffer (wav-url buffer)
|
||||
(and fallback-url (= sid (clip/opens-on document))) fallback-url
|
||||
:else (let [rate 44100
|
||||
n (max 1 (js/Math.ceil (* rate (/ (clip/output-frames document sid)
|
||||
(:fps document)))))]
|
||||
(wav-url (.createBuffer (js/OfflineAudioContext. 1 1 rate) 1 n rate))))))))
|
||||
(-> (clock-source! document sid fallback-url store)
|
||||
(.then (fn [{:keys [buffer seconds]}]
|
||||
(wav-url (or buffer
|
||||
(.createBuffer (js/OfflineAudioContext. 1 1 44100)
|
||||
1 (max 1 (js/Math.ceil (* 44100 seconds))) 44100)))))))
|
||||
|
|
|
|||
25
frontend/test/arthur/audio/mix_test.cljs
Normal file
25
frontend/test/arthur/audio/mix_test.cljs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
(ns arthur.audio.mix-test
|
||||
(:require [arthur.audio.mix :as mix]
|
||||
[cljs.test :refer [deftest is]]))
|
||||
|
||||
(defn- buffer [samples]
|
||||
(let [data (js/Float32Array. (clj->js samples))]
|
||||
#js {:sampleRate 12 :length (count samples) :numberOfChannels 1
|
||||
:getChannelData (fn [_] data)}))
|
||||
|
||||
(deftest fallback-audio-follows-the-timeline-length
|
||||
(let [original (.-OfflineAudioContext js/globalThis)]
|
||||
(set! (.-OfflineAudioContext js/globalThis)
|
||||
(fn [_ _ _]
|
||||
#js {:createBuffer (fn [_ frames _] (buffer (repeat frames 0)))}))
|
||||
(try
|
||||
(let [short (buffer (repeat 92 0.5))
|
||||
fitted (mix/fit-buffer short (/ 5008 12))
|
||||
data (.getChannelData fitted 0)]
|
||||
(is (= 5008 (.-length fitted)))
|
||||
(is (= 0.5 (aget data 91)))
|
||||
(is (= 0 (aget data 92)))
|
||||
(is (= 0 (aget data 5007)))
|
||||
(is (identical? short (mix/fit-buffer short (/ 92 12))))
|
||||
(is (= 20 (.-length (mix/fit-buffer short (/ 20 12))))))
|
||||
(finally (set! (.-OfflineAudioContext js/globalThis) original)))))
|
||||
Loading…
Add table
Add a link
Reference in a new issue