From 49b529ba74f3dc77afc64e2ed44aa712900c81c1 Mon Sep 17 00:00:00 2001 From: Digipom Date: Mon, 16 Jan 2023 14:57:35 -0500 Subject: [PATCH] whisper.android : add support for loading directly from asset in C (#415) --- .../ui/main/MainScreenViewModel.kt | 3 +- .../com/whispercppdemo/whisper/LibWhisper.kt | 11 +++++ .../app/src/main/jni/whisper/Whisper.mk | 2 +- .../app/src/main/jni/whisper/jni.c | 49 +++++++++++++++++++ 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/examples/whisper.android/app/src/main/java/com/whispercppdemo/ui/main/MainScreenViewModel.kt b/examples/whisper.android/app/src/main/java/com/whispercppdemo/ui/main/MainScreenViewModel.kt index d741748..29ac2cd 100644 --- a/examples/whisper.android/app/src/main/java/com/whispercppdemo/ui/main/MainScreenViewModel.kt +++ b/examples/whisper.android/app/src/main/java/com/whispercppdemo/ui/main/MainScreenViewModel.kt @@ -73,8 +73,7 @@ class MainScreenViewModel(private val application: Application) : ViewModel() { printMessage("Loading model...\n") val models = application.assets.list("models/") if (models != null) { - val inputstream = application.assets.open("models/" + models[0]) - whisperContext = WhisperContext.createContextFromInputStream(inputstream) + whisperContext = WhisperContext.createContextFromAsset(application.assets, "models/" + models[0]) printMessage("Loaded model ${models[0]}.\n") } diff --git a/examples/whisper.android/app/src/main/java/com/whispercppdemo/whisper/LibWhisper.kt b/examples/whisper.android/app/src/main/java/com/whispercppdemo/whisper/LibWhisper.kt index edd041a..9a9a963 100644 --- a/examples/whisper.android/app/src/main/java/com/whispercppdemo/whisper/LibWhisper.kt +++ b/examples/whisper.android/app/src/main/java/com/whispercppdemo/whisper/LibWhisper.kt @@ -1,5 +1,6 @@ package com.whispercppdemo.whisper +import android.content.res.AssetManager import android.os.Build import android.util.Log import kotlinx.coroutines.* @@ -56,6 +57,15 @@ class WhisperContext private constructor(private var ptr: Long) { } return WhisperContext(ptr) } + + fun createContextFromAsset(assetManager: AssetManager, assetPath: String): WhisperContext { + val ptr = WhisperLib.initContextFromAsset(assetManager, assetPath) + + if (ptr == 0L) { + throw java.lang.RuntimeException("Couldn't create context from asset $assetPath") + } + return WhisperContext(ptr) + } } } @@ -87,6 +97,7 @@ private class WhisperLib { // JNI methods external fun initContextFromInputStream(inputStream: InputStream): Long + external fun initContextFromAsset(assetManager: AssetManager, assetPath: String): Long external fun initContext(modelPath: String): Long external fun freeContext(contextPtr: Long) external fun fullTranscribe(contextPtr: Long, audioData: FloatArray) diff --git a/examples/whisper.android/app/src/main/jni/whisper/Whisper.mk b/examples/whisper.android/app/src/main/jni/whisper/Whisper.mk index b4f050e..6d9113b 100644 --- a/examples/whisper.android/app/src/main/jni/whisper/Whisper.mk +++ b/examples/whisper.android/app/src/main/jni/whisper/Whisper.mk @@ -1,5 +1,5 @@ WHISPER_LIB_DIR := $(LOCAL_PATH)/../../../../../../../ -LOCAL_LDLIBS := -llog +LOCAL_LDLIBS := -landroid -llog # Make the final output library smaller by only keeping the symbols referenced from the app. ifneq ($(APP_OPTIM),debug) diff --git a/examples/whisper.android/app/src/main/jni/whisper/jni.c b/examples/whisper.android/app/src/main/jni/whisper/jni.c index 0fd2897..160fe78 100644 --- a/examples/whisper.android/app/src/main/jni/whisper/jni.c +++ b/examples/whisper.android/app/src/main/jni/whisper/jni.c @@ -1,4 +1,6 @@ #include +#include +#include #include #include #include @@ -9,6 +11,7 @@ #define TAG "JNI" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__) static inline int min(int a, int b) { return (a < b) ? a : b; @@ -91,6 +94,52 @@ Java_com_whispercppdemo_whisper_WhisperLib_00024Companion_initContextFromInputSt return (jlong) context; } +static size_t asset_read(void *ctx, void *output, size_t read_size) { + return AAsset_read((AAsset *) ctx, output, read_size); +} + +static bool asset_is_eof(void *ctx) { + return AAsset_getRemainingLength64((AAsset *) ctx) <= 0; +} + +static void asset_close(void *ctx) { + AAsset_close((AAsset *) ctx); +} + +static struct whisper_context *whisper_init_from_asset( + JNIEnv *env, + jobject assetManager, + const char *asset_path +) { + LOGI("Loading model from asset '%s'\n", asset_path); + AAssetManager *asset_manager = AAssetManager_fromJava(env, assetManager); + AAsset *asset = AAssetManager_open(asset_manager, asset_path, AASSET_MODE_STREAMING); + if (!asset) { + LOGW("Failed to open '%s'\n", asset_path); + return NULL; + } + + whisper_model_loader loader = { + .context = asset, + .read = &asset_read, + .eof = &asset_is_eof, + .close = &asset_close + }; + + return whisper_init(&loader); +} + +JNIEXPORT jlong JNICALL +Java_com_whispercppdemo_whisper_WhisperLib_00024Companion_initContextFromAsset( + JNIEnv *env, jobject thiz, jobject assetManager, jstring asset_path_str) { + UNUSED(thiz); + struct whisper_context *context = NULL; + const char *asset_path_chars = (*env)->GetStringUTFChars(env, asset_path_str, NULL); + context = whisper_init_from_asset(env, assetManager, asset_path_chars); + (*env)->ReleaseStringUTFChars(env, asset_path_str, asset_path_chars); + return (jlong) context; +} + JNIEXPORT jlong JNICALL Java_com_whispercppdemo_whisper_WhisperLib_00024Companion_initContext( JNIEnv *env, jobject thiz, jstring model_path_str) {