@aethervtt/shard-audio-editor 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +2 -2
- package/dist/dom.d.ts +6 -0
- package/dist/dom.js +11 -0
- package/dist/drawing.d.ts +2 -0
- package/dist/drawing.js +12 -0
- package/dist/icons.d.ts +1 -0
- package/dist/icons.js +29 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/memory.d.ts +13 -0
- package/dist/memory.js +93 -0
- package/dist/methods.d.ts +4 -0
- package/dist/methods.js +79 -0
- package/dist/model.d.ts +61 -0
- package/dist/model.js +560 -0
- package/dist/panels/common.d.ts +33 -0
- package/dist/panels/common.js +1556 -0
- package/dist/panels/diagnostics.d.ts +2 -0
- package/dist/panels/diagnostics.js +7 -0
- package/dist/panels/event.d.ts +2 -0
- package/dist/panels/event.js +7 -0
- package/dist/panels/library.d.ts +2 -0
- package/dist/panels/library.js +7 -0
- package/dist/panels/mixer.d.ts +2 -0
- package/dist/panels/mixer.js +7 -0
- package/dist/panels/music.d.ts +2 -0
- package/dist/panels/music.js +7 -0
- package/dist/panels/parameters.d.ts +2 -0
- package/dist/panels/parameters.js +7 -0
- package/dist/preview-export.d.ts +68 -0
- package/dist/preview-export.js +143 -0
- package/dist/preview-render.d.ts +2 -0
- package/dist/preview-render.js +6 -0
- package/dist/preview-runtime.d.ts +114 -0
- package/dist/preview-runtime.js +414 -0
- package/dist/preview.d.ts +25 -0
- package/dist/preview.js +296 -0
- package/dist/project.d.ts +15 -0
- package/dist/project.js +332 -0
- package/dist/schema.d.ts +22 -0
- package/dist/schema.js +155 -0
- package/dist/styles.d.ts +2 -0
- package/dist/styles.js +192 -0
- package/dist/types.d.ts +113 -0
- package/dist/types.js +0 -0
- package/dist/waveform.d.ts +16 -0
- package/dist/waveform.js +154 -0
- package/package.json +83 -4
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type JsonObject } from '@aethervtt/shard-core';
|
|
2
|
+
import type { AudioAssetCatalog, AudioDocument, AudioDocumentStore } from './types.js';
|
|
3
|
+
/** Resolves the existing render-plan format exclusively through the host catalog. */
|
|
4
|
+
export declare function exportAudioEditorPreview(host: {
|
|
5
|
+
assets: AudioAssetCatalog;
|
|
6
|
+
documents: AudioDocumentStore;
|
|
7
|
+
}, document: AudioDocument, overrides: JsonObject, signal?: AbortSignal): Promise<{
|
|
8
|
+
wav: Uint8Array<ArrayBufferLike>;
|
|
9
|
+
measured: {
|
|
10
|
+
peak: number;
|
|
11
|
+
rms: number;
|
|
12
|
+
clipped: number;
|
|
13
|
+
nonfinite: number;
|
|
14
|
+
sampleRate: number;
|
|
15
|
+
channels: number;
|
|
16
|
+
frames: number;
|
|
17
|
+
duration: number;
|
|
18
|
+
};
|
|
19
|
+
trace: {
|
|
20
|
+
zones: {
|
|
21
|
+
at: number;
|
|
22
|
+
buses: string[];
|
|
23
|
+
}[];
|
|
24
|
+
events: {
|
|
25
|
+
entries: import("@aethervtt/shard-audio").AudioEventTraceEntry[];
|
|
26
|
+
oldestCursor: number;
|
|
27
|
+
nextCursor: number;
|
|
28
|
+
truncated: boolean;
|
|
29
|
+
};
|
|
30
|
+
music: {
|
|
31
|
+
entries: import("@aethervtt/shard-audio").AudioEventTraceEntry[];
|
|
32
|
+
oldestCursor: number;
|
|
33
|
+
nextCursor: number;
|
|
34
|
+
truncated: boolean;
|
|
35
|
+
};
|
|
36
|
+
controls: {
|
|
37
|
+
index: number;
|
|
38
|
+
requestedAt: number;
|
|
39
|
+
appliedAt: number;
|
|
40
|
+
}[];
|
|
41
|
+
sources: {
|
|
42
|
+
id: number;
|
|
43
|
+
presentation: Readonly<import("@aethervtt/shard-platform").AudioPresentation> | undefined;
|
|
44
|
+
}[];
|
|
45
|
+
controlRate: number;
|
|
46
|
+
quantumFrames: number;
|
|
47
|
+
};
|
|
48
|
+
capabilities: Readonly<import("@aethervtt/shard-platform").AudioCapabilities>;
|
|
49
|
+
backend: {
|
|
50
|
+
kind: string;
|
|
51
|
+
userAgent: string;
|
|
52
|
+
sampleRate: number;
|
|
53
|
+
};
|
|
54
|
+
seed: number;
|
|
55
|
+
assets: {
|
|
56
|
+
id: string;
|
|
57
|
+
scope: string;
|
|
58
|
+
revision: string;
|
|
59
|
+
type: string;
|
|
60
|
+
}[];
|
|
61
|
+
plan: JsonObject;
|
|
62
|
+
document: {
|
|
63
|
+
id: string;
|
|
64
|
+
revision: string;
|
|
65
|
+
type: string;
|
|
66
|
+
draft: JsonObject;
|
|
67
|
+
};
|
|
68
|
+
}>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { AudioPreviewPlan, audioClip, validateAudioPreview } from "@aethervtt/shard-audio";
|
|
2
|
+
import {
|
|
3
|
+
ShardError
|
|
4
|
+
} from "@aethervtt/shard-core";
|
|
5
|
+
import { renderAudioEditorBundle } from "./preview-render.js";
|
|
6
|
+
import { getAudioEditorSchema, validateAudioDocument } from "./schema.js";
|
|
7
|
+
async function exportAudioEditorPreview(host, document, overrides, signal) {
|
|
8
|
+
const assets = [];
|
|
9
|
+
const revisions = [];
|
|
10
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
11
|
+
const pending = /* @__PURE__ */ new Set();
|
|
12
|
+
const scope = host.assets.scope;
|
|
13
|
+
const docsScope = host.documents.scope;
|
|
14
|
+
let serial = 0;
|
|
15
|
+
const check = () => {
|
|
16
|
+
if (signal?.aborted)
|
|
17
|
+
throw new ShardError("audio-editor/disposed", "Preview export was cancelled");
|
|
18
|
+
if (scope !== host.assets.scope || docsScope !== host.documents.scope)
|
|
19
|
+
throw new ShardError("audio-editor/asset-unavailable", "Host scope changed during export");
|
|
20
|
+
};
|
|
21
|
+
async function resolve(id, type) {
|
|
22
|
+
check();
|
|
23
|
+
const key = JSON.stringify([type, id]);
|
|
24
|
+
const existing = resolved.get(key);
|
|
25
|
+
if (existing) return existing;
|
|
26
|
+
if (pending.has(key))
|
|
27
|
+
throw new ShardError("audio-editor/invalid-document", "Export dependency cycle", { path: id });
|
|
28
|
+
pending.add(key);
|
|
29
|
+
const number = ++serial;
|
|
30
|
+
const guid = `editor-preview:${number}`;
|
|
31
|
+
const path = `editor-preview/${number}`;
|
|
32
|
+
const ref = { type, guid, path };
|
|
33
|
+
let value;
|
|
34
|
+
if (type === "AudioClip") {
|
|
35
|
+
const entry = await host.assets.get(id, signal);
|
|
36
|
+
if (entry.type !== type)
|
|
37
|
+
throw new ShardError(
|
|
38
|
+
"audio-editor/asset-unavailable",
|
|
39
|
+
"Export reference is not an audio clip",
|
|
40
|
+
{ path: id }
|
|
41
|
+
);
|
|
42
|
+
const content = await host.assets.resolve(id, signal);
|
|
43
|
+
try {
|
|
44
|
+
check();
|
|
45
|
+
if (!content.bytes)
|
|
46
|
+
throw new ShardError(
|
|
47
|
+
"audio-editor/unsupported-capability",
|
|
48
|
+
"Offline export requires authorized encoded bytes",
|
|
49
|
+
{ path: id }
|
|
50
|
+
);
|
|
51
|
+
if (content.settings?.mode === "stream")
|
|
52
|
+
throw new ShardError(
|
|
53
|
+
"audio-editor/unsupported-capability",
|
|
54
|
+
"Offline export requires decoded sources",
|
|
55
|
+
{
|
|
56
|
+
path: id,
|
|
57
|
+
hint: "Choose a decoded catalog asset for deterministic offline rendering."
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
value = audioClip(Uint8Array.from(content.bytes), {
|
|
61
|
+
...content.settings,
|
|
62
|
+
id: JSON.stringify([scope, id, entry.revision])
|
|
63
|
+
});
|
|
64
|
+
revisions.push({ id, scope, revision: entry.revision, type });
|
|
65
|
+
} finally {
|
|
66
|
+
content.release?.();
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
const source = id === document.id ? document : await host.documents.read(id, signal);
|
|
70
|
+
if (source.type !== type)
|
|
71
|
+
throw new ShardError("audio-editor/invalid-document", `Expected ${type} document`, {
|
|
72
|
+
path: id
|
|
73
|
+
});
|
|
74
|
+
const checked = await validateAudioDocument(source, host.documents, host.assets, signal);
|
|
75
|
+
if (checked.errors.length) throw checked.errors[0];
|
|
76
|
+
const schema = getAudioEditorSchema(source.type);
|
|
77
|
+
value = await fields(schema.fields, schema.deserialize(checked.effective.value));
|
|
78
|
+
revisions.push({ id, scope: docsScope, revision: source.revision, type });
|
|
79
|
+
}
|
|
80
|
+
check();
|
|
81
|
+
assets.push({ guid, path, type, value });
|
|
82
|
+
resolved.set(key, ref);
|
|
83
|
+
pending.delete(key);
|
|
84
|
+
return ref;
|
|
85
|
+
}
|
|
86
|
+
async function field(definition, value) {
|
|
87
|
+
if (definition.kind === "handle" && value) {
|
|
88
|
+
const ref = value;
|
|
89
|
+
const id = ref.guid ?? ref.path;
|
|
90
|
+
if (!id)
|
|
91
|
+
throw new ShardError("audio-editor/asset-unavailable", "Export reference has no host ID");
|
|
92
|
+
return resolve(id, definition.assetType);
|
|
93
|
+
}
|
|
94
|
+
if (definition.kind === "list" && Array.isArray(value)) {
|
|
95
|
+
const items = [];
|
|
96
|
+
for (const item of value) items.push(await field(definition.item, item));
|
|
97
|
+
return items;
|
|
98
|
+
}
|
|
99
|
+
if (definition.kind === "struct")
|
|
100
|
+
return fields(definition.fields, value);
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
async function fields(definitions, values) {
|
|
104
|
+
const result2 = { ...values };
|
|
105
|
+
for (const [name, definition] of Object.entries(definitions))
|
|
106
|
+
result2[name] = await field(definition, result2[name]);
|
|
107
|
+
return result2;
|
|
108
|
+
}
|
|
109
|
+
const root = { guid: document.id };
|
|
110
|
+
const defaults = {
|
|
111
|
+
duration: 3,
|
|
112
|
+
...document.type === "audio/AudioEvent" ? { events: [{ id: "audition", asset: root, at: 0 }] } : {},
|
|
113
|
+
...document.type === "audio/MusicProgram" ? { music: [{ id: "audition", asset: root, at: 0 }] } : {},
|
|
114
|
+
...document.type === "audio/AudioMixer" ? { mixer: root } : {}
|
|
115
|
+
};
|
|
116
|
+
const raw = { ...defaults, ...overrides };
|
|
117
|
+
const errors = AudioPreviewPlan.validate(raw);
|
|
118
|
+
if (errors.length) throw errors[0];
|
|
119
|
+
const plan = AudioPreviewPlan.deserialize(raw);
|
|
120
|
+
validateAudioPreview(plan);
|
|
121
|
+
const resolvedPlan = await fields(AudioPreviewPlan.fields, plan);
|
|
122
|
+
check();
|
|
123
|
+
const result = await renderAudioEditorBundle(
|
|
124
|
+
{ plan: AudioPreviewPlan.serialize(resolvedPlan), assets },
|
|
125
|
+
{ signal }
|
|
126
|
+
);
|
|
127
|
+
check();
|
|
128
|
+
return {
|
|
129
|
+
...result,
|
|
130
|
+
seed: plan.seed,
|
|
131
|
+
assets: revisions,
|
|
132
|
+
plan: AudioPreviewPlan.serialize(plan),
|
|
133
|
+
document: {
|
|
134
|
+
id: document.id,
|
|
135
|
+
revision: document.revision,
|
|
136
|
+
type: document.type,
|
|
137
|
+
draft: document.value
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
export {
|
|
142
|
+
exportAudioEditorPreview
|
|
143
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { AudioBackend } from '@aethervtt/shard-platform';
|
|
2
|
+
import type { AudioAssetCatalog, AudioDocument, AudioDocumentStore } from './types.js';
|
|
3
|
+
/** Internal runtime is loaded only by explicit audition, and never opens a project filesystem. */
|
|
4
|
+
export declare function createPreviewRuntime(options: {
|
|
5
|
+
assets: AudioAssetCatalog;
|
|
6
|
+
documents: AudioDocumentStore;
|
|
7
|
+
backend?: AudioBackend;
|
|
8
|
+
createBackend?: () => AudioBackend;
|
|
9
|
+
}): Promise<{
|
|
10
|
+
unlock: () => void;
|
|
11
|
+
play(document: AudioDocument, signal?: AbortSignal): Promise<{
|
|
12
|
+
lease: number;
|
|
13
|
+
prepared: boolean;
|
|
14
|
+
instance?: undefined;
|
|
15
|
+
transport?: undefined;
|
|
16
|
+
configured?: undefined;
|
|
17
|
+
snapshot?: undefined;
|
|
18
|
+
} | {
|
|
19
|
+
lease?: undefined;
|
|
20
|
+
instance: number;
|
|
21
|
+
transport?: undefined;
|
|
22
|
+
configured?: undefined;
|
|
23
|
+
snapshot?: undefined;
|
|
24
|
+
prepared?: undefined;
|
|
25
|
+
} | {
|
|
26
|
+
lease?: undefined;
|
|
27
|
+
instance?: undefined;
|
|
28
|
+
transport: number;
|
|
29
|
+
configured?: undefined;
|
|
30
|
+
snapshot?: undefined;
|
|
31
|
+
prepared?: undefined;
|
|
32
|
+
} | {
|
|
33
|
+
lease?: undefined;
|
|
34
|
+
instance?: undefined;
|
|
35
|
+
transport?: undefined;
|
|
36
|
+
configured: boolean;
|
|
37
|
+
snapshot?: undefined;
|
|
38
|
+
prepared?: undefined;
|
|
39
|
+
} | {
|
|
40
|
+
lease?: undefined;
|
|
41
|
+
instance?: undefined;
|
|
42
|
+
transport?: undefined;
|
|
43
|
+
configured?: undefined;
|
|
44
|
+
snapshot: number;
|
|
45
|
+
prepared?: undefined;
|
|
46
|
+
} | {
|
|
47
|
+
lease?: undefined;
|
|
48
|
+
instance?: undefined;
|
|
49
|
+
transport?: undefined;
|
|
50
|
+
configured?: undefined;
|
|
51
|
+
snapshot?: undefined;
|
|
52
|
+
prepared: boolean;
|
|
53
|
+
}>;
|
|
54
|
+
command(name: string, params: Record<string, unknown>): unknown;
|
|
55
|
+
inspect(): {
|
|
56
|
+
backend: string;
|
|
57
|
+
capabilities: Readonly<import("@aethervtt/shard-platform").AudioCapabilities> | null;
|
|
58
|
+
memory: import("@aethervtt/shard-platform").AudioMemory | null;
|
|
59
|
+
context: import("@aethervtt/shard-platform").AudioContextState;
|
|
60
|
+
listener: {
|
|
61
|
+
entity: number;
|
|
62
|
+
path: string | null;
|
|
63
|
+
position: number[];
|
|
64
|
+
} | null;
|
|
65
|
+
buses: {
|
|
66
|
+
name: string;
|
|
67
|
+
volume: number;
|
|
68
|
+
userVolume: number;
|
|
69
|
+
userMuted: boolean;
|
|
70
|
+
muted: boolean;
|
|
71
|
+
parent: string;
|
|
72
|
+
gain: number;
|
|
73
|
+
ducked: number;
|
|
74
|
+
duck?: import("@aethervtt/shard-audio").AudioDuck | undefined;
|
|
75
|
+
}[];
|
|
76
|
+
voices: {
|
|
77
|
+
voice: number;
|
|
78
|
+
presentation: Readonly<import("@aethervtt/shard-platform").AudioPresentation> | null;
|
|
79
|
+
clip: string | null;
|
|
80
|
+
entity: number | null;
|
|
81
|
+
path: string | null;
|
|
82
|
+
bus: string;
|
|
83
|
+
problem?: string | undefined;
|
|
84
|
+
state: import("@aethervtt/shard-audio").VoiceState;
|
|
85
|
+
gain: number;
|
|
86
|
+
pan: number;
|
|
87
|
+
distance: number | null;
|
|
88
|
+
position: number[] | null;
|
|
89
|
+
time: number;
|
|
90
|
+
elapsed: number;
|
|
91
|
+
duration: number | null;
|
|
92
|
+
loop: boolean;
|
|
93
|
+
pitch: number;
|
|
94
|
+
priority: number;
|
|
95
|
+
}[];
|
|
96
|
+
counts: {
|
|
97
|
+
active: number;
|
|
98
|
+
virtual: number;
|
|
99
|
+
pending: number;
|
|
100
|
+
maxVoices: number;
|
|
101
|
+
maxVoicesPerClip: number;
|
|
102
|
+
dropped: number;
|
|
103
|
+
};
|
|
104
|
+
auditionError?: {
|
|
105
|
+
code: string;
|
|
106
|
+
message: string;
|
|
107
|
+
hint?: string;
|
|
108
|
+
path?: string;
|
|
109
|
+
details?: unknown[];
|
|
110
|
+
} | undefined;
|
|
111
|
+
};
|
|
112
|
+
stop: () => void;
|
|
113
|
+
dispose(): Promise<void> | undefined;
|
|
114
|
+
}>;
|