Skip to content

feat(audio): read tracker modules (XM, IT, S3M) through FFmpeg - #1399

Merged
KnifeLemon merged 2 commits into
mainfrom
feat/xm-modules-1376
Nov 11, 2026
Merged

KnifeLemon merged 2 commits into
mainfrom
feat/xm-modules-1376

Conversation

@KnifeLemon

Copy link
Copy Markdown
Owner

What users notice

FastTracker 2 modules (.xm) open on the audio donut and convert to every audio target (MP3, WAV, FLAC, M4A, OGG, …) through FFmpeg. Impulse Tracker (.it) and Scream Tracker 3 (.s3m) modules come along, since FFmpeg reads all three through the same demuxer. All three are read only.

FFmpeg has no tracker demuxer of its own; it needs libopenmpt (or the older libmodplug). I checked the builds Filee downloads instead of assuming:

Platform FFmpeg libopenmpt
Windows x64 (and ARM64 under emulation) gyan.dev ffmpeg-9.0.2-full_build-shared.zip yes: the README.txt inside the pinned zip lists it under external libraries
Linux x64 / ARM64 BtbN autobuild-2026-09-30-13-08 linux64-gpl / linuxarm64-gpl yes: scripts.d/50-openmpt.sh at that tag turns it off only for winarm64
macOS the user's FFmpeg (Homebrew) no: homebrew-core's ffmpeg.rb has no libopenmpt or libmodplug

So an FFmpeg without either demuxer now stops a tracker module with a translated message ("This FFmpeg can't read tracker modules (XM, IT, S3M): it was built without libopenmpt. …") instead of FFmpeg's "Invalid data found". Filee asks ffmpeg -demuxers once per executable, the same way it already caches -codecs, -encoders and -filters.

Amiga .mod is not added: that extension is already the camcorder MOD video.

What changed

  • src/Filee.Core/Formats/FormatRegistry.cs: xm, it, s3m as read-only audio formats.
  • src/Filee.Core/Formats/FormatFields.cs: XM and IT graded specialist (audio-pro + game), S3M legacy (legacy + audio-pro, replaced by IT).
  • src/Filee.Engines/Media/FfmpegConverter.cs: DemuxersOfAsync / ParseDemuxers and the tracker check in ConvertAsync and JoinAsync.
  • src/Filee.Core/Settings/UserDataStore.cs, AppSettings.cs: settings schema 81 adds the three extensions to existing audio donuts; custom claims stay put. 80 is skipped because feat(image): read Windows animated cursors (.ani) #1390 already claims it, so this may need renumbering depending on merge order.
  • src/Filee.App/Assets/i18n/*.json: error.tracker_needs_openmpt in all 22 languages.
  • docs/ENGINES.md: the tracker paragraph under FFmpeg's read-only formats.

Tests

  • tests/Filee.Core.Tests/TrackerModuleFormatTests.cs (new): detection, audio donut, schema 81 migration (learns once, leaves a user's own claim, survives a restart).
  • tests/Filee.Core.Tests/FormatFieldsTests.cs: the pinned lists and grade counts.
  • tests/Filee.Engines.Tests/MediaTests.cs: routes for all three modules, -demuxers parsing and the libopenmpt/libmodplug decision, and a real conversion of an XM written by the test (two channels, one pattern, a square-wave sample) to WAV, MP3 and FLAC. That test checks the length and that the WAV isn't silent. Against an FFmpeg without libopenmpt it checks Filee's message instead.
  • Ran locally with gyan.dev's FFmpeg 9.0.2 full build: Filee.Core.Tests (all), the Engines tests above, Filee.App.Tests Localization*. dotnet format --verify-no-changes is clean on the touched projects.

Not covered

  • macOS: untested on a Mac. The code path (refusal with Homebrew's FFmpeg) is covered by the parsing test only.
  • CI doesn't install FFmpeg, so the real conversion test skips there, as the other FFmpeg tests do.

Closes #1376

🤖 Generated with Claude Code

FastTracker 2 .xm, Impulse Tracker .it and Scream Tracker 3 .s3m
modules are read-only audio formats that go to every audio target in
one FFmpeg step. FFmpeg plays them only through its libopenmpt (or
libmodplug) demuxer: the gyan.dev full build and BtbN's Linux GPL
builds that Filee downloads have it, Homebrew's FFmpeg on macOS does
not. Filee reads `ffmpeg -demuxers` once per executable and, without
either demuxer, stops the file with error.tracker_needs_openmpt
instead of FFmpeg's "Invalid data found".

Settings schema 81 adds the three extensions to existing audio donuts
without taking custom claims (80 is claimed by #1390). Amiga .mod
stays the camcorder video it already is.

Closes #1376

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the feature New features label Nov 11, 2026
# Conflicts:
#	src/Filee.Core/Settings/AppSettings.cs
#	src/Filee.Core/Settings/UserDataStore.cs
@KnifeLemon
KnifeLemon merged commit 3cddb0a into main Nov 11, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Format]: .xm

1 participant