car-voice 0.14.0

Voice I/O capability for CAR — mic capture, VAD, listener/speaker traits
Documentation
//! Provider factory — builds the right STT/TTS backend from config.

use crate::config::{SttProvider as SttProviderEnum, TtsProvider as TtsProviderEnum};
use crate::elevenlabs_stt::ElevenLabsSttProvider;
use crate::elevenlabs_tts::ElevenLabsSpeaker;
use crate::local_tts::LocalTtsSpeaker;
use crate::stt::SttProvider;
use crate::tts::Speaker;
use crate::whisper_cpp_stt::WhisperCppSttProvider;
use crate::{Result, VoiceConfig};

/// Build a boxed [`SttProvider`] from the current config.
pub fn build_stt_provider(config: &VoiceConfig) -> Result<Box<dyn SttProvider>> {
    match config.stt_provider {
        SttProviderEnum::Elevenlabs => {
            let provider = ElevenLabsSttProvider::from_config(config)?;
            Ok(Box::new(provider))
        }
        SttProviderEnum::WhisperCpp => {
            let provider = WhisperCppSttProvider::from_config(config)?;
            Ok(Box::new(provider))
        }
        SttProviderEnum::Parakeet => {
            #[cfg(feature = "parakeet")]
            {
                let dir = crate::parakeet_stt::ParakeetSttProvider::default_model_dir()
                    .map_err(|e| crate::VoiceError::Stt(e.to_string()))?;
                Ok(Box::new(crate::parakeet_stt::ParakeetSttProvider::new(dir)))
            }
            #[cfg(not(feature = "parakeet"))]
            {
                let _ = config; // silence unused warning when parakeet is off
                Err(crate::VoiceError::Config(
                    "Parakeet provider requires the `parakeet` cargo feature \
                     (pulls in `ort` + `ndarray`)"
                        .into(),
                ))
            }
        }
        SttProviderEnum::AppleSpeech => {
            #[cfg(target_os = "macos")]
            {
                Ok(Box::new(
                    crate::apple_speech_stt::AppleSpeechSttProvider::from_config(config),
                ))
            }
            #[cfg(not(target_os = "macos"))]
            {
                Err(crate::VoiceError::Config(
                    "Apple Speech STT is macOS-only; use `whisper_cpp` or `elevenlabs` \
                     on this platform"
                        .into(),
                ))
            }
        }
    }
}

/// Build a boxed [`Speaker`] from the current config.
pub fn build_tts_speaker(config: &VoiceConfig) -> Result<Box<dyn Speaker>> {
    match config.tts_provider {
        TtsProviderEnum::Elevenlabs => {
            let speaker = ElevenLabsSpeaker::from_config(config)?;
            Ok(Box::new(speaker))
        }
        TtsProviderEnum::Local => {
            let speaker = LocalTtsSpeaker::from_config(config);
            Ok(Box::new(speaker))
        }
        TtsProviderEnum::Kokoro => {
            #[cfg(all(target_os = "macos", target_arch = "aarch64", not(car_skip_mlx)))]
            {
                let speaker = crate::kokoro_tts::KokoroSpeaker::from_config(config)?;
                Ok(Box::new(speaker))
            }
            #[cfg(not(all(target_os = "macos", target_arch = "aarch64", not(car_skip_mlx))))]
            {
                Err(crate::VoiceError::Config(
                    "Kokoro TTS is Apple Silicon macOS only (needs MLX/Metal); \
                     use `local` or `elevenlabs` instead"
                        .into(),
                ))
            }
        }
        TtsProviderEnum::AppleSpeech => {
            #[cfg(target_os = "macos")]
            {
                Ok(Box::new(
                    crate::apple_speech_tts::AppleSpeechSpeaker::from_config(config),
                ))
            }
            #[cfg(not(target_os = "macos"))]
            {
                Err(crate::VoiceError::Config(
                    "Apple Speech TTS is macOS-only; use `local` or `elevenlabs` \
                     on this platform"
                        .into(),
                ))
            }
        }
    }
}