Question. libFluidSynth.so for Android

Hello and Excuse me.

Im using FluidSynth 2.4.6 libfluidsynth.so for Andorid 13.

When I call following code twice. SIGSEGV always happens.

    if (fluid_is_soundfont(file.mb_str())) {
        handle->soundfont_id = fluid_synth_sfload(handle->synth, file.mb_str(), 0);
    }
    else {
        return -1;
    }

How do I reload Another Font for Android.
I wish I could use FluidSynth for Android 13 and Android 15.

Long program code here.

native-lib.cpp
#include <jni.h>
#include <string>
#include <unistd.h>
#include <sstream>
#include "C:/github/fluidsynth-2.4.6-android24/include/fluidsynth.h"
#include <codecvt>
#import <signal.h>

#define FLUID_PEAK_ATTENUATION  960.0f
#define FLUID_CENTS_HZ_SIZE     1200
#define FLUID_VEL_CB_SIZE       128
#define FLUID_CB_AMP_SIZE       1441
#define FLUID_PAN_SIZE          1002

JavaVM *_javaVM = nullptr;
jclass _javaClass = nullptr;
jmethodID _logging = nullptr;
jobject _javaObj = nullptr;

#define USE_LOG 2

void log_java(JNIEnv* env, jobject obj, std::string* message) {
    if (obj != nullptr && obj != _javaObj) {
        _javaObj = obj;
    }
#ifdef USE_LOG
    if (_logging != nullptr) {
        char *buf = (char *)message->c_str();
        jstring text = env->NewStringUTF(buf);
        env->CallVoidMethod(obj, _logging, text);
    }
#endif
}

typedef struct {
    fluid_settings_t *settings;
    fluid_synth_t *synth;
    fluid_audio_driver_t *audio;

    int soundfont_id;
} fluid_handle_t;

#define MAX_SYNTH 1
fluid_handle_t *_allHandle = nullptr;

class MBString {
    std::wstring _w_str;
    std::string _mb_str;

public:
    MBString(JNIEnv *env, jstring &jstr) {
        const jchar *raw = env->GetStringChars(jstr, 0);
        jsize len = env->GetStringLength(jstr);
        _w_str.assign(raw, raw + len);
        env->ReleaseStringChars(jstr, raw);

        std::wstring_convert<std::codecvt_utf8<wchar_t> > converter;

        _mb_str.append(converter.to_bytes(_w_str));
    }

    ~MBString() {
    }

    const wchar_t *w_str() {
        return _w_str.c_str();
    }

    const char *mb_str() {
        return _mb_str.c_str();
    }
};

int findEmptyId() {
    if (_allHandle == nullptr) {
        _allHandle = (fluid_handle_t *) malloc(sizeof(fluid_handle_t) * MAX_SYNTH);
        if (_allHandle == nullptr) {
            return -1;
        }
        memset(_allHandle, 0, sizeof(fluid_handle_t) * MAX_SYNTH);
    }
    return 0;
    /*
    for (int i = 0; i < MAX_SYNTH; ++i) {
        if (_allHandle[i].settings == nullptr) {
            return i;
        }
    }
    return -1;*/
}
/*
void disposeFluidSynth(int id) {
    if (_allHandle == nullptr) {
        return;
    }
    if (id < 0 || id >= MAX_SYNTH) {
        return;
    }
    fluid_handle_t *handle = _allHandle + id;
    if (handle->synth != nullptr) {
        delete_fluid_synth(handle->synth);
        handle->synth = nullptr;
    }
    if (handle->settings != nullptr) {
        delete_fluid_settings(handle->settings);
        handle->settings = nullptr;
    }
    handle->soundfont_id = 0;
}
*/
fluid_handle_t *getFluid(int handle) {
    if (handle < 0 || handle >= MAX_SYNTH) {
        return nullptr;
    }
    fluid_handle_t *ptr = _allHandle + handle;
    return ptr;
}

extern void initializeMod(fluid_handle_t *fluid);

extern void JNICALL jfluid_close(JNIEnv *env, jobject obj, jint id);

jstring  JNICALL jfluid_list_program(JNIEnv *env, jobject obj, int id) {
    fluid_handle_t *handle = getFluid(id);
    if (handle == nullptr) {
        return env->NewStringUTF("");
    }

    fluid_sfont_t *font = fluid_synth_get_sfont_by_id(handle->synth, handle->soundfont_id);
    int offset = fluid_synth_get_bank_offset(handle->synth, handle->soundfont_id);

    fluid_sfont_iteration_start(font);
    std::stringstream result;
    result << "list program " << id << " addresss " << ((unsigned long) (font)) << "\n";
    char buff[101];

    while (true) {
        fluid_preset_t *preset = fluid_sfont_iteration_next(font);
        if (preset == nullptr) {
            preset = fluid_sfont_iteration_next(font);
            if (preset == nullptr) {
                preset = fluid_sfont_iteration_next(font);
                if (preset == nullptr) {
                    break;
                }
            }
        }
        int banknum = fluid_preset_get_banknum(preset) + offset;
        int program = fluid_preset_get_num(preset);
        const char *name = fluid_preset_get_name(preset);

        for (int i = 0; i < 100; ++ i) {
            char c = name[i];
            if ((c & 0x80) != 0) {
                c = '_';
            }
            buff[i] = c;
            if (c == 0) {
                break;
            }
        }
        buff[100] = 0;

        result << program;
        result << ",";
        result << banknum;
        result << ",";
        result << "-1";
        result << ",";
        result << buff;
        result << "\n";
    }

    std::string str(result.str());
    char *buf = (char *)str.c_str();
    return env->NewStringUTF(buf);
}


/* calculate cent from key (0 to 11) */
int getKey12Cent(int key) {
    switch (key) {
        case 0:
            return 0;
        case 2:
            return 204;
        case 4:
            return 386;
        case 5:
            return 498;
        case 7:
            return 702;
        case 9:
            return 884;
        case 11:
            return 1088;
    }
    return (getKey12Cent(key - 1) + getKey12Cent(key + 1)) / 2;
}

/* calculate cent from step (0 to 12 + octave offset (1 octave = 1200)) */
double getKeysCent(int step) {
    if (step == 0) {
        return 0;
    }
    double acent = 0;

    while (step < 0) {
        acent -= 1200;
        step += 12;
    }
    while (step >= 12) {
        acent += 1200;
        step -= 12;
    }
    return acent + getKey12Cent(step);
}

void getPitchesJustIntonation(double *pitches, int root) {
    /* root = 0 to 12 */
    for (int key = 0; key < 0x80; ++key) {
        int distance = key - root;
        double cent = getKeysCent(distance);
        pitches[key] = cent;
    }
}

void getPitchesTemperament(double *pitches) {
    for (int key = 0; key < 0x80; ++key) {
        pitches[key] = key * 100;
    }
}

void adjustAmust(double *pitches, float hzamust) {
    /* hzAmust = around 440 to 443 ? */
    auto log2_ratio = log2(hzamust / 440.);
    double amust = 100. * 69 + 1200. * log2_ratio;

    double slide = amust - pitches[69];
    for (int key = 0; key < 0x80; ++key) {
        pitches[key] += slide;
    }
}

int my_fluid_retune(JNIEnv* env, jobject obj, fluid_handle_t *handle, float hzamust, bool equalTemp, int baseKey) {
    int keys[0x80];
    double pitches[0x80];

    fluid_sfont_t *font = fluid_synth_get_sfont_by_id(handle->synth, handle->soundfont_id);

    fluid_sfont_iteration_start(font);
    int ret = FLUID_OK;
    for (int i = 0; i < 0x80; ++i) {
        keys[i] = i;
    }
    if (equalTemp) {
        getPitchesTemperament(pitches);
        adjustAmust(pitches, hzamust);
    } else {
        getPitchesJustIntonation(pitches, baseKey);
        adjustAmust(pitches, hzamust);
    }

    int offset = fluid_synth_get_bank_offset(handle->synth, handle->soundfont_id);
    while (true) {
        fluid_preset_t *preset = fluid_sfont_iteration_next(font);
        if (preset == nullptr) {
            break;
        }
        int bank = fluid_preset_get_banknum(preset) + offset;
        int program = fluid_preset_get_num(preset);

        std::stringstream debugMessage;

#ifdef USE_LOG
        debugMessage.str("");
        debugMessage.clear();
        debugMessage << "bank:" << bank << ", prog:" << program;
        std::string  str2(debugMessage.str());
        log_java(env, obj, &str2);
#endif
        if (bank < 0 || bank >= 128 || program < 0 || program >= 128) {
            continue;
        }

        /*
        if (equalTemp && hzamust== 440.) {
            for (int ch = 0; ch < 16; ++ ch) {
                fluid_synth_activate_tuning(handle->synth, ch, bank, program, 0);
            }
            continue;
        }*/

        int code = fluid_synth_tune_notes(handle->synth, bank, program, 0x80, keys, pitches, 1);
        if (code == FLUID_OK) {
            for (int ch = 0; ch < 16; ++ch) {
                code = fluid_synth_activate_tuning(handle->synth, ch, bank, program, 1);
            }
        }
        if (code != FLUID_OK) {
            ret = code;
        }
    }
    return ret;
}

void JNICALL
jfluid_retune(JNIEnv *env, jobject obj, jint id, jfloat hzamust, jboolean equalTemp, int baseKey) {
    fluid_handle_t *handle = getFluid(id);
    if (handle == nullptr) {
        return;
    }

    my_fluid_retune(env, obj, handle, hzamust, equalTemp >= 1, baseKey);
}

jboolean JNICALL jfluid_is_soundfont(JNIEnv *env, jobject obj, jstring fontFile) {
    MBString file(env, fontFile);
    if (fluid_is_soundfont(file.mb_str())) {
        return JNI_TRUE;
    }
    else {
        return JNI_FALSE;
    }
}

jint JNICALL jfluid_open(JNIEnv *env, jobject obj, jstring fontFile, jboolean lowlatency) {
    int id = findEmptyId();

#ifdef USE_LOG
    std::stringstream debugMessage;
    debugMessage.str("");
    debugMessage.clear();
    debugMessage << "emptID =" << id;
    std::string  str2(debugMessage.str());
    log_java(env, obj, &str2);
#endif
    if (id < 0) {
        return id;
    }

    fluid_handle_t *handle = getFluid(id);
#ifdef USE_LOG
    debugMessage.str("");
    debugMessage.clear();
    debugMessage << "handle =" << handle;
    std::string  str3(debugMessage.str());
    log_java(env, obj, &str3);
#endif
    if (handle == nullptr) {
        return -1;
    }

    if (handle->settings == nullptr) {
        handle->settings = new_fluid_settings();


#ifdef USE_LOG
    debugMessage.str("");
    debugMessage.clear();
    debugMessage << "setting=" << handle->settings;
    std::string  str4(debugMessage.str());
    log_java(env, obj, &str4);
#endif
        fluid_settings_setstr(handle->settings, "synth.audio-driver", "oboe");
        fluid_settings_setstr(handle->settings, "audio.oboe.sample-rate-conversion-quality", "Fastest");
        if (lowlatency) {
            fluid_settings_setstr(handle->settings, "audio.oboe.sharing-mode", "Exclusive");
            fluid_settings_setint(handle->settings, "audio.periods", 8);
            fluid_settings_setint(handle->settings, "audio.period-size", 8)    ;
            fluid_settings_setstr(handle->settings, "audio.oboe.performance-mode", "LowLatency");
        }
        else {
            fluid_settings_setstr(handle->settings, "audio.oboe.sharing-mode", "Shared");
            fluid_settings_setint(handle->settings, "audio.periods", 16);
            fluid_settings_setint(handle->settings, "audio.period-size", 32);
            fluid_settings_setstr(handle->settings, "audio.oboe.performance-mode", "None");
        }
        //fluid_settings_setint(handle->settings, "synth.audio-channels", 2);
        //fluid_settings_setint(handle->settings, "synth.cpu-cores", 1);
        //fluid_settings_setint(handle->settings, "audio.realtime-prio", 60);
        //fluid_settings_setstr(handle->settings, "player.timing-source", "sample");

    }

    if (handle->synth == nullptr) {
        handle->synth = new_fluid_synth(handle->settings);
    }

#ifdef USE_LOG
    debugMessage.str("");
    debugMessage.clear();
    debugMessage << "synth=" << handle->synth;
    std::string  str5(debugMessage.str());
    log_java(env, obj, &str5);
#endif
    if (handle->synth == nullptr) {
        //jfluid_close(env, obj, id);
        return -1;
    }

    //fluid_synth_set_polyphony(handle->synth, 128);
    //fluid_synth_set_interp_method(handle->synth, -1, FLUID_INTERP_LINEAR);

#ifdef USE_LOG
    debugMessage.str("1");
    debugMessage.clear();
    debugMessage << "mod ok 1 ";
    std::string str61(debugMessage.str());
    log_java(env, obj, &str61);
#endif
    try {

        if (fluid_synth_sfcount(handle->synth)) {
#ifdef USE_LOG
            debugMessage.str("2");
            debugMessage.clear();
            debugMessage << "mod ok 2 ";
            std::string str62(debugMessage.str());
            log_java(env, obj, &str62);
#endif
            fluid_sfont_t *font = fluid_synth_get_sfont(handle->synth, 0);
            if (font != nullptr) {
                fluid_synth_remove_sfont(handle->synth, font);
            }

            if (handle->soundfont_id >= 0) {
                fluid_synth_sfunload(handle->synth, handle->soundfont_id, 0);
                handle->soundfont_id = -1;
            }
        }
#ifdef USE_LOG
        debugMessage.str("3");
        debugMessage.clear();
        debugMessage << "mod ok 3 ";
        std::string str63(debugMessage.str());
        log_java(env, obj, &str63);
#endif
        MBString file(env, fontFile);
#ifdef USE_LOG
        debugMessage.str("");
        debugMessage.clear();
        debugMessage << "mod ok 4 ";
        std::string str64(debugMessage.str());
        log_java(env, obj, &str64);

        std::string str641(file.mb_str());
        log_java(env, obj, &str641);
#endif
        if (fluid_is_soundfont(file.mb_str())) {
            handle->soundfont_id = fluid_synth_sfload(handle->synth, file.mb_str(), 0);
        }
        else {
            return -1;
        }

    } catch (std::exception& e) {
        std::stringstream debugMessage;
        debugMessage.str("");
        debugMessage.clear();
        debugMessage << "handle =" << handle;
        std::string  str3(e.what());
        log_java(env, obj, &str3);
        return -1;
    } catch (...) {
        return -1;
    }

#ifdef USE_LOG
    debugMessage.str("");
    debugMessage.clear();
    debugMessage << "mod ok 5 ";
    std::string str65(debugMessage.str());
    log_java(env, obj, &str65);
#endif
#ifdef USE_LOG
    debugMessage.str("");
    debugMessage.clear();
    debugMessage << "soundFont=" << handle->soundfont_id;
    std::string  str7(debugMessage.str());
    log_java(env, obj, &str7);
#endif

     if (handle->soundfont_id == FLUID_FAILED) {
        //jfluid_close(env, obj, id);
        return -1;
    }
    if (handle->audio == nullptr) {
        handle->audio = new_fluid_audio_driver(handle->settings, handle->synth);
        initializeMod(handle);
    }

#ifdef USE_LOG
    debugMessage.str("");
    debugMessage.clear();
    debugMessage << "audio=" << handle->audio;
    std::string  str8(debugMessage.str());
    log_java(env, obj, &str8);
#endif

    return id;
}

