eSpeak NG bindings for Rust

FFI bindings to the C library eSpeak NG for Rust
Dependencies
Example
This example shows how you can convert a &str to a Vec
#![allow(non_upper_case_globals)]
use espeakng_sys::*;
use std::os::raw::{c_char, c_short, c_int};
use std::ffi::{c_void, CString};
use std::cell::Cell;
use lazy_static::lazy_static;
use std::sync::{Mutex, MutexGuard};
const VOICE_NAME: &str = "English";
const BUFF_LEN: i32 = 500;
const OPTIONS: i32 = 0;
lazy_static! {
static ref AUDIO_RETURN: Mutex<Cell<Vec<i16>>> = Mutex::new(Cell::new(Vec::default()));
static ref AUDIO_BUFFER: Mutex<Cell<Vec<i16>>> = Mutex::new(Cell::new(Vec::default()));
}
pub struct Spoken {
pub wav: Vec<i16>,
pub sample_rate: i32
}
pub fn speak(text: &str) -> Spoken {
let output: espeak_AUDIO_OUTPUT = espeak_AUDIO_OUTPUT_AUDIO_OUTPUT_RETRIEVAL;
AUDIO_RETURN.plock().set(Vec::default());
AUDIO_BUFFER.plock().set(Vec::default());
let path: *const c_char = std::ptr::null();
let voice_name_cstr = CString::new(VOICE_NAME).expect("Failed to convert &str to CString");
let voice_name = voice_name_cstr.as_ptr();
let sample_rate = unsafe { espeak_Initialize(output, BUFF_LEN, path, OPTIONS) };
unsafe {
espeak_SetVoiceByName(voice_name as *const c_char);
espeak_SetSynthCallback(Some(synth_callback))
}
let text_cstr = CString::new(text).expect("Failed to convert &str to CString");
let position = 0u32;
let position_type: espeak_POSITION_TYPE = 0;
let end_position = 0u32;
let flags = espeakCHARS_AUTO;
let identifier = std::ptr::null_mut();
let user_data = std::ptr::null_mut();
unsafe { espeak_Synth(text_cstr.as_ptr() as *const c_void, BUFF_LEN as size_t, position, position_type, end_position, flags, identifier, user_data); }
match unsafe { espeak_Synchronize() } {
espeak_ERROR_EE_OK => {},
espeak_ERROR_EE_INTERNAL_ERROR => {
todo!()
}
_ => unreachable!()
}
let result = AUDIO_RETURN.plock().take();
Spoken {
wav: result,
sample_rate
}
}
unsafe extern "C" fn synth_callback(wav: *mut c_short, sample_count: c_int, events: *mut espeak_EVENT) -> c_int {
let mut events_copy = events.clone();
let mut elem_count = 0;
while (*events_copy).type_ != espeak_EVENT_TYPE_espeakEVENT_LIST_TERMINATED {
elem_count += 1;
events_copy = events_copy.add(1);
}
let event_slice = std::slice::from_raw_parts_mut(events, elem_count);
let event_vec = event_slice.into_iter()
.map(|f| f.clone())
.collect::<Vec<espeak_EVENT>>();
let wav_slice = std::slice::from_raw_parts_mut(wav, sample_count as usize);
let mut wav_vec = wav_slice.into_iter()
.map(|f| f.clone() as i16)
.collect::<Vec<i16>>();
let mut is_end = false;
for event in event_vec {
if event.type_.eq(&espeak_EVENT_TYPE_espeakEVENT_MSG_TERMINATED) {
is_end = true;
}
}
if is_end {
AUDIO_RETURN.plock().set(AUDIO_BUFFER.plock().take());
} else {
let mut curr_data = AUDIO_BUFFER.plock().take();
curr_data.append(&mut wav_vec);
AUDIO_BUFFER.plock().set(curr_data);
}
0
}
trait PoisonlessLock<T> {
fn plock(&self) -> MutexGuard<T>;
}
impl<T> PoisonlessLock<T> for Mutex<T> {
fn plock(&self) -> MutexGuard<T> {
match self.lock() {
Ok(l) => l,
Err(e) => e.into_inner()
}
}
License
espeakng-sys is dual licensed under the Apache-2.0 and MIT license, at your discretion