use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use crate::tts::TTS_SAMPLE_RATE;
const VOICES: usize = 4;
const FADE_S: f32 = 0.15;
const PROGRESSION: [[f32; VOICES]; 4] = [
[261.63, 329.63, 392.00, 523.25], [220.00, 261.63, 329.63, 440.00], [174.61, 220.00, 261.63, 349.23], [196.00, 246.94, 293.66, 392.00], ];
pub struct HoldMusic {
t: u64,
active: Arc<AtomicBool>,
}
impl HoldMusic {
pub fn new(active: Arc<AtomicBool>) -> Self {
Self { t: 0, active }
}
pub fn generate(&mut self, max_samples: usize) -> Vec<f32> {
let active = self.active.load(Ordering::Relaxed);
let mut out = Vec::with_capacity(max_samples);
let rate = TTS_SAMPLE_RATE as f64;
let fade_samples = (FADE_S * TTS_SAMPLE_RATE as f32) as usize;
let chord_dur_samples = (2.8 * rate) as u64;
let n = max_samples.min(4096);
for i in 0..n {
let sample_t = self.t + i as u64;
let chord_idx = ((sample_t / chord_dur_samples) % 4) as usize;
let freqs = PROGRESSION[chord_idx];
let fade_pos = (sample_t % chord_dur_samples) as usize;
let crossfade: f32 = if fade_pos < fade_samples {
fade_pos as f32 / fade_samples as f32
} else if fade_pos >= chord_dur_samples as usize - fade_samples {
1.0 - (fade_pos - (chord_dur_samples as usize - fade_samples)) as f32
/ fade_samples as f32
} else {
1.0
};
let prev_idx = if chord_idx == 0 { 3 } else { chord_idx - 1 };
let prev_freqs = PROGRESSION[prev_idx];
let mut sample: f32 = 0.0;
if active {
let phase = sample_t as f64 * 2.0 * std::f64::consts::PI / rate;
for v in 0..VOICES {
let amp_cur = 0.08 * crossfade;
sample += ((phase * freqs[v] as f64).sin()) as f32 * amp_cur;
let amp_prev = 0.08 * (1.0 - crossfade);
sample += ((phase * prev_freqs[v] as f64).sin()) as f32 * amp_prev;
}
let lfo_phase = sample_t as f64 * 0.3 * 2.0 * std::f64::consts::PI / rate;
let lfo = (lfo_phase.sin() as f32) * 0.3 + 0.7;
sample *= lfo;
}
out.push(sample);
}
self.t += n as u64;
out
}
pub fn elapsed_samples(&self) -> u64 {
self.t
}
pub fn reset(&mut self) {
self.t = 0;
}
}