wavegram 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +198 -0
  3. package/dist/assets/spectrogram.worker-Burn7NUr.js +1 -0
  4. package/dist/audio/decodeAudio.d.ts +2 -0
  5. package/dist/audio/decodeAudio.d.ts.map +1 -0
  6. package/dist/audio/loadAudio.d.ts +2 -0
  7. package/dist/audio/loadAudio.d.ts.map +1 -0
  8. package/dist/audio/playback.d.ts +2 -0
  9. package/dist/audio/playback.d.ts.map +1 -0
  10. package/dist/audio/spectrogram.d.ts +8 -0
  11. package/dist/audio/spectrogram.d.ts.map +1 -0
  12. package/dist/audio/waveform.d.ts +4 -0
  13. package/dist/audio/waveform.d.ts.map +1 -0
  14. package/dist/component/Wavegram.d.ts +108 -0
  15. package/dist/component/Wavegram.d.ts.map +1 -0
  16. package/dist/index.d.ts +6 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.es.js +1024 -0
  19. package/dist/index.umd.js +191 -0
  20. package/dist/render/canvas.d.ts +2 -0
  21. package/dist/render/canvas.d.ts.map +1 -0
  22. package/dist/render/colorMap.d.ts +4 -0
  23. package/dist/render/colorMap.d.ts.map +1 -0
  24. package/dist/render/drawCursor.d.ts +2 -0
  25. package/dist/render/drawCursor.d.ts.map +1 -0
  26. package/dist/render/drawSpectrogram.d.ts +8 -0
  27. package/dist/render/drawSpectrogram.d.ts.map +1 -0
  28. package/dist/render/drawWaveform.d.ts +13 -0
  29. package/dist/render/drawWaveform.d.ts.map +1 -0
  30. package/dist/types.d.ts +50 -0
  31. package/dist/types.d.ts.map +1 -0
  32. package/dist/utils/clamp.d.ts +2 -0
  33. package/dist/utils/clamp.d.ts.map +1 -0
  34. package/dist/utils/formatTime.d.ts +2 -0
  35. package/dist/utils/formatTime.d.ts.map +1 -0
  36. package/dist/utils/resample.d.ts +2 -0
  37. package/dist/utils/resample.d.ts.map +1 -0
  38. package/dist/worker/spectrogram.worker.d.ts +2 -0
  39. package/dist/worker/spectrogram.worker.d.ts.map +1 -0
  40. package/package.json +54 -0
