UNPKG

modern-audio

Version:
412 lines (411 loc) 11.3 kB
const I = ({ context: e }) => { const o = e.createAnalyser(); return o.fftSize = 2048, { name: "analyser", node: o }; }, H = ({ context: e }) => ({ name: "destination", node: e.destination }), O = ({ source: e }) => ({ name: "loop", props: { loop: { value: !1, getter() { return this.value; }, setter(o) { this.value = o = Boolean(o), e instanceof MediaElementAudioSourceNode ? e.mediaElement.loop = o : e instanceof AudioBufferSourceNode && (e.loop = o); } } } }), V = (e) => { const { context: o } = e; let t = !1; const n = o.createBiquadFilter(); n.Q.value = 8.3, n.frequency.value = 355, n.gain.value = 3, n.type = "bandpass"; const r = o.createDynamicsCompressor(); return r.attack.value = 0, r.knee.value = 40, r.ratio.value = 12, r.release.value = 0.25, r.threshold.value = -50, { name: "noise-reduction", node: () => t ? n : void 0, connect: (s) => n.connect(r).connect(s), disconnect: () => { n.disconnect(), r.disconnect(); }, props: { noiseReduction: { value: t, getter() { return this.value; }, setter(s) { s === "" && (s = !0), t !== s && (this.value = t = s, e.reconnect()); } } } }; }, F = ({ context: e }) => { const o = e.createStereoPanner(); return { name: "panner", node: o, props: { pan: { value: o.pan.value, getter() { return this.value; }, setter(t) { this.value = o.pan.value = Number(t); } } } }; }, W = ({ source: e }) => ({ name: "playbackRate", props: { playbackRate: { value: 1, getter() { return this.value; }, setter(o) { this.value = o = Number(o), e instanceof MediaElementAudioSourceNode ? e.mediaElement.playbackRate = o : e instanceof AudioBufferSourceNode && (e.playbackRate.value = o); } } } }), q = ({ source: e }) => ({ name: "source", node: e }); function j(e) { return e instanceof MediaElementAudioSourceNode ? e.mediaElement.playbackRate : e instanceof AudioBufferSourceNode ? e.playbackRate.value : 0; } function z(e) { var o, t; return e instanceof MediaElementAudioSourceNode ? e.mediaElement.duration : e instanceof AudioBufferSourceNode && (t = (o = e.buffer) == null ? void 0 : o.duration) != null ? t : 0; } const $ = ({ context: e, source: o }) => { const t = e.createGain(); return { name: "volume", node: t, props: { db: { value: 1, getter() { return this.value; }, setter(n) { this.value = n = Number(n), t.gain.value = Math.pow(10, n / 20); } }, fadeIn: { value: 0, getter() { return this.value; }, setter(n) { this.value = n; const { at: r, duration: s } = typeof n == "object" ? n : { at: e.currentTime, duration: Number(n) }; t.gain.setValueAtTime(0.01, r), t.gain.exponentialRampToValueAtTime(t.gain.value, r + s); } }, fadeOut: { value: 0, getter() { return this.value; }, setter(n) { this.value = n; const { at: r, duration: s } = typeof n == "object" ? n : { at: e.currentTime + z(o) / j(o) - Number(n), duration: Number(n) }; t.gain.setValueAtTime(t.gain.value, r), t.gain.exponentialRampToValueAtTime(0.01, r + s); } } } }; }; function G(e) { return e = e || [], [ q, W, O, V, $, F, ...e, I, H ]; } function se(e) { return e; } function Q(e, o) { return G(o).map((t) => t(e)); } function k({ node: e }) { return typeof e == "function" ? e() : e; } function N(e) { e.slice(1).reduce( (o, t) => { var r; const n = k(t); return n ? (o.connect ? o.connect(n) : (r = k(o)) == null || r.connect(n), t) : o; }, e[0] ); } function P(e) { e.forEach((o) => { var t; o.disconnect ? o.disconnect() : (t = k(o)) == null || t.disconnect(); }); } function J(e) { const o = /* @__PURE__ */ new Map(); for (const t of e) if (!!t.props) for (const n in t.props) o.set(n, t.props[n]); return o; } function v(e) { return e.replace(/-(\w)/g, (o, t) => t.toLocaleUpperCase()); } function C(e, o) { const t = o in e ? e[o] : void 0; return t instanceof Function ? t.bind(e) : t; } function T(e, o) { return fetch(e).then((t) => t.arrayBuffer()).then((t) => o.decodeAudioData(t)); } function K(e, o, t = 0, n) { t = t || 0, n = n || o - 1; const r = e.length / o, s = ~~(r / 10) || 1, i = e.numberOfChannels, c = [], a = []; for (let u = 0; u < i; u++) { c[u] = c[u] || []; const l = c[u], d = e.getChannelData(u); let f; for (f = t; f <= n; f++) { const g = ~~(f * r), y = ~~(g + r); let h = d[g], p = h; for (let A = g; A < y; A += s) { const b = d[A]; b > p && (p = b), b < h && (h = b); } l[2 * f] = p, l[2 * f + 1] = h, (u === 0 || p > a[2 * f]) && (a[2 * f] = p), (u === 0 || h < a[2 * f + 1]) && (a[2 * f + 1] = h); } } return { splitPeaks: c, mergedPeaks: a }; } async function X(e, o = "#364356", t, n) { const { width: r, height: s } = e, i = e.getContext("2d"), c = typeof t == "string" ? await T(t, n) : t.buffer, { mergedPeaks: a } = K(c, r), u = window.devicePixelRatio || 1, l = a.some((m) => m < 0) ? 2 : 1, f = a.length / l / r, g = s / 2, y = 0.5 / u, h = 2, p = 1, b = 1 / 1, B = h * u, D = Math.max(u, ~~(B / 2)), R = B + D; i.clearRect(0, 0, r, s); for (let m = 0; m < a.length; m += R) { let x = 0, S = Math.floor(m * f) * l; const L = Math.floor((m + R) * f) * l; do { const E = Math.abs(a[S]); E > x && (x = E), S += l; } while (S < L); const M = Math.max(Math.round(x / b * g), p); i.fillStyle = o, i.fillRect(m + y, g - M, B + y, M * 2); } } function Y(e, o = "#364356", t) { const { width: n, height: r } = e, s = e.getContext("2d"), i = t.frequencyBinCount, c = new Uint8Array(i); s.clearRect(0, 0, n, r), function a() { requestAnimationFrame(a), t.getByteTimeDomainData(c); const u = n / i * 1.5; s.clearRect(0, 0, n, r); for (let l = 0, d = 0; l < i; l++) { const f = c[l]; s.fillStyle = o, s.fillRect(d, r - f, u, f), d += u + 1; } }(); } function Z(e, o) { const t = o != null ? o : new AudioContext(); if (typeof e == "string") { const n = t.createBufferSource(); return { context: t, source: n, src: e, load: async () => { n.buffer = await T(e, t); }, clone: () => { const r = n.buffer, s = t.createBufferSource(); return s.buffer = r, s; } }; } else if (e instanceof AudioBuffer) { const n = t.createBufferSource(); return n.buffer = e, { context: t, source: n, clone: () => { const r = n.buffer, s = t.createBufferSource(); return s.buffer = r, s; } }; } else return t instanceof AudioContext && e instanceof HTMLMediaElement ? { context: t, source: t.createMediaElementSource(e), src: e.src } : { context: t, source: e }; } function _(e, o) { const { load: t, clone: n, ...r } = Z(e, o), s = { ...r, processors: [], props: /* @__PURE__ */ new Map(), setup() { P(this.processors), this.processors = Q(this), this.props = J(this.processors), N(this.processors); }, reconnect() { P(this.processors), N(this.processors); }, get(i) { var c, a; if (i) return (a = (c = this.props.get(v(i))) == null ? void 0 : c.getter) == null ? void 0 : a.call(c); { const u = {}; return this.props.forEach((l, d) => { u[d] = this.get(d); }), u; } }, set(i, c) { var a, u; if (typeof i == "string") (u = (a = this.props.get(v(i))) == null ? void 0 : a.setter) == null || u.call(a, c); else for (const l in i) this.set(l, i[l]); }, renderBarChart(i, c) { return X( i, c, this.source instanceof MediaElementAudioSourceNode ? this.source.mediaElement.src : this.source, this.context ); }, renderTimeDomainBarChart(i, c) { return Y( i, c, this.processors.find((a) => a.name === "analyser").node ); } }; return t && (s.load = async function() { await t(), this.setup(); }), n && (s.reset = function() { this.source = n(); const i = this.get(); this.setup(), this.set(i); }, s.resetAndStart = function(i, c, a) { var u; (u = this.reset) == null || u.call(this), this.source.start(i, c, a); }), s.source instanceof AudioBufferSourceNode || s.setup(), new Proxy(s, { get(i, c) { var a; return (a = C(i, c)) != null ? a : C(i.source, c); }, set(i, c, a) { return c in i ? i[c] = a : i.source[c] = a, !0; } }); } class U extends HTMLAudioElement { constructor() { super(), this.source = _(this), this.setup(); } static install() { customElements.define("modern-audio", U, { extends: "audio" }); } async setup() { this.setupListeners(); for (let o = 0; o < this.attributes.length; o++) { const t = this.attributes.item(o); t && this.source.set(t.name, t.value); } } setupListeners() { this.addEventListener("play", () => this.source.context.resume()); } } function ee(e) { return new Blob([ ne( te(e), e.sampleRate ) ], { type: "audio/wav" }); } function te(e) { const o = e.getChannelData(0), t = o.length * 2, n = new Float32Array(t); let r = 0, s = 0; for (; r < t; ) n[r++] = o[s], n[r++] = o[s], s++; return n; } function ne(e, o) { const t = new ArrayBuffer(44 + e.length * 2), n = new DataView(t); return w(n, 0, "RIFF"), n.setUint32(4, 32 + e.length * 2, !0), w(n, 8, "WAVE"), w(n, 12, "fmt "), n.setUint32(16, 16, !0), n.setUint16(20, 1, !0), n.setUint16(22, 2, !0), n.setUint32(24, o, !0), n.setUint32(28, o * 4, !0), n.setUint16(32, 4, !0), n.setUint16(34, 16, !0), w(n, 36, "data"), n.setUint32(40, e.length * 2, !0), oe(n, e, 44); } function oe(e, o, t) { for (let n = 0; n < o.length; n++, t += 2) { const r = Math.max(-1, Math.min(1, o[n])); e.setInt16(t, r < 0 ? r * 32768 : r * 32767, !0); } return e; } function w(e, o, t) { for (let n = 0; n < t.length; n++) e.setUint8(o + n, t.charCodeAt(n)); } function ie(e, o = 2, t = 44100) { return new OfflineAudioContext({ length: t * e, numberOfChannels: o, sampleRate: t }); } async function re(e) { return ee( await e.startRendering() ); } async function ae(e, o = "audio") { const t = await re(e), n = document.createElement("a"); n.style.display = "none", n.href = URL.createObjectURL(t), n.download = `${o}.${t.type.split("/")[1]}`, n.click(); } export { U as ModernAudio, _ as createAudio, ie as createOfflineAudioContext, se as defineProcessor, ae as downloadOfflineAudio, re as exportOfflineAudio };