void JNICALL jfluid_close(JNIEnv *env, jobject obj, jint id) {
#if 0
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr) {
        if (handle->soundfont_id != 0) {
            fluid_synth_sfunload(handle->synth, handle->soundfont_id, 1);
            handle->soundfont_id = -1;
        }
        if (handle->synth != nullptr) {
            delete_fluid_synth(handle->synth);
            handle->synth = nullptr;
        }
        if (handle->audio != nullptr) {
            delete_fluid_audio_driver(handle->audio);
            handle->audio = nullptr;
        }
        if (handle->settings != nullptr) {
            delete_fluid_settings(handle->settings);
            handle->settings = nullptr;
        }
    }
#endif
}


#define SOUND_CTRL_FILTER_RESONANCE 71
#define SOUND_CTRL_RELEASE 72
#define SOUND_CTRL_ATTACK 73
#define SOUND_CTRL_CUTOFF 74
#define SOUND_CTRL_DECAY 75
#define SOUND_CTRL_SUSTAIN 79

#define SOUND_EFFECT_REVERVE 91
#define SOUND_EFFECT_TREMOLO 92
#define SOUND_EFFECT_CHORUS 93
#define SOUND_EFFECT_DETUNE 94
#define SOUND_EFFECT_PHASER 95

void reset_control(fluid_handle_t* handle, int ch) {
    fluid_synth_cc(handle->synth, ch, 10, 64); //panpot
    fluid_synth_cc(handle->synth, ch, SOUND_CTRL_FILTER_RESONANCE, 0);
    fluid_synth_cc(handle->synth, ch, SOUND_CTRL_RELEASE, 64);
    fluid_synth_cc(handle->synth, ch, SOUND_CTRL_ATTACK, 64);
    fluid_synth_cc(handle->synth, ch, SOUND_CTRL_CUTOFF, 64);
    fluid_synth_cc(handle->synth, ch, SOUND_CTRL_DECAY, 64);
    fluid_synth_cc(handle->synth, ch, SOUND_CTRL_SUSTAIN, 64);

    fluid_synth_cc(handle->synth, ch, SOUND_EFFECT_REVERVE, 30);
    fluid_synth_cc(handle->synth, ch, SOUND_EFFECT_TREMOLO, 0);
    fluid_synth_cc(handle->synth, ch, SOUND_EFFECT_CHORUS, 0);
    fluid_synth_cc(handle->synth, ch, SOUND_EFFECT_DETUNE, 0);
    fluid_synth_cc(handle->synth, ch, SOUND_EFFECT_PHASER, 0);
    fluid_synth_set_portamento_mode(handle->synth, ch, FLUID_CHANNEL_PORTAMENTO_MODE_EACH_NOTE);
}

void initializeMod(fluid_handle_t* handle) {

    fluid_synth_set_gain(handle->synth, 0.7);
    // soundfont spec says that if cutoff is >20kHz and resonance Q is 0, then no filtering occurs

    fluid_mod_t *mod;
    /*
    mod  = new_fluid_mod();

    fluid_mod_set_source1(mod,
                          SOUND_EFFECT_REVERVE, //reverve
                          FLUID_MOD_CC
                          | FLUID_MOD_LINEAR
                          | FLUID_MOD_UNIPOLAR
                          | FLUID_MOD_POSITIVE);
    fluid_mod_set_source2(mod, 0, 0);
    fluid_mod_set_dest(mod, GEN_REVERBSEND);
    fluid_mod_set_amount(mod, 200);
    fluid_synth_add_default_mod(handle->synth, mod, FLUID_SYNTH_ADD);

    mod  = new_fluid_mod();

    fluid_mod_set_source1(mod,
                          SOUND_EFFECT_CHORUS, //chorus
                          FLUID_MOD_CC
                          | FLUID_MOD_LINEAR
                          | FLUID_MOD_UNIPOLAR
                          | FLUID_MOD_POSITIVE);
    fluid_mod_set_source2(mod, 0, 0);
    fluid_mod_set_dest(mod, GEN_CHORUSSEND);
    fluid_mod_set_amount(mod, 1000);
    fluid_synth_add_default_mod(handle->synth, mod, FLUID_SYNTH_ADD);

     */
    // http://www.synthfont.com/SoundFont_NRPNs.PDF
/*
    mod = new_fluid_mod();
    fluid_mod_set_source1(mod,
                          SOUND_CTRL_FILTER_RESONANCE,
                          FLUID_MOD_CC
                          | FLUID_MOD_UNIPOLAR
//                            | FLUID_MOD_LINEAR
                          | FLUID_MOD_CONCAVE
                          | FLUID_MOD_POSITIVE);
    fluid_mod_set_source2(mod, 0, 0);
    fluid_mod_set_dest(mod, GEN_FILTERQ);
    fluid_mod_set_amount(mod, FLUID_PEAK_ATTENUATION);
    fluid_synth_add_default_mod(handle->synth, mod, FLUID_SYNTH_OVERWRITE);
    mod = new_fluid_mod();

    fluid_mod_set_source1(mod,
                          SOUND_CTRL_RELEASE, // MIDI CC 72 Release time
                          FLUID_MOD_CC
                          | FLUID_MOD_BIPOLAR
                          | FLUID_MOD_LINEAR
                          | FLUID_MOD_POSITIVE);
    fluid_mod_set_source2(mod, 0, 0);
    fluid_mod_set_dest(mod, GEN_VOLENVRELEASE);
    fluid_mod_set_amount(mod, 12000);
    fluid_synth_add_default_mod(handle->synth, mod, FLUID_SYNTH_ADD);

    mod = new_fluid_mod();

    float env_amount = 20000.0f;
    fluid_mod_set_source1(mod,
                          SOUND_CTRL_ATTACK, // MIDI CC 73 Attack time
                          FLUID_MOD_CC
                          | FLUID_MOD_BIPOLAR
                          | FLUID_MOD_LINEAR
                          | FLUID_MOD_POSITIVE);
    fluid_mod_set_source2(mod, 0, 0);
    fluid_mod_set_dest(mod, GEN_VOLENVATTACK);
    fluid_mod_set_amount(mod, env_amount);
    fluid_synth_add_default_mod(handle->synth, mod, FLUID_SYNTH_ADD);
    // soundfont spec says that if cutoff is >20kHz and resonance Q is 0, then no filtering occurs
    mod = new_fluid_mod();

    fluid_mod_set_source1(mod,
                          SOUND_CTRL_CUTOFF, // MIDI CC 74 Brightness (cutoff frequency, FILTERFC)
                          FLUID_MOD_CC
                          | FLUID_MOD_LINEAR
                          | FLUID_MOD_BIPOLAR
                          | FLUID_MOD_POSITIVE);
    fluid_mod_set_source2(mod, 0, 0);
    fluid_mod_set_dest(mod, GEN_FILTERFC);
    fluid_mod_set_amount(mod, 10000.0f);
    fluid_synth_add_default_mod(handle->synth, mod, FLUID_SYNTH_ADD);

    mod = new_fluid_mod();

    fluid_mod_set_source1(mod,
                          SOUND_CTRL_DECAY, // MIDI CC 75 Decay Time
                          FLUID_MOD_CC
                          | FLUID_MOD_UNIPOLAR
                          | FLUID_MOD_LINEAR
                          | FLUID_MOD_POSITIVE);
    fluid_mod_set_source2(mod, 0, 0);
    fluid_mod_set_dest(mod, GEN_VOLENVDECAY);
    fluid_mod_set_amount(mod, env_amount);
    fluid_synth_add_default_mod(handle->synth, mod, FLUID_SYNTH_ADD);
*/

    mod = new_fluid_mod();

    fluid_mod_set_source1(mod,
                          SOUND_CTRL_SUSTAIN, // MIDI CC 79 undefined
                          FLUID_MOD_CC
                          | FLUID_MOD_UNIPOLAR
                          | FLUID_MOD_CONCAVE
                          | FLUID_MOD_NEGATIVE);
    fluid_mod_set_source2(mod, 0, 0);
    fluid_mod_set_dest(mod, GEN_VOLENVSUSTAIN);

    // fluice_voice.c#fluid_voice_update_param()
    // clamps the range to between 0 and 1000, so we'll copy that
    fluid_mod_set_amount(mod, 1000.0f);
    fluid_synth_add_default_mod(handle->synth, mod, FLUID_SYNTH_ADD);

    for (int ch = 0; ch < 16; ++ch) {
        reset_control(handle, ch);
    }
}

fluid_midi_event_t *_event = nullptr;

