fix(akouo-core): decode >2-channel Opus via multistream instead of collapsing to stereo (#544) - #571
Merged
Conversation
Gate-Passed: kanon 0.1.5 +stages:fmt,check,clippy,nextest,lint sha:9a7a3585a10b70ae86196b4a5f2fa0d2ee80485c
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.
Verified decode-correctness fix from the 2026-07-03 deep-audit workflow (adversarially verified + Opus-judged).
#544 — >2-channel Opus streams were collapsed to stereo.
opus_channelswas forced toMono/Stereoat both decoder construction and post-seek reset, whiletotal = n_samples × channelsused the source's TRUE (possibly >2) channel count. A >2-channel Opus stream therefore either errored on every packet (libopus configured stereo) or fabricated extra-channel audio from staledecode_bufbytes, desyncing every downstream per-channel DSP stage — a silently-wrong-audio bug.Fix — decode multichannel correctly (opusic-c exposes a multistream decoder). Verified against the installed crate source:
opusic_c::multistream::Decoder(libopusOpusMSDecoder) andmultistream::Config<CH>(RFC-7845 stream/coupled/mapping validation) are available. The fix resolves the real channel layout from theOpusHeadidentification header (which Symphonia forwards viaAudioCodecParameters.extra_datafor OGG/MP4-dOps/Matroska):opusic_c::Decoder;multistream::Decoderwith the correct channel-mapping table;OpusHead(no mapping table exists) — returnDecodeError::UnsupportedCodec(the WavPack idiom) instead of collapsing.The seek path now resets decoder state in place (
OPUS_RESET_STATE) instead of rebuilding, removing the duplicated collapse logic.channelsflows from the resolved layout's true count, with anINVARIANTtag tying it to the interleaved-sample math.Note for reviewers: adds one
unsafe impl Send for MultiDecoder—opusic-cmarks the structurally-identicalDecoder/Encoder/multistream::EncoderasSendbut omits it onmultistream::Decoder; the decoder is exclusively-owned heap state (&mut self-only, no thread affinity), so the transfer is sound.parse_opus_headis bounds-checked on all offsets and never panics on malformedextra_data.Gate:
kanon gate --fullgreen — fmt, check, clippy (-D warnings --all-targets), nextest 1923 passed, deny, lint (0 errors). 13 opus-module tests (7 pre-existing + 6 new), including a real libopus 5.1 encode→decode round-trip proving six channels decode a full frame each.Gate-Passedtrailer stamped.Closes #544