fix: crossfade clamps both tracks to active tab's current volume

Crossfades (user-initiated and DJ auto-transitions) faded the incoming
track up to volume 1.0 regardless of how loud the playing tab actually
was, producing a jarring jump in loudness mid-transition. Read
activeMedia.volume when a fade begins and use it as the ceiling for both
the outgoing and incoming tracks; restore that same level if a DJ
crossfade is cancelled.
This commit is contained in:
russell@unturf.com 2026-05-12 19:53:42 -04:00
parent 31f8449c7d
commit 2886a4255d
No known key found for this signature in database

View file

@ -18,6 +18,7 @@
var djCrossfadeActive = false;
var djFadeoutActive = false;
var djCrossfadeData = null;
var djStartVolume = 1; // active tab's volume captured when a DJ fade begins
var preloadingId = null;
var currentProductId = null;
var currentCanonicalUrl = null;
@ -691,6 +692,11 @@
return;
}
// Read the active tab's current volume — both tracks fade within this
// ceiling so the crossfade can never jump louder than what's playing.
var startVolume = activeMedia.volume;
if (!(startVolume >= 0)) startVolume = 1;
standbyMedia.src = data.media_url;
standbyMedia.volume = 0;
standbyMedia.currentTime = 0;
@ -721,8 +727,8 @@
step++;
var progress = step / CROSSFADE_STEPS;
try {
activeMedia.volume = Math.max(0, 1 - progress);
standbyMedia.volume = Math.min(1, progress);
activeMedia.volume = Math.max(0, startVolume * (1 - progress));
standbyMedia.volume = Math.min(startVolume, startVolume * progress);
} catch (e) {}
// Visual opacity crossfade
@ -1469,7 +1475,11 @@
var steps = COUNTDOWN_SECONDS * 20; // 20fps for smooth animation
var step = 0;
var interval = (COUNTDOWN_SECONDS * 1000) / steps;
var startVolume = activeMedia.volume || 1;
// Honor the active tab's current volume as the ceiling — never fade
// louder than what's already playing.
var startVolume = activeMedia.volume;
if (!(startVolume >= 0)) startVolume = 1;
djStartVolume = startVolume;
if (crossfadeTimer) clearInterval(crossfadeTimer);
crossfadeTimer = setInterval(function() {
@ -1479,7 +1489,7 @@
// Volume crossfade
try {
activeMedia.volume = Math.max(0, startVolume * (1 - progress));
standbyMedia.volume = Math.min(1, progress);
standbyMedia.volume = Math.min(startVolume, startVolume * progress);
} catch(e) {}
// Visual crossfade (opacity)
@ -1565,7 +1575,11 @@
var steps = COUNTDOWN_SECONDS * 20; // 20fps
var step = 0;
var interval = (COUNTDOWN_SECONDS * 1000) / steps;
var startVolume = activeMedia.volume || 1;
// Honor the active tab's current volume as the ceiling — never fade
// louder than what's already playing.
var startVolume = activeMedia.volume;
if (!(startVolume >= 0)) startVolume = 1;
djStartVolume = startVolume;
if (crossfadeTimer) clearInterval(crossfadeTimer);
crossfadeTimer = setInterval(function() {
@ -1623,8 +1637,8 @@
crossfadeTimer = null;
}
// Restore active media
activeMedia.volume = 1;
// Restore active media (back to the volume it had before the fade started)
activeMedia.volume = djStartVolume;
activeMedia.style.opacity = '1';
// Clean up standby only during full crossfade (not fadeout)