一、下载fmod三方引擎sdk
去fmod官网下载Engine -> Android下的sdk

二、定义原生方法
2.1、拷贝sdk
新建AndroidStudio Native工程
1,拷贝sdk的lib包到androidstudio工程的lib目录下,并右键add as library
2,拷贝inc头文件到main/cpp目录下

2.2、绘制页面
在MainActivity的xml中绘制几个按钮如下,ui可以完全自定义。

2.3、定义点击事件
MainActivity定义点击事件如下
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// tempCreateFilePath()
}
fun tempCreateFilePath() {
val file = getExternalFilesDir("/zhanglei.m4a")
if (file?.exists() == false) {
file.mkdirs()
}
}
fun mFix(btn: View) {
val file = getExternalFilesDir("/zhanglei.m4a")
val path = file?.path.toString()
if (!File(path).exists()) {
Toast.makeText(this, "文件不存在", Toast.LENGTH_SHORT).show()
return;
}
lifecycleScope.launch(Dispatchers.IO) {
when (btn.id) {
R.id.btn_normal -> EffectUtils.fix(path, EffectUtils.MODE_NORMAL)
R.id.btn_luoli -> EffectUtils.fix(path, EffectUtils.MODE_LUOLI)
R.id.btn_dashu -> EffectUtils.fix(path, EffectUtils.MODE_DASHU)
R.id.btn_jingsong -> EffectUtils.fix(path, EffectUtils.MODE_JINGSONG)
R.id.btn_gaoguai -> EffectUtils.fix(path, EffectUtils.MODE_GAOGUAI)
R.id.btn_kongling -> EffectUtils.fix(path, EffectUtils.MODE_KONGLING)
}
}
}
override fun onDestroy() {
super.onDestroy()
FMOD.close()
}
}
2.4、配置kotlin携程
这里用到了携程需要配置Gradle
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
implementation "androidx.activity:activity-ktx:1.1.0"
2.5、抽出Native工具类
定义Native方法fix方法的工具类EffectUtils
object EffectUtils {
//音效的类型
const val MODE_NORMAL = 0
const val MODE_LUOLI = 1
const val MODE_DASHU = 2
const val MODE_JINGSONG = 3
const val MODE_GAOGUAI = 4
const val MODE_KONGLING = 5
external fun fix(path: String, type: Int)
init {
System.loadLibrary("qqvoicechange")
}
}
2.6、拷贝音频文件到SD卡目录
拷贝一段你录制的音频文件到SD卡app/file/目录下,我这里测试.mp3、.m4a都可以。也可以使用文末demo中的录音文件。
三、实现native方法
3.1、配置CMakeLists.txt文件
cmake_minimum_required(VERSION 3.18.1)
project("qqvoicechange")
set(libs_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../libs)
add_library(fmod
SHARED
IMPORTED)
set_target_properties(fmod
PROPERTIES
IMPORTED_LOCATION ${libs_DIR}/${ANDROID_ABI}/libfmod.so
)
add_library(fmodL
SHARED
IMPORTED)
set_target_properties(fmodL
PROPERTIES
IMPORTED_LOCATION ${libs_DIR}/${ANDROID_ABI}/libfmodL.so
)
add_library(
qqvoicechange
SHARED
native-lib.cpp)
#头文件
target_include_directories(qqvoicechange PRIVATE ./inc/)
target_link_libraries(
qqvoicechange
fmod
fmodL
log)
3.2、实现fix方法
#include <jni.h>
#include "inc/fmod.hpp"
#include <unistd.h>
#include <android/log.h>
#define LOGI(FORMAT, ...) __android_log_print(ANDROID_LOG_INFO,"jason",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,"jason",FORMAT,##__VA_ARGS__);
#define MODE_NORMAL 0
#define MODE_LUOLI 1
#define MODE_DASHU 2
#define MODE_JINGSONG 3
#define MODE_GAOGUAI 4
#define MODE_KONGLING 5
using namespace FMOD;
extern "C"
JNIEXPORT void JNICALL
Java_com_example_qqvoicechange_EffectUtils_fix(JNIEnv *env, jobject thiz, jstring path_jstr,
jint type) {
const char *path_cstr = env->GetStringUTFChars(path_jstr, nullptr);
LOGI("%s", path_cstr);
System *system;
Sound *sound;
Channel *channel;
DSP *dsp;
float frequency = 0;
bool playing = true;
try {
//初始化
System_Create(&system);
system->init(32, FMOD_INIT_NORMAL, nullptr);
//创建声音
system->createSound(path_cstr, FMOD_DEFAULT, nullptr, &sound);
switch (type) {
//原生播放
case MODE_NORMAL:
system->playSound(sound, nullptr, false, &channel);
LOGI("%s", "fix normal");
break;
case MODE_LUOLI:
//萝莉
//DSP digital signal process
//dsp -> 音效 创建fmod中预定义好的音效
//FMOD_DSP_TYPE_PITCHSHIFT dsp,提升或者降低音调用的一种音效
system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
//设置音调的参数
dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 2.5);
system->playSound(sound, nullptr, false, &channel);
//添加到channel
channel->addDSP(0, dsp);
LOGI("%s","fix luoli");
break;
case MODE_DASHU:
//大叔
system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
//设置音调的参数
dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.8);
system->playSound(sound, nullptr, false, &channel);
//添加到channel
channel->addDSP(0, dsp);
LOGI("%s","fix dashu");
break;
case MODE_GAOGUAI:
//搞怪
//提高说话的速度
system->playSound(sound, nullptr, false, &channel);
channel->getFrequency(&frequency);
frequency = frequency * 1.6;
channel->setFrequency(frequency);
LOGI("%s","fix gaoguai");
break;
case MODE_KONGLING:
//空灵
system->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);
dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 300);
dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 20);
system->playSound(sound, nullptr, false, &channel);
channel->addDSP(0, dsp);
LOGI("%s","fix kongling");
break;
}
} catch (...) {
LOGE("%s", "发生异常");
goto end;
}
system->update();
//单位是微秒
//每秒钟判断下是否在播放
while (playing) {
channel->isPlaying(&playing);
usleep(1000 * 1000);
}
goto end;
end:
//释放资源
sound->release();
system->close();
system->release();
env->ReleaseStringUTFChars(path_jstr, path_cstr);
}
四、运行效果
由于博客不支持MP4视频的展示,这里把demo和演示效果压缩放到一块了