@lucaismyname/ginger 0.0.38 → 0.0.41

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 +15 -16
  2. package/dist/client.cjs +1 -1
  3. package/dist/client.js +2 -2
  4. package/dist/context/GingerProvider.d.ts.map +1 -1
  5. package/dist/equalizer/useGingerEqualizer.test.d.ts +2 -0
  6. package/dist/equalizer/useGingerEqualizer.test.d.ts.map +1 -0
  7. package/dist/{ginger-Ca8910_n.js → ginger-CgHqHrrG.js} +309 -304
  8. package/dist/{ginger-Ca8910_n.js.map → ginger-CgHqHrrG.js.map} +1 -1
  9. package/dist/{ginger-DrD8F4HX.cjs → ginger-DFdZGaMi.cjs} +2 -2
  10. package/dist/{ginger-DrD8F4HX.cjs.map → ginger-DFdZGaMi.cjs.map} +1 -1
  11. package/dist/index.cjs +1 -1
  12. package/dist/index.js +2 -2
  13. package/dist/store.test.d.ts +2 -0
  14. package/dist/store.test.d.ts.map +1 -0
  15. package/dist/testing/index.cjs +1 -1
  16. package/dist/testing/index.js +1 -1
  17. package/dist/transcript/index.cjs +7 -7
  18. package/dist/transcript/index.cjs.map +1 -1
  19. package/dist/transcript/index.js +57 -52
  20. package/dist/transcript/index.js.map +1 -1
  21. package/dist/transcript/useGingerTranscriptSync.d.ts.map +1 -1
  22. package/dist/transcript/useGingerTranscriptSync.test.d.ts +2 -0
  23. package/dist/transcript/useGingerTranscriptSync.test.d.ts.map +1 -0
  24. package/dist/{useGingerChapterProgress-TeWWJ8Fd.cjs → useGingerChapterProgress-COLWYX2-.cjs} +2 -2
  25. package/dist/{useGingerChapterProgress-TeWWJ8Fd.cjs.map → useGingerChapterProgress-COLWYX2-.cjs.map} +1 -1
  26. package/dist/{useGingerChapterProgress-Dbwiwnko.js → useGingerChapterProgress-Jj_zfnds.js} +2 -2
  27. package/dist/{useGingerChapterProgress-Dbwiwnko.js.map → useGingerChapterProgress-Jj_zfnds.js.map} +1 -1
  28. package/dist/waveform/index.cjs +1 -1
  29. package/dist/waveform/index.cjs.map +1 -1
  30. package/dist/waveform/index.d.ts +1 -1
  31. package/dist/waveform/index.d.ts.map +1 -1
  32. package/dist/waveform/index.js +129 -129
  33. package/dist/waveform/index.js.map +1 -1
  34. package/dist/waveform/useAudioPeaks.d.ts +14 -1
  35. package/dist/waveform/useAudioPeaks.d.ts.map +1 -1
  36. package/package.json +6 -4
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/waveform/getAudioContextConstructor.ts","../../src/waveform/useAudioPeaks.ts","../../src/internal/fft.ts","../../src/waveform/analyzeAudioFile.ts","../../src/waveform/useAudioFileAnalysis.ts"],"sourcesContent":["/**\n * Resolves the runtime `AudioContext` constructor, including legacy `webkitAudioContext` (Safari).\n * Returns `null` when `window` is unavailable or Web Audio is not implemented.\n */\nexport function getAudioContextConstructor():\n | (new (\n contextOptions?: AudioContextOptions,\n ) => AudioContext)\n | null {\n if (typeof window === \"undefined\") return null;\n const C =\n window.AudioContext ??\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n return C ?? null;\n}\n","import { useEffect, useState } from \"react\";\nimport { getAudioContextConstructor } from \"./getAudioContextConstructor\";\n\nexport type UseAudioPeaksState = {\n peaks: number[];\n isLoading: boolean;\n error: string | null;\n};\n\nexport function useAudioPeaks(\n fileUrl: string | null | undefined,\n buckets = 64,\n): 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 if (typeof window === \"undefined\") {\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)\n throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);\n const buffer = await response.arrayBuffer();\n const Context = getAudioContextConstructor();\n if (!Context) {\n throw new Error(\"Web Audio API is not available\");\n }\n const audioContext = new Context();\n try {\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 if (!cancelled) {\n setState({ peaks, isLoading: false, error: null });\n }\n } finally {\n await audioContext.close().catch(() => {});\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\";\nimport { getAudioContextConstructor } from \"./getAudioContextConstructor\";\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 ? 0 : 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(\n buffer: AudioBuffer,\n options: AnalyzeAudioFileOptions = {},\n): 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 = getAudioContextConstructor();\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 {\n type AnalyzeAudioFileOptions,\n type AudioFileAnalysis,\n analyzeAudioFile,\n} from \"./analyzeAudioFile\";\n\nexport type UseAudioFileAnalysisState = {\n data: AudioFileAnalysis | null;\n isLoading: boolean;\n error: string | null;\n};\n\n/** When extending {@link AnalyzeAudioFileOptions}, thread new fields through the destructuring below and the effect dependency array. */\nexport function useAudioFileAnalysis(\n fileUrl: string | null | undefined,\n options: AnalyzeAudioFileOptions = {},\n): UseAudioFileAnalysisState {\n const { timeSlices, samplesPerSlice, spectrogram, fftSize, frequencyBins, channel } = options;\n\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, {\n timeSlices,\n samplesPerSlice,\n spectrogram,\n fftSize,\n frequencyBins,\n channel,\n });\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 }, [fileUrl, timeSlices, samplesPerSlice, spectrogram, fftSize, frequencyBins, channel]);\n\n return state;\n}\n"],"names":["getAudioContextConstructor","useAudioPeaks","fileUrl","buckets","state","setState","useState","useEffect","cancelled","prev","response","buffer","Context","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","useAudioFileAnalysis","spectrogram"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/waveform/getAudioContextConstructor.ts","../../src/waveform/useAudioPeaks.ts","../../src/internal/fft.ts","../../src/waveform/analyzeAudioFile.ts","../../src/waveform/useAudioFileAnalysis.ts"],"sourcesContent":["/**\n * Resolves the runtime `AudioContext` constructor, including legacy `webkitAudioContext` (Safari).\n * Returns `null` when `window` is unavailable or Web Audio is not implemented.\n */\nexport function getAudioContextConstructor():\n | (new (\n contextOptions?: AudioContextOptions,\n ) => AudioContext)\n | null {\n if (typeof window === \"undefined\") return null;\n const C =\n window.AudioContext ??\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n return C ?? null;\n}\n","import { useEffect, useState } from \"react\";\nimport { getAudioContextConstructor } from \"./getAudioContextConstructor\";\n\nexport type UseAudioPeaksState = {\n peaks: number[];\n isLoading: boolean;\n error: string | null;\n};\n\nexport type UseAudioPeaksOptions = {\n /**\n * Hard cap for computed buckets to avoid very expensive loops.\n * Default: 512.\n */\n maxBuckets?: number;\n /**\n * Maximum number of samples scanned per bucket.\n * Large files are sampled with a stride once this limit is exceeded.\n * Default: 20_000.\n */\n maxSamplesPerBucket?: number;\n};\n\nexport function useAudioPeaks(\n fileUrl: string | null | undefined,\n buckets = 64,\n options: UseAudioPeaksOptions = {},\n): UseAudioPeaksState {\n const { maxBuckets = 512, maxSamplesPerBucket = 20_000 } = options;\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 if (typeof window === \"undefined\") {\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)\n throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);\n const buffer = await response.arrayBuffer();\n const Context = getAudioContextConstructor();\n if (!Context) {\n throw new Error(\"Web Audio API is not available\");\n }\n const audioContext = new Context();\n try {\n const audioBuffer = await audioContext.decodeAudioData(buffer);\n const channel = audioBuffer.getChannelData(0);\n const safeBuckets = Math.min(Math.max(1, buckets), Math.max(1, maxBuckets));\n const step = Math.max(1, Math.floor(channel.length / safeBuckets));\n const peaks: number[] = [];\n for (let i = 0; i < safeBuckets; i += 1) {\n let max = 0;\n const start = i * step;\n const end = Math.min(channel.length, start + step);\n const sampleCount = end - start;\n const stride =\n sampleCount > maxSamplesPerBucket ? Math.ceil(sampleCount / maxSamplesPerBucket) : 1;\n for (let j = start; j < end; j += stride) {\n max = Math.max(max, Math.abs(channel[j] ?? 0));\n }\n peaks.push(max);\n }\n if (!cancelled) {\n setState({ peaks, isLoading: false, error: null });\n }\n } finally {\n await audioContext.close().catch(() => {});\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, maxBuckets, maxSamplesPerBucket]);\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\";\nimport { getAudioContextConstructor } from \"./getAudioContextConstructor\";\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 ? 0 : 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(\n buffer: AudioBuffer,\n options: AnalyzeAudioFileOptions = {},\n): 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 = getAudioContextConstructor();\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 {\n type AnalyzeAudioFileOptions,\n type AudioFileAnalysis,\n analyzeAudioFile,\n} from \"./analyzeAudioFile\";\n\nexport type UseAudioFileAnalysisState = {\n data: AudioFileAnalysis | null;\n isLoading: boolean;\n error: string | null;\n};\n\n/** When extending {@link AnalyzeAudioFileOptions}, thread new fields through the destructuring below and the effect dependency array. */\nexport function useAudioFileAnalysis(\n fileUrl: string | null | undefined,\n options: AnalyzeAudioFileOptions = {},\n): UseAudioFileAnalysisState {\n const { timeSlices, samplesPerSlice, spectrogram, fftSize, frequencyBins, channel } = options;\n\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, {\n timeSlices,\n samplesPerSlice,\n spectrogram,\n fftSize,\n frequencyBins,\n channel,\n });\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 }, [fileUrl, timeSlices, samplesPerSlice, spectrogram, fftSize, frequencyBins, channel]);\n\n return state;\n}\n"],"names":["getAudioContextConstructor","useAudioPeaks","fileUrl","buckets","options","maxBuckets","maxSamplesPerBucket","state","setState","useState","useEffect","cancelled","prev","response","buffer","Context","audioContext","channel","safeBuckets","step","peaks","i","max","start","end","sampleCount","stride","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","wantSpec","data","amplitudeGrid","result","norm","analyzeAudioFile","raw","useAudioFileAnalysis","spectrogram"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  export { useAudioPeaks } from './useAudioPeaks';
2
- export type { UseAudioPeaksState } from './useAudioPeaks';
2
+ export type { UseAudioPeaksOptions, UseAudioPeaksState } from './useAudioPeaks';
3
3
  export { analyzeAudioBuffer, analyzeAudioFile } from './analyzeAudioFile';
4
4
  export type { AnalyzeAudioFileOptions, AudioFileAnalysis } from './analyzeAudioFile';
5
5
  export { useAudioFileAnalysis } from './useAudioFileAnalysis';
@@ -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;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"}
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,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAChF,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"}
@@ -1,242 +1,242 @@
1
- import { useState as p, useEffect as y } from "react";
2
- function x() {
1
+ import { useState as k, useEffect as L } from "react";
2
+ function F() {
3
3
  return typeof window > "u" ? null : window.AudioContext ?? window.webkitAudioContext ?? null;
4
4
  }
5
- function v(n, t = 64) {
6
- const [a, o] = p({
5
+ function W(n, t = 64, a = {}) {
6
+ const { maxBuckets: r = 512, maxSamplesPerBucket: o = 2e4 } = a, [e, s] = k({
7
7
  peaks: [],
8
8
  isLoading: !1,
9
9
  error: null
10
10
  });
11
- return y(() => {
11
+ return L(() => {
12
12
  if (!n) {
13
- o({ peaks: [], isLoading: !1, error: null });
13
+ s({ peaks: [], isLoading: !1, error: null });
14
14
  return;
15
15
  }
16
16
  if (typeof window > "u") {
17
- o({ peaks: [], isLoading: !1, error: null });
17
+ s({ peaks: [], isLoading: !1, error: null });
18
18
  return;
19
19
  }
20
- let r = !1;
21
- return o((e) => ({ ...e, isLoading: !0, error: null })), (async () => {
20
+ let l = !1;
21
+ return s((c) => ({ ...c, isLoading: !0, error: null })), (async () => {
22
22
  try {
23
- const e = await fetch(n);
24
- if (!e.ok)
25
- throw new Error(`Fetch failed: ${e.status} ${e.statusText}`);
26
- const l = await e.arrayBuffer(), s = x();
27
- if (!s)
23
+ const c = await fetch(n);
24
+ if (!c.ok)
25
+ throw new Error(`Fetch failed: ${c.status} ${c.statusText}`);
26
+ const u = await c.arrayBuffer(), h = F();
27
+ if (!h)
28
28
  throw new Error("Web Audio API is not available");
29
- const h = new s();
29
+ const i = new h();
30
30
  try {
31
- const f = (await h.decodeAudioData(l)).getChannelData(0), i = Math.max(1, Math.floor(f.length / t)), w = [];
32
- for (let u = 0; u < t; u += 1) {
33
- let g = 0;
34
- const m = u * i, d = Math.min(f.length, m + i);
35
- for (let M = m; M < d; M += 1)
36
- g = Math.max(g, Math.abs(f[M] ?? 0));
37
- w.push(g);
31
+ const d = (await i.decodeAudioData(u)).getChannelData(0), w = Math.min(Math.max(1, t), Math.max(1, r)), m = Math.max(1, Math.floor(d.length / w)), f = [];
32
+ for (let M = 0; M < w; M += 1) {
33
+ let p = 0;
34
+ const x = M * m, A = Math.min(d.length, x + m), C = A - x, B = C > o ? Math.ceil(C / o) : 1;
35
+ for (let y = x; y < A; y += B)
36
+ p = Math.max(p, Math.abs(d[y] ?? 0));
37
+ f.push(p);
38
38
  }
39
- r || o({ peaks: w, isLoading: !1, error: null });
39
+ l || s({ peaks: f, isLoading: !1, error: null });
40
40
  } finally {
41
- await h.close().catch(() => {
41
+ await i.close().catch(() => {
42
42
  });
43
43
  }
44
- } catch (e) {
45
- r || o({
44
+ } catch (c) {
45
+ l || s({
46
46
  peaks: [],
47
47
  isLoading: !1,
48
- error: e instanceof Error ? e.message : "Failed to decode peaks"
48
+ error: c instanceof Error ? c.message : "Failed to decode peaks"
49
49
  });
50
50
  }
51
51
  })(), () => {
52
- r = !0;
52
+ l = !0;
53
53
  };
54
- }, [t, n]), a;
54
+ }, [t, n, r, o]), e;
55
55
  }
56
- function C(n, t) {
56
+ function b(n, t) {
57
57
  const a = n.length;
58
58
  if (a !== t.length || a < 2 || a & a - 1)
59
59
  throw new Error("fftInPlace: length must be equal powers of 2 >= 2");
60
- let o = 0;
61
- for (let r = 0; r < a - 1; r += 1) {
62
- if (r < o) {
63
- const l = n[r], s = t[r];
64
- n[r] = n[o], t[r] = t[o], n[o] = l, t[o] = s;
60
+ let r = 0;
61
+ for (let o = 0; o < a - 1; o += 1) {
62
+ if (o < r) {
63
+ const s = n[o], l = t[o];
64
+ n[o] = n[r], t[o] = t[r], n[r] = s, t[r] = l;
65
65
  }
66
66
  let e = a >> 1;
67
- for (; e <= o; )
68
- o -= e, e >>= 1;
69
- o += e;
67
+ for (; e <= r; )
68
+ r -= e, e >>= 1;
69
+ r += e;
70
70
  }
71
- for (let r = 2; r <= a; r <<= 1) {
72
- const e = -2 * Math.PI / r, l = Math.cos(e), s = Math.sin(e);
73
- for (let h = 0; h < a; h += r) {
74
- let c = 1, f = 0;
75
- const i = r >> 1;
76
- for (let w = 0; w < i; w += 1) {
77
- const u = h + w, g = u + i, m = c * n[g] - f * t[g], d = c * t[g] + f * n[g];
78
- n[g] = n[u] - m, t[g] = t[u] - d, n[u] = n[u] + m, t[u] = t[u] + d;
79
- const M = c * l - f * s, A = c * s + f * l;
80
- c = M, f = A;
71
+ for (let o = 2; o <= a; o <<= 1) {
72
+ const e = -2 * Math.PI / o, s = Math.cos(e), l = Math.sin(e);
73
+ for (let c = 0; c < a; c += o) {
74
+ let u = 1, h = 0;
75
+ const i = o >> 1;
76
+ for (let g = 0; g < i; g += 1) {
77
+ const d = c + g, w = d + i, m = u * n[w] - h * t[w], f = u * t[w] + h * n[w];
78
+ n[w] = n[d] - m, t[w] = t[d] - f, n[d] = n[d] + m, t[d] = t[d] + f;
79
+ const M = u * s - h * l, p = u * l + h * s;
80
+ u = M, h = p;
81
81
  }
82
82
  }
83
83
  }
84
84
  }
85
- function L(n) {
85
+ function E(n) {
86
86
  const t = n.length;
87
87
  if (t < 2 || t & t - 1)
88
88
  throw new Error("realFftMagnitudes: length must be a power of 2 >= 2");
89
- const a = new Float64Array(t), o = new Float64Array(t);
89
+ const a = new Float64Array(t), r = new Float64Array(t);
90
90
  for (let e = 0; e < t; e += 1) a[e] = n[e];
91
- C(a, o);
92
- const r = new Float64Array(t >> 1);
91
+ b(a, r);
92
+ const o = new Float64Array(t >> 1);
93
93
  for (let e = 0; e < t >> 1; e += 1)
94
- r[e] = Math.hypot(a[e], o[e]);
95
- return r;
94
+ o[e] = Math.hypot(a[e], r[e]);
95
+ return o;
96
96
  }
97
- function F(n) {
97
+ function S(n) {
98
98
  const t = new Float64Array(n);
99
99
  if (n === 1)
100
100
  return t[0] = 1, t;
101
101
  const a = n - 1;
102
- for (let o = 0; o < n; o += 1)
103
- t[o] = 0.5 * (1 - Math.cos(2 * Math.PI * o / a));
102
+ for (let r = 0; r < n; r += 1)
103
+ t[r] = 0.5 * (1 - Math.cos(2 * Math.PI * r / a));
104
104
  return t;
105
105
  }
106
- function k(n) {
106
+ function P(n) {
107
107
  const t = 2 ** Math.round(Math.log2(n));
108
108
  return Math.min(8192, Math.max(32, t));
109
109
  }
110
- function b(n) {
110
+ function z(n) {
111
111
  const { numberOfChannels: t, length: a } = n;
112
112
  if (t === 1)
113
113
  return n.getChannelData(0);
114
- const o = new Float32Array(a), r = 1 / t;
114
+ const r = new Float32Array(a), o = 1 / t;
115
115
  for (let e = 0; e < t; e += 1) {
116
- const l = n.getChannelData(e);
117
- for (let s = 0; s < a; s += 1)
118
- o[s] += l[s] * r;
116
+ const s = n.getChannelData(e);
117
+ for (let l = 0; l < a; l += 1)
118
+ r[l] += s[l] * o;
119
119
  }
120
- return o;
120
+ return r;
121
121
  }
122
- function E(n, t) {
123
- if (t === "mix") return b(n);
122
+ function I(n, t) {
123
+ if (t === "mix") return z(n);
124
124
  const a = Math.max(0, Math.min(n.numberOfChannels - 1, t));
125
125
  return n.getChannelData(a);
126
126
  }
127
- function B(n, t, a) {
128
- const o = [], r = n.length;
129
- if (r === 0) {
130
- for (let l = 0; l < t; l += 1)
131
- o.push(Array.from({ length: a }, () => 0));
132
- return o;
127
+ function v(n, t, a) {
128
+ const r = [], o = n.length;
129
+ if (o === 0) {
130
+ for (let s = 0; s < t; s += 1)
131
+ r.push(Array.from({ length: a }, () => 0));
132
+ return r;
133
133
  }
134
- const e = r / t;
135
- for (let l = 0; l < t; l += 1) {
136
- const s = [], h = Math.floor(l * e), c = Math.floor((l + 1) * e), i = Math.max(1, c - h) / a;
137
- for (let w = 0; w < a; w += 1) {
138
- const u = Math.floor(h + w * i), g = Math.min(c, Math.floor(h + (w + 1) * i));
134
+ const e = o / t;
135
+ for (let s = 0; s < t; s += 1) {
136
+ const l = [], c = Math.floor(s * e), u = Math.floor((s + 1) * e), i = Math.max(1, u - c) / a;
137
+ for (let g = 0; g < a; g += 1) {
138
+ const d = Math.floor(c + g * i), w = Math.min(u, Math.floor(c + (g + 1) * i));
139
139
  let m = 0;
140
- for (let d = u; d < g; d += 1)
141
- m = Math.max(m, Math.abs(n[d] ?? 0));
142
- s.push(m);
140
+ for (let f = d; f < w; f += 1)
141
+ m = Math.max(m, Math.abs(n[f] ?? 0));
142
+ l.push(m);
143
143
  }
144
- o.push(s);
144
+ r.push(l);
145
145
  }
146
- return o;
146
+ return r;
147
147
  }
148
- function S(n, t, a, o) {
149
- const r = [];
148
+ function D(n, t, a, r) {
149
+ const o = [];
150
150
  let e = 1e-12;
151
- const l = n.length, s = k(a), h = s >> 1, c = Math.min(o, h), f = F(s);
152
- if (l < s) {
151
+ const s = n.length, l = P(a), c = l >> 1, u = Math.min(r, c), h = S(l);
152
+ if (s < l) {
153
153
  for (let i = 0; i < t; i += 1)
154
- r.push(Array.from({ length: c }, () => 0));
155
- return { rows: r, maxMag: 1 };
154
+ o.push(Array.from({ length: u }, () => 0));
155
+ return { rows: o, maxMag: 1 };
156
156
  }
157
157
  for (let i = 0; i < t; i += 1) {
158
- const w = t <= 1 ? 0 : Math.min(Math.floor(i * (l - s) / (t - 1)), l - s), u = new Float64Array(s);
159
- for (let d = 0; d < s; d += 1)
160
- u[d] = (n[w + d] ?? 0) * (f[d] ?? 0);
161
- const g = L(u), m = [];
162
- for (let d = 0; d < c; d += 1) {
163
- const M = g[d] ?? 0;
158
+ const g = t <= 1 ? 0 : Math.min(Math.floor(i * (s - l) / (t - 1)), s - l), d = new Float64Array(l);
159
+ for (let f = 0; f < l; f += 1)
160
+ d[f] = (n[g + f] ?? 0) * (h[f] ?? 0);
161
+ const w = E(d), m = [];
162
+ for (let f = 0; f < u; f += 1) {
163
+ const M = w[f] ?? 0;
164
164
  m.push(M), e = Math.max(e, M);
165
165
  }
166
- r.push(m);
166
+ o.push(m);
167
167
  }
168
- return { rows: r, maxMag: e };
168
+ return { rows: o, maxMag: e };
169
169
  }
170
- function P(n, t = {}) {
171
- const a = Math.max(1, t.timeSlices ?? 128), o = Math.max(1, t.samplesPerSlice ?? 8), r = !!t.spectrogram, e = t.fftSize ?? 1024, l = Math.max(1, t.frequencyBins ?? 256), s = t.channel ?? 0, h = E(n, s), c = B(h, a, o), f = {
170
+ function q(n, t = {}) {
171
+ const a = Math.max(1, t.timeSlices ?? 128), r = Math.max(1, t.samplesPerSlice ?? 8), o = !!t.spectrogram, e = t.fftSize ?? 1024, s = Math.max(1, t.frequencyBins ?? 256), l = t.channel ?? 0, c = I(n, l), u = v(c, a, r), h = {
172
172
  duration: n.duration,
173
173
  sampleRate: n.sampleRate,
174
- amplitudeGrid: c
174
+ amplitudeGrid: u
175
175
  };
176
- if (r) {
177
- const { rows: i, maxMag: w } = S(h, a, e, l), u = w > 0 ? 1 / w : 1;
178
- f.spectrogram = i.map((g) => g.map((m) => m * u));
176
+ if (o) {
177
+ const { rows: i, maxMag: g } = D(c, a, e, s), d = g > 0 ? 1 / g : 1;
178
+ h.spectrogram = i.map((w) => w.map((m) => m * d));
179
179
  }
180
- return f;
180
+ return h;
181
181
  }
182
- async function z(n, t = {}) {
182
+ async function $(n, t = {}) {
183
183
  const a = await fetch(n);
184
184
  if (!a.ok)
185
185
  throw new Error(`Fetch failed: ${a.status} ${a.statusText}`);
186
- const o = await a.arrayBuffer(), r = x();
187
- if (!r)
186
+ const r = await a.arrayBuffer(), o = F();
187
+ if (!o)
188
188
  throw new Error("Web Audio API is not available");
189
- const e = new r();
189
+ const e = new o();
190
190
  try {
191
- const l = await e.decodeAudioData(o.slice(0));
192
- return P(l, t);
191
+ const s = await e.decodeAudioData(r.slice(0));
192
+ return q(s, t);
193
193
  } finally {
194
194
  await e.close();
195
195
  }
196
196
  }
197
- function D(n, t = {}) {
198
- const { timeSlices: a, samplesPerSlice: o, spectrogram: r, fftSize: e, frequencyBins: l, channel: s } = t, [h, c] = p({
197
+ function j(n, t = {}) {
198
+ const { timeSlices: a, samplesPerSlice: r, spectrogram: o, fftSize: e, frequencyBins: s, channel: l } = t, [c, u] = k({
199
199
  data: null,
200
200
  isLoading: !1,
201
201
  error: null
202
202
  });
203
- return y(() => {
203
+ return L(() => {
204
204
  if (!n) {
205
- c({ data: null, isLoading: !1, error: null });
205
+ u({ data: null, isLoading: !1, error: null });
206
206
  return;
207
207
  }
208
208
  if (typeof window > "u") {
209
- c({ data: null, isLoading: !1, error: null });
209
+ u({ data: null, isLoading: !1, error: null });
210
210
  return;
211
211
  }
212
- let f = !1;
213
- return c((i) => ({ ...i, isLoading: !0, error: null })), (async () => {
212
+ let h = !1;
213
+ return u((i) => ({ ...i, isLoading: !0, error: null })), (async () => {
214
214
  try {
215
- const i = await z(n, {
215
+ const i = await $(n, {
216
216
  timeSlices: a,
217
- samplesPerSlice: o,
218
- spectrogram: r,
217
+ samplesPerSlice: r,
218
+ spectrogram: o,
219
219
  fftSize: e,
220
- frequencyBins: l,
221
- channel: s
220
+ frequencyBins: s,
221
+ channel: l
222
222
  });
223
- f || c({ data: i, isLoading: !1, error: null });
223
+ h || u({ data: i, isLoading: !1, error: null });
224
224
  } catch (i) {
225
- f || c({
225
+ h || u({
226
226
  data: null,
227
227
  isLoading: !1,
228
228
  error: i instanceof Error ? i.message : "Failed to analyze audio file"
229
229
  });
230
230
  }
231
231
  })(), () => {
232
- f = !0;
232
+ h = !0;
233
233
  };
234
- }, [n, a, o, r, e, l, s]), h;
234
+ }, [n, a, r, o, e, s, l]), c;
235
235
  }
236
236
  export {
237
- P as analyzeAudioBuffer,
238
- z as analyzeAudioFile,
239
- D as useAudioFileAnalysis,
240
- v as useAudioPeaks
237
+ q as analyzeAudioBuffer,
238
+ $ as analyzeAudioFile,
239
+ j as useAudioFileAnalysis,
240
+ W as useAudioPeaks
241
241
  };
242
242
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/waveform/getAudioContextConstructor.ts","../../src/waveform/useAudioPeaks.ts","../../src/internal/fft.ts","../../src/waveform/analyzeAudioFile.ts","../../src/waveform/useAudioFileAnalysis.ts"],"sourcesContent":["/**\n * Resolves the runtime `AudioContext` constructor, including legacy `webkitAudioContext` (Safari).\n * Returns `null` when `window` is unavailable or Web Audio is not implemented.\n */\nexport function getAudioContextConstructor():\n | (new (\n contextOptions?: AudioContextOptions,\n ) => AudioContext)\n | null {\n if (typeof window === \"undefined\") return null;\n const C =\n window.AudioContext ??\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n return C ?? null;\n}\n","import { useEffect, useState } from \"react\";\nimport { getAudioContextConstructor } from \"./getAudioContextConstructor\";\n\nexport type UseAudioPeaksState = {\n peaks: number[];\n isLoading: boolean;\n error: string | null;\n};\n\nexport function useAudioPeaks(\n fileUrl: string | null | undefined,\n buckets = 64,\n): 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 if (typeof window === \"undefined\") {\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)\n throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);\n const buffer = await response.arrayBuffer();\n const Context = getAudioContextConstructor();\n if (!Context) {\n throw new Error(\"Web Audio API is not available\");\n }\n const audioContext = new Context();\n try {\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 if (!cancelled) {\n setState({ peaks, isLoading: false, error: null });\n }\n } finally {\n await audioContext.close().catch(() => {});\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\";\nimport { getAudioContextConstructor } from \"./getAudioContextConstructor\";\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 ? 0 : 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(\n buffer: AudioBuffer,\n options: AnalyzeAudioFileOptions = {},\n): 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 = getAudioContextConstructor();\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 {\n type AnalyzeAudioFileOptions,\n type AudioFileAnalysis,\n analyzeAudioFile,\n} from \"./analyzeAudioFile\";\n\nexport type UseAudioFileAnalysisState = {\n data: AudioFileAnalysis | null;\n isLoading: boolean;\n error: string | null;\n};\n\n/** When extending {@link AnalyzeAudioFileOptions}, thread new fields through the destructuring below and the effect dependency array. */\nexport function useAudioFileAnalysis(\n fileUrl: string | null | undefined,\n options: AnalyzeAudioFileOptions = {},\n): UseAudioFileAnalysisState {\n const { timeSlices, samplesPerSlice, spectrogram, fftSize, frequencyBins, channel } = options;\n\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, {\n timeSlices,\n samplesPerSlice,\n spectrogram,\n fftSize,\n frequencyBins,\n channel,\n });\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 }, [fileUrl, timeSlices, samplesPerSlice, spectrogram, fftSize, frequencyBins, channel]);\n\n return state;\n}\n"],"names":["getAudioContextConstructor","useAudioPeaks","fileUrl","buckets","state","setState","useState","useEffect","cancelled","prev","response","buffer","Context","audioContext","channel","step","peaks","i","max","start","end","j","error","fftInPlace","re","im","n","tr","ti","k","len","ang","wlenR","wlenI","wr","wi","half","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","useAudioFileAnalysis","spectrogram"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/waveform/getAudioContextConstructor.ts","../../src/waveform/useAudioPeaks.ts","../../src/internal/fft.ts","../../src/waveform/analyzeAudioFile.ts","../../src/waveform/useAudioFileAnalysis.ts"],"sourcesContent":["/**\n * Resolves the runtime `AudioContext` constructor, including legacy `webkitAudioContext` (Safari).\n * Returns `null` when `window` is unavailable or Web Audio is not implemented.\n */\nexport function getAudioContextConstructor():\n | (new (\n contextOptions?: AudioContextOptions,\n ) => AudioContext)\n | null {\n if (typeof window === \"undefined\") return null;\n const C =\n window.AudioContext ??\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n return C ?? null;\n}\n","import { useEffect, useState } from \"react\";\nimport { getAudioContextConstructor } from \"./getAudioContextConstructor\";\n\nexport type UseAudioPeaksState = {\n peaks: number[];\n isLoading: boolean;\n error: string | null;\n};\n\nexport type UseAudioPeaksOptions = {\n /**\n * Hard cap for computed buckets to avoid very expensive loops.\n * Default: 512.\n */\n maxBuckets?: number;\n /**\n * Maximum number of samples scanned per bucket.\n * Large files are sampled with a stride once this limit is exceeded.\n * Default: 20_000.\n */\n maxSamplesPerBucket?: number;\n};\n\nexport function useAudioPeaks(\n fileUrl: string | null | undefined,\n buckets = 64,\n options: UseAudioPeaksOptions = {},\n): UseAudioPeaksState {\n const { maxBuckets = 512, maxSamplesPerBucket = 20_000 } = options;\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 if (typeof window === \"undefined\") {\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)\n throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);\n const buffer = await response.arrayBuffer();\n const Context = getAudioContextConstructor();\n if (!Context) {\n throw new Error(\"Web Audio API is not available\");\n }\n const audioContext = new Context();\n try {\n const audioBuffer = await audioContext.decodeAudioData(buffer);\n const channel = audioBuffer.getChannelData(0);\n const safeBuckets = Math.min(Math.max(1, buckets), Math.max(1, maxBuckets));\n const step = Math.max(1, Math.floor(channel.length / safeBuckets));\n const peaks: number[] = [];\n for (let i = 0; i < safeBuckets; i += 1) {\n let max = 0;\n const start = i * step;\n const end = Math.min(channel.length, start + step);\n const sampleCount = end - start;\n const stride =\n sampleCount > maxSamplesPerBucket ? Math.ceil(sampleCount / maxSamplesPerBucket) : 1;\n for (let j = start; j < end; j += stride) {\n max = Math.max(max, Math.abs(channel[j] ?? 0));\n }\n peaks.push(max);\n }\n if (!cancelled) {\n setState({ peaks, isLoading: false, error: null });\n }\n } finally {\n await audioContext.close().catch(() => {});\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, maxBuckets, maxSamplesPerBucket]);\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\";\nimport { getAudioContextConstructor } from \"./getAudioContextConstructor\";\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 ? 0 : 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(\n buffer: AudioBuffer,\n options: AnalyzeAudioFileOptions = {},\n): 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 = getAudioContextConstructor();\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 {\n type AnalyzeAudioFileOptions,\n type AudioFileAnalysis,\n analyzeAudioFile,\n} from \"./analyzeAudioFile\";\n\nexport type UseAudioFileAnalysisState = {\n data: AudioFileAnalysis | null;\n isLoading: boolean;\n error: string | null;\n};\n\n/** When extending {@link AnalyzeAudioFileOptions}, thread new fields through the destructuring below and the effect dependency array. */\nexport function useAudioFileAnalysis(\n fileUrl: string | null | undefined,\n options: AnalyzeAudioFileOptions = {},\n): UseAudioFileAnalysisState {\n const { timeSlices, samplesPerSlice, spectrogram, fftSize, frequencyBins, channel } = options;\n\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, {\n timeSlices,\n samplesPerSlice,\n spectrogram,\n fftSize,\n frequencyBins,\n channel,\n });\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 }, [fileUrl, timeSlices, samplesPerSlice, spectrogram, fftSize, frequencyBins, channel]);\n\n return state;\n}\n"],"names":["getAudioContextConstructor","useAudioPeaks","fileUrl","buckets","options","maxBuckets","maxSamplesPerBucket","state","setState","useState","useEffect","cancelled","prev","response","buffer","Context","audioContext","channel","safeBuckets","step","peaks","i","max","start","end","sampleCount","stride","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","wantSpec","data","amplitudeGrid","result","norm","analyzeAudioFile","raw","useAudioFileAnalysis","spectrogram"],"mappings":"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"}