int gmReset[] = {0xF0, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
int gsReset[] = {0xF0, 0x41, -1, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7};
int xgReset[] = {0xF0, 0x43, -1, 0x4C, 0x00, 0x00, 0x7E, 0x00, 0xF7};
int masterVolume[] = {0xF0, 0x7f, 0x7F, 0x04, 0x01, 0x11, -1, 0xF7};
int masterVolume_pos = 6;

bool matchBytes(int *temp, jbyte *data, int dataLen) {
    for (int i = 0; i < dataLen; ++i) {
        int c = data[i] & 0xff;
        int x = temp[i];
        if (x == 0xf7) {
            if (c == 0xf7) {
                return true;
            } else {
                return false;
            }
        }
        if (x != c) {
            if (x < 0) {
                continue;
            } else {
                return false;
            }
        }
    }
    return false;
}

extern void JNICALL
jfluid_short_message(JNIEnv *env, jobject obj, jint id, jint status, jint data1, jint data2);

void JNICALL jfluid_long_message(JNIEnv *env, jobject obj, jint id, jbyteArray data) {
    jsize len = env->GetArrayLength(data);
    jbyte *b = env->GetByteArrayElements(data, 0);
    if (len <= 3) {
        int status = 0;
        int data1 = 0;
        int data2 = 0;
        if (len >= 1) {
            status = b[0] & 0xff;
        }
        if (len >= 2) {
            data1 = b[1] & 0xff;
        }
        if (len >= 3) {
            data2 = b[2] & 0xff;
        }
        jfluid_short_message(env, obj, id, status, data1, data2);
    } else {
        if (matchBytes(gmReset, b, len) || matchBytes(gsReset, b, len) ||
            matchBytes(xgReset, b, len)) {
            fluid_handle_t *handle = getFluid(id);
            if (handle != nullptr) {
                fluid_synth_system_reset(handle->synth);
                fluid_synth_program_reset(handle->synth);
            }
        } else if (matchBytes(masterVolume, b, len)) {
            fluid_handle_t *handle = getFluid(id);
            if (handle != nullptr) {
                int c = b[masterVolume_pos] & 0x0ff;
                fluid_synth_set_gain(handle->synth, 0.7 * c / 127);
            }
        }
    }
    env->ReleaseByteArrayElements(data, b, JNI_ABORT);
}

void JNICALL
jfluid_short_message(JNIEnv *env, jobject obj, jint id, jint status, jint data1, jint data2) {
    fluid_handle_t *handle = getFluid(id);
    if (_event == nullptr) {
        _event = new_fluid_midi_event();
    }

    if (handle != nullptr && handle->synth != nullptr) {
        int command = status & 0xf0;
        int ch = status & 0x0f;

        if (command == 0x80) { //noteoff
            fluid_synth_noteoff(handle->synth, ch, data1);
            return;
        }
        if (command == 0x90) {//noteon
            if (data2 == 0) {
                fluid_synth_noteoff(handle->synth, ch, data1);
                return;
            } else {
                fluid_synth_noteon(handle->synth, ch, data1, data2);
                return;
            }
        }
        if (command == 0xa0) { //polypressure
            fluid_synth_key_pressure(handle->synth, ch, data1, data2);
            return;
        }
        if (command == 0xb0) { //CC
            if (data1 == 0) {
                data2 &= 0x0ff;

                if (data2 >= 0x78) {
                    fluid_synth_set_channel_type(handle->synth, ch, CHANNEL_TYPE_DRUM);
                }else if (ch == 9 && data2 == 0) {
                    data2 = 0x7f;
                    fluid_synth_set_channel_type(handle->synth, ch, CHANNEL_TYPE_DRUM);
                }else {
                    fluid_synth_set_channel_type(handle->synth, ch, CHANNEL_TYPE_MELODIC);
                }
                fluid_synth_bank_select(handle->synth, ch, data2);
                fluid_synth_program_reset(handle->synth);
            } else {
                fluid_synth_cc(handle->synth, ch, data1, data2);
                if (data1 == 121) {
                    reset_control(handle, ch);
                }
            }
            return;
        }
        if (command == 0xc0) { //program change
            fluid_synth_program_change(handle->synth, ch, data1);
            return;
        }
        if (command == 0xd0) {
            fluid_synth_channel_pressure(handle->synth, ch, data1);
            return;
        }
        if (command == 0xe0) { //pitch
            fluid_synth_pitch_bend(handle->synth, ch, (data2 << 7) | data1);
            return;
        }
    }
}
/*

void JNICALL jfluid_set_double(JNIEnv *env, jobject obj, jint id, jstring key, jdouble value) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        MBString jkey(env, key);
        fluid_settings_setnum(handle->settings, jkey.mb_str(), (float) value);
    }
}

void JNICALL jfluid_set_int(JNIEnv *env, jobject obj, jint id, jstring key, jint value) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        MBString jkey(env, key);
        fluid_settings_setint(handle->settings, jkey.mb_str(), (int) value);
    }
}

void JNICALL jfluid_set_string(JNIEnv *env, jobject obj, jint id, jstring key, jstring value) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        MBString jkey(env, key);
        MBString jvalue(env, value);
        fluid_settings_setstr(handle->settings, jkey.mb_str(), jvalue.mb_str());
    }
}

void JNICALL fluid_get_double(JNIEnv *env, jobject obj, jint id, jstring key, jobject ref) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass cl = env->GetObjectClass(ref);
        jmethodID mid = env->GetMethodID(cl, "setValue", "(D)V");
        if (mid != 0) {
            double value = 0;
            MBString jkey(env, key);

            fluid_settings_getnum(handle->settings, jkey.mb_str(), &value);

            env->CallVoidMethod(ref, mid, (jdouble) value);
        }
    }
}

void JNICALL jfluid_get_int(JNIEnv *env, jobject obj, jint id, jstring key, jobject ref) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass cl = env->GetObjectClass(ref);
        jmethodID mid = env->GetMethodID(cl, "setValue", "(I)V");
        if (mid != 0) {
            int value = 0;
            MBString jkey(env, key);

            fluid_settings_getint(handle->settings, jkey.mb_str(), &value);
            env->CallVoidMethod(ref, mid, (jint) value);
        }
    }
}

void JNICALL jfluid_get_string(JNIEnv *env, jobject obj, jint id, jstring key, jobject ref) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass cl = env->GetObjectClass(ref);
        jmethodID mid = env->GetMethodID(cl, "setValue", "(Ljava/lang/String;)V");
        if (mid != 0) {
            jstring jvalue = nullptr;
            char *value = nullptr;
            MBString jkey(env, key);

            fluid_settings_dupstr(handle->settings, jkey.mb_str(), &value);
            jvalue = env->NewStringUTF(value);

            env->CallVoidMethod(ref, mid, jvalue);
        }
    }
}

void JNICALL
jfluid_get_default_double(JNIEnv *env, jobject obj, jint id, jstring key, jobject ref) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass cl = env->GetObjectClass(ref);
        jmethodID mid = env->GetMethodID(cl, "setValue", "(D)V");
        if (mid != 0) {
            MBString jkey(env, key);
            double value = 0;
            fluid_settings_getnum_default(handle->settings, jkey.mb_str(), &value);

            env->CallVoidMethod(ref, mid, (jdouble) value);
        }
    }
}

void JNICALL jfluid_get_default_int(JNIEnv *env, jobject obj, jint id, jstring key, jobject ref) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass cl = env->GetObjectClass(ref);
        jmethodID mid = env->GetMethodID(cl, "setValue", "(I)V");
        if (mid != 0) {
            MBString jkey(env, key);
            int value = 0;
            fluid_settings_getint_default(handle->settings, jkey.mb_str(), &value);

            env->CallVoidMethod(ref, mid, (jint) value);
        }
    }
}

void JNICALL
jfluid_get_default_string(JNIEnv *env, jobject obj, jint id, jstring key, jobject ref) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass cl = env->GetObjectClass(ref);
        jmethodID mid = env->GetMethodID(cl, "setValue", "(Ljava/lang/String;)V");
        if (mid != 0) {
            MBString jkey(env, key);
            char *value;
            fluid_settings_getstr_default(handle->settings, jkey.mb_str(), &value);
            jstring jvalue = env->NewStringUTF(value);
            env->CallVoidMethod(ref, mid, jvalue);
        }
    }
}

void JNICALL
jfluid_get_double_range(JNIEnv *env, jobject obj, jint id, jstring key, jobject minimumRef,
                        jobject maximumRef) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass clMin = env->GetObjectClass(minimumRef);
        jclass clMax = env->GetObjectClass(maximumRef);
        jmethodID midMin = env->GetMethodID(clMin, "setValue", "(D)V");
        jmethodID midMax = env->GetMethodID(clMax, "setValue", "(D)V");
        if (midMin != 0 && midMax != 0) {
            double minimum = 0;
            double maximum = 0;
            MBString jkey(env, key);

            fluid_settings_getnum_range(handle->settings, jkey.mb_str(), &minimum, &maximum);
            env->CallVoidMethod(minimumRef, midMin, (jdouble) minimum);
            env->CallVoidMethod(maximumRef, midMax, (jdouble) maximum);
        }
    }
}

void JNICALL
jfluid_get_int_range(JNIEnv *env, jobject obj, jint id, jstring key, jobject minimumRef,
                     jobject maximumRef) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass clMin = env->GetObjectClass(minimumRef);
        jclass clMax = env->GetObjectClass(maximumRef);
        jmethodID midMin = env->GetMethodID(clMin, "setValue", "(I)V");
        jmethodID midMax = env->GetMethodID(clMax, "setValue", "(I)V");
        if (midMin != 0 && midMax != 0) {
            int minimum = 0;
            int maximum = 0;
            MBString jkey(env, key);

            fluid_settings_getint_range(handle->settings, jkey.mb_str(), &minimum, &maximum);
            env->CallVoidMethod(minimumRef, midMin, (jint) minimum);
            env->CallVoidMethod(maximumRef, midMax, (jint) maximum);
        }
    }
}


typedef struct {
    JNIEnv *env;
    jobject options;
} fluid_settings_foreach_option_data;

void fluid_settings_foreach_option_callback(void *data, char *name, char *option) {
    fluid_settings_foreach_option_data *handle = (fluid_settings_foreach_option_data *) data;

    jstring joption = (handle->env)->NewStringUTF(option);
    jclass cl = (handle->env)->GetObjectClass(handle->options);
    jmethodID mid = (handle->env)->GetMethodID(cl, "add", "(Ljava/lang/Object;)Z");
    if (mid != 0) {
        (handle->env)->CallBooleanMethod(handle->options, mid, joption);
    }
}


void JNICALL
jfluid_get_properties(JNIEnv *env, jobject obj, jint id, jstring key, jobject options) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr) {
        MBString jkey(env, key);
        fluid_settings_foreach_option_data *data = (fluid_settings_foreach_option_data *) malloc(
                sizeof(fluid_settings_foreach_option_data));
        data->env = env;
        data->options = options;

        fluid_settings_foreach_option(handle->settings, jkey.mb_str(), data,
                                      (fluid_settings_foreach_option_t) fluid_settings_foreach_option_callback);

        free(data);
    }
}

void JNICALL
jfluid_is_realtime_property(JNIEnv *env, jobject obj, jint id, jstring key, jobject ref) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass cl = env->GetObjectClass(ref);
        jmethodID mid = env->GetMethodID(cl, "setValue", "(Z)V");
        if (mid != 0) {
            MBString jkey(env, key);
            int value = fluid_settings_is_realtime(handle->settings, jkey.mb_str());
            env->CallVoidMethod(ref, mid, (value != 0 ? JNI_TRUE : JNI_FALSE));
        }
    }
}

*/

static void sigsegvSignalHandler(int sig){
}

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env;
    if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_1) != JNI_OK) {
        return JNI_ERR;
    }
    _javaVM = vm;

    // Find your class. JNI_OnLoad is called from the correct class loader context for this to work.
    jclass c = env->FindClass("org/star_advance/mixandcc/midione/drivers/fluidsynth/JFluidSynth");
    if (c == nullptr) return JNI_ERR;
    /*
        Type Signature     Java ̃f [ ^ ^
        Z     boolean
        B     byte
        C     char
        S     short
        I     int
        J     long
        F     float
        D     double
        L fully-qualified-class;        S C   w
        Ex) java.lang.String;
        [type      z
        Ex) int[] ̏ꍇ
        [I
        (arg-types)ret-type        \ b h ̌^
        Ex) void main(int argc, String[] args);
        (I[Ljava.lang.String;)V
        V     void
    */


    static JNINativeMethod methods[] = {
            {(char *) "open",             (char *) "(Ljava/lang/String;Z)I", reinterpret_cast<void *>(jfluid_open)},
            {(char *) "isSoundFont",      (char *) "(Ljava/lang/String;)Z",  reinterpret_cast<void *>(jfluid_is_soundfont)},
            {(char *) "retune",           (char *) "(IFZI)V",                reinterpret_cast<void *>(jfluid_retune)},
            {(char *) "close",            (char *) "(I)V",                   reinterpret_cast<void *>(jfluid_close)},
            {(char *) "listProgram",      (char *) "(I)Ljava/lang/String;",  reinterpret_cast<void *>(jfluid_list_program)},
            {(char *) "sendShortMessage", (char *) "(IIII)V",                reinterpret_cast<void *>(jfluid_short_message)},
            {(char *) "sendLongMessage",  (char *) "(I[B)V",                 reinterpret_cast<void *>(jfluid_long_message)},
            /*
            void JNICALL jfluid_set_double(JNIEnv* env, jobject obj, jint id, jstring key, jdouble value)
            void JNICALL jfluid_set_int(JNIEnv* env, jobject obj, jint id, jstring key, jint value)
            void JNICALL jfluid_set_string(JNIEnv* env, jobject obj, jint id, jstring key, jstring value)
            void JNICALL fluid_get_double(JNIEnv* env, jobject obj, jint id, jstring key, jobject ref)
            void JNICALL jfluid_get_int(JNIEnv* env, jobject obj, jint id, jstring key, jobject ref)
            void JNICALL jfluid_get_string(JNIEnv* env, jobject obj, jint id, jstring key, jobject ref)
            void JNICALL jfluid_get_default_double(JNIEnv* env, jobject obj, jint id, jstring key, jobject ref)
            void JNICALL jfluid_get_default_int(JNIEnv* env, jobject obj, jint id, jstring key, jobject ref)
            void JNICALL jfluid_get_default_string(JNIEnv* env, jobject obj, jint id, jstring key, jobject ref)
            void JNICALL jfluid_get_double_range(JNIEnv* env, jobject obj, jint id, jstring key, jobject minimumRef, jobject maximumRef)
            void JNICALL jfluid_get_int_range(JNIEnv* env, jobject obj, jint id, jstring key, jobject minimumRef, jobject maximumRef)
            void JNICALL jfluid_get_properties(JNIEnv* env, jobject obj, jint id, jstring key, jobject options)
            void JNICALL jfluid_is_realtime_property(JNIEnv* env, jobject obj, jint id, jstring key, jobject ref)
            */
    };
    int rc = env->RegisterNatives(c, methods, sizeof(methods) / sizeof(JNINativeMethod));
    if (rc != JNI_OK) return rc;
    _javaClass = c;
    _logging = env->GetMethodID(c, (char*)"log", "(Ljava/lang/String;)V" );
    //signal(SIGSEGV , &sigsegvSignalHandler);

    return JNI_VERSION_1_6;
}
FontFileManager.java
package org.star_advance.mixandcc.midione.drivers.fluidsynth;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

import org.star_advance.mixandcc.R;
import org.star_advance.mixandcc.json.JsonHelper;
import org.star_advance.mixandcc.json.JsonHelperSupported;
import org.star_advance.mixandcc.json.MXJsonValue;
import org.star_advance.mixandcc.ui.config.FluidSetting;
import org.star_advance.mixandcc.usedomino.xml.CXFileStocker;
import org.star_advance.mixandcc.usedomino.xml.CXSoundFontFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;

public class FontFileManager implements JsonHelperSupported  {
    static JFluidSynth _jfluid;
    static {
        try {
            _jfluid = new JFluidSynth();
        }catch (Throwable ex) {
            Log.e("FontFileManager", ex.getMessage(), ex);
        }
    }
    static final String TAG = "FontFileManager";
    ArrayList<FontFileEntry> _list;
    FontFileEntry _currentFile = null;
    static FontFileManager _instance;
    int _handle = -1;

    @Override
    public String getJsonFolder() {
        return "FontFileManager";
    }

    public synchronized static FontFileManager getInstance() {
        if (_instance == null) {
            _instance = new FontFileManager();
        }
        return _instance;
    }

    public FontFileEntry getDefault() {
        return _list.get(0);
    }
    public FontFileEntry getCurrent() {
        return _currentFile;
    }
    public FontFileManager() {
        _list = new ArrayList<>();
        _list.addAll(defaultForList());
    }

    public ArrayList<FontFileEntry> getList() {
        return _list;
    }

    public FontFileEntry findByName(String name) {
        for (FontFileEntry e : _list) {
            if (e.getNameText().equals(name)) {
                return e;
            }
        }
        return null;
    }

