use std::ffi::{c_char, c_void, CString};
use std::os::raw::{c_int, c_long};
use std::ptr;
use std::sync::Mutex;
use audio_cpp_sys::*;
use crate::error::Error;
use crate::ffi;
use crate::types::{StreamEvent, StreamingPolicy, TaskResult};
struct EventSinkInner {
cb: Mutex<Box<dyn FnMut(StreamEvent) + Send>>,
}
unsafe extern "C" fn stream_event_cb(user_data: *mut c_void, event_json: *const c_char, _is_final: c_int) {
if user_data.is_null() || event_json.is_null() {
return;
}
let inner = user_data.cast::<EventSinkInner>();
let json = unsafe { std::ffi::CStr::from_ptr(event_json) }
.to_string_lossy()
.into_owned();
let event = match serde_json::from_str::<StreamEvent>(&json) {
Ok(e) => e,
Err(_) => return, };
let mut guard = match unsafe { (*inner).cb.lock() } {
Ok(g) => g,
Err(poisoned) => poisoned.into_inner(),
};
(guard)(event);
}
pub struct Session {
raw: *mut audiocpp_session,
event_sink: Option<*mut EventSinkInner>,
}
unsafe impl Send for Session {}
impl Session {
pub(crate) fn from_raw(raw: *mut audiocpp_session) -> Self {
Self { raw, event_sink: None }
}
pub fn family(&self) -> String {
unsafe {
let p = audiocpp_session_family(self.raw);
if p.is_null() {
String::new()
} else {
std::ffi::CStr::from_ptr(p).to_string_lossy().into_owned()
}
}
}
pub fn task_kind(&self) -> String {
unsafe {
let p = audiocpp_session_task_kind(self.raw);
if p.is_null() {
String::new()
} else {
std::ffi::CStr::from_ptr(p).to_string_lossy().into_owned()
}
}
}
pub fn run_mode(&self) -> String {
unsafe {
let p = audiocpp_session_run_mode(self.raw);
if p.is_null() {
String::new()
} else {
std::ffi::CStr::from_ptr(p).to_string_lossy().into_owned()
}
}
}
pub fn streaming_policy(&self) -> Result<StreamingPolicy, Error> {
let mut out: *mut c_char = ptr::null_mut();
ffi::check_rc(unsafe { audiocpp_session_streaming_policy_json(self.raw, &mut out) })?;
let json = unsafe { ffi::take_string(out)? };
serde_json::from_str(&json).map_err(Error::from)
}
pub fn prepare(&self, request_json: Option<&str>) -> Result<(), Error> {
let req_c = match request_json {
Some(s) => ffi::cstring(s)?,
None => CString::new("{}").expect("'{}' 不含 NUL"),
};
ffi::check_rc(unsafe { audiocpp_session_prepare(self.raw, req_c.as_ptr() as *const c_char) })
}
pub fn set_event_callback<F>(&mut self, cb: Option<F>)
where
F: FnMut(StreamEvent) + Send + 'static,
{
if let Some(old) = self.event_sink.take() {
unsafe {
audiocpp_session_set_event_sink(self.raw, None, ptr::null_mut());
drop(Box::from_raw(old));
}
}
if let Some(cb) = cb {
let inner = Box::into_raw(Box::new(EventSinkInner {
cb: Mutex::new(Box::new(cb)),
}));
unsafe {
audiocpp_session_set_event_sink(self.raw, Some(stream_event_cb), inner.cast::<c_void>());
}
self.event_sink = Some(inner);
} else {
self.event_sink = None;
}
}
pub fn start(&self, request_json: Option<&str>) -> Result<(), Error> {
let req_c = match request_json {
Some(s) => ffi::cstring(s)?,
None => CString::new("{}").expect("'{}' 不含 NUL"),
};
ffi::check_rc(unsafe { audiocpp_session_start(self.raw, req_c.as_ptr() as *const c_char) })
}
pub fn process_audio(
&self,
samples: &[f32],
sample_rate: i32,
channels: i32,
start_sample: i64,
) -> Result<Option<StreamEvent>, Error> {
let mut out: *mut c_char = ptr::null_mut();
ffi::check_rc(unsafe {
audiocpp_session_process_audio(
self.raw,
samples.as_ptr(),
samples.len(),
sample_rate,
channels,
start_sample,
&mut out,
)
})?;
if out.is_null() {
return Ok(None);
}
let json = unsafe { ffi::take_string(out)? };
Ok(Some(serde_json::from_str(&json).map_err(Error::from)?))
}
pub fn finish(&self) -> Result<TaskResult, Error> {
let mut out: *mut c_char = ptr::null_mut();
ffi::check_rc(unsafe { audiocpp_session_finish(self.raw, &mut out) })?;
if out.is_null() {
return Ok(TaskResult {
speech_segments: Vec::new(),
speaker_turns: Vec::new(),
text_output: None,
audio_output: None,
named_audio_outputs: Vec::new(),
});
}
let json = unsafe { ffi::take_string(out)? };
serde_json::from_str(&json).map_err(Error::from)
}
pub fn run_offline(&self, request_json: &str) -> Result<TaskResult, Error> {
let req_c = ffi::cstring(request_json)?;
let mut out: *mut c_char = ptr::null_mut();
ffi::check_rc(unsafe { audiocpp_session_run_offline(self.raw, req_c.as_ptr() as *const c_char, &mut out) })?;
if out.is_null() {
return Err(Error::Ffi(ffi::last_error()));
}
let json = unsafe { ffi::take_string(out)? };
serde_json::from_str(&json).map_err(Error::from)
}
pub fn reset(&self) {
unsafe {
audiocpp_session_reset(self.raw);
}
}
}
impl Drop for Session {
fn drop(&mut self) {
if let Some(inner) = self.event_sink.take() {
unsafe {
audiocpp_session_set_event_sink(self.raw, None, ptr::null_mut());
drop(Box::from_raw(inner));
}
}
unsafe {
audiocpp_session_free(self.raw);
}
}
}
#[allow(dead_code)]
type RawLong = c_long;