use super::{
select_clips, wav_bytes, CloneHandle, CloneOutcome, CloningMode, VoiceCloning, VoiceIdentity,
};
use crate::qwen3_local_engine::Qwen3LocalEngine;
use crate::qwen3_local_support::encode_embedding;
use crate::types::{TtsError, TtsResult};
use std::collections::HashMap;
pub(crate) struct Qwen3LocalCloner {
engine: Qwen3LocalEngine,
}
impl Qwen3LocalCloner {
pub(crate) fn new(credentials: &HashMap<String, String>) -> TtsResult<Self> {
Ok(Self {
engine: Qwen3LocalEngine::new(credentials)?,
})
}
}
impl VoiceCloning for Qwen3LocalCloner {
fn engine_id(&self) -> &'static str {
"qwen3-local"
}
fn cloning_mode(&self) -> CloningMode {
CloningMode::ZeroShot
}
fn clone_voice(&self, identity: &VoiceIdentity) -> TtsResult<CloneOutcome> {
let picked = select_clips(&identity.clips, 10);
let Some(clip) = picked.first() else {
return Err(TtsError(
"qwen3-local cloning: identity has no clips".into(),
));
};
let wav = wav_bytes(&clip.pcm, clip.sample_rate);
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
let path = std::env::temp_dir().join(format!(
"rust-tts-qwen3-emb-{}-{nanos}.wav",
std::process::id()
));
std::fs::write(&path, &wav).map_err(|e| TtsError(format!("write temp reference: {e}")))?;
let result = self.engine.extract_embedding(&path.to_string_lossy());
let _ = std::fs::remove_file(&path);
let embedding = result?;
Ok(CloneOutcome::Ready(CloneHandle {
engine: "qwen3-local".into(),
voice_id: encode_embedding(&embedding),
model: None,
}))
}
fn list_cloned(&self) -> TtsResult<Vec<CloneHandle>> {
Ok(Vec::new())
}
fn delete_cloned(&self, _handle: &CloneHandle) -> TtsResult<()> {
Ok(())
}
}