pub struct Speech { /* private fields */ }Expand description
Speech and braille output. Reach it through ctx.speech.
Implementations§
Source§impl Speech
impl Speech
Sourcepub fn console() -> Self
pub fn console() -> Self
Speech that only prints to the console. Useful for tests and headless tools.
Sourcepub fn speak(&mut self, text: impl AsRef<str>)
pub fn speak(&mut self, text: impl AsRef<str>)
Speaks text, interrupting whatever is being said. This is what you want most of the time.
Examples found in repository?
More examples
Sourcepub fn speak_queued(&mut self, text: impl AsRef<str>)
pub fn speak_queued(&mut self, text: impl AsRef<str>)
Speaks text after the current speech has finished.
Examples found in repository?
50 fn on_ui(&mut self, event: &UiEvent, ctx: &mut Context) {
51 match event {
52 UiEvent::Activated { menu, item } => match (menu.as_str(), item.as_str()) {
53 ("main", "play") => {
54 let s = ctx.get::<Settings>().expect("settings exist");
55 let text = format!("Good luck, {}! (this is where your game would start)", s.name);
56 ctx.ui.message("intro", text);
57 }
58 ("main", "options") => {
59 let menu = options_menu(ctx.get::<Settings>().expect("settings exist"));
60 ctx.ui.open(menu);
61 }
62 ("main", "name") => ctx.ui.open(TextInput::new("name", "Enter your name").max_length(20).required()),
63 ("main", "quit") => ctx.ui.open(Confirm::new("quit", "Do you really want to quit?").default_no()),
64 ("options", "back") => ctx.ui.close(),
65 _ => {}
66 },
67 UiEvent::Changed { menu, item, value } if menu == "options" => {
68 let s = ctx.get_mut::<Settings>().expect("settings exist");
69 match item.as_str() {
70 "volume" => s.volume = value.as_f32() / 100.0,
71 "difficulty" => s.difficulty = value.as_index(),
72 "name" => s.name = value.as_str().to_string(),
73 _ => {}
74 }
75 let volume = s.volume;
76 ctx.audio.set_master_volume(volume);
77 }
78 UiEvent::TextSubmitted { id, text } if id == "name" => {
79 ctx.get_mut::<Settings>().expect("settings exist").name = text.clone();
80 ctx.speech.speak_queued(format!("Hello, {text}"));
81 }
82 UiEvent::Confirmed { id, yes: true } if id == "quit" => ctx.quit(),
83 _ => {}
84 }
85 }More examples
92 fn on_ui(&mut self, event: &UiEvent, ctx: &mut Context) {
93 match event {
94 UiEvent::Activated { menu, item } if menu == "title" => match item.as_str() {
95 "play" => {
96 ctx.ui.close_all();
97 ctx.switch_scene(Game::default());
98 }
99 "voice" => match voice_menu(ctx) {
100 Some(menu) => ctx.ui.open(menu),
101 None => ctx.ui.message(
102 "no voices",
103 "Your screen reader speaks with its own voice. Change it in the screen reader's settings.",
104 ),
105 },
106 "help" => ctx.ui.message(
107 "help",
108 "Bells fall in one of seven lanes in front of you. Listen where a bell rings, \
109 step under it with left and right, and catch it before it lands. \
110 The bell rings faster and higher the closer it gets. You have three lives. \
111 Press S for your score, Escape for the menu.",
112 ),
113 "quit" => ctx.quit(),
114 _ => {}
115 },
116 UiEvent::Activated { menu, item } if menu == "voices" => {
117 if item == "back" {
118 ctx.ui.close();
119 return;
120 }
121 if item == "default" {
122 ctx.speech.reset_voice();
123 ctx.storage.remove("voice");
124 } else if let Some(voice) =
125 item.strip_prefix("voice").and_then(|i| i.parse::<usize>().ok()).and_then(|i| ctx.speech.voices().get(i).cloned())
126 && ctx.speech.set_voice(&voice.id)
127 {
128 ctx.storage.set("voice", &voice.id);
129 }
130 ctx.ui.close();
131 refresh_title(ctx, "voice");
132 let name = ctx.speech.voice().map_or("Default voice".to_string(), |v| v.name.clone());
133 ctx.speech.speak_queued(format!("{name}. This is how I sound now."));
134 }
135 UiEvent::Changed { menu, item, value } if menu == "title" && item == "rate" => {
136 ctx.speech.set_rate(value.as_f32() / 100.0);
137 ctx.storage.set("rate", value.as_f32());
138 }
139 _ => {}
140 }
141 }Sourcepub fn repeat_last(&mut self)
pub fn repeat_last(&mut self)
Speaks the last message again. Bind this to a key so players can catch up.
Examples found in repository?
14 fn update(&mut self, ctx: &mut Context) {
15 if ctx.input.pressed(Key::Space) {
16 // A pitch that rises over time, so every beep sounds different.
17 let pitch = 300.0 + (ctx.time.elapsed() as f32 * 50.0) % 900.0;
18 ctx.audio.play_tone(pitch.round(), 0.15);
19 }
20 if ctx.input.pressed(Key::F1) {
21 ctx.speech.repeat_last();
22 }
23 if ctx.input.pressed(Key::Escape) {
24 ctx.quit();
25 }
26 }Sourcepub fn set_rate(&mut self, rate: f32)
pub fn set_rate(&mut self, rate: f32)
Sets the speech rate if the output supports it, from 0.0 (slowest) to 1.0 (fastest), passed on to the voice as is. Screen readers usually ignore this and keep the user’s own settings.
Examples found in repository?
22fn main() -> audio_code::Result<()> {
23 Engine::new("Bell Catcher").run_with(|ctx| {
24 ctx.audio.register("bell", Synth::sine(1568.0).with(Synth::sine(3136.0).volume(0.3)).length(0.35).decay(9.0).volume(0.6));
25 ctx.audio.register("step", Synth::noise().length(0.05).decay(80.0).volume(0.5));
26 ctx.audio.register("edge", Synth::sine(110.0).length(0.12).decay(30.0).volume(0.8));
27 ctx.audio.register(
28 "catch",
29 Synth::sine(784.0).length(0.08).then(Synth::sine(1047.0).length(0.08)).then(Synth::sine(1568.0).length(0.2).decay(12.0)).volume(0.4),
30 );
31 ctx.audio.register("miss", Synth::saw(220.0).sweep_to(80.0).length(0.5).decay(5.0).volume(0.35));
32
33 // Restore the speech settings from the last session.
34 if let Some(voice) = ctx.storage.get::<String>("voice") {
35 ctx.speech.set_voice(voice);
36 }
37 let rate: f32 = ctx.storage.get_or("rate", 50.0);
38 if rate != 50.0 {
39 ctx.speech.set_rate(rate / 100.0);
40 }
41 Title
42 })
43}
44
45// ------------------------------------------------------------------------- menu
46
47struct Title;
48
49fn title_menu(ctx: &Context) -> Menu {
50 let voice = ctx.speech.voice().map_or("default".to_string(), |v| v.name.clone());
51 let best: u32 = ctx.storage.get_or("best", 0);
52 Menu::new("title", "Bell Catcher")
53 .button("play", "Play")
54 .button("voice", format!("Voice: {voice}"))
55 .slider("rate", "Speech rate", ctx.storage.get_or("rate", 50.0), 0.0, 100.0, 5.0)
56 .button("help", "How to play")
57 .label(format!("Best score: {best}"))
58 .button("quit", "Quit")
59 .closable(false)
60 .wrap(true)
61}
62
63/// One button per voice, with the current one focused. `None` if the output has no voices.
64fn voice_menu(ctx: &Context) -> Option<Menu> {
65 let voices = ctx.speech.voices();
66 if voices.is_empty() {
67 return None;
68 }
69 let mut menu = Menu::new("voices", "Choose a voice").button("default", "Default voice");
70 for (i, voice) in voices.iter().enumerate() {
71 menu = menu.button(format!("voice{i}"), voice.to_string());
72 }
73 if let Some(i) = ctx.speech.voice().and_then(|current| voices.iter().position(|v| v == current)) {
74 menu = menu.focus(&format!("voice{i}"));
75 }
76 Some(menu.button("back", "Back"))
77}
78
79/// Reopens the title menu so its labels show the new values, focused on `item`.
80fn refresh_title(ctx: &mut Context, item: &str) {
81 ctx.ui.close_id("title");
82 let menu = title_menu(ctx).focus(item);
83 ctx.ui.open(menu);
84}
85
86impl Scene for Title {
87 fn start(&mut self, ctx: &mut Context) {
88 let menu = title_menu(ctx);
89 ctx.ui.open(menu);
90 }
91
92 fn on_ui(&mut self, event: &UiEvent, ctx: &mut Context) {
93 match event {
94 UiEvent::Activated { menu, item } if menu == "title" => match item.as_str() {
95 "play" => {
96 ctx.ui.close_all();
97 ctx.switch_scene(Game::default());
98 }
99 "voice" => match voice_menu(ctx) {
100 Some(menu) => ctx.ui.open(menu),
101 None => ctx.ui.message(
102 "no voices",
103 "Your screen reader speaks with its own voice. Change it in the screen reader's settings.",
104 ),
105 },
106 "help" => ctx.ui.message(
107 "help",
108 "Bells fall in one of seven lanes in front of you. Listen where a bell rings, \
109 step under it with left and right, and catch it before it lands. \
110 The bell rings faster and higher the closer it gets. You have three lives. \
111 Press S for your score, Escape for the menu.",
112 ),
113 "quit" => ctx.quit(),
114 _ => {}
115 },
116 UiEvent::Activated { menu, item } if menu == "voices" => {
117 if item == "back" {
118 ctx.ui.close();
119 return;
120 }
121 if item == "default" {
122 ctx.speech.reset_voice();
123 ctx.storage.remove("voice");
124 } else if let Some(voice) =
125 item.strip_prefix("voice").and_then(|i| i.parse::<usize>().ok()).and_then(|i| ctx.speech.voices().get(i).cloned())
126 && ctx.speech.set_voice(&voice.id)
127 {
128 ctx.storage.set("voice", &voice.id);
129 }
130 ctx.ui.close();
131 refresh_title(ctx, "voice");
132 let name = ctx.speech.voice().map_or("Default voice".to_string(), |v| v.name.clone());
133 ctx.speech.speak_queued(format!("{name}. This is how I sound now."));
134 }
135 UiEvent::Changed { menu, item, value } if menu == "title" && item == "rate" => {
136 ctx.speech.set_rate(value.as_f32() / 100.0);
137 ctx.storage.set("rate", value.as_f32());
138 }
139 _ => {}
140 }
141 }Sourcepub fn set_volume(&mut self, volume: f32)
pub fn set_volume(&mut self, volume: f32)
Sets the speech volume if the output supports it (0.0 to 1.0).
Sourcepub fn voices(&self) -> Vec<Voice>
pub fn voices(&self) -> Vec<Voice>
The voices of the system voice output, sorted by language and name, for example to build a voice menu. Empty when the output has no choice of voices: screen readers (NVDA, VoiceOver, …) speak with the voice the player set up there, and the console has none.
Right after the game starts this waits until the output is ready (at most a few seconds); after that it returns immediately.
for voice in ctx.speech.voices() {
println!("{} {}", voice.name, voice.language.as_deref().unwrap_or("?"));
}Examples found in repository?
64fn voice_menu(ctx: &Context) -> Option<Menu> {
65 let voices = ctx.speech.voices();
66 if voices.is_empty() {
67 return None;
68 }
69 let mut menu = Menu::new("voices", "Choose a voice").button("default", "Default voice");
70 for (i, voice) in voices.iter().enumerate() {
71 menu = menu.button(format!("voice{i}"), voice.to_string());
72 }
73 if let Some(i) = ctx.speech.voice().and_then(|current| voices.iter().position(|v| v == current)) {
74 menu = menu.focus(&format!("voice{i}"));
75 }
76 Some(menu.button("back", "Back"))
77}
78
79/// Reopens the title menu so its labels show the new values, focused on `item`.
80fn refresh_title(ctx: &mut Context, item: &str) {
81 ctx.ui.close_id("title");
82 let menu = title_menu(ctx).focus(item);
83 ctx.ui.open(menu);
84}
85
86impl Scene for Title {
87 fn start(&mut self, ctx: &mut Context) {
88 let menu = title_menu(ctx);
89 ctx.ui.open(menu);
90 }
91
92 fn on_ui(&mut self, event: &UiEvent, ctx: &mut Context) {
93 match event {
94 UiEvent::Activated { menu, item } if menu == "title" => match item.as_str() {
95 "play" => {
96 ctx.ui.close_all();
97 ctx.switch_scene(Game::default());
98 }
99 "voice" => match voice_menu(ctx) {
100 Some(menu) => ctx.ui.open(menu),
101 None => ctx.ui.message(
102 "no voices",
103 "Your screen reader speaks with its own voice. Change it in the screen reader's settings.",
104 ),
105 },
106 "help" => ctx.ui.message(
107 "help",
108 "Bells fall in one of seven lanes in front of you. Listen where a bell rings, \
109 step under it with left and right, and catch it before it lands. \
110 The bell rings faster and higher the closer it gets. You have three lives. \
111 Press S for your score, Escape for the menu.",
112 ),
113 "quit" => ctx.quit(),
114 _ => {}
115 },
116 UiEvent::Activated { menu, item } if menu == "voices" => {
117 if item == "back" {
118 ctx.ui.close();
119 return;
120 }
121 if item == "default" {
122 ctx.speech.reset_voice();
123 ctx.storage.remove("voice");
124 } else if let Some(voice) =
125 item.strip_prefix("voice").and_then(|i| i.parse::<usize>().ok()).and_then(|i| ctx.speech.voices().get(i).cloned())
126 && ctx.speech.set_voice(&voice.id)
127 {
128 ctx.storage.set("voice", &voice.id);
129 }
130 ctx.ui.close();
131 refresh_title(ctx, "voice");
132 let name = ctx.speech.voice().map_or("Default voice".to_string(), |v| v.name.clone());
133 ctx.speech.speak_queued(format!("{name}. This is how I sound now."));
134 }
135 UiEvent::Changed { menu, item, value } if menu == "title" && item == "rate" => {
136 ctx.speech.set_rate(value.as_f32() / 100.0);
137 ctx.storage.set("rate", value.as_f32());
138 }
139 _ => {}
140 }
141 }Sourcepub fn set_voice(&mut self, voice: impl AsRef<str>) -> bool
pub fn set_voice(&mut self, voice: impl AsRef<str>) -> bool
Switches the system voice. voice is a Voice::id or a voice name (any case),
e.g. "Anna". Returns false and keeps the current voice if no such voice exists.
Screen readers ignore this, see Speech::voices.
Examples found in repository?
22fn main() -> audio_code::Result<()> {
23 Engine::new("Bell Catcher").run_with(|ctx| {
24 ctx.audio.register("bell", Synth::sine(1568.0).with(Synth::sine(3136.0).volume(0.3)).length(0.35).decay(9.0).volume(0.6));
25 ctx.audio.register("step", Synth::noise().length(0.05).decay(80.0).volume(0.5));
26 ctx.audio.register("edge", Synth::sine(110.0).length(0.12).decay(30.0).volume(0.8));
27 ctx.audio.register(
28 "catch",
29 Synth::sine(784.0).length(0.08).then(Synth::sine(1047.0).length(0.08)).then(Synth::sine(1568.0).length(0.2).decay(12.0)).volume(0.4),
30 );
31 ctx.audio.register("miss", Synth::saw(220.0).sweep_to(80.0).length(0.5).decay(5.0).volume(0.35));
32
33 // Restore the speech settings from the last session.
34 if let Some(voice) = ctx.storage.get::<String>("voice") {
35 ctx.speech.set_voice(voice);
36 }
37 let rate: f32 = ctx.storage.get_or("rate", 50.0);
38 if rate != 50.0 {
39 ctx.speech.set_rate(rate / 100.0);
40 }
41 Title
42 })
43}
44
45// ------------------------------------------------------------------------- menu
46
47struct Title;
48
49fn title_menu(ctx: &Context) -> Menu {
50 let voice = ctx.speech.voice().map_or("default".to_string(), |v| v.name.clone());
51 let best: u32 = ctx.storage.get_or("best", 0);
52 Menu::new("title", "Bell Catcher")
53 .button("play", "Play")
54 .button("voice", format!("Voice: {voice}"))
55 .slider("rate", "Speech rate", ctx.storage.get_or("rate", 50.0), 0.0, 100.0, 5.0)
56 .button("help", "How to play")
57 .label(format!("Best score: {best}"))
58 .button("quit", "Quit")
59 .closable(false)
60 .wrap(true)
61}
62
63/// One button per voice, with the current one focused. `None` if the output has no voices.
64fn voice_menu(ctx: &Context) -> Option<Menu> {
65 let voices = ctx.speech.voices();
66 if voices.is_empty() {
67 return None;
68 }
69 let mut menu = Menu::new("voices", "Choose a voice").button("default", "Default voice");
70 for (i, voice) in voices.iter().enumerate() {
71 menu = menu.button(format!("voice{i}"), voice.to_string());
72 }
73 if let Some(i) = ctx.speech.voice().and_then(|current| voices.iter().position(|v| v == current)) {
74 menu = menu.focus(&format!("voice{i}"));
75 }
76 Some(menu.button("back", "Back"))
77}
78
79/// Reopens the title menu so its labels show the new values, focused on `item`.
80fn refresh_title(ctx: &mut Context, item: &str) {
81 ctx.ui.close_id("title");
82 let menu = title_menu(ctx).focus(item);
83 ctx.ui.open(menu);
84}
85
86impl Scene for Title {
87 fn start(&mut self, ctx: &mut Context) {
88 let menu = title_menu(ctx);
89 ctx.ui.open(menu);
90 }
91
92 fn on_ui(&mut self, event: &UiEvent, ctx: &mut Context) {
93 match event {
94 UiEvent::Activated { menu, item } if menu == "title" => match item.as_str() {
95 "play" => {
96 ctx.ui.close_all();
97 ctx.switch_scene(Game::default());
98 }
99 "voice" => match voice_menu(ctx) {
100 Some(menu) => ctx.ui.open(menu),
101 None => ctx.ui.message(
102 "no voices",
103 "Your screen reader speaks with its own voice. Change it in the screen reader's settings.",
104 ),
105 },
106 "help" => ctx.ui.message(
107 "help",
108 "Bells fall in one of seven lanes in front of you. Listen where a bell rings, \
109 step under it with left and right, and catch it before it lands. \
110 The bell rings faster and higher the closer it gets. You have three lives. \
111 Press S for your score, Escape for the menu.",
112 ),
113 "quit" => ctx.quit(),
114 _ => {}
115 },
116 UiEvent::Activated { menu, item } if menu == "voices" => {
117 if item == "back" {
118 ctx.ui.close();
119 return;
120 }
121 if item == "default" {
122 ctx.speech.reset_voice();
123 ctx.storage.remove("voice");
124 } else if let Some(voice) =
125 item.strip_prefix("voice").and_then(|i| i.parse::<usize>().ok()).and_then(|i| ctx.speech.voices().get(i).cloned())
126 && ctx.speech.set_voice(&voice.id)
127 {
128 ctx.storage.set("voice", &voice.id);
129 }
130 ctx.ui.close();
131 refresh_title(ctx, "voice");
132 let name = ctx.speech.voice().map_or("Default voice".to_string(), |v| v.name.clone());
133 ctx.speech.speak_queued(format!("{name}. This is how I sound now."));
134 }
135 UiEvent::Changed { menu, item, value } if menu == "title" && item == "rate" => {
136 ctx.speech.set_rate(value.as_f32() / 100.0);
137 ctx.storage.set("rate", value.as_f32());
138 }
139 _ => {}
140 }
141 }Sourcepub fn reset_voice(&mut self)
pub fn reset_voice(&mut self)
Goes back to the default voice, the one the game started with.
Examples found in repository?
92 fn on_ui(&mut self, event: &UiEvent, ctx: &mut Context) {
93 match event {
94 UiEvent::Activated { menu, item } if menu == "title" => match item.as_str() {
95 "play" => {
96 ctx.ui.close_all();
97 ctx.switch_scene(Game::default());
98 }
99 "voice" => match voice_menu(ctx) {
100 Some(menu) => ctx.ui.open(menu),
101 None => ctx.ui.message(
102 "no voices",
103 "Your screen reader speaks with its own voice. Change it in the screen reader's settings.",
104 ),
105 },
106 "help" => ctx.ui.message(
107 "help",
108 "Bells fall in one of seven lanes in front of you. Listen where a bell rings, \
109 step under it with left and right, and catch it before it lands. \
110 The bell rings faster and higher the closer it gets. You have three lives. \
111 Press S for your score, Escape for the menu.",
112 ),
113 "quit" => ctx.quit(),
114 _ => {}
115 },
116 UiEvent::Activated { menu, item } if menu == "voices" => {
117 if item == "back" {
118 ctx.ui.close();
119 return;
120 }
121 if item == "default" {
122 ctx.speech.reset_voice();
123 ctx.storage.remove("voice");
124 } else if let Some(voice) =
125 item.strip_prefix("voice").and_then(|i| i.parse::<usize>().ok()).and_then(|i| ctx.speech.voices().get(i).cloned())
126 && ctx.speech.set_voice(&voice.id)
127 {
128 ctx.storage.set("voice", &voice.id);
129 }
130 ctx.ui.close();
131 refresh_title(ctx, "voice");
132 let name = ctx.speech.voice().map_or("Default voice".to_string(), |v| v.name.clone());
133 ctx.speech.speak_queued(format!("{name}. This is how I sound now."));
134 }
135 UiEvent::Changed { menu, item, value } if menu == "title" && item == "rate" => {
136 ctx.speech.set_rate(value.as_f32() / 100.0);
137 ctx.storage.set("rate", value.as_f32());
138 }
139 _ => {}
140 }
141 }Sourcepub fn voice(&self) -> Option<&Voice>
pub fn voice(&self) -> Option<&Voice>
The voice chosen with Speech::set_voice, or None for the default voice.
Examples found in repository?
49fn title_menu(ctx: &Context) -> Menu {
50 let voice = ctx.speech.voice().map_or("default".to_string(), |v| v.name.clone());
51 let best: u32 = ctx.storage.get_or("best", 0);
52 Menu::new("title", "Bell Catcher")
53 .button("play", "Play")
54 .button("voice", format!("Voice: {voice}"))
55 .slider("rate", "Speech rate", ctx.storage.get_or("rate", 50.0), 0.0, 100.0, 5.0)
56 .button("help", "How to play")
57 .label(format!("Best score: {best}"))
58 .button("quit", "Quit")
59 .closable(false)
60 .wrap(true)
61}
62
63/// One button per voice, with the current one focused. `None` if the output has no voices.
64fn voice_menu(ctx: &Context) -> Option<Menu> {
65 let voices = ctx.speech.voices();
66 if voices.is_empty() {
67 return None;
68 }
69 let mut menu = Menu::new("voices", "Choose a voice").button("default", "Default voice");
70 for (i, voice) in voices.iter().enumerate() {
71 menu = menu.button(format!("voice{i}"), voice.to_string());
72 }
73 if let Some(i) = ctx.speech.voice().and_then(|current| voices.iter().position(|v| v == current)) {
74 menu = menu.focus(&format!("voice{i}"));
75 }
76 Some(menu.button("back", "Back"))
77}
78
79/// Reopens the title menu so its labels show the new values, focused on `item`.
80fn refresh_title(ctx: &mut Context, item: &str) {
81 ctx.ui.close_id("title");
82 let menu = title_menu(ctx).focus(item);
83 ctx.ui.open(menu);
84}
85
86impl Scene for Title {
87 fn start(&mut self, ctx: &mut Context) {
88 let menu = title_menu(ctx);
89 ctx.ui.open(menu);
90 }
91
92 fn on_ui(&mut self, event: &UiEvent, ctx: &mut Context) {
93 match event {
94 UiEvent::Activated { menu, item } if menu == "title" => match item.as_str() {
95 "play" => {
96 ctx.ui.close_all();
97 ctx.switch_scene(Game::default());
98 }
99 "voice" => match voice_menu(ctx) {
100 Some(menu) => ctx.ui.open(menu),
101 None => ctx.ui.message(
102 "no voices",
103 "Your screen reader speaks with its own voice. Change it in the screen reader's settings.",
104 ),
105 },
106 "help" => ctx.ui.message(
107 "help",
108 "Bells fall in one of seven lanes in front of you. Listen where a bell rings, \
109 step under it with left and right, and catch it before it lands. \
110 The bell rings faster and higher the closer it gets. You have three lives. \
111 Press S for your score, Escape for the menu.",
112 ),
113 "quit" => ctx.quit(),
114 _ => {}
115 },
116 UiEvent::Activated { menu, item } if menu == "voices" => {
117 if item == "back" {
118 ctx.ui.close();
119 return;
120 }
121 if item == "default" {
122 ctx.speech.reset_voice();
123 ctx.storage.remove("voice");
124 } else if let Some(voice) =
125 item.strip_prefix("voice").and_then(|i| i.parse::<usize>().ok()).and_then(|i| ctx.speech.voices().get(i).cloned())
126 && ctx.speech.set_voice(&voice.id)
127 {
128 ctx.storage.set("voice", &voice.id);
129 }
130 ctx.ui.close();
131 refresh_title(ctx, "voice");
132 let name = ctx.speech.voice().map_or("Default voice".to_string(), |v| v.name.clone());
133 ctx.speech.speak_queued(format!("{name}. This is how I sound now."));
134 }
135 UiEvent::Changed { menu, item, value } if menu == "title" && item == "rate" => {
136 ctx.speech.set_rate(value.as_f32() / 100.0);
137 ctx.storage.set("rate", value.as_f32());
138 }
139 _ => {}
140 }
141 }Sourcepub fn set_echo(&mut self, echo: bool)
pub fn set_echo(&mut self, echo: bool)
Also print everything spoken to the console. Handy while developing.
Sourcepub fn set_muted(&mut self, muted: bool)
pub fn set_muted(&mut self, muted: bool)
Mutes speech output (messages still go to the history).
Sourcepub fn backend_name(&self) -> Option<String>
pub fn backend_name(&self) -> Option<String>
Name of the active output, e.g. “NVDA”, “SAPI”, “VoiceOver” or “AVSpeechSynthesizer”.
None while starting up or when printing to the console.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Speech
impl RefUnwindSafe for Speech
impl Send for Speech
impl Sync for Speech
impl Unpin for Speech
impl UnsafeUnpin for Speech
impl UnwindSafe for Speech
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.