use std::fmt;
use std::time::Duration;
use xml::writer::XmlEvent;
use xml::{EmitterConfig, EventWriter};
use crate::tts::{Voice, VoiceSelector};
use super::{Pitch, Rate, SayAs, Speech, Volume};
pub struct SpeechBuilder {
state: SpeechBuilderState,
}
enum SpeechBuilderState {
Text(String),
Xml(EventWriter<Vec<u8>>),
}
impl SpeechBuilder {
pub fn new() -> Self {
Self {
state: SpeechBuilderState::Text(String::new()),
}
}
pub fn start_emphasis(&mut self) -> &mut Self {
self.append_xml(XmlEvent::start_element("emph").into())
}
pub fn start_pitch<P: Into<Pitch>>(&mut self, pitch: P) -> &mut Self {
self.append_xml(
XmlEvent::start_element("pitch")
.attr("absmiddle", &pitch.into().to_string())
.into(),
)
}
pub fn start_rate<R: Into<Rate>>(&mut self, rate: R) -> &mut Self {
self.append_xml(
XmlEvent::start_element("rate")
.attr("absspeed", &rate.into().to_string())
.into(),
)
}
pub fn start_voice(&mut self, voice: &Voice) -> &mut Self {
let mut selector = VoiceSelector::new();
if let Some(name) = voice.name() {
selector = selector.name_eq(name.to_string_lossy());
}
self.select_and_start_voice(Some(selector), None)
}
pub fn select_and_start_voice(
&mut self,
required: Option<VoiceSelector>,
optional: Option<VoiceSelector>,
) -> &mut Self {
let mut event = XmlEvent::start_element("voice");
let required_expr = required.map(VoiceSelector::into_sapi_expr);
if let Some(required_expr) = required_expr.as_ref() {
if !required_expr.is_empty() {
event = event.attr("required", &required_expr);
}
}
let optional_expr = optional.map(VoiceSelector::into_sapi_expr);
if let Some(optional_expr) = optional_expr.as_ref() {
if !optional_expr.is_empty() {
event = event.attr("optional", optional_expr);
}
}
self.append_xml(event.into())
}
pub fn start_volume<V: Into<Volume>>(&mut self, volume: V) -> &mut Self {
self.append_xml(
XmlEvent::start_element("volume")
.attr("level", &volume.into().to_string())
.into(),
)
}
pub fn say<S: AsRef<str>>(&mut self, text: S) -> &mut Self {
match &mut self.state {
SpeechBuilderState::Text(contents) => {
contents.push_str(text.as_ref());
}
SpeechBuilderState::Xml(writer) => {
writer.write(text.as_ref()).unwrap();
}
};
self
}
pub fn say_as<S: AsRef<str>>(&mut self, text: S, ctx: SayAs) -> &mut Self {
self.append_xml(
XmlEvent::start_element("context")
.attr("id", ctx.sapi_id())
.into(),
)
.say(text)
.end_element("context")
}
pub fn pronounce<S: AsRef<str>>(&mut self, pronunciation: S) -> &mut Self {
self.append_xml(
XmlEvent::start_element("pron")
.attr("sym", pronunciation.as_ref())
.into(),
)
.end_element("pron")
}
pub fn silence(&mut self, duration: Duration) -> &mut Self {
let millis = duration.as_millis();
if millis == 0 {
return self;
}
self.append_xml(
XmlEvent::start_element("silence")
.attr("msec", &millis.to_string())
.into(),
)
.end_element("silence")
}
pub fn end_emphasis(&mut self) -> &mut Self {
self.end_element("emph")
}
pub fn end_pitch(&mut self) -> &mut Self {
self.end_element("pitch")
}
pub fn end_rate(&mut self) -> &mut Self {
self.end_element("rate")
}
pub fn end_voice(&mut self) -> &mut Self {
self.end_element("voice")
}
pub fn end_volume(&mut self) -> &mut Self {
self.end_element("volume")
}
pub fn build<'s>(&mut self) -> Speech<'s> {
match std::mem::replace(&mut self.state, SpeechBuilderState::Text(String::new())) {
SpeechBuilderState::Text(contents) => Speech::Text(contents.into()),
SpeechBuilderState::Xml(writer) => {
Speech::Xml(String::from_utf8(writer.into_inner()).unwrap().into())
}
}
}
fn end_element(&mut self, name: &str) -> &mut Self {
self.append_xml(XmlEvent::end_element().name(name).into())
}
fn append_xml(&mut self, event: XmlEvent) -> &mut Self {
match &mut self.state {
SpeechBuilderState::Text(contents) => {
let mut writer = EventWriter::new_with_config(
Vec::new(),
EmitterConfig::new()
.keep_element_names_stack(false)
.write_document_declaration(false),
);
writer.write(contents.as_ref()).unwrap();
writer.write(event).unwrap();
self.state = SpeechBuilderState::Xml(writer);
}
SpeechBuilderState::Xml(writer) => {
writer.write(event).unwrap();
}
}
self
}
}
impl fmt::Write for SpeechBuilder {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.say(s);
Ok(())
}
}
impl<'s> From<SpeechBuilder> for Speech<'s> {
fn from(mut builder: SpeechBuilder) -> Self {
builder.build()
}
}
impl<'s> From<&mut SpeechBuilder> for Speech<'s> {
fn from(builder: &mut SpeechBuilder) -> Self {
builder.build()
}
}