Playlist: per-track start/end trim, total/elapsed/remaining times, waveform cards
- Per-track start/end points (mm:ss inputs, 'set to current' buttons, draggable handles on the waveform). Trim applied via audio.setTrim (seek to start, stop at end) and pushed live to the playing track. - Wider cards showing elapsed / total / remaining for the trimmed range. - Waveform rendered per track: peaks computed in main via ffmpeg (mono 8kHz PCM -> 400 normalized buckets, cached), drawn on a canvas with trim region + playhead. New audio:peaks IPC + djv.peaks preload binding. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
a8973619e6
commit
882dd4643a
6 files changed
+249
-18
No files matched your search
@@ -34,6 +34,9 @@ class AudioEngine {
|
||||
this.stream = null; // MediaStream when using live input
|
||||
this.mode = 'none';
|
||||
this.onEnded = null; // callback fired when a (non-looping) track ends
|
||||
this.trimStart = 0; // playback start point (s); seek here on load
|
||||
this.trimEnd = 0; // playback end point (s); 0 = play to the natural end
|
||||
this._trimFired = false;
|
||||
this._videoSrc = null; // persistent source for the playlist video element
|
||||
this._videoGain = null;
|
||||
this._videoSrcEl = null;
|
||||
@@ -68,6 +71,28 @@ class AudioEngine {
|
||||
|
||||
resume() { if (this.ctx.state === 'suspended') this.ctx.resume(); }
|
||||
|
||||
// Trim the current/next track to [start, end] seconds (end 0 = natural end).
|
||||
setTrim(start, end) {
|
||||
this.trimStart = Math.max(0, start || 0);
|
||||
this.trimEnd = Math.max(0, end || 0);
|
||||
this._trimFired = false;
|
||||
}
|
||||
// Seek a freshly-loaded media element to the trim start (waits for metadata).
|
||||
seekToTrimStart(el) {
|
||||
const go = () => { if (this.trimStart > 0) { try { el.currentTime = this.trimStart; } catch (e) {} } };
|
||||
if (el.readyState >= 1) go(); else el.addEventListener('loadedmetadata', go, { once: true });
|
||||
}
|
||||
// Called every frame from the output loop: stop at the trim end point.
|
||||
checkTrim() {
|
||||
const el = this.mediaEl;
|
||||
if (!el || el.paused || this._trimFired) return;
|
||||
if (this.trimEnd > 0 && el.currentTime >= this.trimEnd) {
|
||||
this._trimFired = true;
|
||||
try { el.pause(); } catch (e) {}
|
||||
if (this.onEnded) this.onEnded();
|
||||
}
|
||||
}
|
||||
|
||||
// Stop any current audio/video source but keep the context & analyser alive
|
||||
// (meters read 0, recording keep-alive continues). Used for visual-only
|
||||
// playlist interludes.
|
||||
@@ -128,8 +153,10 @@ class AudioEngine {
|
||||
this.sourceGain = g;
|
||||
this.stream = null;
|
||||
this.mode = 'file';
|
||||
this._trimFired = false;
|
||||
|
||||
await el.play();
|
||||
this.seekToTrimStart(el);
|
||||
return el;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user