use std::f64::consts::PI;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BrainwaveState {
Delta,
Theta,
Alpha,
Beta,
Gamma,
}
impl BrainwaveState {
pub fn typical_frequency(&self) -> f64 {
match self {
BrainwaveState::Delta => 2.0,
BrainwaveState::Theta => 6.0,
BrainwaveState::Alpha => 10.0,
BrainwaveState::Beta => 20.0,
BrainwaveState::Gamma => 40.0,
}
}
pub fn description(&self) -> &str {
match self {
BrainwaveState::Delta => "Deep sleep and restorative rest (0.5–4 Hz)",
BrainwaveState::Theta => "Drowsiness, meditation, and creativity (4–8 Hz)",
BrainwaveState::Alpha => "Relaxed alertness and calm focus (8–12 Hz)",
BrainwaveState::Beta => "Active thinking, focus, and problem solving (12–30 Hz)",
BrainwaveState::Gamma => "Higher cognition and peak concentration (30–100 Hz)",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BinauralBeat {
pub carrier_hz: f64,
pub beat_frequency_hz: f64,
}
impl BinauralBeat {
pub fn new(carrier_hz: f64, beat_frequency_hz: f64) -> Self {
Self { carrier_hz, beat_frequency_hz }
}
pub fn left_channel_hz(&self) -> f64 {
self.carrier_hz
}
pub fn right_channel_hz(&self) -> f64 {
self.carrier_hz + self.beat_frequency_hz
}
pub fn for_state(state: BrainwaveState, carrier: f64) -> BinauralBeat {
BinauralBeat::new(carrier, state.typical_frequency())
}
}
pub struct BinauralGenerator;
impl BinauralGenerator {
pub fn generate(
beat: &BinauralBeat,
duration_samples: usize,
sample_rate: u32,
) -> (Vec<f64>, Vec<f64>) {
let sr = sample_rate as f64;
let left_freq = beat.left_channel_hz();
let right_freq = beat.right_channel_hz();
let mut left = Vec::with_capacity(duration_samples);
let mut right = Vec::with_capacity(duration_samples);
for i in 0..duration_samples {
let t = i as f64 / sr;
left.push((2.0 * PI * left_freq * t).sin());
right.push((2.0 * PI * right_freq * t).sin());
}
(left, right)
}
pub fn fade_in_out(channel: &[f64], fade_samples: usize) -> Vec<f64> {
let n = channel.len();
if n == 0 {
return Vec::new();
}
let fade = fade_samples.min(n / 2);
let mut output = channel.to_vec();
for i in 0..fade {
let gain = i as f64 / fade as f64;
output[i] *= gain;
output[n - 1 - i] *= gain;
}
output
}
pub fn isochronic_tone(
freq_hz: f64,
pulse_rate_hz: f64,
duration_samples: usize,
sample_rate: u32,
) -> Vec<f64> {
let sr = sample_rate as f64;
let mut output = Vec::with_capacity(duration_samples);
for i in 0..duration_samples {
let t = i as f64 / sr;
let carrier = (2.0 * PI * freq_hz * t).sin();
let envelope = 0.5 * (1.0 + (2.0 * PI * pulse_rate_hz * t).cos());
output.push(carrier * envelope);
}
output
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_brainwave_typical_frequencies() {
assert!((BrainwaveState::Delta.typical_frequency() - 2.0).abs() < 1e-9);
assert!((BrainwaveState::Theta.typical_frequency() - 6.0).abs() < 1e-9);
assert!((BrainwaveState::Alpha.typical_frequency() - 10.0).abs() < 1e-9);
assert!((BrainwaveState::Beta.typical_frequency() - 20.0).abs() < 1e-9);
assert!((BrainwaveState::Gamma.typical_frequency() - 40.0).abs() < 1e-9);
}
#[test]
fn test_brainwave_descriptions_nonempty() {
for state in [
BrainwaveState::Delta,
BrainwaveState::Theta,
BrainwaveState::Alpha,
BrainwaveState::Beta,
BrainwaveState::Gamma,
] {
assert!(!state.description().is_empty());
}
}
#[test]
fn test_binaural_beat_channels() {
let beat = BinauralBeat::new(200.0, 10.0);
assert_eq!(beat.left_channel_hz(), 200.0);
assert_eq!(beat.right_channel_hz(), 210.0);
}
#[test]
fn test_binaural_beat_for_state() {
let beat = BinauralBeat::for_state(BrainwaveState::Alpha, 200.0);
assert_eq!(beat.carrier_hz, 200.0);
assert!((beat.beat_frequency_hz - 10.0).abs() < 1e-9);
assert_eq!(beat.right_channel_hz(), 210.0);
}
#[test]
fn test_generate_length() {
let beat = BinauralBeat::new(200.0, 10.0);
let (left, right) = BinauralGenerator::generate(&beat, 1024, 44100);
assert_eq!(left.len(), 1024);
assert_eq!(right.len(), 1024);
}
#[test]
fn test_generate_amplitude_bounded() {
let beat = BinauralBeat::new(440.0, 5.0);
let (left, right) = BinauralGenerator::generate(&beat, 4410, 44100);
for &s in left.iter().chain(right.iter()) {
assert!(s >= -1.0 && s <= 1.0, "sample {} out of [-1, 1]", s);
}
}
#[test]
fn test_generate_channels_differ() {
let beat = BinauralBeat::new(200.0, 10.0);
let (left, right) = BinauralGenerator::generate(&beat, 44100, 44100);
let differs = left.iter().zip(right.iter()).any(|(l, r)| (l - r).abs() > 1e-9);
assert!(differs, "Left and right channels should differ");
}
#[test]
fn test_fade_in_out_edges() {
let samples = vec![1.0_f64; 100];
let faded = BinauralGenerator::fade_in_out(&samples, 10);
assert_eq!(faded.len(), 100);
assert!(faded[0].abs() < 0.2, "first sample should be near 0, got {}", faded[0]);
assert!(faded[99].abs() < 0.2, "last sample should be near 0, got {}", faded[99]);
assert!((faded[50] - 1.0).abs() < 1e-9);
}
#[test]
fn test_fade_in_out_empty() {
let result = BinauralGenerator::fade_in_out(&[], 10);
assert!(result.is_empty());
}
#[test]
fn test_isochronic_tone_length() {
let tone = BinauralGenerator::isochronic_tone(200.0, 10.0, 512, 44100);
assert_eq!(tone.len(), 512);
}
#[test]
fn test_isochronic_tone_bounded() {
let tone = BinauralGenerator::isochronic_tone(440.0, 10.0, 4410, 44100);
for &s in &tone {
assert!(s >= -1.0 && s <= 1.0, "isochronic sample {} out of range", s);
}
}
#[test]
fn test_beat_frequency_ordering() {
assert!(BrainwaveState::Delta.typical_frequency() < BrainwaveState::Theta.typical_frequency());
assert!(BrainwaveState::Theta.typical_frequency() < BrainwaveState::Alpha.typical_frequency());
assert!(BrainwaveState::Alpha.typical_frequency() < BrainwaveState::Beta.typical_frequency());
assert!(BrainwaveState::Beta.typical_frequency() < BrainwaveState::Gamma.typical_frequency());
}
}