RustAudio / rodio

Rust audio playback library
Apache License 2.0
1.78k stars 232 forks source link

Slight clipping, even when using Sink::set_volume to reduce volume #340

Open Boscop opened 4 years ago

Boscop commented 4 years ago

I always get slight clipping (only at the very peaks) with this code (using rodio 0.11):

use rodio::{default_output_device, Decoder, Sink, Source};
use std::{fs::File, io::BufReader};

let mut decoder = Decoder::new(BufReader::new(File::open(in_path_audio)?))?;
// skip offset frames
let audio_frames_to_skip = (time_offset_sec as f64 * decoder.sample_rate() as f64) as _;
skip_frames(&mut decoder, audio_frames_to_skip);
// spawn new thread as workaround: https://github.com/RustAudio/rodio/issues/214
let (tx, rx) = channel();
let _thread_playback = thread::Builder::new()
    .name("start playback".to_string())
    .spawn(move || -> Result<()> {
        let device = default_output_device().expect("default_output_device");
        let playback_sink = {
            // like rodio::play_once()
            let sink = Sink::new(&device);
            sink.append(decoder);

            sink.set_volume(0.5); // it's still clipping the same, just playing at lower volume

            sink
        };
        while let Ok(pause) = rx.recv() {
            if pause {
                playback_sink.pause();
            } else {
                playback_sink.play();
            }
        }
        Ok(())
    })
    .expect("spawn");

Any idea why?

You can test it with this audio, with which it definitely happens, right from the start. (And it doesn't happen in any other normal music player.)

tobx commented 3 years ago

I can confirm this. I also tried it with the proposed song and you can hear it very well with it. Even with simple code like:

fn main() {
    let device = rodio::default_output_device().unwrap();
    let sink = Sink::new(&device);
    let file = File::open("song.flac").unwrap();
    let source = Decoder::new(BufReader::new(file)).unwrap();
    sink.append(source);
    sink.set_volume(0.75); // clipping also occurs without this
    sink.sleep_until_end();
}

I am on macOS Catalina and strangely it only happens with external audio interfaces. I tested it with my dock, external sound interface and headphone AMP. Clipping sounds happen with all three while there is no problem with any other audio player playing the same song. Also no problem putting the headphones in the internal headphone jack of the Mac.

The only thing comes to mind is that the external interfaces need a larger buffer size, but I am not very experienced with this.

Boscop commented 3 years ago

Thanks for confirming this. Btw, I'm on windows, and I was using my external UR22 audio interface.

tobx commented 3 years ago

I was digging a little more into this. My audio file was 44.1 kHz, but my audio interface was set to 96 kHz. When I set the interface to 44.1 everything played smoothly. Trying the opposite way (interface 44.1, audio file 96) everything played smoothly too. Interface 48 and audio file 44.1 sounded good too (for my ears).

So my guess is that rodio does not do any resampling at all. Since 44.1 kHz audio has not enough samples for playing 96 kHz, it might just fill the gaps with zeros. Please correct me if I am wrong here.

Anyway I found a sample lerp function here: https://docs.rs/rodio/0.13.0/rodio/trait.Sample.html#tymethod.lerp

I am not sure though how to access the sample directly in rodio.

Boscop commented 3 years ago

Hm, but in my case, the audio interface was set to 44.1 and the file also had 44.1 kHz.

tobx commented 3 years ago

Yes, you are right, this was a different issue. I was listening to another file and forgot to run it with --release, so the resampling was probably too much for my CPU. It was working when setting it to --release.

Anyway with your example song (Sour Candy) I can still clearly here the clipping compared to playing the same file with other players, even when building with --release and even when the audio interface was set to 44.1 kHz.

tobx commented 3 years ago

Ok, I am sorry for the confusion, multiple audio interfaces and different testing files, so I mixed up things again.

So no, when setting the interface to the same rate as the file and building with --release it works also with your example. So I still think in my case it is a resampling issue.