//! 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(); } }