@aethervtt/shard-audio-editor 0.0.0-stage → 0.2.1

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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -2
  3. package/dist/dom.d.ts +6 -0
  4. package/dist/dom.js +11 -0
  5. package/dist/drawing.d.ts +2 -0
  6. package/dist/drawing.js +12 -0
  7. package/dist/icons.d.ts +1 -0
  8. package/dist/icons.js +29 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.js +2 -0
  11. package/dist/memory.d.ts +13 -0
  12. package/dist/memory.js +93 -0
  13. package/dist/methods.d.ts +4 -0
  14. package/dist/methods.js +79 -0
  15. package/dist/model.d.ts +61 -0
  16. package/dist/model.js +560 -0
  17. package/dist/panels/common.d.ts +33 -0
  18. package/dist/panels/common.js +1556 -0
  19. package/dist/panels/diagnostics.d.ts +2 -0
  20. package/dist/panels/diagnostics.js +7 -0
  21. package/dist/panels/event.d.ts +2 -0
  22. package/dist/panels/event.js +7 -0
  23. package/dist/panels/library.d.ts +2 -0
  24. package/dist/panels/library.js +7 -0
  25. package/dist/panels/mixer.d.ts +2 -0
  26. package/dist/panels/mixer.js +7 -0
  27. package/dist/panels/music.d.ts +2 -0
  28. package/dist/panels/music.js +7 -0
  29. package/dist/panels/parameters.d.ts +2 -0
  30. package/dist/panels/parameters.js +7 -0
  31. package/dist/preview-export.d.ts +68 -0
  32. package/dist/preview-export.js +143 -0
  33. package/dist/preview-render.d.ts +2 -0
  34. package/dist/preview-render.js +6 -0
  35. package/dist/preview-runtime.d.ts +114 -0
  36. package/dist/preview-runtime.js +414 -0
  37. package/dist/preview.d.ts +25 -0
  38. package/dist/preview.js +296 -0
  39. package/dist/project.d.ts +15 -0
  40. package/dist/project.js +332 -0
  41. package/dist/schema.d.ts +22 -0
  42. package/dist/schema.js +155 -0
  43. package/dist/styles.d.ts +2 -0
  44. package/dist/styles.js +192 -0
  45. package/dist/types.d.ts +113 -0
  46. package/dist/types.js +0 -0
  47. package/dist/waveform.d.ts +16 -0
  48. package/dist/waveform.js +154 -0
  49. package/package.json +83 -4
