use alloc::vec;
use alloc::vec::Vec;
use serde::{Deserialize, Serialize};
use crate::error::Result;
use crate::vocalization::{CallIntent, Vocalization};
use crate::voice::CreatureVoice;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CallElement {
pub vocalization: Vocalization,
pub duration: f32,
pub gap: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CallBout {
pub vocalization: Vocalization,
pub count: u32,
pub call_duration: f32,
pub interval: f32,
pub intent: CallIntent,
}
impl CallBout {
pub fn synthesize(&self, voice: &CreatureVoice, sample_rate: f32) -> Result<Vec<f32>> {
let gap_samples = (self.interval * sample_rate) as usize;
let mut output = Vec::new();
for i in 0..self.count {
let call = voice.vocalize_with_intent(
&self.vocalization,
self.intent,
sample_rate,
self.call_duration,
)?;
output.extend_from_slice(&call);
if i < self.count - 1 {
output.resize(output.len() + gap_samples, 0.0);
}
}
Ok(output)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CallPhrase {
pub elements: Vec<CallElement>,
pub intent: CallIntent,
}
impl CallPhrase {
pub fn synthesize(&self, voice: &CreatureVoice, sample_rate: f32) -> Result<Vec<f32>> {
let mut output = Vec::new();
for element in &self.elements {
let call = voice.vocalize_with_intent(
&element.vocalization,
self.intent,
sample_rate,
element.duration,
)?;
output.extend_from_slice(&call);
let gap_samples = (element.gap * sample_rate) as usize;
output.resize(output.len() + gap_samples, 0.0);
}
Ok(output)
}
}
pub fn synthesize_chorus(
voices: &[CreatureVoice],
vocalization: &Vocalization,
intent: CallIntent,
sample_rate: f32,
duration: f32,
timing_spread: f32,
) -> Result<Vec<f32>> {
if voices.is_empty() {
return Ok(Vec::new());
}
let max_offset_samples = (timing_spread * sample_rate) as usize;
let base_samples = (duration * sample_rate) as usize;
let total_len = base_samples + max_offset_samples * 2;
let mut mix = vec![0.0f32; total_len];
let mut rng = crate::rng::Rng::new(voices.len() as u64 * 7919);
let voice_count = voices.len() as f32;
for voice in voices {
let call = voice.vocalize_with_intent(vocalization, intent, sample_rate, duration)?;
let offset = (rng.next_u32() as usize) % (max_offset_samples.max(1) * 2);
for (i, &s) in call.iter().enumerate() {
let idx = offset + i;
if idx < total_len {
mix[idx] += s;
}
}
}
let scale = 1.0 / voice_count.sqrt();
for s in &mut mix {
*s *= scale;
}
Ok(mix)
}