use std::os::raw::c_int;
use crate::{SwrContext, swr_convert as ffi_swr_convert, swr_get_delay as ffi_swr_get_delay};
pub(super) const RESAMPLE_HEADROOM_SAMPLES: i32 = 256;
pub unsafe fn convert(
ctx: *mut SwrContext,
out: *mut *mut u8,
out_count: c_int,
in_: *const *const u8,
in_count: c_int,
) -> Result<c_int, c_int> {
if ctx.is_null() {
return Err(crate::error_codes::EINVAL);
}
if out_count < 0 || in_count < 0 {
return Err(crate::error_codes::EINVAL);
}
let ret = ffi_swr_convert(ctx, out, out_count, in_, in_count);
if ret < 0 { Err(ret) } else { Ok(ret) }
}
pub unsafe fn get_delay(ctx: *mut SwrContext, base: i64) -> i64 {
if ctx.is_null() {
return 0;
}
ffi_swr_get_delay(ctx, base)
}
pub fn estimate_output_samples(out_sample_rate: i32, in_sample_rate: i32, in_samples: i32) -> i32 {
if out_sample_rate <= 0 || in_sample_rate <= 0 || in_samples < 0 {
return 0;
}
let ratio = out_sample_rate as f64 / in_sample_rate as f64;
let estimated = (in_samples as f64 * ratio).ceil() as i32;
estimated + RESAMPLE_HEADROOM_SAMPLES
}
#[cfg(test)]
mod tests {
use super::*;
use crate::swresample::{alloc_set_opts2, channel_layout, free, init, sample_format};
#[test]
fn test_convert_null_context() {
unsafe {
let result = convert(
std::ptr::null_mut(),
std::ptr::null_mut(),
0,
std::ptr::null(),
0,
);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), crate::error_codes::EINVAL);
}
}
#[test]
fn test_convert_invalid_counts() {
unsafe {
let out_layout = channel_layout::stereo();
let in_layout = channel_layout::stereo();
let mut ctx = alloc_set_opts2(
&out_layout,
sample_format::FLT,
48000,
&in_layout,
sample_format::FLT,
48000,
)
.unwrap();
init(ctx).unwrap();
let result = convert(ctx, std::ptr::null_mut(), -1, std::ptr::null(), 0);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), crate::error_codes::EINVAL);
let result = convert(ctx, std::ptr::null_mut(), 0, std::ptr::null(), -1);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), crate::error_codes::EINVAL);
free(&mut ctx);
}
}
#[test]
fn test_convert_s16_to_float() {
unsafe {
let out_layout = channel_layout::stereo();
let in_layout = channel_layout::stereo();
let mut ctx = alloc_set_opts2(
&out_layout,
sample_format::FLTP,
48000,
&in_layout,
sample_format::S16,
48000,
)
.unwrap();
init(ctx).unwrap();
let num_samples = 1024;
let mut in_data: Vec<i16> = vec![0; num_samples * 2];
for i in 0..num_samples {
let sample = ((i as f32 / 48.0).sin() * 16000.0) as i16;
in_data[i * 2] = sample; in_data[i * 2 + 1] = sample; }
let out_samples = estimate_output_samples(48000, 48000, num_samples as i32);
let mut out_left: Vec<f32> = vec![0.0; out_samples as usize];
let mut out_right: Vec<f32> = vec![0.0; out_samples as usize];
let in_ptr = in_data.as_ptr() as *const u8;
let in_ptrs: [*const u8; 1] = [in_ptr];
let mut out_ptr_left = out_left.as_mut_ptr() as *mut u8;
let mut out_ptr_right = out_right.as_mut_ptr() as *mut u8;
let mut out_ptrs: [*mut u8; 2] = [out_ptr_left, out_ptr_right];
let result = convert(
ctx,
out_ptrs.as_mut_ptr(),
out_samples,
in_ptrs.as_ptr(),
num_samples as c_int,
);
assert!(result.is_ok(), "Conversion should succeed");
let samples_out = result.unwrap();
assert!(samples_out > 0, "Should produce output samples");
assert!(samples_out <= out_samples, "Should not exceed buffer size");
let non_zero_left = out_left
.iter()
.take(samples_out as usize)
.any(|&x| x != 0.0);
let non_zero_right = out_right
.iter()
.take(samples_out as usize)
.any(|&x| x != 0.0);
assert!(
non_zero_left || non_zero_right,
"Output should contain data"
);
free(&mut ctx);
}
}
#[test]
fn test_convert_resample_44100_to_48000() {
unsafe {
let out_layout = channel_layout::stereo();
let in_layout = channel_layout::stereo();
let mut ctx = alloc_set_opts2(
&out_layout,
sample_format::FLT,
48000,
&in_layout,
sample_format::FLT,
44100,
)
.unwrap();
init(ctx).unwrap();
let num_samples = 1024;
let mut in_data: Vec<f32> = vec![0.0; num_samples * 2];
for i in 0..num_samples {
let sample = (i as f32 / 100.0).sin();
in_data[i * 2] = sample;
in_data[i * 2 + 1] = sample;
}
let out_samples = estimate_output_samples(48000, 44100, num_samples as i32);
let mut out_data: Vec<f32> = vec![0.0; out_samples as usize * 2];
let in_ptr = in_data.as_ptr() as *const u8;
let in_ptrs: [*const u8; 1] = [in_ptr];
let mut out_ptr = out_data.as_mut_ptr() as *mut u8;
let mut out_ptrs: [*mut u8; 1] = [out_ptr];
let result = convert(
ctx,
out_ptrs.as_mut_ptr(),
out_samples,
in_ptrs.as_ptr(),
num_samples as c_int,
);
assert!(result.is_ok(), "Resampling should succeed");
let samples_out = result.unwrap();
let expected_min = (num_samples as f32 * 1.05) as i32;
let expected_max = (num_samples as f32 * 1.15) as i32;
assert!(
samples_out >= expected_min && samples_out <= expected_max,
"Output sample count {} should be between {} and {}",
samples_out,
expected_min,
expected_max
);
free(&mut ctx);
}
}
#[test]
fn test_get_delay_null() {
unsafe {
let delay = get_delay(std::ptr::null_mut(), 48000);
assert_eq!(delay, 0);
}
}
#[test]
fn test_get_delay_after_init() {
unsafe {
let out_layout = channel_layout::stereo();
let in_layout = channel_layout::stereo();
let mut ctx = alloc_set_opts2(
&out_layout,
sample_format::FLT,
48000,
&in_layout,
sample_format::FLT,
44100,
)
.unwrap();
init(ctx).unwrap();
let delay = get_delay(ctx, 48000);
assert_eq!(delay, 0, "Initial delay should be 0");
free(&mut ctx);
}
}
#[test]
fn test_estimate_output_samples_same_rate() {
let out = estimate_output_samples(48000, 48000, 1024);
assert!(out > 1024);
assert!(out < 2048);
}
#[test]
fn test_estimate_output_samples_upsampling() {
let out = estimate_output_samples(48000, 44100, 1024);
assert!(out > 1100);
assert!(out < 1500);
}
#[test]
fn test_estimate_output_samples_downsampling() {
let out = estimate_output_samples(44100, 48000, 1024);
assert!(out > 900);
assert!(out < 1300);
}
#[test]
fn test_estimate_output_samples_invalid() {
assert_eq!(estimate_output_samples(0, 48000, 1024), 0);
assert_eq!(estimate_output_samples(48000, 0, 1024), 0);
assert_eq!(estimate_output_samples(48000, 48000, -1), 0);
assert_eq!(estimate_output_samples(-1, 48000, 1024), 0);
}
#[test]
fn test_multiple_conversions() {
unsafe {
let out_layout = channel_layout::stereo();
let in_layout = channel_layout::stereo();
let mut ctx = alloc_set_opts2(
&out_layout,
sample_format::FLT,
48000,
&in_layout,
sample_format::FLT,
48000,
)
.unwrap();
init(ctx).unwrap();
let chunk_size = 256;
let num_chunks = 4;
let in_data: Vec<f32> = vec![0.5; chunk_size * 2];
let out_samples = estimate_output_samples(48000, 48000, chunk_size as i32);
let mut out_data: Vec<f32> = vec![0.0; out_samples as usize * 2];
for _ in 0..num_chunks {
let in_ptr = in_data.as_ptr() as *const u8;
let in_ptrs: [*const u8; 1] = [in_ptr];
let mut out_ptr = out_data.as_mut_ptr() as *mut u8;
let mut out_ptrs: [*mut u8; 1] = [out_ptr];
let result = convert(
ctx,
out_ptrs.as_mut_ptr(),
out_samples,
in_ptrs.as_ptr(),
chunk_size as c_int,
);
assert!(result.is_ok(), "Conversion should succeed on each pass");
}
free(&mut ctx);
}
}
#[test]
fn test_flush_remaining_samples() {
unsafe {
let out_layout = channel_layout::stereo();
let in_layout = channel_layout::stereo();
let mut ctx = alloc_set_opts2(
&out_layout,
sample_format::FLT,
48000,
&in_layout,
sample_format::FLT,
44100, )
.unwrap();
init(ctx).unwrap();
let num_samples = 1024;
let in_data: Vec<f32> = vec![0.5; num_samples * 2];
let out_samples = estimate_output_samples(48000, 44100, num_samples as i32);
let mut out_data: Vec<f32> = vec![0.0; out_samples as usize * 2];
let in_ptr = in_data.as_ptr() as *const u8;
let in_ptrs: [*const u8; 1] = [in_ptr];
let mut out_ptr = out_data.as_mut_ptr() as *mut u8;
let mut out_ptrs: [*mut u8; 1] = [out_ptr];
convert(
ctx,
out_ptrs.as_mut_ptr(),
out_samples,
in_ptrs.as_ptr(),
num_samples as c_int,
)
.unwrap();
let result = convert(ctx, out_ptrs.as_mut_ptr(), out_samples, std::ptr::null(), 0);
assert!(result.is_ok(), "Flush should succeed");
free(&mut ctx);
}
}
}