Giter VIP home page Giter VIP logo

rubato's People

Contributors

be-ing avatar daniellga avatar est31 avatar henquist avatar jessa0 avatar jonil avatar nbraud avatar udoprog avatar uklotzde avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rubato's Issues

Method for getting delay

The anti-aliasing filters introduce a delay. Presently there is no way to know the delay of a resampler. Add a trait method to get this information.

FftFixedInOut fails to process

Hi! Thank you very much for your work. I have encountered an issue that I am unsure of whether it is a bug, or am I not understanding something. I am trying to add real-time resampling to my sound processing pipeline using FftFixedInOut. My sound processing model accepts only 48khz samples, and my data is 44.1 khz, and the processing is done in 10 ms chunks. I have initialized 2 resamplers

let u = FftFixedInOut::<f32>::new(44100, 48000, 441, channels).unwrap();
let d = FftFixedInOut::<f32>::new(48000, 44100, 480, channels).unwrap();

And I am having no troubles passing a chunk of length 441, sample rate 44.1khz to my resampler, then to my model. However, I am having troubles processing the output chunk with the downsampler. For some reason, it asks for the input buffer size 640, and output buffer of size 588. I thought that maybe, chunk length of 480 is too small for the resampler, and the next possible values with the same resample ratio as (480, 441) are (640, 588) but then it doesn't make sense that there was no trouble on the upsampling processing, as the operation should be symmetrical. What do you think can be the reason of that behaviour?

hann function

Hi! I don't know which one of you is right, but your hann window is not matching Librosa's hann window.

import numpy as np
import librosa
import matplotlib.pyplot as plt

w = librosa.filters.get_window("hann", 2048, fftbins = False)
w[:10]
w[-10:]
array([0.00000000e+00, 2.35539484e-06, 9.42155716e-06, 2.11984204e-05,
       3.76858736e-05, 5.88837614e-05, 8.47918841e-05, 1.15409998e-04,
       1.50737813e-04, 1.90774999e-04])
array([1.90774999e-04, 1.50737813e-04, 1.15409998e-04, 8.47918841e-05,
       5.88837614e-05, 3.76858736e-05, 2.11984204e-05, 9.42155716e-06,
       2.35539484e-06, 0.00000000e+00])

Apparently the fix would be to introduce the b term below:

/// Standard Hann window
pub fn hann<T>(npoints: usize) -> Vec<T>
where
    T: Sample,
{
    let mut window = vec![T::zero(); npoints];
    let pi2 = T::coerce(2.0) * T::PI;
    let np_f = T::coerce(npoints);
    let a = T::coerce(0.5);
    let b = T::coerce(1.0);
    for (x, item) in window.iter_mut().enumerate() {
        let x_float = T::coerce(x);
        *item = a - a * (pi2 * x_float / (np_f - b)).cos();
    }
    window
}

test result:

fn get_window_test() {
    let a = get_window::<f64>(2048, WindowFunction::Hann);
    println!("{:?}", &a[0..10]);
    println!("{:?}", &a[a.len()-10..]);
}

[0.0, 2.355394838837732e-6, 9.421557163713512e-6, 2.11984204002702e-5, 3.768587359187503e-5, 5.8883761400674306e-5, 8.479188410903671e-5, 0.0001154099976216072, 0.00015073781346730541, 0.0001907749988023788]
[0.0001907749988023788, 0.00015073781346730541, 0.0001154099976216072, 8.479188410903671e-5, 5.8883761400674306e-5, 3.768587359187503e-5, 2.11984204002702e-5, 9.421557163713512e-6, 2.355394838837732e-6, 0.0]

Iterator-based version of process_into_buffer()

#49 proposes making process_into_buffer() more generic by accepting a slice of mutable references instead of a slice of vectors. However, even this doesn't cater to all use cases. In my codebase, I have a multi-channel signal type which can have an arbitrary number of channels whose number isn't known at compile time. I can't make its interface return &mut [&mut [f32]] because that would be unsound: the caller could assign an arbitrary reference to the inner slice instead of using it in the intended way of simply mutating the inner slices. I'm solving the issue by having an interface that returns iterators of slices, so you can still do stuff like for channel in signal.channels_mut() { for sample in channel { *sample *= 0.1 } }.

