pixi-effects 0.47.1 → 0.47.2
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.
- package/CHANGELOG.md +13 -0
- package/README.md +5 -5
- package/ai/chat-template.html +3 -3
- package/dist/Composition-X6MCEFRH.cjs +16 -0
- package/dist/{Composition-AZEATHJB.cjs.map → Composition-X6MCEFRH.cjs.map} +1 -1
- package/dist/Composition-ZYMQ7ZRC.js +7 -0
- package/dist/{Composition-ME3L3EFT.js.map → Composition-ZYMQ7ZRC.js.map} +1 -1
- package/dist/{Spans-LTKEUHDN.cjs → Spans-6HSLKR5Q.cjs} +11 -11
- package/dist/{Spans-LTKEUHDN.cjs.map → Spans-6HSLKR5Q.cjs.map} +1 -1
- package/dist/{Spans-YV7K4X5I.js → Spans-LCCZ4PW7.js} +6 -6
- package/dist/{Spans-YV7K4X5I.js.map → Spans-LCCZ4PW7.js.map} +1 -1
- package/dist/{blendModes-56NHK2MP.js → blendModes-EMANEHZS.js} +4 -4
- package/dist/{blendModes-56NHK2MP.js.map → blendModes-EMANEHZS.js.map} +1 -1
- package/dist/{blendModes-ONOPMMAB.cjs → blendModes-JUISKPCA.cjs} +5 -5
- package/dist/{blendModes-ONOPMMAB.cjs.map → blendModes-JUISKPCA.cjs.map} +1 -1
- package/dist/{chunk-NEMWRJQS.cjs → chunk-2YV7ISX6.cjs} +4 -4
- package/dist/chunk-2YV7ISX6.cjs.map +1 -0
- package/dist/{chunk-BFERTUXK.cjs → chunk-4RC4GXEM.cjs} +92 -92
- package/dist/{chunk-BFERTUXK.cjs.map → chunk-4RC4GXEM.cjs.map} +1 -1
- package/dist/{chunk-QMVK55LC.js → chunk-AOQRB6AU.js} +5 -5
- package/dist/{chunk-QMVK55LC.js.map → chunk-AOQRB6AU.js.map} +1 -1
- package/dist/{chunk-53DXJEO7.cjs → chunk-DQVUKP5G.cjs} +5 -5
- package/dist/{chunk-53DXJEO7.cjs.map → chunk-DQVUKP5G.cjs.map} +1 -1
- package/dist/{chunk-7IDEWQPH.js → chunk-G4PXCV2B.js} +12 -8
- package/dist/chunk-G4PXCV2B.js.map +1 -0
- package/dist/{chunk-OVTTCX2E.js → chunk-M5P7TXZ2.js} +135 -63
- package/dist/chunk-M5P7TXZ2.js.map +1 -0
- package/dist/{chunk-NVKHUAHU.js → chunk-P2P7C2KZ.js} +4 -4
- package/dist/chunk-P2P7C2KZ.js.map +1 -0
- package/dist/{chunk-UTDELFOU.js → chunk-R43646JP.js} +4 -4
- package/dist/{chunk-UTDELFOU.js.map → chunk-R43646JP.js.map} +1 -1
- package/dist/{chunk-4NOTVQ3I.cjs → chunk-TVVC4GVK.cjs} +29 -29
- package/dist/{chunk-4NOTVQ3I.cjs.map → chunk-TVVC4GVK.cjs.map} +1 -1
- package/dist/{chunk-YXIEPTFG.cjs → chunk-UXPTDNNA.cjs} +181 -109
- package/dist/chunk-UXPTDNNA.cjs.map +1 -0
- package/dist/{chunk-6MHJKCXO.js → chunk-YF4JC2AL.js} +5 -5
- package/dist/{chunk-6MHJKCXO.js.map → chunk-YF4JC2AL.js.map} +1 -1
- package/dist/{chunk-SMHTOWXG.cjs → chunk-ZAHCGFGC.cjs} +12 -8
- package/dist/chunk-ZAHCGFGC.cjs.map +1 -0
- package/dist/index.cjs +37 -37
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +6 -6
- package/dist/loader.css +1 -1
- package/dist/present.cjs +6 -6
- package/dist/present.js +6 -6
- package/dist/sound.cjs +2 -2
- package/dist/sound.js +1 -1
- package/dist/three.cjs +93 -26
- package/dist/three.cjs.map +1 -1
- package/dist/three.d.cts +19 -4
- package/dist/three.d.ts +19 -4
- package/dist/three.js +89 -22
- package/dist/three.js.map +1 -1
- package/docs/dsl.md +7 -2
- package/llms-full.txt +9 -4
- package/package.json +1 -1
- package/skills/pixi-effects/SKILL.md +1 -1
- package/skills/pixi-effects/reference/recipes.md +1 -1
- package/skills/pixi-effects/template.html +6 -6
- package/dist/Composition-AZEATHJB.cjs +0 -16
- package/dist/Composition-ME3L3EFT.js +0 -7
- package/dist/chunk-7IDEWQPH.js.map +0 -1
- package/dist/chunk-NEMWRJQS.cjs.map +0 -1
- package/dist/chunk-NVKHUAHU.js.map +0 -1
- package/dist/chunk-OVTTCX2E.js.map +0 -1
- package/dist/chunk-SMHTOWXG.cjs.map +0 -1
- package/dist/chunk-YXIEPTFG.cjs.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk2YV7ISX6_cjs = require('./chunk-2YV7ISX6.cjs');
|
|
4
4
|
var chunkTWZNOUM7_cjs = require('./chunk-TWZNOUM7.cjs');
|
|
5
5
|
|
|
6
6
|
// src/core/blend.ts
|
|
@@ -40,7 +40,7 @@ function usesAdvancedBlend(spec) {
|
|
|
40
40
|
};
|
|
41
41
|
return visit(spec);
|
|
42
42
|
}
|
|
43
|
-
var sayNow = (message, spec) => console.warn(`pixi-effects: ${
|
|
43
|
+
var sayNow = (message, spec) => console.warn(`pixi-effects: ${chunk2YV7ISX6_cjs.describeLayer(spec)}: ${message}`);
|
|
44
44
|
function blendUnavailableMessage(mode) {
|
|
45
45
|
return `blendMode "${mode}" could not be set up (the blend filters are not registered); the layer is drawn normal`;
|
|
46
46
|
}
|
|
@@ -85,7 +85,7 @@ function blendFilterFor(mode) {
|
|
|
85
85
|
}
|
|
86
86
|
async function enableAdvancedBlend(renderer) {
|
|
87
87
|
try {
|
|
88
|
-
const { registerBlendModes } = await import('./blendModes-
|
|
88
|
+
const { registerBlendModes } = await import('./blendModes-JUISKPCA.cjs');
|
|
89
89
|
registerBlendModes();
|
|
90
90
|
} catch (e) {
|
|
91
91
|
const why = e instanceof Error ? e.message : String(e);
|
|
@@ -137,5 +137,5 @@ exports.isAdvancedBlend = isAdvancedBlend;
|
|
|
137
137
|
exports.maxConcurrentAdvanced = maxConcurrentAdvanced;
|
|
138
138
|
exports.setBlendFilterFactory = setBlendFilterFactory;
|
|
139
139
|
exports.usesAdvancedBlend = usesAdvancedBlend;
|
|
140
|
-
//# sourceMappingURL=chunk-
|
|
141
|
-
//# sourceMappingURL=chunk-
|
|
140
|
+
//# sourceMappingURL=chunk-DQVUKP5G.cjs.map
|
|
141
|
+
//# sourceMappingURL=chunk-DQVUKP5G.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/blend.ts"],"names":["suggestName","describeLayer"],"mappings":"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","file":"chunk-53DXJEO7.cjs","sourcesContent":["import { describeLayer } from './lint';\nimport { suggestName } from './options';\n\n/** The modes Pixi draws itself (`add`, `screen`, `multiply`) and `normal`. */\nexport const BASIC_BLEND_MODES = ['normal', 'add', 'screen', 'multiply'] as const;\n/** The modes that need the backdrop: our own `BlendModeFilter`s (`src/filters/blendModes.ts`), loaded only when a movie uses one. */\nexport const ADVANCED_BLEND_MODES = [\n 'overlay', 'soft-light', 'hard-light', 'color-dodge', 'color-burn', 'darken', 'lighten', 'difference', 'exclusion',\n 'hue', 'saturation', 'color', 'luminosity', 'linear-burn',\n] as const;\n/** Every `blendMode` there is (the one table the warnings, the types and the docs check against). */\nexport const BLEND_MODES: readonly string[] = [...BASIC_BLEND_MODES, ...ADVANCED_BLEND_MODES];\n\nexport function isAdvancedBlend(mode: unknown): boolean {\n return typeof mode === 'string' && (ADVANCED_BLEND_MODES as readonly string[]).includes(mode);\n}\n\n/** Why `mode` is not a blend mode, with the likely name and the choices; null when it is one. */\nexport function blendProblem(mode: unknown): string | null {\n if (typeof mode === 'string' && BLEND_MODES.includes(mode)) return null;\n const guess = typeof mode === 'string' ? suggestName(mode, BLEND_MODES) : null;\n const shown = typeof mode === 'string' ? `\"${mode}\"` : String(mode);\n return `blendMode ${shown} is not supported${guess ? `; did you mean \"${guess}\"?` : ''} (use ${BLEND_MODES.join(', ')})`;\n}\n\n/** Does any layer of this composition (or one nested in it, or a mask layer) use an advanced blend? */\nexport function usesAdvancedBlend(spec: unknown): boolean {\n const visit = (n: unknown): boolean => {\n if (!n || typeof n !== 'object') return false;\n if (Array.isArray(n)) return n.some(visit);\n const o = n as Record<string, unknown>;\n return isAdvancedBlend(o.blendMode) || visit(o.sequences) || visit(o.mask) || visit(o.layer); // (`layer`: a mask written as { layer: <spec>, invert })\n };\n return visit(spec);\n}\n\n/** What to say about a layer's blend mode: `Composition` collects these per build, so the same mistake in 300 particle layers is said once. */\nexport type BlendReport = (message: string, spec: { name?: string; type: string }) => void;\nconst sayNow: BlendReport = (message, spec) => console.warn(`pixi-effects: ${describeLayer(spec)}: ${message}`);\n\n/** The blend filters of an advanced mode were not registered (a movie registers them in `Movie.init`, or loading them failed): the layer is drawn normal. */\nexport function blendUnavailableMessage(mode: string): string {\n return `blendMode \"${mode}\" could not be set up (the blend filters are not registered); the layer is drawn normal`;\n}\n\n/**\n * Apply a layer's `blendMode` to its display object; an unknown mode, or an advanced mode while the blend filters are not registered, says so\n * and the layer is drawn normal. Returns the blend filter it added to the layer's filter chain (the caller destroys it with the layer), if any.\n */\nexport function applyBlendMode(\n spec: { blendMode?: string; name?: string; type: string },\n display: { blendMode?: unknown; filters?: unknown },\n report: BlendReport = sayNow,\n): BlendFilterLike | null {\n const mode = spec.blendMode;\n if (mode === undefined) return null;\n const problem = blendProblem(mode);\n if (problem) { report(problem, spec); return null; }\n if (isAdvancedBlend(mode) && !blendRegistered()) { report(blendUnavailableMessage(mode), spec); return null; }\n // A container's blend mode applies INSIDE a filter's offscreen pass (against transparent black: multiply gave\n // solid black) and not to the filtered result. With filters, the pass that draws onto the backdrop is the last\n // filter's, so the mode goes there; the container stays normal.\n const filters = display.filters;\n if (Array.isArray(filters) && filters.length > 0) {\n if (isAdvancedBlend(mode)) {\n // an advanced mode is a filter of its own (it reads the backdrop): it joins the chain as the last pass\n const blend = blendFilterFor(mode);\n if (!blend) { report(blendUnavailableMessage(mode), spec); return null; }\n display.filters = [...filters, blend];\n return blend;\n }\n (filters[filters.length - 1] as { blendMode?: unknown }).blendMode = mode;\n return null;\n }\n display.blendMode = mode;\n return null;\n}\n\n/** What blend.ts needs of a blend filter (the real ones are Pixi filters; tests use plain objects). */\nexport type BlendFilterLike = { blendMode?: unknown; destroy(): void };\nlet factory: ((mode: string) => BlendFilterLike | null) | null = null;\n\n/** `filters/blendModes.ts` hands its filter maker here when it registers, so this file never imports Pixi. */\nexport function setBlendFilterFactory(f: ((mode: string) => BlendFilterLike | null) | null): void {\n factory = f;\n}\n\n/** Are the blend filters registered (a movie that uses an advanced mode registers them in `Movie.init`)? */\nexport function blendRegistered(): boolean {\n return factory !== null;\n}\n\n/** A fresh blend filter for an advanced mode, or null when the blends are not registered (or the mode is not an advanced one). */\nexport function blendFilterFor(mode: string): BlendFilterLike | null {\n return isAdvancedBlend(mode) && factory ? factory(mode) : null;\n}\n\n/**\n * Called by `Movie.init` when a layer uses an advanced mode, and only then: loads our blend filters (a separate chunk), registers them on\n * the pixi the movie runs on, and on WebGL turns the back buffer on (an advanced blend reads what is already drawn; WebGPU copies the\n * backdrop by itself, and has no such switch). A movie that uses none never reaches this, so nothing about it changes.\n */\nexport async function enableAdvancedBlend(renderer: unknown): Promise<void> {\n try {\n const { registerBlendModes } = await import('../filters/blendModes');\n registerBlendModes();\n } catch (e) {\n // a page that serves dist/index.js without its chunk, a CDN hiccup: the movie still plays, its advanced blends drawn normal\n const why = e instanceof Error ? e.message : String(e);\n console.warn(`pixi-effects: the blend filters could not be loaded (${why}); layers with an advanced blendMode are drawn normal. Serve the whole dist/ folder (the filters are the blendModes-*.js file next to index.js)`);\n return;\n }\n const bb = (renderer as { backBuffer?: { useBackBuffer: boolean } }).backBuffer;\n if (bb) bb.useBackBuffer = true;\n}\n\n/** This many layers with an advanced blend on screen at once is slow: each is a full-frame pass (measured on WebGL: about 1.5–2 ms each at 1080p). */\nexport const MANY_ADVANCED = 10;\n\ntype Counted = { at?: number; duration?: number; blendMode?: unknown; type?: string; sequences?: unknown };\n\n/** The layers a composition draws (its children each blend one by one when it has a mode): null, camera and audio layers draw nothing; nested compositions are walked. */\nfunction drawnLeaves(sequences: unknown): number {\n if (!Array.isArray(sequences)) return 0;\n let n = 0;\n for (const s of sequences as Counted[]) {\n if (!s || s.type === 'null' || s.type === 'camera' || s.type === 'audio') continue;\n n += s.type === 'composition' ? drawnLeaves(s.sequences) : 1;\n }\n return n;\n}\n\n/**\n * The full-frame passes one layer costs while it is on screen: 1 for a layer with an advanced mode; for a composition with one, one for each\n * drawn layer inside it (its children inherit the mode and blend one by one); for a composition without, the most that its own layers add\n * at one instant (a card with three blends in it, on screen with four other cards, is twelve).\n */\nfunction passesOf(l: Counted, span: number): number {\n const isComposition = l.type === 'composition';\n if (isAdvancedBlend(l.blendMode)) return isComposition ? Math.max(1, drawnLeaves(l.sequences)) : 1;\n return isComposition && Array.isArray(l.sequences) ? maxConcurrentAdvanced(l.sequences as Counted[], l.duration ?? span) : 0;\n}\n\n/** The most full-frame advanced-blend passes on screen at one instant, from the layers' `at` and `duration` (a layer with no duration lasts to the end). */\nexport function maxConcurrentAdvanced(layers: ReadonlyArray<Counted>, span: number): number {\n const events: Array<[number, number]> = [];\n for (const l of layers) {\n const w = passesOf(l, span);\n if (w === 0) continue;\n const at = l.at ?? 0;\n const end = l.duration === undefined ? span : at + l.duration;\n const start = Math.max(0, at); // a negative `at` starts before the composition: only what is after 0 is on screen\n if (end > start) events.push([start, w], [end, -w]);\n }\n events.sort((a, b) => a[0] - b[0] || a[1] - b[1]); // at one instant, the layers that end go before the ones that start\n let now = 0, most = 0;\n for (const [, d] of events) { now += d; most = Math.max(most, now); }\n return most;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/blend.ts"],"names":["suggestName","describeLayer"],"mappings":"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","file":"chunk-DQVUKP5G.cjs","sourcesContent":["import { describeLayer } from './lint';\nimport { suggestName } from './options';\n\n/** The modes Pixi draws itself (`add`, `screen`, `multiply`) and `normal`. */\nexport const BASIC_BLEND_MODES = ['normal', 'add', 'screen', 'multiply'] as const;\n/** The modes that need the backdrop: our own `BlendModeFilter`s (`src/filters/blendModes.ts`), loaded only when a movie uses one. */\nexport const ADVANCED_BLEND_MODES = [\n 'overlay', 'soft-light', 'hard-light', 'color-dodge', 'color-burn', 'darken', 'lighten', 'difference', 'exclusion',\n 'hue', 'saturation', 'color', 'luminosity', 'linear-burn',\n] as const;\n/** Every `blendMode` there is (the one table the warnings, the types and the docs check against). */\nexport const BLEND_MODES: readonly string[] = [...BASIC_BLEND_MODES, ...ADVANCED_BLEND_MODES];\n\nexport function isAdvancedBlend(mode: unknown): boolean {\n return typeof mode === 'string' && (ADVANCED_BLEND_MODES as readonly string[]).includes(mode);\n}\n\n/** Why `mode` is not a blend mode, with the likely name and the choices; null when it is one. */\nexport function blendProblem(mode: unknown): string | null {\n if (typeof mode === 'string' && BLEND_MODES.includes(mode)) return null;\n const guess = typeof mode === 'string' ? suggestName(mode, BLEND_MODES) : null;\n const shown = typeof mode === 'string' ? `\"${mode}\"` : String(mode);\n return `blendMode ${shown} is not supported${guess ? `; did you mean \"${guess}\"?` : ''} (use ${BLEND_MODES.join(', ')})`;\n}\n\n/** Does any layer of this composition (or one nested in it, or a mask layer) use an advanced blend? */\nexport function usesAdvancedBlend(spec: unknown): boolean {\n const visit = (n: unknown): boolean => {\n if (!n || typeof n !== 'object') return false;\n if (Array.isArray(n)) return n.some(visit);\n const o = n as Record<string, unknown>;\n return isAdvancedBlend(o.blendMode) || visit(o.sequences) || visit(o.mask) || visit(o.layer); // (`layer`: a mask written as { layer: <spec>, invert })\n };\n return visit(spec);\n}\n\n/** What to say about a layer's blend mode: `Composition` collects these per build, so the same mistake in 300 particle layers is said once. */\nexport type BlendReport = (message: string, spec: { name?: string; type: string }) => void;\nconst sayNow: BlendReport = (message, spec) => console.warn(`pixi-effects: ${describeLayer(spec)}: ${message}`);\n\n/** The blend filters of an advanced mode were not registered (a movie registers them in `Movie.init`, or loading them failed): the layer is drawn normal. */\nexport function blendUnavailableMessage(mode: string): string {\n return `blendMode \"${mode}\" could not be set up (the blend filters are not registered); the layer is drawn normal`;\n}\n\n/**\n * Apply a layer's `blendMode` to its display object; an unknown mode, or an advanced mode while the blend filters are not registered, says so\n * and the layer is drawn normal. Returns the blend filter it added to the layer's filter chain (the caller destroys it with the layer), if any.\n */\nexport function applyBlendMode(\n spec: { blendMode?: string; name?: string; type: string },\n display: { blendMode?: unknown; filters?: unknown },\n report: BlendReport = sayNow,\n): BlendFilterLike | null {\n const mode = spec.blendMode;\n if (mode === undefined) return null;\n const problem = blendProblem(mode);\n if (problem) { report(problem, spec); return null; }\n if (isAdvancedBlend(mode) && !blendRegistered()) { report(blendUnavailableMessage(mode), spec); return null; }\n // A container's blend mode applies INSIDE a filter's offscreen pass (against transparent black: multiply gave\n // solid black) and not to the filtered result. With filters, the pass that draws onto the backdrop is the last\n // filter's, so the mode goes there; the container stays normal.\n const filters = display.filters;\n if (Array.isArray(filters) && filters.length > 0) {\n if (isAdvancedBlend(mode)) {\n // an advanced mode is a filter of its own (it reads the backdrop): it joins the chain as the last pass\n const blend = blendFilterFor(mode);\n if (!blend) { report(blendUnavailableMessage(mode), spec); return null; }\n display.filters = [...filters, blend];\n return blend;\n }\n (filters[filters.length - 1] as { blendMode?: unknown }).blendMode = mode;\n return null;\n }\n display.blendMode = mode;\n return null;\n}\n\n/** What blend.ts needs of a blend filter (the real ones are Pixi filters; tests use plain objects). */\nexport type BlendFilterLike = { blendMode?: unknown; destroy(): void };\nlet factory: ((mode: string) => BlendFilterLike | null) | null = null;\n\n/** `filters/blendModes.ts` hands its filter maker here when it registers, so this file never imports Pixi. */\nexport function setBlendFilterFactory(f: ((mode: string) => BlendFilterLike | null) | null): void {\n factory = f;\n}\n\n/** Are the blend filters registered (a movie that uses an advanced mode registers them in `Movie.init`)? */\nexport function blendRegistered(): boolean {\n return factory !== null;\n}\n\n/** A fresh blend filter for an advanced mode, or null when the blends are not registered (or the mode is not an advanced one). */\nexport function blendFilterFor(mode: string): BlendFilterLike | null {\n return isAdvancedBlend(mode) && factory ? factory(mode) : null;\n}\n\n/**\n * Called by `Movie.init` when a layer uses an advanced mode, and only then: loads our blend filters (a separate chunk), registers them on\n * the pixi the movie runs on, and on WebGL turns the back buffer on (an advanced blend reads what is already drawn; WebGPU copies the\n * backdrop by itself, and has no such switch). A movie that uses none never reaches this, so nothing about it changes.\n */\nexport async function enableAdvancedBlend(renderer: unknown): Promise<void> {\n try {\n const { registerBlendModes } = await import('../filters/blendModes');\n registerBlendModes();\n } catch (e) {\n // a page that serves dist/index.js without its chunk, a CDN hiccup: the movie still plays, its advanced blends drawn normal\n const why = e instanceof Error ? e.message : String(e);\n console.warn(`pixi-effects: the blend filters could not be loaded (${why}); layers with an advanced blendMode are drawn normal. Serve the whole dist/ folder (the filters are the blendModes-*.js file next to index.js)`);\n return;\n }\n const bb = (renderer as { backBuffer?: { useBackBuffer: boolean } }).backBuffer;\n if (bb) bb.useBackBuffer = true;\n}\n\n/** This many layers with an advanced blend on screen at once is slow: each is a full-frame pass (measured on WebGL: about 1.5–2 ms each at 1080p). */\nexport const MANY_ADVANCED = 10;\n\ntype Counted = { at?: number; duration?: number; blendMode?: unknown; type?: string; sequences?: unknown };\n\n/** The layers a composition draws (its children each blend one by one when it has a mode): null, camera and audio layers draw nothing; nested compositions are walked. */\nfunction drawnLeaves(sequences: unknown): number {\n if (!Array.isArray(sequences)) return 0;\n let n = 0;\n for (const s of sequences as Counted[]) {\n if (!s || s.type === 'null' || s.type === 'camera' || s.type === 'audio') continue;\n n += s.type === 'composition' ? drawnLeaves(s.sequences) : 1;\n }\n return n;\n}\n\n/**\n * The full-frame passes one layer costs while it is on screen: 1 for a layer with an advanced mode; for a composition with one, one for each\n * drawn layer inside it (its children inherit the mode and blend one by one); for a composition without, the most that its own layers add\n * at one instant (a card with three blends in it, on screen with four other cards, is twelve).\n */\nfunction passesOf(l: Counted, span: number): number {\n const isComposition = l.type === 'composition';\n if (isAdvancedBlend(l.blendMode)) return isComposition ? Math.max(1, drawnLeaves(l.sequences)) : 1;\n return isComposition && Array.isArray(l.sequences) ? maxConcurrentAdvanced(l.sequences as Counted[], l.duration ?? span) : 0;\n}\n\n/** The most full-frame advanced-blend passes on screen at one instant, from the layers' `at` and `duration` (a layer with no duration lasts to the end). */\nexport function maxConcurrentAdvanced(layers: ReadonlyArray<Counted>, span: number): number {\n const events: Array<[number, number]> = [];\n for (const l of layers) {\n const w = passesOf(l, span);\n if (w === 0) continue;\n const at = l.at ?? 0;\n const end = l.duration === undefined ? span : at + l.duration;\n const start = Math.max(0, at); // a negative `at` starts before the composition: only what is after 0 is on screen\n if (end > start) events.push([start, w], [end, -w]);\n }\n events.sort((a, b) => a[0] - b[0] || a[1] - b[1]); // at one instant, the layers that end go before the ones that start\n let now = 0, most = 0;\n for (const [, d] of events) { now += d; most = Math.max(most, now); }\n return most;\n}\n"]}
|
|
@@ -327,6 +327,7 @@ function analyzeAudio(mix, sources, stats, movieDuration, opts = {}) {
|
|
|
327
327
|
return measure(Array.from({ length: b.numberOfChannels }, (_, c) => b.getChannelData(c)), b.sampleRate);
|
|
328
328
|
};
|
|
329
329
|
const scoreNotes = [];
|
|
330
|
+
const normGain = stats?.loudness?.gainDb ?? 0;
|
|
330
331
|
const buried = [];
|
|
331
332
|
const list = [...sources].sort((a, b) => a.start - b.start).map((s) => {
|
|
332
333
|
const layer = s.layer ?? "an audio layer";
|
|
@@ -356,18 +357,21 @@ function analyzeAudio(mix, sources, stats, movieDuration, opts = {}) {
|
|
|
356
357
|
if (over > 2 && over / s.music.seconds > 0.15) scoreNotes.push(`${layer} (${source}): the score is ${Math.round(s.music.seconds * 10) / 10}s long but only ${Math.round(plays * 100) / 100}s of it play, so it is cut off mid-phrase. Write fewer bars (or a faster tempo) so it ends with the movie`);
|
|
357
358
|
}
|
|
358
359
|
if (s.music && s.music.tracks.length > 1) {
|
|
359
|
-
const
|
|
360
|
+
const longest = Math.max(...s.music.tracks.map((t) => t.beats));
|
|
361
|
+
const short = s.music.tracks.filter((t) => t.beats <= longest - s.music.meter);
|
|
360
362
|
if (short.length > 0) {
|
|
361
|
-
scoreNotes.push(`${layer} (${source}): ${short.slice(0, 3).map((t) => `the ${t.inst} track is ${Math.round(t.beats * 100) / 100} beats long against ${
|
|
363
|
+
scoreNotes.push(`${layer} (${source}): ${short.slice(0, 3).map((t) => `the ${t.inst} track is ${Math.round(t.beats * 100) / 100} beats long against ${longest} for the longest and falls silent from beat ${Math.round(t.beats * 100) / 100} (bar ${Math.floor(t.beats / s.music.meter) + 1})`).join("; ")}. If that is a miscount, add its missing beats (a rest is \`_:4\`); if it is on purpose, ignore this`);
|
|
364
|
+
} else if (s.music.beats >= longest + s.music.meter) {
|
|
365
|
+
scoreNotes.push(`${layer} (${source}): the drums run to beat ${s.music.beats}, past the end of every track at beat ${longest}: from there only the drums play`);
|
|
362
366
|
}
|
|
363
367
|
}
|
|
364
368
|
let belowMixDb;
|
|
365
369
|
if (!clock && !s.sourceMap && s.start < movieDuration && ownDb > INAUDIBLE_DB) {
|
|
366
370
|
const at = s.start + sound.loudestAt;
|
|
367
371
|
const around = db(peakIn(Math.max(0, at - 0.05), Math.min(movieDuration, at + 0.05)));
|
|
368
|
-
belowMixDb = Math.max(0, Math.round((around - ownDb) * 10) / 10);
|
|
372
|
+
belowMixDb = Math.max(0, Math.round((around - (ownDb + normGain)) * 10) / 10);
|
|
369
373
|
if (!s.music && source.startsWith("sfx") && sound.length <= 4 && belowMixDb >= BURIED_DB) {
|
|
370
|
-
buried.push({ layer, source, ownDb, topVolume, around, belowMixDb, at: Math.round(at * 100) / 100 });
|
|
374
|
+
buried.push({ layer, source, ownDb: Math.round((ownDb + normGain) * 10) / 10, topVolume, around, belowMixDb, at: Math.round(at * 100) / 100 });
|
|
371
375
|
}
|
|
372
376
|
}
|
|
373
377
|
return { layer, source, start: s.start, end: s.end, peakDb, sound, ...belowMixDb !== void 0 ? { belowMixDb } : {}, ...s.music ? { music: s.music } : {} };
|
|
@@ -383,7 +387,7 @@ function analyzeAudio(mix, sources, stats, movieDuration, opts = {}) {
|
|
|
383
387
|
if (whole.truePeakDb > 0) issues.push(`the mix's true peak is ${whole.truePeakDb} dBTP: it will distort when the video is encoded \u2014 lower the volume`);
|
|
384
388
|
const notes = [...scoreNotes];
|
|
385
389
|
if (whole.integratedLufs !== null) {
|
|
386
|
-
if (whole.integratedLufs < LOUDNESS_NOTES.quietBelow) notes.push(`the mix is quiet for web video: ${whole.integratedLufs} LUFS (typical \u221214 to \u221216); raise the volume, or ask for a loudness: movie.init({ loudness: -16 }), if that is not on purpose`);
|
|
390
|
+
if (whole.integratedLufs < LOUDNESS_NOTES.quietBelow && !stats?.loudness) notes.push(`the mix is quiet for web video: ${whole.integratedLufs} LUFS (typical \u221214 to \u221216); raise the volume, or ask for a loudness: movie.init({ loudness: -16 }), if that is not on purpose`);
|
|
387
391
|
else if (whole.integratedLufs > LOUDNESS_NOTES.loudAbove) notes.push(`the mix is loud: ${whole.integratedLufs} LUFS (typical web video \u221214 to \u221216)`);
|
|
388
392
|
}
|
|
389
393
|
if (whole.truePeakDb > LOUDNESS_NOTES.headroomDb && whole.truePeakDb <= 0) notes.push(`the true peak is ${whole.truePeakDb} dBTP: little headroom left for encoding (keep it below ${LOUDNESS_NOTES.headroomDb} dBTP)`);
|
|
@@ -393,7 +397,7 @@ function analyzeAudio(mix, sources, stats, movieDuration, opts = {}) {
|
|
|
393
397
|
const at = sources.filter((x) => x.start <= t && t < Math.min(x.end, movieDuration)).map((x) => x.layer && x.layer !== "an audio layer" ? `${x.layer}${x.source ? ` (${x.source})` : ""}` : x.source ?? "audio");
|
|
394
398
|
return [...new Set(at)].slice(0, 6).join(", ") || "nothing that is listed";
|
|
395
399
|
};
|
|
396
|
-
if (asked.gainDb !== 0) notes.push(`the whole mix was ${asked.gainDb > 0 ? "raised" : "lowered"} by ${Math.abs(asked.gainDb)} dB to reach ${asked.target} LUFS (init({ loudness: ${asked.target} }))`);
|
|
400
|
+
if (asked.gainDb !== 0) notes.push(`the whole mix was ${asked.gainDb > 0 ? "raised" : "lowered"} by ${Math.abs(asked.gainDb)} dB ${asked.limited ? "toward" : "to reach"} ${asked.target} LUFS (init({ loudness: ${asked.target} }))`);
|
|
397
401
|
if (asked.limited) {
|
|
398
402
|
notes.push(`the mix could not reach ${asked.target} LUFS: it stopped at ${asked.reachedLufs} LUFS because its loudest moment${asked.limiterAt !== void 0 ? ` (at ${asked.limiterAt} s, where ${playingAt(asked.limiterAt)} play)` : ""} would need more than ${asked.limiterDb ?? 10} dB of limiting to stay under -1 dBTP; lower the sound that peaks there (a click or hit on top of a quiet bed), or ask for less`);
|
|
399
403
|
} else if (asked.limiterDb !== void 0 && asked.limiterDb >= 3) {
|
|
@@ -440,5 +444,5 @@ function inspectSound(pcm, opts = {}) {
|
|
|
440
444
|
}
|
|
441
445
|
|
|
442
446
|
export { analyzeAudio, fft, inspectSound, measureLoudness, playSpan, resampleThrough };
|
|
443
|
-
//# sourceMappingURL=chunk-
|
|
444
|
-
//# sourceMappingURL=chunk-
|
|
447
|
+
//# sourceMappingURL=chunk-G4PXCV2B.js.map
|
|
448
|
+
//# sourceMappingURL=chunk-G4PXCV2B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/audioRemap.ts","../src/core/spectrum.ts","../src/audio/loudness.ts","../src/core/inspectAudio.ts"],"names":[],"mappings":";AAKA,SAAS,MAAA,CAAO,GAAgB,CAAA,EAAmB;AACjD,EAAA,IAAI,KAAA,GAAQ,CAAA,CAAE,KAAA,EAAO,KAAA,GAAQ,EAAE,aAAA,IAAiB,CAAA;AAChD,EAAA,IAAI,CAAA,IAAK,OAAO,OAAO,KAAA;AACvB,EAAA,KAAA,MAAW,EAAA,IAAM,CAAA,CAAE,eAAA,IAAmB,EAAC,EAAG;AACxC,IAAA,IAAI,KAAK,EAAA,CAAG,IAAA,EAAM,OAAO,EAAA,CAAG,SAAS,KAAA,GAAQ,EAAA,CAAG,KAAA,GAAQ,KAAA,GAAA,CAAS,GAAG,KAAA,GAAQ,KAAA,KAAA,CAAW,CAAA,GAAI,KAAA,KAAU,GAAG,IAAA,GAAO,KAAA,CAAA,CAAA;AAC/G,IAAA,KAAA,GAAQ,EAAA,CAAG,IAAA;AAAM,IAAA,KAAA,GAAQ,EAAA,CAAG,KAAA;AAAA,EAC9B;AACA,EAAA,OAAO,KAAA;AACT;AAEA,IAAM,OAAO,CAAA,GAAI,GAAA;AAQV,SAAS,gBACd,CAAA,EAAgB,IAAA,EAAoB,KAAA,EAAqB,OAAA,EAAiB,SAAiB,KAAA,EAClC;AACzD,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,IAAA,CAAK,KAAA,GAAQ,IAAI,CAAA,GAAI,CAAA;AACxC,EAAA,MAAM,CAAA,GAAI,IAAI,YAAA,CAAa,KAAK,CAAA;AAChC,EAAA,IAAI,KAAA,GAAQ,IAAI,IAAA,GAAO,EAAA;AACvB,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK;AAC9B,IAAA,MAAM,CAAA,GAAI,EAAE,IAAA,GAAO,CAAA,CAAE,KAAK,CAAA,GAAI,IAAI,IAAI,CAAA,GAAI,IAAA;AAC1C,IAAA,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA;AACP,IAAA,IAAI,MAAA,CAAO,SAAS,CAAC,CAAA,IAAK,KAAK,CAAA,CAAE,KAAA,IAAS,CAAA,GAAI,CAAA,CAAE,GAAA,EAAK;AAAE,MAAA,IAAI,KAAA,GAAQ,GAAG,KAAA,GAAQ,CAAA;AAAG,MAAA,IAAA,GAAO,CAAA;AAAA,IAAG;AAAA,EAC7F;AACA,EAAA,IAAI,KAAA,GAAQ,GAAG,OAAO,IAAA;AACtB,EAAA,MAAM,EAAA,GAAK,IAAA,CAAK,GAAA,CAAI,CAAA,EAAA,CAAI,QAAQ,CAAA,IAAK,IAAI,CAAA,EAAG,EAAA,GAAK,IAAA,CAAK,GAAA,CAAI,KAAA,EAAA,CAAQ,IAAA,GAAO,KAAK,IAAI,CAAA;AAClF,EAAA,MAAM,CAAA,GAAI,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,IAAA,CAAA,CAAM,EAAA,GAAK,EAAA,IAAM,OAAO,CAAC,CAAA;AACpD,EAAA,MAAM,CAAA,GAAI,IAAI,YAAA,CAAa,CAAC,GAAG,CAAA,GAAI,IAAI,aAAa,CAAC,CAAA;AACrD,EAAA,MAAM,GAAA,GAAM,KAAK,MAAA,GAAS,OAAA;AAC1B,EAAA,IAAI,OAAA,GAAU,GAAA;AACd,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AAC1B,IAAA,MAAM,CAAA,GAAI,KAAK,CAAA,GAAI,OAAA;AACnB,IAAA,MAAM,CAAA,GAAI,CAAA,GAAI,IAAA,EAAM,CAAA,GAAI,KAAK,GAAA,CAAI,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,EAAG,KAAA,GAAQ,CAAC,CAAA,EAAG,IAAI,CAAA,GAAI,CAAA;AACpE,IAAA,MAAM,KAAK,CAAA,CAAE,CAAC,GAAI,EAAA,GAAK,CAAA,CAAE,IAAI,CAAC,CAAA;AAC9B,IAAA,IAAI,EAAE,OAAO,QAAA,CAAS,EAAE,KAAK,MAAA,CAAO,QAAA,CAAS,EAAE,CAAA,CAAA,EAAI;AAAE,MAAA,OAAA,GAAU,GAAA;AAAK,MAAA;AAAA,IAAU;AAC9E,IAAA,MAAM,CAAA,GAAI,EAAA,IAAM,CAAA,GAAI,CAAA,CAAA,GAAK,EAAA,GAAK,CAAA;AAC9B,IAAA,IAAI,CAAA,GAAI,CAAA,CAAE,KAAA,IAAS,CAAA,IAAK,EAAE,GAAA,EAAK;AAAE,MAAA,OAAA,GAAU,GAAA;AAAK,MAAA;AAAA,IAAU;AAC1D,IAAA,MAAM,GAAA,GAAM,EAAE,SAAA,GAAY,CAAA,CAAE,UAAU,CAAC,CAAA,GAAI,IAAI,CAAA,CAAE,KAAA;AACjD,IAAA,IAAI,GAAA,GAAM,GAAA;AACV,IAAA,IAAI,CAAA,CAAE,IAAA,EAAM,GAAA,GAAA,CAAQ,GAAA,GAAM,MAAO,GAAA,IAAO,GAAA;AACxC,IAAA,MAAM,IAAA,GAAO,MAAA,CAAO,KAAA,CAAM,OAAO,CAAA,GAAI,IAAI,IAAA,CAAK,GAAA,CAAI,GAAA,GAAM,OAAO,CAAA,GAAI,OAAA;AACnE,IAAA,OAAA,GAAU,GAAA;AACV,IAAA,IAAI,GAAA,GAAM,CAAA,IAAK,GAAA,IAAO,GAAA,EAAK;AAC3B,IAAA,MAAM,CAAA,GAAI,OAAO,CAAA,EAAG,CAAC,IAAI,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,GAAO,IAAI,CAAA;AAChD,IAAA,MAAM,KAAK,GAAA,GAAM,OAAA,EAAS,IAAI,IAAA,CAAK,KAAA,CAAM,EAAE,CAAA,EAAG,EAAA,GAAK,EAAA,GAAK,CAAA,EAAG,KAAK,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,EAAG,IAAA,CAAK,SAAS,CAAC,CAAA;AAC/F,IAAA,CAAA,CAAE,CAAC,CAAA,GAAA,CAAK,IAAA,CAAK,CAAC,CAAA,IAAM,IAAI,EAAA,CAAA,GAAM,IAAA,CAAK,EAAE,CAAA,GAAK,EAAA,IAAM,CAAA;AAChD,IAAA,CAAA,CAAE,CAAC,CAAA,GAAA,CAAK,KAAA,CAAM,CAAC,CAAA,IAAM,IAAI,EAAA,CAAA,GAAM,KAAA,CAAM,EAAE,CAAA,GAAK,EAAA,IAAM,CAAA;AAAA,EACpD;AACA,EAAA,OAAO,EAAE,CAAA,EAAG,CAAA,EAAG,EAAA,EAAI,EAAA,EAAG;AACxB;AAMO,SAAS,QAAA,CAAS,GAAoD,KAAA,EAA+C;AAC1H,EAAA,IAAI,CAAC,CAAA,CAAE,IAAA,EAAM,OAAO,EAAE,OAAO,CAAA,CAAE,KAAA,EAAO,GAAA,EAAK,CAAA,CAAE,GAAA,EAAI;AACjD,EAAA,IAAI,KAAA,GAAQ,IAAI,IAAA,GAAO,EAAA;AACvB,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,KAAA,GAAQ,IAAI,CAAA,GAAI,CAAA;AACpC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,IAAK,CAAA,EAAG,CAAA,EAAA,EAAK;AAC3B,IAAA,MAAM,CAAA,GAAI,CAAA,CAAE,IAAA,CAAK,CAAA,GAAI,IAAI,CAAA;AACzB,IAAA,IAAI,MAAA,CAAO,SAAS,CAAC,CAAA,IAAK,KAAK,CAAA,CAAE,KAAA,IAAS,CAAA,GAAI,CAAA,CAAE,GAAA,EAAK;AAAE,MAAA,IAAI,KAAA,GAAQ,GAAG,KAAA,GAAQ,CAAA;AAAG,MAAA,IAAA,GAAO,CAAA;AAAA,IAAG;AAAA,EAC7F;AACA,EAAA,OAAO,KAAA,GAAQ,CAAA,GAAI,EAAE,KAAA,EAAO,OAAO,GAAA,EAAK,KAAA,EAAM,GAAI,EAAE,OAAO,KAAA,GAAQ,IAAA,EAAM,GAAA,EAAA,CAAM,IAAA,GAAO,KAAK,IAAA,EAAK;AAClG;AAMO,SAAS,UAAA,CAAW,GAAkC,KAAA,EAAuB;AAClF,EAAA,IAAI,KAAA,GAAQ,CAAA,QAAA;AACZ,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,KAAA,GAAQ,IAAI,CAAA,GAAI,CAAA;AACpC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,IAAK,CAAA,EAAG,CAAA,EAAA,EAAK;AAC3B,IAAA,MAAM,CAAA,GAAI,EAAE,IAAA,GAAO,CAAA,CAAE,KAAK,CAAA,GAAI,IAAI,IAAI,CAAA,GAAI,IAAA;AAC1C,IAAA,IAAI,OAAO,QAAA,CAAS,CAAC,CAAA,IAAK,CAAA,GAAI,OAAO,KAAA,GAAQ,CAAA;AAAA,EAC/C;AACA,EAAA,OAAO,KAAA;AACT;;;ACvFO,SAAS,GAAA,CAAI,IAAkB,EAAA,EAAwB;AAC5D,EAAA,MAAM,IAAI,EAAA,CAAG,MAAA;AACb,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,GAAG,CAAA,EAAA,EAAK;AACjC,IAAA,IAAI,MAAM,CAAA,IAAK,CAAA;AACf,IAAA,OAAO,CAAA,GAAI,GAAA,EAAK,GAAA,KAAQ,CAAA,EAAG,CAAA,IAAK,GAAA;AAChC,IAAA,CAAA,IAAK,GAAA;AACL,IAAA,IAAI,IAAI,CAAA,EAAG;AAAE,MAAA,CAAC,EAAA,CAAG,CAAC,CAAA,EAAG,EAAA,CAAG,CAAC,CAAC,CAAA,GAAI,CAAC,EAAA,CAAG,CAAC,CAAA,EAAI,EAAA,CAAG,CAAC,CAAE,CAAA;AAAG,MAAA,CAAC,EAAA,CAAG,CAAC,CAAA,EAAG,EAAA,CAAG,CAAC,CAAC,CAAA,GAAI,CAAC,EAAA,CAAG,CAAC,CAAA,EAAI,EAAA,CAAG,CAAC,CAAE,CAAA;AAAA,IAAG;AAAA,EACrF;AACA,EAAA,KAAA,IAAS,GAAA,GAAM,CAAA,EAAG,GAAA,IAAO,CAAA,EAAG,QAAQ,CAAA,EAAG;AACrC,IAAA,MAAM,GAAA,GAAO,EAAA,GAAK,IAAA,CAAK,EAAA,GAAM,GAAA;AAC7B,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,KAAK,GAAA,EAAK;AAC/B,MAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,GAAA,GAAM,GAAG,CAAA,EAAA,EAAK;AAChC,QAAA,MAAM,EAAA,GAAK,IAAA,CAAK,GAAA,CAAI,GAAA,GAAM,CAAC,GAAG,EAAA,GAAK,IAAA,CAAK,GAAA,CAAI,GAAA,GAAM,CAAC,CAAA;AACnD,QAAA,MAAM,CAAA,GAAI,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,IAAI,GAAA,GAAM,CAAA;AAC/B,QAAA,MAAM,KAAK,EAAA,CAAG,CAAC,CAAA,GAAK,EAAA,GAAK,GAAG,CAAC,CAAA,GAAK,EAAA,EAAI,EAAA,GAAK,GAAG,CAAC,CAAA,GAAK,EAAA,GAAK,EAAA,CAAG,CAAC,CAAA,GAAK,EAAA;AAClE,QAAA,EAAA,CAAG,CAAC,CAAA,GAAI,EAAA,CAAG,CAAC,CAAA,GAAK,EAAA;AAAI,QAAA,EAAA,CAAG,CAAC,CAAA,GAAI,EAAA,CAAG,CAAC,CAAA,GAAK,EAAA;AACtC,QAAA,EAAA,CAAG,CAAC,CAAA,GAAI,EAAA,CAAG,CAAC,CAAA,GAAK,EAAA;AAAI,QAAA,EAAA,CAAG,CAAC,CAAA,GAAI,EAAA,CAAG,CAAC,CAAA,GAAK,EAAA;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAM,KAAA,GAAQ,IAAA;AAOP,SAAS,iBAAiB,OAAA,EAAuB,UAAA,EAAoB,OAAO,CAAA,EAAG,EAAA,GAAK,QAAQ,MAAA,EAAgB;AACjH,EAAA,IAAI,GAAA,GAAM,CAAA;AACV,EAAA,IAAI,GAAA,GAAM,CAAA;AACV,EAAA,MAAM,EAAA,GAAK,IAAI,YAAA,CAAa,KAAK,CAAA;AACjC,EAAA,MAAM,EAAA,GAAK,IAAI,YAAA,CAAa,KAAK,CAAA;AACjC,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,QAAQ,MAAM,CAAA;AACvC,EAAA,KAAA,IAAS,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAI,GAAG,KAAA,GAAQ,GAAA,EAAK,KAAA,IAAS,KAAA,GAAQ,CAAA,EAAG;AACnE,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK;AAC9B,MAAA,MAAM,IAAI,KAAA,GAAQ,CAAA;AAClB,MAAA,MAAM,CAAA,GAAI,GAAA,GAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAK,IAAI,IAAA,CAAK,EAAA,GAAK,CAAA,IAAM,KAAA,GAAQ,CAAA,CAAE,CAAA;AAC9D,MAAA,EAAA,CAAG,CAAC,CAAA,GAAI,CAAA,GAAI,MAAM,OAAA,CAAQ,CAAC,IAAK,CAAA,GAAI,CAAA;AACpC,MAAA,EAAA,CAAG,CAAC,CAAA,GAAI,CAAA;AAAA,IACV;AACA,IAAA,GAAA,CAAI,IAAI,EAAE,CAAA;AACV,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,GAAQ,GAAG,CAAA,EAAA,EAAK;AAClC,MAAA,MAAM,KAAA,GAAQ,EAAA,CAAG,CAAC,CAAA,GAAK,EAAA,CAAG,CAAC,CAAA,GAAK,EAAA,CAAG,CAAC,CAAA,GAAK,EAAA,CAAG,CAAC,CAAA;AAC7C,MAAA,GAAA,IAAO,KAAA,IAAU,IAAI,UAAA,GAAc,KAAA,CAAA;AACnC,MAAA,GAAA,IAAO,KAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,OAAO,GAAA,GAAM,OAAO,IAAA,CAAK,KAAA,CAAM,MAAM,GAAA,GAAM,EAAE,IAAI,EAAA,GAAK,CAAA;AACxD;;;AC/CO,IAAM,iBAAiB,EAAE,UAAA,EAAY,KAAK,SAAA,EAAW,EAAA,EAAI,YAAY,EAAA,EAAG;AAK/E,SAAS,WAAW,EAAA,EAA8B;AAChD,EAAA,MAAM,EAAA,GAAK,iBAAA,EAAmB,CAAA,GAAI,iBAAA,EAAmB,CAAA,GAAI,kBAAA;AACzD,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,EAAA,GAAK,EAAA,GAAK,EAAE,CAAA,EAAG,EAAA,GAAK,EAAA,KAAO,CAAA,GAAI,EAAA,CAAA,EAAK,KAAK,EAAA,IAAM,kBAAA;AACvE,EAAA,MAAM,EAAA,GAAK,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,CAAA;AAC3B,EAAA,MAAM,KAAA,GAAgB;AAAA,IACpB,CAAA,EAAG,EAAE,EAAA,GAAK,EAAA,GAAK,IAAI,CAAA,GAAI,CAAA,GAAI,KAAK,EAAA,EAAI,CAAA,IAAK,IAAI,CAAA,GAAI,EAAA,CAAA,GAAM,KAAK,EAAA,GAAK,EAAA,GAAK,IAAI,CAAA,GAAI,CAAA,GAAI,KAAK,EAAE,CAAA;AAAA,IACzF,CAAA,EAAG,CAAC,CAAA,EAAG,CAAA,IAAK,CAAA,GAAI,CAAA,GAAI,CAAA,CAAA,GAAK,EAAA,EAAA,CAAK,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,KAAK,EAAE;AAAA,GACvD;AACA,EAAA,MAAM,KAAK,iBAAA,EAAmB,EAAA,GAAK,kBAAA,EAAoB,EAAA,GAAK,KAAK,GAAA,CAAI,IAAA,CAAK,EAAA,GAAK,EAAA,GAAK,EAAE,CAAA,EAAG,EAAA,GAAK,CAAA,GAAI,EAAA,GAAK,KAAK,EAAA,GAAK,EAAA;AACjH,EAAA,MAAM,IAAA,GAAe,EAAE,CAAA,EAAG,CAAC,GAAG,EAAA,EAAI,CAAC,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA,EAAG,KAAK,EAAA,GAAK,EAAA,GAAK,KAAK,EAAA,EAAA,CAAK,CAAA,GAAI,KAAK,EAAA,GAAK,EAAA,GAAK,EAAA,IAAM,EAAE,CAAA,EAAE;AACnG,EAAA,OAAO,CAAC,OAAO,IAAI,CAAA;AACrB;AAEA,SAAS,MAAA,CAAO,GAAiB,MAAA,EAAgC;AAC/D,EAAA,IAAI,GAAA,GAAM,CAAA;AACV,EAAA,KAAA,MAAW,EAAE,CAAA,EAAG,CAAA,EAAE,IAAK,MAAA,EAAQ;AAC7B,IAAA,MAAM,GAAA,GAAM,IAAI,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA;AACvC,IAAA,IAAI,KAAK,CAAA,EAAG,EAAA,GAAK,CAAA,EAAG,EAAA,GAAK,GAAG,EAAA,GAAK,CAAA;AACjC,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,GAAA,CAAI,QAAQ,CAAA,EAAA,EAAK;AACnC,MAAA,MAAM,EAAA,GAAK,IAAI,CAAC,CAAA,EAAI,IAAI,CAAA,CAAE,CAAC,CAAA,GAAI,EAAA,GAAK,CAAA,CAAE,CAAC,IAAI,EAAA,GAAK,CAAA,CAAE,CAAC,CAAA,GAAI,EAAA,GAAK,CAAA,CAAE,CAAC,CAAA,GAAI,EAAA,GAAK,CAAA,CAAE,CAAC,CAAA,GAAI,EAAA;AAC/E,MAAA,EAAA,GAAK,EAAA;AAAI,MAAA,EAAA,GAAK,EAAA;AAAI,MAAA,EAAA,GAAK,EAAA;AAAI,MAAA,EAAA,GAAK,CAAA;AAAG,MAAA,GAAA,CAAI,CAAC,CAAA,GAAI,CAAA;AAAA,IAC9C;AACA,IAAA,GAAA,GAAM,GAAA;AAAA,EACR;AACA,EAAA,OAAO,GAAA;AACT;AAEA,IAAM,IAAA,GAAO,CAAC,CAAA,KAAsB,IAAA,CAAK,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAI,CAAC,CAAC,CAAA,GAAI,EAAA;AAEtF,IAAM,OAAA,GAAU,EAAA;AAAhB,IAAoB,QAAA,GAAW,EAAA;AAC/B,IAAM,aAAa,CAAC,IAAA,EAAM,GAAA,EAAK,IAAI,EAAE,GAAA,CAAI,CAAA,CAAA,KAAK,YAAA,CAAa,IAAA,CAAK,EAAE,MAAA,EAAQ,CAAA,GAAI,SAAQ,EAAG,CAAC,GAAG,CAAA,KAAM;AACjG,EAAA,MAAM,IAAI,CAAA,GAAI,OAAA,GAAU,GAAG,CAAA,GAAI,CAAA,GAAI,GAAG,CAAA,GAAI,CAAA,KAAM,CAAA,GAAI,CAAA,GAAI,KAAK,GAAA,CAAI,IAAA,CAAK,KAAK,CAAC,CAAA,IAAK,KAAK,EAAA,GAAK,CAAA,CAAA;AAC3F,EAAA,OAAO,CAAA,IAAK,MAAM,GAAA,GAAM,IAAA,CAAK,IAAI,IAAA,CAAK,EAAA,GAAK,CAAA,IAAK,OAAA,GAAU,CAAA,CAAE,CAAA,CAAA;AAC9D,CAAC,CAAC,CAAA;AAEF,IAAM,UAAU,IAAA,CAAK,GAAA,CAAI,GAAG,UAAA,CAAW,GAAA,CAAI,OAAK,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,EAAG,CAAA,KAAM,IAAI,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,EAAG,CAAC,CAAC,CAAC,CAAA;AAGvF,SAAS,SAAS,KAAA,EAA+B;AAC/C,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACrB,IAAA,MAAM,IAAI,CAAA,CAAE,MAAA;AACZ,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,IAAA,CAAK,CAAA,GAAI,QAAQ,GAAG,QAAA,GAAW,IAAI,YAAA,CAAa,MAAA,GAAS,CAAC,CAAA;AAC9E,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AAAE,MAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,CAAC,CAAE,CAAA,EAAG,CAAA,GAAA,CAAK,CAAA,GAAI,QAAA,GAAW,CAAA,IAAK,CAAA;AAAG,MAAA,IAAI,IAAI,QAAA,CAAS,CAAC,CAAA,EAAI,QAAA,CAAS,CAAC,CAAA,GAAI,CAAA;AAAG,MAAA,IAAI,CAAA,GAAI,MAAM,IAAA,GAAO,CAAA;AAAA,IAAG;AACpJ,IAAA,MAAM,MAAA,GAAS,IAAI,YAAA,CAAa,CAAA,GAAI,IAAI,OAAO,CAAA;AAC/C,IAAA,MAAA,CAAO,GAAA,CAAI,GAAG,OAAO,CAAA;AACrB,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AAC1B,MAAA,MAAM,CAAA,GAAA,CAAK,CAAA,GAAI,QAAA,GAAW,CAAA,IAAK,CAAA;AAC/B,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,QAAA,CAAS,CAAA,GAAI,CAAC,CAAA,EAAI,QAAA,CAAS,CAAC,CAAA,EAAI,QAAA,CAAS,CAAA,GAAI,CAAC,CAAE,CAAA;AACvE,MAAA,IAAI,KAAA,GAAQ,WAAW,IAAA,EAAM;AAC7B,MAAA,KAAA,IAAS,EAAA,GAAK,CAAA,EAAG,EAAA,GAAK,CAAA,EAAG,EAAA,EAAA,EAAM;AAC7B,QAAA,MAAM,CAAA,GAAI,WAAW,EAAE,CAAA;AACvB,QAAA,IAAI,CAAA,GAAI,CAAA;AACR,QAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,GAAI,OAAA,EAAS,CAAA,EAAA,EAAK,CAAA,IAAK,MAAA,CAAO,CAAA,GAAI,CAAA,GAAI,CAAC,CAAA,GAAK,EAAE,CAAC,CAAA;AACnE,QAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA;AACpB,QAAA,IAAI,CAAA,GAAI,MAAM,IAAA,GAAO,CAAA;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAO,IAAA;AACT;AAEO,SAAS,eAAA,CAAgB,QAAA,EAA0B,UAAA,EAAoB,IAAA,GAA+B,EAAC,EAAmB;AAC/H,EAAA,IAAI,UAAA,GAAa,GAAG,OAAA,GAAU,CAAA;AAC9B,EAAA,KAAA,MAAW,CAAA,IAAK,UAAU,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,CAAA,CAAE,QAAQ,CAAA,EAAA,EAAK;AAAE,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,CAAC,CAAE,CAAA;AAAG,IAAA,IAAI,CAAA,GAAI,YAAY,UAAA,GAAa,CAAA;AAAG,IAAA,IAAI,KAAK,CAAA,EAAG,OAAA,EAAA;AAAA,EAAW;AACrJ,EAAA,MAAM,MAAA,GAAS,WAAW,UAAU,CAAA;AACpC,EAAA,MAAM,WAAW,QAAA,CAAS,GAAA,CAAI,OAAK,MAAA,CAAO,CAAA,EAAG,MAAM,CAAC,CAAA;AACpD,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,UAAU,GAAG,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,UAAU,CAAA;AAC7E,EAAA,MAAM,WAAqB,EAAC;AAC5B,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,KAAU,QAAA,CAAS,CAAC,CAAA,EAAG,MAAA,IAAU,CAAA,CAAA,EAAI,CAAA,IAAK,GAAA,EAAK;AACjE,IAAA,IAAI,GAAA,GAAM,CAAA;AACV,IAAA,KAAA,MAAW,KAAK,QAAA,EAAU;AAAE,MAAA,IAAI,CAAA,GAAI,CAAA;AAAG,MAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK,CAAA,IAAK,CAAA,CAAE,CAAC,CAAA,GAAK,CAAA,CAAE,CAAC,CAAA;AAAI,MAAA,GAAA,IAAO,CAAA,GAAI,KAAA;AAAA,IAAO;AACjH,IAAA,QAAA,CAAS,KAAK,GAAG,CAAA;AAAA,EACnB;AACA,EAAA,MAAM,OAAO,CAAC,CAAA,KAAsB,SAAS,EAAA,GAAK,IAAA,CAAK,MAAM,CAAC,CAAA;AAC9D,EAAA,MAAM,QAAA,GAAW,SAAS,MAAA,CAAO,CAAA,CAAA,KAAK,IAAI,CAAA,IAAK,IAAA,CAAK,CAAC,CAAA,GAAI,GAAG,CAAA;AAC5D,EAAA,IAAI,UAAA,GAA4B,IAAA;AAChC,EAAA,IAAI,SAAS,MAAA,EAAQ;AACnB,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,QAAA,CAAS,MAAA,CAAO,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,CAAA,EAAG,CAAC,CAAA,GAAI,QAAA,CAAS,MAAM,CAAA,GAAI,EAAA;AAC/E,IAAA,MAAM,QAAQ,QAAA,CAAS,MAAA,CAAO,OAAK,IAAA,CAAK,CAAC,IAAI,QAAQ,CAAA;AACrD,IAAA,IAAI,MAAM,MAAA,EAAQ,UAAA,GAAa,KAAK,KAAA,CAAM,EAAA,GAAK,KAAK,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,EAAG,CAAA,KAAM,IAAI,CAAA,EAAG,CAAC,IAAI,KAAA,CAAM,MAAM,CAAC,CAAA,GAAI,EAAA;AAAA,EAC1G;AACA,EAAA,OAAO,EAAE,cAAA,EAAgB,UAAA,EAAY,YAAY,IAAA,CAAK,QAAA,KAAa,QAAQ,IAAA,GAAO,IAAA,CAAK,QAAA,CAAS,QAAQ,CAAC,CAAA,EAAG,YAAA,EAAc,KAAK,UAAU,CAAA,EAAG,gBAAgB,OAAA,EAAQ;AACtK;AAGO,SAAS,YAAA,CAAa,QAAA,EAA0B,UAAA,EAAoB,IAAA,GAAqD,EAAC,EAAwC;AACvK,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAA,CAAO,IAAA,CAAK,SAAA,IAAa,CAAA,IAAK,UAAU,CAAA,EAAG,KAAA,GAAQ,EAAA,KAAA,CAAQ,IAAA,CAAK,WAAA,IAAe,GAAA,IAAO,EAAA,CAAA;AACvG,EAAA,MAAM,IAAI,QAAA,CAAS,CAAC,GAAG,MAAA,IAAU,CAAA,EAAG,MAA2C,EAAC;AAChF,EAAA,IAAI,KAAA,GAAQ,EAAA;AACZ,EAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,KAAuB,QAAA,CAAS,KAAA,CAAM,CAAA,CAAA,KAAK,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,CAAC,CAAE,CAAA,GAAI,KAAK,CAAA;AACjF,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,IAAK,CAAA,EAAG,CAAA,EAAA,EAAK;AAC3B,IAAA,MAAM,CAAA,GAAI,CAAA,GAAI,CAAA,IAAK,KAAA,CAAM,CAAC,CAAA;AAC1B,IAAA,IAAI,CAAA,IAAK,KAAA,GAAQ,CAAA,EAAG,KAAA,GAAQ,CAAA;AAC5B,IAAA,IAAI,CAAC,CAAA,IAAK,KAAA,IAAS,CAAA,EAAG;AAAE,MAAA,IAAI,CAAA,GAAI,SAAS,GAAA,EAAK,GAAA,CAAI,KAAK,EAAE,IAAA,EAAM,IAAA,CAAK,KAAA,CAAM,KAAA,GAAQ,UAAA,GAAa,GAAI,CAAA,GAAI,GAAA,EAAM,IAAI,IAAA,CAAK,KAAA,CAAM,IAAI,UAAA,GAAa,GAAI,CAAA,GAAI,GAAA,EAAM,CAAA;AAAG,MAAA,KAAA,GAAQ,EAAA;AAAA,IAAI;AAAA,EAC5K;AACA,EAAA,OAAO,GAAA;AACT;;;AC9BA,IAAM,SAAA,GAAY,EAAA;AAElB,IAAM,YAAA,GAAe,GAAA;AAErB,IAAM,EAAA,GAAK,CAAC,CAAA,KAAsB,IAAA,CAAK,MAAM,EAAA,GAAK,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,KAAK,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAC,CAAA,GAAI,EAAA;AACxF,IAAM,IAAA,GAAO,CAAC,KAAA,KAAwC;AACpD,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,MAAM,CAAC,CAAA;AACtC,EAAA,MAAM,MAAM,IAAI,YAAA,CAAa,KAAA,CAAM,CAAC,EAAG,MAAM,CAAA;AAC7C,EAAA,KAAA,MAAW,KAAK,KAAA,EAAO,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,IAAI,MAAA,EAAQ,CAAA,EAAA,EAAK,GAAA,CAAI,CAAC,IAAI,GAAA,CAAI,CAAC,IAAK,CAAA,CAAE,CAAC,IAAK,KAAA,CAAM,MAAA;AAC7F,EAAA,OAAO,GAAA;AACT,CAAA;AAEA,SAAS,OAAA,CAAQ,OAAuB,UAAA,EAAkC;AACxE,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,IAAI,EAAA,GAAK,CAAA;AACT,EAAA,KAAA,MAAW,KAAK,KAAA,EAAO,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,IAAI,CAAA,CAAE,MAAA,EAAQ,CAAA,EAAA,EAAK,IAAI,KAAK,GAAA,CAAI,CAAA,CAAE,CAAC,CAAE,IAAI,IAAA,EAAM;AAAE,IAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,CAAC,CAAE,CAAA;AAAG,IAAA,EAAA,GAAK,CAAA;AAAA,EAAG;AACxH,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,KAAK,KAAA,CAAO,KAAA,CAAM,CAAC,CAAA,CAAG,MAAA,GAAS,UAAA,GAAc,GAAI,CAAA,GAAI,GAAA;AAAA,IAC7D,MAAA,EAAQ,GAAG,IAAI,CAAA;AAAA,IACf,WAAW,IAAA,CAAK,KAAA,CAAO,EAAA,GAAK,UAAA,GAAc,GAAI,CAAA,GAAI,GAAA;AAAA,IAClD,YAAA,EAAc,gBAAA,CAAiB,IAAA,CAAK,KAAK,GAAG,UAAU;AAAA,GACxD;AACF;AAOO,SAAS,aACd,GAAA,EAAqB,OAAA,EAA4B,OAAwB,aAAA,EAAuB,IAAA,GAA4B,EAAC,EAChH;AAEb,EAAA,MAAM,MAAA,uBAAa,GAAA,EAA0D;AAC7E,EAAA,OAAA,GAAU,OAAA,CAAQ,IAAI,CAAA,CAAA,KAAK;AACzB,IAAA,IAAI,CAAC,CAAA,CAAE,IAAA,EAAM,OAAO,CAAA;AACpB,IAAA,MAAM,KAAA,GAAQ,EAAE,GAAG,CAAA,EAAG,GAAG,QAAA,CAAS,CAAA,EAAG,aAAa,CAAA,EAAE;AACpD,IAAA,MAAA,CAAO,GAAA,CAAI,KAAA,EAAO,EAAE,QAAA,EAAU,CAAA,CAAE,GAAA,EAAK,KAAA,EAAO,UAAA,CAAW,CAAA,EAAG,aAAa,CAAA,EAAG,CAAA;AAC1E,IAAA,OAAO,KAAA;AAAA,EACT,CAAC,CAAA;AACD,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,OAAO;AAAA,MAAE,QAAA,EAAU,aAAA;AAAA,MAAe,UAAA,EAAY,CAAA;AAAA,MAAG,MAAA,EAAQ,IAAA;AAAA,MAAM,MAAA,EAAQ,CAAA;AAAA,MAAG,SAAS,EAAC;AAAA,MAAG,SAAS,EAAC;AAAA,MAC/F,QAAA,EAAU,EAAE,cAAA,EAAgB,IAAA,EAAM,UAAA,EAAY,MAAM,cAAA,EAAgB,CAAA,EAAG,QAAA,EAAU,EAAC,EAAE;AAAA,MAAG,QAAQ,EAAC;AAAA,MAAG,MAAM,EAAC;AAAA,MAAG,OAAO,EAAC;AAAA,MAAG,MAAA,EAAQ,CAAC,6GAA6G;AAAA,KAAE;AAAA,EACpP;AACA,EAAA,MAAM,KAAK,GAAA,CAAI,UAAA;AACf,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,IAAA,CAAK,EAAE,QAAQ,GAAA,CAAI,gBAAA,EAAiB,EAAG,CAAC,CAAA,EAAG,CAAA,KAAM,GAAA,CAAI,cAAA,CAAe,CAAC,CAAC,CAAA;AAC1F,EAAA,MAAM,OAAA,GAAU,KAAK,KAAK,CAAA;AAC1B,EAAA,MAAM,MAAA,GAAS,CAAC,EAAA,EAAY,EAAA,KAAuB;AACjD,IAAA,IAAI,CAAA,GAAI,CAAA;AACR,IAAA,MAAM,EAAA,GAAK,KAAK,GAAA,CAAI,GAAA,CAAI,QAAQ,IAAA,CAAK,IAAA,CAAK,EAAA,GAAK,EAAE,CAAC,CAAA;AAClD,IAAA,KAAA,MAAW,CAAA,IAAK,KAAA,EAAO,KAAA,IAAS,CAAA,GAAI,IAAA,CAAK,IAAI,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,EAAA,GAAK,EAAE,CAAC,GAAG,CAAA,GAAI,EAAA,EAAI,CAAA,EAAA,EAAK,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,CAAC,CAAE,CAAC,CAAA;AAClH,IAAA,OAAO,CAAA;AAAA,EACT,CAAA;AAGA,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,MAAA,IAAU,IAAA,CAAK,MAAA,GAAS,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,IAAA,CAAK,MAAM,CAAA,GAAI,GAAA;AAC3E,EAAA,MAAM,UAAkC,EAAC;AACzC,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,aAAA,GAAgB,IAAA,EAAM,KAAK,GAAA,EAAK;AAClD,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,EAAE,CAAA;AAC5B,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,MAAA,EAAQ,KAAK,KAAA,CAAA,CAAO,CAAA,GAAI,GAAA,IAAO,EAAE,CAAC,CAAA;AAC1D,IAAA,IAAI,GAAA,GAAM,CAAA;AACV,IAAA,IAAI,IAAA,GAAO,CAAA;AACX,IAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACrB,MAAA,KAAA,IAAS,CAAA,GAAI,EAAA,EAAI,CAAA,GAAI,EAAA,EAAI,CAAA,EAAA,EAAK;AAC5B,QAAA,GAAA,IAAO,CAAA,CAAE,CAAC,CAAA,GAAK,CAAA,CAAE,CAAC,CAAA;AAClB,QAAA,IAAA,GAAO,IAAA,CAAK,IAAI,IAAA,EAAM,IAAA,CAAK,IAAI,CAAA,CAAE,CAAC,CAAE,CAAC,CAAA;AAAA,MACvC;AAAA,IACF;AACA,IAAA,MAAM,IAAI,IAAA,CAAK,GAAA,CAAI,IAAI,EAAA,GAAK,EAAA,IAAM,MAAM,MAAM,CAAA;AAC9C,IAAA,OAAA,CAAQ,IAAA,CAAK,EAAE,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,GAAI,CAAA,GAAI,GAAA,EAAM,KAAA,EAAO,EAAA,CAAG,IAAA,CAAK,IAAA,CAAK,GAAA,GAAM,CAAC,CAAC,CAAA,EAAG,MAAA,EAAQ,EAAA,CAAG,IAAI,CAAA,EAAG,YAAA,EAAc,gBAAA,CAAiB,OAAA,EAAS,EAAA,EAAI,EAAA,EAAI,EAAE,CAAA,EAAG,CAAA;AAAA,EACvJ;AAEA,EAAA,MAAM,SAAmB,EAAC;AAC1B,EAAA,IAAI,KAAA,IAAS,KAAA,CAAM,IAAA,GAAO,CAAA,EAAG;AAC3B,IAAA,MAAA,CAAO,IAAA,CAAK,CAAA,iBAAA,EAAoB,EAAA,CAAG,KAAA,CAAM,IAAI,CAAC,CAAA,SAAA,EAAY,KAAA,CAAM,MAAA,CAAO,OAAA,CAAQ,CAAC,CAAC,CAAA,qEAAA,CAAkE,CAAA;AAAA,EACrJ;AACA,EAAA,MAAM,QAAA,uBAAe,GAAA,EAA0B;AAC/C,EAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAAqC;AACpD,IAAA,IAAI,EAAE,KAAA,EAAO;AACX,MAAA,IAAI,CAAA,GAAI,QAAA,CAAS,GAAA,CAAI,CAAA,CAAE,MAAM,GAAG,CAAA;AAChC,MAAA,IAAI,CAAC,CAAA,EAAG,QAAA,CAAS,GAAA,CAAI,EAAE,KAAA,CAAM,GAAA,EAAM,CAAA,GAAI,OAAA,CAAQ,EAAE,KAAA,CAAM,MAAA,CAAO,EAAE,CAAA,EAAG,EAAE,CAAE,CAAA;AACvE,MAAA,OAAO,CAAA;AAAA,IACT;AACA,IAAA,MAAM,IAAI,CAAA,CAAE,MAAA;AACZ,IAAA,OAAO,QAAQ,KAAA,CAAM,IAAA,CAAK,EAAE,MAAA,EAAQ,EAAE,gBAAA,EAAiB,EAAG,CAAC,CAAA,EAAG,MAAM,CAAA,CAAE,cAAA,CAAe,CAAC,CAAC,CAAA,EAAG,EAAE,UAAU,CAAA;AAAA,EACxG,CAAA;AACA,EAAA,MAAM,aAAuB,EAAC;AAC9B,EAAA,MAAM,QAAA,GAAW,KAAA,EAAO,QAAA,EAAU,MAAA,IAAU,CAAA;AAC5C,EAAA,MAAM,SAAqI,EAAC;AAC5I,EAAA,MAAM,IAAA,GAAO,CAAC,GAAG,OAAO,EAAE,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,EAAE,KAAA,GAAQ,CAAA,CAAE,KAAK,CAAA,CAAE,IAAI,CAAA,CAAA,KAAK;AACnE,IAAA,MAAM,KAAA,GAAQ,EAAE,KAAA,IAAS,gBAAA;AACzB,IAAA,MAAM,MAAA,GAAS,EAAE,MAAA,IAAU,OAAA;AAC3B,IAAA,MAAM,MAAA,GAAS,EAAA,CAAG,MAAA,CAAO,CAAA,CAAE,KAAA,EAAO,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,GAAA,EAAK,aAAa,CAAC,CAAC,CAAA;AACjE,IAAA,MAAM,KAAA,GAAQ,QAAQ,CAAC,CAAA;AAGvB,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,iBAAiB,CAAA,EAAG,GAAA,CAAI,CAAA,CAAE,eAAA,IAAmB,EAAC,EAAG,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,KAAK,CAAC,CAAA;AAC/F,IAAA,MAAM,KAAA,GAAQ,EAAA,CAAG,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,KAAA,CAAM,MAAA,GAAS,EAAE,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,SAAS,CAAC,CAAA;AACzE,IAAA,IAAI,CAAA,CAAE,SAAS,aAAA,EAAe;AAC5B,MAAA,MAAA,CAAO,IAAA,CAAK,CAAA,EAAG,KAAK,CAAA,EAAA,EAAK,MAAM,CAAA,YAAA,EAAe,CAAA,CAAE,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,yBAAA,EAA4B,aAAa,CAAA,EAAA,CAAI,CAAA;AAAA,IAC/G,CAAA,MAAA,IAAW,KAAA,GAAQ,YAAA,IAAgB,MAAA,GAAS,YAAA,EAAc;AACxD,MAAA,MAAA,CAAO,IAAA,CAAK,GAAG,KAAK,CAAA,EAAA,EAAK,MAAM,CAAA,oBAAA,EAAuB,CAAA,CAAE,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAC,QAAQ,CAAA,CAAE,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,gBAAA,EAAmB,KAAK,CAAA,gBAAA,EAAmB,SAAS,CAAA,iBAAA,EAAoB,MAAM,CAAA,8BAAA,CAA2B,CAAA;AAAA,IAC3M;AACA,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA;AAC1B,IAAA,IAAI,CAAA,CAAE,KAAA,IAAS,CAAA,CAAE,KAAA,GAAQ,aAAA,EAAe;AACtC,MAAA,IAAI,KAAA,EAAO;AAET,QAAA,IAAI,KAAA,CAAM,KAAA,GAAQ,KAAA,CAAM,QAAA,GAAW,IAAA,EAAM;AACvC,UAAA,MAAA,CAAO,KAAK,CAAA,EAAG,KAAK,CAAA,EAAA,EAAK,MAAM,sDAAsD,KAAA,CAAM,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,gBAAA,EAAmB,KAAA,CAAM,SAAS,OAAA,CAAQ,CAAC,CAAC,CAAA,0HAAA,CAAuH,CAAA;AAAA,QAChR;AAAA,MACF,CAAA,MAAA,IAAW,CAAA,CAAE,GAAA,GAAM,aAAA,GAAgB,IAAA,EAAM;AACvC,QAAA,MAAA,CAAO,IAAA,CAAK,CAAA,EAAG,KAAK,CAAA,EAAA,EAAK,MAAM,CAAA,wCAAA,EAA2C,aAAa,CAAA,cAAA,EAAiB,CAAA,CAAE,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,EAAA,CAAI,CAAA;AAAA,MAC9H;AAAA,IACF;AAGA,IAAA,IAAI,CAAA,CAAE,SAAS,CAAC,CAAA,CAAE,QAAQ,CAAA,CAAE,KAAA,GAAQ,aAAA,IAAiB,CAAC,KAAA,EAAO;AAC3D,MAAA,MAAM,QAAQ,IAAA,CAAK,GAAA,CAAI,EAAE,GAAA,EAAK,aAAa,IAAI,CAAA,CAAE,KAAA;AACjD,MAAA,MAAM,IAAA,GAAO,CAAA,CAAE,KAAA,CAAM,OAAA,GAAU,KAAA;AAC/B,MAAA,IAAI,IAAA,GAAO,CAAA,IAAK,IAAA,GAAO,CAAA,CAAE,KAAA,CAAM,OAAA,GAAU,IAAA,EAAM,UAAA,CAAW,IAAA,CAAK,CAAA,EAAG,KAAK,CAAA,EAAA,EAAK,MAAM,CAAA,gBAAA,EAAmB,IAAA,CAAK,KAAA,CAAM,CAAA,CAAE,KAAA,CAAM,OAAA,GAAU,EAAE,CAAA,GAAI,EAAE,CAAA,gBAAA,EAAmB,IAAA,CAAK,KAAA,CAAM,KAAA,GAAQ,GAAG,CAAA,GAAI,GAAG,CAAA,yGAAA,CAA2G,CAAA;AAAA,IACvS;AAEA,IAAA,IAAI,EAAE,KAAA,IAAS,CAAA,CAAE,KAAA,CAAM,MAAA,CAAO,SAAS,CAAA,EAAG;AACxC,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,CAAE,KAAA,CAAM,MAAA,CAAO,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,KAAK,CAAC,CAAA;AAC5D,MAAA,MAAM,KAAA,GAAQ,CAAA,CAAE,KAAA,CAAM,MAAA,CAAO,MAAA,CAAO,CAAA,CAAA,KAAK,CAAA,CAAE,KAAA,IAAS,OAAA,GAAU,CAAA,CAAE,KAAA,CAAO,KAAK,CAAA;AAC5E,MAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,QAAA,UAAA,CAAW,IAAA,CAAK,GAAG,KAAK,CAAA,EAAA,EAAK,MAAM,CAAA,GAAA,EAAM,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,CAAE,IAAI,CAAA,CAAA,KAAK,CAAA,IAAA,EAAO,EAAE,IAAI,CAAA,UAAA,EAAa,KAAK,KAAA,CAAM,CAAA,CAAE,KAAA,GAAQ,GAAG,CAAA,GAAI,GAAG,uBAAuB,OAAO,CAAA,4CAAA,EAA+C,KAAK,KAAA,CAAM,CAAA,CAAE,QAAQ,GAAG,CAAA,GAAI,GAAG,CAAA,MAAA,EAAS,IAAA,CAAK,KAAA,CAAM,EAAE,KAAA,GAAQ,CAAA,CAAE,KAAA,CAAO,KAAK,CAAA,GAAI,CAAC,GAAG,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA,oGAAA,CAAsG,CAAA;AAAA,MACjZ,WAAW,CAAA,CAAE,KAAA,CAAM,SAAS,OAAA,GAAU,CAAA,CAAE,MAAM,KAAA,EAAO;AACnD,QAAA,UAAA,CAAW,IAAA,CAAK,CAAA,EAAG,KAAK,CAAA,EAAA,EAAK,MAAM,CAAA,yBAAA,EAA4B,CAAA,CAAE,KAAA,CAAM,KAAK,CAAA,sCAAA,EAAyC,OAAO,CAAA,gCAAA,CAAkC,CAAA;AAAA,MAChK;AAAA,IACF;AAGA,IAAA,IAAI,UAAA;AACJ,IAAA,IAAI,CAAC,SAAS,CAAC,CAAA,CAAE,aAAa,CAAA,CAAE,KAAA,GAAQ,aAAA,IAAiB,KAAA,GAAQ,YAAA,EAAc;AAC7E,MAAA,MAAM,EAAA,GAAK,CAAA,CAAE,KAAA,GAAQ,KAAA,CAAM,SAAA;AAC3B,MAAA,MAAM,MAAA,GAAS,EAAA,CAAG,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,EAAG,EAAA,GAAK,IAAI,CAAA,EAAG,KAAK,GAAA,CAAI,aAAA,EAAe,EAAA,GAAK,IAAI,CAAC,CAAC,CAAA;AACpF,MAAA,UAAA,GAAa,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,KAAA,CAAA,CAAO,UAAU,KAAA,GAAQ,QAAA,CAAA,IAAa,EAAE,CAAA,GAAI,EAAE,CAAA;AAC5E,MAAA,IAAI,CAAC,CAAA,CAAE,KAAA,IAAS,MAAA,CAAO,UAAA,CAAW,KAAK,CAAA,IAAK,KAAA,CAAM,MAAA,IAAU,CAAA,IAAK,UAAA,IAAc,SAAA,EAAW;AACxF,QAAA,MAAA,CAAO,IAAA,CAAK,EAAE,KAAA,EAAO,MAAA,EAAQ,OAAO,IAAA,CAAK,KAAA,CAAA,CAAO,KAAA,GAAQ,QAAA,IAAY,EAAE,CAAA,GAAI,IAAI,SAAA,EAAW,MAAA,EAAQ,YAAY,EAAA,EAAI,IAAA,CAAK,MAAM,EAAA,GAAK,GAAG,CAAA,GAAI,GAAA,EAAK,CAAA;AAAA,MAC/I;AAAA,IACF;AACA,IAAA,OAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,CAAA,CAAE,KAAA,EAAO,GAAA,EAAK,CAAA,CAAE,GAAA,EAAK,MAAA,EAAQ,KAAA,EAAO,GAAI,UAAA,KAAe,MAAA,GAAY,EAAE,UAAA,EAAW,GAAI,EAAC,EAAI,GAAI,CAAA,CAAE,KAAA,GAAQ,EAAE,KAAA,EAAO,CAAA,CAAE,KAAA,EAAM,GAAI,EAAC,EAAG;AAAA,EACjK,CAAC,CAAA;AAGD,EAAA,IAAI,MAAA,CAAO,SAAS,CAAA,EAAG;AACrB,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA,EAAG,CAAA,KAAO,CAAA,CAAE,UAAA,GAAa,CAAA,CAAE,UAAA,GAAa,CAAA,GAAI,CAAE,CAAA;AAE3E,IAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,MAAA,CAAO,IAAI,CAAA,CAAA,KAAA,CAAM,CAAA,CAAE,UAAU,gBAAA,GAAmB,CAAA,CAAA,EAAI,OAAO,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA,GAAK,CAAA,CAAE,SAAS,CAAA,CAAE,MAAM,CAAC,CAAA,CAAE,IAAA;AACzH,IAAA,UAAA,CAAW,IAAA,CAAK,CAAA,EAAG,QAAA,KAAa,CAAA,GAAI,mBAAA,GAAsB,CAAA,EAAG,QAAQ,CAAA,kBAAA,CAAoB,CAAA,SAAA,EAAY,SAAS,CAAA,yEAAA,EAA4E,MAAA,CAAO,MAAA,GAAS,QAAA,GAAW,CAAA,EAAA,EAAK,MAAA,CAAO,MAAM,CAAA,aAAA,CAAA,GAAkB,EAAE,CAAA,gBAAA,EAAmB,KAAA,CAAM,KAAK,CAAA,EAAA,EAAK,KAAA,CAAM,MAAM,CAAA,gBAAA,EAAmB,KAAA,CAAM,KAAK,mBAAmB,IAAA,CAAK,KAAA,CAAM,KAAA,CAAM,SAAA,GAAY,GAAG,CAAA,GAAI,GAAG,CAAA,SAAA,EAAY,KAAA,CAAM,MAAM,CAAA,SAAA,EAAY,KAAA,CAAM,EAAE,CAAA,IAAA,EAAO,KAAA,CAAM,UAAU,CAAA,4EAAA,EAA+E,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAA,CAAM,SAAA,GAAY,EAAA,KAAA,CAAQ,KAAA,CAAM,UAAA,GAAa,CAAA,IAAK,EAAA,CAAG,CAAA,GAAI,EAAE,CAAA,GAAI,EAAE,CAAA,oJAAA,CAAsJ,CAAA;AAAA,EACpwB;AAEA,EAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,KAAA,EAAO,EAAE,CAAA;AACvC,EAAA,MAAM,WAAW,YAAA,CAAa,KAAA,EAAO,IAAI,EAAE,SAAA,EAAW,GAAG,CAAA;AACzD,EAAA,IAAI,KAAA,CAAM,iBAAiB,CAAA,EAAG,MAAA,CAAO,KAAK,CAAA,EAAG,KAAA,CAAM,cAAc,CAAA,yGAAA,CAAsG,CAAA;AACvK,EAAA,IAAI,KAAA,CAAM,aAAc,CAAA,EAAG,MAAA,CAAO,KAAK,CAAA,uBAAA,EAA0B,KAAA,CAAM,UAAU,CAAA,wEAAA,CAAqE,CAAA;AACtJ,EAAA,MAAM,KAAA,GAAkB,CAAC,GAAG,UAAU,CAAA;AACtC,EAAA,IAAI,KAAA,CAAM,mBAAmB,IAAA,EAAM;AACjC,IAAA,IAAI,KAAA,CAAM,cAAA,GAAiB,cAAA,CAAe,UAAA,IAAc,CAAC,KAAA,EAAO,QAAA,EAAU,KAAA,CAAM,IAAA,CAAK,CAAA,gCAAA,EAAmC,KAAA,CAAM,cAAc,CAAA,uIAAA,CAA+H,CAAA;AAAA,SAAA,IAClQ,KAAA,CAAM,iBAAiB,cAAA,CAAe,SAAA,QAAiB,IAAA,CAAK,CAAA,iBAAA,EAAoB,KAAA,CAAM,cAAc,CAAA,8CAAA,CAAsC,CAAA;AAAA,EACrJ;AACA,EAAA,IAAI,KAAA,CAAM,UAAA,GAAc,cAAA,CAAe,UAAA,IAAc,MAAM,UAAA,IAAe,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,oBAAoB,KAAA,CAAM,UAAU,CAAA,wDAAA,EAA2D,cAAA,CAAe,UAAU,CAAA,MAAA,CAAQ,CAAA;AACxN,EAAA,MAAM,QAAQ,KAAA,EAAO,QAAA;AACrB,EAAA,IAAI,KAAA,EAAO;AAET,IAAA,MAAM,SAAA,GAAY,CAAC,CAAA,KAAsB;AACvC,MAAA,MAAM,KAAK,OAAA,CAAQ,MAAA,CAAO,CAAA,CAAA,KAAK,CAAA,CAAE,SAAS,CAAA,IAAK,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,EAAE,GAAA,EAAK,aAAa,CAAC,CAAA,CAAE,IAAI,CAAA,CAAA,KAAM,CAAA,CAAE,KAAA,IAAS,CAAA,CAAE,UAAU,gBAAA,GAAmB,CAAA,EAAG,CAAA,CAAE,KAAK,GAAG,CAAA,CAAE,MAAA,GAAS,CAAA,EAAA,EAAK,CAAA,CAAE,MAAM,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA,CAAA,GAAK,CAAA,CAAE,UAAU,OAAQ,CAAA;AAC7M,MAAA,OAAO,CAAC,GAAG,IAAI,GAAA,CAAI,EAAE,CAAC,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA,IAAK,wBAAA;AAAA,IACpD,CAAA;AACA,IAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,CAAA,kBAAA,EAAqB,KAAA,CAAM,MAAA,GAAS,CAAA,GAAI,QAAA,GAAW,SAAS,CAAA,IAAA,EAAO,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,MAAM,CAAC,CAAA,IAAA,EAAO,KAAA,CAAM,OAAA,GAAU,QAAA,GAAW,UAAU,CAAA,CAAA,EAAI,KAAA,CAAM,MAAM,CAAA,wBAAA,EAA2B,KAAA,CAAM,MAAM,CAAA,IAAA,CAAM,CAAA;AACrO,IAAA,IAAI,MAAM,OAAA,EAAS;AACjB,MAAA,KAAA,CAAM,IAAA,CAAK,CAAA,wBAAA,EAA2B,KAAA,CAAM,MAAM,CAAA,qBAAA,EAAwB,MAAM,WAAW,CAAA,gCAAA,EAAmC,KAAA,CAAM,SAAA,KAAc,MAAA,GAAY,CAAA,KAAA,EAAQ,MAAM,SAAS,CAAA,UAAA,EAAa,SAAA,CAAU,KAAA,CAAM,SAAS,CAAC,CAAA,MAAA,CAAA,GAAW,EAAE,CAAA,sBAAA,EAAyB,KAAA,CAAM,SAAA,IAAa,EAAE,CAAA,+HAAA,CAAiI,CAAA;AAAA,IAC1Z,WAAW,KAAA,CAAM,SAAA,KAAc,MAAA,IAAa,KAAA,CAAM,aAAa,CAAA,EAAG;AAChE,MAAA,KAAA,CAAM,KAAK,CAAA,qBAAA,EAAwB,KAAA,CAAM,SAAS,CAAA,6BAAA,EAAgC,MAAM,SAAS,CAAA,UAAA,EAAa,SAAA,CAAU,KAAA,CAAM,aAAa,CAAC,CAAC,CAAA,+BAAA,EAAkC,KAAA,CAAM,MAAM,CAAA,yFAAA,CAA2F,CAAA;AAAA,IACxR;AAAA,EACF;AACA,EAAA,KAAA,MAAW,CAAA,IAAK,QAAA,CAAS,KAAA,CAAM,CAAA,EAAG,CAAC,GAAG,KAAA,CAAM,IAAA,CAAK,CAAA,uBAAA,EAA0B,CAAA,CAAE,IAAI,CAAA,MAAA,EAAS,EAAE,EAAE,CAAA,IAAA,EAAO,IAAA,CAAK,KAAA,CAAA,CAAO,CAAA,CAAE,EAAA,GAAK,EAAE,IAAA,IAAQ,EAAE,CAAA,GAAI,EAAE,CAAA,GAAA,CAAK,CAAA;AAC/I,EAAA,IAAI,QAAA,CAAS,SAAS,CAAA,EAAG,KAAA,CAAM,KAAK,CAAA,IAAA,EAAO,QAAA,CAAS,MAAA,GAAS,CAAC,CAAA,2DAAA,CAA6D,CAAA;AAC3H,EAAA,MAAM,aAAa,IAAA,CAAK,MAAA,IAAU,KAAK,MAAA,CAAO,MAAA,GAAS,KAAK,MAAA,GAAS,CAAC,EAAE,IAAA,EAAM,OAAA,EAAS,OAAO,CAAA,EAAG,GAAA,EAAK,eAAe,CAAA,EAAG,IAAI,CAAA,EAAA,KAAM;AAChI,IAAA,MAAM,CAAA,GAAI,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,EAAA,CAAG,QAAQ,EAAE,CAAC,GAAG,CAAA,GAAI,IAAA,CAAK,IAAI,GAAA,CAAI,MAAA,EAAQ,KAAK,KAAA,CAAM,EAAA,CAAG,GAAA,GAAM,EAAE,CAAC,CAAA;AAClG,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,CAAC,CAAC,CAAC,CAAA;AACzD,IAAA,MAAM,IAAI,eAAA,CAAgB,IAAA,EAAM,IAAI,EAAE,QAAA,EAAU,OAAO,CAAA;AACvD,IAAA,OAAO,EAAE,IAAA,EAAM,EAAA,CAAG,MAAM,IAAA,EAAM,EAAA,CAAG,OAAO,EAAA,EAAI,EAAA,CAAG,KAAK,IAAA,EAAM,CAAA,CAAE,gBAAgB,MAAA,EAAQ,CAAA,CAAE,cAAc,MAAA,EAAQ,CAAA,CAAE,eAAe,GAAA,EAAI;AAAA,EACnI,CAAC,CAAA;AAED,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,aAAA;AAAA,IACV,UAAA,EAAY,EAAA;AAAA,IACZ,QAAQ,EAAA,CAAG,KAAA,EAAO,QAAQ,MAAA,CAAO,CAAA,EAAG,aAAa,CAAC,CAAA;AAAA,IAClD,MAAA,EAAQ,OAAO,MAAA,IAAU,CAAA;AAAA,IACzB,OAAA,EAAS,IAAA;AAAA,IACT,OAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA,EAAU,EAAE,cAAA,EAAgB,KAAA,CAAM,cAAA,EAAgB,UAAA,EAAY,KAAA,CAAM,UAAA,IAAc,IAAA,EAAM,cAAA,EAAgB,KAAA,CAAM,cAAA,EAAgB,QAAA,EAAS;AAAA,IACvI,MAAA,EAAQ,SAAA;AAAA,IACR,MAAM,IAAA,CAAK,GAAA,CAAI,CAAA,CAAA,MAAM,EAAE,GAAG,CAAA,CAAE,KAAA,EAAO,IAAA,EAAM,CAAA,EAAG,EAAE,KAAK,CAAA,EAAA,EAAK,CAAA,CAAE,MAAM,KAAI,CAAE,CAAA;AAAA,IACtE;AAAA,GACF;AACF;AAsBO,SAAS,YAAA,CAAa,GAAA,EAAc,IAAA,GAA4B,EAAC,EAAgB;AACtF,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,MAAA,GAAS,GAAA,CAAI,UAAA;AAClC,EAAA,MAAM,CAAA,GAAI,YAAA,CAAa,GAAA,EAAK,IAAI,IAAA,EAAM,QAAA,EAAU,IAAA,CAAK,MAAA,GAAS,EAAE,MAAA,EAAQ,IAAA,CAAK,MAAA,EAAO,GAAI,EAAE,CAAA;AAE1F,EAAA,MAAM,KAAA,GAAQ,KAAK,MAAA,IAAU,GAAA;AAC7B,EAAA,IAAI,UAAU,CAAA,CAAE,OAAA;AAChB,EAAA,IAAI,QAAQ,MAAA,GAAS,CAAA,IAAK,QAAA,GAAW,OAAA,CAAQ,QAAQ,MAAA,GAAS,CAAC,CAAA,CAAG,CAAA,GAAI,QAAQ,CAAA,EAAG,OAAA,GAAU,OAAA,CAAQ,KAAA,CAAM,GAAG,EAAE,CAAA;AAC9G,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,IAAA,CAAK,KAAA,CAAM,QAAA,GAAW,GAAI,CAAA,GAAI,GAAA;AAAA,IAAM,YAAY,GAAA,CAAI,UAAA;AAAA,IAAY,UAAU,GAAA,CAAI,gBAAA;AAAA,IAAkB,QAAQ,CAAA,CAAE,MAAA;AAAA,IACpH,UAAU,CAAA,CAAE,QAAA;AAAA,IAAU,OAAA,EAAS,OAAA,CAAQ,GAAA,CAAI,CAAA,CAAA,MAAM,EAAE,CAAA,EAAG,CAAA,CAAE,CAAA,EAAG,KAAA,EAAO,CAAA,CAAE,KAAA,EAAM,CAAE,CAAA;AAAA,IAAG,OAAO,CAAC,GAAG,EAAE,KAAA,EAAO,GAAG,EAAE,MAAM;AAAA,GAChH;AACF","file":"chunk-G4PXCV2B.js","sourcesContent":["import type { AudioDescriptor } from '../types';\n\ntype RemapFields = Pick<AudioDescriptor, 'start' | 'end' | 'loop' | 'initialVolume' | 'volumeKeyframes' | 'warp' | 'sourceMap'>;\n\n/** The gain of a sound at its own time `u`: `initialVolume` from `start`, then straight lines to each point (what the Web Audio ramps did). */\nfunction gainAt(a: RemapFields, u: number): number {\n let prevT = a.start, prevV = a.initialVolume ?? 1;\n if (u <= prevT) return prevV;\n for (const kf of a.volumeKeyframes ?? []) {\n if (u <= kf.time) return kf.time === prevT ? kf.value : prevV + (kf.value - prevV) * ((u - prevT) / (kf.time - prevT));\n prevT = kf.time; prevV = kf.value;\n }\n return prevV;\n}\n\nconst CELL = 1 / 1000;\n\n/**\n * Render a sound through its time maps. For every output sample T: u = warp(T) (sampled every millisecond, interpolated), the position\n * in the sound is sourceMap(u) (or u - start), and the sample is read with linear interpolation. Where the position hardly moves (a\n * freeze) it fades to silence rather than hold one sample as a DC offset. Returns null when it is never audible. `at` is where in the\n * mix the returned samples begin. Pitch follows speed (a resample, like a tape).\n */\nexport function resampleThrough(\n a: RemapFields, left: Float32Array, right: Float32Array, srcRate: number, outRate: number, total: number,\n): { L: Float32Array; R: Float32Array; at: number } | null {\n const nCell = Math.ceil(total / CELL) + 2;\n const U = new Float64Array(nCell);\n let first = -1, last = -1;\n for (let k = 0; k < nCell; k++) {\n const u = a.warp ? a.warp(k * CELL) : k * CELL;\n U[k] = u;\n if (Number.isFinite(u) && u >= a.start && u < a.end) { if (first < 0) first = k; last = k; }\n }\n if (first < 0) return null;\n const lo = Math.max(0, (first - 1) * CELL), hi = Math.min(total, (last + 2) * CELL);\n const n = Math.max(1, Math.ceil((hi - lo) * outRate));\n const L = new Float32Array(n), R = new Float32Array(n);\n const dur = left.length / srcRate;\n let prevPos = NaN;\n for (let i = 0; i < n; i++) {\n const T = lo + i / outRate;\n const x = T / CELL, k = Math.min(Math.floor(x), nCell - 2), f = x - k;\n const u0 = U[k]!, u1 = U[k + 1]!;\n if (!(Number.isFinite(u0) && Number.isFinite(u1))) { prevPos = NaN; continue; }\n const u = u0 * (1 - f) + u1 * f;\n if (u < a.start || u >= a.end) { prevPos = NaN; continue; }\n const raw = a.sourceMap ? a.sourceMap(u) : u - a.start;\n let pos = raw;\n if (a.loop) pos = ((pos % dur) + dur) % dur;\n const rate = Number.isNaN(prevPos) ? 1 : Math.abs(raw - prevPos) * outRate;\n prevPos = raw;\n if (pos < 0 || pos >= dur) continue;\n const g = gainAt(a, u) * Math.min(1, rate / 0.02);\n const sp = pos * srcRate, j = Math.floor(sp), fr = sp - j, j1 = Math.min(j + 1, left.length - 1);\n L[i] = (left[j]! * (1 - fr) + left[j1]! * fr) * g;\n R[i] = (right[j]! * (1 - fr) + right[j1]! * fr) * g;\n }\n return { L, R, at: lo };\n}\n\n/**\n * When a sound plays, in the MOVIE's time. Without a `warp` that is `start`..`end`; with one (the sound sits in a remapped composition)\n * `start` / `end` are the composition's local times, so the span is where the warp lands inside them. `total` is the movie's length.\n */\nexport function playSpan(a: Pick<AudioDescriptor, 'start' | 'end' | 'warp'>, total: number): { start: number; end: number } {\n if (!a.warp) return { start: a.start, end: a.end };\n let first = -1, last = -1;\n const n = Math.ceil(total / CELL) + 1;\n for (let k = 0; k <= n; k++) {\n const u = a.warp(k * CELL);\n if (Number.isFinite(u) && u >= a.start && u < a.end) { if (first < 0) first = k; last = k; }\n }\n return first < 0 ? { start: total, end: total } : { start: first * CELL, end: (last + 1) * CELL };\n}\n\n/**\n * The farthest local time the warp (the clock of the composition the sound sits in) reaches during the movie. A sound whose local `end` is\n * beyond it is cut short by that clock (it stops or stands still) — a different cause than the end of the movie.\n */\nexport function clockReach(a: Pick<AudioDescriptor, 'warp'>, total: number): number {\n let reach = -Infinity;\n const n = Math.ceil(total / CELL) + 1;\n for (let k = 0; k <= n; k++) {\n const u = a.warp ? a.warp(k * CELL) : k * CELL;\n if (Number.isFinite(u) && u > reach) reach = u;\n }\n return reach;\n}\n","/** In-place radix-2 FFT (re/im arrays of the same power-of-two length). */\nexport function fft(re: Float64Array, im: Float64Array): void {\n const n = re.length;\n for (let i = 1, j = 0; i < n; i++) {\n let bit = n >> 1;\n for (; j & bit; bit >>= 1) j ^= bit;\n j ^= bit;\n if (i < j) { [re[i], re[j]] = [re[j]!, re[i]!]; [im[i], im[j]] = [im[j]!, im[i]!]; }\n }\n for (let len = 2; len <= n; len <<= 1) {\n const ang = (-2 * Math.PI) / len;\n for (let i = 0; i < n; i += len) {\n for (let k = 0; k < len / 2; k++) {\n const wr = Math.cos(ang * k), wi = Math.sin(ang * k);\n const a = i + k, b = a + len / 2;\n const xr = re[b]! * wr - im[b]! * wi, xi = re[b]! * wi + im[b]! * wr;\n re[b] = re[a]! - xr; im[b] = im[a]! - xi;\n re[a] = re[a]! + xr; im[a] = im[a]! + xi;\n }\n }\n }\n}\n\nconst FRAME = 2048;\n\n/**\n * Spectral centroid (\"brightness\") in Hz of `samples[from, to)`: the power-weighted mean frequency\n * over Hann-windowed 2048-sample frames (louder frames count more). 0 for silence.\n * Rough guide: < 500 Hz dark / boomy, 1–3 kHz mid, > 4 kHz bright / airy.\n */\nexport function spectralCentroid(samples: Float32Array, sampleRate: number, from = 0, to = samples.length): number {\n let num = 0;\n let den = 0;\n const re = new Float64Array(FRAME);\n const im = new Float64Array(FRAME);\n const end = Math.min(to, samples.length);\n for (let start = Math.max(0, from); start < end; start += FRAME / 2) {\n for (let i = 0; i < FRAME; i++) {\n const k = start + i;\n const w = 0.5 - 0.5 * Math.cos((2 * Math.PI * i) / (FRAME - 1));\n re[i] = k < end ? samples[k]! * w : 0;\n im[i] = 0;\n }\n fft(re, im);\n for (let b = 1; b < FRAME / 2; b++) {\n const power = re[b]! * re[b]! + im[b]! * im[b]!;\n num += power * ((b * sampleRate) / FRAME);\n den += power;\n }\n }\n return den > 1e-9 ? Math.round(num / den / 10) * 10 : 0;\n}\n","/** Loudness of a mix, ITU-R BS.1770-4 / EBU R128, without a dependency. Pure: takes channels, returns numbers. */\nexport interface LoudnessReport { integratedLufs: number | null; truePeakDb: number | null; samplePeakDb: number; clippedSamples: number }\n\n/** Where the advice in `inspectAudio().notes` starts: one place, so the docs can point at it. */\nexport const LOUDNESS_NOTES = { quietBelow: -18, loudAbove: -9, headroomDb: -1 } as const;\n\ninterface Biquad { b: [number, number, number]; a: [number, number, number] }\n\n/** The K-weighting of BS.1770-4: a high shelf, then a 38 Hz high-pass, as biquads for any sample rate (bilinear transform). */\nfunction kWeighting(fs: number): [Biquad, Biquad] {\n const f0 = 1681.974450955533, G = 3.999843853973347, Q = 0.7071752369554196;\n const K = Math.tan(Math.PI * f0 / fs), Vh = 10 ** (G / 20), Vb = Vh ** 0.4996667741545416;\n const a0 = 1 + K / Q + K * K;\n const shelf: Biquad = {\n b: [(Vh + Vb * K / Q + K * K) / a0, 2 * (K * K - Vh) / a0, (Vh - Vb * K / Q + K * K) / a0],\n a: [1, 2 * (K * K - 1) / a0, (1 - K / Q + K * K) / a0],\n };\n const f1 = 38.13547087602444, Q1 = 0.5003270373238773, K1 = Math.tan(Math.PI * f1 / fs), a1 = 1 + K1 / Q1 + K1 * K1;\n const high: Biquad = { b: [1, -2, 1], a: [1, 2 * (K1 * K1 - 1) / a1, (1 - K1 / Q1 + K1 * K1) / a1] };\n return [shelf, high];\n}\n\nfunction filter(x: Float32Array, stages: Biquad[]): Float32Array {\n let cur = x;\n for (const { b, a } of stages) {\n const out = new Float32Array(cur.length); // Float32 between stages: half the memory, far below the measurement's resolution\n let x1 = 0, x2 = 0, y1 = 0, y2 = 0;\n for (let i = 0; i < cur.length; i++) {\n const xi = cur[i]!, y = b[0] * xi + b[1] * x1 + b[2] * x2 - a[1] * y1 - a[2] * y2;\n x2 = x1; x1 = xi; y2 = y1; y1 = y; out[i] = y;\n }\n cur = out;\n }\n return cur;\n}\n\nconst toDb = (x: number): number => Math.round(200 * Math.log10(Math.max(x, 1e-6))) / 10; // 0.1 dB, floor -120\n\nconst TP_HALF = 12, TP_BLOCK = 32;\nconst TP_KERNELS = [0.25, 0.5, 0.75].map(p => Float64Array.from({ length: 2 * TP_HALF }, (_, j) => {\n const k = j - TP_HALF + 1, x = k - p, s = x === 0 ? 1 : Math.sin(Math.PI * x) / (Math.PI * x);\n return s * (0.5 + 0.5 * Math.cos(Math.PI * x / (TP_HALF + 1)));\n}));\n/** No interpolated value can exceed (the loudest sample around it) x this: where that cannot beat the peak so far, skip the sums. */\nconst TP_GAIN = Math.max(...TP_KERNELS.map(k => k.reduce((a, v) => a + Math.abs(v), 0)));\n\n/** 4x oversampled peak (windowed-sinc interpolation, 12 samples either side): catches peaks between samples. Equal to checking every phase of every sample, only faster. */\nfunction truePeak(chans: Float32Array[]): number {\n let peak = 0;\n for (const c of chans) {\n const n = c.length;\n const blocks = Math.ceil(n / TP_BLOCK), blockMax = new Float32Array(blocks + 2); // [b + 1] is block b; the ends stay 0\n for (let i = 0; i < n; i++) { const a = Math.abs(c[i]!), b = (i / TP_BLOCK | 0) + 1; if (a > blockMax[b]!) blockMax[b] = a; if (a > peak) peak = a; }\n const padded = new Float32Array(n + 2 * TP_HALF); // zeros around, so the sums need no bounds check\n padded.set(c, TP_HALF);\n for (let i = 0; i < n; i++) {\n const b = (i / TP_BLOCK | 0) + 1;\n const local = Math.max(blockMax[b - 1]!, blockMax[b]!, blockMax[b + 1]!); // the 24 samples around i lie in these three blocks\n if (local * TP_GAIN <= peak) continue;\n for (let pi = 0; pi < 3; pi++) {\n const k = TP_KERNELS[pi]!;\n let v = 0;\n for (let j = 0; j < 2 * TP_HALF; j++) v += padded[i + 1 + j]! * k[j]!;\n const a = Math.abs(v);\n if (a > peak) peak = a;\n }\n }\n }\n return peak;\n}\n\nexport function measureLoudness(channels: Float32Array[], sampleRate: number, opts: { truePeak?: boolean } = {}): LoudnessReport {\n let samplePeak = 0, clipped = 0;\n for (const c of channels) for (let i = 0; i < c.length; i++) { const a = Math.abs(c[i]!); if (a > samplePeak) samplePeak = a; if (a >= 1) clipped++; }\n const stages = kWeighting(sampleRate);\n const weighted = channels.map(c => filter(c, stages));\n const block = Math.round(0.4 * sampleRate), hop = Math.round(0.1 * sampleRate);\n const energies: number[] = [];\n for (let s = 0; s + block <= (weighted[0]?.length ?? 0); s += hop) {\n let sum = 0;\n for (const w of weighted) { let z = 0; for (let i = s; i < s + block; i++) z += w[i]! * w[i]!; sum += z / block; } // the weight is 1 for L, R and mono\n energies.push(sum);\n }\n const lufs = (e: number): number => -0.691 + 10 * Math.log10(e);\n const absolute = energies.filter(e => e > 0 && lufs(e) > -70);\n let integrated: number | null = null;\n if (absolute.length) {\n const relative = lufs(absolute.reduce((a, b) => a + b, 0) / absolute.length) - 10;\n const gated = absolute.filter(e => lufs(e) > relative);\n if (gated.length) integrated = Math.round(10 * lufs(gated.reduce((a, b) => a + b, 0) / gated.length)) / 10;\n }\n return { integratedLufs: integrated, truePeakDb: opts.truePeak === false ? null : toDb(truePeak(channels)), samplePeakDb: toDb(samplePeak), clippedSamples: clipped };\n}\n\n/** Runs of at least `minLength` seconds where every channel stays below `thresholdDb` (default -60 dBFS). */\nexport function findSilences(channels: Float32Array[], sampleRate: number, opts: { minLength?: number; thresholdDb?: number } = {}): Array<{ from: number; to: number }> {\n const min = Math.round((opts.minLength ?? 1) * sampleRate), limit = 10 ** ((opts.thresholdDb ?? -60) / 20);\n const n = channels[0]?.length ?? 0, out: Array<{ from: number; to: number }> = [];\n let start = -1;\n const quiet = (i: number): boolean => channels.every(c => Math.abs(c[i]!) < limit);\n for (let i = 0; i <= n; i++) {\n const q = i < n && quiet(i);\n if (q && start < 0) start = i;\n if (!q && start >= 0) { if (i - start >= min) out.push({ from: Math.round(start / sampleRate * 1000) / 1000, to: Math.round(i / sampleRate * 1000) / 1000 }); start = -1; }\n }\n return out;\n}\n","import type { AudioDescriptor } from '../types';\nimport type { MusicSummary } from '../audio/music/events';\nimport { playSpan, clockReach } from './audioRemap';\nimport type { MixStats } from './AudioMixer';\nimport { spectralCentroid } from './spectrum';\nimport { findSilences, LOUDNESS_NOTES, measureLoudness } from '../audio/loudness';\n\nexport interface AudioInspectOptions {\n /** Width of each loudness window in seconds. Default 0.1. */\n window?: number;\n /** The scenes to measure one by one (`movie.inspectAudio()` fills this from the movie's named top-level compositions). Default: the whole movie as one. */\n scenes?: Array<{ name: string; start: number; end: number }>;\n}\n\n/** One sound measured ON ITS OWN (not mixed), so overlapping layers do not blur it. */\nexport interface SoundMeasure {\n /** Seconds of audio the source has (an sfx: its length; a file: the file's length). */\n length: number;\n /** Its own loudest sample, dBFS (before the layer's volume). */\n peakDb: number;\n /** Seconds from its start to its loudest sample — the moment to line up with the picture. */\n loudestAt: number;\n /** Spectral centroid in Hz: < 500 dark / boomy, 1–4 k mid, > 8 k airy / hissy. */\n brightnessHz: number;\n}\n\nexport interface AudioSourceReport {\n /** `layer \"pop-1\"` (or `unnamed audio layer`). */\n layer: string;\n /** `sfx \"pop\"`, `asset \"bgm\"`, `video \"green\"`. */\n source: string;\n /** Movie time, seconds. */\n start: number;\n end: number;\n /** Loudest sample of the MIX while this source plays, dBFS (−120 = silence). */\n peakDb: number;\n sound: SoundMeasure;\n /**\n * How far below the loudest sample of the MIX around its own loudest moment this source is, dB (0 = it is the loudest thing there; 20 = the mix is 20 dB louder, so\n * the source is hardly heard). Absent for a sound inside a remapped composition (its time is not the movie's).\n */\n belowMixDb?: number;\n /** For a `music` layer: its score read back (bars per track, what starts in each bar, the drum spans). */\n music?: MusicSummary;\n}\n\nexport interface AudioReport {\n duration: number;\n sampleRate: number;\n /** Loudest sample of the mix before limiting, dBFS. Above 0 the mix was limited. */\n peakDb: number;\n peakAt: number;\n /** Every audio layer / video soundtrack in the mix, in start order. */\n sources: AudioSourceReport[];\n /** The mix over time: one row per window, `t` = window start. */\n windows: Array<{ t: number; rmsDb: number; peakDb: number; brightnessHz: number }>;\n /** Problems worth fixing: limiting, clipping, a true peak above 0 dBTP, inaudible layers, sounds cut off by the end of the movie. Read this first. */\n issues: string[];\n /** The mix as a whole (ITU-R BS.1770 loudness; `integratedLufs` is null for a mix shorter than 0.4 s or with nothing above −70 LUFS). */\n loudness: { integratedLufs: number | null; truePeakDb: number; clippedSamples: number; silences: Array<{ from: number; to: number }> };\n /** The same measure for each scene (`silent`: nothing above −60 dBFS). */\n scenes: Array<{ name: string; from: number; to: number; lufs: number | null; peakDb: number; silent: boolean }>;\n /** When each sound starts: what a waveform marks. */\n cues: Array<{ t: number; name: string }>;\n /** Advice, not faults: a mix that is quiet or loud for web video, little headroom, silent stretches. */\n notes: string[];\n}\n\ninterface PcmLike {\n numberOfChannels: number;\n sampleRate: number;\n length: number;\n getChannelData(ch: number): Float32Array;\n}\n\n/** A short sound effect this many dB under the loudest sample of the mix around its loudest moment is hardly heard. */\nconst BURIED_DB = 12;\n/** Below this a source counts as inaudible. */\nconst INAUDIBLE_DB = -50;\n\nconst db = (x: number): number => Math.round(10 * Math.max(-120, 20 * Math.log10(x))) / 10;\nconst mono = (chans: Float32Array[]): Float32Array => {\n if (chans.length === 1) return chans[0]!;\n const out = new Float32Array(chans[0]!.length);\n for (const d of chans) for (let i = 0; i < out.length; i++) out[i] = out[i]! + d[i]! / chans.length;\n return out;\n};\n\nfunction measure(chans: Float32Array[], sampleRate: number): SoundMeasure {\n let peak = 0;\n let at = 0;\n for (const d of chans) for (let i = 0; i < d.length; i++) if (Math.abs(d[i]!) > peak) { peak = Math.abs(d[i]!); at = i; }\n return {\n length: Math.round((chans[0]!.length / sampleRate) * 1000) / 1000,\n peakDb: db(peak),\n loudestAt: Math.round((at / sampleRate) * 1000) / 1000,\n brightnessHz: spectralCentroid(mono(chans), sampleRate),\n };\n}\n\n/**\n * Measure the soundtrack so an author who cannot listen can still check it: every sound on its own\n * (length, level, loudest moment, brightness), when it plays and how loud the mix is there, the mix over\n * time, and issues. Pure: takes the mix and the list of sources that went into it.\n */\nexport function analyzeAudio(\n mix: PcmLike | null, sources: AudioDescriptor[], stats: MixStats | null, movieDuration: number, opts: AudioInspectOptions = {},\n): AudioReport {\n // A sound inside a remapped composition has `start` / `end` in that composition's local time: report when it plays in the MOVIE's time.\n const warped = new Map<AudioDescriptor, { localEnd: number; reach: number }>();\n sources = sources.map(s => {\n if (!s.warp) return s;\n const moved = { ...s, ...playSpan(s, movieDuration) };\n warped.set(moved, { localEnd: s.end, reach: clockReach(s, movieDuration) });\n return moved;\n });\n if (!mix) {\n return { duration: movieDuration, sampleRate: 0, peakDb: -120, peakAt: 0, sources: [], windows: [],\n loudness: { integratedLufs: null, truePeakDb: -120, clippedSamples: 0, silences: [] }, scenes: [], cues: [], notes: [], issues: ['no audio: the movie has no audio layers (sound effects need no files: { type: \"audio\", sfx: \"pop\", at: 1 })'] };\n }\n const sr = mix.sampleRate;\n const chans = Array.from({ length: mix.numberOfChannels }, (_, c) => mix.getChannelData(c));\n const mixMono = mono(chans);\n const peakIn = (t0: number, t1: number): number => {\n let p = 0;\n const i1 = Math.min(mix.length, Math.ceil(t1 * sr));\n for (const d of chans) for (let i = Math.max(0, Math.floor(t0 * sr)); i < i1; i++) p = Math.max(p, Math.abs(d[i]!));\n return p;\n };\n\n // at least 10 ms: a smaller window would ask for a spectrum per sample\n const win = opts.window && opts.window > 0 ? Math.max(0.01, opts.window) : 0.1;\n const windows: AudioReport['windows'] = [];\n for (let t = 0; t < movieDuration - 1e-9; t += win) {\n const i0 = Math.floor(t * sr);\n const i1 = Math.min(mix.length, Math.floor((t + win) * sr));\n let sum = 0;\n let peak = 0;\n for (const d of chans) {\n for (let i = i0; i < i1; i++) {\n sum += d[i]! * d[i]!;\n peak = Math.max(peak, Math.abs(d[i]!));\n }\n }\n const n = Math.max(1, (i1 - i0) * chans.length);\n windows.push({ t: Math.round(t * 1000) / 1000, rmsDb: db(Math.sqrt(sum / n)), peakDb: db(peak), brightnessHz: spectralCentroid(mixMono, sr, i0, i1) });\n }\n\n const issues: string[] = [];\n if (stats && stats.peak > 1) {\n issues.push(`the mix peaks at ${db(stats.peak)} dBFS at ${stats.peakAt.toFixed(2)}s and was limited — lower the volume of the layers playing there`);\n }\n const rendered = new Map<string, SoundMeasure>();\n const soundOf = (s: AudioDescriptor): SoundMeasure => {\n if (s.synth) {\n let m = rendered.get(s.synth.key);\n if (!m) rendered.set(s.synth.key, (m = measure(s.synth.render(sr), sr)));\n return m;\n }\n const b = s.buffer!;\n return measure(Array.from({ length: b.numberOfChannels }, (_, c) => b.getChannelData(c)), b.sampleRate);\n };\n const scoreNotes: string[] = [];\n const normGain = stats?.loudness?.gainDb ?? 0;\n const buried: Array<{ layer: string; source: string; ownDb: number; topVolume: number; around: number; belowMixDb: number; at: number }> = [];\n const list = [...sources].sort((a, b) => a.start - b.start).map(s => {\n const layer = s.layer ?? 'an audio layer';\n const source = s.source ?? 'audio';\n const peakDb = db(peakIn(s.start, Math.min(s.end, movieDuration)));\n const sound = soundOf(s);\n // The layer's OWN level: the sound's peak at the loudest volume it ever has. Judging the mix instead would\n // hide a nearly silent layer under a loud one.\n const topVolume = Math.max(s.initialVolume ?? 1, ...(s.volumeKeyframes ?? []).map(k => k.value));\n const ownDb = db(Math.pow(10, sound.peakDb / 20) * Math.max(0, topVolume));\n if (s.start >= movieDuration) {\n issues.push(`${layer} (${source}) starts at ${s.start.toFixed(2)}s, after the movie ends (${movieDuration}s)`);\n } else if (ownDb < INAUDIBLE_DB || peakDb < INAUDIBLE_DB) {\n issues.push(`${layer} (${source}) is inaudible from ${s.start.toFixed(2)}s to ${s.end.toFixed(2)}s (its own peak ${ownDb} dBFS at volume ${topVolume}; mix peak there ${peakDb} dBFS) — check its volume`);\n }\n const clock = warped.get(s);\n if (s.synth && s.start < movieDuration) {\n if (clock) {\n // inside a remapped composition: it is cut short when the composition's clock never gets to the end of the sound\n if (clock.reach < clock.localEnd - 1e-3) {\n issues.push(`${layer} (${source}) is cut off: its composition's clock only reaches ${clock.reach.toFixed(2)}s (the sound is ${clock.localEnd.toFixed(2)}s long), because the clock stops or the movie ends first — give the layer a shorter duration, or let the clock run on`);\n }\n } else if (s.end > movieDuration + 1e-6) {\n issues.push(`${layer} (${source}) is cut off by the end of the movie at ${movieDuration}s (it runs to ${s.end.toFixed(2)}s)`);\n }\n }\n // A written score (`music`) much longer than what plays: said as a NOTE (a trial: 19.2 s of music under a 10 s film; the read-back showed it but nothing said it).\n // Not a fault: music that outlasts the movie ends with it (see Audio.ts). A tail under 2 s or 15 % over is only the reverb tail; a looping layer is meant to be cut.\n if (s.music && !s.loop && s.start < movieDuration && !clock) {\n const plays = Math.min(s.end, movieDuration) - s.start;\n const over = s.music.seconds - plays;\n if (over > 2 && over / s.music.seconds > 0.15) scoreNotes.push(`${layer} (${source}): the score is ${Math.round(s.music.seconds * 10) / 10}s long but only ${Math.round(plays * 100) / 100}s of it play, so it is cut off mid-phrase. Write fewer bars (or a faster tempo) so it ends with the movie`);\n }\n // A track a bar or more shorter than the longest one is silent from its end on: usually a miscount of the beats (a trial: three agents; only the read-back showed it). A note, not a fault: a bell may come and go on purpose.\n if (s.music && s.music.tracks.length > 1) {\n const longest = Math.max(...s.music.tracks.map(t => t.beats));\n const short = s.music.tracks.filter(t => t.beats <= longest - s.music!.meter);\n if (short.length > 0) {\n scoreNotes.push(`${layer} (${source}): ${short.slice(0, 3).map(t => `the ${t.inst} track is ${Math.round(t.beats * 100) / 100} beats long against ${longest} for the longest and falls silent from beat ${Math.round(t.beats * 100) / 100} (bar ${Math.floor(t.beats / s.music!.meter) + 1})`).join('; ')}. If that is a miscount, add its missing beats (a rest is \\`_:4\\`); if it is on purpose, ignore this`);\n } else if (s.music.beats >= longest + s.music.meter) {\n scoreNotes.push(`${layer} (${source}): the drums run to beat ${s.music.beats}, past the end of every track at beat ${longest}: from there only the drums play`);\n }\n }\n // Buried: the sound's own peak against the mix around its loudest moment. A short sound effect 12 dB or more under what plays with it is a hit nobody hears.\n // (A layer with its own speed or `time` plays the sound at another pace: its loudest moment is not at start + loudestAt, so it is left out.)\n let belowMixDb: number | undefined;\n if (!clock && !s.sourceMap && s.start < movieDuration && ownDb > INAUDIBLE_DB) {\n const at = s.start + sound.loudestAt;\n const around = db(peakIn(Math.max(0, at - 0.05), Math.min(movieDuration, at + 0.05)));\n belowMixDb = Math.max(0, Math.round((around - (ownDb + normGain)) * 10) / 10); // (the mix may have been raised to a loudness target: the sound's own peak is read before that, so it gets the same gain)\n if (!s.music && source.startsWith('sfx') && sound.length <= 4 && belowMixDb >= BURIED_DB) {\n buried.push({ layer, source, ownDb: Math.round((ownDb + normGain) * 10) / 10, topVolume, around, belowMixDb, at: Math.round(at * 100) / 100 });\n }\n }\n return { layer, source, start: s.start, end: s.end, peakDb, sound, ...(belowMixDb !== undefined ? { belowMixDb } : {}), ...(s.music ? { music: s.music } : {}) };\n });\n\n // ONE note for the movie (an accent that is meant to be subtle is common: the numbers are in every source's `belowMixDb`): how many, and the worst\n if (buried.length > 0) {\n const worst = buried.reduce((a, b) => (b.belowMixDb > a.belowMixDb ? b : a));\n // layers with no name all carry the same label, so they are counted one by one; a named layer that plays many times counts once\n const distinct = new Set(buried.map(x => (x.layer === 'an audio layer' ? `#${buried.indexOf(x)}` : x.layer) + x.source)).size;\n scoreNotes.push(`${distinct === 1 ? '1 sound effect is' : `${distinct} sound effects are`} buried (${BURIED_DB} dB or more under the loudest sample of the mix around its loudest moment${buried.length > distinct ? `, ${buried.length} times in all` : ''}): the worst is ${worst.layer} (${worst.source}), its own peak ${worst.ownDb} dBFS at volume ${Math.round(worst.topVolume * 100) / 100} against ${worst.around} dBFS at ${worst.at} s, ${worst.belowMixDb} dB louder. If it is meant to be heard, raise its volume (this one to about ${Math.round(Math.min(4, worst.topVolume * 10 ** ((worst.belowMixDb - 3) / 20)) * 10) / 10}, which puts it about 3 dB under the mix; an accent over music needs 2 to 3) or lower what plays there; every source's belowMixDb has its own number`);\n }\n // the mix as a whole, scene by scene, and the advice\n const whole = measureLoudness(chans, sr);\n const silences = findSilences(chans, sr, { minLength: 1 });\n if (whole.clippedSamples > 0) issues.push(`${whole.clippedSamples} sample(s) of the mix are at full scale (clipped) — lower the volume of the layers that play loudest`);\n if (whole.truePeakDb! > 0) issues.push(`the mix's true peak is ${whole.truePeakDb} dBTP: it will distort when the video is encoded — lower the volume`);\n const notes: string[] = [...scoreNotes];\n if (whole.integratedLufs !== null) {\n if (whole.integratedLufs < LOUDNESS_NOTES.quietBelow && !stats?.loudness) notes.push(`the mix is quiet for web video: ${whole.integratedLufs} LUFS (typical −14 to −16); raise the volume, or ask for a loudness: movie.init({ loudness: -16 }), if that is not on purpose`);\n else if (whole.integratedLufs > LOUDNESS_NOTES.loudAbove) notes.push(`the mix is loud: ${whole.integratedLufs} LUFS (typical web video −14 to −16)`);\n }\n if (whole.truePeakDb! > LOUDNESS_NOTES.headroomDb && whole.truePeakDb! <= 0) notes.push(`the true peak is ${whole.truePeakDb} dBTP: little headroom left for encoding (keep it below ${LOUDNESS_NOTES.headroomDb} dBTP)`);\n const asked = stats?.loudness;\n if (asked) {\n // which layers play at a moment (the ones a peak there is made of); an unnamed layer is told by what it plays\n const playingAt = (t: number): string => {\n const at = sources.filter(x => x.start <= t && t < Math.min(x.end, movieDuration)).map(x => (x.layer && x.layer !== 'an audio layer' ? `${x.layer}${x.source ? ` (${x.source})` : ''}` : x.source ?? 'audio'));\n return [...new Set(at)].slice(0, 6).join(', ') || 'nothing that is listed';\n };\n if (asked.gainDb !== 0) notes.push(`the whole mix was ${asked.gainDb > 0 ? 'raised' : 'lowered'} by ${Math.abs(asked.gainDb)} dB ${asked.limited ? 'toward' : 'to reach'} ${asked.target} LUFS (init({ loudness: ${asked.target} }))`);\n if (asked.limited) {\n notes.push(`the mix could not reach ${asked.target} LUFS: it stopped at ${asked.reachedLufs} LUFS because its loudest moment${asked.limiterAt !== undefined ? ` (at ${asked.limiterAt} s, where ${playingAt(asked.limiterAt)} play)` : ''} would need more than ${asked.limiterDb ?? 10} dB of limiting to stay under -1 dBTP; lower the sound that peaks there (a click or hit on top of a quiet bed), or ask for less`);\n } else if (asked.limiterDb !== undefined && asked.limiterDb >= 3) {\n notes.push(`a limiter took up to ${asked.limiterDb} dB off the loudest peak (at ${asked.limiterAt} s, where ${playingAt(asked.limiterAt ?? 0)} play) so that the mix reaches ${asked.target} LUFS without passing -1 dBTP: if that sound should hit harder, lower what plays under it`);\n }\n }\n for (const q of silences.slice(0, 3)) notes.push(`the mix is silent from ${q.from} s to ${q.to} s (${Math.round((q.to - q.from) * 10) / 10} s)`);\n if (silences.length > 3) notes.push(`and ${silences.length - 3} more silent stretch(es) (loudness.silences lists them all)`);\n const sceneList = (opts.scenes && opts.scenes.length ? opts.scenes : [{ name: 'movie', start: 0, end: movieDuration }]).map(sc => {\n const a = Math.max(0, Math.round(sc.start * sr)), b = Math.min(mix.length, Math.round(sc.end * sr));\n const part = chans.map(c => c.subarray(a, Math.max(a, b)));\n const m = measureLoudness(part, sr, { truePeak: false }); // the true peak is the slow part and is only wanted for the whole mix\n return { name: sc.name, from: sc.start, to: sc.end, lufs: m.integratedLufs, peakDb: m.samplePeakDb, silent: m.samplePeakDb < -60 };\n });\n\n return {\n duration: movieDuration,\n sampleRate: sr,\n peakDb: db(stats?.peak ?? peakIn(0, movieDuration)),\n peakAt: stats?.peakAt ?? 0,\n sources: list,\n windows,\n issues,\n loudness: { integratedLufs: whole.integratedLufs, truePeakDb: whole.truePeakDb ?? -120, clippedSamples: whole.clippedSamples, silences },\n scenes: sceneList,\n cues: list.map(s => ({ t: s.start, name: `${s.layer} (${s.source})` })),\n notes,\n };\n}\n\n/** What `inspectSound` says about a sound. */\nexport interface SoundReport {\n /** Seconds, the sample rate and the number of channels of what was measured. */\n duration: number;\n sampleRate: number;\n channels: number;\n /** The loudest sample, dBFS. */\n peakDb: number;\n /** ITU-R BS.1770 loudness, true peak, samples at full scale and silences, as `check` reports them for a movie's mix. */\n loudness: AudioReport['loudness'];\n /** The level over time: the RMS of each window (`window` seconds, 0.1 by default), dBFS. */\n windows: Array<{ t: number; rmsDb: number }>;\n /** Advice (quiet or loud for web video, little headroom, silent stretches) and what would distort when encoded. */\n notes: string[];\n}\n\n/**\n * Measure the sound of any file, not only a movie's mix: its loudness, peak, clipping, silences and level over time, with the same code `movie.inspectAudio()` uses, so\n * `check` and the `pixi-effects sound` command give the same numbers for the same samples. Takes decoded PCM (an AudioBuffer works).\n */\nexport function inspectSound(pcm: PcmLike, opts: { window?: number } = {}): SoundReport {\n const duration = pcm.length / pcm.sampleRate;\n const r = analyzeAudio(pcm, [], null, duration, opts.window ? { window: opts.window } : {});\n // a file that ends a few ms into a window would show that sliver as a row (a 36 ms tail of encoder padding reads as −90 dBFS): a last window under half the width is dropped\n const width = opts.window ?? 0.1;\n let windows = r.windows;\n if (windows.length > 1 && duration - windows[windows.length - 1]!.t < width / 2) windows = windows.slice(0, -1);\n return {\n duration: Math.round(duration * 1000) / 1000, sampleRate: pcm.sampleRate, channels: pcm.numberOfChannels, peakDb: r.peakDb,\n loudness: r.loudness, windows: windows.map(w => ({ t: w.t, rmsDb: w.rmsDb })), notes: [...r.notes, ...r.issues],\n };\n}\n\n"]}
|