app/src/main/cpp/whisper_jni.c (4448 bytes)
1 // The narrowest bridge to whisper.cpp: load a model, transcribe one piece of 16 kHz mono float 2 // PCM, read the text back. One transcription runs at a time (Engine has one worker). 3 4 #include <jni.h> 5 #include <string.h> 6 #include <android/log.h> 7 8 #include "whisper.h" 9 10 #define TAG "subrep-whisper" 11 #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__) 12 13 #define JNI_FN(name) Java_com_honjimaku_subrep_WhisperLib_##name 14 15 JNIEXPORT jlong JNICALL 16 JNI_FN(initContext)(JNIEnv *env, jobject thiz, jstring model_path) { 17 (void) thiz; 18 const char *path = (*env)->GetStringUTFChars(env, model_path, NULL); 19 struct whisper_context_params cparams = whisper_context_default_params(); 20 cparams.use_gpu = false; 21 struct whisper_context *ctx = whisper_init_from_file_with_params(path, cparams); 22 if (ctx == NULL) LOGW("could not load model %s", path); 23 (*env)->ReleaseStringUTFChars(env, model_path, path); 24 return (jlong) ctx; 25 } 26 27 JNIEXPORT void JNICALL 28 JNI_FN(freeContext)(JNIEnv *env, jobject thiz, jlong ptr) { 29 (void) env; (void) thiz; 30 if (ptr != 0) whisper_free((struct whisper_context *) ptr); 31 } 32 33 // Returns 0 on success, else whisper's own error code. 34 // 35 // audio_ctx: how much of the 30 s encoder window to compute, in units of 20 ms. A piece of a 36 // few seconds does not need the whole window, and the encoder is most of the time on a phone. 37 JNIEXPORT jint JNICALL 38 JNI_FN(transcribe)(JNIEnv *env, jobject thiz, jlong ptr, jfloatArray samples, 39 jint n_threads, jstring language, jint audio_ctx) { 40 (void) thiz; 41 struct whisper_context *ctx = (struct whisper_context *) ptr; 42 if (ctx == NULL) return -2; 43 44 const jsize n = (*env)->GetArrayLength(env, samples); 45 jfloat *pcm = (*env)->GetFloatArrayElements(env, samples, NULL); 46 const char *lang = (*env)->GetStringUTFChars(env, language, NULL); 47 48 struct whisper_full_params p = whisper_full_default_params(WHISPER_SAMPLING_GREEDY); 49 p.print_realtime = false; 50 p.print_progress = false; 51 p.print_timestamps = false; 52 p.print_special = false; 53 p.translate = false; 54 p.language = lang; // "auto" detects the language of the piece 55 p.n_threads = n_threads; 56 // Each piece stands alone. Text carried over as a prompt is how one hallucinated line 57 // becomes a page of the same line. 58 p.no_context = true; 59 p.single_segment = true; 60 p.suppress_blank = true; 61 p.suppress_nst = true; 62 p.audio_ctx = audio_ctx; 63 // Greedy, no second try at a higher temperature: a live caption must not wait for it. 64 p.temperature_inc = 0.0f; 65 // Speech is at most about 8 tokens a second. A cap stops the loop that Whisper falls into 66 // on singing or noise ("o-o-o-o-o...") after a moment instead of a page later. 67 p.max_tokens = (int) (n / 16000.0 * 8) + 24; 68 69 int rc = whisper_full(ctx, p, pcm, n); 70 71 (*env)->ReleaseStringUTFChars(env, language, lang); 72 (*env)->ReleaseFloatArrayElements(env, samples, pcm, JNI_ABORT); 73 74 if (rc != 0) LOGW("whisper_full failed: %d", rc); 75 return rc; 76 } 77 78 JNIEXPORT jint JNICALL 79 JNI_FN(segmentCount)(JNIEnv *env, jobject thiz, jlong ptr) { 80 (void) env; (void) thiz; 81 return whisper_full_n_segments((struct whisper_context *) ptr); 82 } 83 84 // Text as raw UTF-8 bytes. NewStringUTF wants *modified* UTF-8 and mangles or aborts on 85 // anything outside the BMP, and a small model can end a segment inside a multi-byte 86 // character. Kotlin decodes leniently. 87 JNIEXPORT jbyteArray JNICALL 88 JNI_FN(segmentText)(JNIEnv *env, jobject thiz, jlong ptr, jint index) { 89 (void) thiz; 90 const char *text = whisper_full_get_segment_text((struct whisper_context *) ptr, index); 91 if (text == NULL) text = ""; 92 const jsize len = (jsize) strlen(text); 93 jbyteArray out = (*env)->NewByteArray(env, len); 94 if (out != NULL) (*env)->SetByteArrayRegion(env, out, 0, len, (const jbyte *) text); 95 return out; 96 } 97 98 JNIEXPORT jstring JNICALL 99 JNI_FN(detectedLanguage)(JNIEnv *env, jobject thiz, jlong ptr) { 100 (void) thiz; 101 const int id = whisper_full_lang_id((struct whisper_context *) ptr); 102 const char *code = id >= 0 ? whisper_lang_str(id) : NULL; 103 return (*env)->NewStringUTF(env, code != NULL ? code : ""); 104 } 105 106 JNIEXPORT jstring JNICALL 107 JNI_FN(systemInfo)(JNIEnv *env, jobject thiz) { 108 (void) thiz; 109 return (*env)->NewStringUTF(env, whisper_print_system_info()); 110 }