132 lines
5.5 KiB
C++
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"
|