Android-NDK 接入Fmod库,变声操作
fmod是音效引擎庫,游戲引擎cocos2d、unity3d 等都是默認(rèn)集成了 fmod 來做音效。
fmod官網(wǎng)
1.下載Fmod資源
可以在官網(wǎng)下載,也可以直接訪問百度云盤下載:
鏈接:https://pan.baidu.com/s/1Ypqlgk8WWacstURNMw2ETw
提取碼:pkom
2.接入Fmod
-
在main路徑下新建jniLibs文件夾,增加需要的架構(gòu)庫,根據(jù)項(xiàng)目來選擇。并講libfmod.so、libfmodL.so這兩個(gè)so庫拷貝到相應(yīng)的路徑下面。
- arm64-v8a:新手機(jī)使用的這個(gè)架構(gòu),微信項(xiàng)目只支持這個(gè),支付寶用的armeabi-v7a。
- armeabi
- armeabi-v7a :游戲類項(xiàng)目,可只選armeabi-v7a,優(yōu)勢具有輕量級的DSP數(shù)字信號處理能力。
- x86 :模擬器以及WindowPhone手機(jī)使用的這個(gè)架構(gòu)
-
在libs路徑下面增加fmod.jar包,并在gradle中配置依賴
- implementation fileTree(include: ['*.jar'], dir: 'libs') :依賴所有l(wèi)ibs路徑下的jar包。
- implementation files('libs\\fmod.jar') :單獨(dú)依賴此jar包。
-
在build.gradle中配置
- externalNativeBuild {cmake {abiFilters "armeabi-v7a" //指定編譯用的CPU架構(gòu)庫} }
- ndk {abiFilters("armeabi-v7a") //配置最終打包到apk中的包含的CPU架構(gòu) }
-
引入頭文件inc,講inc文件夾全部拷貝到cpp路徑下面。
-
配置CMakeLists.txt文件
- # 最低支持的CMake版本 cmake_minimum_required(VERSION 3.18.1)
- include_directories("inc") #導(dǎo)入頭文件,因?yàn)閕nc跟CMakeLists.txt文件同級所以直接寫inc就行。
- #指定編譯的庫的名稱、類型、以及需要加入的源文件,如果cpp源文件有多個(gè),直接在括號里邊添加即可。 add_library(#庫名字msvoicechange# 庫類型 動(dòng)態(tài)還是靜態(tài)SHARED# 添加的源文件native-lib.cpp)多個(gè)cpp源文件的時(shí)候,也可以通過統(tǒng)一引用file(GLOB allCPP *.c *.h *.cpp) ${allCPP} 直接添加這個(gè)就可以
- #給fmod.so設(shè)置環(huán)境變量,這樣就可以依賴到fmod的動(dòng)態(tài)庫了 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")
- #這個(gè)find_library 其實(shí)就是查找 liblog.so 庫,并將路徑緩存給變量 log-lib,以后直接使用這個(gè)變量就行,如果設(shè)計(jì)多個(gè)使用,可以提高效率。 下面是這個(gè)so庫的路徑 #D:\tools\Android\Sdk\ndk\21.4.7075529\platforms\android-24\arch-arm64\usr\lib\liblog.so find_library(#變量名稱log-lib#庫名 liblog.so 找的是這個(gè)庫 log)
- #進(jìn)行鏈接操作 target_link_libraries( # Specifies the target library.msvoicechangefmod #需要將so 鏈接到msvoicechange 里邊 注意這里的名稱前面的lib默認(rèn)會加這里不能寫 #so也是一樣fmodL${log-lib})
3.編寫Android上層頁面代碼
加載編譯生成的so庫,采用靜態(tài)代碼塊的方式添加。
companion object {init {System.loadLibrary("msvoicechange")} }在onCreate方法里邊,添加初始化的操作,一般的都會有這樣的操作。
override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)FMOD.init(this);}在onDestroy,添加關(guān)閉的操作
override fun onDestroy() {super.onDestroy()FMOD.close(); }定義natvie方法
/*** path:音頻文件的路徑* mode:變聲的類型:原聲、蘿莉、大叔等*/external fun voiceChangeNative(mode: Int, path: String)4.編寫cpp部分
#include "fmod.hpp" //有hpp后綴的引用這個(gè),這個(gè)是最新版本,加強(qiáng)版本 #include <unistd.h> #include <string>//增加命名空間 using namespace std; using namespace FMOD;extern "C" JNIEXPORT void JNICALL Java_com_meishe_msvoicechange_MainActivity_voiceChangeNative(JNIEnv *env, jobject thiz, jint mode,jstring path) {const char *toast_content = "播放完畢";//c++只能使用c或者c++的內(nèi)容 并能用JNI的數(shù)據(jù),所以需要轉(zhuǎn)//JNI的方法參數(shù),只能使用JNI的類型,所以也需要轉(zhuǎn)//得到傳進(jìn)來的音頻路徑const char *audioPath = env->GetStringUTFChars(path, NULL);//fmod 的音效引擎System *system = 0;//fmod 的聲音Sound *sound = 0;//fmod 的音軌Channel *channel = 0;//digital signal process 數(shù)字信號處理DSP *dsp = 0;//c 的初始化方式xxx(system) 一般都是需要傳遞指針的,因?yàn)樾枰獙Φ刂愤M(jìn)行賦值操作,,指針就是地址System_Create(&system);//初始化system->init(32, FMOD_INIT_NORMAL, NULL);//創(chuàng)建聲音system->createSound(audioPath, FMOD_DEFAULT, 0, &sound);//播放聲音system->playSound(sound, 0, false, &channel);switch (mode) {case com_meishe_msvoicechange_MainActivity_MODE_NORMAL:toast_content = "原聲-->播放完畢";break;case com_meishe_msvoicechange_MainActivity_MODE_LUOLI:// 蘿莉 音調(diào)高//創(chuàng)建dsp pitch 音調(diào)調(diào)節(jié) 默認(rèn)是1 小于1音調(diào)低 大于1音調(diào)高 0.5-2(合理的調(diào)節(jié)區(qū)間)system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT,&dsp);//設(shè)置pitch音調(diào)=2.0dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH,2.0f);//添加一個(gè)音效進(jìn)去channel->addDSP(0,dsp);toast_content = "蘿莉:播放完畢";break;case com_meishe_msvoicechange_MainActivity_MODE_DASHU://大叔 音調(diào)低system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT,&dsp);dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH,0.7f);channel->addDSP(0,dsp);toast_content = "大叔:播放完畢";break;case com_meishe_msvoicechange_MainActivity_MODE_JINGSONG://驚悚 多個(gè)音頻軌道 拼接處理//音調(diào)較低system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT,&dsp);dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH,0.7f);channel->addDSP(0,dsp);//echo 回聲system->createDSPByType(FMOD_DSP_TYPE_ECHO,&dsp);dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY,500);dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK,35);channel->addDSP(1,dsp);// 顫抖 Tremolo frequency:頻率 skew :傾斜system->createDSPByType(FMOD_DSP_TYPE_TREMOLO,&dsp);dsp->setParameterFloat(FMOD_DSP_TREMOLO_FREQUENCY,0.8);dsp->setParameterFloat(FMOD_DSP_TREMOLO_SKEW,0.8);channel->addDSP(2,dsp);toast_content = "驚悚:播放完畢";break;case com_meishe_msvoicechange_MainActivity_MODE_GAOGUAI://搞怪 頻率快float frequency;channel->getFrequency(&frequency);channel->setFrequency(frequency*1.5);toast_content = "搞怪:播放完畢";break;case com_meishe_msvoicechange_MainActivity_MODE_KONGLING://空靈system->createDSPByType(FMOD_DSP_TYPE_ECHO,&dsp);dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY,200);//衰減 50 默認(rèn) 0就沒了dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK,10);channel->addDSP(0,dsp);toast_content = "空靈:播放完畢";break;}bool isPlay= true;while (isPlay){channel->isPlaying(&isPlay);usleep(1000*1000); //單位是微秒}//進(jìn)行資源釋放sound->release();system->close();system->release();env->ReleaseStringUTFChars(path,audioPath);//回調(diào)java方法,提示用戶播放完畢jclass _activityClazz = env->GetObjectClass(thiz);jmethodID _toastId = env->GetMethodID(_activityClazz, "playerEnd", "(Ljava/lang/String;)V");jstring _toastJString = env->NewStringUTF(toast_content);env->CallVoidMethod(thiz, _toastId, _toastJString);}System:fmod 的音效引擎
Sound:聲音
Channel:音軌
DSP:數(shù)字信號處理
- pitch:對音調(diào)的調(diào)節(jié),音調(diào)高了一般是蘿莉 音調(diào)低了就是大叔,默認(rèn)是1 調(diào)節(jié)區(qū)間(0.5-2)
- echo:回聲,空靈會使用這個(gè)
- Tremolo:這個(gè)是顫抖
這樣就完成了對Fmod庫的依賴,并使用庫中的方法做了調(diào)音的效果。
源碼地址
總結(jié)
以上是生活随笔為你收集整理的Android-NDK 接入Fmod库,变声操作的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python面试题总结(4)--数据类型
- 下一篇: php把语音转成帧,[转载]用TCP/I