@@ -0,0 +1,1024 @@
1
+ var D = Object.defineProperty;
2
+ var N = (e, s, t) => s in e ? D(e, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : e[s] = t;
3
+ var c = (e, s, t) => N(e, typeof s != "symbol" ? s + "" : s, t);
4
+ let x;
5
+ function q() {
6
+ return x ?? (x = new AudioContext()), x;
7
+ }
8
+ async function z(e) {
9
+ return q().decodeAudioData(e.slice(0));
10
+ }
11
+ async function I(e) {
12
+ const s = await fetch(e);
13
+ if (!s.ok)
14
+ throw new Error(`Failed to load audio: ${s.status} ${s.statusText}`);
15
+ return s.arrayBuffer();
16
+ }
17
+ function O(e, s) {
18
+ const t = new Audio(e);
19
+ return t.preload = "auto", t.autoplay = s, t.crossOrigin = "anonymous", t;
20
+ }
21
+ function S(e, s, t) {
22
+ return Math.min(t, Math.max(s, e));
23
+ }
24
+ const H = 1e-10;
25
+ function W(e) {
26
+ return Number.isInteger(e) && e > 0 && (e & e - 1) === 0;
27
+ }
28
+ function U(e, s) {
29
+ const t = new Float32Array(e);
30
+ if (e === 1)
31
+ return t[0] = 1, t;
32
+ for (let r = 0; r < e; r += 1)
33
+ s === "hann" ? t[r] = 0.5 - 0.5 * Math.cos(2 * Math.PI * r / (e - 1)) : s === "hamming" ? t[r] = 0.54 - 0.46 * Math.cos(2 * Math.PI * r / (e - 1)) : t[r] = 1;
34
+ return t;
35
+ }
36
+ function V(e) {
37
+ let s = 0;
38
+ for (const t of e)
39
+ s += t;
40
+ return s / e.length;
41
+ }
42
+ function $(e) {
43
+ return 20 * Math.log10(Math.abs(e) + H);
44
+ }
45
+ function j(e, s) {
46
+ const t = e.length;
47
+ if (!W(t) || s.length !== t)
48
+ throw new Error("FFT input length must be a radix-2 size.");
49
+ for (let r = 1, i = 0; r < t; r += 1) {
50
+ let o = t >> 1;
51
+ for (; i & o; o >>= 1)
52
+ i ^= o;
53
+ if (i ^= o, r < i) {
54
+ const a = e[r];
55
+ e[r] = e[i], e[i] = a;
56
+ const n = s[r];
57
+ s[r] = s[i], s[i] = n;
58
+ }
59
+ }
60
+ for (let r = 2; r <= t; r <<= 1) {
61
+ const i = -2 * Math.PI / r, o = Math.cos(i), a = Math.sin(i);
62
+ for (let n = 0; n < t; n += r) {
63
+ let h = 1, l = 0;
64
+ for (let u = 0; u < r / 2; u += 1) {
65
+ const d = e[n + u], m = s[n + u], p = e[n + u + r / 2] * h - s[n + u + r / 2] * l, g = e[n + u + r / 2] * l + s[n + u + r / 2] * h;
66
+ e[n + u] = d + p, s[n + u] = m + g, e[n + u + r / 2] = d - p, s[n + u + r / 2] = m - g;
67
+ const b = h * o - l * a;
68
+ l = h * a + l * o, h = b;
69
+ }
70
+ }
71
+ }
72
+ }
73
+ function Y(e, s, t) {
74
+ const { fftSize: r, hopSize: i, windowType: o, minDb: a, maxDb: n } = t;
75
+ if (!W(r))
76
+ throw new Error(`fftSize must be a power of two. Received ${r}.`);
77
+ if (!Number.isInteger(i) || i <= 0)
78
+ throw new Error(`hopSize must be a positive integer. Received ${i}.`);
79
+ if (n <= a)
80
+ throw new Error("maxDb must be greater than minDb.");
81
+ const h = r / 2 + 1, l = Math.max(1, Math.floor(Math.max(0, e.length - r) / i) + 1), u = new Float32Array(l * h), d = U(r, o), m = Math.max(H, r * V(d) / 2), p = new Float32Array(r), g = new Float32Array(r);
82
+ for (let b = 0; b < l; b += 1) {
83
+ const w = b * i;
84
+ p.fill(0), g.fill(0);
85
+ for (let f = 0; f < r; f += 1)
86
+ p[f] = (e[w + f] ?? 0) * d[f];
87
+ j(p, g);
88
+ for (let f = 0; f < h; f += 1) {
89
+ const k = Math.hypot(p[f], g[f]) / m;
90
+ u[b * h + f] = S($(k), a, n);
91
+ }
92
+ }
93
+ return { values: u, freqBins: h, timeFrames: l, sampleRate: s, fftSize: r, hopSize: i, minDb: a, maxDb: n };
94
+ }
95
+ function F(e, s) {
96
+ if (s === "mix") {
97
+ const t = new Float32Array(e.length);
98
+ for (let r = 0; r < e.numberOfChannels; r += 1) {
99
+ const i = e.getChannelData(r);
100
+ for (let o = 0; o < t.length; o += 1)
101
+ t[o] += i[o] / e.numberOfChannels;
102
+ }
103
+ return t;
104
+ }
105
+ if (!Number.isInteger(s) || s < 0 || s >= e.numberOfChannels)
106
+ throw new Error(`Invalid channel ${s}. Audio has ${e.numberOfChannels} channel(s).`);
107
+ return new Float32Array(e.getChannelData(s));
108
+ }
109
+ function K(e, s) {
110
+ const t = Math.max(1, Math.floor(s)), r = new Float32Array(t), i = new Float32Array(t), o = e.length / t;
111
+ for (let a = 0; a < t; a += 1) {
112
+ const n = Math.floor(a * o), h = Math.max(n + 1, Math.floor((a + 1) * o));
113
+ let l = 1, u = -1;
114
+ for (let d = n; d < h && d < e.length; d += 1) {
115
+ const m = e[d] ?? 0;
116
+ m < l && (l = m), m > u && (u = m);
117
+ }
118
+ r[a] = l === 1 ? 0 : l, i[a] = u === -1 ? 0 : u;
119
+ }
120
+ return { min: r, max: i };
121
+ }
122
+ function E(e, s, t) {
123
+ return K(F(e, t), s);
124
+ }
125
+ function v(e, s, t) {
126
+ const r = window.devicePixelRatio || 1;
127
+ e.style.width = "100%", e.style.height = `${t}px`, e.width = Math.max(1, Math.floor(s * r)), e.height = Math.max(1, Math.floor(t * r));
128
+ const i = e.getContext("2d");
129
+ if (!i)
130
+ throw new Error("Canvas 2D context is unavailable.");
131
+ return i.setTransform(r, 0, 0, r, 0, 0), i;
132
+ }
133
+ function T(e, s, t, r, i = "rgba(0, 0, 0, 0.45)") {
134
+ if (!Number.isFinite(t) || t <= 0) return;
135
+ const o = e.getContext("2d");
136
+ if (!o) return;
137
+ const a = e.clientWidth, n = e.clientHeight, h = Math.max(0, Math.min(a, s / t * a));
138
+ o.save(), o.strokeStyle = i, o.lineWidth = 2, o.beginPath(), o.moveTo(h, 0), o.lineTo(h, n), o.stroke(), o.strokeStyle = r, o.lineWidth = 1, o.beginPath(), o.moveTo(h, 0), o.lineTo(h, n), o.stroke(), o.restore();
139
+ }
140
+ function A(e, s) {
141
+ const t = S(s, 0, 1) * (e.length - 1), r = Math.floor(t), i = Math.min(r + 1, e.length - 1), o = t - r, a = e[r], n = e[i];
142
+ return [
143
+ Math.round(a[0] + (n[0] - a[0]) * o),
144
+ Math.round(a[1] + (n[1] - a[1]) * o),
145
+ Math.round(a[2] + (n[2] - a[2]) * o)
146
+ ];
147
+ }
148
+ function G(e, s) {
149
+ const t = S(s, 0, 1);
150
+ if (e === "audition")
151
+ return A(
152
+ [
153
+ [0, 0, 0],
154
+ [12, 7, 34],
155
+ [25, 37, 114],
156
+ [18, 104, 174],
157
+ [39, 178, 172],
158
+ [244, 180, 43],
159
+ [252, 76, 27],
160
+ [255, 238, 117]
161
+ ],
162
+ t
163
+ );
164
+ if (e === "gray") {
165
+ const r = Math.round(t * 255);
166
+ return [r, r, r];
167
+ }
168
+ return A(
169
+ e === "viridis" ? [
170
+ [68, 1, 84],
171
+ [59, 82, 139],
172
+ [33, 145, 140],
173
+ [94, 201, 98],
174
+ [253, 231, 37]
175
+ ] : e === "inferno" ? [
176
+ [0, 0, 4],
177
+ [87, 15, 109],
178
+ [187, 55, 84],
179
+ [249, 142, 8],
180
+ [252, 255, 164]
181
+ ] : [
182
+ [0, 0, 4],
183
+ [73, 16, 105],
184
+ [182, 54, 121],
185
+ [251, 136, 97],
186
+ [252, 253, 191]
187
+ ],
188
+ t
189
+ );
190
+ }
191
+ function X(e, s, t) {
192
+ const r = e.getContext("2d");
193
+ if (!r) return;
194
+ const i = e.clientWidth, o = e.clientHeight, a = Math.max(1, e.width), n = Math.max(1, e.height);
195
+ if (r.clearRect(0, 0, i, o), r.fillStyle = t.background, r.fillRect(0, 0, i, o), !s) return;
196
+ const h = r.createImageData(a, n), l = s.maxDb - s.minDb;
197
+ for (let u = 0; u < h.width; u += 1) {
198
+ const d = Math.min(s.timeFrames - 1, Math.floor(u / h.width * s.timeFrames));
199
+ for (let m = 0; m < h.height; m += 1) {
200
+ const p = Math.min(
201
+ s.freqBins - 1,
202
+ Math.floor((h.height - 1 - m) / h.height * s.freqBins)
203
+ ), g = s.values[d * s.freqBins + p], b = S((g - s.minDb) / l, 0, 1), [w, f, k] = G(t.colorMap, b), y = (m * h.width + u) * 4;
204
+ h.data[y] = w, h.data[y + 1] = f, h.data[y + 2] = k, h.data[y + 3] = 255;
205
+ }
206
+ }
207
+ r.putImageData(h, 0, 0), _(r, i, o, s.sampleRate, t.tickColor);
208
+ }
209
+ function J(e, s) {
210
+ if (!Number.isFinite(e) || e <= 0) return 1e3;
211
+ const t = Math.max(2, Math.min(6, Math.floor(s / 28))), r = e / t, i = 10 ** Math.floor(Math.log10(r)), o = r / i;
212
+ return (o <= 1 ? 1 : o <= 2 ? 2 : o <= 7.5 ? 5 : 10) * i;
213
+ }
214
+ function _(e, s, t, r, i = "rgba(255, 255, 255, 0.42)") {
215
+ const o = r / 2, a = J(o, t), n = 5;
216
+ e.save(), e.strokeStyle = i, e.fillStyle = i, e.lineWidth = 1, e.font = "10px system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif", e.textAlign = "left", e.textBaseline = "middle";
217
+ for (let h = a; h < o; h += a) {
218
+ const l = t - h / o * t;
219
+ e.beginPath(), e.moveTo(0, l + 0.5), e.lineTo(s, l + 0.5), e.stroke(), e.fillText(Q(h), n, l);
220
+ }
221
+ e.restore();
222
+ }
223
+ function Q(e) {
224
+ if (e >= 1e3) {
225
+ const s = e / 1e3;
226
+ return `${Number.isInteger(s) ? s.toFixed(0) : s.toFixed(1)}k`;
227
+ }
228
+ return `${Math.round(e)}`;
229
+ }
230
+ function P(e, s, t) {
231
+ const r = e.getContext("2d");
232
+ if (!r) return;
233
+ const i = e.clientWidth, o = e.clientHeight;
234
+ if (r.clearRect(0, 0, i, o), r.fillStyle = t.background, r.fillRect(0, 0, i, o), r.strokeStyle = t.centerColor ?? t.color, r.lineWidth = 1, !s) return;
235
+ const a = t.style ?? "waveform", n = tt(s), h = et(a), l = Math.max(1, t.barWidth ?? h.barWidth), u = Math.max(0, t.barSpacing ?? h.barSpacing);
236
+ a === "waveform" ? rt(r, s, o, {
237
+ unplayedColor: t.color,
238
+ playedColor: t.playedColor ?? t.progressColor ?? t.color,
239
+ centerColor: t.centerColor ?? t.color,
240
+ progress: t.progress ?? 0
241
+ }) : a === "lines" ? it(r, n, i, o, t.color) : a === "blocks" ? st(r, n, i, o, t.color, l, u) : a === "dots" ? at(r, n, i, o, t.color, l, u) : ot(r, n, i, o, t.color, l, u);
242
+ }
243
+ function Z(e, s) {
244
+ return Math.max(Math.abs(e.min[s] ?? 0), Math.abs(e.max[s] ?? 0));
245
+ }
246
+ function tt(e) {
247
+ const s = [];
248
+ for (let t = 0; t < e.max.length; t += 1)
249
+ s.push(Z(e, t));
250
+ return s;
251
+ }
252
+ function M(e, s) {
253
+ if (e.length === s) return e;
254
+ if (e.length === 0 || s <= 0) return [];
255
+ const t = [];
256
+ if (s > e.length) {
257
+ const r = (e.length - 1) / Math.max(1, s - 1);
258
+ for (let i = 0; i < s; i += 1) {
259
+ const o = i * r, a = Math.floor(o), n = Math.ceil(o), h = o - a;
260
+ n >= e.length ? t.push(e[e.length - 1] ?? 0) : a === n ? t.push(e[a] ?? 0) : t.push((e[a] ?? 0) * (1 - h) + (e[n] ?? 0) * h);
261
+ }
262
+ } else {
263
+ const r = e.length / s;
264
+ for (let i = 0; i < s; i += 1) {
265
+ const o = Math.floor(i * r), a = Math.floor((i + 1) * r);
266
+ let n = 0, h = 0;
267
+ for (let l = o; l <= a && l < e.length; l += 1)
268
+ n = Math.max(n, e[l] ?? 0), h += 1;
269
+ h === 0 && (n = e[Math.min(Math.round(i * r), e.length - 1)] ?? 0), t.push(n);
270
+ }
271
+ }
272
+ return t;
273
+ }
274
+ function et(e) {
275
+ return e === "bars" ? { barWidth: 3, barSpacing: 1 } : e === "blocks" ? { barWidth: 4, barSpacing: 2 } : e === "dots" ? { barWidth: 3, barSpacing: 3 } : { barWidth: 2, barSpacing: 0 };
276
+ }
277
+ function rt(e, s, t, r) {
278
+ const i = t / 2;
279
+ e.strokeStyle = r.centerColor, e.lineWidth = 1, e.beginPath(), e.moveTo(0, i), e.lineTo(s.max.length, i), e.stroke(), B(e, s, t, 0, s.min.length, r.unplayedColor);
280
+ const o = Math.max(0, Math.min(s.min.length, Math.round(s.min.length * r.progress)));
281
+ o > 0 && B(e, s, t, 0, o, r.playedColor);
282
+ }
283
+ function B(e, s, t, r, i, o) {
284
+ const a = t / 2;
285
+ e.strokeStyle = o, e.beginPath();
286
+ for (let n = r; n < i; n += 1) {
287
+ const h = a - s.min[n] * a, l = a - s.max[n] * a;
288
+ e.moveTo(n + 0.5, h), e.lineTo(n + 0.5, l);
289
+ }
290
+ e.stroke();
291
+ }
292
+ function ot(e, s, t, r, i, o, a) {
293
+ const n = Math.floor(t / (o + a)), h = M(s, n);
294
+ e.fillStyle = i;
295
+ for (let l = 0; l < h.length; l += 1) {
296
+ const u = l * (o + a);
297
+ if (u + o > t) break;
298
+ const d = h[l] * r * 0.9;
299
+ e.fillRect(u, r - d, o, d);
300
+ }
301
+ }
302
+ function it(e, s, t, r, i) {
303
+ const o = r / 2, a = M(s, Math.max(2, Math.floor(t / 3)));
304
+ e.strokeStyle = i, e.lineWidth = 2, e.lineCap = "round", e.lineJoin = "round", e.strokeStyle = "rgba(255, 255, 255, 0.03)", e.lineWidth = 0.5, e.beginPath(), e.moveTo(0, o), e.lineTo(t, o), e.stroke();
305
+ for (let h = 0; h <= 10; h += 1) {
306
+ const l = t / 10 * h;
307
+ e.beginPath(), e.moveTo(l, 0), e.lineTo(l, r), e.stroke();
308
+ }
309
+ e.strokeStyle = i, e.lineWidth = 2, e.beginPath(), e.moveTo(0, o);
310
+ const n = a.map((h, l) => {
311
+ const u = l / Math.max(1, a.length - 1) * t, d = o + Math.sin(l * 0.1) * h * r * 0.35;
312
+ return { x: u, y: d };
313
+ });
314
+ for (let h = 0; h < n.length - 1; h += 1) {
315
+ const l = n[h], u = n[h + 1], d = l.x + (u.x - l.x) * 0.5, m = u.x - (u.x - l.x) * 0.5;
316
+ e.bezierCurveTo(d, l.y, m, u.y, u.x, u.y);
317
+ }
318
+ e.stroke();
319
+ }
320
+ function st(e, s, t, r, i, o, a) {
321
+ const n = r / 2, h = Math.floor(t / (o + a)), l = M(s, h), u = 4, d = 2;
322
+ e.fillStyle = i;
323
+ for (let m = 0; m < l.length; m += 1) {
324
+ const p = m * (o + a);
325
+ if (p + o > t) break;
326
+ const g = l[m] * r * 0.9, b = Math.floor(g / (u + d));
327
+ for (let w = 0; w < b; w += 1) {
328
+ const f = w * (u + d);
329
+ e.fillRect(p, n - f - u, o, u), w > 0 && e.fillRect(p, n + f, o, u);
330
+ }
331
+ }
332
+ }
333
+ function at(e, s, t, r, i, o, a) {
334
+ const n = r / 2, h = Math.floor(t / (o + a)), l = M(s, h), u = Math.max(1.5, o / 2);
335
+ e.fillStyle = i;
336
+ for (let d = 0; d < l.length; d += 1) {
337
+ const m = d * (o + a) + o / 2;
338
+ if (m > t) break;
339
+ const p = l[d] * r * 0.9;
340
+ e.beginPath(), e.arc(m, n - p / 2, u, 0, Math.PI * 2), e.fill(), e.beginPath(), e.arc(m, n + p / 2, u, 0, Math.PI * 2), e.fill();
341
+ }
342
+ }
343
+ function R(e) {
344
+ if (!Number.isFinite(e) || e < 0)
345
+ return "00:00.000";
346
+ const s = Math.floor(e * 1e3), t = s % 1e3, r = Math.floor(s / 1e3), i = r % 60, o = Math.floor(r / 60);
347
+ return `${String(o).padStart(2, "0")}:${String(i).padStart(2, "0")}.${String(t).padStart(3, "0")}`;
348
+ }
349
+ const L = document.createElement("template");
350
+ L.innerHTML = `
351
+ <style>
352
+ :host {
353
+ --ap-bg: #ffffff;
354
+ --ap-fg: #222222;
355
+ --ap-muted: #666666;
356
+ --ap-waveform-bg: #020604;
357
+ --ap-waveform: rgba(0, 214, 163, 0.34);
358
+ --ap-waveform-played: #00f0b5;
359
+ --ap-waveform-center: rgba(0, 92, 58, 0.7);
360
+ --ap-waveform-progress: #00f0b5;
361
+ --ap-cursor: #00d7ff;
362
+ --ap-cursor-shadow: rgba(0, 0, 0, 0.45);
363
+ --ap-spectrogram-tick: rgba(255, 255, 255, 0.42);
364
+ --ap-spectrogram-unplayed-opacity: 0.48;
365
+ --ap-border: #dddddd;
366
+ --ap-button-bg: #f7f7f7;
367
+ --ap-button-border: #cfcfcf;
368
+ display: block;
369
+ box-sizing: border-box;
370
+ color: var(--ap-fg);
371
+ background: var(--ap-bg);
372
+ font: 13px/1.4 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
373
+ }
374
+
375
+ *, *::before, *::after {
376
+ box-sizing: border-box;
377
+ }
378
+
379
+ .root {
380
+ border: 1px solid var(--ap-border);
381
+ background: var(--ap-bg);
382
+ min-width: 180px;
383
+ }
384
+
385
+ .toolbar {
386
+ display: flex;
387
+ align-items: center;
388
+ gap: 10px;
389
+ padding: 8px;
390
+ border-bottom: 1px solid var(--ap-border);
391
+ min-height: 40px;
392
+ }
393
+
394
+ .toolbar.hidden {
395
+ display: none;
396
+ }
397
+
398
+ button {
399
+ appearance: none;
400
+ border: 1px solid var(--ap-button-border);
401
+ border-radius: 999px;
402
+ background: var(--ap-button-bg);
403
+ color: var(--ap-fg);
404
+ position: relative;
405
+ width: 30px;
406
+ min-width: 30px;
407
+ height: 30px;
408
+ padding: 0;
409
+ cursor: pointer;
410
+ font: inherit;
411
+ text-indent: -9999px;
412
+ overflow: hidden;
413
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
414
+ transition:
415
+ background 120ms ease,
416
+ border-color 120ms ease,
417
+ transform 120ms ease,
418
+ box-shadow 120ms ease;
419
+ }
420
+
421
+ button::before {
422
+ content: "";
423
+ position: absolute;
424
+ left: 50%;
425
+ top: 50%;
426
+ width: 0;
427
+ height: 0;
428
+ border-top: 7px solid transparent;
429
+ border-bottom: 7px solid transparent;
430
+ border-left: 10px solid currentColor;
431
+ transform: translate(-38%, -50%);
432
+ }
433
+
434
+ button[data-state="pause"]::before,
435
+ button[data-state="pause"]::after {
436
+ content: "";
437
+ position: absolute;
438
+ top: 50%;
439
+ width: 4px;
440
+ height: 14px;
441
+ border: 0;
442
+ background: currentColor;
443
+ border-radius: 1px;
444
+ transform: translateY(-50%);
445
+ }
446
+
447
+ button[data-state="pause"]::before {
448
+ left: calc(50% - 5px);
449
+ }
450
+
451
+ button[data-state="pause"]::after {
452
+ left: calc(50% + 1px);
453
+ }
454
+
455
+ button:hover:not(:disabled) {
456
+ transform: translateY(-1px);
457
+ box-shadow: 0 3px 8px rgba(0, 0, 0, 0.12);
458
+ }
459
+
460
+ button:focus-visible {
461
+ outline: 2px solid var(--ap-cursor);
462
+ outline-offset: 2px;
463
+ }
464
+
465
+ button:disabled {
466
+ cursor: default;
467
+ opacity: 0.55;
468
+ }
469
+
470
+ .time {
471
+ font-variant-numeric: tabular-nums;
472
+ white-space: nowrap;
473
+ }
474
+
475
+ .status {
476
+ color: var(--ap-muted);
477
+ margin-left: auto;
478
+ overflow: hidden;
479
+ text-overflow: ellipsis;
480
+ white-space: nowrap;
481
+ }
482
+
483
+ .error {
484
+ color: #b00020;
485
+ padding: 8px;
486
+ border-bottom: 1px solid var(--ap-border);
487
+ overflow-wrap: anywhere;
488
+ }
489
+
490
+ .hidden {
491
+ display: none;
492
+ }
493
+
494
+ .visuals {
495
+ display: grid;
496
+ width: 100%;
497
+ }
498
+
499
+ .pane {
500
+ position: relative;
501
+ border-top: 1px solid var(--ap-border);
502
+ cursor: pointer;
503
+ user-select: none;
504
+ }
505
+
506
+ .pane:first-child {
507
+ border-top: 0;
508
+ }
509
+
510
+ canvas {
511
+ display: block;
512
+ width: 100%;
513
+ }
514
+
515
+ .cursor {
516
+ position: absolute;
517
+ inset: 0;
518
+ pointer-events: none;
519
+ }
520
+ </style>
521
+ <div class="root">
522
+ <div class="toolbar hidden">
523
+ <button type="button" aria-label="Play" disabled>Play</button>
524
+ <span class="time hidden"><span class="current">00:00.000</span> / <span class="duration">00:00.000</span></span>
525
+ <span class="status"></span>
526
+ </div>
527
+ <div class="error hidden" role="alert"></div>
528
+ <div class="visuals">
529
+ <div class="pane waveform-pane">
530
+ <canvas class="waveform"></canvas>
531
+ <canvas class="cursor waveform-cursor"></canvas>
532
+ </div>
533
+ <div class="pane spectrogram-pane">
534
+ <canvas class="spectrogram"></canvas>
535
+ <canvas class="cursor spectrogram-overlay"></canvas>
536
+ <canvas class="cursor spectrogram-cursor"></canvas>
537
+ </div>
538
+ </div>
539
+ </div>
540
+ `;
541
+ class C extends HTMLElement {
542
+ constructor() {
543
+ super();
544
+ c(this, "audio");
545
+ c(this, "audioBuffer");
546
+ c(this, "waveformPeaks");
547
+ c(this, "spectrogram");
548
+ c(this, "worker");
549
+ c(this, "resizeObserver");
550
+ c(this, "animationFrame", 0);
551
+ c(this, "loadingToken", 0);
552
+ c(this, "blobUrl");
553
+ c(this, "playRequestedAt");
554
+ c(this, "root");
555
+ c(this, "button");
556
+ c(this, "toolbar");
557
+ c(this, "timeEl");
558
+ c(this, "currentEl");
559
+ c(this, "durationEl");
560
+ c(this, "statusEl");
561
+ c(this, "errorEl");
562
+ c(this, "waveformPane");
563
+ c(this, "spectrogramPane");
564
+ c(this, "waveformCanvas");
565
+ c(this, "spectrogramCanvas");
566
+ c(this, "spectrogramOverlay");
567
+ c(this, "waveformCursor");
568
+ c(this, "spectrogramCursor");
569
+ c(this, "handlePlayButton", () => {
570
+ this.audio && (this.audio.paused ? (this.playRequestedAt = performance.now(), this.audio.play().catch((t) => this.handleError("Audio playback failed.", t))) : this.audio.pause());
571
+ });
572
+ c(this, "handleSeekClick", (t) => {
573
+ if (!this.audio || !Number.isFinite(this.duration) || this.duration <= 0) return;
574
+ const i = t.currentTarget.getBoundingClientRect(), o = (t.clientX - i.left) / i.width;
575
+ this.audio.currentTime = Math.max(0, Math.min(this.duration, o * this.duration)), this.updateTimeLabels(), this.drawCursors(), this.dispatchTypedEvent("seek"), this.audio.paused ? (this.playRequestedAt = performance.now(), this.audio.play().catch((a) => this.handleError("Audio playback failed.", a))) : this.audio.pause();
576
+ });
577
+ c(this, "handleKeydown", (t) => {
578
+ t.key !== " " && t.key !== "Enter" || (t.preventDefault(), this.handlePlayButton());
579
+ });
580
+ c(this, "handleTimeUpdate", () => {
581
+ this.updateTimeLabels(), this.dispatchTypedEvent("timeupdate");
582
+ });
583
+ c(this, "handleAudioPlay", () => {
584
+ this.button.textContent = "Pause", this.button.setAttribute("aria-label", "Pause"), this.button.dataset.state = "pause", this.dispatchTypedEvent("play");
585
+ });
586
+ c(this, "handleAudioPlaying", () => {
587
+ if (this.playRequestedAt !== void 0) {
588
+ const t = {
589
+ playToPlayingMs: performance.now() - this.playRequestedAt
590
+ };
591
+ this.dispatchEvent(new CustomEvent("playprofile", { detail: t })), this.playRequestedAt = void 0;
592
+ }
593
+ this.startAnimation();
594
+ });
595
+ c(this, "handleAudioPause", () => {
596
+ this.button.textContent = "Play", this.button.setAttribute("aria-label", "Play"), this.button.dataset.state = "play", this.dispatchTypedEvent("pause"), this.stopAnimation(), this.drawCursors();
597
+ });
598
+ c(this, "updateTimeLabels", () => {
599
+ var t;
600
+ this.currentEl.textContent = R(((t = this.audio) == null ? void 0 : t.currentTime) ?? 0), this.durationEl.textContent = R(this.duration);
601
+ });
602
+ this.root = this.attachShadow({ mode: "open" }), this.root.append(L.content.cloneNode(!0)), this.button = this.requireElement("button"), this.button.dataset.state = "play", this.toolbar = this.requireElement(".toolbar"), this.timeEl = this.requireElement(".time"), this.currentEl = this.requireElement(".current"), this.durationEl = this.requireElement(".duration"), this.statusEl = this.requireElement(".status"), this.errorEl = this.requireElement(".error"), this.waveformPane = this.requireElement(".waveform-pane"), this.spectrogramPane = this.requireElement(".spectrogram-pane"), this.waveformCanvas = this.requireElement(".waveform"), this.spectrogramCanvas = this.requireElement(".spectrogram"), this.spectrogramOverlay = this.requireElement(".spectrogram-overlay"), this.waveformCursor = this.requireElement(".waveform-cursor"), this.spectrogramCursor = this.requireElement(".spectrogram-cursor");
603
+ }
604
+ get src() {
605
+ return this.getAttribute("src") ?? "";
606
+ }
607
+ set src(t) {
608
+ this.setAttribute("src", t);
609
+ }
610
+ get height() {
611
+ return this.getNumberAttribute("height", this.waveformHeight + this.spectrogramHeight);
612
+ }
613
+ set height(t) {
614
+ this.setAttribute("height", String(t));
615
+ }
616
+ get waveformHeight() {
617
+ return this.getNumberAttribute("waveform-height", 80);
618
+ }
619
+ set waveformHeight(t) {
620
+ this.setAttribute("waveform-height", String(t));
621
+ }
622
+ get spectrogramHeight() {
623
+ return this.getNumberAttribute("spectrogram-height", 120);
624
+ }
625
+ set spectrogramHeight(t) {
626
+ this.setAttribute("spectrogram-height", String(t));
627
+ }
628
+ get showWaveform() {
629
+ return this.getBooleanAttribute("show-waveform", !0);
630
+ }
631
+ set showWaveform(t) {
632
+ this.setBooleanAttribute("show-waveform", t);
633
+ }
634
+ get showSpectrogram() {
635
+ return this.getBooleanAttribute("show-spectrogram", !0);
636
+ }
637
+ set showSpectrogram(t) {
638
+ this.setBooleanAttribute("show-spectrogram", t);
639
+ }
640
+ get showControls() {
641
+ return this.getBooleanAttribute("show-controls", !1);
642
+ }
643
+ set showControls(t) {
644
+ this.setBooleanAttribute("show-controls", t);
645
+ }
646
+ get showTime() {
647
+ return this.getBooleanAttribute("show-time", !1);
648
+ }
649
+ set showTime(t) {
650
+ this.setBooleanAttribute("show-time", t);
651
+ }
652
+ get waveformStyle() {
653
+ const t = this.getAttribute("waveform-style");
654
+ return t === "line" ? "lines" : t === "waveform" ? "waveform" : t === "bars" || t === "lines" || t === "blocks" || t === "dots" ? t : "waveform";
655
+ }
656
+ set waveformStyle(t) {
657
+ this.setAttribute("waveform-style", t);
658
+ }
659
+ get waveformBarWidth() {
660
+ return this.getOptionalNumberAttribute("waveform-bar-width");
661
+ }
662
+ set waveformBarWidth(t) {
663
+ this.setAttribute("waveform-bar-width", String(t));
664
+ }
665
+ get waveformBarSpacing() {
666
+ return this.getOptionalNumberAttribute("waveform-bar-spacing");
667
+ }
668
+ set waveformBarSpacing(t) {
669
+ this.setAttribute("waveform-bar-spacing", String(t));
670
+ }
671
+ get autoplay() {
672
+ return this.getBooleanAttribute("autoplay", !1);
673
+ }
674
+ set autoplay(t) {
675
+ this.setBooleanAttribute("autoplay", t);
676
+ }
677
+ get fftSize() {
678
+ return this.getNumberAttribute("fft-size", 1024);
679
+ }
680
+ set fftSize(t) {
681
+ this.setAttribute("fft-size", String(t));
682
+ }
683
+ get hopSize() {
684
+ return this.getNumberAttribute("hop-size", 256);
685
+ }
686
+ set hopSize(t) {
687
+ this.setAttribute("hop-size", String(t));
688
+ }
689
+ get windowType() {
690
+ const t = this.getAttribute("window-type");
691
+ return t === "hamming" || t === "rectangular" ? t : "hann";
692
+ }
693
+ set windowType(t) {
694
+ this.setAttribute("window-type", t);
695
+ }
696
+ get minDb() {
697
+ return this.getNumberAttribute("min-db", -80);
698
+ }
699
+ set minDb(t) {
700
+ this.setAttribute("min-db", String(t));
701
+ }
702
+ get maxDb() {
703
+ return this.getNumberAttribute("max-db", 0);
704
+ }
705
+ set maxDb(t) {
706
+ this.setAttribute("max-db", String(t));
707
+ }
708
+ get colorMap() {
709
+ const t = this.getAttribute("color-map");
710
+ return t === "gray" || t === "magma" || t === "viridis" || t === "inferno" ? t : "audition";
711
+ }
712
+ set colorMap(t) {
713
+ this.setAttribute("color-map", t);
714
+ }
715
+ get channel() {
716
+ const t = this.getAttribute("channel");
717
+ if (!t || t === "mix") return "mix";
718
+ const r = Number(t);
719
+ return Number.isInteger(r) ? r : "mix";
720
+ }
721
+ set channel(t) {
722
+ this.setAttribute("channel", String(t));
723
+ }
724
+ connectedCallback() {
725
+ this.button.addEventListener("click", this.handlePlayButton), this.waveformPane.addEventListener("click", this.handleSeekClick), this.spectrogramPane.addEventListener("click", this.handleSeekClick), this.addEventListener("keydown", this.handleKeydown), this.tabIndex = this.tabIndex >= 0 ? this.tabIndex : 0, this.resizeObserver = new ResizeObserver(() => this.layoutAndRender()), this.resizeObserver.observe(this), this.layoutAndRender(), this.src && this.load();
726
+ }
727
+ disconnectedCallback() {
728
+ var t, r, i;
729
+ this.button.removeEventListener("click", this.handlePlayButton), this.waveformPane.removeEventListener("click", this.handleSeekClick), this.spectrogramPane.removeEventListener("click", this.handleSeekClick), this.removeEventListener("keydown", this.handleKeydown), (t = this.resizeObserver) == null || t.disconnect(), this.stopAnimation(), (r = this.worker) == null || r.terminate(), (i = this.audio) == null || i.pause(), this.revokeBlobUrl();
730
+ }
731
+ attributeChangedCallback(t, r, i) {
732
+ if (!(r === i || !this.isConnected)) {
733
+ if (t === "src") {
734
+ this.load();
735
+ return;
736
+ }
737
+ if (["fft-size", "hop-size", "window-type", "min-db", "max-db", "channel"].includes(t)) {
738
+ this.audioBuffer && this.recomputeAnalysis();
739
+ return;
740
+ }
741
+ this.layoutAndRender();
742
+ }
743
+ }
744
+ async load() {
745
+ var i, o, a;
746
+ const t = this.src, r = ++this.loadingToken;
747
+ if (this.clearError(), this.setStatus(t ? "Loading" : ""), this.dispatchTypedEvent("loadstart"), this.button.disabled = !0, this.waveformPeaks = void 0, this.spectrogram = void 0, this.layoutAndRender(), (i = this.worker) == null || i.terminate(), (o = this.audio) == null || o.pause(), this.revokeBlobUrl(), !t) {
748
+ this.setStatus("");
749
+ return;
750
+ }
751
+ try {
752
+ const n = performance.now(), h = await I(t), l = performance.now();
753
+ if (r !== this.loadingToken) return;
754
+ this.blobUrl = URL.createObjectURL(new Blob([h])), this.audio = O(this.blobUrl, this.autoplay), this.bindAudio();
755
+ const u = this.waitForAudioReady(this.audio);
756
+ this.audioBuffer = await z(h);
757
+ const d = performance.now();
758
+ if (r !== this.loadingToken) return;
759
+ const m = Math.max(1, Math.floor(this.getCanvasWidth())), p = performance.now();
760
+ this.waveformPeaks = this.showWaveform ? E(this.audioBuffer, m, this.channel) : void 0;
761
+ const g = performance.now();
762
+ await u;
763
+ const b = performance.now();
764
+ if (r !== this.loadingToken) return;
765
+ this.button.disabled = !1, this.setStatus(this.showSpectrogram ? "Analyzing" : ""), this.updateTimeLabels(), this.dispatchTypedEvent("loaded");
766
+ const w = performance.now(), f = {
767
+ fetchMs: l - n,
768
+ audioReadyMs: b - l,
769
+ decodeMs: d - l,
770
+ waveformMs: g - p,
771
+ firstUsableMs: w - n
772
+ };
773
+ this.dispatchEvent(new CustomEvent("profile", { detail: f })), this.layoutAndRender(), this.computeSpectrogramForCurrentBuffer(r, f, n), this.autoplay && await ((a = this.audio) == null ? void 0 : a.play());
774
+ } catch (n) {
775
+ if (r !== this.loadingToken) return;
776
+ this.handleError("Failed to load audio.", n);
777
+ }
778
+ }
779
+ async recomputeAnalysis() {
780
+ if (!this.audioBuffer) return;
781
+ const t = Math.max(1, Math.floor(this.getCanvasWidth()));
782
+ this.waveformPeaks = this.showWaveform ? E(this.audioBuffer, t, this.channel) : void 0, this.layoutAndRender(), this.showSpectrogram ? (this.setStatus("Analyzing"), this.spectrogram = await this.computeSpectrogramInWorker(), this.setStatus("")) : this.spectrogram = void 0, this.layoutAndRender();
783
+ }
784
+ async computeSpectrogramForCurrentBuffer(t, r, i) {
785
+ if (!this.audioBuffer || !this.showSpectrogram) {
786
+ this.spectrogram = void 0, this.setStatus(""), this.layoutAndRender();
787
+ return;
788
+ }
789
+ try {
790
+ const o = performance.now();
791
+ this.spectrogram = await this.computeSpectrogramInWorker();
792
+ const a = performance.now();
793
+ if (t !== this.loadingToken) return;
794
+ this.setStatus(""), this.layoutAndRender(), this.dispatchEvent(
795
+ new CustomEvent("profile", {
796
+ detail: {
797
+ ...r,
798
+ spectrogramMs: a - o,
799
+ totalMs: a - i
800
+ }
801
+ })
802
+ );
803
+ } catch (o) {
804
+ if (t !== this.loadingToken) return;
805
+ this.spectrogram = void 0, this.setStatus("Spectrogram unavailable"), this.dispatchEvent(
806
+ new CustomEvent("error", {
807
+ detail: {
808
+ message: o instanceof Error ? o.message : "Failed to compute spectrogram.",
809
+ cause: o
810
+ }
811
+ })
812
+ );
813
+ }
814
+ }
815
+ computeSpectrogramInWorker() {
816
+ if (!this.audioBuffer) return Promise.resolve(void 0);
817
+ const t = F(this.audioBuffer, this.channel), r = {
818
+ samples: t,
819
+ sampleRate: this.audioBuffer.sampleRate,
820
+ fftSize: this.fftSize,
821
+ hopSize: this.hopSize,
822
+ windowType: this.windowType,
823
+ minDb: this.minDb,
824
+ maxDb: this.maxDb
825
+ };
826
+ return typeof Worker > "u" ? Promise.resolve(Y(t, this.audioBuffer.sampleRate, r)) : new Promise((i, o) => {
827
+ var a;
828
+ (a = this.worker) == null || a.terminate(), this.worker = new Worker(new URL(
829
+ /* @vite-ignore */
830
+ "/assets/spectrogram.worker-Burn7NUr.js",
831
+ import.meta.url
832
+ ), { type: "module" }), this.worker.addEventListener(
833
+ "message",
834
+ (n) => {
835
+ "error" in n.data ? o(new Error(n.data.error.message)) : i(n.data);
836
+ },
837
+ { once: !0 }
838
+ ), this.worker.addEventListener("error", () => o(new Error("Spectrogram worker failed.")), { once: !0 }), this.worker.postMessage(r, [r.samples.buffer]);
839
+ });
840
+ }
841
+ bindAudio() {
842
+ this.audio && (this.audio.addEventListener("loadedmetadata", this.updateTimeLabels), this.audio.addEventListener("timeupdate", this.handleTimeUpdate), this.audio.addEventListener("play", this.handleAudioPlay), this.audio.addEventListener("playing", this.handleAudioPlaying), this.audio.addEventListener("pause", this.handleAudioPause), this.audio.addEventListener("ended", this.handleAudioPause), this.audio.addEventListener("error", () => {
843
+ var t;
844
+ return this.handleError("Audio playback failed.", (t = this.audio) == null ? void 0 : t.error);
845
+ }));
846
+ }
847
+ layoutAndRender() {
848
+ const t = Math.max(1, Math.floor(this.getCanvasWidth())), { waveformHeight: r, spectrogramHeight: i } = this.getPaneHeights();
849
+ this.toolbar.classList.toggle("hidden", !this.showControls && !this.showTime), this.button.classList.toggle("hidden", !this.showControls), this.timeEl.classList.toggle("hidden", !this.showTime), this.waveformPane.classList.toggle("hidden", !this.showWaveform), this.spectrogramPane.classList.toggle("hidden", !this.showSpectrogram), this.showWaveform && (v(this.waveformCanvas, t, r), v(this.waveformCursor, t, r), P(this.waveformCanvas, this.waveformPeaks, {
850
+ color: this.cssVar("--ap-waveform", "rgba(0, 214, 163, 0.34)"),
851
+ playedColor: this.cssVar("--ap-waveform-played", "#00f0b5"),
852
+ centerColor: this.cssVar("--ap-waveform-center", "rgba(0, 92, 58, 0.7)"),
853
+ progressColor: this.cssVar("--ap-waveform-progress", "#00f0b5"),
854
+ background: this.cssVar("--ap-waveform-bg", "#020604"),
855
+ style: this.waveformStyle,
856
+ barWidth: this.waveformBarWidth,
857
+ barSpacing: this.waveformBarSpacing,
858
+ progress: this.playbackProgress
859
+ })), this.showSpectrogram && (v(this.spectrogramCanvas, t, i), v(this.spectrogramOverlay, t, i), v(this.spectrogramCursor, t, i), X(this.spectrogramCanvas, this.spectrogram, {
860
+ colorMap: this.colorMap,
861
+ background: this.cssVar("--ap-bg", "#ffffff"),
862
+ tickColor: this.cssVar("--ap-spectrogram-tick", "rgba(255, 255, 255, 0.42)")
863
+ }), this.drawSpectrogramOverlay()), this.drawCursors();
864
+ }
865
+ drawCursors() {
866
+ var a;
867
+ const t = ((a = this.audio) == null ? void 0 : a.currentTime) ?? 0, r = this.duration, i = this.cssVar("--ap-cursor", "#ff0000"), o = this.cssVar("--ap-cursor-shadow", "rgba(0, 0, 0, 0.45)");
868
+ this.showWaveform && this.waveformStyle === "waveform" && P(this.waveformCanvas, this.waveformPeaks, {
869
+ color: this.cssVar("--ap-waveform", "rgba(0, 214, 163, 0.34)"),
870
+ playedColor: this.cssVar("--ap-waveform-played", "#00f0b5"),
871
+ centerColor: this.cssVar("--ap-waveform-center", "rgba(0, 92, 58, 0.7)"),
872
+ progressColor: this.cssVar("--ap-waveform-progress", "#00f0b5"),
873
+ background: this.cssVar("--ap-waveform-bg", "#020604"),
874
+ style: this.waveformStyle,
875
+ barWidth: this.waveformBarWidth,
876
+ barSpacing: this.waveformBarSpacing,
877
+ progress: this.playbackProgress
878
+ }), this.drawSpectrogramOverlay(), this.clearCursor(this.waveformCursor), this.clearCursor(this.spectrogramCursor), this.showWaveform && T(this.waveformCursor, t, r, i, o), this.showSpectrogram && T(this.spectrogramCursor, t, r, i, o);
879
+ }
880
+ clearCursor(t) {
881
+ const r = t.getContext("2d");
882
+ r == null || r.clearRect(0, 0, t.clientWidth, t.clientHeight);
883
+ }
884
+ drawSpectrogramOverlay() {
885
+ const t = this.spectrogramOverlay.getContext("2d");
886
+ if (!t) return;
887
+ const r = this.spectrogramOverlay.clientWidth, i = this.spectrogramOverlay.clientHeight;
888
+ if (t.clearRect(0, 0, r, i), !this.showSpectrogram || !this.spectrogram) return;
889
+ const o = r * this.playbackProgress, a = 1 - this.cssNumberVar("--ap-spectrogram-unplayed-opacity", 0.48);
890
+ o < r && (t.fillStyle = `rgba(0, 0, 0, ${Math.max(0, Math.min(1, a))})`, t.fillRect(o, 0, r - o, i));
891
+ }
892
+ startAnimation() {
893
+ this.stopAnimation();
894
+ const t = () => {
895
+ this.updateTimeLabels(), this.drawCursors(), this.animationFrame = requestAnimationFrame(t);
896
+ };
897
+ this.animationFrame = requestAnimationFrame(t);
898
+ }
899
+ stopAnimation() {
900
+ this.animationFrame && (cancelAnimationFrame(this.animationFrame), this.animationFrame = 0);
901
+ }
902
+ get duration() {
903
+ var t, r;
904
+ return ((t = this.audio) == null ? void 0 : t.duration) || ((r = this.audioBuffer) == null ? void 0 : r.duration) || 0;
905
+ }
906
+ get playbackProgress() {
907
+ var r;
908
+ const t = this.duration;
909
+ return !Number.isFinite(t) || t <= 0 ? 0 : Math.max(0, Math.min(1, (((r = this.audio) == null ? void 0 : r.currentTime) ?? 0) / t));
910
+ }
911
+ dispatchTypedEvent(t) {
912
+ var i;
913
+ const r = {
914
+ currentTime: ((i = this.audio) == null ? void 0 : i.currentTime) ?? 0,
915
+ duration: this.duration
916
+ };
917
+ this.dispatchEvent(new CustomEvent(t, { detail: r }));
918
+ }
919
+ handleError(t, r) {
920
+ this.button.disabled = !0, this.setStatus("");
921
+ const i = {
922
+ message: r instanceof Error ? `${t} ${r.message}` : t,
923
+ cause: r
924
+ };
925
+ this.errorEl.textContent = i.message, this.errorEl.classList.remove("hidden"), this.dispatchEvent(new CustomEvent("error", { detail: i }));
926
+ }
927
+ clearError() {
928
+ this.errorEl.textContent = "", this.errorEl.classList.add("hidden");
929
+ }
930
+ setStatus(t) {
931
+ this.statusEl.textContent = t;
932
+ }
933
+ getCanvasWidth() {
934
+ return this.clientWidth || this.getBoundingClientRect().width || 640;
935
+ }
936
+ getPaneHeights() {
937
+ if (!this.showWaveform && !this.showSpectrogram)
938
+ return { waveformHeight: 0, spectrogramHeight: 0 };
939
+ const t = this.hasAttribute("height"), r = this.hasAttribute("waveform-height"), i = this.hasAttribute("spectrogram-height");
940
+ if (!t)
941
+ return {
942
+ waveformHeight: this.showWaveform ? this.waveformHeight : 0,
943
+ spectrogramHeight: this.showSpectrogram ? this.spectrogramHeight : 0
944
+ };
945
+ const o = this.height;
946
+ return this.showWaveform && !this.showSpectrogram ? { waveformHeight: r ? this.waveformHeight : o, spectrogramHeight: 0 } : !this.showWaveform && this.showSpectrogram ? { waveformHeight: 0, spectrogramHeight: i ? this.spectrogramHeight : o } : r && i ? { waveformHeight: this.waveformHeight, spectrogramHeight: this.spectrogramHeight } : r ? { waveformHeight: this.waveformHeight, spectrogramHeight: Math.max(1, o - this.waveformHeight) } : i ? { waveformHeight: Math.max(1, o - this.spectrogramHeight), spectrogramHeight: this.spectrogramHeight } : {
947
+ waveformHeight: Math.max(1, Math.round(o * (80 / 200))),
948
+ spectrogramHeight: Math.max(1, o - Math.round(o * (80 / 200)))
949
+ };
950
+ }
951
+ getNumberAttribute(t, r) {
952
+ const i = Number(this.getAttribute(t));
953
+ return Number.isFinite(i) && i > 0 ? i : r;
954
+ }
955
+ getOptionalNumberAttribute(t) {
956
+ if (!this.hasAttribute(t)) return;
957
+ const r = Number(this.getAttribute(t));
958
+ return Number.isFinite(r) && r > 0 ? r : void 0;
959
+ }
960
+ getBooleanAttribute(t, r) {
961
+ if (!this.hasAttribute(t)) return r;
962
+ const i = this.getAttribute(t);
963
+ return i === "" || i === "true" || i === t;
964
+ }
965
+ setBooleanAttribute(t, r) {
966
+ r ? this.setAttribute(t, "") : this.setAttribute(t, "false");
967
+ }
968
+ cssVar(t, r) {
969
+ return getComputedStyle(this).getPropertyValue(t).trim() || r;
970
+ }
971
+ cssNumberVar(t, r) {
972
+ const i = Number(getComputedStyle(this).getPropertyValue(t).trim());
973
+ return Number.isFinite(i) ? i : r;
974
+ }
975
+ requireElement(t) {
976
+ const r = this.root.querySelector(t);
977
+ if (!r) throw new Error(`Missing template element: ${t}`);
978
+ return r;
979
+ }
980
+ waitForAudioReady(t) {
981
+ return t.readyState >= HTMLMediaElement.HAVE_METADATA ? Promise.resolve() : new Promise((r, i) => {
982
+ const o = () => {
983
+ t.removeEventListener("loadedmetadata", a), t.removeEventListener("error", n);
984
+ }, a = () => {
985
+ o(), r();
986
+ }, n = () => {
987
+ o(), i(t.error ?? new Error("Audio metadata failed to load."));
988
+ };
989
+ t.addEventListener("loadedmetadata", a, { once: !0 }), t.addEventListener("error", n, { once: !0 }), t.load();
990
+ });
991
+ }
992
+ revokeBlobUrl() {
993
+ this.blobUrl && (URL.revokeObjectURL(this.blobUrl), this.blobUrl = void 0);
994
+ }
995
+ }
996
+ c(C, "observedAttributes", [
997
+ "src",
998
+ "height",
999
+ "waveform-height",
1000
+ "spectrogram-height",
1001
+ "show-waveform",
1002
+ "show-spectrogram",
1003
+ "show-controls",
1004
+ "show-time",
1005
+ "waveform-style",
1006
+ "waveform-bar-width",
1007
+ "waveform-bar-spacing",
1008
+ "autoplay",
1009
+ "fft-size",
1010
+ "hop-size",
1011
+ "window-type",
1012
+ "min-db",
1013
+ "max-db",
1014
+ "color-map",
1015
+ "channel"
1016
+ ]);
1017
+ class nt extends C {
1018
+ }
1019
+ customElements.get("wavegram-player") || customElements.define("wavegram-player", C);
1020
+ customElements.get("audio-preview-spectrogram") || customElements.define("audio-preview-spectrogram", nt);
1021
+ export {
1022
+ nt as AudioPreviewSpectrogram,
1023
+ C as Wavegram
1024
+ };