package com.kazeia.tts /** * Moteur TTS CosyVoice3 distillé (clonage vocal zero-shot, 9 langues : fr, en, de, es, it, ru, zh, ja, ko). * CPU-only (pas de FastRPC/SELinux). Modèle = GGUF distillé 2-NFE. Voix = artefacts .cvps pré-enrôlés offline. * * Empreinte : ~1 Go (modèle) + ~30 Mo / voix chargée. RTF ~1,8 sur SM8750 (génère par phrase pour streaming). * * Pour le portugais/arabe et les 14 autres langues : voir le moteur Chatterbox (dispatch par langue côté app). */ object CosyVoiceJni { external fun nativeLoad(ggufPath: String, nfe: Int, nThreads: Int): Long external fun nativeLoadVoice(handle: Long, cvpsPath: String): Long external fun nativeSynthesize(voiceHandle: Long, text: String, speed: Float): FloatArray? external fun nativeFreeVoice(voiceHandle: Long) external fun nativeFree(handle: Long) } class CosyVoiceTtsEngine( modelGguf: String, nfe: Int = 2, // pas de flow du student distillé (2 = recommandé, qualité = teacher) nThreads: Int = 8, // V79 : 8 cœurs ) { companion object { const val SAMPLE_RATE = 24_000 @Volatile private var loaded = false fun ensureLibrary() { if (!loaded) { System.loadLibrary("kazeia_cosyvoice"); loaded = true } } } private val handle: Long init { ensureLibrary() handle = CosyVoiceJni.nativeLoad(modelGguf, nfe, nThreads) require(handle != 0L) { "CosyVoice: échec de chargement du modèle $modelGguf" } } /** Charge un profil voix pré-enrôlé (.cvps, généré offline depuis la phrase de consentement). */ fun loadVoice(cvpsPath: String): Voice { val v = CosyVoiceJni.nativeLoadVoice(handle, cvpsPath) require(v != 0L) { "CosyVoice: échec de chargement de la voix $cvpsPath" } return Voice(v) } /** Synthèse zero-shot : texte → PCM float[-1,1] mono 24 kHz. Génère phrase par phrase en amont pour le streaming. */ fun synthesize(voice: Voice, text: String, speed: Float = 1.0f): FloatArray = CosyVoiceJni.nativeSynthesize(voice.handle, text, speed) ?: error("CosyVoice: échec de synthèse") fun release() = CosyVoiceJni.nativeFree(handle) inner class Voice(internal val handle: Long) { fun release() = CosyVoiceJni.nativeFreeVoice(handle) } }