Skip to content

Decode IEEE float WAVs to left-justified int32 in WavReader - #74

Merged
jwcullen merged 1 commit into
AOMediaCodec:mainfrom
trsonic:upstream-pr/wav-reader-float-decode
Jul 30, 2026
Merged

Decode IEEE float WAVs to left-justified int32 in WavReader#74
jwcullen merged 1 commit into
AOMediaCodec:mainfrom
trsonic:upstream-pr/wav-reader-float-decode

Conversation

@trsonic

@trsonic trsonic commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

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.

Fix

Convert float samples to the left-justified int32 convention documented on buffers_, 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 downcast to float32 by the C layer and take the same path.

The docs on bit_depth() and buffers_ now state that for IEEE float sources bit_depth() reports the source container width (32 or 64) while the decoded samples span the full int32 range. As before this change, WavSampleProvider rejects readers whose bit depth exceeds the codec's, so float64 input works through WavReader directly 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 0x3F000000 instead of 0x40000000.

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 current main.

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).
@jwcullen
jwcullen merged commit a7b5f4b into AOMediaCodec:main Jul 30, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants