├── .github ├── dependabot.yml └── workflows │ └── speexdsp.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── benches ├── resampling_c.rs ├── resampling_rust.rs ├── resampling_simple_c.rs ├── resampling_simple_c_dbl.rs ├── resampling_simple_rust.rs └── resampling_simple_rust_dbl.rs ├── examples ├── testdenoise.rs ├── testecho.rs ├── testjitter.rs └── testresample.rs ├── fft ├── Cargo.toml ├── README.md ├── clippy.toml ├── src │ ├── dradb.rs │ ├── dradf.rs │ ├── fftwrap.rs │ ├── lib.rs │ └── smallft.rs └── tests │ ├── fftwrap.rs │ └── orig │ ├── fftwrap.rs │ ├── mod.rs │ └── smallft.rs ├── resampler ├── Cargo.toml ├── LICENSE.BSD-3 ├── README.md ├── examples │ └── resample.rs └── src │ ├── lib.rs │ ├── speex.rs │ └── speex │ ├── avx.rs │ ├── dynnative │ ├── avx_wrapper.rs │ ├── mod.rs │ └── sse3_wrapper.rs │ ├── native.rs │ └── sse3.rs ├── rustfmt.toml ├── speexdsp-sys ├── .gitignore ├── Cargo.toml ├── build.rs ├── data │ ├── echo.h │ ├── jitter.h │ ├── preprocess.h │ └── resampler.h ├── examples │ ├── testdenoise_sys.rs │ ├── testecho_sys.rs │ ├── testjitter_sys.rs │ └── testresample_sys.rs └── src │ ├── echo.rs │ ├── jitter.rs │ ├── lib.rs │ ├── preprocess.rs │ └── resampler.rs ├── src ├── echo.rs ├── jitter.rs ├── lib.rs ├── preprocess.rs └── resampler.rs └── tests └── resampler.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | ignore: 9 | - dependency-name: system-deps 10 | versions: 11 | - 3.0.0 12 | - dependency-name: bindgen 13 | versions: 14 | - 0.56.0 15 | - 0.57.0 16 | -------------------------------------------------------------------------------- /.github/workflows/speexdsp.yml: -------------------------------------------------------------------------------- 1 | name: speexdsp 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | 7 | rustfmt-clippy: 8 | 9 | runs-on: ubuntu-latest 10 | 11 | env: 12 | SPEEX_DIR: speex-dir 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Install stable 18 | uses: actions-rs/toolchain@v1 19 | with: 20 | profile: minimal 21 | toolchain: stable 22 | override: true 23 | components: rustfmt, clippy 24 | 25 | - name: Run rustfmt 26 | uses: actions-rs/cargo@v1 27 | with: 28 | command: fmt 29 | args: --all -- --check --verbose 30 | 31 | - name: Install speexdsp 32 | run: | 33 | git clone https://github.com/xiph/speexdsp.git 34 | cd speexdsp 35 | ./autogen.sh 36 | ./configure --prefix=$HOME/$SPEEX_DIR 37 | make -j4 install 38 | 39 | - name: Set environment variables 40 | run: | 41 | echo PKG_CONFIG_PATH="$HOME/$SPEEX_DIR/lib/pkgconfig" >> $GITHUB_ENV 42 | echo LD_LIBRARY_PATH="$HOME/$SPEEX_DIR/lib" >> $GITHUB_ENV 43 | 44 | - name: Run clippy 45 | uses: actions-rs/clippy-check@v1 46 | with: 47 | token: ${{ secrets.GITHUB_TOKEN }} 48 | args: --all --all-targets --all-features --tests -- 49 | 50 | tests: 51 | 52 | strategy: 53 | matrix: 54 | conf: 55 | - tests 56 | - bench-resampler 57 | - bench-resampler-sse 58 | 59 | env: 60 | SPEEX_DIR: speex-dir 61 | 62 | runs-on: ubuntu-latest 63 | 64 | steps: 65 | - uses: actions/checkout@v2 66 | 67 | - name: Install speexdsp 68 | run: | 69 | git clone https://github.com/xiph/speexdsp.git 70 | cd speexdsp 71 | ./autogen.sh 72 | CONF=${{ matrix.conf }} 73 | if [ ${CONF} = "bench-resampler" ] 74 | then 75 | ./configure --disable-sse --prefix=$HOME/$SPEEX_DIR 76 | else 77 | ./configure --prefix=$HOME/$SPEEX_DIR 78 | fi 79 | make -j4 install 80 | 81 | - name: Set environment variables 82 | run: | 83 | echo PKG_CONFIG_PATH="$HOME/$SPEEX_DIR/lib/pkgconfig" >> $GITHUB_ENV 84 | echo LD_LIBRARY_PATH="$HOME/$SPEEX_DIR/lib" >> $GITHUB_ENV 85 | 86 | - name: Run benchmark 87 | if: matrix.conf != 'tests' 88 | run: | 89 | cargo bench -q --features sys -- resampler_c 90 | cargo bench -q -- resampler_rust 91 | 92 | - name: Run no-default-features tests 93 | if: matrix.conf == 'tests' 94 | run: | 95 | cargo test --all --no-default-features 96 | 97 | - name: Run all-features tests 98 | if: matrix.conf == 'tests' 99 | run: | 100 | cargo test --all --all-features 101 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "speexdsp" 3 | version = "0.1.1" 4 | authors = ["Luca Barbato "] 5 | description = "Bindings for the speexdsp library" 6 | homepage = "https://github.com/rust-av/speexdsp-rs" 7 | repository = "https://github.com/rust-av/speexdsp-rs" 8 | keywords = ["audio", "resampler"] 9 | categories = ["api-bindings"] 10 | license = "MIT" 11 | readme = "README.md" 12 | edition = "2018" 13 | 14 | [features] 15 | sys = ["speexdsp-sys"] 16 | sse3 = [ "speexdsp-resampler/sse3" ] 17 | avx = [ "speexdsp-resampler/avx" ] 18 | dynnative = [ "speexdsp-resampler/dynnative" ] 19 | 20 | [dev-dependencies] 21 | assert_approx_eq = "1.1.0" 22 | byteorder = "1.3" 23 | interpolate_name = "0.2.2" 24 | structopt = "0.3" 25 | criterion = "0.3" 26 | 27 | [dependencies] 28 | speexdsp-sys = { version = "0.1.1", path = "speexdsp-sys", optional = true } 29 | speexdsp-resampler = { version = "0.1", path = "resampler" } 30 | 31 | [workspace] 32 | members = ["speexdsp-sys", "resampler", "fft"] 33 | 34 | [[bench]] 35 | name = "resampling_c" 36 | harness = false 37 | 38 | [[bench]] 39 | name = "resampling_rust" 40 | harness = false 41 | 42 | [[bench]] 43 | name = "resampling_simple_rust" 44 | harness = false 45 | 46 | [[bench]] 47 | name = "resampling_simple_c" 48 | harness = false 49 | 50 | [[bench]] 51 | name = "resampling_simple_rust_dbl" 52 | harness = false 53 | 54 | [[bench]] 55 | name = "resampling_simple_c_dbl" 56 | harness = false 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Luca Barbato 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # speexdsp bindings and c2rust version 2 | 3 | [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 4 | [![Actions Status](https://github.com/rust-av/speexdsp-rs/workflows/speexdsp/badge.svg)](https://github.com/rust-av/speexdsp-rs/actions) 5 | [![dependency status](https://deps.rs/repo/github/rust-av/speexdsp-rs/status.svg)](https://deps.rs/repo/github/rust-av/speexdsp-rs) 6 | [![IRC](https://img.shields.io/badge/irc-%23rust--av-blue.svg)](http://webchat.freenode.net?channels=%23rust-av&uio=d4) 7 | 8 | It is a simple safe abstraction based on [speexdsp][2]. 9 | 10 | It is available as [binding][1] or as pure-rust implementation. 11 | 12 | ## Building 13 | 14 | By default the pure-rust implementation is used, optionally the simd-accelerated original 15 | C version can be used instead using the feature `sys`. 16 | 17 | The bindings are generated using the headers and libraries that ought to be present in the system. 18 | 19 | - Make sure you have `clang` and `libclang` installed. 20 | - Make sure the `speexdsp` C headers and pkg-config files are installed. 21 | 22 | ## TODO 23 | - [ ] Source build speexdsp 24 | - [x] Simple bindings 25 | - [x] Safe abstraction 26 | - [x] Examples 27 | - [ ] Clean pure-rust reimplementation 28 | 29 | ## Testing 30 | 31 | Currently we have only an integration test to compare the C and the Rust implementation. 32 | To run it issue: 33 | 34 | ``` sh 35 | $ cargo test --features=sys 36 | ``` 37 | 38 | [1]: https://github.com/servo/rust-bindgen 39 | [2]: https://github.com/xiph/speexdsp 40 | -------------------------------------------------------------------------------- /benches/resampling_c.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "sys")] 2 | extern crate speexdsp_sys; 3 | 4 | use criterion::{criterion_group, criterion_main, Criterion}; 5 | #[cfg(feature = "sys")] 6 | use speexdsp_sys::resampler::*; 7 | 8 | #[cfg(feature = "sys")] 9 | fn resample_c() { 10 | use std::f32::consts::PI; 11 | use std::ptr; 12 | 13 | const PERIOD: f32 = 32f32; 14 | const INBLOCK: usize = 1024; 15 | const RATE: u32 = 48000; 16 | 17 | let mut rate = 1000; 18 | let mut off = 0; 19 | let mut avail = INBLOCK as isize; 20 | 21 | let fin: Vec = (0..INBLOCK * 4) 22 | .map(|i| ((i as f32) / PERIOD * 2.0 * PI).sin() * 0.9) 23 | .collect(); 24 | let mut fout = vec![0f32; INBLOCK * 4]; 25 | 26 | let st = 27 | unsafe { speex_resampler_init(1, RATE, RATE, 8, ptr::null_mut()) }; 28 | unsafe { speex_resampler_set_rate(st, RATE, rate) }; 29 | unsafe { speex_resampler_skip_zeros(st) }; 30 | 31 | loop { 32 | let mut in_len = avail as u32; 33 | let mut out_len = (in_len * rate + RATE - 1) / RATE; 34 | 35 | unsafe { 36 | speex_resampler_process_float( 37 | st, 38 | 0, 39 | fin[off..].as_ptr(), 40 | &mut in_len, 41 | fout.as_mut_ptr(), 42 | &mut out_len, 43 | ) 44 | }; 45 | 46 | off += in_len as usize; 47 | avail += INBLOCK as isize - in_len as isize; 48 | 49 | if off >= INBLOCK { 50 | off -= INBLOCK; 51 | } 52 | 53 | rate += 5000; 54 | if rate > 128000 { 55 | break; 56 | } 57 | 58 | unsafe { speex_resampler_set_rate(st, RATE, rate) }; 59 | } 60 | 61 | unsafe { speex_resampler_destroy(st) }; 62 | } 63 | 64 | #[cfg(not(feature = "sys"))] 65 | fn resample_c() {} 66 | 67 | pub fn criterion_benchmark(c: &mut Criterion) { 68 | c.bench_function("resampler_c", |b| b.iter(resample_c)); 69 | } 70 | 71 | criterion_group! { 72 | name = benches; 73 | config = Criterion::default().sample_size(10); 74 | targets = criterion_benchmark 75 | } 76 | criterion_main!(benches); 77 | -------------------------------------------------------------------------------- /benches/resampling_rust.rs: -------------------------------------------------------------------------------- 1 | use std::f32::consts::PI; 2 | 3 | use criterion::{criterion_group, criterion_main, Criterion}; 4 | use speexdsp::resampler::*; 5 | 6 | const PERIOD: f32 = 32f32; 7 | const INBLOCK: usize = 1024; 8 | const RATE: usize = 48000; 9 | 10 | fn resample_rs() { 11 | let mut rate = 1000; 12 | let mut off = 0; 13 | let mut avail = INBLOCK as isize; 14 | 15 | let fin: Vec = (0..INBLOCK * 4) 16 | .map(|i| ((i as f32) / PERIOD * 2.0 * PI).sin() * 0.9) 17 | .collect(); 18 | let mut fout = vec![0f32; INBLOCK * 8]; 19 | 20 | let mut st = State::new(1, RATE, RATE, 8).unwrap(); 21 | 22 | st.set_rate(RATE, rate).unwrap(); 23 | st.skip_zeros(); 24 | 25 | let mut data = Vec::new(); 26 | 27 | loop { 28 | let in_len = avail as usize; 29 | let out_len = (in_len * rate + RATE - 1) / RATE; 30 | 31 | let (in_len, out_len) = st 32 | .process_float(0, &fin[off..off + in_len], &mut fout[..out_len]) 33 | .unwrap(); 34 | 35 | off += in_len; 36 | avail += INBLOCK as isize - in_len as isize; 37 | 38 | if off >= INBLOCK { 39 | off -= INBLOCK; 40 | } 41 | 42 | data.push(fout[..out_len].to_vec()); 43 | 44 | rate += 5000; 45 | if rate > 128000 { 46 | break; 47 | } 48 | 49 | st.set_rate(RATE, rate).unwrap(); 50 | } 51 | } 52 | 53 | pub fn criterion_benchmark(c: &mut Criterion) { 54 | c.bench_function("resampler_rust", |b| b.iter(resample_rs)); 55 | } 56 | 57 | criterion_group! { 58 | name = benches; 59 | config = Criterion::default().sample_size(10); 60 | targets = criterion_benchmark 61 | } 62 | criterion_main!(benches); 63 | -------------------------------------------------------------------------------- /benches/resampling_simple_c.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | 3 | #[cfg(feature = "sys")] 4 | use speexdsp_sys::resampler::*; 5 | 6 | #[cfg(feature = "sys")] 7 | fn resample_c() { 8 | use std::f32::consts::PI; 9 | use std::ptr; 10 | 11 | const PERIOD: f32 = 32f32; 12 | const RATE: usize = 48000; 13 | const INBLOCK: usize = 1024 * 4; 14 | 15 | let fin: Vec = (0..INBLOCK * 4) 16 | .map(|i| ((i as f32) / PERIOD * 2.0 * PI).sin() * 0.9) 17 | .collect(); 18 | let mut fout = vec![0f32; INBLOCK * 4 * 4]; 19 | 20 | let mut in_len = (INBLOCK * 4) as u32; 21 | let mut out_len = (INBLOCK * 4 * 4) as u32; 22 | 23 | let st = unsafe { 24 | speex_resampler_init( 25 | 1, 26 | RATE as u32, 27 | (RATE * 4) as u32, 28 | 8, 29 | ptr::null_mut(), 30 | ) 31 | }; 32 | unsafe { speex_resampler_skip_zeros(st) }; 33 | 34 | unsafe { 35 | speex_resampler_process_float( 36 | st, 37 | 0, 38 | fin.as_ptr(), 39 | &mut in_len, 40 | fout.as_mut_ptr(), 41 | &mut out_len, 42 | ) 43 | }; 44 | } 45 | 46 | #[cfg(not(feature = "sys"))] 47 | fn resample_c() {} 48 | 49 | pub fn criterion_benchmark(c: &mut Criterion) { 50 | c.bench_function("resampler_simple_c", |b| b.iter(resample_c)); 51 | } 52 | 53 | criterion_group! { 54 | name = benches; 55 | config = Criterion::default().sample_size(10); 56 | targets = criterion_benchmark 57 | } 58 | criterion_main!(benches); 59 | -------------------------------------------------------------------------------- /benches/resampling_simple_c_dbl.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | 3 | #[cfg(feature = "sys")] 4 | use speexdsp_sys::resampler::*; 5 | 6 | #[cfg(feature = "sys")] 7 | fn resample_c() { 8 | use std::f32::consts::PI; 9 | use std::ptr; 10 | 11 | const PERIOD: f32 = 32f32; 12 | const RATE: usize = 48000; 13 | const INBLOCK: usize = 1024 * 4; 14 | 15 | let fin: Vec = (0..INBLOCK * 4) 16 | .map(|i| ((i as f32) / PERIOD * 2.0 * PI).sin() * 0.9) 17 | .collect(); 18 | let mut fout = vec![0f32; INBLOCK * 4 * 4]; 19 | 20 | let mut in_len = (INBLOCK * 4) as u32; 21 | let mut out_len = (INBLOCK * 4 * 4) as u32; 22 | 23 | let st = unsafe { 24 | speex_resampler_init( 25 | 1, 26 | RATE as u32, 27 | (RATE * 4) as u32, 28 | 10, 29 | ptr::null_mut(), 30 | ) 31 | }; 32 | unsafe { speex_resampler_skip_zeros(st) }; 33 | 34 | unsafe { 35 | speex_resampler_process_float( 36 | st, 37 | 0, 38 | fin.as_ptr(), 39 | &mut in_len, 40 | fout.as_mut_ptr(), 41 | &mut out_len, 42 | ) 43 | }; 44 | } 45 | 46 | #[cfg(not(feature = "sys"))] 47 | fn resample_c() {} 48 | 49 | pub fn criterion_benchmark(c: &mut Criterion) { 50 | c.bench_function("resampler_simple_c_dbl", |b| b.iter(resample_c)); 51 | } 52 | 53 | criterion_group! { 54 | name = benches; 55 | config = Criterion::default().sample_size(10); 56 | targets = criterion_benchmark 57 | } 58 | criterion_main!(benches); 59 | -------------------------------------------------------------------------------- /benches/resampling_simple_rust.rs: -------------------------------------------------------------------------------- 1 | use std::f32::consts::PI; 2 | 3 | use criterion::{criterion_group, criterion_main, Criterion}; 4 | use speexdsp::resampler::*; 5 | 6 | const PERIOD: f32 = 32f32; 7 | const RATE: usize = 48000; 8 | const INBLOCK: usize = 1024 * 4; 9 | 10 | fn resample_rs() { 11 | let fin: Vec = (0..INBLOCK * 4) 12 | .map(|i| ((i as f32) / PERIOD * 2.0 * PI).sin() * 0.9) 13 | .collect(); 14 | let mut fout = vec![0f32; INBLOCK * 4 * 4]; 15 | 16 | let mut st = State::new(1, RATE, RATE * 4, 8).unwrap(); 17 | 18 | st.skip_zeros(); 19 | 20 | let (_in_len, _out_len) = st.process_float(0, &fin, &mut fout).unwrap(); 21 | } 22 | 23 | pub fn criterion_benchmark(c: &mut Criterion) { 24 | c.bench_function("resampler_simple_rust", |b| b.iter(resample_rs)); 25 | } 26 | 27 | criterion_group! { 28 | name = benches; 29 | config = Criterion::default().sample_size(10); 30 | targets = criterion_benchmark 31 | } 32 | criterion_main!(benches); 33 | -------------------------------------------------------------------------------- /benches/resampling_simple_rust_dbl.rs: -------------------------------------------------------------------------------- 1 | use std::f32::consts::PI; 2 | 3 | use criterion::{criterion_group, criterion_main, Criterion}; 4 | use speexdsp::resampler::*; 5 | 6 | const PERIOD: f32 = 32f32; 7 | const RATE: usize = 48000; 8 | const INBLOCK: usize = 1024 * 4; 9 | 10 | fn resample_rs() { 11 | let fin: Vec = (0..INBLOCK * 4) 12 | .map(|i| ((i as f32) / PERIOD * 2.0 * PI).sin() * 0.9) 13 | .collect(); 14 | let mut fout = vec![0f32; INBLOCK * 4 * 4]; 15 | 16 | let mut st = State::new(1, RATE, RATE * 4, 10).unwrap(); 17 | 18 | st.skip_zeros(); 19 | 20 | let (_in_len, _out_len) = st.process_float(0, &fin, &mut fout).unwrap(); 21 | } 22 | 23 | pub fn criterion_benchmark(c: &mut Criterion) { 24 | c.bench_function("resampler_simple_rust_dbl", |b| b.iter(resample_rs)); 25 | } 26 | 27 | criterion_group! { 28 | name = benches; 29 | config = Criterion::default().sample_size(10); 30 | targets = criterion_benchmark 31 | } 32 | criterion_main!(benches); 33 | -------------------------------------------------------------------------------- /examples/testdenoise.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(unused_imports)] 3 | extern crate byteorder; 4 | extern crate speexdsp; 5 | 6 | use byteorder::{BigEndian, ByteOrder}; 7 | use std::io::Read; 8 | 9 | #[cfg(feature = "sys")] 10 | fn main() { 11 | use speexdsp::preprocess::SpeexPreprocessConst::*; 12 | use speexdsp::preprocess::*; 13 | 14 | const NN: usize = 160; 15 | let mut input: [i16; NN] = [0; NN]; 16 | let mut buffer: [u8; NN * 2] = [0; NN * 2]; 17 | 18 | let mut st = SpeexPreprocess::new(NN, 8000).unwrap(); 19 | st.preprocess_ctl(SPEEX_PREPROCESS_SET_DENOISE, 1).unwrap(); 20 | st.preprocess_ctl(SPEEX_PREPROCESS_SET_AGC, 0).unwrap(); 21 | st.preprocess_ctl(SPEEX_PREPROCESS_SET_AGC_LEVEL, 8000) 22 | .unwrap(); 23 | st.preprocess_ctl(SPEEX_PREPROCESS_SET_DEREVERB, 0).unwrap(); 24 | st.preprocess_ctl(SPEEX_PREPROCESS_SET_DEREVERB_DECAY, 0f32) 25 | .unwrap(); 26 | st.preprocess_ctl(SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, 0f32) 27 | .unwrap(); 28 | 29 | while let Ok(n) = std::io::stdin().read(&mut buffer) { 30 | if n == 0 { 31 | break; 32 | } 33 | BigEndian::read_i16_into(&buffer, &mut input); 34 | st.preprocess_run(&mut input); 35 | println!("{:?}", &input[..]); 36 | } 37 | } 38 | 39 | #[cfg(not(feature = "sys"))] 40 | fn main() { 41 | unimplemented!(); 42 | } 43 | -------------------------------------------------------------------------------- /examples/testecho.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(unused_imports)] 3 | extern crate byteorder; 4 | extern crate speexdsp; 5 | extern crate structopt; 6 | 7 | use byteorder::{BigEndian, ByteOrder}; 8 | use std::fs::File; 9 | use std::io::{Read, Write}; 10 | use std::path::PathBuf; 11 | use structopt::StructOpt; 12 | 13 | #[derive(Debug, StructOpt)] 14 | struct CliArgs { 15 | #[structopt(parse(from_os_str))] 16 | echo_fd_path: PathBuf, 17 | #[structopt(parse(from_os_str))] 18 | ref_fd_path: PathBuf, 19 | #[structopt(parse(from_os_str))] 20 | e_fd_path: PathBuf, 21 | } 22 | 23 | #[cfg(feature = "sys")] 24 | fn main() -> std::io::Result<()> { 25 | use speexdsp::echo::SpeexEchoConst::*; 26 | use speexdsp::echo::*; 27 | use speexdsp::preprocess::SpeexPreprocessConst::*; 28 | use speexdsp::preprocess::*; 29 | 30 | const NN: usize = 160; 31 | const TAIL: usize = 1024; 32 | 33 | let sample_rate: usize = 8000; 34 | 35 | let mut echo_buf: [i16; NN] = [0; NN]; 36 | let mut echo_read_buf: [u8; NN * 2] = [0; NN * 2]; 37 | let mut ref_buf: [i16; NN] = [0; NN]; 38 | let mut ref_read_buf: [u8; NN * 2] = [0; NN * 2]; 39 | let mut e_buf: [i16; NN] = [0; NN]; 40 | 41 | let opts = CliArgs::from_args(); 42 | 43 | let mut ref_fd = File::open(opts.ref_fd_path)?; 44 | let mut echo_fd = File::open(opts.echo_fd_path)?; 45 | let mut e_fd = File::create(opts.e_fd_path)?; 46 | 47 | let mut st = SpeexEcho::new(NN, TAIL).unwrap(); 48 | let mut den = SpeexPreprocess::new(NN, sample_rate).unwrap(); 49 | st.echo_ctl(SPEEX_ECHO_SET_SAMPLING_RATE, sample_rate) 50 | .unwrap(); 51 | den.preprocess_ctl(SPEEX_PREPROCESS_SET_ECHO_STATE, &st) 52 | .unwrap(); 53 | 54 | loop { 55 | let n = echo_fd.read(&mut echo_read_buf)?; 56 | let nn = ref_fd.read(&mut ref_read_buf)?; 57 | if n == 0 && nn == 0 { 58 | break; 59 | } 60 | BigEndian::read_i16_into(&echo_read_buf, &mut echo_buf); 61 | BigEndian::read_i16_into(&ref_read_buf, &mut ref_buf); 62 | st.echo_cancellation(&ref_buf, &echo_buf, &mut e_buf); 63 | den.preprocess_run(&mut e_buf); 64 | BigEndian::write_i16_into(&e_buf, &mut ref_read_buf); 65 | e_fd.write_all(&ref_read_buf)?; 66 | } 67 | 68 | Ok(()) 69 | } 70 | 71 | #[cfg(not(feature = "sys"))] 72 | fn main() { 73 | unimplemented!(); 74 | } 75 | -------------------------------------------------------------------------------- /examples/testjitter.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_imports)] 2 | extern crate byteorder; 3 | #[cfg(feature = "sys")] 4 | extern crate speexdsp; 5 | 6 | use byteorder::{BigEndian, ByteOrder}; 7 | 8 | #[cfg(feature = "sys")] 9 | use speexdsp::jitter::*; 10 | 11 | #[cfg(feature = "sys")] 12 | fn synth_in(input: &mut SpeexBufferPacket, idx: usize, span: usize) { 13 | let mut buf = [0; 4]; 14 | BigEndian::write_u32(&mut buf, idx as u32); 15 | let mut buf_i8: [i8; 4] = [0; 4]; 16 | for i in 0..3 { 17 | buf_i8[i] = buf[i] as i8; 18 | } 19 | input.create(&mut buf_i8, 32, idx * 10, span * 10, idx, 0); 20 | } 21 | 22 | #[cfg(feature = "sys")] 23 | fn jitter_fill(jb: &mut SpeexJitter) { 24 | let mut buffer: [i8; 65536] = [0; 65536]; 25 | 26 | let mut input = SpeexBufferPacket::new(); 27 | let mut output = SpeexBufferPacket::new(); 28 | 29 | output.set_data(&mut buffer); 30 | 31 | jb.buffer_reset(); 32 | 33 | for i in 0..100 { 34 | synth_in(&mut input, i, 1); 35 | jb.buffer_put(&input); 36 | 37 | output.set_len(65536); 38 | if jb.buffer_get(&mut output, 10, 0) != Error::BufferOk { 39 | eprintln!("Fill test failed iteration {}", i); 40 | } 41 | if output.timestamp() != i * 10 { 42 | println!( 43 | "Fill test expected {} got {}", 44 | i * 10, 45 | output.timestamp() 46 | ); 47 | } 48 | jb.buffer_tick(); 49 | } 50 | } 51 | 52 | #[cfg(feature = "sys")] 53 | fn main() { 54 | let mut buffer: [i8; 65536] = [0; 65536]; 55 | let mut jb = SpeexJitter::new(10).unwrap(); 56 | 57 | let mut input = SpeexBufferPacket::new(); 58 | let mut output = SpeexBufferPacket::new(); 59 | 60 | output.set_data(&mut buffer); 61 | 62 | jitter_fill(&mut jb); 63 | for _ in 0..100 { 64 | output.set_len(65536); 65 | jb.buffer_get(&mut output, 10, 0); 66 | jb.buffer_tick(); 67 | } 68 | 69 | synth_in(&mut input, 100, 1); 70 | jb.buffer_put(&input); 71 | output.set_len(65536); 72 | if jb.buffer_get(&mut output, 10, 0) != Error::BufferOk { 73 | eprintln!("Failed frozen sender resynchronize"); 74 | } else { 75 | println!("Frozen sender: Jitter {}", output.timestamp() - 100 * 10); 76 | } 77 | } 78 | 79 | #[cfg(not(feature = "sys"))] 80 | fn main() { 81 | unimplemented!(); 82 | } 83 | -------------------------------------------------------------------------------- /examples/testresample.rs: -------------------------------------------------------------------------------- 1 | use speexdsp::resampler::*; 2 | 3 | use std::f32::consts::PI; 4 | 5 | const PERIOD: f32 = 32f32; 6 | const INBLOCK: usize = 1024; 7 | const RATE: usize = 48000; 8 | 9 | fn main() { 10 | let mut rate = 1000; 11 | let mut off = 0; 12 | let mut avail = INBLOCK as isize; 13 | 14 | let fin: Vec = (0..INBLOCK * 4) 15 | .map(|i| ((i as f32) / PERIOD * 2.0 * PI).sin() * 0.9) 16 | .collect(); 17 | let mut fout = vec![0f32; INBLOCK * 8]; 18 | 19 | let mut st = State::new(1, RATE, RATE, 4).unwrap(); 20 | 21 | st.set_rate(RATE, rate).unwrap(); 22 | st.skip_zeros(); 23 | 24 | st.set_quality(10).unwrap(); 25 | 26 | eprintln!("Quality: {}", st.get_quality()); 27 | 28 | let mut data = Vec::new(); 29 | 30 | loop { 31 | let in_len = avail as usize; 32 | let out_len = (in_len * rate + RATE - 1) / RATE; 33 | 34 | let prev_in_len = in_len; 35 | let prev_out_len = out_len; 36 | 37 | let (in_len, out_len) = st 38 | .process_float(0, &fin[off..off + in_len], &mut fout[..out_len]) 39 | .unwrap(); 40 | 41 | eprintln!( 42 | "{} {} {} {} -> {} {}", 43 | rate, off, prev_in_len, prev_out_len, in_len, out_len 44 | ); 45 | 46 | off += in_len; 47 | avail += INBLOCK as isize - in_len as isize; 48 | 49 | if off >= INBLOCK { 50 | off -= INBLOCK; 51 | } 52 | 53 | data.push(fout[..out_len].to_vec()); 54 | 55 | rate += 5000; 56 | if rate > 128000 { 57 | break; 58 | } 59 | 60 | st.set_rate(RATE, rate).unwrap(); 61 | } 62 | 63 | println!("{:#?}", data); 64 | } 65 | -------------------------------------------------------------------------------- /fft/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "speexdsp-fft" 3 | version = "0.1.0" 4 | authors = ["Luca Barbato "] 5 | edition = "2018" 6 | license = "BSD-3-Clause" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | libc = "0.2" 12 | -------------------------------------------------------------------------------- /fft/README.md: -------------------------------------------------------------------------------- 1 | # speexdsp fft 2 | 3 | [![LICENSE](https://img.shields.io/badge/license-BSD3-blue.svg)](LICENSE.BSD-3) 4 | [![Build Status](https://travis-ci.org/rust-av/speexdsp-rs.svg?branch=master)](https://travis-ci.org/rust-av/speexdsp-rs) 5 | [![dependency status](https://deps.rs/repo/github/rust-av/speexdsp-rs/status.svg)](https://deps.rs/repo/github/rust-av/speexdsp-rs) 6 | [![IRC](https://img.shields.io/badge/irc-%23rust--av-blue.svg)](http://webchat.freenode.net?channels=%23rust-av&uio=d4) 7 | 8 | [c2rust](https://github.com/immunant/c2rust)-based port of the [speexdsp](https://github.com/xiph/speexdsp) fft. 9 | 10 | ## TODO 11 | - [ ] Simple bindings 12 | - [ ] Safe abstraction 13 | - [ ] Examples 14 | - [ ] Cleaner code 15 | - [ ] More API surface 16 | -------------------------------------------------------------------------------- /fft/clippy.toml: -------------------------------------------------------------------------------- 1 | too-many-arguments-threshold = 11 2 | -------------------------------------------------------------------------------- /fft/src/dradb.rs: -------------------------------------------------------------------------------- 1 | pub(crate) fn dradb2( 2 | ido: i32, 3 | l1: i32, 4 | cc: &[f32], 5 | ch: &mut [f32], 6 | wa1: &[f32], 7 | ) { 8 | let t0 = l1 * ido; 9 | let mut t1 = 0; 10 | let mut t2 = 0; 11 | let mut t3 = (ido << 1) - 1_i32; 12 | 13 | for _ in 0..l1 { 14 | ch[t1 as usize] = cc[t2 as usize] + cc[t3 as usize + t2 as usize]; 15 | ch[t1 as usize + t0 as usize] = 16 | cc[t2 as usize] - cc[t3 as usize + t2 as usize]; 17 | t1 += ido; 18 | t2 = t1 << 1; 19 | } 20 | 21 | if ido < 2 { 22 | return; 23 | } 24 | 25 | if ido != 2 { 26 | t1 = 0; 27 | t2 = 0; 28 | for _ in 0..l1 { 29 | t3 = t1; 30 | let mut t4 = t2; 31 | let mut t5 = t4 + (ido << 1); 32 | let mut t6 = t0 + t1; 33 | for i in (2..ido as usize).step_by(2) { 34 | t3 += 2; 35 | t4 += 2; 36 | t5 -= 2; 37 | t6 += 2; 38 | ch[t3 as usize - 1] = 39 | cc[t4 as usize - 1] + cc[t5 as usize - 1]; 40 | let tr2 = cc[t4 as usize - 1] - cc[t5 as usize - 1]; 41 | ch[t3 as usize] = cc[t4 as usize] - cc[t5 as usize]; 42 | let ti2 = cc[t4 as usize] + cc[t5 as usize]; 43 | ch[t6 as usize - 1] = wa1[i - 2] * tr2 - wa1[i - 1] * ti2; 44 | ch[t6 as usize] = wa1[i - 2] * ti2 + wa1[i - 1] * tr2; 45 | } 46 | t1 += ido; 47 | t2 = t1 << 1; 48 | } 49 | 50 | if ido % 2 == 1 { 51 | return; 52 | } 53 | } 54 | 55 | t1 = ido - 1; 56 | t2 = ido - 1; 57 | for _ in 0..l1 { 58 | ch[t1 as usize] = cc[t2 as usize] + cc[t2 as usize]; 59 | ch[t1 as usize + t0 as usize] = 60 | -(cc[t2 as usize + 1] + cc[t2 as usize + 1]); 61 | t1 += ido; 62 | t2 += ido << 1; 63 | } 64 | } 65 | 66 | pub(crate) fn dradb3( 67 | ido: i32, 68 | l1: i32, 69 | cc: &[f32], 70 | ch: &mut [f32], 71 | wa1: &[f32], 72 | wa2: &[f32], 73 | ) { 74 | const TAUR: f32 = -0.5; 75 | const TAUI: f32 = 0.866_025_4; 76 | 77 | let t0 = l1 * ido; 78 | let mut t1 = 0; 79 | let t2 = t0 << 1; 80 | let mut t3 = ido << 1; 81 | let t4 = ido + (ido << 1); 82 | let mut t5 = 0; 83 | 84 | for _ in 0..l1 { 85 | let tr2 = cc[t3 as usize - 1] + cc[t3 as usize - 1]; 86 | let cr2 = cc[t5 as usize] + TAUR * tr2; 87 | ch[t1 as usize] = cc[t5 as usize] + tr2; 88 | let ci3 = TAUI * (cc[t3 as usize] + cc[t3 as usize]); 89 | ch[t1 as usize + t0 as usize] = cr2 - ci3; 90 | ch[t1 as usize + t2 as usize] = cr2 + ci3; 91 | t1 += ido; 92 | t3 += t4; 93 | t5 += t4; 94 | } 95 | 96 | if ido == 1 { 97 | return; 98 | } 99 | 100 | t1 = 0; 101 | t3 = ido << 1; 102 | for _ in 0..l1 { 103 | let mut t7 = t1 + (t1 << 1); 104 | t5 = t7 + t3; 105 | let mut t6 = t5; 106 | let mut t8 = t1; 107 | let mut t9 = t1 + t0; 108 | let mut t10 = t9 + t0; 109 | for i in (2..ido as usize).step_by(2) { 110 | t5 += 2; 111 | t6 -= 2; 112 | t7 += 2; 113 | t8 += 2; 114 | t9 += 2; 115 | t10 += 2; 116 | let tr2 = cc[t5 as usize - 1] + cc[t6 as usize - 1]; 117 | let cr2 = cc[t7 as usize - 1] + TAUR * tr2; 118 | ch[t8 as usize - 1] = cc[t7 as usize - 1] + tr2; 119 | let ti2 = cc[t5 as usize] - cc[t6 as usize]; 120 | let ci2 = cc[t7 as usize] + TAUR * ti2; 121 | ch[t8 as usize] = cc[t7 as usize] + ti2; 122 | let cr3 = TAUI * (cc[t5 as usize - 1] - cc[t6 as usize - 1]); 123 | let ci3 = TAUI * (cc[t5 as usize] + cc[t6 as usize]); 124 | let dr2 = cr2 - ci3; 125 | let dr3 = cr2 + ci3; 126 | let di2 = ci2 + cr3; 127 | let di3 = ci2 - cr3; 128 | ch[t9 as usize - 1] = wa1[i - 2] * dr2 - wa1[i - 1] * di2; 129 | ch[t9 as usize] = wa1[i - 2] * di2 + wa1[i - 1] * dr2; 130 | ch[t10 as usize - 1] = wa2[i - 2] * dr3 - wa2[i - 1] * di3; 131 | ch[t10 as usize] = wa2[i - 2] * di3 + wa2[i - 1] * dr3; 132 | } 133 | t1 += ido; 134 | } 135 | } 136 | 137 | pub(crate) fn dradb4( 138 | ido: i32, 139 | l1: i32, 140 | cc: &[f32], 141 | ch: &mut [f32], 142 | wa1: &[f32], 143 | wa2: &[f32], 144 | wa3: &[f32], 145 | ) { 146 | const SQRT2: f32 = std::f32::consts::SQRT_2; 147 | 148 | let t0 = l1 * ido; 149 | let mut t1 = 0; 150 | let mut t2 = ido << 2; 151 | let mut t3 = 0; 152 | let t6 = ido << 1; 153 | 154 | for _ in 0..l1 { 155 | let mut t4 = t3 + t6; 156 | let mut t5 = t1; 157 | let tr3 = cc[t4 as usize - 1] + cc[t4 as usize - 1]; 158 | let tr4 = cc[t4 as usize] + cc[t4 as usize]; 159 | t4 += t6; 160 | let tr1 = cc[t3 as usize] - cc[t4 as usize - 1]; 161 | let tr2 = cc[t3 as usize] + cc[t4 as usize - 1]; 162 | ch[t5 as usize] = tr2 + tr3; 163 | t5 += t0; 164 | ch[t5 as usize] = tr1 - tr4; 165 | t5 += t0; 166 | ch[t5 as usize] = tr2 - tr3; 167 | t5 += t0; 168 | ch[t5 as usize] = tr1 + tr4; 169 | t1 += ido; 170 | t3 += t2; 171 | } 172 | 173 | if ido < 2 { 174 | return; 175 | } 176 | 177 | if ido != 2 { 178 | t1 = 0; 179 | for _ in 0..l1 { 180 | t2 = t1 << 2; 181 | t3 = t2 + t6; 182 | let mut t4 = t3; 183 | let mut t5 = t4 + t6; 184 | let mut t7 = t1; 185 | for i in (2..ido as usize).step_by(2) { 186 | t2 += 2; 187 | t3 += 2; 188 | t4 -= 2; 189 | t5 -= 2; 190 | t7 += 2; 191 | let ti1 = cc[t2 as usize] + cc[t5 as usize]; 192 | let ti2 = cc[t2 as usize] - cc[t5 as usize]; 193 | let ti3 = cc[t3 as usize] - cc[t4 as usize]; 194 | let tr4 = cc[t3 as usize] + cc[t4 as usize]; 195 | let tr1 = cc[t2 as usize - 1] - cc[t5 as usize - 1]; 196 | let tr2 = cc[t2 as usize - 1] + cc[t5 as usize - 1]; 197 | let ti4 = cc[t3 as usize - 1] - cc[t4 as usize - 1]; 198 | let tr3 = cc[t3 as usize - 1] + cc[t4 as usize - 1]; 199 | ch[t7 as usize - 1] = tr2 + tr3; 200 | let cr3 = tr2 - tr3; 201 | ch[t7 as usize] = ti2 + ti3; 202 | let ci3 = ti2 - ti3; 203 | let cr2 = tr1 - tr4; 204 | let cr4 = tr1 + tr4; 205 | let ci2 = ti1 + ti4; 206 | let ci4 = ti1 - ti4; 207 | let mut t8 = t7 + t0; 208 | ch[t8 as usize - 1] = wa1[i - 2] * cr2 - wa1[i - 1] * ci2; 209 | ch[t8 as usize] = wa1[i - 2] * ci2 + wa1[i - 1] * cr2; 210 | t8 += t0; 211 | ch[t8 as usize - 1] = wa2[i - 2] * cr3 - wa2[i - 1] * ci3; 212 | ch[t8 as usize] = wa2[i - 2] * ci3 + wa2[i - 1] * cr3; 213 | t8 += t0; 214 | ch[t8 as usize - 1] = wa3[i - 2] * cr4 - wa3[i - 1] * ci4; 215 | ch[t8 as usize] = wa3[i - 2] * ci4 + wa3[i - 1] * cr4; 216 | } 217 | t1 += ido; 218 | } 219 | 220 | if ido % 2 == 1 { 221 | return; 222 | } 223 | } 224 | 225 | t1 = ido; 226 | t2 = ido << 2; 227 | let mut t3 = ido - 1; 228 | let mut t4 = ido + (ido << 1); 229 | for _ in 0..l1 { 230 | let mut t5 = t3; 231 | let ti1 = cc[t1 as usize] + cc[t4 as usize]; 232 | let ti2 = cc[t4 as usize] - cc[t1 as usize]; 233 | let tr1 = cc[t1 as usize - 1] - cc[t4 as usize - 1]; 234 | let tr2 = cc[t1 as usize - 1] + cc[t4 as usize - 1]; 235 | ch[t5 as usize] = tr2 + tr2; 236 | t5 += t0; 237 | ch[t5 as usize] = SQRT2 * (tr1 - ti1); 238 | t5 += t0; 239 | ch[t5 as usize] = ti2 + ti2; 240 | t5 += t0; 241 | ch[t5 as usize] = -SQRT2 * (tr1 + ti1); 242 | t3 += ido; 243 | t1 += t2; 244 | t4 += t2; 245 | } 246 | } 247 | 248 | #[inline(always)] 249 | fn dradbg_l102( 250 | ido: i32, 251 | nbd: i32, 252 | ipp2: i32, 253 | ipph: i32, 254 | l1: i32, 255 | t0: i32, 256 | t10: i32, 257 | cc: &[f32], 258 | ch: &mut [f32], 259 | ) { 260 | if ido != 1 { 261 | if nbd < l1 { 262 | let mut t1 = 0; 263 | let mut t2 = ipp2 * t0; 264 | let mut t7 = 0; 265 | for _ in 1..ipph { 266 | t1 += t0; 267 | t2 -= t0; 268 | let mut t3 = t1; 269 | let mut t4 = t2; 270 | t7 += ido << 1; 271 | let mut t8 = t7; 272 | let mut t9 = t7; 273 | for _ in (2..ido as usize).step_by(2) { 274 | t3 += 2; 275 | t4 += 2; 276 | t8 += 2; 277 | t9 -= 2; 278 | let mut t5 = t3; 279 | let mut t6 = t4; 280 | let mut t11 = t8; 281 | let mut t12 = t9; 282 | for _ in 0..l1 { 283 | ch[t5 as usize - 1] = 284 | cc[t11 as usize - 1] + cc[t12 as usize - 1]; 285 | ch[t6 as usize - 1] = 286 | cc[t11 as usize - 1] - cc[t12 as usize - 1]; 287 | ch[t5 as usize] = cc[t11 as usize] - cc[t12 as usize]; 288 | ch[t6 as usize] = cc[t11 as usize] + cc[t12 as usize]; 289 | t5 += ido; 290 | t6 += ido; 291 | t11 += t10; 292 | t12 += t10; 293 | } 294 | } 295 | } 296 | } else { 297 | let mut t1 = 0; 298 | let mut t2 = ipp2 * t0; 299 | let mut t7 = 0; 300 | for _ in 1..ipph { 301 | t1 += t0; 302 | t2 -= t0; 303 | let mut t3 = t1; 304 | let mut t4 = t2; 305 | t7 += ido << 1; 306 | let mut t8 = t7; 307 | for _ in 0..l1 { 308 | let mut t5 = t3; 309 | let mut t6 = t4; 310 | let mut t9 = t8; 311 | let mut t11 = t8; 312 | for _ in (2..ido as usize).step_by(2) { 313 | t5 += 2; 314 | t6 += 2; 315 | t9 += 2; 316 | t11 -= 2; 317 | ch[t5 as usize - 1] = 318 | cc[t9 as usize - 1] + cc[t11 as usize - 1]; 319 | ch[t6 as usize - 1] = 320 | cc[t9 as usize - 1] - cc[t11 as usize - 1]; 321 | ch[t5 as usize] = cc[t9 as usize] - cc[t11 as usize]; 322 | ch[t6 as usize] = cc[t9 as usize] + cc[t11 as usize]; 323 | } 324 | t3 += ido; 325 | t4 += ido; 326 | t8 += t10; 327 | } 328 | } 329 | } 330 | } 331 | } 332 | 333 | #[inline(always)] 334 | fn dradbg_l103( 335 | ido: i32, 336 | nbd: i32, 337 | ipp2: i32, 338 | ipph: i32, 339 | l1: i32, 340 | t0: i32, 341 | cc: &[f32], 342 | ch: &mut [f32], 343 | ) { 344 | if ido != 1 { 345 | if nbd < l1 { 346 | let mut t1 = 0; 347 | let mut t2 = ipp2 * t0; 348 | for _ in 1..ipph { 349 | t1 += t0; 350 | t2 -= t0; 351 | let mut t3 = t1; 352 | let mut t4 = t2; 353 | for _ in (2..ido as usize).step_by(2) { 354 | t3 += 2; 355 | t4 += 2; 356 | let mut t5 = t3; 357 | let mut t6 = t4; 358 | for _ in 0..l1 { 359 | ch[t5 as usize - 1] = 360 | cc[t5 as usize - 1] - cc[t6 as usize]; 361 | ch[t6 as usize - 1] = 362 | cc[t5 as usize - 1] + cc[t6 as usize]; 363 | ch[t5 as usize] = 364 | cc[t5 as usize] + cc[t6 as usize - 1]; 365 | ch[t6 as usize] = 366 | cc[t5 as usize] - cc[t6 as usize - 1]; 367 | t5 += ido; 368 | t6 += ido; 369 | } 370 | } 371 | } 372 | } else { 373 | let mut t1 = 0; 374 | let mut t2 = ipp2 * t0; 375 | for _ in 1..ipph { 376 | t1 += t0; 377 | t2 -= t0; 378 | let mut t3 = t1; 379 | let mut t4 = t2; 380 | for _ in 0..l1 { 381 | let mut t5 = t3; 382 | let mut t6 = t4; 383 | for _ in (2..ido as usize).step_by(2) { 384 | t5 += 2; 385 | t6 += 2; 386 | ch[t5 as usize - 1] = 387 | cc[t5 as usize - 1] - cc[t6 as usize]; 388 | ch[t6 as usize - 1] = 389 | cc[t5 as usize - 1] + cc[t6 as usize]; 390 | ch[t5 as usize] = 391 | cc[t5 as usize] + cc[t6 as usize - 1]; 392 | ch[t6 as usize] = 393 | cc[t5 as usize] - cc[t6 as usize - 1]; 394 | } 395 | t3 += ido; 396 | t4 += ido; 397 | } 398 | } 399 | } 400 | } 401 | } 402 | 403 | #[inline(always)] 404 | fn dradbg_l104( 405 | ido: i32, 406 | nbd: i32, 407 | ip: i32, 408 | l1: i32, 409 | t0: i32, 410 | cc: &mut [f32], 411 | ch: &[f32], 412 | wa: &[f32], 413 | ) { 414 | if nbd > l1 { 415 | let mut is = -ido - 1; 416 | let mut t1 = 0; 417 | for _ in 1..ip { 418 | is += ido; 419 | t1 += t0; 420 | let mut t2 = t1; 421 | for _ in 0..l1 { 422 | let mut idij = is; 423 | let mut t3 = t2; 424 | for _ in (2..ido as usize).step_by(2) { 425 | idij += 2; 426 | t3 += 2; 427 | cc[t3 as usize - 1] = wa[idij as usize - 1] 428 | * ch[t3 as usize - 1] 429 | - wa[idij as usize] * ch[t3 as usize]; 430 | cc[t3 as usize] = wa[idij as usize - 1] * ch[t3 as usize] 431 | + wa[idij as usize] * ch[t3 as usize - 1]; 432 | } 433 | t2 += ido; 434 | } 435 | } 436 | } else { 437 | let mut is = -ido - 1; 438 | let mut t1 = 0; 439 | for _ in 1..ip { 440 | is += ido; 441 | t1 += t0; 442 | let mut idij = is; 443 | let mut t2 = t1; 444 | for _ in (2..ido as usize).step_by(2) { 445 | t2 += 2; 446 | idij += 2; 447 | let mut t3 = t2; 448 | for _ in 0..l1 { 449 | cc[t3 as usize - 1] = wa[idij as usize - 1] 450 | * ch[t3 as usize - 1] 451 | - wa[idij as usize] * ch[t3 as usize]; 452 | cc[t3 as usize] = wa[idij as usize - 1] * ch[t3 as usize] 453 | + wa[idij as usize] * ch[t3 as usize - 1]; 454 | t3 += ido; 455 | } 456 | } 457 | } 458 | } 459 | } 460 | 461 | pub(crate) fn dradbg( 462 | ido: i32, 463 | ip: i32, 464 | l1: i32, 465 | idl1: i32, 466 | cc: &mut [f32], 467 | ch: &mut [f32], 468 | wa: &[f32], 469 | ) { 470 | const TPI: f32 = 6.283_185_5; 471 | 472 | let t10 = ip * ido; 473 | let t0 = l1 * ido; 474 | let arg = TPI / ip as f32; 475 | let dcp = f64::cos(arg as f64) as f32; 476 | let dsp = f64::sin(arg as f64) as f32; 477 | let nbd = (ido - 1) >> 1_i32; 478 | let ipp2 = ip; 479 | let ipph = (ip + 1) >> 1_i32; 480 | 481 | if ido < l1 { 482 | for t1 in 0..ido { 483 | let mut t2 = t1; 484 | let mut t3 = t1; 485 | for _ in 0..l1 { 486 | ch[t2 as usize] = cc[t3 as usize]; 487 | t2 += ido; 488 | t3 += t10; 489 | } 490 | } 491 | } else { 492 | let mut t1 = 0; 493 | let mut t2 = 0; 494 | for _ in 0..l1 { 495 | let mut t3 = t1; 496 | let mut t4 = t2; 497 | for _ in 0..ido { 498 | ch[t3 as usize] = cc[t4 as usize]; 499 | t3 += 1; 500 | t4 += 1; 501 | } 502 | t1 += ido; 503 | t2 += t10; 504 | } 505 | } 506 | 507 | let mut t1 = 0; 508 | let mut t2 = ipp2 * t0; 509 | let mut t5 = ido << 1; 510 | let t7 = t5; 511 | 512 | for _ in 1..ipph { 513 | t1 += t0; 514 | t2 -= t0; 515 | let mut t3 = t1; 516 | let mut t4 = t2; 517 | let mut t6 = t5; 518 | for _ in 0..l1 { 519 | ch[t3 as usize] = cc[t6 as usize - 1] + cc[t6 as usize - 1]; 520 | ch[t4 as usize] = cc[t6 as usize] + cc[t6 as usize]; 521 | t3 += ido; 522 | t4 += ido; 523 | t6 += t10; 524 | } 525 | t5 += t7; 526 | } 527 | 528 | dradbg_l102(ido, nbd, ipp2, ipph, l1, t0, t10, cc, ch); 529 | 530 | let ar1: f32 = 1.0; 531 | let ai1: f32 = 0.0; 532 | let mut t1 = 0; 533 | let mut t2 = ipp2 * idl1; 534 | let t9 = t2; 535 | let t3 = (ip - 1) * idl1; 536 | 537 | for _ in 1..ipph { 538 | t1 += idl1; 539 | t2 -= idl1; 540 | let ar1h = dcp * ar1 - dsp * ai1; 541 | let ai1 = dcp * ai1 + dsp * ar1; 542 | let ar1 = ar1h; 543 | let mut t4 = t1; 544 | let mut t5 = t2; 545 | let mut t7 = idl1; 546 | let mut t8 = t3; 547 | 548 | for t6 in 0..idl1 { 549 | let fresh13 = t6; 550 | let fresh14 = t7; 551 | t7 += 1; 552 | let fresh15 = t4; 553 | t4 += 1; 554 | cc[fresh15 as usize] = 555 | ch[fresh13 as usize] + ar1 * ch[fresh14 as usize]; 556 | let fresh16 = t8; 557 | t8 += 1; 558 | let fresh17 = t5; 559 | t5 += 1; 560 | cc[fresh17 as usize] = ai1 * ch[fresh16 as usize]; 561 | } 562 | let dcc = ar1; 563 | let ds2 = ai1; 564 | let ar2 = ar1; 565 | let ai2 = ai1; 566 | let mut t6 = idl1; 567 | let mut t7 = t9 - idl1; 568 | 569 | for _ in 2..ipph { 570 | t6 += idl1; 571 | t7 -= idl1; 572 | let ar2h = dcc * ar2 - ds2 * ai2; 573 | let ai2 = dcc * ai2 + ds2 * ar2; 574 | let ar2 = ar2h; 575 | let mut t4 = t1; 576 | let mut t5 = t2; 577 | let mut t11 = t6; 578 | let mut t12 = t7; 579 | 580 | for _ in 0..idl1 { 581 | let fresh18 = t11; 582 | t11 += 1; 583 | let fresh19 = t4; 584 | t4 += 1; 585 | cc[fresh19 as usize] += ar2 * ch[fresh18 as usize]; 586 | let fresh20 = t12; 587 | t12 += 1; 588 | let fresh21 = t5; 589 | t5 += 1; 590 | cc[fresh21 as usize] += ai2 * ch[fresh20 as usize]; 591 | } 592 | } 593 | } 594 | 595 | t1 = 0; 596 | for _ in 1..ipph { 597 | t1 += idl1; 598 | let mut t2 = t1; 599 | for ik in 0..idl1 { 600 | let fresh22 = t2; 601 | t2 += 1; 602 | ch[ik as usize] += ch[fresh22 as usize]; 603 | } 604 | } 605 | 606 | t1 = 0; 607 | t2 = ipp2 * t0; 608 | for _ in 1..ipph { 609 | t1 += t0; 610 | t2 -= t0; 611 | let mut t3 = t1; 612 | let mut t4 = t2; 613 | for _ in 0..l1 { 614 | ch[t3 as usize] = cc[t3 as usize] - cc[t4 as usize]; 615 | ch[t4 as usize] = cc[t3 as usize] + cc[t4 as usize]; 616 | t3 += ido; 617 | t4 += ido; 618 | } 619 | } 620 | 621 | dradbg_l103(ido, nbd, ipp2, ipph, l1, t0, cc, ch); 622 | 623 | if ido == 1 { 624 | return; 625 | } 626 | 627 | for ik in 0..idl1 { 628 | cc[ik as usize] = ch[ik as usize]; 629 | } 630 | 631 | t1 = 0; 632 | for _ in 1..ip { 633 | t1 += t0; 634 | t2 = t1; 635 | for _ in 0..l1 { 636 | cc[t2 as usize] = ch[t2 as usize]; 637 | t2 += ido; 638 | } 639 | } 640 | 641 | dradbg_l104(ido, nbd, ip, l1, t0, cc, ch, wa); 642 | } 643 | -------------------------------------------------------------------------------- /fft/src/dradf.rs: -------------------------------------------------------------------------------- 1 | pub(crate) fn dradf2( 2 | ido: i32, 3 | l1: i32, 4 | cc: &mut [f32], 5 | ch: &mut [f32], 6 | wa1: &mut [f32], 7 | ) { 8 | let mut t1 = 0; 9 | let mut t2 = l1 * ido; 10 | let t0 = t2; 11 | let mut t3 = ido << 1; 12 | 13 | for _ in 0..l1 { 14 | ch[(t1 as usize) << 1] = cc[t1 as usize] + cc[t2 as usize]; 15 | ch[(t1 as usize) << (1 + t3 as usize - 1)] = 16 | cc[t1 as usize] - cc[t2 as usize]; 17 | t1 += ido; 18 | t2 += ido; 19 | } 20 | 21 | if ido < 2 { 22 | return; 23 | } 24 | 25 | if ido != 2 { 26 | t1 = 0; 27 | t2 = t0; 28 | for _ in 0..l1 { 29 | let mut t3 = t2; 30 | let mut t4 = (t1 << 1) + (ido << 1); 31 | let mut t5 = t1; 32 | let mut t6 = t1 + t1; 33 | for i in (2..ido as usize).step_by(2) { 34 | t3 += 2; 35 | t4 -= 2; 36 | t5 += 2; 37 | t6 += 2; 38 | let tr2 = wa1[i - 2] * cc[t3 as usize - 1] 39 | + wa1[i - 1] * cc[t3 as usize]; 40 | let ti2 = wa1[i - 2] * cc[t3 as usize] 41 | - wa1[i - 1] * cc[t3 as usize - 1]; 42 | ch[t6 as usize] = cc[t5 as usize] + ti2; 43 | ch[t4 as usize] = ti2 - cc[t5 as usize]; 44 | ch[t6 as usize - 1] = cc[t5 as usize - 1] + tr2; 45 | ch[t4 as usize - 1] = cc[t5 as usize - 1] - tr2; 46 | } 47 | t1 += ido; 48 | t2 += ido; 49 | } 50 | 51 | if ido % 2 == 1 { 52 | return; 53 | } 54 | } 55 | 56 | t1 = ido; 57 | t2 = t1 - 1; 58 | t3 = t2; 59 | t2 += t0; 60 | 61 | for _ in 0..l1 { 62 | ch[t1 as usize] = -cc[t2 as usize]; 63 | ch[t1 as usize - 1] = cc[t3 as usize]; 64 | t1 += ido << 1; 65 | t2 += ido; 66 | t3 += ido; 67 | } 68 | } 69 | 70 | pub(crate) fn dradf4( 71 | ido: i32, 72 | l1: i32, 73 | cc: &mut [f32], 74 | ch: &mut [f32], 75 | wa1: &[f32], 76 | wa2: &[f32], 77 | wa3: &[f32], 78 | ) { 79 | const HSQT2: f32 = 0.70710677; 80 | 81 | let t0 = l1 * ido; 82 | let mut t1 = t0; 83 | let mut t4 = t1 << 1; 84 | let mut t2 = t1 + (t1 << 1); 85 | let mut t3 = 0; 86 | 87 | for _ in 0..l1 { 88 | let tr1 = cc[t1 as usize] + cc[t2 as usize]; 89 | let tr2 = cc[t3 as usize] + cc[t4 as usize]; 90 | let mut t5 = t3 << 2; 91 | ch[t5 as usize] = tr1 + tr2; 92 | ch[(ido << 2) as usize + t5 as usize - 1] = tr2 - tr1; 93 | t5 += ido << 1; 94 | ch[t5 as usize - 1] = cc[t3 as usize] - cc[t4 as usize]; 95 | ch[t5 as usize] = cc[t2 as usize] - cc[t1 as usize]; 96 | t1 += ido; 97 | t2 += ido; 98 | t3 += ido; 99 | t4 += ido; 100 | } 101 | 102 | if ido < 2 { 103 | return; 104 | } 105 | 106 | if ido != 2 { 107 | t1 = 0; 108 | for _ in 0..l1 { 109 | t2 = t1; 110 | t4 = t1 << 2; 111 | let t6 = ido << 1; 112 | let mut t5 = t6 + t4; 113 | for i in (2..ido as usize).step_by(2) { 114 | t2 += 2; 115 | t3 = t2; 116 | t4 += 2; 117 | t5 -= 2; 118 | t3 += t0; 119 | let cr2 = wa1[i - 2] * cc[t3 as usize - 1] 120 | + wa1[i - 1] * cc[t3 as usize]; 121 | let ci2 = wa1[i - 2] * cc[t3 as usize] 122 | - wa1[i - 1] * cc[t3 as usize - 1]; 123 | t3 += t0; 124 | let cr3 = wa2[i - 2] * cc[t3 as usize - 1] 125 | + wa2[i - 1] * cc[t3 as usize]; 126 | let ci3 = wa2[i - 2] * cc[t3 as usize] 127 | - wa2[i - 1] * cc[t3 as usize - 1]; 128 | t3 += t0; 129 | let cr4 = wa3[i - 2] * cc[t3 as usize - 1] 130 | + wa3[i - 1] * cc[t3 as usize]; 131 | let ci4 = wa3[i - 2] * cc[t3 as usize] 132 | - wa3[i - 1] * cc[t3 as usize - 1]; 133 | let tr1 = cr2 + cr4; 134 | let tr4 = cr4 - cr2; 135 | let ti1 = ci2 + ci4; 136 | let ti4 = ci2 - ci4; 137 | let ti2 = cc[t2 as usize] + ci3; 138 | let ti3 = cc[t2 as usize] - ci3; 139 | let tr2 = cc[t2 as usize - 1] + cr3; 140 | let tr3 = cc[t2 as usize - 1] - cr3; 141 | ch[t4 as usize - 1] = tr1 + tr2; 142 | ch[t4 as usize] = ti1 + ti2; 143 | ch[t5 as usize - 1] = tr3 - ti4; 144 | ch[t5 as usize] = tr4 - ti3; 145 | ch[t4 as usize + t6 as usize - 1] = ti4 + tr3; 146 | ch[t4 as usize + t6 as usize] = tr4 + ti3; 147 | ch[t5 as usize + t6 as usize - 1] = tr2 - tr1; 148 | ch[t5 as usize + t6 as usize] = ti1 - ti2; 149 | } 150 | t1 += ido; 151 | } 152 | 153 | if ido & 1 != 0 { 154 | return; 155 | } 156 | } 157 | 158 | t1 = t0 + ido - 1; 159 | t2 = t1 + (t0 << 1); 160 | t3 = ido << 2; 161 | t4 = ido; 162 | let t5 = ido << 1; 163 | let mut t6 = ido; 164 | 165 | for _ in 0..l1 { 166 | let ti1 = -HSQT2 * (cc[t1 as usize] + cc[t2 as usize]); 167 | let tr1 = HSQT2 * (cc[t1 as usize] - cc[t2 as usize]); 168 | ch[t4 as usize - 1] = tr1 + cc[t6 as usize - 1]; 169 | ch[t4 as usize + t5 as usize - 1] = cc[t6 as usize - 1] - tr1; 170 | ch[t4 as usize] = ti1 - cc[t1 as usize + t0 as usize]; 171 | ch[t4 as usize + t5 as usize] = ti1 + cc[t1 as usize + t0 as usize]; 172 | t1 += ido; 173 | t2 += ido; 174 | t4 += t3; 175 | t6 += ido; 176 | } 177 | } 178 | 179 | #[inline(always)] 180 | fn dradfg_l102( 181 | ido: i32, 182 | nbd: i32, 183 | ip: i32, 184 | l1: i32, 185 | t0: i32, 186 | cc: &[f32], 187 | wa: &[f32], 188 | ch: &mut [f32], 189 | ) { 190 | let mut is = -ido; 191 | let mut t1 = 0; 192 | if nbd > l1 { 193 | for _ in 1..ip { 194 | t1 += t0; 195 | is += ido; 196 | let mut t2 = -ido + t1; 197 | for _ in 0..l1 { 198 | let mut idij = is - 1; 199 | t2 += ido; 200 | let mut t3 = t2; 201 | for _ in (2..ido as usize).step_by(2) { 202 | idij += 2; 203 | t3 += 2; 204 | ch[t3 as usize - 1] = wa[idij as usize - 1] 205 | * cc[t3 as usize - 1] 206 | + wa[idij as usize] * cc[t3 as usize]; 207 | ch[t3 as usize] = wa[idij as usize - 1] * cc[t3 as usize] 208 | - wa[idij as usize] * cc[t3 as usize - 1]; 209 | } 210 | } 211 | } 212 | } else { 213 | for _ in 0..ip { 214 | is += ido; 215 | let mut idij = is - 1; 216 | t1 += t0; 217 | let mut t2 = t1; 218 | for _ in (2..ido as usize).step_by(2) { 219 | idij += 2; 220 | t2 += 2; 221 | let mut t3 = t2; 222 | for _ in 0..l1 { 223 | ch[t3 as usize - 1] = wa[idij as usize - 1] 224 | * cc[t3 as usize - 1] 225 | + wa[idij as usize] * cc[t3 as usize]; 226 | ch[t3 as usize] = wa[idij as usize - 1] * cc[t3 as usize] 227 | - wa[idij as usize] * cc[t3 as usize - 1]; 228 | t3 += ido; 229 | } 230 | } 231 | } 232 | } 233 | } 234 | 235 | #[inline(always)] 236 | fn dradfg_l103( 237 | ido: i32, 238 | nbd: i32, 239 | ipp2: i32, 240 | ipph: i32, 241 | l1: i32, 242 | t0: i32, 243 | ch: &[f32], 244 | cc: &mut [f32], 245 | ) { 246 | let mut t1 = 0; 247 | let mut t2 = ipp2 * t0; 248 | if nbd < l1 { 249 | for _ in 1..ipph { 250 | t1 += t0; 251 | t2 -= t0; 252 | let mut t3 = t1; 253 | let mut t4 = t2; 254 | for _ in (2..ido as usize).step_by(2) { 255 | t3 += 2; 256 | t4 += 2; 257 | let mut t5 = t3 - ido; 258 | let mut t6 = t4 - ido; 259 | for _ in 0..l1 { 260 | t5 += ido; 261 | t6 += ido; 262 | cc[t5 as usize - 1] = 263 | ch[t5 as usize - 1] + ch[t6 as usize - 1]; 264 | cc[t6 as usize - 1] = ch[t5 as usize] - ch[t6 as usize]; 265 | cc[t5 as usize] = ch[t5 as usize] + ch[t6 as usize]; 266 | cc[t6 as usize] = 267 | ch[t6 as usize - 1] - ch[t5 as usize - 1]; 268 | } 269 | } 270 | } 271 | } else { 272 | for _ in 1..ipph { 273 | t1 += t0; 274 | t2 -= t0; 275 | let mut t3 = t1; 276 | let mut t4 = t2; 277 | for _ in 0..l1 { 278 | let mut t5 = t3; 279 | let mut t6 = t4; 280 | for _ in (2..ido as usize).step_by(2) { 281 | t5 += 2; 282 | t6 += 2; 283 | cc[t5 as usize - 1] = 284 | ch[t5 as usize - 1] + ch[t6 as usize - 1]; 285 | cc[t6 as usize - 1] = ch[t5 as usize] - ch[t6 as usize]; 286 | cc[t5 as usize] = ch[t5 as usize] + ch[t6 as usize]; 287 | cc[t6 as usize] = 288 | ch[t6 as usize - 1] - ch[t5 as usize - 1]; 289 | } 290 | t3 += ido; 291 | t4 += ido; 292 | } 293 | } 294 | } 295 | } 296 | 297 | #[inline(always)] 298 | fn dradfg_l104( 299 | ido: i32, 300 | nbd: i32, 301 | idp2: i32, 302 | ipp2: i32, 303 | ipph: i32, 304 | l1: i32, 305 | t0: i32, 306 | t2: i32, 307 | t10: i32, 308 | ch: &[f32], 309 | cc: &mut [f32], 310 | ) { 311 | if nbd < l1 { 312 | let mut t1 = -ido; 313 | let mut t3 = 0; 314 | let mut t4 = 0; 315 | let mut t5 = ipp2 * t0; 316 | for _ in 1..ipph { 317 | t1 += t2; 318 | t3 += t2; 319 | t4 += t0; 320 | t5 -= t0; 321 | for i in (2..ido).step_by(2) { 322 | let mut t6 = idp2 + t1 - i; 323 | let mut t7 = i + t3; 324 | let mut t8 = i + t4; 325 | let mut t9 = i + t5; 326 | for _ in 0..l1 { 327 | cc[t7 as usize - 1] = 328 | ch[t8 as usize - 1] + ch[t9 as usize - 1]; 329 | cc[t6 as usize - 1] = 330 | ch[t8 as usize - 1] - ch[t9 as usize - 1]; 331 | cc[t7 as usize] = ch[t8 as usize] + ch[t9 as usize]; 332 | cc[t6 as usize] = ch[t9 as usize] - ch[t8 as usize]; 333 | t6 += t10; 334 | t7 += t10; 335 | t8 += ido; 336 | t9 += ido; 337 | } 338 | } 339 | } 340 | } else { 341 | let mut t1 = -ido; 342 | let mut t3 = 0; 343 | let mut t4 = 0; 344 | let mut t5 = ipp2 * t0; 345 | for _ in 1..ipph { 346 | t1 += t2; 347 | t3 += t2; 348 | t4 += t0; 349 | t5 -= t0; 350 | let mut t6 = t1; 351 | let mut t7 = t3; 352 | let mut t8 = t4; 353 | let mut t9 = t5; 354 | for _ in 0..l1 { 355 | for i in (2..ido as usize).step_by(2) { 356 | let ic = idp2 - i as i32; 357 | cc[i + t7 as usize - 1] = 358 | ch[i + t8 as usize - 1] + ch[i + t9 as usize - 1]; 359 | cc[ic as usize + t6 as usize - 1] = 360 | ch[i + t8 as usize - 1] - ch[i + t9 as usize - 1]; 361 | cc[i + t7 as usize] = 362 | ch[i + t8 as usize] + ch[i + t9 as usize]; 363 | cc[ic as usize + t6 as usize] = 364 | ch[i + t9 as usize] - ch[i + t8 as usize]; 365 | } 366 | t6 += t10; 367 | t7 += t10; 368 | t8 += ido; 369 | t9 += ido; 370 | } 371 | } 372 | } 373 | } 374 | 375 | pub(crate) fn dradfg( 376 | ido: i32, 377 | ip: i32, 378 | l1: i32, 379 | idl1: i32, 380 | cc: &mut [f32], 381 | ch: &mut [f32], 382 | wa: &[f32], 383 | ) { 384 | let arg = std::f32::consts::TAU / ip as f32; 385 | let dcp = f64::cos(arg as f64) as f32; 386 | let dsp = f64::sin(arg as f64) as f32; 387 | let ipph = (ip + 1) >> 1; 388 | let ipp2 = ip; 389 | let idp2 = ido; 390 | let nbd = (ido - 1) >> 1; 391 | let t0 = l1 * ido; 392 | let t10 = ip * ido; 393 | 394 | if ido != 1 { 395 | for ik in 0..idl1 { 396 | ch[ik as usize] = cc[ik as usize]; 397 | } 398 | 399 | let mut t1 = 0; 400 | for _ in 1..ip { 401 | t1 += t0; 402 | let mut t2 = t1; 403 | for _ in 0..l1 { 404 | ch[t2 as usize] = cc[t2 as usize]; 405 | t2 += ido; 406 | } 407 | } 408 | 409 | dradfg_l102(ido, nbd, ip, l1, t0, cc, wa, ch); 410 | 411 | dradfg_l103(ido, nbd, ipp2, ipph, l1, t0, ch, cc); 412 | } 413 | 414 | for ik in 0..idl1 { 415 | cc[ik as usize] = ch[ik as usize]; 416 | } 417 | 418 | let mut t1 = 0; 419 | let mut t2 = ipp2 * idl1; 420 | for _ in 1..ipph { 421 | t1 += t0; 422 | t2 -= t0; 423 | let mut t3 = t1 - ido; 424 | let mut t4 = t2 - ido; 425 | for _ in 0..l1 { 426 | t3 += ido; 427 | t4 += ido; 428 | cc[t3 as usize] = ch[t3 as usize] + ch[t4 as usize]; 429 | cc[t4 as usize] = ch[t4 as usize] - ch[t3 as usize]; 430 | } 431 | } 432 | 433 | let mut ar1: f32 = 1.0; 434 | let mut ai1: f32 = 0.0; 435 | t1 = 0; 436 | t2 = ipp2 * idl1; 437 | let mut t3 = (ip - 1) * idl1; 438 | 439 | for _ in 1..ipph { 440 | t1 += idl1; 441 | t2 -= idl1; 442 | let ar1h = dcp * ar1 - dsp * ai1; 443 | ai1 = dcp * ai1 + dsp * ar1; 444 | ar1 = ar1h; 445 | let mut t4 = t1; 446 | let mut t5 = t2; 447 | let mut t6 = t3; 448 | let mut t7 = idl1; 449 | for ik in 0..idl1 { 450 | let fresh2 = t7; 451 | t7 += 1; 452 | let fresh3 = t4; 453 | t4 += 1; 454 | ch[fresh3 as usize] = cc[ik as usize] + ar1 * cc[fresh2 as usize]; 455 | let fresh4 = t6; 456 | t6 += 1; 457 | let fresh5 = t5; 458 | t5 += 1; 459 | ch[fresh5 as usize] = ai1 * cc[fresh4 as usize]; 460 | } 461 | let dc2 = ar1; 462 | let ds2 = ai1; 463 | let mut ar2 = ar1; 464 | let mut ai2 = ai1; 465 | t4 = idl1; 466 | t5 = (ipp2 - 1) * idl1; 467 | for _ in 2..ipph { 468 | t4 += idl1; 469 | t5 -= idl1; 470 | let ar2h = dc2 * ar2 - ds2 * ai2; 471 | ai2 = dc2 * ai2 + ds2 * ar2; 472 | ar2 = ar2h; 473 | let mut t6 = t1; 474 | let mut t7 = t2; 475 | let mut t8 = t4; 476 | let mut t9 = t5; 477 | for _ in 0..idl1 { 478 | let fresh6 = t8; 479 | t8 += 1; 480 | let fresh7 = t6; 481 | t6 += 1; 482 | ch[fresh7 as usize] += ar2 * cc[fresh6 as usize]; 483 | let fresh8 = t9; 484 | t9 += 1; 485 | let fresh9 = t7; 486 | t7 += 1; 487 | ch[fresh9 as usize] += ai2 * cc[fresh8 as usize]; 488 | } 489 | } 490 | } 491 | 492 | t1 = 0; 493 | for _ in 1..ipph { 494 | t1 += idl1; 495 | t2 = t1; 496 | for ik in 0..idl1 { 497 | let fresh10 = t2; 498 | t2 += 1; 499 | ch[ik as usize] += cc[fresh10 as usize]; 500 | } 501 | } 502 | 503 | if ido < l1 { 504 | for i in 0..ido { 505 | t1 = i; 506 | t2 = i; 507 | for _ in 0..l1 { 508 | cc[t2 as usize] = ch[t1 as usize]; 509 | t1 += ido; 510 | t2 += t10; 511 | } 512 | } 513 | } else { 514 | t1 = 0; 515 | t2 = 0; 516 | for _ in 0..l1 { 517 | t3 = t1; 518 | let mut t4 = t2; 519 | for _ in 0..ido { 520 | let fresh11 = t3; 521 | t3 += 1; 522 | let fresh12 = t4; 523 | t4 += 1; 524 | cc[fresh12 as usize] = ch[fresh11 as usize]; 525 | } 526 | t1 += ido; 527 | t2 += t10; 528 | } 529 | } 530 | 531 | t1 = 0; 532 | t2 = ido << 1; 533 | t3 = 0; 534 | let mut t4 = ipp2 * t0; 535 | for _ in 1..ipph { 536 | t1 += t2; 537 | t3 += t0; 538 | t4 -= t0; 539 | let mut t5 = t1; 540 | let mut t6 = t3; 541 | let mut t7 = t4; 542 | for _ in 0..l1 { 543 | cc[t5 as usize - 1] = ch[t6 as usize]; 544 | cc[t5 as usize] = ch[t7 as usize]; 545 | t5 += t10; 546 | t6 += ido; 547 | t7 += ido; 548 | } 549 | } 550 | 551 | if ido == 1 { 552 | return; 553 | } 554 | 555 | dradfg_l104(ido, nbd, idp2, ipp2, ipph, l1, t0, t2, t10, ch, cc); 556 | } 557 | -------------------------------------------------------------------------------- /fft/src/fftwrap.rs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2005-2006 Jean-Marc Valin 2 | File: fftwrap.c 3 | 4 | Wrapper for various FFTs 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions 8 | are met: 9 | 10 | - Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 13 | - Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in the 15 | documentation and/or other materials provided with the distribution. 16 | 17 | - Neither the name of the Xiph.org Foundation nor the names of its 18 | contributors may be used to endorse or promote products derived from 19 | this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR 25 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | 33 | */ 34 | 35 | use crate::smallft::*; 36 | 37 | #[derive(Clone)] 38 | pub struct DrftLookup { 39 | pub n: usize, 40 | pub trigcache: Vec, 41 | pub splitcache: Vec, 42 | } 43 | 44 | impl DrftLookup { 45 | pub fn new(n: usize) -> Self { 46 | let mut drft = Self { 47 | n, 48 | trigcache: vec![0.0; 3 * n], 49 | splitcache: vec![0; 32], 50 | }; 51 | 52 | fdrffti(n, &mut drft.trigcache, &mut drft.splitcache); 53 | 54 | drft 55 | } 56 | 57 | pub fn spx_fft(&mut self, in_0: &[f32], out: &mut [f32]) { 58 | let scale = (1.0f64 / self.n as f64) as f32; 59 | if in_0 == out { 60 | eprintln!("FFT should not be done in-place"); 61 | } 62 | 63 | out.iter_mut() 64 | .zip(in_0.iter()) 65 | .take(self.n) 66 | .for_each(|(o, i)| *o = scale * *i); 67 | 68 | self.spx_drft_forward(out); 69 | } 70 | 71 | pub fn spx_ifft(&mut self, in_0: &[f32], out: &mut [f32]) { 72 | if in_0 == out { 73 | eprintln!("FFT should not be done in-place"); 74 | } else { 75 | out.copy_from_slice(&in_0[..self.n]); 76 | } 77 | 78 | self.spx_drft_backward(out); 79 | } 80 | 81 | pub fn spx_fft_float(&mut self, in_0: &[f32], out: &mut [f32]) { 82 | self.spx_fft(in_0, out); 83 | } 84 | 85 | pub fn spx_ifft_float(&mut self, in_0: &[f32], out: &mut [f32]) { 86 | self.spx_ifft(in_0, out); 87 | } 88 | 89 | pub fn spx_drft_forward(&mut self, data: &mut [f32]) { 90 | if self.n == 1 { 91 | return; 92 | } 93 | 94 | let mut trigcache_temp = self.trigcache[self.n..].to_vec(); 95 | 96 | drftf1( 97 | self.n as i32, 98 | data, 99 | &mut self.trigcache, 100 | &mut trigcache_temp, 101 | &mut self.splitcache, 102 | ); 103 | 104 | self.trigcache[self.n..].copy_from_slice(&trigcache_temp); 105 | } 106 | 107 | pub fn spx_drft_backward(&mut self, data: &mut [f32]) { 108 | if self.n == 1 { 109 | return; 110 | } 111 | 112 | let mut trigcache_temp = self.trigcache[self.n..].to_vec(); 113 | 114 | drftb1( 115 | self.n as i32, 116 | data, 117 | &mut self.trigcache, 118 | &mut trigcache_temp, 119 | &mut self.splitcache, 120 | ); 121 | 122 | self.trigcache[self.n..].copy_from_slice(&trigcache_temp); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /fft/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod dradb; 2 | mod dradf; 3 | mod fftwrap; 4 | mod smallft; 5 | 6 | pub use crate::fftwrap::*; 7 | -------------------------------------------------------------------------------- /fft/src/smallft.rs: -------------------------------------------------------------------------------- 1 | use crate::dradb::*; 2 | use crate::dradf::*; 3 | 4 | #[inline(always)] 5 | fn drfti1_c_10244(ifac: &mut [i32], n: i32, nf: &mut i32) { 6 | const NTRYH: [i32; 4] = [4, 2, 3, 5]; 7 | 8 | let mut j: i32 = -1; 9 | let mut ntry = 0; 10 | let mut nl = n; 11 | 12 | 'c_10244: loop { 13 | j += 1; 14 | if j < 4 { 15 | ntry = NTRYH[j as usize] 16 | } else { 17 | ntry += 2 18 | } 19 | loop { 20 | let nq = nl / ntry; 21 | let nr = nl - ntry * nq; 22 | if nr != 0 { 23 | break; 24 | } 25 | *nf += 1; 26 | ifac[*nf as usize + 1] = ntry; 27 | nl = nq; 28 | if ntry == 2 && *nf != 1 { 29 | for i in 1..*nf { 30 | let ib = *nf - i + 1; 31 | ifac[ib as usize + 1] = ifac[ib as usize]; 32 | } 33 | ifac[2] = 2; 34 | } 35 | if nl == 1 { 36 | break 'c_10244; 37 | } 38 | } 39 | } 40 | } 41 | 42 | pub(crate) fn drfti1(wa: &mut [f32], ifac: &mut [i32]) { 43 | let n = wa.len() as i32; 44 | let mut nf = 0; 45 | 46 | drfti1_c_10244(ifac, n, &mut nf); 47 | 48 | ifac[0] = n; 49 | ifac[1] = nf; 50 | let nfm1 = nf - 1; 51 | let argh = std::f32::consts::TAU / n as f32; 52 | let mut is = 0; 53 | let mut l1 = 1; 54 | 55 | if nfm1 == 0 { 56 | return; 57 | } 58 | 59 | for k1 in 0..nfm1 { 60 | let ip = ifac[k1 as usize + 2]; 61 | let l2 = l1 * ip; 62 | let ido = n / l2; 63 | let ipm = ip - 1; 64 | let mut ld = 0; 65 | let mut j = 0; 66 | while j < ipm { 67 | ld += l1; 68 | let argld = ld as f32 * argh; 69 | let mut i = is; 70 | let mut ii = 2; 71 | let mut fi = 0.0f32; 72 | while ii < ido { 73 | fi += 1.0f32; 74 | let arg = fi * argld; 75 | let fresh0 = i; 76 | i += 1; 77 | wa[fresh0] = f64::cos(arg as f64) as f32; 78 | let fresh1 = i; 79 | i += 1; 80 | wa[fresh1] = f64::sin(arg as f64) as f32; 81 | ii += 2i32 82 | } 83 | is += ido as usize; 84 | j += 1 85 | } 86 | l1 = l2; 87 | } 88 | } 89 | 90 | pub(crate) fn fdrffti(n: usize, wsave: &mut [f32], ifac: &mut [i32]) { 91 | if n == 1 { 92 | return; 93 | } 94 | drfti1(&mut wsave[n..n * 2], ifac); 95 | } 96 | 97 | #[inline(always)] 98 | fn drftf1_l102( 99 | ip: i32, 100 | ido: i32, 101 | l1: i32, 102 | idl1: i32, 103 | iw: i32, 104 | na: &mut i32, 105 | c: &mut [f32], 106 | ch: &mut [f32], 107 | wa: &mut [f32], 108 | ) { 109 | let iw_temp = iw as usize - 1; 110 | if ip != 4 { 111 | if ip != 2 { 112 | if ido == 1 { 113 | *na = 1 - *na; 114 | } 115 | if *na != 0 { 116 | dradfg(ido, ip, l1, idl1, ch, c, &wa[iw_temp..]); 117 | *na = 0; 118 | } else { 119 | dradfg(ido, ip, l1, idl1, c, ch, &wa[iw_temp..]); 120 | *na = 1; 121 | } 122 | } else if *na != 0 { 123 | dradf2(ido, l1, ch, c, &mut wa[iw_temp..]); 124 | } else { 125 | dradf2(ido, l1, c, ch, &mut wa[iw_temp..]); 126 | } 127 | } 128 | } 129 | 130 | pub(crate) fn drftf1( 131 | n: i32, 132 | c: &mut [f32], 133 | ch: &mut [f32], 134 | wa: &mut [f32], 135 | ifac: &mut [i32], 136 | ) { 137 | let nf = ifac[1]; 138 | let mut na = 1; 139 | let mut l2 = n; 140 | let mut iw = n; 141 | 142 | for k1 in 0..nf { 143 | let kh = nf - k1; 144 | let ip = ifac[kh as usize + 1]; 145 | let l1 = l2 / ip; 146 | let ido = n / l2; 147 | let idl1 = ido * l1; 148 | iw -= (ip - 1) * ido; 149 | na = 1 - na; 150 | 151 | if ip != 4 { 152 | drftf1_l102(ip, ido, l1, idl1, iw, &mut na, c, ch, wa); 153 | } else { 154 | let ix2 = iw + ido; 155 | let ix3 = ix2 + ido; 156 | if na != 0 { 157 | dradf4( 158 | ido, 159 | l1, 160 | ch, 161 | c, 162 | &wa[(iw as usize - 1)..], 163 | &wa[(ix2 as usize - 1)..], 164 | &wa[(ix3 as usize - 1)..], 165 | ); 166 | } else { 167 | dradf4( 168 | ido, 169 | l1, 170 | c, 171 | ch, 172 | &wa[(iw as usize - 1)..], 173 | &wa[(ix2 as usize - 1)..], 174 | &wa[(ix3 as usize - 1)..], 175 | ); 176 | } 177 | } 178 | 179 | l2 = l1; 180 | } 181 | 182 | if na == 1 { 183 | return; 184 | } 185 | 186 | c[..n as usize].copy_from_slice(&ch[..n as usize]); 187 | } 188 | 189 | #[inline(always)] 190 | fn drftb1_l102( 191 | ip: i32, 192 | ido: i32, 193 | l1: i32, 194 | idl1: i32, 195 | iw: i32, 196 | na: &mut i32, 197 | c: &mut [f32], 198 | ch: &mut [f32], 199 | wa: &mut [f32], 200 | ) { 201 | let iw_temp = iw as usize - 1; 202 | if ip != 2 { 203 | if ip != 3 { 204 | if *na != 0 { 205 | dradbg(ido, ip, l1, idl1, ch, c, &wa[iw_temp..]); 206 | } else { 207 | dradbg(ido, ip, l1, idl1, c, ch, &wa[iw_temp..]); 208 | } 209 | 210 | if ido == 1 { 211 | *na = 1 - *na; 212 | } 213 | } else { 214 | let ix2 = iw + ido - 1; 215 | if *na != 0 { 216 | dradb3(ido, l1, ch, c, &wa[iw_temp..], &wa[ix2 as usize..]); 217 | } else { 218 | dradb3(ido, l1, c, ch, &wa[iw_temp..], &wa[ix2 as usize..]); 219 | } 220 | *na = 1 - *na; 221 | } 222 | } else { 223 | if *na != 0 { 224 | dradb2(ido, l1, ch, c, &wa[iw_temp..]); 225 | } else { 226 | dradb2(ido, l1, c, ch, &wa[iw_temp..]); 227 | } 228 | *na = 1 - *na; 229 | } 230 | } 231 | 232 | pub(crate) fn drftb1( 233 | n: i32, 234 | c: &mut [f32], 235 | ch: &mut [f32], 236 | wa: &mut [f32], 237 | ifac: &mut [i32], 238 | ) { 239 | let nf = ifac[1]; 240 | let mut l1 = 1; 241 | let mut na = 0; 242 | let mut iw = 1; 243 | 244 | for k1 in 0..nf { 245 | let ip = ifac[k1 as usize + 2]; 246 | let l2 = ip * l1; 247 | let ido = n / l2; 248 | let idl1 = ido * l1; 249 | 250 | if ip != 4 { 251 | drftb1_l102(ip, ido, l1, idl1, iw, &mut na, c, ch, wa); 252 | } else { 253 | let ix2 = iw + ido; 254 | let ix3 = ix2 + ido; 255 | if na != 0 { 256 | dradb4( 257 | ido, 258 | l1, 259 | ch, 260 | c, 261 | &wa[(iw as usize - 1)..], 262 | &wa[(ix2 as usize - 1)..], 263 | &wa[(ix3 as usize - 1)..], 264 | ); 265 | } else { 266 | dradb4( 267 | ido, 268 | l1, 269 | c, 270 | ch, 271 | &wa[(iw as usize - 1)..], 272 | &wa[(ix2 as usize - 1)..], 273 | &wa[(ix3 as usize - 1)..], 274 | ); 275 | } 276 | na = 1 - na 277 | } 278 | 279 | l1 = l2; 280 | iw += (ip - 1) * ido; 281 | } 282 | 283 | if na == 0 { 284 | return; 285 | } 286 | 287 | c[..n as usize].copy_from_slice(&ch[..n as usize]); 288 | } 289 | 290 | #[cfg(test)] 291 | mod tests { 292 | use super::*; 293 | 294 | const EPSILON: f32 = 1e-6; 295 | 296 | #[test] 297 | fn fdrffti_simple() { 298 | let mut trigcache = [42.; 3]; 299 | let mut splitcache = [24; 32]; 300 | 301 | fdrffti(1, &mut trigcache, &mut splitcache); 302 | 303 | assert!(trigcache.iter().all(|&x| (x - 42.).abs() < EPSILON)); 304 | assert!(splitcache.iter().all(|&x| x == 24)); 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /fft/tests/fftwrap.rs: -------------------------------------------------------------------------------- 1 | extern crate speexdsp_fft; 2 | 3 | mod orig; 4 | 5 | use std::ptr::null_mut; 6 | 7 | use speexdsp_fft::*; 8 | 9 | use crate::orig::fftwrap as original_fftwrap; 10 | use crate::orig::smallft as original_smallft; 11 | 12 | const INPUT: [f32; 64] = [ 13 | 1.0, 14 | 1.999002, 15 | 2.832922, 16 | 3.3986773, 17 | 3.6254587, 18 | 3.484332, 19 | 2.992106, 20 | 2.2089396, 21 | 1.230057, 22 | 0.17277533, 23 | -0.8392913, 24 | -1.692595, 25 | -2.2982898, 26 | -2.6031098, 27 | -2.5950398, 28 | -2.303094, 29 | -1.7913916, 30 | -1.1485368, 31 | -0.47395423, 32 | 0.13674068, 33 | 0.60517603, 34 | 0.8805469, 35 | 0.94540364, 36 | 0.8161983, 37 | 0.53870463, 38 | 0.17917383, 39 | -0.18727368, 40 | -0.4891553, 41 | -0.6700415, 42 | -0.69716847, 43 | -0.5659317, 44 | -0.29971862, 45 | 0.05482897, 46 | 0.4362133, 47 | 0.77873474, 48 | 1.0236322, 49 | 1.1288913, 50 | 1.0761548, 51 | 0.8736177, 52 | 0.55450416, 53 | 0.1713717, 54 | -0.21276897, 55 | -0.53509253, 56 | -0.74354666, 57 | -0.8057217, 58 | -0.7144466, 59 | -0.48923856, 60 | -0.17328042, 61 | 0.17359133, 62 | 0.48484406, 63 | 0.6984716, 64 | 0.7677859, 65 | 0.6698748, 66 | 0.41039884, 67 | 0.023795009, 68 | -0.43124664, 69 | -0.8804407, 70 | -1.2453988, 71 | -1.4562676, 72 | -1.4634898, 73 | -1.2468007, 74 | -0.8200231, 75 | -0.23070133, 76 | 0.4454986, 77 | ]; 78 | 79 | const EPSILON: f32 = 1e-8; 80 | const N: usize = 2; 81 | const SPLITCACHE_SIZE: usize = 32; 82 | 83 | macro_rules! test_fftwrap { 84 | ($func: ident) => { 85 | let mut output = [0.; 64]; 86 | let mut drft_lookup = DrftLookup::new(INPUT.len()); 87 | let input = INPUT.clone(); 88 | 89 | drft_lookup.$func(&input, &mut output); 90 | 91 | let mut expected_output = [0.; 64]; 92 | unsafe { 93 | let drft_lookup = 94 | original_fftwrap::spx_fft_init(INPUT.len() as i32); 95 | let mut input = INPUT.clone(); 96 | 97 | original_fftwrap::$func( 98 | drft_lookup, 99 | input.as_mut_ptr(), 100 | expected_output.as_mut_ptr(), 101 | ); 102 | original_fftwrap::spx_fft_destroy(drft_lookup); 103 | }; 104 | 105 | assert!(output 106 | .iter() 107 | .zip(expected_output.iter()) 108 | .all(|(a, b)| (a - b).abs() < EPSILON)); 109 | }; 110 | } 111 | 112 | macro_rules! drft { 113 | ($func: ident) => { 114 | let mut drft_lookup = DrftLookup::new(N); 115 | let mut data = vec![0.; 32]; 116 | 117 | drft_lookup.$func(&mut data); 118 | 119 | let mut original_drft_lookup = original_smallft::drft_lookup { 120 | n: 0, 121 | trigcache: null_mut(), 122 | splitcache: null_mut(), 123 | }; 124 | let mut data_original = vec![0.; 32]; 125 | unsafe { 126 | original_smallft::spx_drft_init( 127 | &mut original_drft_lookup, 128 | N as i32, 129 | ); 130 | original_smallft::$func( 131 | &mut original_drft_lookup, 132 | data_original.as_mut_ptr(), 133 | ); 134 | } 135 | 136 | let expected_trigcache = unsafe { 137 | Vec::from_raw_parts( 138 | original_drft_lookup.trigcache as *mut f32, 139 | 3 * N, 140 | 3 * N, 141 | ) 142 | }; 143 | 144 | let expected_splitcache = unsafe { 145 | Vec::from_raw_parts( 146 | original_drft_lookup.splitcache as *mut i32, 147 | SPLITCACHE_SIZE, 148 | SPLITCACHE_SIZE, 149 | ) 150 | }; 151 | 152 | assert!(drft_lookup 153 | .trigcache 154 | .iter() 155 | .zip(expected_trigcache.iter()) 156 | .all(|(&a, &b)| (a - b).abs() < EPSILON)); 157 | 158 | assert_eq!(&drft_lookup.splitcache, &expected_splitcache); 159 | }; 160 | } 161 | 162 | #[test] 163 | fn fft() { 164 | test_fftwrap!(spx_fft); 165 | } 166 | 167 | #[test] 168 | fn ifft() { 169 | test_fftwrap!(spx_ifft); 170 | } 171 | 172 | #[test] 173 | fn fdrffti() { 174 | let drft_lookup = DrftLookup::new(N); 175 | 176 | let mut original_drft_lookup = original_smallft::drft_lookup { 177 | n: 0, 178 | trigcache: null_mut(), 179 | splitcache: null_mut(), 180 | }; 181 | unsafe { 182 | original_smallft::spx_drft_init(&mut original_drft_lookup, N as i32); 183 | } 184 | 185 | let expected_trigcache = unsafe { 186 | Vec::from_raw_parts( 187 | original_drft_lookup.trigcache as *mut f32, 188 | 3 * N, 189 | 3 * N, 190 | ) 191 | }; 192 | 193 | let expected_splitcache = unsafe { 194 | Vec::from_raw_parts( 195 | original_drft_lookup.splitcache as *mut i32, 196 | SPLITCACHE_SIZE, 197 | SPLITCACHE_SIZE, 198 | ) 199 | }; 200 | 201 | assert!(drft_lookup 202 | .trigcache 203 | .iter() 204 | .zip(expected_trigcache.iter()) 205 | .all(|(&a, &b)| (a - b).abs() < EPSILON)); 206 | 207 | assert_eq!(&drft_lookup.splitcache, &expected_splitcache); 208 | } 209 | 210 | #[test] 211 | fn drftf1() { 212 | drft!(spx_drft_forward); 213 | } 214 | 215 | #[test] 216 | fn drftb1() { 217 | drft!(spx_drft_backward); 218 | } 219 | -------------------------------------------------------------------------------- /fft/tests/orig/fftwrap.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | dead_code, 3 | mutable_transmutes, 4 | non_camel_case_types, 5 | non_snake_case, 6 | non_upper_case_globals, 7 | unused_assignments, 8 | unused_mut 9 | )] 10 | 11 | use std::{ 12 | ffi::c_void, 13 | os::raw::{c_double, c_float, c_int, c_ulong}, 14 | }; 15 | 16 | use super::smallft::*; 17 | 18 | extern "C" { 19 | 20 | fn calloc(_: c_ulong, _: c_ulong) -> *mut c_void; 21 | 22 | fn free(__ptr: *mut c_void); 23 | } 24 | /* Copyright (C) 2007 Jean-Marc Valin 25 | 26 | File: os_support.h 27 | This is the (tiny) OS abstraction layer. Aside from math.h, this is the 28 | only place where system headers are allowed. 29 | 30 | Redistribution and use in source and binary forms, with or without 31 | modification, are permitted provided that the following conditions are 32 | met: 33 | 34 | 1. Redistributions of source code must retain the above copyright notice, 35 | this list of conditions and the following disclaimer. 36 | 37 | 2. Redistributions in binary form must reproduce the above copyright 38 | notice, this list of conditions and the following disclaimer in the 39 | documentation and/or other materials provided with the distribution. 40 | 41 | 3. The name of the author may not be used to endorse or promote products 42 | derived from this software without specific prior written permission. 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 45 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 46 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 47 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 48 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 49 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 50 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 51 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 52 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 53 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 54 | POSSIBILITY OF SUCH DAMAGE. 55 | */ 56 | /* * Speex wrapper for calloc. To do your own dynamic allocation, all you need to do is replace this function, speex_realloc and speex_free 57 | NOTE: speex_alloc needs to CLEAR THE MEMORY */ 58 | #[inline] 59 | pub unsafe fn speex_alloc(mut size: c_int) -> *mut c_void { 60 | /* WARNING: this is not equivalent to malloc(). If you want to use malloc() 61 | or your own allocator, YOU NEED TO CLEAR THE MEMORY ALLOCATED. Otherwise 62 | you will experience strange bugs */ 63 | calloc(size as c_ulong, 1_i32 as c_ulong) 64 | } 65 | /* * Speex wrapper for calloc. To do your own dynamic allocation, all you need to do is replace this function, speex_realloc and speex_alloc */ 66 | #[inline] 67 | pub unsafe fn speex_free(mut ptr: *mut c_void) { 68 | free(ptr); 69 | } 70 | /* Copyright (C) 2005-2006 Jean-Marc Valin 71 | File: fftwrap.c 72 | 73 | Wrapper for various FFTs 74 | 75 | Redistribution and use in source and binary forms, with or without 76 | modification, are permitted provided that the following conditions 77 | are met: 78 | 79 | - Redistributions of source code must retain the above copyright 80 | notice, this list of conditions and the following disclaimer. 81 | 82 | - Redistributions in binary form must reproduce the above copyright 83 | notice, this list of conditions and the following disclaimer in the 84 | documentation and/or other materials provided with the distribution. 85 | 86 | - Neither the name of the Xiph.org Foundation nor the names of its 87 | contributors may be used to endorse or promote products derived from 88 | this software without specific prior written permission. 89 | 90 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 91 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 92 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 93 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR 94 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 95 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 96 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 97 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 98 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 99 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 100 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 101 | 102 | */ 103 | 104 | pub unsafe fn spx_fft_init(mut size: c_int) -> *mut c_void { 105 | let mut table: *mut drft_lookup = std::ptr::null_mut::(); 106 | table = 107 | speex_alloc(::std::mem::size_of::() as c_ulong as c_int) 108 | as *mut drft_lookup; 109 | spx_drft_init(table, size); 110 | table as *mut c_void 111 | } 112 | 113 | pub unsafe fn spx_fft_destroy(mut table: *mut c_void) { 114 | spx_drft_clear(table as *mut drft_lookup); 115 | speex_free(table); 116 | } 117 | 118 | pub unsafe fn spx_fft( 119 | mut table: *mut c_void, 120 | mut in_0: *mut c_float, 121 | mut out: *mut c_float, 122 | ) { 123 | if in_0 == out { 124 | let mut i: c_int = 0; 125 | let mut scale: c_float = 126 | (1.0f64 / (*(table as *mut drft_lookup)).n as c_double) as c_float; 127 | eprintln!("FFT should not be done in-place"); 128 | i = 0_i32; 129 | while i < (*(table as *mut drft_lookup)).n { 130 | *out.offset(i as isize) = scale * *in_0.offset(i as isize); 131 | i += 1 132 | } 133 | } else { 134 | let mut i_0: c_int = 0; 135 | let mut scale_0: c_float = 136 | (1.0f64 / (*(table as *mut drft_lookup)).n as c_double) as c_float; 137 | i_0 = 0_i32; 138 | while i_0 < (*(table as *mut drft_lookup)).n { 139 | *out.offset(i_0 as isize) = scale_0 * *in_0.offset(i_0 as isize); 140 | i_0 += 1 141 | } 142 | } 143 | spx_drft_forward(table as *mut drft_lookup, out); 144 | } 145 | 146 | pub unsafe fn spx_ifft( 147 | mut table: *mut c_void, 148 | mut in_0: *mut c_float, 149 | mut out: *mut c_float, 150 | ) { 151 | if in_0 == out { 152 | eprintln!("FFT should not be done in-place"); 153 | } else { 154 | let mut i: c_int = 0; 155 | i = 0_i32; 156 | while i < (*(table as *mut drft_lookup)).n { 157 | *out.offset(i as isize) = *in_0.offset(i as isize); 158 | i += 1 159 | } 160 | } 161 | spx_drft_backward(table as *mut drft_lookup, out); 162 | } 163 | 164 | pub unsafe fn spx_fft_float( 165 | mut table: *mut c_void, 166 | mut in_0: *mut c_float, 167 | mut out: *mut c_float, 168 | ) { 169 | spx_fft(table, in_0, out); 170 | } 171 | 172 | pub unsafe fn spx_ifft_float( 173 | mut table: *mut c_void, 174 | mut in_0: *mut c_float, 175 | mut out: *mut c_float, 176 | ) { 177 | spx_ifft(table, in_0, out); 178 | } 179 | -------------------------------------------------------------------------------- /fft/tests/orig/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod fftwrap; 2 | pub mod smallft; 3 | -------------------------------------------------------------------------------- /resampler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "speexdsp-resampler" 3 | version = "0.1.0" 4 | authors = ["Luca Barbato "] 5 | description = "Pure rust port of the speexdsp resampler" 6 | homepage = "https://github.com/rust-av/speexdsp-rs" 7 | repository = "https://github.com/rust-av/speexdsp-rs" 8 | keywords = ["audio", "resampler"] 9 | license = "BSD-3-Clause" 10 | readme = "README.md" 11 | edition = "2021" 12 | 13 | [features] 14 | sse3 = [] 15 | avx = ["sse3"] 16 | dynnative = ["avx"] 17 | 18 | [dependencies] 19 | cfg-if = "1.0.0" 20 | -------------------------------------------------------------------------------- /resampler/LICENSE.BSD-3: -------------------------------------------------------------------------------- 1 | Copyright 2002-2008 Xiph.org Foundation 2 | Copyright 2002-2008 Jean-Marc Valin 3 | Copyright 2005-2007 Analog Devices Inc. 4 | Copyright 2005-2008 Commonwealth Scientific and Industrial Research 5 | Organisation (CSIRO) 6 | Copyright 1993, 2002, 2006 David Rowe 7 | Copyright 2003 EpicGames 8 | Copyright 1992-1994 Jutta Degener, Carsten Bormann 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions 12 | are met: 13 | 14 | - Redistributions of source code must retain the above copyright 15 | notice, this list of conditions and the following disclaimer. 16 | 17 | - Redistributions in binary form must reproduce the above copyright 18 | notice, this list of conditions and the following disclaimer in the 19 | documentation and/or other materials provided with the distribution. 20 | 21 | - Neither the name of the Xiph.org Foundation nor the names of its 22 | contributors may be used to endorse or promote products derived from 23 | this software without specific prior written permission. 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR 29 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 30 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 31 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 32 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 33 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 34 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 35 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | -------------------------------------------------------------------------------- /resampler/README.md: -------------------------------------------------------------------------------- 1 | # speexdsp resampler 2 | 3 | [![LICENSE](https://img.shields.io/badge/license-BSD3-blue.svg)](LICENSE.BSD-3) 4 | ![Crates.io](https://img.shields.io/crates/v/speexdsp-resampler) 5 | [![Build Status](https://travis-ci.org/rust-av/speexdsp-rs.svg?branch=master)](https://travis-ci.org/rust-av/speexdsp-rs) 6 | [![dependency status](https://deps.rs/repo/github/rust-av/speexdsp-rs/status.svg)](https://deps.rs/repo/github/rust-av/speexdsp-rs) 7 | [![IRC](https://img.shields.io/badge/irc-%23rust--av-blue.svg)](http://webchat.freenode.net?channels=%23rust-av&uio=d4) 8 | 9 | [c2rust](https://github.com/immunant/c2rust)-based port of the [speexdsp](https://github.com/xiph/speexdsp) resampler. 10 | 11 | ## TODO 12 | - [x] Simple bindings 13 | - [x] Safe abstraction 14 | - [x] Examples 15 | - [ ] Cleaner code 16 | - [ ] More API surface 17 | -------------------------------------------------------------------------------- /resampler/examples/resample.rs: -------------------------------------------------------------------------------- 1 | use speexdsp_resampler::*; 2 | 3 | use std::f32::consts::PI; 4 | 5 | const PERIOD: f32 = 32f32; 6 | const INBLOCK: usize = 1024; 7 | const RATE: usize = 48000; 8 | 9 | fn main() { 10 | let mut rate = 1000; 11 | let mut off = 0; 12 | let mut avail = INBLOCK as isize; 13 | 14 | let fin: Vec = (0..INBLOCK * 4) 15 | .map(|i| ((i as f32) / PERIOD * 2.0 * PI).sin() * 0.9) 16 | .collect(); 17 | let mut fout = vec![0f32; INBLOCK * 8]; 18 | 19 | let mut st = State::new(1, RATE, RATE, 4).unwrap(); 20 | 21 | st.set_rate(RATE, rate).unwrap(); 22 | st.skip_zeros(); 23 | 24 | st.set_quality(10).unwrap(); 25 | 26 | eprintln!("Quality: {}", st.get_quality()); 27 | 28 | let mut data = Vec::new(); 29 | 30 | loop { 31 | let in_len = avail as usize; 32 | let out_len = (in_len * rate + RATE - 1) / RATE; 33 | 34 | let prev_in_len = in_len; 35 | let prev_out_len = out_len; 36 | 37 | let (in_len, out_len) = st 38 | .process(0, &fin[off..off + in_len], &mut fout[..out_len]) 39 | .unwrap(); 40 | 41 | eprintln!( 42 | "{} {} {} {} -> {} {}", 43 | rate, off, prev_in_len, prev_out_len, in_len, out_len 44 | ); 45 | 46 | off += in_len; 47 | avail += INBLOCK as isize - in_len as isize; 48 | 49 | if off >= INBLOCK { 50 | off -= INBLOCK; 51 | } 52 | 53 | data.push(fout[..out_len].to_vec()); 54 | 55 | rate += 5000; 56 | if rate > 128000 { 57 | break; 58 | } 59 | 60 | st.set_rate(RATE, rate).unwrap(); 61 | } 62 | 63 | println!("{:#?}", data); 64 | } 65 | -------------------------------------------------------------------------------- /resampler/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::unreadable_literal)] 2 | 3 | mod speex; 4 | 5 | pub struct State { 6 | st: speex::SpeexResamplerState, 7 | } 8 | 9 | #[derive(Debug, Clone, Copy)] 10 | pub enum Error { 11 | AllocFailed, 12 | InvalidArg, 13 | } 14 | 15 | pub trait Sample: Copy + Clone { 16 | fn process( 17 | st: &mut speex::SpeexResamplerState, 18 | index: usize, 19 | input: &[Self], 20 | output: &mut [Self], 21 | ) -> Result<(usize, usize), Error>; 22 | 23 | fn process_interleaved( 24 | st: &mut speex::SpeexResamplerState, 25 | input: &[Self], 26 | output: &mut [Self], 27 | ) -> Result<(usize, usize), Error>; 28 | } 29 | 30 | impl Sample for i16 { 31 | fn process( 32 | st: &mut speex::SpeexResamplerState, 33 | index: usize, 34 | input: &[i16], 35 | output: &mut [i16], 36 | ) -> Result<(usize, usize), Error> { 37 | let mut in_len = input.len() as u32; 38 | let mut out_len = output.len() as u32; 39 | let ret = st.process_int( 40 | index as u32, 41 | input, 42 | &mut in_len, 43 | output, 44 | &mut out_len, 45 | ); 46 | 47 | if ret != 0 { 48 | Err(Error::AllocFailed) 49 | } else { 50 | Ok((in_len as usize, out_len as usize)) 51 | } 52 | } 53 | 54 | fn process_interleaved( 55 | st: &mut speex::SpeexResamplerState, 56 | input: &[i16], 57 | output: &mut [i16], 58 | ) -> Result<(usize, usize), Error> { 59 | let mut in_len = input.len() as u32; 60 | let mut out_len = output.len() as u32; 61 | let ret = st.process_interleaved_int( 62 | input, 63 | &mut in_len, 64 | output, 65 | &mut out_len, 66 | ); 67 | 68 | if ret != 0 { 69 | Err(Error::AllocFailed) 70 | } else { 71 | Ok((in_len as usize, out_len as usize)) 72 | } 73 | } 74 | } 75 | 76 | impl Sample for f32 { 77 | fn process( 78 | st: &mut speex::SpeexResamplerState, 79 | index: usize, 80 | input: &[f32], 81 | output: &mut [f32], 82 | ) -> Result<(usize, usize), Error> { 83 | let mut in_len = input.len() as u32; 84 | let mut out_len = output.len() as u32; 85 | let ret = st.process_float( 86 | index as u32, 87 | input, 88 | &mut in_len, 89 | output, 90 | &mut out_len, 91 | ); 92 | 93 | if ret != 0 { 94 | Err(Error::AllocFailed) 95 | } else { 96 | Ok((in_len as usize, out_len as usize)) 97 | } 98 | } 99 | 100 | fn process_interleaved( 101 | st: &mut speex::SpeexResamplerState, 102 | input: &[f32], 103 | output: &mut [f32], 104 | ) -> Result<(usize, usize), Error> { 105 | let mut in_len = input.len() as u32; 106 | let mut out_len = output.len() as u32; 107 | let ret = st.process_interleaved_float( 108 | input, 109 | &mut in_len, 110 | output, 111 | &mut out_len, 112 | ); 113 | 114 | if ret != 0 { 115 | Err(Error::AllocFailed) 116 | } else { 117 | Ok((in_len as usize, out_len as usize)) 118 | } 119 | } 120 | } 121 | 122 | impl State { 123 | pub fn new( 124 | channels: usize, 125 | in_rate: usize, 126 | out_rate: usize, 127 | quality: usize, 128 | ) -> Result { 129 | let st = speex::SpeexResamplerState::new( 130 | channels, in_rate, out_rate, quality, 131 | ); 132 | 133 | Ok(State { st }) 134 | } 135 | 136 | pub fn set_rate( 137 | &mut self, 138 | in_rate: usize, 139 | out_rate: usize, 140 | ) -> Result<(), Error> { 141 | if self.st.set_rate(in_rate, out_rate) != 0 { 142 | Err(Error::InvalidArg) 143 | } else { 144 | Ok(()) 145 | } 146 | } 147 | 148 | pub fn get_rate(&self) -> (usize, usize) { 149 | self.st.get_rate() 150 | } 151 | 152 | pub fn get_ratio(&self) -> (usize, usize) { 153 | self.st.get_ratio() 154 | } 155 | 156 | pub fn process( 157 | &mut self, 158 | index: usize, 159 | input: &[S], 160 | output: &mut [S], 161 | ) -> Result<(usize, usize), Error> { 162 | S::process(&mut self.st, index, input, output) 163 | } 164 | 165 | pub fn process_interleaved( 166 | &mut self, 167 | input: &[S], 168 | output: &mut [S], 169 | ) -> Result<(usize, usize), Error> { 170 | S::process_interleaved(&mut self.st, input, output) 171 | } 172 | 173 | pub fn skip_zeros(&mut self) { 174 | self.st.skip_zeros(); 175 | } 176 | 177 | pub fn reset(&mut self) { 178 | self.st.reset_mem(); 179 | } 180 | 181 | pub fn get_input_latency(&self) -> usize { 182 | self.st.get_input_latency() 183 | } 184 | 185 | pub fn get_output_latency(&self) -> usize { 186 | self.st.get_output_latency() 187 | } 188 | 189 | pub fn set_quality(&mut self, quality: usize) -> Result<(), Error> { 190 | if self.st.set_quality(quality) != 0 { 191 | Err(Error::InvalidArg) 192 | } else { 193 | Ok(()) 194 | } 195 | } 196 | 197 | pub fn get_quality(&self) -> usize { 198 | self.st.get_quality() 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /resampler/src/speex.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | /// [speexdsp][1]-derived audio resampler 4 | /// 5 | /// [1]: https://github.com/xiph/speexdsp 6 | use std::mem; 7 | 8 | use cfg_if::cfg_if; 9 | 10 | use std::f64::consts::PI as PI_64; 11 | 12 | pub const RESAMPLER_ERR_SUCCESS: usize = 0; 13 | pub const RESAMPLER_ERR_ALLOC_FAILED: usize = 1; 14 | pub const RESAMPLER_ERR_INVALID_ARG: usize = 3; 15 | pub const RESAMPLER_ERR_OVERFLOW: usize = 5; 16 | 17 | #[derive(Clone)] 18 | pub struct SpeexResamplerState { 19 | in_rate: u32, 20 | out_rate: u32, 21 | num_rate: u32, 22 | den_rate: u32, 23 | quality: u32, 24 | nb_channels: u32, 25 | filt_len: u32, 26 | mem_alloc_size: u32, 27 | buffer_size: u32, 28 | int_advance: u32, 29 | frac_advance: u32, 30 | cutoff: f32, 31 | oversample: u32, 32 | initialised: u32, 33 | started: u32, 34 | last_sample: Vec, 35 | samp_frac_num: Vec, 36 | magic_samples: Vec, 37 | mem: Vec, 38 | sinc_table: Vec, 39 | sinc_table_length: u32, 40 | resampler_ptr: ResamplerBasicFunc, 41 | in_stride: u32, 42 | out_stride: u32, 43 | } 44 | 45 | #[derive(Copy, Clone)] 46 | pub struct FuncDef { 47 | table: &'static [f64], 48 | oversample: usize, 49 | } 50 | 51 | impl FuncDef { 52 | pub const fn new(table: &'static [f64], oversample: usize) -> Self { 53 | Self { table, oversample } 54 | } 55 | } 56 | 57 | #[derive(Copy, Clone)] 58 | pub struct QualityMapping { 59 | base_length: usize, 60 | oversample: usize, 61 | downsample_bandwidth: f32, 62 | upsample_bandwidth: f32, 63 | window_func: &'static FuncDef, 64 | } 65 | 66 | impl QualityMapping { 67 | pub const fn new( 68 | base_length: usize, 69 | oversample: usize, 70 | downsample_bandwidth: f32, 71 | upsample_bandwidth: f32, 72 | window_func: &'static FuncDef, 73 | ) -> Self { 74 | Self { 75 | base_length, 76 | oversample, 77 | downsample_bandwidth, 78 | upsample_bandwidth, 79 | window_func, 80 | } 81 | } 82 | } 83 | 84 | pub type ResamplerBasicFunc = Option< 85 | fn( 86 | _: &mut SpeexResamplerState, 87 | _: u32, 88 | _: &[f32], 89 | _: &mut u32, 90 | _: &mut [f32], 91 | _: &mut u32, 92 | ) -> i32, 93 | >; 94 | 95 | macro_rules! chunk_assign { 96 | ($ch_mut:ident, $lbound_mut:expr, $ubound_mut:expr, $val:expr) => { 97 | $ch_mut[$lbound_mut as usize..$ubound_mut as usize] 98 | .iter_mut() 99 | .for_each(|x| *x = $val); 100 | }; 101 | } 102 | 103 | macro_rules! chunk_copy { 104 | ($ch_mut:ident, $lbound_mut:expr, $ubound_mut:expr, 105 | $ch:ident, $lbound:expr, $ubound:expr) => { 106 | $ch_mut[$lbound_mut as usize..$ubound_mut as usize] 107 | .iter_mut() 108 | .zip($ch[$lbound as usize..$ubound as usize].iter()) 109 | .for_each(|(x, y)| *x = *y); 110 | }; 111 | } 112 | 113 | macro_rules! algo { 114 | ($self:ident, $ch_mut:ident, $ch:ident, 115 | $old_length:ident, $magic:expr) => { 116 | let olen = $old_length + 2 * $magic; 117 | let filt_len = $self.filt_len - 1; 118 | if $self.filt_len > olen { 119 | let new_filt_len = $self.filt_len - olen; 120 | for new_last_sample in &mut $self.last_sample { 121 | chunk_copy!($ch_mut, new_filt_len, filt_len, $ch, 0, olen - 1); 122 | chunk_assign!($ch_mut, 0, new_filt_len, 0.0); 123 | $magic = 0; 124 | *new_last_sample += new_filt_len / 2; 125 | } 126 | } else { 127 | $magic = (olen - $self.filt_len) / 2; 128 | let ubound_mut = filt_len + $magic; 129 | let ubound = ubound_mut + $magic; 130 | chunk_copy!($ch_mut, 0, ubound_mut, $ch, $magic, ubound); 131 | } 132 | }; 133 | } 134 | 135 | impl SpeexResamplerState { 136 | /* * Create a new resampler with integer input and output rates. 137 | * @param nb_channels number of channels to be processed 138 | * @param in_rate Input sampling rate (integer number of Hz). 139 | * @param out_rate Output sampling rate (integer number of Hz). 140 | * @param quality Resampling quality between 0 and 10, where 0 has poor quality 141 | * and 10 has very high quality. 142 | * @return newly created resampler state 143 | */ 144 | pub fn new( 145 | nb_channels: usize, 146 | in_rate: usize, 147 | out_rate: usize, 148 | quality: usize, 149 | ) -> Self { 150 | Self::init_frac( 151 | nb_channels, 152 | in_rate, 153 | out_rate, 154 | in_rate, 155 | out_rate, 156 | quality, 157 | ) 158 | } 159 | 160 | /* * Create a new resampler with fractional input/output rates. The sampling 161 | * rate ratio is an arbitrary rational number with both the numerator and 162 | * denominator being 32-bit integers. 163 | * @param nb_channels number of channels to be processed 164 | * @param ratio_num numerator of the sampling rate ratio 165 | * @param ratio_den Denominator of the sampling rate ratio 166 | * @param in_rate Input sampling rate rounded to the nearest integer (in Hz). 167 | * @param out_rate Output sampling rate rounded to the nearest integer (in Hz). 168 | * @param quality Resampling quality between 0 and 10, where 0 has poor quality 169 | * and 10 has very high quality. 170 | * @return newly created resampler state 171 | * @retval nULL Error: not enough memory 172 | */ 173 | pub fn init_frac( 174 | nb_channels: usize, 175 | ratio_num: usize, 176 | ratio_den: usize, 177 | in_rate: usize, 178 | out_rate: usize, 179 | quality: usize, 180 | ) -> Self { 181 | if nb_channels == 0 || ratio_num == 0 || ratio_den == 0 || quality > 10 182 | { 183 | panic!("Set the correct parameters!"); 184 | } 185 | let mut st = Self { 186 | initialised: 0, 187 | started: 0, 188 | in_rate: 0, 189 | out_rate: 0, 190 | num_rate: 0, 191 | den_rate: 0, 192 | quality: 0, 193 | sinc_table: Vec::new(), 194 | sinc_table_length: 0, 195 | mem: Vec::new(), 196 | frac_advance: 0, 197 | int_advance: 0, 198 | mem_alloc_size: 0, 199 | filt_len: 0, 200 | resampler_ptr: None, 201 | cutoff: 1.0, 202 | nb_channels: nb_channels as u32, 203 | in_stride: 1, 204 | out_stride: 1, 205 | buffer_size: 160, 206 | oversample: 0, 207 | last_sample: vec![0; nb_channels], 208 | magic_samples: vec![0; nb_channels], 209 | samp_frac_num: vec![0; nb_channels], 210 | }; 211 | st.set_quality(quality); 212 | st.set_rate_frac(ratio_num, ratio_den, in_rate, out_rate); 213 | let filter_err = st.update_filter(); 214 | if filter_err == RESAMPLER_ERR_SUCCESS { 215 | st.initialised = 1; 216 | } else { 217 | panic!("Error"); 218 | } 219 | st 220 | } 221 | 222 | /* * Resample a float array. The input and output buffers must *not* overlap. 223 | * @param st Resampler state 224 | * @param channel_index Index of the channel to process for the multi-channel 225 | * base (0 otherwise) 226 | * @param in Input buffer 227 | * @param in_len number of input samples in the input buffer. Returns the 228 | * number of samples processed 229 | * @param out Output buffer 230 | * @param out_len Size of the output buffer. Returns the number of samples written 231 | */ 232 | pub fn process_float( 233 | &mut self, 234 | channel_index: u32, 235 | mut in_0: &[f32], 236 | in_len: &mut u32, 237 | mut out: &mut [f32], 238 | out_len: &mut u32, 239 | ) -> usize { 240 | if in_0.is_empty() { 241 | panic!("Empty input slice is not allowed"); 242 | } 243 | let mut ilen = *in_len; 244 | let mut olen = *out_len; 245 | let channel_idx = channel_index as usize; 246 | let filt_offs = (self.filt_len - 1) as usize; 247 | let mem_idx = filt_offs + channel_idx * self.mem_alloc_size as usize; 248 | let xlen = self.mem_alloc_size - self.filt_len - 1; 249 | let istride = self.in_stride as usize; 250 | if self.magic_samples[channel_idx] != 0 { 251 | olen -= speex_resampler_magic(self, channel_index, &mut out, olen); 252 | } 253 | if self.magic_samples[channel_idx] == 0 { 254 | while 0 != ilen && 0 != olen { 255 | let mut ichunk: u32 = if ilen > xlen { xlen } else { ilen }; 256 | let mut ochunk: u32 = olen; 257 | let mem_slice = &mut self.mem[mem_idx..]; 258 | let mem_iter = mem_slice.iter_mut(); 259 | let in_iter = in_0.iter().step_by(istride); 260 | mem_iter.zip(in_iter).take(ichunk as usize).for_each( 261 | |(x, &y)| { 262 | *x = y; 263 | }, 264 | ); 265 | speex_resampler_process_native( 266 | self, 267 | channel_index, 268 | &mut ichunk, 269 | out, 270 | &mut ochunk, 271 | ); 272 | ilen -= ichunk; 273 | olen -= ochunk; 274 | out = &mut out[(ochunk * self.out_stride) as usize..][..]; 275 | in_0 = &in_0[(ichunk * self.in_stride) as usize..][..]; 276 | } 277 | } 278 | *in_len -= ilen; 279 | *out_len -= olen; 280 | let resampler = self.resampler_ptr.unwrap(); 281 | if resampler as usize == resampler_basic_zero as usize { 282 | RESAMPLER_ERR_ALLOC_FAILED 283 | } else { 284 | RESAMPLER_ERR_SUCCESS 285 | } 286 | } 287 | 288 | /* * Resample an int array. The input and output buffers muself *not* overlap. 289 | * @param self Resampler selfate 290 | * @param channel_index Index of the channel to process for the multi-channel 291 | * base (0 otherwise) 292 | * @param in Input buffer 293 | * @param in_len number of input samples in the input buffer. Returns the number 294 | * of samples processed 295 | * @param out Output buffer 296 | * @param out_len Size of the output buffer. Returns the number of samples written 297 | */ 298 | pub fn process_int( 299 | &mut self, 300 | channel_index: u32, 301 | mut in_0: &[i16], 302 | in_len: &mut u32, 303 | mut out: &mut [i16], 304 | out_len: &mut u32, 305 | ) -> usize { 306 | if in_0.is_empty() { 307 | panic!("Empty input slice is not allowed"); 308 | } 309 | let istride_save = self.in_stride; 310 | let ostride_save = self.out_stride; 311 | let mut ilen = *in_len; 312 | let mut olen = *out_len; 313 | let mem_idx = (channel_index * self.mem_alloc_size) as usize; 314 | let xlen: u32 = self.mem_alloc_size - self.filt_len - 1; 315 | let ylen: u32 = if olen < 8192 { olen } else { 8192 }; 316 | let mut yselfack: Vec = vec![0.; ylen as usize]; 317 | self.out_stride = 1; 318 | while 0 != ilen && 0 != olen { 319 | let mut ichunk: u32 = if ilen > xlen { xlen } else { ilen }; 320 | let mut ochunk: u32 = if olen > ylen { ylen } else { olen }; 321 | let mut omagic: u32 = 0; 322 | if self.magic_samples[channel_index as usize] != 0 { 323 | omagic = speex_resampler_magic( 324 | self, 325 | channel_index, 326 | &mut yselfack.as_mut_slice(), 327 | ochunk, 328 | ); 329 | ochunk -= omagic; 330 | olen -= omagic 331 | } 332 | if 0 == self.magic_samples[channel_index as usize] { 333 | self.mem 334 | .iter_mut() 335 | .zip(in_0.iter().step_by(istride_save as usize)) 336 | .skip(mem_idx + (self.filt_len - 1) as usize) 337 | .take(ichunk as usize) 338 | .for_each(|(x, in_0_v)| { 339 | *x = *in_0_v as f32; 340 | }); 341 | speex_resampler_process_native( 342 | self, 343 | channel_index, 344 | &mut ichunk, 345 | yselfack.as_mut_slice(), 346 | &mut ochunk, 347 | ); 348 | } else { 349 | ichunk = 0i32 as u32; 350 | ochunk = 0i32 as u32 351 | } 352 | 353 | out.iter_mut() 354 | .step_by(ostride_save as usize) 355 | .zip(yselfack.iter()) 356 | .for_each(|(x, y)| { 357 | *x = (if *y < -32767.5 { 358 | -32768 359 | } else if *y > 32766.5 { 360 | 32767 361 | } else { 362 | (0.5 + *y).floor() as i32 363 | }) as i16 364 | }); 365 | 366 | ilen -= ichunk; 367 | olen -= ochunk; 368 | out = &mut out[(ochunk + omagic * ostride_save) as usize..]; 369 | in_0 = &in_0[(ichunk * istride_save) as usize..]; 370 | } 371 | self.out_stride = ostride_save; 372 | *in_len -= ilen; 373 | *out_len -= olen; 374 | let resampler = self.resampler_ptr.unwrap(); 375 | if resampler as usize == resampler_basic_zero as usize { 376 | RESAMPLER_ERR_ALLOC_FAILED 377 | } else { 378 | RESAMPLER_ERR_SUCCESS 379 | } 380 | } 381 | 382 | /* * Resample an interleaved float array. The input and output buffers muself *not* overlap. 383 | * @param self Resampler selfate 384 | * @param in Input buffer 385 | * @param in_len number of input samples in the input buffer. Returns the number 386 | * of samples processed. This is all per-channel. 387 | * @param out Output buffer 388 | * @param out_len Size of the output buffer. Returns the number of samples written. 389 | * This is all per-channel. 390 | */ 391 | pub fn process_interleaved_float( 392 | &mut self, 393 | in_0: &[f32], 394 | in_len: &mut u32, 395 | out: &mut [f32], 396 | out_len: &mut u32, 397 | ) -> usize { 398 | if in_0.is_empty() { 399 | panic!("Empty input slice is not allowed"); 400 | } 401 | let istride_save = self.in_stride; 402 | let ostride_save = self.out_stride; 403 | let bak_out_len = *out_len; 404 | let bak_in_len = *in_len; 405 | 406 | self.out_stride = self.nb_channels; 407 | self.in_stride = self.out_stride; 408 | (0..self.nb_channels as usize).for_each(|i| { 409 | *out_len = bak_out_len; 410 | *in_len = bak_in_len; 411 | self.process_float( 412 | i as u32, 413 | &in_0[i..], 414 | in_len, 415 | &mut out[i..], 416 | out_len, 417 | ); 418 | }); 419 | self.in_stride = istride_save; 420 | self.out_stride = ostride_save; 421 | let resampler = self.resampler_ptr.unwrap(); 422 | if resampler as usize == resampler_basic_zero as usize { 423 | RESAMPLER_ERR_ALLOC_FAILED 424 | } else { 425 | RESAMPLER_ERR_SUCCESS 426 | } 427 | } 428 | 429 | /* * Resample an interleaved int array. The input and output buffers muself *not* overlap. 430 | * @param self Resampler selfate 431 | * @param in Input buffer 432 | * @param in_len number of input samples in the input buffer. Returns the number 433 | * of samples processed. This is all per-channel. 434 | * @param out Output buffer 435 | * @param out_len Size of the output buffer. Returns the number of samples written. 436 | * This is all per-channel. 437 | */ 438 | pub fn process_interleaved_int( 439 | &mut self, 440 | in_0: &[i16], 441 | in_len: &mut u32, 442 | out: &mut [i16], 443 | out_len: &mut u32, 444 | ) -> usize { 445 | if in_0.is_empty() { 446 | panic!("Empty input slice is not allowed"); 447 | } 448 | let istride_save = self.in_stride; 449 | let ostride_save = self.out_stride; 450 | let bak_out_len = *out_len; 451 | let bak_in_len = *in_len; 452 | 453 | self.out_stride = self.nb_channels; 454 | self.in_stride = self.out_stride; 455 | (0..self.nb_channels as usize).for_each(|i| { 456 | *out_len = bak_out_len; 457 | *in_len = bak_in_len; 458 | self.process_int( 459 | i as u32, 460 | &in_0[i..], 461 | in_len, 462 | &mut out[i..], 463 | out_len, 464 | ); 465 | }); 466 | self.in_stride = istride_save; 467 | self.out_stride = ostride_save; 468 | let resampler = self.resampler_ptr.unwrap(); 469 | if resampler as usize == resampler_basic_zero as usize { 470 | RESAMPLER_ERR_ALLOC_FAILED 471 | } else { 472 | RESAMPLER_ERR_SUCCESS 473 | } 474 | } 475 | 476 | /* * Set (change) the conversion quality. 477 | * @param st Resampler state 478 | * @param quality Resampling quality between 0 and 10, where 0 has poor 479 | * quality and 10 has very high quality. 480 | */ 481 | pub fn set_quality(&mut self, quality: usize) -> usize { 482 | if quality > 10 { 483 | RESAMPLER_ERR_INVALID_ARG 484 | } else if self.quality as usize == quality { 485 | RESAMPLER_ERR_SUCCESS 486 | } else { 487 | self.quality = quality as u32; 488 | if self.initialised != 0 { 489 | self.update_filter() 490 | } else { 491 | RESAMPLER_ERR_SUCCESS 492 | } 493 | } 494 | } 495 | 496 | /* * Set (change) the input/output sampling rates (integer value). 497 | * @param st Resampler state 498 | * @param in_rate Input sampling rate (integer number of Hz). 499 | * @param out_rate Output sampling rate (integer number of Hz). 500 | */ 501 | pub fn set_rate(&mut self, in_rate: usize, out_rate: usize) -> usize { 502 | self.set_rate_frac(in_rate, out_rate, in_rate, out_rate) 503 | } 504 | 505 | /* * Set (change) the input/output sampling rates and resampling ratio 506 | * (fractional values in Hz supported). 507 | * @param st Resampler state 508 | * @param ratio_num numerator of the sampling rate ratio 509 | * @param ratio_den Denominator of the sampling rate ratio 510 | * @param in_rate Input sampling rate rounded to the nearest integer (in Hz). 511 | * @param out_rate Output sampling rate rounded to the nearest integer (in Hz). 512 | */ 513 | pub fn set_rate_frac( 514 | &mut self, 515 | ratio_num: usize, 516 | ratio_den: usize, 517 | in_rate: usize, 518 | out_rate: usize, 519 | ) -> usize { 520 | if ratio_num == 0 || ratio_den == 0 { 521 | RESAMPLER_ERR_INVALID_ARG 522 | } else if self.in_rate == in_rate as u32 523 | && self.out_rate == out_rate as u32 524 | && self.num_rate == ratio_num as u32 525 | && self.den_rate == ratio_den as u32 526 | { 527 | RESAMPLER_ERR_SUCCESS 528 | } else { 529 | let old_den = self.den_rate; 530 | self.in_rate = in_rate as u32; 531 | self.out_rate = out_rate as u32; 532 | self.num_rate = ratio_num as u32; 533 | self.den_rate = ratio_den as u32; 534 | let fact = _gcd(self.num_rate, self.den_rate); 535 | self.num_rate /= fact; 536 | self.den_rate /= fact; 537 | if old_den > 0 { 538 | for val in &mut self.samp_frac_num { 539 | let res = _muldiv(val, *val, self.den_rate, old_den); 540 | if res != RESAMPLER_ERR_SUCCESS { 541 | return RESAMPLER_ERR_OVERFLOW; 542 | } else if *val >= self.den_rate { 543 | *val = self.den_rate - 1; 544 | } 545 | } 546 | } 547 | if self.initialised != 0 { 548 | self.update_filter() 549 | } else { 550 | RESAMPLER_ERR_SUCCESS 551 | } 552 | } 553 | } 554 | 555 | /* * Get the current input/output sampling rates (integer value). 556 | * @param st Resampler state 557 | */ 558 | pub fn get_rate(&self) -> (usize, usize) { 559 | (self.in_rate as usize, self.out_rate as usize) 560 | } 561 | 562 | /* * Get the current resampling ratio. This will be reduced to the least 563 | * common denominator. 564 | * @param st Resampler state 565 | */ 566 | pub fn get_ratio(&self) -> (usize, usize) { 567 | (self.num_rate as usize, self.den_rate as usize) 568 | } 569 | 570 | /* * Get the conversion quality. 571 | * @param st Resampler state 572 | * @return Resampling quality between 0 and 10, where 0 has poor 573 | * quality and 10 has very high quality. 574 | */ 575 | pub fn get_quality(&self) -> usize { 576 | self.quality as usize 577 | } 578 | 579 | /* * Set (change) the input stride. 580 | * @param st Resampler state 581 | * @param stride Input stride 582 | */ 583 | pub fn set_input_stride(&mut self, stride: usize) { 584 | self.in_stride = stride as u32; 585 | } 586 | 587 | /* * Get the input stride. 588 | * @param st Resampler state 589 | * @return Input stride copied 590 | */ 591 | pub fn get_input_stride(&mut self) -> usize { 592 | self.in_stride as usize 593 | } 594 | 595 | /* * Set (change) the output stride. 596 | * @param st Resampler state 597 | * @param stride Output stride 598 | */ 599 | pub fn set_output_stride(&mut self, stride: usize) { 600 | self.out_stride = stride as u32; 601 | } 602 | 603 | /* * Get the output stride. 604 | * @param st Resampler state copied 605 | * @return Output stride 606 | */ 607 | pub fn get_output_stride(&mut self) -> usize { 608 | self.out_stride as usize 609 | } 610 | 611 | /* * Get the latency introduced by the resampler measured in input samples. 612 | * @param st Resampler state 613 | */ 614 | pub fn get_input_latency(&self) -> usize { 615 | (self.filt_len / 2) as usize 616 | } 617 | 618 | /* * Get the latency introduced by the resampler measured in output samples. 619 | * @param st Resampler state 620 | */ 621 | pub fn get_output_latency(&self) -> usize { 622 | (((self.filt_len / 2) * self.den_rate + (self.num_rate >> 1)) 623 | / self.num_rate) as usize 624 | } 625 | 626 | /* * Make sure that the first samples to go out of the resamplers don't have 627 | * leading zeros. This is only useful before starting to use a newly created 628 | * resampler. It is recommended to use that when resampling an audio file, as 629 | * it will generate a file with the same length. For real-time processing, 630 | * it is probably easier not to use this call (so that the output duration 631 | * is the same for the first frame). 632 | * @param st Resampler state 633 | */ 634 | pub fn skip_zeros(&mut self) { 635 | let filt_len = self.filt_len / 2; 636 | self.last_sample.iter_mut().for_each(|v: &mut u32| { 637 | *v = filt_len; 638 | }); 639 | } 640 | 641 | /* * Reset a resampler so a new (unrelated) stream can be processed. 642 | * @param st Resampler state 643 | */ 644 | pub fn reset_mem(&mut self) { 645 | self.last_sample.iter_mut().for_each(|elem| *elem = 0); 646 | self.magic_samples.iter_mut().for_each(|elem| *elem = 0); 647 | self.samp_frac_num.iter_mut().for_each(|elem| *elem = 0); 648 | 649 | self.mem.iter_mut().for_each(|elem| *elem = 0.); 650 | } 651 | 652 | #[inline] 653 | fn num_den(&mut self) { 654 | self.cutoff = QUALITY_MAP[self.quality as usize].downsample_bandwidth 655 | * self.den_rate as f32 656 | / self.num_rate as f32; 657 | let pass = self.filt_len; 658 | _muldiv(&mut self.filt_len, pass, self.num_rate, self.den_rate); 659 | self.filt_len = ((self.filt_len - 1) & (!7)) + 8; 660 | self.oversample = (1..5) 661 | .filter(|x| 2u32.pow(*x) * self.den_rate < self.num_rate) 662 | .fold(self.oversample, |acc, _| acc >> 1); 663 | 664 | if self.oversample < 1 { 665 | self.oversample = 1; 666 | } 667 | } 668 | 669 | #[inline] 670 | fn use_direct(&mut self) { 671 | let iter_chunk = self.sinc_table.chunks_mut(self.filt_len as usize); 672 | for (i, chunk) in iter_chunk.enumerate() { 673 | for (j, elem) in chunk.iter_mut().enumerate() { 674 | *elem = sinc( 675 | self.cutoff, 676 | (j as f32 - self.filt_len as f32 / 2.0 + 1.0) 677 | - (i as f32) / self.den_rate as f32, 678 | self.filt_len as i32, 679 | QUALITY_MAP[self.quality as usize].window_func, 680 | ); 681 | } 682 | } 683 | if self.quality > 8 { 684 | self.resampler_ptr = Some(resampler_basic_direct_double); 685 | } else { 686 | self.resampler_ptr = Some(resampler_basic_direct_single); 687 | } 688 | } 689 | 690 | #[inline] 691 | fn not_use_direct(&mut self) { 692 | let quality = self.quality as usize; 693 | let cutoff = self.cutoff; 694 | let oversample = self.oversample; 695 | let filt_len = self.filt_len; 696 | self.sinc_table 697 | .iter_mut() 698 | .enumerate() 699 | .take((oversample * filt_len + 8) as usize) 700 | .for_each(|(i, x)| { 701 | *x = sinc( 702 | cutoff, 703 | (i as i32 - 4) as f32 / oversample as f32 704 | - filt_len as f32 / 2.0, 705 | filt_len as i32, 706 | QUALITY_MAP[quality].window_func, 707 | ) 708 | }); 709 | if self.quality > 8 { 710 | self.resampler_ptr = Some(resampler_basic_interpolate_double); 711 | } else { 712 | self.resampler_ptr = Some(resampler_basic_interpolate_single); 713 | } 714 | } 715 | 716 | #[inline(always)] 717 | fn chunks_iterator( 718 | &mut self, 719 | old_length: u32, 720 | alloc_size: usize, 721 | algo: usize, 722 | ) { 723 | let mem_copy = self.mem.clone(); 724 | 725 | let mut_mem = self.mem.chunks_mut(self.mem_alloc_size as usize); 726 | let mem = mem_copy.chunks(alloc_size); 727 | 728 | for (ch_mut, ch) in mut_mem.zip(mem) { 729 | for magic in &mut self.magic_samples { 730 | if algo == 0 { 731 | let range = old_length - 1 + *magic; 732 | chunk_copy!(ch_mut, *magic, range, ch, 0, range); 733 | chunk_assign!(ch_mut, 0, *magic, 0.0); 734 | } else if algo == 1 { 735 | algo!(self, ch_mut, ch, old_length, *magic); 736 | } else { 737 | let skip = (old_length - self.filt_len) / 2; 738 | let ubound = self.filt_len - 1 + skip + *magic; 739 | chunk_copy!(ch_mut, 0, ubound, ch, skip, ubound + skip); 740 | *magic += skip; 741 | } 742 | } 743 | } 744 | } 745 | 746 | fn update_filter(&mut self) -> usize { 747 | let old_length = self.filt_len; 748 | let quality = self.quality as usize; 749 | let old_alloc_size = self.mem_alloc_size as usize; 750 | self.int_advance = self.num_rate / self.den_rate; 751 | self.frac_advance = self.num_rate % self.den_rate; 752 | self.oversample = QUALITY_MAP[quality].oversample as u32; 753 | self.filt_len = QUALITY_MAP[quality].base_length as u32; 754 | if self.num_rate > self.den_rate { 755 | self.num_den(); 756 | } else { 757 | self.cutoff = QUALITY_MAP[quality].upsample_bandwidth; 758 | } 759 | 760 | let use_direct = self.filt_len * self.den_rate 761 | <= self.filt_len * self.oversample + 8 762 | && 2147483647u64 763 | / ::std::mem::size_of::() as u64 764 | / self.den_rate as u64 765 | >= self.filt_len as u64; 766 | 767 | let min_sinc_table_length = if !use_direct { 768 | self.filt_len * self.oversample + 8 769 | } else { 770 | self.filt_len * self.den_rate 771 | }; 772 | 773 | if self.sinc_table_length < min_sinc_table_length { 774 | self.sinc_table = vec![0.0; min_sinc_table_length as usize]; 775 | self.sinc_table_length = min_sinc_table_length; 776 | } 777 | 778 | if use_direct { 779 | self.use_direct(); 780 | } else { 781 | self.not_use_direct(); 782 | } 783 | 784 | let min_alloc_size = self.filt_len - 1 + self.buffer_size; 785 | if min_alloc_size > self.mem_alloc_size { 786 | let mem = self.mem.clone(); 787 | self.mem = vec![0.0; (self.nb_channels * min_alloc_size) as usize]; 788 | self.mem[0..mem.len()].copy_from_slice(&mem); 789 | self.mem_alloc_size = min_alloc_size; 790 | } 791 | 792 | if self.started == 0 { 793 | let dim = (self.nb_channels * self.mem_alloc_size) as usize; 794 | self.mem = vec![0.0; dim]; 795 | } else if self.filt_len > old_length { 796 | self.chunks_iterator(old_length, old_alloc_size, 0); 797 | self.chunks_iterator(old_length, self.mem_alloc_size as usize, 1); 798 | } else if self.filt_len < old_length { 799 | self.chunks_iterator(old_length, self.mem_alloc_size as usize, 2); 800 | } 801 | RESAMPLER_ERR_SUCCESS 802 | } 803 | } 804 | 805 | fn resampler_basic_zero( 806 | st: &mut SpeexResamplerState, 807 | channel_index: u32, 808 | _in_0: &[f32], 809 | in_len: &mut u32, 810 | out: &mut [f32], 811 | out_len: &mut u32, 812 | ) -> i32 { 813 | let mut out_sample: u32 = 0; 814 | let mut last_sample = st.last_sample[channel_index as usize]; 815 | let mut samp_frac_num = st.samp_frac_num[channel_index as usize]; 816 | let out_stride = st.out_stride; 817 | let int_advance = st.int_advance; 818 | let frac_advance = st.frac_advance; 819 | let den_rate: u32 = st.den_rate; 820 | while !(last_sample >= *in_len || out_sample >= *out_len) { 821 | out[(out_stride * out_sample) as usize] = 0.0; 822 | out_sample += 1; 823 | last_sample += int_advance; 824 | samp_frac_num += frac_advance; 825 | if samp_frac_num >= den_rate { 826 | samp_frac_num -= den_rate; 827 | last_sample += 1 828 | } 829 | } 830 | st.last_sample[channel_index as usize] = last_sample; 831 | st.samp_frac_num[channel_index as usize] = samp_frac_num; 832 | out_sample as i32 833 | } 834 | 835 | cfg_if! { 836 | if #[cfg(feature = "dynnative")] { 837 | use dynnative::*; 838 | } else if #[cfg(feature = "avx")] { 839 | use avx::*; 840 | } else if #[cfg(feature = "sse3")] { 841 | use sse3::*; 842 | } else { 843 | use native::*; 844 | } 845 | } 846 | 847 | fn resampler_basic_interpolate_single( 848 | st: &mut SpeexResamplerState, 849 | channel_index: u32, 850 | in_0: &[f32], 851 | in_len: &mut u32, 852 | out: &mut [f32], 853 | out_len: &mut u32, 854 | ) -> i32 { 855 | let n = st.filt_len as usize; 856 | let channel_idx = channel_index as usize; 857 | let mut last_sample = st.last_sample[channel_idx]; 858 | let mut samp_frac_num = st.samp_frac_num[channel_idx]; 859 | let out_stride = st.out_stride; 860 | let int_advance = st.int_advance; 861 | let frac_advance = st.frac_advance; 862 | let den_rate = st.den_rate; 863 | let oversample = st.oversample; 864 | let sinc_table = &st.sinc_table; 865 | 866 | let mut out_sample: u32 = 0; 867 | while !(last_sample >= *in_len || out_sample >= *out_len) { 868 | let iptr = &in_0[last_sample as usize..]; 869 | let offset = samp_frac_num * oversample / den_rate; 870 | let frac = 871 | ((samp_frac_num * oversample) % den_rate) as f32 / den_rate as f32; 872 | 873 | interpolate_step_single( 874 | iptr, 875 | out, 876 | out_stride as usize, 877 | out_sample as usize, 878 | oversample as usize, 879 | offset as usize, 880 | n, 881 | sinc_table, 882 | frac, 883 | ); 884 | 885 | out_sample += 1; 886 | last_sample += int_advance; 887 | samp_frac_num += frac_advance; 888 | if samp_frac_num >= den_rate { 889 | samp_frac_num -= den_rate; 890 | last_sample += 1; 891 | } 892 | } 893 | st.last_sample[channel_idx] = last_sample; 894 | st.samp_frac_num[channel_idx] = samp_frac_num; 895 | out_sample as i32 896 | } 897 | 898 | fn resampler_basic_interpolate_double( 899 | st: &mut SpeexResamplerState, 900 | channel_index: u32, 901 | in_0: &[f32], 902 | in_len: &mut u32, 903 | out: &mut [f32], 904 | out_len: &mut u32, 905 | ) -> i32 { 906 | let n = st.filt_len as usize; 907 | let channel_idx = channel_index as usize; 908 | let mut last_sample = st.last_sample[channel_idx]; 909 | let mut samp_frac_num = st.samp_frac_num[channel_idx]; 910 | let out_stride = st.out_stride; 911 | let int_advance = st.int_advance; 912 | let oversample = st.oversample; 913 | let frac_advance = st.frac_advance; 914 | let den_rate = st.den_rate; 915 | let sinc_table = &st.sinc_table; 916 | 917 | let mut out_sample: u32 = 0; 918 | 919 | while !(last_sample >= *in_len || out_sample >= *out_len) { 920 | let iptr: &[f32] = &in_0[last_sample as usize..]; 921 | let offset = samp_frac_num * st.oversample / st.den_rate; 922 | let frac = 923 | (samp_frac_num * oversample % den_rate) as f32 / den_rate as f32; 924 | 925 | interpolate_step_double( 926 | iptr, 927 | out, 928 | out_stride as usize, 929 | out_sample as usize, 930 | oversample as usize, 931 | offset as usize, 932 | n, 933 | sinc_table, 934 | frac, 935 | ); 936 | 937 | out_sample += 1; 938 | last_sample += int_advance; 939 | samp_frac_num += frac_advance; 940 | if samp_frac_num >= den_rate { 941 | samp_frac_num -= den_rate; 942 | last_sample += 1 943 | } 944 | } 945 | st.last_sample[channel_index as usize] = last_sample; 946 | st.samp_frac_num[channel_index as usize] = samp_frac_num; 947 | out_sample as i32 948 | } 949 | 950 | static QUALITY_MAP: [QualityMapping; 11] = [ 951 | QualityMapping::new(8, 4, 0.83, 0.86, &_KAISER6), 952 | QualityMapping::new(16, 4, 0.85, 0.88, &_KAISER6), 953 | QualityMapping::new(32, 4, 0.882, 0.91, &_KAISER6), 954 | QualityMapping::new(48, 8, 0.895, 0.917, &_KAISER8), 955 | QualityMapping::new(64, 8, 0.921, 0.94, &_KAISER8), 956 | QualityMapping::new(80, 16, 0.922, 0.94, &_KAISER10), 957 | QualityMapping::new(96, 16, 0.94, 0.945, &_KAISER10), 958 | QualityMapping::new(128, 16, 0.95, 0.95, &_KAISER10), 959 | QualityMapping::new(160, 16, 0.96, 0.96, &_KAISER10), 960 | QualityMapping::new(192, 32, 0.968, 0.968, &_KAISER12), 961 | QualityMapping::new(256, 32, 0.975, 0.975, &_KAISER12), 962 | ]; 963 | 964 | static _KAISER12: FuncDef = FuncDef::new(&KAISER12_TABLE, 64); 965 | 966 | static KAISER12_TABLE: [f64; 68] = { 967 | [ 968 | 0.99859849, 969 | 1.0, 970 | 0.99859849, 971 | 0.99440475, 972 | 0.98745105, 973 | 0.97779076, 974 | 0.9654977, 975 | 0.95066529, 976 | 0.93340547, 977 | 0.91384741, 978 | 0.89213598, 979 | 0.86843014, 980 | 0.84290116, 981 | 0.81573067, 982 | 0.78710866, 983 | 0.75723148, 984 | 0.7262997, 985 | 0.69451601, 986 | 0.66208321, 987 | 0.62920216, 988 | 0.59606986, 989 | 0.56287762, 990 | 0.52980938, 991 | 0.49704014, 992 | 0.46473455, 993 | 0.43304576, 994 | 0.40211431, 995 | 0.37206735, 996 | 0.343018, 997 | 0.3150649, 998 | 0.28829195, 999 | 0.26276832, 1000 | 0.23854851, 1001 | 0.21567274, 1002 | 0.19416736, 1003 | 0.17404546, 1004 | 0.15530766, 1005 | 0.13794294, 1006 | 0.12192957, 1007 | 0.10723616, 1008 | 0.09382272, 1009 | 0.08164178, 1010 | 0.0706395, 1011 | 0.06075685, 1012 | 0.05193064, 1013 | 0.04409466, 1014 | 0.03718069, 1015 | 0.03111947, 1016 | 0.02584161, 1017 | 0.02127838, 1018 | 0.0173625, 1019 | 0.01402878, 1020 | 0.01121463, 1021 | 0.00886058, 1022 | 0.00691064, 1023 | 0.00531256, 1024 | 0.00401805, 1025 | 0.00298291, 1026 | 0.00216702, 1027 | 0.00153438, 1028 | 0.00105297, 1029 | 0.00069463, 1030 | 0.00043489, 1031 | 0.00025272, 1032 | 0.00013031, 1033 | 0.0000527734, 1034 | 0.00001, 1035 | 0.0, 1036 | ] 1037 | }; 1038 | 1039 | static _KAISER10: FuncDef = FuncDef::new(&KAISER10_TABLE, 32); 1040 | 1041 | static KAISER10_TABLE: [f64; 36] = { 1042 | [ 1043 | 0.99537781, 1.0, 0.99537781, 0.98162644, 0.95908712, 0.92831446, 1044 | 0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 1045 | 0.62226347, 0.56155915, 0.5011968, 0.44221549, 0.38553619, 0.33194107, 1046 | 0.28205962, 0.23636152, 0.19515633, 0.15859932, 0.1267028, 0.09935205, 1047 | 0.07632451, 0.05731132, 0.0419398, 0.02979584, 0.0204451, 0.01345224, 1048 | 0.00839739, 0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.0, 0.0, 1049 | ] 1050 | }; 1051 | 1052 | static _KAISER8: FuncDef = FuncDef::new(&KAISER8_TABLE, 32); 1053 | 1054 | static KAISER8_TABLE: [f64; 36] = { 1055 | [ 1056 | 0.99635258, 1.0, 0.99635258, 0.98548012, 0.96759014, 0.943022, 1057 | 0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 1058 | 0.68797126, 0.6345175, 0.58014482, 0.52566725, 0.47185369, 0.4194115, 1059 | 0.36897272, 0.32108304, 0.27619388, 0.23465776, 0.1967267, 0.1625538, 1060 | 0.13219758, 0.10562887, 0.08273982, 0.06335451, 0.04724088, 1061 | 0.03412321, 0.0236949, 0.01563093, 0.00959968, 0.00527363, 0.00233883, 1062 | 0.0005, 0.0, 1063 | ] 1064 | }; 1065 | 1066 | static _KAISER6: FuncDef = FuncDef::new(&KAISER6_TABLE, 32); 1067 | 1068 | static KAISER6_TABLE: [f64; 36] = { 1069 | [ 1070 | 0.99733006, 1.0, 0.99733006, 0.98935595, 0.97618418, 0.95799003, 1071 | 0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 1072 | 0.76076565, 0.71712752, 0.67172623, 0.62508937, 0.57774224, 1073 | 0.53019925, 0.48295561, 0.43647969, 0.39120616, 0.34752997, 1074 | 0.30580127, 0.26632152, 0.22934058, 0.19505503, 0.16360756, 1075 | 0.13508755, 0.10953262, 0.0869312, 0.067226, 0.0503182, 0.03607231, 1076 | 0.02432151, 0.01487334, 0.00752, 0.0, 1077 | ] 1078 | }; 1079 | 1080 | fn sinc(cutoff: f32, x: f32, n: i32, window_func: &FuncDef) -> f32 { 1081 | let xx = f64::from(x * cutoff); 1082 | let x_abs = f64::from(x).abs(); 1083 | let n_64 = f64::from(n); 1084 | let cutoff_64 = f64::from(cutoff); 1085 | if x_abs < 0.000001 { 1086 | cutoff 1087 | } else if x_abs > 0.5 * n_64 { 1088 | 0.0 1089 | } else { 1090 | let first_factor = cutoff_64 * (PI_64 * xx).sin() / (PI_64 * xx); 1091 | let second_factor = compute_func( 1092 | (2.0 * f64::from(x) / n_64).abs() as f32, 1093 | window_func, 1094 | ); 1095 | (first_factor * second_factor) as f32 1096 | } 1097 | } 1098 | 1099 | fn compute_func(x: f32, func: &FuncDef) -> f64 { 1100 | let mut interp: [f64; 4] = [0.0; 4]; 1101 | let y = x * func.oversample as f32; 1102 | let ind = y.floor() as usize; 1103 | let frac = f64::from(y - ind as f32); 1104 | interp[3] = -0.1666666667 * frac + 0.1666666667 * frac.powi(3); 1105 | interp[2] = frac + 0.5 * frac.powi(2) - 0.5 * frac.powi(3); 1106 | interp[0] = -0.3333333333 * frac + 0.5 * frac.powi(2) 1107 | - 0.1666666667 * frac.powi(3); 1108 | interp[1] = 1.0 - interp[3] - interp[2] - interp[0]; 1109 | 1110 | interp 1111 | .iter() 1112 | .zip(func.table.iter().skip(ind)) 1113 | .map(|(&x, &y)| x * y) 1114 | .sum() 1115 | } 1116 | 1117 | fn resampler_basic_direct_single( 1118 | st: &mut SpeexResamplerState, 1119 | channel_index: u32, 1120 | in_0: &[f32], 1121 | in_len: &mut u32, 1122 | out: &mut [f32], 1123 | out_len: &mut u32, 1124 | ) -> i32 { 1125 | let n = st.filt_len as usize; 1126 | let mut out_sample: u32 = 0; 1127 | let mut last_sample = st.last_sample[channel_index as usize]; 1128 | let mut samp_frac_num = st.samp_frac_num[channel_index as usize]; 1129 | let out_stride = st.out_stride; 1130 | let int_advance = st.int_advance; 1131 | let frac_advance = st.frac_advance; 1132 | let den_rate: u32 = st.den_rate; 1133 | while !(last_sample >= *in_len || out_sample >= *out_len) { 1134 | let sinct: &[f32] = 1135 | &st.sinc_table[(samp_frac_num * n as u32) as usize..]; 1136 | let iptr: &[f32] = &in_0[last_sample as usize..]; 1137 | 1138 | direct_step_single( 1139 | iptr, 1140 | out, 1141 | out_stride as usize, 1142 | out_sample as usize, 1143 | n, 1144 | sinct, 1145 | ); 1146 | 1147 | out_sample += 1; 1148 | last_sample += int_advance; 1149 | samp_frac_num += frac_advance; 1150 | if samp_frac_num >= den_rate { 1151 | samp_frac_num -= den_rate; 1152 | last_sample += 1 1153 | } 1154 | } 1155 | st.last_sample[channel_index as usize] = last_sample; 1156 | st.samp_frac_num[channel_index as usize] = samp_frac_num; 1157 | out_sample as i32 1158 | } 1159 | 1160 | fn resampler_basic_direct_double( 1161 | st: &mut SpeexResamplerState, 1162 | channel_index: u32, 1163 | in_0: &[f32], 1164 | in_len: &mut u32, 1165 | out: &mut [f32], 1166 | out_len: &mut u32, 1167 | ) -> i32 { 1168 | let n = st.filt_len as usize; 1169 | let mut out_sample: u32 = 0; 1170 | let mut last_sample = st.last_sample[channel_index as usize]; 1171 | let mut samp_frac_num = st.samp_frac_num[channel_index as usize]; 1172 | let out_stride = st.out_stride; 1173 | let int_advance = st.int_advance; 1174 | let frac_advance = st.frac_advance; 1175 | let den_rate: u32 = st.den_rate; 1176 | while !(last_sample >= *in_len || out_sample >= *out_len) { 1177 | let sinct: &[f32] = 1178 | &st.sinc_table[(samp_frac_num * n as u32) as usize..]; 1179 | let iptr: &[f32] = &in_0[last_sample as usize..]; 1180 | 1181 | direct_step_double( 1182 | iptr, 1183 | out, 1184 | out_stride as usize, 1185 | out_sample as usize, 1186 | n, 1187 | sinct, 1188 | ); 1189 | 1190 | out_sample += 1; 1191 | last_sample += int_advance; 1192 | samp_frac_num += frac_advance; 1193 | if samp_frac_num >= den_rate { 1194 | samp_frac_num -= den_rate; 1195 | last_sample += 1; 1196 | } 1197 | } 1198 | st.last_sample[channel_index as usize] = last_sample; 1199 | st.samp_frac_num[channel_index as usize] = samp_frac_num; 1200 | out_sample as i32 1201 | } 1202 | 1203 | fn _muldiv(result: &mut u32, value: u32, mul: u32, div: u32) -> usize { 1204 | let major: u32 = value / div; 1205 | let remainder: u32 = value % div; 1206 | if remainder > u32::MAX / mul 1207 | || major > u32::MAX / mul 1208 | || major * mul > u32::MAX - remainder * mul / div 1209 | { 1210 | RESAMPLER_ERR_OVERFLOW 1211 | } else { 1212 | *result = remainder * mul / div + major * mul; 1213 | RESAMPLER_ERR_SUCCESS 1214 | } 1215 | } 1216 | 1217 | fn _gcd(mut a: u32, mut b: u32) -> u32 { 1218 | while b != 0 { 1219 | let temp = a; 1220 | a = b; 1221 | b = temp % b; 1222 | } 1223 | a 1224 | } 1225 | 1226 | fn speex_resampler_process_native( 1227 | st: &mut SpeexResamplerState, 1228 | channel_index: u32, 1229 | in_len: &mut u32, 1230 | out: &mut [f32], 1231 | out_len: &mut u32, 1232 | ) -> usize { 1233 | let n: usize = st.filt_len as usize; 1234 | let mem_idx = (channel_index * st.mem_alloc_size) as usize; 1235 | st.started = 1; 1236 | let mem = &st.mem.clone(); 1237 | let out_sample: i32 = st.resampler_ptr.expect("non-null function pointer")( 1238 | st, 1239 | channel_index, 1240 | mem, 1241 | in_len, 1242 | out, 1243 | out_len, 1244 | ); 1245 | if st.last_sample[channel_index as usize] < *in_len { 1246 | *in_len = st.last_sample[channel_index as usize]; 1247 | } 1248 | *out_len = out_sample as u32; 1249 | st.last_sample[channel_index as usize] -= *in_len; 1250 | let ilen = *in_len as usize; 1251 | 1252 | st.mem[mem_idx..(mem_idx + n - 1)] 1253 | .copy_from_slice(&mem[(mem_idx + ilen)..(mem_idx + ilen + n - 1)]); 1254 | 1255 | RESAMPLER_ERR_SUCCESS 1256 | } 1257 | 1258 | fn speex_resampler_magic<'a, 'b>( 1259 | st: &mut SpeexResamplerState, 1260 | channel_index: u32, 1261 | out: &'a mut &'b mut [f32], 1262 | mut out_len: u32, 1263 | ) -> u32 { 1264 | let channel_idx = channel_index as usize; 1265 | let mut tmp_in_len = st.magic_samples[channel_idx]; 1266 | let mem_idx = (st.filt_len + channel_index * st.mem_alloc_size) as usize; 1267 | speex_resampler_process_native( 1268 | st, 1269 | channel_index, 1270 | &mut tmp_in_len, 1271 | out, 1272 | &mut out_len, 1273 | ); 1274 | st.magic_samples[channel_idx] -= tmp_in_len; 1275 | if st.magic_samples[channel_idx] != 0 { 1276 | let mem = &st.mem[mem_idx - 1 + tmp_in_len as usize..].to_vec(); 1277 | st.mem 1278 | .iter_mut() 1279 | .skip(mem_idx - 1) 1280 | .zip(mem.iter()) 1281 | .take(st.magic_samples[channel_idx] as usize) 1282 | .for_each(|(x, &y)| *x = y); 1283 | } 1284 | let value: &'b mut [f32] = mem::take(out); 1285 | *out = &mut value[(out_len * st.out_stride) as usize..]; 1286 | out_len 1287 | } 1288 | 1289 | #[cfg(feature = "avx")] 1290 | mod avx; 1291 | #[cfg(feature = "dynnative")] 1292 | mod dynnative; 1293 | mod native; 1294 | #[cfg(feature = "sse3")] 1295 | mod sse3; 1296 | -------------------------------------------------------------------------------- /resampler/src/speex/avx.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_arch = "x86")] 2 | use std::arch::x86::*; 3 | #[cfg(target_arch = "x86_64")] 4 | use std::arch::x86_64::*; 5 | 6 | #[inline(always)] 7 | unsafe fn hsum_m256d(v: __m256d) -> f64 { 8 | #[cfg(target_arch = "x86")] 9 | use std::arch::x86::*; 10 | #[cfg(target_arch = "x86_64")] 11 | use std::arch::x86_64::*; 12 | 13 | let v1 = _mm256_hadd_pd(v, v); 14 | let v2 = _mm256_add_pd(v1, _mm256_permute2f128_pd(v1, v1, 1)); 15 | _mm256_cvtsd_f64(v2) 16 | } 17 | 18 | use super::sse3::cubic_coef; 19 | 20 | #[allow(clippy::too_many_arguments)] 21 | #[inline(always)] 22 | pub fn interpolate_step_single( 23 | in_slice: &[f32], 24 | out_slice: &mut [f32], 25 | out_stride: usize, 26 | out_sample: usize, 27 | oversample: usize, 28 | offset: usize, 29 | n: usize, 30 | sinc_table: &[f32], 31 | frac: f32, 32 | ) { 33 | unsafe { 34 | let mut accum = _mm_setzero_ps(); 35 | in_slice.iter().zip(0..n).for_each(|(&curr_in, j)| { 36 | let idx = (2 + (j + 1) * oversample) - offset; 37 | let sinc_ptr: *const f32 = sinc_table[idx..].as_ptr(); 38 | accum = _mm_add_ps( 39 | accum, 40 | _mm_mul_ps(_mm_loadu_ps(sinc_ptr), _mm_set1_ps(curr_in)), 41 | ); 42 | }); 43 | let mut interp = _mm_setzero_ps(); 44 | cubic_coef(frac, &mut interp); 45 | let v = _mm_mul_ps(interp, accum); 46 | out_slice[out_stride * out_sample] = 47 | _mm_cvtss_f32(_mm_hadd_ps(_mm_hadd_ps(v, v), v)); 48 | } 49 | } 50 | 51 | #[allow(clippy::too_many_arguments)] 52 | #[inline(always)] 53 | pub fn interpolate_step_double( 54 | in_slice: &[f32], 55 | out_slice: &mut [f32], 56 | out_stride: usize, 57 | out_sample: usize, 58 | oversample: usize, 59 | offset: usize, 60 | n: usize, 61 | sinc_table: &[f32], 62 | frac: f32, 63 | ) { 64 | unsafe { 65 | let mut accum = _mm256_setzero_pd(); 66 | in_slice.iter().zip(0..n).for_each(|(&curr_in, j)| { 67 | let idx = (2 + (j + 1) * oversample) - offset; 68 | let sinct_ptr: *const f32 = sinc_table[idx..].as_ptr(); 69 | let v = _mm_mul_ps(_mm_loadu_ps(sinct_ptr), _mm_set1_ps(curr_in)); 70 | accum = _mm256_add_pd(accum, _mm256_cvtps_pd(v)); 71 | }); 72 | 73 | let mut interp = _mm_setzero_ps(); 74 | cubic_coef(frac, &mut interp); 75 | 76 | let accum = _mm256_mul_pd(accum, _mm256_cvtps_pd(interp)); 77 | 78 | out_slice[out_stride * out_sample] = hsum_m256d(accum) as f32; 79 | } 80 | } 81 | 82 | #[inline(always)] 83 | pub fn direct_step_single( 84 | in_slice: &[f32], 85 | out_slice: &mut [f32], 86 | out_stride: usize, 87 | out_sample: usize, 88 | n: usize, 89 | sinc_table: &[f32], 90 | ) { 91 | unsafe { 92 | let accum = sinc_table 93 | .chunks_exact(8) 94 | .zip(in_slice.chunks_exact(8)) 95 | .take(n / 8) 96 | .fold(_mm256_setzero_ps(), |acc, (sinct_p, iptr_p)| { 97 | let sinct_v = _mm256_loadu_ps(sinct_p.as_ptr()); 98 | let iptr_v = _mm256_loadu_ps(iptr_p.as_ptr()); 99 | 100 | _mm256_add_ps(acc, _mm256_mul_ps(sinct_v, iptr_v)) 101 | }); 102 | 103 | let accum = _mm256_hadd_ps(accum, accum); 104 | let accum = 105 | _mm256_add_ps(accum, _mm256_permute2f128_ps(accum, accum, 1)); 106 | let accum = _mm256_hadd_ps(accum, accum); 107 | 108 | _mm_store_ss( 109 | out_slice[(out_stride * out_sample)..].as_mut_ptr(), 110 | _mm256_castps256_ps128(accum), 111 | ) 112 | } 113 | } 114 | 115 | #[inline(always)] 116 | pub fn direct_step_double( 117 | in_slice: &[f32], 118 | out_slice: &mut [f32], 119 | out_stride: usize, 120 | out_sample: usize, 121 | n: usize, 122 | sinc_table: &[f32], 123 | ) { 124 | unsafe { 125 | let mut accum = _mm256_setzero_pd(); 126 | let mut j = 0; 127 | 128 | while j < n { 129 | let sinct_v = _mm_loadu_ps(sinc_table[j..].as_ptr()); 130 | let iptr_v = _mm_loadu_ps(in_slice[j..].as_ptr()); 131 | let v = _mm_mul_ps(sinct_v, iptr_v); 132 | 133 | accum = _mm256_add_pd(accum, _mm256_cvtps_pd(v)); 134 | j += 4; 135 | } 136 | 137 | out_slice[out_stride * out_sample] = hsum_m256d(accum) as f32; 138 | } 139 | } 140 | 141 | #[cfg(test)] 142 | mod tests { 143 | #[cfg(target_arch = "x86")] 144 | use std::arch::x86::*; 145 | #[cfg(target_arch = "x86_64")] 146 | use std::arch::x86_64::*; 147 | 148 | #[test] 149 | fn test_hsum_m256d() { 150 | let input: Vec = vec![1.0, 2.0, 3.0, 4.0]; 151 | 152 | let result = unsafe { 153 | let v = _mm256_loadu_pd(input.as_ptr()); 154 | super::hsum_m256d(v) 155 | }; 156 | 157 | assert_eq!(result as usize, 10_usize); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /resampler/src/speex/dynnative/avx_wrapper.rs: -------------------------------------------------------------------------------- 1 | #[allow(clippy::too_many_arguments)] 2 | #[target_feature(enable = "avx")] 3 | pub unsafe fn interpolate_step_single( 4 | in_slice: &[f32], 5 | out_slice: &mut [f32], 6 | out_stride: usize, 7 | out_sample: usize, 8 | oversample: usize, 9 | offset: usize, 10 | n: usize, 11 | sinc_table: &[f32], 12 | frac: f32, 13 | ) { 14 | crate::speex::avx::interpolate_step_single( 15 | in_slice, out_slice, out_stride, out_sample, oversample, offset, n, 16 | sinc_table, frac, 17 | ); 18 | } 19 | 20 | #[allow(clippy::too_many_arguments)] 21 | #[target_feature(enable = "avx")] 22 | pub unsafe fn interpolate_step_double( 23 | in_slice: &[f32], 24 | out_slice: &mut [f32], 25 | out_stride: usize, 26 | out_sample: usize, 27 | oversample: usize, 28 | offset: usize, 29 | n: usize, 30 | sinc_table: &[f32], 31 | frac: f32, 32 | ) { 33 | crate::speex::avx::interpolate_step_double( 34 | in_slice, out_slice, out_stride, out_sample, oversample, offset, n, 35 | sinc_table, frac, 36 | ); 37 | } 38 | 39 | #[target_feature(enable = "avx")] 40 | pub unsafe fn direct_step_single( 41 | in_slice: &[f32], 42 | out_slice: &mut [f32], 43 | out_stride: usize, 44 | out_sample: usize, 45 | n: usize, 46 | sinc_table: &[f32], 47 | ) { 48 | crate::speex::avx::direct_step_single( 49 | in_slice, out_slice, out_stride, out_sample, n, sinc_table, 50 | ); 51 | } 52 | 53 | #[target_feature(enable = "avx")] 54 | pub unsafe fn direct_step_double( 55 | in_slice: &[f32], 56 | out_slice: &mut [f32], 57 | out_stride: usize, 58 | out_sample: usize, 59 | n: usize, 60 | sinc_table: &[f32], 61 | ) { 62 | crate::speex::avx::direct_step_double( 63 | in_slice, out_slice, out_stride, out_sample, n, sinc_table, 64 | ); 65 | } 66 | -------------------------------------------------------------------------------- /resampler/src/speex/dynnative/mod.rs: -------------------------------------------------------------------------------- 1 | #[allow(clippy::too_many_arguments)] 2 | #[inline(always)] 3 | pub fn interpolate_step_single( 4 | in_slice: &[f32], 5 | out_slice: &mut [f32], 6 | out_stride: usize, 7 | out_sample: usize, 8 | oversample: usize, 9 | offset: usize, 10 | n: usize, 11 | sinc_table: &[f32], 12 | frac: f32, 13 | ) { 14 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 15 | { 16 | if is_x86_feature_detected!("avx") { 17 | unsafe { 18 | avx_wrapper::interpolate_step_single( 19 | in_slice, out_slice, out_stride, out_sample, oversample, 20 | offset, n, sinc_table, frac, 21 | ); 22 | } 23 | return; 24 | } 25 | 26 | if is_x86_feature_detected!("sse3") { 27 | unsafe { 28 | sse3_wrapper::interpolate_step_single( 29 | in_slice, out_slice, out_stride, out_sample, oversample, 30 | offset, n, sinc_table, frac, 31 | ); 32 | } 33 | return; 34 | } 35 | } 36 | 37 | super::native::interpolate_step_single( 38 | in_slice, out_slice, out_stride, out_sample, oversample, offset, n, 39 | sinc_table, frac, 40 | ); 41 | } 42 | 43 | #[allow(clippy::too_many_arguments)] 44 | #[inline(always)] 45 | pub fn interpolate_step_double( 46 | in_slice: &[f32], 47 | out_slice: &mut [f32], 48 | out_stride: usize, 49 | out_sample: usize, 50 | oversample: usize, 51 | offset: usize, 52 | n: usize, 53 | sinc_table: &[f32], 54 | frac: f32, 55 | ) { 56 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 57 | { 58 | if is_x86_feature_detected!("avx") { 59 | unsafe { 60 | avx_wrapper::interpolate_step_double( 61 | in_slice, out_slice, out_stride, out_sample, oversample, 62 | offset, n, sinc_table, frac, 63 | ); 64 | } 65 | return; 66 | } 67 | 68 | if is_x86_feature_detected!("sse3") { 69 | unsafe { 70 | sse3_wrapper::interpolate_step_double( 71 | in_slice, out_slice, out_stride, out_sample, oversample, 72 | offset, n, sinc_table, frac, 73 | ); 74 | } 75 | return; 76 | } 77 | } 78 | 79 | super::native::interpolate_step_double( 80 | in_slice, out_slice, out_stride, out_sample, oversample, offset, n, 81 | sinc_table, frac, 82 | ); 83 | } 84 | 85 | #[inline(always)] 86 | pub fn direct_step_single( 87 | in_slice: &[f32], 88 | out_slice: &mut [f32], 89 | out_stride: usize, 90 | out_sample: usize, 91 | n: usize, 92 | sinc_table: &[f32], 93 | ) { 94 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 95 | { 96 | if is_x86_feature_detected!("avx") { 97 | unsafe { 98 | avx_wrapper::direct_step_single( 99 | in_slice, out_slice, out_stride, out_sample, n, sinc_table, 100 | ); 101 | } 102 | return; 103 | } 104 | 105 | if is_x86_feature_detected!("sse3") { 106 | unsafe { 107 | sse3_wrapper::direct_step_single( 108 | in_slice, out_slice, out_stride, out_sample, n, sinc_table, 109 | ); 110 | } 111 | return; 112 | } 113 | } 114 | 115 | super::native::direct_step_single( 116 | in_slice, out_slice, out_stride, out_sample, n, sinc_table, 117 | ); 118 | } 119 | 120 | #[inline(always)] 121 | pub fn direct_step_double( 122 | in_slice: &[f32], 123 | out_slice: &mut [f32], 124 | out_stride: usize, 125 | out_sample: usize, 126 | n: usize, 127 | sinc_table: &[f32], 128 | ) { 129 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 130 | { 131 | if is_x86_feature_detected!("avx") { 132 | unsafe { 133 | avx_wrapper::direct_step_double( 134 | in_slice, out_slice, out_stride, out_sample, n, sinc_table, 135 | ); 136 | } 137 | return; 138 | } 139 | 140 | if is_x86_feature_detected!("sse3") { 141 | unsafe { 142 | sse3_wrapper::direct_step_double( 143 | in_slice, out_slice, out_stride, out_sample, n, sinc_table, 144 | ); 145 | } 146 | return; 147 | } 148 | } 149 | 150 | super::native::direct_step_double( 151 | in_slice, out_slice, out_stride, out_sample, n, sinc_table, 152 | ); 153 | } 154 | 155 | mod avx_wrapper; 156 | mod sse3_wrapper; 157 | -------------------------------------------------------------------------------- /resampler/src/speex/dynnative/sse3_wrapper.rs: -------------------------------------------------------------------------------- 1 | #[allow(clippy::too_many_arguments)] 2 | #[target_feature(enable = "sse3")] 3 | pub unsafe fn interpolate_step_single( 4 | in_slice: &[f32], 5 | out_slice: &mut [f32], 6 | out_stride: usize, 7 | out_sample: usize, 8 | oversample: usize, 9 | offset: usize, 10 | n: usize, 11 | sinc_table: &[f32], 12 | frac: f32, 13 | ) { 14 | crate::speex::sse3::interpolate_step_single( 15 | in_slice, out_slice, out_stride, out_sample, oversample, offset, n, 16 | sinc_table, frac, 17 | ); 18 | } 19 | 20 | #[allow(clippy::too_many_arguments)] 21 | #[target_feature(enable = "sse3")] 22 | pub unsafe fn interpolate_step_double( 23 | in_slice: &[f32], 24 | out_slice: &mut [f32], 25 | out_stride: usize, 26 | out_sample: usize, 27 | oversample: usize, 28 | offset: usize, 29 | n: usize, 30 | sinc_table: &[f32], 31 | frac: f32, 32 | ) { 33 | crate::speex::sse3::interpolate_step_double( 34 | in_slice, out_slice, out_stride, out_sample, oversample, offset, n, 35 | sinc_table, frac, 36 | ); 37 | } 38 | 39 | #[target_feature(enable = "sse3")] 40 | pub unsafe fn direct_step_single( 41 | in_slice: &[f32], 42 | out_slice: &mut [f32], 43 | out_stride: usize, 44 | out_sample: usize, 45 | n: usize, 46 | sinc_table: &[f32], 47 | ) { 48 | crate::speex::sse3::direct_step_single( 49 | in_slice, out_slice, out_stride, out_sample, n, sinc_table, 50 | ); 51 | } 52 | 53 | #[target_feature(enable = "sse3")] 54 | pub unsafe fn direct_step_double( 55 | in_slice: &[f32], 56 | out_slice: &mut [f32], 57 | out_stride: usize, 58 | out_sample: usize, 59 | n: usize, 60 | sinc_table: &[f32], 61 | ) { 62 | crate::speex::sse3::direct_step_double( 63 | in_slice, out_slice, out_stride, out_sample, n, sinc_table, 64 | ); 65 | } 66 | -------------------------------------------------------------------------------- /resampler/src/speex/native.rs: -------------------------------------------------------------------------------- 1 | #[inline(always)] 2 | pub fn cubic_coef(frac: f32, interp: &mut [f32]) { 3 | interp[0] = -0.166_67 * frac + 0.166_67 * frac * frac * frac; 4 | interp[1] = frac + 0.5 * frac * frac - 0.5f32 * frac * frac * frac; 5 | interp[3] = 6 | -0.333_33 * frac + 0.5 * frac * frac - 0.166_67 * frac * frac * frac; 7 | interp[2] = 8 | (1.0f64 - interp[0] as f64 - interp[1] as f64 - interp[3] as f64) 9 | as f32; 10 | } 11 | 12 | #[allow(clippy::too_many_arguments)] 13 | #[inline(always)] 14 | pub fn interpolate_step_single( 15 | in_slice: &[f32], 16 | out_slice: &mut [f32], 17 | out_stride: usize, 18 | out_sample: usize, 19 | oversample: usize, 20 | offset: usize, 21 | n: usize, 22 | sinc_table: &[f32], 23 | frac: f32, 24 | ) { 25 | let mut accum: [f32; 4] = [0.; 4]; 26 | in_slice.iter().zip(0..n).for_each(|(&curr_in, j)| { 27 | let idx = (2 + (j + 1) * oversample) - offset; 28 | accum.iter_mut().zip(sinc_table.iter().skip(idx)).for_each( 29 | |(v, &s)| { 30 | *v += curr_in * s; 31 | }, 32 | ); 33 | }); 34 | let mut interp: [f32; 4] = [0.; 4]; 35 | cubic_coef(frac, &mut interp); 36 | out_slice[out_stride * out_sample] = interp 37 | .iter() 38 | .zip(accum.iter()) 39 | .map(|(&x, &y)| x * y) 40 | .fold(0., |acc, x| acc + x); 41 | } 42 | 43 | #[allow(clippy::too_many_arguments)] 44 | #[inline(always)] 45 | pub fn interpolate_step_double( 46 | in_slice: &[f32], 47 | out_slice: &mut [f32], 48 | out_stride: usize, 49 | out_sample: usize, 50 | oversample: usize, 51 | offset: usize, 52 | n: usize, 53 | sinc_table: &[f32], 54 | frac: f32, 55 | ) { 56 | let mut accum: [f64; 4] = [0.0; 4]; 57 | in_slice.iter().zip(0..n).for_each(|(&curr_in, j)| { 58 | let idx = (2 + (j + 1) * oversample) - offset; 59 | accum.iter_mut().zip(sinc_table.iter().skip(idx)).for_each( 60 | |(v, &s)| { 61 | *v += (curr_in * s) as f64; 62 | }, 63 | ); 64 | }); 65 | let mut interp: [f32; 4] = [0.; 4]; 66 | cubic_coef(frac, &mut interp); 67 | out_slice[out_stride * out_sample] = interp 68 | .iter() 69 | .zip(accum.iter()) 70 | .map(|(&x, &y)| x * y as f32) 71 | .fold(0., |acc, x| acc + x); 72 | } 73 | 74 | #[inline(always)] 75 | pub fn direct_step_single( 76 | in_slice: &[f32], 77 | out_slice: &mut [f32], 78 | out_stride: usize, 79 | out_sample: usize, 80 | n: usize, 81 | sinc_table: &[f32], 82 | ) { 83 | let mut sum: f32 = 0.0; 84 | let mut j = 0; 85 | while j < n { 86 | sum += sinc_table[j] * in_slice[j]; 87 | j += 1 88 | } 89 | out_slice[out_stride * out_sample] = sum; 90 | } 91 | 92 | #[inline(always)] 93 | pub fn direct_step_double( 94 | in_slice: &[f32], 95 | out_slice: &mut [f32], 96 | out_stride: usize, 97 | out_sample: usize, 98 | n: usize, 99 | sinc_table: &[f32], 100 | ) { 101 | let mut accum: [f64; 4] = [0.0; 4]; 102 | let mut j = 0; 103 | 104 | while j < n { 105 | accum[0usize] += f64::from(sinc_table[j] * in_slice[j]); 106 | accum[1usize] += f64::from(sinc_table[j + 1] * in_slice[j + 1]); 107 | accum[2usize] += f64::from(sinc_table[j + 2] * in_slice[j + 2]); 108 | accum[3usize] += f64::from(sinc_table[j + 3] * in_slice[j + 3]); 109 | j += 4 110 | } 111 | let sum: f64 = 112 | accum[0usize] + accum[1usize] + accum[2usize] + accum[3usize]; 113 | out_slice[out_stride * out_sample] = sum as f32; 114 | } 115 | -------------------------------------------------------------------------------- /resampler/src/speex/sse3.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_arch = "x86")] 2 | use std::arch::x86::*; 3 | #[cfg(target_arch = "x86_64")] 4 | use std::arch::x86_64::*; 5 | 6 | #[inline(always)] 7 | unsafe fn swap_lo_and_hi(v: __m128) -> __m128 { 8 | _mm_shuffle_ps(v, v, 0b01001110) 9 | } 10 | 11 | #[inline(always)] 12 | unsafe fn sum_m128_into_m128d(v: __m128) -> __m128d { 13 | let a = _mm_cvtps_pd(v); 14 | let b = _mm_cvtps_pd(swap_lo_and_hi(v)); 15 | _mm_add_pd(a, b) 16 | } 17 | 18 | #[inline(always)] 19 | unsafe fn split_m128_into_m128d(v: __m128) -> (__m128d, __m128d) { 20 | let a = _mm_cvtps_pd(v); 21 | let b = _mm_cvtps_pd(swap_lo_and_hi(v)); 22 | (a, b) 23 | } 24 | 25 | unsafe fn hsum_m128d(v: __m128d) -> f64 { 26 | #[cfg(target_arch = "x86")] 27 | use std::arch::x86::*; 28 | #[cfg(target_arch = "x86_64")] 29 | use std::arch::x86_64::*; 30 | 31 | _mm_cvtsd_f64(_mm_hadd_pd(v, v)) 32 | } 33 | 34 | #[inline(always)] 35 | pub unsafe fn cubic_coef(frac: f32, interp: &mut std::arch::x86_64::__m128) { 36 | let mut interp_v = [0.0, 0.0, 0.0, 0.0]; 37 | 38 | interp_v[0] = -0.166_67 * frac + 0.166_67 * frac * frac * frac; 39 | interp_v[1] = frac + 0.5 * frac * frac - 0.5f32 * frac * frac * frac; 40 | interp_v[3] = 41 | -0.333_33 * frac + 0.5 * frac * frac - 0.166_67 * frac * frac * frac; 42 | interp_v[2] = (1.0f64 43 | - interp_v[0] as f64 44 | - interp_v[1] as f64 45 | - interp_v[3] as f64) as f32; 46 | 47 | *interp = _mm_loadu_ps(interp_v.as_ptr()); 48 | } 49 | 50 | #[allow(clippy::too_many_arguments)] 51 | #[inline(always)] 52 | pub fn interpolate_step_single( 53 | in_slice: &[f32], 54 | out_slice: &mut [f32], 55 | out_stride: usize, 56 | out_sample: usize, 57 | oversample: usize, 58 | offset: usize, 59 | n: usize, 60 | sinc_table: &[f32], 61 | frac: f32, 62 | ) { 63 | unsafe { 64 | let mut accum = _mm_setzero_ps(); 65 | in_slice.iter().zip(0..n).for_each(|(&curr_in, j)| { 66 | let idx = (2 + (j + 1) * oversample) - offset; 67 | let sinc_ptr: *const f32 = sinc_table[idx..].as_ptr(); 68 | accum = _mm_add_ps( 69 | accum, 70 | _mm_mul_ps(_mm_loadu_ps(sinc_ptr), _mm_set1_ps(curr_in)), 71 | ); 72 | }); 73 | let mut interp = _mm_setzero_ps(); 74 | cubic_coef(frac, &mut interp); 75 | let v = _mm_mul_ps(interp, accum); 76 | out_slice[out_stride * out_sample] = 77 | _mm_cvtss_f32(_mm_hadd_ps(_mm_hadd_ps(v, v), v)); 78 | } 79 | } 80 | 81 | #[allow(clippy::too_many_arguments)] 82 | #[inline(always)] 83 | pub fn interpolate_step_double( 84 | in_slice: &[f32], 85 | out_slice: &mut [f32], 86 | out_stride: usize, 87 | out_sample: usize, 88 | oversample: usize, 89 | offset: usize, 90 | n: usize, 91 | sinc_table: &[f32], 92 | frac: f32, 93 | ) { 94 | unsafe { 95 | let mut accum_lo = _mm_setzero_pd(); 96 | let mut accum_hi = _mm_setzero_pd(); 97 | in_slice.iter().zip(0..n).for_each(|(&curr_in, j)| { 98 | let idx = (2 + (j + 1) * oversample) - offset; 99 | let sinct_ptr: *const f32 = sinc_table[idx..].as_ptr(); 100 | let v = _mm_mul_ps(_mm_loadu_ps(sinct_ptr), _mm_set1_ps(curr_in)); 101 | let (v64_lo, v64_hi) = split_m128_into_m128d(v); 102 | accum_lo = _mm_add_pd(accum_lo, v64_lo); 103 | accum_hi = _mm_add_pd(accum_hi, v64_hi); 104 | }); 105 | 106 | let mut interp = _mm_setzero_ps(); 107 | cubic_coef(frac, &mut interp); 108 | 109 | let accum32 = _mm_add_ps( 110 | _mm_cvtpd_ps(accum_lo), 111 | swap_lo_and_hi(_mm_cvtpd_ps(accum_hi)), 112 | ); 113 | let v = _mm_mul_ps(accum32, interp); 114 | 115 | out_slice[out_stride * out_sample] = 116 | _mm_cvtss_f32(_mm_hadd_ps(_mm_hadd_ps(v, v), v)); 117 | } 118 | } 119 | 120 | #[inline(always)] 121 | pub fn direct_step_single( 122 | in_slice: &[f32], 123 | out_slice: &mut [f32], 124 | out_stride: usize, 125 | out_sample: usize, 126 | n: usize, 127 | sinc_table: &[f32], 128 | ) { 129 | unsafe { 130 | let accum = sinc_table 131 | .chunks_exact(8) 132 | .zip(in_slice.chunks_exact(8)) 133 | .take(n / 8) 134 | .fold(_mm_setzero_ps(), |acc, (sinct_p, iptr_p)| { 135 | let sinct_v = _mm_loadu_ps(sinct_p.as_ptr()); 136 | let iptr_v = _mm_loadu_ps(iptr_p.as_ptr()); 137 | 138 | let acc = _mm_add_ps(acc, _mm_mul_ps(sinct_v, iptr_v)); 139 | 140 | let sinct_v = _mm_loadu_ps(sinct_p[4..].as_ptr()); 141 | let iptr_v = _mm_loadu_ps(iptr_p[4..].as_ptr()); 142 | _mm_add_ps(acc, _mm_mul_ps(sinct_v, iptr_v)) 143 | }); 144 | 145 | let accum = _mm_add_ps(accum, _mm_movehl_ps(accum, accum)); 146 | let accum = 147 | _mm_add_ss(accum, _mm_shuffle_ps(accum, accum, 0b01010101)); 148 | 149 | _mm_store_ss( 150 | out_slice[(out_stride * out_sample)..].as_mut_ptr(), 151 | accum, 152 | ) 153 | } 154 | } 155 | 156 | #[inline(always)] 157 | pub fn direct_step_double( 158 | in_slice: &[f32], 159 | out_slice: &mut [f32], 160 | out_stride: usize, 161 | out_sample: usize, 162 | n: usize, 163 | sinc_table: &[f32], 164 | ) { 165 | unsafe { 166 | let mut accum = _mm_setzero_pd(); 167 | let mut j = 0; 168 | 169 | while j < n { 170 | let sinct_v = _mm_loadu_ps(sinc_table[j..].as_ptr()); 171 | let iptr_v = _mm_loadu_ps(in_slice[j..].as_ptr()); 172 | let v = _mm_mul_ps(sinct_v, iptr_v); 173 | 174 | accum = _mm_add_pd(accum, _mm_cvtps_pd(v)); 175 | j += 2; 176 | } 177 | 178 | out_slice[out_stride * out_sample] = hsum_m128d(accum) as f32; 179 | } 180 | } 181 | 182 | #[cfg(test)] 183 | mod tests { 184 | #[cfg(target_arch = "x86")] 185 | use std::arch::x86::*; 186 | #[cfg(target_arch = "x86_64")] 187 | use std::arch::x86_64::*; 188 | 189 | #[test] 190 | fn test_swap_lo_and_hi() { 191 | let input: Vec = vec![1.0, 2.0, 3.0, 4.0]; 192 | let mut output: Vec = vec![0.0; 4]; 193 | 194 | unsafe { 195 | let v = _mm_loadu_ps(input.as_ptr()); 196 | let result = super::swap_lo_and_hi(v); 197 | _mm_storeu_ps(output.as_mut_ptr(), result) 198 | } 199 | 200 | assert_eq!(output, vec![3.0, 4.0, 1.0, 2.0]); 201 | } 202 | 203 | #[test] 204 | fn test_sum_m128_into_m128d() { 205 | let input: Vec = vec![1.0, 2.0, 3.0, 4.0]; 206 | let mut output: Vec = vec![0.0; 2]; 207 | 208 | unsafe { 209 | let v = _mm_loadu_ps(input.as_ptr()); 210 | let result = super::sum_m128_into_m128d(v); 211 | _mm_storeu_pd(output.as_mut_ptr(), result) 212 | } 213 | 214 | assert_eq!(output, vec![4.0, 6.0]); 215 | } 216 | 217 | #[test] 218 | fn test_hsum_m128d() { 219 | let input: Vec = vec![1.0, 2.0, 3.0, 4.0]; 220 | 221 | let result = unsafe { 222 | let v = _mm_loadu_pd(input.as_ptr()); 223 | super::hsum_m128d(v) 224 | }; 225 | 226 | assert_eq!(result as usize, 3_usize); 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 79 2 | -------------------------------------------------------------------------------- /speexdsp-sys/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /speexdsp-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "speexdsp-sys" 3 | version = "0.1.2" 4 | authors = ["Luca Barbato "] 5 | license = "MIT" 6 | description = "low level bindings for the speexdsp library" 7 | repository = "https://github.com/rust-av/speexdsp-rs" 8 | readme = "../README.md" 9 | homepage = "https://github.com/rust-av/speexdsp-rs" 10 | keywords = ["audio", "resampler"] 11 | categories = ["api-bindings"] 12 | 13 | [build-dependencies] 14 | autotools = "0.2" 15 | system-deps = "6.0" 16 | bindgen = "0.59.2" 17 | 18 | [dev-dependencies] 19 | byteorder = "1.3" 20 | structopt = "0.3" 21 | 22 | [package.metadata.system-deps] 23 | speexdsp = "1.2" 24 | -------------------------------------------------------------------------------- /speexdsp-sys/build.rs: -------------------------------------------------------------------------------- 1 | extern crate autotools; 2 | extern crate bindgen; 3 | extern crate system_deps; 4 | 5 | use std::env; 6 | use std::fs::File; 7 | use std::io::Write; 8 | use std::path::PathBuf; 9 | 10 | fn format_write(builder: bindgen::Builder) -> String { 11 | builder 12 | .generate() 13 | .unwrap() 14 | .to_string() 15 | .replace("/**", "/*") 16 | .replace("/*!", "/*") 17 | } 18 | 19 | fn main() { 20 | let libs = system_deps::Config::new() 21 | .add_build_internal("speexdsp", |lib, version| { 22 | // TODO: decide how to fetch the source 23 | let dst = autotools::build("speexdsp"); 24 | system_deps::Library::from_internal_pkg_config(dst, lib, version) 25 | }) 26 | .probe() 27 | .unwrap(); 28 | 29 | let headers = libs.get_by_name("speexdsp").unwrap().include_paths.clone(); 30 | 31 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); 32 | 33 | for e in ["echo", "jitter", "preprocess", "resampler"].iter() { 34 | let mut builder = bindgen::builder() 35 | .size_t_is_usize(true) 36 | .layout_tests(false) 37 | .header(format!("data/{}.h", e)); 38 | 39 | for header in headers.iter() { 40 | builder = 41 | builder.clang_arg("-I").clang_arg(header.to_str().unwrap()); 42 | } 43 | 44 | // Manually fix the comment so rustdoc won't try to pick them 45 | let s = format_write(builder); 46 | 47 | let lib = format!("{}.rs", e); 48 | 49 | let mut file = File::create(out_path.join(lib)).unwrap(); 50 | 51 | let _ = file.write(s.as_bytes()); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /speexdsp-sys/data/echo.h: -------------------------------------------------------------------------------- 1 | #include "speex/speex_echo.h" 2 | -------------------------------------------------------------------------------- /speexdsp-sys/data/jitter.h: -------------------------------------------------------------------------------- 1 | #include "speex/speex_jitter.h" 2 | -------------------------------------------------------------------------------- /speexdsp-sys/data/preprocess.h: -------------------------------------------------------------------------------- 1 | #include "speex/speex_preprocess.h" 2 | -------------------------------------------------------------------------------- /speexdsp-sys/data/resampler.h: -------------------------------------------------------------------------------- 1 | #include "speex/speex_resampler.h" 2 | -------------------------------------------------------------------------------- /speexdsp-sys/examples/testdenoise_sys.rs: -------------------------------------------------------------------------------- 1 | extern crate byteorder; 2 | extern crate speexdsp_sys; 3 | 4 | use byteorder::{BigEndian, ByteOrder}; 5 | use speexdsp_sys::preprocess::*; 6 | use std::ffi::c_void; 7 | use std::io::Read; 8 | 9 | macro_rules! preprocess_ctl { 10 | ($st:ident, $param:ident, $value:expr) => { 11 | let v = &mut ($value as f32) as *mut f32 as *mut c_void; 12 | unsafe { speex_preprocess_ctl($st, $param as i32, v) }; 13 | }; 14 | } 15 | 16 | const NN: usize = 160; 17 | 18 | fn main() { 19 | let mut input: [i16; NN] = [0; NN]; 20 | let mut buffer: [u8; NN * 2] = [0; NN * 2]; 21 | 22 | let st = unsafe { speex_preprocess_state_init(NN as i32, 8000) }; 23 | preprocess_ctl!(st, SPEEX_PREPROCESS_SET_DENOISE, 1); 24 | preprocess_ctl!(st, SPEEX_PREPROCESS_SET_AGC, 0); 25 | preprocess_ctl!(st, SPEEX_PREPROCESS_SET_AGC_LEVEL, 8000); 26 | preprocess_ctl!(st, SPEEX_PREPROCESS_SET_DEREVERB, 0); 27 | preprocess_ctl!(st, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, 0f32); 28 | preprocess_ctl!(st, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, 0f32); 29 | 30 | while let Ok(n) = std::io::stdin().read(&mut buffer) { 31 | if n == 0 { 32 | break; 33 | } 34 | BigEndian::read_i16_into(&buffer, &mut input); 35 | unsafe { speex_preprocess_run(st, input.as_mut_ptr()) }; 36 | println!("{:?}", &input[..]); 37 | } 38 | 39 | unsafe { speex_preprocess_state_destroy(st) }; 40 | } 41 | -------------------------------------------------------------------------------- /speexdsp-sys/examples/testecho_sys.rs: -------------------------------------------------------------------------------- 1 | extern crate byteorder; 2 | extern crate speexdsp_sys; 3 | extern crate structopt; 4 | 5 | use byteorder::{BigEndian, ByteOrder}; 6 | use speexdsp_sys::echo::*; 7 | use speexdsp_sys::preprocess::*; 8 | use std::ffi::c_void; 9 | use std::fs::File; 10 | use std::io::{Read, Write}; 11 | use std::path::PathBuf; 12 | use structopt::StructOpt; 13 | 14 | macro_rules! preprocess_ctl { 15 | ($st:ident, $param:ident, $value:expr, $type:tt) => { 16 | let v = $value as *mut $type as *mut c_void; 17 | unsafe { speex_preprocess_ctl($st, $param as i32, v) }; 18 | }; 19 | } 20 | 21 | #[derive(Debug, StructOpt)] 22 | struct CliArgs { 23 | #[structopt(parse(from_os_str))] 24 | echo_fd_path: PathBuf, 25 | #[structopt(parse(from_os_str))] 26 | ref_fd_path: PathBuf, 27 | #[structopt(parse(from_os_str))] 28 | e_fd_path: PathBuf, 29 | } 30 | 31 | fn main() -> std::io::Result<()> { 32 | const NN: usize = 160; 33 | const TAIL: usize = 1024; 34 | 35 | let sample_rate: i32 = 8000; 36 | 37 | let mut echo_buf: [i16; NN] = [0; NN]; 38 | let mut echo_read_buf: [u8; NN * 2] = [0; NN * 2]; 39 | let mut ref_buf: [i16; NN] = [0; NN]; 40 | let mut ref_read_buf: [u8; NN * 2] = [0; NN * 2]; 41 | let mut e_buf: [i16; NN] = [0; NN]; 42 | 43 | let opts = CliArgs::from_args(); 44 | 45 | let mut ref_fd = File::open(opts.ref_fd_path)?; 46 | let mut echo_fd = File::open(opts.echo_fd_path)?; 47 | let mut e_fd = File::create(opts.e_fd_path)?; 48 | 49 | let st = unsafe { speex_echo_state_init(NN as i32, TAIL as i32) }; 50 | let den = unsafe { speex_preprocess_state_init(NN as i32, sample_rate) }; 51 | unsafe { 52 | speex_echo_ctl( 53 | st, 54 | SPEEX_ECHO_SET_SAMPLING_RATE as i32, 55 | sample_rate as *mut c_void, 56 | ) 57 | }; 58 | preprocess_ctl!( 59 | den, 60 | SPEEX_PREPROCESS_SET_ECHO_STATE, 61 | st, 62 | SpeexPreprocessState 63 | ); 64 | 65 | loop { 66 | let n = echo_fd.read(&mut echo_read_buf)?; 67 | let nn = ref_fd.read(&mut ref_read_buf)?; 68 | if n == 0 && nn == 0 { 69 | break; 70 | } 71 | BigEndian::read_i16_into(&echo_read_buf, &mut echo_buf); 72 | BigEndian::read_i16_into(&ref_read_buf, &mut ref_buf); 73 | unsafe { 74 | speex_echo_cancellation( 75 | st, 76 | ref_buf.as_ptr(), 77 | echo_buf.as_ptr(), 78 | e_buf.as_mut_ptr(), 79 | ) 80 | }; 81 | unsafe { speex_preprocess_run(den, e_buf.as_mut_ptr()) }; 82 | BigEndian::write_i16_into(&e_buf, &mut ref_read_buf); 83 | e_fd.write_all(&ref_read_buf)?; 84 | } 85 | 86 | unsafe { speex_echo_state_destroy(st) }; 87 | unsafe { speex_preprocess_state_destroy(den) }; 88 | 89 | Ok(()) 90 | } 91 | -------------------------------------------------------------------------------- /speexdsp-sys/examples/testjitter_sys.rs: -------------------------------------------------------------------------------- 1 | extern crate byteorder; 2 | extern crate speexdsp_sys; 3 | 4 | use byteorder::{BigEndian, ByteOrder}; 5 | use speexdsp_sys::jitter::*; 6 | use std::ptr; 7 | 8 | macro_rules! null_struct { 9 | ($v:ident) => { 10 | let mut $v = JitterBufferPacket { 11 | data: std::ptr::null_mut::(), 12 | len: 0, 13 | timestamp: 0, 14 | span: 0, 15 | sequence: 0, 16 | user_data: 0, 17 | }; 18 | }; 19 | } 20 | 21 | fn synth_in(input: &mut JitterBufferPacket, idx: usize, span: usize) { 22 | let mut buf = [0; 4]; 23 | BigEndian::write_u32(&mut buf, idx as u32); 24 | input.data = buf.as_mut_ptr() as *mut i8; 25 | input.len = 32; 26 | input.timestamp = (idx * 10) as u32; 27 | input.span = (span * 10) as u32; 28 | input.sequence = idx as u16; 29 | input.user_data = 0; 30 | } 31 | 32 | fn jitter_fill(jb: *mut JitterBuffer) { 33 | let mut buffer: [i8; 65536] = [0; 65536]; 34 | 35 | null_struct!(input); 36 | null_struct!(output); 37 | 38 | output.data = buffer.as_mut_ptr(); 39 | 40 | unsafe { 41 | jitter_buffer_reset(jb); 42 | } 43 | 44 | for i in 0..100 { 45 | synth_in(&mut input, i, 1); 46 | unsafe { 47 | jitter_buffer_put(jb, &input); 48 | } 49 | 50 | output.len = 65536; 51 | let err = unsafe { 52 | jitter_buffer_get(jb, &mut output, 10, ptr::null_mut::()) 53 | }; 54 | if err != (JITTER_BUFFER_OK as i32) { 55 | eprintln!("Fill test failed iteration {}", i); 56 | } 57 | if output.timestamp != (i * 10) as u32 { 58 | println!("Fill test expected {} got {}", i * 10, output.timestamp); 59 | } 60 | unsafe { 61 | jitter_buffer_tick(jb); 62 | } 63 | } 64 | } 65 | 66 | fn main() { 67 | let mut buffer: [i8; 65536] = [0; 65536]; 68 | let jb = unsafe { jitter_buffer_init(10) }; 69 | 70 | null_struct!(input); 71 | null_struct!(output); 72 | 73 | output.data = buffer.as_mut_ptr(); 74 | 75 | jitter_fill(jb); 76 | 77 | for _ in 0..100 { 78 | output.len = 65536; 79 | unsafe { 80 | jitter_buffer_get(jb, &mut output, 10, ptr::null_mut::()); 81 | jitter_buffer_tick(jb); 82 | } 83 | } 84 | 85 | synth_in(&mut input, 100, 1); 86 | unsafe { 87 | jitter_buffer_put(jb, &input); 88 | } 89 | output.len = 65536; 90 | let err = unsafe { 91 | jitter_buffer_get(jb, &mut output, 10, ptr::null_mut::()) 92 | }; 93 | if err != (JITTER_BUFFER_OK as i32) { 94 | eprintln!("Failed frozen sender resynchronize"); 95 | } else { 96 | println!("Frozen sender: Jitter {}", output.timestamp - 100 * 10); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /speexdsp-sys/examples/testresample_sys.rs: -------------------------------------------------------------------------------- 1 | extern crate speexdsp_sys; 2 | 3 | use speexdsp_sys::resampler::*; 4 | use std::f32::consts::PI; 5 | use std::ptr; 6 | 7 | const PERIOD: f32 = 32f32; 8 | const INBLOCK: usize = 1024; 9 | const RATE: u32 = 48000; 10 | 11 | fn main() { 12 | let mut rate = 1000; 13 | let mut off = 0; 14 | let mut avail = INBLOCK as isize; 15 | 16 | let fin: Vec = (0..INBLOCK * 2) 17 | .map(|i| ((i as f32) / PERIOD * 2.0 * PI).sin() * 0.9) 18 | .collect(); 19 | let mut fout = vec![0f32; INBLOCK * 4]; 20 | 21 | let st = 22 | unsafe { speex_resampler_init(1, RATE, RATE, 4, ptr::null_mut()) }; 23 | unsafe { speex_resampler_set_rate(st, RATE, rate) }; 24 | unsafe { speex_resampler_skip_zeros(st) }; 25 | 26 | loop { 27 | let mut in_len = avail as u32; 28 | let mut out_len = (in_len * rate + RATE - 1) / RATE; 29 | 30 | let prev_in_len = in_len; 31 | let prev_out_len = out_len; 32 | 33 | unsafe { 34 | speex_resampler_process_float( 35 | st, 36 | 0, 37 | fin[off..].as_ptr(), 38 | &mut in_len, 39 | fout.as_mut_ptr(), 40 | &mut out_len, 41 | ) 42 | }; 43 | 44 | eprintln!( 45 | "{} {} {} {} -> {} {}", 46 | rate, off, prev_in_len, prev_out_len, in_len, out_len 47 | ); 48 | 49 | off += in_len as usize; 50 | avail += INBLOCK as isize - in_len as isize; 51 | 52 | if off >= INBLOCK { 53 | off -= INBLOCK; 54 | } 55 | 56 | println!("{:#?}", &fout[..out_len as usize]); 57 | 58 | rate += 100; 59 | if rate > 128000 { 60 | break; 61 | } 62 | 63 | unsafe { speex_resampler_set_rate(st, RATE, rate) }; 64 | } 65 | 66 | unsafe { speex_resampler_destroy(st) }; 67 | } 68 | -------------------------------------------------------------------------------- /speexdsp-sys/src/echo.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | #![allow(non_upper_case_globals)] 5 | 6 | include!(concat!(env!("OUT_DIR"), "/echo.rs")); 7 | -------------------------------------------------------------------------------- /speexdsp-sys/src/jitter.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | #![allow(non_upper_case_globals)] 5 | 6 | include!(concat!(env!("OUT_DIR"), "/jitter.rs")); 7 | -------------------------------------------------------------------------------- /speexdsp-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod echo; 2 | pub mod jitter; 3 | pub mod preprocess; 4 | pub mod resampler; 5 | -------------------------------------------------------------------------------- /speexdsp-sys/src/preprocess.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | #![allow(non_upper_case_globals)] 5 | 6 | include!(concat!(env!("OUT_DIR"), "/preprocess.rs")); 7 | -------------------------------------------------------------------------------- /speexdsp-sys/src/resampler.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | #![allow(non_upper_case_globals)] 5 | 6 | include!(concat!(env!("OUT_DIR"), "/resampler.rs")); 7 | -------------------------------------------------------------------------------- /src/echo.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | 3 | #[cfg(feature = "sys")] 4 | mod sys { 5 | use speexdsp_sys::echo::*; 6 | use std::ffi::c_void; 7 | use std::fmt; 8 | 9 | #[derive(Clone, Copy, Debug)] 10 | pub enum SpeexEchoConst { 11 | SPEEX_ECHO_GET_FRAME_SIZE = 3, 12 | SPEEX_ECHO_SET_SAMPLING_RATE = 24, 13 | SPEEX_ECHO_GET_SAMPLING_RATE = 25, 14 | SPEEX_ECHO_GET_IMPULSE_RESPONSE_SIZE = 27, 15 | SPEEX_ECHO_GET_IMPULSE_RESPONSE = 29, 16 | } 17 | 18 | #[derive(Clone, Copy, Debug)] 19 | pub enum Error { 20 | FailedInit, 21 | UnknownRequest, 22 | } 23 | 24 | impl fmt::Display for Error { 25 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 26 | let v = match self { 27 | Error::FailedInit => "Failed to initialize", 28 | Error::UnknownRequest => "The request is unknown", 29 | }; 30 | 31 | write!(f, "{}", v) 32 | } 33 | } 34 | 35 | #[derive(Clone)] 36 | pub struct SpeexEcho { 37 | st: *mut SpeexEchoState, 38 | } 39 | 40 | impl SpeexEcho { 41 | pub fn new( 42 | frame_size: usize, 43 | filter_length: usize, 44 | ) -> Result { 45 | let st = unsafe { 46 | speex_echo_state_init(frame_size as i32, filter_length as i32) 47 | }; 48 | 49 | if st.is_null() { 50 | Err(Error::FailedInit) 51 | } else { 52 | Ok(SpeexEcho { st }) 53 | } 54 | } 55 | 56 | pub fn echo_init( 57 | frame_size: usize, 58 | filter_length: usize, 59 | nb_mic: usize, 60 | nb_speakers: usize, 61 | ) -> Result { 62 | let st = unsafe { 63 | speex_echo_state_init_mc( 64 | frame_size as i32, 65 | filter_length as i32, 66 | nb_mic as i32, 67 | nb_speakers as i32, 68 | ) 69 | }; 70 | 71 | if st.is_null() { 72 | Err(Error::FailedInit) 73 | } else { 74 | Ok(SpeexEcho { st }) 75 | } 76 | } 77 | 78 | pub fn echo_cancellation( 79 | &mut self, 80 | rec: &[i16], 81 | play: &[i16], 82 | out: &mut [i16], 83 | ) { 84 | unsafe { 85 | speex_echo_cancellation( 86 | self.st, 87 | rec.as_ptr(), 88 | play.as_ptr(), 89 | out.as_mut_ptr(), 90 | ) 91 | }; 92 | } 93 | 94 | pub fn echo_cancel( 95 | &mut self, 96 | rec: &[i16], 97 | play: &[i16], 98 | out: &mut [i16], 99 | yout: &mut [i32], 100 | ) { 101 | unsafe { 102 | speex_echo_cancel( 103 | self.st, 104 | rec.as_ptr(), 105 | play.as_ptr(), 106 | out.as_mut_ptr(), 107 | yout.as_mut_ptr(), 108 | ) 109 | }; 110 | } 111 | 112 | pub fn echo_capture(&mut self, rec: &[i16], out: &mut [i16]) { 113 | unsafe { 114 | speex_echo_capture(self.st, rec.as_ptr(), out.as_mut_ptr()) 115 | }; 116 | } 117 | 118 | pub fn echo_playback(&mut self, play: &[i16]) { 119 | unsafe { speex_echo_playback(self.st, play.as_ptr()) }; 120 | } 121 | 122 | pub fn echo_reset(&mut self) { 123 | unsafe { speex_echo_state_reset(self.st) }; 124 | } 125 | 126 | pub fn echo_ctl( 127 | &mut self, 128 | request: SpeexEchoConst, 129 | ptr: usize, 130 | ) -> Result<(), Error> { 131 | let ret = unsafe { 132 | speex_echo_ctl(self.st, request as i32, ptr as *mut c_void) 133 | as usize 134 | }; 135 | if ret != 0 { 136 | Err(Error::UnknownRequest) 137 | } else { 138 | Ok(()) 139 | } 140 | } 141 | 142 | pub(crate) fn get_ptr(&self) -> *mut SpeexEchoState { 143 | self.st 144 | } 145 | } 146 | 147 | impl Drop for SpeexEcho { 148 | fn drop(&mut self) { 149 | unsafe { speex_echo_state_destroy(self.st) }; 150 | } 151 | } 152 | 153 | pub struct SpeexDecorr { 154 | st: *mut SpeexDecorrState, 155 | } 156 | 157 | impl SpeexDecorr { 158 | pub fn new( 159 | rate: usize, 160 | channels: usize, 161 | frame_size: usize, 162 | ) -> Result { 163 | let st = unsafe { 164 | speex_decorrelate_new( 165 | rate as i32, 166 | channels as i32, 167 | frame_size as i32, 168 | ) 169 | }; 170 | 171 | if st.is_null() { 172 | Err(Error::FailedInit) 173 | } else { 174 | Ok(SpeexDecorr { st }) 175 | } 176 | } 177 | 178 | pub fn decorrelate( 179 | &mut self, 180 | input: &[i16], 181 | out: &mut [i16], 182 | strength: usize, 183 | ) { 184 | unsafe { 185 | speex_decorrelate( 186 | self.st, 187 | input.as_ptr(), 188 | out.as_mut_ptr(), 189 | strength as i32, 190 | ) 191 | }; 192 | } 193 | } 194 | 195 | impl Drop for SpeexDecorr { 196 | fn drop(&mut self) { 197 | unsafe { speex_decorrelate_destroy(self.st) }; 198 | } 199 | } 200 | } 201 | 202 | #[cfg(feature = "sys")] 203 | pub use self::sys::{Error, SpeexDecorr, SpeexEcho, SpeexEchoConst}; 204 | -------------------------------------------------------------------------------- /src/jitter.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | 3 | #[cfg(feature = "sys")] 4 | mod sys { 5 | use speexdsp_sys::jitter::*; 6 | use std::ffi::c_void; 7 | use std::fmt; 8 | 9 | const BUFFER_OK: i32 = JITTER_BUFFER_OK as i32; 10 | const BUFFER_MISSING: i32 = JITTER_BUFFER_MISSING as i32; 11 | const BUFFER_INSERTION: i32 = JITTER_BUFFER_INSERTION as i32; 12 | 13 | #[derive(Clone, Copy, Debug)] 14 | pub enum SpeexJitterConst { 15 | JITTER_BUFFER_SET_MARGIN = 0, 16 | JITTER_BUFFER_GET_MARGIN = 1, 17 | JITTER_BUFFER_GET_AVAILABLE_COUNT = 3, 18 | JITTER_BUFFER_SET_DESTROY_CALLBACK = 4, 19 | JITTER_BUFFER_GET_DESTROY_CALLBACK = 5, 20 | JITTER_BUFFER_SET_DELAY_STEP = 6, 21 | JITTER_BUFFER_GET_DELAY_STEP = 7, 22 | JITTER_BUFFER_SET_CONCEALMENT_SIZE = 8, 23 | JITTER_BUFFER_GET_CONCEALMENT_SIZE = 9, 24 | JITTER_BUFFER_SET_MAX_LATE_RATE = 10, 25 | JITTER_BUFFER_GET_MAX_LATE_RATE = 11, 26 | JITTER_BUFFER_SET_LATE_COST = 12, 27 | JITTER_BUFFER_GET_LATE_COST = 13, 28 | } 29 | 30 | #[derive(Clone, Copy, Debug, PartialEq)] 31 | pub enum Error { 32 | BufferOk = 0, 33 | BufferMissing = 1, 34 | BufferInsertion = 2, 35 | BufferInternalError = -1, 36 | BufferBadArgument = -2, 37 | } 38 | 39 | impl fmt::Display for Error { 40 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 41 | let v = match self { 42 | Error::BufferOk => "The buffer is ok", 43 | Error::BufferMissing => "The buffer is missing", 44 | Error::BufferInsertion => "Bad insertion into the buffer", 45 | Error::BufferInternalError => "Internal Error", 46 | Error::BufferBadArgument => "Bad Argument...", 47 | }; 48 | 49 | write!(f, "{}", v) 50 | } 51 | } 52 | 53 | impl Default for SpeexBufferPacket { 54 | fn default() -> Self { 55 | Self::new() 56 | } 57 | } 58 | 59 | impl Error { 60 | fn from_i32(v: i32) -> Self { 61 | match v { 62 | BUFFER_OK => Error::BufferOk, 63 | BUFFER_MISSING => Error::BufferMissing, 64 | BUFFER_INSERTION => Error::BufferInsertion, 65 | JITTER_BUFFER_INTERNAL_ERROR => Error::BufferInternalError, 66 | JITTER_BUFFER_BAD_ARGUMENT => Error::BufferBadArgument, 67 | _ => unreachable!(), 68 | } 69 | } 70 | } 71 | 72 | pub struct SpeexBufferPacket { 73 | pt: JitterBufferPacket, 74 | } 75 | 76 | impl SpeexBufferPacket { 77 | pub fn new() -> Self { 78 | let pt = JitterBufferPacket { 79 | data: std::ptr::null_mut(), 80 | len: 0, 81 | timestamp: 0, 82 | span: 0, 83 | sequence: 0, 84 | user_data: 0, 85 | }; 86 | 87 | SpeexBufferPacket { pt } 88 | } 89 | 90 | pub fn create( 91 | &mut self, 92 | data: &mut [i8], 93 | len: usize, 94 | timestamp: usize, 95 | span: usize, 96 | sequence: usize, 97 | user_data: usize, 98 | ) { 99 | let pt = JitterBufferPacket { 100 | data: data.as_mut_ptr(), 101 | len: len as u32, 102 | timestamp: timestamp as u32, 103 | span: span as u32, 104 | sequence: sequence as u16, 105 | user_data: user_data as u32, 106 | }; 107 | self.pt = pt; 108 | } 109 | 110 | pub fn len(&self) -> usize { 111 | self.pt.len as usize 112 | } 113 | 114 | pub fn is_empty(&self) -> bool { 115 | self.pt.len != 0 116 | } 117 | 118 | pub fn timestamp(&self) -> usize { 119 | self.pt.timestamp as usize 120 | } 121 | 122 | pub fn span(&self) -> usize { 123 | self.pt.span as usize 124 | } 125 | 126 | pub fn sequence(&self) -> usize { 127 | self.pt.sequence as usize 128 | } 129 | 130 | pub fn user_data(&self) -> usize { 131 | self.pt.user_data as usize 132 | } 133 | 134 | pub fn set_data(&mut self, data: &mut [i8]) { 135 | self.pt.data = data.as_mut_ptr(); 136 | } 137 | 138 | pub fn set_len(&mut self, len: usize) { 139 | self.pt.len = len as u32; 140 | } 141 | } 142 | 143 | pub struct SpeexJitter { 144 | st: *mut JitterBuffer, 145 | } 146 | 147 | impl SpeexJitter { 148 | pub fn new(step_size: usize) -> Result { 149 | let st = unsafe { jitter_buffer_init(step_size as i32) }; 150 | 151 | if st.is_null() { 152 | Err(Error::BufferInternalError) 153 | } else { 154 | Ok(SpeexJitter { st }) 155 | } 156 | } 157 | 158 | pub fn buffer_reset(&mut self) { 159 | unsafe { jitter_buffer_reset(self.st) }; 160 | } 161 | 162 | pub fn buffer_put(&mut self, packet: &SpeexBufferPacket) { 163 | unsafe { jitter_buffer_put(self.st, &packet.pt) }; 164 | } 165 | 166 | pub fn buffer_get( 167 | &mut self, 168 | packet: &mut SpeexBufferPacket, 169 | desired_span: usize, 170 | offset: usize, 171 | ) -> Error { 172 | let err_i32 = unsafe { 173 | jitter_buffer_get( 174 | self.st, 175 | &mut packet.pt, 176 | desired_span as i32, 177 | offset as *mut i32, 178 | ) 179 | }; 180 | Error::from_i32(err_i32) 181 | } 182 | 183 | pub fn buffer_get_another( 184 | &mut self, 185 | packet: &mut SpeexBufferPacket, 186 | ) -> Error { 187 | let err_i32 = 188 | unsafe { jitter_buffer_get_another(self.st, &mut packet.pt) }; 189 | Error::from_i32(err_i32) 190 | } 191 | 192 | pub fn buffer_get_pointer_timestap(&mut self) -> usize { 193 | unsafe { jitter_buffer_get_pointer_timestamp(self.st) as usize } 194 | } 195 | 196 | pub fn buffer_tick(&mut self) { 197 | unsafe { jitter_buffer_tick(self.st) }; 198 | } 199 | 200 | pub fn buffer_remaining_span(&mut self, rem: usize) { 201 | unsafe { jitter_buffer_remaining_span(self.st, rem as u32) }; 202 | } 203 | 204 | pub fn buffer_ctl( 205 | &mut self, 206 | request: SpeexJitterConst, 207 | ptr: usize, 208 | ) -> Result<(), Error> { 209 | let ret = unsafe { 210 | jitter_buffer_ctl(self.st, request as i32, ptr as *mut c_void) 211 | as usize 212 | }; 213 | if ret != 0 { 214 | Err(Error::BufferBadArgument) 215 | } else { 216 | Ok(()) 217 | } 218 | } 219 | 220 | pub fn buffer_update_delay( 221 | &mut self, 222 | packet: &mut SpeexBufferPacket, 223 | start_offset: usize, 224 | ) -> Error { 225 | let err_i32 = unsafe { 226 | jitter_buffer_update_delay( 227 | self.st, 228 | &mut packet.pt, 229 | start_offset as *mut i32, 230 | ) 231 | }; 232 | Error::from_i32(err_i32) 233 | } 234 | } 235 | 236 | impl Drop for SpeexJitter { 237 | fn drop(&mut self) { 238 | unsafe { jitter_buffer_destroy(self.st) }; 239 | } 240 | } 241 | } 242 | #[cfg(feature = "sys")] 243 | pub use self::sys::{Error, SpeexBufferPacket, SpeexJitter, SpeexJitterConst}; 244 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod echo; 2 | pub mod jitter; 3 | pub mod preprocess; 4 | pub mod resampler; 5 | -------------------------------------------------------------------------------- /src/preprocess.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | 3 | #[cfg(feature = "sys")] 4 | mod sys { 5 | use crate::echo::SpeexEcho; 6 | use speexdsp_sys::preprocess::*; 7 | use std::convert::From; 8 | use std::ffi::c_void; 9 | use std::fmt; 10 | 11 | #[derive(Clone, Copy, Debug)] 12 | pub enum SpeexPreprocessConst { 13 | SPEEX_PREPROCESS_SET_DENOISE = 0, 14 | SPEEX_PREPROCESS_GET_DENOISE = 1, 15 | SPEEX_PREPROCESS_SET_AGC = 2, 16 | SPEEX_PREPROCESS_GET_AGC = 3, 17 | SPEEX_PREPROCESS_SET_VAD = 4, 18 | SPEEX_PREPROCESS_GET_VAD = 5, 19 | SPEEX_PREPROCESS_SET_AGC_LEVEL = 6, 20 | SPEEX_PREPROCESS_GET_AGC_LEVEL = 7, 21 | SPEEX_PREPROCESS_SET_DEREVERB = 8, 22 | SPEEX_PREPROCESS_GET_DEREVERB = 9, 23 | SPEEX_PREPROCESS_SET_DEREVERB_LEVEL = 10, 24 | SPEEX_PREPROCESS_GET_DEREVERB_LEVEL = 11, 25 | SPEEX_PREPROCESS_SET_DEREVERB_DECAY = 12, 26 | SPEEX_PREPROCESS_GET_DEREVERB_DECAY = 13, 27 | SPEEX_PREPROCESS_SET_PROB_START = 14, 28 | SPEEX_PREPROCESS_GET_PROB_START = 15, 29 | SPEEX_PREPROCESS_SET_PROB_CONTINUE = 16, 30 | SPEEX_PREPROCESS_GET_PROB_CONTINUE = 17, 31 | SPEEX_PREPROCESS_SET_NOISE_SUPPRESS = 18, 32 | SPEEX_PREPROCESS_GET_NOISE_SUPPRESS = 19, 33 | SPEEX_PREPROCESS_SET_ECHO_SUPPRESS = 20, 34 | SPEEX_PREPROCESS_GET_ECHO_SUPPRESS = 21, 35 | SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE = 22, 36 | SPEEX_PREPROCESS_GET_ECHO_SUPPRESS_ACTIVE = 23, 37 | SPEEX_PREPROCESS_SET_ECHO_STATE = 24, 38 | SPEEX_PREPROCESS_GET_ECHO_STATE = 25, 39 | SPEEX_PREPROCESS_SET_AGC_INCREMENT = 26, 40 | SPEEX_PREPROCESS_GET_AGC_INCREMENT = 27, 41 | SPEEX_PREPROCESS_SET_AGC_DECREMENT = 28, 42 | SPEEX_PREPROCESS_GET_AGC_DECREMENT = 29, 43 | SPEEX_PREPROCESS_SET_AGC_MAX_GAIN = 30, 44 | SPEEX_PREPROCESS_GET_AGC_MAX_GAIN = 31, 45 | SPEEX_PREPROCESS_GET_AGC_LOUDNESS = 33, 46 | SPEEX_PREPROCESS_GET_AGC_GAIN = 35, 47 | SPEEX_PREPROCESS_GET_PSD_SIZE = 37, 48 | SPEEX_PREPROCESS_GET_PSD = 39, 49 | SPEEX_PREPROCESS_GET_NOISE_PSD_SIZE = 41, 50 | SPEEX_PREPROCESS_GET_NOISE_PSD = 43, 51 | SPEEX_PREPROCESS_GET_PROB = 45, 52 | SPEEX_PREPROCESS_SET_AGC_TARGET = 46, 53 | SPEEX_PREPROCESS_GET_AGC_TARGET = 47, 54 | } 55 | 56 | #[derive(Clone, Copy, Debug)] 57 | pub enum Error { 58 | FailedInit, 59 | UnknownRequest, 60 | } 61 | 62 | impl fmt::Display for Error { 63 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 64 | let v = match self { 65 | Error::FailedInit => "Failed to initialize", 66 | Error::UnknownRequest => "The request is unknown", 67 | }; 68 | 69 | write!(f, "{}", v) 70 | } 71 | } 72 | 73 | pub enum Variant { 74 | U32(u32), 75 | F32(f32), 76 | Echo(SpeexEcho), 77 | } 78 | 79 | impl From for Variant { 80 | fn from(item: u32) -> Self { 81 | Variant::U32(item) 82 | } 83 | } 84 | 85 | impl From for Variant { 86 | fn from(item: f32) -> Self { 87 | Variant::F32(item) 88 | } 89 | } 90 | 91 | impl From<&SpeexEcho> for Variant { 92 | fn from(item: &SpeexEcho) -> Self { 93 | Variant::Echo(item.clone()) 94 | } 95 | } 96 | 97 | pub struct SpeexPreprocess { 98 | st: *mut SpeexPreprocessState, 99 | } 100 | 101 | impl SpeexPreprocess { 102 | pub fn new( 103 | frame_size: usize, 104 | sampling_rate: usize, 105 | ) -> Result { 106 | let st = unsafe { 107 | speex_preprocess_state_init( 108 | frame_size as i32, 109 | sampling_rate as i32, 110 | ) 111 | }; 112 | 113 | if st.is_null() { 114 | Err(Error::FailedInit) 115 | } else { 116 | Ok(SpeexPreprocess { st }) 117 | } 118 | } 119 | 120 | pub fn preprocess_run(&mut self, x: &mut [i16]) -> usize { 121 | unsafe { speex_preprocess_run(self.st, x.as_mut_ptr()) as usize } 122 | } 123 | 124 | pub fn preprocess(&mut self, x: &mut [i16], echo: usize) -> usize { 125 | unsafe { 126 | speex_preprocess(self.st, x.as_mut_ptr(), echo as *mut i32) 127 | as usize 128 | } 129 | } 130 | 131 | pub fn preprocess_estimate_update(&mut self, x: &mut [i16]) { 132 | unsafe { 133 | speex_preprocess_estimate_update(self.st, x.as_mut_ptr()) 134 | }; 135 | } 136 | 137 | pub fn preprocess_ctl>( 138 | &mut self, 139 | request: SpeexPreprocessConst, 140 | value: T, 141 | ) -> Result<(), Error> { 142 | let ptr_v = match (request, value.into()) { 143 | ( 144 | SpeexPreprocessConst::SPEEX_PREPROCESS_SET_DEREVERB_DECAY, 145 | Variant::F32(val), 146 | ) 147 | | ( 148 | SpeexPreprocessConst::SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, 149 | Variant::F32(val), 150 | ) => &val as *const f32 as *mut c_void, 151 | ( 152 | SpeexPreprocessConst::SPEEX_PREPROCESS_SET_ECHO_STATE, 153 | Variant::Echo(val), 154 | ) => val.get_ptr() as *mut c_void, 155 | (_, Variant::U32(val)) => &val as *const u32 as *mut c_void, 156 | _ => panic!("This type is not accepted"), 157 | }; 158 | let ret = unsafe { 159 | speex_preprocess_ctl(self.st, request as i32, ptr_v) as usize 160 | }; 161 | if ret != 0 { 162 | Err(Error::UnknownRequest) 163 | } else { 164 | Ok(()) 165 | } 166 | } 167 | } 168 | 169 | impl Drop for SpeexPreprocess { 170 | fn drop(&mut self) { 171 | unsafe { speex_preprocess_state_destroy(self.st) }; 172 | } 173 | } 174 | } 175 | 176 | #[cfg(feature = "sys")] 177 | pub use self::sys::{Error, SpeexPreprocess, SpeexPreprocessConst}; 178 | -------------------------------------------------------------------------------- /src/resampler.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | #![allow(unreachable_patterns)] 3 | 4 | #[derive(Clone, Copy, Debug)] 5 | pub enum Error { 6 | AllocFailed = 1, 7 | BadState = 2, 8 | InvalidArg = 3, 9 | PtrOverlap = 4, 10 | } 11 | 12 | use std::fmt; 13 | 14 | impl fmt::Display for Error { 15 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 16 | let v = match self { 17 | Error::AllocFailed => "Memory allocation failed.", 18 | Error::BadState => "Bad resampler state.", 19 | Error::InvalidArg => "Invalid argument.", 20 | Error::PtrOverlap => "Input and output buffers overlap.", 21 | }; 22 | 23 | write!(f, "{}", v) 24 | } 25 | } 26 | 27 | pub trait Resampler: Sized { 28 | fn new( 29 | channels: usize, 30 | in_rate: usize, 31 | out_rate: usize, 32 | quality: usize, 33 | ) -> Result; 34 | fn set_rate( 35 | &mut self, 36 | in_rate: usize, 37 | out_rate: usize, 38 | ) -> Result<(), Error>; 39 | fn get_rate(&self) -> (usize, usize); 40 | fn get_ratio(&self) -> (usize, usize); 41 | fn process_float( 42 | &mut self, 43 | index: usize, 44 | input: &[f32], 45 | output: &mut [f32], 46 | ) -> Result<(usize, usize), Error>; 47 | fn skip_zeros(&mut self); 48 | fn reset(&mut self); 49 | fn get_input_latency(&self) -> usize; 50 | fn get_output_latency(&self) -> usize; 51 | fn set_quality(&mut self, quality: usize) -> Result<(), Error>; 52 | fn get_quality(&self) -> usize; 53 | } 54 | 55 | #[cfg(feature = "sys")] 56 | mod sys { 57 | use super::{Error, Resampler}; 58 | use speexdsp_sys::resampler::*; 59 | 60 | impl From for Error { 61 | fn from(v: i32) -> Error { 62 | match v as u32 { 63 | RESAMPLER_ERR_ALLOC_FAILED => Error::AllocFailed, 64 | RESAMPLER_ERR_BAD_STATE => Error::BadState, 65 | RESAMPLER_ERR_INVALID_ARG => Error::InvalidArg, 66 | RESAMPLER_ERR_PTR_OVERLAP => Error::PtrOverlap, 67 | _ => unreachable!(), 68 | } 69 | } 70 | } 71 | 72 | pub struct State { 73 | st: *mut SpeexResamplerState, 74 | } 75 | 76 | impl Resampler for State { 77 | fn new( 78 | channels: usize, 79 | in_rate: usize, 80 | out_rate: usize, 81 | quality: usize, 82 | ) -> Result { 83 | let mut err = 0; 84 | let st = unsafe { 85 | speex_resampler_init( 86 | channels as u32, 87 | in_rate as u32, 88 | out_rate as u32, 89 | quality as i32, 90 | &mut err, 91 | ) 92 | }; 93 | 94 | if st.is_null() { 95 | Err(err.into()) 96 | } else { 97 | Ok(State { st }) 98 | } 99 | } 100 | 101 | fn set_rate( 102 | &mut self, 103 | in_rate: usize, 104 | out_rate: usize, 105 | ) -> Result<(), Error> { 106 | let ret = unsafe { 107 | speex_resampler_set_rate( 108 | self.st, 109 | in_rate as u32, 110 | out_rate as u32, 111 | ) 112 | }; 113 | if ret != 0 { 114 | Err(ret.into()) 115 | } else { 116 | Ok(()) 117 | } 118 | } 119 | 120 | fn get_rate(&self) -> (usize, usize) { 121 | let mut in_rate = 0; 122 | let mut out_rate = 0; 123 | 124 | unsafe { 125 | speex_resampler_get_rate(self.st, &mut in_rate, &mut out_rate) 126 | }; 127 | 128 | (in_rate as usize, out_rate as usize) 129 | } 130 | 131 | fn get_ratio(&self) -> (usize, usize) { 132 | let mut num = 0; 133 | let mut den = 0; 134 | 135 | unsafe { speex_resampler_get_ratio(self.st, &mut num, &mut den) }; 136 | 137 | (num as usize, den as usize) 138 | } 139 | 140 | fn process_float( 141 | &mut self, 142 | index: usize, 143 | input: &[f32], 144 | output: &mut [f32], 145 | ) -> Result<(usize, usize), Error> { 146 | let mut in_len = input.len() as u32; 147 | let mut out_len = output.len() as u32; 148 | let ret = unsafe { 149 | speex_resampler_process_float( 150 | self.st, 151 | index as u32, 152 | input.as_ptr(), 153 | &mut in_len, 154 | output.as_mut_ptr(), 155 | &mut out_len, 156 | ) 157 | }; 158 | 159 | if ret != 0 { 160 | Err(ret.into()) 161 | } else { 162 | Ok((in_len as usize, out_len as usize)) 163 | } 164 | } 165 | 166 | fn skip_zeros(&mut self) { 167 | unsafe { speex_resampler_skip_zeros(self.st) }; 168 | } 169 | 170 | fn reset(&mut self) { 171 | unsafe { speex_resampler_reset_mem(self.st) }; 172 | } 173 | 174 | fn get_input_latency(&self) -> usize { 175 | unsafe { speex_resampler_get_input_latency(self.st) as usize } 176 | } 177 | 178 | fn get_output_latency(&self) -> usize { 179 | unsafe { speex_resampler_get_output_latency(self.st) as usize } 180 | } 181 | 182 | fn set_quality(&mut self, quality: usize) -> Result<(), Error> { 183 | let ret = unsafe { 184 | speex_resampler_set_quality(self.st, quality as i32) 185 | }; 186 | if ret != 0 { 187 | Err(ret.into()) 188 | } else { 189 | Ok(()) 190 | } 191 | } 192 | 193 | fn get_quality(&self) -> usize { 194 | let mut c_get = 0; 195 | unsafe { speex_resampler_get_quality(self.st, &mut c_get) }; 196 | c_get as usize 197 | } 198 | } 199 | 200 | impl Drop for State { 201 | fn drop(&mut self) { 202 | unsafe { speex_resampler_destroy(self.st) }; 203 | } 204 | } 205 | } 206 | 207 | pub mod native { 208 | use super::{Error, Resampler}; 209 | pub use speexdsp_resampler::State; 210 | 211 | impl From for Error { 212 | fn from(v: speexdsp_resampler::Error) -> Error { 213 | match v { 214 | speexdsp_resampler::Error::AllocFailed => Error::AllocFailed, 215 | speexdsp_resampler::Error::InvalidArg => Error::InvalidArg, 216 | } 217 | } 218 | } 219 | 220 | impl Resampler for State { 221 | fn new( 222 | channels: usize, 223 | in_rate: usize, 224 | out_rate: usize, 225 | quality: usize, 226 | ) -> Result { 227 | State::new(channels, in_rate, out_rate, quality) 228 | .map_err(|e| e.into()) 229 | } 230 | fn set_rate( 231 | &mut self, 232 | in_rate: usize, 233 | out_rate: usize, 234 | ) -> Result<(), Error> { 235 | State::set_rate(self, in_rate, out_rate).map_err(|e| e.into()) 236 | } 237 | fn get_rate(&self) -> (usize, usize) { 238 | State::get_rate(self) 239 | } 240 | fn get_ratio(&self) -> (usize, usize) { 241 | State::get_ratio(self) 242 | } 243 | fn process_float( 244 | &mut self, 245 | index: usize, 246 | input: &[f32], 247 | output: &mut [f32], 248 | ) -> Result<(usize, usize), Error> { 249 | State::process(self, index, input, output).map_err(|e| e.into()) 250 | } 251 | fn skip_zeros(&mut self) { 252 | State::skip_zeros(self); 253 | } 254 | fn reset(&mut self) { 255 | State::reset(self); 256 | } 257 | fn get_input_latency(&self) -> usize { 258 | State::get_input_latency(self) 259 | } 260 | fn get_output_latency(&self) -> usize { 261 | State::get_output_latency(self) 262 | } 263 | fn set_quality(&mut self, quality: usize) -> Result<(), Error> { 264 | State::set_quality(self, quality).map_err(|e| e.into()) 265 | } 266 | fn get_quality(&self) -> usize { 267 | State::get_quality(self) 268 | } 269 | } 270 | } 271 | 272 | #[cfg(feature = "sys")] 273 | pub use self::sys::*; 274 | 275 | #[cfg(not(feature = "sys"))] 276 | pub use self::native::*; 277 | -------------------------------------------------------------------------------- /tests/resampler.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "sys")] 2 | mod comparison { 3 | use assert_approx_eq::assert_approx_eq; 4 | use interpolate_name::interpolate_test; 5 | 6 | use speexdsp::resampler as sys; 7 | use speexdsp::resampler::native; 8 | use speexdsp::resampler::Resampler; 9 | 10 | use std::f32::consts::PI; 11 | 12 | const PERIOD: f32 = 32f32; 13 | const INBLOCK: usize = 1024; 14 | const RATE: usize = 48000; 15 | 16 | #[interpolate_test(quality8_num_gt_den, 8, true)] 17 | #[interpolate_test(quality10_num_gt_den, 10, true)] 18 | #[interpolate_test(quality8_num_lt_den, 8, false)] 19 | #[interpolate_test(quality10_num_lt_den, 10, false)] 20 | fn resampling(quality: usize, num_gt_den: bool) { 21 | let mut init_rate = RATE; 22 | let mut start_rate = 1000; 23 | let mut off = 0; 24 | let mut avail = INBLOCK as isize; 25 | 26 | let fin: Vec = (0..INBLOCK * 4) 27 | .map(|i| ((i as f32) / PERIOD * 2.0 * PI).sin() * 0.9) 28 | .collect(); 29 | let mut fout = vec![0f32; INBLOCK * 8]; 30 | let mut fout_native = vec![0f32; INBLOCK * 8]; 31 | 32 | if !num_gt_den { 33 | init_rate = 96000; 34 | start_rate = 96000; 35 | } 36 | 37 | let mut st = sys::State::new(1, RATE, init_rate, 4).unwrap(); 38 | 39 | let mut st_native = native::State::new(1, RATE, init_rate, 4).unwrap(); 40 | 41 | st.set_rate(RATE, start_rate).unwrap(); 42 | st.skip_zeros(); 43 | 44 | st.set_quality(quality).unwrap(); 45 | 46 | st_native.set_rate(RATE, start_rate).unwrap(); 47 | st_native.skip_zeros(); 48 | 49 | st_native.set_quality(quality).unwrap(); 50 | 51 | for rate in (start_rate..128000).step_by(5000) { 52 | let in_len = avail as usize; 53 | let out_len = (in_len * rate + RATE - 1) / RATE; 54 | let prev_in_len = in_len; 55 | let prev_out_len = out_len; 56 | 57 | let (in_len_native, out_len_native) = st_native 58 | .process_float( 59 | 0, 60 | &fin[off..off + in_len], 61 | &mut fout_native[..out_len], 62 | ) 63 | .unwrap(); 64 | 65 | let (in_len, out_len) = st 66 | .process_float( 67 | 0, 68 | &fin[off..off + in_len], 69 | &mut fout[..out_len], 70 | ) 71 | .unwrap(); 72 | 73 | eprintln!( 74 | "{} {} {} {} -> {} {}", 75 | rate, off, prev_in_len, prev_out_len, in_len, out_len 76 | ); 77 | 78 | assert_eq!(in_len, in_len_native); 79 | assert_eq!(out_len, out_len_native); 80 | 81 | off += in_len; 82 | avail += INBLOCK as isize - in_len as isize; 83 | 84 | if off >= INBLOCK { 85 | off -= INBLOCK; 86 | } 87 | 88 | let fout_s = &fout[..out_len]; 89 | let fout_native_s = &fout_native[..out_len]; 90 | 91 | fout_s 92 | .iter() 93 | .zip(fout_native_s.iter()) 94 | .for_each(|(&x, &y)| { 95 | assert_approx_eq!(x, y, 1.0e-6); 96 | }); 97 | 98 | if num_gt_den { 99 | st.set_rate(RATE, rate).unwrap(); 100 | st_native.set_rate(RATE, rate).unwrap(); 101 | } else { 102 | st.set_rate(rate, RATE).unwrap(); 103 | st_native.set_rate(rate, RATE).unwrap(); 104 | } 105 | } 106 | } 107 | } 108 | --------------------------------------------------------------------------------