FMOD的集成与使用
集成第三方 so库的步骤
- 1、到官网 下载对应的库文件
- 2、找到对应cpu架构的 库文件
- 3、把头文件、库文件放到项目对应目录里面
- 4、在gradle(指定CPU架构,Cmake CPU环境变量值)、cmake(第三方库文件链接到项目库文件里面)
- 5、在java、jni里面使用第三方库的api
记住了:Android就是linux的二次封装,所以如果要移植linux的 库到手机端,库是通用的。直接把 .so 库放到项目对应目录里面就好
------------------手机cpu架构指令集----------------------------------
arm64-v8a 实体手机 基本上就是 arm64-v8a
armeabi-v7a
armeabi
x86 模拟器,WindowsPhone(已经倒闭)
FMOD 简介
fmod 是一个音效引擎库 www.fmod.com
游戏引擎 cocos2d unity3d 等 都是默认集成了 fmod来做音效, 因为 fmod是音效引擎库(C库)
FMOD 的使用就类似于你玩 调音师软件的流程,只是调音师可以直接使用,而FMOD通过api,让你能实现类似于调音师的功能
开始调音
pitch 音调调节 默认:1.0 2.0萝莉 0.8大叔 0.5老头
tremolo 颤音调节 默认:5 20.0明显颤抖了
模拟信号的声音是无法保存到电脑里面的,所以科学家就把 模拟信号转换成了数字信号,以便对数字信号的声音进行存储和处理(DSP-digital signal process 数字信号处理)
开始集成
首先把对应的文件集成到项目目录里
修改 CMakeLists.txt
cmake_minimum_required(VERSION 3.22.1) # 表示最低支持的cmake版本
project("voicechange")
# TODO 第一步:导入头文件
include_directories(inc) # CMakeLists.txt 目录下的inc文件夹 放头文件
# TODO 第二步:导入库文件(C++的环境变量)- 最新的方法,以前是 add_xxx(已淘汰)6.0以下才可以
# 环境变量的添加是追加的方式的:%JAVA_HOME%;%ANDROID_HOME%;path1;path2;
# CMAKE_SOURCE_DIR:获得当前CmakeLists.txt的路径
# CMAKE_ANDROID_ARCH_ABI:自动获取 四大平台架构值 也可以在gradle里面进行指定
# 这个set的写法就相当于 path = path + CMAKE_CXX_FLAGS;
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")
# 批量导入 所有的C++文件
#file(GLOB allCPP *.c *.h *.cpp)
# TODO 库名的规则: lib + 名称 + .so (lib .so 系统自动拼接的)
add_library( # Sets the name of the library.
voicechange #libvoicechange.so
SHARED #表示动态库 STATIC 表示静态库
native-lib.cpp
# ${allCPP} # 批量导入
)
# 相当于 var log-lib = log (liblog.so 库的路径)
find_library( # Sets the name of the path variable.
m-lib # 变量名
# Specifies the name of the NDK library that
# you want CMake to locate.
# 这里就相当于 定位到ndk的 liblog.so 库的路径
log #查找NDK的日志打印库
)
# TODO 第三步:链接第三方库 到 我们自己的库(libvoicechange.so)里面
target_link_libraries( # Specifies the target library.
voicechange
# Links the target library to the log library
# included in the NDK.
${m-lib} # 链接m-lib库 到 我们自己的库(libvoicechange.so)里面
fmod
fmodL
)
修改 app/build.gradle
android {
...
defaultConfig {
...
// TODO 第四步:指定CPU架构 Cmake中的本地库,例如:libnative-lib.so libderryku.so
externalNativeBuild{
cmake{
// cppFlags "" // 这样写,默认是支持四大平台
// 指定CPU架构是armeabi-v7a
// 指定CMakeLists.txt里面的环境变量CMAKE_ANDROID_ARCH_ABI的值
// 【注意:这里只指定本地库到armeabi-v7a, x86】 这句代码 还不能决定apk生成
abiFilters ("armeabi-v7a", "x86")
}
// TODO 第五步:指定CPU的架构 apk/lib/平台
// 下面代码不写,默认是四大CPU架构平台
ndk{
// 指定CPU架构是armeabi-v7a, x86
// 【注意:这里只指定编译所有库到armeabi-v7a, x86 进apk】
abiFilters ("armeabi-v7a", "x86")
}
}
}
...
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.22.1'
}
}
...
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
...
// 引入 app下 libs文件夹里面的 fmod.jar库
implementation files('libs/fmod.jar')
...
}
FMOD api的使用
#include
#include
// xxx.h: C的头文件
// xxx.hpp:C++的头文件
// xxx.h ----- xxx.c 一般对应的早期版本
// xxx.hpp -----xxx.cpp C++ NDK 以后基本上 C++多
// 声明 c的头文件,cpp的实现,这样的组合也是可以的。因为C++是兼容C的
#include
#include
using namespace FMOD;
#include "common_log.h"
#undef TYPE_YUANSHENG
#define TYPE_YUANSHENG 0
#undef TYPE_DASHU
#define TYPE_DASHU 1
#undef TYPE_LUOLI
#define TYPE_LUOLI 2
#undef TYPE_JINGSONG
#define TYPE_JINGSONG 3
#undef TYPE_GAOGUAI
#define TYPE_GAOGUAI 4
#undef TYPE_KONGLING
#define TYPE_KONGLING 5
extern "C"
JNIEXPORT void JNICALL
Java_com_hehe_voicechange_MainActivity_fmodPlaySound(JNIEnv *env, jobject thiz, jint type, jstring path) {
const char * _path = env->GetStringUTFChars(path, NULL);
// 声明指针
System * system;
Sound * sound;
Channel * channel;
DSP * dsp;
// TODO 第一步 创建系统
System_Create(&system);
// TODO 第二步 系统的初始化
//参1:最大音轨数,参2:系统初始化标记,参3:额外数据
system->init(32, FMOD_INIT_NORMAL, 0);
// TODO 第三步 创建声音
//参1:路径,参2:声音的初始化标记,参3:额外数据,参4:声音指针
system->createSound(_path, FMOD_DEFAULT, 0, &sound);
// TODO 第四步 播放声音 音轨 声音
//参1:声音,参2:分组音轨,参3:控制,参4:通道
system->playSound(sound, 0, false, &channel);
// TODO 第五步 增加特效
//pitch 音调调节 默认:1.0 2.0萝莉 0.8大叔 0.5老头
//tremolo 颤音调节 默认:5 20.0明显颤抖了
//ECHO 回音
jstring content = nullptr;
float frequency = 1.0f;
switch (type) {
case TYPE_DASHU:
content = env->NewStringUTF("大叔音效播放完成");
// 1、创建数字信号处理对象
system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
// 2、设置音调值 0.8
dsp->setParameterFloat(0, 0.8f);
// 3、把音效添加到音轨里面去
channel->addDSP(0, dsp);
break;
case TYPE_LUOLI:
content = env->NewStringUTF("萝莉音效播放完成");
system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
dsp->setParameterFloat(0, 2.0f);
channel->addDSP(0, dsp);
break;
case TYPE_GAOGUAI:
// 搞怪小黄人音效,就是把音轨的频率调节了快慢
content = env->NewStringUTF("搞怪音效播放完成");
channel->getFrequency(&frequency); //获取当前帧率
channel->setFrequency(frequency*1.5f);// 修改帧率
break;
case TYPE_KONGLING:
system->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);
dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 200);// 回音延时 默认500
dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 10);// 回音衰减度 默认50
channel->addDSP(0, dsp);
break;
case TYPE_JINGSONG:
// ECHO 回音
system->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);
dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 300);// 回音延时 默认500
dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 10);// 回音衰减度 默认50
channel->addDSP(0, dsp); //音轨一
// pitch 音调调节 默认:1.0 2.0萝莉 0.8大叔 0.5老头
system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
dsp->setParameterFloat(0, 0.8f);
channel->addDSP(1, dsp);//音轨二
// tremolo 颤音调节 默认:5 20.0明显颤抖了
system->createDSPByType(FMOD_DSP_TYPE_TREMOLO, &dsp);
dsp->setParameterFloat(0, 20.0f);
channel->addDSP(2, dsp);//音轨三
}
bool isPlaying = 1;
while (isPlaying){
channel->isPlaying(&isPlaying);
usleep(1000*30); // 休眠30毫秒
}
printf("播放结束");
if (content){ // 通知java方法
jclass mainCls = env->GetObjectClass(thiz);
jmethodID mid = env->GetMethodID(mainCls, "soundEnd", "(Ljava/lang/String;)V");
env->CallVoidMethod(thiz, mid, content);
}
// 好习惯:释放资源
dsp->release();
sound->release();
system->close();
system->release();
env->ReleaseStringUTFChars(path, _path);
}