use crate::transforms::Transform;
use scirs2_core::random::{Rng, RngExt};
use std::marker::PhantomData;
use tenflowers_core::{Result, Tensor, TensorError};
pub struct AddNoise<T> {
noise_std: T,
}
impl<T> AddNoise<T>
where
T: Clone
+ Default
+ scirs2_core::numeric::Float
+ Send
+ Sync
+ 'static
+ bytemuck::Pod
+ bytemuck::Zeroable,
{
pub fn new(noise_std: T) -> Self {
Self { noise_std }
}
}
impl<T> Transform<T> for AddNoise<T>
where
T: Clone
+ Default
+ scirs2_core::numeric::Float
+ Send
+ Sync
+ 'static
+ bytemuck::Pod
+ bytemuck::Zeroable,
{
fn apply(&self, sample: (Tensor<T>, Tensor<T>)) -> Result<(Tensor<T>, Tensor<T>)> {
let (features, labels) = sample;
let shape = features.shape().dims();
let noise = if std::any::type_name::<T>() == std::any::type_name::<f32>() {
let _noise_f32 = tenflowers_core::ops::random_normal_f32(
shape,
T::zero().to_f32().unwrap_or(0.0),
self.noise_std.to_f32().unwrap_or(0.1),
None,
)?;
Tensor::zeros(shape)
} else {
Tensor::zeros(shape)
};
let noisy_features = features.add(&noise)?;
Ok((noisy_features, labels))
}
}
#[derive(Debug, Clone)]
pub enum NoiseType {
White,
Pink,
Brown,
}
pub struct BackgroundNoise<T> {
noise_level: f64, noise_type: NoiseType,
_phantom: PhantomData<T>,
}
impl<T> BackgroundNoise<T>
where
T: Clone
+ Default
+ scirs2_core::numeric::Float
+ Send
+ Sync
+ 'static
+ bytemuck::Pod
+ bytemuck::Zeroable,
{
pub fn new(noise_level: f64, noise_type: NoiseType) -> Result<Self> {
if !(0.0..=1.0).contains(&noise_level) {
return Err(TensorError::invalid_argument(
"Noise level must be between 0.0 and 1.0".to_string(),
));
}
Ok(Self {
noise_level,
noise_type,
_phantom: PhantomData,
})
}
pub fn random(max_noise_level: f64, noise_type: NoiseType) -> Result<Self> {
if !(0.0..=1.0).contains(&max_noise_level) {
return Err(TensorError::invalid_argument(
"Max noise level must be between 0.0 and 1.0".to_string(),
));
}
let mut rng = scirs2_core::random::rng();
let noise_level = rng.random_range(0.0..max_noise_level);
Ok(Self {
noise_level,
noise_type,
_phantom: PhantomData,
})
}
fn generate_noise(&self, length: usize) -> Vec<T> {
let mut rng = scirs2_core::random::rng();
match self.noise_type {
NoiseType::White => {
(0..length)
.map(|_| {
let noise_sample = rng.random::<f64>() * 2.0 - 1.0; T::from(noise_sample * self.noise_level).unwrap_or(T::zero())
})
.collect()
}
NoiseType::Pink => {
let mut pink_noise = Vec::with_capacity(length);
let mut filter_state = [0.0; 7];
for _ in 0..length {
let white_sample = rng.random::<f64>() * 2.0 - 1.0;
filter_state[0] = 0.99886 * filter_state[0] + white_sample * 0.0555179;
filter_state[1] = 0.99332 * filter_state[1] + white_sample * 0.0750759;
filter_state[2] = 0.96900 * filter_state[2] + white_sample * 0.1538520;
filter_state[3] = 0.86650 * filter_state[3] + white_sample * 0.3104856;
filter_state[4] = 0.55000 * filter_state[4] + white_sample * 0.5329522;
filter_state[5] = -0.7616 * filter_state[5] - white_sample * 0.0168980;
let pink_sample = filter_state[0]
+ filter_state[1]
+ filter_state[2]
+ filter_state[3]
+ filter_state[4]
+ filter_state[5]
+ filter_state[6]
+ white_sample * 0.5362;
filter_state[6] = white_sample * 0.115926;
pink_noise
.push(T::from(pink_sample * self.noise_level * 0.11).unwrap_or(T::zero()));
}
pink_noise
}
NoiseType::Brown => {
let mut brown_noise = Vec::with_capacity(length);
let mut accumulator = 0.0;
for _ in 0..length {
let white_sample = rng.random::<f64>() * 2.0 - 1.0;
accumulator += white_sample;
accumulator *= 0.999;
brown_noise
.push(T::from(accumulator * self.noise_level * 0.1).unwrap_or(T::zero()));
}
brown_noise
}
}
}
fn add_noise(&self, audio: &Tensor<T>) -> Result<Tensor<T>> {
if self.noise_level == 0.0 {
return Ok(audio.clone());
}
let audio_data = audio.as_slice().ok_or_else(|| {
TensorError::invalid_argument(
"Cannot access audio data (GPU tensor not supported)".to_string(),
)
})?;
if audio_data.is_empty() {
return Ok(audio.clone());
}
let noise = self.generate_noise(audio_data.len());
let noisy_data: Vec<T> = audio_data
.iter()
.zip(noise.iter())
.map(|(&signal, &noise_sample)| signal + noise_sample)
.collect();
Tensor::from_vec(noisy_data, audio.shape().dims())
}
}
impl<T> Transform<T> for BackgroundNoise<T>
where
T: Clone
+ Default
+ scirs2_core::numeric::Float
+ Send
+ Sync
+ 'static
+ bytemuck::Pod
+ bytemuck::Zeroable,
{
fn apply(&self, sample: (Tensor<T>, Tensor<T>)) -> Result<(Tensor<T>, Tensor<T>)> {
let (features, labels) = sample;
let noisy_features = self.add_noise(&features)?;
Ok((noisy_features, labels))
}
}
pub struct GaussianNoise {
mean: f32,
std: f32,
}
impl GaussianNoise {
pub fn new(mean: f32, std: f32) -> Self {
Self { mean, std }
}
}
impl<T> Transform<T> for GaussianNoise
where
T: Clone
+ Default
+ scirs2_core::numeric::Float
+ Send
+ Sync
+ 'static
+ bytemuck::Pod
+ bytemuck::Zeroable,
{
fn apply(&self, sample: (Tensor<T>, Tensor<T>)) -> Result<(Tensor<T>, Tensor<T>)> {
let (features, labels) = sample;
let data = if let Some(data) = features.as_slice() {
data
} else {
return Err(TensorError::invalid_argument(
"Cannot access tensor data (GPU tensor not supported)".to_string(),
));
};
let mut rng = scirs2_core::random::rng();
let mut noisy_data = Vec::new();
for (i, &value) in data.iter().enumerate() {
let noise = if i % 2 == 0 {
let u1: f32 = rng.random();
let u2: f32 = rng.random();
let z0 = (-2.0 * u1.ln()).sqrt() * (2.0 * std::f32::consts::PI * u2).cos();
self.mean + self.std * z0
} else {
let u1: f32 = rng.random();
let u2: f32 = rng.random();
let z1 = (-2.0 * u1.ln()).sqrt() * (2.0 * std::f32::consts::PI * u2).sin();
self.mean + self.std * z1
};
let noisy_value =
T::from(value).unwrap_or(T::zero()) + T::from(noise).unwrap_or(T::zero());
noisy_data.push(noisy_value);
}
let noisy_features = Tensor::from_vec(noisy_data, features.shape().dims())?;
Ok((noisy_features, labels))
}
}
pub trait AudioAugmentation<T>: Send + Sync {
fn apply_audio(&self, audio: &Tensor<T>) -> Result<Tensor<T>>;
fn name(&self) -> &'static str;
fn processing_latency_ms(&self) -> f64; }
pub struct RealTimeAudioAugmentation<T> {
augmentations: Vec<Box<dyn AudioAugmentation<T>>>,
apply_probability: f32,
max_concurrent: usize,
_phantom: PhantomData<T>,
}
impl<T> Default for RealTimeAudioAugmentation<T>
where
T: Clone
+ Default
+ scirs2_core::numeric::Float
+ Send
+ Sync
+ 'static
+ bytemuck::Pod
+ bytemuck::Zeroable,
{
fn default() -> Self {
Self::new()
}
}
impl<T> RealTimeAudioAugmentation<T>
where
T: Clone
+ Default
+ scirs2_core::numeric::Float
+ Send
+ Sync
+ 'static
+ bytemuck::Pod
+ bytemuck::Zeroable,
{
pub fn new() -> Self {
Self {
augmentations: Vec::new(),
apply_probability: 0.5,
max_concurrent: 3,
_phantom: PhantomData,
}
}
pub fn add_augmentation(&mut self, aug: Box<dyn AudioAugmentation<T>>) -> &mut Self {
self.augmentations.push(aug);
self
}
pub fn with_probability(mut self, prob: f32) -> Self {
self.apply_probability = prob.clamp(0.0, 1.0);
self
}
pub fn with_max_concurrent(mut self, max: usize) -> Self {
self.max_concurrent = max;
self
}
}
impl<T> Transform<T> for RealTimeAudioAugmentation<T>
where
T: Clone
+ Default
+ scirs2_core::numeric::Float
+ Send
+ Sync
+ 'static
+ bytemuck::Pod
+ bytemuck::Zeroable,
{
fn apply(&self, sample: (Tensor<T>, Tensor<T>)) -> Result<(Tensor<T>, Tensor<T>)> {
let (mut features, labels) = sample;
if self.augmentations.is_empty() {
return Ok((features, labels));
}
let mut rng = scirs2_core::random::rng();
let mut selected_augmentations = Vec::new();
for aug in &self.augmentations {
if rng.random::<f32>() < self.apply_probability {
selected_augmentations.push(aug);
if selected_augmentations.len() >= self.max_concurrent {
break;
}
}
}
for aug in selected_augmentations {
features = aug.apply_audio(&features)?;
}
Ok((features, labels))
}
}