Add sample config and the option to speak repeated lines at a faster rate
This commit is contained in:
+81
-11
@@ -40,6 +40,15 @@ pub enum KokoroStatus {
|
||||
pub struct SynthesisJob {
|
||||
pub text: String,
|
||||
pub voice: String,
|
||||
pub speed: f32,
|
||||
pub repeat_key: String,
|
||||
pub observed_at: Instant,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RepeatState {
|
||||
last_seen: Instant,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -145,7 +154,7 @@ pub fn spawn_kokoro_worker(
|
||||
};
|
||||
let mut voices = Vec::new();
|
||||
let mut last_health_check = Instant::now() - Duration::from_secs(30);
|
||||
let mut recent = HashMap::<String, Instant>::new();
|
||||
let mut repeats = HashMap::<String, RepeatState>::new();
|
||||
let mut queued = VecDeque::new();
|
||||
|
||||
loop {
|
||||
@@ -196,26 +205,24 @@ pub fn spawn_kokoro_worker(
|
||||
}
|
||||
|
||||
while let Ok(job) = jobs.try_recv() {
|
||||
let key = format!("{}:{}", job.voice, job.text);
|
||||
let is_duplicate = recent.get(&key).is_some_and(|last| {
|
||||
last.elapsed() < Duration::from_secs(config.dedup_window_seconds)
|
||||
});
|
||||
if is_duplicate {
|
||||
let _ =
|
||||
event_sender.send(WorkerEvent::Activity("duplicate line skipped".into()));
|
||||
} else if queued.len() >= config.max_queue_length {
|
||||
if queued.len() >= config.max_queue_length {
|
||||
let _ = event_sender.send(WorkerEvent::Activity(
|
||||
"speech queue full; line dropped".into(),
|
||||
));
|
||||
} else {
|
||||
recent.insert(key, Instant::now());
|
||||
let mut job = job;
|
||||
job.speed =
|
||||
repeat_speed(&mut repeats, &job.repeat_key, &config, job.observed_at);
|
||||
queued.push_back(job);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(job) = queued.pop_front() {
|
||||
let _ = event_sender.send(WorkerEvent::QueueDepth(queued.len() + 1));
|
||||
match client.synthesize_wav(&job.text, &job.voice).await {
|
||||
match client
|
||||
.synthesize_wav(&job.text, &job.voice, job.speed)
|
||||
.await
|
||||
{
|
||||
Ok(wav) => {
|
||||
if audio_sender.send(AudioCommand::Play(wav)).is_err() {
|
||||
let _ = event_sender.send(WorkerEvent::Activity(
|
||||
@@ -238,6 +245,30 @@ pub fn spawn_kokoro_worker(
|
||||
control_sender
|
||||
}
|
||||
|
||||
fn repeat_speed(
|
||||
repeats: &mut HashMap<String, RepeatState>,
|
||||
key: &str,
|
||||
config: &Config,
|
||||
now: Instant,
|
||||
) -> f32 {
|
||||
let window = Duration::from_secs(config.repeat_window_seconds);
|
||||
let state = repeats.entry(key.to_owned()).or_insert(RepeatState {
|
||||
last_seen: now,
|
||||
count: 0,
|
||||
});
|
||||
if now.duration_since(state.last_seen) >= window {
|
||||
state.count = 0;
|
||||
}
|
||||
state.last_seen = now;
|
||||
state.count += 1;
|
||||
|
||||
match state.count {
|
||||
1 => 1.0,
|
||||
2 => config.repeat_second_speed.clamp(0.25, 4.0),
|
||||
_ => config.repeat_third_plus_speed.clamp(0.25, 4.0),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -256,4 +287,43 @@ mod tests {
|
||||
fs::write(&path, "fresh\n").unwrap();
|
||||
assert_eq!(read_new_lines(&path, &mut position).unwrap(), vec!["fresh"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_dialogue_speeds_up_then_resets() {
|
||||
let config = Config::default();
|
||||
let now = Instant::now();
|
||||
let mut repeats = HashMap::new();
|
||||
|
||||
assert_eq!(
|
||||
repeat_speed(&mut repeats, "npc:turga\u{1f}hello", &config, now),
|
||||
1.0
|
||||
);
|
||||
assert_eq!(
|
||||
repeat_speed(
|
||||
&mut repeats,
|
||||
"npc:turga\u{1f}hello",
|
||||
&config,
|
||||
now + Duration::from_secs(1),
|
||||
),
|
||||
1.5
|
||||
);
|
||||
assert_eq!(
|
||||
repeat_speed(
|
||||
&mut repeats,
|
||||
"npc:turga\u{1f}hello",
|
||||
&config,
|
||||
now + Duration::from_secs(2),
|
||||
),
|
||||
2.0
|
||||
);
|
||||
assert_eq!(
|
||||
repeat_speed(
|
||||
&mut repeats,
|
||||
"npc:turga\u{1f}hello",
|
||||
&config,
|
||||
now + Duration::from_secs(18),
|
||||
),
|
||||
1.0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user