At the moment, interfacing my signal type with Rubato requires me to have a temporary buffer which I use to copy to/from my signal type and that's then processed by Rubato. If Rubato had a version of process_into_buffer() that accepted iterators of slices, I could get rid of the extra copies and temporary buffers.

I would be happy to submit a pull request for this if this sounds like something that you would consider accepting.

validate_buffers is unsound in certain situations

In asynchro_fast.rs, validate_buffers checks the lengths of wave_in and wave_out and is subtly unsound.

as_ref and as_mut do not necessarily return pointers to the same structure every time, so the length of the objects can change between successive calls to as_mut

Resample a Vec<Vec<f64>>

Hi! Thank you for your work.

I am rather new to Rust and I am struggling to resample a Vec<Vec<f64>>. I saw you put some examples but they seem to read directly from a file. I tried the code below, which works for FftFixedIn but doesn't work for FftFixedInOut. It gives an error: WrongNumberOfFrames { channel: 0, expected: 104736, actual: 96000 }. Is it expected some treatment to the vector (reshaping, interpolation, etc...) before using process() in each case? Could you help me with a working example?

Here's what I am trying to do, passing chunk_size_in as the as the entire length of a single channel (96000):

let mut resampler = FftFixedIn::<f64>::new(sr_in, sr_out, chunk_size_in, sub_chunks, channels);

//create vec of shape (2, 96000) from ndarray
let mut waves_in: Vec<Vec<f64>> = Vec::with_capacity(channels);

for j in 0..channels {
    waves_in.push(arr_in.slice(s![.., j]).to_vec());
}

let waves_out = resampler.process(&waves_in).expect("cannot process resampler");

and

let mut resampler = FftFixedInOut::<f64>::new(sr_in as usize, sr_out as usize, chunk_size_in, channels);
  
//create vec of shape (2, 96000) from ndarray
let mut waves_in: Vec<Vec<f64>> = Vec::with_capacity(channels);
   
for j in 0..channels {
    waves_in.push(arr_in.slice(s![.., j]).to_vec());
}

let waves_out = resampler.process(&waves_in).expect("cannot process resampler");

Specify Framecount of Input and Output

Is there a way to resample while not changing the number of frames?
I know that resampling is changing the amount of samples present in the buffer, but I have to use an audio process that only accepts one specific sample rate / frame size config.

unit tests

Hi! I was looking this repo for some kind of tests for the differente resamplers against expected outcomes and didn't find any... I am using Rubato on my R package and would like to use these tests in R to assert I am doing everything ok when using Rubato. Do you happen to have these tests to share? Thank you in advance!

Method to clear FFT buffers

Unless I'm mistaken, the current implementation of the FftResampler has no way to clear the filter buffers. This is needed to be able to properly re-use the resampler for new sample sources.

`output_frames_max` returns an incorrect upper bound for at least `FftFixedIn`

As described in #62 (comment), output_frames_max returns an incorrect value for FftFixedIn and maybe other resamplers with a variable number of output frames. Given that this violates the API design principle of least surprise, I think that the return value should be fixed, or if not possible due to the how the resampler works, an error should be returned.

Minimal reproducible example

use rubato::{FftFixedIn, Resampler};

fn main() {
    let audio_data = vec![0.0_f32; 44100 * 5];

    let mut resampler = FftFixedIn::<f32>::new(44100, 40000, 512, 2, 1).unwrap();

    // The following buffer could be more concisely created with resampler.output_buffer_allocate(),
    // but it doesn't work: https://github.com/HEnquist/rubato/pull/62
    let mut output_buf = vec![vec![0.0; resampler.output_frames_max()]; resampler.nbr_channels()];

    for chunk in audio_data.chunks(512) {
        resampler
            .process_into_buffer(&[chunk], &mut output_buf, None)
            .expect("Unexpected resampling error");
    }
}

