@dawcore/components 0.0.15 → 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 +353 -85
- package/dist/index.d.ts +353 -85
- package/dist/index.js +1202 -199
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1190 -189
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -45,11 +45,13 @@ __export(index_exports, {
|
|
|
45
45
|
DawGridElement: () => DawGridElement,
|
|
46
46
|
DawKeyboardShortcutsElement: () => DawKeyboardShortcutsElement,
|
|
47
47
|
DawPauseButtonElement: () => DawPauseButtonElement,
|
|
48
|
+
DawPianoRollElement: () => DawPianoRollElement,
|
|
48
49
|
DawPlayButtonElement: () => DawPlayButtonElement,
|
|
49
50
|
DawPlayheadElement: () => DawPlayheadElement,
|
|
50
51
|
DawRecordButtonElement: () => DawRecordButtonElement,
|
|
51
52
|
DawRulerElement: () => DawRulerElement,
|
|
52
53
|
DawSelectionElement: () => DawSelectionElement,
|
|
54
|
+
DawSpectrogramElement: () => DawSpectrogramElement,
|
|
53
55
|
DawStopButtonElement: () => DawStopButtonElement,
|
|
54
56
|
DawTrackControlsElement: () => DawTrackControlsElement,
|
|
55
57
|
DawTrackElement: () => DawTrackElement,
|
|
@@ -57,6 +59,7 @@ __export(index_exports, {
|
|
|
57
59
|
DawTransportElement: () => DawTransportElement,
|
|
58
60
|
DawWaveformElement: () => DawWaveformElement,
|
|
59
61
|
RecordingController: () => RecordingController,
|
|
62
|
+
SpectrogramController: () => SpectrogramController,
|
|
60
63
|
isDomClip: () => isDomClip,
|
|
61
64
|
splitAtPlayhead: () => splitAtPlayhead
|
|
62
65
|
});
|
|
@@ -79,11 +82,48 @@ var DawClipElement = class extends import_lit.LitElement {
|
|
|
79
82
|
this.fadeIn = 0;
|
|
80
83
|
this.fadeOut = 0;
|
|
81
84
|
this.fadeType = "linear";
|
|
85
|
+
this.midiNotes = null;
|
|
86
|
+
this._midiChannel = null;
|
|
87
|
+
this._midiProgram = null;
|
|
82
88
|
this.clipId = crypto.randomUUID();
|
|
83
89
|
// Removal is detected by the editor's MutationObserver — detached elements
|
|
84
90
|
// cannot bubble events to ancestors.
|
|
85
91
|
this._hasRendered = false;
|
|
86
92
|
}
|
|
93
|
+
get midiChannel() {
|
|
94
|
+
return this._midiChannel;
|
|
95
|
+
}
|
|
96
|
+
set midiChannel(value) {
|
|
97
|
+
const old = this._midiChannel;
|
|
98
|
+
if (value === null) {
|
|
99
|
+
this._midiChannel = null;
|
|
100
|
+
this.requestUpdate("midiChannel", old);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0 || value > 15) {
|
|
104
|
+
console.warn("[dawcore] daw-clip midi-channel " + value + " is out of range 0-15 \u2014 ignored");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this._midiChannel = value;
|
|
108
|
+
this.requestUpdate("midiChannel", old);
|
|
109
|
+
}
|
|
110
|
+
get midiProgram() {
|
|
111
|
+
return this._midiProgram;
|
|
112
|
+
}
|
|
113
|
+
set midiProgram(value) {
|
|
114
|
+
const old = this._midiProgram;
|
|
115
|
+
if (value === null) {
|
|
116
|
+
this._midiProgram = null;
|
|
117
|
+
this.requestUpdate("midiProgram", old);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0 || value > 127) {
|
|
121
|
+
console.warn("[dawcore] daw-clip midi-program " + value + " is out of range 0-127 \u2014 ignored");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
this._midiProgram = value;
|
|
125
|
+
this.requestUpdate("midiProgram", old);
|
|
126
|
+
}
|
|
87
127
|
// Light DOM — no visual rendering, just a data container
|
|
88
128
|
createRenderRoot() {
|
|
89
129
|
return this;
|
|
@@ -115,7 +155,10 @@ var DawClipElement = class extends import_lit.LitElement {
|
|
|
115
155
|
"name",
|
|
116
156
|
"fadeIn",
|
|
117
157
|
"fadeOut",
|
|
118
|
-
"fadeType"
|
|
158
|
+
"fadeType",
|
|
159
|
+
"midiNotes",
|
|
160
|
+
"midiChannel",
|
|
161
|
+
"midiProgram"
|
|
119
162
|
];
|
|
120
163
|
if (clipProps.some((p) => changed.has(p))) {
|
|
121
164
|
const trackEl = this.closest("daw-track");
|
|
@@ -162,6 +205,15 @@ __decorateClass([
|
|
|
162
205
|
__decorateClass([
|
|
163
206
|
(0, import_decorators.property)({ attribute: "fade-type" })
|
|
164
207
|
], DawClipElement.prototype, "fadeType", 2);
|
|
208
|
+
__decorateClass([
|
|
209
|
+
(0, import_decorators.property)({ attribute: false })
|
|
210
|
+
], DawClipElement.prototype, "midiNotes", 2);
|
|
211
|
+
__decorateClass([
|
|
212
|
+
(0, import_decorators.property)({ type: Number, attribute: "midi-channel", noAccessor: true })
|
|
213
|
+
], DawClipElement.prototype, "midiChannel", 1);
|
|
214
|
+
__decorateClass([
|
|
215
|
+
(0, import_decorators.property)({ type: Number, attribute: "midi-program", noAccessor: true })
|
|
216
|
+
], DawClipElement.prototype, "midiProgram", 1);
|
|
165
217
|
DawClipElement = __decorateClass([
|
|
166
218
|
(0, import_decorators.customElement)("daw-clip")
|
|
167
219
|
], DawClipElement);
|
|
@@ -178,6 +230,8 @@ var DawTrackElement = class extends import_lit2.LitElement {
|
|
|
178
230
|
this.pan = 0;
|
|
179
231
|
this.muted = false;
|
|
180
232
|
this.soloed = false;
|
|
233
|
+
this.renderMode = "waveform";
|
|
234
|
+
this.spectrogramConfig = null;
|
|
181
235
|
this.trackId = crypto.randomUUID();
|
|
182
236
|
// Track removal is detected by the editor's MutationObserver,
|
|
183
237
|
// not by dispatching from disconnectedCallback (detached elements
|
|
@@ -205,7 +259,16 @@ var DawTrackElement = class extends import_lit2.LitElement {
|
|
|
205
259
|
this._hasRendered = true;
|
|
206
260
|
return;
|
|
207
261
|
}
|
|
208
|
-
const trackProps = [
|
|
262
|
+
const trackProps = [
|
|
263
|
+
"volume",
|
|
264
|
+
"pan",
|
|
265
|
+
"muted",
|
|
266
|
+
"soloed",
|
|
267
|
+
"src",
|
|
268
|
+
"name",
|
|
269
|
+
"renderMode",
|
|
270
|
+
"spectrogramConfig"
|
|
271
|
+
];
|
|
209
272
|
const hasTrackChange = trackProps.some((p) => changed.has(p));
|
|
210
273
|
if (hasTrackChange) {
|
|
211
274
|
this.dispatchEvent(
|
|
@@ -236,6 +299,12 @@ __decorateClass([
|
|
|
236
299
|
__decorateClass([
|
|
237
300
|
(0, import_decorators2.property)({ type: Boolean })
|
|
238
301
|
], DawTrackElement.prototype, "soloed", 2);
|
|
302
|
+
__decorateClass([
|
|
303
|
+
(0, import_decorators2.property)({ attribute: "render-mode" })
|
|
304
|
+
], DawTrackElement.prototype, "renderMode", 2);
|
|
305
|
+
__decorateClass([
|
|
306
|
+
(0, import_decorators2.property)({ attribute: false })
|
|
307
|
+
], DawTrackElement.prototype, "spectrogramConfig", 2);
|
|
239
308
|
DawTrackElement = __decorateClass([
|
|
240
309
|
(0, import_decorators2.customElement)("daw-track")
|
|
241
310
|
], DawTrackElement);
|
|
@@ -585,9 +654,229 @@ DawWaveformElement = __decorateClass([
|
|
|
585
654
|
(0, import_decorators3.customElement)("daw-waveform")
|
|
586
655
|
], DawWaveformElement);
|
|
587
656
|
|
|
588
|
-
// src/elements/daw-
|
|
657
|
+
// src/elements/daw-piano-roll.ts
|
|
589
658
|
var import_lit4 = require("lit");
|
|
590
659
|
var import_decorators4 = require("lit/decorators.js");
|
|
660
|
+
var MAX_CANVAS_WIDTH2 = 1e3;
|
|
661
|
+
var LAYOUT_PROPS2 = /* @__PURE__ */ new Set([
|
|
662
|
+
"length",
|
|
663
|
+
"waveHeight",
|
|
664
|
+
"samplesPerPixel",
|
|
665
|
+
"sampleRate",
|
|
666
|
+
"clipOffsetSeconds",
|
|
667
|
+
"midiNotes",
|
|
668
|
+
"selected"
|
|
669
|
+
]);
|
|
670
|
+
var DawPianoRollElement = class extends import_lit4.LitElement {
|
|
671
|
+
constructor() {
|
|
672
|
+
super(...arguments);
|
|
673
|
+
this.midiNotes = [];
|
|
674
|
+
this.length = 0;
|
|
675
|
+
this.waveHeight = 128;
|
|
676
|
+
this._samplesPerPixel = 1024;
|
|
677
|
+
this._sampleRate = 48e3;
|
|
678
|
+
this.clipOffsetSeconds = 0;
|
|
679
|
+
this.visibleStart = -Infinity;
|
|
680
|
+
this.visibleEnd = Infinity;
|
|
681
|
+
this.originX = 0;
|
|
682
|
+
this.selected = false;
|
|
683
|
+
this._rafHandle = null;
|
|
684
|
+
}
|
|
685
|
+
get samplesPerPixel() {
|
|
686
|
+
return this._samplesPerPixel;
|
|
687
|
+
}
|
|
688
|
+
set samplesPerPixel(value) {
|
|
689
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
690
|
+
console.warn("[dawcore] daw-piano-roll samplesPerPixel " + value + " is invalid \u2014 ignored");
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
const old = this._samplesPerPixel;
|
|
694
|
+
this._samplesPerPixel = value;
|
|
695
|
+
this.requestUpdate("samplesPerPixel", old);
|
|
696
|
+
}
|
|
697
|
+
get sampleRate() {
|
|
698
|
+
return this._sampleRate;
|
|
699
|
+
}
|
|
700
|
+
set sampleRate(value) {
|
|
701
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
702
|
+
console.warn("[dawcore] daw-piano-roll sampleRate " + value + " is invalid \u2014 ignored");
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
const old = this._sampleRate;
|
|
706
|
+
this._sampleRate = value;
|
|
707
|
+
this.requestUpdate("sampleRate", old);
|
|
708
|
+
}
|
|
709
|
+
_scheduleDraw() {
|
|
710
|
+
if (this._rafHandle !== null) return;
|
|
711
|
+
this._rafHandle = requestAnimationFrame(() => {
|
|
712
|
+
this._rafHandle = null;
|
|
713
|
+
this._draw();
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
willUpdate(_changed) {
|
|
717
|
+
this._scheduleDraw();
|
|
718
|
+
}
|
|
719
|
+
updated(changedProperties) {
|
|
720
|
+
const needsFullDraw = [...changedProperties.keys()].some((key) => LAYOUT_PROPS2.has(key));
|
|
721
|
+
if (needsFullDraw) return;
|
|
722
|
+
if (changedProperties.has("visibleStart") || changedProperties.has("visibleEnd") || changedProperties.has("originX")) {
|
|
723
|
+
this._scheduleDraw();
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
_getPitchRange() {
|
|
727
|
+
if (this.midiNotes.length === 0) return { minMidi: 0, maxMidi: 127 };
|
|
728
|
+
let min = 127;
|
|
729
|
+
let max = 0;
|
|
730
|
+
for (const note of this.midiNotes) {
|
|
731
|
+
if (note.midi < min) min = note.midi;
|
|
732
|
+
if (note.midi > max) max = note.midi;
|
|
733
|
+
}
|
|
734
|
+
return {
|
|
735
|
+
minMidi: Math.max(0, min - 1),
|
|
736
|
+
maxMidi: Math.min(127, max + 1)
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
_getNoteColor() {
|
|
740
|
+
const cs = getComputedStyle(this);
|
|
741
|
+
const note = cs.getPropertyValue("--daw-piano-roll-note-color").trim() || "#2a7070";
|
|
742
|
+
const selectedColor = cs.getPropertyValue("--daw-piano-roll-selected-note-color").trim() || "#3d9e9e";
|
|
743
|
+
return this.selected ? selectedColor : note;
|
|
744
|
+
}
|
|
745
|
+
_draw() {
|
|
746
|
+
if (!this.shadowRoot) return;
|
|
747
|
+
const canvases = this.shadowRoot.querySelectorAll("canvas");
|
|
748
|
+
if (canvases.length === 0) return;
|
|
749
|
+
const { minMidi, maxMidi } = this._getPitchRange();
|
|
750
|
+
const noteRange = maxMidi - minMidi + 1;
|
|
751
|
+
const noteHeight = Math.max(2, this.waveHeight / noteRange);
|
|
752
|
+
const pixelsPerSecond = this.sampleRate / this.samplesPerPixel;
|
|
753
|
+
const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
754
|
+
const color = this._getNoteColor();
|
|
755
|
+
for (const canvas of canvases) {
|
|
756
|
+
const chunkIdx = Number(canvas.dataset.index);
|
|
757
|
+
const chunkPixelStart = chunkIdx * MAX_CANVAS_WIDTH2;
|
|
758
|
+
const canvasWidth = canvas.width / dpr;
|
|
759
|
+
const ctx = canvas.getContext("2d");
|
|
760
|
+
if (!ctx) continue;
|
|
761
|
+
ctx.resetTransform();
|
|
762
|
+
ctx.clearRect(
|
|
763
|
+
0,
|
|
764
|
+
0,
|
|
765
|
+
canvas.width,
|
|
766
|
+
canvas.height
|
|
767
|
+
);
|
|
768
|
+
ctx.imageSmoothingEnabled = false;
|
|
769
|
+
ctx.scale(dpr, dpr);
|
|
770
|
+
const chunkStartTime = chunkPixelStart * this.samplesPerPixel / this.sampleRate;
|
|
771
|
+
const chunkEndTime = (chunkPixelStart + canvasWidth) * this.samplesPerPixel / this.sampleRate;
|
|
772
|
+
for (const note of this.midiNotes) {
|
|
773
|
+
const noteStart = note.time - this.clipOffsetSeconds;
|
|
774
|
+
const noteEnd = noteStart + note.duration;
|
|
775
|
+
if (noteEnd <= chunkStartTime || noteStart >= chunkEndTime) continue;
|
|
776
|
+
const x = noteStart * pixelsPerSecond - chunkPixelStart;
|
|
777
|
+
const w = Math.max(2, note.duration * pixelsPerSecond);
|
|
778
|
+
const y = (maxMidi - note.midi) / noteRange * this.waveHeight;
|
|
779
|
+
const alpha = 0.3 + note.velocity * 0.7;
|
|
780
|
+
ctx.fillStyle = color;
|
|
781
|
+
ctx.globalAlpha = alpha;
|
|
782
|
+
ctx.beginPath();
|
|
783
|
+
ctx.roundRect(x, y, w, noteHeight, 1);
|
|
784
|
+
ctx.fill();
|
|
785
|
+
}
|
|
786
|
+
ctx.globalAlpha = 1;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
connectedCallback() {
|
|
790
|
+
super.connectedCallback();
|
|
791
|
+
this._scheduleDraw();
|
|
792
|
+
}
|
|
793
|
+
disconnectedCallback() {
|
|
794
|
+
super.disconnectedCallback();
|
|
795
|
+
if (this._rafHandle !== null) {
|
|
796
|
+
cancelAnimationFrame(this._rafHandle);
|
|
797
|
+
this._rafHandle = null;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
render() {
|
|
801
|
+
if (this.length <= 0)
|
|
802
|
+
return import_lit4.html`<div class="container" style="width: 0; height: ${this.waveHeight}px;"></div>`;
|
|
803
|
+
const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
804
|
+
const visibleIndices = getVisibleChunkIndices(
|
|
805
|
+
this.length,
|
|
806
|
+
MAX_CANVAS_WIDTH2,
|
|
807
|
+
this.visibleStart,
|
|
808
|
+
this.visibleEnd,
|
|
809
|
+
this.originX
|
|
810
|
+
);
|
|
811
|
+
return import_lit4.html`
|
|
812
|
+
<div class="container" style="width: ${this.length}px; height: ${this.waveHeight}px;">
|
|
813
|
+
${visibleIndices.map((i) => {
|
|
814
|
+
const chunkLeft = i * MAX_CANVAS_WIDTH2;
|
|
815
|
+
const chunkWidth = Math.min(this.length - chunkLeft, MAX_CANVAS_WIDTH2);
|
|
816
|
+
return import_lit4.html`<canvas
|
|
817
|
+
data-index=${i}
|
|
818
|
+
width=${chunkWidth * dpr}
|
|
819
|
+
height=${this.waveHeight * dpr}
|
|
820
|
+
style="left: ${chunkLeft}px; width: ${chunkWidth}px; height: ${this.waveHeight}px;"
|
|
821
|
+
></canvas>`;
|
|
822
|
+
})}
|
|
823
|
+
</div>
|
|
824
|
+
`;
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
DawPianoRollElement.styles = import_lit4.css`
|
|
828
|
+
:host {
|
|
829
|
+
display: block;
|
|
830
|
+
position: relative;
|
|
831
|
+
}
|
|
832
|
+
.container {
|
|
833
|
+
position: relative;
|
|
834
|
+
background: var(--daw-piano-roll-background, #1a1a2e);
|
|
835
|
+
}
|
|
836
|
+
canvas {
|
|
837
|
+
position: absolute;
|
|
838
|
+
top: 0;
|
|
839
|
+
image-rendering: pixelated;
|
|
840
|
+
image-rendering: crisp-edges;
|
|
841
|
+
}
|
|
842
|
+
`;
|
|
843
|
+
__decorateClass([
|
|
844
|
+
(0, import_decorators4.property)({ attribute: false })
|
|
845
|
+
], DawPianoRollElement.prototype, "midiNotes", 2);
|
|
846
|
+
__decorateClass([
|
|
847
|
+
(0, import_decorators4.property)({ type: Number, attribute: false })
|
|
848
|
+
], DawPianoRollElement.prototype, "length", 2);
|
|
849
|
+
__decorateClass([
|
|
850
|
+
(0, import_decorators4.property)({ type: Number, attribute: false })
|
|
851
|
+
], DawPianoRollElement.prototype, "waveHeight", 2);
|
|
852
|
+
__decorateClass([
|
|
853
|
+
(0, import_decorators4.property)({ type: Number, attribute: "samples-per-pixel", noAccessor: true })
|
|
854
|
+
], DawPianoRollElement.prototype, "samplesPerPixel", 1);
|
|
855
|
+
__decorateClass([
|
|
856
|
+
(0, import_decorators4.property)({ type: Number, attribute: "sample-rate", noAccessor: true })
|
|
857
|
+
], DawPianoRollElement.prototype, "sampleRate", 1);
|
|
858
|
+
__decorateClass([
|
|
859
|
+
(0, import_decorators4.property)({ type: Number, attribute: false })
|
|
860
|
+
], DawPianoRollElement.prototype, "clipOffsetSeconds", 2);
|
|
861
|
+
__decorateClass([
|
|
862
|
+
(0, import_decorators4.property)({ type: Number, attribute: false })
|
|
863
|
+
], DawPianoRollElement.prototype, "visibleStart", 2);
|
|
864
|
+
__decorateClass([
|
|
865
|
+
(0, import_decorators4.property)({ type: Number, attribute: false })
|
|
866
|
+
], DawPianoRollElement.prototype, "visibleEnd", 2);
|
|
867
|
+
__decorateClass([
|
|
868
|
+
(0, import_decorators4.property)({ type: Number, attribute: false })
|
|
869
|
+
], DawPianoRollElement.prototype, "originX", 2);
|
|
870
|
+
__decorateClass([
|
|
871
|
+
(0, import_decorators4.property)({ type: Boolean, reflect: true })
|
|
872
|
+
], DawPianoRollElement.prototype, "selected", 2);
|
|
873
|
+
DawPianoRollElement = __decorateClass([
|
|
874
|
+
(0, import_decorators4.customElement)("daw-piano-roll")
|
|
875
|
+
], DawPianoRollElement);
|
|
876
|
+
|
|
877
|
+
// src/elements/daw-playhead.ts
|
|
878
|
+
var import_lit5 = require("lit");
|
|
879
|
+
var import_decorators5 = require("lit/decorators.js");
|
|
591
880
|
|
|
592
881
|
// src/controllers/animation-controller.ts
|
|
593
882
|
var AnimationController = class {
|
|
@@ -620,14 +909,14 @@ var AnimationController = class {
|
|
|
620
909
|
};
|
|
621
910
|
|
|
622
911
|
// src/elements/daw-playhead.ts
|
|
623
|
-
var DawPlayheadElement = class extends
|
|
912
|
+
var DawPlayheadElement = class extends import_lit5.LitElement {
|
|
624
913
|
constructor() {
|
|
625
914
|
super(...arguments);
|
|
626
915
|
this._animation = new AnimationController(this);
|
|
627
916
|
this._line = null;
|
|
628
917
|
}
|
|
629
918
|
render() {
|
|
630
|
-
return
|
|
919
|
+
return import_lit5.html`<div></div>`;
|
|
631
920
|
}
|
|
632
921
|
firstUpdated() {
|
|
633
922
|
this._line = this.shadowRoot.querySelector("div");
|
|
@@ -683,7 +972,7 @@ var DawPlayheadElement = class extends import_lit4.LitElement {
|
|
|
683
972
|
}
|
|
684
973
|
}
|
|
685
974
|
};
|
|
686
|
-
DawPlayheadElement.styles =
|
|
975
|
+
DawPlayheadElement.styles = import_lit5.css`
|
|
687
976
|
:host {
|
|
688
977
|
position: absolute;
|
|
689
978
|
top: 0;
|
|
@@ -702,13 +991,13 @@ DawPlayheadElement.styles = import_lit4.css`
|
|
|
702
991
|
}
|
|
703
992
|
`;
|
|
704
993
|
DawPlayheadElement = __decorateClass([
|
|
705
|
-
(0,
|
|
994
|
+
(0, import_decorators5.customElement)("daw-playhead")
|
|
706
995
|
], DawPlayheadElement);
|
|
707
996
|
|
|
708
997
|
// src/elements/daw-transport.ts
|
|
709
|
-
var
|
|
710
|
-
var
|
|
711
|
-
var DawTransportElement = class extends
|
|
998
|
+
var import_lit6 = require("lit");
|
|
999
|
+
var import_decorators6 = require("lit/decorators.js");
|
|
1000
|
+
var DawTransportElement = class extends import_lit6.LitElement {
|
|
712
1001
|
constructor() {
|
|
713
1002
|
super(...arguments);
|
|
714
1003
|
this.for = "";
|
|
@@ -723,25 +1012,25 @@ var DawTransportElement = class extends import_lit5.LitElement {
|
|
|
723
1012
|
}
|
|
724
1013
|
};
|
|
725
1014
|
__decorateClass([
|
|
726
|
-
(0,
|
|
1015
|
+
(0, import_decorators6.property)()
|
|
727
1016
|
], DawTransportElement.prototype, "for", 2);
|
|
728
1017
|
DawTransportElement = __decorateClass([
|
|
729
|
-
(0,
|
|
1018
|
+
(0, import_decorators6.customElement)("daw-transport")
|
|
730
1019
|
], DawTransportElement);
|
|
731
1020
|
|
|
732
1021
|
// src/elements/daw-play-button.ts
|
|
733
|
-
var
|
|
734
|
-
var
|
|
1022
|
+
var import_lit8 = require("lit");
|
|
1023
|
+
var import_decorators7 = require("lit/decorators.js");
|
|
735
1024
|
|
|
736
1025
|
// src/elements/daw-transport-button.ts
|
|
737
|
-
var
|
|
738
|
-
var DawTransportButton = class extends
|
|
1026
|
+
var import_lit7 = require("lit");
|
|
1027
|
+
var DawTransportButton = class extends import_lit7.LitElement {
|
|
739
1028
|
get target() {
|
|
740
1029
|
const transport = this.closest("daw-transport");
|
|
741
1030
|
return transport?.target ?? null;
|
|
742
1031
|
}
|
|
743
1032
|
};
|
|
744
|
-
DawTransportButton.styles =
|
|
1033
|
+
DawTransportButton.styles = import_lit7.css`
|
|
745
1034
|
button {
|
|
746
1035
|
cursor: pointer;
|
|
747
1036
|
background: var(--daw-controls-background, #1a1a2e);
|
|
@@ -793,7 +1082,7 @@ var DawPlayButtonElement = class extends DawTransportButton {
|
|
|
793
1082
|
}
|
|
794
1083
|
}
|
|
795
1084
|
render() {
|
|
796
|
-
return
|
|
1085
|
+
return import_lit8.html`
|
|
797
1086
|
<button part="button" ?disabled=${this._isRecording} @click=${this._onClick}>
|
|
798
1087
|
<slot>Play</slot>
|
|
799
1088
|
</button>
|
|
@@ -811,15 +1100,15 @@ var DawPlayButtonElement = class extends DawTransportButton {
|
|
|
811
1100
|
}
|
|
812
1101
|
};
|
|
813
1102
|
__decorateClass([
|
|
814
|
-
(0,
|
|
1103
|
+
(0, import_decorators7.state)()
|
|
815
1104
|
], DawPlayButtonElement.prototype, "_isRecording", 2);
|
|
816
1105
|
DawPlayButtonElement = __decorateClass([
|
|
817
|
-
(0,
|
|
1106
|
+
(0, import_decorators7.customElement)("daw-play-button")
|
|
818
1107
|
], DawPlayButtonElement);
|
|
819
1108
|
|
|
820
1109
|
// src/elements/daw-pause-button.ts
|
|
821
|
-
var
|
|
822
|
-
var
|
|
1110
|
+
var import_lit9 = require("lit");
|
|
1111
|
+
var import_decorators8 = require("lit/decorators.js");
|
|
823
1112
|
var DawPauseButtonElement = class extends DawTransportButton {
|
|
824
1113
|
constructor() {
|
|
825
1114
|
super(...arguments);
|
|
@@ -833,6 +1122,12 @@ var DawPauseButtonElement = class extends DawTransportButton {
|
|
|
833
1122
|
this._isRecording = false;
|
|
834
1123
|
this._isPaused = false;
|
|
835
1124
|
};
|
|
1125
|
+
this._onRecPause = () => {
|
|
1126
|
+
this._isPaused = true;
|
|
1127
|
+
};
|
|
1128
|
+
this._onRecResume = () => {
|
|
1129
|
+
this._isPaused = false;
|
|
1130
|
+
};
|
|
836
1131
|
}
|
|
837
1132
|
connectedCallback() {
|
|
838
1133
|
super.connectedCallback();
|
|
@@ -843,6 +1138,8 @@ var DawPauseButtonElement = class extends DawTransportButton {
|
|
|
843
1138
|
target.addEventListener("daw-recording-start", this._onRecStart);
|
|
844
1139
|
target.addEventListener("daw-recording-complete", this._onRecEnd);
|
|
845
1140
|
target.addEventListener("daw-recording-error", this._onRecEnd);
|
|
1141
|
+
target.addEventListener("daw-recording-pause", this._onRecPause);
|
|
1142
|
+
target.addEventListener("daw-recording-resume", this._onRecResume);
|
|
846
1143
|
});
|
|
847
1144
|
}
|
|
848
1145
|
disconnectedCallback() {
|
|
@@ -851,11 +1148,13 @@ var DawPauseButtonElement = class extends DawTransportButton {
|
|
|
851
1148
|
this._targetRef.removeEventListener("daw-recording-start", this._onRecStart);
|
|
852
1149
|
this._targetRef.removeEventListener("daw-recording-complete", this._onRecEnd);
|
|
853
1150
|
this._targetRef.removeEventListener("daw-recording-error", this._onRecEnd);
|
|
1151
|
+
this._targetRef.removeEventListener("daw-recording-pause", this._onRecPause);
|
|
1152
|
+
this._targetRef.removeEventListener("daw-recording-resume", this._onRecResume);
|
|
854
1153
|
this._targetRef = null;
|
|
855
1154
|
}
|
|
856
1155
|
}
|
|
857
1156
|
render() {
|
|
858
|
-
return
|
|
1157
|
+
return import_lit9.html`
|
|
859
1158
|
<button part="button" ?data-paused=${this._isPaused} @click=${this._onClick}>
|
|
860
1159
|
<slot>Pause</slot>
|
|
861
1160
|
</button>
|
|
@@ -870,15 +1169,7 @@ var DawPauseButtonElement = class extends DawTransportButton {
|
|
|
870
1169
|
return;
|
|
871
1170
|
}
|
|
872
1171
|
if (this._isRecording) {
|
|
873
|
-
|
|
874
|
-
target.resumeRecording();
|
|
875
|
-
target.play(target.currentTime);
|
|
876
|
-
this._isPaused = false;
|
|
877
|
-
} else {
|
|
878
|
-
target.pauseRecording();
|
|
879
|
-
target.pause();
|
|
880
|
-
this._isPaused = true;
|
|
881
|
-
}
|
|
1172
|
+
target.togglePauseRecording();
|
|
882
1173
|
} else {
|
|
883
1174
|
target.pause();
|
|
884
1175
|
}
|
|
@@ -886,7 +1177,7 @@ var DawPauseButtonElement = class extends DawTransportButton {
|
|
|
886
1177
|
};
|
|
887
1178
|
DawPauseButtonElement.styles = [
|
|
888
1179
|
DawTransportButton.styles,
|
|
889
|
-
|
|
1180
|
+
import_lit9.css`
|
|
890
1181
|
button[data-paused] {
|
|
891
1182
|
background: rgba(255, 255, 255, 0.1);
|
|
892
1183
|
border-color: var(--daw-controls-text, #e0d4c8);
|
|
@@ -894,21 +1185,21 @@ DawPauseButtonElement.styles = [
|
|
|
894
1185
|
`
|
|
895
1186
|
];
|
|
896
1187
|
__decorateClass([
|
|
897
|
-
(0,
|
|
1188
|
+
(0, import_decorators8.state)()
|
|
898
1189
|
], DawPauseButtonElement.prototype, "_isPaused", 2);
|
|
899
1190
|
__decorateClass([
|
|
900
|
-
(0,
|
|
1191
|
+
(0, import_decorators8.state)()
|
|
901
1192
|
], DawPauseButtonElement.prototype, "_isRecording", 2);
|
|
902
1193
|
DawPauseButtonElement = __decorateClass([
|
|
903
|
-
(0,
|
|
1194
|
+
(0, import_decorators8.customElement)("daw-pause-button")
|
|
904
1195
|
], DawPauseButtonElement);
|
|
905
1196
|
|
|
906
1197
|
// src/elements/daw-stop-button.ts
|
|
907
|
-
var
|
|
908
|
-
var
|
|
1198
|
+
var import_lit10 = require("lit");
|
|
1199
|
+
var import_decorators9 = require("lit/decorators.js");
|
|
909
1200
|
var DawStopButtonElement = class extends DawTransportButton {
|
|
910
1201
|
render() {
|
|
911
|
-
return
|
|
1202
|
+
return import_lit10.html`
|
|
912
1203
|
<button part="button" @click=${this._onClick}>
|
|
913
1204
|
<slot>Stop</slot>
|
|
914
1205
|
</button>
|
|
@@ -923,18 +1214,31 @@ var DawStopButtonElement = class extends DawTransportButton {
|
|
|
923
1214
|
return;
|
|
924
1215
|
}
|
|
925
1216
|
if (target.isRecording) {
|
|
926
|
-
target.stopRecording()
|
|
1217
|
+
target.stopRecording().catch((err) => {
|
|
1218
|
+
console.warn("[dawcore] stopRecording failed: " + String(err));
|
|
1219
|
+
}).then(() => {
|
|
1220
|
+
try {
|
|
1221
|
+
target.stop();
|
|
1222
|
+
} catch (err) {
|
|
1223
|
+
console.warn("[dawcore] stop after stopRecording failed: " + String(err));
|
|
1224
|
+
}
|
|
1225
|
+
});
|
|
1226
|
+
} else {
|
|
1227
|
+
try {
|
|
1228
|
+
target.stop();
|
|
1229
|
+
} catch (err) {
|
|
1230
|
+
console.warn("[dawcore] stop failed: " + String(err));
|
|
1231
|
+
}
|
|
927
1232
|
}
|
|
928
|
-
target.stop();
|
|
929
1233
|
}
|
|
930
1234
|
};
|
|
931
1235
|
DawStopButtonElement = __decorateClass([
|
|
932
|
-
(0,
|
|
1236
|
+
(0, import_decorators9.customElement)("daw-stop-button")
|
|
933
1237
|
], DawStopButtonElement);
|
|
934
1238
|
|
|
935
1239
|
// src/elements/daw-editor.ts
|
|
936
|
-
var
|
|
937
|
-
var
|
|
1240
|
+
var import_lit14 = require("lit");
|
|
1241
|
+
var import_decorators12 = require("lit/decorators.js");
|
|
938
1242
|
|
|
939
1243
|
// src/types.ts
|
|
940
1244
|
function isDomClip(desc) {
|
|
@@ -1414,9 +1718,9 @@ var PeakPipeline = class {
|
|
|
1414
1718
|
};
|
|
1415
1719
|
|
|
1416
1720
|
// src/elements/daw-track-controls.ts
|
|
1417
|
-
var
|
|
1418
|
-
var
|
|
1419
|
-
var DawTrackControlsElement = class extends
|
|
1721
|
+
var import_lit11 = require("lit");
|
|
1722
|
+
var import_decorators10 = require("lit/decorators.js");
|
|
1723
|
+
var DawTrackControlsElement = class extends import_lit11.LitElement {
|
|
1420
1724
|
constructor() {
|
|
1421
1725
|
super(...arguments);
|
|
1422
1726
|
this.trackId = null;
|
|
@@ -1464,7 +1768,7 @@ var DawTrackControlsElement = class extends import_lit10.LitElement {
|
|
|
1464
1768
|
const volPercent = Math.round(this.volume * 100);
|
|
1465
1769
|
const panPercent = Math.round(Math.abs(this.pan) * 100);
|
|
1466
1770
|
const panDisplay = this.pan === 0 ? "C" : (this.pan > 0 ? "R" : "L") + panPercent;
|
|
1467
|
-
return
|
|
1771
|
+
return import_lit11.html`
|
|
1468
1772
|
<div class="header">
|
|
1469
1773
|
<span class="name" title=${this.trackName}>${this.trackName || "Untitled"}</span>
|
|
1470
1774
|
<button class="remove-btn" @click=${this._onRemoveClick} title="Remove track">
|
|
@@ -1514,7 +1818,7 @@ var DawTrackControlsElement = class extends import_lit10.LitElement {
|
|
|
1514
1818
|
`;
|
|
1515
1819
|
}
|
|
1516
1820
|
};
|
|
1517
|
-
DawTrackControlsElement.styles =
|
|
1821
|
+
DawTrackControlsElement.styles = import_lit11.css`
|
|
1518
1822
|
:host {
|
|
1519
1823
|
display: flex;
|
|
1520
1824
|
flex-direction: column;
|
|
@@ -1648,30 +1952,30 @@ DawTrackControlsElement.styles = import_lit10.css`
|
|
|
1648
1952
|
}
|
|
1649
1953
|
`;
|
|
1650
1954
|
__decorateClass([
|
|
1651
|
-
(0,
|
|
1955
|
+
(0, import_decorators10.property)({ attribute: false })
|
|
1652
1956
|
], DawTrackControlsElement.prototype, "trackId", 2);
|
|
1653
1957
|
__decorateClass([
|
|
1654
|
-
(0,
|
|
1958
|
+
(0, import_decorators10.property)({ attribute: false })
|
|
1655
1959
|
], DawTrackControlsElement.prototype, "trackName", 2);
|
|
1656
1960
|
__decorateClass([
|
|
1657
|
-
(0,
|
|
1961
|
+
(0, import_decorators10.property)({ type: Number, attribute: false })
|
|
1658
1962
|
], DawTrackControlsElement.prototype, "volume", 2);
|
|
1659
1963
|
__decorateClass([
|
|
1660
|
-
(0,
|
|
1964
|
+
(0, import_decorators10.property)({ type: Number, attribute: false })
|
|
1661
1965
|
], DawTrackControlsElement.prototype, "pan", 2);
|
|
1662
1966
|
__decorateClass([
|
|
1663
|
-
(0,
|
|
1967
|
+
(0, import_decorators10.property)({ type: Boolean, attribute: false })
|
|
1664
1968
|
], DawTrackControlsElement.prototype, "muted", 2);
|
|
1665
1969
|
__decorateClass([
|
|
1666
|
-
(0,
|
|
1970
|
+
(0, import_decorators10.property)({ type: Boolean, attribute: false })
|
|
1667
1971
|
], DawTrackControlsElement.prototype, "soloed", 2);
|
|
1668
1972
|
DawTrackControlsElement = __decorateClass([
|
|
1669
|
-
(0,
|
|
1973
|
+
(0, import_decorators10.customElement)("daw-track-controls")
|
|
1670
1974
|
], DawTrackControlsElement);
|
|
1671
1975
|
|
|
1672
1976
|
// src/elements/daw-grid.ts
|
|
1673
|
-
var
|
|
1674
|
-
var
|
|
1977
|
+
var import_lit12 = require("lit");
|
|
1978
|
+
var import_decorators11 = require("lit/decorators.js");
|
|
1675
1979
|
var import_core2 = require("@waveform-playlist/core");
|
|
1676
1980
|
|
|
1677
1981
|
// src/utils/musical-tick-cache.ts
|
|
@@ -1702,8 +2006,8 @@ function getCachedMusicalTicks(params) {
|
|
|
1702
2006
|
}
|
|
1703
2007
|
|
|
1704
2008
|
// src/elements/daw-grid.ts
|
|
1705
|
-
var
|
|
1706
|
-
var DawGridElement = class extends
|
|
2009
|
+
var MAX_CANVAS_WIDTH3 = 1e3;
|
|
2010
|
+
var DawGridElement = class extends import_lit12.LitElement {
|
|
1707
2011
|
constructor() {
|
|
1708
2012
|
super(...arguments);
|
|
1709
2013
|
this.ticksPerPixel = 24;
|
|
@@ -1731,25 +2035,25 @@ var DawGridElement = class extends import_lit11.LitElement {
|
|
|
1731
2035
|
}
|
|
1732
2036
|
}
|
|
1733
2037
|
render() {
|
|
1734
|
-
if (!this._tickData) return
|
|
2038
|
+
if (!this._tickData) return import_lit12.html``;
|
|
1735
2039
|
const totalWidth = this.length;
|
|
1736
2040
|
const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
1737
2041
|
const indices = getVisibleChunkIndices(
|
|
1738
2042
|
totalWidth,
|
|
1739
|
-
|
|
2043
|
+
MAX_CANVAS_WIDTH3,
|
|
1740
2044
|
this.visibleStart,
|
|
1741
2045
|
this.visibleEnd
|
|
1742
2046
|
);
|
|
1743
|
-
return
|
|
2047
|
+
return import_lit12.html`
|
|
1744
2048
|
<div class="container" style="width: ${totalWidth}px; height: ${this.height}px;">
|
|
1745
2049
|
${indices.map((i) => {
|
|
1746
|
-
const width = Math.min(
|
|
1747
|
-
return
|
|
2050
|
+
const width = Math.min(MAX_CANVAS_WIDTH3, totalWidth - i * MAX_CANVAS_WIDTH3);
|
|
2051
|
+
return import_lit12.html`
|
|
1748
2052
|
<canvas
|
|
1749
2053
|
data-index=${i}
|
|
1750
2054
|
width=${width * dpr}
|
|
1751
2055
|
height=${this.height * dpr}
|
|
1752
|
-
style="left: ${i *
|
|
2056
|
+
style="left: ${i * MAX_CANVAS_WIDTH3}px; width: ${width}px; height: ${this.height}px;"
|
|
1753
2057
|
></canvas>
|
|
1754
2058
|
`;
|
|
1755
2059
|
})}
|
|
@@ -1773,8 +2077,8 @@ var DawGridElement = class extends import_lit11.LitElement {
|
|
|
1773
2077
|
const idx = Number(canvas.dataset.index);
|
|
1774
2078
|
const ctx = canvas.getContext("2d");
|
|
1775
2079
|
if (!ctx) continue;
|
|
1776
|
-
const chunkLeft = idx *
|
|
1777
|
-
const canvasWidth = Math.min(
|
|
2080
|
+
const chunkLeft = idx * MAX_CANVAS_WIDTH3;
|
|
2081
|
+
const canvasWidth = Math.min(MAX_CANVAS_WIDTH3, this.length - chunkLeft);
|
|
1778
2082
|
ctx.resetTransform();
|
|
1779
2083
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1780
2084
|
ctx.scale(dpr, dpr);
|
|
@@ -1805,7 +2109,7 @@ var DawGridElement = class extends import_lit11.LitElement {
|
|
|
1805
2109
|
}
|
|
1806
2110
|
}
|
|
1807
2111
|
};
|
|
1808
|
-
DawGridElement.styles =
|
|
2112
|
+
DawGridElement.styles = import_lit12.css`
|
|
1809
2113
|
:host {
|
|
1810
2114
|
display: block;
|
|
1811
2115
|
position: absolute;
|
|
@@ -1823,33 +2127,33 @@ DawGridElement.styles = import_lit11.css`
|
|
|
1823
2127
|
}
|
|
1824
2128
|
`;
|
|
1825
2129
|
__decorateClass([
|
|
1826
|
-
(0,
|
|
2130
|
+
(0, import_decorators11.property)({ type: Number, attribute: false })
|
|
1827
2131
|
], DawGridElement.prototype, "ticksPerPixel", 2);
|
|
1828
2132
|
__decorateClass([
|
|
1829
|
-
(0,
|
|
2133
|
+
(0, import_decorators11.property)({ attribute: false })
|
|
1830
2134
|
], DawGridElement.prototype, "meterEntries", 2);
|
|
1831
2135
|
__decorateClass([
|
|
1832
|
-
(0,
|
|
2136
|
+
(0, import_decorators11.property)({ type: Number, attribute: false })
|
|
1833
2137
|
], DawGridElement.prototype, "ppqn", 2);
|
|
1834
2138
|
__decorateClass([
|
|
1835
|
-
(0,
|
|
2139
|
+
(0, import_decorators11.property)({ type: Number, attribute: false })
|
|
1836
2140
|
], DawGridElement.prototype, "visibleStart", 2);
|
|
1837
2141
|
__decorateClass([
|
|
1838
|
-
(0,
|
|
2142
|
+
(0, import_decorators11.property)({ type: Number, attribute: false })
|
|
1839
2143
|
], DawGridElement.prototype, "visibleEnd", 2);
|
|
1840
2144
|
__decorateClass([
|
|
1841
|
-
(0,
|
|
2145
|
+
(0, import_decorators11.property)({ type: Number, attribute: false })
|
|
1842
2146
|
], DawGridElement.prototype, "length", 2);
|
|
1843
2147
|
__decorateClass([
|
|
1844
|
-
(0,
|
|
2148
|
+
(0, import_decorators11.property)({ type: Number, attribute: false })
|
|
1845
2149
|
], DawGridElement.prototype, "height", 2);
|
|
1846
2150
|
DawGridElement = __decorateClass([
|
|
1847
|
-
(0,
|
|
2151
|
+
(0, import_decorators11.customElement)("daw-grid")
|
|
1848
2152
|
], DawGridElement);
|
|
1849
2153
|
|
|
1850
2154
|
// src/styles/theme.ts
|
|
1851
|
-
var
|
|
1852
|
-
var hostStyles =
|
|
2155
|
+
var import_lit13 = require("lit");
|
|
2156
|
+
var hostStyles = import_lit13.css`
|
|
1853
2157
|
:host {
|
|
1854
2158
|
--daw-wave-color: #c49a6c;
|
|
1855
2159
|
--daw-progress-color: #63c75f;
|
|
@@ -1865,7 +2169,7 @@ var hostStyles = import_lit12.css`
|
|
|
1865
2169
|
--daw-clip-header-text: #e0d4c8;
|
|
1866
2170
|
}
|
|
1867
2171
|
`;
|
|
1868
|
-
var clipStyles =
|
|
2172
|
+
var clipStyles = import_lit13.css`
|
|
1869
2173
|
.clip-container {
|
|
1870
2174
|
position: absolute;
|
|
1871
2175
|
overflow: hidden;
|
|
@@ -2110,6 +2414,9 @@ var RecordingController = class {
|
|
|
2110
2414
|
constructor(host) {
|
|
2111
2415
|
this._sessions = /* @__PURE__ */ new Map();
|
|
2112
2416
|
this._workletLoadedCtx = null;
|
|
2417
|
+
/** Tracks worklet pause state explicitly so external consumers (editor,
|
|
2418
|
+
* pause button, spacebar) can share one source of truth. */
|
|
2419
|
+
this._isPaused = false;
|
|
2113
2420
|
this._host = host;
|
|
2114
2421
|
host.addController(this);
|
|
2115
2422
|
}
|
|
@@ -2124,6 +2431,9 @@ var RecordingController = class {
|
|
|
2124
2431
|
get isRecording() {
|
|
2125
2432
|
return this._sessions.size > 0;
|
|
2126
2433
|
}
|
|
2434
|
+
get isPaused() {
|
|
2435
|
+
return this._isPaused && this._sessions.size > 0;
|
|
2436
|
+
}
|
|
2127
2437
|
getSession(trackId) {
|
|
2128
2438
|
return this._sessions.get(trackId);
|
|
2129
2439
|
}
|
|
@@ -2196,7 +2506,9 @@ var RecordingController = class {
|
|
|
2196
2506
|
latencySamples,
|
|
2197
2507
|
wasOverdub: options.overdub ?? false,
|
|
2198
2508
|
_onTrackEnded: onTrackEnded,
|
|
2199
|
-
_audioTrack: audioTrack
|
|
2509
|
+
_audioTrack: audioTrack,
|
|
2510
|
+
stopAckResolve: null,
|
|
2511
|
+
stopping: false
|
|
2200
2512
|
};
|
|
2201
2513
|
this._sessions.set(trackId, session);
|
|
2202
2514
|
workletNode.port.onmessage = (e) => {
|
|
@@ -2234,25 +2546,69 @@ var RecordingController = class {
|
|
|
2234
2546
|
const id = trackId ?? [...this._sessions.keys()][0];
|
|
2235
2547
|
if (!id) return;
|
|
2236
2548
|
const session = this._sessions.get(id);
|
|
2237
|
-
if (!session) return;
|
|
2549
|
+
if (!session || session.stopping) return;
|
|
2238
2550
|
session.workletNode.port.postMessage({ command: "pause" });
|
|
2551
|
+
this._isPaused = true;
|
|
2552
|
+
this._host.dispatchEvent(
|
|
2553
|
+
new CustomEvent("daw-recording-pause", {
|
|
2554
|
+
bubbles: true,
|
|
2555
|
+
composed: true,
|
|
2556
|
+
detail: { trackId: id }
|
|
2557
|
+
})
|
|
2558
|
+
);
|
|
2239
2559
|
}
|
|
2240
2560
|
resumeRecording(trackId) {
|
|
2241
2561
|
const id = trackId ?? [...this._sessions.keys()][0];
|
|
2242
2562
|
if (!id) return;
|
|
2243
2563
|
const session = this._sessions.get(id);
|
|
2244
|
-
if (!session) return;
|
|
2564
|
+
if (!session || session.stopping) return;
|
|
2245
2565
|
session.workletNode.port.postMessage({ command: "resume" });
|
|
2566
|
+
this._isPaused = false;
|
|
2567
|
+
this._host.dispatchEvent(
|
|
2568
|
+
new CustomEvent("daw-recording-resume", {
|
|
2569
|
+
bubbles: true,
|
|
2570
|
+
composed: true,
|
|
2571
|
+
detail: { trackId: id }
|
|
2572
|
+
})
|
|
2573
|
+
);
|
|
2246
2574
|
}
|
|
2247
|
-
stopRecording(trackId) {
|
|
2575
|
+
async stopRecording(trackId) {
|
|
2248
2576
|
const id = trackId ?? [...this._sessions.keys()][0];
|
|
2249
2577
|
if (!id) return;
|
|
2250
2578
|
const session = this._sessions.get(id);
|
|
2251
2579
|
if (!session) return;
|
|
2580
|
+
const wasPaused = this._isPaused;
|
|
2581
|
+
this._isPaused = false;
|
|
2582
|
+
session.stopping = true;
|
|
2252
2583
|
if (session.wasOverdub && typeof this._host.stop === "function") {
|
|
2253
2584
|
this._host.stop();
|
|
2254
2585
|
}
|
|
2255
|
-
|
|
2586
|
+
if (wasPaused) {
|
|
2587
|
+
session.workletNode.port.postMessage({ command: "stop" });
|
|
2588
|
+
} else {
|
|
2589
|
+
const stopAck = new Promise((resolve) => {
|
|
2590
|
+
session.stopAckResolve = resolve;
|
|
2591
|
+
});
|
|
2592
|
+
let timeoutId;
|
|
2593
|
+
const timeout = new Promise((resolve) => {
|
|
2594
|
+
timeoutId = setTimeout(resolve, 1e3);
|
|
2595
|
+
});
|
|
2596
|
+
session.workletNode.port.postMessage({ command: "stop" });
|
|
2597
|
+
await Promise.race([stopAck, timeout]);
|
|
2598
|
+
clearTimeout(timeoutId);
|
|
2599
|
+
session.stopAckResolve = null;
|
|
2600
|
+
let lastSamples = -1;
|
|
2601
|
+
let stable = 0;
|
|
2602
|
+
for (let i = 0; i < 50; i++) {
|
|
2603
|
+
if (session.totalSamples === lastSamples) {
|
|
2604
|
+
if (++stable >= 3) break;
|
|
2605
|
+
} else {
|
|
2606
|
+
stable = 0;
|
|
2607
|
+
lastSamples = session.totalSamples;
|
|
2608
|
+
}
|
|
2609
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2256
2612
|
session.source.disconnect();
|
|
2257
2613
|
session.workletNode.disconnect();
|
|
2258
2614
|
this._removeTrackEndedListener(session);
|
|
@@ -2324,8 +2680,20 @@ var RecordingController = class {
|
|
|
2324
2680
|
_onWorkletMessage(trackId, data) {
|
|
2325
2681
|
const session = this._sessions.get(trackId);
|
|
2326
2682
|
if (!session) return;
|
|
2327
|
-
const { channels } = data;
|
|
2328
|
-
|
|
2683
|
+
const { channels, done } = data;
|
|
2684
|
+
try {
|
|
2685
|
+
const hasSamples = !!(channels && channels.length > 0 && channels[0] && channels[0].length > 0);
|
|
2686
|
+
if (!hasSamples) return;
|
|
2687
|
+
this._processWorkletSamples(trackId, session, channels);
|
|
2688
|
+
} finally {
|
|
2689
|
+
if (done && session.stopAckResolve) {
|
|
2690
|
+
const resolve = session.stopAckResolve;
|
|
2691
|
+
session.stopAckResolve = null;
|
|
2692
|
+
resolve();
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
_processWorkletSamples(trackId, session, channels) {
|
|
2329
2697
|
const samplesProcessedBefore = session.totalSamples;
|
|
2330
2698
|
for (let ch = 0; ch < session.channelCount; ch++) {
|
|
2331
2699
|
if (channels[ch]) {
|
|
@@ -2333,6 +2701,7 @@ var RecordingController = class {
|
|
|
2333
2701
|
}
|
|
2334
2702
|
}
|
|
2335
2703
|
session.totalSamples += channels[0].length;
|
|
2704
|
+
if (session.stopAckResolve !== null) return;
|
|
2336
2705
|
for (let ch = 0; ch < session.channelCount; ch++) {
|
|
2337
2706
|
if (!channels[ch]) continue;
|
|
2338
2707
|
const oldPeakCount = Math.floor(session.peaks[ch].length / 2);
|
|
@@ -2403,6 +2772,121 @@ var RecordingController = class {
|
|
|
2403
2772
|
}
|
|
2404
2773
|
};
|
|
2405
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
|
+
|
|
2406
2890
|
// src/interactions/pointer-handler.ts
|
|
2407
2891
|
var import_core4 = require("@waveform-playlist/core");
|
|
2408
2892
|
|
|
@@ -2667,6 +3151,7 @@ var ClipPointerHandler = class {
|
|
|
2667
3151
|
const trackId = boundary.dataset.trackId;
|
|
2668
3152
|
const edge = boundary.dataset.boundaryEdge;
|
|
2669
3153
|
if (!clipId || !trackId || edge !== "left" && edge !== "right") return false;
|
|
3154
|
+
if (this._host.isMidiClip(trackId, clipId)) return true;
|
|
2670
3155
|
this._beginDrag(edge === "left" ? "trim-left" : "trim-right", clipId, trackId, e);
|
|
2671
3156
|
this._boundaryEl = boundary;
|
|
2672
3157
|
return true;
|
|
@@ -2960,6 +3445,7 @@ async function loadFiles(host, files) {
|
|
|
2960
3445
|
pan: 0,
|
|
2961
3446
|
muted: false,
|
|
2962
3447
|
soloed: false,
|
|
3448
|
+
renderMode: "waveform",
|
|
2963
3449
|
clips: [
|
|
2964
3450
|
{
|
|
2965
3451
|
kind: "drop",
|
|
@@ -2972,7 +3458,10 @@ async function loadFiles(host, files) {
|
|
|
2972
3458
|
name,
|
|
2973
3459
|
fadeIn: 0,
|
|
2974
3460
|
fadeOut: 0,
|
|
2975
|
-
fadeType: "linear"
|
|
3461
|
+
fadeType: "linear",
|
|
3462
|
+
midiNotes: null,
|
|
3463
|
+
midiChannel: null,
|
|
3464
|
+
midiProgram: null
|
|
2976
3465
|
}
|
|
2977
3466
|
]
|
|
2978
3467
|
});
|
|
@@ -3059,7 +3548,10 @@ function addRecordedClip(host, trackId, buf, startSample, durSamples, offsetSamp
|
|
|
3059
3548
|
name: "Recording",
|
|
3060
3549
|
fadeIn: 0,
|
|
3061
3550
|
fadeOut: 0,
|
|
3062
|
-
fadeType: "linear"
|
|
3551
|
+
fadeType: "linear",
|
|
3552
|
+
midiNotes: null,
|
|
3553
|
+
midiChannel: null,
|
|
3554
|
+
midiProgram: null
|
|
3063
3555
|
};
|
|
3064
3556
|
host._tracks = new Map(host._tracks).set(trackId, {
|
|
3065
3557
|
...desc,
|
|
@@ -3118,7 +3610,10 @@ function canSplitAtTime(host, time) {
|
|
|
3118
3610
|
const track = state5.tracks.find((t) => t.id === state5.selectedTrackId);
|
|
3119
3611
|
if (!track) return false;
|
|
3120
3612
|
const atSample = Math.round(time * host.effectiveSampleRate);
|
|
3121
|
-
|
|
3613
|
+
const clip = findClipAtSample(track.clips, atSample);
|
|
3614
|
+
if (!clip) return false;
|
|
3615
|
+
if (clip.midiNotes != null) return false;
|
|
3616
|
+
return true;
|
|
3122
3617
|
}
|
|
3123
3618
|
function performSplit(host, time) {
|
|
3124
3619
|
const { engine } = host;
|
|
@@ -3272,8 +3767,9 @@ async function loadWaveformDataFromUrl(src) {
|
|
|
3272
3767
|
}
|
|
3273
3768
|
|
|
3274
3769
|
// src/elements/daw-editor.ts
|
|
3770
|
+
var import_meta = {};
|
|
3275
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();";
|
|
3276
|
-
var DawEditorElement = class extends
|
|
3772
|
+
var DawEditorElement = class extends import_lit14.LitElement {
|
|
3277
3773
|
constructor() {
|
|
3278
3774
|
super(...arguments);
|
|
3279
3775
|
this._samplesPerPixel = 1024;
|
|
@@ -3287,6 +3783,8 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
3287
3783
|
this.clipHeaderHeight = 20;
|
|
3288
3784
|
this.interactiveClips = false;
|
|
3289
3785
|
this.indefinitePlayback = false;
|
|
3786
|
+
this._spectrogramConfig = null;
|
|
3787
|
+
this._spectrogramColorMap = null;
|
|
3290
3788
|
this.scaleMode = "temporal";
|
|
3291
3789
|
this._ticksPerPixel = 24;
|
|
3292
3790
|
this._bpm = 120;
|
|
@@ -3322,6 +3820,7 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
3322
3820
|
this._childObserver = null;
|
|
3323
3821
|
this._audioResume = new AudioResumeController(this);
|
|
3324
3822
|
this._recordingController = new RecordingController(this);
|
|
3823
|
+
this._spectrogramController = null;
|
|
3325
3824
|
this._clipPointer = new ClipPointerHandler(this);
|
|
3326
3825
|
this._pointer = new PointerHandler(this);
|
|
3327
3826
|
this._viewport = (() => {
|
|
@@ -3362,6 +3861,26 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
3362
3861
|
if (oldDescriptor?.src !== descriptor.src) {
|
|
3363
3862
|
this._loadTrack(trackId, descriptor);
|
|
3364
3863
|
}
|
|
3864
|
+
if (descriptor.renderMode === "spectrogram" && oldDescriptor?.renderMode !== "spectrogram") {
|
|
3865
|
+
const engineTrack = this._engineTracks.get(trackId);
|
|
3866
|
+
if (engineTrack) {
|
|
3867
|
+
for (const clip of engineTrack.clips) {
|
|
3868
|
+
this._maybeRegisterSpectrogramClipAudio(trackId, clip);
|
|
3869
|
+
}
|
|
3870
|
+
}
|
|
3871
|
+
}
|
|
3872
|
+
if (descriptor.renderMode !== "spectrogram" && oldDescriptor?.renderMode === "spectrogram") {
|
|
3873
|
+
const engineTrack = this._engineTracks.get(trackId);
|
|
3874
|
+
if (engineTrack && this._spectrogramController) {
|
|
3875
|
+
for (const clip of engineTrack.clips) {
|
|
3876
|
+
this._spectrogramController.unregisterClipAudio(clip.id);
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
this._disposeSpectrogramControllerIfEmpty();
|
|
3880
|
+
}
|
|
3881
|
+
if (descriptor.spectrogramConfig !== oldDescriptor?.spectrogramConfig) {
|
|
3882
|
+
this._spectrogramController?.setTrackConfig(trackId, descriptor.spectrogramConfig ?? null);
|
|
3883
|
+
}
|
|
3365
3884
|
};
|
|
3366
3885
|
this._onTrackControl = (e) => {
|
|
3367
3886
|
const { trackId, prop, value } = e.detail ?? {};
|
|
@@ -3432,7 +3951,10 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
3432
3951
|
name: clipEl.name,
|
|
3433
3952
|
fadeIn: clipEl.fadeIn,
|
|
3434
3953
|
fadeOut: clipEl.fadeOut,
|
|
3435
|
-
fadeType: clipEl.fadeType
|
|
3954
|
+
fadeType: clipEl.fadeType,
|
|
3955
|
+
midiNotes: clipEl.midiNotes,
|
|
3956
|
+
midiChannel: clipEl.midiChannel,
|
|
3957
|
+
midiProgram: clipEl.midiProgram
|
|
3436
3958
|
};
|
|
3437
3959
|
this._loadAndAppendClip(trackId, clipDesc);
|
|
3438
3960
|
};
|
|
@@ -3483,6 +4005,9 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
3483
4005
|
};
|
|
3484
4006
|
// --- Recording ---
|
|
3485
4007
|
this.recordingStream = null;
|
|
4008
|
+
/** Set in togglePauseRecording when Transport is paused alongside the
|
|
4009
|
+
* worklet, so resume can restart it. Cleared on resume and on stop. */
|
|
4010
|
+
this._wasPlayingDuringRecording = false;
|
|
3486
4011
|
}
|
|
3487
4012
|
get samplesPerPixel() {
|
|
3488
4013
|
return this._samplesPerPixel;
|
|
@@ -3499,6 +4024,72 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
3499
4024
|
this._samplesPerPixel = clamped;
|
|
3500
4025
|
this.requestUpdate("samplesPerPixel", old);
|
|
3501
4026
|
}
|
|
4027
|
+
get spectrogramConfig() {
|
|
4028
|
+
return this._spectrogramConfig;
|
|
4029
|
+
}
|
|
4030
|
+
set spectrogramConfig(value) {
|
|
4031
|
+
const old = this._spectrogramConfig;
|
|
4032
|
+
this._spectrogramConfig = value;
|
|
4033
|
+
this._spectrogramController?.setEditorConfig(value);
|
|
4034
|
+
this.requestUpdate("spectrogramConfig", old);
|
|
4035
|
+
}
|
|
4036
|
+
get spectrogramColorMap() {
|
|
4037
|
+
return this._spectrogramColorMap;
|
|
4038
|
+
}
|
|
4039
|
+
set spectrogramColorMap(value) {
|
|
4040
|
+
const old = this._spectrogramColorMap;
|
|
4041
|
+
this._spectrogramColorMap = value;
|
|
4042
|
+
this._spectrogramController?.setEditorColorMap(value);
|
|
4043
|
+
this.requestUpdate("spectrogramColorMap", old);
|
|
4044
|
+
}
|
|
4045
|
+
_ensureSpectrogramController() {
|
|
4046
|
+
if (!this._spectrogramController) {
|
|
4047
|
+
this._spectrogramController = new SpectrogramController(
|
|
4048
|
+
this,
|
|
4049
|
+
() => new Worker(new URL("@dawcore/spectrogram/worker/spectrogram.worker", import_meta.url), {
|
|
4050
|
+
type: "module"
|
|
4051
|
+
})
|
|
4052
|
+
);
|
|
4053
|
+
if (this._spectrogramConfig) {
|
|
4054
|
+
this._spectrogramController.setEditorConfig(this._spectrogramConfig);
|
|
4055
|
+
}
|
|
4056
|
+
if (this._spectrogramColorMap) {
|
|
4057
|
+
this._spectrogramController.setEditorColorMap(this._spectrogramColorMap);
|
|
4058
|
+
}
|
|
4059
|
+
}
|
|
4060
|
+
return this._spectrogramController;
|
|
4061
|
+
}
|
|
4062
|
+
/** Called by <daw-spectrogram> after transferControlToOffscreen. */
|
|
4063
|
+
_spectrogramRegisterCanvas(reg) {
|
|
4064
|
+
this._ensureSpectrogramController().registerCanvas(reg);
|
|
4065
|
+
}
|
|
4066
|
+
/** Called by <daw-spectrogram> on chunk unmount / element disconnect. */
|
|
4067
|
+
_spectrogramUnregisterCanvas(canvasId) {
|
|
4068
|
+
this._spectrogramController?.unregisterCanvas(canvasId);
|
|
4069
|
+
}
|
|
4070
|
+
/**
|
|
4071
|
+
* Push a clip's decoded audio into the spectrogram controller. No-op
|
|
4072
|
+
* unless the track is in spectrogram render-mode and the controller
|
|
4073
|
+
* already exists (it bootstraps from canvas registration).
|
|
4074
|
+
*/
|
|
4075
|
+
_maybeRegisterSpectrogramClipAudio(trackId, clip) {
|
|
4076
|
+
const descriptor = this._tracks.get(trackId);
|
|
4077
|
+
if (descriptor?.renderMode !== "spectrogram") return;
|
|
4078
|
+
const buffer = clip.audioBuffer ?? this._clipBuffers.get(clip.id);
|
|
4079
|
+
if (!buffer) return;
|
|
4080
|
+
const channelData = [];
|
|
4081
|
+
for (let i = 0; i < buffer.numberOfChannels; i++) {
|
|
4082
|
+
channelData.push(buffer.getChannelData(i));
|
|
4083
|
+
}
|
|
4084
|
+
this._ensureSpectrogramController().registerClipAudio({
|
|
4085
|
+
clipId: clip.id,
|
|
4086
|
+
trackId,
|
|
4087
|
+
channelData,
|
|
4088
|
+
sampleRate: buffer.sampleRate,
|
|
4089
|
+
durationSamples: clip.durationSamples,
|
|
4090
|
+
offsetSamples: clip.offsetSamples
|
|
4091
|
+
});
|
|
4092
|
+
}
|
|
3502
4093
|
get ticksPerPixel() {
|
|
3503
4094
|
return this._ticksPerPixel;
|
|
3504
4095
|
}
|
|
@@ -3582,6 +4173,17 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
3582
4173
|
);
|
|
3583
4174
|
return result.get(clipId) ?? null;
|
|
3584
4175
|
}
|
|
4176
|
+
/**
|
|
4177
|
+
* Returns true if the clip is a MIDI clip (has midiNotes).
|
|
4178
|
+
* Used by ClipPointerHandler to make trim handles inert for MIDI clips.
|
|
4179
|
+
* Returns false for unknown track/clip IDs (defensive).
|
|
4180
|
+
*/
|
|
4181
|
+
isMidiClip(trackId, clipId) {
|
|
4182
|
+
const track = this._engineTracks.get(trackId);
|
|
4183
|
+
if (!track) return false;
|
|
4184
|
+
const clip = track.clips.find((c) => c.id === clipId);
|
|
4185
|
+
return clip?.midiNotes != null;
|
|
4186
|
+
}
|
|
3585
4187
|
get effectiveSampleRate() {
|
|
3586
4188
|
return this._resolvedSampleRate ?? this.sampleRate;
|
|
3587
4189
|
}
|
|
@@ -3728,6 +4330,8 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
3728
4330
|
this._clipOffsets.clear();
|
|
3729
4331
|
this._peakPipeline.terminate();
|
|
3730
4332
|
this._minSamplesPerPixel = 0;
|
|
4333
|
+
this._spectrogramController?.dispose();
|
|
4334
|
+
this._spectrogramController = null;
|
|
3731
4335
|
try {
|
|
3732
4336
|
this._disposeEngine();
|
|
3733
4337
|
} catch (err) {
|
|
@@ -3756,6 +4360,23 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
3756
4360
|
}
|
|
3757
4361
|
}
|
|
3758
4362
|
}
|
|
4363
|
+
updated(_changed) {
|
|
4364
|
+
if (this._spectrogramController) {
|
|
4365
|
+
const vs = this._viewport.visibleStart;
|
|
4366
|
+
const ve = this._viewport.visibleEnd;
|
|
4367
|
+
if (Number.isFinite(vs) && Number.isFinite(ve)) {
|
|
4368
|
+
const span = ve - vs;
|
|
4369
|
+
const bufferPad = span * 0.25;
|
|
4370
|
+
this._spectrogramController.setViewport({
|
|
4371
|
+
visibleStartPx: vs,
|
|
4372
|
+
visibleEndPx: ve,
|
|
4373
|
+
bufferStartPx: Math.max(0, vs - bufferPad),
|
|
4374
|
+
bufferEndPx: ve + bufferPad,
|
|
4375
|
+
samplesPerPixel: this._renderSpp
|
|
4376
|
+
});
|
|
4377
|
+
}
|
|
4378
|
+
}
|
|
4379
|
+
}
|
|
3759
4380
|
_onTrackRemoved(trackId) {
|
|
3760
4381
|
this._trackElements.delete(trackId);
|
|
3761
4382
|
const removedTrack = this._engineTracks.get(trackId);
|
|
@@ -3765,6 +4386,7 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
3765
4386
|
this._clipBuffers.delete(clip.id);
|
|
3766
4387
|
this._clipOffsets.delete(clip.id);
|
|
3767
4388
|
nextPeaks.delete(clip.id);
|
|
4389
|
+
this._spectrogramController?.unregisterClipAudio(clip.id);
|
|
3768
4390
|
}
|
|
3769
4391
|
this._peaksData = nextPeaks;
|
|
3770
4392
|
}
|
|
@@ -3779,11 +4401,23 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
3779
4401
|
this._engine.removeTrack(trackId);
|
|
3780
4402
|
}
|
|
3781
4403
|
this._minSamplesPerPixel = this._peakPipeline.getMaxCachedScale(this._clipBuffers);
|
|
4404
|
+
this._disposeSpectrogramControllerIfEmpty();
|
|
3782
4405
|
if (nextEngine.size === 0) {
|
|
3783
4406
|
this._currentTime = 0;
|
|
3784
4407
|
this._stopPlayhead();
|
|
3785
4408
|
}
|
|
3786
4409
|
}
|
|
4410
|
+
/** Drop the controller when no spectrogram tracks remain. */
|
|
4411
|
+
_disposeSpectrogramControllerIfEmpty() {
|
|
4412
|
+
if (!this._spectrogramController) return;
|
|
4413
|
+
const stillNeeded = Array.from(this._tracks.values()).some(
|
|
4414
|
+
(d) => d.renderMode === "spectrogram"
|
|
4415
|
+
);
|
|
4416
|
+
if (!stillNeeded) {
|
|
4417
|
+
this._spectrogramController.dispose();
|
|
4418
|
+
this._spectrogramController = null;
|
|
4419
|
+
}
|
|
4420
|
+
}
|
|
3787
4421
|
_onClipRemovedFromDom(clipEl) {
|
|
3788
4422
|
const clipId = clipEl.clipId;
|
|
3789
4423
|
for (const [trackId, t] of this._engineTracks.entries()) {
|
|
@@ -3825,6 +4459,7 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
3825
4459
|
});
|
|
3826
4460
|
}
|
|
3827
4461
|
this._commitTrackChange(trackId, updatedTrack);
|
|
4462
|
+
this._maybeRegisterSpectrogramClipAudio(trackId, clip);
|
|
3828
4463
|
this.dispatchEvent(
|
|
3829
4464
|
new CustomEvent("daw-clip-ready", {
|
|
3830
4465
|
bubbles: true,
|
|
@@ -3931,6 +4566,65 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
3931
4566
|
}
|
|
3932
4567
|
return clip;
|
|
3933
4568
|
}
|
|
4569
|
+
/**
|
|
4570
|
+
* Filter MIDI notes to only those with finite, in-range fields. Logs a
|
|
4571
|
+
* warning for each dropped note. Used by _buildMidiClip and the
|
|
4572
|
+
* _applyClipUpdate MIDI branch to prevent NaN propagation through the
|
|
4573
|
+
* timeline.
|
|
4574
|
+
*/
|
|
4575
|
+
_validMidiNotes(notes) {
|
|
4576
|
+
return notes.filter((n) => {
|
|
4577
|
+
const ok = Number.isFinite(n.time) && n.time >= 0 && Number.isFinite(n.duration) && n.duration > 0 && Number.isInteger(n.midi) && n.midi >= 0 && n.midi <= 127 && Number.isFinite(n.velocity) && n.velocity >= 0 && n.velocity <= 1;
|
|
4578
|
+
if (!ok) {
|
|
4579
|
+
console.warn("[dawcore] dropping malformed MIDI note: " + JSON.stringify(n));
|
|
4580
|
+
}
|
|
4581
|
+
return ok;
|
|
4582
|
+
});
|
|
4583
|
+
}
|
|
4584
|
+
/**
|
|
4585
|
+
* A clip descriptor is treated as MIDI when it has no audio src.
|
|
4586
|
+
* Includes placeholder MIDI clips (no notes, no duration yet — registered
|
|
4587
|
+
* with a 1s default span; notes upgrade via _applyClipUpdate). Warns when
|
|
4588
|
+
* a clip ambiguously has both src and midiNotes — the audio path runs
|
|
4589
|
+
* and notes would be silently ignored.
|
|
4590
|
+
*/
|
|
4591
|
+
_isMidiDescriptor(clipDesc) {
|
|
4592
|
+
if (clipDesc.src) {
|
|
4593
|
+
if (clipDesc.midiNotes != null) {
|
|
4594
|
+
console.warn(
|
|
4595
|
+
'[dawcore] clip "' + (clipDesc.name || (isDomClip(clipDesc) ? clipDesc.clipId : "?")) + '" has both src and midiNotes \u2014 treating as audio (notes will be ignored)'
|
|
4596
|
+
);
|
|
4597
|
+
}
|
|
4598
|
+
return false;
|
|
4599
|
+
}
|
|
4600
|
+
return true;
|
|
4601
|
+
}
|
|
4602
|
+
/**
|
|
4603
|
+
* Build an engine clip from a MIDI clip descriptor. Always returns a clip
|
|
4604
|
+
* — empty notes / no declared duration get a 1-second placeholder span so
|
|
4605
|
+
* the clip is reachable via `engine.updateTrack` once notes arrive.
|
|
4606
|
+
*/
|
|
4607
|
+
_buildMidiClip(clipDesc) {
|
|
4608
|
+
const sr = this.effectiveSampleRate;
|
|
4609
|
+
const notes = this._validMidiNotes(clipDesc.midiNotes ?? []);
|
|
4610
|
+
const noteSpanSeconds = notes.length ? notes.reduce((max, n) => Math.max(max, n.time + n.duration), 0) : 0;
|
|
4611
|
+
const sourceDurationSamples = Math.ceil(Math.max(noteSpanSeconds, clipDesc.duration, 1) * sr);
|
|
4612
|
+
const requestedDurationSamples = clipDesc.duration > 0 ? Math.round(clipDesc.duration * sr) : sourceDurationSamples;
|
|
4613
|
+
const clip = (0, import_core8.createClip)({
|
|
4614
|
+
startSample: Math.round(clipDesc.start * sr),
|
|
4615
|
+
durationSamples: requestedDurationSamples,
|
|
4616
|
+
offsetSamples: Math.round(clipDesc.offset * sr),
|
|
4617
|
+
sampleRate: sr,
|
|
4618
|
+
sourceDurationSamples,
|
|
4619
|
+
gain: clipDesc.gain,
|
|
4620
|
+
name: clipDesc.name,
|
|
4621
|
+
midiNotes: notes,
|
|
4622
|
+
midiChannel: clipDesc.midiChannel ?? void 0,
|
|
4623
|
+
midiProgram: clipDesc.midiProgram ?? void 0
|
|
4624
|
+
});
|
|
4625
|
+
if (isDomClip(clipDesc)) clip.id = clipDesc.clipId;
|
|
4626
|
+
return clip;
|
|
4627
|
+
}
|
|
3934
4628
|
/** Remove a single clip from all per-clip caches. Used by error rollbacks. */
|
|
3935
4629
|
_purgeClipCaches(clipId) {
|
|
3936
4630
|
const nextBuffers = new Map(this._clipBuffers);
|
|
@@ -3940,6 +4634,7 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
3940
4634
|
nextPeaks.delete(clipId);
|
|
3941
4635
|
this._peaksData = nextPeaks;
|
|
3942
4636
|
this._clipOffsets.delete(clipId);
|
|
4637
|
+
this._spectrogramController?.unregisterClipAudio(clipId);
|
|
3943
4638
|
}
|
|
3944
4639
|
/**
|
|
3945
4640
|
* Recompute duration and forward an updated track to the engine. Single
|
|
@@ -3968,6 +4663,34 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
3968
4663
|
}
|
|
3969
4664
|
const oldClip = t.clips[idx];
|
|
3970
4665
|
const sr = oldClip.sampleRate ?? this.effectiveSampleRate;
|
|
4666
|
+
const isMidiNow = clipEl.midiNotes != null;
|
|
4667
|
+
const wasMidi = oldClip.midiNotes != null;
|
|
4668
|
+
if (isMidiNow || wasMidi) {
|
|
4669
|
+
const notes = this._validMidiNotes(clipEl.midiNotes ?? []);
|
|
4670
|
+
const noteSpanSeconds = notes.length ? notes.reduce((max, n) => Math.max(max, n.time + n.duration), 0) : 0;
|
|
4671
|
+
const sourceDurationSamples = Math.ceil(Math.max(noteSpanSeconds, clipEl.duration, 1) * sr);
|
|
4672
|
+
const requestedDurationSamples = clipEl.duration > 0 ? Math.round(clipEl.duration * sr) : sourceDurationSamples;
|
|
4673
|
+
const updatedClip2 = {
|
|
4674
|
+
...oldClip,
|
|
4675
|
+
audioBuffer: void 0,
|
|
4676
|
+
startSample: Math.round(clipEl.start * sr),
|
|
4677
|
+
offsetSamples: Math.round(clipEl.offset * sr),
|
|
4678
|
+
durationSamples: requestedDurationSamples,
|
|
4679
|
+
sourceDurationSamples,
|
|
4680
|
+
gain: clipEl.gain,
|
|
4681
|
+
name: clipEl.name || oldClip.name,
|
|
4682
|
+
midiNotes: notes,
|
|
4683
|
+
midiChannel: clipEl.midiChannel ?? void 0,
|
|
4684
|
+
midiProgram: clipEl.midiProgram ?? void 0
|
|
4685
|
+
};
|
|
4686
|
+
const updatedClips2 = [...t.clips];
|
|
4687
|
+
updatedClips2[idx] = updatedClip2;
|
|
4688
|
+
const updatedTrack2 = { ...t, clips: updatedClips2 };
|
|
4689
|
+
this._engineTracks = new Map(this._engineTracks).set(trackId, updatedTrack2);
|
|
4690
|
+
this._purgeClipCaches(clipId);
|
|
4691
|
+
this._commitTrackChange(trackId, updatedTrack2);
|
|
4692
|
+
return;
|
|
4693
|
+
}
|
|
3971
4694
|
const newStartSample = Math.round(clipEl.start * sr);
|
|
3972
4695
|
const newDurationSamples = clipEl.duration > 0 ? Math.round(clipEl.duration * sr) : oldClip.durationSamples;
|
|
3973
4696
|
const newOffsetSamples = Math.round(clipEl.offset * sr);
|
|
@@ -4044,7 +4767,10 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4044
4767
|
name: trackEl.name || "",
|
|
4045
4768
|
fadeIn: 0,
|
|
4046
4769
|
fadeOut: 0,
|
|
4047
|
-
fadeType: "linear"
|
|
4770
|
+
fadeType: "linear",
|
|
4771
|
+
midiNotes: null,
|
|
4772
|
+
midiChannel: null,
|
|
4773
|
+
midiProgram: null
|
|
4048
4774
|
});
|
|
4049
4775
|
} else {
|
|
4050
4776
|
for (const clipEl of clipEls) {
|
|
@@ -4060,7 +4786,10 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4060
4786
|
name: clipEl.name,
|
|
4061
4787
|
fadeIn: clipEl.fadeIn,
|
|
4062
4788
|
fadeOut: clipEl.fadeOut,
|
|
4063
|
-
fadeType: clipEl.fadeType
|
|
4789
|
+
fadeType: clipEl.fadeType,
|
|
4790
|
+
midiNotes: clipEl.midiNotes,
|
|
4791
|
+
midiChannel: clipEl.midiChannel,
|
|
4792
|
+
midiProgram: clipEl.midiProgram
|
|
4064
4793
|
});
|
|
4065
4794
|
}
|
|
4066
4795
|
}
|
|
@@ -4071,6 +4800,7 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4071
4800
|
pan: trackEl.pan,
|
|
4072
4801
|
muted: trackEl.muted,
|
|
4073
4802
|
soloed: trackEl.soloed,
|
|
4803
|
+
renderMode: trackEl.renderMode,
|
|
4074
4804
|
clips
|
|
4075
4805
|
};
|
|
4076
4806
|
}
|
|
@@ -4079,7 +4809,10 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4079
4809
|
try {
|
|
4080
4810
|
const clips = [];
|
|
4081
4811
|
for (const clipDesc of descriptor.clips) {
|
|
4082
|
-
if (
|
|
4812
|
+
if (this._isMidiDescriptor(clipDesc)) {
|
|
4813
|
+
clips.push(this._buildMidiClip(clipDesc));
|
|
4814
|
+
continue;
|
|
4815
|
+
}
|
|
4083
4816
|
try {
|
|
4084
4817
|
const waveformDataPromise = clipDesc.peaksSrc ? this._resolvePeaks(clipDesc.peaksSrc) : Promise.resolve(null);
|
|
4085
4818
|
const audioPromise = this._fetchAndDecode(clipDesc.src);
|
|
@@ -4184,6 +4917,9 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4184
4917
|
track.id = trackId;
|
|
4185
4918
|
this._engineTracks = new Map(this._engineTracks).set(trackId, track);
|
|
4186
4919
|
this._recomputeDuration();
|
|
4920
|
+
for (const c of clips) {
|
|
4921
|
+
this._maybeRegisterSpectrogramClipAudio(trackId, c);
|
|
4922
|
+
}
|
|
4187
4923
|
const engine = await this._ensureEngine();
|
|
4188
4924
|
engine.setTracks([...this._engineTracks.values()]);
|
|
4189
4925
|
this.dispatchEvent(
|
|
@@ -4291,7 +5027,11 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4291
5027
|
nextTracks.set(track.id, track);
|
|
4292
5028
|
}
|
|
4293
5029
|
this._engineTracks = nextTracks;
|
|
4294
|
-
|
|
5030
|
+
const audioTracks = engineState.tracks.filter((t) => {
|
|
5031
|
+
const desc = this._tracks.get(t.id);
|
|
5032
|
+
return desc?.renderMode !== "piano-roll";
|
|
5033
|
+
});
|
|
5034
|
+
syncPeaksForChangedClips(this, audioTracks);
|
|
4295
5035
|
}
|
|
4296
5036
|
});
|
|
4297
5037
|
engine.on("pause", () => {
|
|
@@ -4366,7 +5106,17 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4366
5106
|
if (config.pan !== void 0) trackEl.pan = config.pan;
|
|
4367
5107
|
if (config.muted) trackEl.setAttribute("muted", "");
|
|
4368
5108
|
if (config.soloed) trackEl.setAttribute("soloed", "");
|
|
4369
|
-
|
|
5109
|
+
const renderMode = config.renderMode ?? (config.midi ? "piano-roll" : void 0);
|
|
5110
|
+
if (renderMode !== void 0) trackEl.setAttribute("render-mode", renderMode);
|
|
5111
|
+
const clipConfigs = [...config.clips ?? []];
|
|
5112
|
+
if (config.midi) {
|
|
5113
|
+
clipConfigs.push({
|
|
5114
|
+
midiNotes: config.midi.notes,
|
|
5115
|
+
midiChannel: config.midi.channel,
|
|
5116
|
+
midiProgram: config.midi.program
|
|
5117
|
+
});
|
|
5118
|
+
}
|
|
5119
|
+
for (const clipConfig of clipConfigs) {
|
|
4370
5120
|
trackEl.appendChild(this._buildClipElement(clipConfig));
|
|
4371
5121
|
}
|
|
4372
5122
|
return this._awaitId(
|
|
@@ -4412,6 +5162,7 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4412
5162
|
if (partial.soloed) trackEl.setAttribute("soloed", "");
|
|
4413
5163
|
else trackEl.removeAttribute("soloed");
|
|
4414
5164
|
}
|
|
5165
|
+
if (partial.renderMode !== void 0) trackEl.setAttribute("render-mode", partial.renderMode);
|
|
4415
5166
|
return;
|
|
4416
5167
|
}
|
|
4417
5168
|
const oldDesc = this._tracks.get(trackId);
|
|
@@ -4422,7 +5173,8 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4422
5173
|
...partial.volume !== void 0 && { volume: partial.volume },
|
|
4423
5174
|
...partial.pan !== void 0 && { pan: partial.pan },
|
|
4424
5175
|
...partial.muted !== void 0 && { muted: partial.muted },
|
|
4425
|
-
...partial.soloed !== void 0 && { soloed: partial.soloed }
|
|
5176
|
+
...partial.soloed !== void 0 && { soloed: partial.soloed },
|
|
5177
|
+
...partial.renderMode !== void 0 && { renderMode: partial.renderMode }
|
|
4426
5178
|
};
|
|
4427
5179
|
this._tracks = new Map(this._tracks).set(trackId, newDesc);
|
|
4428
5180
|
if (this._engine) {
|
|
@@ -4557,6 +5309,11 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4557
5309
|
if (config.fadeIn !== void 0) clipEl.fadeIn = config.fadeIn;
|
|
4558
5310
|
if (config.fadeOut !== void 0) clipEl.fadeOut = config.fadeOut;
|
|
4559
5311
|
if (config.fadeType !== void 0) clipEl.setAttribute("fade-type", config.fadeType);
|
|
5312
|
+
if (config.midiNotes !== void 0) clipEl.midiNotes = config.midiNotes;
|
|
5313
|
+
if (config.midiChannel !== void 0)
|
|
5314
|
+
clipEl.setAttribute("midi-channel", String(config.midiChannel));
|
|
5315
|
+
if (config.midiProgram !== void 0)
|
|
5316
|
+
clipEl.setAttribute("midi-program", String(config.midiProgram));
|
|
4560
5317
|
return clipEl;
|
|
4561
5318
|
}
|
|
4562
5319
|
// --- Playback ---
|
|
@@ -4592,7 +5349,9 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4592
5349
|
}
|
|
4593
5350
|
/** Toggle between play and pause. */
|
|
4594
5351
|
togglePlayPause() {
|
|
4595
|
-
if (this.
|
|
5352
|
+
if (this.isRecording) {
|
|
5353
|
+
this.togglePauseRecording();
|
|
5354
|
+
} else if (this._isPlaying) {
|
|
4596
5355
|
this.pause();
|
|
4597
5356
|
} else {
|
|
4598
5357
|
this.play();
|
|
@@ -4666,14 +5425,41 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4666
5425
|
get isRecording() {
|
|
4667
5426
|
return this._recordingController.isRecording;
|
|
4668
5427
|
}
|
|
5428
|
+
get isRecordingPaused() {
|
|
5429
|
+
return this._recordingController.isPaused;
|
|
5430
|
+
}
|
|
4669
5431
|
pauseRecording() {
|
|
4670
5432
|
this._recordingController.pauseRecording();
|
|
4671
5433
|
}
|
|
4672
5434
|
resumeRecording() {
|
|
4673
5435
|
this._recordingController.resumeRecording();
|
|
5436
|
+
this._wasPlayingDuringRecording = false;
|
|
5437
|
+
}
|
|
5438
|
+
/**
|
|
5439
|
+
* Audacity-style pause toggle for active recordings: pauses both the
|
|
5440
|
+
* worklet capture and (if running) the playback Transport. On resume,
|
|
5441
|
+
* Transport restarts only if it was running before — non-overdub
|
|
5442
|
+
* recordings stay silent on resume.
|
|
5443
|
+
*/
|
|
5444
|
+
togglePauseRecording() {
|
|
5445
|
+
if (!this.isRecording) return;
|
|
5446
|
+
if (this.isRecordingPaused) {
|
|
5447
|
+
const wasPlaying = this._wasPlayingDuringRecording;
|
|
5448
|
+
this.resumeRecording();
|
|
5449
|
+
if (wasPlaying) {
|
|
5450
|
+
void this.play(this.currentTime);
|
|
5451
|
+
}
|
|
5452
|
+
} else {
|
|
5453
|
+
this.pauseRecording();
|
|
5454
|
+
if (this._isPlaying) {
|
|
5455
|
+
this._wasPlayingDuringRecording = true;
|
|
5456
|
+
this.pause();
|
|
5457
|
+
}
|
|
5458
|
+
}
|
|
4674
5459
|
}
|
|
4675
5460
|
stopRecording() {
|
|
4676
|
-
this.
|
|
5461
|
+
this._wasPlayingDuringRecording = false;
|
|
5462
|
+
return this._recordingController.stopRecording();
|
|
4677
5463
|
}
|
|
4678
5464
|
_addRecordedClip(trackId, buf, startSample, durSamples, offsetSamples = 0) {
|
|
4679
5465
|
addRecordedClip(this, trackId, buf, startSample, durSamples, offsetSamples);
|
|
@@ -4709,7 +5495,7 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4709
5495
|
const w = Math.floor(audibleSamples / renderSpp);
|
|
4710
5496
|
return rs.peaks.map((chPeaks, ch) => {
|
|
4711
5497
|
const slicedPeaks = latencyPixels > 0 ? chPeaks.slice(latencyPixels * 2) : chPeaks;
|
|
4712
|
-
return
|
|
5498
|
+
return import_lit14.html`
|
|
4713
5499
|
<daw-waveform
|
|
4714
5500
|
data-recording-track=${trackId}
|
|
4715
5501
|
data-recording-channel=${ch}
|
|
@@ -4806,11 +5592,11 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4806
5592
|
trackHeight: this.waveHeight * numChannels + (this.clipHeaders ? this.clipHeaderHeight : 0)
|
|
4807
5593
|
};
|
|
4808
5594
|
});
|
|
4809
|
-
return
|
|
4810
|
-
${orderedTracks.length > 0 || this.indefinitePlayback ?
|
|
4811
|
-
${this.timescale ?
|
|
5595
|
+
return import_lit14.html`
|
|
5596
|
+
${orderedTracks.length > 0 || this.indefinitePlayback ? import_lit14.html`<div class="controls-column">
|
|
5597
|
+
${this.timescale ? import_lit14.html`<div style="height: 30px;"></div>` : ""}
|
|
4812
5598
|
${orderedTracks.map(
|
|
4813
|
-
(t) =>
|
|
5599
|
+
(t) => import_lit14.html`
|
|
4814
5600
|
<daw-track-controls
|
|
4815
5601
|
style="height: ${t.trackHeight}px;"
|
|
4816
5602
|
.trackId=${t.trackId}
|
|
@@ -4833,7 +5619,7 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4833
5619
|
@dragleave=${this._onDragLeave}
|
|
4834
5620
|
@drop=${this._onDrop}
|
|
4835
5621
|
>
|
|
4836
|
-
${(orderedTracks.length > 0 || this.scaleMode === "beats" || this.indefinitePlayback) && this.timescale ?
|
|
5622
|
+
${(orderedTracks.length > 0 || this.scaleMode === "beats" || this.indefinitePlayback) && this.timescale ? import_lit14.html`<daw-ruler
|
|
4837
5623
|
.samplesPerPixel=${spp}
|
|
4838
5624
|
.sampleRate=${this.effectiveSampleRate}
|
|
4839
5625
|
.duration=${this._duration}
|
|
@@ -4843,7 +5629,7 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4843
5629
|
.ppqn=${this.ppqn}
|
|
4844
5630
|
.totalWidth=${this._totalWidth}
|
|
4845
5631
|
></daw-ruler>` : ""}
|
|
4846
|
-
${this.scaleMode === "beats" ?
|
|
5632
|
+
${this.scaleMode === "beats" ? import_lit14.html`<daw-grid
|
|
4847
5633
|
style="top: ${this.timescale ? 30 : 0}px;"
|
|
4848
5634
|
.ticksPerPixel=${this.ticksPerPixel}
|
|
4849
5635
|
.meterEntries=${this._meterEntries}
|
|
@@ -4853,11 +5639,11 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4853
5639
|
.length=${this._totalWidth}
|
|
4854
5640
|
.height=${orderedTracks.length > 0 ? orderedTracks.reduce((sum, t) => sum + t.trackHeight + 1, 0) : this._emptyGridHeight}
|
|
4855
5641
|
></daw-grid>` : ""}
|
|
4856
|
-
${orderedTracks.length > 0 || this.scaleMode === "beats" || this.indefinitePlayback ?
|
|
5642
|
+
${orderedTracks.length > 0 || this.scaleMode === "beats" || this.indefinitePlayback ? import_lit14.html`<daw-selection .startPx=${selStartPx} .endPx=${selEndPx}></daw-selection>
|
|
4857
5643
|
<daw-playhead></daw-playhead>` : ""}
|
|
4858
5644
|
${orderedTracks.map((t) => {
|
|
4859
5645
|
const channelHeight = this.waveHeight;
|
|
4860
|
-
return
|
|
5646
|
+
return import_lit14.html`
|
|
4861
5647
|
<div
|
|
4862
5648
|
class="track-row ${t.trackId === this._selectedTrackId ? "selected" : ""}"
|
|
4863
5649
|
style="height: ${t.trackHeight}px;"
|
|
@@ -4920,12 +5706,12 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4920
5706
|
const channels = segmentChannels ?? peakData?.data ?? [new Int16Array(0)];
|
|
4921
5707
|
const hdrH = this.clipHeaders ? this.clipHeaderHeight : 0;
|
|
4922
5708
|
const chH = this.waveHeight;
|
|
4923
|
-
return
|
|
5709
|
+
return import_lit14.html` <div
|
|
4924
5710
|
class="clip-container"
|
|
4925
5711
|
style="left:${clipLeft}px;top:0;width:${width}px;height:${t.trackHeight}px;"
|
|
4926
5712
|
data-clip-id=${clip.id}
|
|
4927
5713
|
>
|
|
4928
|
-
${hdrH > 0 ?
|
|
5714
|
+
${hdrH > 0 ? import_lit14.html`<div
|
|
4929
5715
|
class="clip-header"
|
|
4930
5716
|
data-clip-id=${clip.id}
|
|
4931
5717
|
data-track-id=${t.trackId}
|
|
@@ -4933,21 +5719,48 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4933
5719
|
>
|
|
4934
5720
|
<span>${clip.name || t.descriptor?.name || ""}</span>
|
|
4935
5721
|
</div>` : ""}
|
|
4936
|
-
${
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
.peaks=${chPeaks}
|
|
5722
|
+
${t.descriptor?.renderMode === "piano-roll" ? import_lit14.html`<daw-piano-roll
|
|
5723
|
+
style="position:absolute;left:0;top:${hdrH}px;"
|
|
5724
|
+
.midiNotes=${clip.midiNotes ?? []}
|
|
4940
5725
|
.length=${peakData?.length ?? width}
|
|
4941
|
-
.waveHeight=${chH}
|
|
4942
|
-
.
|
|
4943
|
-
.
|
|
5726
|
+
.waveHeight=${chH * channels.length}
|
|
5727
|
+
.samplesPerPixel=${this._renderSpp}
|
|
5728
|
+
.sampleRate=${this.effectiveSampleRate}
|
|
5729
|
+
.clipOffsetSeconds=${(clip.offsetSamples ?? 0) / this.effectiveSampleRate}
|
|
4944
5730
|
.visibleStart=${this._viewport.visibleStart}
|
|
4945
5731
|
.visibleEnd=${this._viewport.visibleEnd}
|
|
4946
5732
|
.originX=${clipLeft}
|
|
4947
|
-
|
|
4948
|
-
></daw-
|
|
5733
|
+
?selected=${t.trackId === this._selectedTrackId}
|
|
5734
|
+
></daw-piano-roll>` : t.descriptor?.renderMode === "spectrogram" ? channels.map(
|
|
5735
|
+
(_chPeaks, chIdx) => import_lit14.html`<daw-spectrogram
|
|
5736
|
+
style="position:absolute;left:0;top:${hdrH + chIdx * chH}px;height:${chH}px;width:${peakData?.length ?? width}px;"
|
|
5737
|
+
.clipId=${clip.id}
|
|
5738
|
+
.trackId=${t.trackId}
|
|
5739
|
+
.channelIndex=${chIdx}
|
|
5740
|
+
.length=${peakData?.length ?? width}
|
|
5741
|
+
.waveHeight=${chH}
|
|
5742
|
+
.samplesPerPixel=${this._renderSpp}
|
|
5743
|
+
.sampleRate=${this.effectiveSampleRate}
|
|
5744
|
+
.clipOffsetSeconds=${(clip.offsetSamples ?? 0) / this.effectiveSampleRate}
|
|
5745
|
+
.visibleStart=${this._viewport.visibleStart}
|
|
5746
|
+
.visibleEnd=${this._viewport.visibleEnd}
|
|
5747
|
+
.originX=${clipLeft}
|
|
5748
|
+
></daw-spectrogram>`
|
|
5749
|
+
) : channels.map(
|
|
5750
|
+
(chPeaks, chIdx) => import_lit14.html` <daw-waveform
|
|
5751
|
+
style="position:absolute;left:0;top:${hdrH + chIdx * chH}px;"
|
|
5752
|
+
.peaks=${chPeaks}
|
|
5753
|
+
.length=${peakData?.length ?? width}
|
|
5754
|
+
.waveHeight=${chH}
|
|
5755
|
+
.barWidth=${this.barWidth}
|
|
5756
|
+
.barGap=${this.barGap}
|
|
5757
|
+
.visibleStart=${this._viewport.visibleStart}
|
|
5758
|
+
.visibleEnd=${this._viewport.visibleEnd}
|
|
5759
|
+
.originX=${clipLeft}
|
|
5760
|
+
.segments=${clipSegments}
|
|
5761
|
+
></daw-waveform>`
|
|
4949
5762
|
)}
|
|
4950
|
-
${this.interactiveClips ?
|
|
5763
|
+
${this.interactiveClips ? import_lit14.html` <div
|
|
4951
5764
|
class="clip-boundary"
|
|
4952
5765
|
data-boundary-edge="left"
|
|
4953
5766
|
data-clip-id=${clip.id}
|
|
@@ -4973,7 +5786,7 @@ var DawEditorElement = class extends import_lit13.LitElement {
|
|
|
4973
5786
|
};
|
|
4974
5787
|
DawEditorElement.styles = [
|
|
4975
5788
|
hostStyles,
|
|
4976
|
-
|
|
5789
|
+
import_lit14.css`
|
|
4977
5790
|
:host {
|
|
4978
5791
|
display: flex;
|
|
4979
5792
|
position: relative;
|
|
@@ -5021,99 +5834,105 @@ DawEditorElement.styles = [
|
|
|
5021
5834
|
];
|
|
5022
5835
|
DawEditorElement._CONTROL_PROPS = /* @__PURE__ */ new Set(["volume", "pan", "muted", "soloed"]);
|
|
5023
5836
|
__decorateClass([
|
|
5024
|
-
(0,
|
|
5837
|
+
(0, import_decorators12.property)({ type: Number, attribute: "samples-per-pixel", noAccessor: true })
|
|
5025
5838
|
], DawEditorElement.prototype, "samplesPerPixel", 1);
|
|
5026
5839
|
__decorateClass([
|
|
5027
|
-
(0,
|
|
5840
|
+
(0, import_decorators12.property)({ type: Number, attribute: "wave-height" })
|
|
5028
5841
|
], DawEditorElement.prototype, "waveHeight", 2);
|
|
5029
5842
|
__decorateClass([
|
|
5030
|
-
(0,
|
|
5843
|
+
(0, import_decorators12.property)({ type: Boolean })
|
|
5031
5844
|
], DawEditorElement.prototype, "timescale", 2);
|
|
5032
5845
|
__decorateClass([
|
|
5033
|
-
(0,
|
|
5846
|
+
(0, import_decorators12.property)({ type: Boolean })
|
|
5034
5847
|
], DawEditorElement.prototype, "mono", 2);
|
|
5035
5848
|
__decorateClass([
|
|
5036
|
-
(0,
|
|
5849
|
+
(0, import_decorators12.property)({ type: Number, attribute: "bar-width" })
|
|
5037
5850
|
], DawEditorElement.prototype, "barWidth", 2);
|
|
5038
5851
|
__decorateClass([
|
|
5039
|
-
(0,
|
|
5852
|
+
(0, import_decorators12.property)({ type: Number, attribute: "bar-gap" })
|
|
5040
5853
|
], DawEditorElement.prototype, "barGap", 2);
|
|
5041
5854
|
__decorateClass([
|
|
5042
|
-
(0,
|
|
5855
|
+
(0, import_decorators12.property)({ type: Boolean, attribute: "file-drop" })
|
|
5043
5856
|
], DawEditorElement.prototype, "fileDrop", 2);
|
|
5044
5857
|
__decorateClass([
|
|
5045
|
-
(0,
|
|
5858
|
+
(0, import_decorators12.property)({ type: Boolean, attribute: "clip-headers" })
|
|
5046
5859
|
], DawEditorElement.prototype, "clipHeaders", 2);
|
|
5047
5860
|
__decorateClass([
|
|
5048
|
-
(0,
|
|
5861
|
+
(0, import_decorators12.property)({ type: Number, attribute: "clip-header-height" })
|
|
5049
5862
|
], DawEditorElement.prototype, "clipHeaderHeight", 2);
|
|
5050
5863
|
__decorateClass([
|
|
5051
|
-
(0,
|
|
5864
|
+
(0, import_decorators12.property)({ type: Boolean, attribute: "interactive-clips" })
|
|
5052
5865
|
], DawEditorElement.prototype, "interactiveClips", 2);
|
|
5053
5866
|
__decorateClass([
|
|
5054
|
-
(0,
|
|
5867
|
+
(0, import_decorators12.property)({ type: Boolean, attribute: "indefinite-playback" })
|
|
5055
5868
|
], DawEditorElement.prototype, "indefinitePlayback", 2);
|
|
5056
5869
|
__decorateClass([
|
|
5057
|
-
(0,
|
|
5870
|
+
(0, import_decorators12.property)({ attribute: false, noAccessor: true })
|
|
5871
|
+
], DawEditorElement.prototype, "spectrogramConfig", 1);
|
|
5872
|
+
__decorateClass([
|
|
5873
|
+
(0, import_decorators12.property)({ attribute: false, noAccessor: true })
|
|
5874
|
+
], DawEditorElement.prototype, "spectrogramColorMap", 1);
|
|
5875
|
+
__decorateClass([
|
|
5876
|
+
(0, import_decorators12.property)({ type: String, attribute: "scale-mode" })
|
|
5058
5877
|
], DawEditorElement.prototype, "scaleMode", 2);
|
|
5059
5878
|
__decorateClass([
|
|
5060
|
-
(0,
|
|
5879
|
+
(0, import_decorators12.property)({ type: Number, attribute: "ticks-per-pixel", noAccessor: true })
|
|
5061
5880
|
], DawEditorElement.prototype, "ticksPerPixel", 1);
|
|
5062
5881
|
__decorateClass([
|
|
5063
|
-
(0,
|
|
5882
|
+
(0, import_decorators12.property)({ type: Number, noAccessor: true })
|
|
5064
5883
|
], DawEditorElement.prototype, "bpm", 1);
|
|
5065
5884
|
__decorateClass([
|
|
5066
|
-
(0,
|
|
5885
|
+
(0, import_decorators12.property)({ attribute: false })
|
|
5067
5886
|
], DawEditorElement.prototype, "timeSignature", 2);
|
|
5068
5887
|
__decorateClass([
|
|
5069
|
-
(0,
|
|
5888
|
+
(0, import_decorators12.property)({ attribute: false })
|
|
5070
5889
|
], DawEditorElement.prototype, "meterEntries", 2);
|
|
5071
5890
|
__decorateClass([
|
|
5072
|
-
(0,
|
|
5891
|
+
(0, import_decorators12.property)({ type: Number, noAccessor: true })
|
|
5073
5892
|
], DawEditorElement.prototype, "ppqn", 1);
|
|
5074
5893
|
__decorateClass([
|
|
5075
|
-
(0,
|
|
5894
|
+
(0, import_decorators12.property)({ type: String, attribute: "snap-to" })
|
|
5076
5895
|
], DawEditorElement.prototype, "snapTo", 2);
|
|
5077
5896
|
__decorateClass([
|
|
5078
|
-
(0,
|
|
5897
|
+
(0, import_decorators12.property)({ attribute: false })
|
|
5079
5898
|
], DawEditorElement.prototype, "secondsToTicks", 2);
|
|
5080
5899
|
__decorateClass([
|
|
5081
|
-
(0,
|
|
5900
|
+
(0, import_decorators12.property)({ attribute: false })
|
|
5082
5901
|
], DawEditorElement.prototype, "ticksToSeconds", 2);
|
|
5083
5902
|
__decorateClass([
|
|
5084
|
-
(0,
|
|
5903
|
+
(0, import_decorators12.state)()
|
|
5085
5904
|
], DawEditorElement.prototype, "_tracks", 2);
|
|
5086
5905
|
__decorateClass([
|
|
5087
|
-
(0,
|
|
5906
|
+
(0, import_decorators12.state)()
|
|
5088
5907
|
], DawEditorElement.prototype, "_engineTracks", 2);
|
|
5089
5908
|
__decorateClass([
|
|
5090
|
-
(0,
|
|
5909
|
+
(0, import_decorators12.state)()
|
|
5091
5910
|
], DawEditorElement.prototype, "_peaksData", 2);
|
|
5092
5911
|
__decorateClass([
|
|
5093
|
-
(0,
|
|
5912
|
+
(0, import_decorators12.state)()
|
|
5094
5913
|
], DawEditorElement.prototype, "_isPlaying", 2);
|
|
5095
5914
|
__decorateClass([
|
|
5096
|
-
(0,
|
|
5915
|
+
(0, import_decorators12.state)()
|
|
5097
5916
|
], DawEditorElement.prototype, "_duration", 2);
|
|
5098
5917
|
__decorateClass([
|
|
5099
|
-
(0,
|
|
5918
|
+
(0, import_decorators12.state)()
|
|
5100
5919
|
], DawEditorElement.prototype, "_selectedTrackId", 2);
|
|
5101
5920
|
__decorateClass([
|
|
5102
|
-
(0,
|
|
5921
|
+
(0, import_decorators12.state)()
|
|
5103
5922
|
], DawEditorElement.prototype, "_dragOver", 2);
|
|
5104
5923
|
__decorateClass([
|
|
5105
|
-
(0,
|
|
5924
|
+
(0, import_decorators12.property)({ attribute: false })
|
|
5106
5925
|
], DawEditorElement.prototype, "adapter", 1);
|
|
5107
5926
|
__decorateClass([
|
|
5108
|
-
(0,
|
|
5927
|
+
(0, import_decorators12.property)({ attribute: "eager-resume" })
|
|
5109
5928
|
], DawEditorElement.prototype, "eagerResume", 2);
|
|
5110
5929
|
DawEditorElement = __decorateClass([
|
|
5111
|
-
(0,
|
|
5930
|
+
(0, import_decorators12.customElement)("daw-editor")
|
|
5112
5931
|
], DawEditorElement);
|
|
5113
5932
|
|
|
5114
5933
|
// src/elements/daw-ruler.ts
|
|
5115
|
-
var
|
|
5116
|
-
var
|
|
5934
|
+
var import_lit15 = require("lit");
|
|
5935
|
+
var import_decorators13 = require("lit/decorators.js");
|
|
5117
5936
|
|
|
5118
5937
|
// src/utils/time-format.ts
|
|
5119
5938
|
function formatTime(milliseconds) {
|
|
@@ -5164,8 +5983,8 @@ function computeTemporalTicks(samplesPerPixel, sampleRate, duration, rulerHeight
|
|
|
5164
5983
|
}
|
|
5165
5984
|
|
|
5166
5985
|
// src/elements/daw-ruler.ts
|
|
5167
|
-
var
|
|
5168
|
-
var DawRulerElement = class extends
|
|
5986
|
+
var MAX_CANVAS_WIDTH4 = 1e3;
|
|
5987
|
+
var DawRulerElement = class extends import_lit15.LitElement {
|
|
5169
5988
|
constructor() {
|
|
5170
5989
|
super(...arguments);
|
|
5171
5990
|
this.samplesPerPixel = 1024;
|
|
@@ -5209,33 +6028,33 @@ var DawRulerElement = class extends import_lit14.LitElement {
|
|
|
5209
6028
|
}
|
|
5210
6029
|
render() {
|
|
5211
6030
|
const widthX = this.scaleMode === "beats" ? this.totalWidth : this._tickData?.widthX ?? 0;
|
|
5212
|
-
if (widthX <= 0) return
|
|
5213
|
-
const totalChunks = Math.ceil(widthX /
|
|
6031
|
+
if (widthX <= 0) return import_lit15.html``;
|
|
6032
|
+
const totalChunks = Math.ceil(widthX / MAX_CANVAS_WIDTH4);
|
|
5214
6033
|
const indices = Array.from({ length: totalChunks }, (_, i) => i);
|
|
5215
6034
|
const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
5216
6035
|
const beatsLabels = this.scaleMode === "beats" ? this._musicalTickData?.ticks.filter((t) => t.label) ?? [] : [];
|
|
5217
6036
|
const temporalLabels = this.scaleMode !== "beats" ? this._tickData?.labels ?? [] : [];
|
|
5218
|
-
return
|
|
6037
|
+
return import_lit15.html`
|
|
5219
6038
|
<div class="container" style="width: ${widthX}px; height: ${this.rulerHeight}px;">
|
|
5220
6039
|
${indices.map((i) => {
|
|
5221
|
-
const width = Math.min(
|
|
5222
|
-
return
|
|
6040
|
+
const width = Math.min(MAX_CANVAS_WIDTH4, widthX - i * MAX_CANVAS_WIDTH4);
|
|
6041
|
+
return import_lit15.html`
|
|
5223
6042
|
<canvas
|
|
5224
6043
|
data-index=${i}
|
|
5225
6044
|
width=${width * dpr}
|
|
5226
6045
|
height=${this.rulerHeight * dpr}
|
|
5227
|
-
style="left: ${i *
|
|
6046
|
+
style="left: ${i * MAX_CANVAS_WIDTH4}px; width: ${width}px; height: ${this.rulerHeight}px;"
|
|
5228
6047
|
></canvas>
|
|
5229
6048
|
`;
|
|
5230
6049
|
})}
|
|
5231
6050
|
${this.scaleMode === "beats" ? beatsLabels.map(
|
|
5232
|
-
(t) =>
|
|
6051
|
+
(t) => import_lit15.html`<span
|
|
5233
6052
|
class="label ${t.pixel > 0 ? "centered" : ""}"
|
|
5234
6053
|
style="left: ${t.pixel > 0 ? t.pixel : t.pixel + 4}px;"
|
|
5235
6054
|
>${t.label}</span
|
|
5236
6055
|
>`
|
|
5237
6056
|
) : temporalLabels.map(
|
|
5238
|
-
({ pix, text }) =>
|
|
6057
|
+
({ pix, text }) => import_lit15.html`<span class="label" style="left: ${pix + 4}px;">${text}</span>`
|
|
5239
6058
|
)}
|
|
5240
6059
|
</div>
|
|
5241
6060
|
`;
|
|
@@ -5253,8 +6072,8 @@ var DawRulerElement = class extends import_lit14.LitElement {
|
|
|
5253
6072
|
const idx = Number(canvas.dataset.index);
|
|
5254
6073
|
const ctx = canvas.getContext("2d");
|
|
5255
6074
|
if (!ctx) continue;
|
|
5256
|
-
const canvasWidth = Math.min(
|
|
5257
|
-
const globalOffset = idx *
|
|
6075
|
+
const canvasWidth = Math.min(MAX_CANVAS_WIDTH4, widthX - idx * MAX_CANVAS_WIDTH4);
|
|
6076
|
+
const globalOffset = idx * MAX_CANVAS_WIDTH4;
|
|
5258
6077
|
ctx.resetTransform();
|
|
5259
6078
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
5260
6079
|
ctx.scale(dpr, dpr);
|
|
@@ -5286,7 +6105,7 @@ var DawRulerElement = class extends import_lit14.LitElement {
|
|
|
5286
6105
|
}
|
|
5287
6106
|
}
|
|
5288
6107
|
};
|
|
5289
|
-
DawRulerElement.styles =
|
|
6108
|
+
DawRulerElement.styles = import_lit15.css`
|
|
5290
6109
|
:host {
|
|
5291
6110
|
display: block;
|
|
5292
6111
|
position: relative;
|
|
@@ -5312,40 +6131,40 @@ DawRulerElement.styles = import_lit14.css`
|
|
|
5312
6131
|
}
|
|
5313
6132
|
`;
|
|
5314
6133
|
__decorateClass([
|
|
5315
|
-
(0,
|
|
6134
|
+
(0, import_decorators13.property)({ type: Number, attribute: false })
|
|
5316
6135
|
], DawRulerElement.prototype, "samplesPerPixel", 2);
|
|
5317
6136
|
__decorateClass([
|
|
5318
|
-
(0,
|
|
6137
|
+
(0, import_decorators13.property)({ type: Number, attribute: false })
|
|
5319
6138
|
], DawRulerElement.prototype, "sampleRate", 2);
|
|
5320
6139
|
__decorateClass([
|
|
5321
|
-
(0,
|
|
6140
|
+
(0, import_decorators13.property)({ type: Number, attribute: false })
|
|
5322
6141
|
], DawRulerElement.prototype, "duration", 2);
|
|
5323
6142
|
__decorateClass([
|
|
5324
|
-
(0,
|
|
6143
|
+
(0, import_decorators13.property)({ type: Number, attribute: false })
|
|
5325
6144
|
], DawRulerElement.prototype, "rulerHeight", 2);
|
|
5326
6145
|
__decorateClass([
|
|
5327
|
-
(0,
|
|
6146
|
+
(0, import_decorators13.property)({ type: String, attribute: false })
|
|
5328
6147
|
], DawRulerElement.prototype, "scaleMode", 2);
|
|
5329
6148
|
__decorateClass([
|
|
5330
|
-
(0,
|
|
6149
|
+
(0, import_decorators13.property)({ type: Number, attribute: false })
|
|
5331
6150
|
], DawRulerElement.prototype, "ticksPerPixel", 2);
|
|
5332
6151
|
__decorateClass([
|
|
5333
|
-
(0,
|
|
6152
|
+
(0, import_decorators13.property)({ attribute: false })
|
|
5334
6153
|
], DawRulerElement.prototype, "meterEntries", 2);
|
|
5335
6154
|
__decorateClass([
|
|
5336
|
-
(0,
|
|
6155
|
+
(0, import_decorators13.property)({ type: Number, attribute: false })
|
|
5337
6156
|
], DawRulerElement.prototype, "ppqn", 2);
|
|
5338
6157
|
__decorateClass([
|
|
5339
|
-
(0,
|
|
6158
|
+
(0, import_decorators13.property)({ type: Number, attribute: false })
|
|
5340
6159
|
], DawRulerElement.prototype, "totalWidth", 2);
|
|
5341
6160
|
DawRulerElement = __decorateClass([
|
|
5342
|
-
(0,
|
|
6161
|
+
(0, import_decorators13.customElement)("daw-ruler")
|
|
5343
6162
|
], DawRulerElement);
|
|
5344
6163
|
|
|
5345
6164
|
// src/elements/daw-selection.ts
|
|
5346
|
-
var
|
|
5347
|
-
var
|
|
5348
|
-
var DawSelectionElement = class extends
|
|
6165
|
+
var import_lit16 = require("lit");
|
|
6166
|
+
var import_decorators14 = require("lit/decorators.js");
|
|
6167
|
+
var DawSelectionElement = class extends import_lit16.LitElement {
|
|
5349
6168
|
constructor() {
|
|
5350
6169
|
super(...arguments);
|
|
5351
6170
|
this.startPx = 0;
|
|
@@ -5354,11 +6173,11 @@ var DawSelectionElement = class extends import_lit15.LitElement {
|
|
|
5354
6173
|
render() {
|
|
5355
6174
|
const left = Math.min(this.startPx, this.endPx);
|
|
5356
6175
|
const width = Math.abs(this.endPx - this.startPx);
|
|
5357
|
-
if (width === 0) return
|
|
5358
|
-
return
|
|
6176
|
+
if (width === 0) return import_lit16.html``;
|
|
6177
|
+
return import_lit16.html`<div style="left: ${left}px; width: ${width}px;"></div>`;
|
|
5359
6178
|
}
|
|
5360
6179
|
};
|
|
5361
|
-
DawSelectionElement.styles =
|
|
6180
|
+
DawSelectionElement.styles = import_lit16.css`
|
|
5362
6181
|
:host {
|
|
5363
6182
|
position: absolute;
|
|
5364
6183
|
top: 0;
|
|
@@ -5375,18 +6194,18 @@ DawSelectionElement.styles = import_lit15.css`
|
|
|
5375
6194
|
}
|
|
5376
6195
|
`;
|
|
5377
6196
|
__decorateClass([
|
|
5378
|
-
(0,
|
|
6197
|
+
(0, import_decorators14.property)({ type: Number, attribute: false })
|
|
5379
6198
|
], DawSelectionElement.prototype, "startPx", 2);
|
|
5380
6199
|
__decorateClass([
|
|
5381
|
-
(0,
|
|
6200
|
+
(0, import_decorators14.property)({ type: Number, attribute: false })
|
|
5382
6201
|
], DawSelectionElement.prototype, "endPx", 2);
|
|
5383
6202
|
DawSelectionElement = __decorateClass([
|
|
5384
|
-
(0,
|
|
6203
|
+
(0, import_decorators14.customElement)("daw-selection")
|
|
5385
6204
|
], DawSelectionElement);
|
|
5386
6205
|
|
|
5387
6206
|
// src/elements/daw-record-button.ts
|
|
5388
|
-
var
|
|
5389
|
-
var
|
|
6207
|
+
var import_lit17 = require("lit");
|
|
6208
|
+
var import_decorators15 = require("lit/decorators.js");
|
|
5390
6209
|
var DawRecordButtonElement = class extends DawTransportButton {
|
|
5391
6210
|
constructor() {
|
|
5392
6211
|
super(...arguments);
|
|
@@ -5427,7 +6246,7 @@ var DawRecordButtonElement = class extends DawTransportButton {
|
|
|
5427
6246
|
}
|
|
5428
6247
|
}
|
|
5429
6248
|
render() {
|
|
5430
|
-
return
|
|
6249
|
+
return import_lit17.html`
|
|
5431
6250
|
<button part="button" ?data-recording=${this._isRecording} @click=${this._onClick}>
|
|
5432
6251
|
<slot>Record</slot>
|
|
5433
6252
|
</button>
|
|
@@ -5447,7 +6266,7 @@ var DawRecordButtonElement = class extends DawTransportButton {
|
|
|
5447
6266
|
};
|
|
5448
6267
|
DawRecordButtonElement.styles = [
|
|
5449
6268
|
DawTransportButton.styles,
|
|
5450
|
-
|
|
6269
|
+
import_lit17.css`
|
|
5451
6270
|
button[data-recording] {
|
|
5452
6271
|
color: #d08070;
|
|
5453
6272
|
border-color: #d08070;
|
|
@@ -5456,17 +6275,17 @@ DawRecordButtonElement.styles = [
|
|
|
5456
6275
|
`
|
|
5457
6276
|
];
|
|
5458
6277
|
__decorateClass([
|
|
5459
|
-
(0,
|
|
6278
|
+
(0, import_decorators15.state)()
|
|
5460
6279
|
], DawRecordButtonElement.prototype, "_isRecording", 2);
|
|
5461
6280
|
DawRecordButtonElement = __decorateClass([
|
|
5462
|
-
(0,
|
|
6281
|
+
(0, import_decorators15.customElement)("daw-record-button")
|
|
5463
6282
|
], DawRecordButtonElement);
|
|
5464
6283
|
|
|
5465
6284
|
// src/elements/daw-keyboard-shortcuts.ts
|
|
5466
|
-
var
|
|
5467
|
-
var
|
|
6285
|
+
var import_lit18 = require("lit");
|
|
6286
|
+
var import_decorators16 = require("lit/decorators.js");
|
|
5468
6287
|
var import_core9 = require("@waveform-playlist/core");
|
|
5469
|
-
var DawKeyboardShortcutsElement = class extends
|
|
6288
|
+
var DawKeyboardShortcutsElement = class extends import_lit18.LitElement {
|
|
5470
6289
|
constructor() {
|
|
5471
6290
|
super(...arguments);
|
|
5472
6291
|
this.playback = false;
|
|
@@ -5620,17 +6439,198 @@ var DawKeyboardShortcutsElement = class extends import_lit17.LitElement {
|
|
|
5620
6439
|
}
|
|
5621
6440
|
};
|
|
5622
6441
|
__decorateClass([
|
|
5623
|
-
(0,
|
|
6442
|
+
(0, import_decorators16.property)({ type: Boolean })
|
|
5624
6443
|
], DawKeyboardShortcutsElement.prototype, "playback", 2);
|
|
5625
6444
|
__decorateClass([
|
|
5626
|
-
(0,
|
|
6445
|
+
(0, import_decorators16.property)({ type: Boolean })
|
|
5627
6446
|
], DawKeyboardShortcutsElement.prototype, "splitting", 2);
|
|
5628
6447
|
__decorateClass([
|
|
5629
|
-
(0,
|
|
6448
|
+
(0, import_decorators16.property)({ type: Boolean })
|
|
5630
6449
|
], DawKeyboardShortcutsElement.prototype, "undo", 2);
|
|
5631
6450
|
DawKeyboardShortcutsElement = __decorateClass([
|
|
5632
|
-
(0,
|
|
6451
|
+
(0, import_decorators16.customElement)("daw-keyboard-shortcuts")
|
|
5633
6452
|
], DawKeyboardShortcutsElement);
|
|
6453
|
+
|
|
6454
|
+
// src/elements/daw-spectrogram.ts
|
|
6455
|
+
var import_lit19 = require("lit");
|
|
6456
|
+
var import_decorators17 = require("lit/decorators.js");
|
|
6457
|
+
var MAX_CANVAS_WIDTH5 = 1e3;
|
|
6458
|
+
var DawSpectrogramElement = class extends import_lit19.LitElement {
|
|
6459
|
+
constructor() {
|
|
6460
|
+
super(...arguments);
|
|
6461
|
+
this.clipId = "";
|
|
6462
|
+
this.trackId = "";
|
|
6463
|
+
this.channelIndex = 0;
|
|
6464
|
+
this.length = 0;
|
|
6465
|
+
this.waveHeight = 128;
|
|
6466
|
+
this._samplesPerPixel = 1024;
|
|
6467
|
+
this._sampleRate = 44100;
|
|
6468
|
+
this.clipOffsetSeconds = 0;
|
|
6469
|
+
this.visibleStart = -Infinity;
|
|
6470
|
+
this.visibleEnd = Infinity;
|
|
6471
|
+
this.originX = 0;
|
|
6472
|
+
this._canvases = [];
|
|
6473
|
+
this._registeredCanvasIds = [];
|
|
6474
|
+
}
|
|
6475
|
+
get samplesPerPixel() {
|
|
6476
|
+
return this._samplesPerPixel;
|
|
6477
|
+
}
|
|
6478
|
+
set samplesPerPixel(value) {
|
|
6479
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
6480
|
+
console.warn("[dawcore] daw-spectrogram samplesPerPixel " + value + " is invalid \u2014 ignored");
|
|
6481
|
+
return;
|
|
6482
|
+
}
|
|
6483
|
+
const old = this._samplesPerPixel;
|
|
6484
|
+
this._samplesPerPixel = value;
|
|
6485
|
+
this.requestUpdate("samplesPerPixel", old);
|
|
6486
|
+
}
|
|
6487
|
+
get sampleRate() {
|
|
6488
|
+
return this._sampleRate;
|
|
6489
|
+
}
|
|
6490
|
+
set sampleRate(value) {
|
|
6491
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
6492
|
+
console.warn("[dawcore] daw-spectrogram sampleRate " + value + " is invalid \u2014 ignored");
|
|
6493
|
+
return;
|
|
6494
|
+
}
|
|
6495
|
+
const old = this._sampleRate;
|
|
6496
|
+
this._sampleRate = value;
|
|
6497
|
+
this.requestUpdate("sampleRate", old);
|
|
6498
|
+
}
|
|
6499
|
+
/**
|
|
6500
|
+
* Walk up to the editor host. `closest('daw-editor')` does NOT cross
|
|
6501
|
+
* shadow boundaries — and this element lives inside the editor's shadow
|
|
6502
|
+
* DOM — so use getRootNode().host to step out.
|
|
6503
|
+
*/
|
|
6504
|
+
_findHostEditor() {
|
|
6505
|
+
const root = this.getRootNode();
|
|
6506
|
+
const host = root instanceof ShadowRoot ? root.host : null;
|
|
6507
|
+
if (!host) return null;
|
|
6508
|
+
if (host.tagName === "DAW-EDITOR") return host;
|
|
6509
|
+
return host.closest("daw-editor");
|
|
6510
|
+
}
|
|
6511
|
+
willUpdate(changed) {
|
|
6512
|
+
const layoutChanged = changed.has("length") || changed.has("waveHeight") || changed.has("samplesPerPixel") || changed.has("clipId") || changed.has("channelIndex");
|
|
6513
|
+
if (layoutChanged) {
|
|
6514
|
+
this._rebuildChunks();
|
|
6515
|
+
}
|
|
6516
|
+
}
|
|
6517
|
+
_rebuildChunks() {
|
|
6518
|
+
this._unregisterAllCanvases();
|
|
6519
|
+
this._canvases = [];
|
|
6520
|
+
if (this.length <= 0) return;
|
|
6521
|
+
const chunkCount = Math.ceil(this.length / MAX_CANVAS_WIDTH5);
|
|
6522
|
+
for (let i = 0; i < chunkCount; i++) {
|
|
6523
|
+
const widthPx = Math.min(MAX_CANVAS_WIDTH5, this.length - i * MAX_CANVAS_WIDTH5);
|
|
6524
|
+
const canvas = document.createElement("canvas");
|
|
6525
|
+
canvas.style.left = i * MAX_CANVAS_WIDTH5 + "px";
|
|
6526
|
+
canvas.style.width = widthPx + "px";
|
|
6527
|
+
const dpr = window.devicePixelRatio || 1;
|
|
6528
|
+
canvas.width = widthPx * dpr;
|
|
6529
|
+
canvas.height = this.waveHeight * dpr;
|
|
6530
|
+
this._canvases.push(canvas);
|
|
6531
|
+
}
|
|
6532
|
+
}
|
|
6533
|
+
updated(_changed) {
|
|
6534
|
+
if (this._registeredCanvasIds.length === 0 && this._canvases.length > 0) {
|
|
6535
|
+
requestAnimationFrame(() => this._registerCanvases());
|
|
6536
|
+
}
|
|
6537
|
+
}
|
|
6538
|
+
_registerCanvases() {
|
|
6539
|
+
const editor = this._findHostEditor();
|
|
6540
|
+
if (!editor || typeof editor._spectrogramRegisterCanvas !== "function") return;
|
|
6541
|
+
for (let i = 0; i < this._canvases.length; i++) {
|
|
6542
|
+
const canvas = this._canvases[i];
|
|
6543
|
+
const canvasId = this.clipId + "-ch" + this.channelIndex + "-chunk" + i;
|
|
6544
|
+
let offscreen;
|
|
6545
|
+
try {
|
|
6546
|
+
offscreen = canvas.transferControlToOffscreen();
|
|
6547
|
+
} catch (err) {
|
|
6548
|
+
console.warn(
|
|
6549
|
+
"[dawcore] daw-spectrogram transferControlToOffscreen failed for " + canvasId + ": " + (err instanceof Error ? err.message : String(err))
|
|
6550
|
+
);
|
|
6551
|
+
continue;
|
|
6552
|
+
}
|
|
6553
|
+
editor._spectrogramRegisterCanvas({
|
|
6554
|
+
canvasId,
|
|
6555
|
+
canvas: offscreen,
|
|
6556
|
+
clipId: this.clipId,
|
|
6557
|
+
trackId: this.trackId,
|
|
6558
|
+
channelIndex: this.channelIndex,
|
|
6559
|
+
chunkIndex: i,
|
|
6560
|
+
globalPixelOffset: this.originX + i * MAX_CANVAS_WIDTH5,
|
|
6561
|
+
widthPx: parseFloat(canvas.style.width),
|
|
6562
|
+
heightPx: this.waveHeight
|
|
6563
|
+
});
|
|
6564
|
+
this._registeredCanvasIds.push(canvasId);
|
|
6565
|
+
}
|
|
6566
|
+
}
|
|
6567
|
+
_unregisterAllCanvases() {
|
|
6568
|
+
const editor = this._findHostEditor();
|
|
6569
|
+
if (editor && typeof editor._spectrogramUnregisterCanvas === "function") {
|
|
6570
|
+
for (const id of this._registeredCanvasIds) {
|
|
6571
|
+
editor._spectrogramUnregisterCanvas(id);
|
|
6572
|
+
}
|
|
6573
|
+
}
|
|
6574
|
+
this._registeredCanvasIds = [];
|
|
6575
|
+
}
|
|
6576
|
+
disconnectedCallback() {
|
|
6577
|
+
super.disconnectedCallback();
|
|
6578
|
+
this._unregisterAllCanvases();
|
|
6579
|
+
}
|
|
6580
|
+
render() {
|
|
6581
|
+
return import_lit19.html`${this._canvases.map((c) => c)}`;
|
|
6582
|
+
}
|
|
6583
|
+
};
|
|
6584
|
+
DawSpectrogramElement.styles = import_lit19.css`
|
|
6585
|
+
:host {
|
|
6586
|
+
display: block;
|
|
6587
|
+
position: relative;
|
|
6588
|
+
background: var(--daw-spectrogram-background, #000);
|
|
6589
|
+
}
|
|
6590
|
+
canvas {
|
|
6591
|
+
position: absolute;
|
|
6592
|
+
top: 0;
|
|
6593
|
+
left: 0;
|
|
6594
|
+
height: 100%;
|
|
6595
|
+
pointer-events: none;
|
|
6596
|
+
}
|
|
6597
|
+
`;
|
|
6598
|
+
__decorateClass([
|
|
6599
|
+
(0, import_decorators17.property)({ attribute: false })
|
|
6600
|
+
], DawSpectrogramElement.prototype, "clipId", 2);
|
|
6601
|
+
__decorateClass([
|
|
6602
|
+
(0, import_decorators17.property)({ attribute: false })
|
|
6603
|
+
], DawSpectrogramElement.prototype, "trackId", 2);
|
|
6604
|
+
__decorateClass([
|
|
6605
|
+
(0, import_decorators17.property)({ type: Number, attribute: false })
|
|
6606
|
+
], DawSpectrogramElement.prototype, "channelIndex", 2);
|
|
6607
|
+
__decorateClass([
|
|
6608
|
+
(0, import_decorators17.property)({ type: Number, attribute: false })
|
|
6609
|
+
], DawSpectrogramElement.prototype, "length", 2);
|
|
6610
|
+
__decorateClass([
|
|
6611
|
+
(0, import_decorators17.property)({ type: Number, attribute: false })
|
|
6612
|
+
], DawSpectrogramElement.prototype, "waveHeight", 2);
|
|
6613
|
+
__decorateClass([
|
|
6614
|
+
(0, import_decorators17.property)({ type: Number, attribute: false, noAccessor: true })
|
|
6615
|
+
], DawSpectrogramElement.prototype, "samplesPerPixel", 1);
|
|
6616
|
+
__decorateClass([
|
|
6617
|
+
(0, import_decorators17.property)({ type: Number, attribute: false, noAccessor: true })
|
|
6618
|
+
], DawSpectrogramElement.prototype, "sampleRate", 1);
|
|
6619
|
+
__decorateClass([
|
|
6620
|
+
(0, import_decorators17.property)({ type: Number, attribute: false })
|
|
6621
|
+
], DawSpectrogramElement.prototype, "clipOffsetSeconds", 2);
|
|
6622
|
+
__decorateClass([
|
|
6623
|
+
(0, import_decorators17.property)({ type: Number, attribute: false })
|
|
6624
|
+
], DawSpectrogramElement.prototype, "visibleStart", 2);
|
|
6625
|
+
__decorateClass([
|
|
6626
|
+
(0, import_decorators17.property)({ type: Number, attribute: false })
|
|
6627
|
+
], DawSpectrogramElement.prototype, "visibleEnd", 2);
|
|
6628
|
+
__decorateClass([
|
|
6629
|
+
(0, import_decorators17.property)({ type: Number, attribute: false })
|
|
6630
|
+
], DawSpectrogramElement.prototype, "originX", 2);
|
|
6631
|
+
DawSpectrogramElement = __decorateClass([
|
|
6632
|
+
(0, import_decorators17.customElement)("daw-spectrogram")
|
|
6633
|
+
], DawSpectrogramElement);
|
|
5634
6634
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5635
6635
|
0 && (module.exports = {
|
|
5636
6636
|
AudioResumeController,
|
|
@@ -5640,11 +6640,13 @@ DawKeyboardShortcutsElement = __decorateClass([
|
|
|
5640
6640
|
DawGridElement,
|
|
5641
6641
|
DawKeyboardShortcutsElement,
|
|
5642
6642
|
DawPauseButtonElement,
|
|
6643
|
+
DawPianoRollElement,
|
|
5643
6644
|
DawPlayButtonElement,
|
|
5644
6645
|
DawPlayheadElement,
|
|
5645
6646
|
DawRecordButtonElement,
|
|
5646
6647
|
DawRulerElement,
|
|
5647
6648
|
DawSelectionElement,
|
|
6649
|
+
DawSpectrogramElement,
|
|
5648
6650
|
DawStopButtonElement,
|
|
5649
6651
|
DawTrackControlsElement,
|
|
5650
6652
|
DawTrackElement,
|
|
@@ -5652,6 +6654,7 @@ DawKeyboardShortcutsElement = __decorateClass([
|
|
|
5652
6654
|
DawTransportElement,
|
|
5653
6655
|
DawWaveformElement,
|
|
5654
6656
|
RecordingController,
|
|
6657
|
+
SpectrogramController,
|
|
5655
6658
|
isDomClip,
|
|
5656
6659
|
splitAtPlayhead
|
|
5657
6660
|
});
|