udoprog / audio

A crate for working with audio in Rust

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Conversions cause distortion

HEnquist opened this issue · comments

The conversions currently scale with a different constant for positive and negative sample values.
https://github.com/udoprog/audio/blob/main/audio-core/src/translate.rs#L37
This creates distortion. Not very much, but it could and should be distortion-free.

Cpal does the same mistake, see my old comment here:
RustAudio/cpal#512 (comment)
In short, it's better to just divide by -MIN. For i8 it then works like this:

  • i8 to float: value as f32 / 128. Resulting range: -1.0 to +0.992.
  • float to i8: value*128, if below -128 or above +127 clamp to those values and cast to i8.
    This way, a 24-bit int can be losslessly converted to 32-bit float and back again. The int->float conversion will not reach +1.0, just very close. This may seem wrong but it's actually more correct.

Good call! Can't remember now where I borrowed those algos from but they clearly should be fixed (with some tests to sanity check different extremes).

I needed to do these conversions plus a few more in my DSP project. I ended up splitting that part off as a separate crate. Maybe that could be useful here? https://github.com/HEnquist/rawsample
It would be neat if it was possible to create an audio buffer directly from a buffer of raw bytes, for all the common sample formats. Especially the 24-bit ones are a handful.

🤔 Hm... proper wrapping and abstracting over raw audio buffers is on my (personal) todo list to try out. And it has crossed my mind that it might be the "more appropriate" way to build robust audio abstractions. While statically typing everything is really nice when everything is using the same format everywhere it can get quite awkward when you have dynamic components. Most notably I/O devices where they either don't support resampling internally, the API doesn't (WASAPI), or you don't want them to.

But for now we should at least ensure the translation is more correctly implemented as-is. But I'd love to talk more about the broader problem space involving dynamic I/O buffers. And thanks for linking the project!