Android端FMODSoundTouch音频变声解决方案
源码
https://github.com/iDeMonnnnnn/DeMon_Sound
FMOD
FMOD是一个强大的声音引擎框架,QQ、魔兽世界及其他很多游戏都是使用的这套框架,框架内包含几十种声音类型,还可以修改声音的频率、速度等等。
1.FMOD download
进入FMOD download,下载Android端引擎库。
2.复制代码
将下载下来的FOMD引擎库中的Jar包,so库和jni c++文件,复制到项目中。
3.编辑配置
编辑配置CMakeLists.txt
-----------------------------------------
find_library( log-liblog )set(lib_path ${CMAKE_SOURCE_DIR}/libs)
# 添加三方的so库
add_library(libfmodSHAREDIMPORTED )# 指名第三方库的绝对路径
set_target_properties( libfmodPROPERTIES IMPORTED_LOCATION${lib_path}/${ANDROID_ABI}/libfmod.so )add_library(libfmodLSHAREDIMPORTED )set_target_properties( libfmodLPROPERTIES IMPORTED_LOCATION${lib_path}/${ANDROID_ABI}/libfmodL.so )#--------------------------------
add_library( # Sets the name of the library.FmodSound# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).src/main/cpp/native-lib.cpp)#---------------------
# 导入路径,为了让编译时能够寻找到这个文件夹
include_directories(src/main/cpp/inc)# 链接好三个路径
target_link_libraries( FmodSoundlibfmodlibfmodL${log-lib} )
4.修改代码
修改native-lib.cpp
,已完成项目需求,如保存变声文件的代码:
extern "C"
JNIEXPORT jint JNICALL
Java_com_demon_fmodsound_FmodSound_saveSound(JNIEnv *env, jobject cls, jstring path_jstr, jint type, jstring save_jstr) {Sound *sound;DSP *dsp;bool playing = true;float frequency = 0;System *mSystem;JNIEnv *mEnv = env;int code = 0;System_Create(&mSystem);const char *path_cstr = mEnv->GetStringUTFChars(path_jstr, NULL);LOGI("saveAiSound-%s", path_cstr)const char *save_cstr;if (save_jstr != NULL) {save_cstr = mEnv->GetStringUTFChars(save_jstr, NULL);LOGI("saveAiSound-save_path=%s", save_cstr)}try {if (save_jstr != NULL) {char cDest[200];strcpy(cDest, save_cstr);mSystem->setSoftwareFormat(8000, FMOD_SPEAKERMODE_MONO, 0); //设置采样率为8000,channel为1mSystem->setOutput(FMOD_OUTPUTTYPE_WAVWRITER); //保存文件格式为WAVmSystem->init(32, FMOD_INIT_NORMAL, cDest);mSystem->recordStart(0, sound, true);}//创建声音mSystem->createSound(path_cstr, FMOD_DEFAULT, NULL, &sound);mSystem->playSound(sound, 0, false, &channel);LOGI("saveAiSound-%s", "save_start")switch (type) {case MODE_NORMAL:LOGI("saveAiSound-%s", "save MODE_NORMAL")break;case MODE_FUNNY:LOGI("saveAiSound-%s", "save MODE_FUNNY")mSystem->createDSPByType(FMOD_DSP_TYPE_NORMALIZE, &dsp);channel->getFrequency(&frequency);frequency = frequency * 1.6;channel->setFrequency(frequency);break;case MODE_UNCLE:LOGI("saveAiSound-%s", "save MODE_UNCLE")mSystem->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.8);channel->addDSP(0, dsp);break;case MODE_LOLITA:LOGI("saveAiSound-%s", "save MODE_LOLITA")mSystem->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 1.8);channel->addDSP(0, dsp);break;case MODE_ROBOT:LOGI("saveAiSound-%s", "save MODE_ROBOT")mSystem->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 50);dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 60);channel->addDSP(0, dsp);break;case MODE_ETHEREAL:LOGI("saveAiSound-%s", "save MODE_ETHEREAL")mSystem->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 300);dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 20);channel->addDSP(0, dsp);break;case MODE_CHORUS:LOGI("saveAiSound-%s", "save MODE_CHORUS")mSystem->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 100);dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 50);channel->addDSP(0, dsp);break;case MODE_HORROR:LOGI("saveAiSound-%s", "save MODE_HORROR")mSystem->createDSPByType(FMOD_DSP_TYPE_TREMOLO, &dsp);dsp->setParameterFloat(FMOD_DSP_TREMOLO_SKEW, 0.8);channel->addDSP(0, dsp);break;default:break;}mSystem->update();} catch (...) {LOGE("saveAiSound-%s", "save error!")code = 1;goto end;}while (playing) {usleep(1000);channel->isPlaying(&playing);}LOGI("saveAiSound-%s", "save over!")goto end;end:if (path_jstr != NULL) {mEnv->ReleaseStringUTFChars(path_jstr, path_cstr);}if (save_jstr != NULL) {mEnv->ReleaseStringUTFChars(save_jstr, save_cstr);}sound->release();mSystem->close();mSystem->release();return code;
}
5.原生加载SO
加载FMOD的方法,提供给Android调用:
object FmodSound {//音效的类型const val MODE_NORMAL = 0 //正常const val MODE_FUNNY = 1 //搞笑const val MODE_UNCLE = 2 //大叔const val MODE_LOLITA = 3 //萝莉const val MODE_ROBOT = 4 //机器人const val MODE_ETHEREAL = 5 //空灵const val MODE_CHORUS = 6 //混合const val MODE_HORROR = 7 //恐怖init {System.loadLibrary("fmodL")System.loadLibrary("fmod")System.loadLibrary("FmodSound")}external fun saveSound(path: String, type: Int, savePath: String): Intexternal fun playSound(path: String, type: Int = MODE_NORMAL): Intexternal fun stopPlay()external fun resumePlay()external fun pausePlay()external fun isPlaying(): Booleanfun saveSoundAsync(path: String, type: Int, savePath: String, listener: ISaveSoundListener? = null) {try {if (isPlaying()) {stopPlay()}val result = saveSound(path, type, savePath)if (result == 0) {listener?.onFinish(path, savePath, type)} else {listener?.onError("error")}} catch (e: Exception) {listener?.onError(e.message)}}interface ISaveSoundListener {//成功fun onFinish(path: String, savePath: String, type: Int)//出错fun onError(msg: String?)}}
6.原生调用
在项目中调用FMOD方法,先保存变声文件,保存成功后播放。
override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)//初始化FMOD.init(this)binding = ActivitySoundFmodBinding.inflate(layoutInflater)setContentView(binding.root)var path = intent.getStringExtra("path") ?: ""val file = File(path)if (!file.exists()) {showToast("录音文件不存在,请重新录制!")finish()} else {if (path.endsWith(".amr")) {path = AmrToWav.makeAmrToWav(path, false)}binding.tvPath.text = "音频文件:$path"}binding.fnRG.setOnCheckedChangeListener { group, checkedId ->val pos = group.indexOfChild(group.findViewById(checkedId))Log.i(TAG, "onCreate: $pos")type = pos}binding.btnPlay.setOnClickListener {GlobalScope.launchIO {FmodSound.playSound(path, type)}}binding.btnSave.setOnClickListener {binding.tvSave.text = "开始变声..."//耗时任务,需要在子线程中执行GlobalScope.launchIO {FmodSound.saveSoundAsync(path, type, getRecordFilePath(1), object : FmodSound.ISaveSoundListener {override fun onFinish(path: String, savePath: String, type: Int) {runOnUiThread {binding.tvSave.text = "变声输出文件路径:$savePath"}FmodSound.playSound(savePath)}override fun onError(msg: String?) {Log.e(TAG, "onError: $msg")runOnUiThread {binding.tvSave.text = "变声失败:$msg"}}})}}}override fun onDestroy() {super.onDestroy()//释放FMOD.close()}
7.FMOD优缺分析
- 优点:变声类型多,自定义功能强大,百度文档比较多。
- 缺点:变声引擎库体积大,调用方法多,使用麻烦,保存变声文件的速度较慢。
SoundTouch
SoundTouch是一个开源音频处理库,用于更改音频流或音频文件的速度,音调和播放速率。该库还支持估算音轨的稳定每分钟节拍速率。
1.下载源码
进入SoundTouch的Gitlab仓库下载最新的源码。
2.解压编译
解压下载的源码,进入到目录soundtouch/source/Android-lib/jni
,打开CMD执行ndk-build
,将jni编译成so库。
注意安装NDK环境。
如果编译中遇到下图的错误:
可以在Android.mk
中添加APP_ALLOW_MISSING_DEPS=true
即可。
3.复制SO库
将soundtouch/source/Android-lib/libs
生成的so库,选择需要的平台复制到你的项目中。
由于SoundTouch已经不再支持armeabi
,需要armeabi
平台的,可以直接使用armeabi-v7a
平台的,两个平台的so库是完全兼容的。
如果so不是放在默认的'src/main/jniLibs'
目录下,需要在在build.gradle
中配置。
例如放在libs目录下:
sourceSets {main {jniLibs.srcDirs = ['libs']}}
4.原生加载SO
复制source/Android-lib/src/net/surina/soundtouch/SoundTouch.java
到你项目中,注意该文件需要放在包名net.surina.soundtouch
下(如果你修改了soundtouch-jni.cpp
中的包名,则放到自己指定的包名下即可)。
5.原生调用
在项目中调用,设置变声音调/速率,生成变声文件,并播放。
/*** 执行变声,需要在子线程中执行** @param path 音频文件陆宇* @param savePath 变声后文件保存路径*/private fun process(path: String, savePath: String) {try {val st = SoundTouch()st.setTempo(tempo) //速度st.setSpeed(speed) //速度&音调st.setPitchSemiTones(pitch) //音调val res = st.processFile(path, savePath)//res==0 变声成功if (res == 0) {//播放savePath } else {showToast(SoundTouch.getErrorString())}} catch (e: Exception) {e.printStackTrace()}}
6.SoundTouch优缺分析
- 优点:so库体积小,使用方便,方法简单,生成变声文件速度快。
- 缺点:变声选择少,只能控制音调和速率;没有现成的so库需要自己配环境编译。
参考文档
FMOD
http://blackchy.com/2018/12/10/2018-12-10-Fmod-Voice-Change/
https://www.jianshu.com/p/2e1fd3035ae1
SoundTouch
http://www.surina.net/soundtouch/README-SoundTouch-Android.html
https://gitlab.com/soundtouch/soundtouch
Android端FMODSoundTouch音频变声解决方案相关推荐
- 如何在3分钟内实现音频变声和趣味音效?
在泛娱乐社交和互动游戏场景中,用户已不再满足于高清的音质体验和实时的互动效果,他们还想要增加趣味性和沉浸感,让声音变得好玩有趣.拍乐云Pano上线了音频变声接口,开发者可以通过 Pano SDK 设置 ...
- 怎样将音频变声?1分钟教你音频变声怎么弄!
大家平常爱不爱刷短视频呢?不知道你们看短视频的时候有没有注意到,一些视频的配音很是搞怪,常常让人捧腹大笑!那么这些声音是怎么制作出来的呢?其实大部分的搞怪配音,都是借助软件对音频进行变音来实现的~今天 ...
- 如何将音频变声?试试这几个方法吧
昨天,我在构思视频素材的时候想到可以在其中添加一个鬼畜片段,以此来提升视频的丰富度和乐趣.那么如果要添加鬼畜片段的话,首先就需要我们将音频进行变声操作,不过由于不知道怎么进行音频变声,于是我就上网搜索 ...
- 这三个音频变声编辑软件教你怎么给音频变声
现如今随着我们生活的节奏越来越快,大家都爱上了短视频,每天也都有大量的短视频被创作出来.在这样的背景下,不知道有没有小伙伴也充满创意的想去尝试,但是却因为不知道怎么调整音频变声而阻挡了创作的步伐呢?其 ...
- 怎样将音频变声?三分钟教会你
音频变声是一种常见的音频处理方式,可以对人声或音乐进行特殊处理,使其声音产生不同的效果.它的应用广泛,像是音乐制作人可能需要使用音频变声来改变歌手的声音,使其更加独特或与歌曲的风格相匹配.例如,将男歌 ...
- 音频变声的软件叫什么?这些软件值得收藏
在今天这个数字时代,随着人们对于娱乐消费需求的不断增长,很多人开始涉足各种新领域,比如语音包.游戏配音.BGM制作等等.这些领域的共同点是需要一定的音频素材,同时也需要创意性地处理这些素材.这时,音频 ...
- Android 开发之 QQ变声功能实现
1.简介 在QQ中我们使用到的一个功能就是变声,QQ是使用FMOD实现的,那么同样的我们也使用FMOD让自己的应用可以变音 2.FMOD简介 fmod Ex 声音系统是为游戏开发者准备的革命性音频引擎 ...
- ffmpeg 音频 变声
记录一次ffmpeg编码变声的根本原因: 音频源采样率和ffmpeg编码时的采样率不同,音频源在采集是采样率为48000,而编码时用44100,所以播放出来的声音显得宽厚.是真.
- FFmpeg进阶: 音频变声滤镜
声音最重要的两个元素就是语速和语调,改变声音的辨识度主要也是从这两方面入手.我们可以通过对音频数据进行插值或者抽值修改,以达到降低语速和增加语速的目的.同时我们也可以通过对数据进行线性拉伸来调节音调. ...
- 使用 fmod windows 下实现音频变声 -- 萝莉 大叔 等 特效
将文件拖到窗口内, 点击播放可以看到变声效果. 也可以 点击输出将文件变声输出到文件. 可以在路径 https://download.csdn.net/download/w839687571/16 ...
最新文章
- iOS runtime实用篇:让你快速上手一个项目
- 使用onenote记HTML笔记,如何在Windows 10中使用OneNote做笔记
- 【技术好文转载】为什么AI芯片是当今热点
- 10丨 Redis主从同步与故障切换,有哪些坑
- 三、值类型与引用类型
- jira在linux下面的安装和配置
- 请先设置tkk_理光MP2014扫描至文件夹的设置方法
- 【服务器实战搭建】centos7下使用yum安装mysql
- Linux系统可卸载内核模块完全指南(上)
- 凉山州计算机等级考试时间,2020年四川凉山中考考试时间及科目安排(已公布)...
- How does RECORDLENGTH affect your exp speed?
- 压缩文件RAR和ZIP的区别
- Qt 界面美化教程 QSS QML Qt自绘方式优缺点对比
- 中国5G基站将带动155GWh以上锂电池需求
- Unity UnityWebRequest从网页加载图片并永久保存在本地
- java 禁用迅雷_【Java】我擦!迅雷的代码结构竟然被扒了精光~
- 众里寻他千百度,不如用它来搜库!
- 联通服务器维护破解限速,网速1mbps(联通限速1mbps解除方法)
- 中国天气雷达行业市场供需与战略研究报告
- oracle建表的时候同时创建主键,外键,注释,约束,索引
热门文章
- Conditional Generative Adversarial Nets(CGAN)
- 网站封装APP详细打包教程
- 实现两个路由器漫游(传统路由器做AP)
- in作为介词的用法_英语介词丨in、on 、at 的具体用法到底有哪些?
- Zabbix通过SNMP协议监控H3C交换机
- 20162330 第十二周 蓝墨云班课 hash
- 苹果笔记本电脑怎么编辑html,苹果笔记本电脑怎么操作 苹果笔记本电脑操作方法【详解】...
- 安防监控直播中无插件web直播方案中实现快照抓取的功能
- 数据科学家:人工智能领域的知识图谱理论总结
- 如何给PDF文件添加页码