use stft_rs::prelude::*;
fn generate_tone(freq: f32, duration_samples: usize, sample_rate: f32) -> Vec<f32> {
(0..duration_samples)
.map(|i| {
let t = i as f32 / sample_rate;
(2.0 * std::f32::consts::PI * freq * t).sin()
})
.collect()
}
fn lr_to_ms(left: &[f32], right: &[f32]) -> (Vec<f32>, Vec<f32>) {
assert_eq!(left.len(), right.len());
let mid: Vec<f32> = left.iter().zip(right).map(|(l, r)| (l + r) / 2.0).collect();
let side: Vec<f32> = left.iter().zip(right).map(|(l, r)| (l - r) / 2.0).collect();
(mid, side)
}
fn ms_to_lr(mid: &[f32], side: &[f32]) -> (Vec<f32>, Vec<f32>) {
assert_eq!(mid.len(), side.len());
let left: Vec<f32> = mid.iter().zip(side).map(|(m, s)| m + s).collect();
let right: Vec<f32> = mid.iter().zip(side).map(|(m, s)| m - s).collect();
(left, right)
}
fn main() {
println!("=== Mid/Side Stereo Width Processing Example ===\n");
let sample_rate = 44100.0;
let duration = 1.0;
let samples = (sample_rate * duration) as usize;
let config = StftConfigF32::default_4096();
let stft = BatchStftF32::new(config.clone());
let istft = BatchIstftF32::new(config);
println!("Step 1: Create stereo test signal");
println!("----------------------------------");
let left = generate_tone(220.0, samples, sample_rate);
let right = generate_tone(440.0, samples, sample_rate);
println!("Created stereo signal:");
println!(" Left: 220 Hz tone");
println!(" Right: 440 Hz tone\n");
println!("Step 2: Convert L/R to Mid/Side");
println!("--------------------------------");
let (mid, side) = lr_to_ms(&left, &right);
println!("Mid/Side encoding:");
println!(" Mid = (L + R) / 2 (mono sum - center information)");
println!(" Side = (L - R) / 2 (stereo difference - width information)");
println!("Mid contains: Both 220 Hz and 440 Hz (center)");
println!("Side contains: Difference between L and R (stereo width)\n");
println!("Step 3: Process Mid/Side with STFT");
println!("-----------------------------------");
let channels = vec![mid, side];
let spectra = stft.process_multichannel(&channels);
println!("Created {} spectra (mid and side)", spectra.len());
println!(
"Each spectrum: {} frames × {} bins\n",
spectra[0].num_frames, spectra[0].freq_bins
);
println!("Example A: Widen stereo image (boost side by 50%)");
println!("--------------------------------------------------");
let mid_spec = spectra[0].clone();
let mut side_spec = spectra[1].clone();
side_spec.apply_gain(0..side_spec.freq_bins, 1.5);
println!("Applied 1.5× gain to side channel");
println!("Effect: Stereo image will be 50% wider\n");
let processed = istft.process_multichannel(&[mid_spec, side_spec]);
let (left_wide, _right_wide) = ms_to_lr(&processed[0], &processed[1]);
println!("Reconstructed wider stereo:");
println!(" Output length: {} samples per channel", left_wide.len());
println!(" Stereo width: 150% of original\n");
println!("Example B: Narrow stereo image (reduce side by 50%)");
println!("----------------------------------------------------");
let mid_spec = spectra[0].clone();
let mut side_spec = spectra[1].clone();
side_spec.apply_gain(0..side_spec.freq_bins, 0.5);
println!("Applied 0.5× gain to side channel");
println!("Effect: Stereo image will be 50% narrower\n");
let processed = istft.process_multichannel(&[mid_spec, side_spec]);
let (left_narrow, _right_narrow) = ms_to_lr(&processed[0], &processed[1]);
println!("Reconstructed narrower stereo:");
println!(" Output length: {} samples per channel", left_narrow.len());
println!(" Stereo width: 50% of original\n");
println!("Example C: Convert to mono (zero side channel)");
println!("-----------------------------------------------");
let mid_spec = spectra[0].clone();
let mut side_spec = spectra[1].clone();
side_spec.zero_bins(0..side_spec.freq_bins);
println!("Zeroed side channel completely");
println!("Effect: Result is mono (L = R)\n");
let processed = istft.process_multichannel(&[mid_spec, side_spec]);
let (left_mono, _right_mono) = ms_to_lr(&processed[0], &processed[1]);
println!("Reconstructed mono (from mid channel only):");
println!(" Output length: {} samples per channel", left_mono.len());
println!(" Left and right channels are identical\n");
println!("Example D: Frequency-dependent stereo width");
println!("--------------------------------------------");
let mid_spec = spectra[0].clone();
let mut side_spec = spectra[1].clone();
let freq_bins = side_spec.freq_bins;
let crossover_bin = freq_bins / 4;
side_spec.apply_gain(0..crossover_bin, 0.3);
side_spec.apply_gain(crossover_bin..freq_bins, 1.8);
println!("Applied frequency-dependent gain to side channel:");
println!(" Low frequencies: 0.3× (narrower)");
println!(" High frequencies: 1.8× (wider)");
println!("Effect: Tight bass, wide highs\n");
let processed = istft.process_multichannel(&[mid_spec, side_spec]);
let (left_split, _right_split) = ms_to_lr(&processed[0], &processed[1]);
println!("Reconstructed frequency-split stereo:");
println!(" Output length: {} samples per channel", left_split.len());
println!(" Bass is narrower, highs are wider\n");
println!("Summary");
println!("-------");
println!("✓ Converted L/R to Mid/Side format");
println!("✓ Processed mid and side channels independently");
println!("✓ Widened stereo (1.5× side gain)");
println!("✓ Narrowed stereo (0.5× side gain)");
println!("✓ Created mono (zero side)");
println!("✓ Applied frequency-dependent width");
println!("\nMid/Side processing is a powerful technique for stereo manipulation!");
println!("\nUse cases:");
println!(" - Stereo width enhancement");
println!(" - Mono compatibility checking");
println!(" - Separate processing of center vs. sides");
println!(" - Mastering and mixing applications");
}