UNPKG

audio

Version:

Audio loading, editing, and rendering for JavaScript

1,431 lines (1,422 loc) 143 kB
var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __esm = (fn3, res) => function __init() { return fn3 && (res = (0, fn3[__getOwnPropNames(fn3)[0]])(fn3 = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // node_modules/audio-mic/browser.js var browser_exports = {}; __export(browser_exports, { default: () => mic }); async function mic(opts = {}) { const channels2 = opts.channels || 1; const sampleRate2 = opts.sampleRate || 44100; const bitDepth = opts.bitDepth || 16; const constraints = { audio: { sampleRate: { ideal: sampleRate2 }, channelCount: { ideal: channels2 }, echoCancellation: opts.echoCancellation ?? false, noiseSuppression: opts.noiseSuppression ?? false, autoGainControl: opts.autoGainControl ?? false } }; const stream = await navigator.mediaDevices.getUserMedia(constraints); const ownCtx = !opts.context; const ctx = opts.context || new AudioContext({ sampleRate: sampleRate2 }); const source = ctx.createMediaStreamSource(stream); let closed = false; let pending = null; let node; if (ctx.audioWorklet) { const workletCode = ` class MicProcessor extends AudioWorkletProcessor { process(inputs) { const input = inputs[0] if (input && input.length > 0) { const channels = [] for (let i = 0; i < input.length; i++) channels.push(input[i].slice()) this.port.postMessage(channels) } return true } } registerProcessor('mic-processor', MicProcessor) `; const blob = new Blob([workletCode], { type: "application/javascript" }); const url = URL.createObjectURL(blob); await ctx.audioWorklet.addModule(url); URL.revokeObjectURL(url); node = new AudioWorkletNode(ctx, "mic-processor", { numberOfInputs: 1, numberOfOutputs: 0, channelCount: channels2 }); source.connect(node); node.port.onmessage = (e) => { if (closed || !pending) return; const cb = pending; pending = null; cb(null, float32ToPCM(e.data, bitDepth)); }; } else { const bufSize = 2048; node = ctx.createScriptProcessor(bufSize, channels2, 1); source.connect(node); node.connect(ctx.destination); node.onaudioprocess = (e) => { if (closed || !pending) return; const cb = pending; pending = null; const chans = []; for (let i = 0; i < channels2; i++) chans.push(e.inputBuffer.getChannelData(i).slice()); cb(null, float32ToPCM(chans, bitDepth)); }; } read.close = close; read.end = close; read.backend = "mediastream"; return read; function read(cb) { if (cb == null || closed) { close(); return; } if (ctx.state === "suspended") ctx.resume(); pending = cb; } function close() { if (closed) return; closed = true; pending = null; source.disconnect(); stream.getTracks().forEach((t) => t.stop()); if (ownCtx) ctx.close?.(); } function float32ToPCM(channelData, bits) { const ch = channelData.length; const len = channelData[0].length; const bps = bits / 8; const buf = new Uint8Array(len * ch * bps); const view = new DataView(buf.buffer); for (let i = 0; i < len; i++) { for (let c = 0; c < ch; c++) { const sample = channelData[c][i]; const offset = (i * ch + c) * bps; if (bits === 16) { view.setInt16(offset, Math.max(-32768, Math.min(32767, Math.round(sample * 32767))), true); } else if (bits === 32) { view.setFloat32(offset, sample, true); } else if (bits === 8) { buf[offset] = Math.max(0, Math.min(255, Math.round((sample + 1) * 127.5))); } } } return buf; } } var init_browser = __esm({ "node_modules/audio-mic/browser.js"() { } }); // node_modules/audio-type/audio-type.js function audioType(buf) { if (!buf) return; buf = new Uint8Array(buf.buffer || buf); if (isWav(buf)) return "wav"; if (isAiff(buf)) return "aiff"; if (isMp3(buf)) return "mp3"; if (isAac(buf)) return "aac"; if (isFlac(buf)) return "flac"; if (isM4a(buf)) return "m4a"; if (isOpus(buf)) return "opus"; if (isOgg(buf)) return "oga"; if (isQoa(buf)) return "qoa"; if (isMidi(buf)) return "mid"; if (isCaf(buf)) return "caf"; if (isWma(buf)) return "wma"; if (isAmr(buf)) return "amr"; if (isWebm(buf)) return "webm"; } function isMp3(buf) { if (!buf || buf.length < 3) return; return buf[0] === 73 && buf[1] === 68 && buf[2] === 51 || // ID3 buf[0] === 255 && (buf[1] & 224) === 224 && (buf[1] & 6) !== 0 || // MPEG sync, layer != 0 (excludes AAC) buf[0] === 84 && buf[1] === 65 && buf[2] === 71; } function isWav(buf) { if (!buf || buf.length < 12) return; return buf[0] === 82 && buf[1] === 73 && buf[2] === 70 && buf[3] === 70 && buf[8] === 87 && buf[9] === 65 && buf[10] === 86 && buf[11] === 69; } function isOgg(buf) { if (!buf || buf.length < 4) return; return buf[0] === 79 && buf[1] === 103 && buf[2] === 103 && buf[3] === 83; } function isFlac(buf) { if (!buf || buf.length < 4) return; return buf[0] === 102 && buf[1] === 76 && buf[2] === 97 && buf[3] === 67; } function isM4a(buf) { if (!buf || buf.length < 8) return; return buf[4] === 102 && buf[5] === 116 && buf[6] === 121 && buf[7] === 112 || buf[0] === 77 && buf[1] === 52 && buf[2] === 65 && buf[3] === 32; } function isOpus(buf) { if (!buf || buf.length < 36) return; return buf[0] === 79 && buf[1] === 103 && buf[2] === 103 && buf[3] === 83 && buf[28] === 79 && buf[29] === 112 && buf[30] === 117 && buf[31] === 115 && buf[32] === 72 && buf[33] === 101 && buf[34] === 97 && buf[35] === 100; } function isQoa(buf) { if (!buf || buf.length < 4) return; return buf[0] === 113 && buf[1] === 111 && buf[2] === 97 && buf[3] === 102; } function isAiff(buf) { if (!buf || buf.length < 12) return; return buf[0] === 70 && buf[1] === 79 && buf[2] === 82 && buf[3] === 77 && buf[8] === 65 && buf[9] === 73 && buf[10] === 70 && (buf[11] === 70 || buf[11] === 67); } function isAac(buf) { if (!buf || buf.length < 2) return; return buf[0] === 255 && (buf[1] & 240) === 240 && (buf[1] & 6) === 0; } function isMidi(buf) { if (!buf || buf.length < 4) return; return buf[0] === 77 && buf[1] === 84 && buf[2] === 104 && buf[3] === 100; } function isCaf(buf) { if (!buf || buf.length < 4) return; return buf[0] === 99 && buf[1] === 97 && buf[2] === 102 && buf[3] === 102; } function isWma(buf) { if (!buf || buf.length < 8) return; return buf[0] === 48 && buf[1] === 38 && buf[2] === 178 && buf[3] === 117 && buf[4] === 142 && buf[5] === 102 && buf[6] === 207 && buf[7] === 17; } function isAmr(buf) { if (!buf || buf.length < 5) return; return buf[0] === 35 && buf[1] === 33 && buf[2] === 65 && buf[3] === 77 && buf[4] === 82; } function isWebm(buf) { if (!buf || buf.length < 4) return; return buf[0] === 26 && buf[1] === 69 && buf[2] === 223 && buf[3] === 163; } // node_modules/audio-decode/audio-decode.js var EMPTY = Object.freeze({ channelData: Object.freeze([]), sampleRate: 0 }); function decode(src, format) { if (format) return decodeChunked(src, format); return decodeWhole(src); } async function decodeWhole(src) { if (!src || typeof src === "string" || !(src.buffer || src.byteLength || src.length)) throw TypeError("Expected ArrayBuffer or Uint8Array"); let buf = new Uint8Array(src); let type = audioType(buf); if (!type) throw Error("Unknown audio format"); if (!decode[type]) throw Error("No decoder for " + type); let dec = await decode[type](); try { let result = await dec(buf); let flushed = await dec(); return merge(result, flushed); } catch (e) { dec.free(); throw e; } } async function* decodeChunked(source, format) { if (!decode[format]) throw Error("No decoder for " + format); let dec = await decode[format](); try { if (source.getReader) { let reader = source.getReader(); while (true) { let { done, value } = await reader.read(); if (done) break; let result = await dec(value instanceof Uint8Array ? value : new Uint8Array(value)); if (result.channelData.length) yield result; } } else { for await (let chunk of source) { let result = await dec(chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk)); if (result.channelData.length) yield result; } } let flushed = await dec(); if (flushed.channelData.length) yield flushed; } finally { dec.free(); } } function reg(name, load) { decode[name] = fmt(name, async () => { let mod = await load(); if (mod.decoder) { let codec2 = await mod.decoder(); return streamDecoder( (chunk) => codec2.decode(chunk), codec2.flush ? () => codec2.flush() : null, codec2.free ? () => codec2.free() : null ); } let init2 = mod.default || mod; let codec = typeof init2 === "function" ? await init2() : init2; if (codec.ready) await codec.ready; return streamDecoder( (chunk) => codec.decode(chunk), codec.flush ? () => codec.flush() : null, codec.free ? () => codec.free() : null ); }); } function fmt(name, init2) { let fn3 = (src) => { if (!src) return init2(); if (src[Symbol.asyncIterator] || src.getReader) return decodeChunked(src, name); console.warn("decode." + name + "(data) is deprecated, use decode(data)"); return (async () => { let dec = await init2(); try { let result = await dec(src instanceof Uint8Array ? src : new Uint8Array(src.buffer || src)); let flushed = await dec(); return merge(result, flushed); } catch (e) { dec.free(); throw e; } })(); }; return fn3; } reg("mp3", () => import("@audio/decode-mp3")); reg("flac", () => import("@audio/decode-flac")); reg("opus", () => import("@audio/decode-opus")); reg("oga", () => import("@audio/decode-vorbis")); reg("m4a", () => import("@audio/decode-aac")); reg("wav", () => import("@audio/decode-wav")); reg("qoa", () => import("@audio/decode-qoa")); reg("aac", () => import("@audio/decode-aac")); reg("aiff", () => import("@audio/decode-aiff")); reg("caf", () => import("@audio/decode-caf")); reg("webm", () => import("@audio/decode-webm")); reg("amr", () => import("@audio/decode-amr")); reg("wma", () => import("@audio/decode-wma")); function streamDecoder(onDecode, onFlush, onFree) { let done = false; let fn3 = async (chunk) => { if (chunk) { if (done) throw Error("Decoder already freed"); try { return norm(await onDecode(chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk))); } catch (e) { done = true; onFree?.(); throw e; } } if (done) return EMPTY; done = true; try { let result = onFlush ? norm(await onFlush()) : EMPTY; onFree?.(); return result; } catch (e) { onFree?.(); throw e; } }; fn3.flush = async () => { if (done) return EMPTY; return onFlush ? norm(await onFlush()) : EMPTY; }; fn3.free = () => { if (done) return; done = true; onFree?.(); }; return fn3; } function norm(r) { if (!r?.channelData?.length) return EMPTY; let { channelData, sampleRate: sampleRate2, samplesDecoded } = r; if (samplesDecoded != null && samplesDecoded < channelData[0].length) channelData = channelData.map((ch) => ch.subarray(0, samplesDecoded)); if (!channelData[0]?.length) return EMPTY; if (channelData.length === 2) { let a2 = channelData[0], b = channelData[1], same = true; for (let i = 0; i < a2.length; i += 37) { if (a2[i] !== b[i]) { same = false; break; } } if (same) channelData = [a2]; } return { channelData, sampleRate: sampleRate2 }; } function merge(a2, b) { if (!b?.channelData?.length) return a2; if (!a2?.channelData?.length) return b; let ach = a2.channelData.length, bch = b.channelData.length; let ch = Math.max(ach, bch); return { channelData: Array.from({ length: ch }, (_, i) => { let ac = a2.channelData[i % ach], bc = b.channelData[i % bch]; let merged = new Float32Array(ac.length + bc.length); merged.set(ac); merged.set(bc, ac.length); return merged; }), sampleRate: a2.sampleRate }; } // node_modules/encode-audio/audio-encode.js var EMPTY2 = new Uint8Array(0); var encode = {}; var audio_encode_default = encode; function reg2(name, load) { encode[name] = fmt2(async (opts) => { let init2 = (await load()).default; let codec = await init2(opts); return streamEncoder((ch) => codec.encode(ch), () => codec.flush(), () => codec.free()); }); } reg2("wav", () => import("@audio/encode-wav")); reg2("aiff", () => import("@audio/encode-aiff")); reg2("mp3", () => import("@audio/encode-mp3")); reg2("ogg", () => import("@audio/encode-ogg")); reg2("flac", () => import("@audio/encode-flac")); reg2("opus", () => import("@audio/encode-opus")); function fmt2(init2) { let fn3 = async (data, opts) => { if (!opts) return init2(data); if (!opts.sampleRate) throw Error("sampleRate is required"); let ch = channels(data); if (!ch.length || !ch[0].length) return EMPTY2; let enc = await init2({ channels: ch.length, ...opts }); try { let result = await enc(ch); let flushed = await enc(); return merge2(result, flushed); } catch (e) { enc.free(); throw e; } }; fn3.stream = init2; return fn3; } function channels(data) { if (!data) return []; if (Array.isArray(data)) { if (data[0] instanceof Float32Array) return data; return []; } if (data instanceof Float32Array) return [data]; if (data.getChannelData && data.numberOfChannels) { let ch = []; for (let i = 0; i < data.numberOfChannels; i++) ch.push(data.getChannelData(i)); return ch; } return []; } function streamEncoder(onEncode, onFlush, onFree) { let done = false; let fn3 = async (data) => { if (data) { if (done) throw Error("Encoder already freed"); let ch = channels(data); try { return norm2(await onEncode(ch)); } catch (e) { done = true; onFree?.(); throw e; } } if (done) return EMPTY2; done = true; try { let result = onFlush ? norm2(await onFlush()) : EMPTY2; onFree?.(); return result; } catch (e) { onFree?.(); throw e; } }; fn3.encode = fn3; fn3.flush = async () => { if (done) return EMPTY2; return onFlush ? norm2(await onFlush()) : EMPTY2; }; fn3.free = () => { if (done) return; done = true; onFree?.(); }; return fn3; } function norm2(r) { if (!r?.length) return EMPTY2; if (r instanceof Uint8Array) return r; if (r.buffer) return new Uint8Array(r.buffer, r.byteOffset, r.byteLength); return new Uint8Array(r); } function merge2(a2, b) { if (!b?.length) return a2 || EMPTY2; if (!a2?.length) return b || EMPTY2; let out = new Uint8Array(a2.length + b.length); out.set(a2); out.set(b, a2.length); return out; } // node_modules/pcm-convert/index.js var sampleRate = [8e3, 11025, 16e3, 22050, 44100, 48e3, 88200, 96e3, 176400, 192e3, 352800, 384e3]; var _AudioBuffer = typeof AudioBuffer !== "undefined" ? AudioBuffer : null; try { _AudioBuffer ??= (await import("audio-buffer")).default; } catch { } var RATE_SET = new Set(sampleRate); var DTYPE = { float32: { C: Float32Array, min: -1, max: 1 }, float64: { C: Float64Array, min: -1, max: 1 }, uint8: { C: Uint8Array, min: 0, max: 255 }, uint16: { C: Uint16Array, min: 0, max: 65535 }, uint32: { C: Uint32Array, min: 0, max: 4294967295 }, int8: { C: Int8Array, min: -128, max: 127 }, int16: { C: Int16Array, min: -32768, max: 32767 }, int32: { C: Int32Array, min: -2147483648, max: 2147483647 } }; var dtype = (d2) => DTYPE[d2] && d2 || DTYPE[d2 + "32"] && d2 + "32"; var CONTAINER = { array: 1, arraybuffer: 1, buffer: 1, audiobuffer: 1 }; var CHANNELS = { mono: 1, stereo: 2, "2.1": 3, quad: 4, "5.1": 6 }; for (let i = 3; i <= 32; i++) CHANNELS[i + "-channel"] ||= i; var CHANNEL_NAME = {}; for (let k in CHANNELS) CHANNEL_NAME[CHANNELS[k]] ||= k; var isTyped = (v) => ArrayBuffer.isView(v) && !(v instanceof DataView); var isPlanar = (v) => Array.isArray(v) && v.length > 0 && isTyped(v[0]); var isContainer = (v) => v != null && typeof v !== "string" && !isPlanar(v) && (Array.isArray(v) || isTyped(v) || v instanceof ArrayBuffer); var isAudioBuffer = (v) => v != null && typeof v.getChannelData === "function" && typeof v.numberOfChannels === "number"; function parse(fmt3) { if (!fmt3) return {}; if (typeof fmt3 !== "string") { let r2 = {}; let d2 = fmt3.dtype || fmt3.type; if (dtype(d2)) r2.dtype = dtype(d2); if (d2 && CONTAINER[d2]) r2.container = d2; if (fmt3.channels != null) r2.channels = CHANNELS[fmt3.channels] || +fmt3.channels; if (fmt3.numberOfChannels != null) r2.channels ??= fmt3.numberOfChannels; if (fmt3.interleaved != null) r2.interleaved = fmt3.interleaved; if (fmt3.endianness) r2.endianness = fmt3.endianness; if (fmt3.sampleRate != null) r2.sampleRate = fmt3.sampleRate; if (fmt3.rate != null) r2.sampleRate ??= fmt3.rate; if (fmt3.container) r2.container = fmt3.container; return r2; } let r = {}; for (let t of fmt3.split(/\s*[,;_]\s*|\s+/)) { let lo = t.toLowerCase(); if (dtype(lo)) r.dtype = dtype(lo); else if (CONTAINER[lo]) r.container = lo; else if (CHANNELS[lo]) r.channels = CHANNELS[lo]; else if (lo === "interleaved") r.interleaved = true; else if (lo === "planar") r.interleaved = false; else if (lo === "le") r.endianness = "le"; else if (lo === "be") r.endianness = "be"; else if (/^\d+$/.test(lo) && RATE_SET.has(+lo)) r.sampleRate = +lo; else throw Error("Unknown format token: " + t); } return r; } function detect(data) { if (data == null) return {}; if (isAudioBuffer(data)) return { dtype: "float32", channels: data.numberOfChannels, interleaved: false, sampleRate: data.sampleRate }; if (typeof Buffer !== "undefined" && Buffer.isBuffer(data)) return { dtype: "uint8", container: "buffer" }; if (data instanceof Float32Array) return { dtype: "float32" }; if (data instanceof Float64Array) return { dtype: "float64" }; if (data instanceof Int8Array) return { dtype: "int8" }; if (data instanceof Int16Array) return { dtype: "int16" }; if (data instanceof Int32Array) return { dtype: "int32" }; if (data instanceof Uint8Array) return { dtype: "uint8" }; if (data instanceof Uint8ClampedArray) return { dtype: "uint8" }; if (data instanceof Uint16Array) return { dtype: "uint16" }; if (data instanceof Uint32Array) return { dtype: "uint32" }; if (data instanceof ArrayBuffer) return { container: "arraybuffer" }; if (Array.isArray(data)) { if (isPlanar(data)) return { ...detect(data[0]), channels: data.length, interleaved: false }; return { container: "array" }; } return {}; } function range(d2) { return DTYPE[d2] || { min: -1, max: 1 }; } function convert(src, from, to, dst) { if (!src) throw Error("Source data required"); if (from == null) throw Error("Format required"); let srcInfo = detect(src); if (to === void 0 && dst === void 0) { if (isContainer(from)) { dst = from; to = detect(dst); from = srcInfo; } else { to = parse(from); from = srcInfo; } } else if (dst === void 0) { if (isContainer(to)) { dst = to; to = parse(from); from = srcInfo; } else { from = { ...srcInfo, ...parse(from) }; to = parse(to); } } else { from = { ...srcInfo, ...parse(from) }; to = { ...dst ? detect(dst) : {}, ...parse(to) }; } if (to.container === "audiobuffer") to.dtype = "float32"; if (!to.dtype) to.dtype = from.dtype; if (to.channels == null && from.channels != null) to.channels = from.channels; if (to.interleaved != null && from.interleaved == null) { from.interleaved = !to.interleaved; if (!from.channels) from.channels = 2; } if (from.interleaved != null && !from.channels) from.channels = 2; let fromR = from.container === "array" ? { min: -1, max: 1 } : range(from.dtype); let toR = to.container === "array" ? { min: -1, max: 1 } : range(to.dtype); let samples; if (isPlanar(src)) { let ch2 = src.length, len2 = src[0].length; samples = new src[0].constructor(len2 * ch2); for (let c = 0; c < ch2; c++) samples.set(src[c], len2 * c); } else if (isAudioBuffer(src)) { let nc = src.numberOfChannels, len2 = src.length; samples = new Float32Array(len2 * nc); for (let c = 0; c < nc; c++) samples.set(src.getChannelData(c), len2 * c); } else if (src instanceof ArrayBuffer) { samples = new (DTYPE[from.dtype]?.C || Uint8Array)(src); } else if (typeof Buffer !== "undefined" && Buffer.isBuffer(src)) { samples = new (DTYPE[from.dtype]?.C || Uint8Array)( src.buffer.slice(src.byteOffset, src.byteOffset + src.byteLength) ); } else { samples = src; } let len = samples.length; let needsMap = fromR.min !== toR.min || fromR.max !== toR.max; let reinterleave = from.interleaved != null && to.interleaved != null && from.interleaved !== to.interleaved; let ch = from.channels || 1, seg2 = Math.floor(len / ch); let Ctor = DTYPE[to.dtype]?.C || Float32Array; let out; if (!needsMap && !reinterleave) { out = to.container === "array" ? Array.from(samples) : new Ctor(samples); } else { out = to.container === "array" ? new Array(len) : new Ctor(len); let fromSpan = fromR.max - fromR.min; let toIsInt = toR.max > 1; let toSpan = fromR.min === -1 && fromR.max === 1 && toIsInt ? toR.max - toR.min + 1 : toR.max - toR.min; let roundInt = fromR.min === -1 && fromR.max === 1 && toIsInt; if (!reinterleave) { for (let i = 0; i < len; i++) { let v = (samples[i] - fromR.min) / fromSpan * toSpan + toR.min; if (roundInt) v = Math.round(v); out[i] = v < toR.min ? toR.min : v > toR.max ? toR.max : v; } } else { let deint = from.interleaved; for (let i = 0; i < len; i++) { let si = deint ? i % seg2 * ch + ~~(i / seg2) : i % ch * seg2 + ~~(i / ch); let v = samples[si]; if (needsMap) { v = (v - fromR.min) / fromSpan * toSpan + toR.min; if (roundInt) v = Math.round(v); if (v < toR.min) v = toR.min; else if (v > toR.max) v = toR.max; } out[i] = v; } } } if (dst) { if (Array.isArray(dst)) { for (let i = 0; i < len; i++) dst[i] = out[i]; out = dst; } else if (dst instanceof ArrayBuffer) { let tc = new (DTYPE[to.dtype]?.C || Uint8Array)(dst); tc.set(out); out = tc; } else { dst.set(out); out = dst; } } let info = DTYPE[to.dtype]; if (info && info.C.BYTES_PER_ELEMENT > 1 && from.endianness && to.endianness && from.endianness !== to.endianness && out.buffer) { let le = to.endianness === "le"; let view = new DataView(out.buffer); let step = info.C.BYTES_PER_ELEMENT; let fn3 = "set" + to.dtype[0].toUpperCase() + to.dtype.slice(1); for (let i = 0; i < len; i++) view[fn3](i * step, out[i], le); } if (to.container === "audiobuffer") { let ABCtor = typeof AudioBuffer !== "undefined" ? AudioBuffer : _AudioBuffer; if (!ABCtor) throw Error("AudioBuffer not available. In Node.js: install audio-buffer package or set globalThis.AudioBuffer"); let ch2 = to.channels || 1, segLen = Math.floor(out.length / ch2); let sr = to.sampleRate || from.sampleRate || 44100; let ab = new ABCtor({ length: segLen, numberOfChannels: ch2, sampleRate: sr }); let interleaved = reinterleave ? to.interleaved : from.interleaved ?? false; if (interleaved && ch2 > 1) { for (let c = 0; c < ch2; c++) { let data = new Float32Array(segLen); for (let i = 0; i < segLen; i++) data[i] = out[i * ch2 + c]; ab.copyToChannel(data, c); } } else { for (let c = 0; c < ch2; c++) ab.copyToChannel(out.subarray(c * segLen, (c + 1) * segLen), c); } return ab; } if (to.container === "arraybuffer" || to.container === "buffer") return out.buffer || out; return out; } // node_modules/parse-duration/locale/en.js var unit = /* @__PURE__ */ Object.create(null); var m = 6e4; var h = m * 60; var d = h * 24; var y = d * 365.25; unit.year = unit.yr = unit.y = y; unit.month = unit.mo = unit.mth = y / 12; unit.week = unit.wk = unit.w = d * 7; unit.day = unit.d = d; unit.hour = unit.hr = unit.h = h; unit.minute = unit.min = unit.m = m; unit.second = unit.sec = unit.s = 1e3; unit.millisecond = unit.millisec = unit.ms = 1; unit.microsecond = unit.microsec = unit.us = unit.\u00B5s = 1e-3; unit.nanosecond = unit.nanosec = unit.ns = 1e-6; unit.group = ","; unit.decimal = "."; unit.placeholder = " _"; var en_default = unit; // node_modules/parse-duration/index.js var durationRE = /((?:\d{1,16}(?:\.\d{1,16})?|\.\d{1,16})(?:[eE][-+]?\d{1,4})?)\s?([\p{L}]{0,14})/gu; parse2.unit = en_default; function parse2(str = "", format = "ms") { let result = null, prevUnits; String(str).replace(new RegExp(`(\\d)[${parse2.unit.placeholder}${parse2.unit.group}](\\d)`, "g"), "$1$2").replace(parse2.unit.decimal, ".").replace(durationRE, (_, n, units) => { if (!units) { if (prevUnits) { for (const u in parse2.unit) if (parse2.unit[u] < prevUnits) { units = u; break; } } else units = format; } else units = units.toLowerCase(); prevUnits = units = parse2.unit[units] || parse2.unit[units.replace(/s$/, "")]; if (units) result = (result || 0) + n * units; }); return result && result / (parse2.unit[format] || 1) * (str[0] === "-" ? -1 : 1); } // core.js audio.version = "2.2.0"; function parseTime(v) { if (v == null) return v; if (typeof v === "number") { if (!Number.isFinite(v)) throw new Error(`Invalid time: ${v}`); return v; } let tc = v.match(/^(\d+):(\d{1,2})(?::(\d{1,2}))?(?:\.(\d+))?$/); if (tc) { let [, a2, b, c, frac] = tc; let s2 = c != null ? +a2 * 3600 + +b * 60 + +c : +a2 * 60 + +b; if (frac) s2 += +("0." + frac); return s2; } let s = parse2(v, "s"); if (s != null && isFinite(s)) return s; throw new Error(`Invalid time: ${v}`); } function audio(source, opts = {}) { if (source == null) { let sr = opts.sampleRate || 44100, ch = opts.channels || 1; let waiters2 = []; let notify2 = () => { for (let w of waiters2.splice(0)) w(); }; let a3 = create([], sr, ch, 0, opts, null); a3.decoded = false; a3.recording = false; a3._.acc = pageAccumulator({ pages: a3.pages, notify: notify2, ondata: (...args) => emit(a3, "data", ...args) }); a3._.waiters = waiters2; return a3; } if (source && typeof source === "object" && !Array.isArray(source) && source.edits) { if (!source.source) throw new TypeError("audio: cannot restore document without source reference"); let a3 = audio(source.source, opts); if (a3.run) for (let e of source.edits) a3.run(e); return a3; } if (Array.isArray(source) && source.length && !(source[0] instanceof Float32Array)) { let instances = source.map((s) => s?.pages ? s : audio(s, opts)); let first = instances[0].clip ? instances[0].clip() : audio.from(instances[0]); if (!first.insert) throw new Error('audio([...]): concat requires insert plugin \u2014 import "audio" instead of "audio/core.js"'); for (let i = 1; i < instances.length; i++) first.insert(instances[i]); let loading = instances.filter((s) => !s.decoded); if (loading.length) { first.ready = Promise.all(loading.map((s) => s.ready)).then(() => { delete first.then; delete first.catch; return true; }); first.ready.catch(() => { }); makeThenable(first); } return first; } if (source?.getChannelData && source?.numberOfChannels) return audio.from(source, opts); if (Array.isArray(source) && source[0] instanceof Float32Array || typeof source === "number") { let a3 = audio.from(source, opts); if (audio.evict && a3.cache && a3.budget !== Infinity) { a3.ready = audio.evict(a3).then(() => { delete a3.then; delete a3.catch; return true; }); a3.ready.catch(() => { }); makeThenable(a3); } return a3; } let ref = typeof source === "string" ? source : source instanceof URL ? source.href : null; let pages = [], waiters = []; let notify = () => { for (let w of waiters.splice(0)) w(); }; let a2 = create(pages, 0, 0, 0, { ...opts, source: ref }, null); a2._.waiters = waiters; a2.decoded = false; let readyResolve, readyReject; a2._.ready = new Promise((r, j) => { readyResolve = r; readyReject = j; }); a2._.ready.catch(() => { }); a2.ready = (async () => { try { if (opts.storage === "persistent") { if (!audio.opfsCache) throw new Error('Persistent storage requires cache module (import "./cache.js")'); try { opts = { ...opts, cache: await audio.opfsCache(), budget: opts.budget ?? audio.DEFAULT_BUDGET ?? Infinity }; } catch { throw new Error('OPFS not available (required by storage: "persistent")'); } a2.cache = opts.cache; a2.budget = opts.budget; } let result = await decodeSource(source, { pages, notify, ondata: (...args) => emit(a2, "data", ...args) }); a2.sampleRate = result.sampleRate; a2._.ch = result.channels; a2._.chV = -1; if (result.acc) a2._.acc = result.acc; if (result.estDuration) a2._.estDur = result.estDuration; emit(a2, "metadata", { sampleRate: result.sampleRate, channels: result.channels, estDuration: result.estDuration }); readyResolve(); let final = await result.decoding; a2._.len = final.length; a2._.lenV = -1; a2.stats = final.stats; a2.decoded = true; notify(); audio.evict?.(a2); delete a2.then; delete a2.catch; return true; } catch (e) { readyReject(e); throw e; } })(); a2.ready.catch(() => { }); makeThenable(a2); return a2; } function makeThenable(a2) { a2.then = function(resolve, reject) { return a2.ready.then(() => { delete a2.then; delete a2.catch; return a2; }).then(resolve, reject); }; a2.catch = function(reject) { return a2.then(null, reject); }; } audio.from = function(source, opts = {}) { if (Array.isArray(source) && source[0] instanceof Float32Array) return fromChannels(source, opts); if (typeof source === "number") return fromSilence(source, opts); if (typeof source === "function") return fromFunction(source, opts); if (source?.pages) { return create( source.pages, opts.sampleRate ?? source.sampleRate, opts.channels ?? source._.ch, source._.len, { source: source.source, storage: source.storage, cache: source.cache, budget: opts.budget ?? source.budget }, source.stats ); } if (source?.getChannelData) { let chs = Array.from({ length: source.numberOfChannels }, (_, i) => new Float32Array(source.getChannelData(i))); return fromChannels(chs, { sampleRate: source.sampleRate, ...opts }); } if (ArrayBuffer.isView(source) && opts.format) { let fmt3 = parse(opts.format); let ch = fmt3.channels || opts.channels || 1; let sr = fmt3.sampleRate || opts.sampleRate || 44100; let src = { ...fmt3, channels: ch }; if (ch > 1 && src.interleaved == null) src.interleaved = true; let pcm = convert(source, src, { dtype: "float32", interleaved: false, channels: ch }); let perCh = pcm.length / ch; let chs = Array.from({ length: ch }, (_, c) => pcm.subarray(c * perCh, (c + 1) * perCh)); return fromChannels(chs, { sampleRate: sr }); } throw new TypeError("audio.from: expected Float32Array[], AudioBuffer, audio instance, function, or number"); }; var fn = {}; audio.fn = fn; audio.BLOCK_SIZE = 1024; audio.PAGE_SIZE = 1024 * audio.BLOCK_SIZE; var LOAD = /* @__PURE__ */ Symbol("load"); var READ = /* @__PURE__ */ Symbol("read"); function emit(a2, event, ...args) { let arr = a2._.ev[event]; if (arr) for (let cb of arr) cb(...args); } fn.on = function(event, cb) { (this._.ev[event] ??= []).push(cb); return this; }; fn.off = function(event, cb) { if (!event) { this._.ev = {}; return this; } if (!cb) { delete this._.ev[event]; return this; } let arr = this._.ev[event]; if (arr) { let i = arr.indexOf(cb); if (i >= 0) arr.splice(i, 1); } return this; }; fn.dispose = function() { this.stop(); this._.ev = {}; this._.pcm = null; this._.plan = null; this.pages.length = 0; this.stats = null; this._.waiters = null; this._.acc = null; }; if (Symbol.dispose) fn[Symbol.dispose] = fn.dispose; audio.use = function(...plugins) { for (let p of plugins) p(audio); }; function create(pages, sampleRate2, ch, length, opts = {}, stats) { let a2 = Object.create(fn); a2.pages = pages; a2.sampleRate = sampleRate2; a2.source = opts.source ?? null; a2.storage = opts.storage || "memory"; a2.cache = opts.cache || null; a2.budget = opts.budget ?? Infinity; a2.stats = stats; a2.decoded = true; a2.ready = Promise.resolve(true); a2._ = { ch, // source channel count len: length, // source sample length waiters: null, // decode notify queue (null when not streaming) ev: {}, // instance event listeners ct: 0, ctStamp: 0, // currentTime wall-clock interpolation vol: 1, muted: false, // volume 0..1 linear with change events rate: 1 // playbackRate }; a2.edits = []; a2.version = 0; a2._.pcm = null; a2._.pcmV = -1; a2._.plan = null; a2._.planV = -1; a2._.statsV = -1; a2._.lenC = a2._.len; a2._.lenV = 0; a2._.chC = a2._.ch; a2._.chV = 0; Object.defineProperties(a2, { currentTime: { get() { if (this.playing && !this.paused) { let t = this._.ct + (performance.now() - this._.ctStamp) / 1e3 * (this._.rate || 1); let d2 = this.duration; return d2 > 0 ? Math.min(t, d2) : t; } return this._.ct; }, set(v) { this._.ct = v; this._.ctStamp = performance.now(); }, enumerable: true, configurable: true }, volume: { get() { return this._.vol; }, set(v) { v = Math.max(0, Math.min(1, +v || 0)); if (this._.vol !== v) { this._.vol = v; emit(this, "volumechange"); } }, enumerable: true, configurable: true }, muted: { get() { return this._.muted; }, set(v) { v = !!v; if (this._.muted !== v) { this._.muted = v; emit(this, "volumechange"); } }, enumerable: true, configurable: true }, playbackRate: { get() { return this._.rate; }, set(v) { v = Math.max(0.0625, Math.min(16, +v || 1)); if (this._.rate !== v) { this._.rate = v; emit(this, "ratechange"); } }, enumerable: true, configurable: true } }); a2.playing = false; a2.paused = false; a2.ended = false; a2.seeking = false; a2.loop = false; a2.block = null; a2._.lru = /* @__PURE__ */ new Set(); return a2; } function fromChannels(channelData, opts = {}) { let sr = opts.sampleRate || 44100; return create(paginate(channelData), sr, channelData.length, channelData[0].length, opts, audio.statSession?.(sr).page(channelData).done()); } function fromSilence(seconds, opts = {}) { let sr = opts.sampleRate || 44100, ch = opts.channels || 1; return fromChannels(Array.from({ length: ch }, () => new Float32Array(Math.round(seconds * sr))), { ...opts, sampleRate: sr }); } function fromFunction(fn3, opts = {}) { let sr = opts.sampleRate || 44100, ch = opts.channels || 1; let dur = opts.duration; if (dur == null) throw new TypeError("audio.from(fn): duration required"); let len = Math.round(dur * sr); let chs = Array.from({ length: ch }, () => new Float32Array(len)); for (let i = 0; i < len; i++) { let v = fn3(i / sr, i); if (typeof v === "number") for (let c = 0; c < ch; c++) chs[c][i] = v; else for (let c = 0; c < ch; c++) chs[c][i] = v[c] ?? 0; } return fromChannels(chs, { sampleRate: sr }); } Object.defineProperties(fn, { length: { get() { return this._.len; }, configurable: true }, duration: { get() { return this.length / this.sampleRate; }, configurable: true }, channels: { get() { return this._.ch; }, configurable: true } }); fn[LOAD] = async function() { if (this._.ready) await this._.ready; this._.acc?.drain(); }; fn[READ] = function(offset, duration) { return readPages(this, offset, duration); }; fn.push = function(data, fmt3) { let acc = this._.acc; if (!acc) throw new Error("push: instance is not pushable \u2014 create with audio()"); let ch = this._.ch, sr = this.sampleRate; let chData; if (Array.isArray(data) && data[0] instanceof Float32Array) chData = data; else if (data instanceof Float32Array) chData = [data]; else if (ArrayBuffer.isView(data)) { let f = fmt3 || {}; let srcFmt = typeof f === "string" ? f : f.format || "int16"; let nch = f.channels || ch; let src = { dtype: srcFmt, channels: nch }; if (nch > 1) src.interleaved = true; let pcm = convert(data, src, { dtype: "float32", interleaved: false, channels: nch }); let perCh = pcm.length / nch; chData = Array.from({ length: nch }, (_, c) => pcm.subarray(c * perCh, (c + 1) * perCh)); } else throw new TypeError("push: expected Float32Array[], Float32Array, or typed array"); if (!this._.ch) { this._.ch = chData.length; this._.chV = -1; } else if (chData.length !== this._.ch) throw new TypeError(`push: expected ${this._.ch} channels, got ${chData.length}`); acc.push(chData, fmt3 && fmt3.sampleRate || sr); this._.len = acc.length; this._.lenV = -1; return this; }; fn.stop = function() { this.playing = false; this.paused = false; this.seeking = false; if (this._._wake) this._._wake(); if (this.recording) { this.recording = false; if (this._._mic) { this._._mic(null); this._._mic = null; } } if (this._.acc && !this.decoded) { this._.acc.drain(); this.decoded = true; if (this._.waiters) for (let w of this._.waiters.splice(0)) w(); } return this; }; fn.record = function(opts = {}) { if (!this._.acc) throw new Error("record: instance is not pushable \u2014 create with audio()"); if (this.recording) return this; this.recording = true; this.decoded = false; let self = this, sr = this.sampleRate, ch = this._.ch; let _rec = (async () => { try { let { default: mic2 } = await Promise.resolve().then(() => (init_browser(), browser_exports)); let read = mic2({ sampleRate: sr, channels: ch, bitDepth: 16, ...opts }); self._._mic = read; read((err, buf) => { if (!self.recording) return; if (err || !buf) return; self.push(new Int16Array(buf.buffer, buf.byteOffset, buf.byteLength / 2), "int16"); }); } catch (e) { self.recording = false; self.decoded = true; if (self._.waiters) for (let w of self._.waiters.splice(0)) w(); throw e.code === "ERR_MODULE_NOT_FOUND" ? new Error("record: audio-mic not installed \u2014 npm i audio-mic") : e; } })(); _rec.catch(() => { }); return this; }; fn.seek = function(t) { t = Math.max(0, t); this.seeking = true; this.currentTime = t; if (this.cache) { let page = Math.floor(t * this.sampleRate / audio.PAGE_SIZE); (async () => { for (let i = Math.max(0, page - 1); i <= Math.min(page + 2, this.pages.length - 1); i++) if (this.pages[i] === null && await this.cache.has(i)) this.pages[i] = await this.cache.read(i); })(); } if (this.playing) { this._._seekTo = t; if (this._._wake) this._._wake(); } else this.seeking = false; return this; }; fn.read = async function(opts) { if (typeof opts !== "object" || opts === null) opts = {}; let { at, duration, format, channel, meta } = opts; at = parseTime(at); duration = parseTime(duration); await this[LOAD](); let pcm = await this[READ](at, duration); if (channel != null) pcm = [pcm[channel]]; if (!format) return channel != null ? pcm[0] : pcm; let converted = audio_encode_default[format] ? await audio_encode_default[format](pcm, { sampleRate: this.sampleRate, ...meta }) : pcm.map((ch) => convert(ch, "float32", format)); return channel != null ? Array.isArray(converted) ? converted[0] : converted : converted; }; function paginate(channelData) { let len = channelData[0].length, pages = []; for (let off = 0; off < len; off += audio.PAGE_SIZE) pages.push(channelData.map((ch) => ch.subarray(off, Math.min(off + audio.PAGE_SIZE, len)))); return pages; } function walkPages(a2, c, srcOff, len, visitor) { let p0 = Math.floor(srcOff / audio.PAGE_SIZE), pos = p0 * audio.PAGE_SIZE; for (let p = p0; p < a2.pages.length && pos < srcOff + len; p++) { let pg = a2.pages[p], pLen = pg ? pg[0].length : audio.PAGE_SIZE; if (pos + pLen > srcOff && pg) { let s = Math.max(srcOff - pos, 0), e = Math.min(srcOff + len - pos, pLen); if (a2._.lru) { a2._.lru.delete(p); a2._.lru.add(p); } visitor(pg, c, s, e, Math.max(pos - srcOff, 0)); } pos += pLen; } } function copyPages(a2, c, srcOff, len, target, tOff) { walkPages(a2, c, srcOff, len, (pg, ch, s, e, off) => target.set(pg[ch].subarray(s, e), tOff + off)); } function readPages(a2, offset, duration) { let sr = a2.sampleRate, ch = a2._.ch; let s = offset != null ? Math.min(Math.max(Math.round(offset * sr), 0), a2._.len) : 0; let len = duration != null ? Math.round(duration * sr) : a2._.len - s; len = Math.min(Math.max(len, 0), a2._.len - s); let out = Array.from({ length: ch }, () => new Float32Array(len)); for (let c = 0; c < ch; c++) copyPages(a2, c, s, len, out[c], 0); return out; } async function resolveSource(source) { if (source instanceof ArrayBuffer) return source; if (source instanceof Uint8Array) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength); if (source instanceof URL) return resolveSource(source.href); if (typeof source === "string") { if (/^(https?|data|blob):/.test(source) || typeof window !== "undefined") return (await fetch(source)).arrayBuffer(); if (source.startsWith("file:")) { let { fileURLToPath } = await import("url"); source = fileURLToPath(source); } let { readFile } = await import("fs/promises"); let buf = await readFile(source); return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); } throw new TypeError("audio: unsupported source type"); } async function detectSource(source) { if (source instanceof ArrayBuffer || source instanceof Uint8Array) { let bytes2 = source instanceof ArrayBuffer ? new Uint8Array(source) : source.byteOffset || source.byteLength !== source.buffer.byteLength ? new Uint8Array(source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength)) : new Uint8Array(source.buffer); return { format: audioType(bytes2), bytes: bytes2 }; } if (typeof source === "string" && !/^(https?|data|blob):/.test(source) && typeof window === "undefined") { let path = source; if (source.startsWith("file:")) { let { fileURLToPath } = await import("url"); path = fileURLToPath(source); } let { open, stat } = await import("fs/promises"); let fh = await open(path, "r"); let hdr = new Uint8Array(12); await fh.read(hdr, 0, 12, 0); await fh.close(); let format = audioType(new Uint8Array(hdr)); let fileSize = (await stat(path)).size; let { createReadStream } = await import("fs"); return { format, reader: createReadStream(path), fileSize }; } let buf = await resolveSource(source); let bytes = new Uint8Array(buf); return { format: audioType(bytes), bytes }; } function pageAccumulator(opts = {}) { let { pages = [], notify, ondata } = opts; let sr = 0, ch = 0, totalLen = 0, pagePos = 0; let pageBuf = null, session; function emit2(page) { pages.push(page); totalLen += page[0].length; notify?.(); } return { pages, get sampleRate() { return sr; }, get channels() { return ch; }, get length() { return totalLen + pagePos; }, get partial() { return pagePos > 0 ? pageBuf.map((c) => c.subarray(0, pagePos)) : null; }, get partialLen() { return pagePos; }, push(chData, sampleRate2) { if (!pageBuf) { sr = sampleRate2; ch = chData.length; pageBuf = Array.from({ length: ch }, () => new Float32Array(audio.PAGE_SIZE)); session = audio.statSession?.(sr); } session?.page(chData); let srcPos = 0, chunkLen = chData[0].length; while (srcPos < chunkLen) { let n = Math.min(chunkLen - srcPos, audio.PAGE_SIZE - pagePos); for (let c = 0; c < ch; c++) pageBuf[c].set(chData[c].subarray(srcPos, srcPos + n), pagePos); srcPos += n; pagePos += n; if (pagePos === audio.PAGE_SIZE) { emit2(pageBuf); pageBuf = Array.from({ length: ch }, () => new Float32Array(audio.PAGE_SIZE)); pagePos = 0; } } if (ondata) { let delta = session?.delta(); if (delta) ondata({ delta, offset: (totalLen + pagePos) / sr, sampleRate: sr, channels: ch, pages }); } notify?.(); }, /** Flush partial page into pages array. Non-destructive — accumulator stays open. */ drain() { if (pagePos > 0) { emit2(pageBuf.map((c) => c.slice(0, pagePos))); pageBuf = Array.from({ length: ch }, () => new Float32Array(audio.PAGE_SIZE)); pagePos = 0; } }, done() { if (pagePos > 0) emit2(pageBuf.map((c) => c.slice(0, pagePos))); session?.flush(); if (ondata && session) { let delta = session.delta(); if (delta) ondata({ delta, offset: totalLen / sr, sampleRate: sr, channels: ch, pages }); } return { stats: session?.done(), length: totalLen }; } }; } function estimateDuration(fileSize, format, sampleRate2, channels2) { if (!fileSize || !sampleRate2 || !channels2) return null; if (format === "wav") return Math.max(0, (fileSize - 44) / (sampleRate2 * channels2 * 2)); if (format === "flac") return fileSize / (sampleRate2 * channels2 * 0.7); if (format === "mp3") return fileSize / (128e3 / 8); if (format === "ogg" || format === "opus") return fileSize / (96e3 / 8); return null; } async function decodeSource(source, opts = {}) { let { format, bytes, reader, fileSize } = await detectSource(source); if (!format || !decode[format]) { if (!bytes) bytes = new Uint8Array(await resolveSource(source)); let { channelData, sampleRate: sampleRate2 } = await decode(bytes.buffer || bytes); let pages = opts.pages || []; let ps = paginate(channelData); for (let p of ps) { pages.push(p); opts.notify?.(); } let stats = audio.statSession?.(sampleRate2)?.page(channelData)?.done() ?? null; return { pages, sampleRate: sampleRate2, channels: channelData.length, decoding: Promise.resolve({ stats, length: channelData[0].length }) }; } let dec = await decode[format](); let yieldLoop = () => new Promise((r) => setTimeout(r, 0)); let firstResolve; let origNotify = opts.notify; let firstReady = new Promise((r) => { firstResolve = r; }); let acc = pageAccumulator({ pages: opts.pages, ondata: opts.ondata, notify: () => { origNotify?.(); if (firstResolve) { let f = firstResolve; firstResolve = null; f(); } } }); let decoding = (async () => { try { if (reader) { for await (let chunk of reader) { let buf = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk); let r = await dec(buf); if (r.channelData.length) acc.push(r.channelData, r.sampleRate); await yieldLoop(); } } else { let FEED = 64 * 1024; for (let off = 0; off < bytes.length; off += FEED) { let r = await dec(bytes.subarray(off, Math.min(off + FEED, bytes.length))); if (r.channelData.length) acc.push(r.channelData, r.sampleRate); await yieldLoop(); } } let flushed = await dec(); if (flushed.channelData.length) acc.push(flushed.channelData, flushed.sampleRate); let final = acc.done(); return final; } catch (e) { if (firstResolve) { let f = firstResolve; firstResolve = null; f(); } ; throw e; } })(); await firstReady; if (!acc.sampleRate) throw new Error("audio: decoded no audio data"); let estDuration = estimateDuration(fileSize || bytes?.length, format, acc.sampleRate, acc.channels); return { pages: acc.pages, sampleRate: acc.sampleRate, channels: acc.channels, decoding, acc, estDuration }; } // plan.js var fn2 = audio.fn; var ops = {}; function seg(src, count, dst, rate, ref) { let s = [src, count, dst]; if (rate != null && rate !== 1) s[3] = rate; if (ref !== void 0) s[4] = ref; return s; } function planOff