Decode IEEE float WAVs to left-justified int32 in WavReader - #74
Merged
jwcullen merged 1 commit intoJul 30, 2026
Merged
Conversation
WavReader::CreateFromFile forces destination_alignment_bytes = 4 but never inspects the decoded sample format. For IEEE float sources (format tag 3), audio_to_tactile's ReadWavSamples fills the 32-bit destination with raw float values (ReadWavInfo::sample_format == kFloat), so ReadFrame stored float bit patterns directly into the int32 sample buffers. Header parsing succeeds and WavSampleProvider validates only bit depth / sample rate / channel ids, so a float WAV fed to the encoder produced garbage audio while appearing to encode successfully. Convert float samples to the left-justified int32 convention that buffers_ documents, using the same scaling as audio_to_tactile's InPlaceFloatToInt32Conversion (which only runs on the whole-file ReadWavFile path this reader does not use): scale [-1, 1] by 2^31, clamp out-of-range values, map NaN to 0. float64 sources are already downcast to float32 by the C layer and take the same path. Document on bit_depth() and buffers_ that for IEEE float sources bit_depth() reports the source container width (32 or 64) while the decoded samples span the full int32 range. Add regression tests with hand-crafted mono float32/float64 fixtures covering scaling, clamping (at +/-1.0 and for out-of-range 1.5, -1.5, +inf), and NaN. The tests fail without the fix (float bit patterns surface verbatim, e.g. 0.5f reads as 0x3F000000 instead of 0x40000000).
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.
WavReader::CreateFromFileforcesdestination_alignment_bytes = 4but never inspects the decoded sample format. For IEEE float sources (format tag 3), audio_to_tactile'sReadWavSamplesfills the 32-bit destination with raw float values (ReadWavInfo::sample_format == kFloat), soReadFramestored float bit patterns directly into the int32 sample buffers. Header parsing succeeds andWavSampleProvidervalidates only bit depth / sample rate / channel IDs, so a float WAV fed to the encoder produced garbage audio while appearing to encode successfully.Fix
Convert float samples to the left-justified int32 convention documented on
buffers_, using the same scaling as audio_to_tactile'sInPlaceFloatToInt32Conversion(which only runs on the whole-fileReadWavFilepath this reader does not use): scale [-1, 1] by 2^31, clamp out-of-range values, map NaN to 0. float64 sources are downcast to float32 by the C layer and take the same path.The docs on
bit_depth()andbuffers_now state that for IEEE float sourcesbit_depth()reports the source container width (32 or 64) while the decoded samples span the full int32 range. As before this change,WavSampleProviderrejects readers whose bit depth exceeds the codec's, so float64 input works throughWavReaderdirectly but not the encoder pipeline; float32 now encodes correctly.Tests
Regression tests with hand-crafted mono float32/float64 fixtures cover scaling, clamping (at +/-1.0 and for out-of-range 1.5, -1.5, +inf), and NaN. Both tests fail without the fix — float bit patterns surface verbatim, e.g. 0.5f reads as
0x3F000000instead of0x40000000.Tested:
bazel test //iamf/cli/tests:wav_reader_test //iamf/cli/tests:wav_sample_provider_test //iamf/cli/tests:wav_writer_test-> 3/3 pass on top of currentmain.