Kazeia-engine/dist/jni/kazeia_stt_jni.cpp

132 lines
5.5 KiB
C++

// 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 <jni.h>
#include <sstream>
#include <string>
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"