@@ -0,0 +1,154 @@
1
+ import { ShardError } from "@aethervtt/shard-core";
2
+ async function browserDecode(bytes) {
3
+ if (typeof OfflineAudioContext === "undefined")
4
+ throw new ShardError(
5
+ "audio-editor/unsupported-capability",
6
+ "Waveform decoding is unavailable",
7
+ {
8
+ hint: "Supply a waveform decoder in this host."
9
+ }
10
+ );
11
+ const context = new OfflineAudioContext(1, 1, 48e3);
12
+ const buffer = await context.decodeAudioData(Uint8Array.from(bytes).buffer);
13
+ return {
14
+ channels: Array.from({ length: buffer.numberOfChannels }, (_, i) => buffer.getChannelData(i)),
15
+ sampleRate: buffer.sampleRate
16
+ };
17
+ }
18
+ function createAudioWaveformCache(assets, options = {}) {
19
+ const maxBytes = options.maxBytes ?? 32 * 1024 * 1024;
20
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 0)
21
+ throw new ShardError("audio-editor/invalid-document", "Waveform cache size must be nonnegative");
22
+ const entries = /* @__PURE__ */ new Map();
23
+ const controller = new AbortController();
24
+ let size = 0;
25
+ let generation = 0;
26
+ let scope = assets.scope;
27
+ let disposed = false;
28
+ const clear = () => {
29
+ entries.clear();
30
+ size = 0;
31
+ generation++;
32
+ };
33
+ const unsubscribe = assets.subscribe?.(() => clear());
34
+ const assertLive = (signal) => {
35
+ if (disposed || signal?.aborted)
36
+ throw new ShardError("audio-editor/disposed", "Waveform request was cancelled", {
37
+ hint: "Request a waveform from an active editor session."
38
+ });
39
+ };
40
+ return {
41
+ get bytes() {
42
+ return size;
43
+ },
44
+ get count() {
45
+ return entries.size;
46
+ },
47
+ clear,
48
+ async get(id, bins, signal, channel = 0) {
49
+ assertLive(signal);
50
+ if (!Number.isSafeInteger(bins) || bins < 1 || bins > 65536 || !Number.isSafeInteger(channel) || channel < 0)
51
+ throw new ShardError(
52
+ "audio-editor/invalid-document",
53
+ "Invalid waveform resolution or channel"
54
+ );
55
+ if (scope !== assets.scope) {
56
+ scope = assets.scope;
57
+ clear();
58
+ }
59
+ const request = generation;
60
+ const combined = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
61
+ const metadata = await assets.get(id, combined);
62
+ assertLive(combined);
63
+ if (scope !== assets.scope || request !== generation)
64
+ throw new ShardError(
65
+ "audio-editor/asset-unavailable",
66
+ "Asset scope or revision changed during lookup",
67
+ { path: id }
68
+ );
69
+ const key = JSON.stringify([scope, id, metadata.revision, channel, bins]);
70
+ const cached = entries.get(key);
71
+ if (cached) {
72
+ entries.delete(key);
73
+ entries.set(key, cached);
74
+ return cached.waveform;
75
+ }
76
+ const content = await assets.resolve(id, combined);
77
+ try {
78
+ assertLive(combined);
79
+ if (!content.bytes)
80
+ throw new ShardError(
81
+ "audio-editor/unsupported-capability",
82
+ "Waveforms require authorized encoded bytes",
83
+ {
84
+ path: id,
85
+ hint: "Have the host asset resolver provide bytes for waveform requests."
86
+ }
87
+ );
88
+ const decoded = await (options.decode ?? browserDecode)(content.bytes, combined);
89
+ assertLive(combined);
90
+ if (scope !== assets.scope || request !== generation)
91
+ throw new ShardError(
92
+ "audio-editor/asset-unavailable",
93
+ "Asset scope or revision changed during decoding",
94
+ { path: id }
95
+ );
96
+ const samples = decoded.channels[channel];
97
+ if (!samples || !Number.isFinite(decoded.sampleRate) || decoded.sampleRate <= 0)
98
+ throw new ShardError(
99
+ "audio-editor/asset-unavailable",
100
+ "Requested waveform channel is unavailable",
101
+ { path: id }
102
+ );
103
+ const peaks = new Float32Array(bins * 2);
104
+ for (let bin = 0; bin < bins; bin++) {
105
+ const start = Math.floor(bin * samples.length / bins);
106
+ const end = Math.min(
107
+ samples.length,
108
+ Math.max(start + 1, Math.floor((bin + 1) * samples.length / bins))
109
+ );
110
+ let min = 0;
111
+ let max = 0;
112
+ for (let index = start; index < end; index++) {
113
+ const value = samples[index];
114
+ if (Number.isFinite(value)) {
115
+ min = Math.min(min, value);
116
+ max = Math.max(max, value);
117
+ }
118
+ }
119
+ peaks[bin * 2] = min;
120
+ peaks[bin * 2 + 1] = max;
121
+ }
122
+ const waveform = {
123
+ peaks,
124
+ duration: samples.length / decoded.sampleRate,
125
+ sampleRate: decoded.sampleRate
126
+ };
127
+ if (peaks.byteLength <= maxBytes) {
128
+ while (size + peaks.byteLength > maxBytes) {
129
+ const oldest = entries.keys().next().value;
130
+ if (oldest === void 0) break;
131
+ size -= entries.get(oldest).waveform.peaks.byteLength;
132
+ entries.delete(oldest);
133
+ }
134
+ size -= entries.get(key)?.waveform.peaks.byteLength ?? 0;
135
+ entries.set(key, { id, waveform });
136
+ size += peaks.byteLength;
137
+ }
138
+ return waveform;
139
+ } finally {
140
+ content.release?.();
141
+ }
142
+ },
143
+ dispose() {
144
+ if (disposed) return;
145
+ disposed = true;
146
+ controller.abort();
147
+ unsubscribe?.();
148
+ clear();
149
+ }
150
+ };
151
+ }
152
+ export {
153
+ createAudioWaveformCache
154
+ };
package/package.json CHANGED
@@ -1,6 +1,85 @@
1
1
  {
2
2
  "name": "@aethervtt/shard-audio-editor",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.1",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "./model": {
13
+ "types": "./dist/model.d.ts",
14
+ "default": "./dist/model.js"
15
+ },
16
+ "./dom": {
17
+ "types": "./dist/dom.d.ts",
18
+ "default": "./dist/dom.js"
19
+ },
20
+ "./panels/event": {
21
+ "types": "./dist/panels/event.d.ts",
22
+ "default": "./dist/panels/event.js"
23
+ },
24
+ "./panels/mixer": {
25
+ "types": "./dist/panels/mixer.d.ts",
26
+ "default": "./dist/panels/mixer.js"
27
+ },
28
+ "./panels/music": {
29
+ "types": "./dist/panels/music.d.ts",
30
+ "default": "./dist/panels/music.js"
31
+ },
32
+ "./panels/parameters": {
33
+ "types": "./dist/panels/parameters.d.ts",
34
+ "default": "./dist/panels/parameters.js"
35
+ },
36
+ "./panels/library": {
37
+ "types": "./dist/panels/library.d.ts",
38
+ "default": "./dist/panels/library.js"
39
+ },
40
+ "./panels/diagnostics": {
41
+ "types": "./dist/panels/diagnostics.d.ts",
42
+ "default": "./dist/panels/diagnostics.js"
43
+ },
44
+ "./preview": {
45
+ "types": "./dist/preview.d.ts",
46
+ "default": "./dist/preview.js"
47
+ },
48
+ "./project": {
49
+ "types": "./dist/project.d.ts",
50
+ "default": "./dist/project.js"
51
+ },
52
+ "./preview-render": {
53
+ "types": "./dist/preview-render.d.ts",
54
+ "default": "./dist/preview-render.js"
55
+ }
56
+ },
57
+ "files": [
58
+ "dist",
59
+ "LICENSE",
60
+ "README.md"
61
+ ],
62
+ "dependencies": {
63
+ "@aethervtt/shard-assets": "0.2.1",
64
+ "@aethervtt/shard-audio": "0.2.1",
65
+ "@aethervtt/shard-core": "0.2.1",
66
+ "@aethervtt/shard-platform": "0.2.1",
67
+ "@aethervtt/shard-platform-web": "0.2.1",
68
+ "@aethervtt/shard-project": "0.2.1",
69
+ "@aethervtt/shard-runtime": "0.2.1",
70
+ "@aethervtt/shard-transform": "0.2.1",
71
+ "@aethervtt/shard-audio-preview": "0.2.1"
72
+ },
73
+ "repository": {
74
+ "type": "git",
75
+ "url": "git+https://github.com/aethervtt/shard.git",
76
+ "directory": "packages/audio-editor"
77
+ },
78
+ "publishConfig": {
79
+ "access": "public",
80
+ "provenance": true
81
+ },
82
+ "shard": {
83
+ "buildSha": "772eb0d2b8a5fc499105708f4ae92c58788cc9ea"
84
+ }
85
+ }