audio 2.0.0-1 → 2.1.0
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 +660 -44
- package/audio.d.ts +244 -0
- package/audio.js +54 -0
- package/bin/cli.js +982 -0
- package/cache.js +89 -0
- package/core.js +630 -0
- package/dist/audio.all.js +105450 -0
- package/dist/audio.js +3571 -0
- package/dist/audio.min.js +14 -0
- package/fn/cepstrum.js +55 -0
- package/fn/clip.js +11 -0
- package/fn/crop.js +19 -0
- package/fn/fade.js +47 -0
- package/fn/filter.js +67 -0
- package/fn/gain.js +16 -0
- package/fn/insert.js +31 -0
- package/fn/loudness.js +102 -0
- package/fn/mix.js +21 -0
- package/fn/normalize.js +80 -0
- package/fn/pad.js +19 -0
- package/fn/pan.js +35 -0
- package/fn/play.js +123 -0
- package/fn/remix.js +25 -0
- package/fn/remove.js +25 -0
- package/fn/repeat.js +35 -0
- package/fn/reverse.js +25 -0
- package/fn/save.js +55 -0
- package/fn/silence.js +34 -0
- package/fn/spectrum.js +104 -0
- package/fn/speed.js +23 -0
- package/fn/split.js +9 -0
- package/fn/stat.js +77 -0
- package/fn/transform.js +6 -0
- package/fn/trim.js +68 -0
- package/fn/write.js +15 -0
- package/{LICENSE → license.md} +21 -21
- package/package.json +60 -19
- package/plan.js +456 -0
- package/stats.js +255 -0
- package/index.js +0 -107
package/cache.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page cache — LRU eviction to OPFS and lazy restore.
|
|
3
|
+
* Self-registers on import — exposes opfsCache/evict/ensurePages on audio.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import audio from './core.js'
|
|
7
|
+
|
|
8
|
+
const DEFAULT_BUDGET = 500 * 1024 * 1024 // 500MB
|
|
9
|
+
|
|
10
|
+
/** Evict pages to cache until resident bytes fit within budget. True LRU. */
|
|
11
|
+
async function evict(a) {
|
|
12
|
+
if (!a.cache || a.budget === Infinity) return
|
|
13
|
+
let bytes = p => p ? p.reduce((s, ch) => s + ch.byteLength, 0) : 0
|
|
14
|
+
let current = a.pages.reduce((sum, p) => sum + bytes(p), 0)
|
|
15
|
+
if (current <= a.budget) return
|
|
16
|
+
// Build eviction order: LRU (oldest first) if tracked, else FIFO fallback
|
|
17
|
+
let order = a._.lru && a._.lru.size
|
|
18
|
+
? [...a._.lru]
|
|
19
|
+
: a.pages.map((_, i) => i)
|
|
20
|
+
for (let i of order) {
|
|
21
|
+
if (current <= a.budget) break
|
|
22
|
+
if (!a.pages[i]) continue
|
|
23
|
+
await a.cache.write(i, a.pages[i])
|
|
24
|
+
current -= bytes(a.pages[i])
|
|
25
|
+
a.pages[i] = null
|
|
26
|
+
if (a._.lru) a._.lru.delete(i)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Restore evicted pages covering a sample range from cache. */
|
|
31
|
+
async function ensurePages(a, offset, duration) {
|
|
32
|
+
if (!a.cache) return
|
|
33
|
+
let PS = audio.PAGE_SIZE, sr = a.sampleRate
|
|
34
|
+
let s = offset != null ? Math.max(0, Math.round(offset * sr)) : 0
|
|
35
|
+
let len = duration != null ? Math.round(duration * sr) : a._.len - s
|
|
36
|
+
let p0 = Math.floor(s / PS), pEnd = Math.min(Math.ceil((s + len) / PS), a.pages.length)
|
|
37
|
+
for (let i = p0; i < pEnd; i++)
|
|
38
|
+
if (a.pages[i] === null && await a.cache.has(i)) a.pages[i] = await a.cache.read(i)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Create an OPFS-backed cache backend. Browser only. */
|
|
42
|
+
async function opfsCache(dirName = 'audio-cache') {
|
|
43
|
+
if (typeof navigator === 'undefined' || !navigator.storage?.getDirectory)
|
|
44
|
+
throw new Error('OPFS not available in this environment')
|
|
45
|
+
let root = await navigator.storage.getDirectory()
|
|
46
|
+
let dir = await root.getDirectoryHandle(dirName, { create: true })
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
async read(i) {
|
|
50
|
+
let handle = await dir.getFileHandle(`p${i}`)
|
|
51
|
+
let file = await handle.getFile()
|
|
52
|
+
let buf = await file.arrayBuffer()
|
|
53
|
+
let view = new Float32Array(buf)
|
|
54
|
+
let ch = view[0] | 0, samplesPerCh = ((view.length - 1) / ch) | 0
|
|
55
|
+
let data = []
|
|
56
|
+
for (let c = 0; c < ch; c++) data.push(view.slice(1 + c * samplesPerCh, 1 + (c + 1) * samplesPerCh))
|
|
57
|
+
return data
|
|
58
|
+
},
|
|
59
|
+
async write(i, data) {
|
|
60
|
+
let handle = await dir.getFileHandle(`p${i}`, { create: true })
|
|
61
|
+
let writable = await handle.createWritable()
|
|
62
|
+
let total = 1 + data.reduce((s, ch) => s + ch.length, 0)
|
|
63
|
+
let packed = new Float32Array(total)
|
|
64
|
+
packed[0] = data.length
|
|
65
|
+
let off = 1
|
|
66
|
+
for (let ch of data) { packed.set(ch, off); off += ch.length }
|
|
67
|
+
await writable.write(packed.buffer)
|
|
68
|
+
await writable.close()
|
|
69
|
+
},
|
|
70
|
+
has(i) {
|
|
71
|
+
return dir.getFileHandle(`p${i}`).then(() => true, () => false)
|
|
72
|
+
},
|
|
73
|
+
async evict(i) {
|
|
74
|
+
try { await dir.removeEntry(`p${i}`) } catch {}
|
|
75
|
+
},
|
|
76
|
+
async clear() {
|
|
77
|
+
for await (let [name] of dir) await dir.removeEntry(name)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
// ── Self-register ────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
audio.opfsCache = opfsCache
|
|
87
|
+
audio.evict = evict
|
|
88
|
+
audio.ensurePages = ensurePages
|
|
89
|
+
audio.DEFAULT_BUDGET = DEFAULT_BUDGET
|