crossfade-dual-player
Installation
SKILL.md
Dual-player crossfade
Two items must be audible at once, so a crossfade cannot be built on one player instance. The shape that works on both Android (Media3/ExoPlayer) and desktop engines:
- One player instance per media item. The audible one is
currentPlayer; the one fading in issecondaryPlayer. Upcoming items may already exist as precached instances. - One completion move for every early exit — pause, seek, skip: release the outgoing instance, promote the incoming one, restore its normal levels, clear the fading flag. Call it commit the incoming track as current. The natural finish usually keeps a near-twin of its own; hold the two in lockstep — every cleanup step added to one must appear in the other, or extract their shared tail.
- Equal-power (cosine/sine) gain curves, driven by one cancellable job.
val fadeAngle = (progress * PI / 2).toFloat()
currentPlayer?.volume = targetVolume * cos(fadeAngle) // outgoing
nextPlayer.volume = targetVolume * sin(fadeAngle) // incoming