// JNI bridge pour CosyVoice3 distillé (cosyvoice.cpp, CPU-only, pas de FastRPC/SELinux). // Pattern aligne sur kazeia_tts_jni.cpp (package com.kazeia.tts). // load(gguf) -> handle modele ; loadVoice(handle, cvps) -> handle voix ; synthesize(voice, text, speed) -> float PCM 24kHz. #include #include #include #include #include "cosyvoice.h" // NB : on n'appelle AUCUN symbole ggml depuis la JNI (ggml est statique+masque dans libcosyvoice). // -> la JNI ne lie que libcosyvoice.so. Chargement via l'API haut-niveau (backend interne). namespace { struct Voice { cosyvoice_prompt_speech_t ps = nullptr; cosyvoice_prompt_t prompt = nullptr; cosyvoice_tts_context_t tts = nullptr; }; const char* cstr(JNIEnv* env, jstring s, std::string& buf) { const char* p = env->GetStringUTFChars(s, nullptr); buf = p ? p : ""; if (p) env->ReleaseStringUTFChars(s, p); return buf.c_str(); } } // namespace extern "C" { // nativeLoad(gguf, nfe, nThreads) : configure le mode reflow distille + charge le modele. Retourne le handle (0 si echec). JNIEXPORT jlong JNICALL Java_com_kazeia_tts_CosyVoiceJni_nativeLoad(JNIEnv* env, jobject, jstring jgguf, jint nfe, jint nThreads) { std::string gguf; cstr(env, jgguf, gguf); // mode student distille : schedule lineaire + CFG off + N pas (lu par le loader patche) setenv("CV_REFLOW", "1", 1); char nbuf[8]; snprintf(nbuf, sizeof(nbuf), "%d", nfe > 0 ? (int)nfe : 2); setenv("CV_NFE", nbuf, 1); // streaming chunk-causal + cache K/V prompt persistant (2c) : flow ÷4 sur les tours 2+ (meme voix), // RTF<1 soutenu sur V79. CV_PROMPT_DET = bruit prompt deterministe (prerequis du cache, qualite preservee). setenv("CV_STREAM", "1", 1); setenv("CV_STREAM_CACHE", "1", 1); setenv("CV_PROMPT_DET", "1", 1); // enregistrement des backends ggml (interne a libcosyvoice ; indispensable avant load) cosyvoice_init_backend_from_path(nullptr); cosyvoice_context_params_t params; cosyvoice_init_default_context_params(¶ms); // backend par defaut (CPU) + threads = concurrence materielle (8 sur V79) gere en interne. // nThreads conserve dans l'API mais non force ici (le defaut = nb de coeurs, optimal V79). (void)nThreads; cosyvoice_context_t ctx = cosyvoice_load_from_file_with_params(gguf.c_str(), ¶ms); return reinterpret_cast(ctx); } // nativeLoadVoice(handle, cvps) : charge un profil voix pre-enrole. Retourne le handle voix (0 si echec). JNIEXPORT jlong JNICALL Java_com_kazeia_tts_CosyVoiceJni_nativeLoadVoice(JNIEnv* env, jobject, jlong handle, jstring jcvps) { auto ctx = reinterpret_cast(handle); if (!ctx) return 0; std::string cvps; cstr(env, jcvps, cvps); auto* v = new Voice(); v->ps = cosyvoice_prompt_speech_load_from_file(cvps.c_str()); if (!v->ps) { delete v; return 0; } v->prompt = cosyvoice_prompt_init_from_prompt_speech(ctx, v->ps); if (!v->prompt) { cosyvoice_prompt_speech_free(v->ps); delete v; return 0; } v->tts = cosyvoice_tts_context_new(ctx, v->prompt); if (!v->tts) { cosyvoice_prompt_free(v->prompt); cosyvoice_prompt_speech_free(v->ps); delete v; return 0; } // build CPU-only = NO_ICU : la normalisation de texte ICU n'est pas dispo (normaliser cote app si besoin). // split = true : decoupe par phrase (premier son plus rapide, streaming). cosyvoice_tts_context_set_text_normalization_enabled(v->tts, false); cosyvoice_tts_context_set_split_text_enabled(v->tts, true); return reinterpret_cast(v); } // nativeSynthesize(voice, text, speed) : synthese zero-shot. Retourne float[] PCM 24kHz mono (null si echec). JNIEXPORT jfloatArray JNICALL Java_com_kazeia_tts_CosyVoiceJni_nativeSynthesize(JNIEnv* env, jobject, jlong voiceHandle, jstring jtext, jfloat speed) { auto* v = reinterpret_cast(voiceHandle); if (!v || !v->tts) return nullptr; std::string text; cstr(env, jtext, text); cosyvoice_generated_speech result = {}; if (!cosyvoice_tts_zero_shot(v->tts, text.c_str(), speed > 0 ? speed : 1.0f, &result) || !result.data || result.length == 0) { return nullptr; } jfloatArray out = env->NewFloatArray(result.length); if (out) env->SetFloatArrayRegion(out, 0, result.length, result.data); return out; } JNIEXPORT void JNICALL Java_com_kazeia_tts_CosyVoiceJni_nativeFreeVoice(JNIEnv*, jobject, jlong voiceHandle) { auto* v = reinterpret_cast(voiceHandle); if (!v) return; if (v->tts) cosyvoice_tts_context_free(v->tts); if (v->prompt) cosyvoice_prompt_free(v->prompt); if (v->ps) cosyvoice_prompt_speech_free(v->ps); delete v; } JNIEXPORT void JNICALL Java_com_kazeia_tts_CosyVoiceJni_nativeFree(JNIEnv*, jobject, jlong handle) { auto ctx = reinterpret_cast(handle); if (ctx) cosyvoice_free(ctx); } } // extern "C"