use core::f64::consts::FRAC_PI_2;
use core::num::NonZeroU32;
use core::ops::Range;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(not(feature = "std"))]
use num_traits::Float;
use super::Declicker;
impl Declicker {
pub fn fade_to_enabled_f64(&mut self, enabled: bool, declick_values: &DeclickValuesF64) {
if enabled {
self.fade_to_1_f64(declick_values);
} else {
self.fade_to_0_f64(declick_values);
}
}
pub fn fade_to_0_f64(&mut self, declick_values: &DeclickValuesF64) {
match self {
Self::SettledAt1 => {
*self = Self::FadingTo0 {
frames_left: declick_values.frames(),
}
}
Self::FadingTo1 { frames_left } => {
let frames_left = if *frames_left <= declick_values.frames() {
declick_values.frames() - *frames_left
} else {
declick_values.frames()
};
*self = Self::FadingTo0 { frames_left }
}
_ => {}
}
}
pub fn fade_to_1_f64(&mut self, declick_values: &DeclickValuesF64) {
match self {
Self::SettledAt0 => {
*self = Self::FadingTo1 {
frames_left: declick_values.frames(),
}
}
Self::FadingTo0 { frames_left } => {
let frames_left = if *frames_left <= declick_values.frames() {
declick_values.frames() - *frames_left
} else {
declick_values.frames()
};
*self = Self::FadingTo1 { frames_left }
}
_ => {}
}
}
pub fn process_crossfade_f64<VA: AsRef<[f64]>, VB: AsMut<[f64]>>(
&mut self,
buffers_a: &[VA],
buffers_b: &mut [VB],
frames: usize,
declick_values: &DeclickValuesF64,
) {
let mut crossfade_buffers =
|declick_frames_left: &mut usize, values_a: &[f64], values_b: &[f64]| -> usize {
let process_frames = frames.min(*declick_frames_left);
let values_start = values_a.len() - *declick_frames_left;
let values_a = &values_a[values_start..values_start + process_frames];
let values_b = &values_b[values_start..values_start + process_frames];
for (ch_a, ch_b) in buffers_a.iter().zip(buffers_b.iter_mut()) {
let slice_a = &ch_a.as_ref()[..process_frames];
let slice_b = &mut ch_b.as_mut()[..process_frames];
for i in 0..process_frames {
slice_b[i] = (slice_a[i] * values_a[i]) + (slice_b[i] * values_b[i]);
}
}
*declick_frames_left -= process_frames;
process_frames
};
match self {
Self::SettledAt0 => {
for (ch_a, ch_b) in buffers_a.iter().zip(buffers_b.iter_mut()) {
let slice_a = &ch_a.as_ref()[..frames];
let slice_b = &mut ch_b.as_mut()[..frames];
slice_b.copy_from_slice(slice_a);
}
}
Self::FadingTo0 { frames_left } => {
let frames_processed = crossfade_buffers(
frames_left,
&declick_values.values_0_to_1,
&declick_values.values_1_to_0,
);
if frames_processed < frames {
for (ch_a, ch_b) in buffers_a.iter().zip(buffers_b.iter_mut()) {
let slice_a = &ch_a.as_ref()[frames_processed..frames];
let slice_b = &mut ch_b.as_mut()[frames_processed..frames];
slice_b.copy_from_slice(slice_a);
}
}
if *frames_left == 0 {
*self = Self::SettledAt0;
}
}
Self::FadingTo1 { frames_left } => {
crossfade_buffers(
frames_left,
&declick_values.values_1_to_0,
&declick_values.values_0_to_1,
);
if *frames_left == 0 {
*self = Self::SettledAt1;
}
}
_ => {}
}
}
pub fn process_f64<V: AsMut<[f64]>>(
&mut self,
buffers: &mut [V],
range_in_buffer: Range<usize>,
declick_values: &DeclickValuesF64,
gain: f64,
) {
let mut fade_buffers = |declick_frames_left: &mut usize, values: &[f64]| -> usize {
let buffer_frames = range_in_buffer.end - range_in_buffer.start;
let process_frames = buffer_frames.min(*declick_frames_left);
let start_frame = values.len() - *declick_frames_left;
if gain == 1.0 {
for b in buffers.iter_mut() {
let b = &mut b.as_mut()
[range_in_buffer.start..range_in_buffer.start + process_frames];
for (s, &g) in b
.iter_mut()
.zip(values[start_frame..start_frame + process_frames].iter())
{
*s *= g;
}
}
} else {
for b in buffers.iter_mut() {
let b = &mut b.as_mut()
[range_in_buffer.start..range_in_buffer.start + process_frames];
for (s, &g) in b
.iter_mut()
.zip(values[start_frame..start_frame + process_frames].iter())
{
*s *= g * gain;
}
}
}
*declick_frames_left -= process_frames;
process_frames
};
match self {
Self::SettledAt0 => {
for b in buffers.iter_mut() {
let b = &mut b.as_mut();
b[range_in_buffer.clone()].fill(0.0);
}
}
Self::FadingTo0 { frames_left } => {
let frames_processed = fade_buffers(frames_left, &declick_values.values_1_to_0);
if frames_processed < range_in_buffer.end - range_in_buffer.start {
for b in buffers.iter_mut() {
let b = &mut b.as_mut()
[range_in_buffer.start + frames_processed..range_in_buffer.end];
b.fill(0.0);
}
}
if *frames_left == 0 {
*self = Self::SettledAt0;
}
}
Self::FadingTo1 { frames_left } => {
let frames_processed = fade_buffers(frames_left, &declick_values.values_0_to_1);
if frames_processed < range_in_buffer.end - range_in_buffer.start && gain != 1.0 {
for b in buffers.iter_mut() {
let b = &mut b.as_mut()
[range_in_buffer.start + frames_processed..range_in_buffer.end];
for s in b.iter_mut() {
*s *= gain;
}
}
}
if *frames_left == 0 {
*self = Self::SettledAt1;
}
}
_ => {}
}
}
}
pub struct DeclickValuesF64 {
pub values_0_to_1: Vec<f64>,
pub values_1_to_0: Vec<f64>,
}
impl DeclickValuesF64 {
pub const DEFAULT_FADE_SECONDS: f64 = 10.0 / 1_000.0;
pub fn linear(frames: NonZeroU32) -> Self {
let frames = frames.get() as usize;
let frames_recip = (frames as f64).recip();
let mut values_0_to_1 = Vec::new();
let mut values_1_to_0 = Vec::new();
values_0_to_1.reserve_exact(frames);
values_1_to_0.reserve_exact(frames);
values_0_to_1 = (0..frames).map(|i| i as f64 * frames_recip).collect();
values_1_to_0 = values_0_to_1.iter().rev().copied().collect();
Self {
values_0_to_1,
values_1_to_0,
}
}
pub fn equal_power_3db(frames: NonZeroU32) -> Self {
let frames = frames.get() as usize;
let frames_recip = (frames as f64).recip();
let mut values_0_to_1 = Vec::new();
let mut values_1_to_0 = Vec::new();
values_0_to_1.reserve_exact(frames);
values_1_to_0.reserve_exact(frames);
values_0_to_1 = (0..frames)
.map(|i| (i as f64 * frames_recip * FRAC_PI_2).sin())
.collect();
values_1_to_0 = values_0_to_1.iter().rev().copied().collect();
Self {
values_0_to_1,
values_1_to_0,
}
}
pub fn frames(&self) -> usize {
self.values_0_to_1.len()
}
}