@lucaismyname/ginger 0.0.13 → 0.0.14

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.
Files changed (36) hide show
  1. package/README.md +76 -0
  2. package/dist/analyzer/liveAudioGraph.d.ts +14 -0
  3. package/dist/analyzer/liveAudioGraph.d.ts.map +1 -0
  4. package/dist/analyzer/useGingerLiveAnalyzer.d.ts +21 -0
  5. package/dist/analyzer/useGingerLiveAnalyzer.d.ts.map +1 -0
  6. package/dist/client.cjs +1 -1
  7. package/dist/client.js +21 -18
  8. package/dist/index.cjs +1 -1
  9. package/dist/index.d.ts +4 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +21 -18
  12. package/dist/internal/fft.d.ts +7 -0
  13. package/dist/internal/fft.d.ts.map +1 -0
  14. package/dist/internal/fft.test.d.ts +2 -0
  15. package/dist/internal/fft.test.d.ts.map +1 -0
  16. package/dist/useSeekDrag-Cc0wpC70.cjs +2 -0
  17. package/dist/useSeekDrag-Cc0wpC70.cjs.map +1 -0
  18. package/dist/useSeekDrag-DkAJvf8H.js +289 -0
  19. package/dist/useSeekDrag-DkAJvf8H.js.map +1 -0
  20. package/dist/waveform/analyzeAudioFile.d.ts +31 -0
  21. package/dist/waveform/analyzeAudioFile.d.ts.map +1 -0
  22. package/dist/waveform/analyzeAudioFile.test.d.ts +2 -0
  23. package/dist/waveform/analyzeAudioFile.test.d.ts.map +1 -0
  24. package/dist/waveform/index.cjs +1 -1
  25. package/dist/waveform/index.cjs.map +1 -1
  26. package/dist/waveform/index.d.ts +4 -0
  27. package/dist/waveform/index.d.ts.map +1 -1
  28. package/dist/waveform/index.js +207 -23
  29. package/dist/waveform/index.js.map +1 -1
  30. package/dist/waveform/useAudioFileAnalysis.d.ts +8 -0
  31. package/dist/waveform/useAudioFileAnalysis.d.ts.map +1 -0
  32. package/package.json +1 -1
  33. package/dist/useSeekDrag-DBzoym0-.cjs +0 -2
  34. package/dist/useSeekDrag-DBzoym0-.cjs.map +0 -1
  35. package/dist/useSeekDrag-jLsYA-uG.js +0 -174
  36. package/dist/useSeekDrag-jLsYA-uG.js.map +0 -1
