@dawcore/components 0.0.18 → 0.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +171 -114
- package/dist/index.d.ts +171 -114
- package/dist/index.js +201 -42
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +181 -19
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -5
package/dist/index.mjs
CHANGED
|
@@ -174,7 +174,7 @@ var DawTrackElement = class extends LitElement2 {
|
|
|
174
174
|
this.pan = 0;
|
|
175
175
|
this.muted = false;
|
|
176
176
|
this.soloed = false;
|
|
177
|
-
this.
|
|
177
|
+
this._renderMode = "waveform";
|
|
178
178
|
this.spectrogramConfig = null;
|
|
179
179
|
this.trackId = crypto.randomUUID();
|
|
180
180
|
// Track removal is detected by the editor's MutationObserver,
|
|
@@ -182,6 +182,21 @@ var DawTrackElement = class extends LitElement2 {
|
|
|
182
182
|
// cannot bubble events to ancestors).
|
|
183
183
|
this._hasRendered = false;
|
|
184
184
|
}
|
|
185
|
+
get renderMode() {
|
|
186
|
+
return this._renderMode;
|
|
187
|
+
}
|
|
188
|
+
set renderMode(value) {
|
|
189
|
+
const old = this._renderMode;
|
|
190
|
+
let next = value;
|
|
191
|
+
if (next === "both") {
|
|
192
|
+
console.warn(
|
|
193
|
+
`[dawcore] <daw-track render-mode="both"> is not yet supported; falling back to 'spectrogram'`
|
|
194
|
+
);
|
|
195
|
+
next = "spectrogram";
|
|
196
|
+
}
|
|
197
|
+
this._renderMode = next;
|
|
198
|
+
this.requestUpdate("renderMode", old);
|
|
199
|
+
}
|
|
185
200
|
// Light DOM so <daw-clip> children are queryable.
|
|
186
201
|
createRenderRoot() {
|
|
187
202
|
return this;
|
|
@@ -244,8 +259,8 @@ __decorateClass([
|
|
|
244
259
|
property2({ type: Boolean })
|
|
245
260
|
], DawTrackElement.prototype, "soloed", 2);
|
|
246
261
|
__decorateClass([
|
|
247
|
-
property2({ attribute: "render-mode" })
|
|
248
|
-
], DawTrackElement.prototype, "renderMode",
|
|
262
|
+
property2({ attribute: "render-mode", noAccessor: true })
|
|
263
|
+
], DawTrackElement.prototype, "renderMode", 1);
|
|
249
264
|
__decorateClass([
|
|
250
265
|
property2({ attribute: false })
|
|
251
266
|
], DawTrackElement.prototype, "spectrogramConfig", 2);
|
|
@@ -2725,15 +2740,10 @@ var RecordingController = class {
|
|
|
2725
2740
|
import {
|
|
2726
2741
|
SpectrogramOrchestrator
|
|
2727
2742
|
} from "@dawcore/spectrogram";
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
minFrequency: 0,
|
|
2733
|
-
gainDb: 20,
|
|
2734
|
-
rangeDb: 80
|
|
2735
|
-
};
|
|
2736
|
-
var LIBRARY_DEFAULT_COLOR_MAP = "viridis";
|
|
2743
|
+
import {
|
|
2744
|
+
SPECTROGRAM_DEFAULTS as LIBRARY_DEFAULTS,
|
|
2745
|
+
DEFAULT_SPECTROGRAM_COLOR_MAP as LIBRARY_DEFAULT_COLOR_MAP
|
|
2746
|
+
} from "@waveform-playlist/core";
|
|
2737
2747
|
var SpectrogramController = class {
|
|
2738
2748
|
constructor(host, workerFactory) {
|
|
2739
2749
|
this.orchestrator = null;
|
|
@@ -2807,7 +2817,21 @@ var SpectrogramController = class {
|
|
|
2807
2817
|
const detail = e.detail;
|
|
2808
2818
|
this.host.dispatchEvent(
|
|
2809
2819
|
new CustomEvent("daw-spectrogram-ready", {
|
|
2810
|
-
detail,
|
|
2820
|
+
detail: { trackId: detail.trackId, generation: detail.generation },
|
|
2821
|
+
bubbles: true,
|
|
2822
|
+
composed: true
|
|
2823
|
+
})
|
|
2824
|
+
);
|
|
2825
|
+
});
|
|
2826
|
+
this.orchestrator.addEventListener("viewport-error", (e) => {
|
|
2827
|
+
const detail = e.detail;
|
|
2828
|
+
this.host.dispatchEvent(
|
|
2829
|
+
new CustomEvent("daw-spectrogram-error", {
|
|
2830
|
+
detail: {
|
|
2831
|
+
trackId: detail.trackId,
|
|
2832
|
+
generation: detail.generation,
|
|
2833
|
+
error: detail.error
|
|
2834
|
+
},
|
|
2811
2835
|
bubbles: true,
|
|
2812
2836
|
composed: true
|
|
2813
2837
|
})
|
|
@@ -3446,6 +3470,108 @@ async function loadFiles(host, files) {
|
|
|
3446
3470
|
return { loaded, failed };
|
|
3447
3471
|
}
|
|
3448
3472
|
|
|
3473
|
+
// src/interactions/midi-loader.ts
|
|
3474
|
+
var INSTALL_HINT = "@dawcore/midi is required for loadMidi(). Install with: npm install @dawcore/midi";
|
|
3475
|
+
async function loadMidiImpl(host, source, options = {}) {
|
|
3476
|
+
const startTime = options.startTime ?? 0;
|
|
3477
|
+
if (!Number.isFinite(startTime) || startTime < 0) {
|
|
3478
|
+
throw new RangeError(
|
|
3479
|
+
"loadMidi: startTime must be a non-negative finite number (got " + String(options.startTime) + ")"
|
|
3480
|
+
);
|
|
3481
|
+
}
|
|
3482
|
+
let midiModule;
|
|
3483
|
+
try {
|
|
3484
|
+
midiModule = await import("@dawcore/midi");
|
|
3485
|
+
} catch (originalErr) {
|
|
3486
|
+
console.warn("[dawcore] @dawcore/midi dynamic import failed: " + String(originalErr));
|
|
3487
|
+
throw new Error(INSTALL_HINT);
|
|
3488
|
+
}
|
|
3489
|
+
const { parseMidiUrl, parseMidiFile } = midiModule;
|
|
3490
|
+
let parsed;
|
|
3491
|
+
if (typeof source === "string") {
|
|
3492
|
+
parsed = await parseMidiUrl(source, void 0, options.signal);
|
|
3493
|
+
} else {
|
|
3494
|
+
let buffer;
|
|
3495
|
+
try {
|
|
3496
|
+
buffer = await source.arrayBuffer();
|
|
3497
|
+
} catch (err) {
|
|
3498
|
+
throw new Error(
|
|
3499
|
+
'loadMidi: failed to read File "' + source.name + '" (' + source.size + " bytes): " + String(err)
|
|
3500
|
+
);
|
|
3501
|
+
}
|
|
3502
|
+
parsed = parseMidiFile(buffer);
|
|
3503
|
+
}
|
|
3504
|
+
const childrenBefore = new Set(host.querySelectorAll("daw-track"));
|
|
3505
|
+
const settlements = await Promise.allSettled(
|
|
3506
|
+
parsed.tracks.map(
|
|
3507
|
+
(t) => host.addTrack({
|
|
3508
|
+
name: t.name,
|
|
3509
|
+
renderMode: "piano-roll",
|
|
3510
|
+
clips: [
|
|
3511
|
+
{
|
|
3512
|
+
midiNotes: t.notes,
|
|
3513
|
+
midiChannel: t.channel,
|
|
3514
|
+
midiProgram: t.programNumber,
|
|
3515
|
+
start: startTime
|
|
3516
|
+
}
|
|
3517
|
+
]
|
|
3518
|
+
})
|
|
3519
|
+
)
|
|
3520
|
+
);
|
|
3521
|
+
const succeeded = [];
|
|
3522
|
+
const rejections = [];
|
|
3523
|
+
for (const s of settlements) {
|
|
3524
|
+
if (s.status === "fulfilled") {
|
|
3525
|
+
succeeded.push(s.value);
|
|
3526
|
+
} else {
|
|
3527
|
+
rejections.push(s.reason);
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
if (rejections.length > 0) {
|
|
3531
|
+
const appendedTracks = Array.from(host.querySelectorAll("daw-track")).filter(
|
|
3532
|
+
(el) => !childrenBefore.has(el)
|
|
3533
|
+
);
|
|
3534
|
+
for (const el of appendedTracks) {
|
|
3535
|
+
try {
|
|
3536
|
+
el.remove();
|
|
3537
|
+
} catch (cleanupErr) {
|
|
3538
|
+
console.warn("[dawcore] loadMidi cleanup failed for a track: " + String(cleanupErr));
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
await Promise.resolve();
|
|
3542
|
+
for (let i = 1; i < rejections.length; i++) {
|
|
3543
|
+
console.warn(
|
|
3544
|
+
"[dawcore] loadMidi: additional track failure (" + i + "): " + stringifyReason(rejections[i])
|
|
3545
|
+
);
|
|
3546
|
+
}
|
|
3547
|
+
const first = rejections[0];
|
|
3548
|
+
if (rejections.length > 1) {
|
|
3549
|
+
const message = "loadMidi: " + rejections.length + " of " + settlements.length + " tracks failed; first: " + (first instanceof Error ? first.message : stringifyReason(first));
|
|
3550
|
+
throw new Error(message);
|
|
3551
|
+
}
|
|
3552
|
+
throw first instanceof Error ? first : new Error(stringifyReason(first));
|
|
3553
|
+
}
|
|
3554
|
+
return {
|
|
3555
|
+
trackIds: succeeded.map((el) => el.trackId),
|
|
3556
|
+
bpm: parsed.bpm,
|
|
3557
|
+
timeSignature: parsed.timeSignature,
|
|
3558
|
+
duration: parsed.duration,
|
|
3559
|
+
name: parsed.name
|
|
3560
|
+
};
|
|
3561
|
+
}
|
|
3562
|
+
function stringifyReason(reason) {
|
|
3563
|
+
if (reason === null) return "null";
|
|
3564
|
+
if (reason === void 0) return "undefined";
|
|
3565
|
+
if (typeof reason === "object") {
|
|
3566
|
+
try {
|
|
3567
|
+
return JSON.stringify(reason);
|
|
3568
|
+
} catch {
|
|
3569
|
+
return Object.prototype.toString.call(reason);
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
return String(reason);
|
|
3573
|
+
}
|
|
3574
|
+
|
|
3449
3575
|
// src/interactions/recording-clip.ts
|
|
3450
3576
|
import { createClip as createClip2 } from "@waveform-playlist/core";
|
|
3451
3577
|
function addRecordedClip(host, trackId, buf, startSample, durSamples, offsetSamples = 0) {
|
|
@@ -3784,7 +3910,10 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
3784
3910
|
* `_selectedTrackId`, etc.) — most of which don't affect the spectrogram
|
|
3785
3911
|
* viewport. Skip the cross-controller call when nothing changed.
|
|
3786
3912
|
*
|
|
3787
|
-
* The orchestrator dedupes too,
|
|
3913
|
+
* The orchestrator dedupes identical viewports too, so removing this cache
|
|
3914
|
+
* wouldn't change observable behavior — but it would push a fresh
|
|
3915
|
+
* `setViewport` call (with object allocation) into every Lit reactive
|
|
3916
|
+
* update for properties unrelated to the viewport.
|
|
3788
3917
|
*/
|
|
3789
3918
|
this._lastSpectrogramViewport = null;
|
|
3790
3919
|
// --- Track Events ---
|
|
@@ -4027,9 +4156,10 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
4027
4156
|
this._spectrogramController?.unregisterCanvas(canvasId);
|
|
4028
4157
|
}
|
|
4029
4158
|
/**
|
|
4030
|
-
*
|
|
4031
|
-
*
|
|
4032
|
-
*
|
|
4159
|
+
* Forward a clip's AudioBuffer to the spectrogram controller if the parent
|
|
4160
|
+
* track is in spectrogram render-mode. Eagerly creates the controller via
|
|
4161
|
+
* `_ensureSpectrogramController` so the audio data is queued for the first
|
|
4162
|
+
* render — even if no canvases have been registered yet.
|
|
4033
4163
|
*/
|
|
4034
4164
|
_maybeRegisterSpectrogramClipAudio(trackId, clip) {
|
|
4035
4165
|
const descriptor = this._tracks.get(trackId);
|
|
@@ -5017,6 +5147,22 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
5017
5147
|
async loadFiles(files) {
|
|
5018
5148
|
return loadFiles(this, files);
|
|
5019
5149
|
}
|
|
5150
|
+
/**
|
|
5151
|
+
* Imperatively load a `.mid` file (URL or File) and create N `<daw-track>`
|
|
5152
|
+
* elements — one per note-bearing MIDI track. On any per-track failure,
|
|
5153
|
+
* every `<daw-track>` appended during the call is removed (both successful
|
|
5154
|
+
* and failed) so the editor returns to its pre-call state.
|
|
5155
|
+
*
|
|
5156
|
+
* `options.signal` is forwarded to `fetch()` only for URL sources; aborting
|
|
5157
|
+
* after parsing does not cancel in-flight `addTrack` calls.
|
|
5158
|
+
*
|
|
5159
|
+
* Requires the optional `@dawcore/midi` peer dep — throws with an install
|
|
5160
|
+
* hint (and `console.warn`s the original error) when the dynamic import
|
|
5161
|
+
* fails for any reason.
|
|
5162
|
+
*/
|
|
5163
|
+
async loadMidi(source, options) {
|
|
5164
|
+
return loadMidiImpl(this, source, options);
|
|
5165
|
+
}
|
|
5020
5166
|
// --- Programmatic Track API ---
|
|
5021
5167
|
/**
|
|
5022
5168
|
* Build the engine if it hasn't been built yet. Lets consumers obtain a
|
|
@@ -5130,6 +5276,13 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
5130
5276
|
}
|
|
5131
5277
|
const oldDesc = this._tracks.get(trackId);
|
|
5132
5278
|
if (!oldDesc) return;
|
|
5279
|
+
let normalizedRenderMode = partial.renderMode;
|
|
5280
|
+
if (normalizedRenderMode === "both") {
|
|
5281
|
+
console.warn(
|
|
5282
|
+
`[dawcore] render-mode="both" is not yet supported; falling back to 'spectrogram'`
|
|
5283
|
+
);
|
|
5284
|
+
normalizedRenderMode = "spectrogram";
|
|
5285
|
+
}
|
|
5133
5286
|
const newDesc = {
|
|
5134
5287
|
...oldDesc,
|
|
5135
5288
|
...partial.name !== void 0 && { name: partial.name },
|
|
@@ -5137,7 +5290,7 @@ var DawEditorElement = class extends LitElement10 {
|
|
|
5137
5290
|
...partial.pan !== void 0 && { pan: partial.pan },
|
|
5138
5291
|
...partial.muted !== void 0 && { muted: partial.muted },
|
|
5139
5292
|
...partial.soloed !== void 0 && { soloed: partial.soloed },
|
|
5140
|
-
...
|
|
5293
|
+
...normalizedRenderMode !== void 0 && { renderMode: normalizedRenderMode }
|
|
5141
5294
|
};
|
|
5142
5295
|
this._tracks = new Map(this._tracks).set(trackId, newDesc);
|
|
5143
5296
|
if (this._engine) {
|
|
@@ -6434,6 +6587,7 @@ var DawSpectrogramElement = class extends LitElement14 {
|
|
|
6434
6587
|
this.originX = 0;
|
|
6435
6588
|
this._canvases = [];
|
|
6436
6589
|
this._registeredCanvasIds = [];
|
|
6590
|
+
this._warnedNoHost = false;
|
|
6437
6591
|
}
|
|
6438
6592
|
get samplesPerPixel() {
|
|
6439
6593
|
return this._samplesPerPixel;
|
|
@@ -6500,7 +6654,15 @@ var DawSpectrogramElement = class extends LitElement14 {
|
|
|
6500
6654
|
}
|
|
6501
6655
|
_registerCanvases() {
|
|
6502
6656
|
const editor = this._findHostEditor();
|
|
6503
|
-
if (!editor || typeof editor._spectrogramRegisterCanvas !== "function")
|
|
6657
|
+
if (!editor || typeof editor._spectrogramRegisterCanvas !== "function") {
|
|
6658
|
+
if (!this._warnedNoHost) {
|
|
6659
|
+
this._warnedNoHost = true;
|
|
6660
|
+
console.warn(
|
|
6661
|
+
"[dawcore] <daw-spectrogram> (clip " + this.clipId + ") could not find host <daw-editor>. Canvases will not render. Ensure the element is mounted inside a <daw-editor>."
|
|
6662
|
+
);
|
|
6663
|
+
}
|
|
6664
|
+
return;
|
|
6665
|
+
}
|
|
6504
6666
|
for (let i = 0; i < this._canvases.length; i++) {
|
|
6505
6667
|
const canvas = this._canvases[i];
|
|
6506
6668
|
const canvasId = this.clipId + "-ch" + this.channelIndex + "-chunk" + i;
|