    ArrayList<FontFileEntry> defaultForList() {
        ArrayList<FontFileEntry> list = new ArrayList<>();
        FontFileEntry e = new FontFileEntry("GeneralUser GS");
        e._file = null;
        e._url = "https://schristiancollins.com/generaluser.php";
        e._rawRes = R.raw.general_user_gs;
        list.add(e);
        return list;
    }
    void copyStream(InputStream input, OutputStream output) throws IOException {
        byte[] buffer = new byte[2048];
        while(true){
            int x = input.read(buffer, 0, buffer.length);
            if (x <= 0) {
                break;
            }
            output.write(buffer, 0, x);
        }
    }
    @Override
    public void loadFromJsonStructure(MXJsonValue.HelperForStructure parent) {
        MXJsonValue.HelperForStructure root = parent.getFollowingStructure("soundFont");
        MXJsonValue.HelperForArray listFiles = root.getFollowingArray("listFiles");
        _list.clear();
        _list.addAll(defaultForList());
        if (listFiles != null) {
            for (int i = 0 ;i  < listFiles.count(); ++ i) {
                MXJsonValue.HelperForStructure item = listFiles.getFollowingStructure(i);
                String file = item.getFollowingText("file", null);
                String name =  item.getFollowingText("name", null);
                String url = item.getFollowingText("url", null);
                if (name != null) {
                    FontFileEntry e = new FontFileEntry(name);
                    e._file = file;
                    e._url = url;
                    _list.add(e);
                }
            }
        }
    }

    @Override
    public void saveToJsonStructure(MXJsonValue.HelperForStructure parent) {
        MXJsonValue.HelperForStructure root = parent.addFollowingStructure("soundFont");
        MXJsonValue.HelperForArray listFiles = root.addFollowingArray("listFiles");
        for(FontFileEntry seek : _list) {
            if (seek._rawRes == 0) {
                MXJsonValue.HelperForStructure item = listFiles.addFollowingStructure();
                item.setFollowingText("file", seek._file);
                item.setFollowingText("name", seek._name);
                item.setFollowingText("url", seek._url);
            }
        }
    }

    public boolean isFluidUsable() {
        return _jfluid.isUsable();
    }

    public boolean isFluidReadyForPlay() {
        if(isFluidUsable()) {
            return _handle >= 0;
        }
        return false;
    }

    public FontFileEntry createFileFromStream(Context context, String name, InputStream input) throws  IOException {
        String fname = name + "_.sf2";
        for (int i = 0; i < _list.size(); ++ i) {
            if (_list.get(i).getNameText().equals(name)) {
                _list.remove(i);
                i --;
                continue;
            }
        }
        OutputStream output = context.openFileOutput(fname, Context.MODE_PRIVATE);
        if (output != null) {
            try {
                copyStream(input, output);
                output.close();
                output = null;

                File file = new File(context.getFilesDir(), fname);
                if (!_jfluid.isSoundFont(file.getPath())) {
                    Log.e(TAG, "Not soundfont");
                    context.deleteFile(name);
                    return null;
                }
                FontFileEntry e = new FontFileEntry(name);
                e._name = name;
                e._file = fname;
                _list.add(e);
                JsonHelper.write(context, this);
                return e;
            }finally {
                try {
                    if (output != null) {
                        output.close();
                    }
                }catch (IOException ex) {
                    Log.e(TAG, ex.getMessage(), ex);
                }
            }
        }
        return null;
    }
    boolean createFileFromResource(Context context, FontFileEntry entry) {
        entry._file = entry._name + "_.sf2";
        File file = new File(context.getFilesDir(), entry._file);
        if (file.exists()) {
            return true;
        }
        InputStream input = null;
        OutputStream output = null;
        try {
            input = context.getResources().openRawResource(entry._rawRes);
            output = context.openFileOutput(entry._file, Context.MODE_PRIVATE);
            copyStream(input, output);
            output.close();
            output = null;

            File path = new File(context.getFilesDir(), entry._file);
            if (_jfluid.isSoundFont(path.toString())) {
                return true;
            }
        }catch (IOException ex) {
            Log.e(TAG, ex.getMessage(), ex);
        }finally {
            if (output != null) {
                try {
                    output.close();
                }catch(IOException ex) {

                }
            }
            if (input != null) {
                try {
                    input.close();
                }catch(IOException ex) {

                }
            }
        }
        context.deleteFile(entry._file);
        return false;
    }

    public boolean loadFontFile(Context context, FontFileEntry entry, boolean reload) {
        FluidSetting params = FluidSetting.getInstance();
        if (_currentFile == entry && !reload) {
            return true;
        }
        boolean lowLatency = params._soundFontLowLatency.getValue();

        synchronized (_jfluid) {
            if (entry._file == null || entry._file.isBlank()) {
                entry._file = entry._name  +"_.sf2";
            }
            File file = new File(context.getFilesDir(), entry._file);
            if (file.exists() == false && entry._rawRes > 0) {
                if (!createFileFromResource(context, entry)) {
                    return false;
                }
            }
            if (_handle >= 0) {
                //_fluid.close(_handle);
                _handle = -1;
            }
            _handle = _jfluid.open(file.getPath(), lowLatency);
            if (_handle < 0) {
                new Handler(Looper.getMainLooper()).post(() -> {
                    Toast.makeText(context, "this font file broken or too large to load.", Toast.LENGTH_SHORT).show();
                });
                return false;
            }

            _currentFile = entry;
            if (!params._soundFontTuneEqualTemperament.getValue() || params._soundFontTuneAHz.getValue() != 440) {
                _jfluid.retune(_handle, params._soundFontTuneAHz.getValue(), params._soundFontTuneEqualTemperament.getValue(), params._soundFontTuneBaseKey.getValue());
            }

            FluidSetting.getInstance()._soundFont.setValue(entry._name);

            byte[] reset = new byte[]{(byte) 0xF0, (byte) 0x7e, (byte) 0x7f, (byte) 0x09, (byte) 0x01, (byte) 0xf7};
            _jfluid.sendLongMessage(_handle, reset);
            entry._cxFile = new CXSoundFontFile(entry._name);
            CXFileStocker.getInstance().setCXSoundFontFile(entry._cxFile);
            return true;
        }
    }

    public String listProgram() {
        if (_jfluid != null && _handle >= 0) {
            return _jfluid.listProgram(_handle);
        }
        return null;
    }

    public void sendShortMessage(int status, int data1, int data2) {
        if (_jfluid != null && _handle >= 0) {
            _jfluid.sendShortMessage(_handle, status, data1, data2);
        }
    }
    public void sendLongMessage(byte[] data) {
        if (_jfluid != null && _handle >= 0) {
            _jfluid.sendLongMessage(_handle, data);
        }
    }
}
FontFileEntry.java
package org.star_advance.mixandcc.midione.drivers.fluidsynth;

import org.star_advance.mixandcc.ui.userchoice.UserChoiceElement;
import org.star_advance.mixandcc.usedomino.xml.CXSoundFontFile;

public class FontFileEntry implements UserChoiceElement{
    CXSoundFontFile _cxFile;
    String _name;
    String _file;
    int _rawRes;
    String _url;
    FontFileEntry(String name) {
        _name = name;
        _file = null;
    }

    public CXSoundFontFile getCXFile() {
        return _cxFile;
    }


    @Override
    public int getNameRes() {
        return 0;
    }

    @Override
    public int getSubLabel() {
        return 0;
    }

    @Override
    public String getNameText() {
        return _name;
    }

    @Override
    public String getSubLabelText() {
        return _url;
    }
}
JFluidSynth.java
package org.star_advance.mixandcc.midione.drivers.fluidsynth;

import android.util.Log;

public class JFluidSynth {
    private boolean _loaded = false;

    JFluidSynth() {
        try {
            System.loadLibrary("native-lib");
            _loaded = true;
        } catch (Throwable e) {
            System.err.println("App can't load native-lib - (FluidSynth).");
            e.printStackTrace();
        }
    }

    public  void log(String str) {
        Log.e("Fluid", str);
    }
    public boolean isUsable() {
        return _loaded;
    }

    public synchronized native int open(String font, boolean lowlatency);

    public native boolean isSoundFont(String font);
    public synchronized native String listProgram(int handle);

    public synchronized native void close(int handle);

    public synchronized native void retune(int handle, float hzamust, boolean equalTemp, int baseKey);

    public synchronized native void sendShortMessage(int handle, int status, int data1, int data2);
    public synchronized native void sendLongMessage(int handle, byte[] data);
}
OneDeviceSoundFont.java
package org.star_advance.mixandcc.midione.drivers.fluidsynth;

import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;

import org.star_advance.mixandcc.common.MXMidiStatic;
import org.star_advance.mixandcc.common.MXQueue;
import org.star_advance.mixandcc.midione.MidiOne;
import org.star_advance.mixandcc.midione.MidiOneStart;
import org.star_advance.mixandcc.midione.v1.OneDevice;
import org.star_advance.mixandcc.midione.v1.OneEventCounter;
import org.star_advance.mixandcc.midione.v1.OneInput;
import org.star_advance.mixandcc.midione.v1.OneMessage;
import org.star_advance.mixandcc.midione.v1.OneOutput;
import org.star_advance.mixandcc.ui.config.FluidSetting;

public class OneDeviceSoundFont extends OneDevice {
    public static final String TAG = "OneDeviceSoundFont";

    public OneDeviceSoundFont(@NonNull OneDriverSoundFont service) {
        super(service, "<SoundFont>", "sfz");
    }

    MXQueue<OneMessage> _queue = new MXQueue<>();
    boolean useThread = false;
    FontFileManager _manager = FontFileManager.getInstance();
    public void prepareOutput() {
        if (!_manager.isFluidUsable()) {
            return;
        }

        if (_manager.isFluidReadyForPlay()) {
            OneOutput out = getOutput(0);
            getEventCounter().countIt(OneEventCounter.EVENT_CONNECTED);
            MidiOne.getInstance().fireOnDeviceConnectionChanged(this);
            MidiOne.getInstance().fireOnOutputOpened(out);
            return;
        }
        else {
            new Thread(()->{
                Context context = FluidSetting.getInstance().getAppContext();
                if (_manager.loadFontFile(context,  _manager.getDefault(), false)) {
                    OneOutput out = getOutput(0);
                    getEventCounter().countIt(OneEventCounter.EVENT_CONNECTED);
                    MidiOne.getInstance().fireOnDeviceConnectionChanged(this);
                    MidiOne.getInstance().fireOnOutputOpened(out);
                }
                else {
                    MidiOne.getInstance().cancelIfPrimalOut(getOutput(0));
                }
            }).start();
        }
    }

    public void retuneAgain() {
        FluidSetting params = FluidSetting.getInstance();
        retune(params._soundFontTuneAHz.getValue(), params._soundFontTuneEqualTemperament.getValue(), params._soundFontTuneBaseKey.getValue());
    }

    public void retune(int ahz, boolean equalTemp, int baseKey) {
        FluidSetting params = FluidSetting.getInstance();

        params._soundFontTuneAHz.setValue(ahz);
        params._soundFontTuneEqualTemperament.setValue(equalTemp);

        if (_manager.isFluidReadyForPlay()) {
            synchronized (MidiOneStart.getInstance()) {
                _manager._jfluid.retune(_manager._handle, params._soundFontTuneAHz.getValue(), params._soundFontTuneEqualTemperament.getValue(), baseKey);
            }
        }
    }

    @Override
    public int getSortOrder() {
        return 2;
    }

    @Override
    public void startAccessDevice() {
        prepareOutput();
    }

    @Override
    protected void onDispose() {

    }

    @Override
    public int countOutput() {
        return 1;
    }

    @Override
    public int countInput() {
        return 0;
    }

    @Override
    public OneOutput allocateOutput(int track) {
        OneOutput out = new OneOutput(this, track) {
            @Override
            public boolean dispatchOne(OneMessage one) {
                if (one != null && _manager.isFluidReadyForPlay()) {
                    if (useThread) {
                        launchInfinity();
                        _queue.push(one);
                    } else {
                        if (one.isBinaryMessage()) {
                            _manager.sendLongMessage(one.getBinary());
                        } else {
                            int status = one.getStatus();
                            int command = status & 0xf0;
                            int data1 = one.getData1();
                            int data2 = one.getData2();

                            if (command >= 0x80 && command <= 0xe0) {
                                if (command == MXMidiStatic.COMMAND_CH_CONTROLCHANGE) {
                                    int x = -1;
                                    if (data1 == MXMidiStatic.DATA1_CC_SOUND_RESONANCE) {
                                        x = 22;
                                    }
                                    if (data1 == MXMidiStatic.DATA1_CC_SOUND_BLIGHTNESS) {
                                        x = 21;
                                    }
                                    if (data1 == MXMidiStatic.DATA1_CC_SOUND_ATTACKTIME) {
                                        x = 11;
                                    }
                                    if (data1 == MXMidiStatic.DATA1_CC_SOUND_RELEASETIME) {
                                        x = 15;
                                    }
                                    if (x >= 0) {
                                        int channel = status & 0x0f;
                                        int cc = MXMidiStatic.COMMAND_CH_CONTROLCHANGE + channel;
                                        _manager.sendShortMessage(cc, MXMidiStatic.DATA1_CC_NRPN_MSB, 127);
                                        _manager.sendShortMessage(cc, MXMidiStatic.DATA1_CC_NRPN_LSB, x);
                                        _manager.sendShortMessage(cc, MXMidiStatic.DATA1_CC_DATAENTRY, data2);
                                        _manager.sendShortMessage(cc, MXMidiStatic.DATA1_CC_DATAENTRY2, data2);
                                        //Log.e(TAG, "@CC " + data1 +"  will replase to NPRN " + x + " -> " + data2);
                                        return true;
                                    }
                                }
                                _manager.sendShortMessage(status, data1, data2);
                            }
                        }
                    }
                    return true;
                }
                return false;
            }

            @Override
            public int getNameRes() {
                return 0;
            }

            @Override
            public String getNameText() {
                FluidSetting params = FluidSetting.getInstance();
                if (params._soundFont.getValue() == null || params._soundFont.getValue().isEmpty()) {
                    return _name + "(" + FluidSetting.defaultFontName + ")";
                } else {
                    return _name + "(" + params._soundFont + ")";
                }
            }

            @Override
            public void onClose() {
            }
        };
        return out;
    }

    @Override
    public OneInput allocateInput(int track) {
        return null;
    }

    Thread _infinity;

