software/resampling: add wrapper
This commit is contained in:
@ -1,2 +1,5 @@
|
|||||||
#[cfg(feature = "software-scaling")]
|
#[cfg(feature = "software-scaling")]
|
||||||
pub mod scaling;
|
pub mod scaling;
|
||||||
|
|
||||||
|
#[cfg(feature = "software-resampling")]
|
||||||
|
pub mod resampling;
|
||||||
|
127
src/software/resampling/context.rs
Normal file
127
src/software/resampling/context.rs
Normal file
@ -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<Self, Error> {
|
||||||
|
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<Delay> {
|
||||||
|
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<Option<Delay>, 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<Option<Delay>, 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
src/software/resampling/delay.rs
Normal file
24
src/software/resampling/delay.rs
Normal file
@ -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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
src/software/resampling/dither.rs
Normal file
57
src/software/resampling/dither.rs
Normal file
@ -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<SwrDitherType> 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<SwrDitherType> 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
src/software/resampling/engine.rs
Normal file
26
src/software/resampling/engine.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
use ffi::*;
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||||
|
pub enum Engine {
|
||||||
|
Software,
|
||||||
|
SoundExchange,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SwrEngine> 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<SwrEngine> for Engine {
|
||||||
|
fn into(self) -> SwrEngine {
|
||||||
|
match self {
|
||||||
|
Engine::Software => SWR_ENGINE_SWR,
|
||||||
|
Engine::SoundExchange => SWR_ENGINE_SOXR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
src/software/resampling/extensions.rs
Normal file
10
src/software/resampling/extensions.rs
Normal file
@ -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, Error> {
|
||||||
|
Context::get(self.format(), self.channel_layout(), self.rate(),
|
||||||
|
format, channel_layout, rate)
|
||||||
|
}
|
||||||
|
}
|
28
src/software/resampling/filter.rs
Normal file
28
src/software/resampling/filter.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
use ffi::*;
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||||
|
pub enum Filter {
|
||||||
|
Cubic,
|
||||||
|
BlackmanNuttall,
|
||||||
|
Kaiser,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SwrFilterType> 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<SwrFilterType> 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
src/software/resampling/flag.rs
Normal file
8
src/software/resampling/flag.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
use libc::c_int;
|
||||||
|
use ffi::*;
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
flags Flags: c_int {
|
||||||
|
const FORCE = SWR_FLAG_RESAMPLE,
|
||||||
|
}
|
||||||
|
}
|
42
src/software/resampling/mod.rs
Normal file
42
src/software/resampling/mod.rs
Normal file
@ -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())
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user