Fix Android waveform IndexOutOfBoundsException on long files - #48
Merged
Conversation
On long files the 32-bit product `i * totalSamples` overflowed to a negative index, crashing getWaveform/getWaveformBytes with an IndexOutOfBoundsException. Extract the RMS reduction into a testable computeWaveform helper and widen the offset math to Long.
Synthesize a multi-million-frame WAV at runtime and run getWaveform over it, so the 32-bit window-offset overflow is caught end-to-end on Android without bundling a large audio asset.
There was a problem hiding this comment.
Pull request overview
Fixes a 32-bit integer overflow in the Android waveform reduction that crashed getWaveform/getWaveformBytes on long files (issue #45). The window-offset arithmetic now uses 64-bit math, and the reduction is extracted into a testable helper with new unit and integration coverage. Version bumped to 0.8.1.
Changes:
- Extract RMS reduction into
internal fun computeWaveform(...)and compute window starts via(i.toLong() * totalSamples / numberOfSamples).toInt(). - Add Android unit tests (large-sample regression, empty input, absolute normalization) and a Dart integration regression test using a new
buildPcmWavhelper. - Release 0.8.1 (CHANGELOG, pubspec, example lockfile).
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| android/src/main/kotlin/nl/silversoft/audio_decoder/AudioDecoderPlugin.kt | Extracts computeWaveform helper; uses 64-bit math for window offsets. |
| android/src/test/kotlin/nl/silversoft/audio_decoder/AudioDecoderPluginTest.kt | Adds 3 unit tests covering the helper, including the overflow regression. |
| example/integration_test/test_helpers.dart | Adds buildPcmWav helper to synthesize long PCM WAV files. |
| example/integration_test/plugin_integration_test.dart | Adds on-device regression test using a 3M-frame synthetic WAV. |
| CHANGELOG.md | Documents the 0.8.1 fix. |
| pubspec.yaml | Bumps version to 0.8.1. |
| example/pubspec.lock | Refreshes resolved local package version. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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.
Summary
IndexOutOfBoundsException(negative index) ingetWaveform/getWaveformByteson Android for medium-to-large audio files (e.g. a 5-minute MP3).i * totalSamplesexceedsInt.MAX_VALUEonce a file decodes to ~millions of samples, wrapping to a negative index.Closes #45
Changes
android/.../AudioDecoderPlugin.kt: extract the RMS/normalization reduction into aninternal fun computeWaveform(...)and compute the window start with(i.toLong() * totalSamples / numberOfSamples).toInt()so large files no longer overflow.android/.../AudioDecoderPluginTest.kt: add three tests — a regression test with 15,567,358 samples (the length from the bug report), an empty-input test, and an absolute-normalization test.example/integration_test/: add an on-device regression test that synthesizes a long (3M-frame) WAV at runtime via a newbuildPcmWavhelper and runsgetWaveformover it, catching the overflow end-to-end without bundling a large asset.CHANGELOG.md/pubspec.yaml: release0.8.1documenting the fix.example/pubspec.lock: refresh the resolved local package version to0.8.1.Test plan
example/android/:./gradlew :audio_decoder:testDebugUnitTest— all 14 tests pass, includingcomputeWaveform_largeSampleCount_doesNotOverflow.getWaveformover a long file without anIndexOutOfBoundsException— verified via the new integration test using a synthetic 3M-frame WAV that crosses the same 32-bit overflow threshold (not a literal 4–6 min MP3, but exercises the identical code path).getWaveform/getWaveformBytesintegration tests pass on device, and the example app renders waveforms correctly.Time spent
⏱️ Estimated time spent: 3 hours