better tts a little

This commit is contained in:
legop3 2026-06-14 05:14:28 -04:00
parent fb8bd8e738
commit 2630ca154f
3 changed files with 63 additions and 7 deletions

View file

@ -72,9 +72,10 @@ timeout_seconds = 10
max_attempts = 3
```
The menu uses `espeak-ng` for local text-to-speech. Press `#` to repeat the
current menu page, `9` for the next page when available, and `*` for the
previous page when available.
The menu uses `espeak-ng` for local text-to-speech with a female English voice.
Emoji and common Discord channel separators are skipped in spoken names. Press
`#` to repeat the current menu page, `9` for the next page when available, and
`*` for the previous page when available.
### 4a. Run with Docker (recommended)

View file

@ -1594,7 +1594,7 @@ async fn select_guild_from_menu(
let prompt = build_option_prompt(
"Select a Discord server.",
page_items,
|guild| guild.name.as_str(),
|guild| clean_tts_label(&guild.name),
page,
guilds.len(),
);
@ -1651,11 +1651,11 @@ async fn select_channel_from_menu(
let mut attempts = 0u8;
loop {
let page_items = page_slice(channels, page);
let intro = format!("Select a voice channel in {}.", guild.name);
let intro = format!("Select a voice channel in {}.", clean_tts_label(&guild.name));
let prompt = build_option_prompt(
&intro,
page_items,
|channel| channel.name.as_str(),
|channel| clean_tts_label(&channel.name),
page,
channels.len(),
);
@ -1716,7 +1716,7 @@ fn has_next_page(total: usize, page: usize) -> bool {
fn build_option_prompt<T>(
intro: &str,
items: &[T],
label: impl Fn(&T) -> &str,
label: impl Fn(&T) -> String,
page: usize,
total: usize,
) -> String {
@ -1734,6 +1734,54 @@ fn build_option_prompt<T>(
prompt
}
fn clean_tts_label(label: &str) -> String {
let mut out = String::new();
let mut last_was_space = false;
for ch in label.chars() {
let replacement = if is_tts_skipped_symbol(ch) || ch.is_control() {
Some(' ')
} else {
match ch {
'_' | '-' | '|' | '/' | '\\' | ':' | ';' | ',' | '.' | '#' | '[' | ']' | '('
| ')' | '{' | '}' => Some(' '),
_ => Some(ch),
}
};
if let Some(ch) = replacement {
if ch.is_whitespace() {
if !last_was_space {
out.push(' ');
last_was_space = true;
}
} else {
out.push(ch);
last_was_space = false;
}
}
}
let cleaned = out.trim();
if cleaned.is_empty() {
"unnamed".to_string()
} else {
cleaned.to_string()
}
}
fn is_tts_skipped_symbol(ch: char) -> bool {
matches!(
ch as u32,
0x200D
| 0x20E3
| 0xFE00..=0xFE0F
| 0x2500..=0x257F
| 0x2600..=0x27BF
| 0x1F000..=0x1FAFF
)
}
async fn wait_for_menu_digit(
call_id: CallId,
menu: &MenuRoute,
@ -1846,6 +1894,8 @@ async fn synthesize_tts_samples(
let out_path = std::env::temp_dir().join(format!("sipcord-tts-{}-{}.wav", call_id, stamp));
let espeak_status = Command::new("espeak-ng")
.arg("-v")
.arg("en+f3")
.arg("-w")
.arg(&raw_path)
.arg(text)

5
todo Normal file
View file

@ -0,0 +1,5 @@
call status feedback in discord
simple directory in toml config with exts to call / labels
OR
build something into the IVR that uses your CID and extension to register your phone into the directory?