@dawcore/components 0.0.16 → 0.0.18
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/dist/index.d.mts +145 -3
- package/dist/index.d.ts +145 -3
- package/dist/index.js +487 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +486 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -51,6 +51,7 @@ __export(index_exports, {
|
|
|
51
51
|
DawRecordButtonElement: () => DawRecordButtonElement,
|
|
52
52
|
DawRulerElement: () => DawRulerElement,
|
|
53
53
|
DawSelectionElement: () => DawSelectionElement,
|
|
54
|
+
DawSpectrogramElement: () => DawSpectrogramElement,
|
|
54
55
|
DawStopButtonElement: () => DawStopButtonElement,
|
|
55
56
|
DawTrackControlsElement: () => DawTrackControlsElement,
|
|
56
57
|
DawTrackElement: () => DawTrackElement,
|
|
@@ -58,6 +59,7 @@ __export(index_exports, {
|
|
|
58
59
|
DawTransportElement: () => DawTransportElement,
|
|
59
60
|
DawWaveformElement: () => DawWaveformElement,
|
|
60
61
|
RecordingController: () => RecordingController,
|
|
62
|
+
SpectrogramController: () => SpectrogramController,
|
|
61
63
|
isDomClip: () => isDomClip,
|
|
62
64
|
splitAtPlayhead: () => splitAtPlayhead
|
|
63
65
|
});
|
|
@@ -229,6 +231,7 @@ var DawTrackElement = class extends import_lit2.LitElement {
|
|
|
229
231
|
this.muted = false;
|
|
230
232
|
this.soloed = false;
|
|
231
233
|
this.renderMode = "waveform";
|
|
234
|
+
this.spectrogramConfig = null;
|
|
232
235
|
this.trackId = crypto.randomUUID();
|
|
233
236
|
// Track removal is detected by the editor's MutationObserver,
|
|
234
237
|
// not by dispatching from disconnectedCallback (detached elements
|
|
@@ -256,7 +259,16 @@ var DawTrackElement = class extends import_lit2.LitElement {
|
|
|
256
259
|
this._hasRendered = true;
|
|
257
260
|
return;
|
|
258
261
|
}
|
|
259
|
-
const trackProps = [
|
|
262
|
+
const trackProps = [
|
|
263
|
+
"volume",
|
|
264
|
+
"pan",
|
|
265
|
+
"muted",
|
|
266
|
+
"soloed",
|
|
267
|
+
"src",
|
|
268
|
+
"name",
|
|
269
|
+
"renderMode",
|
|
270
|
+
"spectrogramConfig"
|
|
271
|
+
];
|
|
260
272
|
const hasTrackChange = trackProps.some((p) => changed.has(p));
|
|
261
273
|
if (hasTrackChange) {
|
|
262
274
|
this.dispatchEvent(
|
|
@@ -290,6 +302,9 @@ __decorateClass([
|
|
|
290
302
|
__decorateClass([
|
|
291
303
|
(0, import_decorators2.property)({ attribute: "render-mode" })
|
|
292
304
|
], DawTrackElement.prototype, "renderMode", 2);
|
|
305
|
+
__decorateClass([
|
|
306
|
+
(0, import_decorators2.property)({ attribute: false })
|
|
307
|
+
], DawTrackElement.prototype, "spectrogramConfig", 2);
|
|
293
308
|
DawTrackElement = __decorateClass([
|
|
294
309
|
(0, import_decorators2.customElement)("daw-track")
|
|
295
310
|
], DawTrackElement);
|
|
@@ -2757,6 +2772,121 @@ var RecordingController = class {
|
|
|
2757
2772
|
}
|
|
2758
2773
|
};
|
|
2759
2774
|
|
|
2775
|
+
// src/controllers/spectrogram-controller.ts
|
|
2776
|
+
var import_spectrogram = require("@dawcore/spectrogram");
|
|
2777
|
+
var LIBRARY_DEFAULTS = {
|
|
2778
|
+
fftSize: 2048,
|
|
2779
|
+
windowFunction: "hann",
|
|
2780
|
+
frequencyScale: "mel",
|
|
2781
|
+
minFrequency: 0,
|
|
2782
|
+
gainDb: 20,
|
|
2783
|
+
rangeDb: 80
|
|
2784
|
+
};
|
|
2785
|
+
var LIBRARY_DEFAULT_COLOR_MAP = "viridis";
|
|
2786
|
+
var SpectrogramController = class {
|
|
2787
|
+
constructor(host, workerFactory) {
|
|
2788
|
+
this.orchestrator = null;
|
|
2789
|
+
this.editorConfig = null;
|
|
2790
|
+
this.editorColorMap = null;
|
|
2791
|
+
this.trackConfigs = /* @__PURE__ */ new Map();
|
|
2792
|
+
this.trackColorMaps = /* @__PURE__ */ new Map();
|
|
2793
|
+
this.host = host;
|
|
2794
|
+
this.workerFactory = workerFactory;
|
|
2795
|
+
this.host.addController(this);
|
|
2796
|
+
}
|
|
2797
|
+
hostConnected() {
|
|
2798
|
+
}
|
|
2799
|
+
hostDisconnected() {
|
|
2800
|
+
this.dispose();
|
|
2801
|
+
}
|
|
2802
|
+
setEditorConfig(config) {
|
|
2803
|
+
this.editorConfig = config;
|
|
2804
|
+
this.reapply();
|
|
2805
|
+
}
|
|
2806
|
+
setEditorColorMap(colorMap) {
|
|
2807
|
+
this.editorColorMap = colorMap;
|
|
2808
|
+
this.reapply();
|
|
2809
|
+
}
|
|
2810
|
+
setTrackConfig(trackId, config) {
|
|
2811
|
+
if (config === null) {
|
|
2812
|
+
this.trackConfigs.delete(trackId);
|
|
2813
|
+
} else {
|
|
2814
|
+
this.trackConfigs.set(trackId, config);
|
|
2815
|
+
}
|
|
2816
|
+
this.reapply();
|
|
2817
|
+
}
|
|
2818
|
+
setTrackColorMap(trackId, colorMap) {
|
|
2819
|
+
if (colorMap === null) {
|
|
2820
|
+
this.trackColorMaps.delete(trackId);
|
|
2821
|
+
} else {
|
|
2822
|
+
this.trackColorMaps.set(trackId, colorMap);
|
|
2823
|
+
}
|
|
2824
|
+
this.reapply();
|
|
2825
|
+
}
|
|
2826
|
+
registerClipAudio(reg) {
|
|
2827
|
+
this.ensureOrchestrator().registerClip(reg);
|
|
2828
|
+
}
|
|
2829
|
+
unregisterClipAudio(clipId) {
|
|
2830
|
+
this.orchestrator?.unregisterClip(clipId);
|
|
2831
|
+
}
|
|
2832
|
+
registerCanvas(reg) {
|
|
2833
|
+
this.ensureOrchestrator().registerCanvas(reg);
|
|
2834
|
+
}
|
|
2835
|
+
unregisterCanvas(canvasId) {
|
|
2836
|
+
this.orchestrator?.unregisterCanvas(canvasId);
|
|
2837
|
+
}
|
|
2838
|
+
setViewport(state5) {
|
|
2839
|
+
this.orchestrator?.setViewport(state5);
|
|
2840
|
+
}
|
|
2841
|
+
dispose() {
|
|
2842
|
+
if (this.orchestrator) {
|
|
2843
|
+
this.orchestrator.dispose();
|
|
2844
|
+
this.orchestrator = null;
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
ensureOrchestrator() {
|
|
2848
|
+
if (!this.orchestrator) {
|
|
2849
|
+
this.orchestrator = new import_spectrogram.SpectrogramOrchestrator({
|
|
2850
|
+
workerFactory: this.workerFactory,
|
|
2851
|
+
workerPoolSize: 2,
|
|
2852
|
+
config: this.mergedConfig(),
|
|
2853
|
+
colorMap: this.mergedColorMap()
|
|
2854
|
+
});
|
|
2855
|
+
this.orchestrator.addEventListener("viewport-ready", (e) => {
|
|
2856
|
+
const detail = e.detail;
|
|
2857
|
+
this.host.dispatchEvent(
|
|
2858
|
+
new CustomEvent("daw-spectrogram-ready", {
|
|
2859
|
+
detail,
|
|
2860
|
+
bubbles: true,
|
|
2861
|
+
composed: true
|
|
2862
|
+
})
|
|
2863
|
+
);
|
|
2864
|
+
});
|
|
2865
|
+
this.reapply();
|
|
2866
|
+
}
|
|
2867
|
+
return this.orchestrator;
|
|
2868
|
+
}
|
|
2869
|
+
reapply() {
|
|
2870
|
+
if (!this.orchestrator) return;
|
|
2871
|
+
this.orchestrator.setConfig(this.mergedConfig());
|
|
2872
|
+
this.orchestrator.setColorMap(this.mergedColorMap());
|
|
2873
|
+
}
|
|
2874
|
+
mergedConfig() {
|
|
2875
|
+
let track = null;
|
|
2876
|
+
for (const c of this.trackConfigs.values()) {
|
|
2877
|
+
track = c;
|
|
2878
|
+
break;
|
|
2879
|
+
}
|
|
2880
|
+
return { ...LIBRARY_DEFAULTS, ...this.editorConfig ?? {}, ...track ?? {} };
|
|
2881
|
+
}
|
|
2882
|
+
mergedColorMap() {
|
|
2883
|
+
for (const c of this.trackColorMaps.values()) {
|
|
2884
|
+
return c ?? LIBRARY_DEFAULT_COLOR_MAP;
|
|
2885
|
+
}
|
|
2886
|
+
return this.editorColorMap ?? LIBRARY_DEFAULT_COLOR_MAP;
|
|
2887
|
+
}
|
|
2888
|
+
};
|
|
2889
|
+
|
|
2760
2890
|
// src/interactions/pointer-handler.ts
|
|
2761
2891
|
var import_core4 = require("@waveform-playlist/core");
|
|
2762
2892
|
|
|
@@ -3637,6 +3767,7 @@ async function loadWaveformDataFromUrl(src) {
|
|
|
3637
3767
|
}
|
|
3638
3768
|
|
|
3639
3769
|
// src/elements/daw-editor.ts
|
|
3770
|
+
var import_meta = {};
|
|
3640
3771
|
var NO_ADAPTER_ERROR = "No PlayoutAdapter set on <daw-editor>. Set editor.adapter before use.\n\n // Option 1: Native Web Audio (no Tone.js)\n npm install @dawcore/transport\n import { NativePlayoutAdapter } from '@dawcore/transport';\n editor.adapter = new NativePlayoutAdapter(new AudioContext());\n\n // Option 2: Tone.js (effects, MIDI synths)\n npm install @waveform-playlist/playout\n import { createToneAdapter } from '@waveform-playlist/playout';\n editor.adapter = createToneAdapter();";
|
|
3641
3772
|
var DawEditorElement = class extends import_lit14.LitElement {
|
|
3642
3773
|
constructor() {
|
|
@@ -3652,6 +3783,8 @@ var DawEditorElement = class extends import_lit14.LitElement {
|
|
|
3652
3783
|
this.clipHeaderHeight = 20;
|
|
3653
3784
|
this.interactiveClips = false;
|
|
3654
3785
|
this.indefinitePlayback = false;
|
|
3786
|
+
this._spectrogramConfig = null;
|
|
3787
|
+
this._spectrogramColorMap = null;
|
|
3655
3788
|
this.scaleMode = "temporal";
|
|
3656
3789
|
this._ticksPerPixel = 24;
|
|
3657
3790
|
this._bpm = 120;
|
|
@@ -3687,6 +3820,7 @@ var DawEditorElement = class extends import_lit14.LitElement {
|
|
|
3687
3820
|
this._childObserver = null;
|
|
3688
3821
|
this._audioResume = new AudioResumeController(this);
|
|
3689
3822
|
this._recordingController = new RecordingController(this);
|
|
3823
|
+
this._spectrogramController = null;
|
|
3690
3824
|
this._clipPointer = new ClipPointerHandler(this);
|
|
3691
3825
|
this._pointer = new PointerHandler(this);
|
|
3692
3826
|
this._viewport = (() => {
|
|
@@ -3694,6 +3828,15 @@ var DawEditorElement = class extends import_lit14.LitElement {
|
|
|
3694
3828
|
v.scrollSelector = ".scroll-area";
|
|
3695
3829
|
return v;
|
|
3696
3830
|
})();
|
|
3831
|
+
/**
|
|
3832
|
+
* Cache of the last ViewportState forwarded to the spectrogram controller.
|
|
3833
|
+
* Lit's `updated()` fires on every reactive state change (`_isPlaying`,
|
|
3834
|
+
* `_selectedTrackId`, etc.) — most of which don't affect the spectrogram
|
|
3835
|
+
* viewport. Skip the cross-controller call when nothing changed.
|
|
3836
|
+
*
|
|
3837
|
+
* The orchestrator dedupes too, but this avoids the call entirely.
|
|
3838
|
+
*/
|
|
3839
|
+
this._lastSpectrogramViewport = null;
|
|
3697
3840
|
// --- Track Events ---
|
|
3698
3841
|
this._onTrackConnected = (e) => {
|
|
3699
3842
|
const trackId = e.detail?.trackId;
|
|
@@ -3727,6 +3870,26 @@ var DawEditorElement = class extends import_lit14.LitElement {
|
|
|
3727
3870
|
if (oldDescriptor?.src !== descriptor.src) {
|
|
3728
3871
|
this._loadTrack(trackId, descriptor);
|
|
3729
3872
|
}
|
|
3873
|
+
if (descriptor.renderMode === "spectrogram" && oldDescriptor?.renderMode !== "spectrogram") {
|
|
3874
|
+
const engineTrack = this._engineTracks.get(trackId);
|
|
3875
|
+
if (engineTrack) {
|
|
3876
|
+
for (const clip of engineTrack.clips) {
|
|
3877
|
+
this._maybeRegisterSpectrogramClipAudio(trackId, clip);
|
|
3878
|
+
}
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3881
|
+
if (descriptor.renderMode !== "spectrogram" && oldDescriptor?.renderMode === "spectrogram") {
|
|
3882
|
+
const engineTrack = this._engineTracks.get(trackId);
|
|
3883
|
+
if (engineTrack && this._spectrogramController) {
|
|
3884
|
+
for (const clip of engineTrack.clips) {
|
|
3885
|
+
this._spectrogramController.unregisterClipAudio(clip.id);
|
|
3886
|
+
}
|
|
3887
|
+
}
|
|
3888
|
+
this._disposeSpectrogramControllerIfEmpty();
|
|
3889
|
+
}
|
|
3890
|
+
if (descriptor.spectrogramConfig !== oldDescriptor?.spectrogramConfig) {
|
|
3891
|
+
this._spectrogramController?.setTrackConfig(trackId, descriptor.spectrogramConfig ?? null);
|
|
3892
|
+
}
|
|
3730
3893
|
};
|
|
3731
3894
|
this._onTrackControl = (e) => {
|
|
3732
3895
|
const { trackId, prop, value } = e.detail ?? {};
|
|
@@ -3870,6 +4033,72 @@ var DawEditorElement = class extends import_lit14.LitElement {
|
|
|
3870
4033
|
this._samplesPerPixel = clamped;
|
|
3871
4034
|
this.requestUpdate("samplesPerPixel", old);
|
|
3872
4035
|
}
|
|
4036
|
+
get spectrogramConfig() {
|
|
4037
|
+
return this._spectrogramConfig;
|
|
4038
|
+
}
|
|
4039
|
+
set spectrogramConfig(value) {
|
|
4040
|
+
const old = this._spectrogramConfig;
|
|
4041
|
+
this._spectrogramConfig = value;
|
|
4042
|
+
this._spectrogramController?.setEditorConfig(value);
|
|
4043
|
+
this.requestUpdate("spectrogramConfig", old);
|
|
4044
|
+
}
|
|
4045
|
+
get spectrogramColorMap() {
|
|
4046
|
+
return this._spectrogramColorMap;
|
|
4047
|
+
}
|
|
4048
|
+
set spectrogramColorMap(value) {
|
|
4049
|
+
const old = this._spectrogramColorMap;
|
|
4050
|
+
this._spectrogramColorMap = value;
|
|
4051
|
+
this._spectrogramController?.setEditorColorMap(value);
|
|
4052
|
+
this.requestUpdate("spectrogramColorMap", old);
|
|
4053
|
+
}
|
|
4054
|
+
_ensureSpectrogramController() {
|
|
4055
|
+
if (!this._spectrogramController) {
|
|
4056
|
+
this._spectrogramController = new SpectrogramController(
|
|
4057
|
+
this,
|
|
4058
|
+
() => new Worker(new URL("@dawcore/spectrogram/worker/spectrogram.worker", import_meta.url), {
|
|
4059
|
+
type: "module"
|
|
4060
|
+
})
|
|
4061
|
+
);
|
|
4062
|
+
if (this._spectrogramConfig) {
|
|
4063
|
+
this._spectrogramController.setEditorConfig(this._spectrogramConfig);
|
|
4064
|
+
}
|
|
4065
|
+
if (this._spectrogramColorMap) {
|
|
4066
|
+
this._spectrogramController.setEditorColorMap(this._spectrogramColorMap);
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
4069
|
+
return this._spectrogramController;
|
|
4070
|
+
}
|
|
4071
|
+
/** Called by <daw-spectrogram> after transferControlToOffscreen. */
|
|
4072
|
+
_spectrogramRegisterCanvas(reg) {
|
|
4073
|
+
this._ensureSpectrogramController().registerCanvas(reg);
|
|
4074
|
+
}
|
|
4075
|
+
/** Called by <daw-spectrogram> on chunk unmount / element disconnect. */
|
|
4076
|
+
_spectrogramUnregisterCanvas(canvasId) {
|
|
4077
|
+
this._spectrogramController?.unregisterCanvas(canvasId);
|
|
4078
|
+
}
|
|
4079
|
+
/**
|
|
4080
|
+
* Push a clip's decoded audio into the spectrogram controller. No-op
|
|
4081
|
+
* unless the track is in spectrogram render-mode and the controller
|
|
4082
|
+
* already exists (it bootstraps from canvas registration).
|
|
4083
|
+
*/
|
|
4084
|
+
_maybeRegisterSpectrogramClipAudio(trackId, clip) {
|
|
4085
|
+
const descriptor = this._tracks.get(trackId);
|
|
4086
|
+
if (descriptor?.renderMode !== "spectrogram") return;
|
|
4087
|
+
const buffer = clip.audioBuffer ?? this._clipBuffers.get(clip.id);
|
|
4088
|
+
if (!buffer) return;
|
|
4089
|
+
const channelData = [];
|
|
4090
|
+
for (let i = 0; i < buffer.numberOfChannels; i++) {
|
|
4091
|
+
channelData.push(buffer.getChannelData(i));
|
|
4092
|
+
}
|
|
4093
|
+
this._ensureSpectrogramController().registerClipAudio({
|
|
4094
|
+
clipId: clip.id,
|
|
4095
|
+
trackId,
|
|
4096
|
+
channelData,
|
|
4097
|
+
sampleRate: buffer.sampleRate,
|
|
4098
|
+
durationSamples: clip.durationSamples,
|
|
4099
|
+
offsetSamples: clip.offsetSamples
|
|
4100
|
+
});
|
|
4101
|
+
}
|
|
3873
4102
|
get ticksPerPixel() {
|
|
3874
4103
|
return this._ticksPerPixel;
|
|
3875
4104
|
}
|
|
@@ -4110,6 +4339,8 @@ var DawEditorElement = class extends import_lit14.LitElement {
|
|
|
4110
4339
|
this._clipOffsets.clear();
|
|
4111
4340
|
this._peakPipeline.terminate();
|
|
4112
4341
|
this._minSamplesPerPixel = 0;
|
|
4342
|
+
this._spectrogramController?.dispose();
|
|
4343
|
+
this._spectrogramController = null;
|
|
4113
4344
|
try {
|
|
4114
4345
|
this._disposeEngine();
|
|
4115
4346
|
} catch (err) {
|
|
@@ -4138,6 +4369,27 @@ var DawEditorElement = class extends import_lit14.LitElement {
|
|
|
4138
4369
|
}
|
|
4139
4370
|
}
|
|
4140
4371
|
}
|
|
4372
|
+
updated(_changed) {
|
|
4373
|
+
if (this._spectrogramController) {
|
|
4374
|
+
const vs = this._viewport.visibleStart;
|
|
4375
|
+
const ve = this._viewport.visibleEnd;
|
|
4376
|
+
const spp = this._renderSpp;
|
|
4377
|
+
if (Number.isFinite(vs) && Number.isFinite(ve)) {
|
|
4378
|
+
const prev = this._lastSpectrogramViewport;
|
|
4379
|
+
if (prev && prev.vs === vs && prev.ve === ve && prev.spp === spp) return;
|
|
4380
|
+
this._lastSpectrogramViewport = { vs, ve, spp };
|
|
4381
|
+
const span = ve - vs;
|
|
4382
|
+
const bufferPad = span * 0.25;
|
|
4383
|
+
this._spectrogramController.setViewport({
|
|
4384
|
+
visibleStartPx: vs,
|
|
4385
|
+
visibleEndPx: ve,
|
|
4386
|
+
bufferStartPx: Math.max(0, vs - bufferPad),
|
|
4387
|
+
bufferEndPx: ve + bufferPad,
|
|
4388
|
+
samplesPerPixel: spp
|
|
4389
|
+
});
|
|
4390
|
+
}
|
|
4391
|
+
}
|
|
4392
|
+
}
|
|
4141
4393
|
_onTrackRemoved(trackId) {
|
|
4142
4394
|
this._trackElements.delete(trackId);
|
|
4143
4395
|
const removedTrack = this._engineTracks.get(trackId);
|
|
@@ -4147,6 +4399,7 @@ var DawEditorElement = class extends import_lit14.LitElement {
|
|
|
4147
4399
|
this._clipBuffers.delete(clip.id);
|
|
4148
4400
|
this._clipOffsets.delete(clip.id);
|
|
4149
4401
|
nextPeaks.delete(clip.id);
|
|
4402
|
+
this._spectrogramController?.unregisterClipAudio(clip.id);
|
|
4150
4403
|
}
|
|
4151
4404
|
this._peaksData = nextPeaks;
|
|
4152
4405
|
}
|
|
@@ -4161,11 +4414,23 @@ var DawEditorElement = class extends import_lit14.LitElement {
|
|
|
4161
4414
|
this._engine.removeTrack(trackId);
|
|
4162
4415
|
}
|
|
4163
4416
|
this._minSamplesPerPixel = this._peakPipeline.getMaxCachedScale(this._clipBuffers);
|
|
4417
|
+
this._disposeSpectrogramControllerIfEmpty();
|
|
4164
4418
|
if (nextEngine.size === 0) {
|
|
4165
4419
|
this._currentTime = 0;
|
|
4166
4420
|
this._stopPlayhead();
|
|
4167
4421
|
}
|
|
4168
4422
|
}
|
|
4423
|
+
/** Drop the controller when no spectrogram tracks remain. */
|
|
4424
|
+
_disposeSpectrogramControllerIfEmpty() {
|
|
4425
|
+
if (!this._spectrogramController) return;
|
|
4426
|
+
const stillNeeded = Array.from(this._tracks.values()).some(
|
|
4427
|
+
(d) => d.renderMode === "spectrogram"
|
|
4428
|
+
);
|
|
4429
|
+
if (!stillNeeded) {
|
|
4430
|
+
this._spectrogramController.dispose();
|
|
4431
|
+
this._spectrogramController = null;
|
|
4432
|
+
}
|
|
4433
|
+
}
|
|
4169
4434
|
_onClipRemovedFromDom(clipEl) {
|
|
4170
4435
|
const clipId = clipEl.clipId;
|
|
4171
4436
|
for (const [trackId, t] of this._engineTracks.entries()) {
|
|
@@ -4207,6 +4472,7 @@ var DawEditorElement = class extends import_lit14.LitElement {
|
|
|
4207
4472
|
});
|
|
4208
4473
|
}
|
|
4209
4474
|
this._commitTrackChange(trackId, updatedTrack);
|
|
4475
|
+
this._maybeRegisterSpectrogramClipAudio(trackId, clip);
|
|
4210
4476
|
this.dispatchEvent(
|
|
4211
4477
|
new CustomEvent("daw-clip-ready", {
|
|
4212
4478
|
bubbles: true,
|
|
@@ -4381,6 +4647,7 @@ var DawEditorElement = class extends import_lit14.LitElement {
|
|
|
4381
4647
|
nextPeaks.delete(clipId);
|
|
4382
4648
|
this._peaksData = nextPeaks;
|
|
4383
4649
|
this._clipOffsets.delete(clipId);
|
|
4650
|
+
this._spectrogramController?.unregisterClipAudio(clipId);
|
|
4384
4651
|
}
|
|
4385
4652
|
/**
|
|
4386
4653
|
* Recompute duration and forward an updated track to the engine. Single
|
|
@@ -4663,6 +4930,9 @@ var DawEditorElement = class extends import_lit14.LitElement {
|
|
|
4663
4930
|
track.id = trackId;
|
|
4664
4931
|
this._engineTracks = new Map(this._engineTracks).set(trackId, track);
|
|
4665
4932
|
this._recomputeDuration();
|
|
4933
|
+
for (const c of clips) {
|
|
4934
|
+
this._maybeRegisterSpectrogramClipAudio(trackId, c);
|
|
4935
|
+
}
|
|
4666
4936
|
const engine = await this._ensureEngine();
|
|
4667
4937
|
engine.setTracks([...this._engineTracks.values()]);
|
|
4668
4938
|
this.dispatchEvent(
|
|
@@ -5474,19 +5744,34 @@ var DawEditorElement = class extends import_lit14.LitElement {
|
|
|
5474
5744
|
.visibleEnd=${this._viewport.visibleEnd}
|
|
5475
5745
|
.originX=${clipLeft}
|
|
5476
5746
|
?selected=${t.trackId === this._selectedTrackId}
|
|
5477
|
-
></daw-piano-roll>` : channels.map(
|
|
5747
|
+
></daw-piano-roll>` : t.descriptor?.renderMode === "spectrogram" ? channels.map(
|
|
5748
|
+
(_chPeaks, chIdx) => import_lit14.html`<daw-spectrogram
|
|
5749
|
+
style="position:absolute;left:0;top:${hdrH + chIdx * chH}px;height:${chH}px;width:${peakData?.length ?? width}px;"
|
|
5750
|
+
.clipId=${clip.id}
|
|
5751
|
+
.trackId=${t.trackId}
|
|
5752
|
+
.channelIndex=${chIdx}
|
|
5753
|
+
.length=${peakData?.length ?? width}
|
|
5754
|
+
.waveHeight=${chH}
|
|
5755
|
+
.samplesPerPixel=${this._renderSpp}
|
|
5756
|
+
.sampleRate=${this.effectiveSampleRate}
|
|
5757
|
+
.clipOffsetSeconds=${(clip.offsetSamples ?? 0) / this.effectiveSampleRate}
|
|
5758
|
+
.visibleStart=${this._viewport.visibleStart}
|
|
5759
|
+
.visibleEnd=${this._viewport.visibleEnd}
|
|
5760
|
+
.originX=${clipLeft}
|
|
5761
|
+
></daw-spectrogram>`
|
|
5762
|
+
) : channels.map(
|
|
5478
5763
|
(chPeaks, chIdx) => import_lit14.html` <daw-waveform
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5764
|
+
style="position:absolute;left:0;top:${hdrH + chIdx * chH}px;"
|
|
5765
|
+
.peaks=${chPeaks}
|
|
5766
|
+
.length=${peakData?.length ?? width}
|
|
5767
|
+
.waveHeight=${chH}
|
|
5768
|
+
.barWidth=${this.barWidth}
|
|
5769
|
+
.barGap=${this.barGap}
|
|
5770
|
+
.visibleStart=${this._viewport.visibleStart}
|
|
5771
|
+
.visibleEnd=${this._viewport.visibleEnd}
|
|
5772
|
+
.originX=${clipLeft}
|
|
5773
|
+
.segments=${clipSegments}
|
|
5774
|
+
></daw-waveform>`
|
|
5490
5775
|
)}
|
|
5491
5776
|
${this.interactiveClips ? import_lit14.html` <div
|
|
5492
5777
|
class="clip-boundary"
|
|
@@ -5594,6 +5879,12 @@ __decorateClass([
|
|
|
5594
5879
|
__decorateClass([
|
|
5595
5880
|
(0, import_decorators12.property)({ type: Boolean, attribute: "indefinite-playback" })
|
|
5596
5881
|
], DawEditorElement.prototype, "indefinitePlayback", 2);
|
|
5882
|
+
__decorateClass([
|
|
5883
|
+
(0, import_decorators12.property)({ attribute: false, noAccessor: true })
|
|
5884
|
+
], DawEditorElement.prototype, "spectrogramConfig", 1);
|
|
5885
|
+
__decorateClass([
|
|
5886
|
+
(0, import_decorators12.property)({ attribute: false, noAccessor: true })
|
|
5887
|
+
], DawEditorElement.prototype, "spectrogramColorMap", 1);
|
|
5597
5888
|
__decorateClass([
|
|
5598
5889
|
(0, import_decorators12.property)({ type: String, attribute: "scale-mode" })
|
|
5599
5890
|
], DawEditorElement.prototype, "scaleMode", 2);
|
|
@@ -6172,6 +6463,187 @@ __decorateClass([
|
|
|
6172
6463
|
DawKeyboardShortcutsElement = __decorateClass([
|
|
6173
6464
|
(0, import_decorators16.customElement)("daw-keyboard-shortcuts")
|
|
6174
6465
|
], DawKeyboardShortcutsElement);
|
|
6466
|
+
|
|
6467
|
+
// src/elements/daw-spectrogram.ts
|
|
6468
|
+
var import_lit19 = require("lit");
|
|
6469
|
+
var import_decorators17 = require("lit/decorators.js");
|
|
6470
|
+
var MAX_CANVAS_WIDTH5 = 1e3;
|
|
6471
|
+
var DawSpectrogramElement = class extends import_lit19.LitElement {
|
|
6472
|
+
constructor() {
|
|
6473
|
+
super(...arguments);
|
|
6474
|
+
this.clipId = "";
|
|
6475
|
+
this.trackId = "";
|
|
6476
|
+
this.channelIndex = 0;
|
|
6477
|
+
this.length = 0;
|
|
6478
|
+
this.waveHeight = 128;
|
|
6479
|
+
this._samplesPerPixel = 1024;
|
|
6480
|
+
this._sampleRate = 44100;
|
|
6481
|
+
this.clipOffsetSeconds = 0;
|
|
6482
|
+
this.visibleStart = -Infinity;
|
|
6483
|
+
this.visibleEnd = Infinity;
|
|
6484
|
+
this.originX = 0;
|
|
6485
|
+
this._canvases = [];
|
|
6486
|
+
this._registeredCanvasIds = [];
|
|
6487
|
+
}
|
|
6488
|
+
get samplesPerPixel() {
|
|
6489
|
+
return this._samplesPerPixel;
|
|
6490
|
+
}
|
|
6491
|
+
set samplesPerPixel(value) {
|
|
6492
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
6493
|
+
console.warn("[dawcore] daw-spectrogram samplesPerPixel " + value + " is invalid \u2014 ignored");
|
|
6494
|
+
return;
|
|
6495
|
+
}
|
|
6496
|
+
const old = this._samplesPerPixel;
|
|
6497
|
+
this._samplesPerPixel = value;
|
|
6498
|
+
this.requestUpdate("samplesPerPixel", old);
|
|
6499
|
+
}
|
|
6500
|
+
get sampleRate() {
|
|
6501
|
+
return this._sampleRate;
|
|
6502
|
+
}
|
|
6503
|
+
set sampleRate(value) {
|
|
6504
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
6505
|
+
console.warn("[dawcore] daw-spectrogram sampleRate " + value + " is invalid \u2014 ignored");
|
|
6506
|
+
return;
|
|
6507
|
+
}
|
|
6508
|
+
const old = this._sampleRate;
|
|
6509
|
+
this._sampleRate = value;
|
|
6510
|
+
this.requestUpdate("sampleRate", old);
|
|
6511
|
+
}
|
|
6512
|
+
/**
|
|
6513
|
+
* Walk up to the editor host. `closest('daw-editor')` does NOT cross
|
|
6514
|
+
* shadow boundaries — and this element lives inside the editor's shadow
|
|
6515
|
+
* DOM — so use getRootNode().host to step out.
|
|
6516
|
+
*/
|
|
6517
|
+
_findHostEditor() {
|
|
6518
|
+
const root = this.getRootNode();
|
|
6519
|
+
const host = root instanceof ShadowRoot ? root.host : null;
|
|
6520
|
+
if (!host) return null;
|
|
6521
|
+
if (host.tagName === "DAW-EDITOR") return host;
|
|
6522
|
+
return host.closest("daw-editor");
|
|
6523
|
+
}
|
|
6524
|
+
willUpdate(changed) {
|
|
6525
|
+
const layoutChanged = changed.has("length") || changed.has("waveHeight") || changed.has("samplesPerPixel") || changed.has("clipId") || changed.has("channelIndex");
|
|
6526
|
+
if (layoutChanged) {
|
|
6527
|
+
this._rebuildChunks();
|
|
6528
|
+
}
|
|
6529
|
+
}
|
|
6530
|
+
_rebuildChunks() {
|
|
6531
|
+
this._unregisterAllCanvases();
|
|
6532
|
+
this._canvases = [];
|
|
6533
|
+
if (this.length <= 0) return;
|
|
6534
|
+
const chunkCount = Math.ceil(this.length / MAX_CANVAS_WIDTH5);
|
|
6535
|
+
for (let i = 0; i < chunkCount; i++) {
|
|
6536
|
+
const widthPx = Math.min(MAX_CANVAS_WIDTH5, this.length - i * MAX_CANVAS_WIDTH5);
|
|
6537
|
+
const canvas = document.createElement("canvas");
|
|
6538
|
+
canvas.style.left = i * MAX_CANVAS_WIDTH5 + "px";
|
|
6539
|
+
canvas.style.width = widthPx + "px";
|
|
6540
|
+
const dpr = window.devicePixelRatio || 1;
|
|
6541
|
+
canvas.width = widthPx * dpr;
|
|
6542
|
+
canvas.height = this.waveHeight * dpr;
|
|
6543
|
+
this._canvases.push(canvas);
|
|
6544
|
+
}
|
|
6545
|
+
}
|
|
6546
|
+
updated(_changed) {
|
|
6547
|
+
if (this._registeredCanvasIds.length === 0 && this._canvases.length > 0) {
|
|
6548
|
+
requestAnimationFrame(() => this._registerCanvases());
|
|
6549
|
+
}
|
|
6550
|
+
}
|
|
6551
|
+
_registerCanvases() {
|
|
6552
|
+
const editor = this._findHostEditor();
|
|
6553
|
+
if (!editor || typeof editor._spectrogramRegisterCanvas !== "function") return;
|
|
6554
|
+
for (let i = 0; i < this._canvases.length; i++) {
|
|
6555
|
+
const canvas = this._canvases[i];
|
|
6556
|
+
const canvasId = this.clipId + "-ch" + this.channelIndex + "-chunk" + i;
|
|
6557
|
+
let offscreen;
|
|
6558
|
+
try {
|
|
6559
|
+
offscreen = canvas.transferControlToOffscreen();
|
|
6560
|
+
} catch (err) {
|
|
6561
|
+
console.warn(
|
|
6562
|
+
"[dawcore] daw-spectrogram transferControlToOffscreen failed for " + canvasId + ": " + (err instanceof Error ? err.message : String(err))
|
|
6563
|
+
);
|
|
6564
|
+
continue;
|
|
6565
|
+
}
|
|
6566
|
+
editor._spectrogramRegisterCanvas({
|
|
6567
|
+
canvasId,
|
|
6568
|
+
canvas: offscreen,
|
|
6569
|
+
clipId: this.clipId,
|
|
6570
|
+
trackId: this.trackId,
|
|
6571
|
+
channelIndex: this.channelIndex,
|
|
6572
|
+
chunkIndex: i,
|
|
6573
|
+
globalPixelOffset: this.originX + i * MAX_CANVAS_WIDTH5,
|
|
6574
|
+
widthPx: parseFloat(canvas.style.width),
|
|
6575
|
+
heightPx: this.waveHeight
|
|
6576
|
+
});
|
|
6577
|
+
this._registeredCanvasIds.push(canvasId);
|
|
6578
|
+
}
|
|
6579
|
+
}
|
|
6580
|
+
_unregisterAllCanvases() {
|
|
6581
|
+
const editor = this._findHostEditor();
|
|
6582
|
+
if (editor && typeof editor._spectrogramUnregisterCanvas === "function") {
|
|
6583
|
+
for (const id of this._registeredCanvasIds) {
|
|
6584
|
+
editor._spectrogramUnregisterCanvas(id);
|
|
6585
|
+
}
|
|
6586
|
+
}
|
|
6587
|
+
this._registeredCanvasIds = [];
|
|
6588
|
+
}
|
|
6589
|
+
disconnectedCallback() {
|
|
6590
|
+
super.disconnectedCallback();
|
|
6591
|
+
this._unregisterAllCanvases();
|
|
6592
|
+
}
|
|
6593
|
+
render() {
|
|
6594
|
+
return import_lit19.html`${this._canvases.map((c) => c)}`;
|
|
6595
|
+
}
|
|
6596
|
+
};
|
|
6597
|
+
DawSpectrogramElement.styles = import_lit19.css`
|
|
6598
|
+
:host {
|
|
6599
|
+
display: block;
|
|
6600
|
+
position: relative;
|
|
6601
|
+
background: var(--daw-spectrogram-background, #000);
|
|
6602
|
+
}
|
|
6603
|
+
canvas {
|
|
6604
|
+
position: absolute;
|
|
6605
|
+
top: 0;
|
|
6606
|
+
left: 0;
|
|
6607
|
+
height: 100%;
|
|
6608
|
+
pointer-events: none;
|
|
6609
|
+
}
|
|
6610
|
+
`;
|
|
6611
|
+
__decorateClass([
|
|
6612
|
+
(0, import_decorators17.property)({ attribute: false })
|
|
6613
|
+
], DawSpectrogramElement.prototype, "clipId", 2);
|
|
6614
|
+
__decorateClass([
|
|
6615
|
+
(0, import_decorators17.property)({ attribute: false })
|
|
6616
|
+
], DawSpectrogramElement.prototype, "trackId", 2);
|
|
6617
|
+
__decorateClass([
|
|
6618
|
+
(0, import_decorators17.property)({ type: Number, attribute: false })
|
|
6619
|
+
], DawSpectrogramElement.prototype, "channelIndex", 2);
|
|
6620
|
+
__decorateClass([
|
|
6621
|
+
(0, import_decorators17.property)({ type: Number, attribute: false })
|
|
6622
|
+
], DawSpectrogramElement.prototype, "length", 2);
|
|
6623
|
+
__decorateClass([
|
|
6624
|
+
(0, import_decorators17.property)({ type: Number, attribute: false })
|
|
6625
|
+
], DawSpectrogramElement.prototype, "waveHeight", 2);
|
|
6626
|
+
__decorateClass([
|
|
6627
|
+
(0, import_decorators17.property)({ type: Number, attribute: false, noAccessor: true })
|
|
6628
|
+
], DawSpectrogramElement.prototype, "samplesPerPixel", 1);
|
|
6629
|
+
__decorateClass([
|
|
6630
|
+
(0, import_decorators17.property)({ type: Number, attribute: false, noAccessor: true })
|
|
6631
|
+
], DawSpectrogramElement.prototype, "sampleRate", 1);
|
|
6632
|
+
__decorateClass([
|
|
6633
|
+
(0, import_decorators17.property)({ type: Number, attribute: false })
|
|
6634
|
+
], DawSpectrogramElement.prototype, "clipOffsetSeconds", 2);
|
|
6635
|
+
__decorateClass([
|
|
6636
|
+
(0, import_decorators17.property)({ type: Number, attribute: false })
|
|
6637
|
+
], DawSpectrogramElement.prototype, "visibleStart", 2);
|
|
6638
|
+
__decorateClass([
|
|
6639
|
+
(0, import_decorators17.property)({ type: Number, attribute: false })
|
|
6640
|
+
], DawSpectrogramElement.prototype, "visibleEnd", 2);
|
|
6641
|
+
__decorateClass([
|
|
6642
|
+
(0, import_decorators17.property)({ type: Number, attribute: false })
|
|
6643
|
+
], DawSpectrogramElement.prototype, "originX", 2);
|
|
6644
|
+
DawSpectrogramElement = __decorateClass([
|
|
6645
|
+
(0, import_decorators17.customElement)("daw-spectrogram")
|
|
6646
|
+
], DawSpectrogramElement);
|
|
6175
6647
|
// Annotate the CommonJS export names for ESM import in node:
|
|
6176
6648
|
0 && (module.exports = {
|
|
6177
6649
|
AudioResumeController,
|
|
@@ -6187,6 +6659,7 @@ DawKeyboardShortcutsElement = __decorateClass([
|
|
|
6187
6659
|
DawRecordButtonElement,
|
|
6188
6660
|
DawRulerElement,
|
|
6189
6661
|
DawSelectionElement,
|
|
6662
|
+
DawSpectrogramElement,
|
|
6190
6663
|
DawStopButtonElement,
|
|
6191
6664
|
DawTrackControlsElement,
|
|
6192
6665
|
DawTrackElement,
|
|
@@ -6194,6 +6667,7 @@ DawKeyboardShortcutsElement = __decorateClass([
|
|
|
6194
6667
|
DawTransportElement,
|
|
6195
6668
|
DawWaveformElement,
|
|
6196
6669
|
RecordingController,
|
|
6670
|
+
SpectrogramController,
|
|
6197
6671
|
isDomClip,
|
|
6198
6672
|
splitAtPlayhead
|
|
6199
6673
|
});
|