sapi_lite/tts/synthesizer/
mod.rs1use windows as Windows;
2use Windows::core::IUnknown;
3use Windows::Win32::Media::Speech::{ISpVoice, SpVoice};
4use Windows::Win32::System::Com::{CoCreateInstance, CLSCTX_ALL};
5
6use crate::audio::AudioStream;
7use crate::com_util::{out_to_ret, Intf};
8use crate::token::Token;
9use crate::Result;
10
11use super::{Rate, Speech, Voice, Volume};
12
13mod event;
14mod sync;
15
16pub use event::{EventHandler, EventfulSynthesizer};
17pub use sync::SyncSynthesizer;
18
19pub enum SpeechOutput {
21 Default,
23 Stream(AudioStream),
25}
26
27impl SpeechOutput {
28 fn to_sapi(self) -> Option<IUnknown> {
29 match self {
30 Self::Default => None,
31 Self::Stream(stream) => Some(stream.to_sapi().0),
32 }
33 }
34}
35
36pub struct Synthesizer {
38 intf: Intf<ISpVoice>,
39}
40
41impl Synthesizer {
42 fn new() -> Result<Self> {
43 unsafe { CoCreateInstance(&SpVoice, None, CLSCTX_ALL) }
44 .map(|intf| Self { intf: Intf(intf) })
45 }
46
47 pub fn set_output(&self, output: SpeechOutput, allow_fmt_changes: bool) -> Result<()> {
49 unsafe { self.intf.SetOutput(output.to_sapi(), allow_fmt_changes) }
50 }
51
52 pub fn rate(&self) -> Result<Rate> {
54 unsafe { out_to_ret(|out| self.intf.GetRate(out)) }.map(Rate::new)
55 }
56
57 pub fn voice(&self) -> Result<Voice> {
59 unsafe { self.intf.GetVoice() }.map(|intf| Voice {
60 token: Token::from_sapi(intf),
61 })
62 }
63
64 pub fn volume(&self) -> Result<Volume> {
66 unsafe { out_to_ret(|out| self.intf.GetVolume(out)) }.map(Volume::from_sapi)
67 }
68
69 pub fn set_rate<R: Into<Rate>>(&self, rate: R) -> Result<()> {
71 unsafe { self.intf.SetRate(rate.into().value()) }
72 }
73
74 pub fn set_voice(&self, voice: &Voice) -> Result<()> {
76 unsafe { self.intf.SetVoice(&voice.token) }
77 }
78
79 pub fn set_volume<V: Into<Volume>>(&self, volume: V) -> Result<()> {
81 unsafe { self.intf.SetVolume(volume.into().sapi_value()) }
82 }
83
84 fn speak<'s, S: Into<Speech<'s>>>(&self, speech: S, base_flags: u32) -> Result<u32> {
85 let speech = speech.into();
86 unsafe {
87 self.intf
88 .Speak(speech.contents(), speech.flags() | base_flags)
89 }
90 }
91}