penguin/loopdeck

A simple player that support A-B looping for FiiO M11S

app/src/main/cpp/jni.cpp

raw ยท 4278 bytes

#include "decoder.h"
#include <jni.h>
#include <memory>
#include <stdexcept>

static Decoder* decoder(jlong handle) {
    if (!handle) throw std::runtime_error("No file is open");
    return reinterpret_cast<Decoder*>(handle);
}
static void report(JNIEnv* env, const std::exception& e) {
    env->ThrowNew(env->FindClass("java/io/IOException"), e.what());
}
extern "C" JNIEXPORT jlong JNICALL
Java_net_astropenguin_loopdeck_NativeDecoder_open(JNIEnv* env, jclass, jint fd) {
    try {
        auto d = std::make_unique<Decoder>(fd); d->open();
        return reinterpret_cast<jlong>(d.release());
    } catch (const std::exception& e) { report(env, e); return 0; }
}
extern "C" JNIEXPORT void JNICALL
Java_net_astropenguin_loopdeck_NativeDecoder_close(JNIEnv*, jclass, jlong handle) { delete reinterpret_cast<Decoder*>(handle); }
extern "C" JNIEXPORT jint JNICALL
Java_net_astropenguin_loopdeck_NativeDecoder_sourceRate(JNIEnv* env, jclass, jlong handle) {
    try { return decoder(handle)->sourceRate(); } catch (const std::exception& e) { report(env,e); return 0; }
}
extern "C" JNIEXPORT jlong JNICALL
Java_net_astropenguin_loopdeck_NativeDecoder_duration(JNIEnv* env, jclass, jlong handle) {
    try { return decoder(handle)->durationMs(); } catch (const std::exception& e) { report(env,e); return 0; }
}
extern "C" JNIEXPORT jstring JNICALL
Java_net_astropenguin_loopdeck_NativeDecoder_description(JNIEnv* env, jclass, jlong handle) {
    try { return env->NewStringUTF(decoder(handle)->description().c_str()); } catch (const std::exception& e) { report(env,e); return nullptr; }
}
extern "C" JNIEXPORT void JNICALL
Java_net_astropenguin_loopdeck_NativeDecoder_outputRate(JNIEnv* env, jclass, jlong handle, jint rate) {
    try { decoder(handle)->outputRate(rate); } catch (const std::exception& e) { report(env,e); }
}
extern "C" JNIEXPORT void JNICALL
Java_net_astropenguin_loopdeck_NativeDecoder_seek(JNIEnv* env, jclass, jlong handle, jlong frame) {
    try { decoder(handle)->seek(frame); } catch (const std::exception& e) { report(env,e); }
}
extern "C" JNIEXPORT jint JNICALL
Java_net_astropenguin_loopdeck_NativeDecoder_read(JNIEnv* env, jclass, jlong handle, jfloatArray array, jint frames) {
    try {
        auto* d = decoder(handle);
        if (frames < 0 || frames > env->GetArrayLength(array) / 2) throw std::runtime_error("Invalid PCM buffer size");
        // Avoid pinning a Java array across file IO and codec work.
        thread_local std::vector<float> scratch;
        scratch.resize(static_cast<size_t>(frames) * 2);
        int count = d->read(scratch.data(), frames);
        env->SetFloatArrayRegion(array, 0, count * 2, scratch.data());
        return count;
    } catch (const std::exception& e) { report(env,e); return 0; }
}

// Decode standard UTF-8 rather than JNI's modified UTF-8 (track names may contain emoji).
static jstring utf8(JNIEnv* env, const std::string& value) {
    auto bytes=env->NewByteArray(value.size());
    if (!bytes) return nullptr;
    env->SetByteArrayRegion(bytes,0,value.size(),reinterpret_cast<const jbyte*>(value.data()));
    auto charset=env->NewStringUTF("UTF-8");
    auto type=env->FindClass("java/lang/String");
    auto result=static_cast<jstring>(env->NewObject(type,env->GetMethodID(type,"<init>","([BLjava/lang/String;)V"),bytes,charset));
    env->DeleteLocalRef(bytes); env->DeleteLocalRef(charset); env->DeleteLocalRef(type);
    return result;
}
extern "C" JNIEXPORT jstring JNICALL
Java_net_astropenguin_loopdeck_NativeDecoder_cueSheet(JNIEnv* env,jclass,jlong handle) {
    try { return utf8(env,decoder(handle)->cueSheet()); } catch (const std::exception& e) { report(env,e); return nullptr; }
}
extern "C" JNIEXPORT jobjectArray JNICALL
Java_net_astropenguin_loopdeck_NativeDecoder_chapters(JNIEnv* env,jclass,jlong handle) {
    try {
        auto values=decoder(handle)->chapters();
        auto result=env->NewObjectArray(values.size(),env->FindClass("java/lang/String"),nullptr);
        if (!result) return nullptr;
        for (size_t i=0;i<values.size();++i) {
            auto value=utf8(env,values[i]); env->SetObjectArrayElement(result,i,value); env->DeleteLocalRef(value);
        }
        return result;
    } catch (const std::exception& e) { report(env,e); return nullptr; }
}