use std::num::NonZeroU32;
use non_empty_slice::NonEmptyVec;
use spectrograms::WindowType;
use crate::codecs::perceptual::codec::AudioCodec;
use crate::codecs::perceptual::stereo::{mid_side_decode, mid_side_encode};
use crate::traits::AudioTypeConversion;
use crate::{AudioSampleError, AudioSampleResult, AudioSamples, ParameterError, StandardSample};
use super::codec::{OpusCodec, OpusEncodedAudio};
use super::mode::OpusConfig;
#[derive(Debug, Clone)]
pub struct OpusStereoEncodedAudio {
pub mid: OpusEncodedAudio,
pub side: OpusEncodedAudio,
pub original_length: usize,
pub sample_rate: NonZeroU32,
}
#[derive(Debug, Clone)]
pub struct OpusStereoCodec {
pub mid_config: OpusConfig,
pub side_config: OpusConfig,
pub window: WindowType,
}
impl OpusStereoCodec {
#[inline]
#[must_use]
pub fn new(mid_config: OpusConfig, side_config: OpusConfig, window: WindowType) -> Self {
Self {
mid_config,
side_config,
window,
}
}
fn mid_codec(&self) -> OpusCodec {
OpusCodec::new(self.mid_config.clone(), self.window.clone())
}
fn side_codec(&self) -> OpusCodec {
OpusCodec::new(self.side_config.clone(), self.window.clone())
}
}
impl AudioCodec for OpusStereoCodec {
type Encoded = OpusStereoEncodedAudio;
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!(
"OpusStereoCodec requires exactly 2 channels, got {}",
audio.num_channels()
),
)));
}
let sample_rate = audio.sample_rate();
let original_length = audio.samples_per_channel().get();
let audio_f32 = audio.to_format::<f32>();
let mut channels = audio_f32.channels();
let left = channels
.next()
.expect("validated 2 channels")
.as_slice()
.expect("to_format always contiguous")
.to_vec();
let right = channels
.next()
.expect("validated 2 channels")
.as_slice()
.expect("to_format always contiguous")
.to_vec();
let (mid_samples, side_samples) = mid_side_encode(&left, &right);
let mid_ne = NonEmptyVec::new(mid_samples).map_err(|_| AudioSampleError::EmptyData {
operation: "opus stereo encode".to_string(),
})?;
let side_ne = NonEmptyVec::new(side_samples).map_err(|_| AudioSampleError::EmptyData {
operation: "opus stereo encode".to_string(),
})?;
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(OpusStereoEncodedAudio {
mid,
side,
original_length,
sample_rate,
})
}
fn decode<U: StandardSample>(
encoded: Self::Encoded,
) -> AudioSampleResult<AudioSamples<'static, U>>
where
f32: crate::ConvertFrom<U>,
{
let sample_rate = encoded.sample_rate;
let target_length = encoded.original_length;
let mid_f32 = OpusCodec::decode::<f32>(encoded.mid)?;
let side_f32 = OpusCodec::decode::<f32>(encoded.side)?;
let mid_ch = mid_f32
.channels()
.next()
.expect("OpusCodec::decode returns mono");
let side_ch = side_f32
.channels()
.next()
.expect("OpusCodec::decode returns mono");
let mid_samples = mid_ch.as_slice().expect("mono contiguous");
let side_samples = side_ch.as_slice().expect("mono contiguous");
let (left, right) = mid_side_decode(mid_samples, side_samples);
let interleaved: Vec<f32> = left
.iter()
.zip(right.iter())
.take(target_length)
.flat_map(|(&l, &r)| [l, r])
.collect();
let interleaved_ne =
NonEmptyVec::new(interleaved).map_err(|_| AudioSampleError::EmptyData {
operation: "opus stereo encode".to_string(),
})?;
let stereo_f32: AudioSamples<'static, f32> = AudioSamples::from_interleaved_vec(
interleaved_ne,
NonZeroU32::new(2).expect("2 > 0"),
sample_rate,
)?;
Ok(stereo_f32.to_format::<U>())
}
}