use super::*;
use rustfft::num_complex::Complex;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MorphTarget {
Harmonic,
Noise,
Whisper,
Robot,
}
#[derive(Clone, Debug)]
pub struct SpectralMorph {
stft: STFT,
fft_size: usize,
sample_rate: f32,
target: MorphTarget,
morph_amount: f32,
fundamental: f32,
enabled: bool,
}
impl SpectralMorph {
pub fn new(fft_size: usize, hop_size: usize, window_type: WindowType, sample_rate: f32) -> Self {
assert!(fft_size.is_power_of_two(), "FFT size must be power of 2");
assert!(hop_size <= fft_size, "Hop size must be <= FFT size");
assert!(sample_rate > 0.0, "Sample rate must be positive");
let stft = STFT::new(fft_size, hop_size, window_type);
Self {
stft,
fft_size,
sample_rate,
target: MorphTarget::Harmonic,
morph_amount: 0.5,
fundamental: 110.0, enabled: true,
}
}
pub fn set_target(&mut self, target: MorphTarget) {
self.target = target;
}
pub fn set_morph_amount(&mut self, amount: f32) {
self.morph_amount = amount.clamp(0.0, 1.0);
}
pub fn set_fundamental(&mut self, frequency: f32) {
self.fundamental = frequency.clamp(20.0, 2000.0);
}
pub fn target(&self) -> MorphTarget {
self.target
}
pub fn morph_amount(&self) -> f32 {
self.morph_amount
}
pub fn fundamental(&self) -> f32 {
self.fundamental
}
pub fn process(&mut self, output: &mut [f32], _input: &[f32]) {
if !self.enabled {
return;
}
let target = self.target;
let morph_amount = self.morph_amount;
let fundamental = self.fundamental;
let sample_rate = self.sample_rate;
let fft_size = self.fft_size;
self.stft.process(output, |spectrum| {
Self::apply_morph_static(spectrum, target, morph_amount, fundamental, sample_rate, fft_size);
});
}
#[inline]
fn apply_morph_static(
spectrum: &mut [Complex<f32>],
target: MorphTarget,
morph_amount: f32,
fundamental: f32,
sample_rate: f32,
fft_size: usize,
) {
let len = spectrum.len();
let hz_per_bin = sample_rate / fft_size as f32;
let mut magnitudes: Vec<f32> = spectrum.iter().map(|c| c.norm()).collect();
let phases: Vec<f32> = spectrum.iter().map(|c| c.arg()).collect();
let target_mags = match target {
MorphTarget::Harmonic => {
Self::generate_harmonic_spectrum(len, fundamental, hz_per_bin)
}
MorphTarget::Noise => {
Self::generate_noise_spectrum(len)
}
MorphTarget::Whisper => {
Self::generate_whisper_spectrum(len, hz_per_bin)
}
MorphTarget::Robot => {
Self::generate_robot_spectrum(len, fundamental, hz_per_bin)
}
};
for i in 0..len {
magnitudes[i] = magnitudes[i] * (1.0 - morph_amount) + target_mags[i] * morph_amount;
}
for i in 0..len {
spectrum[i] = Complex::from_polar(magnitudes[i], phases[i]);
}
}
fn generate_harmonic_spectrum(len: usize, fundamental: f32, hz_per_bin: f32) -> Vec<f32> {
let mut mags = vec![0.0; len];
for harmonic in 1..50 {
let freq = fundamental * harmonic as f32;
let bin = (freq / hz_per_bin) as usize;
if bin < len {
mags[bin] = 1.0 / harmonic as f32;
}
}
mags
}
fn generate_noise_spectrum(len: usize) -> Vec<f32> {
vec![1.0; len]
}
fn generate_whisper_spectrum(len: usize, hz_per_bin: f32) -> Vec<f32> {
let mut mags = vec![0.0; len];
let cutoff_freq = 1000.0;
for (i, mag) in mags.iter_mut().enumerate().take(len) {
let freq = i as f32 * hz_per_bin;
if freq > cutoff_freq {
let ratio = (freq - cutoff_freq) / cutoff_freq;
*mag = ratio.min(1.0);
}
}
mags
}
fn generate_robot_spectrum(len: usize, fundamental: f32, hz_per_bin: f32) -> Vec<f32> {
let mut mags = vec![0.0; len];
for octave in 0..6 {
let freq = fundamental * 2.0_f32.powi(octave);
let bin = (freq / hz_per_bin) as usize;
if bin < len {
mags[bin] = 1.0 / (octave + 1) as f32;
}
}
mags
}
pub fn reset(&mut self) {
self.stft.reset();
}
pub fn fft_size(&self) -> usize {
self.fft_size
}
pub fn hop_size(&self) -> usize {
self.stft.hop_size
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
pub fn is_enabled(&self) -> bool {
self.enabled
}
}
impl SpectralMorph {
pub fn harmonic() -> Self {
let mut morph = Self::new(2048, 512, WindowType::Hann, 44100.0);
morph.set_target(MorphTarget::Harmonic);
morph.set_morph_amount(0.7);
morph.set_fundamental(110.0);
morph
}
pub fn noise() -> Self {
let mut morph = Self::new(2048, 512, WindowType::Hann, 44100.0);
morph.set_target(MorphTarget::Noise);
morph.set_morph_amount(0.6);
morph
}
pub fn whisper() -> Self {
let mut morph = Self::new(2048, 512, WindowType::Hann, 44100.0);
morph.set_target(MorphTarget::Whisper);
morph.set_morph_amount(0.8);
morph
}
pub fn robot() -> Self {
let mut morph = Self::new(2048, 512, WindowType::Hann, 44100.0);
morph.set_target(MorphTarget::Robot);
morph.set_morph_amount(0.9);
morph.set_fundamental(220.0);
morph
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_spectral_morph_creation() {
let morph = SpectralMorph::new(2048, 512, WindowType::Hann, 44100.0);
assert!(morph.is_enabled());
assert_eq!(morph.fft_size(), 2048);
assert_eq!(morph.target(), MorphTarget::Harmonic);
assert_eq!(morph.morph_amount(), 0.5);
assert_eq!(morph.fundamental(), 110.0);
}
#[test]
#[should_panic(expected = "FFT size must be power of 2")]
fn test_requires_power_of_two() {
SpectralMorph::new(1000, 250, WindowType::Hann, 44100.0);
}
#[test]
#[should_panic(expected = "Hop size must be <= FFT size")]
fn test_hop_validation() {
SpectralMorph::new(512, 1024, WindowType::Hann, 44100.0);
}
#[test]
#[should_panic(expected = "Sample rate must be positive")]
fn test_sample_rate_validation() {
SpectralMorph::new(512, 128, WindowType::Hann, 0.0);
}
#[test]
fn test_set_target() {
let mut morph = SpectralMorph::new(512, 128, WindowType::Hann, 44100.0);
morph.set_target(MorphTarget::Noise);
assert_eq!(morph.target(), MorphTarget::Noise);
morph.set_target(MorphTarget::Whisper);
assert_eq!(morph.target(), MorphTarget::Whisper);
morph.set_target(MorphTarget::Robot);
assert_eq!(morph.target(), MorphTarget::Robot);
morph.set_target(MorphTarget::Harmonic);
assert_eq!(morph.target(), MorphTarget::Harmonic);
}
#[test]
fn test_set_morph_amount() {
let mut morph = SpectralMorph::new(512, 128, WindowType::Hann, 44100.0);
morph.set_morph_amount(0.7);
assert_eq!(morph.morph_amount(), 0.7);
morph.set_morph_amount(-0.5);
assert_eq!(morph.morph_amount(), 0.0);
morph.set_morph_amount(1.5);
assert_eq!(morph.morph_amount(), 1.0);
}
#[test]
fn test_set_fundamental() {
let mut morph = SpectralMorph::new(512, 128, WindowType::Hann, 44100.0);
morph.set_fundamental(440.0);
assert_eq!(morph.fundamental(), 440.0);
morph.set_fundamental(10.0);
assert_eq!(morph.fundamental(), 20.0);
morph.set_fundamental(5000.0);
assert_eq!(morph.fundamental(), 2000.0);
}
#[test]
fn test_enable_disable() {
let mut morph = SpectralMorph::new(512, 128, WindowType::Hann, 44100.0);
assert!(morph.is_enabled());
morph.set_enabled(false);
assert!(!morph.is_enabled());
morph.set_enabled(true);
assert!(morph.is_enabled());
}
#[test]
fn test_process_disabled() {
let mut morph = SpectralMorph::new(512, 128, WindowType::Hann, 44100.0);
morph.set_enabled(false);
let input = vec![0.1; 512];
let mut output = vec![0.0; 512];
morph.process(&mut output, &input);
assert!(output.iter().all(|&x| x == 0.0));
}
#[test]
fn test_process_basic() {
let mut morph = SpectralMorph::new(512, 128, WindowType::Hann, 44100.0);
let input = vec![0.1; 512];
let mut output = vec![0.0; 512];
morph.process(&mut output, &input);
}
#[test]
fn test_reset() {
let mut morph = SpectralMorph::new(512, 128, WindowType::Hann, 44100.0);
let input = vec![0.1; 512];
let mut output = vec![0.0; 512];
morph.process(&mut output, &input);
morph.reset();
morph.process(&mut output, &input);
}
#[test]
fn test_preset_harmonic() {
let morph = SpectralMorph::harmonic();
assert_eq!(morph.target(), MorphTarget::Harmonic);
assert_eq!(morph.morph_amount(), 0.7);
assert_eq!(morph.fundamental(), 110.0);
assert!(morph.is_enabled());
}
#[test]
fn test_preset_noise() {
let morph = SpectralMorph::noise();
assert_eq!(morph.target(), MorphTarget::Noise);
assert_eq!(morph.morph_amount(), 0.6);
assert!(morph.is_enabled());
}
#[test]
fn test_preset_whisper() {
let morph = SpectralMorph::whisper();
assert_eq!(morph.target(), MorphTarget::Whisper);
assert_eq!(morph.morph_amount(), 0.8);
assert!(morph.is_enabled());
}
#[test]
fn test_preset_robot() {
let morph = SpectralMorph::robot();
assert_eq!(morph.target(), MorphTarget::Robot);
assert_eq!(morph.morph_amount(), 0.9);
assert_eq!(morph.fundamental(), 220.0);
assert!(morph.is_enabled());
}
#[test]
fn test_different_fft_sizes() {
let morph_512 = SpectralMorph::new(512, 128, WindowType::Hann, 44100.0);
let morph_1024 = SpectralMorph::new(1024, 256, WindowType::Hann, 44100.0);
let morph_2048 = SpectralMorph::new(2048, 512, WindowType::Hann, 44100.0);
assert_eq!(morph_512.fft_size(), 512);
assert_eq!(morph_1024.fft_size(), 1024);
assert_eq!(morph_2048.fft_size(), 2048);
}
#[test]
fn test_morph_target_equality() {
assert_eq!(MorphTarget::Harmonic, MorphTarget::Harmonic);
assert_eq!(MorphTarget::Noise, MorphTarget::Noise);
assert_eq!(MorphTarget::Whisper, MorphTarget::Whisper);
assert_eq!(MorphTarget::Robot, MorphTarget::Robot);
assert_ne!(MorphTarget::Harmonic, MorphTarget::Noise);
}
}