[go: up one dir, main page]

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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*!
 * a Text-To-Speech (TTS) library providing high-level interfaces to a variety of backends.
 * Currently supported backends are:
 * *  [Speech Dispatcher](https://freebsoft.org/speechd) (Linux)
 * * WebAssembly
*/

use std::boxed::Box;
use std::convert;
use std::fmt;
use std::io;

use failure::Fail;

mod backends;

pub enum Backends {
    #[cfg(target_os = "linux")]
    SpeechDispatcher,
    #[cfg(target_arch = "wasm32")]
    Web,
}

#[derive(Debug, Fail)]
pub struct Error(String);

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.0)?;
        Ok(())
    }
}

impl convert::From<Error> for io::Error {
    fn from(e: Error) -> io::Error {
        io::Error::new(io::ErrorKind::Other, e.0)
    }
}

trait Backend {
    fn speak(&self, text: &str, interrupt: bool) -> Result<(), Error>;
    fn stop(&self) -> Result<(), Error>;
    fn get_rate(&self) -> Result<u8, Error>;
    fn set_rate(&mut self, rate: u8) -> Result<(), Error>;
    fn get_pitch(&self) -> Result<u8, Error>;
    fn set_pitch(&mut self, pitch: u8) -> Result<(), Error>;
    fn get_volume(&self) -> Result<u8, Error>;
    fn set_volume(&mut self, volume: u8) -> Result<(), Error>;
}

pub struct TTS(Box<Backend>);

unsafe impl std::marker::Send for TTS {}

unsafe impl std::marker::Sync for TTS {}

impl TTS {
    /**
     * Create a new `TTS` instance with the specified backend.
     */
    pub fn new(backend: Backends) -> Result<TTS, Error> {
        match backend {
            #[cfg(target_os = "linux")]
            Backends::SpeechDispatcher => Ok(TTS(Box::new(backends::SpeechDispatcher::new()))),
            #[cfg(target_arch = "wasm32")]
            Backends::Web => {
                let tts = backends::Web::new()?;
                Ok(TTS(Box::new(tts)))
            }
        }
    }

    pub fn default() -> Result<TTS, Error> {
        #[cfg(target_os = "linux")]
        let tts = TTS::new(Backends::SpeechDispatcher);
        #[cfg(target_arch = "wasm32")]
        let tts = TTS::new(Backends::Web);
        tts
    }

    /**
     * Speaks the specified text, optionally interrupting current speech.
     */
    pub fn speak<S: Into<String>>(&self, text: S, interrupt: bool) -> Result<&Self, Error> {
        self.0.speak(text.into().as_str(), interrupt)?;
        Ok(self)
    }

    /**
     * Stops current speech.
     */
    pub fn stop(&self) -> Result<&Self, Error> {
        self.0.stop()?;
        Ok(self)
    }

    /**
     * Gets the current speech rate.
     */
    pub fn get_rate(&self) -> Result<u8, Error> {
        self.0.get_rate()
    }

    /**
     * Sets the desired speech rate.
     */
    pub fn set_rate(&mut self, rate: u8) -> Result<&Self, Error> {
        self.0.set_rate(rate)?;
        Ok(self)
    }

    /**
     * Gets the current speech pitch.
     */
    pub fn get_pitch(&self) -> Result<u8, Error> {
        self.0.get_pitch()
    }

    /**
     * Sets the desired speech pitch.
     */
    pub fn set_pitch(&mut self, pitch: u8) -> Result<&Self, Error> {
        self.0.set_pitch(pitch)?;
        Ok(self)
    }

    /**
     * Gets the current speech volume.
     */
    pub fn get_volume(&self) -> Result<u8, Error> {
        self.0.get_volume()
    }

    /**
     * Sets the desired speech volume.
     */
    pub fn set_volume(&mut self, volume: u8) -> Result<&Self, Error> {
        self.0.set_volume(volume)?;
        Ok(self)
    }
}