RustAudio / rodio

Rust audio playback library
Apache License 2.0
1.66k stars 214 forks source link

Channel converter does not scale to more then 2 channels #558

Closed ideka closed 3 months ago

ideka commented 3 months ago

Playing sounds on the left channel works fine, but if I try to play something on the right channel, it plays on both (but maybe with slightly less volume on the left? hard to tell).

When running cargo run --example stereo, what you're supposed to hear is a tone that alternates right/left/right/left channels, but what I hear is both/left/both/left.

If I play the actual file (Assets/RL.ogg) in a different program, it sounds fine. But when playing via rodio, I get this issue.

I'm assuming this doesn't happen to everyone. Any ideas on what the issue could be? A weird quirk of my system/setup, or a rodio bug?

dvdsk commented 3 months ago

I think its a weird quirk as I can not reproduce it. And if it is it will be on Cpal's side (its what rodio uses to produce sound).

Could you try playing the file with symphonia's symphonia-play example? link: https://github.com/pdeljanov/Symphonia/tree/master/symphonia-play

It uses Cpal too. If you run into the same quirk there its highly probable its a cpal bug regarding your setup.

ideka commented 3 months ago

Oh good call. I just tried that, I ran cargo run -- ../../rodio/assets/RL.ogg on the symphonia-play directory. For some reason it seems to squish the file, playing the 27 second clip in only ~7 seconds which makes it sound really high pitched, but the R/L channels work fine.

I did some more testing. If I use a different audio device, both symphonia-play and the rodio stereo example work fine.

My headphones do have a fancy "surround emulation" feature that gives them 8 channels, which I usually keep disabled. Whether it's enabled or not, if I play a rodio Source that reports channels() to be 8, now I can get it to play only on the left or right.

The same root cause may be responsible for the weird fast playback in Symphonia. Maybe it's squishing samples meant for 8 channels into only 2... 8/2 is roughly 27/7... 🤔

ideka commented 3 months ago

Alright now I'm convinced the issue is my headphones simply have 8 channels, whether the feature is enabled or not, and in that case the failure point is here: https://github.com/RustAudio/rodio/blob/master/src/conversions/channels.rs#L57

It seems when playing a source with X channels to a device with Y channels, where Y > X, rodio will send the first X samples, and then repeat the last sample until filling Y. I think what it should do instead is alternate all the previous samples. So let's say, source with two channels to a device with 8 channels, what rodio sends is

And what it probably should send is

I'll see if I can get it working and maybe submit a PR.

dvdsk commented 3 months ago

My headphones do have a fancy "surround emulation" feature that gives them 8 channels, which I usually keep disabled. Whether it's enabled or not, if I play a rodio Source that reports channels() to be 8, now I can get it to play only on the left or right.

Alright now I'm convinced the issue is my headphones simply have 8 channels, whether the feature is enabled or not, and in that case the failure point is here: https://github.com/RustAudio/rodio/blob/master/src/conversions/channels.rs#L57

Good catch! and a nice investigation thank you very much! And kind of a funny bug tbh :)

I can imagine it can get quite complicated converting 3 channel audio to 9 channel outputs (if those even exist). As you said there may be a better way then cloning the last sample.

We would love a PR! If you do please add a comment describing why the channels are spread the way you do. If you don't then again thanks for the investigation!

There is a bit of a backlog of PR's to work through for me but I'll take a look if I see this pop up!

ideka commented 3 months ago

Awesome.

I got it working, it wasn't too bad.

If you do please add a comment describing why the channels are spread the way you do.

To be honest, the thing is I'm not 100% sure. It fixes this issue, it seems to make more sense, and I'm just kinda guessing it's how you're supposed to do that? Haha... I wish I could give a stronger justification.