@dawcore/components 0.0.3 → 0.0.4

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 CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as lit from 'lit';
2
2
  import { LitElement, PropertyValues, ReactiveController, ReactiveControllerHost } from 'lit';
3
3
  import { Peaks, Bits, FadeType, PeakData, ClipTrack, KeyboardShortcut } from '@waveform-playlist/core';
4
+ import WaveformData from 'waveform-data';
4
5
  import { PlaylistEngine } from '@waveform-playlist/engine';
5
6
 
6
7
  declare class DawClipElement extends LitElement {
@@ -184,6 +185,7 @@ interface TrackDescriptor {
184
185
  }
185
186
  interface ClipDescriptor {
186
187
  src: string;
188
+ peaksSrc: string;
187
189
  start: number;
188
190
  duration: number;
189
191
  offset: number;
@@ -213,6 +215,11 @@ declare class PeakPipeline {
213
215
  private _baseScale;
214
216
  private _bits;
215
217
  constructor(baseScale?: number, bits?: 8 | 16);
218
+ /**
219
+ * Inject externally-loaded WaveformData (e.g., from a .dat file) into the cache.
220
+ * Prevents worker generation for this AudioBuffer on all subsequent calls.
221
+ */
222
+ cacheWaveformData(audioBuffer: AudioBuffer, waveformData: WaveformData): void;
216
223
  /**
217
224
  * Generate PeakData for a clip from its AudioBuffer.
218
225
  * Uses cached WaveformData when available; otherwise generates via worker.
@@ -221,14 +228,26 @@ declare class PeakPipeline {
221
228
  generatePeaks(audioBuffer: AudioBuffer, samplesPerPixel: number, isMono: boolean, offsetSamples?: number, durationSamples?: number): Promise<PeakData>;
222
229
  /**
223
230
  * Re-extract peaks for all clips at a new zoom level using cached WaveformData.
224
- * Only works for zoom levels coarser than (or equal to) the cached base scale.
225
- * Returns a new Map of clipId PeakData. Clips without cached data or where
226
- * the target scale is finer than the cached base are skipped.
231
+ * Returns a new Map of clipId PeakData. Clips without cached data are skipped.
232
+ * When the requested scale is finer than cached data, peaks are clamped to the
233
+ * cached scale and a single summary warning is logged.
227
234
  */
228
235
  reextractPeaks(clipBuffers: ReadonlyMap<string, AudioBuffer>, samplesPerPixel: number, isMono: boolean, clipOffsets?: ReadonlyMap<string, {
229
236
  offsetSamples: number;
230
237
  durationSamples: number;
231
238
  }>): Map<string, PeakData>;
239
+ /**
240
+ * Clamp requested scale to cached WaveformData scale.
241
+ * WaveformData.resample() can only go coarser — if the requested zoom is
242
+ * finer than the cached data, use the cached scale. Set warn=true to log
243
+ * (default); reextractPeaks passes false and logs a single summary instead.
244
+ */
245
+ private _clampScale;
246
+ /**
247
+ * Return the coarsest (largest) scale among cached WaveformData entries
248
+ * that correspond to the given clip buffers. Returns 0 if none are cached.
249
+ */
250
+ getMaxCachedScale(clipBuffers: ReadonlyMap<string, AudioBuffer>): number;
232
251
  terminate(): void;
233
252
  private _getWaveformData;
234
253
  }
@@ -516,7 +535,9 @@ interface LoadFilesResult {
516
535
  }
517
536
 
518
537
  declare class DawEditorElement extends LitElement {
519
- samplesPerPixel: number;
538
+ get samplesPerPixel(): number;
539
+ set samplesPerPixel(value: number);
540
+ private _samplesPerPixel;
520
541
  waveHeight: number;
521
542
  timescale: boolean;
522
543
  mono: boolean;
@@ -543,12 +564,15 @@ declare class DawEditorElement extends LitElement {
543
564
  _engine: PlaylistEngine | null;
544
565
  private _enginePromise;
545
566
  _audioCache: Map<string, Promise<AudioBuffer>>;
567
+ private _peaksCache;
546
568
  _clipBuffers: Map<string, AudioBuffer>;
547
569
  _clipOffsets: Map<string, {
548
570
  offsetSamples: number;
549
571
  durationSamples: number;
550
572
  }>;
551
573
  _peakPipeline: PeakPipeline;
574
+ /** Coarsest scale from pre-computed peaks — zoom cannot go finer than this. 0 = no limit. */
575
+ private _minSamplesPerPixel;
552
576
  private _trackElements;
553
577
  private _childObserver;
554
578
  private _audioResume;
@@ -588,6 +612,7 @@ declare class DawEditorElement extends LitElement {
588
612
  private _readTrackDescriptor;
589
613
  private _loadTrack;
590
614
  _fetchAndDecode(src: string): Promise<AudioBuffer>;
615
+ private _fetchPeaks;
591
616
  _recomputeDuration(): void;
592
617
  _ensureEngine(): Promise<PlaylistEngine>;
593
618
  private _buildEngine;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as lit from 'lit';
2
2
  import { LitElement, PropertyValues, ReactiveController, ReactiveControllerHost } from 'lit';
3
3
  import { Peaks, Bits, FadeType, PeakData, ClipTrack, KeyboardShortcut } from '@waveform-playlist/core';
4
+ import WaveformData from 'waveform-data';
4
5
  import { PlaylistEngine } from '@waveform-playlist/engine';
5
6
 
6
7
  declare class DawClipElement extends LitElement {
@@ -184,6 +185,7 @@ interface TrackDescriptor {
184
185
  }
185
186
  interface ClipDescriptor {
186
187
  src: string;
188
+ peaksSrc: string;
187
189
  start: number;
188
190
  duration: number;
189
191
  offset: number;
@@ -213,6 +215,11 @@ declare class PeakPipeline {
213
215
  private _baseScale;
214
216
  private _bits;
215
217
  constructor(baseScale?: number, bits?: 8 | 16);
218
+ /**
219
+ * Inject externally-loaded WaveformData (e.g., from a .dat file) into the cache.
220
+ * Prevents worker generation for this AudioBuffer on all subsequent calls.
221
+ */
222
+ cacheWaveformData(audioBuffer: AudioBuffer, waveformData: WaveformData): void;
216
223
  /**
217
224
  * Generate PeakData for a clip from its AudioBuffer.
218
225
  * Uses cached WaveformData when available; otherwise generates via worker.
@@ -221,14 +228,26 @@ declare class PeakPipeline {
221
228
  generatePeaks(audioBuffer: AudioBuffer, samplesPerPixel: number, isMono: boolean, offsetSamples?: number, durationSamples?: number): Promise<PeakData>;
222
229
  /**
223
230
  * Re-extract peaks for all clips at a new zoom level using cached WaveformData.
224
- * Only works for zoom levels coarser than (or equal to) the cached base scale.
225
- * Returns a new Map of clipId PeakData. Clips without cached data or where
226
- * the target scale is finer than the cached base are skipped.
231
+ * Returns a new Map of clipId PeakData. Clips without cached data are skipped.
232
+ * When the requested scale is finer than cached data, peaks are clamped to the
233
+ * cached scale and a single summary warning is logged.
227
234
  */
228
235
  reextractPeaks(clipBuffers: ReadonlyMap<string, AudioBuffer>, samplesPerPixel: number, isMono: boolean, clipOffsets?: ReadonlyMap<string, {
229
236
  offsetSamples: number;
230
237
  durationSamples: number;
231
238
  }>): Map<string, PeakData>;
239
+ /**
240
+ * Clamp requested scale to cached WaveformData scale.
241
+ * WaveformData.resample() can only go coarser — if the requested zoom is
242
+ * finer than the cached data, use the cached scale. Set warn=true to log
243
+ * (default); reextractPeaks passes false and logs a single summary instead.
244
+ */
245
+ private _clampScale;
246
+ /**
247
+ * Return the coarsest (largest) scale among cached WaveformData entries
248
+ * that correspond to the given clip buffers. Returns 0 if none are cached.
249
+ */
250
+ getMaxCachedScale(clipBuffers: ReadonlyMap<string, AudioBuffer>): number;
232
251
  terminate(): void;
233
252
  private _getWaveformData;
234
253
  }
@@ -516,7 +535,9 @@ interface LoadFilesResult {
516
535
  }
517
536
 
518
537
  declare class DawEditorElement extends LitElement {
519
- samplesPerPixel: number;
538
+ get samplesPerPixel(): number;
539
+ set samplesPerPixel(value: number);
540
+ private _samplesPerPixel;
520
541
  waveHeight: number;
521
542
  timescale: boolean;
522
543
  mono: boolean;
@@ -543,12 +564,15 @@ declare class DawEditorElement extends LitElement {
543
564
  _engine: PlaylistEngine | null;
544
565
  private _enginePromise;
545
566
  _audioCache: Map<string, Promise<AudioBuffer>>;
567
+ private _peaksCache;
546
568
  _clipBuffers: Map<string, AudioBuffer>;
547
569
  _clipOffsets: Map<string, {
548
570
  offsetSamples: number;
549
571
  durationSamples: number;
550
572
  }>;
551
573
  _peakPipeline: PeakPipeline;
574
+ /** Coarsest scale from pre-computed peaks — zoom cannot go finer than this. 0 = no limit. */
575
+ private _minSamplesPerPixel;
552
576
  private _trackElements;
553
577
  private _childObserver;
554
578
  private _audioResume;
@@ -588,6 +612,7 @@ declare class DawEditorElement extends LitElement {
588
612
  private _readTrackDescriptor;
589
613
  private _loadTrack;
590
614
  _fetchAndDecode(src: string): Promise<AudioBuffer>;
615
+ private _fetchPeaks;
591
616
  _recomputeDuration(): void;
592
617
  _ensureEngine(): Promise<PlaylistEngine>;
593
618
  private _buildEngine;
package/dist/index.js CHANGED
@@ -1134,6 +1134,13 @@ var PeakPipeline = class {
1134
1134
  this._baseScale = baseScale;
1135
1135
  this._bits = bits;
1136
1136
  }
1137
+ /**
1138
+ * Inject externally-loaded WaveformData (e.g., from a .dat file) into the cache.
1139
+ * Prevents worker generation for this AudioBuffer on all subsequent calls.
1140
+ */
1141
+ cacheWaveformData(audioBuffer, waveformData) {
1142
+ this._cache.set(audioBuffer, waveformData);
1143
+ }
1137
1144
  /**
1138
1145
  * Generate PeakData for a clip from its AudioBuffer.
1139
1146
  * Uses cached WaveformData when available; otherwise generates via worker.
@@ -1141,8 +1148,9 @@ var PeakPipeline = class {
1141
1148
  */
1142
1149
  async generatePeaks(audioBuffer, samplesPerPixel, isMono, offsetSamples, durationSamples) {
1143
1150
  const waveformData = await this._getWaveformData(audioBuffer);
1151
+ const effectiveScale = this._clampScale(waveformData, samplesPerPixel);
1144
1152
  try {
1145
- return extractPeaks(waveformData, samplesPerPixel, isMono, offsetSamples, durationSamples);
1153
+ return extractPeaks(waveformData, effectiveScale, isMono, offsetSamples, durationSamples);
1146
1154
  } catch (err) {
1147
1155
  console.warn("[dawcore] extractPeaks failed: " + String(err));
1148
1156
  throw err;
@@ -1150,23 +1158,29 @@ var PeakPipeline = class {
1150
1158
  }
1151
1159
  /**
1152
1160
  * Re-extract peaks for all clips at a new zoom level using cached WaveformData.
1153
- * Only works for zoom levels coarser than (or equal to) the cached base scale.
1154
- * Returns a new Map of clipId PeakData. Clips without cached data or where
1155
- * the target scale is finer than the cached base are skipped.
1161
+ * Returns a new Map of clipId PeakData. Clips without cached data are skipped.
1162
+ * When the requested scale is finer than cached data, peaks are clamped to the
1163
+ * cached scale and a single summary warning is logged.
1156
1164
  */
1157
1165
  reextractPeaks(clipBuffers, samplesPerPixel, isMono, clipOffsets) {
1158
1166
  const result = /* @__PURE__ */ new Map();
1167
+ let clampedCount = 0;
1168
+ let clampedScale = 0;
1159
1169
  for (const [clipId, audioBuffer] of clipBuffers) {
1160
1170
  const cached = this._cache.get(audioBuffer);
1161
1171
  if (cached) {
1162
- if (samplesPerPixel < cached.scale) continue;
1172
+ const effectiveScale = this._clampScale(cached, samplesPerPixel, false);
1173
+ if (effectiveScale !== samplesPerPixel) {
1174
+ clampedCount++;
1175
+ clampedScale = effectiveScale;
1176
+ }
1163
1177
  try {
1164
1178
  const offsets = clipOffsets?.get(clipId);
1165
1179
  result.set(
1166
1180
  clipId,
1167
1181
  extractPeaks(
1168
1182
  cached,
1169
- samplesPerPixel,
1183
+ effectiveScale,
1170
1184
  isMono,
1171
1185
  offsets?.offsetSamples,
1172
1186
  offsets?.durationSamples
@@ -1177,8 +1191,42 @@ var PeakPipeline = class {
1177
1191
  }
1178
1192
  }
1179
1193
  }
1194
+ if (clampedCount > 0) {
1195
+ console.warn(
1196
+ "[dawcore] Requested zoom " + samplesPerPixel + " spp is finer than pre-computed peaks (" + clampedScale + " spp) \u2014 " + clampedCount + " clip(s) using available resolution"
1197
+ );
1198
+ }
1180
1199
  return result;
1181
1200
  }
1201
+ /**
1202
+ * Clamp requested scale to cached WaveformData scale.
1203
+ * WaveformData.resample() can only go coarser — if the requested zoom is
1204
+ * finer than the cached data, use the cached scale. Set warn=true to log
1205
+ * (default); reextractPeaks passes false and logs a single summary instead.
1206
+ */
1207
+ _clampScale(waveformData, requestedScale, warn = true) {
1208
+ if (requestedScale < waveformData.scale) {
1209
+ if (warn) {
1210
+ console.warn(
1211
+ "[dawcore] Requested zoom " + requestedScale + " spp is finer than pre-computed peaks (" + waveformData.scale + " spp) \u2014 using available resolution"
1212
+ );
1213
+ }
1214
+ return waveformData.scale;
1215
+ }
1216
+ return requestedScale;
1217
+ }
1218
+ /**
1219
+ * Return the coarsest (largest) scale among cached WaveformData entries
1220
+ * that correspond to the given clip buffers. Returns 0 if none are cached.
1221
+ */
1222
+ getMaxCachedScale(clipBuffers) {
1223
+ let max = 0;
1224
+ for (const audioBuffer of clipBuffers.values()) {
1225
+ const cached = this._cache.get(audioBuffer);
1226
+ if (cached && cached.scale > max) max = cached.scale;
1227
+ }
1228
+ return max;
1229
+ }
1182
1230
  terminate() {
1183
1231
  this._worker?.terminate();
1184
1232
  this._worker = null;
@@ -2487,6 +2535,7 @@ async function loadFiles(host, files) {
2487
2535
  clips: [
2488
2536
  {
2489
2537
  src: "",
2538
+ peaksSrc: "",
2490
2539
  start: 0,
2491
2540
  duration: audioBuffer.duration,
2492
2541
  offset: 0,
@@ -2572,6 +2621,7 @@ function addRecordedClip(host, trackId, buf, startSample, durSamples, offsetSamp
2572
2621
  const sr = host.effectiveSampleRate;
2573
2622
  const clipDesc = {
2574
2623
  src: "",
2624
+ peaksSrc: "",
2575
2625
  start: startSample / sr,
2576
2626
  duration: durSamples / sr,
2577
2627
  offset: 0,
@@ -2773,11 +2823,29 @@ function findAudioBufferForClip(host, clip, track) {
2773
2823
  return null;
2774
2824
  }
2775
2825
 
2826
+ // src/interactions/peaks-loader.ts
2827
+ var import_waveform_data2 = __toESM(require("waveform-data"));
2828
+ async function loadWaveformDataFromUrl(src) {
2829
+ const response = await fetch(src);
2830
+ if (!response.ok) {
2831
+ throw new Error("[dawcore] Failed to fetch peaks data: " + response.statusText);
2832
+ }
2833
+ const { pathname } = new URL(src, globalThis.location?.href ?? "http://localhost");
2834
+ const isBinary = pathname.toLowerCase().endsWith(".dat");
2835
+ if (isBinary) {
2836
+ const arrayBuffer = await response.arrayBuffer();
2837
+ return import_waveform_data2.default.create(arrayBuffer);
2838
+ } else {
2839
+ const json = await response.json();
2840
+ return import_waveform_data2.default.create(json);
2841
+ }
2842
+ }
2843
+
2776
2844
  // src/elements/daw-editor.ts
2777
2845
  var DawEditorElement = class extends import_lit12.LitElement {
2778
2846
  constructor() {
2779
2847
  super(...arguments);
2780
- this.samplesPerPixel = 1024;
2848
+ this._samplesPerPixel = 1024;
2781
2849
  this.waveHeight = 128;
2782
2850
  this.timescale = false;
2783
2851
  this.mono = false;
@@ -2804,9 +2872,12 @@ var DawEditorElement = class extends import_lit12.LitElement {
2804
2872
  this._engine = null;
2805
2873
  this._enginePromise = null;
2806
2874
  this._audioCache = /* @__PURE__ */ new Map();
2875
+ this._peaksCache = /* @__PURE__ */ new Map();
2807
2876
  this._clipBuffers = /* @__PURE__ */ new Map();
2808
2877
  this._clipOffsets = /* @__PURE__ */ new Map();
2809
2878
  this._peakPipeline = new PeakPipeline();
2879
+ /** Coarsest scale from pre-computed peaks — zoom cannot go finer than this. 0 = no limit. */
2880
+ this._minSamplesPerPixel = 0;
2810
2881
  this._trackElements = /* @__PURE__ */ new Map();
2811
2882
  this._childObserver = null;
2812
2883
  this._audioResume = new AudioResumeController(this);
@@ -2928,6 +2999,21 @@ var DawEditorElement = class extends import_lit12.LitElement {
2928
2999
  // --- Recording ---
2929
3000
  this.recordingStream = null;
2930
3001
  }
3002
+ get samplesPerPixel() {
3003
+ return this._samplesPerPixel;
3004
+ }
3005
+ set samplesPerPixel(value) {
3006
+ const old = this._samplesPerPixel;
3007
+ if (!Number.isFinite(value) || value <= 0) return;
3008
+ const clamped = this._minSamplesPerPixel > 0 && value < this._minSamplesPerPixel ? this._minSamplesPerPixel : value;
3009
+ if (clamped !== value) {
3010
+ console.warn(
3011
+ "[dawcore] Zoom " + value + " spp rejected \u2014 pre-computed peaks limit is " + this._minSamplesPerPixel + " spp"
3012
+ );
3013
+ }
3014
+ this._samplesPerPixel = clamped;
3015
+ this.requestUpdate("samplesPerPixel", old);
3016
+ }
2931
3017
  get _clipHandler() {
2932
3018
  return this.interactiveClips ? this._clipPointer : null;
2933
3019
  }
@@ -3023,9 +3109,11 @@ var DawEditorElement = class extends import_lit12.LitElement {
3023
3109
  this._childObserver = null;
3024
3110
  this._trackElements.clear();
3025
3111
  this._audioCache.clear();
3112
+ this._peaksCache.clear();
3026
3113
  this._clipBuffers.clear();
3027
3114
  this._clipOffsets.clear();
3028
3115
  this._peakPipeline.terminate();
3116
+ this._minSamplesPerPixel = 0;
3029
3117
  try {
3030
3118
  this._disposeEngine();
3031
3119
  } catch (err) {
@@ -3075,6 +3163,7 @@ var DawEditorElement = class extends import_lit12.LitElement {
3075
3163
  if (this._engine) {
3076
3164
  this._engine.removeTrack(trackId);
3077
3165
  }
3166
+ this._minSamplesPerPixel = this._peakPipeline.getMaxCachedScale(this._clipBuffers);
3078
3167
  if (nextEngine.size === 0) {
3079
3168
  this._currentTime = 0;
3080
3169
  this._stopPlayhead();
@@ -3086,6 +3175,7 @@ var DawEditorElement = class extends import_lit12.LitElement {
3086
3175
  if (clipEls.length === 0 && trackEl.src) {
3087
3176
  clips.push({
3088
3177
  src: trackEl.src,
3178
+ peaksSrc: "",
3089
3179
  start: 0,
3090
3180
  duration: 0,
3091
3181
  offset: 0,
@@ -3099,6 +3189,7 @@ var DawEditorElement = class extends import_lit12.LitElement {
3099
3189
  for (const clipEl of clipEls) {
3100
3190
  clips.push({
3101
3191
  src: clipEl.src,
3192
+ peaksSrc: clipEl.peaksSrc,
3102
3193
  start: clipEl.start,
3103
3194
  duration: clipEl.duration,
3104
3195
  offset: clipEl.offset,
@@ -3126,7 +3217,77 @@ var DawEditorElement = class extends import_lit12.LitElement {
3126
3217
  const clips = [];
3127
3218
  for (const clipDesc of descriptor.clips) {
3128
3219
  if (!clipDesc.src) continue;
3129
- const audioBuffer = await this._fetchAndDecode(clipDesc.src);
3220
+ const waveformDataPromise = clipDesc.peaksSrc ? this._fetchPeaks(clipDesc.peaksSrc) : null;
3221
+ const audioPromise = this._fetchAndDecode(clipDesc.src);
3222
+ let waveformData = null;
3223
+ if (waveformDataPromise) {
3224
+ try {
3225
+ waveformData = await waveformDataPromise;
3226
+ } catch (err) {
3227
+ console.warn(
3228
+ "[dawcore] Failed to load peaks from " + clipDesc.peaksSrc + ": " + String(err) + " \u2014 falling back to AudioBuffer generation"
3229
+ );
3230
+ }
3231
+ }
3232
+ if (waveformData) {
3233
+ const clip2 = (0, import_core4.createClipFromSeconds)({
3234
+ waveformData,
3235
+ startTime: clipDesc.start,
3236
+ duration: clipDesc.duration || waveformData.duration,
3237
+ offset: clipDesc.offset,
3238
+ gain: clipDesc.gain,
3239
+ name: clipDesc.name,
3240
+ sampleRate: waveformData.sample_rate,
3241
+ sourceDuration: waveformData.duration
3242
+ });
3243
+ const effectiveScale = Math.max(this.samplesPerPixel, waveformData.scale);
3244
+ const peakData2 = extractPeaks(
3245
+ waveformData,
3246
+ effectiveScale,
3247
+ this.mono,
3248
+ clip2.offsetSamples,
3249
+ clip2.durationSamples
3250
+ );
3251
+ this._clipOffsets.set(clip2.id, {
3252
+ offsetSamples: clip2.offsetSamples,
3253
+ durationSamples: clip2.durationSamples
3254
+ });
3255
+ this._peaksData = new Map(this._peaksData).set(clip2.id, peakData2);
3256
+ this._minSamplesPerPixel = Math.max(this._minSamplesPerPixel, waveformData.scale);
3257
+ const previewTrack = (0, import_core4.createTrack)({
3258
+ name: descriptor.name,
3259
+ clips: [clip2],
3260
+ volume: descriptor.volume,
3261
+ pan: descriptor.pan,
3262
+ muted: descriptor.muted,
3263
+ soloed: descriptor.soloed
3264
+ });
3265
+ previewTrack.id = trackId;
3266
+ this._engineTracks = new Map(this._engineTracks).set(trackId, previewTrack);
3267
+ this._recomputeDuration();
3268
+ let audioBuffer2;
3269
+ try {
3270
+ audioBuffer2 = await audioPromise;
3271
+ } catch (audioErr) {
3272
+ const nextPeaks = new Map(this._peaksData);
3273
+ nextPeaks.delete(clip2.id);
3274
+ this._peaksData = nextPeaks;
3275
+ this._clipOffsets.delete(clip2.id);
3276
+ const nextEngine = new Map(this._engineTracks);
3277
+ nextEngine.delete(trackId);
3278
+ this._engineTracks = nextEngine;
3279
+ this._minSamplesPerPixel = this._peakPipeline.getMaxCachedScale(this._clipBuffers);
3280
+ this._recomputeDuration();
3281
+ throw audioErr;
3282
+ }
3283
+ this._resolvedSampleRate = audioBuffer2.sampleRate;
3284
+ const updatedClip = { ...clip2, audioBuffer: audioBuffer2 };
3285
+ this._clipBuffers = new Map(this._clipBuffers).set(clip2.id, audioBuffer2);
3286
+ this._peakPipeline.cacheWaveformData(audioBuffer2, waveformData);
3287
+ clips.push(updatedClip);
3288
+ continue;
3289
+ }
3290
+ const audioBuffer = await audioPromise;
3130
3291
  this._resolvedSampleRate = audioBuffer.sampleRate;
3131
3292
  const clip = (0, import_core4.createClipFromSeconds)({
3132
3293
  audioBuffer,
@@ -3208,6 +3369,16 @@ var DawEditorElement = class extends import_lit12.LitElement {
3208
3369
  throw err;
3209
3370
  }
3210
3371
  }
3372
+ _fetchPeaks(src) {
3373
+ const cached = this._peaksCache.get(src);
3374
+ if (cached) return cached;
3375
+ const promise = loadWaveformDataFromUrl(src).catch((err) => {
3376
+ this._peaksCache.delete(src);
3377
+ throw err;
3378
+ });
3379
+ this._peaksCache.set(src, promise);
3380
+ return promise;
3381
+ }
3211
3382
  _recomputeDuration() {
3212
3383
  let maxSample = 0;
3213
3384
  for (const track of this._engineTracks.values()) {
@@ -3620,8 +3791,8 @@ DawEditorElement.styles = [
3620
3791
  ];
3621
3792
  DawEditorElement._CONTROL_PROPS = /* @__PURE__ */ new Set(["volume", "pan", "muted", "soloed"]);
3622
3793
  __decorateClass([
3623
- (0, import_decorators10.property)({ type: Number, attribute: "samples-per-pixel" })
3624
- ], DawEditorElement.prototype, "samplesPerPixel", 2);
3794
+ (0, import_decorators10.property)({ type: Number, attribute: "samples-per-pixel", noAccessor: true })
3795
+ ], DawEditorElement.prototype, "samplesPerPixel", 1);
3625
3796
  __decorateClass([
3626
3797
  (0, import_decorators10.property)({ type: Number, attribute: "wave-height" })
3627
3798
  ], DawEditorElement.prototype, "waveHeight", 2);