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)))
|
(.arrayBuffer response)))
|
||||||
(.then decode-bytes!)))
|
(.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!
|
(defn clock-source!
|
||||||
"Promise of `{:buffer :seconds}` — the audio the transport runs its clock on
|
"Promise of `{:buffer :seconds}` — the audio the transport runs its clock on
|
||||||
while symbol `sid` is open, and how long that clock is.
|
while symbol `sid` is open, and how long that clock is.
|
||||||
|
|
@ -216,7 +233,9 @@
|
||||||
|
|
||||||
(and fallback-url (= sid (clip/opens-on document)))
|
(and fallback-url (= sid (clip/opens-on document)))
|
||||||
(-> (decode! fallback-url)
|
(-> (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
|
:else
|
||||||
{:buffer nil
|
{:buffer nil
|
||||||
|
|
@ -236,12 +255,8 @@
|
||||||
symbol played against the whole take's soundtrack would run ten frames and then
|
symbol played against the whole take's soundtrack would run ten frames and then
|
||||||
keep the clock going for minutes."
|
keep the clock going for minutes."
|
||||||
[document sid fallback-url store]
|
[document sid fallback-url store]
|
||||||
(-> (buffer! document sid store)
|
(-> (clock-source! document sid fallback-url store)
|
||||||
(.then (fn [buffer]
|
(.then (fn [{:keys [buffer seconds]}]
|
||||||
(cond
|
(wav-url (or buffer
|
||||||
buffer (wav-url buffer)
|
(.createBuffer (js/OfflineAudioContext. 1 1 44100)
|
||||||
(and fallback-url (= sid (clip/opens-on document))) fallback-url
|
1 (max 1 (js/Math.ceil (* 44100 seconds))) 44100)))))))
|
||||||
: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))))))))
|
|
||||||
|
|
|
||||||
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