use std::num::{NonZeroU32, NonZeroUsize};
use non_empty_slice::NonEmptyVec;
use spectrograms::WindowType;
use crate::traits::AudioTypeConversion;
use crate::{AudioSampleError, AudioSampleResult, AudioSamples, ParameterError, StandardSample};
use super::codec::{AudioCodec, PerceptualCodec, PerceptualEncodedAudio, decode as mono_decode};
use super::{BandLayout, PsychoacousticConfig};
#[must_use]
pub fn mid_side_encode(left: &[f32], right: &[f32]) -> (Vec<f32>, Vec<f32>) {
debug_assert_eq!(left.len(), right.len());
let mid = left
.iter()
.zip(right)
.map(|(&l, &r)| (l + r) * 0.5)
.collect();
let side = left
.iter()
.zip(right)
.map(|(&l, &r)| (l - r) * 0.5)
.collect();
(mid, side)
}
#[must_use]
pub fn mid_side_decode(mid: &[f32], side: &[f32]) -> (Vec<f32>, Vec<f32>) {
debug_assert_eq!(mid.len(), side.len());
let left = mid.iter().zip(side).map(|(&m, &s)| m + s).collect();
let right = mid.iter().zip(side).map(|(&m, &s)| m - s).collect();
(left, right)
}
#[derive(Debug, Clone)]
pub struct StereoPerceptualEncodedAudio {
pub mid: PerceptualEncodedAudio,
pub side: PerceptualEncodedAudio,
}
#[derive(Debug, Clone)]
pub struct StereoPerceptualCodec {
pub band_layout: BandLayout,
pub config: PsychoacousticConfig,
pub window: WindowType,
pub mid_bit_budget: u32,
pub side_bit_budget: u32,
pub min_bits_per_band: u8,
pub window_size: Option<NonZeroUsize>,
pub short_window_size: Option<NonZeroUsize>,
pub transient_threshold: f32,
}
impl StereoPerceptualCodec {
#[inline]
#[must_use]
pub fn new(
band_layout: BandLayout,
config: PsychoacousticConfig,
window: WindowType,
mid_bit_budget: u32,
side_bit_budget: u32,
min_bits_per_band: u8,
) -> Self {
Self {
band_layout,
config,
window,
mid_bit_budget,
side_bit_budget,
min_bits_per_band,
window_size: None,
short_window_size: None,
transient_threshold: 8.0,
}
}
#[inline]
#[must_use]
pub fn with_window_switching(
band_layout: BandLayout,
config: PsychoacousticConfig,
window: WindowType,
mid_bit_budget: u32,
side_bit_budget: u32,
min_bits_per_band: u8,
window_size: NonZeroUsize,
short_window_size: NonZeroUsize,
transient_threshold: f32,
) -> Self {
Self {
band_layout,
config,
window,
mid_bit_budget,
side_bit_budget,
min_bits_per_band,
window_size: Some(window_size),
short_window_size: Some(short_window_size),
transient_threshold,
}
}
fn mid_codec(&self) -> PerceptualCodec {
PerceptualCodec {
band_layout: self.band_layout.clone(),
config: self.config.clone(),
window: self.window.clone(),
bit_budget: self.mid_bit_budget,
min_bits_per_band: self.min_bits_per_band,
window_size: self.window_size,
short_window_size: self.short_window_size,
transient_threshold: self.transient_threshold,
}
}
fn side_codec(&self) -> PerceptualCodec {
PerceptualCodec {
band_layout: self.band_layout.clone(),
config: self.config.clone(),
window: self.window.clone(),
bit_budget: self.side_bit_budget,
min_bits_per_band: self.min_bits_per_band,
window_size: self.window_size,
short_window_size: self.short_window_size,
transient_threshold: self.transient_threshold,
}
}
}
impl AudioCodec for StereoPerceptualCodec {
type Encoded = StereoPerceptualEncodedAudio;
fn encode<T: StandardSample>(
self,
audio: &AudioSamples<T>,
) -> AudioSampleResult<Self::Encoded> {
if audio.num_channels().get() != 2 {
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"audio",
format!(
"StereoPerceptualCodec requires exactly 2 channels, got {}",
audio.num_channels()
),
)));
}
let sample_rate = audio.sample_rate();
let audio_f32 = audio.to_format::<f32>();
let mut channels = audio_f32.channels();
let left = channels
.next()
.expect("validated 2 channels")
.as_slice()
.expect("contiguous")
.to_vec();
let right = channels
.next()
.expect("validated 2 channels")
.as_slice()
.expect("contiguous")
.to_vec();
let (mid_samples, side_samples) = mid_side_encode(&left, &right);
let mid_ne = NonEmptyVec::new(mid_samples).map_err(|_| AudioSampleError::EmptyData)?;
let side_ne = NonEmptyVec::new(side_samples).map_err(|_| AudioSampleError::EmptyData)?;
let mid_audio: AudioSamples<'static, f32> =
AudioSamples::from_mono_vec(mid_ne, sample_rate);
let side_audio: AudioSamples<'static, f32> =
AudioSamples::from_mono_vec(side_ne, sample_rate);
let mid = self.mid_codec().encode(&mid_audio)?;
let side = self.side_codec().encode(&side_audio)?;
Ok(StereoPerceptualEncodedAudio { mid, side })
}
fn decode<U: StandardSample>(
encoded: Self::Encoded,
) -> AudioSampleResult<AudioSamples<'static, U>>
where
f32: crate::ConvertFrom<U>,
{
let mid_audio: AudioSamples<'static, f32> =
mono_decode::<PerceptualCodec, f32>(encoded.mid)?;
let side_audio: AudioSamples<'static, f32> =
mono_decode::<PerceptualCodec, f32>(encoded.side)?;
let mid_samples = mid_audio
.channels()
.next()
.expect("mono")
.as_slice()
.expect("contiguous")
.to_vec();
let side_samples = side_audio
.channels()
.next()
.expect("mono")
.as_slice()
.expect("contiguous")
.to_vec();
let (left, right) = mid_side_decode(&mid_samples, &side_samples);
let interleaved: Vec<f32> = left
.iter()
.zip(right.iter())
.flat_map(|(&l, &r)| [l, r])
.collect();
let interleaved_ne =
NonEmptyVec::new(interleaved).map_err(|_| AudioSampleError::EmptyData)?;
let stereo_f32: AudioSamples<'static, f32> = AudioSamples::from_interleaved_vec(
interleaved_ne,
NonZeroU32::new(2).expect("2 channels"),
mid_audio.sample_rate(),
)?;
Ok(stereo_f32.to_format::<U>())
}
}