Output

thread 'main' panicked at 'Unexpected resampling error: Insufficient buffer size 512 for output channel 0, expected 800', src/main.rs:15:14
stack backtrace:
   0: rust_begin_unwind
             at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/std/src/panicking.rs:578:5
   1: core::panicking::panic_fmt
             at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/panicking.rs:67:14
   2: core::result::unwrap_failed
             at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/result.rs:1651:5
   3: core::result::Result<T,E>::expect
             at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/result.rs:1033:23
   4: rubato_issue_repro::main
             at ./src/main.rs:13:9
   5: core::ops::function::FnOnce::call_once
             at /rustc/1a5f8bce74ee432f7cc3aa131bc3d6920e06de10/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

State of the stack frame where the variables that trigger the validation error are set

Stack frame state

Remove initial silence?

Hello 👋

Thanks a lot for this awesome library! 🎉

I have a question: is it possible to remove, somehow, the initial silence created by the anti-aliasing filters?

Thanks in advance!

Integer sample support

It looks the library supports only floats, would be fine to add support for i16 samples?

Deterministic resampling

Given a rubato::FftFixedIn with the same input for:

  • new()
  • process_into_buffer()

with the same buffer capacities, is the resampling process deterministic?

i.e. will 2 instances of resampling with the same inputs have the same output every time? (excluding small float differences)

Real-time: Write output into `AsMut<[T]>` instead of `Vec<T>`

The real-time API is not as generic as it ought to be. The output buffer should be a slice of (mutable) references instead of a slice of vectors.

Actual

    fn process_into_buffer<V: AsRef<[T]>>(
        &mut self,
        wave_in: &[V],
        wave_out: &mut [Vec<T>],
        active_channels_mask: Option<&[bool]>,
    ) -> ResampleResult<()>;

Desired

    fn process_into_buffer<Vin: AsRef<[T]>, Vout: AsMut<[T]>>(
        &mut self,
        wave_in: &[Vin],
        wave_out: &mut [Vout],
        active_channels_mask: Option<&[bool]>,
    ) -> ResampleResult<()>;

If the size of the output buffers is insufficient then the invocation should return with an error.

This is the expected signature required for a generic audio processing graph. Currently resampling requires a separate buffer and copying the results.

Typically you would use a crate like rsor for composing those slices of (mutable) references.

Resample audio to fit buffer?

Is it possible to resample audio to fit a dynamic buffer length?
I tried using the sample ratio, but it's not exact and the difference is quite big depending on the scenario.

+/- 10% limitation for asynchronous resamplers

I am working on a DJ application and considering using Rubato. The requirement of staying within +/- 10% of the original resampling ratio is quite limiting for my use case. It's not terrible considering DJ turntables have pitch faders with +/- 8% range, but it does create a creative limitation for software. Is this limitation inherent to the algorithms used? Is there any plan to increase the accepted resampling ratio range or remove the limitation altogether?

asynchronous sampler crashes with access violation in fn interp_lin<T> when using chunksize of 1

i got a access violation exception calling resampler.process_into_buffer(&self.resampler_input, &mut self.resampler_output, Some(&self.channel_mask)) ;
using rubato = "0.14.1"
using FastFixedOut = FastFixedOut::new(96000.0/44100.0, 100.0, PolynomialDegree::Linear, 1, 8);
using Resampler::input_buffer_allocate and Resampler::output_buffer_allocate(); methods to preallocate the io buffers.
this happened after a few successful calls to proccess_into_buffer() so i'm completely lost on what could be happening.
image
image
image

How to do downsampling?

Hello,

Thanks a lot for this crate!

The provided upsampling example from 44100 -> 48000 works very well, however I was wondering how one can do downsampling. Slighly adapting your code example to downsample from 48000 -> 16000 as follows:

    let params = InterpolationParameters {
        sinc_len: 256,
        f_cutoff: 0.95,
        interpolation: InterpolationType::Nearest,
        oversampling_factor: 160,
        window: WindowFunction::BlackmanHarris2,
    };
    let mut resampler = SincFixedIn::<f64>::new(
        16000 as f64 / 48000 as f64,
        params,
        1024,
        2,
    );

    let waves_in = vec![vec![0.0f64; 1024];2];
    let waves_out = resampler.process(&waves_in).unwrap();

