use crate::error::Result;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
pub const DEFAULT_SAMPLE_RATE_HZ: u32 = 24_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BackendKind {
Local,
}
#[derive(Debug, Clone)]
pub struct SynthesisOptions {
pub model: String,
pub voice: String,
pub language: String,
pub sample_rate_hz: Option<u32>,
pub speaking_rate: f32,
pub timeout_ms: u64,
pub cancel: Option<crate::cancel::CancelFlag>,
pub local_only: bool,
pub pack_dir: Option<std::path::PathBuf>,
pub allow_unverified: bool,
}
impl Default for SynthesisOptions {
fn default() -> Self {
Self {
model: crate::tts::DEFAULT_TTS_MODEL.to_string(),
voice: crate::tts::DEFAULT_TTS_VOICE.to_string(),
language: "en".to_string(),
sample_rate_hz: None,
speaking_rate: 1.0,
timeout_ms: crate::tts::DEFAULT_TIMEOUT_MS,
cancel: None,
local_only: false,
pack_dir: None,
allow_unverified: false,
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct SynthesisResult {
#[serde(skip)]
pub pcm_i16_mono: Vec<i16>,
pub sample_rate_hz: u32,
pub channels: u16,
pub backend_kind: BackendKind,
pub provider: String,
pub model: String,
pub voice: String,
pub language: String,
pub duration_ms: u64,
pub text_chars: usize,
pub text_truncated: bool,
pub chunk_count: usize,
pub synthesized_chars: usize,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub adapter: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trust: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provenance: Option<String>,
}
#[async_trait]
pub trait SynthesisProvider: Send + Sync {
fn name(&self) -> &'static str;
async fn synthesize(&self, text: &str, opts: &SynthesisOptions) -> Result<SynthesisResult>;
async fn preload(&self, model: &str, voice: &str) -> Result<()>;
}