2020-12-12 21:37:24 +00:00
|
|
|
//! Bindings to this library to allow it to be called from C/C++.
|
|
|
|
|
2020-12-12 01:13:40 +00:00
|
|
|
use std::{
|
|
|
|
cell::RefCell,
|
2020-12-12 19:11:05 +00:00
|
|
|
ffi::{CStr, CString, NulError},
|
2020-12-12 01:13:40 +00:00
|
|
|
ptr,
|
|
|
|
};
|
|
|
|
|
2020-12-12 19:11:05 +00:00
|
|
|
use crate::{Backends, Features, UtteranceId, TTS};
|
2020-12-12 01:40:13 +00:00
|
|
|
|
2020-12-12 01:13:40 +00:00
|
|
|
thread_local! {
|
|
|
|
/// Stores the last reported error, so it can be retrieved at will from C
|
|
|
|
static LAST_ERROR: RefCell<Option<CString>> = RefCell::new(None);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_last_error<E: Into<Vec<u8>>>(err: E) -> Result<(), NulError> {
|
|
|
|
LAST_ERROR.with(|last| {
|
|
|
|
*last.borrow_mut() = Some(CString::new(err)?);
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-12-12 19:11:05 +00:00
|
|
|
/// Get the last reported error as a const C string.
|
2020-12-12 01:13:40 +00:00
|
|
|
/// This string will be valid until at least the next call to `tts_get_error`.
|
|
|
|
/// It is never called internally by the library.
|
|
|
|
#[no_mangle]
|
2021-03-06 11:30:42 +00:00
|
|
|
pub extern "C" fn tts_get_error() -> *const i8 {
|
2020-12-12 01:13:40 +00:00
|
|
|
LAST_ERROR.with(|err| match &*err.borrow() {
|
|
|
|
Some(e) => e.as_ptr(),
|
|
|
|
None => ptr::null(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Deallocate the last reported error (if any).
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "C" fn tts_clear_error() {
|
|
|
|
LAST_ERROR.with(|err| {
|
|
|
|
*err.borrow_mut() = None;
|
|
|
|
});
|
|
|
|
}
|
2020-12-12 01:40:13 +00:00
|
|
|
|
2020-12-12 17:06:58 +00:00
|
|
|
/// Create a new `TTS` instance with the specified backend.
|
2020-12-31 15:31:46 +00:00
|
|
|
/// If an error occurs, returns a null pointer,
|
2020-12-12 16:32:53 +00:00
|
|
|
/// Call `tts_get_error()` for more information about the specific error.
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "C" fn tts_new(backend: Backends) -> *mut TTS {
|
|
|
|
match TTS::new(backend) {
|
|
|
|
Ok(tts) => Box::into_raw(Box::new(tts)),
|
|
|
|
Err(e) => {
|
|
|
|
set_last_error(e.to_string()).unwrap();
|
|
|
|
ptr::null_mut()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-12 17:06:58 +00:00
|
|
|
/// Create a new TTS object with the default backend.
|
2020-12-31 15:31:46 +00:00
|
|
|
/// If an error occurs, returns a null pointer,
|
2020-12-12 01:40:13 +00:00
|
|
|
/// Call `tts_get_error()` for more information about the specific error.
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "C" fn tts_default() -> *mut TTS {
|
|
|
|
match TTS::default() {
|
|
|
|
Ok(tts) => Box::into_raw(Box::new(tts)),
|
|
|
|
Err(e) => {
|
|
|
|
set_last_error(e.to_string()).unwrap();
|
|
|
|
ptr::null_mut()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-12 17:06:58 +00:00
|
|
|
/// Free the memory associated with a TTS object.
|
2020-12-12 01:40:13 +00:00
|
|
|
/// If `tts` is a null pointer, this function does nothing.
|
|
|
|
#[no_mangle]
|
2020-12-12 19:37:44 +00:00
|
|
|
pub unsafe extern "C" fn tts_free(tts: *mut TTS) {
|
2020-12-12 01:40:13 +00:00
|
|
|
if tts.is_null() {
|
|
|
|
return;
|
|
|
|
}
|
2020-12-12 19:37:44 +00:00
|
|
|
Box::from_raw(tts); // Goes out of scope and is dropped
|
2020-12-12 01:40:13 +00:00
|
|
|
}
|
2020-12-12 16:59:07 +00:00
|
|
|
|
|
|
|
/// Returns the features supported by this TTS engine.
|
2020-12-12 17:06:58 +00:00
|
|
|
/// `tts` must be a valid pointer to a TTS object.
|
2020-12-12 16:59:07 +00:00
|
|
|
#[no_mangle]
|
2021-03-06 09:36:45 +00:00
|
|
|
pub unsafe extern "C" fn tts_supported_features(tts: *const TTS) -> Features {
|
2020-12-12 19:37:44 +00:00
|
|
|
tts.as_ref().unwrap().supported_features()
|
2020-12-12 16:59:07 +00:00
|
|
|
}
|
2020-12-12 19:11:05 +00:00
|
|
|
|
|
|
|
/// Speaks the specified text, optionally interrupting current speech.
|
|
|
|
/// If `utterance` is not NULL, , fills it with a pointer to the returned UtteranceId (or NULL if
|
|
|
|
/// the backend doesn't provide one).
|
|
|
|
/// Returns true on success, false on error or if `tts` is NULL.
|
|
|
|
#[no_mangle]
|
2020-12-12 19:37:44 +00:00
|
|
|
pub unsafe extern "C" fn tts_speak(
|
2020-12-12 19:11:05 +00:00
|
|
|
tts: *mut TTS,
|
2021-03-06 11:30:42 +00:00
|
|
|
text: *const i8,
|
2020-12-12 19:11:05 +00:00
|
|
|
interrupt: bool,
|
|
|
|
utterance: *mut *mut UtteranceId,
|
|
|
|
) -> bool {
|
|
|
|
if tts.is_null() {
|
2021-03-06 09:36:45 +00:00
|
|
|
return false;
|
2020-12-12 19:11:05 +00:00
|
|
|
}
|
2020-12-12 19:37:44 +00:00
|
|
|
let text = CStr::from_ptr(text).to_string_lossy().into_owned();
|
|
|
|
match tts.as_mut().unwrap().speak(text, interrupt) {
|
|
|
|
Ok(u) => {
|
|
|
|
if !utterance.is_null() {
|
|
|
|
*utterance = match u {
|
|
|
|
Some(u) => Box::into_raw(Box::new(u)),
|
|
|
|
None => ptr::null_mut(),
|
|
|
|
};
|
2020-12-12 19:11:05 +00:00
|
|
|
}
|
2020-12-12 19:37:44 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
set_last_error(e.to_string()).unwrap();
|
|
|
|
return false;
|
2020-12-12 19:11:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Free the memory associated with an `UtteranceId`.
|
|
|
|
/// Does nothing if `utterance` is NULL.
|
|
|
|
#[no_mangle]
|
2020-12-12 19:37:44 +00:00
|
|
|
pub unsafe extern "C" fn tts_free_utterance(utterance: *mut UtteranceId) {
|
2020-12-12 19:11:05 +00:00
|
|
|
if utterance.is_null() {
|
|
|
|
return;
|
|
|
|
}
|
2020-12-12 19:37:44 +00:00
|
|
|
Box::from_raw(utterance);
|
2020-12-12 19:11:05 +00:00
|
|
|
}
|
2021-03-06 09:36:45 +00:00
|
|
|
|
2020-12-12 19:25:36 +00:00
|
|
|
/// Stops current speech.
|
|
|
|
/// Returns true on success, false on error or if `tts` is NULL.
|
|
|
|
#[no_mangle]
|
2020-12-12 19:37:44 +00:00
|
|
|
pub unsafe extern "C" fn tts_stop(tts: *mut TTS) -> bool {
|
2020-12-12 19:25:36 +00:00
|
|
|
if tts.is_null() {
|
|
|
|
return false;
|
|
|
|
}
|
2020-12-12 19:37:44 +00:00
|
|
|
match tts.as_mut().unwrap().stop() {
|
2020-12-12 19:25:36 +00:00
|
|
|
Ok(_) => true,
|
|
|
|
Err(e) => {
|
|
|
|
set_last_error(e.to_string()).unwrap();
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-12 20:04:48 +00:00
|
|
|
|
|
|
|
/// Returns the minimum rate for this speech synthesizer.
|
|
|
|
/// `tts` must be a valid pointer to a TTS object.
|
|
|
|
#[no_mangle]
|
2021-03-06 11:30:42 +00:00
|
|
|
pub unsafe extern "C" fn tts_min_rate(tts: *const TTS) -> f32 {
|
2020-12-12 20:04:48 +00:00
|
|
|
tts.as_ref().unwrap().min_rate()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the maximum rate for this speech synthesizer.
|
|
|
|
/// `tts` must be a valid pointer to a TTS object.
|
|
|
|
#[no_mangle]
|
2021-03-06 11:30:42 +00:00
|
|
|
pub unsafe extern "C" fn tts_max_rate(tts: *const TTS) -> f32 {
|
2020-12-12 20:04:48 +00:00
|
|
|
tts.as_ref().unwrap().max_rate()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the normal rate for this speech synthesizer.
|
|
|
|
/// `tts` must be a valid pointer to a TTS object.
|
|
|
|
#[no_mangle]
|
2021-03-06 11:30:42 +00:00
|
|
|
pub unsafe extern "C" fn tts_normal_rate(tts: *const TTS) -> f32 {
|
2020-12-12 20:04:48 +00:00
|
|
|
tts.as_ref().unwrap().normal_rate()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the current speech rate.
|
|
|
|
/// Returns true on success, false on error (likely that the backend doesn't support rate changes)
|
|
|
|
/// or if `tts` is NULL.
|
|
|
|
/// Does nothing if `rate` is NULL.
|
|
|
|
#[no_mangle]
|
2021-03-06 11:30:42 +00:00
|
|
|
pub unsafe extern "C" fn tts_get_rate(tts: *const TTS, rate: *mut f32) -> bool {
|
2020-12-12 20:04:48 +00:00
|
|
|
if tts.is_null() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
match tts.as_ref().unwrap().get_rate() {
|
|
|
|
Ok(r) => {
|
|
|
|
if !rate.is_null() {
|
|
|
|
*rate = r;
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
set_last_error(e.to_string()).unwrap();
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the desired speech rate.
|
|
|
|
/// Returns true on success, false on error (likely that the backend doesn't support rate changes)
|
|
|
|
/// or if `tts` is NULL.
|
|
|
|
#[no_mangle]
|
2021-03-06 11:30:42 +00:00
|
|
|
pub unsafe extern "C" fn tts_set_rate(tts: *mut TTS, rate: f32) -> bool {
|
2020-12-12 20:04:48 +00:00
|
|
|
if tts.is_null() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
match tts.as_mut().unwrap().set_rate(rate) {
|
|
|
|
Ok(_) => true,
|
|
|
|
Err(e) => {
|
|
|
|
set_last_error(e.to_string()).unwrap();
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-12 20:29:34 +00:00
|
|
|
|
|
|
|
/// Returns the minimum pitch for this speech synthesizer.
|
|
|
|
/// `tts` must be a valid pointer to a TTS object.
|
|
|
|
#[no_mangle]
|
2021-03-06 11:30:42 +00:00
|
|
|
pub unsafe extern "C" fn tts_min_pitch(tts: *const TTS) -> f32 {
|
2020-12-12 20:29:34 +00:00
|
|
|
tts.as_ref().unwrap().min_pitch()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the maximum pitch for this speech synthesizer.
|
|
|
|
/// `tts` must be a valid pointer to a TTS object.
|
|
|
|
#[no_mangle]
|
2021-03-06 11:30:42 +00:00
|
|
|
pub unsafe extern "C" fn tts_max_pitch(tts: *const TTS) -> f32 {
|
2020-12-12 20:29:34 +00:00
|
|
|
tts.as_ref().unwrap().max_pitch()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the normal pitch for this speech synthesizer.
|
|
|
|
/// `tts` must be a valid pointer to a TTS object.
|
|
|
|
#[no_mangle]
|
2021-03-06 11:30:42 +00:00
|
|
|
pub unsafe extern "C" fn tts_normal_pitch(tts: *const TTS) -> f32 {
|
2020-12-12 20:29:34 +00:00
|
|
|
tts.as_ref().unwrap().normal_pitch()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the current speech pitch.
|
|
|
|
/// Returns true on success, false on error (likely that the backend doesn't support pitch changes)
|
|
|
|
/// or if `tts` is NULL.
|
|
|
|
/// Does nothing if `pitch` is NULL.
|
|
|
|
#[no_mangle]
|
2021-03-06 11:30:42 +00:00
|
|
|
pub unsafe extern "C" fn tts_get_pitch(tts: *const TTS, pitch: *mut f32) -> bool {
|
2020-12-12 20:29:34 +00:00
|
|
|
if tts.is_null() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
match tts.as_ref().unwrap().get_pitch() {
|
|
|
|
Ok(r) => {
|
|
|
|
if !pitch.is_null() {
|
|
|
|
*pitch = r;
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
set_last_error(e.to_string()).unwrap();
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the desired speech pitch.
|
|
|
|
/// Returns true on success, false on error (likely that the backend doesn't support pitch changes)
|
|
|
|
/// or if `tts` is NULL.
|
|
|
|
#[no_mangle]
|
2021-03-06 11:30:42 +00:00
|
|
|
pub unsafe extern "C" fn tts_set_pitch(tts: *mut TTS, pitch: f32) -> bool {
|
2020-12-12 20:29:34 +00:00
|
|
|
if tts.is_null() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
match tts.as_mut().unwrap().set_pitch(pitch) {
|
|
|
|
Ok(_) => true,
|
|
|
|
Err(e) => {
|
|
|
|
set_last_error(e.to_string()).unwrap();
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-12 20:31:30 +00:00
|
|
|
|
|
|
|
/// Returns the minimum volume for this speech synthesizer.
|
|
|
|
/// `tts` must be a valid pointer to a TTS object.
|
|
|
|
#[no_mangle]
|
2021-03-06 11:30:42 +00:00
|
|
|
pub unsafe extern "C" fn tts_min_volume(tts: *const TTS) -> f32 {
|
2020-12-12 20:31:30 +00:00
|
|
|
tts.as_ref().unwrap().min_volume()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the maximum volume for this speech synthesizer.
|
|
|
|
/// `tts` must be a valid pointer to a TTS object.
|
|
|
|
#[no_mangle]
|
2021-03-06 11:30:42 +00:00
|
|
|
pub unsafe extern "C" fn tts_max_volume(tts: *const TTS) -> f32 {
|
2020-12-12 20:31:30 +00:00
|
|
|
tts.as_ref().unwrap().max_volume()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the normal volume for this speech synthesizer.
|
|
|
|
/// `tts` must be a valid pointer to a TTS object.
|
|
|
|
#[no_mangle]
|
2021-03-06 11:30:42 +00:00
|
|
|
pub unsafe extern "C" fn tts_normal_volume(tts: *const TTS) -> f32 {
|
2020-12-12 20:31:30 +00:00
|
|
|
tts.as_ref().unwrap().normal_volume()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the current speech volume.
|
|
|
|
/// Returns true on success, false on error (likely that the backend doesn't support volume changes)
|
|
|
|
/// or if `tts` is NULL.
|
|
|
|
/// Does nothing if `volume` is NULL.
|
|
|
|
#[no_mangle]
|
2021-03-06 11:30:42 +00:00
|
|
|
pub unsafe extern "C" fn tts_get_volume(tts: *const TTS, volume: *mut f32) -> bool {
|
2020-12-12 20:31:30 +00:00
|
|
|
if tts.is_null() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
match tts.as_ref().unwrap().get_volume() {
|
|
|
|
Ok(r) => {
|
|
|
|
if !volume.is_null() {
|
|
|
|
*volume = r;
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
set_last_error(e.to_string()).unwrap();
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the desired speech volume.
|
|
|
|
/// Returns true on success, false on error (likely that the backend doesn't support volume changes)
|
|
|
|
/// or if `tts` is NULL.
|
|
|
|
#[no_mangle]
|
2021-03-06 11:30:42 +00:00
|
|
|
pub unsafe extern "C" fn tts_set_volume(tts: *mut TTS, volume: f32) -> bool {
|
2020-12-12 20:31:30 +00:00
|
|
|
if tts.is_null() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
match tts.as_mut().unwrap().set_volume(volume) {
|
|
|
|
Ok(_) => true,
|
|
|
|
Err(e) => {
|
|
|
|
set_last_error(e.to_string()).unwrap();
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-12 20:44:44 +00:00
|
|
|
|
|
|
|
/// fills `speaking` with a bool indicating whether this speech synthesizer is speaking.
|
|
|
|
/// Returns true on success, false on error (likely that the backend doesn't support speaking
|
|
|
|
/// status) or if `tts` is NULL.
|
|
|
|
/// If `speaking` is NULL, returns this value instead, meaning you can't tell the difference
|
|
|
|
/// between an error occuring and the synth not speaking.
|
|
|
|
#[no_mangle]
|
2021-03-06 09:36:45 +00:00
|
|
|
pub unsafe extern "C" fn tts_is_speaking(tts: *const TTS, speaking: *mut bool) -> bool {
|
2020-12-12 20:44:44 +00:00
|
|
|
if tts.is_null() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
match tts.as_ref().unwrap().is_speaking() {
|
|
|
|
Ok(s) => {
|
|
|
|
if speaking.is_null() {
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
*speaking = s;
|
|
|
|
true
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
set_last_error(e.to_string()).unwrap();
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|