throws this error:

thread 'main' panicked at 'Tried to interpolate for index 1281, max for the given input is 1279', /home/patrick/.cargo/registry/src/github.com-1ecc6299db9ec823/rubato-0.8.0/src/interpolator_avx.rs:146:9

I would be very grateful if you could provide a downsampling example using your library!

Thanks

Provide interface for interleaved resampling

It would be great if we can provide options for input/output formats (interleave/deinterleaved).

This can be done in userland (interleaving/deinterleaving before hand). But by integrating it nothing new has to be allocated/processing step needed and only the iterating logic needs to change inside the synchro process.

How to down sample audio?

Hi, I can only find examples for upsampling audio data. Currently I am trying to down sample from 48000 Hz to 16000 Hz, but I do not understand why so less values are returned.

For example I expect when passing in 48000 values that the result would be 16000 values, meaning the duration should be kept to 1 sec.
But with the following code, when passing in 4 sec recording with 48000 Hz sample rate (resulting in 192000 values), I get only a vector sized 1x639 values:

    use rubato::{Resampler, SincFixedIn, SincInterpolationType, SincInterpolationParameters, WindowFunction};
    let params = SincInterpolationParameters {
        sinc_len: 256,
        f_cutoff: 0.95,
        interpolation: SincInterpolationType::Linear,
        oversampling_factor: 128,
        window: WindowFunction::BlackmanHarris2,
    };
    let mut resampler = SincFixedIn::<f32>::new(
        target_sr as f64 / origin_sr as f64,
        1.0,
        params,
        2048,
        1,
    ).unwrap();

    let waves_out = resampler.process(&waves_in, None).unwrap();
    waves_out

I played also with the configuration parameters a bit. When increasing chunk_size from 2048 to 48000 then I get around 15000 values back. But at the end I did not get the expected output.

Could you help in this case?

Expected would be: 192000 values as input (4 sec recording) => 64000 values output (16000 Hz as 4 sec recording)

fftfixedout example problem

I am trying to mimic your examples and they work pretty well for the fftfixedin and fftfixedinout examples. The number of samples the resampler is outputting for the sinwave example you provided, considering a downgrade in sampling rate from 44.1 khz to 22 khz, is 21560 and 22000, respectively.
However, for the fftfixedout example, I am getting 21504 samples against 16384 from your fftfixedout64.rs. The sum(wf) from your example is also giving a value of 4.24... Wasn't it supposed to be near 0?

My implementation is showed below. It is the same implementation for fftfixedin, fftfixedinout and fftfixedout. The only difference is the variable notfixedin is true for the fftfixedout case.
It basically has a Vec as input, asks initially the resampler for input_frames_next() and gives these samples from the Vec to the resampler. At the end of the loop, it asks again for input_frames_next. It repeats doing it until input_frames_next > number of available samples in the Vec when it breaks, losing the last samples of the Vec.

Could you help me where am I getting it wrong?

resample.rs

pub fn resample_fftfixedin(
    arr_in: ArrayView2<f64>,
    sr_in: i32,
    sr_out: i32,
    chunk_size_in: i32,
    sub_chunks: i32,
    channels: usize,
) -> Array2<f64> {
    let resampler = FftFixedIn::<f64>::new(
        sr_in as usize,
        sr_out as usize,
        chunk_size_in as usize,
        sub_chunks as usize,
        channels,
    ).expect("cannot create FftFixedIn resampler");

    fft_resampler(arr_in, channels, resampler, false)
}

