use std::collections::BTreeMap;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::mpsc::Sender;
use std::thread::JoinHandle;
use crate::voxcpm::tokenizer::VoxCpm2Tokenizer;
use numr::dtype::DType;
use numr::ops::{
ActivationOps, BinaryOps, CompareOps, ConditionalOps, HostCopyOps, IndexingOps, RandomOps,
ReduceOps, ScalarOps, ShapeOps, TensorOps, TypeConversionOps, UnaryOps,
};
use numr::runtime::Runtime;
use numr::tensor::Tensor;
use boostr::model::audio::voxcpm::VoxCpmClient;
use boostr::model::audio::voxcpm::model::{LoraAdapterReport, VoxCpm2Model};
use boostr::model::audio::voxcpm::vae::decoder::HOP_LENGTH;
use boostr::quant::traits::DequantOps;
use crate::voxcpm::engine::batch::Work;
use crate::voxcpm::options::VoxCpm2SynthOptions;
pub(super) const REF_RATE: u32 = 16_000;
pub(super) const MAX_LEN_CAP: usize = 4096;
pub const VOICE_EXTENSIONS: [&str; 4] = ["wav", "flac", "mp3", "ogg"];
pub const ZERO_SHOT_VOICE_ID: &str = "zero-shot";
pub trait VoxCpm2RenderClient<R: Runtime<DType = DType>>:
VoxCpmClient<R>
+ TensorOps<R>
+ ScalarOps<R>
+ ReduceOps<R>
+ IndexingOps<R>
+ ShapeOps<R>
+ ActivationOps<R>
+ BinaryOps<R>
+ UnaryOps<R>
+ CompareOps<R>
+ ConditionalOps<R>
+ TypeConversionOps<R>
+ RandomOps<R>
+ HostCopyOps<R>
+ DequantOps<R>
+ Send
+ Sync
+ 'static
{
}
impl<R, C> VoxCpm2RenderClient<R> for C
where
R: Runtime<DType = DType>,
C: VoxCpmClient<R>
+ TensorOps<R>
+ ScalarOps<R>
+ ReduceOps<R>
+ IndexingOps<R>
+ ShapeOps<R>
+ ActivationOps<R>
+ BinaryOps<R>
+ UnaryOps<R>
+ CompareOps<R>
+ ConditionalOps<R>
+ TypeConversionOps<R>
+ RandomOps<R>
+ HostCopyOps<R>
+ DequantOps<R>
+ Send
+ Sync
+ 'static,
{
}
pub(super) struct EncodedVoice<R: Runtime> {
pub(super) ref_feat: Tensor<R>,
}
pub(super) struct EngineInner<R: Runtime<DType = DType>> {
pub(super) model: VoxCpm2Model<R>,
pub(super) client: Arc<R::Client>,
pub(super) tokenizer: VoxCpm2Tokenizer,
pub(super) voices: BTreeMap<String, EncodedVoice<R>>,
pub(super) options: VoxCpm2SynthOptions,
pub(super) adapter: Option<LoraAdapterReport>,
pub(super) id: u64,
}
pub(super) fn next_engine_id() -> u64 {
static NEXT: AtomicU64 = AtomicU64::new(0);
NEXT.fetch_add(1, Ordering::Relaxed)
}
pub struct VoxCpm2Engine<R: Runtime<DType = DType>> {
pub(super) inner: Arc<EngineInner<R>>,
pub(super) jobs: Option<Sender<Work<R>>>,
pub(super) worker: Option<JoinHandle<()>>,
}
impl<R: Runtime<DType = DType>> VoxCpm2Engine<R> {
pub fn adapter(&self) -> Option<&LoraAdapterReport> {
self.inner.adapter.as_ref()
}
pub fn samples_per_patch(&self) -> usize {
self.inner.model.config.patch_size * HOP_LENGTH
}
}
impl<R: Runtime<DType = DType>> Drop for VoxCpm2Engine<R> {
fn drop(&mut self) {
self.jobs.take();
if let Some(worker) = self.worker.take() {
let _ = worker.join();
}
}
}