Skip to content

Down-mix stereo MP3 to mono in readMp3 - #4544

Open
Daksha1611 wants to merge 1 commit into
openvinotoolkit:mainfrom
Daksha1611:fix-mp3-stereo-downmix
Open

Down-mix stereo MP3 to mono in readMp3#4544
Daksha1611 wants to merge 1 commit into
openvinotoolkit:mainfrom
Daksha1611:fix-mp3-stereo-downmix

Conversation

@Daksha1611

@Daksha1611 Daksha1611 commented Sep 11, 2026

Copy link
Copy Markdown

🛠 Summary

Fixes #4543.

readMp3() is documented in audio_utils.hpp as returning mono float32 PCM samples, and readWav() down-mixes stereo to match. readMp3() did not — it appended framesRead * mp3.channels floats per chunk, so a 2-channel file came back interleaved and twice as long as the frame count. The chat completions input_audio path copies that straight into a 1-D f32 tensor, so stereo MP3 reached the model as garbage audio at double the real duration. The resampling path was affected too, since outputLength was derived from the interleaved sample count.

Down-mixes per decode chunk, so the existing size guard still measures the final buffer. The metadata size check now passes 1 channel, matching the readWav call. Dropping the drmp3_uninit() before the overflow throw also removes a double-uninit — the enclosing catch (...) already calls it.

The two existing max-file-size tests build a joint-stereo frame and expected the interleaved count of 2304; they now expect the mono count of 1152. Adds mp3StereoIsDownmixedToMono.

I have no OVMS build container available, so this is not compiled against the full tree and CI will need to confirm the build. I did check the rewritten loop in isolation, including across chunk boundaries where the write-offset arithmetic matters. Draft for that reason.

🧪 Checklist

  • Unit tests added.
  • The documentation updated.
  • Change follows security best practices.

readMp3() is documented in audio_utils.hpp as returning mono float32 PCM
samples, and readWav() down-mixes stereo accordingly. readMp3() did not:
it appended framesRead * mp3.channels floats per chunk, so a 2-channel file
came back as an interleaved L/R stream twice as long as the frame count.

Downstream that buffer is consumed as a mono waveform - the chat completions
input_audio path copies it straight into a 1-D f32 tensor - so stereo MP3
input produced garbage audio at twice the real duration. The resampling path
was affected too: outputLength was computed from the interleaved sample count
and resample_audio() interpolated between adjacent L and R samples.

Down-mix per decode chunk so the existing size guard keeps measuring the final
buffer, and pass 1 channel to the metadata-based size validation to match
readWav().

Dropping the drmp3_uninit() call before the overflow throw also removes a
double-uninit: the enclosing catch(...) already calls it before rethrowing.

Tests: the two existing max-file-size tests used a joint-stereo frame and
encoded the interleaved count (2304); they now expect the mono count (1152).
Adds mp3StereoIsDownmixedToMono covering the contract directly.
@Daksha1611
Daksha1611 marked this pull request as ready for review September 12, 2026 07:09
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.

readMp3() never down-mixes stereo - returns interleaved samples where the API contract says mono

1 participant