Skip to content

AudioProcessor: don't abort() on a non-frame-aligned Consume() - #161

Merged
lalinsky merged 4 commits into
acoustid:masterfrom
OzGav:fix-multi-channel-crash
Jun 16, 2026
Merged

lalinsky merged 4 commits into
acoustid:masterfrom
OzGav:fix-multi-channel-crash

Conversation

@OzGav

@OzGav OzGav commented Jun 15, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #90.

AudioProcessor::Consume() asserts that the input length is a whole multiple
of the channel count:

assert(length % m_num_channels == 0);

When a caller passes a buffer that splits the audio mid-frame, this assertion aborts the entire host process instead of failing gracefully. It's easy to hit when feeding PCM in fixed-size byte chunks (the chunk boundary rarely lands
on a frame boundary), and especially likely with multi-channel sources where a frame spans more bytes. A library should never abort() the process that embeds it on account of input framing.

Reported downstream where an AcoustID scan of a 5.1 file took the whole application down with:

Assertion length % m_num_channels == 0 failed.

Consume() now tolerates input that isn't aligned to a whole number of frames. Any trailing partial frame is buffered in a new m_leftover member and prepended to the next call, so the audio is processed losslessly and channel
interleaving stays aligned across calls. The existing whole-frame processing loop is unchanged, it's simply moved into a private ConsumeAligned() helper that Consume() calls once the input has been split into a whole-frame portion
and a carried remainder.

Consume() splits input into complete frames + a carried remainder ConsumeAligned() the original Consume() body (still asserts alignment, which is now always satisfied) m_leftover holds a sub-frame remainder between calls; cleared in Reset() Behaviour for callers that already pass whole frames is identical as the leftover buffer stays empty, remainder is always 0, and the exact same processing path runs.

@coderabbitai

coderabbitai Bot commented Jun 15, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

AudioProcessor gains a private ConsumeAligned() helper and an m_leftover buffer (std::vector<int16_t>). Consume() now completes any pending partial frame from previous calls before forwarding aligned input to ConsumeAligned(), storing any trailing remainder back into m_leftover. Reset() clears m_leftover.

Changes

Partial Frame Buffering in AudioProcessor

Layer / File(s) Summary
Header contract: ConsumeAligned and m_leftover
src/audio_processor.h
Declares private ConsumeAligned(const int16_t*, int) and adds m_leftover (std::vector<int16_t>) to buffer partial frames between calls.
Implementation: leftover logic in Consume() and Reset()
src/audio_processor.cpp
Reset() clears m_leftover. Consume() drains any existing partial frame into ConsumeAligned(), processes the aligned bulk, then stashes the trailing remainder. Debug message corrected to say ConsumeAligned().

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning No description was provided by the author, making it impossible to assess whether it relates to the changeset. Add a description explaining what the crash was, how the buffering mechanism fixes it, and why handling partial frames across calls is necessary.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately describes the core fix: handling non-frame-aligned Consume() calls without aborting, which matches the implementation changes of buffering incomplete samples in m_leftover.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@OzGav OzGav changed the title Fix multi channel crash AudioProcessor: don't abort() on a non-frame-aligned Consume() Jun 15, 2026
@lalinsky

Copy link
Copy Markdown
Member

Thank you!

@lalinsky
lalinsky merged commit ab48115 into acoustid:master Jun 16, 2026
14 checks passed
@trostli

trostli commented Jun 23, 2026

Copy link
Copy Markdown

Great! I've run into this error many times. @lalinsky would it be possible to issue a new release with this fix included?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants