Files
rustytorch/crates/models/rtx-tts/src/lib.rs
T
osobhandClaude Sonnet 5 4aaa36a57a style: cargo fmt --workspace (whitespace/wrapping only, no semantic change)
Whole-workspace rustfmt pass picked up while iterating on Mamba GPU
backward work. Verified formatting-only via diff sampling; no logic
changed.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-10 07:09:36 -07:00

224 lines
6.4 KiB
Rust

//! Text-to-Speech (TTS) synthesis for RustyTorch
//!
//! This crate provides comprehensive text-to-speech synthesis capabilities including:
//!
//! # Features
//!
//! - **Text Processing**: Advanced text normalization, phonemization, and prosody prediction
//! - **Neural Vocoding**: GPU-accelerated neural vocoders for high-quality speech synthesis
//! - **Multi-language Support**: Extensible framework for multiple languages and voices
//! - **Real-time Synthesis**: Optimized for low-latency speech generation
//!
//! # Architecture
//!
//! The TTS pipeline consists of several stages:
//!
//! 1. **Text Processing** (`text` module):
//! - Text normalization (case, punctuation, abbreviations, numbers)
//! - Grapheme-to-phoneme (G2P) conversion
//! - Prosody prediction (pauses, emphasis, intonation)
//!
//! 2. **Acoustic Modeling** (`acoustic` module):
//! - Mel-spectrogram generation from phonemes
//! - Duration and pitch prediction with variance adaptors
//! - FastSpeech2 and Tacotron2 models
//!
//! 3. **Vocoding** (`vocoder` module):
//! - Neural vocoders (HiFi-GAN) for high-quality synthesis
//! - Griffin-Lim algorithm for fast baseline vocoding
//! - GPU-accelerated waveform generation
//!
//! # Example
//!
//! ```rust
//! use rtx_tts::text::{TextPipeline, TextNormalizerConfig};
//!
//! # fn main() -> rtx_tts::error::Result<()> {
//! // Create a text processing pipeline
//! let pipeline = TextPipeline::default();
//!
//! // Process input text
//! let text = "Hello, world! I have 3 apples.";
//! let processed = pipeline.process_full(text)?;
//!
//! println!("Normalized: {}", processed.normalized);
//! println!("Phonemes: {}", processed.phoneme_string(true));
//! # Ok(())
//! # }
//! ```
//!
//! # Text Normalization
//!
//! The text normalizer handles various text transformations:
//!
//! ```rust
//! use rtx_tts::text::{TextNormalizer, TextNormalizerConfig};
//!
//! # fn main() -> rtx_tts::error::Result<()> {
//! let config = TextNormalizerConfig::default();
//! let normalizer = TextNormalizer::new(config);
//!
//! let result = normalizer.normalize("Dr. Smith has 50 dollars and 3 cats")?;
//! assert_eq!(result, "doctor smith has fifty dollars and three cats");
//! # Ok(())
//! # }
//! ```
//!
//! # Phonemization
//!
//! Convert text to phonemes using ARPAbet notation:
//!
//! ```rust
//! use rtx_tts::text::{Phonemizer, PhonemizerConfig};
//!
//! # fn main() -> rtx_tts::error::Result<()> {
//! let phonemizer = Phonemizer::default();
//! let phonemes = phonemizer.g2p("hello world")?;
//!
//! // Each word gets its own phoneme sequence
//! assert_eq!(phonemes.len(), 2);
//! # Ok(())
//! # }
//! ```
//!
//! # Prosody Prediction
//!
//! Predict prosody markers for natural-sounding speech:
//!
//! ```rust
//! use rtx_tts::text::{Prosody, ProsodyConfig, ProsodyMarker};
//!
//! # fn main() -> rtx_tts::error::Result<()> {
//! let prosody = Prosody::default();
//! let annotations = prosody.predict("How are you?")?;
//!
//! // Questions should have rising intonation
//! let has_rising = annotations.iter().any(|a| {
//! a.markers.iter().any(|m| matches!(m, ProsodyMarker::Rising))
//! });
//! assert!(has_rising);
//! # Ok(())
//! # }
//! ```
//!
//! # Acoustic Modeling
//!
//! Convert phoneme sequences to mel spectrograms:
//!
//! ```rust,ignore
//! use rtx_tts::acoustic::{AcousticModel, FastSpeech2, FastSpeech2Config};
//! use rtx_tensor::{Tensor, Device};
//!
//! # fn main() -> rtx_tts::error::Result<()> {
//! let device = Device::default();
//! let config = FastSpeech2Config::default();
//! let model = FastSpeech2::new(config, &device)?;
//!
//! // Convert phoneme IDs to mel spectrogram
//! let phoneme_ids = Tensor::from_slice(&[1, 2, 3, 4], &[1, 4], &device)?;
//! let mel = model.forward(&phoneme_ids, None)?;
//! # Ok(())
//! # }
//! ```
//!
//! # Vocoding
//!
//! Convert mel spectrograms to audio waveforms:
//!
//! ```rust
//! use rtx_tts::vocoder::{GriffinLim, GriffinLimConfig, Vocoder};
//! use rtx_tensor::{Tensor, Device};
//!
//! # fn main() -> rtx_tts::error::Result<()> {
//! let device = Device::cpu();
//! let config = GriffinLimConfig::default();
//! let vocoder = GriffinLim::new(config, &device)?;
//!
//! // Synthesize audio from mel spectrogram
//! let mel = Tensor::randn(&[80, 100], &device).unwrap();
//! let audio = vocoder.synthesize(&mel)?;
//! # Ok(())
//! # }
//! ```
#![deny(missing_docs)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::missing_panics_doc)]
pub mod acoustic;
pub mod error;
pub mod text;
pub mod vocoder;
// Re-export commonly used types
pub use acoustic::{
AcousticModel, FastSpeech2, FastSpeech2Config, MelSpectrogramConfig, Tacotron2,
Tacotron2Config, VarianceAdaptor, VarianceAdaptorConfig,
};
pub use error::{Result, TtsError};
pub use text::{
Phoneme, Phonemizer, PhonemizerBackend, PhonemizerConfig, ProcessedText, Prosody,
ProsodyAnnotation, ProsodyConfig, ProsodyMarker, TextNormalizer, TextNormalizerConfig,
TextPipeline,
};
pub use vocoder::{
GriffinLim, GriffinLimConfig, HiFiGAN, HiFiGANConfig, HiFiGANGenerator, MelConfig,
MelSpectrogram, Vocoder, VocoderConfig,
};
/// TTS framework version
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
/// Get the version string
#[must_use]
pub fn version() -> &'static str {
VERSION
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_version() {
let version = version();
assert!(!version.is_empty());
}
#[test]
fn test_full_pipeline() {
let pipeline = TextPipeline::default();
let result = pipeline.process_full("Hello, world!").unwrap();
assert!(!result.original.is_empty());
assert!(!result.normalized.is_empty());
}
#[test]
fn test_re_exports() {
// Test that re-exports are working
let _normalizer = TextNormalizer::default();
let _phonemizer = Phonemizer::default();
let _prosody = Prosody::default();
let _pipeline = TextPipeline::default();
}
#[test]
fn test_vocoder_re_exports() {
use rtx_tensor::Device;
let device = Device::cpu();
// Test vocoder re-exports
let gl_config = GriffinLimConfig::default();
let _gl = GriffinLim::new(gl_config, &device).unwrap();
let hg_config = HiFiGANConfig::default();
let _hg = HiFiGAN::new(hg_config, &device).unwrap();
let mel_config = MelConfig::default();
let _mel = MelSpectrogram::new(mel_config, &device).unwrap();
}
}