Fix standard crossfade falling back to a hard cut on some tracks#4253
Merged
Conversation
FFmpeg's acrossfade emits no output (with a success exit code) when an input is even a fraction of a sample shorter than the requested duration. StandardCrossFade computed the overlap as a float but sliced the PCM buffers on frame boundaries, so a non-integer crossfade left the buffer a sliver short of `d` and produced nothing — common when a quiet outro is trimmed from the outgoing track. Tracks ending on an exact second were unaffected, which made it look intermittent. Drive both the buffer slice and the filter from a single frame-aligned overlap size and use acrossfade's integer `ns=` form, so the requested length always matches the buffer exactly. Also fix the misleading "Smart crossfade" wording in the shared FFmpeg error message (the standard path uses the same code).
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes an intermittent failure mode in the standard crossfade path where FFmpeg’s acrossfade can return success but emit zero output if the requested overlap slightly exceeds the provided PCM buffer. It does this by quantizing the overlap to an integer frame-aligned size and driving both buffer slicing and the FFmpeg filter length from that same integer (via acrossfade=ns=), plus adds regression tests.
Changes:
- Update
StandardCrossFadeto compute a single frame-aligned overlap size once and reuse it for both slicing and filter construction. - Extend
CrossfadeFilterto support sample-count overlaps (acrossfade=ns=) and enforce “exactly one of duration or samples” at construction time. - Add/adjust tests to validate
ns=emission and guard the fractional-overlap regression.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| tests/core/test_smartfade_transition_timings.py | Adds regression coverage for fractional overlaps and asserts the standard path uses sample-count crossfade length. |
| tests/controllers/streams/smart_fades/test_filters.py | Adds unit tests for acrossfade=ns= output and constructor validation. |
| music_assistant/controllers/streams/smart_fades/filters.py | Enhances CrossfadeFilter to emit d= or ns= depending on how it’s constructed. |
| music_assistant/controllers/streams/smart_fades/fades.py | Makes standard crossfade overlap frame-aligned and updates FFmpeg error wording to “Crossfade …”. |
CI mypy flagged the int | None sample count in a multiplication, and Copilot noted the test fed a non-frame-aligned byte length (not a valid PCM buffer). Narrow the type and build the fade-out length on a frame boundary — still a fractional number of seconds, so it keeps exercising the no-output regression.
crossfade_size now defaults to 0, which is also a valid built state (silent/tiny buffer), so guard apply() on the filter chain set by _build() — restoring the fail-fast on incorrect usage, consistent with SmartFade._get_ffmpeg_filters().
marcelveldt
added a commit
that referenced
this pull request
Jun 16, 2026
marcelveldt
added a commit
that referenced
this pull request
Jun 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this implement/fix?
Since 2.9, Standard Crossfade silently dropped to a hard cut for some tracks, logging
Crossfade mixer failed ... produced no output.FFmpeg's
acrossfadeemits zero output (with a success exit code) when an input is even afraction of a sample shorter than the requested duration
d.StandardCrossFadecomputed theoverlap as a float but sliced the PCM buffers on frame boundaries, so a non-integer crossfade —
e.g. when a quiet outro is trimmed from the outgoing track — left the buffer a sliver shorter
than
dand produced nothing. Tracks ending on an exact second were unaffected, which made itlook intermittent.
Changes
StandardCrossFadeand use it for both thebuffer slice and the filter, so they can no longer disagree.
CrossfadeFilteremitacrossfade=ns=<samples>(exact sample count) instead ofd=<seconds>; the standard path now uses it. Smart crossfade is unchanged.Types of changes
bugfixnew-featureenhancementnew-providerbreaking-changerefactordocumentationmaintenancecidependenciesChecklist
pre-commit run --all-filespasses.pytestpasses, and tests have been added/updated undertests/where applicable.music-assistant/modelsis linked.music-assistant/frontendis linked.