software/resampling: add wrapper
This commit is contained in:
parent
2439e67fd0
commit
6c7db403f2
@ -1,2 +1,5 @@
|
||||
#[cfg(feature = "software-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())
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user