From 6c7db403f2bdfad1ece025c73871a27566e6f08f Mon Sep 17 00:00:00 2001 From: meh Date: Mon, 8 Jun 2015 15:53:21 +0200 Subject: [PATCH] software/resampling: add wrapper --- src/software/mod.rs | 3 + src/software/resampling/context.rs | 127 ++++++++++++++++++++++++++ src/software/resampling/delay.rs | 24 +++++ src/software/resampling/dither.rs | 57 ++++++++++++ src/software/resampling/engine.rs | 26 ++++++ src/software/resampling/extensions.rs | 10 ++ src/software/resampling/filter.rs | 28 ++++++ src/software/resampling/flag.rs | 8 ++ src/software/resampling/mod.rs | 42 +++++++++ 9 files changed, 325 insertions(+) create mode 100644 src/software/resampling/context.rs create mode 100644 src/software/resampling/delay.rs create mode 100644 src/software/resampling/dither.rs create mode 100644 src/software/resampling/engine.rs create mode 100644 src/software/resampling/extensions.rs create mode 100644 src/software/resampling/filter.rs create mode 100644 src/software/resampling/flag.rs create mode 100644 src/software/resampling/mod.rs diff --git a/src/software/mod.rs b/src/software/mod.rs index c22302f..cea256a 100644 --- a/src/software/mod.rs +++ b/src/software/mod.rs @@ -1,2 +1,5 @@ #[cfg(feature = "software-scaling")] pub mod scaling; + +#[cfg(feature = "software-resampling")] +pub mod resampling; diff --git a/src/software/resampling/context.rs b/src/software/resampling/context.rs new file mode 100644 index 0000000..ae1b2b2 --- /dev/null +++ b/src/software/resampling/context.rs @@ -0,0 +1,127 @@ +use std::ptr; + +use libc::{c_int, int64_t}; +use ffi::*; +use ::util::format; +use ::{Error, ChannelLayout, frame}; +use super::Delay; + +#[derive(Eq, PartialEq, Copy, Clone)] +pub struct Definition { + pub format: format::Sample, + pub channel_layout: ChannelLayout, + pub rate: u32, +} + +pub struct Context { + ptr: *mut SwrContext, + + input: Definition, + output: Definition, +} + +impl Context { + pub unsafe fn as_ptr(&self) -> *const SwrContext { + self.ptr as *const _ + } + + pub unsafe fn as_mut_ptr(&mut self) -> *mut SwrContext { + self.ptr + } +} + +impl Context { + pub fn get(src_format: format::Sample, src_channel_layout: ChannelLayout, src_rate: u32, + dst_format: format::Sample, dst_channel_layout: ChannelLayout, dst_rate: u32) -> Result { + unsafe { + let ptr = swr_alloc_set_opts(ptr::null_mut(), + dst_channel_layout.bits() as int64_t, dst_format.into(), dst_rate as c_int, + src_channel_layout.bits() as int64_t, src_format.into(), src_rate as c_int, + 0, ptr::null_mut()); + + if ptr != ptr::null_mut() { + match swr_init(ptr) { + e if e < 0 => + Err(Error::from(e)), + + _ => + Ok(Context { + ptr: ptr, + + input: Definition { + format: src_format, + channel_layout: src_channel_layout, + rate: src_rate, + }, + + output: Definition { + format: dst_format, + channel_layout: dst_channel_layout, + rate: dst_rate, + } + }) + } + } + else { + Err(Error::InvalidData) + } + } + } + + pub fn input(&self) -> &Definition { + &self.input + } + + pub fn output(&self) -> &Definition { + &self.output + } + + pub fn delay(&self) -> Option { + unsafe { + match swr_get_delay(self.as_ptr(), 1) { + 0 => None, + _ => Some(Delay::from(self)) + } + } + } + + pub fn run(&mut self, input: &frame::Audio, output: &mut frame::Audio) -> Result, Error> { + output.set_rate(self.output.rate); + + unsafe { + if output.is_empty() { + output.alloc(self.output.format, input.samples(), self.output.channel_layout); + } + + match swr_convert_frame(self.as_mut_ptr(), output.as_mut_ptr(), input.as_ptr()) { + 0 => + Ok(self.delay()), + + e => + Err(Error::from(e)) + } + } + } + + pub fn next(&mut self, output: &mut frame::Audio) -> Result, Error> { + output.set_rate(self.output.rate); + + unsafe { + match swr_convert_frame(self.as_mut_ptr(), output.as_mut_ptr(), ptr::null()) { + 0 => + Ok(self.delay()), + + e => + Err(Error::from(e)) + } + } + } +} + +impl Drop for Context { + fn drop(&mut self) { + unsafe { + swr_free(&mut self.as_mut_ptr()); + } + } +} diff --git a/src/software/resampling/delay.rs b/src/software/resampling/delay.rs new file mode 100644 index 0000000..d70c30e --- /dev/null +++ b/src/software/resampling/delay.rs @@ -0,0 +1,24 @@ +use libc::int64_t; +use ffi::*; +use super::Context; + +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +pub struct Delay { + pub seconds: i64, + pub milliseconds: i64, + pub input: i64, + pub output: i64, +} + +impl Delay { + pub fn from(context: &Context) -> Self { + unsafe { + Delay { + seconds: swr_get_delay(context.as_ptr(), 1), + milliseconds: swr_get_delay(context.as_ptr(), 1000), + input: swr_get_delay(context.as_ptr(), context.input().rate as int64_t), + output: swr_get_delay(context.as_ptr(), context.output().rate as int64_t), + } + } + } +} diff --git a/src/software/resampling/dither.rs b/src/software/resampling/dither.rs new file mode 100644 index 0000000..6279a8e --- /dev/null +++ b/src/software/resampling/dither.rs @@ -0,0 +1,57 @@ +use ffi::*; + +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum Dither { + None, + Rectangular, + Triangular, + TriangularHighPass, + + NoiseShapingLipshitz, + NoiseShapingFWeighted, + NoiseShapingModifiedEWeighted, + NoiseShapingImprovedEWeighted, + NoiseShapingShibata, + NoiseShapingLowShibata, + NoiseShapingHighShibata, +} + +impl From for Dither { + fn from(value: SwrDitherType) -> Dither { + match value { + SWR_DITHER_NONE => Dither::None, + SWR_DITHER_RECTANGULAR => Dither::Rectangular, + SWR_DITHER_TRIANGULAR => Dither::Triangular, + SWR_DITHER_TRIANGULAR_HIGHPASS => Dither::TriangularHighPass, + + SWR_DITHER_NS => Dither::None, + SWR_DITHER_NS_LIPSHITZ => Dither::NoiseShapingLipshitz, + SWR_DITHER_NS_F_WEIGHTED => Dither::NoiseShapingFWeighted, + SWR_DITHER_NS_MODIFIED_E_WEIGHTED => Dither::NoiseShapingModifiedEWeighted, + SWR_DITHER_NS_IMPROVED_E_WEIGHTED => Dither::NoiseShapingImprovedEWeighted, + SWR_DITHER_NS_SHIBATA => Dither::NoiseShapingShibata, + SWR_DITHER_NS_LOW_SHIBATA => Dither::NoiseShapingLowShibata, + SWR_DITHER_NS_HIGH_SHIBATA => Dither::NoiseShapingHighShibata, + SWR_DITHER_NB => Dither::None, + } + } +} + +impl Into for Dither { + fn into(self) -> SwrDitherType { + match self { + Dither::None => SWR_DITHER_NONE, + Dither::Rectangular => SWR_DITHER_RECTANGULAR, + Dither::Triangular => SWR_DITHER_TRIANGULAR, + Dither::TriangularHighPass => SWR_DITHER_TRIANGULAR_HIGHPASS, + + Dither::NoiseShapingLipshitz => SWR_DITHER_NS_LIPSHITZ, + Dither::NoiseShapingFWeighted => SWR_DITHER_NS_F_WEIGHTED, + Dither::NoiseShapingModifiedEWeighted => SWR_DITHER_NS_MODIFIED_E_WEIGHTED, + Dither::NoiseShapingImprovedEWeighted => SWR_DITHER_NS_IMPROVED_E_WEIGHTED, + Dither::NoiseShapingShibata => SWR_DITHER_NS_SHIBATA, + Dither::NoiseShapingLowShibata => SWR_DITHER_NS_LOW_SHIBATA, + Dither::NoiseShapingHighShibata => SWR_DITHER_NS_HIGH_SHIBATA, + } + } +} diff --git a/src/software/resampling/engine.rs b/src/software/resampling/engine.rs new file mode 100644 index 0000000..92a26c7 --- /dev/null +++ b/src/software/resampling/engine.rs @@ -0,0 +1,26 @@ +use ffi::*; + +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum Engine { + Software, + SoundExchange, +} + +impl From for Engine { + fn from(value: SwrEngine) -> Engine { + match value { + SWR_ENGINE_SWR => Engine::Software, + SWR_ENGINE_SOXR => Engine::SoundExchange, + SWR_ENGINE_NB => Engine::Software, + } + } +} + +impl Into for Engine { + fn into(self) -> SwrEngine { + match self { + Engine::Software => SWR_ENGINE_SWR, + Engine::SoundExchange => SWR_ENGINE_SOXR, + } + } +} diff --git a/src/software/resampling/extensions.rs b/src/software/resampling/extensions.rs new file mode 100644 index 0000000..dee5d92 --- /dev/null +++ b/src/software/resampling/extensions.rs @@ -0,0 +1,10 @@ +use util::format; +use ::{decoder, Error, ChannelLayout}; +use super::Context; + +impl decoder::Audio { + pub fn resampler(&self, format: format::Sample, channel_layout: ChannelLayout, rate: u32) -> Result { + Context::get(self.format(), self.channel_layout(), self.rate(), + format, channel_layout, rate) + } +} diff --git a/src/software/resampling/filter.rs b/src/software/resampling/filter.rs new file mode 100644 index 0000000..871132f --- /dev/null +++ b/src/software/resampling/filter.rs @@ -0,0 +1,28 @@ +use ffi::*; + +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum Filter { + Cubic, + BlackmanNuttall, + Kaiser, +} + +impl From for Filter { + fn from(value: SwrFilterType) -> Filter { + match value { + SWR_FILTER_TYPE_CUBIC => Filter::Cubic, + SWR_FILTER_TYPE_BLACKMAN_NUTTALL => Filter::BlackmanNuttall, + SWR_FILTER_TYPE_KAISER => Filter::Kaiser, + } + } +} + +impl Into for Filter { + fn into(self) -> SwrFilterType { + match self { + Filter::Cubic => SWR_FILTER_TYPE_CUBIC, + Filter::BlackmanNuttall => SWR_FILTER_TYPE_BLACKMAN_NUTTALL, + Filter::Kaiser => SWR_FILTER_TYPE_KAISER, + } + } +} diff --git a/src/software/resampling/flag.rs b/src/software/resampling/flag.rs new file mode 100644 index 0000000..7e8399c --- /dev/null +++ b/src/software/resampling/flag.rs @@ -0,0 +1,8 @@ +use libc::c_int; +use ffi::*; + +bitflags! { + flags Flags: c_int { + const FORCE = SWR_FLAG_RESAMPLE, + } +} diff --git a/src/software/resampling/mod.rs b/src/software/resampling/mod.rs new file mode 100644 index 0000000..cd02930 --- /dev/null +++ b/src/software/resampling/mod.rs @@ -0,0 +1,42 @@ +pub mod flag; +pub use self::flag::Flags; + +pub mod dither; +pub use self::dither::Dither; + +pub mod engine; +pub use self::engine::Engine; + +pub mod filter; +pub use self::filter::Filter; + +pub mod delay; +pub use self::delay::Delay; + +pub mod context; +pub use self::context::Context; + +mod extensions; + +use std::ffi::CStr; +use std::str::from_utf8_unchecked; + +use ffi::*; + +pub fn version() -> u32 { + unsafe { + swresample_version() + } +} + +pub fn configuration() -> &'static str { + unsafe { + from_utf8_unchecked(CStr::from_ptr(swresample_configuration()).to_bytes()) + } +} + +pub fn license() -> &'static str { + unsafe { + from_utf8_unchecked(CStr::from_ptr(swresample_license()).to_bytes()) + } +}