1.简介
在QQ中我们使用到的一个功能就是变声,QQ是使用FMOD实现的,那么同样的我们也使用FMOD让自己的应用可以变音
2.FMOD简介
fmod Ex 声音系统是为游戏开发者准备的革命性音频引擎,有时候我们下载FMOD可能不是太方便下载,我已将他下载好了放在百度云盘中:
链接:https://pan.baidu.com/s/1TW3ctQd0o5bOVCx5gKL0hA 密码:x2o4
下载下来后解压我们看到解压目录如下:
3.搭建项目
在开始之前,最好已经了解了NDK开发的基本知识,需要下载好ndk并配置好,我用的Android studio版本是2.3.2,所以安装了cmake。
新建项目,输入项目名称,勾选包含支持c++
一直next最后一步勾选c++支持如下:
点击finish项目就创建完成了。
4.项目配置
4.1 将api\lowlevel\lib的文件复制到项目lib文件夹下
并且将fmod.jar引入进入,项目中如下图所示:
4.2将api/lowlevl/inc目录下的文件复制到你项目中的cpp目录下
复制后如下图:
4.3 准备好将要变声的mp3文件,放到assets目录下,如果没有就新建一个,在这里我下载的mp3文件是大家耳熟能详的敢问路在何方,这样听起来效果更加明显。
4.4 在app下的build.gradle下,添加jnilib指向的文件夹
sourceSets.main { jniLibs.srcDirs = ['libs'] jni.srcDirs = [] }
5.代码编写
配置好后,就可以编写代码了,(这里Android版本不同或者其他按照第四部可能会出现其他的各种问题,大家可以可以和我一起交流,至于2.3之前的版本ndk对应的是.mk文件并不是cmake)
5.1新建一个工具类ChangeUtils,声明声音对应的类型,以QQ为例分为正常、大叔、萝莉等6中音效,定义改变声音的方法,传入声音路径和声音类型,代码如下:
public class ChangeUtils { //定义音效类型常量 public static final int zhengchang = 0;//正常 public static final int luoli = 1;//萝莉 public static final int dashu = 2;//大叔 public static final int jingsong = 3;//惊悚 public static final int gaoguai = 4;//搞怪 public static final int kongling = 5;//空灵 /** * * @param path * @param type */ public native static void change(String path, int type); static { System.loadLibrary("fmodL"); System.loadLibrary("fmod"); System.loadLibrary("sound"); } }
5.2生成对应的头文件
我们使用javah命令生成工具类对应的头文件
上面报错是因为在代码注释中有中文,所以我们可以给javah添加utf8,就不报错误了,命令如下:
执行成功后就生成了对应的头文件:
5.3将生成的头文件复制到cpp目录下,并在cpp目录下新建voice.cpp文件
我们可以根据下载实例中的下述文件编写voice的代码(个人能力有限,不造轮子)
voice.cpp的代码如下:
#include "inc/fmod.hpp" #include <stdlib.h> #include <unistd.h> #include "hlq_utils_ChangeUtils.h" #include <jni.h> #include <android/log.h> #define LOGI(FORMAT, ...) __android_log_print(ANDROID_LOG_INFO,"zph",FORMAT,##__VA_ARGS__); #define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,"zph",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; JNIEXPORT void JNICALL Java_hlq_utils_ChangeUtils_change(JNIEnv *env, jclass jcls, jstring path_jstr, jint type) { //声音引擎 System *system; //声音 Sound *sound; //数字处理(音效) DSP *dsp; //正在播放 bool playing = true; //音乐轨道 Channel *channel; //播放速度 float frequency = 0; //音频地址 const char *path_cstr = env->GetStringUTFChars(path_jstr, NULL); System_Create(&system); system->init(32, FMOD_INIT_NORMAL, NULL); try { //创建声音 system->createSound(path_cstr, FMOD_DEFAULT, NULL, &sound); switch (type) { case MODE_NORMAL: //原生播放 system->playSound(sound, 0, false, &channel); break; case MODE_LUOLI: //提升或者降低音调的一种音效 system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp); //设置音调的参数 dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 1.8); //添加进到channel,添加进轨道 system->playSound(sound, 0, false, &channel); channel->addDSP(0, dsp); break; case MODE_DASHU: system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp); dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.8); system->playSound(sound, 0, false, &channel); channel->addDSP(0, dsp); break; case MODE_JINGSONG: system->createDSPByType(FMOD_DSP_TYPE_TREMOLO, &dsp); dsp->setParameterFloat(FMOD_DSP_TREMOLO_SKEW, 0.8); system->playSound(sound, 0, false, &channel); channel->addDSP(0, dsp); break; case MODE_GAOGUAI: //提高说话的速度 system->playSound(sound, 0, false, &channel); channel->getFrequency(&frequency); frequency = frequency * 2; channel->setFrequency(frequency); 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, 0, false, &channel); channel->addDSP(0, dsp); break; } } catch (...) { LOGE("%s", "发生异常"); goto end; } system->update(); while (playing) { channel->isPlaying(&playing); usleep(1000); } goto end; end: env->ReleaseStringUTFChars(path_jstr, path_cstr); sound->release(); system->close(); system->release(); }
5.4配置cmake文件,声明cmake版本,设置一个路径变量,指向我们的libs目录,也就是fmod.jar和so库的目录
添加我们需要的fmod、fmodL、sound的库
我的cmake文件如下:
cmake_minimum_required(VERSION 3.4.1) find_library( # Sets the name of the path variable. log-lib log ) set(distribution_DIR ${CMAKE_SOURCE_DIR}/libs) add_library( fmod SHARED IMPORTED ) set_target_properties( fmod PROPERTIES IMPORTED_LOCATION ${distribution_DIR}/${ANDROID_ABI}/libfmod.so ) add_library( fmodL SHARED IMPORTED ) set_target_properties( fmodL PROPERTIES IMPORTED_LOCATION ${distribution_DIR}/${ANDROID_ABI}/libfmodL.so ) add_library( sound SHARED src/main/cpp/voice.cpp ) include_directories(src/main/cpp/inc) target_link_libraries( sound fmod fmodL ${log-lib} )
5.5 最后一步
终于到了最后一步,那就是在页面放上几个按钮,给按钮添加监听事件,不同监听事件对应音效不同
在Activity中初始化FMOD
FMOD.init(this);
结合工具类设置工具类型
class PlayerThread implements Runnable { @Override public void run() { ChangeUtils.change(path, type); } }
playerThread = new PlayerThread(); fixedThreadPool.execute(playerThread);
这样就完成了变声效果,需要注意的事,点击播放后,需要重新退出才可以切换声音效果,感兴趣的可以自行扩展成语音对讲那种。
6.总结
- 原声:直接播放音频文件
- 萝莉:对音频提高八度
- 大叔:对音频减低八度
- 惊悚:增加音频的颤音
- 搞笑:增加音频的播放速度
- 空灵:增加音频的回音
源码地址:https://github.com/huanglinqing123/ChangeVoice/tree/master