    public void launchInfinity() {
        if (_infinity != null) {
            return;
        }
        Thread t = new Thread() {
            public void run() {
                _infinity = Thread.currentThread();
                try {
                    while (true) {
                        OneMessage one = _queue.pop();
                        if (one != null) {
                            if (one.isBinaryMessage()) {
                                _manager.sendLongMessage(one.getBinary());

                                if (one.isReset()) {
                                    retuneAgain();
                                }
                            } else {
                                int status = one.getStatus();
                                int data1 = one.getData1();
                                int data2 = one.getData2();
                                if (status >= 0x80 && status <= 0xef) {
                                    int command = status & 0xf0;
                                    int channel = status & 0x0f;

                                    _manager.sendShortMessage(status, data1, data2);
                                }
                            }
                        } else {
                            break;
                        }
                    }
                } catch (Throwable ex) {
                    Log.e(TAG, "PostMessageError", ex);
                } finally {
                    _infinity = null;
                }
            }
        };
        t.start();
        try {
            while (_infinity == null) {
                Thread.sleep(1);
            }
        } catch (InterruptedException ex) {

        }
    }
}
OneDriverSoundFont.java
package org.star_advance.mixandcc.midione.drivers.fluidsynth;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log;

import org.star_advance.mixandcc.midione.MidiOne;
import org.star_advance.mixandcc.midione.v1.OneDispatcher;
import org.star_advance.mixandcc.midione.v1.OneDriver;

public class OneDriverSoundFont extends OneDriver {
    static final String TAG = "MihMidiDriverSFZ";

    @Override
    public void startDeepScan(Context context) {
    }

    @Override
    public void stopDeepScan(Context context) {

    }

    boolean _usable = true;

    @SuppressLint("NewApi")
    public OneDriverSoundFont(Context context, MidiOne manager) {
        super("SFZ", manager);
    }

    OneDeviceSoundFont _sfz;

    boolean _alreadyEnum = false;

    @Override
    public void startFirstContact(Context context) {
        if (_alreadyEnum) {
            return;
        }
        if (!_usable) {
            return;
        }
        try {
            _alreadyEnum = true;
            if (_sfz == null) {
                _sfz = new OneDeviceSoundFont(this);
                addDevice(_sfz);
            }
        } catch (Throwable ex) {
            Log.e(TAG, ex.getMessage(), ex);
            _alreadyEnum = false;
        }
    }

    @Override
    public void terminateAllDevices() {
        super.terminateAllDevices();
    }

    OneDispatcher _onRead = null;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)

project(mylib
        VERSION 1.0
        DESCRIPTION "mylib project"
        LANGUAGES CXX
)

add_library(native-lib SHARED src/main/cpp/native-lib.cpp)

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

target_compile_features(native-lib PUBLIC cxx_std_20)

# Create a variable fluidsynth_DIR to specify where the fluidsynth library is located.
set(fluidsynth_DIR C:/github/fluidsynth-2.4.6-android24)

