1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//! This showcases all the different rules you can define in a speech recognition grammar.
use std::collections::HashMap;
use std::time::Duration;
use sapi_lite::stt::{Recognizer, Rule, SyncContext};
fn main() {
// Initialize SAPI.
sapi_lite::initialize().unwrap();
// Create a speech recognizer and recognition context.
let recog = Recognizer::new().unwrap();
let ctx = SyncContext::new(&recog).unwrap();
// Create the grammar and load it into the context.
let grammar = ctx
.grammar_builder()
.add_rule(
// The root of the semantic tree will be a string that identifies the command user
// issued.
&Rule::semantic(
// For this command, it's "set_color".
"set_color",
// This rule has several parts, so we'll use a sequence to represent them.
&Rule::sequence(vec![
// The first part is the word "set".
&Rule::text("set"),
// The word "the" is optional, so we'll say it can be repeated 0..=1 times.
&Rule::repeat(..=1, &Rule::text("the")),
// Which color does the user want to change, foreground or background? We'll use
// the associated semantic value as a key in a hash map.
&Rule::choice(vec![
&Rule::semantic("bg", &Rule::text("background")),
&Rule::semantic("fg", &Rule::text("foreground")),
]),
// The word "color" is also optional.
&Rule::repeat(..=1, &Rule::text("color")),
// But the word "to" is mandatory.
&Rule::text("to"),
// We want to offer 8 possible colors, so we'll use a choice to represent that.
// Each alternative will be a semantic rule that maps an RGB integer value to
// a text rule with the name of that color.
&Rule::choice(vec![
&Rule::semantic(0x000000, &Rule::text("black")),
&Rule::semantic(0x0000ff, &Rule::text("blue")),
&Rule::semantic(0x00ff00, &Rule::text("green")),
&Rule::semantic(0x00ffff, &Rule::text("cyan")),
&Rule::semantic(0xff0000, &Rule::text("red")),
&Rule::semantic(0xff00ff, &Rule::text("magenta")),
&Rule::semantic(0xffff00, &Rule::text("yellow")),
&Rule::semantic(0xffffff, &Rule::text("white")),
]),
]),
),
)
.add_rule(&Rule::semantic("quit", &Rule::text("quit")))
.build()
.unwrap();
// Enable the grammar so its phrases can be recognized.
grammar.set_enabled(true).unwrap();
// Prime the hash map with the recognizable keys that we'll extract from the semantic tree.
let mut colors = HashMap::new();
colors.insert("bg".to_string(), 0);
colors.insert("fg".to_string(), 0);
loop {
// Print the current RGB values for foreground and background color.
println!(
"Background: #{:08x} Foreground: #{:08x}",
colors["bg"], colors["fg"]
);
// Wait up to 10 seconds for the engine to recognize a command.
let result = ctx.recognize(Duration::from_secs(10)).unwrap();
if let Some(phrase) = result {
// Given the grammar we've loaded, there should be only one top-level semantic tag, and
// that one holds a string that identifies the command the user issued. Its children
// will hold the values specific to that command.
let command = &phrase.semantics[0];
// Note that `SemanticValue` implements `PartialEq` for comparisons with several other
// types.
if command.value == "quit" {
break;
}
// The structure of the semantic information is dictated by the nesting of the grammar
// rules. For example, the phrase "set the background to red" should produce "bg" as the
// first child of the top-level semantic value "set_color", and 0xff0000 as its second
// child, because those semantic rules are nested inside choice rules, which are nested
// inside the sequence rule which is inside the semantic rule that maps "set_color" to
// this phrase.
if command.value == "set_color" {
// Extract the hash map key from the semantic tree.
let key = command[0].value.as_string().unwrap().to_string_lossy();
// Extract the RGB value from the semantic tree.
let value = command[1].value.as_int().unwrap();
// Set the value in the hash map.
*colors.get_mut(key.as_ref()).unwrap() = *value;
}
}
}
// We don't need SAPI anymore. Clean up and free the resources.
sapi_lite::finalize();
}