美文网首页
Android 导入 So 库的流程

Android 导入 So 库的流程

作者: __Y_Q | 来源:发表于2023-01-27 18:13 被阅读0次

    本章内容用来记录导入so库的流程, 会以 Fmod为例. 点击直达FMOD官网

    1. 新建好工程后将 FMOD 的头文件复制到工程中

    2. src 目录下新建 jniLibs 文件夹, 将各 CPU 架构对应的 so 库复制进去.

    3. 在 app 目录下新建 libs 文件夹, 放入 fmod.jar

    4, 在 app gradle 中引入 jar 包

    dependencies {
        ...
        //引入 jar 包
        implementation files("libs\\fmod.jar")
    }
    

    5.修改 CMakeLists.txt

    5.1 导入头文件
    #导入头文件, 和 cmakelists 同级的情况下直接写文件夹名字, 再外层可以使用相对路径
    include_directories("inc")
    
    5.2 导入库文件
    #上面之导入了头文件, 还需要导入库文件, 必须放到jniLibs中. (jniLibs必须这样命名), 或者再 build 中使用 sourceSet 修改.
    #类似于配置环境变量, 再后面追加. -L${CMakeLists 文件的路径}/../jniLibs/${对应CPU架构的文件夹}
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")
    
    5.3 批量导入源文件
    #批量导入所有源文件
    file(GLOB myAllCppFile *.c *.h *.cpp)
    add_library(
            study002-lib
            SHARED
            ${myAllCppFile}) #这里就不再指定单一的源文件,而使用批量的
    
    5.4 将具体的 so 库链接到 study002-lib
    target_link_libraries(
            study002-lib
            log
            fmod  #将具体的库链接到总库 study002-lib.so 中 ,省略 lib. 当前路径为 jniLibs/arm64-v8a/libfmod.so, cmake会自动寻找
            fmodL #将具体的库链接到总库 study002-lib.so 中 ,省略 lib. 当前路径为 jniLibs/arm64-v8a/libfmodL.so,cmake会自动寻找
    )
    

    6. 生成头文件

    cd 到 app/src/main/java/ 目录下使用 javah com.example.study002.MainActivity 生成头文件(自带宏定义),将生成的头文件放到 cpp 目录下


    CMake 完整代码
    cmake_minimum_required(VERSION 3.22)
    project("study002")
    
    #导入头文件, 和 cmakelists 同级的情况下直接写文件夹名字, 再外层可以使用相对路径
    include_directories("inc")
    
    #上面之导入了头文件, 还需要导入库文件, 必须放到jniLibs中. (jniLibs必须这样命名), 或者再 build 中使用 sourceSet 修改.
    #类似于配置环境变量, 再后面追加. -L${CMakeLists 文件的路径}/../jniLibs/${对应CPU架构的文件夹}
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")
    
    #另一种方式
    #去app build.gradle -> android内添加
    #add_library(xxx SHARED IMPORTED)
    #set_target_properties(xxx PROPERTIES IMPORTED_LOCATION #${CMAKE_SOURCE_DIR}/../jniLibs/arm64-v8a/xxx.so)
    
    
    #批量导入所有源文件
    file(GLOB myAllCppFile *.c *.h *.cpp)
    add_library(
            study002-lib
            SHARED
            ${myAllCppFile}) #这里就不再指定单一的源文件,而使用批量的
    
    #find_library(
    #        log-lib
    #        log)
    
    target_link_libraries(
            study002-lib
            log
            fmod  #将具体的库链接到总库 study002-lib.so 中 ,省略 lib. 当前路径为 jniLibs/arm64-v8a/libfmod.so, cmake会自动寻找
            fmodL #将具体的库链接到总库 study002-lib.so 中 ,省略 lib. 当前路径为 jniLibs/arm64-v8a/libfmodL.so,cmake会自动寻找
    )
    

    Java 代码

    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
        public static final  int MODE_NORMAL = 0;
        public static final  int MODE_DASHU = 1;
        public static final  int MODE_LUOLI = 2;
        public static final  int MODE_GAOGUAI = 3;
        public static final  int MODE_JINGSONG = 4;
        public static final  int MODE_KONGLING = 5;
    
    
        // Used to load the 'study002' library on application startup.
        static {
            System.loadLibrary("study002-lib");
        }
    
        private ActivityMainBinding binding;
    
        private String path;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            binding = ActivityMainBinding.inflate(getLayoutInflater());
            setContentView(binding.getRoot());
    
            path = "file:///android_asset/26.mp3";
            FMOD.init(this);
    
    
            binding.btYuansheng.setOnClickListener(this);
            binding.btDashu.setOnClickListener(this);
            binding.btGaoguai.setOnClickListener(this);
            binding.btJingsong.setOnClickListener(this);
            binding.btKongling.setOnClickListener(this);
            binding.btLuoli.setOnClickListener(this);
        }
    
        @Override
        protected void onDestroy() {
            FMOD.close();
            super.onDestroy();
        }
    
        @SuppressLint("NonConstantResourceId")
        @Override
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.bt_yuansheng:
                    fix(path, MODE_NORMAL);  //正式开发时,这里需要用子线程,.不能使用主线程
                    break;
                case R.id.bt_luoli:
                    fix(path, MODE_LUOLI);
                    break;
                case R.id.bt_dashu:
                    fix(path, MODE_DASHU);
                    break;
                case R.id.bt_gaoguai:
                    fix(path, MODE_GAOGUAI);
                    break;
                case R.id.bt_jingsong:
                    fix(path, MODE_JINGSONG);
                    break;
                case R.id.bt_kongling:
                    fix(path, MODE_KONGLING);
                    break;
                default:
                    break;
            }
        }
        public native void fix(String path, int type);
    
        //让C++调用
    
        public void playerEnd(String msg){
            Toast.makeText(this,  msg, Toast.LENGTH_SHORT).show();
        }
    }
    

    生成的头文件代码

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_example_study002_MainActivity */
    #include "fmod.hpp"
    
    
    #ifndef _Included_com_example_study002_MainActivity
    #define _Included_com_example_study002_MainActivity
    #ifdef __cplusplus
    extern "C" {
    #endif
    #undef com_example_study002_MainActivity_MODE_NORMAL
    #define com_example_study002_MainActivity_MODE_NORMAL 0L
    #undef com_example_study002_MainActivity_MODE_DASHU
    #define com_example_study002_MainActivity_MODE_DASHU 1L
    #undef com_example_study002_MainActivity_MODE_LUOLI
    #define com_example_study002_MainActivity_MODE_LUOLI 2L
    #undef com_example_study002_MainActivity_MODE_GAOGUAI
    #define com_example_study002_MainActivity_MODE_GAOGUAI 3L
    #undef com_example_study002_MainActivity_MODE_JINGSONG
    #define com_example_study002_MainActivity_MODE_JINGSONG 4L
    #undef com_example_study002_MainActivity_MODE_KONGLING
    #define com_example_study002_MainActivity_MODE_KONGLING 5L
    /*
     * Class:     com_example_study002_MainActivity
     * Method:    fix
     * Signature: (Ljava/lang/String;I)V
     */
    JNIEXPORT void JNICALL Java_com_example_study002_MainActivity_fix(JNIEnv *, jobject, jstring, jint);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

    总库的代码 native-lib.cpp

    #include <jni.h>
    #include <string>
    #include <unistd.h>
    #include <android/log.h>
    #include "com_example_study002_MainActivity.h"
    
    using namespace FMOD;
    
    #define _MY_LOGE(...) __android_log_print(ANDROID_LOG_DEBUG, "JNI", __VA_ARGS__);
    
    extern "C"
    JNIEXPORT void JNICALL Java_com_example_study002_MainActivity_fix(JNIEnv * env, jobject thisz, jstring path, jint type){
    
        char * content_ = "播放完毕";
    
        const char *path_ = env->GetStringUTFChars(path, nullptr);
    
        System  * system = nullptr;
        Sound   * sound = nullptr;
        Channel * channel = nullptr;
        DSP     * dsp = nullptr;
    
        //基础回顾, 二级指针存放的是一级指针的地址. 可以修改一级指针的值
    
        //开始初始化
        System_Create(&system);
        //参数1: 最大音轨
        //参数2: 初始化标记
        system->init(32, FMOD_INIT_NORMAL, nullptr);
        //创建声音
        system->createSound(path_, FMOD_DEFAULT, nullptr, &sound);
        //播放声音
        system->playSound(sound, nullptr, false, &channel);
    
        switch (type) {
            case com_example_study002_MainActivity_MODE_NORMAL:
                break;
            case com_example_study002_MainActivity_MODE_LUOLI:
                //创建DSP类型的pitch 音调调节
                system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
                //设置 pitch 音调调节
                dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 2.0f);
                channel->addDSP(0, dsp);
                break;
            case com_example_study002_MainActivity_MODE_DASHU:
                //创建DSP类型的pitch 音调调节
                system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
                //设置 pitch 音调调节
                dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.6f);
                channel->addDSP(0, dsp);
                break;
            case com_example_study002_MainActivity_MODE_GAOGUAI:
                //需要改变音轨频率
                //先获得当前频率
                float mFrequency;
                channel->getFrequency(&mFrequency);
                //修改频率
                channel->setFrequency(mFrequency * 2.0f);
                break;
            case com_example_study002_MainActivity_MODE_JINGSONG:
                //会有很多通道, 进行拼接
                //先调低音调
                system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
                //设置 pitch 音调调节
                dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.8f);
                channel->addDSP(0, dsp);
                //再调整回声
                system->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);
                dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 100); //回音延迟,10-5000, 默认500
                dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 20);//回音的衰减度,默认50
                channel->addDSP(1,dsp);
                //再添加颤抖
                system->createDSPByType(FMOD_DSP_TYPE_TREMOLO, &dsp);
                dsp->setParameterFloat(FMOD_DSP_TREMOLO_FREQUENCY, 16.0f);
                dsp->setParameterFloat(FMOD_DSP_TREMOLO_SKEW, 16.0f);
                dsp->setParameterFloat(FMOD_DSP_TREMOLO_SPREAD, 3.0f);
                channel->addDSP(2,dsp);
                break;
            case com_example_study002_MainActivity_MODE_KONGLING:
                //设置回音
                system->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);
                dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 100); //回音延迟,10-5000, 默认500
                dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 10);//回音的衰减度,默认50
                channel->addDSP(0,dsp);
                break;
        }
    
        
        bool  isPlayer = true;
        while (isPlayer) {
            channel->isPlaying(&isPlayer);
            usleep(1000 * 1000);
        }
    
        //开始回收
        sound->release();
        system->close();
        system->release();
        env->ReleaseStringUTFChars(path, path_);
    
        jclass objectClass = env->GetObjectClass(thisz);
        jmethodID methodId = env->GetMethodID(objectClass, "playerEnd", "(Ljava/lang/String;)V");
        jstring msg = env->NewStringUTF(content_);
        env->CallVoidMethod(thisz, methodId, msg);
    }
    
    
    

    相关文章

      网友评论

          本文标题:Android 导入 So 库的流程

          本文链接:https://www.haomeiwen.com/subject/kauehdtx.html