@dawcore/components 0.0.8 → 0.0.10
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 +17 -19
- package/dist/index.d.mts +129 -3
- package/dist/index.d.ts +129 -3
- package/dist/index.js +822 -185
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +805 -169
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -13
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;
|
|
@@ -3475,8 +3920,8 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3475
3920
|
syncPeaksForChangedClips(this, engineState.tracks);
|
|
3476
3921
|
}
|
|
3477
3922
|
});
|
|
3478
|
-
engine.on("
|
|
3479
|
-
this._currentTime =
|
|
3923
|
+
engine.on("pause", () => {
|
|
3924
|
+
this._currentTime = engine.getCurrentTime();
|
|
3480
3925
|
});
|
|
3481
3926
|
engine.on("stop", () => {
|
|
3482
3927
|
this._currentTime = engine.getCurrentTime();
|
|
@@ -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) {
|
|
@@ -3576,7 +4022,7 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3576
4022
|
splitAtPlayhead() {
|
|
3577
4023
|
return splitAtPlayhead({
|
|
3578
4024
|
effectiveSampleRate: this.effectiveSampleRate,
|
|
3579
|
-
currentTime: this.
|
|
4025
|
+
currentTime: this.currentTime,
|
|
3580
4026
|
isPlaying: this._isPlaying,
|
|
3581
4027
|
engine: this._engine,
|
|
3582
4028
|
dispatchEvent: (e) => this.dispatchEvent(e),
|
|
@@ -3594,6 +4040,9 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3594
4040
|
});
|
|
3595
4041
|
}
|
|
3596
4042
|
get currentTime() {
|
|
4043
|
+
if (this._isPlaying && this._engine) {
|
|
4044
|
+
return this._engine.getCurrentTime();
|
|
4045
|
+
}
|
|
3597
4046
|
return this._currentTime;
|
|
3598
4047
|
}
|
|
3599
4048
|
get isRecording() {
|
|
@@ -3624,12 +4073,13 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3624
4073
|
if (!rs) return "";
|
|
3625
4074
|
const audibleSamples = Math.max(0, rs.totalSamples - rs.latencySamples);
|
|
3626
4075
|
if (audibleSamples === 0) return "";
|
|
3627
|
-
const
|
|
3628
|
-
const
|
|
3629
|
-
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);
|
|
3630
4080
|
return rs.peaks.map((chPeaks, ch) => {
|
|
3631
4081
|
const slicedPeaks = latencyPixels > 0 ? chPeaks.slice(latencyPixels * 2) : chPeaks;
|
|
3632
|
-
return
|
|
4082
|
+
return import_lit13.html`
|
|
3633
4083
|
<daw-waveform
|
|
3634
4084
|
data-recording-track=${trackId}
|
|
3635
4085
|
data-recording-channel=${ch}
|
|
@@ -3652,19 +4102,39 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3652
4102
|
if (!playhead || !this._engine) return;
|
|
3653
4103
|
const engine = this._engine;
|
|
3654
4104
|
const ctx = this.audioContext;
|
|
3655
|
-
|
|
3656
|
-
() =>
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
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
|
+
}
|
|
3663
4125
|
}
|
|
3664
4126
|
_stopPlayhead() {
|
|
3665
4127
|
const playhead = this._getPlayhead();
|
|
3666
4128
|
if (!playhead) return;
|
|
3667
|
-
|
|
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
|
+
}
|
|
3668
4138
|
}
|
|
3669
4139
|
_getPlayhead() {
|
|
3670
4140
|
return this.shadowRoot?.querySelector("daw-playhead");
|
|
@@ -3685,8 +4155,18 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3685
4155
|
// --- Render ---
|
|
3686
4156
|
render() {
|
|
3687
4157
|
const sr = this.effectiveSampleRate;
|
|
3688
|
-
const
|
|
3689
|
-
|
|
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
|
+
}
|
|
3690
4170
|
const orderedTracks = this._getOrderedTracks().map(([trackId, track]) => {
|
|
3691
4171
|
const descriptor = this._tracks.get(trackId);
|
|
3692
4172
|
const firstPeaks = track.clips.map((c) => this._peaksData.get(c.id)).find((p) => p && p.data.length > 0);
|
|
@@ -3700,11 +4180,11 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3700
4180
|
trackHeight: this.waveHeight * numChannels + (this.clipHeaders ? this.clipHeaderHeight : 0)
|
|
3701
4181
|
};
|
|
3702
4182
|
});
|
|
3703
|
-
return
|
|
3704
|
-
${orderedTracks.length > 0 ?
|
|
3705
|
-
${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>` : ""}
|
|
3706
4186
|
${orderedTracks.map(
|
|
3707
|
-
(t) =>
|
|
4187
|
+
(t) => import_lit13.html`
|
|
3708
4188
|
<daw-track-controls
|
|
3709
4189
|
style="height: ${t.trackHeight}px;"
|
|
3710
4190
|
.trackId=${t.trackId}
|
|
@@ -3727,16 +4207,31 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3727
4207
|
@dragleave=${this._onDragLeave}
|
|
3728
4208
|
@drop=${this._onDrop}
|
|
3729
4209
|
>
|
|
3730
|
-
${orderedTracks.length > 0 && this.timescale ?
|
|
3731
|
-
.samplesPerPixel=${
|
|
4210
|
+
${(orderedTracks.length > 0 || this.scaleMode === "beats") && this.timescale ? import_lit13.html`<daw-ruler
|
|
4211
|
+
.samplesPerPixel=${spp}
|
|
3732
4212
|
.sampleRate=${this.effectiveSampleRate}
|
|
3733
4213
|
.duration=${this._duration}
|
|
4214
|
+
.scaleMode=${this.scaleMode}
|
|
4215
|
+
.ticksPerPixel=${this.ticksPerPixel}
|
|
4216
|
+
.meterEntries=${this._meterEntries}
|
|
4217
|
+
.ppqn=${this.ppqn}
|
|
4218
|
+
.totalWidth=${this._totalWidth}
|
|
3734
4219
|
></daw-ruler>` : ""}
|
|
3735
|
-
${
|
|
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>
|
|
3736
4231
|
<daw-playhead></daw-playhead>` : ""}
|
|
3737
4232
|
${orderedTracks.map((t) => {
|
|
3738
4233
|
const channelHeight = this.waveHeight;
|
|
3739
|
-
return
|
|
4234
|
+
return import_lit13.html`
|
|
3740
4235
|
<div
|
|
3741
4236
|
class="track-row ${t.trackId === this._selectedTrackId ? "selected" : ""}"
|
|
3742
4237
|
style="height: ${t.trackHeight}px;"
|
|
@@ -3744,21 +4239,67 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3744
4239
|
>
|
|
3745
4240
|
${t.track.clips.map((clip) => {
|
|
3746
4241
|
const peakData = this._peaksData.get(clip.id);
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
this.
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
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)];
|
|
3754
4295
|
const hdrH = this.clipHeaders ? this.clipHeaderHeight : 0;
|
|
3755
4296
|
const chH = this.waveHeight;
|
|
3756
|
-
return
|
|
4297
|
+
return import_lit13.html` <div
|
|
3757
4298
|
class="clip-container"
|
|
3758
4299
|
style="left:${clipLeft}px;top:0;width:${width}px;height:${t.trackHeight}px;"
|
|
3759
4300
|
data-clip-id=${clip.id}
|
|
3760
4301
|
>
|
|
3761
|
-
${hdrH > 0 ?
|
|
4302
|
+
${hdrH > 0 ? import_lit13.html`<div
|
|
3762
4303
|
class="clip-header"
|
|
3763
4304
|
data-clip-id=${clip.id}
|
|
3764
4305
|
data-track-id=${t.trackId}
|
|
@@ -3767,7 +4308,7 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3767
4308
|
<span>${clip.name || t.descriptor?.name || ""}</span>
|
|
3768
4309
|
</div>` : ""}
|
|
3769
4310
|
${channels.map(
|
|
3770
|
-
(chPeaks, chIdx) =>
|
|
4311
|
+
(chPeaks, chIdx) => import_lit13.html` <daw-waveform
|
|
3771
4312
|
style="position:absolute;left:0;top:${hdrH + chIdx * chH}px;"
|
|
3772
4313
|
.peaks=${chPeaks}
|
|
3773
4314
|
.length=${peakData?.length ?? width}
|
|
@@ -3777,9 +4318,10 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3777
4318
|
.visibleStart=${this._viewport.visibleStart}
|
|
3778
4319
|
.visibleEnd=${this._viewport.visibleEnd}
|
|
3779
4320
|
.originX=${clipLeft}
|
|
4321
|
+
.segments=${clipSegments}
|
|
3780
4322
|
></daw-waveform>`
|
|
3781
4323
|
)}
|
|
3782
|
-
${this.interactiveClips ?
|
|
4324
|
+
${this.interactiveClips ? import_lit13.html` <div
|
|
3783
4325
|
class="clip-boundary"
|
|
3784
4326
|
data-boundary-edge="left"
|
|
3785
4327
|
data-clip-id=${clip.id}
|
|
@@ -3805,7 +4347,7 @@ var DawEditorElement = class extends import_lit12.LitElement {
|
|
|
3805
4347
|
};
|
|
3806
4348
|
DawEditorElement.styles = [
|
|
3807
4349
|
hostStyles,
|
|
3808
|
-
|
|
4350
|
+
import_lit13.css`
|
|
3809
4351
|
:host {
|
|
3810
4352
|
display: flex;
|
|
3811
4353
|
position: relative;
|
|
@@ -3835,6 +4377,15 @@ DawEditorElement.styles = [
|
|
|
3835
4377
|
.track-row.selected {
|
|
3836
4378
|
background: rgba(99, 199, 95, 0.08);
|
|
3837
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
|
+
}
|
|
3838
4389
|
.timeline.drag-over {
|
|
3839
4390
|
outline: 2px dashed var(--daw-selection-color, rgba(99, 199, 95, 0.3));
|
|
3840
4391
|
outline-offset: -2px;
|
|
@@ -3844,69 +4395,96 @@ DawEditorElement.styles = [
|
|
|
3844
4395
|
];
|
|
3845
4396
|
DawEditorElement._CONTROL_PROPS = /* @__PURE__ */ new Set(["volume", "pan", "muted", "soloed"]);
|
|
3846
4397
|
__decorateClass([
|
|
3847
|
-
(0,
|
|
4398
|
+
(0, import_decorators11.property)({ type: Number, attribute: "samples-per-pixel", noAccessor: true })
|
|
3848
4399
|
], DawEditorElement.prototype, "samplesPerPixel", 1);
|
|
3849
4400
|
__decorateClass([
|
|
3850
|
-
(0,
|
|
4401
|
+
(0, import_decorators11.property)({ type: Number, attribute: "wave-height" })
|
|
3851
4402
|
], DawEditorElement.prototype, "waveHeight", 2);
|
|
3852
4403
|
__decorateClass([
|
|
3853
|
-
(0,
|
|
4404
|
+
(0, import_decorators11.property)({ type: Boolean })
|
|
3854
4405
|
], DawEditorElement.prototype, "timescale", 2);
|
|
3855
4406
|
__decorateClass([
|
|
3856
|
-
(0,
|
|
4407
|
+
(0, import_decorators11.property)({ type: Boolean })
|
|
3857
4408
|
], DawEditorElement.prototype, "mono", 2);
|
|
3858
4409
|
__decorateClass([
|
|
3859
|
-
(0,
|
|
4410
|
+
(0, import_decorators11.property)({ type: Number, attribute: "bar-width" })
|
|
3860
4411
|
], DawEditorElement.prototype, "barWidth", 2);
|
|
3861
4412
|
__decorateClass([
|
|
3862
|
-
(0,
|
|
4413
|
+
(0, import_decorators11.property)({ type: Number, attribute: "bar-gap" })
|
|
3863
4414
|
], DawEditorElement.prototype, "barGap", 2);
|
|
3864
4415
|
__decorateClass([
|
|
3865
|
-
(0,
|
|
4416
|
+
(0, import_decorators11.property)({ type: Boolean, attribute: "file-drop" })
|
|
3866
4417
|
], DawEditorElement.prototype, "fileDrop", 2);
|
|
3867
4418
|
__decorateClass([
|
|
3868
|
-
(0,
|
|
4419
|
+
(0, import_decorators11.property)({ type: Boolean, attribute: "clip-headers" })
|
|
3869
4420
|
], DawEditorElement.prototype, "clipHeaders", 2);
|
|
3870
4421
|
__decorateClass([
|
|
3871
|
-
(0,
|
|
4422
|
+
(0, import_decorators11.property)({ type: Number, attribute: "clip-header-height" })
|
|
3872
4423
|
], DawEditorElement.prototype, "clipHeaderHeight", 2);
|
|
3873
4424
|
__decorateClass([
|
|
3874
|
-
(0,
|
|
4425
|
+
(0, import_decorators11.property)({ type: Boolean, attribute: "interactive-clips" })
|
|
3875
4426
|
], DawEditorElement.prototype, "interactiveClips", 2);
|
|
3876
4427
|
__decorateClass([
|
|
3877
|
-
(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" })
|
|
3878
4456
|
], DawEditorElement.prototype, "sampleRate", 2);
|
|
3879
4457
|
__decorateClass([
|
|
3880
|
-
(0,
|
|
4458
|
+
(0, import_decorators11.state)()
|
|
3881
4459
|
], DawEditorElement.prototype, "_tracks", 2);
|
|
3882
4460
|
__decorateClass([
|
|
3883
|
-
(0,
|
|
4461
|
+
(0, import_decorators11.state)()
|
|
3884
4462
|
], DawEditorElement.prototype, "_engineTracks", 2);
|
|
3885
4463
|
__decorateClass([
|
|
3886
|
-
(0,
|
|
4464
|
+
(0, import_decorators11.state)()
|
|
3887
4465
|
], DawEditorElement.prototype, "_peaksData", 2);
|
|
3888
4466
|
__decorateClass([
|
|
3889
|
-
(0,
|
|
4467
|
+
(0, import_decorators11.state)()
|
|
3890
4468
|
], DawEditorElement.prototype, "_isPlaying", 2);
|
|
3891
4469
|
__decorateClass([
|
|
3892
|
-
(0,
|
|
4470
|
+
(0, import_decorators11.state)()
|
|
3893
4471
|
], DawEditorElement.prototype, "_duration", 2);
|
|
3894
4472
|
__decorateClass([
|
|
3895
|
-
(0,
|
|
4473
|
+
(0, import_decorators11.state)()
|
|
3896
4474
|
], DawEditorElement.prototype, "_selectedTrackId", 2);
|
|
3897
4475
|
__decorateClass([
|
|
3898
|
-
(0,
|
|
4476
|
+
(0, import_decorators11.state)()
|
|
3899
4477
|
], DawEditorElement.prototype, "_dragOver", 2);
|
|
3900
4478
|
__decorateClass([
|
|
3901
|
-
(0,
|
|
4479
|
+
(0, import_decorators11.property)({ attribute: "eager-resume" })
|
|
3902
4480
|
], DawEditorElement.prototype, "eagerResume", 2);
|
|
3903
4481
|
DawEditorElement = __decorateClass([
|
|
3904
|
-
(0,
|
|
4482
|
+
(0, import_decorators11.customElement)("daw-editor")
|
|
3905
4483
|
], DawEditorElement);
|
|
3906
4484
|
|
|
3907
4485
|
// src/elements/daw-ruler.ts
|
|
3908
|
-
var
|
|
3909
|
-
var
|
|
4486
|
+
var import_lit14 = require("lit");
|
|
4487
|
+
var import_decorators12 = require("lit/decorators.js");
|
|
3910
4488
|
|
|
3911
4489
|
// src/utils/time-format.ts
|
|
3912
4490
|
function formatTime(milliseconds) {
|
|
@@ -3957,18 +4535,36 @@ function computeTemporalTicks(samplesPerPixel, sampleRate, duration, rulerHeight
|
|
|
3957
4535
|
}
|
|
3958
4536
|
|
|
3959
4537
|
// src/elements/daw-ruler.ts
|
|
3960
|
-
var
|
|
3961
|
-
var DawRulerElement = class extends
|
|
4538
|
+
var MAX_CANVAS_WIDTH3 = 1e3;
|
|
4539
|
+
var DawRulerElement = class extends import_lit14.LitElement {
|
|
3962
4540
|
constructor() {
|
|
3963
4541
|
super(...arguments);
|
|
3964
4542
|
this.samplesPerPixel = 1024;
|
|
3965
4543
|
this.sampleRate = 48e3;
|
|
3966
4544
|
this.duration = 0;
|
|
3967
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;
|
|
3968
4553
|
this._tickData = null;
|
|
4554
|
+
this._musicalTickData = null;
|
|
3969
4555
|
}
|
|
3970
4556
|
willUpdate() {
|
|
3971
|
-
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;
|
|
3972
4568
|
this._tickData = computeTemporalTicks(
|
|
3973
4569
|
this.samplesPerPixel,
|
|
3974
4570
|
this.sampleRate,
|
|
@@ -3976,30 +4572,39 @@ var DawRulerElement = class extends import_lit13.LitElement {
|
|
|
3976
4572
|
this.rulerHeight
|
|
3977
4573
|
);
|
|
3978
4574
|
} else {
|
|
4575
|
+
this._musicalTickData = null;
|
|
3979
4576
|
this._tickData = null;
|
|
3980
4577
|
}
|
|
3981
4578
|
}
|
|
3982
4579
|
render() {
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
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);
|
|
3986
4583
|
const indices = Array.from({ length: totalChunks }, (_, i) => i);
|
|
3987
4584
|
const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
3988
|
-
|
|
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`
|
|
3989
4588
|
<div class="container" style="width: ${widthX}px; height: ${this.rulerHeight}px;">
|
|
3990
4589
|
${indices.map((i) => {
|
|
3991
|
-
const width = Math.min(
|
|
3992
|
-
return
|
|
4590
|
+
const width = Math.min(MAX_CANVAS_WIDTH3, widthX - i * MAX_CANVAS_WIDTH3);
|
|
4591
|
+
return import_lit14.html`
|
|
3993
4592
|
<canvas
|
|
3994
4593
|
data-index=${i}
|
|
3995
4594
|
width=${width * dpr}
|
|
3996
4595
|
height=${this.rulerHeight * dpr}
|
|
3997
|
-
style="left: ${i *
|
|
4596
|
+
style="left: ${i * MAX_CANVAS_WIDTH3}px; width: ${width}px; height: ${this.rulerHeight}px;"
|
|
3998
4597
|
></canvas>
|
|
3999
4598
|
`;
|
|
4000
4599
|
})}
|
|
4001
|
-
${
|
|
4002
|
-
(
|
|
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>`
|
|
4003
4608
|
)}
|
|
4004
4609
|
</div>
|
|
4005
4610
|
`;
|
|
@@ -4008,37 +4613,49 @@ var DawRulerElement = class extends import_lit13.LitElement {
|
|
|
4008
4613
|
this._drawTicks();
|
|
4009
4614
|
}
|
|
4010
4615
|
_drawTicks() {
|
|
4011
|
-
if (!this._tickData) return;
|
|
4012
4616
|
const canvases = this.shadowRoot?.querySelectorAll("canvas");
|
|
4013
4617
|
if (!canvases) return;
|
|
4014
4618
|
const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
4015
4619
|
const rulerColor = getComputedStyle(this).getPropertyValue("--daw-ruler-color").trim() || "#c49a6c";
|
|
4620
|
+
const widthX = this.scaleMode === "beats" ? this.totalWidth : this._tickData?.widthX ?? 0;
|
|
4016
4621
|
for (const canvas of canvases) {
|
|
4017
4622
|
const idx = Number(canvas.dataset.index);
|
|
4018
4623
|
const ctx = canvas.getContext("2d");
|
|
4019
4624
|
if (!ctx) continue;
|
|
4020
|
-
const canvasWidth = Math.min(
|
|
4021
|
-
|
|
4022
|
-
this._tickData.widthX - idx * MAX_CANVAS_WIDTH2
|
|
4023
|
-
);
|
|
4024
|
-
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;
|
|
4025
4627
|
ctx.resetTransform();
|
|
4026
4628
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
4027
4629
|
ctx.scale(dpr, dpr);
|
|
4028
4630
|
ctx.strokeStyle = rulerColor;
|
|
4029
4631
|
ctx.lineWidth = 1;
|
|
4030
|
-
|
|
4031
|
-
const
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
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
|
+
}
|
|
4037
4654
|
}
|
|
4038
4655
|
}
|
|
4039
4656
|
}
|
|
4040
4657
|
};
|
|
4041
|
-
DawRulerElement.styles =
|
|
4658
|
+
DawRulerElement.styles = import_lit14.css`
|
|
4042
4659
|
:host {
|
|
4043
4660
|
display: block;
|
|
4044
4661
|
position: relative;
|
|
@@ -4054,31 +4671,50 @@ DawRulerElement.styles = import_lit13.css`
|
|
|
4054
4671
|
.label {
|
|
4055
4672
|
position: absolute;
|
|
4056
4673
|
font-size: 0.7rem;
|
|
4674
|
+
line-height: 1;
|
|
4057
4675
|
white-space: nowrap;
|
|
4058
4676
|
color: var(--daw-ruler-color, #c49a6c);
|
|
4059
|
-
top:
|
|
4677
|
+
top: 1px;
|
|
4678
|
+
}
|
|
4679
|
+
.label.centered {
|
|
4680
|
+
transform: translateX(-50%);
|
|
4060
4681
|
}
|
|
4061
4682
|
`;
|
|
4062
4683
|
__decorateClass([
|
|
4063
|
-
(0,
|
|
4684
|
+
(0, import_decorators12.property)({ type: Number, attribute: false })
|
|
4064
4685
|
], DawRulerElement.prototype, "samplesPerPixel", 2);
|
|
4065
4686
|
__decorateClass([
|
|
4066
|
-
(0,
|
|
4687
|
+
(0, import_decorators12.property)({ type: Number, attribute: false })
|
|
4067
4688
|
], DawRulerElement.prototype, "sampleRate", 2);
|
|
4068
4689
|
__decorateClass([
|
|
4069
|
-
(0,
|
|
4690
|
+
(0, import_decorators12.property)({ type: Number, attribute: false })
|
|
4070
4691
|
], DawRulerElement.prototype, "duration", 2);
|
|
4071
4692
|
__decorateClass([
|
|
4072
|
-
(0,
|
|
4693
|
+
(0, import_decorators12.property)({ type: Number, attribute: false })
|
|
4073
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);
|
|
4074
4710
|
DawRulerElement = __decorateClass([
|
|
4075
|
-
(0,
|
|
4711
|
+
(0, import_decorators12.customElement)("daw-ruler")
|
|
4076
4712
|
], DawRulerElement);
|
|
4077
4713
|
|
|
4078
4714
|
// src/elements/daw-selection.ts
|
|
4079
|
-
var
|
|
4080
|
-
var
|
|
4081
|
-
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 {
|
|
4082
4718
|
constructor() {
|
|
4083
4719
|
super(...arguments);
|
|
4084
4720
|
this.startPx = 0;
|
|
@@ -4087,11 +4723,11 @@ var DawSelectionElement = class extends import_lit14.LitElement {
|
|
|
4087
4723
|
render() {
|
|
4088
4724
|
const left = Math.min(this.startPx, this.endPx);
|
|
4089
4725
|
const width = Math.abs(this.endPx - this.startPx);
|
|
4090
|
-
if (width === 0) return
|
|
4091
|
-
return
|
|
4726
|
+
if (width === 0) return import_lit15.html``;
|
|
4727
|
+
return import_lit15.html`<div style="left: ${left}px; width: ${width}px;"></div>`;
|
|
4092
4728
|
}
|
|
4093
4729
|
};
|
|
4094
|
-
DawSelectionElement.styles =
|
|
4730
|
+
DawSelectionElement.styles = import_lit15.css`
|
|
4095
4731
|
:host {
|
|
4096
4732
|
position: absolute;
|
|
4097
4733
|
top: 0;
|
|
@@ -4108,18 +4744,18 @@ DawSelectionElement.styles = import_lit14.css`
|
|
|
4108
4744
|
}
|
|
4109
4745
|
`;
|
|
4110
4746
|
__decorateClass([
|
|
4111
|
-
(0,
|
|
4747
|
+
(0, import_decorators13.property)({ type: Number, attribute: false })
|
|
4112
4748
|
], DawSelectionElement.prototype, "startPx", 2);
|
|
4113
4749
|
__decorateClass([
|
|
4114
|
-
(0,
|
|
4750
|
+
(0, import_decorators13.property)({ type: Number, attribute: false })
|
|
4115
4751
|
], DawSelectionElement.prototype, "endPx", 2);
|
|
4116
4752
|
DawSelectionElement = __decorateClass([
|
|
4117
|
-
(0,
|
|
4753
|
+
(0, import_decorators13.customElement)("daw-selection")
|
|
4118
4754
|
], DawSelectionElement);
|
|
4119
4755
|
|
|
4120
4756
|
// src/elements/daw-record-button.ts
|
|
4121
|
-
var
|
|
4122
|
-
var
|
|
4757
|
+
var import_lit16 = require("lit");
|
|
4758
|
+
var import_decorators14 = require("lit/decorators.js");
|
|
4123
4759
|
var DawRecordButtonElement = class extends DawTransportButton {
|
|
4124
4760
|
constructor() {
|
|
4125
4761
|
super(...arguments);
|
|
@@ -4160,7 +4796,7 @@ var DawRecordButtonElement = class extends DawTransportButton {
|
|
|
4160
4796
|
}
|
|
4161
4797
|
}
|
|
4162
4798
|
render() {
|
|
4163
|
-
return
|
|
4799
|
+
return import_lit16.html`
|
|
4164
4800
|
<button part="button" ?data-recording=${this._isRecording} @click=${this._onClick}>
|
|
4165
4801
|
<slot>Record</slot>
|
|
4166
4802
|
</button>
|
|
@@ -4180,7 +4816,7 @@ var DawRecordButtonElement = class extends DawTransportButton {
|
|
|
4180
4816
|
};
|
|
4181
4817
|
DawRecordButtonElement.styles = [
|
|
4182
4818
|
DawTransportButton.styles,
|
|
4183
|
-
|
|
4819
|
+
import_lit16.css`
|
|
4184
4820
|
button[data-recording] {
|
|
4185
4821
|
color: #d08070;
|
|
4186
4822
|
border-color: #d08070;
|
|
@@ -4189,17 +4825,17 @@ DawRecordButtonElement.styles = [
|
|
|
4189
4825
|
`
|
|
4190
4826
|
];
|
|
4191
4827
|
__decorateClass([
|
|
4192
|
-
(0,
|
|
4828
|
+
(0, import_decorators14.state)()
|
|
4193
4829
|
], DawRecordButtonElement.prototype, "_isRecording", 2);
|
|
4194
4830
|
DawRecordButtonElement = __decorateClass([
|
|
4195
|
-
(0,
|
|
4831
|
+
(0, import_decorators14.customElement)("daw-record-button")
|
|
4196
4832
|
], DawRecordButtonElement);
|
|
4197
4833
|
|
|
4198
4834
|
// src/elements/daw-keyboard-shortcuts.ts
|
|
4199
|
-
var
|
|
4200
|
-
var
|
|
4201
|
-
var
|
|
4202
|
-
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 {
|
|
4203
4839
|
constructor() {
|
|
4204
4840
|
super(...arguments);
|
|
4205
4841
|
this.playback = false;
|
|
@@ -4218,7 +4854,7 @@ var DawKeyboardShortcutsElement = class extends import_lit16.LitElement {
|
|
|
4218
4854
|
const shortcuts = this.shortcuts;
|
|
4219
4855
|
if (shortcuts.length === 0) return;
|
|
4220
4856
|
try {
|
|
4221
|
-
(0,
|
|
4857
|
+
(0, import_core9.handleKeyboardEvent)(e, shortcuts, true);
|
|
4222
4858
|
} catch (err) {
|
|
4223
4859
|
console.warn("[dawcore] Keyboard shortcut failed (key=" + e.key + "): " + String(err));
|
|
4224
4860
|
const target = this._editor ?? this;
|
|
@@ -4353,16 +4989,16 @@ var DawKeyboardShortcutsElement = class extends import_lit16.LitElement {
|
|
|
4353
4989
|
}
|
|
4354
4990
|
};
|
|
4355
4991
|
__decorateClass([
|
|
4356
|
-
(0,
|
|
4992
|
+
(0, import_decorators15.property)({ type: Boolean })
|
|
4357
4993
|
], DawKeyboardShortcutsElement.prototype, "playback", 2);
|
|
4358
4994
|
__decorateClass([
|
|
4359
|
-
(0,
|
|
4995
|
+
(0, import_decorators15.property)({ type: Boolean })
|
|
4360
4996
|
], DawKeyboardShortcutsElement.prototype, "splitting", 2);
|
|
4361
4997
|
__decorateClass([
|
|
4362
|
-
(0,
|
|
4998
|
+
(0, import_decorators15.property)({ type: Boolean })
|
|
4363
4999
|
], DawKeyboardShortcutsElement.prototype, "undo", 2);
|
|
4364
5000
|
DawKeyboardShortcutsElement = __decorateClass([
|
|
4365
|
-
(0,
|
|
5001
|
+
(0, import_decorators15.customElement)("daw-keyboard-shortcuts")
|
|
4366
5002
|
], DawKeyboardShortcutsElement);
|
|
4367
5003
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4368
5004
|
0 && (module.exports = {
|
|
@@ -4370,6 +5006,7 @@ DawKeyboardShortcutsElement = __decorateClass([
|
|
|
4370
5006
|
ClipPointerHandler,
|
|
4371
5007
|
DawClipElement,
|
|
4372
5008
|
DawEditorElement,
|
|
5009
|
+
DawGridElement,
|
|
4373
5010
|
DawKeyboardShortcutsElement,
|
|
4374
5011
|
DawPauseButtonElement,
|
|
4375
5012
|
DawPlayButtonElement,
|