use crate::synthesis::spectral::{WindowType, STFT};
use rand::{Rng, SeedableRng};
use rand::rngs::StdRng;
#[derive(Clone)]
pub struct SpectralScramble {
stft: STFT,
fft_size: usize,
sample_rate: f32,
scramble_amount: f32, low_freq: f32, high_freq: f32, mix: f32, enabled: bool,
rng: StdRng,
permutation: Vec<usize>,
}
impl SpectralScramble {
pub fn new(fft_size: usize, hop_size: usize, window: WindowType, sample_rate: f32) -> Self {
let num_bins = fft_size / 2 + 1;
let rng = StdRng::seed_from_u64(42); Self {
stft: STFT::new(fft_size, hop_size, window),
fft_size,
sample_rate,
scramble_amount: 1.0,
low_freq: 200.0,
high_freq: 12000.0,
mix: 1.0,
enabled: true,
rng,
permutation: (0..num_bins).collect(),
}
}
pub fn scramble_amount(&self) -> f32 { self.scramble_amount }
pub fn set_scramble_amount(&mut self, amount: f32) {
self.scramble_amount = amount.clamp(0.0, 1.0);
self.update_permutation();
}
pub fn low_freq(&self) -> f32 { self.low_freq }
pub fn set_low_freq(&mut self, freq: f32) {
self.low_freq = freq.max(0.0);
self.update_permutation();
}
pub fn high_freq(&self) -> f32 { self.high_freq }
pub fn set_high_freq(&mut self, freq: f32) {
self.high_freq = freq.min(self.sample_rate / 2.0);
self.update_permutation();
}
pub fn mix(&self) -> f32 { self.mix }
pub fn set_mix(&mut self, mix: f32) { self.mix = mix.clamp(0.0, 1.0); }
fn update_permutation(&mut self) {
let num_bins = self.permutation.len();
let bin_width = self.sample_rate / self.fft_size as f32;
let low_bin = (self.low_freq / bin_width) as usize;
let high_bin = ((self.high_freq / bin_width) as usize).min(num_bins - 1);
for i in 0..num_bins {
self.permutation[i] = i;
}
if self.scramble_amount > 0.0 {
let shuffle_count = ((high_bin - low_bin) as f32 * self.scramble_amount) as usize;
for _ in 0..shuffle_count {
let i = self.rng.random_range(low_bin..=high_bin);
let j = self.rng.random_range(low_bin..=high_bin);
self.permutation.swap(i, j);
}
}
}
pub fn subtle() -> Self {
let mut s = Self::new(2048, 512, WindowType::Hann, 44100.0);
s.scramble_amount = 0.3; s.low_freq = 1000.0; s.high_freq = 8000.0; s.mix = 0.5;
s.update_permutation();
s
}
pub fn moderate() -> Self {
let mut s = Self::new(2048, 512, WindowType::Hann, 44100.0);
s.scramble_amount = 0.6; s.low_freq = 500.0; s.high_freq = 12000.0; s.mix = 0.7;
s.update_permutation();
s
}
pub fn chaos() -> Self {
let mut s = Self::new(2048, 512, WindowType::Hann, 44100.0);
s.scramble_amount = 1.0; s.low_freq = 200.0; s.high_freq = 16000.0; s.mix = 1.0;
s.update_permutation();
s
}
pub fn glitch() -> Self {
let mut s = Self::new(2048, 512, WindowType::Hann, 44100.0);
s.scramble_amount = 0.8; s.low_freq = 2000.0; s.high_freq = 8000.0; s.mix = 0.9;
s.update_permutation();
s
}
pub fn digital() -> Self {
let mut s = Self::new(2048, 512, WindowType::Hann, 44100.0);
s.scramble_amount = 0.5; s.low_freq = 4000.0; s.high_freq = 12000.0; s.mix = 0.8;
s.update_permutation();
s
}
pub fn process(&mut self, output: &mut [f32], input: &[f32]) {
if !self.enabled {
output.copy_from_slice(input);
return;
}
self.stft.add_input(input);
let permutation = self.permutation.clone();
self.stft.process(output, |spectrum| {
let mut scrambled = spectrum.to_vec();
let max_bins = spectrum.len().min(permutation.len());
for i in 0..max_bins {
if permutation[i] < spectrum.len() {
scrambled[i] = spectrum[permutation[i]];
}
}
for i in 0..max_bins {
spectrum[i].re = spectrum[i].re * (1.0 - self.mix) + scrambled[i].re * self.mix;
spectrum[i].im = spectrum[i].im * (1.0 - self.mix) + scrambled[i].im * self.mix;
}
});
}
pub fn reset(&mut self) {
self.stft.reset();
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
pub fn is_enabled(&self) -> bool {
self.enabled
}
pub fn fft_size(&self) -> usize {
self.fft_size
}
pub fn hop_size(&self) -> usize {
self.stft.hop_size
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_spectral_scramble_creation() {
let scramble = SpectralScramble::new(2048, 512, WindowType::Hann, 44100.0);
assert!(scramble.is_enabled());
assert_eq!(scramble.fft_size(), 2048);
assert_eq!(scramble.scramble_amount(), 1.0);
assert_eq!(scramble.low_freq(), 200.0);
assert_eq!(scramble.high_freq(), 12000.0);
assert_eq!(scramble.mix(), 1.0);
}
#[test]
fn test_set_scramble_amount() {
let mut scramble = SpectralScramble::new(512, 128, WindowType::Hann, 44100.0);
scramble.set_scramble_amount(0.5);
assert_eq!(scramble.scramble_amount(), 0.5);
scramble.set_scramble_amount(1.5);
assert_eq!(scramble.scramble_amount(), 1.0);
scramble.set_scramble_amount(-0.5);
assert_eq!(scramble.scramble_amount(), 0.0);
}
#[test]
fn test_set_low_freq() {
let mut scramble = SpectralScramble::new(512, 128, WindowType::Hann, 44100.0);
scramble.set_low_freq(500.0);
assert_eq!(scramble.low_freq(), 500.0);
scramble.set_low_freq(-100.0);
assert_eq!(scramble.low_freq(), 0.0);
}
#[test]
fn test_set_high_freq() {
let mut scramble = SpectralScramble::new(512, 128, WindowType::Hann, 44100.0);
scramble.set_high_freq(8000.0);
assert_eq!(scramble.high_freq(), 8000.0);
scramble.set_high_freq(50000.0);
assert_eq!(scramble.high_freq(), 22050.0); }
#[test]
fn test_set_mix() {
let mut scramble = SpectralScramble::new(512, 128, WindowType::Hann, 44100.0);
scramble.set_mix(0.5);
assert_eq!(scramble.mix(), 0.5);
scramble.set_mix(1.5);
assert_eq!(scramble.mix(), 1.0);
scramble.set_mix(-0.5);
assert_eq!(scramble.mix(), 0.0);
}
#[test]
fn test_enable_disable() {
let mut scramble = SpectralScramble::new(512, 128, WindowType::Hann, 44100.0);
assert!(scramble.is_enabled());
scramble.set_enabled(false);
assert!(!scramble.is_enabled());
scramble.set_enabled(true);
assert!(scramble.is_enabled());
}
#[test]
fn test_process_disabled() {
let mut scramble = SpectralScramble::new(512, 128, WindowType::Hann, 44100.0);
scramble.set_enabled(false);
let input = vec![0.1; 512];
let mut output = vec![0.0; 512];
scramble.process(&mut output, &input);
assert_eq!(output, input);
}
#[test]
fn test_process_basic() {
let mut scramble = SpectralScramble::new(512, 128, WindowType::Hann, 44100.0);
let input = vec![0.1; 512];
let mut output = vec![0.0; 512];
scramble.process(&mut output, &input);
}
#[test]
fn test_reset() {
let mut scramble = SpectralScramble::new(512, 128, WindowType::Hann, 44100.0);
let input = vec![0.1; 512];
let mut output = vec![0.0; 512];
scramble.process(&mut output, &input);
scramble.reset();
scramble.process(&mut output, &input);
}
#[test]
fn test_full_scramble() {
let mut scramble = SpectralScramble::new(512, 128, WindowType::Hann, 44100.0);
scramble.set_scramble_amount(1.0);
let input = vec![0.1; 512];
let mut output = vec![0.0; 512];
scramble.process(&mut output, &input);
}
#[test]
fn test_no_scramble() {
let mut scramble = SpectralScramble::new(512, 128, WindowType::Hann, 44100.0);
scramble.set_scramble_amount(0.0);
let input = vec![0.1; 512];
let mut output = vec![0.0; 512];
scramble.process(&mut output, &input);
}
#[test]
fn test_preset_subtle() {
let scramble = SpectralScramble::subtle();
assert_eq!(scramble.scramble_amount(), 0.3);
assert_eq!(scramble.low_freq(), 1000.0);
assert_eq!(scramble.high_freq(), 8000.0);
assert_eq!(scramble.mix(), 0.5);
assert!(scramble.is_enabled());
}
#[test]
fn test_preset_moderate() {
let scramble = SpectralScramble::moderate();
assert_eq!(scramble.scramble_amount(), 0.6);
assert_eq!(scramble.low_freq(), 500.0);
assert_eq!(scramble.high_freq(), 12000.0);
assert_eq!(scramble.mix(), 0.7);
assert!(scramble.is_enabled());
}
#[test]
fn test_preset_chaos() {
let scramble = SpectralScramble::chaos();
assert_eq!(scramble.scramble_amount(), 1.0);
assert_eq!(scramble.low_freq(), 200.0);
assert_eq!(scramble.high_freq(), 16000.0);
assert_eq!(scramble.mix(), 1.0);
assert!(scramble.is_enabled());
}
#[test]
fn test_preset_glitch() {
let scramble = SpectralScramble::glitch();
assert_eq!(scramble.scramble_amount(), 0.8);
assert_eq!(scramble.low_freq(), 2000.0);
assert_eq!(scramble.high_freq(), 8000.0);
assert_eq!(scramble.mix(), 0.9);
assert!(scramble.is_enabled());
}
#[test]
fn test_preset_digital() {
let scramble = SpectralScramble::digital();
assert_eq!(scramble.scramble_amount(), 0.5);
assert_eq!(scramble.low_freq(), 4000.0);
assert_eq!(scramble.high_freq(), 12000.0);
assert_eq!(scramble.mix(), 0.8);
assert!(scramble.is_enabled());
}
#[test]
fn test_different_fft_sizes() {
let scramble_512 = SpectralScramble::new(512, 128, WindowType::Hann, 44100.0);
let scramble_1024 = SpectralScramble::new(1024, 256, WindowType::Hann, 44100.0);
let scramble_2048 = SpectralScramble::new(2048, 512, WindowType::Hann, 44100.0);
assert_eq!(scramble_512.fft_size(), 512);
assert_eq!(scramble_1024.fft_size(), 1024);
assert_eq!(scramble_2048.fft_size(), 2048);
}
#[test]
fn test_frequency_range() {
let mut scramble = SpectralScramble::new(512, 128, WindowType::Hann, 44100.0);
scramble.set_low_freq(1000.0);
scramble.set_high_freq(2000.0);
assert_eq!(scramble.low_freq(), 1000.0);
assert_eq!(scramble.high_freq(), 2000.0);
let input = vec![0.1; 512];
let mut output = vec![0.0; 512];
scramble.process(&mut output, &input);
}
}