pub fn resample_fftfixedinout(
    arr_in: ArrayView2<f64>,
    sr_in: i32,
    sr_out: i32,
    chunk_size_in: i32,
    channels: usize,
) -> Array2<f64> {
    let resampler = FftFixedInOut::<f64>::new(
        sr_in as usize,
        sr_out as usize,
        chunk_size_in as usize,
        channels,
    ).expect("cannot create FftFixedInOut resampler");

    fft_resampler(arr_in, channels, resampler, false)
}

pub fn resample_fftfixedout(
    arr_in: ArrayView2<f64>,
    sr_in: i32,
    sr_out: i32,
    chunk_size_out: i32,
    sub_chunks: i32,
    channels: usize,
) -> Array2<f64> {
    let resampler = FftFixedOut::<f64>::new(
        sr_in as usize,
        sr_out as usize,
        chunk_size_out as usize,
        sub_chunks as usize,
        channels,
    ).expect("cannot create FftFixedOut resampler");

    fft_resampler(arr_in, channels, resampler, true)
}

fn fft_resampler<T: Resampler<f64>>(
    arr_in: ArrayView2<f64>,
    channels: usize,
    mut resampler: T,
    notfixedin: bool,
) -> Array2<f64> {
    let samples_per_channel_in = arr_in.nrows();
    let mut nbr_frames_next = resampler.input_frames_next();
    let mut v_out: Vec<Vec<f64>> = Vec::with_capacity(channels);

    for _ in 0..channels {
        v_out.push(Vec::<f64>::new());
    }

    let mut idx = 0_usize;

    loop {
        let mut waves_in: Vec<Vec<f64>> = Vec::with_capacity(channels);

        for ch in 0..channels {
            let sli = arr_in.slice(s![idx..(idx + nbr_frames_next), ch]);
            waves_in.push(sli.to_vec());
        }
        
        let mut waves_out = resampler
            .process(&waves_in, None)
            .expect("cannot process resampler");

        for ch in 0..channels {
            v_out[ch].append(&mut waves_out[ch]);
        }

        idx += nbr_frames_next;

        if notfixedin {
            nbr_frames_next = resampler.input_frames_next();
        }

        if idx + nbr_frames_next > samples_per_channel_in {
            break;
        }
    }

    let samples_per_channel_out: usize = v_out[0].len();
    let mut arr_out = Array2::<f64>::zeros((samples_per_channel_out, channels));

    for ch in 0..channels {
        for sample in 0..samples_per_channel_out {
            arr_out[[sample, ch]] = v_out[ch][sample]
        }
    }

    arr_out
}

lib.rs

