@dawcore/components 0.0.16 → 0.0.17
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 +136 -3
- package/dist/index.d.ts +136 -3
- package/dist/index.js +474 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +473 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
package/dist/index.mjs
CHANGED
|
@@ -175,6 +175,7 @@ var DawTrackElement = class extends LitElement2 {
|
|
|
175
175
|
this.muted = false;
|
|
176
176
|
this.soloed = false;
|
|
177
177
|
this.renderMode = "waveform";
|
|
178
|
+
this.spectrogramConfig = null;
|
|
178
179
|
this.trackId = crypto.randomUUID();
|
|
179
180
|
// Track removal is detected by the editor's MutationObserver,
|
|
180
181
|
// not by dispatching from disconnectedCallback (detached elements
|
|
@@ -202,7 +203,16 @@ var DawTrackElement = class extends LitElement2 {
|
|
|
202
203
|
this._hasRendered = true;
|
|
203
204
|
return;
|
|
204
205
|
}
|
|
205
|
-
const trackProps = [
|
|
206
|
+
const trackProps = [
|
|
207
|
+
"volume",
|
|
208
|
+
"pan",
|
|
209
|
+
"muted",
|
|
210
|
+
"soloed",
|
|
211
|
+
"src",
|
|
212
|
+
"name",
|
|
213
|
+
"renderMode",
|
|
214
|
+
"spectrogramConfig"
|
|
215
|
+
];
|
|
206
216
|
const hasTrackChange = trackProps.some((p) => changed.has(p));
|
|
207
217
|
if (hasTrackChange) {
|
|
208
218
|
this.dispatchEvent(
|
|
@@ -236,6 +246,9 @@ __decorateClass([
|
|
|
236
246
|
__decorateClass([
|
|
237
247
|
property2({ attribute: "render-mode" })
|
|
238
248
|
], DawTrackElement.prototype, "renderMode", 2);
|
|
249
|
+
__decorateClass([
|
|
250
|
+
property2({ attribute: false })
|
|
251
|
+
], DawTrackElement.prototype, "spectrogramConfig", 2);
|
|
239
252
|
DawTrackElement = __decorateClass([
|
|
240
253
|
customElement2("daw-track")
|
|
241
254
|
], DawTrackElement);
|
|
@@ -2708,6 +2721,123 @@ var RecordingController = class {
|
|
|
2708
2721
|
}
|
|
2709
2722
|
};
|
|
2710
2723
|
|
|
2724
|
+
// src/controllers/spectrogram-controller.ts
|
|
2725
|
+
import {
|
|
2726
|
+
SpectrogramOrchestrator
|
|
2727
|
+
} from "@dawcore/spectrogram";
|
|
2728
|
+
var LIBRARY_DEFAULTS = {
|
|
2729
|
+
fftSize: 2048,
|
|
2730
|
+
windowFunction: "hann",
|
|
2731
|
+
frequencyScale: "mel",
|
|
2732
|
+
minFrequency: 0,
|
|
2733
|
+
gainDb: 20,
|
|
2734
|
+
rangeDb: 80
|
|
2735
|
+
};
|
|
2736
|
+
var LIBRARY_DEFAULT_COLOR_MAP = "viridis";
|
|
2737
|
+
var SpectrogramController = class {
|
|
2738
|
+
constructor(host, workerFactory) {
|
|
2739
|
+
this.orchestrator = null;
|
|
2740
|
+
this.editorConfig = null;
|
|
2741
|
+
this.editorColorMap = null;
|
|
2742
|
+
this.trackConfigs = /* @__PURE__ */ new Map();
|
|
2743
|
+
this.trackColorMaps = /* @__PURE__ */ new Map();
|
|
2744
|
+
this.host = host;
|
|
2745
|
+
this.workerFactory = workerFactory;
|
|
2746
|
+
this.host.addController(this);
|
|
2747
|
+
}
|
|
2748
|
+
hostConnected() {
|
|
2749
|
+
}
|
|
2750
|
+
hostDisconnected() {
|
|
2751
|
+
this.dispose();
|
|
2752
|
+
}
|
|
2753
|
+
setEditorConfig(config) {
|
|
2754
|
+
this.editorConfig = config;
|
|
2755
|
+
this.reapply();
|
|
2756
|
+
}
|
|
2757
|
+
setEditorColorMap(colorMap) {
|
|
2758
|
+
this.editorColorMap = colorMap;
|
|
2759
|
+
this.reapply();
|
|
2760
|
+
}
|
|
2761
|
+
setTrackConfig(trackId, config) {
|
|
2762
|
+
if (config === null) {
|
|
2763
|
+
this.trackConfigs.delete(trackId);
|
|
2764
|
+
} else {
|
|
2765
|
+
this.trackConfigs.set(trackId, config);
|
|
2766
|
+
}
|
|
2767
|
+
this.reapply();
|
|
2768
|
+
}
|
|
2769
|
+
setTrackColorMap(trackId, colorMap) {
|
|
2770
|
+
if (colorMap === null) {
|
|
2771
|
+
this.trackColorMaps.delete(trackId);
|
|
2772
|
+
} else {
|
|
2773
|
+
this.trackColorMaps.set(trackId, colorMap);
|
|
2774
|
+
}
|
|
2775
|
+
this.reapply();
|
|
2776
|
+
}
|
|
2777
|
+
registerClipAudio(reg) {
|
|
2778
|
+
this.ensureOrchestrator().registerClip(reg);
|
|
2779
|
+
}
|
|
2780
|
+
unregisterClipAudio(clipId) {
|
|
2781
|
+
this.orchestrator?.unregisterClip(clipId);
|
|
2782
|
+
}
|
|
2783
|
+
registerCanvas(reg) {
|
|
2784
|
+
this.ensureOrchestrator().registerCanvas(reg);
|
|
2785
|
+
}
|
|
2786
|
+
unregisterCanvas(canvasId) {
|
|
2787
|
+
this.orchestrator?.unregisterCanvas(canvasId);
|
|
2788
|
+
}
|
|
2789
|
+
setViewport(state5) {
|
|
2790
|
+
this.orchestrator?.setViewport(state5);
|
|
2791
|
+
}
|
|
2792
|
+
dispose() {
|
|
2793
|
+
if (this.orchestrator) {
|
|
2794
|
+
this.orchestrator.dispose();
|
|
2795
|
+
this.orchestrator = null;
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
ensureOrchestrator() {
|
|
2799
|
+
if (!this.orchestrator) {
|
|
2800
|
+
this.orchestrator = new SpectrogramOrchestrator({
|
|
2801
|
+
workerFactory: this.workerFactory,
|
|
2802
|
+
workerPoolSize: 2,
|
|
2803
|
+
config: this.mergedConfig(),
|
|
2804
|
+
colorMap: this.mergedColorMap()
|
|
2805
|
+
});
|
|
2806
|
+
this.orchestrator.addEventListener("viewport-ready", (e) => {
|
|
2807
|
+
const detail = e.detail;
|
|
2808
|
+
this.host.dispatchEvent(
|
|
2809
|
+
new CustomEvent("daw-spectrogram-ready", {
|
|
2810
|
+
detail,
|
|
2811
|
+
bubbles: true,
|
|
2812
|
+
composed: true
|
|
2813
|
+
})
|
|
2814
|
+
);
|
|
2815
|
+
});
|
|
2816
|
+
this.reapply();
|
|
2817
|
+
}
|
|
2818
|
+
return this.orchestrator;
|
|
2819
|
+
}
|
|
2820
|
+
reapply() {
|
|
2821
|
+
if (!this.orchestrator) return;
|
|
2822
|
+
this.orchestrator.setConfig(this.mergedConfig());
|
|
2823
|
+
this.orchestrator.setColorMap(this.mergedColorMap());
|
|
2824
|
+
}
|
|
2825
|
+
mergedConfig() {
|
|
2826
|
+
let track = null;
|
|
2827
|
+
for (const c of this.trackConfigs.values()) {
|
|
2828
|
+
track = c;
|
|
2829
|
+
break;
|
|
2830
|
+
}
|
|
2831
|
+
return { ...LIBRARY_DEFAULTS, ...this.editorConfig ?? {}, ...track ?? {} };
|
|
2832
|
+
}
|
|
2833
|
+
mergedColorMap() {
|
|
2834
|
+
for (const c of this.trackColorMaps.values()) {
|
|
2835
|
+
return c ?? LIBRARY_DEFAULT_COLOR_MAP;
|
|
2836
|
+
}
|
|
2837
|
+
return this.editorColorMap ?? LIBRARY_DEFAULT_COLOR_MAP;
|
|
2838
|
+
}
|
|
2839
|
+
};
|
|
2840
|
+
|
|
2711
2841
|
// src/interactions/pointer-handler.ts
|
|
2712
2842
|
import { pixelsToSeconds, snapTickToGrid } from "@waveform-playlist/core";
|
|
2713
2843
|
|
|
@@ -3603,6 +3733,8 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
3603
3733
|
this.clipHeaderHeight = 20;
|
|
3604
3734
|
this.interactiveClips = false;
|
|
3605
3735
|
this.indefinitePlayback = false;
|
|
3736
|
+
this._spectrogramConfig = null;
|
|
3737
|
+
this._spectrogramColorMap = null;
|
|
3606
3738
|
this.scaleMode = "temporal";
|
|
3607
3739
|
this._ticksPerPixel = 24;
|
|
3608
3740
|
this._bpm = 120;
|
|
@@ -3638,6 +3770,7 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
3638
3770
|
this._childObserver = null;
|
|
3639
3771
|
this._audioResume = new AudioResumeController(this);
|
|
3640
3772
|
this._recordingController = new RecordingController(this);
|
|
3773
|
+
this._spectrogramController = null;
|
|
3641
3774
|
this._clipPointer = new ClipPointerHandler(this);
|
|
3642
3775
|
this._pointer = new PointerHandler(this);
|
|
3643
3776
|
this._viewport = (() => {
|
|
@@ -3678,6 +3811,26 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
3678
3811
|
if (oldDescriptor?.src !== descriptor.src) {
|
|
3679
3812
|
this._loadTrack(trackId, descriptor);
|
|
3680
3813
|
}
|
|
3814
|
+
if (descriptor.renderMode === "spectrogram" && oldDescriptor?.renderMode !== "spectrogram") {
|
|
3815
|
+
const engineTrack = this._engineTracks.get(trackId);
|
|
3816
|
+
if (engineTrack) {
|
|
3817
|
+
for (const clip of engineTrack.clips) {
|
|
3818
|
+
this._maybeRegisterSpectrogramClipAudio(trackId, clip);
|
|
3819
|
+
}
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
if (descriptor.renderMode !== "spectrogram" && oldDescriptor?.renderMode === "spectrogram") {
|
|
3823
|
+
const engineTrack = this._engineTracks.get(trackId);
|
|
3824
|
+
if (engineTrack && this._spectrogramController) {
|
|
3825
|
+
for (const clip of engineTrack.clips) {
|
|
3826
|
+
this._spectrogramController.unregisterClipAudio(clip.id);
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3829
|
+
this._disposeSpectrogramControllerIfEmpty();
|
|
3830
|
+
}
|
|
3831
|
+
if (descriptor.spectrogramConfig !== oldDescriptor?.spectrogramConfig) {
|
|
3832
|
+
this._spectrogramController?.setTrackConfig(trackId, descriptor.spectrogramConfig ?? null);
|
|
3833
|
+
}
|
|
3681
3834
|
};
|
|
3682
3835
|
this._onTrackControl = (e) => {
|
|
3683
3836
|
const { trackId, prop, value } = e.detail ?? {};
|
|
@@ -3821,6 +3974,72 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
3821
3974
|
this._samplesPerPixel = clamped;
|
|
3822
3975
|
this.requestUpdate("samplesPerPixel", old);
|
|
3823
3976
|
}
|
|
3977
|
+
get spectrogramConfig() {
|
|
3978
|
+
return this._spectrogramConfig;
|
|
3979
|
+
}
|
|
3980
|
+
set spectrogramConfig(value) {
|
|
3981
|
+
const old = this._spectrogramConfig;
|
|
3982
|
+
this._spectrogramConfig = value;
|
|
3983
|
+
this._spectrogramController?.setEditorConfig(value);
|
|
3984
|
+
this.requestUpdate("spectrogramConfig", old);
|
|
3985
|
+
}
|
|
3986
|
+
get spectrogramColorMap() {
|
|
3987
|
+
return this._spectrogramColorMap;
|
|
3988
|
+
}
|
|
3989
|
+
set spectrogramColorMap(value) {
|
|
3990
|
+
const old = this._spectrogramColorMap;
|
|
3991
|
+
this._spectrogramColorMap = value;
|
|
3992
|
+
this._spectrogramController?.setEditorColorMap(value);
|
|
3993
|
+
this.requestUpdate("spectrogramColorMap", old);
|
|
3994
|
+
}
|
|
3995
|
+
_ensureSpectrogramController() {
|
|
3996
|
+
if (!this._spectrogramController) {
|
|
3997
|
+
this._spectrogramController = new SpectrogramController(
|
|
3998
|
+
this,
|
|
3999
|
+
() => new Worker(new URL("@dawcore/spectrogram/worker/spectrogram.worker", import.meta.url), {
|
|
4000
|
+
type: "module"
|
|
4001
|
+
})
|
|
4002
|
+
);
|
|
4003
|
+
if (this._spectrogramConfig) {
|
|
4004
|
+
this._spectrogramController.setEditorConfig(this._spectrogramConfig);
|
|
4005
|
+
}
|
|
4006
|
+
if (this._spectrogramColorMap) {
|
|
4007
|
+
this._spectrogramController.setEditorColorMap(this._spectrogramColorMap);
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
4010
|
+
return this._spectrogramController;
|
|
4011
|
+
}
|
|
4012
|
+
/** Called by <daw-spectrogram> after transferControlToOffscreen. */
|
|
4013
|
+
_spectrogramRegisterCanvas(reg) {
|
|
4014
|
+
this._ensureSpectrogramController().registerCanvas(reg);
|
|
4015
|
+
}
|
|
4016
|
+
/** Called by <daw-spectrogram> on chunk unmount / element disconnect. */
|
|
4017
|
+
_spectrogramUnregisterCanvas(canvasId) {
|
|
4018
|
+
this._spectrogramController?.unregisterCanvas(canvasId);
|
|
4019
|
+
}
|
|
4020
|
+
/**
|
|
4021
|
+
* Push a clip's decoded audio into the spectrogram controller. No-op
|
|
4022
|
+
* unless the track is in spectrogram render-mode and the controller
|
|
4023
|
+
* already exists (it bootstraps from canvas registration).
|
|
4024
|
+
*/
|
|
4025
|
+
_maybeRegisterSpectrogramClipAudio(trackId, clip) {
|
|
4026
|
+
const descriptor = this._tracks.get(trackId);
|
|
4027
|
+
if (descriptor?.renderMode !== "spectrogram") return;
|
|
4028
|
+
const buffer = clip.audioBuffer ?? this._clipBuffers.get(clip.id);
|
|
4029
|
+
if (!buffer) return;
|
|
4030
|
+
const channelData = [];
|
|
4031
|
+
for (let i = 0; i < buffer.numberOfChannels; i++) {
|
|
4032
|
+
channelData.push(buffer.getChannelData(i));
|
|
4033
|
+
}
|
|
4034
|
+
this._ensureSpectrogramController().registerClipAudio({
|
|
4035
|
+
clipId: clip.id,
|
|
4036
|
+
trackId,
|
|
4037
|
+
channelData,
|
|
4038
|
+
sampleRate: buffer.sampleRate,
|
|
4039
|
+
durationSamples: clip.durationSamples,
|
|
4040
|
+
offsetSamples: clip.offsetSamples
|
|
4041
|
+
});
|
|
4042
|
+
}
|
|
3824
4043
|
get ticksPerPixel() {
|
|
3825
4044
|
return this._ticksPerPixel;
|
|
3826
4045
|
}
|
|
@@ -4061,6 +4280,8 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
4061
4280
|
this._clipOffsets.clear();
|
|
4062
4281
|
this._peakPipeline.terminate();
|
|
4063
4282
|
this._minSamplesPerPixel = 0;
|
|
4283
|
+
this._spectrogramController?.dispose();
|
|
4284
|
+
this._spectrogramController = null;
|
|
4064
4285
|
try {
|
|
4065
4286
|
this._disposeEngine();
|
|
4066
4287
|
} catch (err) {
|
|
@@ -4089,6 +4310,23 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
4089
4310
|
}
|
|
4090
4311
|
}
|
|
4091
4312
|
}
|
|
4313
|
+
updated(_changed) {
|
|
4314
|
+
if (this._spectrogramController) {
|
|
4315
|
+
const vs = this._viewport.visibleStart;
|
|
4316
|
+
const ve = this._viewport.visibleEnd;
|
|
4317
|
+
if (Number.isFinite(vs) && Number.isFinite(ve)) {
|
|
4318
|
+
const span = ve - vs;
|
|
4319
|
+
const bufferPad = span * 0.25;
|
|
4320
|
+
this._spectrogramController.setViewport({
|
|
4321
|
+
visibleStartPx: vs,
|
|
4322
|
+
visibleEndPx: ve,
|
|
4323
|
+
bufferStartPx: Math.max(0, vs - bufferPad),
|
|
4324
|
+
bufferEndPx: ve + bufferPad,
|
|
4325
|
+
samplesPerPixel: this._renderSpp
|
|
4326
|
+
});
|
|
4327
|
+
}
|
|
4328
|
+
}
|
|
4329
|
+
}
|
|
4092
4330
|
_onTrackRemoved(trackId) {
|
|
4093
4331
|
this._trackElements.delete(trackId);
|
|
4094
4332
|
const removedTrack = this._engineTracks.get(trackId);
|
|
@@ -4098,6 +4336,7 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
4098
4336
|
this._clipBuffers.delete(clip.id);
|
|
4099
4337
|
this._clipOffsets.delete(clip.id);
|
|
4100
4338
|
nextPeaks.delete(clip.id);
|
|
4339
|
+
this._spectrogramController?.unregisterClipAudio(clip.id);
|
|
4101
4340
|
}
|
|
4102
4341
|
this._peaksData = nextPeaks;
|
|
4103
4342
|
}
|
|
@@ -4112,11 +4351,23 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
4112
4351
|
this._engine.removeTrack(trackId);
|
|
4113
4352
|
}
|
|
4114
4353
|
this._minSamplesPerPixel = this._peakPipeline.getMaxCachedScale(this._clipBuffers);
|
|
4354
|
+
this._disposeSpectrogramControllerIfEmpty();
|
|
4115
4355
|
if (nextEngine.size === 0) {
|
|
4116
4356
|
this._currentTime = 0;
|
|
4117
4357
|
this._stopPlayhead();
|
|
4118
4358
|
}
|
|
4119
4359
|
}
|
|
4360
|
+
/** Drop the controller when no spectrogram tracks remain. */
|
|
4361
|
+
_disposeSpectrogramControllerIfEmpty() {
|
|
4362
|
+
if (!this._spectrogramController) return;
|
|
4363
|
+
const stillNeeded = Array.from(this._tracks.values()).some(
|
|
4364
|
+
(d) => d.renderMode === "spectrogram"
|
|
4365
|
+
);
|
|
4366
|
+
if (!stillNeeded) {
|
|
4367
|
+
this._spectrogramController.dispose();
|
|
4368
|
+
this._spectrogramController = null;
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4120
4371
|
_onClipRemovedFromDom(clipEl) {
|
|
4121
4372
|
const clipId = clipEl.clipId;
|
|
4122
4373
|
for (const [trackId, t] of this._engineTracks.entries()) {
|
|
@@ -4158,6 +4409,7 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
4158
4409
|
});
|
|
4159
4410
|
}
|
|
4160
4411
|
this._commitTrackChange(trackId, updatedTrack);
|
|
4412
|
+
this._maybeRegisterSpectrogramClipAudio(trackId, clip);
|
|
4161
4413
|
this.dispatchEvent(
|
|
4162
4414
|
new CustomEvent("daw-clip-ready", {
|
|
4163
4415
|
bubbles: true,
|
|
@@ -4332,6 +4584,7 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
4332
4584
|
nextPeaks.delete(clipId);
|
|
4333
4585
|
this._peaksData = nextPeaks;
|
|
4334
4586
|
this._clipOffsets.delete(clipId);
|
|
4587
|
+
this._spectrogramController?.unregisterClipAudio(clipId);
|
|
4335
4588
|
}
|
|
4336
4589
|
/**
|
|
4337
4590
|
* Recompute duration and forward an updated track to the engine. Single
|
|
@@ -4614,6 +4867,9 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
4614
4867
|
track.id = trackId;
|
|
4615
4868
|
this._engineTracks = new Map(this._engineTracks).set(trackId, track);
|
|
4616
4869
|
this._recomputeDuration();
|
|
4870
|
+
for (const c of clips) {
|
|
4871
|
+
this._maybeRegisterSpectrogramClipAudio(trackId, c);
|
|
4872
|
+
}
|
|
4617
4873
|
const engine = await this._ensureEngine();
|
|
4618
4874
|
engine.setTracks([...this._engineTracks.values()]);
|
|
4619
4875
|
this.dispatchEvent(
|
|
@@ -5425,19 +5681,34 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
5425
5681
|
.visibleEnd=${this._viewport.visibleEnd}
|
|
5426
5682
|
.originX=${clipLeft}
|
|
5427
5683
|
?selected=${t.trackId === this._selectedTrackId}
|
|
5428
|
-
></daw-piano-roll>` : channels.map(
|
|
5684
|
+
></daw-piano-roll>` : t.descriptor?.renderMode === "spectrogram" ? channels.map(
|
|
5685
|
+
(_chPeaks, chIdx) => html9`<daw-spectrogram
|
|
5686
|
+
style="position:absolute;left:0;top:${hdrH + chIdx * chH}px;height:${chH}px;width:${peakData?.length ?? width}px;"
|
|
5687
|
+
.clipId=${clip.id}
|
|
5688
|
+
.trackId=${t.trackId}
|
|
5689
|
+
.channelIndex=${chIdx}
|
|
5690
|
+
.length=${peakData?.length ?? width}
|
|
5691
|
+
.waveHeight=${chH}
|
|
5692
|
+
.samplesPerPixel=${this._renderSpp}
|
|
5693
|
+
.sampleRate=${this.effectiveSampleRate}
|
|
5694
|
+
.clipOffsetSeconds=${(clip.offsetSamples ?? 0) / this.effectiveSampleRate}
|
|
5695
|
+
.visibleStart=${this._viewport.visibleStart}
|
|
5696
|
+
.visibleEnd=${this._viewport.visibleEnd}
|
|
5697
|
+
.originX=${clipLeft}
|
|
5698
|
+
></daw-spectrogram>`
|
|
5699
|
+
) : channels.map(
|
|
5429
5700
|
(chPeaks, chIdx) => html9` <daw-waveform
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5701
|
+
style="position:absolute;left:0;top:${hdrH + chIdx * chH}px;"
|
|
5702
|
+
.peaks=${chPeaks}
|
|
5703
|
+
.length=${peakData?.length ?? width}
|
|
5704
|
+
.waveHeight=${chH}
|
|
5705
|
+
.barWidth=${this.barWidth}
|
|
5706
|
+
.barGap=${this.barGap}
|
|
5707
|
+
.visibleStart=${this._viewport.visibleStart}
|
|
5708
|
+
.visibleEnd=${this._viewport.visibleEnd}
|
|
5709
|
+
.originX=${clipLeft}
|
|
5710
|
+
.segments=${clipSegments}
|
|
5711
|
+
></daw-waveform>`
|
|
5441
5712
|
)}
|
|
5442
5713
|
${this.interactiveClips ? html9` <div
|
|
5443
5714
|
class="clip-boundary"
|
|
@@ -5545,6 +5816,12 @@ __decorateClass([
|
|
|
5545
5816
|
__decorateClass([
|
|
5546
5817
|
property8({ type: Boolean, attribute: "indefinite-playback" })
|
|
5547
5818
|
], DawEditorElement.prototype, "indefinitePlayback", 2);
|
|
5819
|
+
__decorateClass([
|
|
5820
|
+
property8({ attribute: false, noAccessor: true })
|
|
5821
|
+
], DawEditorElement.prototype, "spectrogramConfig", 1);
|
|
5822
|
+
__decorateClass([
|
|
5823
|
+
property8({ attribute: false, noAccessor: true })
|
|
5824
|
+
], DawEditorElement.prototype, "spectrogramColorMap", 1);
|
|
5548
5825
|
__decorateClass([
|
|
5549
5826
|
property8({ type: String, attribute: "scale-mode" })
|
|
5550
5827
|
], DawEditorElement.prototype, "scaleMode", 2);
|
|
@@ -6123,6 +6400,187 @@ __decorateClass([
|
|
|
6123
6400
|
DawKeyboardShortcutsElement = __decorateClass([
|
|
6124
6401
|
customElement16("daw-keyboard-shortcuts")
|
|
6125
6402
|
], DawKeyboardShortcutsElement);
|
|
6403
|
+
|
|
6404
|
+
// src/elements/daw-spectrogram.ts
|
|
6405
|
+
import { LitElement as LitElement14, html as html13, css as css13 } from "lit";
|
|
6406
|
+
import { customElement as customElement17, property as property12 } from "lit/decorators.js";
|
|
6407
|
+
var MAX_CANVAS_WIDTH5 = 1e3;
|
|
6408
|
+
var DawSpectrogramElement = class extends LitElement14 {
|
|
6409
|
+
constructor() {
|
|
6410
|
+
super(...arguments);
|
|
6411
|
+
this.clipId = "";
|
|
6412
|
+
this.trackId = "";
|
|
6413
|
+
this.channelIndex = 0;
|
|
6414
|
+
this.length = 0;
|
|
6415
|
+
this.waveHeight = 128;
|
|
6416
|
+
this._samplesPerPixel = 1024;
|
|
6417
|
+
this._sampleRate = 44100;
|
|
6418
|
+
this.clipOffsetSeconds = 0;
|
|
6419
|
+
this.visibleStart = -Infinity;
|
|
6420
|
+
this.visibleEnd = Infinity;
|
|
6421
|
+
this.originX = 0;
|
|
6422
|
+
this._canvases = [];
|
|
6423
|
+
this._registeredCanvasIds = [];
|
|
6424
|
+
}
|
|
6425
|
+
get samplesPerPixel() {
|
|
6426
|
+
return this._samplesPerPixel;
|
|
6427
|
+
}
|
|
6428
|
+
set samplesPerPixel(value) {
|
|
6429
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
6430
|
+
console.warn("[dawcore] daw-spectrogram samplesPerPixel " + value + " is invalid \u2014 ignored");
|
|
6431
|
+
return;
|
|
6432
|
+
}
|
|
6433
|
+
const old = this._samplesPerPixel;
|
|
6434
|
+
this._samplesPerPixel = value;
|
|
6435
|
+
this.requestUpdate("samplesPerPixel", old);
|
|
6436
|
+
}
|
|
6437
|
+
get sampleRate() {
|
|
6438
|
+
return this._sampleRate;
|
|
6439
|
+
}
|
|
6440
|
+
set sampleRate(value) {
|
|
6441
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
6442
|
+
console.warn("[dawcore] daw-spectrogram sampleRate " + value + " is invalid \u2014 ignored");
|
|
6443
|
+
return;
|
|
6444
|
+
}
|
|
6445
|
+
const old = this._sampleRate;
|
|
6446
|
+
this._sampleRate = value;
|
|
6447
|
+
this.requestUpdate("sampleRate", old);
|
|
6448
|
+
}
|
|
6449
|
+
/**
|
|
6450
|
+
* Walk up to the editor host. `closest('daw-editor')` does NOT cross
|
|
6451
|
+
* shadow boundaries — and this element lives inside the editor's shadow
|
|
6452
|
+
* DOM — so use getRootNode().host to step out.
|
|
6453
|
+
*/
|
|
6454
|
+
_findHostEditor() {
|
|
6455
|
+
const root = this.getRootNode();
|
|
6456
|
+
const host = root instanceof ShadowRoot ? root.host : null;
|
|
6457
|
+
if (!host) return null;
|
|
6458
|
+
if (host.tagName === "DAW-EDITOR") return host;
|
|
6459
|
+
return host.closest("daw-editor");
|
|
6460
|
+
}
|
|
6461
|
+
willUpdate(changed) {
|
|
6462
|
+
const layoutChanged = changed.has("length") || changed.has("waveHeight") || changed.has("samplesPerPixel") || changed.has("clipId") || changed.has("channelIndex");
|
|
6463
|
+
if (layoutChanged) {
|
|
6464
|
+
this._rebuildChunks();
|
|
6465
|
+
}
|
|
6466
|
+
}
|
|
6467
|
+
_rebuildChunks() {
|
|
6468
|
+
this._unregisterAllCanvases();
|
|
6469
|
+
this._canvases = [];
|
|
6470
|
+
if (this.length <= 0) return;
|
|
6471
|
+
const chunkCount = Math.ceil(this.length / MAX_CANVAS_WIDTH5);
|
|
6472
|
+
for (let i = 0; i < chunkCount; i++) {
|
|
6473
|
+
const widthPx = Math.min(MAX_CANVAS_WIDTH5, this.length - i * MAX_CANVAS_WIDTH5);
|
|
6474
|
+
const canvas = document.createElement("canvas");
|
|
6475
|
+
canvas.style.left = i * MAX_CANVAS_WIDTH5 + "px";
|
|
6476
|
+
canvas.style.width = widthPx + "px";
|
|
6477
|
+
const dpr = window.devicePixelRatio || 1;
|
|
6478
|
+
canvas.width = widthPx * dpr;
|
|
6479
|
+
canvas.height = this.waveHeight * dpr;
|
|
6480
|
+
this._canvases.push(canvas);
|
|
6481
|
+
}
|
|
6482
|
+
}
|
|
6483
|
+
updated(_changed) {
|
|
6484
|
+
if (this._registeredCanvasIds.length === 0 && this._canvases.length > 0) {
|
|
6485
|
+
requestAnimationFrame(() => this._registerCanvases());
|
|
6486
|
+
}
|
|
6487
|
+
}
|
|
6488
|
+
_registerCanvases() {
|
|
6489
|
+
const editor = this._findHostEditor();
|
|
6490
|
+
if (!editor || typeof editor._spectrogramRegisterCanvas !== "function") return;
|
|
6491
|
+
for (let i = 0; i < this._canvases.length; i++) {
|
|
6492
|
+
const canvas = this._canvases[i];
|
|
6493
|
+
const canvasId = this.clipId + "-ch" + this.channelIndex + "-chunk" + i;
|
|
6494
|
+
let offscreen;
|
|
6495
|
+
try {
|
|
6496
|
+
offscreen = canvas.transferControlToOffscreen();
|
|
6497
|
+
} catch (err) {
|
|
6498
|
+
console.warn(
|
|
6499
|
+
"[dawcore] daw-spectrogram transferControlToOffscreen failed for " + canvasId + ": " + (err instanceof Error ? err.message : String(err))
|
|
6500
|
+
);
|
|
6501
|
+
continue;
|
|
6502
|
+
}
|
|
6503
|
+
editor._spectrogramRegisterCanvas({
|
|
6504
|
+
canvasId,
|
|
6505
|
+
canvas: offscreen,
|
|
6506
|
+
clipId: this.clipId,
|
|
6507
|
+
trackId: this.trackId,
|
|
6508
|
+
channelIndex: this.channelIndex,
|
|
6509
|
+
chunkIndex: i,
|
|
6510
|
+
globalPixelOffset: this.originX + i * MAX_CANVAS_WIDTH5,
|
|
6511
|
+
widthPx: parseFloat(canvas.style.width),
|
|
6512
|
+
heightPx: this.waveHeight
|
|
6513
|
+
});
|
|
6514
|
+
this._registeredCanvasIds.push(canvasId);
|
|
6515
|
+
}
|
|
6516
|
+
}
|
|
6517
|
+
_unregisterAllCanvases() {
|
|
6518
|
+
const editor = this._findHostEditor();
|
|
6519
|
+
if (editor && typeof editor._spectrogramUnregisterCanvas === "function") {
|
|
6520
|
+
for (const id of this._registeredCanvasIds) {
|
|
6521
|
+
editor._spectrogramUnregisterCanvas(id);
|
|
6522
|
+
}
|
|
6523
|
+
}
|
|
6524
|
+
this._registeredCanvasIds = [];
|
|
6525
|
+
}
|
|
6526
|
+
disconnectedCallback() {
|
|
6527
|
+
super.disconnectedCallback();
|
|
6528
|
+
this._unregisterAllCanvases();
|
|
6529
|
+
}
|
|
6530
|
+
render() {
|
|
6531
|
+
return html13`${this._canvases.map((c) => c)}`;
|
|
6532
|
+
}
|
|
6533
|
+
};
|
|
6534
|
+
DawSpectrogramElement.styles = css13`
|
|
6535
|
+
:host {
|
|
6536
|
+
display: block;
|
|
6537
|
+
position: relative;
|
|
6538
|
+
background: var(--daw-spectrogram-background, #000);
|
|
6539
|
+
}
|
|
6540
|
+
canvas {
|
|
6541
|
+
position: absolute;
|
|
6542
|
+
top: 0;
|
|
6543
|
+
left: 0;
|
|
6544
|
+
height: 100%;
|
|
6545
|
+
pointer-events: none;
|
|
6546
|
+
}
|
|
6547
|
+
`;
|
|
6548
|
+
__decorateClass([
|
|
6549
|
+
property12({ attribute: false })
|
|
6550
|
+
], DawSpectrogramElement.prototype, "clipId", 2);
|
|
6551
|
+
__decorateClass([
|
|
6552
|
+
property12({ attribute: false })
|
|
6553
|
+
], DawSpectrogramElement.prototype, "trackId", 2);
|
|
6554
|
+
__decorateClass([
|
|
6555
|
+
property12({ type: Number, attribute: false })
|
|
6556
|
+
], DawSpectrogramElement.prototype, "channelIndex", 2);
|
|
6557
|
+
__decorateClass([
|
|
6558
|
+
property12({ type: Number, attribute: false })
|
|
6559
|
+
], DawSpectrogramElement.prototype, "length", 2);
|
|
6560
|
+
__decorateClass([
|
|
6561
|
+
property12({ type: Number, attribute: false })
|
|
6562
|
+
], DawSpectrogramElement.prototype, "waveHeight", 2);
|
|
6563
|
+
__decorateClass([
|
|
6564
|
+
property12({ type: Number, attribute: false, noAccessor: true })
|
|
6565
|
+
], DawSpectrogramElement.prototype, "samplesPerPixel", 1);
|
|
6566
|
+
__decorateClass([
|
|
6567
|
+
property12({ type: Number, attribute: false, noAccessor: true })
|
|
6568
|
+
], DawSpectrogramElement.prototype, "sampleRate", 1);
|
|
6569
|
+
__decorateClass([
|
|
6570
|
+
property12({ type: Number, attribute: false })
|
|
6571
|
+
], DawSpectrogramElement.prototype, "clipOffsetSeconds", 2);
|
|
6572
|
+
__decorateClass([
|
|
6573
|
+
property12({ type: Number, attribute: false })
|
|
6574
|
+
], DawSpectrogramElement.prototype, "visibleStart", 2);
|
|
6575
|
+
__decorateClass([
|
|
6576
|
+
property12({ type: Number, attribute: false })
|
|
6577
|
+
], DawSpectrogramElement.prototype, "visibleEnd", 2);
|
|
6578
|
+
__decorateClass([
|
|
6579
|
+
property12({ type: Number, attribute: false })
|
|
6580
|
+
], DawSpectrogramElement.prototype, "originX", 2);
|
|
6581
|
+
DawSpectrogramElement = __decorateClass([
|
|
6582
|
+
customElement17("daw-spectrogram")
|
|
6583
|
+
], DawSpectrogramElement);
|
|
6126
6584
|
export {
|
|
6127
6585
|
AudioResumeController,
|
|
6128
6586
|
ClipPointerHandler,
|
|
@@ -6137,6 +6595,7 @@ export {
|
|
|
6137
6595
|
DawRecordButtonElement,
|
|
6138
6596
|
DawRulerElement,
|
|
6139
6597
|
DawSelectionElement,
|
|
6598
|
+
DawSpectrogramElement,
|
|
6140
6599
|
DawStopButtonElement,
|
|
6141
6600
|
DawTrackControlsElement,
|
|
6142
6601
|
DawTrackElement,
|
|
@@ -6144,6 +6603,7 @@ export {
|
|
|
6144
6603
|
DawTransportElement,
|
|
6145
6604
|
DawWaveformElement,
|
|
6146
6605
|
RecordingController,
|
|
6606
|
+
SpectrogramController,
|
|
6147
6607
|
isDomClip,
|
|
6148
6608
|
splitAtPlayhead
|
|
6149
6609
|
};
|