@banou/ripple 0.0.35 → 0.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/ar-CS4Yzfxw-ntfoSy6g.js +52 -0
- package/assets/worker-C5z2LwD4.js +23 -0
- package/cs-CxBZ1aTx-DJlDhThf.js +52 -0
- package/de-BByk96M5.js +529 -0
- package/de-DphEdq0O-Cx5KYfi3.js +52 -0
- package/el-BVTNfAWK-DR4z_l3g.js +52 -0
- package/es-419-BSBCtqRj.js +529 -0
- package/es-419-D6gsvnn8-DPOi-mTp.js +52 -0
- package/fkn.json +1 -1
- package/fr-BqAzAJX0-tO4ZnrRx.js +52 -0
- package/fr-DTVt1r9F.js +529 -0
- package/he-DbFw1CdO-BQBjAJw3.js +52 -0
- package/hi-rBroV7g8-BKMVjsFT.js +52 -0
- package/hu-m-QKaaxF-BMZqq-7P.js +52 -0
- package/id-BYpApdK9-Dkgrchef.js +52 -0
- package/index.js +17467 -11400
- package/it-DFIqX3Zv.js +529 -0
- package/it-NTcUh3Gr-lPnPd57N.js +52 -0
- package/ja-BNOeE4ac.js +529 -0
- package/ja-DSmX_bxj-CtZfcziw.js +52 -0
- package/ko-AySLianc.js +529 -0
- package/ko-DyPzWasj-BzWTky6S.js +52 -0
- package/libav-jspi.wasm +0 -0
- package/libav-worker.js +74 -129
- package/libav.wasm +0 -0
- package/ms-8pQbqM0D-CqJOEt8R.js +52 -0
- package/nl-q7xvc3xW-wNb_UuNc.js +52 -0
- package/package.json +1 -1
- package/pl-C6Ow4ra5-DnFaIeqF.js +52 -0
- package/pl-CbVJGC-B.js +529 -0
- package/pt-BR-BcfbDHsH.js +529 -0
- package/pt-BR-wudK3giz-CNnIslbU.js +52 -0
- package/remote-CbOGAlJZ.js +229 -0
- package/ro-DBrd08Df-DVgJ76rr.js +52 -0
- package/ru-BuCRhXdv.js +529 -0
- package/ru-D5kxBkik-Co3SZGx7.js +52 -0
- package/sv-DJC3tXfW-CtNN1hTP.js +52 -0
- package/sw.js +2 -2
- package/th-BNzU_FYo-R9JeWVkr.js +52 -0
- package/tr-CW7R7N9k-CKbYpVyF.js +52 -0
- package/tr-C_v8NzIK.js +529 -0
- package/uk-C66esN2W-BVOzQwRO.js +52 -0
- package/vi-oOaDECwu-P7-ObqFb.js +52 -0
- package/volume-normalizer-worklet.js +40 -26
- package/zh-Hans-D_1CA1C3.js +529 -0
- package/zh-Hans-IhL1Pl78-BSKvaC2F.js +52 -0
- package/zh-Hant-C5YyFjEc-Dghd5nFF.js +52 -0
- package/zh-Hant-CQCQPnti.js +529 -0
- package/assets/worker-kaROsro7.js +0 -23
- package/remote-DvdN47iT.js +0 -209
|
@@ -440,8 +440,9 @@ export const audioSamplesOf = (fragment, track) => {
|
|
|
440
440
|
/**
|
|
441
441
|
* The analysis: 1 ms bins of whatever audio it is given, the normal level learned from them, and the cut for the
|
|
442
442
|
* blocks around the playhead. `toWorklet` takes `{ block, gain, finger }` (10 s of bins from `block * 10` s, NaN where
|
|
443
|
-
* the cut or the audio is not known)
|
|
444
|
-
* the worklet keeps
|
|
443
|
+
* the cut or the audio is not known), `{ drop }` for each block past the 60 sent nearest the playhead, which is all
|
|
444
|
+
* the worklet keeps, and `{ end }`, the bin the file's audio ends at, once a run reaches it; `toPage` takes the
|
|
445
|
+
* coverage reports the page schedules reads on.
|
|
445
446
|
* Separate from the worker's messages so a test can drive it with decoded audio.
|
|
446
447
|
*/
|
|
447
448
|
export const createAnalysis = ({ toWorklet, toPage = () => {} }) => {
|
|
@@ -556,8 +557,6 @@ export const createAnalysis = ({ toWorklet, toPage = () => {} }) => {
|
|
|
556
557
|
finger[bin - start] = Math.log(chunk.finger[bin % BLOCK_BINS] + 1e-10)
|
|
557
558
|
if (bin < known) gain[bin - start] = curve[bin - from]
|
|
558
559
|
}
|
|
559
|
-
// the last cut holds past the end of the file, where playback's audio can run on by its offset
|
|
560
|
-
if (b >= endBin && b > start && b < end) gain.fill(gain[b - 1 - start], b - start)
|
|
561
560
|
}
|
|
562
561
|
return { gain, finger }
|
|
563
562
|
}
|
|
@@ -616,7 +615,10 @@ export const createAnalysis = ({ toWorklet, toPage = () => {} }) => {
|
|
|
616
615
|
endRun: (finished) => {
|
|
617
616
|
if (!run) return
|
|
618
617
|
run.binner?.flush()
|
|
619
|
-
if (finished && run.last >= 0)
|
|
618
|
+
if (finished && run.last >= 0) {
|
|
619
|
+
endBin = run.last + 1
|
|
620
|
+
toWorklet({ end: endBin })
|
|
621
|
+
}
|
|
620
622
|
run = null
|
|
621
623
|
schedule()
|
|
622
624
|
},
|
|
@@ -644,10 +646,11 @@ export const createAnalysis = ({ toWorklet, toPage = () => {} }) => {
|
|
|
644
646
|
*
|
|
645
647
|
* { type: 'port', port } the worklet's end of a MessageChannel, for the blocks
|
|
646
648
|
* { type: 'init', data, codec } the init segment and the codec string MediaSource was given
|
|
647
|
-
* { type: 'run', run, pts, start }
|
|
648
|
-
*
|
|
649
|
-
*
|
|
650
|
-
*
|
|
649
|
+
* { type: 'run', run, pts, from, start }
|
|
650
|
+
* a run of fragments starts at `from` s, `pts` added to their times as the
|
|
651
|
+
* SourceBuffer's timestampOffset adds it for playback; `start` for the one
|
|
652
|
+
* from the start, and `join: true` for one whose first fragments went by
|
|
653
|
+
* unseen, which starts at its first sample
|
|
651
654
|
* { type: 'fragment', run, data } answered { type: 'ack', run, spans, end, normal } once its audio is queued
|
|
652
655
|
* for decoding: the analysed seconds, the file's end once known, and the
|
|
653
656
|
* level learned so far in LUFS
|
|
@@ -682,10 +685,10 @@ const serve = (scope) => {
|
|
|
682
685
|
})
|
|
683
686
|
/*
|
|
684
687
|
* Each output is placed by its own chunk's timestamp, never the one AudioData carries: Chrome 155 stamps outputs by
|
|
685
|
-
* counting samples from the first chunk,
|
|
686
|
-
*
|
|
687
|
-
*
|
|
688
|
-
* 2026-10-17).
|
|
688
|
+
* counting samples from the first chunk, so past a gap in the timestamps, which MediaSource plays as a gap, what
|
|
689
|
+
* follows comes out early by the gap. Before 0.9.0 libav-wasm's first audio sample of a run lasted until the video's
|
|
690
|
+
* first frame, 62 ms more than its own length in Matroska and mp4 with B-frames and 82 ms in FLAC, too far for the
|
|
691
|
+
* first match to find (HOR-498 build, 2026-10-17).
|
|
689
692
|
*/
|
|
690
693
|
let stamps = []
|
|
691
694
|
const open = (id) => {
|
|
@@ -722,7 +725,7 @@ const serve = (scope) => {
|
|
|
722
725
|
pts = data.pts
|
|
723
726
|
open(runId)
|
|
724
727
|
joining = !!data.join
|
|
725
|
-
if (!joining) analysis.startRun(runId,
|
|
728
|
+
if (!joining) analysis.startRun(runId, data.from, data.start)
|
|
726
729
|
} else if (data.type === 'fragment') {
|
|
727
730
|
if (data.run !== runId || decoder.state !== 'configured') return
|
|
728
731
|
const samples = audioSamplesOf(data.data, track)
|
|
@@ -781,13 +784,14 @@ const HANDOVER_SECONDS = 0.05
|
|
|
781
784
|
const CROSSFADE_SECONDS = 0.05
|
|
782
785
|
/*
|
|
783
786
|
* How far either way the live audio is searched for in the analysed audio, in ms: first, and after a seek, against
|
|
784
|
-
* where the last match put it. Played from the start, the fixtures matched 21 to 60 ms off, and FLAC past 60 ms
|
|
785
|
-
*
|
|
787
|
+
* where the last match put it. Played from the start, the fixtures matched 21 to 60 ms off, and FLAC past 60 ms, while
|
|
788
|
+
* libav-wasm stretched each run's first audio sample to the video's first frame (HOR-498 build, 2026-10-17); with each
|
|
789
|
+
* track at its own time (0.9.0), within 3.4 ms (2026-10-30).
|
|
786
790
|
*/
|
|
787
791
|
const LOCK_RANGE_MS = 250
|
|
788
792
|
const RELOCK_RANGE_MS = 30
|
|
789
793
|
// how far from the last match a seek may move the audio, which the deepest cut in doubt covers, in ms: FLAC moved 82 ms
|
|
790
|
-
// across the first remuxer seek
|
|
794
|
+
// across the first remuxer seek while libav-wasm stretched a run's first audio sample, and at most 3 ms since 0.9.0
|
|
791
795
|
const DOUBT_MS = 100
|
|
792
796
|
// how far from the best offset another must come close for the match to be in question, in ms
|
|
793
797
|
const CLEAR_MS = 60
|
|
@@ -805,14 +809,15 @@ const SCORES_PER_QUANTUM = 40_000
|
|
|
805
809
|
* context.currentTime read in one task names the media time of the audio reaching a worklet that same render frame,
|
|
806
810
|
* to the sample for a 48 kHz file and within about 2 ms for a 44.1 kHz one, except for the odd pair off by up to
|
|
807
811
|
* 13 ms; so a jump of more than 25 ms re-anchors the timeline, and the median of the next five pairs settles it. Where
|
|
808
|
-
* playback's audio sits against the analysed audio
|
|
809
|
-
* the fixtures, the same file 44 ms apart before and after a seek
|
|
810
|
-
*
|
|
811
|
-
*
|
|
812
|
-
*
|
|
813
|
-
*
|
|
814
|
-
*
|
|
815
|
-
*
|
|
812
|
+
* playback's audio sits against the analysed audio changed with each run of the remuxer behind it while libav-wasm
|
|
813
|
+
* moved each run by its own amount: -18 to +44 ms on the fixtures, the same file 44 ms apart before and after a seek
|
|
814
|
+
* (HOR-498 build, 2026-10-17); with each track at its own time, -3.4 to +1.2 ms (0.9.0, 2026-10-30). So the offset
|
|
815
|
+
* is found from the audio itself: the live fingerprint against the analysed one, every 250 ms at normal speed, to a
|
|
816
|
+
* fraction of a millisecond. The same match catches the rare stall (once, 60 ms of silence came in mid-file and the
|
|
817
|
+
* audio after it ran 11 ms ahead of the clock). Nothing is cut ahead until the first match, which also proves the
|
|
818
|
+
* blocks belong to this audio; after a seek, until the next one, each quantum gets the deepest cut within the doubt,
|
|
819
|
+
* never less than the cut asks, as it does away from normal speed (Chrome's time stretching moves the audio up to
|
|
820
|
+
* about 30 ms either way).
|
|
816
821
|
*/
|
|
817
822
|
const defineProcessor = () => class VolumeNormalizer extends AudioWorkletProcessor {
|
|
818
823
|
constructor({ processorOptions = {} } = {}) {
|
|
@@ -867,6 +872,8 @@ const defineProcessor = () => class VolumeNormalizer extends AudioWorkletProcess
|
|
|
867
872
|
this.shortTime = 0
|
|
868
873
|
|
|
869
874
|
this.blocks = new Map()
|
|
875
|
+
// the bin the file's audio ends at; the last cut holds past it, where playback's audio can run on
|
|
876
|
+
this.end = Infinity
|
|
870
877
|
this.analysis = null
|
|
871
878
|
this.fingerFilter = fingerprintFilter(sampleRate)
|
|
872
879
|
this.fingerState = []
|
|
@@ -909,6 +916,7 @@ const defineProcessor = () => class VolumeNormalizer extends AudioWorkletProcess
|
|
|
909
916
|
if (data?.analysis !== undefined) {
|
|
910
917
|
this.analysis?.close()
|
|
911
918
|
this.blocks.clear()
|
|
919
|
+
this.end = Infinity
|
|
912
920
|
this.search = null
|
|
913
921
|
this.analysis = data.analysis
|
|
914
922
|
if (this.analysis) this.analysis.onmessage = ({ data: block }) => this.receive(block)
|
|
@@ -930,6 +938,7 @@ const defineProcessor = () => class VolumeNormalizer extends AudioWorkletProcess
|
|
|
930
938
|
receive(message) {
|
|
931
939
|
if (typeof message?.drop === 'number') this.blocks.delete(message.drop)
|
|
932
940
|
else if (typeof message?.block === 'number') this.blocks.set(message.block, message)
|
|
941
|
+
else if (typeof message?.end === 'number') this.end = message.end
|
|
933
942
|
this.lastBlock = undefined
|
|
934
943
|
}
|
|
935
944
|
|
|
@@ -937,7 +946,11 @@ const defineProcessor = () => class VolumeNormalizer extends AudioWorkletProcess
|
|
|
937
946
|
const frame = ctx * sampleRate
|
|
938
947
|
const model = this.model
|
|
939
948
|
if (paused) {
|
|
940
|
-
|
|
949
|
+
// More than 25 ms from where the clock had it, the media is where the pair says: a seek moved it, its audio
|
|
950
|
+
// already playing (10 to 27 ms after the move in Chrome 155, where the main thread once took 52 ms to say so,
|
|
951
|
+
// 2026-10-30), or a pause was told late. The seek's reset, posted on the same event, is what puts it in doubt.
|
|
952
|
+
const held = model && !model.paused ? this.mediaAt(frame) : media
|
|
953
|
+
this.model = { paused: true, media: Math.abs(media - held) <= 0.025 ? held : media }
|
|
941
954
|
this.liveCount = 0
|
|
942
955
|
return
|
|
943
956
|
}
|
|
@@ -973,6 +986,7 @@ const defineProcessor = () => class VolumeNormalizer extends AudioWorkletProcess
|
|
|
973
986
|
}
|
|
974
987
|
|
|
975
988
|
gainBin(i) {
|
|
989
|
+
if (i >= this.end) i = this.end - 1
|
|
976
990
|
const block = this.blockOf(i)
|
|
977
991
|
return block.gain ? block.gain[i - block.block * BLOCK_BINS] : NaN
|
|
978
992
|
}
|