inoftrobinson / inoft_vocal_framework

Create Alexa Skills, Google Actions, Samsung Bixby Capsules and Siri 'skills', with the same codebase. Then deploy automatically your app to AWS. All that in Python !
MIT License
9 stars 4 forks source link

Realtime Dynamic Audio - Handle resampling of two to one audio tracks, and from 24 bits to 16 bits. #25

Closed inoftrobinson closed 3 years ago

inoftrobinson commented 3 years ago
inoftrobinson commented 3 years ago

Supported channels conversions with the following handlers :

Handler resample sample rate without channels conversion

fn resample_sample_rate_without_channels_conversion(samples: WavSamples<BufReader<File>, i16>, resample_interval: f32) -> Vec<i16> {
    let mut out_samples: Vec<i16> = Vec::new();
    let mut resample_count: f32 = 0.0;
    for (i, sample) in samples.enumerate() {
        let sample_value = sample.unwrap();
        let float_index = i as f32;
        while float_index > resample_count {
            out_samples.push(sample_value);
            resample_count += resample_interval;
        }
    }
    out_samples
}

Handler resample sample rate and convert one channel to two

fn resample_sample_rate_and_channels_from_one_to_two(samples: WavSamples<BufReader<File>, i16>, resample_interval: f32) -> Vec<i16> {    
    // This function is almost exactly like the resample_sample_rate_without_channels_conversion function.
    let mut out_samples: Vec<i16> = Vec::new();
    let mut resample_count: f32 = 0.0;
    for (i, sample) in samples.enumerate() {
        let sample_value = sample.unwrap();
        let float_index = i as f32;
        while float_index > resample_count {
            // To change the channel from one to two, we just need to add twice the sample_value to the out_samples.
            // Because a WAV file with 2 channels is represented like [240, 240, 102, 102, 720, 720], where the
            // first two samples are played together as the same sample, but coming from different channels.
            out_samples.push(sample_value);
            out_samples.push(sample_value);
            resample_count += resample_interval;
        }
    }
    out_samples
}

Handler resample sample rate and convert two channels to one

fn resample_sample_rate_and_channels_from_two_to_one(samples: WavSamples<BufReader<File>, i16>, resample_interval: f32) -> Vec<i16> {
    let mut out_samples: Vec<i16> = Vec::new();
    let mut resample_count: f32 = 0.0;

    let num_channel_unique_samples = samples.len() / 2;
    let samples: Vec<i16> = samples.map(|sample| sample.unwrap()).collect();
    for i in 0..num_channel_unique_samples {
        let float_index = i as f32;
        let sample_value = (samples[(2 * i)] / 2) + (samples[(2 * i) + 1] / 2);
        while float_index > resample_count {
            out_samples.push(sample_value);
            resample_count += resample_interval;
        }
    }
    out_samples
}
inoftrobinson commented 3 years ago

Slightly faster revision : Handler resample sample rate and convert two channels to one

fn resample_sample_rate_and_channels_from_two_to_one(samples: WavSamples<BufReader<File>, i16>, resample_interval: f32) -> Vec<i16> {
    let mut out_samples: Vec<i16> = Vec::new();
    let mut resample_count: f32 = 0.0;

    let num_samples_unique_across_channels = samples.len() / 2;
    let mut samples_enumerator = samples.enumerate();
    for i in 0..num_samples_unique_across_channels {
        let float_index = i as f32;
        // A WAV file with 2 channels is represented like [240, 240, 102, 102, 720, 720].
        let channel_one_sample_data = samples_enumerator.next().unwrap().1.unwrap();
        let channel_two_sample_data = samples_enumerator.next().unwrap().1.unwrap();
        // So, since this function only support conversion from two channels to one, we can simply use an
        // iterator twice, to get the sample value both in the first channel, and in the second channel.
        let sample_value = (channel_one_sample_data / 2) + (channel_two_sample_data / 2);
        // And then add them together, by dividing them separately, instead of dividing them once they have been
        // added, to avoid overflow issues when they have been added before they have been divided. Of course, it
        // cost us two divisions instead of one, but it free us from a type conversion from i16 to something like i32.
        while float_index > resample_count {
            out_samples.push(sample_value);
            resample_count += resample_interval;
        }
    }
    out_samples
}