Skip to content
Blog
MethodSeptember 8, 2026

Ask the decoder, not the extension

Most of my files had stopped playing. The app promised Opus support on the word of an extension list its audio engine could not honor. The fix: ask the decoder itself. And the measurement that kept me from uniformizing too far.

Most of my files no longer play

That is the finding that opened AquaTube's v0.16.0 work (July 16, 2026). Not a path problem, not a corrupted database: healthy files, indexed, offered in the app's dialogs, and silent on playback. First among them, the .opus files.

The cause was a promise. Somewhere in the code, an extension list declared Opus supported. The indexer believed that list. The audio engine, Symphonia, has no Opus support at all. Two parallel truths lived in the same app: the declaration's and that of the code doing the work. The user, meanwhile, saw a player that promised a format and went quiet.

An extension list is a declaration

The problem is not specific to Opus. An extension list is a capability claim written by hand, at a distance from the code that has to honor it. Every time the engine evolves, the list should follow; nobody ever performs that synchronization. ALAC, WMA: same absentees, same silence.

And a list can only lie in both directions. Too broad, it promises what you cannot decode. Too narrow, it refuses what you could read. Either way, it answers in place of someone who knows the real answer.

Ask the one doing the work

The v0.16.0 fix fits in one sentence: we ask Symphonia, and if it declines, FFmpeg takes over. FFmpeg already shipped with the app for video; it can decode Opus, ALAC, WMA, without anyone naming them. No extension list decides anything anymore, so none can lie.

The file is probed for what it contains, not for what its name claims. Capability is established by trying, at the moment it matters, on the file at hand.

The temptation of uniformity

If FFmpeg can read everything, why keep two engines? Moving everything to FFmpeg would have simplified the architecture: one code path, no fallback to maintain.

Before deciding, a dedicated probe decoded the same files twice, in full, with both engines, and counted the samples rendered. On the library's real mp3 files, FFmpeg renders 1152 samples more than Symphonia: one full MP3 frame, 26 ms of LAME/Xing padding that Symphonia trims and FFmpeg let through. Inaudible on an isolated track. At the track boundaries of a continuously mixed album, it is a clean snag, at every transition. And those albums live precisely in the part of the library that still worked, the 32% that absolutely could not be degraded.

So mp3 stays on Symphonia. Uniformity would have broken the files that worked in order to play the ones that did not.

An extension promises, a decode attempt proves, a measurement decides.

What I take away

Two decisions in this batch, one principle: question reality rather than the declaration. To know whether a format decodes, ask the decoder. To choose between two capable decoders, measure their output, sample by sample.

The transposable rule: every time a hand-written table claims to answer in place of the code, there is a version of the question you can put to the code directly. It is always more expensive to ask, and always cheaper than the lie.