UNPKG

@webav/av-canvas

Version:

Combine Text, Image, Video, Audio, UserMedia, DisplayMedia to generate MediaStream. With [AVRcorder](../av-recorder/README.md) you can output MP4 streams and save them as local files or push them to the server.

751 lines (750 loc) 24.1 kB
var St = Object.defineProperty; var ut = (e) => { throw TypeError(e); }; var bt = (e, t, n) => t in e ? St(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n; var U = (e, t, n) => bt(e, typeof t != "symbol" ? t + "" : t, n), at = (e, t, n) => t.has(e) || ut("Cannot " + n); var r = (e, t, n) => (at(e, t, "read from private field"), n ? n.call(e) : t.get(e)), y = (e, t, n) => t.has(e) ? ut("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(e) : t.set(e, n), M = (e, t, n, i) => (at(e, t, "write to private field"), i ? i.call(e, n) : t.set(e, n), n), I = (e, t, n) => (at(e, t, "access private method"), n); import { Rect as O, Log as _, MediaStreamClip as xt, Combinator as gt, OffscreenSprite as vt } from "@webav/av-cliper"; import { EventTool as ht, workerTimer as Ct } from "@webav/internal-utils"; const Mt = [ "t", "b", "l", "r", "lt", "lb", "rt", "rb", "rotate" ]; function V(e) { return document.createElement(e); } function At(e) { let t = 16; const n = new ResizeObserver((o) => { const s = o[0]; s != null && (t = 10 / (s.contentRect.width / e.width)); }); n.observe(e); function i(o) { const { w: s, h } = o, a = t, c = a / 2, l = s / 2, d = h / 2, u = a * 1.5, f = u / 2; return { ...o.fixedAspectRatio ? {} : { t: new O(-c, -d - c, a, a, o), b: new O(-c, d - c, a, a, o), l: new O(-l - c, -c, a, a, o), r: new O(l - c, -c, a, a, o) }, lt: new O(-l - c, -d - c, a, a, o), lb: new O(-l - c, d - c, a, a, o), rt: new O(l - c, -d - c, a, a, o), rb: new O(l - c, d - c, a, a, o), rotate: new O(-f, -d - a * 2 - f, u, u, o) }; } return { rectCtrlsGetter: i, destroy: () => { n.disconnect(); } }; } var q = /* @__PURE__ */ ((e) => (e.ActiveSpriteChange = "activeSpriteChange", e.AddSprite = "addSprite", e))(q || {}), T, X, F, B; class Tt { constructor() { y(this, T, []); y(this, X, null); y(this, F, new ht()); U(this, "on", r(this, F).on); y(this, B, 0); } get activeSprite() { return r(this, X); } set activeSprite(t) { t !== r(this, X) && (M(this, X, t), r(this, F).emit("activeSpriteChange", t)); } async addSprite(t) { await t.ready, r(this, T).push(t), M(this, T, r(this, T).sort((n, i) => n.zIndex - i.zIndex)), t.on("propsChange", (n) => { n.zIndex != null && M(this, T, r(this, T).sort((i, o) => i.zIndex - o.zIndex)); }), r(this, F).emit("addSprite", t); } removeSprite(t) { r(this, X) === t && (this.activeSprite = null), M(this, T, r(this, T).filter((n) => n !== t)), t.destroy(); } getSprites(t = { time: !0 }) { return r(this, T).filter( (n) => n.visible && (t.time ? r(this, B) >= n.time.offset && r(this, B) <= n.time.offset + n.time.duration : !0) ); } updateRenderTime(t) { M(this, B, t); const n = this.activeSprite; n != null && (t < n.time.offset || t > n.time.offset + n.time.duration) && (this.activeSprite = null); } destroy() { r(this, F).destroy(), r(this, T).forEach((t) => t.destroy()), M(this, T, []); } } T = new WeakMap(), X = new WeakMap(), F = new WeakMap(), B = new WeakMap(); function zt(e, t, n, i) { const o = { w: t.clientWidth / t.width, h: t.clientHeight / t.height }, s = new ResizeObserver(() => { o.w = t.clientWidth / t.width, o.h = t.clientHeight / t.height, n.activeSprite != null && ct( n.activeSprite, a, c, o, i ); }); s.observe(t); let h = () => { }; const { rectEl: a, ctrlsEl: c } = kt(e), l = n.on(q.ActiveSpriteChange, (d) => { if (h(), d == null) { a.style.display = "none"; return; } ct(d, a, c, o, i), h = d.on("propsChange", () => { ct(d, a, c, o, i); }), a.style.display = ""; }); return () => { s.disconnect(), l(), a.remove(), h(); }; } function kt(e) { const t = V("div"); t.style.cssText = ` position: absolute; pointer-events: none; border: 1px solid #eee; box-sizing: border-box; display: none; `; const n = Object.fromEntries( Mt.map((i) => { const o = V("div"); return o.style.cssText = ` display: none; position: absolute; border: 1px solid #3ee; border-radius: 50%; box-sizing: border-box; background-color: #fff; `, [i, o]; }) ); return Object.values(n).forEach((i) => t.appendChild(i)), e.appendChild(t), { rectEl: t, ctrlsEl: n }; } function ct(e, t, n, i, o) { const { x: s, y: h, w: a, h: c, angle: l } = e.rect; Object.assign(t.style, { left: `${s * i.w}px`, top: `${h * i.h}px`, width: `${a * i.w}px`, height: `${c * i.h}px`, rotate: `${l}rad` }), Object.entries(o(e.rect)).forEach(([d, { x: u, y: f, w, h: b }]) => { Object.assign(n[d].style, { display: "block", left: "50%", top: "50%", width: `${w * i.w}px`, height: `${b * i.h}px`, // border 1px, 所以要 -1 transform: `translate(${u * i.w}px, ${f * i.h}px)` }); }); } function Lt(e, t, n) { const i = { w: e.clientWidth / e.width, h: e.clientHeight / e.height }, o = new ResizeObserver(() => { i.w = e.clientWidth / e.width, i.h = e.clientHeight / e.height; }); o.observe(e); const s = (h) => { if (h.button !== 0) return; const { offsetX: a, offsetY: c } = h, l = a / i.w, d = c / i.h; if (t.activeSprite != null) { const [u] = Object.entries(n(t.activeSprite.rect)).find( ([, f]) => f.checkHit(l, d) ) ?? []; if (u != null) return; } t.activeSprite = t.getSprites().reverse().find((u) => u.visible && u.rect.checkHit(l, d)) ?? null; }; return e.addEventListener("pointerdown", s), () => { o.disconnect(), e.removeEventListener("pointerdown", s); }; } function Ot(e, t, n, i) { const o = { w: e.clientWidth / e.width, h: e.clientHeight / e.height }, s = new ResizeObserver(() => { o.w = e.clientWidth / e.width, o.h = e.clientHeight / e.height; }); s.observe(e); let h = 0, a = 0, c = null; const l = Dt(e, n); let d = null; const u = (b) => { if (b.button !== 0 || t.activeSprite == null) return; d = t.activeSprite; const { offsetX: A, offsetY: v, clientX: m, clientY: p } = b; It({ rect: d.rect, offsetX: A, offsetY: v, clientX: m, clientY: p, cvsRatio: o, cvsEl: e, rectCtrlsGetter: i }) || (c = d.rect.clone(), l.magneticEffect(d.rect.x, d.rect.y, d.rect), h = m, a = p, window.addEventListener("pointermove", f), window.addEventListener("pointerup", w)); }, f = (b) => { if (d == null || c == null) return; const { clientX: A, clientY: v } = b; let m = c.x + (A - h) / o.w, p = c.y + (v - a) / o.h; pt( d.rect, e, l.magneticEffect(m, p, d.rect) ); }; e.addEventListener("pointerdown", u); const w = () => { l.hide(), window.removeEventListener("pointermove", f), window.removeEventListener("pointerup", w); }; return () => { s.disconnect(), l.destroy(), w(), e.removeEventListener("pointerdown", u); }; } function Wt({ sprRect: e, startX: t, startY: n, ctrlKey: i, cvsRatio: o, cvsEl: s }) { const h = e.clone(), a = (l) => { const { clientX: d, clientY: u } = l, f = (d - t) / o.w, w = (u - n) / o.h, b = i.length === 1 ? Rt : Ht, { x: A, y: v, w: m, h: p } = h, D = Math.atan2(p, m), { incW: K, incH: tt, incS: j, rotateAngle: dt } = b({ deltaX: f, deltaY: w, angle: e.angle, ctrlKey: i, diagonalAngle: D }), C = 10; let L = m, Y = p, et = h.fixedScaleCenter ? K * 2 : K, it = h.fixedScaleCenter ? tt * 2 : tt, H = j; const lt = Math.sqrt(p ** 2 + m ** 2), ft = Math.sqrt((C * (p / m)) ** 2 + C ** 2); switch (i) { case "l": L = Math.max(m + et, C), H = Math.min(j, m - C); break; case "r": L = Math.max(m + et, C), H = Math.max(j, C - m); break; case "b": Y = Math.max(p + it, C), H = Math.min(j, p - C); break; case "t": Y = Math.max(p + it, C), H = Math.max(j, C - p); break; case "lt": case "lb": L = Math.max(m + et, C), Y = L === C ? p / m * L : p + it, H = Math.min(j, lt - ft); break; case "rt": case "rb": L = Math.max(m + et, C), Y = L === C ? p / m * L : p + it, H = Math.max(j, ft - lt); break; } let ot = A, st = v; if (h.fixedScaleCenter) ot = A + m / 2 - L / 2, st = v + p / 2 - Y / 2; else { const mt = H / 2 * Math.cos(dt) + A + m / 2, yt = H / 2 * Math.sin(dt) + v + p / 2; ot = mt - L / 2, st = yt - Y / 2; } pt(e, s, { x: ot, y: st, w: L, h: Y }); }, c = () => { window.removeEventListener("pointermove", a), window.removeEventListener("pointerup", c); }; window.addEventListener("pointermove", a), window.addEventListener("pointerup", c); } function Rt({ deltaX: e, deltaY: t, angle: n, ctrlKey: i }) { let o = 0, s = 0, h = 0, a = n; return i === "l" || i === "r" ? (o = e * Math.cos(n) + t * Math.sin(n), s = o * (i === "l" ? -1 : 1)) : (i === "t" || i === "b") && (a = n - Math.PI / 2, o = e * Math.cos(a) + t * Math.sin(a), h = o * (i === "b" ? -1 : 1)), { incW: s, incH: h, incS: o, rotateAngle: a }; } function Ht({ deltaX: e, deltaY: t, angle: n, ctrlKey: i, diagonalAngle: o }) { const s = (i === "lt" || i === "rb" ? 1 : -1) * o + n, h = e * Math.cos(s) + t * Math.sin(s), a = i === "lt" || i === "lb" ? -1 : 1, c = h * Math.cos(o) * a, l = h * Math.sin(o) * a; return { incW: c, incH: l, incS: h, rotateAngle: s }; } function It({ rect: e, cvsRatio: t, offsetX: n, offsetY: i, clientX: o, clientY: s, cvsEl: h, rectCtrlsGetter: a }) { const c = n / t.w, l = i / t.h, [d] = Object.entries(a(e)).find( ([, u]) => u.checkHit(c, l) ) ?? []; return d == null ? !1 : (d === "rotate" ? $t(e, Pt(e.center, t, h)) : Wt({ sprRect: e, ctrlKey: d, startX: o, startY: s, cvsRatio: t, cvsEl: h }), !0); } function $t(e, t) { const n = ({ clientX: o, clientY: s }) => { const h = o - t.x, a = s - t.y, c = Math.atan2(a, h) + Math.PI / 2; e.angle = c; }, i = () => { window.removeEventListener("pointermove", n), window.removeEventListener("pointerup", i); }; window.addEventListener("pointermove", n), window.addEventListener("pointerup", i); } function Pt(e, t, n) { const i = e.x * t.w, o = e.y * t.h, { left: s, top: h } = n.getBoundingClientRect(); return { x: i + s, y: o + h }; } function pt(e, t, n) { const i = { x: e.x, y: e.y, w: e.w, h: e.h, ...n }, o = t.width * 0.05, s = t.height * 0.05; i.x < -i.w + o ? i.x = -i.w + o : i.x > t.width - o && (i.x = t.width - o), i.y < -i.h + s ? i.y = -i.h + s : i.y > t.height - s && (i.y = t.height - s), e.x = i.x, e.y = i.y, e.w = i.w, e.h = i.h; } function Dt(e, t) { const n = "display: none; position: absolute;", i = { w: 0, h: 0, x: 0, y: 0 }, o = { vertMiddle: { ...i, h: 100, x: 50, ref: { prop: "x", val: ({ w: c }) => (e.width - c) / 2 } }, horMiddle: { ...i, w: 100, y: 50, ref: { prop: "y", val: ({ h: c }) => (e.height - c) / 2 } }, top: { ...i, w: 100, ref: { prop: "y", val: () => 0 } }, bottom: { ...i, w: 100, y: 100, ref: { prop: "y", val: ({ h: c }) => e.height - c } }, left: { ...i, h: 100, ref: { prop: "x", val: () => 0 } }, right: { ...i, h: 100, x: 100, ref: { prop: "x", val: ({ w: c }) => e.width - c } } }, s = V("div"); s.style.cssText = ` position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; box-sizing: border-box; `; const h = Object.fromEntries( Object.entries(o).map(([c, { w: l, h: d, x: u, y: f }]) => { const w = V("div"); return w.style.cssText = ` ${n} border-${l > 0 ? "top" : "left"}: 1px solid #3ee; top: ${f}%; left: ${u}%; ${u === 100 ? "margin-left: -1px" : ""}; ${f === 100 ? "margin-top: -1px" : ""}; width: ${l}%; height: ${d}%; `, s.appendChild(w), [c, w]; }) ); t.appendChild(s); const a = 6 / (900 / e.width); return { magneticEffect(c, l, d) { const u = { x: c, y: l }; let f, w = { x: !1, y: !1 }; for (f in o) { const { prop: b, val: A } = o[f].ref; if (w[b]) continue; const v = A(d); Math.abs(d[b] - v) <= a && Math.abs(d[b] - (b === "x" ? c : l)) <= a ? (u[b] = v, h[f].style.display = "block", w[b] = !0) : h[f].style.display = "none"; } return u; }, hide() { Object.values(h).forEach((c) => c.style.display = "none"); }, destroy() { s.remove(); } }; } function jt(e, t, n) { const i = { w: e.clientWidth / e.width, h: e.clientHeight / e.height }, o = new ResizeObserver(() => { i.w = e.clientWidth / e.width, i.h = e.clientHeight / e.height; }); o.observe(e); const s = e.style; let h = t.activeSprite; t.on(q.ActiveSpriteChange, (w) => { h = w, w == null && (s.cursor = ""); }); let a = !1; const c = ({ offsetX: w, offsetY: b }) => { a = !0; const A = w / i.w, v = b / i.h; (h == null ? void 0 : h.rect.checkHit(A, v)) === !0 && s.cursor === "" && (s.cursor = "move"); }, l = () => { a = !1; }, d = [ "ns-resize", "nesw-resize", "ew-resize", "nwse-resize", "ns-resize", "nesw-resize", "ew-resize", "nwse-resize" ], u = { t: 0, rt: 1, r: 2, rb: 3, b: 4, lb: 5, l: 6, lt: 7 }, f = (w) => { if (h == null || a) return; const { offsetX: b, offsetY: A } = w, v = b / i.w, m = A / i.h, [p] = Object.entries(n(h.rect)).find( ([, D]) => D.checkHit(v, m) ) ?? []; if (p != null) { if (p === "rotate") { s.cursor = "crosshair"; return; } const D = h.rect.angle, K = D < 0 ? D + 2 * Math.PI : D, tt = (u[p] + Math.floor((K + Math.PI / 8) / (Math.PI / 4))) % 8; s.cursor = d[tt]; return; } if (h.rect.checkHit(v, m)) { s.cursor = "move"; return; } s.cursor = ""; }; return e.addEventListener("pointermove", f), e.addEventListener("pointerdown", c), window.addEventListener("pointerup", l), () => { o.disconnect(), e.removeEventListener("pointermove", f), e.removeEventListener("pointerdown", c), window.removeEventListener("pointerup", l); }; } const Yt = { sampleRate: 48e3, channelCount: 2, codec: "mp4a.40.2" }; function Xt(e) { const t = V("canvas"); return t.style.cssText = ` width: 100%; height: 100%; display: block; touch-action: none; `, t.width = e.width, t.height = e.height, t; } var x, S, N, E, G, J, W, Q, R, k, nt, rt, g, $, P, wt, z, Z; class _t { /** * 创建 `AVCanvas` 类的实例。 * @param attchEl - 要添加画布的元素。 * @param opts - 画布的选项 * @param opts.bgColor - 画布的背景颜色。 * @param opts.width - 画布的宽度。 * @param opts.height - 画布的高度。 */ constructor(t, n) { y(this, k); y(this, x); y(this, S); y(this, N); y(this, E, !1); y(this, G, []); y(this, J); y(this, W, new ht()); U(this, "on", r(this, W).on); y(this, Q); y(this, R, 0); y(this, g, new AudioContext()); y(this, $, r(this, g).createMediaStreamDestination()); y(this, P, /* @__PURE__ */ new Set()); y(this, z, { start: 0, end: 0, // paused state when step equal 0 step: 0, // step: (1000 / 30) * 1000, audioPlayAt: 0 }); y(this, Z, /* @__PURE__ */ new WeakMap()); /** * 添加 {@link VisibleSprite} * @param args {@link VisibleSprite} * @example * const sprite = new VisibleSprite( * new ImgClip({ * type: 'image/gif', * stream: (await fetch('https://xx.gif')).body!, * }), * ); */ U(this, "addSprite", async (t) => { r(this, g).state === "suspended" && r(this, g).resume().catch(_.error); const n = t.getClip(); if (n instanceof xt && n.audioTrack != null) { const i = r(this, g).createMediaStreamSource( new MediaStream([n.audioTrack]) ); i.connect(r(this, $)), r(this, Z).set(t, i); } await r(this, S).addSprite(t); }); /** * 删除 {@link VisibleSprite} * @param args * @returns * @example * const sprite = new VisibleSprite(); * avCvs.removeSprite(sprite); */ U(this, "removeSprite", (t) => { var n; (n = r(this, Z).get(t)) == null || n.disconnect(), r(this, S).removeSprite(t); }); M(this, Q, n), M(this, x, Xt(n)); const i = r(this, x).getContext("2d", { alpha: !1 }); if (i == null) throw Error("canvas context is null"); M(this, N, i); const o = V("div"); o.style.cssText = "width: 100%; height: 100%; position: relative; overflow: hidden;", o.appendChild(r(this, x)), t.appendChild(o), Nt(r(this, g)).connect(r(this, $)), M(this, S, new Tt()); const { rectCtrlsGetter: s, destroy: h } = At( r(this, x) ); r(this, G).push( h, // 鼠标样式、控制 sprite 依赖 activeSprite, // activeSprite 需要在他们之前监听到 mousedown 事件 (代码顺序需要靠前) Lt(r(this, x), r(this, S), s), jt(r(this, x), r(this, S), s), Ot( r(this, x), r(this, S), o, s ), zt(o, r(this, x), r(this, S), s), r(this, S).on(q.AddSprite, (u) => { const { rect: f } = u; f.x === 0 && f.y === 0 && (f.x = (r(this, x).width - f.w) / 2, f.y = (r(this, x).height - f.h) / 2); }), ht.forwardEvent(r(this, S), r(this, W), [ q.ActiveSpriteChange ]) ); let a = r(this, R), c = performance.now(), l = 0; const d = 1e3 / 30; M(this, J, Ct(() => { (performance.now() - c) / (d * l) < 1 || (l += 1, r(this, N).fillStyle = n.bgColor, r(this, N).fillRect(0, 0, r(this, x).width, r(this, x).height), I(this, k, wt).call(this), a !== r(this, R) && (a = r(this, R), r(this, W).emit("timeupdate", Math.round(a)))); }, d)); } /** * 每 33ms 更新一次画布,绘制已添加的 Sprite * @param opts - 播放选项 * @param opts.start - 开始播放的时间(单位:微秒) * @param [opts.end] - 结束播放的时间(单位:微秒)。如果未指定,则播放到最后一个 Sprite 的结束时间 * @param [opts.playbackRate] - 播放速率。1 表示正常速度,2 表示两倍速度,0.5 表示半速等。如果未指定,则默认为 1 * @throws 如果开始时间大于等于结束时间或小于 0,则抛出错误 */ play(t) { const n = r(this, S).getSprites({ time: !1 }).map((o) => o.time.offset + o.time.duration), i = t.end ?? (n.length > 0 ? Math.max(...n) : 1 / 0); if (t.start >= i || t.start < 0) throw Error( `Invalid time parameter, ${JSON.stringify({ start: t.start, end: i })}` ); I(this, k, nt).call(this, t.start), r(this, S).getSprites({ time: !1 }).forEach((o) => { const { offset: s, duration: h } = o.time, a = r(this, R) - s; o.preFrame(a > 0 && a < h ? a : 0); }), r(this, z).start = t.start, r(this, z).end = i, r(this, z).step = (t.playbackRate ?? 1) * (1e3 / 30) * 1e3, r(this, g).resume(), r(this, z).audioPlayAt = 0, r(this, W).emit("playing"), _.info("AVCanvs play by:", r(this, z)); } /** * 暂停播放,画布内容不再更新 */ pause() { I(this, k, rt).call(this); } /** * 预览 `AVCanvas` 指定时间的图像帧 */ previewFrame(t) { I(this, k, nt).call(this, t), I(this, k, rt).call(this); } /** * 获取当前帧的截图图像 返回的是一个base64 */ captureImage() { return r(this, x).toDataURL(); } get activeSprite() { return r(this, S).activeSprite; } set activeSprite(t) { r(this, S).activeSprite = t; } /** * 销毁实例 */ destroy() { var t; r(this, E) || (M(this, E, !0), r(this, g).close(), r(this, $).disconnect(), r(this, W).destroy(), r(this, J).call(this), (t = r(this, x).parentElement) == null || t.remove(), r(this, G).forEach((n) => n()), r(this, P).clear(), r(this, S).destroy()); } /** * 合成所有素材的图像与音频,返回实时媒体流 `MediaStream` * * 可用于 WebRTC 推流,或由 {@link [AVRecorder](../../av-recorder/classes/AVRecorder.html)} 录制生成视频文件 * * @see [直播录制](https://webav-tech.github.io/WebAV/demo/4_2-recorder-avcanvas) * */ captureStream() { r(this, g).state === "suspended" && r(this, g).resume().catch(_.error); const t = new MediaStream( r(this, x).captureStream().getTracks().concat(r(this, $).stream.getTracks()) ); return _.info( "AVCanvas.captureStream, tracks:", t.getTracks().map((n) => n.kind) ), t; } /** * 创建一个视频合成器 {@link [Combinator](../../av-cliper/classes/Combinator.html)} 实例,用于将当前画布添加的 Sprite 导出为视频文件流 * * @param opts - 创建 Combinator 的可选参数 * @throws 如果没有添加素材,会抛出错误 * * @example * avCvs.createCombinator().output() // => ReadableStream * * @see [视频剪辑](https://webav-tech.github.io/WebAV/demo/6_4-video-editor) */ async createCombinator(t = {}) { _.info("AVCanvas.createCombinator, opts:", t); const n = new gt({ ...r(this, Q), ...t }), i = r(this, S).getSprites({ time: !1 }); if (i.length === 0) throw Error("No sprite added"); for (const o of i) { const s = new vt(o.getClip()); s.time = { ...o.time }, o.copyStateTo(s), await n.addSprite(s); } return n; } } x = new WeakMap(), S = new WeakMap(), N = new WeakMap(), E = new WeakMap(), G = new WeakMap(), J = new WeakMap(), W = new WeakMap(), Q = new WeakMap(), R = new WeakMap(), k = new WeakSet(), nt = function(t) { M(this, R, t), r(this, S).updateRenderTime(t); }, rt = function() { const t = r(this, z).step !== 0; r(this, z).step = 0, t && (r(this, W).emit("paused"), r(this, g).suspend()); for (const n of r(this, P)) n.stop(), n.disconnect(); r(this, P).clear(); }, g = new WeakMap(), $ = new WeakMap(), P = new WeakMap(), wt = function() { var c; const t = r(this, N); let n = r(this, R); const { start: i, end: o, step: s, audioPlayAt: h } = r(this, z); s !== 0 && n >= i && n < o ? n += s : I(this, k, rt).call(this), I(this, k, nt).call(this, n); const a = []; for (const l of r(this, S).getSprites()) { t.save(); const { audio: d } = l.render(t, n - l.time.offset); t.restore(), a.push(d); } if (t.resetTransform(), s !== 0) { const l = Math.max(r(this, g).currentTime, h), d = Ft( a, r(this, g) ); let u = 0; for (const f of d) f.start(l), f.connect(r(this, g).destination), f.connect(r(this, $)), r(this, P).add(f), f.onended = () => { f.disconnect(), r(this, P).delete(f); }, u = Math.max(u, ((c = f.buffer) == null ? void 0 : c.duration) ?? 0); r(this, z).audioPlayAt = l + u; } }, z = new WeakMap(), Z = new WeakMap(); function Ft(e, t) { const n = []; if (e.length === 0) return n; for (const [i, o] of e) { if (i == null || i.length <= 0) continue; const s = t.createBuffer( 2, i.length, Yt.sampleRate ); s.copyToChannel(i, 0), s.copyToChannel(o ?? i, 1); const h = t.createBufferSource(); h.buffer = s, n.push(h); } return n; } function Nt(e) { const t = e.createOscillator(), n = new Float32Array([0, 0]), i = new Float32Array([0, 0]), o = e.createPeriodicWave(n, i, { disableNormalization: !0 }); return t.setPeriodicWave(o), t.start(), t; } export { _t as AVCanvas }; //# sourceMappingURL=av-canvas.js.map