Skip to main content

Speech

Struct Speech 

Source
pub struct Speech { /* private fields */ }
Expand description

Speech and braille output. Reach it through ctx.speech.

Implementations§

Source§

impl Speech

Source

pub fn console() -> Self

Speech that only prints to the console. Useful for tests and headless tools.

Source

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?
examples/hello.rs (line 11)
10    fn start(&mut self, ctx: &mut Context) {
11        ctx.speech.speak("Hello! Press space to beep, F1 to repeat, escape to quit.");
12    }
More examples
Hide additional examples
examples/flight.rs (line 64)
59    fn fly(&mut self, ctx: &mut Context, result: MoveResult) {
60        if result.blocked && !self.blocked
61            && let Some(&hit) = result.hits.first()
62        {
63            ctx.play("bump");
64            ctx.speech.speak(ctx.physics.tag(hit));
65        }
66        self.blocked = result.blocked;
67    }
Source

pub fn speak_queued(&mut self, text: impl AsRef<str>)

Speaks text after the current speech has finished.

Examples found in repository?
examples/menus.rs (line 80)
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
Hide additional examples
examples/bell_catcher.rs (line 133)
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    }
Source

pub fn braille(&mut self, text: impl AsRef<str>)

Shows text on a braille display only.

Source

pub fn stop(&mut self)

Silences speech immediately.

Source

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?
examples/hello.rs (line 21)
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    }
Source

pub fn last(&self) -> Option<&str>

The most recently spoken message.

Source

pub fn history(&self) -> &[String]

Recently spoken messages, oldest first (up to 100).

Source

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?
examples/bell_catcher.rs (line 39)
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    }
Source

pub fn set_volume(&mut self, volume: f32)

Sets the speech volume if the output supports it (0.0 to 1.0).

Source

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?
examples/bell_catcher.rs (line 65)
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    }
Source

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?
examples/bell_catcher.rs (line 35)
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    }
Source

pub fn reset_voice(&mut self)

Goes back to the default voice, the one the game started with.

Examples found in repository?
examples/bell_catcher.rs (line 122)
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    }
Source

pub fn voice(&self) -> Option<&Voice>

The voice chosen with Speech::set_voice, or None for the default voice.

Examples found in repository?
examples/bell_catcher.rs (line 50)
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    }
Source

pub fn set_echo(&mut self, echo: bool)

Also print everything spoken to the console. Handy while developing.

Source

pub fn set_muted(&mut self, muted: bool)

Mutes speech output (messages still go to the history).

Source

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§

Source§

impl Debug for Speech

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert 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>

Convert 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)

Convert &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)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync> ⓘ

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<F, T> IntoSample<T> for F
where T: FromSample<F>,

Source§

fn into_sample(self) -> T

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

Source§

fn to_sample_(self) -> U

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more