@@ -0,0 +1,289 @@
1
+ import { useMemo as R, useState as P, useRef as b, useCallback as Q, useLayoutEffect as Y, useEffect as I } from "react";
2
+ import { b as S, u as q, g as W, c as Z } from "./GingerSplitContexts-4YZ-OJ9V.js";
3
+ import { r as _, h as $, p as j, i as ee, j as te, b as ne, k as re } from "./ginger-Dntdd6zH.js";
4
+ function se() {
5
+ const n = S(), t = q();
6
+ return R(
7
+ () => {
8
+ const e = W(n, t);
9
+ return {
10
+ state: e,
11
+ currentTrack: re(e),
12
+ playbackUi: ne(e),
13
+ duration: te(e),
14
+ remaining: ee(e),
15
+ progress: j(e),
16
+ artworkUrl: $(e),
17
+ albumLine: _(e),
18
+ play: n.play,
19
+ pause: n.pause,
20
+ togglePlayPause: n.togglePlayPause,
21
+ seek: t.seek,
22
+ setVolume: t.setVolume,
23
+ setMuted: t.setMuted,
24
+ toggleMute: t.toggleMute,
25
+ setPlaybackRate: t.setPlaybackRate,
26
+ next: n.next,
27
+ prev: n.prev,
28
+ setRepeatMode: n.setRepeatMode,
29
+ cycleRepeat: n.cycleRepeat,
30
+ toggleShuffle: n.toggleShuffle,
31
+ setQueue: n.setQueue,
32
+ insertTrackAt: n.insertTrackAt,
33
+ removeTrackAt: n.removeTrackAt,
34
+ moveTrack: n.moveTrack,
35
+ enqueueNext: n.enqueueNext,
36
+ playTrackAt: n.playTrackAt,
37
+ selectTrackAt: n.selectTrackAt,
38
+ setPlaylistMeta: n.setPlaylistMeta,
39
+ init: n.init,
40
+ audioRef: t.audioRef,
41
+ dispatch: n.dispatch
42
+ };
43
+ },
44
+ [n, t]
45
+ );
46
+ }
47
+ const B = /* @__PURE__ */ new WeakMap();
48
+ function ce(n) {
49
+ const t = 2 ** Math.round(Math.log2(n));
50
+ return Math.min(32768, Math.max(32, t));
51
+ }
52
+ function oe(n, t) {
53
+ let e = B.get(n);
54
+ if (!e) {
55
+ const i = window.AudioContext ?? window.webkitAudioContext;
56
+ if (!i)
57
+ throw new Error("Web Audio API is not available");
58
+ const l = new i(), y = l.createMediaElementSource(n);
59
+ e = { context: l, source: y, consumers: /* @__PURE__ */ new Map(), nextId: 0 }, B.set(n, e);
60
+ }
61
+ const { context: s, source: o } = e, c = s.createAnalyser();
62
+ c.fftSize = ce(t.fftSize), c.smoothingTimeConstant = t.smoothingTimeConstant, c.minDecibels = t.minDecibels, c.maxDecibels = t.maxDecibels, o.connect(c);
63
+ const r = e.consumers.size === 0;
64
+ r && c.connect(s.destination);
65
+ const a = e.nextId;
66
+ return e.nextId += 1, e.consumers.set(a, { analyser: c, isPlaybackSink: r }), { id: a, context: s, analyser: c };
67
+ }
68
+ function V(n, t) {
69
+ const e = B.get(n);
70
+ if (!e) return;
71
+ const s = e.consumers.get(t);
72
+ if (!s) return;
73
+ const { analyser: o, isPlaybackSink: c } = s;
74
+ if (o.disconnect(), e.consumers.delete(t), e.consumers.size === 0) {
75
+ try {
76
+ e.source.disconnect();
77
+ } catch {
78
+ }
79
+ e.context.close(), B.delete(n);
80
+ return;
81
+ }
82
+ if (c) {
83
+ const r = e.consumers.values().next().value;
84
+ r && (r.analyser.connect(e.context.destination), r.isPlaybackSink = !0);
85
+ }
86
+ }
87
+ const G = new Uint8Array(0), N = new Uint8Array(0);
88
+ function me(n = {}) {
89
+ const {
90
+ enabled: t = !0,
91
+ fftSize: e = 2048,
92
+ smoothingTimeConstant: s = 0.8,
93
+ minDecibels: o = -100,
94
+ maxDecibels: c = -30
95
+ } = n, { audioRef: r, state: a } = se(), i = R(
96
+ () => ({
97
+ fftSize: e,
98
+ smoothingTimeConstant: s,
99
+ minDecibels: o,
100
+ maxDecibels: c
101
+ }),
102
+ [e, s, o, c]
103
+ ), [l, y] = P(0), [m, u] = P(null), [x, g] = P(!1), [C, T] = P({ frequencyBinCount: 0, sampleRate: 0 }), d = b(G), k = b(N), J = Q(async () => {
104
+ const v = w.current;
105
+ v && v.state === "suspended" && await v.resume();
106
+ }, []), w = b(null), M = b(null);
107
+ return Y(() => {
108
+ if (!t || typeof window > "u")
109
+ return;
110
+ let v = !1, D = null, E = null, F = 0;
111
+ const U = () => {
112
+ const f = w.current;
113
+ f && g(f.state === "suspended");
114
+ }, z = () => {
115
+ if (v) return;
116
+ const f = M.current, h = d.current, p = k.current;
117
+ f && h.length > 0 && p.length > 0 && (f.getByteFrequencyData(h), f.getByteTimeDomainData(p), y((A) => A + 1)), F = requestAnimationFrame(z);
118
+ }, K = () => {
119
+ const f = r.current;
120
+ if (!f || v) return "no-element";
121
+ try {
122
+ const { id: h, context: p, analyser: A } = oe(f, i);
123
+ D = h, E = f, w.current = p, M.current = A, g(p.state === "suspended"), u(null), p.addEventListener("statechange", U);
124
+ const L = A.frequencyBinCount, O = A.fftSize;
125
+ return d.current = new Uint8Array(L), k.current = new Uint8Array(O), T({ frequencyBinCount: L, sampleRate: p.sampleRate }), F = requestAnimationFrame(z), "ok";
126
+ } catch (h) {
127
+ const p = h instanceof Error ? h.message : "Failed to attach live analyser";
128
+ return u(p), w.current = null, M.current = null, d.current = G, k.current = N, T({ frequencyBinCount: 0, sampleRate: 0 }), "error";
129
+ }
130
+ }, X = K();
131
+ if (X !== "ok") {
132
+ let f = 0;
133
+ const h = 120;
134
+ let p = 0;
135
+ const A = () => {
136
+ if (v) return;
137
+ const L = K();
138
+ L === "ok" || L === "error" || (p += 1, !(p >= h) && (f = requestAnimationFrame(A)));
139
+ };
140
+ return X === "no-element" && (f = requestAnimationFrame(A)), () => {
141
+ var L;
142
+ v = !0, cancelAnimationFrame(f), cancelAnimationFrame(F), D != null && E && V(E, D), (L = w.current) == null || L.removeEventListener("statechange", U), w.current = null, M.current = null, d.current = G, k.current = N;
143
+ };
144
+ }
145
+ return () => {
146
+ var f;
147
+ v = !0, cancelAnimationFrame(F), D != null && E && V(E, D), (f = w.current) == null || f.removeEventListener("statechange", U), w.current = null, M.current = null, d.current = G, k.current = N, T({ frequencyBinCount: 0, sampleRate: 0 });
148
+ };
149
+ }, [t, r, i, a.currentIndex]), {
150
+ frequencyData: d.current,
151
+ timeDomainData: k.current,
152
+ frequencyBinCount: C.frequencyBinCount,
153
+ sampleRate: C.sampleRate,
154
+ isSuspended: x,
155
+ error: m,
156
+ resume: J
157
+ };
158
+ }
159
+ function de(n = !0, t = {}) {
160
+ const { togglePlayPause: e, next: s, prev: o } = S(), { toggleMute: c } = q(), r = t.mute;
161
+ I(() => {
162
+ if (!n || typeof window > "u") return;
163
+ const a = (t.playPause ?? " ").toLowerCase(), i = (t.next ?? "ArrowRight").toLowerCase(), l = (t.previous ?? "ArrowLeft").toLowerCase(), y = r == null ? void 0 : r.toLowerCase(), m = (u) => {
164
+ const x = u.target;
165
+ if (x && (["INPUT", "TEXTAREA", "SELECT"].includes(x.tagName) || x.isContentEditable)) return;
166
+ const g = u.key.toLowerCase();
167
+ g === a ? (u.preventDefault(), e()) : g === i ? (u.preventDefault(), s()) : g === l ? (u.preventDefault(), o()) : y && g === y && (u.preventDefault(), c());
168
+ };
169
+ return window.addEventListener("keydown", m), () => window.removeEventListener("keydown", m);
170
+ }, [t.next, t.playPause, t.previous, n, r, s, o, c, e]);
171
+ }
172
+ function pe() {
173
+ const { tracks: n, currentIndex: t } = S(), { currentTime: e, seek: s } = q(), o = R(() => {
174
+ var a;
175
+ return [...((a = n[t]) == null ? void 0 : a.chapters) ?? []].filter((i) => i && Number.isFinite(i.startSeconds) && i.startSeconds >= 0).sort((i, l) => i.startSeconds - l.startSeconds);
176
+ }, [t, n]), c = R(() => {
177
+ if (o.length === 0) return -1;
178
+ for (let r = o.length - 1; r >= 0; r -= 1)
179
+ if (e >= o[r].startSeconds) return r;
180
+ return -1;
181
+ }, [e, o]);
182
+ return {
183
+ list: o,
184
+ activeIndex: c,
185
+ active: c >= 0 ? o[c] ?? null : null,
186
+ seekTo: (r) => {
187
+ const a = o[r];
188
+ a && s(a.startSeconds);
189
+ }
190
+ };
191
+ }
192
+ const H = /\[(\d{1,2}):(\d{2})(?:\.(\d{1,3}))?\]/g;
193
+ function ae(n) {
194
+ const t = [];
195
+ for (const e of n.split(/\r?\n/)) {
196
+ const s = [...e.matchAll(H)];
197
+ if (s.length === 0) continue;
198
+ const o = e.replace(H, "").trim();
199
+ for (const c of s) {
200
+ const r = Number(c[1] ?? 0), a = Number(c[2] ?? 0), i = Number((c[3] ?? "0").padEnd(3, "0")), l = r * 60 + a + i / 1e3;
201
+ Number.isFinite(l) && l >= 0 && t.push({ time: l, text: o });
202
+ }
203
+ }
204
+ return t.sort((e, s) => e.time - s.time);
205
+ }
206
+ function ye() {
207
+ const { tracks: n, currentIndex: t } = S(), { currentTime: e } = q(), s = n[t], o = R(() => s ? Array.isArray(s.lyricsTimed) && s.lyricsTimed.length > 0 ? [...s.lyricsTimed].filter((r) => Number.isFinite(r.time) && r.time >= 0).sort((r, a) => r.time - a.time) : typeof s.lyrics == "string" ? ae(s.lyrics) : [] : [], [s]), c = R(() => {
208
+ for (let r = o.length - 1; r >= 0; r -= 1)
209
+ if (e >= o[r].time) return r;
210
+ return -1;
211
+ }, [e, o]);
212
+ return {
213
+ lines: o,
214
+ activeIndex: c,
215
+ activeLine: c >= 0 ? o[c] ?? null : null
216
+ };
217
+ }
218
+ function ge(n) {
219
+ const { durationMs: t, stopAfterTracks: e, respectPause: s = !0, enabled: o = !0, onFire: c } = n, { currentIndex: r, pause: a, isPaused: i } = S(), l = b(e ?? 0), y = b(r);
220
+ I(() => {
221
+ l.current = e ?? 0;
222
+ }, [e]), I(() => {
223
+ if (!o || !t || t <= 0 || s && i) return;
224
+ const m = setTimeout(() => {
225
+ a(), c == null || c();
226
+ }, t);
227
+ return () => clearTimeout(m);
228
+ }, [t, o, i, c, a, s]), I(() => {
229
+ if (!o || !e || e <= 0) return;
230
+ const m = y.current;
231
+ y.current = r, r !== m && (l.current -= 1, l.current <= 0 && (a(), c == null || c()));
232
+ }, [r, o, c, a, e]);
233
+ }
234
+ function ke(n = !1) {
235
+ const t = Z(), e = b(t);
236
+ I(() => {
237
+ if (!n || typeof console > "u") return;
238
+ const s = e.current;
239
+ s !== t && console.debug("[ginger]", {
240
+ from: {
241
+ currentIndex: s.currentIndex,
242
+ isPaused: s.isPaused,
243
+ currentTime: s.currentTime,
244
+ repeatMode: s.repeatMode
245
+ },
246
+ to: {
247
+ currentIndex: t.currentIndex,
248
+ isPaused: t.isPaused,
249
+ currentTime: t.currentTime,
250
+ repeatMode: t.repeatMode
251
+ }
252
+ }), e.current = t;
253
+ }, [n, t]);
254
+ }
255
+ function ie(n) {
256
+ return Math.max(0, Math.min(1, n));
257
+ }
258
+ function ve(n) {
259
+ const t = q(), e = S(), { seek: s } = t, [o, c] = P(0), [r, a] = P(!1), i = j(W(e, t)), l = r ? o : i, y = Q(
260
+ (m) => {
261
+ if (!(n > 0)) return;
262
+ const u = m.currentTarget, x = u.getBoundingClientRect(), g = (d) => {
263
+ const k = ie((d - x.left) / x.width);
264
+ c(k), s(k * n);
265
+ };
266
+ a(!0), u.setPointerCapture(m.pointerId), g(m.clientX);
267
+ const C = (d) => g(d.clientX), T = (d) => {
268
+ g(d.clientX), a(!1), u.releasePointerCapture(m.pointerId), u.removeEventListener("pointermove", C), u.removeEventListener("pointerup", T), u.removeEventListener("pointercancel", T);
269
+ };
270
+ u.addEventListener("pointermove", C), u.addEventListener("pointerup", T), u.addEventListener("pointercancel", T);
271
+ },
272
+ [n, s]
273
+ );
274
+ return { fraction: o, displayFraction: l, isDragging: r, onPointerDown: y };
275
+ }
276
+ export {
277
+ oe as a,
278
+ pe as b,
279
+ ke as c,
280
+ V as d,
281
+ de as e,
282
+ me as f,
283
+ ye as g,
284
+ ge as h,
285
+ ve as i,
286
+ ae as p,
287
+ se as u
288
+ };
289
+ //# sourceMappingURL=useSeekDrag-DkAJvf8H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSeekDrag-DkAJvf8H.js","sources":["../src/hooks/useGinger.ts","../src/analyzer/liveAudioGraph.ts","../src/analyzer/useGingerLiveAnalyzer.ts","../src/hooks/useGingerKeyboardShortcuts.ts","../src/hooks/useGingerChapters.ts","../src/internal/lyrics.ts","../src/hooks/useGingerLyricsSync.ts","../src/hooks/useGingerSleepTimer.ts","../src/hooks/useGingerDebugLog.ts","../src/hooks/useSeekDrag.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport { gingerStateFromContextValues, useGingerMedia, useGingerPlayback } from \"../context/GingerSplitContexts\";\nimport {\n derivePlaybackUiState,\n effectiveDuration,\n effectiveRemaining,\n getCurrentTrack,\n progressFraction,\n resolvedAlbumLine,\n resolvedArtwork,\n} from \"../internal/selectors\";\n\nexport function useGinger() {\n const pb = useGingerPlayback();\n const md = useGingerMedia();\n\n return useMemo(\n () => {\n const state = gingerStateFromContextValues(pb, md);\n return {\n state,\n currentTrack: getCurrentTrack(state),\n playbackUi: derivePlaybackUiState(state),\n duration: effectiveDuration(state),\n remaining: effectiveRemaining(state),\n progress: progressFraction(state),\n artworkUrl: resolvedArtwork(state),\n albumLine: resolvedAlbumLine(state),\n play: pb.play,\n pause: pb.pause,\n togglePlayPause: pb.togglePlayPause,\n seek: md.seek,\n setVolume: md.setVolume,\n setMuted: md.setMuted,\n toggleMute: md.toggleMute,\n setPlaybackRate: md.setPlaybackRate,\n next: pb.next,\n prev: pb.prev,\n setRepeatMode: pb.setRepeatMode,\n cycleRepeat: pb.cycleRepeat,\n toggleShuffle: pb.toggleShuffle,\n setQueue: pb.setQueue,\n insertTrackAt: pb.insertTrackAt,\n removeTrackAt: pb.removeTrackAt,\n moveTrack: pb.moveTrack,\n enqueueNext: pb.enqueueNext,\n playTrackAt: pb.playTrackAt,\n selectTrackAt: pb.selectTrackAt,\n setPlaylistMeta: pb.setPlaylistMeta,\n init: pb.init,\n audioRef: md.audioRef,\n dispatch: pb.dispatch,\n };\n },\n [pb, md],\n );\n}\n","/** One MediaElementAudioSourceNode per HTMLAudioElement; multiple AnalyserNodes may tap the source. */\n\nexport type LiveAnalyserOptions = {\n fftSize: number;\n smoothingTimeConstant: number;\n minDecibels: number;\n maxDecibels: number;\n};\n\ntype Consumer = {\n analyser: AnalyserNode;\n /** This analyser is wired to `audioContext.destination` so the graph is audible. */\n isPlaybackSink: boolean;\n};\n\ntype ElementEntry = {\n context: AudioContext;\n source: MediaElementAudioSourceNode;\n consumers: Map<number, Consumer>;\n nextId: number;\n};\n\nconst entries = new WeakMap<HTMLAudioElement, ElementEntry>();\n\nfunction clampFftSize(n: number): number {\n const p = 2 ** Math.round(Math.log2(n));\n return Math.min(32768, Math.max(32, p));\n}\n\nexport function attachLiveAnalyser(\n element: HTMLAudioElement,\n options: LiveAnalyserOptions,\n): { id: number; context: AudioContext; analyser: AnalyserNode } {\n let entry = entries.get(element);\n if (!entry) {\n const Context = window.AudioContext ?? (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!Context) {\n throw new Error(\"Web Audio API is not available\");\n }\n const context = new Context();\n const source = context.createMediaElementSource(element);\n entry = { context, source, consumers: new Map(), nextId: 0 };\n entries.set(element, entry);\n }\n\n const { context, source } = entry;\n const analyser = context.createAnalyser();\n analyser.fftSize = clampFftSize(options.fftSize);\n analyser.smoothingTimeConstant = options.smoothingTimeConstant;\n analyser.minDecibels = options.minDecibels;\n analyser.maxDecibels = options.maxDecibels;\n\n source.connect(analyser);\n\n const isFirst = entry.consumers.size === 0;\n if (isFirst) {\n analyser.connect(context.destination);\n }\n\n const id = entry.nextId;\n entry.nextId += 1;\n entry.consumers.set(id, { analyser, isPlaybackSink: isFirst });\n\n return { id, context, analyser };\n}\n\nexport function detachLiveAnalyser(element: HTMLAudioElement, id: number): void {\n const entry = entries.get(element);\n if (!entry) return;\n\n const consumer = entry.consumers.get(id);\n if (!consumer) return;\n\n const { analyser, isPlaybackSink } = consumer;\n analyser.disconnect();\n entry.consumers.delete(id);\n\n if (entry.consumers.size === 0) {\n try {\n entry.source.disconnect();\n } catch {\n // ignore\n }\n void entry.context.close();\n entries.delete(element);\n return;\n }\n\n if (isPlaybackSink) {\n const first = entry.consumers.values().next().value as Consumer | undefined;\n if (first) {\n first.analyser.connect(entry.context.destination);\n first.isPlaybackSink = true;\n }\n }\n}\n","import { useCallback, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport { useGinger } from \"../hooks/useGinger\";\nimport { attachLiveAnalyser, detachLiveAnalyser, type LiveAnalyserOptions } from \"./liveAudioGraph\";\n\nexport type UseGingerLiveAnalyzerOptions = {\n /** When false, the analyser is detached and no frames are read. Default true. */\n enabled?: boolean;\n fftSize?: number;\n smoothingTimeConstant?: number;\n minDecibels?: number;\n maxDecibels?: number;\n};\n\nexport type UseGingerLiveAnalyzerResult = {\n /** Byte frequency data (0–255); length equals `frequencyBinCount`. Updated each animation frame while enabled. */\n frequencyData: Uint8Array;\n /** Byte time-domain data (0–255); length equals `fftSize`. */\n timeDomainData: Uint8Array;\n frequencyBinCount: number;\n sampleRate: number;\n isSuspended: boolean;\n error: string | null;\n resume: () => Promise<void>;\n};\n\nconst emptyFreq = new Uint8Array(0);\nconst emptyTime = new Uint8Array(0);\n\nexport function useGingerLiveAnalyzer(options: UseGingerLiveAnalyzerOptions = {}): UseGingerLiveAnalyzerResult {\n const {\n enabled = true,\n fftSize = 2048,\n smoothingTimeConstant = 0.8,\n minDecibels = -100,\n maxDecibels = -30,\n } = options;\n\n const { audioRef, state } = useGinger();\n const opts = useMemo<LiveAnalyserOptions>(\n () => ({\n fftSize,\n smoothingTimeConstant,\n minDecibels,\n maxDecibels,\n }),\n [fftSize, smoothingTimeConstant, minDecibels, maxDecibels],\n );\n\n const [frame, setFrame] = useState(0);\n const [error, setError] = useState<string | null>(null);\n const [isSuspended, setIsSuspended] = useState(false);\n const [meta, setMeta] = useState({ frequencyBinCount: 0, sampleRate: 0 });\n\n const freqRef = useRef<Uint8Array>(emptyFreq);\n const timeRef = useRef<Uint8Array>(emptyTime);\n\n const resume = useCallback(async () => {\n const ctx = contextHolderRef.current;\n if (ctx && ctx.state === \"suspended\") {\n await ctx.resume();\n }\n }, []);\n\n const contextHolderRef = useRef<AudioContext | null>(null);\n const analyserHolderRef = useRef<AnalyserNode | null>(null);\n\n useLayoutEffect(() => {\n if (!enabled || typeof window === \"undefined\") {\n return;\n }\n\n let cancelled = false;\n let consumerId: number | null = null;\n let element: HTMLAudioElement | null = null;\n let rafId = 0;\n\n const onStateChange = () => {\n const ctx = contextHolderRef.current;\n if (ctx) setIsSuspended(ctx.state === \"suspended\");\n };\n\n const runLoop = () => {\n if (cancelled) return;\n const a = analyserHolderRef.current;\n const fq = freqRef.current;\n const td = timeRef.current;\n if (a && fq.length > 0 && td.length > 0) {\n a.getByteFrequencyData(fq as Uint8Array<ArrayBuffer>);\n a.getByteTimeDomainData(td as Uint8Array<ArrayBuffer>);\n setFrame((n) => n + 1);\n }\n rafId = requestAnimationFrame(runLoop);\n };\n\n type AttachOutcome = \"ok\" | \"no-element\" | \"error\";\n\n const attach = (): AttachOutcome => {\n const el = audioRef.current;\n if (!el || cancelled) return \"no-element\";\n try {\n const { id, context, analyser } = attachLiveAnalyser(el, opts);\n consumerId = id;\n element = el;\n contextHolderRef.current = context;\n analyserHolderRef.current = analyser;\n setIsSuspended(context.state === \"suspended\");\n setError(null);\n\n context.addEventListener(\"statechange\", onStateChange);\n\n const n = analyser.frequencyBinCount;\n const fft = analyser.fftSize;\n freqRef.current = new Uint8Array(n);\n timeRef.current = new Uint8Array(fft);\n setMeta({ frequencyBinCount: n, sampleRate: context.sampleRate });\n\n rafId = requestAnimationFrame(runLoop);\n return \"ok\";\n } catch (e) {\n const msg = e instanceof Error ? e.message : \"Failed to attach live analyser\";\n setError(msg);\n contextHolderRef.current = null;\n analyserHolderRef.current = null;\n freqRef.current = emptyFreq;\n timeRef.current = emptyTime;\n setMeta({ frequencyBinCount: 0, sampleRate: 0 });\n return \"error\";\n }\n };\n\n const first = attach();\n if (first !== \"ok\") {\n let retryRaf = 0;\n const maxAttempts = 120;\n let attempts = 0;\n\n const retryLoop = () => {\n if (cancelled) return;\n const out = attach();\n if (out === \"ok\" || out === \"error\") return;\n attempts += 1;\n if (attempts >= maxAttempts) return;\n retryRaf = requestAnimationFrame(retryLoop);\n };\n\n if (first === \"no-element\") {\n retryRaf = requestAnimationFrame(retryLoop);\n }\n\n return () => {\n cancelled = true;\n cancelAnimationFrame(retryRaf);\n cancelAnimationFrame(rafId);\n if (consumerId != null && element) {\n detachLiveAnalyser(element, consumerId);\n }\n contextHolderRef.current?.removeEventListener(\"statechange\", onStateChange);\n contextHolderRef.current = null;\n analyserHolderRef.current = null;\n freqRef.current = emptyFreq;\n timeRef.current = emptyTime;\n };\n }\n\n return () => {\n cancelled = true;\n cancelAnimationFrame(rafId);\n if (consumerId != null && element) {\n detachLiveAnalyser(element, consumerId);\n }\n contextHolderRef.current?.removeEventListener(\"statechange\", onStateChange);\n contextHolderRef.current = null;\n analyserHolderRef.current = null;\n freqRef.current = emptyFreq;\n timeRef.current = emptyTime;\n setMeta({ frequencyBinCount: 0, sampleRate: 0 });\n };\n }, [enabled, audioRef, opts, state.currentIndex]);\n\n void frame;\n\n return {\n frequencyData: freqRef.current,\n timeDomainData: timeRef.current,\n frequencyBinCount: meta.frequencyBinCount,\n sampleRate: meta.sampleRate,\n isSuspended,\n error,\n resume,\n };\n}\n","import { useEffect } from \"react\";\nimport { useGingerMedia, useGingerPlayback } from \"../context/GingerSplitContexts\";\n\nexport type GingerKeyboardShortcutBindings = {\n playPause?: string;\n next?: string;\n previous?: string;\n mute?: string;\n};\n\nexport function useGingerKeyboardShortcuts(\n enabled = true,\n bindings: GingerKeyboardShortcutBindings = {},\n): void {\n const { togglePlayPause, next, prev } = useGingerPlayback();\n const { toggleMute } = useGingerMedia();\n\n const muteBinding = bindings.mute;\n\n useEffect(() => {\n if (!enabled || typeof window === \"undefined\") return;\n const playPause = (bindings.playPause ?? \" \").toLowerCase();\n const nextKey = (bindings.next ?? \"ArrowRight\").toLowerCase();\n const prevKey = (bindings.previous ?? \"ArrowLeft\").toLowerCase();\n const muteKey = muteBinding?.toLowerCase();\n\n const onKeyDown = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement | null;\n if (target && ([\"INPUT\", \"TEXTAREA\", \"SELECT\"].includes(target.tagName) || target.isContentEditable)) return;\n const key = event.key.toLowerCase();\n if (key === playPause) {\n event.preventDefault();\n togglePlayPause();\n } else if (key === nextKey) {\n event.preventDefault();\n next();\n } else if (key === prevKey) {\n event.preventDefault();\n prev();\n } else if (muteKey && key === muteKey) {\n event.preventDefault();\n toggleMute();\n }\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, [bindings.next, bindings.playPause, bindings.previous, enabled, muteBinding, next, prev, toggleMute, togglePlayPause]);\n}\n","import { useMemo } from \"react\";\nimport { useGingerMedia, useGingerPlayback } from \"../context/GingerSplitContexts\";\n\nexport type GingerChapter = {\n title: string;\n startSeconds: number;\n};\n\nexport type GingerChapterState = {\n list: GingerChapter[];\n activeIndex: number;\n active: GingerChapter | null;\n seekTo: (index: number) => void;\n};\n\nexport function useGingerChapters(): GingerChapterState {\n const { tracks, currentIndex } = useGingerPlayback();\n const { currentTime, seek } = useGingerMedia();\n\n const list = useMemo(() => {\n const chapters = tracks[currentIndex]?.chapters ?? [];\n return [...chapters]\n .filter((item) => item && Number.isFinite(item.startSeconds) && item.startSeconds >= 0)\n .sort((a, b) => a.startSeconds - b.startSeconds);\n }, [currentIndex, tracks]);\n\n const activeIndex = useMemo(() => {\n if (list.length === 0) return -1;\n for (let i = list.length - 1; i >= 0; i -= 1) {\n if (currentTime >= list[i]!.startSeconds) return i;\n }\n return -1;\n }, [currentTime, list]);\n\n return {\n list,\n activeIndex,\n active: activeIndex >= 0 ? list[activeIndex] ?? null : null,\n seekTo: (index: number) => {\n const chapter = list[index];\n if (chapter) seek(chapter.startSeconds);\n },\n };\n}\n","export type TimedLyricLine = {\n time: number;\n text: string;\n};\n\nconst lrcTag = /\\[(\\d{1,2}):(\\d{2})(?:\\.(\\d{1,3}))?\\]/g;\n\nexport function parseLrc(lrc: string): TimedLyricLine[] {\n const lines: TimedLyricLine[] = [];\n for (const rawLine of lrc.split(/\\r?\\n/)) {\n const matches = [...rawLine.matchAll(lrcTag)];\n if (matches.length === 0) continue;\n const text = rawLine.replace(lrcTag, \"\").trim();\n for (const m of matches) {\n const minutes = Number(m[1] ?? 0);\n const seconds = Number(m[2] ?? 0);\n const millis = Number((m[3] ?? \"0\").padEnd(3, \"0\"));\n const time = minutes * 60 + seconds + millis / 1000;\n if (Number.isFinite(time) && time >= 0) {\n lines.push({ time, text });\n }\n }\n }\n return lines.sort((a, b) => a.time - b.time);\n}\n","import { useMemo } from \"react\";\nimport { useGingerMedia, useGingerPlayback } from \"../context/GingerSplitContexts\";\nimport { parseLrc, type TimedLyricLine } from \"../internal/lyrics\";\n\nexport type GingerLyricsSyncState = {\n lines: TimedLyricLine[];\n activeIndex: number;\n activeLine: TimedLyricLine | null;\n};\n\nexport function useGingerLyricsSync(): GingerLyricsSyncState {\n const { tracks, currentIndex } = useGingerPlayback();\n const { currentTime } = useGingerMedia();\n const currentTrack = tracks[currentIndex];\n\n const lines = useMemo(() => {\n if (!currentTrack) return [];\n if (Array.isArray(currentTrack.lyricsTimed) && currentTrack.lyricsTimed.length > 0) {\n return [...currentTrack.lyricsTimed]\n .filter((line) => Number.isFinite(line.time) && line.time >= 0)\n .sort((a, b) => a.time - b.time);\n }\n if (typeof currentTrack.lyrics === \"string\") {\n return parseLrc(currentTrack.lyrics);\n }\n return [];\n }, [currentTrack]);\n\n const activeIndex = useMemo(() => {\n for (let i = lines.length - 1; i >= 0; i -= 1) {\n if (currentTime >= lines[i]!.time) return i;\n }\n return -1;\n }, [currentTime, lines]);\n\n return {\n lines,\n activeIndex,\n activeLine: activeIndex >= 0 ? lines[activeIndex] ?? null : null,\n };\n}\n","import { useEffect, useRef } from \"react\";\nimport { useGingerPlayback } from \"../context/GingerSplitContexts\";\n\nexport type GingerSleepTimerOptions = {\n durationMs?: number;\n stopAfterTracks?: number;\n respectPause?: boolean;\n enabled?: boolean;\n onFire?: () => void;\n};\n\nexport function useGingerSleepTimer(options: GingerSleepTimerOptions): void {\n const { durationMs, stopAfterTracks, respectPause = true, enabled = true, onFire } = options;\n const { currentIndex, pause, isPaused } = useGingerPlayback();\n const remainingTracksRef = useRef(stopAfterTracks ?? 0);\n const prevIndexRef = useRef(currentIndex);\n\n useEffect(() => {\n remainingTracksRef.current = stopAfterTracks ?? 0;\n }, [stopAfterTracks]);\n\n useEffect(() => {\n if (!enabled || !durationMs || durationMs <= 0) return;\n if (respectPause && isPaused) return;\n const id = setTimeout(() => {\n pause();\n onFire?.();\n }, durationMs);\n return () => clearTimeout(id);\n }, [durationMs, enabled, isPaused, onFire, pause, respectPause]);\n\n useEffect(() => {\n if (!enabled || !stopAfterTracks || stopAfterTracks <= 0) return;\n const prev = prevIndexRef.current;\n prevIndexRef.current = currentIndex;\n if (currentIndex === prev) return;\n remainingTracksRef.current -= 1;\n if (remainingTracksRef.current <= 0) {\n pause();\n onFire?.();\n }\n }, [currentIndex, enabled, onFire, pause, stopAfterTracks]);\n}\n","import { useEffect, useRef } from \"react\";\nimport { useGingerState } from \"../context/GingerSplitContexts\";\n\nexport function useGingerDebugLog(enabled = false): void {\n const state = useGingerState();\n const prevRef = useRef(state);\n\n useEffect(() => {\n if (!enabled || typeof console === \"undefined\") return;\n const prev = prevRef.current;\n if (prev !== state) {\n console.debug(\"[ginger]\", {\n from: {\n currentIndex: prev.currentIndex,\n isPaused: prev.isPaused,\n currentTime: prev.currentTime,\n repeatMode: prev.repeatMode,\n },\n to: {\n currentIndex: state.currentIndex,\n isPaused: state.isPaused,\n currentTime: state.currentTime,\n repeatMode: state.repeatMode,\n },\n });\n }\n prevRef.current = state;\n }, [enabled, state]);\n}\n","import { useCallback, useState } from \"react\";\nimport type { PointerEvent as ReactPointerEvent } from \"react\";\nimport { useGingerMedia, useGingerPlayback, gingerStateFromContextValues } from \"../context/GingerSplitContexts\";\nimport { progressFraction } from \"../internal/selectors\";\n\nexport type SeekDragState = {\n /** Raw drag fraction — only updated during an active drag gesture. */\n fraction: number;\n /** Blended fraction: follows live playback when idle, drag position when dragging. */\n displayFraction: number;\n isDragging: boolean;\n onPointerDown: (event: ReactPointerEvent<HTMLElement>) => void;\n};\n\nfunction clamp01(value: number): number {\n return Math.max(0, Math.min(1, value));\n}\n\nexport function useSeekDrag(duration: number): SeekDragState {\n const media = useGingerMedia();\n const playback = useGingerPlayback();\n const { seek } = media;\n const [fraction, setFraction] = useState(0);\n const [isDragging, setIsDragging] = useState(false);\n\n const liveFraction = progressFraction(gingerStateFromContextValues(playback, media));\n const displayFraction = isDragging ? fraction : liveFraction;\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent<HTMLElement>) => {\n if (!(duration > 0)) return;\n const target = event.currentTarget;\n const rect = target.getBoundingClientRect();\n const update = (clientX: number) => {\n const ratio = clamp01((clientX - rect.left) / rect.width);\n setFraction(ratio);\n seek(ratio * duration);\n };\n setIsDragging(true);\n target.setPointerCapture(event.pointerId);\n update(event.clientX);\n const onMove = (moveEvent: PointerEvent) => update(moveEvent.clientX);\n const onUp = (upEvent: PointerEvent) => {\n update(upEvent.clientX);\n setIsDragging(false);\n target.releasePointerCapture(event.pointerId);\n target.removeEventListener(\"pointermove\", onMove);\n target.removeEventListener(\"pointerup\", onUp);\n target.removeEventListener(\"pointercancel\", onUp);\n };\n target.addEventListener(\"pointermove\", onMove);\n target.addEventListener(\"pointerup\", onUp);\n target.addEventListener(\"pointercancel\", onUp);\n },\n [duration, seek],\n );\n\n return { fraction, displayFraction, isDragging, onPointerDown };\n}\n"],"names":["useGinger","pb","useGingerPlayback","md","useGingerMedia","useMemo","state","gingerStateFromContextValues","getCurrentTrack","derivePlaybackUiState","effectiveDuration","effectiveRemaining","progressFraction","resolvedArtwork","resolvedAlbumLine","entries","clampFftSize","p","attachLiveAnalyser","element","options","entry","Context","context","source","analyser","isFirst","id","detachLiveAnalyser","consumer","isPlaybackSink","first","emptyFreq","emptyTime","useGingerLiveAnalyzer","enabled","fftSize","smoothingTimeConstant","minDecibels","maxDecibels","audioRef","opts","frame","setFrame","useState","error","setError","isSuspended","setIsSuspended","meta","setMeta","freqRef","useRef","timeRef","resume","useCallback","ctx","contextHolderRef","analyserHolderRef","useLayoutEffect","cancelled","consumerId","rafId","onStateChange","runLoop","a","fq","td","n","attach","el","fft","e","msg","retryRaf","maxAttempts","attempts","retryLoop","out","_a","useGingerKeyboardShortcuts","bindings","togglePlayPause","next","prev","toggleMute","muteBinding","useEffect","playPause","nextKey","prevKey","muteKey","onKeyDown","event","target","key","useGingerChapters","tracks","currentIndex","currentTime","seek","list","item","b","activeIndex","i","index","chapter","lrcTag","parseLrc","lrc","lines","rawLine","matches","text","m","minutes","seconds","millis","time","useGingerLyricsSync","currentTrack","line","useGingerSleepTimer","durationMs","stopAfterTracks","respectPause","onFire","pause","isPaused","remainingTracksRef","prevIndexRef","useGingerDebugLog","useGingerState","prevRef","clamp01","value","useSeekDrag","duration","media","playback","fraction","setFraction","isDragging","setIsDragging","liveFraction","displayFraction","onPointerDown","rect","update","clientX","ratio","onMove","moveEvent","onUp","upEvent"],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ export type AnalyzeAudioFileOptions = {
2
+ /** Number of time rows in the amplitude grid (and spectrogram if enabled). Default 128. */
3
+ timeSlices?: number;
4
+ /** Sub-buckets per time slice for the amplitude grid. Default 8. */
5
+ samplesPerSlice?: number;
6
+ /** When true, include `spectrogram` using windowed FFT per time slice. Default false. */
7
+ spectrogram?: boolean;
8
+ /** FFT length for spectrogram (power of 2). Default 1024. */
9
+ fftSize?: number;
10
+ /** Number of frequency bins to keep per row (first bins; capped by fftSize/2). Default 256. */
11
+ frequencyBins?: number;
12
+ /** Channel index, or `"mix"` for equal mix of all channels. Default 0. */
13
+ channel?: number | "mix";
14
+ };
15
+ export type AudioFileAnalysis = {
16
+ duration: number;
17
+ sampleRate: number;
18
+ /** Peak amplitudes in [0, 1]: `timeSlices` rows × `samplesPerSlice` columns. */
19
+ amplitudeGrid: number[][];
20
+ /** Optional magnitude spectrogram rows, each length `frequencyBins`, normalized to [0, 1] globally. */
21
+ spectrogram?: number[][];
22
+ };
23
+ /**
24
+ * Decodes an `AudioBuffer` into visualization-friendly grids (no network).
25
+ */
26
+ export declare function analyzeAudioBuffer(buffer: AudioBuffer, options?: AnalyzeAudioFileOptions): AudioFileAnalysis;
27
+ /**
28
+ * Fetches a URL, decodes audio to an `AudioBuffer`, runs {@link analyzeAudioBuffer}, then closes the temporary `AudioContext`.
29
+ */
30
+ export declare function analyzeAudioFile(fileUrl: string, options?: AnalyzeAudioFileOptions): Promise<AudioFileAnalysis>;
31
+ //# sourceMappingURL=analyzeAudioFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzeAudioFile.d.ts","sourceRoot":"","sources":["../../src/waveform/analyzeAudioFile.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,uBAAuB,GAAG;IACpC,2FAA2F;IAC3F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yFAAyF;IACzF,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+FAA+F;IAC/F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,aAAa,EAAE,MAAM,EAAE,EAAE,CAAC;IAC1B,uGAAuG;IACvG,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;CAC1B,CAAC;AAwGF;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,GAAE,uBAA4B,GAAG,iBAAiB,CAwBhH;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,iBAAiB,CAAC,CAiB5B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=analyzeAudioFile.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzeAudioFile.test.d.ts","sourceRoot":"","sources":["../../src/waveform/analyzeAudioFile.test.ts"],"names":[],"mappings":""}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("react");function k(a,o=64){const[g,t]=h.useState({peaks:[],isLoading:!1,error:null});return h.useEffect(()=>{if(!a){t({peaks:[],isLoading:!1,error:null});return}let r=!1;return t(e=>({...e,isLoading:!0,error:null})),(async()=>{try{const e=await fetch(a);if(!e.ok)throw new Error(`Fetch failed: ${e.status} ${e.statusText}`);const p=await e.arrayBuffer(),c=new AudioContext,s=(await c.decodeAudioData(p)).getChannelData(0),l=Math.max(1,Math.floor(s.length/o)),f=[];for(let n=0;n<o;n+=1){let i=0;const d=n*l,w=Math.min(s.length,d+l);for(let u=d;u<w;u+=1)i=Math.max(i,Math.abs(s[u]??0));f.push(i)}await c.close(),r||t({peaks:f,isLoading:!1,error:null})}catch(e){r||t({peaks:[],isLoading:!1,error:e instanceof Error?e.message:"Failed to decode peaks"})}})(),()=>{r=!0}},[o,a]),g}exports.useAudioPeaks=k;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("react");function F(e,t=64){const[r,a]=y.useState({peaks:[],isLoading:!1,error:null});return y.useEffect(()=>{if(!e){a({peaks:[],isLoading:!1,error:null});return}let o=!1;return a(n=>({...n,isLoading:!0,error:null})),(async()=>{try{const n=await fetch(e);if(!n.ok)throw new Error(`Fetch failed: ${n.status} ${n.statusText}`);const l=await n.arrayBuffer(),s=new AudioContext,u=(await s.decodeAudioData(l)).getChannelData(0),w=Math.max(1,Math.floor(u.length/t)),f=[];for(let d=0;d<t;d+=1){let i=0;const h=d*w,M=Math.min(u.length,h+w);for(let c=h;c<M;c+=1)i=Math.max(i,Math.abs(u[c]??0));f.push(i)}await s.close(),o||a({peaks:f,isLoading:!1,error:null})}catch(n){o||a({peaks:[],isLoading:!1,error:n instanceof Error?n.message:"Failed to decode peaks"})}})(),()=>{o=!0}},[t,e]),r}function L(e,t){const r=e.length;if(r!==t.length||r<2||r&r-1)throw new Error("fftInPlace: length must be equal powers of 2 >= 2");let a=0;for(let o=0;o<r-1;o+=1){if(o<a){const l=e[o],s=t[o];e[o]=e[a],t[o]=t[a],e[a]=l,t[a]=s}let n=r>>1;for(;n<=a;)a-=n,n>>=1;a+=n}for(let o=2;o<=r;o<<=1){const n=-2*Math.PI/o,l=Math.cos(n),s=Math.sin(n);for(let g=0;g<r;g+=o){let u=1,w=0;const f=o>>1;for(let d=0;d<f;d+=1){const i=g+d,h=i+f,M=u*e[h]-w*t[h],c=u*t[h]+w*e[h];e[h]=e[i]-M,t[h]=t[i]-c,e[i]=e[i]+M,t[i]=t[i]+c;const m=u*l-w*s,p=u*s+w*l;u=m,w=p}}}}function C(e){const t=e.length;if(t<2||t&t-1)throw new Error("realFftMagnitudes: length must be a power of 2 >= 2");const r=new Float64Array(t),a=new Float64Array(t);for(let n=0;n<t;n+=1)r[n]=e[n];L(r,a);const o=new Float64Array(t>>1);for(let n=0;n<t>>1;n+=1)o[n]=Math.hypot(r[n],a[n]);return o}function k(e){const t=new Float64Array(e);if(e===1)return t[0]=1,t;const r=e-1;for(let a=0;a<e;a+=1)t[a]=.5*(1-Math.cos(2*Math.PI*a/r));return t}function S(e){const t=2**Math.round(Math.log2(e));return Math.min(8192,Math.max(32,t))}function b(e){const{numberOfChannels:t,length:r}=e;if(t===1)return e.getChannelData(0);const a=new Float32Array(r),o=1/t;for(let n=0;n<t;n+=1){const l=e.getChannelData(n);for(let s=0;s<r;s+=1)a[s]+=l[s]*o}return a}function B(e,t){if(t==="mix")return b(e);const r=Math.max(0,Math.min(e.numberOfChannels-1,t));return e.getChannelData(r)}function E(e,t,r){const a=[],o=e.length;if(o===0){for(let l=0;l<t;l+=1)a.push(Array.from({length:r},()=>0));return a}const n=o/t;for(let l=0;l<t;l+=1){const s=[],g=Math.floor(l*n),u=Math.floor((l+1)*n),f=Math.max(1,u-g)/r;for(let d=0;d<r;d+=1){const i=Math.floor(g+d*f),h=Math.min(u,Math.floor(g+(d+1)*f));let M=0;for(let c=i;c<h;c+=1)M=Math.max(M,Math.abs(e[c]??0));s.push(M)}a.push(s)}return a}function z(e,t,r,a){const o=[];let n=1e-12;const l=e.length,s=S(r),g=s>>1,u=Math.min(a,g),w=k(s);if(l<s){for(let f=0;f<t;f+=1)o.push(Array.from({length:u},()=>0));return{rows:o,maxMag:1}}for(let f=0;f<t;f+=1){const d=t<=1?0:Math.min(Math.floor(f*(l-s)/(t-1)),l-s),i=new Float64Array(s);for(let c=0;c<s;c+=1)i[c]=(e[d+c]??0)*(w[c]??0);const h=C(i),M=[];for(let c=0;c<u;c+=1){const m=h[c]??0;M.push(m),n=Math.max(n,m)}o.push(M)}return{rows:o,maxMag:n}}function A(e,t={}){const r=Math.max(1,t.timeSlices??128),a=Math.max(1,t.samplesPerSlice??8),o=!!t.spectrogram,n=t.fftSize??1024,l=Math.max(1,t.frequencyBins??256),s=t.channel??0,g=B(e,s),u=E(g,r,a),w={duration:e.duration,sampleRate:e.sampleRate,amplitudeGrid:u};if(o){const{rows:f,maxMag:d}=z(g,r,n,l),i=d>0?1/d:1;w.spectrogram=f.map(h=>h.map(M=>M*i))}return w}async function x(e,t={}){const r=await fetch(e);if(!r.ok)throw new Error(`Fetch failed: ${r.status} ${r.statusText}`);const a=await r.arrayBuffer(),o=window.AudioContext??window.webkitAudioContext;if(!o)throw new Error("Web Audio API is not available");const n=new o;try{const l=await n.decodeAudioData(a.slice(0));return A(l,t)}finally{await n.close()}}function P(e,t={}){const[r,a]=y.useState({data:null,isLoading:!1,error:null});return y.useEffect(()=>{if(!e){a({data:null,isLoading:!1,error:null});return}if(typeof window>"u"){a({data:null,isLoading:!1,error:null});return}let o=!1;return a(n=>({...n,isLoading:!0,error:null})),(async()=>{try{const n=await x(e,t);o||a({data:n,isLoading:!1,error:null})}catch(n){o||a({data:null,isLoading:!1,error:n instanceof Error?n.message:"Failed to analyze audio file"})}})(),()=>{o=!0}},[e,t.timeSlices,t.samplesPerSlice,t.spectrogram,t.fftSize,t.frequencyBins,t.channel]),r}exports.analyzeAudioBuffer=A;exports.analyzeAudioFile=x;exports.useAudioFileAnalysis=P;exports.useAudioPeaks=F;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/waveform/useAudioPeaks.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nexport type UseAudioPeaksState = {\n peaks: number[];\n isLoading: boolean;\n error: string | null;\n};\n\nexport function useAudioPeaks(fileUrl: string | null | undefined, buckets = 64): UseAudioPeaksState {\n const [state, setState] = useState<UseAudioPeaksState>({\n peaks: [],\n isLoading: false,\n error: null,\n });\n\n useEffect(() => {\n if (!fileUrl) {\n setState({ peaks: [], isLoading: false, error: null });\n return;\n }\n let cancelled = false;\n setState((prev) => ({ ...prev, isLoading: true, error: null }));\n void (async () => {\n try {\n const response = await fetch(fileUrl);\n if (!response.ok) throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);\n const buffer = await response.arrayBuffer();\n const audioContext = new AudioContext();\n const audioBuffer = await audioContext.decodeAudioData(buffer);\n const channel = audioBuffer.getChannelData(0);\n const step = Math.max(1, Math.floor(channel.length / buckets));\n const peaks: number[] = [];\n for (let i = 0; i < buckets; i += 1) {\n let max = 0;\n const start = i * step;\n const end = Math.min(channel.length, start + step);\n for (let j = start; j < end; j += 1) {\n max = Math.max(max, Math.abs(channel[j] ?? 0));\n }\n peaks.push(max);\n }\n await audioContext.close();\n if (!cancelled) {\n setState({ peaks, isLoading: false, error: null });\n }\n } catch (error) {\n if (!cancelled) {\n setState({\n peaks: [],\n isLoading: false,\n error: error instanceof Error ? error.message : \"Failed to decode peaks\",\n });\n }\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [buckets, fileUrl]);\n\n return state;\n}\n"],"names":["useAudioPeaks","fileUrl","buckets","state","setState","useState","useEffect","cancelled","prev","response","buffer","audioContext","channel","step","peaks","i","max","start","end","j","error"],"mappings":"yGAQO,SAASA,EAAcC,EAAoCC,EAAU,GAAwB,CAClG,KAAM,CAACC,EAAOC,CAAQ,EAAIC,WAA6B,CACrD,MAAO,CAAA,EACP,UAAW,GACX,MAAO,IAAA,CACR,EAEDC,OAAAA,EAAAA,UAAU,IAAM,CACd,GAAI,CAACL,EAAS,CACZG,EAAS,CAAE,MAAO,CAAA,EAAI,UAAW,GAAO,MAAO,KAAM,EACrD,MACF,CACA,IAAIG,EAAY,GAChB,OAAAH,EAAUI,IAAU,CAAE,GAAGA,EAAM,UAAW,GAAM,MAAO,IAAA,EAAO,GACxD,SAAY,CAChB,GAAI,CACF,MAAMC,EAAW,MAAM,MAAMR,CAAO,EACpC,GAAI,CAACQ,EAAS,GAAI,MAAM,IAAI,MAAM,iBAAiBA,EAAS,MAAM,IAAIA,EAAS,UAAU,EAAE,EAC3F,MAAMC,EAAS,MAAMD,EAAS,YAAA,EACxBE,EAAe,IAAI,aAEnBC,GADc,MAAMD,EAAa,gBAAgBD,CAAM,GACjC,eAAe,CAAC,EACtCG,EAAO,KAAK,IAAI,EAAG,KAAK,MAAMD,EAAQ,OAASV,CAAO,CAAC,EACvDY,EAAkB,CAAA,EACxB,QAASC,EAAI,EAAGA,EAAIb,EAASa,GAAK,EAAG,CACnC,IAAIC,EAAM,EACV,MAAMC,EAAQF,EAAIF,EACZK,EAAM,KAAK,IAAIN,EAAQ,OAAQK,EAAQJ,CAAI,EACjD,QAASM,EAAIF,EAAOE,EAAID,EAAKC,GAAK,EAChCH,EAAM,KAAK,IAAIA,EAAK,KAAK,IAAIJ,EAAQO,CAAC,GAAK,CAAC,CAAC,EAE/CL,EAAM,KAAKE,CAAG,CAChB,CACA,MAAML,EAAa,MAAA,EACdJ,GACHH,EAAS,CAAE,MAAAU,EAAO,UAAW,GAAO,MAAO,KAAM,CAErD,OAASM,EAAO,CACTb,GACHH,EAAS,CACP,MAAO,CAAA,EACP,UAAW,GACX,MAAOgB,aAAiB,MAAQA,EAAM,QAAU,wBAAA,CACjD,CAEL,CACF,GAAA,EAEO,IAAM,CACXb,EAAY,EACd,CACF,EAAG,CAACL,EAASD,CAAO,CAAC,EAEdE,CACT"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/waveform/useAudioPeaks.ts","../../src/internal/fft.ts","../../src/waveform/analyzeAudioFile.ts","../../src/waveform/useAudioFileAnalysis.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nexport type UseAudioPeaksState = {\n peaks: number[];\n isLoading: boolean;\n error: string | null;\n};\n\nexport function useAudioPeaks(fileUrl: string | null | undefined, buckets = 64): UseAudioPeaksState {\n const [state, setState] = useState<UseAudioPeaksState>({\n peaks: [],\n isLoading: false,\n error: null,\n });\n\n useEffect(() => {\n if (!fileUrl) {\n setState({ peaks: [], isLoading: false, error: null });\n return;\n }\n let cancelled = false;\n setState((prev) => ({ ...prev, isLoading: true, error: null }));\n void (async () => {\n try {\n const response = await fetch(fileUrl);\n if (!response.ok) throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);\n const buffer = await response.arrayBuffer();\n const audioContext = new AudioContext();\n const audioBuffer = await audioContext.decodeAudioData(buffer);\n const channel = audioBuffer.getChannelData(0);\n const step = Math.max(1, Math.floor(channel.length / buckets));\n const peaks: number[] = [];\n for (let i = 0; i < buckets; i += 1) {\n let max = 0;\n const start = i * step;\n const end = Math.min(channel.length, start + step);\n for (let j = start; j < end; j += 1) {\n max = Math.max(max, Math.abs(channel[j] ?? 0));\n }\n peaks.push(max);\n }\n await audioContext.close();\n if (!cancelled) {\n setState({ peaks, isLoading: false, error: null });\n }\n } catch (error) {\n if (!cancelled) {\n setState({\n peaks: [],\n isLoading: false,\n error: error instanceof Error ? error.message : \"Failed to decode peaks\",\n });\n }\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [buckets, fileUrl]);\n\n return state;\n}\n","/** In-place radix-2 Cooley–Tukey FFT; `length` must be a power of 2 and >= 2. */\nexport function fftInPlace(re: Float64Array, im: Float64Array): void {\n const n = re.length;\n if (n !== im.length || n < 2 || (n & (n - 1)) !== 0) {\n throw new Error(\"fftInPlace: length must be equal powers of 2 >= 2\");\n }\n\n let j = 0;\n for (let i = 0; i < n - 1; i += 1) {\n if (i < j) {\n const tr = re[i]!;\n const ti = im[i]!;\n re[i] = re[j]!;\n im[i] = im[j]!;\n re[j] = tr;\n im[j] = ti;\n }\n let k = n >> 1;\n while (k <= j) {\n j -= k;\n k >>= 1;\n }\n j += k;\n }\n\n for (let len = 2; len <= n; len <<= 1) {\n const ang = (-2 * Math.PI) / len;\n const wlenR = Math.cos(ang);\n const wlenI = Math.sin(ang);\n for (let i = 0; i < n; i += len) {\n let wr = 1;\n let wi = 0;\n const half = len >> 1;\n for (let k = 0; k < half; k += 1) {\n const u = i + k;\n const v = u + half;\n const tr = wr * re[v]! - wi * im[v]!;\n const ti = wr * im[v]! + wi * re[v]!;\n re[v] = re[u]! - tr;\n im[v] = im[u]! - ti;\n re[u] = re[u]! + tr;\n im[u] = im[u]! + ti;\n const nwr = wr * wlenR - wi * wlenI;\n const nwi = wr * wlenI + wi * wlenR;\n wr = nwr;\n wi = nwi;\n }\n }\n }\n}\n\n/** Magnitude spectrum for real input: length `n` (power of 2). Returns `n/2` magnitudes for bins 0..n/2-1. */\nexport function realFftMagnitudes(samples: Float64Array): Float64Array {\n const n = samples.length;\n if (n < 2 || (n & (n - 1)) !== 0) {\n throw new Error(\"realFftMagnitudes: length must be a power of 2 >= 2\");\n }\n const re = new Float64Array(n);\n const im = new Float64Array(n);\n for (let i = 0; i < n; i += 1) re[i] = samples[i]!;\n fftInPlace(re, im);\n const out = new Float64Array(n >> 1);\n for (let k = 0; k < n >> 1; k += 1) {\n out[k] = Math.hypot(re[k]!, im[k]!);\n }\n return out;\n}\n\nexport function hanningWindow(length: number): Float64Array {\n const w = new Float64Array(length);\n if (length === 1) {\n w[0] = 1;\n return w;\n }\n const denom = length - 1;\n for (let i = 0; i < length; i += 1) {\n w[i] = 0.5 * (1 - Math.cos((2 * Math.PI * i) / denom));\n }\n return w;\n}\n\nexport function clampFftSize(n: number): number {\n const p = 2 ** Math.round(Math.log2(n));\n return Math.min(8192, Math.max(32, p));\n}\n","import { clampFftSize, hanningWindow, realFftMagnitudes } from \"../internal/fft\";\n\nexport type AnalyzeAudioFileOptions = {\n /** Number of time rows in the amplitude grid (and spectrogram if enabled). Default 128. */\n timeSlices?: number;\n /** Sub-buckets per time slice for the amplitude grid. Default 8. */\n samplesPerSlice?: number;\n /** When true, include `spectrogram` using windowed FFT per time slice. Default false. */\n spectrogram?: boolean;\n /** FFT length for spectrogram (power of 2). Default 1024. */\n fftSize?: number;\n /** Number of frequency bins to keep per row (first bins; capped by fftSize/2). Default 256. */\n frequencyBins?: number;\n /** Channel index, or `\"mix\"` for equal mix of all channels. Default 0. */\n channel?: number | \"mix\";\n};\n\nexport type AudioFileAnalysis = {\n duration: number;\n sampleRate: number;\n /** Peak amplitudes in [0, 1]: `timeSlices` rows × `samplesPerSlice` columns. */\n amplitudeGrid: number[][];\n /** Optional magnitude spectrogram rows, each length `frequencyBins`, normalized to [0, 1] globally. */\n spectrogram?: number[][];\n};\n\nfunction mixChannels(buffer: AudioBuffer): Float32Array {\n const { numberOfChannels, length } = buffer;\n if (numberOfChannels === 1) {\n return buffer.getChannelData(0);\n }\n const out = new Float32Array(length);\n const scale = 1 / numberOfChannels;\n for (let c = 0; c < numberOfChannels; c += 1) {\n const ch = buffer.getChannelData(c);\n for (let i = 0; i < length; i += 1) {\n out[i] += ch[i]! * scale;\n }\n }\n return out;\n}\n\nfunction getChannel(buffer: AudioBuffer, channel: number | \"mix\"): Float32Array {\n if (channel === \"mix\") return mixChannels(buffer);\n const idx = Math.max(0, Math.min(buffer.numberOfChannels - 1, channel));\n return buffer.getChannelData(idx);\n}\n\nfunction buildAmplitudeGrid(\n channel: Float32Array,\n timeSlices: number,\n samplesPerSlice: number,\n): number[][] {\n const grid: number[][] = [];\n const len = channel.length;\n if (len === 0) {\n for (let t = 0; t < timeSlices; t += 1) {\n grid.push(Array.from({ length: samplesPerSlice }, () => 0));\n }\n return grid;\n }\n\n const segmentLen = len / timeSlices;\n\n for (let t = 0; t < timeSlices; t += 1) {\n const row: number[] = [];\n const segStart = Math.floor(t * segmentLen);\n const segEnd = Math.floor((t + 1) * segmentLen);\n const segLen = Math.max(1, segEnd - segStart);\n const subLen = segLen / samplesPerSlice;\n\n for (let s = 0; s < samplesPerSlice; s += 1) {\n const a = Math.floor(segStart + s * subLen);\n const b = Math.min(segEnd, Math.floor(segStart + (s + 1) * subLen));\n let peak = 0;\n for (let i = a; i < b; i += 1) {\n peak = Math.max(peak, Math.abs(channel[i] ?? 0));\n }\n row.push(peak);\n }\n grid.push(row);\n }\n return grid;\n}\n\nfunction buildSpectrogram(\n channel: Float32Array,\n timeSlices: number,\n fftSize: number,\n frequencyBins: number,\n): { rows: number[][]; maxMag: number } {\n const rows: number[][] = [];\n let maxMag = 1e-12;\n const len = channel.length;\n const n = clampFftSize(fftSize);\n const half = n >> 1;\n const bins = Math.min(frequencyBins, half);\n const window = hanningWindow(n);\n\n if (len < n) {\n for (let t = 0; t < timeSlices; t += 1) {\n rows.push(Array.from({ length: bins }, () => 0));\n }\n return { rows, maxMag: 1 };\n }\n\n for (let t = 0; t < timeSlices; t += 1) {\n const start =\n timeSlices <= 1\n ? 0\n : Math.min(Math.floor((t * (len - n)) / (timeSlices - 1)), len - n);\n const frame = new Float64Array(n);\n for (let i = 0; i < n; i += 1) {\n frame[i] = (channel[start + i] ?? 0) * (window[i] ?? 0);\n }\n const mags = realFftMagnitudes(frame);\n const row: number[] = [];\n for (let k = 0; k < bins; k += 1) {\n const v = mags[k] ?? 0;\n row.push(v);\n maxMag = Math.max(maxMag, v);\n }\n rows.push(row);\n }\n\n return { rows, maxMag };\n}\n\n/**\n * Decodes an `AudioBuffer` into visualization-friendly grids (no network).\n */\nexport function analyzeAudioBuffer(buffer: AudioBuffer, options: AnalyzeAudioFileOptions = {}): AudioFileAnalysis {\n const timeSlices = Math.max(1, options.timeSlices ?? 128);\n const samplesPerSlice = Math.max(1, options.samplesPerSlice ?? 8);\n const wantSpec = Boolean(options.spectrogram);\n const fftSize = options.fftSize ?? 1024;\n const frequencyBins = Math.max(1, options.frequencyBins ?? 256);\n const channel = options.channel ?? 0;\n\n const data = getChannel(buffer, channel);\n const amplitudeGrid = buildAmplitudeGrid(data, timeSlices, samplesPerSlice);\n\n const result: AudioFileAnalysis = {\n duration: buffer.duration,\n sampleRate: buffer.sampleRate,\n amplitudeGrid,\n };\n\n if (wantSpec) {\n const { rows, maxMag } = buildSpectrogram(data, timeSlices, fftSize, frequencyBins);\n const norm = maxMag > 0 ? 1 / maxMag : 1;\n result.spectrogram = rows.map((row) => row.map((v) => v * norm));\n }\n\n return result;\n}\n\n/**\n * Fetches a URL, decodes audio to an `AudioBuffer`, runs {@link analyzeAudioBuffer}, then closes the temporary `AudioContext`.\n */\nexport async function analyzeAudioFile(\n fileUrl: string,\n options: AnalyzeAudioFileOptions = {},\n): Promise<AudioFileAnalysis> {\n const response = await fetch(fileUrl);\n if (!response.ok) {\n throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);\n }\n const raw = await response.arrayBuffer();\n const Context = window.AudioContext ?? (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!Context) {\n throw new Error(\"Web Audio API is not available\");\n }\n const audioContext = new Context();\n try {\n const buffer = await audioContext.decodeAudioData(raw.slice(0));\n return analyzeAudioBuffer(buffer, options);\n } finally {\n await audioContext.close();\n }\n}\n","import { useEffect, useState } from \"react\";\nimport { analyzeAudioFile, type AnalyzeAudioFileOptions, type AudioFileAnalysis } from \"./analyzeAudioFile\";\n\nexport type UseAudioFileAnalysisState = {\n data: AudioFileAnalysis | null;\n isLoading: boolean;\n error: string | null;\n};\n\nexport function useAudioFileAnalysis(\n fileUrl: string | null | undefined,\n options: AnalyzeAudioFileOptions = {},\n): UseAudioFileAnalysisState {\n const [state, setState] = useState<UseAudioFileAnalysisState>({\n data: null,\n isLoading: false,\n error: null,\n });\n\n useEffect(() => {\n if (!fileUrl) {\n setState({ data: null, isLoading: false, error: null });\n return;\n }\n if (typeof window === \"undefined\") {\n setState({ data: null, isLoading: false, error: null });\n return;\n }\n\n let cancelled = false;\n setState((prev) => ({ ...prev, isLoading: true, error: null }));\n\n void (async () => {\n try {\n const data = await analyzeAudioFile(fileUrl, options);\n if (!cancelled) {\n setState({ data, isLoading: false, error: null });\n }\n } catch (error) {\n if (!cancelled) {\n setState({\n data: null,\n isLoading: false,\n error: error instanceof Error ? error.message : \"Failed to analyze audio file\",\n });\n }\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [\n fileUrl,\n options.timeSlices,\n options.samplesPerSlice,\n options.spectrogram,\n options.fftSize,\n options.frequencyBins,\n options.channel,\n ]);\n\n return state;\n}\n"],"names":["useAudioPeaks","fileUrl","buckets","state","setState","useState","useEffect","cancelled","prev","response","buffer","audioContext","channel","step","peaks","i","max","start","end","j","error","fftInPlace","re","im","n","tr","ti","k","len","ang","wlenR","wlenI","wr","wi","half","u","v","nwr","nwi","realFftMagnitudes","samples","out","hanningWindow","length","w","denom","clampFftSize","p","mixChannels","numberOfChannels","scale","c","ch","getChannel","idx","buildAmplitudeGrid","timeSlices","samplesPerSlice","grid","t","segmentLen","row","segStart","segEnd","subLen","s","a","b","peak","buildSpectrogram","fftSize","frequencyBins","rows","maxMag","bins","window","frame","mags","analyzeAudioBuffer","options","wantSpec","data","amplitudeGrid","result","norm","analyzeAudioFile","raw","Context","useAudioFileAnalysis"],"mappings":"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"}
@@ -1,3 +1,7 @@
1
1
  export { useAudioPeaks } from './useAudioPeaks';
2
2
  export type { UseAudioPeaksState } from './useAudioPeaks';
3
+ export { analyzeAudioBuffer, analyzeAudioFile } from './analyzeAudioFile';
4
+ export type { AnalyzeAudioFileOptions, AudioFileAnalysis } from './analyzeAudioFile';
5
+ export { useAudioFileAnalysis } from './useAudioFileAnalysis';
6
+ export type { UseAudioFileAnalysisState } from './useAudioFileAnalysis';
3
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/waveform/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/waveform/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,YAAY,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACrF,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC"}