use alpha2_time::MediaTime;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ChannelLayout {
Mono,
Stereo,
}
impl ChannelLayout {
#[must_use]
pub const fn from_channels(channels: usize) -> Option<Self> {
match channels {
1 => Some(Self::Mono),
2 => Some(Self::Stereo),
_ => None,
}
}
#[must_use]
pub const fn channels(self) -> usize {
match self {
Self::Mono => 1,
Self::Stereo => 2,
}
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub enum TransportState {
#[default]
Stopped,
Playing,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ProcessCtx {
pub position: MediaTime,
pub sample_position: i64,
pub sample_rate: u32,
pub transport: TransportState,
}
impl ProcessCtx {
#[must_use]
pub fn offset_by(&self, samples: i64) -> Self {
if samples == 0 {
return *self;
}
let sample_position = self.sample_position.saturating_add(samples);
let ticks = (i128::from(sample_position) * i128::from(alpha2_time::TICKS_PER_SECOND))
.div_euclid(i128::from(self.sample_rate.max(1)));
let ticks = i64::try_from(ticks).unwrap_or(if ticks < 0 { i64::MIN } else { i64::MAX });
Self {
position: MediaTime::from_ticks(ticks),
sample_position,
..*self
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct StateBlob(Vec<u8>);
impl StateBlob {
#[must_use]
pub fn new(bytes: Vec<u8>) -> Self {
Self(bytes)
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum StateError {
Mismatch,
Invalid,
}
impl core::fmt::Display for StateError {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str(match self {
Self::Mismatch => "the saved state was made by a processor prepared otherwise",
Self::Invalid => "the saved state holds a value the processor never keeps",
})
}
}
impl std::error::Error for StateError {}
pub trait Processor: Send {
fn prepare(&mut self, sample_rate: u32, max_block: usize);
fn process(&mut self, ctx: &ProcessCtx, inputs: &[&[f32]], outputs: &mut [&mut [f32]]);
fn inputs_per_output(&self) -> usize {
1
}
fn reset(&mut self);
fn latency_samples(&self) -> u32 {
0
}
fn history_samples(&self) -> u32 {
0
}
fn save_state(&self) -> StateBlob;
fn load_state(&mut self, state: &StateBlob) -> Result<(), StateError>;
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
pub(crate) fn settling_samples(coefficient: f64) -> u32 {
if coefficient <= 0.0 {
0
} else if coefficient >= 1.0 || !coefficient.is_finite() {
u32::MAX
} else {
libm::ceil(-32.0 / libm::log(coefficient)) as u32
}
}