lrbalt / libsoxr-rs

Rust wrapper for libsoxr (resampling library for sounds)
Other
8 stars 2 forks source link

`Soxr::process` crashes if used with more than one channel #4

Closed SolraBizna closed 3 years ago

SolraBizna commented 3 years ago

Soxr::process is a wrapper around soxr_process. It passes the lengths of the input and output buffers as a sample count, but what soxr_process actually expects (and returns) is sample-for-each-channel count. If the channel count is greater than one, soxr_process believes both buffers are longer than they actually are, quickly resulting in a crash.

To fix this, buf.len() and buf_out.len() must be divided by channel count before passing to soxr_process. In order to behave as the documentation for Soxr::process specifies, the results must also be multiplied by channel count.

I have confirmed in my own testing that, if I artificially divide the lengths of the passed slices by the channel count and multiply the returned values by the channel count, behavior is otherwise correct.

lrbalt commented 3 years ago

Thanks for this report. I've got a fix for a buffer problem locally (SEGFAULT). Do you have test or a minimal example that triggers this crash?

lrbalt commented 3 years ago

I just pushed a fix for the SEGFAULT that I found. Could you check if this fixes your issue?

SolraBizna commented 3 years ago

I'll be able to test it with my code in about ten hours. Looking at the commit in question, it looks like that might fix the crash if set_input is used, but not if Soxr::process is used instead.

I produced a very barebones test program:

use libsoxr::Soxr;

fn main() {
    let mut soxr = Soxr::create(1.0, 2.0, 2, None, None, None).unwrap();
    let mut in_buf: [f32; 2000] = [1.0; 2000];
    for n in 1000..2000 { in_buf[n] = -1.0 }
    let mut out_buf: [f32; 4000] = [999.0; 4000];
    let (i, o) = soxr.process(Some(&in_buf[0..1000]), &mut out_buf[0..2000]).unwrap();
    println!("i = {}, o = {}", i, o);
    let mut has_negative = false;
    for v in out_buf.iter() {
        if *v < 0.0 { has_negative = true; break }
    }
    if has_negative {
        println!("Output contains negative samples. in_buf was read out of bounds!");
    }
    if out_buf[2000] != 999.0 {
        println!("Output sentinel overwritten. out_buf was written out of bounds!");
    }
}
lrbalt commented 3 years ago

I was able to reproduce. I'll look into it further. Thanks!

lrbalt commented 3 years ago

I have just pushed a fix which lets your example pass. I've added your example as a test. Could you check if this works for you? I'll then push to crates.io if it works

SolraBizna commented 3 years ago

The test_process_stereo_2 test should probably panic! instead of println! for the out-of-bounds checks. Also, you might want to add a note in the documentation for Soxr::process that "number of samples" and "number of array elements" won't be the same if the channel count is greater than one, so that nobody finds the return value surprising. Otherwise, looks good to me; though I won't be able to test it in my application until ten-ish hours from now.

lrbalt commented 3 years ago

good points. I'll make asserts for these cases.

SolraBizna commented 3 years ago

My application now works with the latest git.

Thanks for the timely fix!

lrbalt commented 3 years ago

I will push a new version tomorrow. Thanks for the report and testing!

lrbalt commented 3 years ago

just pushed 0.2.6 to crates.io

SolraBizna commented 3 years ago

You left a debug println! on line 231 of soxr.rs in bb57921 :)

lrbalt commented 3 years ago

Ai, it just show you should never rust a new release ;-) Thanks. I've removed the println in 0.2.7