use crate::{
AicError, Model, OtelConfig, Processor, ProcessorConfig, ProcessorContext, VadContext,
};
use async_lock::Mutex;
use futures_channel::oneshot;
use std::sync::{Arc, OnceLock};
static RAYON_POOL: OnceLock<rayon::ThreadPool> = OnceLock::new();
fn get_global_thread_pool() -> &'static rayon::ThreadPool {
RAYON_POOL.get_or_init(|| {
let num_threads = std::env::var("AIC_NUM_THREADS")
.ok()
.and_then(|s| s.parse::<usize>().ok())
.filter(|&n| n > 0)
.unwrap_or_else(|| {
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1)
});
rayon::ThreadPoolBuilder::new()
.num_threads(num_threads)
.thread_name(|i| format!("aic-processing-thread-{i}"))
.build()
.expect("failed to build aic thread pool")
})
}
pub struct ProcessorAsync {
inner: Arc<Mutex<Processor<'static>>>,
}
impl ProcessorAsync {
pub fn new(model: &Model<'static>, license_key: &str) -> Result<Self, AicError> {
let processor = Processor::new(model, license_key)?;
Ok(Self {
inner: Arc::new(Mutex::new(processor)),
})
}
pub fn with_otel_config(
model: &Model<'static>,
license_key: &str,
otel_config: &OtelConfig,
) -> Result<Self, AicError> {
let processor = Processor::with_otel_config(model, license_key, otel_config)?;
Ok(Self {
inner: Arc::new(Mutex::new(processor)),
})
}
pub async fn with_config(self, config: &ProcessorConfig) -> Result<Self, AicError> {
self.initialize(config).await?;
Ok(self)
}
pub async fn initialize(&self, config: &ProcessorConfig) -> Result<(), AicError> {
let config = config.clone();
let (tx, rx) = oneshot::channel();
let mut processor = self.inner.lock_arc().await;
get_global_thread_pool().spawn(move || {
let _ = tx.send(processor.initialize(&config));
});
rx.await.expect("Rayon worker dropped")
}
pub async fn process_interleaved(&self, mut audio: Vec<f32>) -> Result<Vec<f32>, AicError> {
let (tx, rx) = oneshot::channel();
let mut processor = self.inner.lock_arc().await;
get_global_thread_pool().spawn(move || {
let result = processor.process_interleaved(&mut audio).map(|_| audio);
let _ = tx.send(result);
});
rx.await.expect("Rayon worker dropped")
}
pub async fn process_planar(
&self,
mut audio: Vec<Vec<f32>>,
) -> Result<Vec<Vec<f32>>, AicError> {
let (tx, rx) = oneshot::channel();
let mut processor = self.inner.lock_arc().await;
get_global_thread_pool().spawn(move || {
let result = processor.process_planar(&mut audio).map(|_| audio);
let _ = tx.send(result);
});
rx.await.expect("Rayon worker dropped")
}
pub async fn process_sequential(&self, mut audio: Vec<f32>) -> Result<Vec<f32>, AicError> {
let (tx, rx) = oneshot::channel();
let mut processor = self.inner.lock_arc().await;
get_global_thread_pool().spawn(move || {
let result = processor.process_sequential(&mut audio).map(|_| audio);
let _ = tx.send(result);
});
rx.await.expect("Rayon worker dropped")
}
pub async fn processor_context(&self) -> ProcessorContext {
self.inner.lock().await.processor_context()
}
pub async fn vad_context(&self) -> VadContext {
self.inner.lock().await.vad_context()
}
}