@dawcore/components 0.0.9 → 0.0.11
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/README.md +18 -20
- package/dist/index.d.mts +129 -3
- package/dist/index.d.ts +129 -3
- package/dist/index.js +816 -182
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +799 -166
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -14
package/dist/index.js
CHANGED
|
@@ -42,6 +42,7 @@ __export(index_exports, {
|
|
|
42
42
|
ClipPointerHandler: () => ClipPointerHandler,
|
|
43
43
|
DawClipElement: () => DawClipElement,
|
|
44
44
|
DawEditorElement: () => DawEditorElement,
|
|
45
|
+
DawGridElement: () => DawGridElement,
|
|
45
46
|
DawKeyboardShortcutsElement: () => DawKeyboardShortcutsElement,
|
|
46
47
|
DawPauseButtonElement: () => DawPauseButtonElement,
|
|
47
48
|
DawPlayButtonElement: () => DawPlayButtonElement,
|
|
@@ -244,7 +245,7 @@ function getVisibleChunkIndices(totalWidth, chunkWidth, visibleStart, visibleEnd
|
|
|
244
245
|
|
|
245
246
|
// src/elements/daw-waveform.ts
|
|
246
247
|
var MAX_CANVAS_WIDTH = 1e3;
|
|
247
|
-
var LAYOUT_PROPS = /* @__PURE__ */ new Set(["length", "waveHeight", "barWidth", "barGap"]);
|
|
248
|
+
var LAYOUT_PROPS = /* @__PURE__ */ new Set(["length", "waveHeight", "barWidth", "barGap", "segments"]);
|
|
248
249
|
function groupDirtyByChunk(dirtyPixels, step) {
|
|
249
250
|
const dirtyByChunk = /* @__PURE__ */ new Map();
|
|
250
251
|
for (const peakIdx of dirtyPixels) {
|
|
@@ -351,14 +352,22 @@ var DawWaveformElement = class extends import_lit3.LitElement {
|
|
|
351
352
|
const halfHeight = this.waveHeight / 2;
|
|
352
353
|
const bits = this.bits;
|
|
353
354
|
const waveColor = getComputedStyle(this).getPropertyValue("--daw-wave-color").trim() || "#c49a6c";
|
|
354
|
-
const dirtyByChunk = groupDirtyByChunk(this._dirtyPixels, step);
|
|
355
355
|
this._drawnChunks.clear();
|
|
356
|
-
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
356
|
+
if (this.segments) {
|
|
357
|
+
for (const canvas of canvases) {
|
|
358
|
+
const chunkIdx = Number(canvas.dataset.index);
|
|
359
|
+
this._drawnChunks.add(chunkIdx);
|
|
360
|
+
this._drawSegments(canvas, chunkIdx, dpr, halfHeight, bits, waveColor);
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
const dirtyByChunk = groupDirtyByChunk(this._dirtyPixels, step);
|
|
364
|
+
for (const canvas of canvases) {
|
|
365
|
+
const chunkIdx = Number(canvas.dataset.index);
|
|
366
|
+
this._drawnChunks.add(chunkIdx);
|
|
367
|
+
const range = dirtyByChunk.get(chunkIdx);
|
|
368
|
+
if (!range) continue;
|
|
369
|
+
this._drawChunk(canvas, chunkIdx, range, step, dpr, halfHeight, bits, waveColor);
|
|
370
|
+
}
|
|
362
371
|
}
|
|
363
372
|
this._dirtyPixels.clear();
|
|
364
373
|
}
|
|
@@ -392,6 +401,45 @@ var DawWaveformElement = class extends import_lit3.LitElement {
|
|
|
392
401
|
}
|
|
393
402
|
}
|
|
394
403
|
}
|
|
404
|
+
_drawSegments(canvas, chunkIdx, dpr, halfHeight, bits, waveColor) {
|
|
405
|
+
if (!this.segments) return;
|
|
406
|
+
const ctx = canvas.getContext("2d");
|
|
407
|
+
if (!ctx) return;
|
|
408
|
+
const globalOffset = chunkIdx * MAX_CANVAS_WIDTH;
|
|
409
|
+
const canvasWidth = Math.min(MAX_CANVAS_WIDTH, this.length - globalOffset);
|
|
410
|
+
ctx.resetTransform();
|
|
411
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
412
|
+
ctx.scale(dpr, dpr);
|
|
413
|
+
ctx.fillStyle = waveColor;
|
|
414
|
+
const step = Math.max(1, Math.round(this.barWidth + this.barGap));
|
|
415
|
+
for (const seg of this.segments) {
|
|
416
|
+
if (seg.pixelEnd <= globalOffset || seg.pixelStart >= globalOffset + canvasWidth) continue;
|
|
417
|
+
const localStart = Math.max(0, seg.pixelStart - globalOffset);
|
|
418
|
+
const localEnd = Math.min(canvasWidth, seg.pixelEnd - globalOffset);
|
|
419
|
+
const segPixelWidth = seg.pixelEnd - seg.pixelStart;
|
|
420
|
+
const segPeakWidth = seg.peakEnd - seg.peakStart;
|
|
421
|
+
if (segPixelWidth <= 0 || segPeakWidth <= 0) continue;
|
|
422
|
+
const peaksPerPixel = segPeakWidth / segPixelWidth;
|
|
423
|
+
for (let px = Math.floor(localStart); px < Math.ceil(localEnd); px += step) {
|
|
424
|
+
const pxInSeg = px + globalOffset - seg.pixelStart;
|
|
425
|
+
const peakPos = seg.peakStart + pxInSeg * peaksPerPixel;
|
|
426
|
+
const peakEnd = peakPos + step * peaksPerPixel;
|
|
427
|
+
const peak = aggregatePeaks(this._peaks, bits, Math.floor(peakPos), Math.ceil(peakEnd));
|
|
428
|
+
if (!peak) continue;
|
|
429
|
+
const rects = calculateBarRects(
|
|
430
|
+
px,
|
|
431
|
+
this.barWidth,
|
|
432
|
+
halfHeight,
|
|
433
|
+
peak.min,
|
|
434
|
+
peak.max,
|
|
435
|
+
"normal"
|
|
436
|
+
);
|
|
437
|
+
for (const r of rects) {
|
|
438
|
+
ctx.fillRect(r.x, r.y, r.width, r.height);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
395
443
|
connectedCallback() {
|
|
396
444
|
super.connectedCallback();
|
|
397
445
|
if (this._dirtyPixels.size > 0) {
|
|
@@ -486,6 +534,9 @@ __decorateClass([
|
|
|
486
534
|
__decorateClass([
|
|
487
535
|
(0, import_decorators3.property)({ type: Number, attribute: false })
|
|
488
536
|
], DawWaveformElement.prototype, "originX", 2);
|
|
537
|
+
__decorateClass([
|
|
538
|
+
(0, import_decorators3.property)({ attribute: false })
|
|
539
|
+
], DawWaveformElement.prototype, "segments", 2);
|
|
489
540
|
DawWaveformElement = __decorateClass([
|
|
490
541
|
(0, import_decorators3.customElement)("daw-waveform")
|
|
491
542
|
], DawWaveformElement);
|
|
@@ -553,6 +604,40 @@ var DawPlayheadElement = class extends import_lit4.LitElement {
|
|
|
553
604
|
this._line.style.transform = `translate3d(${px}px, 0, 0)`;
|
|
554
605
|
}
|
|
555
606
|
}
|
|
607
|
+
startBeatsAnimation(getTime, bpm, ppqn, ticksPerPixel) {
|
|
608
|
+
const ticksPerSecond = bpm * ppqn / 60;
|
|
609
|
+
this._animation.start(() => {
|
|
610
|
+
const time = getTime();
|
|
611
|
+
const px = time * ticksPerSecond / ticksPerPixel;
|
|
612
|
+
if (this._line) {
|
|
613
|
+
this._line.style.transform = `translate3d(${px}px, 0, 0)`;
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
stopBeatsAnimation(time, bpm, ppqn, ticksPerPixel) {
|
|
618
|
+
this._animation.stop();
|
|
619
|
+
const px = time * bpm * ppqn / (60 * ticksPerPixel);
|
|
620
|
+
if (this._line) {
|
|
621
|
+
this._line.style.transform = `translate3d(${px}px, 0, 0)`;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
startBeatsAnimationWithMap(getTime, secondsToTicks, ticksPerPixel) {
|
|
625
|
+
this._animation.start(() => {
|
|
626
|
+
const time = getTime();
|
|
627
|
+
const tick = secondsToTicks(time);
|
|
628
|
+
const px = tick / ticksPerPixel;
|
|
629
|
+
if (this._line) {
|
|
630
|
+
this._line.style.transform = `translate3d(${px}px, 0, 0)`;
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
stopBeatsAnimationWithMap(time, secondsToTicks, ticksPerPixel) {
|
|
635
|
+
this._animation.stop();
|
|
636
|
+
const px = secondsToTicks(time) / ticksPerPixel;
|
|
637
|
+
if (this._line) {
|
|
638
|
+
this._line.style.transform = `translate3d(${px}px, 0, 0)`;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
556
641
|
};
|
|
557
642
|
DawPlayheadElement.styles = import_lit4.css`
|
|
558
643
|
:host {
|
|
@@ -804,9 +889,9 @@ DawStopButtonElement = __decorateClass([
|
|
|
804
889
|
], DawStopButtonElement);
|
|
805
890
|
|
|
806
891
|
// src/elements/daw-editor.ts
|
|
807
|
-
var
|
|
808
|
-
var
|
|
809
|
-
var
|
|
892
|
+
var import_lit13 = require("lit");
|
|
893
|
+
var import_decorators11 = require("lit/decorators.js");
|
|
894
|
+
var import_core8 = require("@waveform-playlist/core");
|
|
810
895
|
|
|
811
896
|
// src/workers/peaksWorker.ts
|
|
812
897
|
var import_waveform_data = __toESM(require("waveform-data"));
|
|
@@ -1215,6 +1300,19 @@ var PeakPipeline = class {
|
|
|
1215
1300
|
}
|
|
1216
1301
|
return requestedScale;
|
|
1217
1302
|
}
|
|
1303
|
+
/**
|
|
1304
|
+
* Extract peaks at the base scale from cached WaveformData.
|
|
1305
|
+
* Returns null if no cached data exists for this buffer.
|
|
1306
|
+
* Used by variable-tempo segments which handle stretching themselves.
|
|
1307
|
+
*/
|
|
1308
|
+
getBaseScalePeaks(audioBuffer, isMono, offsetSamples, durationSamples) {
|
|
1309
|
+
const cached = this._cache.get(audioBuffer);
|
|
1310
|
+
if (!cached) return null;
|
|
1311
|
+
return {
|
|
1312
|
+
peaks: extractPeaks(cached, cached.scale, isMono, offsetSamples, durationSamples),
|
|
1313
|
+
scale: cached.scale
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1218
1316
|
/**
|
|
1219
1317
|
* Return the coarsest (largest) scale among cached WaveformData entries
|
|
1220
1318
|
* that correspond to the given clip buffers. Returns 0 if none are cached.
|
|
@@ -1520,9 +1618,187 @@ DawTrackControlsElement = __decorateClass([
|
|
|
1520
1618
|
(0, import_decorators9.customElement)("daw-track-controls")
|
|
1521
1619
|
], DawTrackControlsElement);
|
|
1522
1620
|
|
|
1523
|
-
// src/
|
|
1621
|
+
// src/elements/daw-grid.ts
|
|
1524
1622
|
var import_lit11 = require("lit");
|
|
1525
|
-
var
|
|
1623
|
+
var import_decorators10 = require("lit/decorators.js");
|
|
1624
|
+
var import_core2 = require("@waveform-playlist/core");
|
|
1625
|
+
|
|
1626
|
+
// src/utils/musical-tick-cache.ts
|
|
1627
|
+
var import_core = require("@waveform-playlist/core");
|
|
1628
|
+
var cachedParams = null;
|
|
1629
|
+
var cachedResult = null;
|
|
1630
|
+
function meterEntriesMatch(a, b) {
|
|
1631
|
+
if (a.length !== b.length) return false;
|
|
1632
|
+
for (let i = 0; i < a.length; i++) {
|
|
1633
|
+
if (a[i].tick !== b[i].tick || a[i].numerator !== b[i].numerator || a[i].denominator !== b[i].denominator)
|
|
1634
|
+
return false;
|
|
1635
|
+
}
|
|
1636
|
+
return true;
|
|
1637
|
+
}
|
|
1638
|
+
function paramsMatch(a, b) {
|
|
1639
|
+
return a.ticksPerPixel === b.ticksPerPixel && a.startPixel === b.startPixel && a.endPixel === b.endPixel && meterEntriesMatch(a.meterEntries, b.meterEntries) && (a.ppqn ?? 960) === (b.ppqn ?? 960);
|
|
1640
|
+
}
|
|
1641
|
+
function getCachedMusicalTicks(params) {
|
|
1642
|
+
if (cachedParams && cachedResult && paramsMatch(cachedParams, params)) {
|
|
1643
|
+
return cachedResult;
|
|
1644
|
+
}
|
|
1645
|
+
cachedResult = (0, import_core.computeMusicalTicks)(params);
|
|
1646
|
+
cachedParams = {
|
|
1647
|
+
...params,
|
|
1648
|
+
meterEntries: params.meterEntries.map((e) => ({ ...e }))
|
|
1649
|
+
};
|
|
1650
|
+
return cachedResult;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
// src/elements/daw-grid.ts
|
|
1654
|
+
var MAX_CANVAS_WIDTH2 = 1e3;
|
|
1655
|
+
var DawGridElement = class extends import_lit11.LitElement {
|
|
1656
|
+
constructor() {
|
|
1657
|
+
super(...arguments);
|
|
1658
|
+
this.ticksPerPixel = 24;
|
|
1659
|
+
this.meterEntries = [
|
|
1660
|
+
{ tick: 0, numerator: 4, denominator: 4 }
|
|
1661
|
+
];
|
|
1662
|
+
this.ppqn = 960;
|
|
1663
|
+
this.visibleStart = -Infinity;
|
|
1664
|
+
this.visibleEnd = Infinity;
|
|
1665
|
+
this.length = 0;
|
|
1666
|
+
this.height = 200;
|
|
1667
|
+
this._tickData = null;
|
|
1668
|
+
}
|
|
1669
|
+
willUpdate() {
|
|
1670
|
+
if (this.length > 0) {
|
|
1671
|
+
this._tickData = getCachedMusicalTicks({
|
|
1672
|
+
ticksPerPixel: this.ticksPerPixel,
|
|
1673
|
+
meterEntries: this.meterEntries,
|
|
1674
|
+
ppqn: this.ppqn,
|
|
1675
|
+
startPixel: 0,
|
|
1676
|
+
endPixel: this.length
|
|
1677
|
+
});
|
|
1678
|
+
} else {
|
|
1679
|
+
this._tickData = null;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
render() {
|
|
1683
|
+
if (!this._tickData) return import_lit11.html``;
|
|
1684
|
+
const totalWidth = this.length;
|
|
1685
|
+
const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
1686
|
+
const indices = getVisibleChunkIndices(
|
|
1687
|
+
totalWidth,
|
|
1688
|
+
MAX_CANVAS_WIDTH2,
|
|
1689
|
+
this.visibleStart,
|
|
1690
|
+
this.visibleEnd
|
|
1691
|
+
);
|
|
1692
|
+
return import_lit11.html`
|
|
1693
|
+
<div class="container" style="width: ${totalWidth}px; height: ${this.height}px;">
|
|
1694
|
+
${indices.map((i) => {
|
|
1695
|
+
const width = Math.min(MAX_CANVAS_WIDTH2, totalWidth - i * MAX_CANVAS_WIDTH2);
|
|
1696
|
+
return import_lit11.html`
|
|
1697
|
+
<canvas
|
|
1698
|
+
data-index=${i}
|
|
1699
|
+
width=${width * dpr}
|
|
1700
|
+
height=${this.height * dpr}
|
|
1701
|
+
style="left: ${i * MAX_CANVAS_WIDTH2}px; width: ${width}px; height: ${this.height}px;"
|
|
1702
|
+
></canvas>
|
|
1703
|
+
`;
|
|
1704
|
+
})}
|
|
1705
|
+
</div>
|
|
1706
|
+
`;
|
|
1707
|
+
}
|
|
1708
|
+
updated() {
|
|
1709
|
+
this._drawGrid();
|
|
1710
|
+
}
|
|
1711
|
+
_drawGrid() {
|
|
1712
|
+
if (!this._tickData) return;
|
|
1713
|
+
const canvases = this.shadowRoot?.querySelectorAll("canvas");
|
|
1714
|
+
if (!canvases) return;
|
|
1715
|
+
const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
1716
|
+
const style = getComputedStyle(this);
|
|
1717
|
+
const barHighlight = style.getPropertyValue("--daw-grid-bar-highlight").trim() || "rgba(255,255,255,0.02)";
|
|
1718
|
+
const majorLine = style.getPropertyValue("--daw-grid-major-line").trim() || "rgba(255,255,255,0.1)";
|
|
1719
|
+
const minorLine = style.getPropertyValue("--daw-grid-minor-line").trim() || "rgba(255,255,255,0.06)";
|
|
1720
|
+
const { ticks, pixelsPerQuarterNote } = this._tickData;
|
|
1721
|
+
for (const canvas of canvases) {
|
|
1722
|
+
const idx = Number(canvas.dataset.index);
|
|
1723
|
+
const ctx = canvas.getContext("2d");
|
|
1724
|
+
if (!ctx) continue;
|
|
1725
|
+
const chunkLeft = idx * MAX_CANVAS_WIDTH2;
|
|
1726
|
+
const canvasWidth = Math.min(MAX_CANVAS_WIDTH2, this.length - chunkLeft);
|
|
1727
|
+
ctx.resetTransform();
|
|
1728
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1729
|
+
ctx.scale(dpr, dpr);
|
|
1730
|
+
if (pixelsPerQuarterNote * 4 >= import_core2.MIN_PIXELS_PER_UNIT) {
|
|
1731
|
+
ctx.fillStyle = barHighlight;
|
|
1732
|
+
const majorTicks = ticks.filter((t) => t.type === "major");
|
|
1733
|
+
for (let i = 0; i < majorTicks.length; i++) {
|
|
1734
|
+
if (majorTicks[i].barIndex % 2 === 1) {
|
|
1735
|
+
const x = majorTicks[i].pixel - chunkLeft;
|
|
1736
|
+
const lastMeter = this.meterEntries[this.meterEntries.length - 1];
|
|
1737
|
+
const lastBarWidth = pixelsPerQuarterNote * lastMeter.numerator * (4 / lastMeter.denominator);
|
|
1738
|
+
const nextX = i + 1 < majorTicks.length ? majorTicks[i + 1].pixel - chunkLeft : x + lastBarWidth;
|
|
1739
|
+
ctx.fillRect(x, 0, nextX - x, this.height);
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
ctx.lineWidth = 1;
|
|
1744
|
+
for (const tick of ticks) {
|
|
1745
|
+
if (tick.type === "minorMinor") continue;
|
|
1746
|
+
const localX = tick.pixel - chunkLeft;
|
|
1747
|
+
if (localX < 0 || localX >= canvasWidth) continue;
|
|
1748
|
+
ctx.strokeStyle = tick.type === "major" ? majorLine : minorLine;
|
|
1749
|
+
ctx.beginPath();
|
|
1750
|
+
ctx.moveTo(localX + 0.5, 0);
|
|
1751
|
+
ctx.lineTo(localX + 0.5, this.height);
|
|
1752
|
+
ctx.stroke();
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
};
|
|
1757
|
+
DawGridElement.styles = import_lit11.css`
|
|
1758
|
+
:host {
|
|
1759
|
+
display: block;
|
|
1760
|
+
position: absolute;
|
|
1761
|
+
top: 0;
|
|
1762
|
+
left: 0;
|
|
1763
|
+
pointer-events: none;
|
|
1764
|
+
z-index: 0;
|
|
1765
|
+
}
|
|
1766
|
+
.container {
|
|
1767
|
+
position: relative;
|
|
1768
|
+
}
|
|
1769
|
+
canvas {
|
|
1770
|
+
position: absolute;
|
|
1771
|
+
top: 0;
|
|
1772
|
+
}
|
|
1773
|
+
`;
|
|
1774
|
+
__decorateClass([
|
|
1775
|
+
(0, import_decorators10.property)({ type: Number, attribute: false })
|
|
1776
|
+
], DawGridElement.prototype, "ticksPerPixel", 2);
|
|
1777
|
+
__decorateClass([
|
|
1778
|
+
(0, import_decorators10.property)({ attribute: false })
|
|
1779
|
+
], DawGridElement.prototype, "meterEntries", 2);
|
|
1780
|
+
__decorateClass([
|
|
1781
|
+
(0, import_decorators10.property)({ type: Number, attribute: false })
|
|
1782
|
+
], DawGridElement.prototype, "ppqn", 2);
|
|
1783
|
+
__decorateClass([
|
|
1784
|
+
(0, import_decorators10.property)({ type: Number, attribute: false })
|
|
1785
|
+
], DawGridElement.prototype, "visibleStart", 2);
|
|
1786
|
+
__decorateClass([
|
|
1787
|
+
(0, import_decorators10.property)({ type: Number, attribute: false })
|
|
1788
|
+
], DawGridElement.prototype, "visibleEnd", 2);
|
|
1789
|
+
__decorateClass([
|
|
1790
|
+
(0, import_decorators10.property)({ type: Number, attribute: false })
|
|
1791
|
+
], DawGridElement.prototype, "length", 2);
|
|
1792
|
+
__decorateClass([
|
|
1793
|
+
(0, import_decorators10.property)({ type: Number, attribute: false })
|
|
1794
|
+
], DawGridElement.prototype, "height", 2);
|
|
1795
|
+
DawGridElement = __decorateClass([
|
|
1796
|
+
(0, import_decorators10.customElement)("daw-grid")
|
|
1797
|
+
], DawGridElement);
|
|
1798
|
+
|
|
1799
|
+
// src/styles/theme.ts
|
|
1800
|
+
var import_lit12 = require("lit");
|
|
1801
|
+
var hostStyles = import_lit12.css`
|
|
1526
1802
|
:host {
|
|
1527
1803
|
--daw-wave-color: #c49a6c;
|
|
1528
1804
|
--daw-progress-color: #63c75f;
|
|
@@ -1538,7 +1814,7 @@ var hostStyles = import_lit11.css`
|
|
|
1538
1814
|
--daw-clip-header-text: #e0d4c8;
|
|
1539
1815
|
}
|
|
1540
1816
|
`;
|
|
1541
|
-
var clipStyles =
|
|
1817
|
+
var clipStyles = import_lit12.css`
|
|
1542
1818
|
.clip-container {
|
|
1543
1819
|
position: absolute;
|
|
1544
1820
|
overflow: hidden;
|
|
@@ -1751,8 +2027,7 @@ var AudioResumeController = class {
|
|
|
1751
2027
|
};
|
|
1752
2028
|
|
|
1753
2029
|
// src/controllers/recording-controller.ts
|
|
1754
|
-
var
|
|
1755
|
-
var import_recording = require("@waveform-playlist/recording");
|
|
2030
|
+
var import_core3 = require("@waveform-playlist/core");
|
|
1756
2031
|
var RecordingController = class {
|
|
1757
2032
|
constructor(host) {
|
|
1758
2033
|
this._sessions = /* @__PURE__ */ new Map();
|
|
@@ -1789,7 +2064,15 @@ var RecordingController = class {
|
|
|
1789
2064
|
const rawCtx = this._host.audioContext;
|
|
1790
2065
|
this._host.resolveAudioContextSampleRate(rawCtx.sampleRate);
|
|
1791
2066
|
if (!this._workletLoadedCtx || this._workletLoadedCtx !== rawCtx) {
|
|
1792
|
-
|
|
2067
|
+
let recordingProcessorUrl;
|
|
2068
|
+
try {
|
|
2069
|
+
({ recordingProcessorUrl } = await import("@waveform-playlist/worklets"));
|
|
2070
|
+
} catch {
|
|
2071
|
+
throw new Error(
|
|
2072
|
+
"Recording requires @waveform-playlist/worklets. Install it: npm install @waveform-playlist/worklets"
|
|
2073
|
+
);
|
|
2074
|
+
}
|
|
2075
|
+
await rawCtx.audioWorklet.addModule(recordingProcessorUrl);
|
|
1793
2076
|
this._workletLoadedCtx = rawCtx;
|
|
1794
2077
|
}
|
|
1795
2078
|
const detectedChannelCount = stream.getAudioTracks()[0]?.getSettings()?.channelCount;
|
|
@@ -1905,8 +2188,8 @@ var RecordingController = class {
|
|
|
1905
2188
|
return;
|
|
1906
2189
|
}
|
|
1907
2190
|
const stopCtx = this._host.audioContext;
|
|
1908
|
-
const channelData = session.chunks.map((chunkArr) => (0,
|
|
1909
|
-
const audioBuffer = (0,
|
|
2191
|
+
const channelData = session.chunks.map((chunkArr) => (0, import_core3.concatenateAudioData)(chunkArr));
|
|
2192
|
+
const audioBuffer = (0, import_core3.createAudioBuffer)(
|
|
1910
2193
|
stopCtx,
|
|
1911
2194
|
channelData,
|
|
1912
2195
|
this._host.effectiveSampleRate,
|
|
@@ -1971,7 +2254,7 @@ var RecordingController = class {
|
|
|
1971
2254
|
for (let ch = 0; ch < session.channelCount; ch++) {
|
|
1972
2255
|
if (!channels[ch]) continue;
|
|
1973
2256
|
const oldPeakCount = Math.floor(session.peaks[ch].length / 2);
|
|
1974
|
-
session.peaks[ch] = (0,
|
|
2257
|
+
session.peaks[ch] = (0, import_core3.appendPeaks)(
|
|
1975
2258
|
session.peaks[ch],
|
|
1976
2259
|
channels[ch],
|
|
1977
2260
|
this._host.samplesPerPixel,
|
|
@@ -2039,7 +2322,7 @@ var RecordingController = class {
|
|
|
2039
2322
|
};
|
|
2040
2323
|
|
|
2041
2324
|
// src/interactions/pointer-handler.ts
|
|
2042
|
-
var
|
|
2325
|
+
var import_core4 = require("@waveform-playlist/core");
|
|
2043
2326
|
|
|
2044
2327
|
// src/interactions/constants.ts
|
|
2045
2328
|
var DRAG_THRESHOLD = 3;
|
|
@@ -2049,6 +2332,7 @@ var PointerHandler = class {
|
|
|
2049
2332
|
constructor(host) {
|
|
2050
2333
|
this._isDragging = false;
|
|
2051
2334
|
this._dragStartPx = 0;
|
|
2335
|
+
this._dragStartTime = 0;
|
|
2052
2336
|
this._timeline = null;
|
|
2053
2337
|
// Cached from onPointerDown to avoid forced layout reflows at 60fps during drag
|
|
2054
2338
|
this._timelineRect = null;
|
|
@@ -2095,21 +2379,18 @@ var PointerHandler = class {
|
|
|
2095
2379
|
const currentPx = this._pxFromPointer(e);
|
|
2096
2380
|
if (!this._isDragging && Math.abs(currentPx - this._dragStartPx) > DRAG_THRESHOLD) {
|
|
2097
2381
|
this._isDragging = true;
|
|
2382
|
+
this._dragStartTime = this._pxToTime(this._dragStartPx);
|
|
2098
2383
|
}
|
|
2099
2384
|
if (this._isDragging) {
|
|
2100
2385
|
const h = this._host;
|
|
2101
|
-
const startTime =
|
|
2102
|
-
|
|
2103
|
-
h.samplesPerPixel,
|
|
2104
|
-
h.effectiveSampleRate
|
|
2105
|
-
);
|
|
2106
|
-
const endTime = (0, import_core.pixelsToSeconds)(currentPx, h.samplesPerPixel, h.effectiveSampleRate);
|
|
2386
|
+
const startTime = this._dragStartTime;
|
|
2387
|
+
const endTime = this._pxToTime(currentPx);
|
|
2107
2388
|
h._selectionStartTime = Math.min(startTime, endTime);
|
|
2108
2389
|
h._selectionEndTime = Math.max(startTime, endTime);
|
|
2109
2390
|
const sel = h.shadowRoot?.querySelector("daw-selection");
|
|
2110
2391
|
if (sel) {
|
|
2111
|
-
sel.startPx = h._selectionStartTime
|
|
2112
|
-
sel.endPx = h._selectionEndTime
|
|
2392
|
+
sel.startPx = this._timeToPx(h._selectionStartTime);
|
|
2393
|
+
sel.endPx = this._timeToPx(h._selectionEndTime);
|
|
2113
2394
|
}
|
|
2114
2395
|
}
|
|
2115
2396
|
};
|
|
@@ -2147,6 +2428,23 @@ var PointerHandler = class {
|
|
|
2147
2428
|
}
|
|
2148
2429
|
return e.clientX - this._timelineRect.left;
|
|
2149
2430
|
}
|
|
2431
|
+
_pxToTime(px) {
|
|
2432
|
+
const h = this._host;
|
|
2433
|
+
if (h.scaleMode === "beats") {
|
|
2434
|
+
let tick = px * h.ticksPerPixel;
|
|
2435
|
+
tick = (0, import_core4.snapTickToGrid)(tick, h.snapTo, h._meterEntries, h.ppqn);
|
|
2436
|
+
return h._ticksToSeconds(tick);
|
|
2437
|
+
}
|
|
2438
|
+
return (0, import_core4.pixelsToSeconds)(px, h.samplesPerPixel, h.effectiveSampleRate);
|
|
2439
|
+
}
|
|
2440
|
+
_timeToPx(time) {
|
|
2441
|
+
const h = this._host;
|
|
2442
|
+
if (h.scaleMode === "beats") {
|
|
2443
|
+
const tick = h._secondsToTicks(time);
|
|
2444
|
+
return tick / h.ticksPerPixel;
|
|
2445
|
+
}
|
|
2446
|
+
return time * h.effectiveSampleRate / h.samplesPerPixel;
|
|
2447
|
+
}
|
|
2150
2448
|
_finalizeSelection() {
|
|
2151
2449
|
const h = this._host;
|
|
2152
2450
|
if (h._engine) {
|
|
@@ -2164,7 +2462,7 @@ var PointerHandler = class {
|
|
|
2164
2462
|
_handleSeekClick(e) {
|
|
2165
2463
|
const h = this._host;
|
|
2166
2464
|
const px = this._pxFromPointer(e);
|
|
2167
|
-
const time =
|
|
2465
|
+
const time = this._pxToTime(px);
|
|
2168
2466
|
h._selectionStartTime = 0;
|
|
2169
2467
|
h._selectionEndTime = 0;
|
|
2170
2468
|
if (this._timeline) {
|
|
@@ -2229,6 +2527,7 @@ var PointerHandler = class {
|
|
|
2229
2527
|
};
|
|
2230
2528
|
|
|
2231
2529
|
// src/interactions/clip-pointer-handler.ts
|
|
2530
|
+
var import_core5 = require("@waveform-playlist/core");
|
|
2232
2531
|
var ClipPointerHandler = class {
|
|
2233
2532
|
constructor(host) {
|
|
2234
2533
|
this._mode = null;
|
|
@@ -2236,7 +2535,6 @@ var ClipPointerHandler = class {
|
|
|
2236
2535
|
this._trackId = "";
|
|
2237
2536
|
this._startPx = 0;
|
|
2238
2537
|
this._isDragging = false;
|
|
2239
|
-
this._lastDeltaPx = 0;
|
|
2240
2538
|
this._cumulativeDeltaSamples = 0;
|
|
2241
2539
|
// Trim visual feedback: snapshot of original clip state
|
|
2242
2540
|
this._clipContainer = null;
|
|
@@ -2245,8 +2543,31 @@ var ClipPointerHandler = class {
|
|
|
2245
2543
|
this._originalWidth = 0;
|
|
2246
2544
|
this._originalOffsetSamples = 0;
|
|
2247
2545
|
this._originalDurationSamples = 0;
|
|
2546
|
+
this._originalStartSample = 0;
|
|
2248
2547
|
this._host = host;
|
|
2249
2548
|
}
|
|
2549
|
+
/**
|
|
2550
|
+
* Convert a pixel delta to samples, snapping in tick space when in beats mode.
|
|
2551
|
+
*
|
|
2552
|
+
* The anchor is the absolute sample position being moved (e.g., clip start
|
|
2553
|
+
* for move/left-trim, clip end for right-trim). Snapping the absolute
|
|
2554
|
+
* position — not just the delta — ensures clips land exactly on grid lines
|
|
2555
|
+
* even if they started off-grid.
|
|
2556
|
+
*/
|
|
2557
|
+
_snapDeltaToSamples(totalDeltaPx, anchorSample) {
|
|
2558
|
+
const h = this._host;
|
|
2559
|
+
if (h.scaleMode === "beats") {
|
|
2560
|
+
const anchorSeconds = anchorSample / h.effectiveSampleRate;
|
|
2561
|
+
const anchorTick = h._secondsToTicks(anchorSeconds);
|
|
2562
|
+
const deltaTicks = totalDeltaPx * h.ticksPerPixel;
|
|
2563
|
+
const targetTick = anchorTick + deltaTicks;
|
|
2564
|
+
const snappedTick = h.snapTo !== "off" ? (0, import_core5.snapTickToGrid)(targetTick, h.snapTo, h._meterEntries, h.ppqn) : targetTick;
|
|
2565
|
+
const snappedSeconds = h._ticksToSeconds(snappedTick);
|
|
2566
|
+
const snappedSample = Math.round(snappedSeconds * h.effectiveSampleRate);
|
|
2567
|
+
return snappedSample - anchorSample;
|
|
2568
|
+
}
|
|
2569
|
+
return Math.round(totalDeltaPx * h.renderSamplesPerPixel);
|
|
2570
|
+
}
|
|
2250
2571
|
/** Returns true if a drag interaction is currently in progress. */
|
|
2251
2572
|
get isActive() {
|
|
2252
2573
|
return this._mode !== null;
|
|
@@ -2283,10 +2604,16 @@ var ClipPointerHandler = class {
|
|
|
2283
2604
|
this._trackId = trackId;
|
|
2284
2605
|
this._startPx = e.clientX;
|
|
2285
2606
|
this._isDragging = false;
|
|
2286
|
-
this._lastDeltaPx = 0;
|
|
2287
2607
|
this._cumulativeDeltaSamples = 0;
|
|
2288
|
-
|
|
2289
|
-
|
|
2608
|
+
const engine = this._host.engine;
|
|
2609
|
+
if (engine) {
|
|
2610
|
+
const bounds = engine.getClipBounds(trackId, clipId);
|
|
2611
|
+
if (bounds) {
|
|
2612
|
+
this._originalStartSample = bounds.startSample;
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
if (engine) {
|
|
2616
|
+
engine.beginTransaction();
|
|
2290
2617
|
} else {
|
|
2291
2618
|
console.warn(
|
|
2292
2619
|
"[dawcore] beginDrag: engine unavailable, drag mutations will not be grouped for undo"
|
|
@@ -2303,9 +2630,9 @@ var ClipPointerHandler = class {
|
|
|
2303
2630
|
} else {
|
|
2304
2631
|
console.warn("[dawcore] clip container not found for trim visual feedback: " + clipId);
|
|
2305
2632
|
}
|
|
2306
|
-
const
|
|
2307
|
-
if (
|
|
2308
|
-
const bounds =
|
|
2633
|
+
const engine2 = this._host.engine;
|
|
2634
|
+
if (engine2) {
|
|
2635
|
+
const bounds = engine2.getClipBounds(trackId, clipId);
|
|
2309
2636
|
if (bounds) {
|
|
2310
2637
|
this._originalOffsetSamples = bounds.offsetSamples;
|
|
2311
2638
|
this._originalDurationSamples = bounds.durationSamples;
|
|
@@ -2327,21 +2654,33 @@ var ClipPointerHandler = class {
|
|
|
2327
2654
|
const engine = this._host.engine;
|
|
2328
2655
|
if (!engine) return;
|
|
2329
2656
|
if (this._mode === "move") {
|
|
2330
|
-
const
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2657
|
+
const totalSnappedDelta = this._snapDeltaToSamples(totalDeltaPx, this._originalStartSample);
|
|
2658
|
+
const incrementalDeltaSamples = totalSnappedDelta - this._cumulativeDeltaSamples;
|
|
2659
|
+
if (incrementalDeltaSamples !== 0) {
|
|
2660
|
+
const applied = engine.moveClip(this._trackId, this._clipId, incrementalDeltaSamples, true);
|
|
2661
|
+
this._cumulativeDeltaSamples += applied;
|
|
2662
|
+
}
|
|
2335
2663
|
} else {
|
|
2336
2664
|
const boundary = this._mode === "trim-left" ? "left" : "right";
|
|
2337
|
-
const
|
|
2665
|
+
const anchor = boundary === "left" ? this._originalStartSample : this._originalStartSample + this._originalDurationSamples;
|
|
2666
|
+
const rawDeltaSamples = this._snapDeltaToSamples(totalDeltaPx, anchor);
|
|
2338
2667
|
const deltaSamples = engine.constrainTrimDelta(
|
|
2339
2668
|
this._trackId,
|
|
2340
2669
|
this._clipId,
|
|
2341
2670
|
boundary,
|
|
2342
2671
|
rawDeltaSamples
|
|
2343
2672
|
);
|
|
2344
|
-
|
|
2673
|
+
let deltaPx;
|
|
2674
|
+
if (this._host.scaleMode === "beats") {
|
|
2675
|
+
const h = this._host;
|
|
2676
|
+
const anchorSec = anchor / h.effectiveSampleRate;
|
|
2677
|
+
const anchorTick = h._secondsToTicks(anchorSec);
|
|
2678
|
+
const newSec = anchorSec + deltaSamples / h.effectiveSampleRate;
|
|
2679
|
+
const newTick = h._secondsToTicks(newSec);
|
|
2680
|
+
deltaPx = Math.round((newTick - anchorTick) / h.ticksPerPixel);
|
|
2681
|
+
} else {
|
|
2682
|
+
deltaPx = Math.round(deltaSamples / this._host.renderSamplesPerPixel);
|
|
2683
|
+
}
|
|
2345
2684
|
this._cumulativeDeltaSamples = deltaSamples;
|
|
2346
2685
|
if (this._clipContainer) {
|
|
2347
2686
|
if (this._mode === "trim-left") {
|
|
@@ -2473,18 +2812,18 @@ var ClipPointerHandler = class {
|
|
|
2473
2812
|
this._trackId = "";
|
|
2474
2813
|
this._startPx = 0;
|
|
2475
2814
|
this._isDragging = false;
|
|
2476
|
-
this._lastDeltaPx = 0;
|
|
2477
2815
|
this._cumulativeDeltaSamples = 0;
|
|
2478
2816
|
this._clipContainer = null;
|
|
2479
2817
|
this._originalLeft = 0;
|
|
2480
2818
|
this._originalWidth = 0;
|
|
2481
2819
|
this._originalOffsetSamples = 0;
|
|
2482
2820
|
this._originalDurationSamples = 0;
|
|
2821
|
+
this._originalStartSample = 0;
|
|
2483
2822
|
}
|
|
2484
2823
|
};
|
|
2485
2824
|
|
|
2486
2825
|
// src/interactions/file-loader.ts
|
|
2487
|
-
var
|
|
2826
|
+
var import_core6 = require("@waveform-playlist/core");
|
|
2488
2827
|
async function loadFiles(host, files) {
|
|
2489
2828
|
if (!files) {
|
|
2490
2829
|
console.warn("[dawcore] loadFiles called with null/undefined");
|
|
@@ -2506,31 +2845,31 @@ async function loadFiles(host, files) {
|
|
|
2506
2845
|
host._audioCache.delete(blobUrl);
|
|
2507
2846
|
host._resolvedSampleRate = audioBuffer.sampleRate;
|
|
2508
2847
|
const name = file.name.replace(/\.\w+$/, "");
|
|
2509
|
-
const clip = (0,
|
|
2848
|
+
const clip = (0, import_core6.createClip)({
|
|
2510
2849
|
audioBuffer,
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2850
|
+
startSample: 0,
|
|
2851
|
+
durationSamples: audioBuffer.length,
|
|
2852
|
+
offsetSamples: 0,
|
|
2514
2853
|
gain: 1,
|
|
2515
2854
|
name,
|
|
2516
2855
|
sampleRate: audioBuffer.sampleRate,
|
|
2517
|
-
|
|
2856
|
+
sourceDurationSamples: audioBuffer.length
|
|
2518
2857
|
});
|
|
2519
2858
|
host._clipBuffers = new Map(host._clipBuffers).set(clip.id, audioBuffer);
|
|
2520
|
-
host._clipOffsets.set(clip.id, {
|
|
2859
|
+
host._clipOffsets = new Map(host._clipOffsets).set(clip.id, {
|
|
2521
2860
|
offsetSamples: clip.offsetSamples,
|
|
2522
2861
|
durationSamples: clip.durationSamples
|
|
2523
2862
|
});
|
|
2524
2863
|
const peakData = await host._peakPipeline.generatePeaks(
|
|
2525
2864
|
audioBuffer,
|
|
2526
|
-
host.
|
|
2865
|
+
host.renderSamplesPerPixel,
|
|
2527
2866
|
host.mono,
|
|
2528
2867
|
clip.offsetSamples,
|
|
2529
2868
|
clip.durationSamples
|
|
2530
2869
|
);
|
|
2531
2870
|
host._peaksData = new Map(host._peaksData).set(clip.id, peakData);
|
|
2532
2871
|
const trackId = crypto.randomUUID();
|
|
2533
|
-
const track = (0,
|
|
2872
|
+
const track = (0, import_core6.createTrack)({ name, clips: [clip] });
|
|
2534
2873
|
track.id = trackId;
|
|
2535
2874
|
host._tracks = new Map(host._tracks).set(trackId, {
|
|
2536
2875
|
name,
|
|
@@ -2585,7 +2924,7 @@ async function loadFiles(host, files) {
|
|
|
2585
2924
|
}
|
|
2586
2925
|
|
|
2587
2926
|
// src/interactions/recording-clip.ts
|
|
2588
|
-
var
|
|
2927
|
+
var import_core7 = require("@waveform-playlist/core");
|
|
2589
2928
|
function addRecordedClip(host, trackId, buf, startSample, durSamples, offsetSamples = 0) {
|
|
2590
2929
|
let trimmedBuf = buf;
|
|
2591
2930
|
if (offsetSamples > 0 && offsetSamples < buf.length) {
|
|
@@ -2600,7 +2939,7 @@ function addRecordedClip(host, trackId, buf, startSample, durSamples, offsetSamp
|
|
|
2600
2939
|
}
|
|
2601
2940
|
trimmedBuf = trimmed;
|
|
2602
2941
|
}
|
|
2603
|
-
const clip = (0,
|
|
2942
|
+
const clip = (0, import_core7.createClip)({
|
|
2604
2943
|
audioBuffer: trimmedBuf,
|
|
2605
2944
|
startSample,
|
|
2606
2945
|
durationSamples: durSamples,
|
|
@@ -2768,13 +3107,13 @@ function syncPeaksForChangedClips(host, tracks) {
|
|
|
2768
3107
|
continue;
|
|
2769
3108
|
}
|
|
2770
3109
|
host._clipBuffers = new Map(host._clipBuffers).set(clip.id, audioBuffer);
|
|
2771
|
-
host._clipOffsets.set(clip.id, {
|
|
3110
|
+
host._clipOffsets = new Map(host._clipOffsets).set(clip.id, {
|
|
2772
3111
|
offsetSamples: clip.offsetSamples,
|
|
2773
3112
|
durationSamples: clip.durationSamples
|
|
2774
3113
|
});
|
|
2775
3114
|
host._peakPipeline.generatePeaks(
|
|
2776
3115
|
audioBuffer,
|
|
2777
|
-
host.
|
|
3116
|
+
host.renderSamplesPerPixel,
|
|
2778
3117
|
host.mono,
|
|
2779
3118
|
clip.offsetSamples,
|
|
2780
3119
|
clip.durationSamples
|
|
@@ -2849,7 +3188,7 @@ async function loadWaveformDataFromUrl(src) {
|
|
|
2849
3188
|
}
|
|
2850
3189
|
|
|
2851
3190
|
// src/elements/daw-editor.ts
|
|
2852
|
-
var DawEditorElement = class extends
|
|
3191
|
+
var DawEditorElement = class extends import_lit13.LitElement {
|
|
2853
3192
|
constructor() {
|
|
2854
3193
|
super(...arguments);
|
|
2855
3194
|
this._samplesPerPixel = 1024;
|
|
@@ -2862,6 +3201,12 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
2862
3201
|
this.clipHeaders = false;
|
|
2863
3202
|
this.clipHeaderHeight = 20;
|
|
2864
3203
|
this.interactiveClips = false;
|
|
3204
|
+
this.scaleMode = "temporal";
|
|
3205
|
+
this._ticksPerPixel = 24;
|
|
3206
|
+
this._bpm = 120;
|
|
3207
|
+
this.timeSignature = [4, 4];
|
|
3208
|
+
this._ppqn = 960;
|
|
3209
|
+
this.snapTo = "off";
|
|
2865
3210
|
this.sampleRate = 48e3;
|
|
2866
3211
|
/** Resolved sample rate — falls back to sampleRate property until first audio decode. */
|
|
2867
3212
|
this._resolvedSampleRate = null;
|
|
@@ -2880,6 +3225,9 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
2880
3225
|
this._externalAudioContext = null;
|
|
2881
3226
|
this._ownedAudioContext = null;
|
|
2882
3227
|
this._engine = null;
|
|
3228
|
+
this._adapter = null;
|
|
3229
|
+
this._warnedMissingTicksToSeconds = false;
|
|
3230
|
+
this._warnedMissingSecondsToTicks = false;
|
|
2883
3231
|
this._enginePromise = null;
|
|
2884
3232
|
this._audioCache = /* @__PURE__ */ new Map();
|
|
2885
3233
|
this._peaksCache = /* @__PURE__ */ new Map();
|
|
@@ -3024,6 +3372,41 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3024
3372
|
this._samplesPerPixel = clamped;
|
|
3025
3373
|
this.requestUpdate("samplesPerPixel", old);
|
|
3026
3374
|
}
|
|
3375
|
+
get ticksPerPixel() {
|
|
3376
|
+
return this._ticksPerPixel;
|
|
3377
|
+
}
|
|
3378
|
+
set ticksPerPixel(value) {
|
|
3379
|
+
const old = this._ticksPerPixel;
|
|
3380
|
+
if (!Number.isFinite(value) || value <= 0) return;
|
|
3381
|
+
this._ticksPerPixel = value;
|
|
3382
|
+
this.requestUpdate("ticksPerPixel", old);
|
|
3383
|
+
}
|
|
3384
|
+
get bpm() {
|
|
3385
|
+
return this._bpm;
|
|
3386
|
+
}
|
|
3387
|
+
set bpm(value) {
|
|
3388
|
+
const old = this._bpm;
|
|
3389
|
+
if (!Number.isFinite(value) || value <= 0) return;
|
|
3390
|
+
this._bpm = value;
|
|
3391
|
+
if (this._engine) {
|
|
3392
|
+
this._engine.setTempo(value);
|
|
3393
|
+
}
|
|
3394
|
+
this.requestUpdate("bpm", old);
|
|
3395
|
+
}
|
|
3396
|
+
/** MeterEntries for grid/ruler: explicit meterEntries if set, otherwise derived from timeSignature. */
|
|
3397
|
+
get _meterEntries() {
|
|
3398
|
+
if (this.meterEntries && this.meterEntries.length > 0) return this.meterEntries;
|
|
3399
|
+
return [{ tick: 0, numerator: this.timeSignature[0], denominator: this.timeSignature[1] }];
|
|
3400
|
+
}
|
|
3401
|
+
get ppqn() {
|
|
3402
|
+
return this._ppqn;
|
|
3403
|
+
}
|
|
3404
|
+
set ppqn(value) {
|
|
3405
|
+
const old = this._ppqn;
|
|
3406
|
+
if (!Number.isFinite(value) || value <= 0) return;
|
|
3407
|
+
this._ppqn = value;
|
|
3408
|
+
this.requestUpdate("ppqn", old);
|
|
3409
|
+
}
|
|
3027
3410
|
/** Set an AudioContext to use for all audio operations. Must be set before tracks load. */
|
|
3028
3411
|
set audioContext(ctx) {
|
|
3029
3412
|
if (ctx && ctx.state === "closed") {
|
|
@@ -3055,6 +3438,13 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3055
3438
|
get engine() {
|
|
3056
3439
|
return this._engine;
|
|
3057
3440
|
}
|
|
3441
|
+
/** The adapter's Transport — use for tempo, metronome, and effects. */
|
|
3442
|
+
get transport() {
|
|
3443
|
+
return this._adapter?.transport ?? null;
|
|
3444
|
+
}
|
|
3445
|
+
get renderSamplesPerPixel() {
|
|
3446
|
+
return this._renderSpp;
|
|
3447
|
+
}
|
|
3058
3448
|
/** Re-extract peaks for a clip at new offset/duration from cached WaveformData. */
|
|
3059
3449
|
reextractClipPeaks(clipId, offsetSamples, durationSamples) {
|
|
3060
3450
|
const buf = this._clipBuffers.get(clipId);
|
|
@@ -3063,7 +3453,7 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3063
3453
|
const singleClipOffsets = /* @__PURE__ */ new Map([[clipId, { offsetSamples, durationSamples }]]);
|
|
3064
3454
|
const result = this._peakPipeline.reextractPeaks(
|
|
3065
3455
|
singleClipBuffers,
|
|
3066
|
-
this.
|
|
3456
|
+
this._renderSpp,
|
|
3067
3457
|
this.mono,
|
|
3068
3458
|
singleClipOffsets
|
|
3069
3459
|
);
|
|
@@ -3077,9 +3467,59 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3077
3467
|
resolveAudioContextSampleRate(rate) {
|
|
3078
3468
|
if (!this._resolvedSampleRate) this._resolvedSampleRate = rate;
|
|
3079
3469
|
}
|
|
3470
|
+
/**
|
|
3471
|
+
* In beats mode, derive samplesPerPixel from ticksPerPixel so that
|
|
3472
|
+
* clip positions, waveforms, and the tick-space grid all align.
|
|
3473
|
+
*/
|
|
3474
|
+
get _renderSpp() {
|
|
3475
|
+
if (this.scaleMode === "beats") {
|
|
3476
|
+
const spp = Math.ceil(
|
|
3477
|
+
60 * this.effectiveSampleRate * this.ticksPerPixel / (this.ppqn * this.bpm)
|
|
3478
|
+
);
|
|
3479
|
+
return this._minSamplesPerPixel > 0 ? Math.max(spp, this._minSamplesPerPixel) : spp;
|
|
3480
|
+
}
|
|
3481
|
+
return this.samplesPerPixel;
|
|
3482
|
+
}
|
|
3483
|
+
/** Convert seconds to ticks — uses callback if provided, otherwise single-BPM fallback. */
|
|
3484
|
+
_secondsToTicks(seconds) {
|
|
3485
|
+
if (this.secondsToTicks) {
|
|
3486
|
+
if (!this.ticksToSeconds && !this._warnedMissingTicksToSeconds) {
|
|
3487
|
+
this._warnedMissingTicksToSeconds = true;
|
|
3488
|
+
console.warn(
|
|
3489
|
+
"[waveform-playlist] daw-editor: secondsToTicks is set but ticksToSeconds is missing. Both callbacks are required for variable tempo."
|
|
3490
|
+
);
|
|
3491
|
+
}
|
|
3492
|
+
return this.secondsToTicks(seconds);
|
|
3493
|
+
}
|
|
3494
|
+
return seconds * this.bpm * this.ppqn / 60;
|
|
3495
|
+
}
|
|
3496
|
+
/** Convert ticks to seconds — uses callback if provided, otherwise single-BPM fallback. */
|
|
3497
|
+
_ticksToSeconds(ticks) {
|
|
3498
|
+
if (this.ticksToSeconds) {
|
|
3499
|
+
if (!this.secondsToTicks && !this._warnedMissingSecondsToTicks) {
|
|
3500
|
+
this._warnedMissingSecondsToTicks = true;
|
|
3501
|
+
console.warn(
|
|
3502
|
+
"[waveform-playlist] daw-editor: ticksToSeconds is set but secondsToTicks is missing. Both callbacks are required for variable tempo."
|
|
3503
|
+
);
|
|
3504
|
+
}
|
|
3505
|
+
return this.ticksToSeconds(ticks);
|
|
3506
|
+
}
|
|
3507
|
+
return ticks * 60 / (this.bpm * this.ppqn);
|
|
3508
|
+
}
|
|
3080
3509
|
get _totalWidth() {
|
|
3510
|
+
if (this.scaleMode === "beats") {
|
|
3511
|
+
const contentTicks = this._secondsToTicks(this._duration);
|
|
3512
|
+
const [num] = this.timeSignature;
|
|
3513
|
+
const minTicks = 32 * num * this.ppqn;
|
|
3514
|
+
return Math.ceil(Math.max(contentTicks, minTicks) / this.ticksPerPixel);
|
|
3515
|
+
}
|
|
3081
3516
|
return Math.ceil(this._duration * this.effectiveSampleRate / this.samplesPerPixel);
|
|
3082
3517
|
}
|
|
3518
|
+
/** Grid height when no tracks exist — matches scroll area's rendered height. */
|
|
3519
|
+
get _emptyGridHeight() {
|
|
3520
|
+
const scrollArea = this.shadowRoot?.querySelector(".scroll-area");
|
|
3521
|
+
return scrollArea?.clientHeight ?? 200;
|
|
3522
|
+
}
|
|
3083
3523
|
_setSelectedTrackId(trackId) {
|
|
3084
3524
|
this._selectedTrackId = trackId;
|
|
3085
3525
|
}
|
|
@@ -3165,13 +3605,14 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3165
3605
|
if (changedProperties.has("eagerResume")) {
|
|
3166
3606
|
this._audioResume.target = this.eagerResume;
|
|
3167
3607
|
}
|
|
3168
|
-
if (changedProperties.has("samplesPerPixel") && this._isPlaying) {
|
|
3608
|
+
if ((changedProperties.has("samplesPerPixel") || changedProperties.has("ticksPerPixel") || changedProperties.has("bpm") || changedProperties.has("secondsToTicks")) && this._isPlaying) {
|
|
3169
3609
|
this._startPlayhead();
|
|
3170
3610
|
}
|
|
3171
|
-
|
|
3611
|
+
const zoomChanged = changedProperties.has("samplesPerPixel") || changedProperties.has("ticksPerPixel") || changedProperties.has("bpm") || changedProperties.has("scaleMode") || changedProperties.has("secondsToTicks");
|
|
3612
|
+
if (zoomChanged && this._clipBuffers.size > 0) {
|
|
3172
3613
|
const re = this._peakPipeline.reextractPeaks(
|
|
3173
3614
|
this._clipBuffers,
|
|
3174
|
-
this.
|
|
3615
|
+
this._renderSpp,
|
|
3175
3616
|
this.mono,
|
|
3176
3617
|
this._clipOffsets
|
|
3177
3618
|
);
|
|
@@ -3280,7 +3721,7 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3280
3721
|
}
|
|
3281
3722
|
if (waveformData) {
|
|
3282
3723
|
const wdRate = waveformData.sample_rate;
|
|
3283
|
-
const clip2 = (0,
|
|
3724
|
+
const clip2 = (0, import_core8.createClip)({
|
|
3284
3725
|
waveformData,
|
|
3285
3726
|
startSample: Math.round(clipDesc.start * wdRate),
|
|
3286
3727
|
durationSamples: Math.round((clipDesc.duration || waveformData.duration) * wdRate),
|
|
@@ -3290,7 +3731,7 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3290
3731
|
sampleRate: wdRate,
|
|
3291
3732
|
sourceDurationSamples: Math.ceil(waveformData.duration * wdRate)
|
|
3292
3733
|
});
|
|
3293
|
-
const effectiveScale = Math.max(this.
|
|
3734
|
+
const effectiveScale = Math.max(this._renderSpp, waveformData.scale);
|
|
3294
3735
|
const peakData2 = extractPeaks(
|
|
3295
3736
|
waveformData,
|
|
3296
3737
|
effectiveScale,
|
|
@@ -3304,7 +3745,7 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3304
3745
|
});
|
|
3305
3746
|
this._peaksData = new Map(this._peaksData).set(clip2.id, peakData2);
|
|
3306
3747
|
this._minSamplesPerPixel = Math.max(this._minSamplesPerPixel, waveformData.scale);
|
|
3307
|
-
const previewTrack = (0,
|
|
3748
|
+
const previewTrack = (0, import_core8.createTrack)({
|
|
3308
3749
|
name: descriptor.name,
|
|
3309
3750
|
clips: [clip2],
|
|
3310
3751
|
volume: descriptor.volume,
|
|
@@ -3339,7 +3780,7 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3339
3780
|
}
|
|
3340
3781
|
const audioBuffer = await audioPromise;
|
|
3341
3782
|
this._resolvedSampleRate = audioBuffer.sampleRate;
|
|
3342
|
-
const clip = (0,
|
|
3783
|
+
const clip = (0, import_core8.createClipFromSeconds)({
|
|
3343
3784
|
audioBuffer,
|
|
3344
3785
|
startTime: clipDesc.start,
|
|
3345
3786
|
duration: clipDesc.duration || audioBuffer.duration,
|
|
@@ -3356,7 +3797,7 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3356
3797
|
});
|
|
3357
3798
|
const peakData = await this._peakPipeline.generatePeaks(
|
|
3358
3799
|
audioBuffer,
|
|
3359
|
-
this.
|
|
3800
|
+
this._renderSpp,
|
|
3360
3801
|
this.mono,
|
|
3361
3802
|
clip.offsetSamples,
|
|
3362
3803
|
clip.durationSamples
|
|
@@ -3364,7 +3805,7 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3364
3805
|
this._peaksData = new Map(this._peaksData).set(clip.id, peakData);
|
|
3365
3806
|
clips.push(clip);
|
|
3366
3807
|
}
|
|
3367
|
-
const track = (0,
|
|
3808
|
+
const track = (0, import_core8.createTrack)({
|
|
3368
3809
|
name: descriptor.name,
|
|
3369
3810
|
clips,
|
|
3370
3811
|
volume: descriptor.volume,
|
|
@@ -3454,10 +3895,14 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3454
3895
|
import("@dawcore/transport")
|
|
3455
3896
|
]);
|
|
3456
3897
|
const adapter = new NativePlayoutAdapter(this.audioContext);
|
|
3898
|
+
this._adapter = adapter;
|
|
3899
|
+
adapter.setTempo(this._bpm);
|
|
3457
3900
|
const engine = new PlaylistEngine({
|
|
3458
3901
|
adapter,
|
|
3459
3902
|
sampleRate: this.effectiveSampleRate,
|
|
3460
3903
|
samplesPerPixel: this.samplesPerPixel,
|
|
3904
|
+
bpm: this._bpm,
|
|
3905
|
+
ppqn: this._ppqn,
|
|
3461
3906
|
zoomLevels: [256, 512, 1024, 2048, 4096, 8192, this.samplesPerPixel].filter((v, i, a) => a.indexOf(v) === i).sort((a, b) => a - b)
|
|
3462
3907
|
});
|
|
3463
3908
|
let lastTracksVersion = -1;
|
|
@@ -3490,6 +3935,7 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3490
3935
|
this._engine.dispose();
|
|
3491
3936
|
this._engine = null;
|
|
3492
3937
|
}
|
|
3938
|
+
this._adapter = null;
|
|
3493
3939
|
this._enginePromise = null;
|
|
3494
3940
|
}
|
|
3495
3941
|
async loadFiles(files) {
|
|
@@ -3627,12 +4073,13 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3627
4073
|
if (!rs) return "";
|
|
3628
4074
|
const audibleSamples = Math.max(0, rs.totalSamples - rs.latencySamples);
|
|
3629
4075
|
if (audibleSamples === 0) return "";
|
|
3630
|
-
const
|
|
3631
|
-
const
|
|
3632
|
-
const
|
|
4076
|
+
const renderSpp = this._renderSpp;
|
|
4077
|
+
const latencyPixels = Math.floor(rs.latencySamples / renderSpp);
|
|
4078
|
+
const left = Math.floor(rs.startSample / renderSpp);
|
|
4079
|
+
const w = Math.floor(audibleSamples / renderSpp);
|
|
3633
4080
|
return rs.peaks.map((chPeaks, ch) => {
|
|
3634
4081
|
const slicedPeaks = latencyPixels > 0 ? chPeaks.slice(latencyPixels * 2) : chPeaks;
|
|
3635
|
-
return
|
|
4082
|
+
return import_lit13.html`
|
|
3636
4083
|
<daw-waveform
|
|
3637
4084
|
data-recording-track=${trackId}
|
|
3638
4085
|
data-recording-channel=${ch}
|
|
@@ -3655,19 +4102,39 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3655
4102
|
if (!playhead || !this._engine) return;
|
|
3656
4103
|
const engine = this._engine;
|
|
3657
4104
|
const ctx = this.audioContext;
|
|
3658
|
-
|
|
3659
|
-
() =>
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
4105
|
+
if (this.scaleMode === "beats") {
|
|
4106
|
+
const secondsToTicksFn = (s) => this._secondsToTicks(s);
|
|
4107
|
+
playhead.startBeatsAnimationWithMap(
|
|
4108
|
+
() => {
|
|
4109
|
+
const latency = "outputLatency" in ctx ? ctx.outputLatency : 0;
|
|
4110
|
+
return Math.max(0, engine.getCurrentTime() - latency);
|
|
4111
|
+
},
|
|
4112
|
+
secondsToTicksFn,
|
|
4113
|
+
this.ticksPerPixel
|
|
4114
|
+
);
|
|
4115
|
+
} else {
|
|
4116
|
+
playhead.startAnimation(
|
|
4117
|
+
() => {
|
|
4118
|
+
const latency = "outputLatency" in ctx ? ctx.outputLatency : 0;
|
|
4119
|
+
return Math.max(0, engine.getCurrentTime() - latency);
|
|
4120
|
+
},
|
|
4121
|
+
this.effectiveSampleRate,
|
|
4122
|
+
this.samplesPerPixel
|
|
4123
|
+
);
|
|
4124
|
+
}
|
|
3666
4125
|
}
|
|
3667
4126
|
_stopPlayhead() {
|
|
3668
4127
|
const playhead = this._getPlayhead();
|
|
3669
4128
|
if (!playhead) return;
|
|
3670
|
-
|
|
4129
|
+
if (this.scaleMode === "beats") {
|
|
4130
|
+
playhead.stopBeatsAnimationWithMap(
|
|
4131
|
+
this._currentTime,
|
|
4132
|
+
(s) => this._secondsToTicks(s),
|
|
4133
|
+
this.ticksPerPixel
|
|
4134
|
+
);
|
|
4135
|
+
} else {
|
|
4136
|
+
playhead.stopAnimation(this._currentTime, this.effectiveSampleRate, this.samplesPerPixel);
|
|
4137
|
+
}
|
|
3671
4138
|
}
|
|
3672
4139
|
_getPlayhead() {
|
|
3673
4140
|
return this.shadowRoot?.querySelector("daw-playhead");
|
|
@@ -3688,8 +4155,18 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3688
4155
|
// --- Render ---
|
|
3689
4156
|
render() {
|
|
3690
4157
|
const sr = this.effectiveSampleRate;
|
|
3691
|
-
const
|
|
3692
|
-
|
|
4158
|
+
const spp = this._renderSpp;
|
|
4159
|
+
let selStartPx;
|
|
4160
|
+
let selEndPx;
|
|
4161
|
+
if (this.scaleMode === "beats") {
|
|
4162
|
+
const startTick = this._secondsToTicks(this._selectionStartTime);
|
|
4163
|
+
const endTick = this._secondsToTicks(this._selectionEndTime);
|
|
4164
|
+
selStartPx = startTick / this.ticksPerPixel;
|
|
4165
|
+
selEndPx = endTick / this.ticksPerPixel;
|
|
4166
|
+
} else {
|
|
4167
|
+
selStartPx = this._selectionStartTime * sr / spp;
|
|
4168
|
+
selEndPx = this._selectionEndTime * sr / spp;
|
|
4169
|
+
}
|
|
3693
4170
|
const orderedTracks = this._getOrderedTracks().map(([trackId, track]) => {
|
|
3694
4171
|
const descriptor = this._tracks.get(trackId);
|
|
3695
4172
|
const firstPeaks = track.clips.map((c) => this._peaksData.get(c.id)).find((p) => p && p.data.length > 0);
|
|
@@ -3703,11 +4180,11 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3703
4180
|
trackHeight: this.waveHeight * numChannels + (this.clipHeaders ? this.clipHeaderHeight : 0)
|
|
3704
4181
|
};
|
|
3705
4182
|
});
|
|
3706
|
-
return
|
|
3707
|
-
${orderedTracks.length > 0 ?
|
|
3708
|
-
${this.timescale ?
|
|
4183
|
+
return import_lit13.html`
|
|
4184
|
+
${orderedTracks.length > 0 ? import_lit13.html`<div class="controls-column">
|
|
4185
|
+
${this.timescale ? import_lit13.html`<div style="height: 30px;"></div>` : ""}
|
|
3709
4186
|
${orderedTracks.map(
|
|
3710
|
-
(t) =>
|
|
4187
|
+
(t) => import_lit13.html`
|
|
3711
4188
|
<daw-track-controls
|
|
3712
4189
|
style="height: ${t.trackHeight}px;"
|
|
3713
4190
|
.trackId=${t.trackId}
|
|
@@ -3730,16 +4207,31 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3730
4207
|
@dragleave=${this._onDragLeave}
|
|
3731
4208
|
@drop=${this._onDrop}
|
|
3732
4209
|
>
|
|
3733
|
-
${orderedTracks.length > 0 && this.timescale ?
|
|
3734
|
-
.samplesPerPixel=${
|
|
4210
|
+
${(orderedTracks.length > 0 || this.scaleMode === "beats") && this.timescale ? import_lit13.html`<daw-ruler
|
|
4211
|
+
.samplesPerPixel=${spp}
|
|
3735
4212
|
.sampleRate=${this.effectiveSampleRate}
|
|
3736
4213
|
.duration=${this._duration}
|
|
4214
|
+
.scaleMode=${this.scaleMode}
|
|
4215
|
+
.ticksPerPixel=${this.ticksPerPixel}
|
|
4216
|
+
.meterEntries=${this._meterEntries}
|
|
4217
|
+
.ppqn=${this.ppqn}
|
|
4218
|
+
.totalWidth=${this._totalWidth}
|
|
3737
4219
|
></daw-ruler>` : ""}
|
|
3738
|
-
${
|
|
4220
|
+
${this.scaleMode === "beats" ? import_lit13.html`<daw-grid
|
|
4221
|
+
style="top: ${this.timescale ? 30 : 0}px;"
|
|
4222
|
+
.ticksPerPixel=${this.ticksPerPixel}
|
|
4223
|
+
.meterEntries=${this._meterEntries}
|
|
4224
|
+
.ppqn=${this.ppqn}
|
|
4225
|
+
.visibleStart=${this._viewport.visibleStart}
|
|
4226
|
+
.visibleEnd=${this._viewport.visibleEnd}
|
|
4227
|
+
.length=${this._totalWidth}
|
|
4228
|
+
.height=${orderedTracks.length > 0 ? orderedTracks.reduce((sum, t) => sum + t.trackHeight + 1, 0) : this._emptyGridHeight}
|
|
4229
|
+
></daw-grid>` : ""}
|
|
4230
|
+
${orderedTracks.length > 0 || this.scaleMode === "beats" ? import_lit13.html`<daw-selection .startPx=${selStartPx} .endPx=${selEndPx}></daw-selection>
|
|
3739
4231
|
<daw-playhead></daw-playhead>` : ""}
|
|
3740
4232
|
${orderedTracks.map((t) => {
|
|
3741
4233
|
const channelHeight = this.waveHeight;
|
|
3742
|
-
return
|
|
4234
|
+
return import_lit13.html`
|
|
3743
4235
|
<div
|
|
3744
4236
|
class="track-row ${t.trackId === this._selectedTrackId ? "selected" : ""}"
|
|
3745
4237
|
style="height: ${t.trackHeight}px;"
|
|
@@ -3747,21 +4239,67 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3747
4239
|
>
|
|
3748
4240
|
${t.track.clips.map((clip) => {
|
|
3749
4241
|
const peakData = this._peaksData.get(clip.id);
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
this.
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
4242
|
+
let clipLeft;
|
|
4243
|
+
let width;
|
|
4244
|
+
if (this.scaleMode === "beats") {
|
|
4245
|
+
const startTick = clip.startTick !== void 0 ? clip.startTick : this._secondsToTicks(clip.startSample / sr);
|
|
4246
|
+
const durSec = clip.durationSamples / sr;
|
|
4247
|
+
const startSec = clip.startTick !== void 0 ? this._ticksToSeconds(clip.startTick) : clip.startSample / sr;
|
|
4248
|
+
const endTick = this._secondsToTicks(startSec + durSec);
|
|
4249
|
+
clipLeft = Math.round(startTick / this.ticksPerPixel);
|
|
4250
|
+
width = Math.round(endTick / this.ticksPerPixel) - clipLeft;
|
|
4251
|
+
} else {
|
|
4252
|
+
clipLeft = Math.floor(clip.startSample / spp);
|
|
4253
|
+
width = (0, import_core8.clipPixelWidth)(clip.startSample, clip.durationSamples, spp);
|
|
4254
|
+
}
|
|
4255
|
+
let clipSegments;
|
|
4256
|
+
let segmentChannels;
|
|
4257
|
+
if (this.scaleMode === "beats" && this.secondsToTicks) {
|
|
4258
|
+
const audioBuffer = this._clipBuffers.get(clip.id);
|
|
4259
|
+
const basePeaks = audioBuffer ? this._peakPipeline.getBaseScalePeaks(
|
|
4260
|
+
audioBuffer,
|
|
4261
|
+
this.mono,
|
|
4262
|
+
clip.offsetSamples,
|
|
4263
|
+
clip.durationSamples
|
|
4264
|
+
) : null;
|
|
4265
|
+
if (basePeaks) {
|
|
4266
|
+
const baseScale = basePeaks.scale;
|
|
4267
|
+
segmentChannels = basePeaks.peaks.data;
|
|
4268
|
+
const MIN_RENDER_STEP = 80;
|
|
4269
|
+
const stepTicks = Math.max(MIN_RENDER_STEP, Math.ceil(this.ticksPerPixel));
|
|
4270
|
+
const startSec = clip.startTick !== void 0 ? this._ticksToSeconds(clip.startTick) : clip.startSample / sr;
|
|
4271
|
+
const clipOffsetSec = clip.offsetSamples / sr;
|
|
4272
|
+
const segStartTick = clip.startTick !== void 0 ? clip.startTick : this._secondsToTicks(startSec);
|
|
4273
|
+
const endTick = this._secondsToTicks(startSec + clip.durationSamples / sr);
|
|
4274
|
+
clipSegments = [];
|
|
4275
|
+
for (let tick = segStartTick; tick < endTick; tick += stepTicks) {
|
|
4276
|
+
const segEndTick = Math.min(tick + stepTicks, endTick);
|
|
4277
|
+
const segStartAudioSec = this._ticksToSeconds(tick) - startSec + clipOffsetSec;
|
|
4278
|
+
const segEndAudioSec = this._ticksToSeconds(segEndTick) - startSec + clipOffsetSec;
|
|
4279
|
+
const segStartSample = Math.round(segStartAudioSec * sr);
|
|
4280
|
+
const segEndSample = Math.round(segEndAudioSec * sr);
|
|
4281
|
+
const totalPeaks = clip.durationSamples / baseScale;
|
|
4282
|
+
clipSegments.push({
|
|
4283
|
+
peakStart: Math.max(0, (segStartSample - clip.offsetSamples) / baseScale),
|
|
4284
|
+
peakEnd: Math.min(
|
|
4285
|
+
totalPeaks,
|
|
4286
|
+
(segEndSample - clip.offsetSamples) / baseScale
|
|
4287
|
+
),
|
|
4288
|
+
pixelStart: (tick - segStartTick) / this.ticksPerPixel,
|
|
4289
|
+
pixelEnd: (segEndTick - segStartTick) / this.ticksPerPixel
|
|
4290
|
+
});
|
|
4291
|
+
}
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
const channels = segmentChannels ?? peakData?.data ?? [new Int16Array(0)];
|
|
3757
4295
|
const hdrH = this.clipHeaders ? this.clipHeaderHeight : 0;
|
|
3758
4296
|
const chH = this.waveHeight;
|
|
3759
|
-
return
|
|
4297
|
+
return import_lit13.html` <div
|
|
3760
4298
|
class="clip-container"
|
|
3761
4299
|
style="left:${clipLeft}px;top:0;width:${width}px;height:${t.trackHeight}px;"
|
|
3762
4300
|
data-clip-id=${clip.id}
|
|
3763
4301
|
>
|
|
3764
|
-
${hdrH > 0 ?
|
|
4302
|
+
${hdrH > 0 ? import_lit13.html`<div
|
|
3765
4303
|
class="clip-header"
|
|
3766
4304
|
data-clip-id=${clip.id}
|
|
3767
4305
|
data-track-id=${t.trackId}
|
|
@@ -3770,7 +4308,7 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3770
4308
|
<span>${clip.name || t.descriptor?.name || ""}</span>
|
|
3771
4309
|
</div>` : ""}
|
|
3772
4310
|
${channels.map(
|
|
3773
|
-
(chPeaks, chIdx) =>
|
|
4311
|
+
(chPeaks, chIdx) => import_lit13.html` <daw-waveform
|
|
3774
4312
|
style="position:absolute;left:0;top:${hdrH + chIdx * chH}px;"
|
|
3775
4313
|
.peaks=${chPeaks}
|
|
3776
4314
|
.length=${peakData?.length ?? width}
|
|
@@ -3780,9 +4318,10 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3780
4318
|
.visibleStart=${this._viewport.visibleStart}
|
|
3781
4319
|
.visibleEnd=${this._viewport.visibleEnd}
|
|
3782
4320
|
.originX=${clipLeft}
|
|
4321
|
+
.segments=${clipSegments}
|
|
3783
4322
|
></daw-waveform>`
|
|
3784
4323
|
)}
|
|
3785
|
-
${this.interactiveClips ?
|
|
4324
|
+
${this.interactiveClips ? import_lit13.html` <div
|
|
3786
4325
|
class="clip-boundary"
|
|
3787
4326
|
data-boundary-edge="left"
|
|
3788
4327
|
data-clip-id=${clip.id}
|
|
@@ -3808,7 +4347,7 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3808
4347
|
};
|
|
3809
4348
|
DawEditorElement.styles = [
|
|
3810
4349
|
hostStyles,
|
|
3811
|
-
|
|
4350
|
+
import_lit13.css`
|
|
3812
4351
|
:host {
|
|
3813
4352
|
display: flex;
|
|
3814
4353
|
position: relative;
|
|
@@ -3838,6 +4377,15 @@ DawEditorElement.styles = [
|
|
|
3838
4377
|
.track-row.selected {
|
|
3839
4378
|
background: rgba(99, 199, 95, 0.08);
|
|
3840
4379
|
}
|
|
4380
|
+
:host([scale-mode='beats']) .track-row {
|
|
4381
|
+
background: transparent;
|
|
4382
|
+
}
|
|
4383
|
+
:host([scale-mode='beats']) .clip-container {
|
|
4384
|
+
background: var(--daw-track-background, #16213e);
|
|
4385
|
+
}
|
|
4386
|
+
:host([scale-mode='beats']) .track-row.selected .clip-container {
|
|
4387
|
+
box-shadow: inset 0 0 0 1000px rgba(99, 199, 95, 0.06);
|
|
4388
|
+
}
|
|
3841
4389
|
.timeline.drag-over {
|
|
3842
4390
|
outline: 2px dashed var(--daw-selection-color, rgba(99, 199, 95, 0.3));
|
|
3843
4391
|
outline-offset: -2px;
|
|
@@ -3847,69 +4395,96 @@ DawEditorElement.styles = [
|
|
|
3847
4395
|
];
|
|
3848
4396
|
DawEditorElement._CONTROL_PROPS = /* @__PURE__ */ new Set(["volume", "pan", "muted", "soloed"]);
|
|
3849
4397
|
__decorateClass([
|
|
3850
|
-
(0,
|
|
4398
|
+
(0, import_decorators11.property)({ type: Number, attribute: "samples-per-pixel", noAccessor: true })
|
|
3851
4399
|
], DawEditorElement.prototype, "samplesPerPixel", 1);
|
|
3852
4400
|
__decorateClass([
|
|
3853
|
-
(0,
|
|
4401
|
+
(0, import_decorators11.property)({ type: Number, attribute: "wave-height" })
|
|
3854
4402
|
], DawEditorElement.prototype, "waveHeight", 2);
|
|
3855
4403
|
__decorateClass([
|
|
3856
|
-
(0,
|
|
4404
|
+
(0, import_decorators11.property)({ type: Boolean })
|
|
3857
4405
|
], DawEditorElement.prototype, "timescale", 2);
|
|
3858
4406
|
__decorateClass([
|
|
3859
|
-
(0,
|
|
4407
|
+
(0, import_decorators11.property)({ type: Boolean })
|
|
3860
4408
|
], DawEditorElement.prototype, "mono", 2);
|
|
3861
4409
|
__decorateClass([
|
|
3862
|
-
(0,
|
|
4410
|
+
(0, import_decorators11.property)({ type: Number, attribute: "bar-width" })
|
|
3863
4411
|
], DawEditorElement.prototype, "barWidth", 2);
|
|
3864
4412
|
__decorateClass([
|
|
3865
|
-
(0,
|
|
4413
|
+
(0, import_decorators11.property)({ type: Number, attribute: "bar-gap" })
|
|
3866
4414
|
], DawEditorElement.prototype, "barGap", 2);
|
|
3867
4415
|
__decorateClass([
|
|
3868
|
-
(0,
|
|
4416
|
+
(0, import_decorators11.property)({ type: Boolean, attribute: "file-drop" })
|
|
3869
4417
|
], DawEditorElement.prototype, "fileDrop", 2);
|
|
3870
4418
|
__decorateClass([
|
|
3871
|
-
(0,
|
|
4419
|
+
(0, import_decorators11.property)({ type: Boolean, attribute: "clip-headers" })
|
|
3872
4420
|
], DawEditorElement.prototype, "clipHeaders", 2);
|
|
3873
4421
|
__decorateClass([
|
|
3874
|
-
(0,
|
|
4422
|
+
(0, import_decorators11.property)({ type: Number, attribute: "clip-header-height" })
|
|
3875
4423
|
], DawEditorElement.prototype, "clipHeaderHeight", 2);
|
|
3876
4424
|
__decorateClass([
|
|
3877
|
-
(0,
|
|
4425
|
+
(0, import_decorators11.property)({ type: Boolean, attribute: "interactive-clips" })
|
|
3878
4426
|
], DawEditorElement.prototype, "interactiveClips", 2);
|
|
3879
4427
|
__decorateClass([
|
|
3880
|
-
(0,
|
|
4428
|
+
(0, import_decorators11.property)({ type: String, attribute: "scale-mode" })
|
|
4429
|
+
], DawEditorElement.prototype, "scaleMode", 2);
|
|
4430
|
+
__decorateClass([
|
|
4431
|
+
(0, import_decorators11.property)({ type: Number, attribute: "ticks-per-pixel", noAccessor: true })
|
|
4432
|
+
], DawEditorElement.prototype, "ticksPerPixel", 1);
|
|
4433
|
+
__decorateClass([
|
|
4434
|
+
(0, import_decorators11.property)({ type: Number, noAccessor: true })
|
|
4435
|
+
], DawEditorElement.prototype, "bpm", 1);
|
|
4436
|
+
__decorateClass([
|
|
4437
|
+
(0, import_decorators11.property)({ attribute: false })
|
|
4438
|
+
], DawEditorElement.prototype, "timeSignature", 2);
|
|
4439
|
+
__decorateClass([
|
|
4440
|
+
(0, import_decorators11.property)({ attribute: false })
|
|
4441
|
+
], DawEditorElement.prototype, "meterEntries", 2);
|
|
4442
|
+
__decorateClass([
|
|
4443
|
+
(0, import_decorators11.property)({ type: Number, noAccessor: true })
|
|
4444
|
+
], DawEditorElement.prototype, "ppqn", 1);
|
|
4445
|
+
__decorateClass([
|
|
4446
|
+
(0, import_decorators11.property)({ type: String, attribute: "snap-to" })
|
|
4447
|
+
], DawEditorElement.prototype, "snapTo", 2);
|
|
4448
|
+
__decorateClass([
|
|
4449
|
+
(0, import_decorators11.property)({ attribute: false })
|
|
4450
|
+
], DawEditorElement.prototype, "secondsToTicks", 2);
|
|
4451
|
+
__decorateClass([
|
|
4452
|
+
(0, import_decorators11.property)({ attribute: false })
|
|
4453
|
+
], DawEditorElement.prototype, "ticksToSeconds", 2);
|
|
4454
|
+
__decorateClass([
|
|
4455
|
+
(0, import_decorators11.property)({ type: Number, attribute: "sample-rate" })
|
|
3881
4456
|
], DawEditorElement.prototype, "sampleRate", 2);
|
|
3882
4457
|
__decorateClass([
|
|
3883
|
-
(0,
|
|
4458
|
+
(0, import_decorators11.state)()
|
|
3884
4459
|
], DawEditorElement.prototype, "_tracks", 2);
|
|
3885
4460
|
__decorateClass([
|
|
3886
|
-
(0,
|
|
4461
|
+
(0, import_decorators11.state)()
|
|
3887
4462
|
], DawEditorElement.prototype, "_engineTracks", 2);
|
|
3888
4463
|
__decorateClass([
|
|
3889
|
-
(0,
|
|
4464
|
+
(0, import_decorators11.state)()
|
|
3890
4465
|
], DawEditorElement.prototype, "_peaksData", 2);
|
|
3891
4466
|
__decorateClass([
|
|
3892
|
-
(0,
|
|
4467
|
+
(0, import_decorators11.state)()
|
|
3893
4468
|
], DawEditorElement.prototype, "_isPlaying", 2);
|
|
3894
4469
|
__decorateClass([
|
|
3895
|
-
(0,
|
|
4470
|
+
(0, import_decorators11.state)()
|
|
3896
4471
|
], DawEditorElement.prototype, "_duration", 2);
|
|
3897
4472
|
__decorateClass([
|
|
3898
|
-
(0,
|
|
4473
|
+
(0, import_decorators11.state)()
|
|
3899
4474
|
], DawEditorElement.prototype, "_selectedTrackId", 2);
|
|
3900
4475
|
__decorateClass([
|
|
3901
|
-
(0,
|
|
4476
|
+
(0, import_decorators11.state)()
|
|
3902
4477
|
], DawEditorElement.prototype, "_dragOver", 2);
|
|
3903
4478
|
__decorateClass([
|
|
3904
|
-
(0,
|
|
4479
|
+
(0, import_decorators11.property)({ attribute: "eager-resume" })
|
|
3905
4480
|
], DawEditorElement.prototype, "eagerResume", 2);
|
|
3906
4481
|
DawEditorElement = __decorateClass([
|
|
3907
|
-
(0,
|
|
4482
|
+
(0, import_decorators11.customElement)("daw-editor")
|
|
3908
4483
|
], DawEditorElement);
|
|
3909
4484
|
|
|
3910
4485
|
// src/elements/daw-ruler.ts
|
|
3911
|
-
var
|
|
3912
|
-
var
|
|
4486
|
+
var import_lit14 = require("lit");
|
|
4487
|
+
var import_decorators12 = require("lit/decorators.js");
|
|
3913
4488
|
|
|
3914
4489
|
// src/utils/time-format.ts
|
|
3915
4490
|
function formatTime(milliseconds) {
|
|
@@ -3960,18 +4535,36 @@ function computeTemporalTicks(samplesPerPixel, sampleRate, duration, rulerHeight
|
|
|
3960
4535
|
}
|
|
3961
4536
|
|
|
3962
4537
|
// src/elements/daw-ruler.ts
|
|
3963
|
-
var
|
|
3964
|
-
var DawRulerElement = class extends
|
|
4538
|
+
var MAX_CANVAS_WIDTH3 = 1e3;
|
|
4539
|
+
var DawRulerElement = class extends import_lit14.LitElement {
|
|
3965
4540
|
constructor() {
|
|
3966
4541
|
super(...arguments);
|
|
3967
4542
|
this.samplesPerPixel = 1024;
|
|
3968
4543
|
this.sampleRate = 48e3;
|
|
3969
4544
|
this.duration = 0;
|
|
3970
4545
|
this.rulerHeight = 30;
|
|
4546
|
+
this.scaleMode = "temporal";
|
|
4547
|
+
this.ticksPerPixel = 4;
|
|
4548
|
+
this.meterEntries = [
|
|
4549
|
+
{ tick: 0, numerator: 4, denominator: 4 }
|
|
4550
|
+
];
|
|
4551
|
+
this.ppqn = 960;
|
|
4552
|
+
this.totalWidth = 0;
|
|
3971
4553
|
this._tickData = null;
|
|
4554
|
+
this._musicalTickData = null;
|
|
3972
4555
|
}
|
|
3973
4556
|
willUpdate() {
|
|
3974
|
-
if (this.
|
|
4557
|
+
if (this.scaleMode === "beats" && this.totalWidth > 0) {
|
|
4558
|
+
this._musicalTickData = getCachedMusicalTicks({
|
|
4559
|
+
meterEntries: this.meterEntries,
|
|
4560
|
+
ticksPerPixel: this.ticksPerPixel,
|
|
4561
|
+
startPixel: 0,
|
|
4562
|
+
endPixel: this.totalWidth,
|
|
4563
|
+
ppqn: this.ppqn
|
|
4564
|
+
});
|
|
4565
|
+
this._tickData = null;
|
|
4566
|
+
} else if (this.duration > 0) {
|
|
4567
|
+
this._musicalTickData = null;
|
|
3975
4568
|
this._tickData = computeTemporalTicks(
|
|
3976
4569
|
this.samplesPerPixel,
|
|
3977
4570
|
this.sampleRate,
|
|
@@ -3979,30 +4572,39 @@ var DawRulerElement = class extends import_lit13.LitElement {
|
|
|
3979
4572
|
this.rulerHeight
|
|
3980
4573
|
);
|
|
3981
4574
|
} else {
|
|
4575
|
+
this._musicalTickData = null;
|
|
3982
4576
|
this._tickData = null;
|
|
3983
4577
|
}
|
|
3984
4578
|
}
|
|
3985
4579
|
render() {
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
const totalChunks = Math.ceil(widthX /
|
|
4580
|
+
const widthX = this.scaleMode === "beats" ? this.totalWidth : this._tickData?.widthX ?? 0;
|
|
4581
|
+
if (widthX <= 0) return import_lit14.html``;
|
|
4582
|
+
const totalChunks = Math.ceil(widthX / MAX_CANVAS_WIDTH3);
|
|
3989
4583
|
const indices = Array.from({ length: totalChunks }, (_, i) => i);
|
|
3990
4584
|
const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
3991
|
-
|
|
4585
|
+
const beatsLabels = this.scaleMode === "beats" ? this._musicalTickData?.ticks.filter((t) => t.label) ?? [] : [];
|
|
4586
|
+
const temporalLabels = this.scaleMode !== "beats" ? this._tickData?.labels ?? [] : [];
|
|
4587
|
+
return import_lit14.html`
|
|
3992
4588
|
<div class="container" style="width: ${widthX}px; height: ${this.rulerHeight}px;">
|
|
3993
4589
|
${indices.map((i) => {
|
|
3994
|
-
const width = Math.min(
|
|
3995
|
-
return
|
|
4590
|
+
const width = Math.min(MAX_CANVAS_WIDTH3, widthX - i * MAX_CANVAS_WIDTH3);
|
|
4591
|
+
return import_lit14.html`
|
|
3996
4592
|
<canvas
|
|
3997
4593
|
data-index=${i}
|
|
3998
4594
|
width=${width * dpr}
|
|
3999
4595
|
height=${this.rulerHeight * dpr}
|
|
4000
|
-
style="left: ${i *
|
|
4596
|
+
style="left: ${i * MAX_CANVAS_WIDTH3}px; width: ${width}px; height: ${this.rulerHeight}px;"
|
|
4001
4597
|
></canvas>
|
|
4002
4598
|
`;
|
|
4003
4599
|
})}
|
|
4004
|
-
${
|
|
4005
|
-
(
|
|
4600
|
+
${this.scaleMode === "beats" ? beatsLabels.map(
|
|
4601
|
+
(t) => import_lit14.html`<span
|
|
4602
|
+
class="label ${t.pixel > 0 ? "centered" : ""}"
|
|
4603
|
+
style="left: ${t.pixel > 0 ? t.pixel : t.pixel + 4}px;"
|
|
4604
|
+
>${t.label}</span
|
|
4605
|
+
>`
|
|
4606
|
+
) : temporalLabels.map(
|
|
4607
|
+
({ pix, text }) => import_lit14.html`<span class="label" style="left: ${pix + 4}px;">${text}</span>`
|
|
4006
4608
|
)}
|
|
4007
4609
|
</div>
|
|
4008
4610
|
`;
|
|
@@ -4011,37 +4613,49 @@ var DawRulerElement = class extends import_lit13.LitElement {
|
|
|
4011
4613
|
this._drawTicks();
|
|
4012
4614
|
}
|
|
4013
4615
|
_drawTicks() {
|
|
4014
|
-
if (!this._tickData) return;
|
|
4015
4616
|
const canvases = this.shadowRoot?.querySelectorAll("canvas");
|
|
4016
4617
|
if (!canvases) return;
|
|
4017
4618
|
const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
4018
4619
|
const rulerColor = getComputedStyle(this).getPropertyValue("--daw-ruler-color").trim() || "#c49a6c";
|
|
4620
|
+
const widthX = this.scaleMode === "beats" ? this.totalWidth : this._tickData?.widthX ?? 0;
|
|
4019
4621
|
for (const canvas of canvases) {
|
|
4020
4622
|
const idx = Number(canvas.dataset.index);
|
|
4021
4623
|
const ctx = canvas.getContext("2d");
|
|
4022
4624
|
if (!ctx) continue;
|
|
4023
|
-
const canvasWidth = Math.min(
|
|
4024
|
-
|
|
4025
|
-
this._tickData.widthX - idx * MAX_CANVAS_WIDTH2
|
|
4026
|
-
);
|
|
4027
|
-
const globalOffset = idx * MAX_CANVAS_WIDTH2;
|
|
4625
|
+
const canvasWidth = Math.min(MAX_CANVAS_WIDTH3, widthX - idx * MAX_CANVAS_WIDTH3);
|
|
4626
|
+
const globalOffset = idx * MAX_CANVAS_WIDTH3;
|
|
4028
4627
|
ctx.resetTransform();
|
|
4029
4628
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
4030
4629
|
ctx.scale(dpr, dpr);
|
|
4031
4630
|
ctx.strokeStyle = rulerColor;
|
|
4032
4631
|
ctx.lineWidth = 1;
|
|
4033
|
-
|
|
4034
|
-
const
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4632
|
+
if (this.scaleMode === "beats" && this._musicalTickData) {
|
|
4633
|
+
const h = this.rulerHeight;
|
|
4634
|
+
for (const tick of this._musicalTickData.ticks) {
|
|
4635
|
+
const localX = tick.pixel - globalOffset;
|
|
4636
|
+
if (localX < 0 || localX >= canvasWidth) continue;
|
|
4637
|
+
const tickH = tick.type === "major" ? h * 0.6 : tick.type === "minor" ? h * 0.35 : h * 0.15;
|
|
4638
|
+
ctx.globalAlpha = tick.type === "major" ? 1 : 0.5;
|
|
4639
|
+
ctx.beginPath();
|
|
4640
|
+
ctx.moveTo(localX + 0.5, h);
|
|
4641
|
+
ctx.lineTo(localX + 0.5, h - tickH);
|
|
4642
|
+
ctx.stroke();
|
|
4643
|
+
}
|
|
4644
|
+
ctx.globalAlpha = 1;
|
|
4645
|
+
} else if (this._tickData) {
|
|
4646
|
+
for (const [pix, height] of this._tickData.canvasInfo) {
|
|
4647
|
+
const localX = pix - globalOffset;
|
|
4648
|
+
if (localX < 0 || localX >= canvasWidth) continue;
|
|
4649
|
+
ctx.beginPath();
|
|
4650
|
+
ctx.moveTo(localX + 0.5, this.rulerHeight);
|
|
4651
|
+
ctx.lineTo(localX + 0.5, this.rulerHeight - height);
|
|
4652
|
+
ctx.stroke();
|
|
4653
|
+
}
|
|
4040
4654
|
}
|
|
4041
4655
|
}
|
|
4042
4656
|
}
|
|
4043
4657
|
};
|
|
4044
|
-
DawRulerElement.styles =
|
|
4658
|
+
DawRulerElement.styles = import_lit14.css`
|
|
4045
4659
|
:host {
|
|
4046
4660
|
display: block;
|
|
4047
4661
|
position: relative;
|
|
@@ -4057,31 +4671,50 @@ DawRulerElement.styles = import_lit13.css`
|
|
|
4057
4671
|
.label {
|
|
4058
4672
|
position: absolute;
|
|
4059
4673
|
font-size: 0.7rem;
|
|
4674
|
+
line-height: 1;
|
|
4060
4675
|
white-space: nowrap;
|
|
4061
4676
|
color: var(--daw-ruler-color, #c49a6c);
|
|
4062
|
-
top:
|
|
4677
|
+
top: 1px;
|
|
4678
|
+
}
|
|
4679
|
+
.label.centered {
|
|
4680
|
+
transform: translateX(-50%);
|
|
4063
4681
|
}
|
|
4064
4682
|
`;
|
|
4065
4683
|
__decorateClass([
|
|
4066
|
-
(0,
|
|
4684
|
+
(0, import_decorators12.property)({ type: Number, attribute: false })
|
|
4067
4685
|
], DawRulerElement.prototype, "samplesPerPixel", 2);
|
|
4068
4686
|
__decorateClass([
|
|
4069
|
-
(0,
|
|
4687
|
+
(0, import_decorators12.property)({ type: Number, attribute: false })
|
|
4070
4688
|
], DawRulerElement.prototype, "sampleRate", 2);
|
|
4071
4689
|
__decorateClass([
|
|
4072
|
-
(0,
|
|
4690
|
+
(0, import_decorators12.property)({ type: Number, attribute: false })
|
|
4073
4691
|
], DawRulerElement.prototype, "duration", 2);
|
|
4074
4692
|
__decorateClass([
|
|
4075
|
-
(0,
|
|
4693
|
+
(0, import_decorators12.property)({ type: Number, attribute: false })
|
|
4076
4694
|
], DawRulerElement.prototype, "rulerHeight", 2);
|
|
4695
|
+
__decorateClass([
|
|
4696
|
+
(0, import_decorators12.property)({ type: String, attribute: false })
|
|
4697
|
+
], DawRulerElement.prototype, "scaleMode", 2);
|
|
4698
|
+
__decorateClass([
|
|
4699
|
+
(0, import_decorators12.property)({ type: Number, attribute: false })
|
|
4700
|
+
], DawRulerElement.prototype, "ticksPerPixel", 2);
|
|
4701
|
+
__decorateClass([
|
|
4702
|
+
(0, import_decorators12.property)({ attribute: false })
|
|
4703
|
+
], DawRulerElement.prototype, "meterEntries", 2);
|
|
4704
|
+
__decorateClass([
|
|
4705
|
+
(0, import_decorators12.property)({ type: Number, attribute: false })
|
|
4706
|
+
], DawRulerElement.prototype, "ppqn", 2);
|
|
4707
|
+
__decorateClass([
|
|
4708
|
+
(0, import_decorators12.property)({ type: Number, attribute: false })
|
|
4709
|
+
], DawRulerElement.prototype, "totalWidth", 2);
|
|
4077
4710
|
DawRulerElement = __decorateClass([
|
|
4078
|
-
(0,
|
|
4711
|
+
(0, import_decorators12.customElement)("daw-ruler")
|
|
4079
4712
|
], DawRulerElement);
|
|
4080
4713
|
|
|
4081
4714
|
// src/elements/daw-selection.ts
|
|
4082
|
-
var
|
|
4083
|
-
var
|
|
4084
|
-
var DawSelectionElement = class extends
|
|
4715
|
+
var import_lit15 = require("lit");
|
|
4716
|
+
var import_decorators13 = require("lit/decorators.js");
|
|
4717
|
+
var DawSelectionElement = class extends import_lit15.LitElement {
|
|
4085
4718
|
constructor() {
|
|
4086
4719
|
super(...arguments);
|
|
4087
4720
|
this.startPx = 0;
|
|
@@ -4090,11 +4723,11 @@ var DawSelectionElement = class extends import_lit14.LitElement {
|
|
|
4090
4723
|
render() {
|
|
4091
4724
|
const left = Math.min(this.startPx, this.endPx);
|
|
4092
4725
|
const width = Math.abs(this.endPx - this.startPx);
|
|
4093
|
-
if (width === 0) return
|
|
4094
|
-
return
|
|
4726
|
+
if (width === 0) return import_lit15.html``;
|
|
4727
|
+
return import_lit15.html`<div style="left: ${left}px; width: ${width}px;"></div>`;
|
|
4095
4728
|
}
|
|
4096
4729
|
};
|
|
4097
|
-
DawSelectionElement.styles =
|
|
4730
|
+
DawSelectionElement.styles = import_lit15.css`
|
|
4098
4731
|
:host {
|
|
4099
4732
|
position: absolute;
|
|
4100
4733
|
top: 0;
|
|
@@ -4111,18 +4744,18 @@ DawSelectionElement.styles = import_lit14.css`
|
|
|
4111
4744
|
}
|
|
4112
4745
|
`;
|
|
4113
4746
|
__decorateClass([
|
|
4114
|
-
(0,
|
|
4747
|
+
(0, import_decorators13.property)({ type: Number, attribute: false })
|
|
4115
4748
|
], DawSelectionElement.prototype, "startPx", 2);
|
|
4116
4749
|
__decorateClass([
|
|
4117
|
-
(0,
|
|
4750
|
+
(0, import_decorators13.property)({ type: Number, attribute: false })
|
|
4118
4751
|
], DawSelectionElement.prototype, "endPx", 2);
|
|
4119
4752
|
DawSelectionElement = __decorateClass([
|
|
4120
|
-
(0,
|
|
4753
|
+
(0, import_decorators13.customElement)("daw-selection")
|
|
4121
4754
|
], DawSelectionElement);
|
|
4122
4755
|
|
|
4123
4756
|
// src/elements/daw-record-button.ts
|
|
4124
|
-
var
|
|
4125
|
-
var
|
|
4757
|
+
var import_lit16 = require("lit");
|
|
4758
|
+
var import_decorators14 = require("lit/decorators.js");
|
|
4126
4759
|
var DawRecordButtonElement = class extends DawTransportButton {
|
|
4127
4760
|
constructor() {
|
|
4128
4761
|
super(...arguments);
|
|
@@ -4163,7 +4796,7 @@ var DawRecordButtonElement = class extends DawTransportButton {
|
|
|
4163
4796
|
}
|
|
4164
4797
|
}
|
|
4165
4798
|
render() {
|
|
4166
|
-
return
|
|
4799
|
+
return import_lit16.html`
|
|
4167
4800
|
<button part="button" ?data-recording=${this._isRecording} @click=${this._onClick}>
|
|
4168
4801
|
<slot>Record</slot>
|
|
4169
4802
|
</button>
|
|
@@ -4183,7 +4816,7 @@ var DawRecordButtonElement = class extends DawTransportButton {
|
|
|
4183
4816
|
};
|
|
4184
4817
|
DawRecordButtonElement.styles = [
|
|
4185
4818
|
DawTransportButton.styles,
|
|
4186
|
-
|
|
4819
|
+
import_lit16.css`
|
|
4187
4820
|
button[data-recording] {
|
|
4188
4821
|
color: #d08070;
|
|
4189
4822
|
border-color: #d08070;
|
|
@@ -4192,17 +4825,17 @@ DawRecordButtonElement.styles = [
|
|
|
4192
4825
|
`
|
|
4193
4826
|
];
|
|
4194
4827
|
__decorateClass([
|
|
4195
|
-
(0,
|
|
4828
|
+
(0, import_decorators14.state)()
|
|
4196
4829
|
], DawRecordButtonElement.prototype, "_isRecording", 2);
|
|
4197
4830
|
DawRecordButtonElement = __decorateClass([
|
|
4198
|
-
(0,
|
|
4831
|
+
(0, import_decorators14.customElement)("daw-record-button")
|
|
4199
4832
|
], DawRecordButtonElement);
|
|
4200
4833
|
|
|
4201
4834
|
// src/elements/daw-keyboard-shortcuts.ts
|
|
4202
|
-
var
|
|
4203
|
-
var
|
|
4204
|
-
var
|
|
4205
|
-
var DawKeyboardShortcutsElement = class extends
|
|
4835
|
+
var import_lit17 = require("lit");
|
|
4836
|
+
var import_decorators15 = require("lit/decorators.js");
|
|
4837
|
+
var import_core9 = require("@waveform-playlist/core");
|
|
4838
|
+
var DawKeyboardShortcutsElement = class extends import_lit17.LitElement {
|
|
4206
4839
|
constructor() {
|
|
4207
4840
|
super(...arguments);
|
|
4208
4841
|
this.playback = false;
|
|
@@ -4221,7 +4854,7 @@ var DawKeyboardShortcutsElement = class extends import_lit16.LitElement {
|
|
|
4221
4854
|
const shortcuts = this.shortcuts;
|
|
4222
4855
|
if (shortcuts.length === 0) return;
|
|
4223
4856
|
try {
|
|
4224
|
-
(0,
|
|
4857
|
+
(0, import_core9.handleKeyboardEvent)(e, shortcuts, true);
|
|
4225
4858
|
} catch (err) {
|
|
4226
4859
|
console.warn("[dawcore] Keyboard shortcut failed (key=" + e.key + "): " + String(err));
|
|
4227
4860
|
const target = this._editor ?? this;
|
|
@@ -4356,16 +4989,16 @@ var DawKeyboardShortcutsElement = class extends import_lit16.LitElement {
|
|
|
4356
4989
|
}
|
|
4357
4990
|
};
|
|
4358
4991
|
__decorateClass([
|
|
4359
|
-
(0,
|
|
4992
|
+
(0, import_decorators15.property)({ type: Boolean })
|
|
4360
4993
|
], DawKeyboardShortcutsElement.prototype, "playback", 2);
|
|
4361
4994
|
__decorateClass([
|
|
4362
|
-
(0,
|
|
4995
|
+
(0, import_decorators15.property)({ type: Boolean })
|
|
4363
4996
|
], DawKeyboardShortcutsElement.prototype, "splitting", 2);
|
|
4364
4997
|
__decorateClass([
|
|
4365
|
-
(0,
|
|
4998
|
+
(0, import_decorators15.property)({ type: Boolean })
|
|
4366
4999
|
], DawKeyboardShortcutsElement.prototype, "undo", 2);
|
|
4367
5000
|
DawKeyboardShortcutsElement = __decorateClass([
|
|
4368
|
-
(0,
|
|
5001
|
+
(0, import_decorators15.customElement)("daw-keyboard-shortcuts")
|
|
4369
5002
|
], DawKeyboardShortcutsElement);
|
|
4370
5003
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4371
5004
|
0 && (module.exports = {
|
|
@@ -4373,6 +5006,7 @@ DawKeyboardShortcutsElement = __decorateClass([
|
|
|
4373
5006
|
ClipPointerHandler,
|
|
4374
5007
|
DawClipElement,
|
|
4375
5008
|
DawEditorElement,
|
|
5009
|
+
DawGridElement,
|
|
4376
5010
|
DawKeyboardShortcutsElement,
|
|
4377
5011
|
DawPauseButtonElement,
|
|
4378
5012
|
DawPlayButtonElement,
|