/// Resample the audio data from sr_in to sr_out.
/// @export
#[extendr]
pub fn resample(
    r_arr: RMatrix<f64>,
    sr_in: i32,
    sr_out: i32,
    interpolation: &str,
    sinc_len: i32,
    f_cutoff: f64,
    oversampling_factor: i32,
    window: &str,
    resampler: &str,
    #[default = "1024"] chunk_size_in: i32,
    #[default = "1024"] chunk_size_out: i32,
    #[default = "2"] sub_chunks: i32,
) -> Robj {
    if sr_in <=0 { 
        panic!("sr_in must be positive");
    } else if sr_out <=0 { 
        panic!("sr_out must be positive");
    } else if sinc_len <=0 { 
        panic!("sinc_len must be positive");
    } else if oversampling_factor <=0 { 
        panic!("oversampling_factor must be positive");
    }

    let arr_in: ArrayView2<f64> =
        ArrayView2::from_robj(&r_arr).expect("cannot convert Robj to ArrayView2");

    let channels = r_arr.ncols();

    let resampled_arr: Array2<f64> = match resampler {
        "fftfixedin" => resample::resample_fftfixedin(
            arr_in,
            sr_in,
            sr_out,
            chunk_size_in,
            sub_chunks,
            channels,
        ),
        "fftfixedinout" => 
            resample::resample_fftfixedinout(
                arr_in,
                sr_in,
                sr_out,
                chunk_size_in,
                channels
        ),
        "fftfixedout" => resample::resample_fftfixedout(
            arr_in,
            sr_in,
            sr_out,
            chunk_size_out,
            sub_chunks,
            channels,
        ),

I call these functions from R, so in R I do:

test.R

sr_0 <- 44100
sr_f <- 22000
# wave is the same sinusoidal wave as you generated in your python script (the same as your script but translated to R)

wave_resampled_fftfixedin <- resample(
                                    wave,
                                    sr_0,
                                    sr_f,
                                    "linear",
                                    128L,
                                    0.925914648491266,
                                    2048L,
                                    "blackman2",
                                    "fftfixedin")

wave_resampled_fftfixedinout <- resample(wave,
                                    sr_0,
                                    sr_f,
                                    "linear",
                                    128L,
                                    0.925914648491266,
                                    2048L,
                                    "blackman2",
                                    "fftfixedinout")

wave_resampled_fftfixedout <- resample(wave,
                                    sr_0,
                                    sr_f,
                                    "linear",
                                    128L,
                                    0.925914648491266,
                                    2048L,
                                    "blackman2",
                                    "fftfixedout")

PS: extendr allows the use of default arguments, so for the arguments that are lacking in the calls above, you can check the functions definitions with the #default flag. The default arguments are:
#[default = "1024"] chunk_size_in: i32,
#[default = "1024"] chunk_size_out: i32,
#[default = "2"] sub_chunks: i32,

Silent failure on Apple Silicon

Rubato seems to silently fail when used on Apple Silicon. I have tested this with all of the examples and they just seem to silently fail on my M2 mac. But work fine on an x86 based linux VM on AWS with the exact same configuration. I also don't get any logs with the log feature when this happens.

rubato is not realtime safe

Rubato allocates its output buffer on every call to Resample::process. I think this should be switched to taking a mutable reference to a buffer. This way, the resamplers with fixed size output buffers could be used in realtime code. The calling code would be responsible for setting the capacity of the Vec in a non-realtime thread before passing it to Rubato. I also suggest adding a debug_assert! checking that the Vec's capacity >= the output size.

Also, there are log macros which allocate Strings in various parts of the code. I suggest to hide these behind a Cargo feature.

assert_no_alloc would be useful for verifying that there are no other allocations in the code besides the constructors.

Question about chunk size

I'm currently trying to make my dj engine (https://codeberg.org/obsoleszenz/librecdj/) sample accurate.
Until now I used SincFixedOut and whenever the chunk size from the audio host changed, i offloaded the creation of a new SincFixedOut on a worker thread.
Buf to make it sample accurate, i want to partition the buffer into smaller buffers, according to the midi events and their timestamp. The slices would be all of different sizes, meaning i would need multiple Resamplers. As this is a realtime context i can't heap allocate a new Resampler and I wonder if it would be possible to change the chunk size on an existing resampler?
Or am I overseeing something?

Resampling without adjusting pitch

Hi, is there a way to use this library to resample audio without changing the pitch of the played back audio?

I.e. if I have a device that is set to output sound at 48000 and I set the resample ratio to 1.5, I want the audio to play back 1.5x as fast but with the same pitch as it was on 1.0.

Using this library for Clock Drift correction

I am building an audio transmission system over Ethernet in Rust. I have a audio stream with 64 sample buffers (so quite small) and for clock drift correction I have to add or remove a sample from time to time.

For now I am just removing or adding a sample and smooth using linear interpolation, so quite an easy approach. It works quite good and is not noticeable as long as you don't play back a high frequency (1KHz) Sinus but I still want to get rid of these artifacts.

Is this library suited for this purpose? I tried something using the FFT sampler:

let mut resampler = rubato::FftFixedIn::<f32>::new(
        48000,
        48000,
        64,
        64,
        2,
    );

but I am not able to change the output sample rate from time to time. To reinstantiate the sampler also doesn't work, as this generates artifacts.

Speed of talk change for small input (~1s) from 44100 to 16000 Mono

I tried the library with different audio files

For very small input ~1s or less the speed of audio is changed

Based on your example I tried with and without skip_frame function but just flatten the result, no luck

This is my result for this small input

It's correctly working for audio > 1s , is this known or is it a parameter issue ?

My script:

/**
 * This is the Rust main smart ,function, use all pure function inside
 * Main logic is here
 */
fn re_sample_audio_buffer(
  buffer: Vec<Vec<f32>>,
  input_sample_rate: u16,
  output_sample_rate: u16,
  input_channels: u8,
  output_channels: u8,
) -> Vec<f32> {
  let fs_in = input_sample_rate as usize;
  let channels = input_channels as usize;
  let nbr_input_frames = buffer[0].len(); // ? because for stereo
  let duration_total = Instant::now();

  let fs_out = output_sample_rate;
  debug!("Sample {} for output {}", &fs_in, &fs_out);

  // Create buffer for storing output
  let mut outdata =
    vec![
      Vec::with_capacity(2 * (nbr_input_frames as f32 * fs_out as f32 / fs_in as f32) as usize);
      channels
    ];

  let f_ratio = fs_out as f64 / fs_in as f64;

  let mut resampler = FftFixedInOut::<f32>::new(
    input_sample_rate as usize,
    output_sample_rate as usize,
    1024,
    // f_ratio,
    // 1.1,
    // PolynomialDegree::Linear,
    // 1024,
    output_channels as usize,
  )
  .unwrap();

  // Prepare
  let mut input_frames_next = resampler.input_frames_next();
  let resampler_delay = resampler.output_delay();
  let mut outbuffer = vec![vec![0.0f32; resampler.output_frames_max()]; channels];
  let mut indata_slices: Vec<&[f32]> = buffer.iter().map(|v| &v[..]).collect();

  // Process all full chunks
  while indata_slices[0].len() >= input_frames_next {
    let (nbr_in, nbr_out) = resampler
      .process_into_buffer(&indata_slices, &mut outbuffer, None)
      .unwrap();
    for chan in indata_slices.iter_mut() {
      *chan = &chan[nbr_in..];
    }
    append_frames(&mut outdata, &outbuffer, nbr_out);
    input_frames_next = resampler.input_frames_next();
  }

  // Process a partial chunk with the last frames.
  if !indata_slices[0].is_empty() {
    let (_nbr_in, nbr_out) = resampler
      .process_partial_into_buffer(Some(&indata_slices), &mut outbuffer, None)
      .unwrap();
    append_frames(&mut outdata, &outbuffer, nbr_out);
  }

  let nbr_output_frames = (nbr_input_frames as f64 * fs_out as f64 / fs_in as f64) as usize;

  let duration_total_time = duration_total.elapsed();
  debug!("Resampling buffer took: {:?}", duration_total_time);

  flatten_frames(outdata)

  // skip_frames(outdata, resampler_delay, nbr_output_frames).unwrap()
}


pub fn flatten_frames(frames: Vec<Vec<f32>>) -> Vec<f32> {
  let num_channels = frames.len();
  let num_samples = frames[0].len(); // On suppose que tous les canaux ont la même longueur

  let mut flattened_data: Vec<f32> = Vec::with_capacity(num_channels * num_samples);

  for sample_index in 0..num_samples {
    for channel_index in 0..num_channels {
      flattened_data.push(frames[channel_index][sample_index]);
    }
  }

  flattened_data
}

Review and make asserts more helpful

So I've recently taken this library for a spin, and one of the things I noted is that it panics in case you give it the wrong kind of parameters.

This is characterized by the asserts, which is not bad in themselves, but some of the asserts are quite nondescript like this one:
https://github.com/HEnquist/rubato/blob/master/src/interpolator_sse.rs#L23

If you backtrack a bit you'll realize that one or more of your input parameters were incorrect (chunk_size or sinc_len, can't remember from the top of my head).

So the suggestion is:

  • It would be helpful if all of the input parameters were validated as early as possible, and that panic messages include instructions of what you did wrong and how to fix it. Generally this can be done with asserts by adding a custom message as the second parameter.
  • Review the existing asserts and convert them to errors instead where appropriate. The one above should probably remain (since it guarantees safety of the wrapping API), but an underlying caller could for example validate the parameters. Panics can be quite hard to recover from. With an error the user can decide whether or not to panic.

Make `realfft` an optional dependency?

This looks like a really nice crate, but I was wondering if it would be possible to make realfft an optional dependency, for users that only want/need the fast/low-quality resampling? Or are all paths using the fft?

Usage with interleaved data

Hey. I'm new to the world of sample rates, so I probably missed something. I receive 480 bytes (2 channels; 240bytes / channel) of opus decoded samples every x milliseconds. Opus uses 48khz f32 sample format, so I'm trying to resample this to 44.1khz f32. Because the data is interleaved, I also have to reformat it into the format that rubato uses. Unfortunately the resampled data sounds horrible(Not recognizable at all), so I'm probably doing something wrong. I know the input data is correct, because without resampling it, it sound perfect. Here's what I've tried (pseudo-codey):

pub fn get_resampler(in_hz: usize, out_hz: usize, ch_in: usize, chunk_size: usize) -> SincFixedIn<f32> {
        let sinc_len = 128;
        let f_cutoff = 0.925914648491266;
        let params = InterpolationParameters {
            sinc_len,
            f_cutoff,
            interpolation: InterpolationType::Linear,
            oversampling_factor: 2048,
            window: WindowFunction::Blackman2,
        };
        SincFixedIn::<f32>::new(
            out_hz as f64 / in_hz as f64,
            params,
            chunk_size,
            ch_in,
        )
}
let resampler = Audio::get_resampler(48000, 44100 as usize, 2 as usize, 480 / 2);

// This function gets called every x milliseconds
pub fn decode_and_queue_packet(&mut self, data: &[u8]) {
        let decoder = self.decoder.as_mut().unwrap();
        
        let mut out = [0f32; 5760*2];
        let read = decoder.decode_float(&data, &mut out, false).unwrap(); // read is always 480

        let buf = self.buffer.as_mut().unwrap(); // Output buffer, this will be read to the OS which will in turn output the sample
        let mut split = [Vec::with_capacity(read), Vec::with_capacity(read)];
        for ch in out[0..read].chunks_exact(2) {
            split[0].push(ch[0]);
            split[1].push(ch[1]);
        }
        let resampled = resampler.process(&split).unwrap();

        for ch in resampled {
            buf.push(ch[0]).unwrap();
            buf.push(ch[1]).unwrap();
        }
}

Could you point me in the direction of what I'm doing wrong?
Feel free to close the issue if you feel like this doesn't belong here.

FtfFixedInOut resampler expects wrong number of frames

Hello!
I've been coding a voice recorder using this amazing library. I've now ran into a problem I've been trying to fix for ours and just think that it must be something to do with the library, since I just don't see any way for me to fix it.
I'm using cpal to record data from the microphone at a buffer size of 1024 in a stereo channel, since microphones usually have a sample rate of 44100 hz instead of 48000 hz (the sample rate I need to encode with opus), I used this resampler library like this:

let mut resampler = FftFixedInOut::<f64>::new(
    sample_rate as usize,
    opus_sample_rate as usize,
    1024,
    channels as usize,
).unwrap();

Every time I try to pass my 1024 frames per channel into the library it just says "Wrong number of frames 1024 in input channel 0, expected 1029".

I also tried measuring the size of the vectors like this:

// Convert into seperate channels
let channels = extract_channels(data, channels as usize);

println!("channels: {:?}", channels.len());
println!("samples in 0: {:?}", channels[0].len());
println!("samples in 1: {:?}", channels[1].len());

// Resample
let resampled = resampler.process(&channels, None).unwrap();

Then I get the following output:

channels: 2
samples in 0: 1024
samples in 1: 1024
thread 'cpal_alsa_in' panicked at 'called `Result::unwrap()` on an `Err` value: Wrong number of frames 1024 in input channel 0, expected 1029', src/record.rs:57:64

I don't know why this happens, but I'm kind of new to rust, so I'm sorry if I'm getting something wrong here.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.