message(“Architecture1 = ${ANDROID_ABI}")
message(“Architecture2 = ${CMAKE_ANDROID_ARCH_ABI}")

set(NDK_DIR C:/Users/yaman/AppData/Local/Android/Sdk/ndk/android-ndk-r26d)
set(NDK_SUBDIR ${NDK_DIR}/toolchains/llvm/prebuilt/windows-x86_64/lib/clang/17.0.2/lib/linux)

set(NDK_DIR C:/Users/yaman/AppData/Local/Android/Sdk/ndk/27.0.11718014)
set(NDK_SUBDIR ${NDK_DIR}/toolchains/llvm/prebuilt/windows-x86_64/lib/clang/18/lib/linux)

set(NDK_DIR C:/Users/yaman/AppData/Local/Android/Sdk/ndk/28.0.12916984)
set(NDK_SUBDIR ${NDK_DIR}/toolchains/llvm/prebuilt/windows-x86_64/lib/clang/19/lib/linux)

set(NDK_DIR C:/Users/yaman/AppData/Local/Android/Sdk/ndk/29.0.13113456)
set(NDK_SUBDIR ${NDK_DIR}/toolchains/llvm/prebuilt/windows-x86_64/lib/clang/20/lib/linux)

if (${ANDROID_ABI} STREQUAL "arm64-v8a")
    set(lib_omp_DIR ${NDK_SUBDIR}/aarch64/)
elseif (${ANDROID_ABI} STREQUAL "armeabi-v7a")
    set(lib_omp_DIR ${NDK_SUBDIR}/arm/)
elseif (${ANDROID_ABI} STREQUAL "x86_64")
    set(lib_omp_DIR ${NDK_SUBDIR}/x86_64/)
elseif (${ANDROID_ABI} STREQUAL "x86")
    set(lib_omp_DIR ${NDK_SUBDIR}/i386/)
endif ()

# Create a variable lib_other_DIR to specify where the other non-fluidsynth libraries is located.
#set(lib_c_DIR C:/github/android-ndk-r27c/toolchains/llvm/prebuilt/windows-x86_64/sysroot/usr/lib/aarch64-linux-android/)
# set(lib_c_DIR C:/github/back/MixAndCC/app/build/intermediates/cxx/Debug/5ea1y5rp/obj/arm64-v8a)
# Fluidsynth library code will be calling some non-fluidsynth functions which are not part of
# default NDK, so we add the binaries as dependencies of our code.

#add_library(libc++_shared SHARED IMPORTED)
#set_target_properties(libc++_shared PROPERTIES IMPORTED_LOCATION ${lib_c_DIR}/libc++_shared.so)

add_library(libomp SHARED IMPORTED)
set_target_properties(libomp PROPERTIES IMPORTED_LOCATION ${lib_omp_DIR}/libomp.so)

# Our code (native-lib.cpp) will be calling fluidsynth functions, so adding the fluidsynth binaries as dependencies.

add_library(libFLAC SHARED IMPORTED)
set_target_properties(libFLAC PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/libFLAC.so)

add_library(libfluidsynth SHARED IMPORTED)
set_target_properties(libfluidsynth PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/libfluidsynth.so)

add_library(libfluidsynth-assetloader SHARED IMPORTED)
set_target_properties(libfluidsynth-assetloader PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/libfluidsynth-assetloader.so)

add_library(libgio-2.0 SHARED IMPORTED)
set_target_properties(libgio-2.0 PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/libgio-2.0.so)

add_library(libglib-2.0 SHARED IMPORTED)
set_target_properties(libglib-2.0 PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/libglib-2.0.so)

add_library(libgmodule-2.0 SHARED IMPORTED)
set_target_properties(libgmodule-2.0 PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/libgmodule-2.0.so)

add_library(libgobject-2.0 SHARED IMPORTED)
set_target_properties(libgobject-2.0 PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/libgobject-2.0.so)

add_library(libgthread-2.0 SHARED IMPORTED)
set_target_properties(libgthread-2.0 PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/libgthread-2.0.so)

add_library(libinstpatch-1.0 SHARED IMPORTED)
set_target_properties(libinstpatch-1.0 PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/libinstpatch-1.0.so)

add_library(liboboe SHARED IMPORTED)
set_target_properties(liboboe PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/liboboe.so)

add_library(libogg SHARED IMPORTED)
set_target_properties(libogg PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/libogg.so)

add_library(libopus SHARED IMPORTED)
set_target_properties(libopus PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/libopus.so)

add_library(libpcre SHARED IMPORTED)
set_target_properties(libpcre PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/libpcre.so)

add_library(libsndfile SHARED IMPORTED)
set_target_properties(libsndfile PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/libsndfile.so)

add_library(libvorbis SHARED IMPORTED)
set_target_properties(libvorbis PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/libvorbis.so)

add_library(libvorbisenc SHARED IMPORTED)
set_target_properties(libvorbisenc PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/libvorbisenc.so)

add_library(libvorbisfile SHARED IMPORTED)
set_target_properties(libvorbisfile PROPERTIES IMPORTED_LOCATION ${fluidsynth_DIR}/lib/${ANDROID_ABI}/libvorbisfile.so)


# Specifies the directory where the C or C++ source code will look the #include <yourlibrary.h> header files
target_include_directories(native-lib PRIVATE ${fluidsynth_DIR}/include)


# Link everything all together. Notice that native-lib should be the first element in the list.
target_link_libraries(
        native-lib

        # Non-fluidsynth binaries
        #libc++_shared
        libomp

        # fluidsynth binaries
        libFLAC
        libfluidsynth
        libfluidsynth-assetloader
        libgio-2.0
        libglib-2.0
        libgmodule-2.0
        libgobject-2.0
        libgthread-2.0
        libinstpatch-1.0
        liboboe
        libogg
        libopus
        libpcre
        libsndfile
        libvorbis
        libvorbisenc
        libvorbisfile
)

Now, tried something,I think If i can disable Mem-MAP it will work,,
but, following approach not worked.

    my_sfloader = new_fluid_defsfloader(handle->settings);
    int x = fluid_sfloader_set_callbacks(my_sfloader,
                                         my_open,
                                         my_read,
                                         my_seek,
                                         my_tell,
                                         my_close);
    fluid_synth_add_sfloader(handle->synth, my_sfloader);

with “FluidR3_GM.sf2”, when I Load it twice on Android13
after my_close, this error happens.

A Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x10001ffffffff in tid 32609 (Thread-6), pid 32444 (dvance.mixandcc)
DEBUG crash_dump64 A Cmdline: org.star_advance.mixandcc
DEBUG crash_dump64 A pid: 32444, tid: 32609, name: Thread-6 >>> org.star_advance.mixandcc <<<
DEBUG crash_dump64 A #00 pc 000000000004bca0 /data/app/nw80t0gJKl2af2_SKYx_oA==/org.star_advance.mixandcc-

I need assist. Thank you.

And I feel I can diagnostics myself. but,
Can I hear how to compile FluidSynth myself use Android Studio?

Thank you.

native_lib2.cpp
#include <jni.h>
#include <string>
#include <unistd.h>
#include <sstream>
#include "C:/github/fluidsynth-2.4.6-android24/include/fluidsynth.h"
#include <codecvt>
#import <signal.h>

#define FLUID_PEAK_ATTENUATION  960.0f
#define FLUID_CENTS_HZ_SIZE     1200
#define FLUID_VEL_CB_SIZE       128
#define FLUID_CB_AMP_SIZE       1441
#define FLUID_PAN_SIZE          1002
JavaVM *_javaVM = nullptr;
jclass _javaClass = nullptr;
jmethodID _logging = nullptr;
jobject _javaObj = nullptr;
JNIEnv *_env = nullptr;

#define USE_LOG 1
//#define USE_LOG2 2

void log_java(JNIEnv* env, jobject obj, std::string* message) {
    if (obj != nullptr && obj != _javaObj) {
        _javaObj = obj;
        _env = env;
    }
    if (_logging != nullptr) {
        char *buf = (char *)message->c_str();
        jstring text = env->NewStringUTF(buf);
        env->CallVoidMethod(obj, _logging, text);
    }
}


char* _fileBuf = nullptr;
fpos_t _fileLength = 0;
fpos_t _cursor;

char* readFile(const char *filename) {
    if (_fileBuf != nullptr) {
        free(_fileBuf);
        _fileBuf = nullptr;
    }

    FILE *fp = fopen(filename, "rb");
    if (fseek(fp,0,SEEK_END) == 0) {
        fpos_t fsize;
        fsize = ftell(fp);
        if (fsize >= 1) {
            if (fseek(fp, 0, SEEK_SET) == 0) {
                char *buf = (char *) malloc(fsize + 100);
                if (fread(buf, 1, fsize, fp) == fsize) {
                    _fileBuf = buf;
                    _cursor = 0;
                    _fileLength = fsize;
                    fclose(fp);
                    return buf;
                }
                else if (buf != nullptr) {
                    free(buf);
                }
            }
        }
    }
    _fileBuf = nullptr;
    _cursor = 0;
    _fileLength = 0;
    fclose(fp);
    return nullptr;
}

extern "C" {
    __cdecl void *my_open(const char *filename) {
        char *ptr = readFile(filename);
    #ifdef USE_LOG
        std::stringstream result;
        result << "my_open = " << _fileLength << "ptr = " << ((unsigned long)ptr);
        std::string str(result.str());
        log_java(_env, _javaObj, &str);
    #endif
        return ptr;
    }

    __cdecl int my_read(void *buf, fluid_long_long_t count, void *handle) {
        if (handle != nullptr && handle == _fileBuf) {

            char *ptr = (char *) handle;
            if (ptr != _fileBuf) {
#ifdef USE_LOG2
                std::stringstream result;
                result << "read  broken";
                std::string str(result.str());
                log_java(_env, _javaObj, &str);
#endif
                return -1;
            }
            if (_cursor > _fileLength) {
#ifdef USE_LOG
                std::stringstream result;
                result << "eof";
                std::string str(result.str());
                log_java(_env, _javaObj, &str);
#endif
                return -1;
            }

            char *w = (char *) buf;
            long x = 0;
            for (int i = 0; (i < count) && (_cursor + x < _fileLength); ++i) {
                w[i] = ptr[_cursor + x];
                x ++;
            }
            _cursor += x;
#ifdef USE_LOG2
            std::stringstream result;
            long left = _fileLength - _cursor;
            result << "read  " << x << "then next is " << _cursor << "/" << _fileLength << " left = " << left << " -> request was " << count;
            std::string str(result.str());
            log_java(_env, _javaObj, &str);
#endif
            return x;
        }
#ifdef USE_LOG2
        std::stringstream result;
        long left = _fileLength - _cursor;
        result << "close";
        std::string str(result.str());
        log_java(_env, _javaObj, &str);
#endif
        return -1;
    }

    __cdecl int my_seek(void *handle, fluid_long_long_t offset, int origin) {
        if (handle != nullptr && handle == _fileBuf) {
#ifdef USE_LOG2
            std::stringstream result;
            result << "my_seek " << offset << " / " << origin;
            std::string str(result.str());
            log_java(_env, _javaObj, &str);
#endif

            if (origin == SEEK_SET) {
                int x = offset;
                if (x >= 0 && x <= _fileLength) {
                    _cursor = x;
#ifdef USE_LOG2
                    std::stringstream result2;
                    result2 << "now  " << _cursor << " / " << _fileLength;
                    std::string str2(result2.str());
                    log_java(_env, _javaObj, &str2);
#endif
                    return 0;
                }
            } else if (origin == SEEK_CUR) {
                int x = _cursor + offset;
                if (x >= 0 && x <= _fileLength) {
                    _cursor = x;
#ifdef USE_LOG2
                    std::stringstream result2;
                    result2 << "now  " << _cursor << " / " << _fileLength;
                    std::string str2(result2.str());
                    log_java(_env, _javaObj, &str2);
#endif
                    return x >= _fileLength ? -1 : 0;
                }
            } else if (origin == SEEK_END) {
                int x = _fileLength - offset;
                if (x >= 0 && x <= _fileLength) {
                    _cursor = x;
#ifdef USE_LOG2
                    std::stringstream result2;
                    result2 << "now  " << _cursor << " / " << _fileLength;
                    std::string str2(result2.str());
                    log_java(_env, _javaObj, &str2);
#endif
                    return 0;
                }
            }
        }
#ifdef USE_LOG2
        std::stringstream result;
        result << "err";
        std::string str2(result.str());
        log_java(_env, _javaObj, &str2);
#endif
        return -1;
    }

    __cdecl int my_close(void *handle) {
#ifdef USE_LOG
        std::stringstream result;
        result << "my_close" << " prepare " << _cursor;
        std::string str(result.str());
        log_java(_env, _javaObj, &str);
#endif
        if (handle != nullptr && handle == _fileBuf) {
            if (_fileBuf != nullptr) {
                free(_fileBuf);
                _fileLength = 0;
                _cursor = 0;
                _fileBuf = nullptr;
            }
        }
#ifdef USE_LOG
        std::stringstream result2;
        result2 << "my_close" << " done " << _cursor;
        std::string str2(result2.str());
        log_java(_env, _javaObj, &str2);
#endif
        return 0;
    }

    __cdecl fluid_long_long_t my_tell(void *handle) {
        if (handle != nullptr && handle == _fileBuf) {
#ifdef USE_LOG2
            std::stringstream result;
            result << "my_tell = " << _cursor;
            std::string str(result.str());
            log_java(_env, _javaObj, &str);
#endif
            return _cursor;
        }
#ifdef USE_LOG2
        std::stringstream result;
        result << "my_tell = -1";
        std::string str(result.str());
        log_java(_env, _javaObj, &str);
#endif
        return -1;
    } ;
}

typedef struct {
    fluid_settings_t *settings;
    fluid_synth_t *synth;
    fluid_audio_driver_t *audio;

    int soundfont_id;
} fluid_handle_t;

#define MAX_SYNTH 10
fluid_handle_t *_allHandle = nullptr;

class MBString {
    std::wstring _w_str;
    std::string _mb_str;

public:
    MBString(JNIEnv *env, jstring &jstr) {
        const jchar *raw = env->GetStringChars(jstr, 0);
        jsize len = env->GetStringLength(jstr);
        _w_str.assign(raw, raw + len);
        env->ReleaseStringChars(jstr, raw);

        std::wstring_convert<std::codecvt_utf8<wchar_t> > converter;

        _mb_str.append(converter.to_bytes(_w_str));
    }

    ~MBString() {
    }

    const wchar_t *w_str() {
        return _w_str.c_str();
    }

    const char *mb_str() {
        return _mb_str.c_str();
    }
};

int findEmptyId() {
    if (_allHandle == nullptr) {
        auto ptr = (unsigned long)malloc(sizeof(fluid_handle_t ) * MAX_SYNTH * 2 + 32) + 1;
        while ((ptr % 16) != 0) {
            ptr ++;
        }
        _allHandle = (fluid_handle_t *) (void *)ptr;
        if (_allHandle == nullptr) {
            return -1;
        }
        memset(_allHandle, 0, sizeof(fluid_handle_t) * MAX_SYNTH);
    }
    return 0;
    /*
    for (int i = 0; i < MAX_SYNTH; ++i) {
        if (_allHandle[i].settings == nullptr) {
            return i;
        }
    }
    return -1;*/
}
/*
void disposeFluidSynth(int id) {
    if (_allHandle == nullptr) {
        return;
    }
    if (id < 0 || id >= MAX_SYNTH) {
        return;
    }
    fluid_handle_t *handle = _allHandle + id;
    if (handle->synth != nullptr) {
        delete_fluid_synth(handle->synth);
        handle->synth = nullptr;
    }
    if (handle->settings != nullptr) {
        delete_fluid_settings(handle->settings);
        handle->settings = nullptr;
    }
    handle->soundfont_id = 0;
}
*/
fluid_handle_t *getFluid(int handle) {
    if (handle < 0 || handle >= MAX_SYNTH) {
        return nullptr;
    }
    fluid_handle_t *ptr = _allHandle + handle;
    return ptr;
}

extern void initializeMod(fluid_handle_t *fluid);

extern void JNICALL jfluid_close(JNIEnv *env, jobject obj, jint id);

jstring  JNICALL jfluid_list_program(JNIEnv *env, jobject obj, int id) {
    fluid_handle_t *handle = getFluid(id);
    if (handle == nullptr) {
        return env->NewStringUTF("");
    }

    fluid_sfont_t *font = fluid_synth_get_sfont_by_id(handle->synth, handle->soundfont_id);
    int offset = fluid_synth_get_bank_offset(handle->synth, handle->soundfont_id);

    fluid_sfont_iteration_start(font);
    std::stringstream result;
    result << "list program " << id << " addresss " << ((unsigned long) (font)) << "\n";
    char buff[101];

    while (true) {
        fluid_preset_t *preset = fluid_sfont_iteration_next(font);
        if (preset == nullptr) {
            preset = fluid_sfont_iteration_next(font);
            if (preset == nullptr) {
                preset = fluid_sfont_iteration_next(font);
                if (preset == nullptr) {
                    break;
                }
            }
        }
        int banknum = fluid_preset_get_banknum(preset) + offset;
        int program = fluid_preset_get_num(preset);
        const char *name = fluid_preset_get_name(preset);

        for (int i = 0; i < 100; ++ i) {
            char c = name[i];
            if ((c & 0x80) != 0) {
                c = '_';
            }
            buff[i] = c;
            if (c == 0) {
                break;
            }
        }
        buff[100] = 0;

        result << program;
        result << ",";
        result << banknum;
        result << ",";
        result << "-1";
        result << ",";
        result << buff;
        result << "\n";
    }

    std::string str(result.str());
    char *buf = (char *)str.c_str();
    return env->NewStringUTF(buf);
}


/* calculate cent from key (0 to 11) */
int getKey12Cent(int key) {
    switch (key) {
        case 0:
            return 0;
        case 2:
            return 204;
        case 4:
            return 386;
        case 5:
            return 498;
        case 7:
            return 702;
        case 9:
            return 884;
        case 11:
            return 1088;
    }
    return (getKey12Cent(key - 1) + getKey12Cent(key + 1)) / 2;
}

/* calculate cent from step (0 to 12 + octave offset (1 octave = 1200)) */
double getKeysCent(int step) {
    if (step == 0) {
        return 0;
    }
    double acent = 0;

    while (step < 0) {
        acent -= 1200;
        step += 12;
    }
    while (step >= 12) {
        acent += 1200;
        step -= 12;
    }
    return acent + getKey12Cent(step);
}

void getPitchesJustIntonation(double *pitches, int root) {
    /* root = 0 to 12 */
    for (int key = 0; key < 0x80; ++key) {
        int distance = key - root;
        double cent = getKeysCent(distance);
        pitches[key] = cent;
    }
}

void getPitchesTemperament(double *pitches) {
    for (int key = 0; key < 0x80; ++key) {
        pitches[key] = key * 100;
    }
}

void adjustAmust(double *pitches, float hzamust) {
    /* hzAmust = around 440 to 443 ? */
    auto log2_ratio = log2(hzamust / 440.);
    double amust = 100. * 69 + 1200. * log2_ratio;

    double slide = amust - pitches[69];
    for (int key = 0; key < 0x80; ++key) {
        pitches[key] += slide;
    }
}

int my_fluid_retune(JNIEnv* env, jobject obj, fluid_handle_t *handle, float hzamust, bool equalTemp, int baseKey) {
    int keys[0x80];
    double pitches[0x80];

    fluid_sfont_t *font = fluid_synth_get_sfont_by_id(handle->synth, handle->soundfont_id);

    fluid_sfont_iteration_start(font);
    int ret = FLUID_OK;
    for (int i = 0; i < 0x80; ++i) {
        keys[i] = i;
    }
    if (equalTemp) {
        getPitchesTemperament(pitches);
        adjustAmust(pitches, hzamust);
    } else {
        getPitchesJustIntonation(pitches, baseKey);
        adjustAmust(pitches, hzamust);
    }

    int offset = fluid_synth_get_bank_offset(handle->synth, handle->soundfont_id);
    while (true) {
        fluid_preset_t *preset = fluid_sfont_iteration_next(font);
        if (preset == nullptr) {
            break;
        }
        int bank = fluid_preset_get_banknum(preset) + offset;
        int program = fluid_preset_get_num(preset);

        std::stringstream debugMessage;

#ifdef USE_LOG
        debugMessage.str("");
        debugMessage.clear();
        debugMessage << "bank:" << bank << ", prog:" << program;
        std::string  str2(debugMessage.str());
        log_java(env, obj, &str2);
#endif
        if (bank < 0 || bank >= 128 || program < 0 || program >= 128) {
            continue;
        }

        /*
        if (equalTemp && hzamust== 440.) {
            for (int ch = 0; ch < 16; ++ ch) {
                fluid_synth_activate_tuning(handle->synth, ch, bank, program, 0);
            }
            continue;
        }*/

        int code = fluid_synth_tune_notes(handle->synth, bank, program, 0x80, keys, pitches, 1);
        if (code == FLUID_OK) {
            for (int ch = 0; ch < 16; ++ch) {
                code = fluid_synth_activate_tuning(handle->synth, ch, bank, program, 1);
            }
        }
        if (code != FLUID_OK) {
            ret = code;
        }
    }
    return ret;
}

void JNICALL
jfluid_retune(JNIEnv *env, jobject obj, jint id, jfloat hzamust, jboolean equalTemp, int baseKey) {
    fluid_handle_t *handle = getFluid(id);
    if (handle == nullptr) {
        return;
    }

    my_fluid_retune(env, obj, handle, hzamust, equalTemp >= 1, baseKey);
}

jboolean JNICALL jfluid_is_soundfont(JNIEnv *env, jobject obj, jstring fontFile) {
    MBString file(env, fontFile);
    if (fluid_is_soundfont(file.mb_str())) {
        return JNI_TRUE;
    }
    else {
        return JNI_FALSE;
    }
}

fluid_sfloader_t *my_sfloader = nullptr;

jint JNICALL jfluid_open(JNIEnv *env, jobject obj, jstring fontFile, jboolean lowlatency) {
    int id = findEmptyId();

#ifdef USE_LOG
    std::stringstream debugMessage;
    debugMessage.str("");
    debugMessage.clear();
    debugMessage << "emptID =" << id;
    std::string  str2(debugMessage.str());
    log_java(env, obj, &str2);
#endif
    if (id < 0) {
        return id;
    }

    fluid_handle_t *handle = getFluid(id);
#ifdef USE_LOG
    debugMessage.str("");
    debugMessage.clear();
    debugMessage << "handle =" << handle;
    std::string  str3(debugMessage.str());
    log_java(env, obj, &str3);
#endif
    if (handle == nullptr) {
        return -1;
    }
    /*
    if (handle->audio != nullptr) {
        delete_fluid_audio_driver(handle->audio);
        handle->audio = nullptr;
    }*/

    if (handle->settings == nullptr) {
        handle->settings = new_fluid_settings();


        fluid_settings_setstr(handle->settings, "synth.audio-driver", "oboe");
        fluid_settings_setstr(handle->settings, "audio.oboe.sample-rate-conversion-quality", "Fastest");
        if (lowlatency) {
            fluid_settings_setstr(handle->settings, "audio.oboe.sharing-mode", "Exclusive");
            //fluid_settings_setint(handle->settings, "audio.periods", 8);
            //fluid_settings_setint(handle->settings, "audio.period-size", 8)    ;
            fluid_settings_setstr(handle->settings, "audio.oboe.performance-mode", "LowLatency");
        }
        else {
            fluid_settings_setstr(handle->settings, "audio.oboe.sharing-mode", "Shared");
            //fluid_settings_setint(handle->settings, "audio.periods", 16);
            //fluid_settings_setint(handle->settings, "audio.period-size", 32);
            fluid_settings_setstr(handle->settings, "audio.oboe.performance-mode", "None");
        }
        //fluid_settings_setint(handle->settings, "synth.audio-channels", 2);
        //fluid_settings_setint(handle->settings, "synth.cpu-cores", 1);
        //fluid_settings_setint(handle->settings, "audio.realtime-prio", 60);
        //fluid_settings_setstr(handle->settings, "player.timing-source", "sample");

    }
#ifdef USE_LOG
    debugMessage.str("");
    debugMessage.clear();
    debugMessage << "setting=" << handle->settings;
    std::string  str4(debugMessage.str());
    log_java(env, obj, &str4);
#endif

    if (handle->synth == nullptr) {
        handle->synth = new_fluid_synth(handle->settings);
        if (my_sfloader == nullptr) {
            my_sfloader = new_fluid_defsfloader(handle->settings);
            int x = fluid_sfloader_set_callbacks(my_sfloader,
                                                 my_open,
                                                 my_read,
                                                 my_seek,
                                                 my_tell,
                                                 my_close);
            fluid_synth_add_sfloader(handle->synth, my_sfloader);
#ifdef USE_LOG
            debugMessage.str("");
            debugMessage.clear();
            debugMessage << "x = " << x;
            std::string str41(debugMessage.str());
            log_java(env, obj, &str41);
#endif
        }
    }

#ifdef USE_LOG
    debugMessage.str("");
    debugMessage.clear();
    debugMessage << "synth=" << handle->synth;
    std::string  str5(debugMessage.str());
    log_java(env, obj, &str5);
#endif
    if (handle->synth == nullptr) {
        //jfluid_close(env, obj, id);
        return -1;
    }

    //fluid_synth_set_polyphony(handle->synth, 128);
    //fluid_synth_set_interp_method(handle->synth, -1, FLUID_INTERP_LINEAR);

#ifdef USE_LOG
    debugMessage.str("");
    debugMessage.clear();
    debugMessage << "mod ok 1 ";
    std::string str60(debugMessage.str());
    log_java(env, obj, &str60);
#endif
    /*
    while (false && fluid_synth_sfcount(handle->synth)) {
        fluid_sfont_t *font = fluid_synth_get_sfont(handle->synth, 0);
#ifdef USE_LOG
        debugMessage.clear();
        debugMessage << "mod ok 2 " << (unsigned long)font ;
        std::string str61(debugMessage.str());
        log_java(env, obj, &str61);
#endif

        if (font == nullptr) {
            break;
        }
        fluid_synth_remove_sfont(handle->synth, font);

    }
    if (handle->soundfont_id >= 0) {
        fluid_synth_sfunload(handle->synth, handle->soundfont_id, 0);
        handle->soundfont_id = -1;
    }*/
#ifdef USE_LOG
        debugMessage.str("");
        debugMessage.clear();
        debugMessage << "mod ok 2";
        std::string str61(debugMessage.str());
        log_java(env, obj, &str61);
#endif
#ifdef USE_LOG
        debugMessage.str("");
        debugMessage.clear();
        debugMessage << "mod ok 3 ";
        std::string str63(debugMessage.str());
        log_java(env, obj, &str63);
#endif
        MBString file(env, fontFile);
        if (fluid_is_soundfont(file.mb_str())) {
#ifdef USE_LOG
            debugMessage.str("");
            debugMessage.clear();
            debugMessage << "mod ok 4 ";
            std::string str64(debugMessage.str());
            log_java(env, obj, &str64);

            std::string str641(file.mb_str());
            log_java(env, obj, &str641);
#endif

#ifdef USE_LOG
            debugMessage.str("");
            debugMessage.clear();
            debugMessage << "mod ok 4.5 ";
            std::string str642(debugMessage.str());
            log_java(env, obj, &str642);

            std::string str643(file.mb_str());
            log_java(env, obj, &str643);
#endif
            handle->soundfont_id = fluid_synth_sfload(handle->synth, file.mb_str(), 0);
        }
        else {
            return -1;
        }


#ifdef USE_LOG
    debugMessage.str("");
    debugMessage.clear();
    debugMessage << "mod ok 5 ";
    debugMessage << "soundFont=" << handle->soundfont_id;
    std::string str65(debugMessage.str());
    log_java(env, obj, &str65);
#endif

     if (handle->soundfont_id == FLUID_FAILED) {
        //jfluid_close(env, obj, id);
        return -1;
     }


     if (handle->audio == nullptr) {
        handle->audio = new_fluid_audio_driver(handle->settings, handle->synth);
        //initializeMod(handle);
    }

#ifdef USE_LOG
debugMessage.str("");
    debugMessage.clear();
    debugMessage << "audio=" << handle->audio;
    std::string  str8(debugMessage.str());
    log_java(env, obj, &str8);
#endif

    return id;
}

void JNICALL jfluid_close(JNIEnv *env, jobject obj, jint id) {
        /*
    }
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr) {
        if (handle->soundfont_id != 0) {
            fluid_synth_sfunload(handle->synth, handle->soundfont_id, 1);
            handle->soundfont_id = -1;
        }
        if (handle->synth != nullptr) {
            delete_fluid_synth(handle->synth);
            handle->synth = nullptr;
        }
        if (handle->audio != nullptr) {
            delete_fluid_audio_driver(handle->audio);
            handle->audio = nullptr;
        }
        if (handle->settings != nullptr) {
            delete_fluid_settings(handle->settings);
            handle->settings = nullptr;
        }
    }*/
}


#define SOUND_CTRL_FILTER_RESONANCE 71
#define SOUND_CTRL_RELEASE 72
#define SOUND_CTRL_ATTACK 73
#define SOUND_CTRL_CUTOFF 74
#define SOUND_CTRL_DECAY 75
#define SOUND_CTRL_SUSTAIN 79

#define SOUND_EFFECT_REVERVE 91
#define SOUND_EFFECT_TREMOLO 92
#define SOUND_EFFECT_CHORUS 93
#define SOUND_EFFECT_DETUNE 94
#define SOUND_EFFECT_PHASER 95

void reset_control(fluid_handle_t* handle, int ch) {
    fluid_synth_cc(handle->synth, ch, 10, 64); //panpot
    fluid_synth_cc(handle->synth, ch, SOUND_CTRL_FILTER_RESONANCE, 0);
    fluid_synth_cc(handle->synth, ch, SOUND_CTRL_RELEASE, 64);
    fluid_synth_cc(handle->synth, ch, SOUND_CTRL_ATTACK, 64);
    fluid_synth_cc(handle->synth, ch, SOUND_CTRL_CUTOFF, 64);
    fluid_synth_cc(handle->synth, ch, SOUND_CTRL_DECAY, 64);
    fluid_synth_cc(handle->synth, ch, SOUND_CTRL_SUSTAIN, 64);

    fluid_synth_cc(handle->synth, ch, SOUND_EFFECT_REVERVE, 30);
    fluid_synth_cc(handle->synth, ch, SOUND_EFFECT_TREMOLO, 0);
    fluid_synth_cc(handle->synth, ch, SOUND_EFFECT_CHORUS, 0);
    fluid_synth_cc(handle->synth, ch, SOUND_EFFECT_DETUNE, 0);
    fluid_synth_cc(handle->synth, ch, SOUND_EFFECT_PHASER, 0);
    fluid_synth_set_portamento_mode(handle->synth, ch, FLUID_CHANNEL_PORTAMENTO_MODE_EACH_NOTE);
}

void initializeMod(fluid_handle_t* handle) {

    fluid_synth_set_gain(handle->synth, 0.7);
    // soundfont spec says that if cutoff is >20kHz and resonance Q is 0, then no filtering occurs

    fluid_mod_t *mod;
    /*
    mod  = new_fluid_mod();

    fluid_mod_set_source1(mod,
                          SOUND_EFFECT_REVERVE, //reverve
                          FLUID_MOD_CC
                          | FLUID_MOD_LINEAR
                          | FLUID_MOD_UNIPOLAR
                          | FLUID_MOD_POSITIVE);
    fluid_mod_set_source2(mod, 0, 0);
    fluid_mod_set_dest(mod, GEN_REVERBSEND);
    fluid_mod_set_amount(mod, 200);
    fluid_synth_add_default_mod(handle->synth, mod, FLUID_SYNTH_ADD);

    mod  = new_fluid_mod();

    fluid_mod_set_source1(mod,
                          SOUND_EFFECT_CHORUS, //chorus
                          FLUID_MOD_CC
                          | FLUID_MOD_LINEAR
                          | FLUID_MOD_UNIPOLAR
                          | FLUID_MOD_POSITIVE);
    fluid_mod_set_source2(mod, 0, 0);
    fluid_mod_set_dest(mod, GEN_CHORUSSEND);
    fluid_mod_set_amount(mod, 1000);
    fluid_synth_add_default_mod(handle->synth, mod, FLUID_SYNTH_ADD);

     */
    // http://www.synthfont.com/SoundFont_NRPNs.PDF
/*
    mod = new_fluid_mod();
    fluid_mod_set_source1(mod,
                          SOUND_CTRL_FILTER_RESONANCE,
                          FLUID_MOD_CC
                          | FLUID_MOD_UNIPOLAR
//                            | FLUID_MOD_LINEAR
                          | FLUID_MOD_CONCAVE
                          | FLUID_MOD_POSITIVE);
    fluid_mod_set_source2(mod, 0, 0);
    fluid_mod_set_dest(mod, GEN_FILTERQ);
    fluid_mod_set_amount(mod, FLUID_PEAK_ATTENUATION);
    fluid_synth_add_default_mod(handle->synth, mod, FLUID_SYNTH_OVERWRITE);
    mod = new_fluid_mod();

    fluid_mod_set_source1(mod,
                          SOUND_CTRL_RELEASE, // MIDI CC 72 Release time
                          FLUID_MOD_CC
                          | FLUID_MOD_BIPOLAR
                          | FLUID_MOD_LINEAR
                          | FLUID_MOD_POSITIVE);
    fluid_mod_set_source2(mod, 0, 0);
    fluid_mod_set_dest(mod, GEN_VOLENVRELEASE);
    fluid_mod_set_amount(mod, 12000);
    fluid_synth_add_default_mod(handle->synth, mod, FLUID_SYNTH_ADD);

    mod = new_fluid_mod();

    float env_amount = 20000.0f;
    fluid_mod_set_source1(mod,
                          SOUND_CTRL_ATTACK, // MIDI CC 73 Attack time
                          FLUID_MOD_CC
                          | FLUID_MOD_BIPOLAR
                          | FLUID_MOD_LINEAR
                          | FLUID_MOD_POSITIVE);
    fluid_mod_set_source2(mod, 0, 0);
    fluid_mod_set_dest(mod, GEN_VOLENVATTACK);
    fluid_mod_set_amount(mod, env_amount);
    fluid_synth_add_default_mod(handle->synth, mod, FLUID_SYNTH_ADD);
    // soundfont spec says that if cutoff is >20kHz and resonance Q is 0, then no filtering occurs
    mod = new_fluid_mod();

    fluid_mod_set_source1(mod,
                          SOUND_CTRL_CUTOFF, // MIDI CC 74 Brightness (cutoff frequency, FILTERFC)
                          FLUID_MOD_CC
                          | FLUID_MOD_LINEAR
                          | FLUID_MOD_BIPOLAR
                          | FLUID_MOD_POSITIVE);
    fluid_mod_set_source2(mod, 0, 0);
    fluid_mod_set_dest(mod, GEN_FILTERFC);
    fluid_mod_set_amount(mod, 10000.0f);
    fluid_synth_add_default_mod(handle->synth, mod, FLUID_SYNTH_ADD);

    mod = new_fluid_mod();

    fluid_mod_set_source1(mod,
                          SOUND_CTRL_DECAY, // MIDI CC 75 Decay Time
                          FLUID_MOD_CC
                          | FLUID_MOD_UNIPOLAR
                          | FLUID_MOD_LINEAR
                          | FLUID_MOD_POSITIVE);
    fluid_mod_set_source2(mod, 0, 0);
    fluid_mod_set_dest(mod, GEN_VOLENVDECAY);
    fluid_mod_set_amount(mod, env_amount);
    fluid_synth_add_default_mod(handle->synth, mod, FLUID_SYNTH_ADD);

    mod = new_fluid_mod();

    fluid_mod_set_source1(mod,
                          SOUND_CTRL_SUSTAIN, // MIDI CC 79 undefined
                          FLUID_MOD_CC
                          | FLUID_MOD_UNIPOLAR
                          | FLUID_MOD_CONCAVE
                          | FLUID_MOD_NEGATIVE);
    fluid_mod_set_source2(mod, 0, 0);
    fluid_mod_set_dest(mod, GEN_VOLENVSUSTAIN);

    // fluice_voice.c#fluid_voice_update_param()
    // clamps the range to between 0 and 1000, so we'll copy that
    fluid_mod_set_amount(mod, 1000.0f);
    fluid_synth_add_default_mod(handle->synth, mod, FLUID_SYNTH_ADD);

    for (int ch = 0; ch < 16; ++ch) {
        reset_control(handle, ch);
    }
*/
}

fluid_midi_event_t *_event = nullptr;

int gmReset[] = {0xF0, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
int gsReset[] = {0xF0, 0x41, -1, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7};
int xgReset[] = {0xF0, 0x43, -1, 0x4C, 0x00, 0x00, 0x7E, 0x00, 0xF7};
int masterVolume[] = {0xF0, 0x7f, 0x7F, 0x04, 0x01, 0x11, -1, 0xF7};
int masterVolume_pos = 6;

bool matchBytes(int *temp, jbyte *data, int dataLen) {
    for (int i = 0; i < dataLen; ++i) {
        int c = data[i] & 0xff;
        int x = temp[i];
        if (x == 0xf7) {
            if (c == 0xf7) {
                return true;
            } else {
                return false;
            }
        }
        if (x != c) {
            if (x < 0) {
                continue;
            } else {
                return false;
            }
        }
    }
    return false;
}

extern void JNICALL
jfluid_short_message(JNIEnv *env, jobject obj, jint id, jint status, jint data1, jint data2);

void JNICALL jfluid_long_message(JNIEnv *env, jobject obj, jint id, jbyteArray data) {
    jsize len = env->GetArrayLength(data);
    jbyte *b = env->GetByteArrayElements(data, 0);
    if (len <= 3) {
        int status = 0;
        int data1 = 0;
        int data2 = 0;
        if (len >= 1) {
            status = b[0] & 0xff;
        }
        if (len >= 2) {
            data1 = b[1] & 0xff;
        }
        if (len >= 3) {
            data2 = b[2] & 0xff;
        }
        jfluid_short_message(env, obj, id, status, data1, data2);
    } else {
        if (matchBytes(gmReset, b, len) || matchBytes(gsReset, b, len) ||
            matchBytes(xgReset, b, len)) {
            fluid_handle_t *handle = getFluid(id);
            if (handle != nullptr) {
                fluid_synth_system_reset(handle->synth);
                fluid_synth_program_reset(handle->synth);
            }
        } else if (matchBytes(masterVolume, b, len)) {
            fluid_handle_t *handle = getFluid(id);
            if (handle != nullptr) {
                int c = b[masterVolume_pos] & 0x0ff;
                fluid_synth_set_gain(handle->synth, 0.7 * c / 127);
            }
        }
    }
    env->ReleaseByteArrayElements(data, b, JNI_ABORT);
}

void JNICALL
jfluid_short_message(JNIEnv *env, jobject obj, jint id, jint status, jint data1, jint data2) {
    fluid_handle_t *handle = getFluid(id);
    if (_event == nullptr) {
        _event = new_fluid_midi_event();
    }

    if (handle != nullptr && handle->synth != nullptr) {
        int command = status & 0xf0;
        int ch = status & 0x0f;

        if (command == 0x80) { //noteoff
            fluid_synth_noteoff(handle->synth, ch, data1);
            return;
        }
        if (command == 0x90) {//noteon
            if (data2 == 0) {
                fluid_synth_noteoff(handle->synth, ch, data1);
                return;
            } else {
                fluid_synth_noteon(handle->synth, ch, data1, data2);
                return;
            }
        }
        if (command == 0xa0) { //polypressure
            fluid_synth_key_pressure(handle->synth, ch, data1, data2);
            return;
        }
        if (command == 0xb0) { //CC
            if (data1 == 0) {
                data2 &= 0x0ff;

                if (data2 >= 0x78) {
                    fluid_synth_set_channel_type(handle->synth, ch, CHANNEL_TYPE_DRUM);
                }else if (ch == 9 && data2 == 0) {
                    data2 = 0x7f;
                    fluid_synth_set_channel_type(handle->synth, ch, CHANNEL_TYPE_DRUM);
                }else {
                    fluid_synth_set_channel_type(handle->synth, ch, CHANNEL_TYPE_MELODIC);
                }
                fluid_synth_bank_select(handle->synth, ch, data2);
                fluid_synth_program_reset(handle->synth);
            } else {
                fluid_synth_cc(handle->synth, ch, data1, data2);
                if (data1 == 121) {
                    reset_control(handle, ch);
                }
            }
            return;
        }
        if (command == 0xc0) { //program change
            fluid_synth_program_change(handle->synth, ch, data1);
            return;
        }
        if (command == 0xd0) {
            fluid_synth_channel_pressure(handle->synth, ch, data1);
            return;
        }
        if (command == 0xe0) { //pitch
            fluid_synth_pitch_bend(handle->synth, ch, (data2 << 7) | data1);
            return;
        }
    }
}
/*

void JNICALL jfluid_set_double(JNIEnv *env, jobject obj, jint id, jstring key, jdouble value) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        MBString jkey(env, key);
        fluid_settings_setnum(handle->settings, jkey.mb_str(), (float) value);
    }
}

void JNICALL jfluid_set_int(JNIEnv *env, jobject obj, jint id, jstring key, jint value) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        MBString jkey(env, key);
        fluid_settings_setint(handle->settings, jkey.mb_str(), (int) value);
    }
}

void JNICALL jfluid_set_string(JNIEnv *env, jobject obj, jint id, jstring key, jstring value) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        MBString jkey(env, key);
        MBString jvalue(env, value);
        fluid_settings_setstr(handle->settings, jkey.mb_str(), jvalue.mb_str());
    }
}

void JNICALL fluid_get_double(JNIEnv *env, jobject obj, jint id, jstring key, jobject ref) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass cl = env->GetObjectClass(ref);
        jmethodID mid = env->GetMethodID(cl, "setValue", "(D)V");
        if (mid != 0) {
            double value = 0;
            MBString jkey(env, key);

            fluid_settings_getnum(handle->settings, jkey.mb_str(), &value);

            env->CallVoidMethod(ref, mid, (jdouble) value);
        }
    }
}

void JNICALL jfluid_get_int(JNIEnv *env, jobject obj, jint id, jstring key, jobject ref) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass cl = env->GetObjectClass(ref);
        jmethodID mid = env->GetMethodID(cl, "setValue", "(I)V");
        if (mid != 0) {
            int value = 0;
            MBString jkey(env, key);

            fluid_settings_getint(handle->settings, jkey.mb_str(), &value);
            env->CallVoidMethod(ref, mid, (jint) value);
        }
    }
}

void JNICALL jfluid_get_string(JNIEnv *env, jobject obj, jint id, jstring key, jobject ref) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass cl = env->GetObjectClass(ref);
        jmethodID mid = env->GetMethodID(cl, "setValue", "(Ljava/lang/String;)V");
        if (mid != 0) {
            jstring jvalue = nullptr;
            char *value = nullptr;
            MBString jkey(env, key);

            fluid_settings_dupstr(handle->settings, jkey.mb_str(), &value);
            jvalue = env->NewStringUTF(value);

            env->CallVoidMethod(ref, mid, jvalue);
        }
    }
}

void JNICALL
jfluid_get_default_double(JNIEnv *env, jobject obj, jint id, jstring key, jobject ref) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass cl = env->GetObjectClass(ref);
        jmethodID mid = env->GetMethodID(cl, "setValue", "(D)V");
        if (mid != 0) {
            MBString jkey(env, key);
            double value = 0;
            fluid_settings_getnum_default(handle->settings, jkey.mb_str(), &value);

            env->CallVoidMethod(ref, mid, (jdouble) value);
        }
    }
}

void JNICALL jfluid_get_default_int(JNIEnv *env, jobject obj, jint id, jstring key, jobject ref) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass cl = env->GetObjectClass(ref);
        jmethodID mid = env->GetMethodID(cl, "setValue", "(I)V");
        if (mid != 0) {
            MBString jkey(env, key);
            int value = 0;
            fluid_settings_getint_default(handle->settings, jkey.mb_str(), &value);

            env->CallVoidMethod(ref, mid, (jint) value);
        }
    }
}

void JNICALL
jfluid_get_default_string(JNIEnv *env, jobject obj, jint id, jstring key, jobject ref) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass cl = env->GetObjectClass(ref);
        jmethodID mid = env->GetMethodID(cl, "setValue", "(Ljava/lang/String;)V");
        if (mid != 0) {
            MBString jkey(env, key);
            char *value;
            fluid_settings_getstr_default(handle->settings, jkey.mb_str(), &value);
            jstring jvalue = env->NewStringUTF(value);
            env->CallVoidMethod(ref, mid, jvalue);
        }
    }
}

void JNICALL
jfluid_get_double_range(JNIEnv *env, jobject obj, jint id, jstring key, jobject minimumRef,
                        jobject maximumRef) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass clMin = env->GetObjectClass(minimumRef);
        jclass clMax = env->GetObjectClass(maximumRef);
        jmethodID midMin = env->GetMethodID(clMin, "setValue", "(D)V");
        jmethodID midMax = env->GetMethodID(clMax, "setValue", "(D)V");
        if (midMin != 0 && midMax != 0) {
            double minimum = 0;
            double maximum = 0;
            MBString jkey(env, key);

            fluid_settings_getnum_range(handle->settings, jkey.mb_str(), &minimum, &maximum);
            env->CallVoidMethod(minimumRef, midMin, (jdouble) minimum);
            env->CallVoidMethod(maximumRef, midMax, (jdouble) maximum);
        }
    }
}

void JNICALL
jfluid_get_int_range(JNIEnv *env, jobject obj, jint id, jstring key, jobject minimumRef,
                     jobject maximumRef) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass clMin = env->GetObjectClass(minimumRef);
        jclass clMax = env->GetObjectClass(maximumRef);
        jmethodID midMin = env->GetMethodID(clMin, "setValue", "(I)V");
        jmethodID midMax = env->GetMethodID(clMax, "setValue", "(I)V");
        if (midMin != 0 && midMax != 0) {
            int minimum = 0;
            int maximum = 0;
            MBString jkey(env, key);

            fluid_settings_getint_range(handle->settings, jkey.mb_str(), &minimum, &maximum);
            env->CallVoidMethod(minimumRef, midMin, (jint) minimum);
            env->CallVoidMethod(maximumRef, midMax, (jint) maximum);
        }
    }
}


typedef struct {
    JNIEnv *env;
    jobject options;
} fluid_settings_foreach_option_data;

void fluid_settings_foreach_option_callback(void *data, char *name, char *option) {
    fluid_settings_foreach_option_data *handle = (fluid_settings_foreach_option_data *) data;

    jstring joption = (handle->env)->NewStringUTF(option);
    jclass cl = (handle->env)->GetObjectClass(handle->options);
    jmethodID mid = (handle->env)->GetMethodID(cl, "add", "(Ljava/lang/Object;)Z");
    if (mid != 0) {
        (handle->env)->CallBooleanMethod(handle->options, mid, joption);
    }
}


void JNICALL
jfluid_get_properties(JNIEnv *env, jobject obj, jint id, jstring key, jobject options) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr) {
        MBString jkey(env, key);
        fluid_settings_foreach_option_data *data = (fluid_settings_foreach_option_data *) malloc(
                sizeof(fluid_settings_foreach_option_data));
        data->env = env;
        data->options = options;

        fluid_settings_foreach_option(handle->settings, jkey.mb_str(), data,
                                      (fluid_settings_foreach_option_t) fluid_settings_foreach_option_callback);

        free(data);
    }
}

void JNICALL
jfluid_is_realtime_property(JNIEnv *env, jobject obj, jint id, jstring key, jobject ref) {
    fluid_handle_t *handle = getFluid(id);
    if (handle != nullptr && handle->settings != nullptr && key != nullptr) {
        jclass cl = env->GetObjectClass(ref);
        jmethodID mid = env->GetMethodID(cl, "setValue", "(Z)V");
        if (mid != 0) {
            MBString jkey(env, key);
            int value = fluid_settings_is_realtime(handle->settings, jkey.mb_str());
            env->CallVoidMethod(ref, mid, (value != 0 ? JNI_TRUE : JNI_FALSE));
        }
    }
}

*/

static void sigsegvSignalHandler(int sig){
}

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env;
    if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_1) != JNI_OK) {
        return JNI_ERR;
    }
    _javaVM = vm;

    // Find your class. JNI_OnLoad is called from the correct class loader context for this to work.
    jclass c = env->FindClass("org/star_advance/mixandcc/midione/drivers/fluidsynth/JFluidSynth");
    if (c == nullptr) return JNI_ERR;
    /*
        Type Signature     Java ̃f [ ^ ^
        Z     boolean
        B     byte
        C     char
        S     short
        I     int
        J     long
        F     float
        D     double
        L fully-qualified-class;        S C   w
        Ex) java.lang.String;
        [type      z
        Ex) int[] ̏ꍇ
        [I
        (arg-types)ret-type        \ b h ̌^
        Ex) void main(int argc, String[] args);
        (I[Ljava.lang.String;)V
        V     void
    */


    static JNINativeMethod methods[] = {
            {(char *) "open",             (char *) "(Ljava/lang/String;Z)I", reinterpret_cast<void *>(jfluid_open)},
            {(char *) "isSoundFont",      (char *) "(Ljava/lang/String;)Z",  reinterpret_cast<void *>(jfluid_is_soundfont)},
            {(char *) "retune",           (char *) "(IFZI)V",                reinterpret_cast<void *>(jfluid_retune)},
            {(char *) "close",            (char *) "(I)V",                   reinterpret_cast<void *>(jfluid_close)},
            {(char *) "listProgram",      (char *) "(I)Ljava/lang/String;",  reinterpret_cast<void *>(jfluid_list_program)},
            {(char *) "sendShortMessage", (char *) "(IIII)V",                reinterpret_cast<void *>(jfluid_short_message)},
            {(char *) "sendLongMessage",  (char *) "(I[B)V",                 reinterpret_cast<void *>(jfluid_long_message)},
            /*
            void JNICALL jfluid_set_double(JNIEnv* env, jobject obj, jint id, jstring key, jdouble value)
            void JNICALL jfluid_set_int(JNIEnv* env, jobject obj, jint id, jstring key, jint value)
            void JNICALL jfluid_set_string(JNIEnv* env, jobject obj, jint id, jstring key, jstring value)
            void JNICALL fluid_get_double(JNIEnv* env, jobject obj, jint id, jstring key, jobject ref)
            void JNICALL jfluid_get_int(JNIEnv* env, jobject obj, jint id, jstring key, jobject ref)
            void JNICALL jfluid_get_string(JNIEnv* env, jobject obj, jint id, jstring key, jobject ref)
            void JNICALL jfluid_get_default_double(JNIEnv* env, jobject obj, jint id, jstring key, jobject ref)
            void JNICALL jfluid_get_default_int(JNIEnv* env, jobject obj, jint id, jstring key, jobject ref)
            void JNICALL jfluid_get_default_string(JNIEnv* env, jobject obj, jint id, jstring key, jobject ref)
            void JNICALL jfluid_get_double_range(JNIEnv* env, jobject obj, jint id, jstring key, jobject minimumRef, jobject maximumRef)
            void JNICALL jfluid_get_int_range(JNIEnv* env, jobject obj, jint id, jstring key, jobject minimumRef, jobject maximumRef)
            void JNICALL jfluid_get_properties(JNIEnv* env, jobject obj, jint id, jstring key, jobject options)
            void JNICALL jfluid_is_realtime_property(JNIEnv* env, jobject obj, jint id, jstring key, jobject ref)
            */
    };
    int rc = env->RegisterNatives(c, methods, sizeof(methods) / sizeof(JNINativeMethod));
    if (rc != JNI_OK) return rc;
    _javaClass = c;
    _logging = env->GetMethodID(c, (char*)"log", "(Ljava/lang/String;)V" );
    //signal(SIGSEGV , &sigsegvSignalHandler);

    return JNI_VERSION_1_6;
}

タイトルとURLをコピーしました