// JNI bridge pour libkazeia_stt.so. Thin wrapper autour de stt_engine.{h,cpp}. // Le handle exposé côté Kotlin est un jlong = (jlong)(SttEngine*). // // Signatures (à matcher dans SttEngine.kt) : // nativeLoad(modelDir: String, useHtp: Boolean, nThreads: Int, maxDecodeSteps: Int) : Long // nativeTranscribe(handle: Long, pcm: ShortArray, sampleRate: Int, // language: String, forceTranscribe: Boolean) // : String // "{err}|{text}|{mel_ms}|{enc_ms}|{dec_ms}|{total_ms}|{n_tokens}|{rtf}" // // text est en UTF-8, sans séparateur '|' parasite (pipe-rare) // nativeFree(handle: Long) // // nativeVadNew(sampleRate, frameSize, rmsThreshold, minSpeechFrames, silenceEndFrames) : Long // nativeVadPush(handle: Long, pcm: ShortArray) : Int // 0=silence, 1=speech, 2=end_of_speech // nativeVadReset(handle: Long) // nativeVadFree(handle: Long) #include "stt_engine.h" #include #include #include namespace { struct JStr { JNIEnv * env; jstring j; const char * c; JStr(JNIEnv* e, jstring s) : env(e), j(s), c(s ? e->GetStringUTFChars(s, nullptr) : nullptr) {} ~JStr() { if (j && c) env->ReleaseStringUTFChars(j, c); } }; } // namespace extern "C" { // ============================================================================= // STT : load / transcribe / free // ============================================================================= JNIEXPORT jlong JNICALL Java_com_kazeia_stt_SttJni_nativeLoad(JNIEnv* env, jobject /*thiz*/, jstring model_dir, jboolean use_htp, jint n_threads, jint max_decode_steps) { JStr d(env, model_dir); SttEngineLoadCfg lc; lc.model_dir = d.c; lc.use_htp = (use_htp == JNI_TRUE); lc.n_threads = n_threads; lc.max_decode_steps = max_decode_steps > 0 ? max_decode_steps : 200; auto * eng = stt_engine_load(lc); return (jlong)(uintptr_t)eng; } JNIEXPORT jstring JNICALL Java_com_kazeia_stt_SttJni_nativeTranscribe(JNIEnv* env, jobject /*thiz*/, jlong handle, jshortArray pcm, jint sample_rate, jstring language, jboolean force_transcribe) { if (!handle) return env->NewStringUTF("-1|||||||0|0.0"); auto * eng = (SttEngine*)(uintptr_t)handle; jsize n = env->GetArrayLength(pcm); if (n <= 0) return env->NewStringUTF("-2|||||||0|0.0"); jshort * data = env->GetShortArrayElements(pcm, nullptr); JStr lang(env, language); SttTranscribeCfg tc; tc.pcm16 = (const int16_t*)data; tc.n_samples = (int)n; tc.sample_rate = sample_rate; tc.language = lang.c ? lang.c : "fr"; tc.force_transcribe = (force_transcribe == JNI_TRUE); auto R = stt_engine_transcribe(eng, tc); env->ReleaseShortArrayElements(pcm, data, JNI_ABORT); // Sérialise le résultat dans une chaîne `err|text|mel|enc|dec|total|n_tokens|rtf`. // Le texte est UTF-8 brut. On l'encode sans transformation : Kotlin split sur '|' // en gardant le 2e champ comme du texte (limit=8 pour ne pas couper si '|' apparaît // — pas le cas en pratique sur Whisper FR/EN). std::ostringstream oss; oss << R.err << '|' << R.text << '|' << R.mel_ms << '|' << R.encoder_ms << '|' << R.decoder_ms << '|' << R.total_ms << '|' << R.n_tokens << '|' << R.rtf; return env->NewStringUTF(oss.str().c_str()); } JNIEXPORT void JNICALL Java_com_kazeia_stt_SttJni_nativeFree(JNIEnv* /*env*/, jobject /*thiz*/, jlong handle) { if (!handle) return; stt_engine_free((SttEngine*)(uintptr_t)handle); } // ============================================================================= // VAD : new / push / reset / free (utilisable indépendamment de l'engine STT) // ============================================================================= JNIEXPORT jlong JNICALL Java_com_kazeia_stt_SttJni_nativeVadNew(JNIEnv* /*env*/, jobject /*thiz*/, jint sample_rate, jint frame_size, jint rms_threshold, jint min_speech_frames, jint silence_end_frames) { auto * s = stt_vad_new(sample_rate, frame_size, rms_threshold, min_speech_frames, silence_end_frames); return (jlong)(uintptr_t)s; } // Push retourne : 0 = silence, 1 = en cours de parole, 2 = parole vient de se terminer JNIEXPORT jint JNICALL Java_com_kazeia_stt_SttJni_nativeVadPush(JNIEnv* env, jobject /*thiz*/, jlong handle, jshortArray pcm) { if (!handle) return -1; auto * s = (SttVadState*)(uintptr_t)handle; jsize n = env->GetArrayLength(pcm); if (n <= 0) return -2; jshort * data = env->GetShortArrayElements(pcm, nullptr); stt_vad_push(s, (const int16_t*)data, (int)n); env->ReleaseShortArrayElements(pcm, data, JNI_ABORT); if (stt_vad_is_end_of_speech(s)) return 2; if (stt_vad_is_speech(s)) return 1; return 0; } JNIEXPORT void JNICALL Java_com_kazeia_stt_SttJni_nativeVadReset(JNIEnv* /*env*/, jobject /*thiz*/, jlong handle) { if (!handle) return; stt_vad_reset((SttVadState*)(uintptr_t)handle); } JNIEXPORT void JNICALL Java_com_kazeia_stt_SttJni_nativeVadFree(JNIEnv* /*env*/, jobject /*thiz*/, jlong handle) { if (!handle) return; stt_vad_free((SttVadState*)(uintptr_t)handle); } } // extern "C"