本章内容用来记录导入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);
}
网友评论