美文网首页
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