use rubato::audioadapter_buffers::direct::InterleavedSlice;
use rubato::{
Async, FixedAsync, Indexing, Resampler, SincInterpolationParameters, SincInterpolationType,
WindowFunction,
};
use crate::types::{
ChunkFlags, Error, ErrorContext, ErrorGroup, Operation, OutputFormat, Result, CHANNELS,
SAMPLE_RATE,
};
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct NormalizedChunk {
pub samples: Vec<f32>,
pub pts_ns: i64,
pub flags: ChunkFlags,
}
pub const CHUNK_FRAMES: usize = 960;
const INNER_CH: usize = CHANNELS as usize;
pub trait InnerProcessor: Send {
fn process(&mut self, samples: &mut [f32]);
fn flush(&mut self) -> Vec<f32>;
}
pub struct Normalizer {
flush_result: Option<Result<()>>,
in_sample_rate: u32,
in_channels: usize,
pending_input: Vec<f32>,
pending_input_pts: Option<i64>,
stage1_resampler: Option<ResamplerState>,
inner_scratch: Vec<f32>,
total_inner_frames: u64,
inner_processor: Option<Box<dyn InnerProcessor>>,
primary: OutputTap,
capture: Option<OutputTap>,
secondary: Option<OutputTap>,
}
struct OutputTap {
output: OutputFormat,
stage2: Option<OutputStage>,
out_buf: Vec<f32>,
out_chunk_frames: usize,
out_channels: usize,
out_frame_origin: u64,
pts_anchor: Option<PtsAnchor>,
canonical_clock: bool,
padding: std::collections::VecDeque<std::ops::Range<u64>>,
}
#[derive(Clone, Copy)]
struct PtsAnchor {
out_frame: u64,
pts_ns: i64,
}
struct ResamplerState {
inner: Async<f32>,
channels: usize,
chunk_in_frames: usize,
max_out_frames: usize,
in_accum: Vec<f32>,
out_scratch: Vec<f32>,
}
struct OutputStage {
out_channels: usize,
resampler: Option<ResamplerState>,
ch_scratch: Vec<f32>,
}
impl Normalizer {
pub fn new(in_sample_rate: u32, in_channels: u16, output: OutputFormat) -> Result<Self> {
if in_sample_rate == 0 || in_channels == 0 {
return Err(Error::InvalidArg(
"native rate and channels must be positive".into(),
));
}
if in_channels > 2 {
return Err(Error::UnsupportedFormat(
"native input supports only mono or stereo".into(),
));
}
output.validate()?;
let in_channels = usize::from(in_channels);
let stage1_resampler = if in_sample_rate == SAMPLE_RATE {
None
} else {
Some(ResamplerState::new(in_sample_rate, SAMPLE_RATE, INNER_CH)?)
};
Ok(Self {
flush_result: None,
in_sample_rate,
in_channels,
pending_input: Vec::new(),
pending_input_pts: None,
stage1_resampler,
inner_scratch: Vec::with_capacity(CHUNK_FRAMES * INNER_CH * 4),
total_inner_frames: 0,
inner_processor: None,
primary: OutputTap::new(output)?,
capture: None,
secondary: None,
})
}
pub fn with_secondary(mut self, secondary: OutputFormat) -> Result<Self> {
self.secondary = Some(OutputTap::new(secondary)?);
Ok(self)
}
pub fn with_capture_tap(mut self) -> Result<Self> {
let mut capture = OutputTap::new(OutputFormat::default())?;
capture.canonical_clock = true;
self.capture = Some(capture);
Ok(self)
}
pub fn buffered_capture_frames(&self) -> usize {
self.capture
.as_ref()
.map_or(0, OutputTap::buffered_out_frames)
}
pub fn pop_capture(&mut self, stopping: bool) -> Option<(Vec<f32>, i64)> {
self.pop_capture_with_metadata(stopping)
.map(|chunk| (chunk.samples, chunk.pts_ns))
}
pub fn pop_capture_with_metadata(&mut self, stopping: bool) -> Option<NormalizedChunk> {
let tap = self.capture.as_mut()?;
let frames = tap.buffered_out_frames();
if frames == 0 || (frames < CHUNK_FRAMES && !stopping) {
return None;
}
let take = frames.min(CHUNK_FRAMES);
let pts = tap.pts_for_out_frame(tap.out_frame_origin);
let data = tap.out_buf.drain(..take * INNER_CH).collect();
tap.out_frame_origin += take as u64;
Some(NormalizedChunk {
samples: data,
pts_ns: pts,
flags: ChunkFlags::empty(),
})
}
pub fn with_inner_processor(mut self, processor: Box<dyn InnerProcessor>) -> Self {
self.inner_processor = Some(processor);
self
}
pub fn in_sample_rate(&self) -> u32 {
self.in_sample_rate
}
pub fn output(&self) -> OutputFormat {
self.primary.output
}
pub fn secondary_output(&self) -> Option<OutputFormat> {
self.secondary.as_ref().map(|t| t.output)
}
pub fn has_secondary(&self) -> bool {
self.secondary.is_some()
}
pub fn is_passthrough(&self) -> bool {
self.stage1_resampler.is_none()
}
pub fn is_output_passthrough(&self) -> bool {
self.primary.stage2.is_none()
}
pub fn push(&mut self, interleaved: &[f32], device_pts_ns: i64) -> Result<()> {
if interleaved.is_empty() {
return Ok(());
}
if let Some(Err(error)) = &self.flush_result {
return Err(error.clone());
}
self.flush_result = None;
if self.pending_input.is_empty() {
self.pending_input_pts = Some(device_pts_ns);
}
self.pending_input.extend_from_slice(interleaved);
let in_frames = self.pending_input.len() / self.in_channels;
if in_frames == 0 {
return Ok(());
}
let device_pts_ns = self.pending_input_pts.unwrap_or(device_pts_ns);
self.primary
.update_pts_anchor(self.total_inner_frames, device_pts_ns)?;
if let Some(sec) = self.secondary.as_mut() {
sec.update_pts_anchor(self.total_inner_frames, device_pts_ns)?;
}
if let Some(capture) = self.capture.as_mut() {
if capture.pts_anchor.is_none() {
capture.update_pts_anchor(self.total_inner_frames, device_pts_ns)?;
}
}
self.inner_scratch.clear();
let mut stereo = Vec::with_capacity(in_frames * INNER_CH);
Self::mix_to_stereo(
&self.pending_input,
self.in_channels,
in_frames,
&mut stereo,
);
self.pending_input.drain(..in_frames * self.in_channels);
self.pending_input_pts = if self.pending_input.is_empty() {
None
} else {
Some(device_pts_ns.saturating_add(
(in_frames as i64).saturating_mul(1_000_000_000) / self.in_sample_rate as i64,
))
};
match &mut self.stage1_resampler {
None => {
self.total_inner_frames += in_frames as u64;
self.inner_scratch.extend_from_slice(&stereo);
}
Some(rs) => {
rs.in_accum.extend_from_slice(&stereo);
let produced = rs.drain_into(&mut self.inner_scratch)?;
self.total_inner_frames += produced;
}
}
if let Some(proc) = self.inner_processor.as_mut() {
proc.process(&mut self.inner_scratch);
}
self.distribute_inner()
}
pub fn pop_chunk(&mut self) -> Option<(Vec<f32>, i64)> {
self.pop_chunk_with_metadata()
.map(|chunk| (chunk.samples, chunk.pts_ns))
}
pub fn pop_secondary(&mut self) -> Option<(Vec<f32>, i64)> {
self.pop_secondary_with_metadata()
.map(|chunk| (chunk.samples, chunk.pts_ns))
}
pub fn pop_chunk_with_metadata(&mut self) -> Option<NormalizedChunk> {
self.primary.pop()
}
pub fn pop_secondary_with_metadata(&mut self) -> Option<NormalizedChunk> {
self.secondary.as_mut().and_then(OutputTap::pop)
}
pub fn flush(&mut self) -> Result<()> {
if let Some(result) = &self.flush_result {
return result.clone();
}
let mut errors = Vec::new();
if !self.pending_input.is_empty() {
errors.push(Error::Backend("incomplete native frame at flush".into()));
self.pending_input.clear();
self.pending_input_pts = None;
}
self.inner_scratch.clear();
if let Some(stage) = self.stage1_resampler.as_mut() {
match stage.flush_into(&mut self.inner_scratch) {
Ok(produced) => self.total_inner_frames += produced,
Err(error) => errors.push(error),
}
if let Some(processor) = self.inner_processor.as_mut() {
processor.process(&mut self.inner_scratch);
}
if let Err(error) = self.distribute_inner() {
errors.push(error);
}
}
if let Some(processor) = self.inner_processor.as_mut() {
self.inner_scratch = processor.flush();
let empty_capture = if self.total_inner_frames == 0 {
self.capture.take()
} else {
None
};
if let Err(error) = self.distribute_inner() {
errors.push(error);
}
if let Some(capture) = empty_capture {
self.capture = Some(capture);
}
}
if let Err(error) = self.primary.flush() {
errors.push(error);
}
if let Some(secondary) = self.secondary.as_mut() {
if let Err(error) = secondary.flush() {
errors.push(error);
}
}
if let Some(capture) = self.capture.as_mut() {
if let Some(stage) = capture.stage2.as_mut() {
if let Err(error) = stage.flush_into(&mut capture.out_buf) {
errors.push(error);
}
}
}
let result = combine_failures(errors)
.map_err(|error| error.with_context(ErrorContext::new(Operation::Flush)));
self.flush_result = Some(result.clone());
result
}
pub fn buffered_out_frames(&self) -> usize {
self.primary.buffered_out_frames()
}
fn distribute_inner(&mut self) -> Result<()> {
if self.inner_scratch.is_empty() {
return Ok(());
}
let inner = std::mem::take(&mut self.inner_scratch);
let r_primary = self.primary.feed_inner(&inner);
let r_capture = self
.capture
.as_mut()
.map(|capture| capture.feed_inner(&inner))
.unwrap_or(Ok(()));
let r_secondary = self
.secondary
.as_mut()
.map(|sec| sec.feed_inner(&inner))
.unwrap_or(Ok(()));
self.inner_scratch = inner;
self.inner_scratch.clear();
combine_failures(
[r_primary, r_capture, r_secondary]
.into_iter()
.filter_map(Result::err)
.collect(),
)
}
fn mix_to_stereo(src: &[f32], in_ch: usize, in_frames: usize, dst: &mut Vec<f32>) {
match in_ch {
1 => {
for &s in &src[..in_frames] {
dst.push(s);
dst.push(s);
}
}
2 => {
dst.extend_from_slice(&src[..in_frames * 2]);
}
_ => unreachable!("native channel count validated by constructor"),
}
}
}
#[cfg(test)]
mod tests;
#[cfg(test)]
mod repro_tests;
mod output;
mod resampler;
fn combine_failures(errors: Vec<Error>) -> Result<()> {
let mut errors = errors.into_iter();
match (errors.next(), errors.next()) {
(None, _) => Ok(()),
(Some(error), None) => Err(error),
(Some(primary), Some(first)) => Err(Error::Multiple(ErrorGroup::new(
primary,
first,
errors.collect(),
))),
}
}