mirror of
https://github.com/ndarilek/tts-rs.git
synced 2024-11-22 19:09:37 +00:00
Clean up speech synthesis properties, and implement everything for WinRT.
I'd previously attempted to normalize everything to `u8`, but this had some drawbacks: * It failed to account for some synthesis drivers defining normal as mid-range, while most define it very low. * It didn't track the normal value for a given synthesizer. * There was no clean way to map a curve between the minimum, normal, and maximum rates. Here we track the minimum, normal, and maximum values of rate, pitch, and volume. Sanity checks are done on set. Also, as a further proof-of-concept, all properties are now implemented for the WinRT driver.
This commit is contained in:
parent
3198a537f0
commit
7b4fb8dae4
|
@ -1,5 +1,4 @@
|
|||
use std::io;
|
||||
use std::u8;
|
||||
|
||||
use tts::*;
|
||||
|
||||
|
@ -11,28 +10,34 @@ fn main() -> Result<(), Error> {
|
|||
if rate {
|
||||
let original_rate = tts.get_rate()?;
|
||||
tts.speak(format!("Current rate: {}", original_rate), false)?;
|
||||
tts.set_rate(u8::MAX)?;
|
||||
tts.set_rate(tts.max_rate())?;
|
||||
tts.speak("This is very fast.", false)?;
|
||||
tts.set_rate(0)?;
|
||||
tts.set_rate(tts.min_rate())?;
|
||||
tts.speak("This is very slow.", false)?;
|
||||
tts.set_rate(tts.normal_rate())?;
|
||||
tts.speak("This is the normal rate.", false)?;
|
||||
tts.set_rate(original_rate)?;
|
||||
}
|
||||
let Features { pitch, .. } = tts.supported_features();
|
||||
if pitch {
|
||||
let original_pitch = tts.get_pitch()?;
|
||||
tts.set_pitch(u8::MAX)?;
|
||||
tts.set_pitch(tts.max_pitch())?;
|
||||
tts.speak("This is high-pitch.", false)?;
|
||||
tts.set_pitch(0)?;
|
||||
tts.set_pitch(tts.min_pitch())?;
|
||||
tts.speak("This is low pitch.", false)?;
|
||||
tts.set_pitch(tts.normal_pitch())?;
|
||||
tts.speak("This is normal pitch.", false)?;
|
||||
tts.set_pitch(original_pitch)?;
|
||||
}
|
||||
let Features { volume, .. } = tts.supported_features();
|
||||
if volume {
|
||||
let original_volume = tts.get_volume()?;
|
||||
tts.set_volume(u8::MAX)?;
|
||||
tts.set_volume(tts.max_volume())?;
|
||||
tts.speak("This is loud!", false)?;
|
||||
tts.set_volume(0)?;
|
||||
tts.set_volume(tts.min_volume())?;
|
||||
tts.speak("This is quiet.", false)?;
|
||||
tts.set_volume(tts.normal_volume())?;
|
||||
tts.speak("This is normal volume.", false)?;
|
||||
tts.set_volume(original_volume)?;
|
||||
}
|
||||
tts.speak("Goodbye.", false)?;
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
#[cfg(target_os = "linux")]
|
||||
use std::u8;
|
||||
|
||||
use log::{info, trace};
|
||||
use speech_dispatcher::*;
|
||||
|
||||
|
@ -16,18 +14,6 @@ impl SpeechDispatcher {
|
|||
}
|
||||
}
|
||||
|
||||
fn u8_to_i32(v: u8) -> i32 {
|
||||
let ratio: f32 = v as f32 / u8::MAX as f32;
|
||||
(ratio * 200. - 100.) as i32
|
||||
}
|
||||
|
||||
fn i32_to_u8(v: i32) -> u8 {
|
||||
let v = v as f32;
|
||||
let ratio: f32 = (v + 100.) / 200.;
|
||||
let v = ratio * u8::MAX as f32;
|
||||
v as u8
|
||||
}
|
||||
|
||||
impl Backend for SpeechDispatcher {
|
||||
fn supported_features(&self) -> Features {
|
||||
Features {
|
||||
|
@ -60,30 +46,66 @@ impl Backend for SpeechDispatcher {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_rate(&self) -> Result<u8, Error> {
|
||||
Ok(i32_to_u8(self.0.get_voice_rate()))
|
||||
fn min_rate(&self) -> f32 {
|
||||
-100.
|
||||
}
|
||||
|
||||
fn set_rate(&mut self, rate: u8) -> Result<(), Error> {
|
||||
self.0.set_voice_rate(u8_to_i32(rate));
|
||||
fn max_rate(&self) -> f32 {
|
||||
100.
|
||||
}
|
||||
|
||||
fn normal_rate(&self) -> f32 {
|
||||
0.
|
||||
}
|
||||
|
||||
fn get_rate(&self) -> Result<f32, Error> {
|
||||
Ok(self.0.get_voice_rate() as f32)
|
||||
}
|
||||
|
||||
fn set_rate(&mut self, rate: f32) -> Result<(), Error> {
|
||||
self.0.set_voice_rate(rate as i32);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_pitch(&self) -> Result<u8, Error> {
|
||||
Ok(i32_to_u8(self.0.get_voice_pitch()))
|
||||
fn min_pitch(&self) -> f32 {
|
||||
-100.
|
||||
}
|
||||
|
||||
fn set_pitch(&mut self, pitch: u8) -> Result<(), Error> {
|
||||
self.0.set_voice_pitch(u8_to_i32(pitch));
|
||||
fn max_pitch(&self) -> f32 {
|
||||
100.
|
||||
}
|
||||
|
||||
fn normal_pitch(&self) -> f32 {
|
||||
0.
|
||||
}
|
||||
|
||||
fn get_pitch(&self) -> Result<f32, Error> {
|
||||
Ok(self.0.get_voice_pitch() as f32)
|
||||
}
|
||||
|
||||
fn set_pitch(&mut self, pitch: f32) -> Result<(), Error> {
|
||||
self.0.set_voice_pitch(pitch as i32);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_volume(&self) -> Result<u8, Error> {
|
||||
Ok(i32_to_u8(self.0.get_volume()))
|
||||
fn min_volume(&self) -> f32 {
|
||||
-100.
|
||||
}
|
||||
|
||||
fn set_volume(&mut self, volume: u8) -> Result<(), Error> {
|
||||
self.0.set_volume(u8_to_i32(volume));
|
||||
fn max_volume(&self) -> f32 {
|
||||
100.
|
||||
}
|
||||
|
||||
fn normal_volume(&self) -> f32 {
|
||||
0.
|
||||
}
|
||||
|
||||
fn get_volume(&self) -> Result<f32, Error> {
|
||||
Ok(self.0.get_volume() as f32)
|
||||
}
|
||||
|
||||
fn set_volume(&mut self, volume: f32) -> Result<(), Error> {
|
||||
self.0.set_volume(volume as i32);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,27 +37,63 @@ impl Backend for Tolk {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_rate(&self) -> Result<u8, Error> {
|
||||
fn min_rate(&self) -> f32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn max_rate(&self) -> f32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn normal_rate(&self) -> f32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_rate(&self) -> Result<f32, Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn set_rate(&mut self, _rate: u8) -> Result<(), Error> {
|
||||
fn set_rate(&mut self, _rate: f32) -> Result<(), Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn get_pitch(&self) -> Result<u8, Error> {
|
||||
fn min_pitch(&self) -> f32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn max_pitch(&self) -> f32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn normal_pitch(&self) -> f32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_pitch(&self) -> Result<f32, Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn set_pitch(&mut self, _pitch: u8) -> Result<(), Error> {
|
||||
fn set_pitch(&mut self, _pitch: f32) -> Result<(), Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn get_volume(&self) -> Result<u8, Error> {
|
||||
fn min_volume(&self) -> f32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn max_volume(&self) -> f32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn normal_volume(&self) -> f32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_volume(&self) -> Result<f32, Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn set_volume(&mut self, _volume: u8) -> Result<(), Error> {
|
||||
fn set_volume(&mut self, _volume: f32) -> Result<(), Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
#[cfg(target_arch = "wasm32")]
|
||||
use std::u8;
|
||||
|
||||
use log::{info, trace};
|
||||
use web_sys::SpeechSynthesisUtterance;
|
||||
|
||||
use crate::{Backend, Error};
|
||||
use crate::{Backend, Error, Features};
|
||||
|
||||
pub struct Web {
|
||||
rate: u8,
|
||||
pitch: u8,
|
||||
volume: u8,
|
||||
rate: f32,
|
||||
pitch: f32,
|
||||
volume: f32,
|
||||
}
|
||||
|
||||
impl Web {
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
info!("Initializing Web backend");
|
||||
Ok(Web {
|
||||
rate: 25,
|
||||
pitch: 127,
|
||||
volume: u8::MAX,
|
||||
rate: 1.,
|
||||
pitch: 1.,
|
||||
volume: 1.,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -36,15 +34,9 @@ impl Backend for Web {
|
|||
fn speak(&self, text: &str, interrupt: bool) -> Result<(), Error> {
|
||||
trace!("speak({}, {})", text, interrupt);
|
||||
let utterance = SpeechSynthesisUtterance::new_with_text(text).unwrap();
|
||||
let mut rate: f32 = self.rate as f32;
|
||||
rate = rate / u8::MAX as f32 * 10.;
|
||||
utterance.set_rate(rate);
|
||||
let mut pitch: f32 = self.pitch as f32;
|
||||
pitch = pitch / u8::MAX as f32 * 2.;
|
||||
utterance.set_pitch(pitch);
|
||||
let mut volume: f32 = self.volume as f32;
|
||||
volume = volume / u8::MAX as f32 * 1.;
|
||||
utterance.set_volume(volume);
|
||||
utterance.set_rate(self.rate);
|
||||
utterance.set_pitch(self.pitch);
|
||||
utterance.set_volume(self.volume);
|
||||
if interrupt {
|
||||
self.stop()?;
|
||||
}
|
||||
|
@ -64,29 +56,65 @@ impl Backend for Web {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_rate(&self) -> Result<u8, Error> {
|
||||
fn min_rate(&self) -> f32 {
|
||||
0.1
|
||||
}
|
||||
|
||||
fn max_rate(&self) -> f32 {
|
||||
10.
|
||||
}
|
||||
|
||||
fn normal_rate(&self) -> f32 {
|
||||
1.
|
||||
}
|
||||
|
||||
fn get_rate(&self) -> Result<f32, Error> {
|
||||
Ok(self.rate)
|
||||
}
|
||||
|
||||
fn set_rate(&mut self, rate: u8) -> Result<(), Error> {
|
||||
fn set_rate(&mut self, rate: f32) -> Result<(), Error> {
|
||||
self.rate = rate;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_pitch(&self) -> Result<u8, Error> {
|
||||
fn min_pitch(&self) -> f32 {
|
||||
0.
|
||||
}
|
||||
|
||||
fn max_pitch(&self) -> f32 {
|
||||
2.
|
||||
}
|
||||
|
||||
fn normal_pitch(&self) -> f32 {
|
||||
1.
|
||||
}
|
||||
|
||||
fn get_pitch(&self) -> Result<f32, Error> {
|
||||
Ok(self.pitch)
|
||||
}
|
||||
|
||||
fn set_pitch(&mut self, pitch: u8) -> Result<(), Error> {
|
||||
fn set_pitch(&mut self, pitch: f32) -> Result<(), Error> {
|
||||
self.pitch = pitch;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_volume(&self) -> Result<u8, Error> {
|
||||
fn min_volume(&self) -> f32 {
|
||||
0.
|
||||
}
|
||||
|
||||
fn max_volume(&self) -> f32 {
|
||||
1.
|
||||
}
|
||||
|
||||
fn normal_volume(&self) -> f32 {
|
||||
1.
|
||||
}
|
||||
|
||||
fn get_volume(&self) -> Result<f32, Error> {
|
||||
Ok(self.volume)
|
||||
}
|
||||
|
||||
fn set_volume(&mut self, volume: u8) -> Result<(), Error> {
|
||||
fn set_volume(&mut self, volume: f32) -> Result<(), Error> {
|
||||
self.volume = volume;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -48,9 +48,9 @@ impl Backend for WinRT {
|
|||
fn supported_features(&self) -> Features {
|
||||
Features {
|
||||
stop: true,
|
||||
rate: false,
|
||||
pitch: false,
|
||||
volume: false,
|
||||
rate: true,
|
||||
pitch: true,
|
||||
volume: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,27 +71,69 @@ impl Backend for WinRT {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_rate(&self) -> std::result::Result<u8, Error> {
|
||||
unimplemented!();
|
||||
fn min_rate(&self) -> f32 {
|
||||
0.5
|
||||
}
|
||||
|
||||
fn set_rate(&mut self, _rate: u8) -> std::result::Result<(), Error> {
|
||||
unimplemented!();
|
||||
fn max_rate(&self) -> f32 {
|
||||
6.0
|
||||
}
|
||||
|
||||
fn get_pitch(&self) -> std::result::Result<u8, Error> {
|
||||
unimplemented!();
|
||||
fn normal_rate(&self) -> f32 {
|
||||
1.
|
||||
}
|
||||
|
||||
fn set_pitch(&mut self, _pitch: u8) -> std::result::Result<(), Error> {
|
||||
unimplemented!();
|
||||
fn get_rate(&self) -> std::result::Result<f32, Error> {
|
||||
let rate = self.synth.options()?.speaking_rate()?;
|
||||
Ok(rate as f32)
|
||||
}
|
||||
|
||||
fn get_volume(&self) -> std::result::Result<u8, Error> {
|
||||
unimplemented!();
|
||||
fn set_rate(&mut self, rate: f32) -> std::result::Result<(), Error> {
|
||||
self.synth.options()?.set_speaking_rate(rate.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_volume(&mut self, _volume: u8) -> std::result::Result<(), Error> {
|
||||
unimplemented!();
|
||||
fn min_pitch(&self) -> f32 {
|
||||
0.
|
||||
}
|
||||
|
||||
fn max_pitch(&self) -> f32 {
|
||||
2.
|
||||
}
|
||||
|
||||
fn normal_pitch(&self) -> f32 {
|
||||
1.
|
||||
}
|
||||
|
||||
fn get_pitch(&self) -> std::result::Result<f32, Error> {
|
||||
let pitch = self.synth.options()?.audio_pitch()?;
|
||||
Ok(pitch as f32)
|
||||
}
|
||||
|
||||
fn set_pitch(&mut self, pitch: f32) -> std::result::Result<(), Error> {
|
||||
self.synth.options()?.set_audio_pitch(pitch.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn min_volume(&self) -> f32 {
|
||||
0.
|
||||
}
|
||||
|
||||
fn max_volume(&self) -> f32 {
|
||||
1.
|
||||
}
|
||||
|
||||
fn normal_volume(&self) -> f32 {
|
||||
1.
|
||||
}
|
||||
|
||||
fn get_volume(&self) -> std::result::Result<f32, Error> {
|
||||
let volume = self.synth.options()?.audio_volume()?;
|
||||
Ok(volume as f32)
|
||||
}
|
||||
|
||||
fn set_volume(&mut self, volume: f32) -> std::result::Result<(), Error> {
|
||||
self.synth.options()?.set_audio_volume(volume.into())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
122
src/lib.rs
122
src/lib.rs
|
@ -39,18 +39,29 @@ pub enum Error {
|
|||
WinRT(winrt::Error),
|
||||
#[error("Unsupported feature")]
|
||||
UnsupportedFeature,
|
||||
#[error("Out of range")]
|
||||
OutOfRange,
|
||||
}
|
||||
|
||||
pub trait Backend {
|
||||
fn supported_features(&self) -> Features;
|
||||
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>;
|
||||
fn min_rate(&self) -> f32;
|
||||
fn max_rate(&self) -> f32;
|
||||
fn normal_rate(&self) -> f32;
|
||||
fn get_rate(&self) -> Result<f32, Error>;
|
||||
fn set_rate(&mut self, rate: f32) -> Result<(), Error>;
|
||||
fn min_pitch(&self) -> f32;
|
||||
fn max_pitch(&self) -> f32;
|
||||
fn normal_pitch(&self) -> f32;
|
||||
fn get_pitch(&self) -> Result<f32, Error>;
|
||||
fn set_pitch(&mut self, pitch: f32) -> Result<(), Error>;
|
||||
fn min_volume(&self) -> f32;
|
||||
fn max_volume(&self) -> f32;
|
||||
fn normal_volume(&self) -> f32;
|
||||
fn get_volume(&self) -> Result<f32, Error>;
|
||||
fn set_volume(&mut self, volume: f32) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub struct TTS(Box<dyn Backend>);
|
||||
|
@ -130,10 +141,31 @@ impl TTS {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum rate for this speech synthesizer.
|
||||
*/
|
||||
pub fn min_rate(&self) -> f32 {
|
||||
self.0.min_rate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum rate for this speech synthesizer.
|
||||
*/
|
||||
pub fn max_rate(&self) -> f32 {
|
||||
self.0.max_rate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the normal rate for this speech synthesizer.
|
||||
*/
|
||||
pub fn normal_rate(&self) -> f32 {
|
||||
self.0.normal_rate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current speech rate.
|
||||
*/
|
||||
pub fn get_rate(&self) -> Result<u8, Error> {
|
||||
pub fn get_rate(&self) -> Result<f32, Error> {
|
||||
let Features { rate, .. } = self.supported_features();
|
||||
if rate {
|
||||
self.0.get_rate()
|
||||
|
@ -145,22 +177,47 @@ impl TTS {
|
|||
/**
|
||||
* Sets the desired speech rate.
|
||||
*/
|
||||
pub fn set_rate(&mut self, rate: u8) -> Result<&Self, Error> {
|
||||
pub fn set_rate(&mut self, rate: f32) -> Result<&Self, Error> {
|
||||
let Features {
|
||||
rate: rate_feature, ..
|
||||
} = self.supported_features();
|
||||
if rate_feature {
|
||||
self.0.set_rate(rate)?;
|
||||
Ok(self)
|
||||
if rate < self.0.min_rate() || rate > self.0.max_rate() {
|
||||
Err(Error::OutOfRange)
|
||||
} else {
|
||||
self.0.set_rate(rate)?;
|
||||
Ok(self)
|
||||
}
|
||||
} else {
|
||||
Err(Error::UnsupportedFeature)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum pitch for this speech synthesizer.
|
||||
*/
|
||||
pub fn min_pitch(&self) -> f32 {
|
||||
self.0.min_pitch()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum pitch for this speech synthesizer.
|
||||
*/
|
||||
pub fn max_pitch(&self) -> f32 {
|
||||
self.0.max_pitch()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the normal pitch for this speech synthesizer.
|
||||
*/
|
||||
pub fn normal_pitch(&self) -> f32 {
|
||||
self.0.normal_pitch()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current speech pitch.
|
||||
*/
|
||||
pub fn get_pitch(&self) -> Result<u8, Error> {
|
||||
pub fn get_pitch(&self) -> Result<f32, Error> {
|
||||
let Features { pitch, .. } = self.supported_features();
|
||||
if pitch {
|
||||
self.0.get_pitch()
|
||||
|
@ -172,23 +229,48 @@ impl TTS {
|
|||
/**
|
||||
* Sets the desired speech pitch.
|
||||
*/
|
||||
pub fn set_pitch(&mut self, pitch: u8) -> Result<&Self, Error> {
|
||||
pub fn set_pitch(&mut self, pitch: f32) -> Result<&Self, Error> {
|
||||
let Features {
|
||||
pitch: pitch_feature,
|
||||
..
|
||||
} = self.supported_features();
|
||||
if pitch_feature {
|
||||
self.0.set_pitch(pitch)?;
|
||||
Ok(self)
|
||||
if pitch < self.0.min_pitch() || pitch > self.0.max_pitch() {
|
||||
Err(Error::OutOfRange)
|
||||
} else {
|
||||
self.0.set_pitch(pitch)?;
|
||||
Ok(self)
|
||||
}
|
||||
} else {
|
||||
Err(Error::UnsupportedFeature)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum volume for this speech synthesizer.
|
||||
*/
|
||||
pub fn min_volume(&self) -> f32 {
|
||||
self.0.min_volume()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum volume for this speech synthesizer.
|
||||
*/
|
||||
pub fn max_volume(&self) -> f32 {
|
||||
self.0.max_volume()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the normal volume for this speech synthesizer.
|
||||
*/
|
||||
pub fn normal_volume(&self) -> f32 {
|
||||
self.0.normal_volume()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current speech volume.
|
||||
*/
|
||||
pub fn get_volume(&self) -> Result<u8, Error> {
|
||||
pub fn get_volume(&self) -> Result<f32, Error> {
|
||||
let Features { volume, .. } = self.supported_features();
|
||||
if volume {
|
||||
self.0.get_volume()
|
||||
|
@ -200,14 +282,18 @@ impl TTS {
|
|||
/**
|
||||
* Sets the desired speech volume.
|
||||
*/
|
||||
pub fn set_volume(&mut self, volume: u8) -> Result<&Self, Error> {
|
||||
pub fn set_volume(&mut self, volume: f32) -> Result<&Self, Error> {
|
||||
let Features {
|
||||
volume: volume_feature,
|
||||
..
|
||||
} = self.supported_features();
|
||||
if volume_feature {
|
||||
self.0.set_volume(volume)?;
|
||||
Ok(self)
|
||||
if volume < self.0.min_volume() || volume > self.0.max_volume() {
|
||||
Err(Error::OutOfRange)
|
||||
} else {
|
||||
self.0.set_volume(volume)?;
|
||||
Ok(self)
|
||||
}
|
||||
} else {
|
||||
Err(Error::UnsupportedFeature)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user