Android NDK 导入 C库的开发流程学习;通过使用fmod的C库,实现变声器功能。

  1. 导入库文件

1)复制fmod的C库到cpp目录下

2)复制fmod的so库到jniLibs目录下

3)复制fmod的jar库到libs目录下

4)将声音文件复制到assets目录下

fmod库文件资源:https://wwgl.lanzout.com/ilXHP0pn7f4f

  1. 打开CMakeLists.txt导入fmod库文件

cmake_minimum_required(VERSION 3.10.2) # 最低支持的CMake版本project("whine")# TODO 第一步 导入头文件(fmod库)
include_directories("inc")# 批量导入所有文件
file(GLOB allCPP *.c *.h *.cpp)add_library(native-lib # 打包出来库的名称 libnative-lib.soSHARED # 动态库${allCPP} # 添加所有库文件
)# TODO 第二步 导入库文件(fmod库)
# ${CMAKE_CXX_FLAGS} 原先的CMake环境 +
# -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI} 加上新的fmod库的路径
# CMAKE_SOURCE_DIR == CMakeLists.txt所在的路径
# /../ 回退到cpp目录
# jniLibs/ 进入jniLibs目录
# ${CMAKE_ANDROID_ARCH_ABI} 使用当前手机CPU系统结构对应的库 如:arm64-v8a
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")find_library(log-liblog # 自动寻找log库
)# TODO 第三步 链接具体的库,到 libnative-lib.so总库
target_link_libraries(native-lib${log-lib}fmod # 具体的库 链接到 libnative-lib.so里面去fmodL # 具体的库 链接到 libnative-lib.so里面去
)
  1. 打开build.gradle,指定CPU架构和依赖fmod的jar包

defaultConfig {// TODO 第四步externalNativeBuild {cmake {// cppFlags "" // 默认五大平台// 指定CPU架构,Cmake的本地库, 例如:native-lib ---> arm64-v8aabiFilters "arm64-v8a"}}// TODO 第五步// 指定CPU架构,打入APK lib/CPU平台ndk {abiFilters "arm64-v8a"}
}dependencies {// TODO 第六步:依赖fmod的jar包implementation files("libs\\fmod.jar");
}
  1. 准备工作完成,开始编写Activity代码

1)定义变声模式

/*** 定义变声模式* 0 正常; 1 萝莉; 2 大叔; 3 惊悚; 4 搞怪; 5 回声;*/
private static final int MODE_NORMAL = 0;
private static final int MODE_LUOLI = 1;
private static final int MODE_DASHU = 2;
private static final int MODE_JINGSONG = 3;
private static final int MODE_GAOGUAI = 4;
private static final int MODE_KONGLING = 5;

2)加载编译后的lib库

static {System.loadLibrary("native-lib");
}

3)初始化fmod.jar库,并在页面关闭的时候释放

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);FMOD.init(this);
}@Override
protected void onDestroy() {super.onDestroy();FMOD.close();
}

4)编写变声native层方法和变声结束后提供native调用的方法

public native void voiceChangeNative(int modeNormal, String path);private void playerEnd(String msg) {isPlaying = false;runOnUiThread(() -> Toast.makeText(this, msg, Toast.LENGTH_SHORT).show());
}

5)Activity完整代码

package com.whine;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;
import android.view.View;
import android.widget.Toast;import org.fmod.FMOD;/*** TODO Android NDK 导入 C库,开发流程 之 变声器*/
public class MainActivity extends AppCompatActivity {/*** 定义变声模式* 0 正常; 1 萝莉; 2 大叔; 3 惊悚; 4 搞怪; 5 回声;*/private static final int MODE_NORMAL = 0;private static final int MODE_LUOLI = 1;private static final int MODE_DASHU = 2;private static final int MODE_JINGSONG = 3;private static final int MODE_GAOGUAI = 4;private static final int MODE_KONGLING = 5;/*** 声音路径*/private String soundPath;private boolean isPlaying = false;static {System.loadLibrary("native-lib");}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// TODO 第七步 初始化FMOD库FMOD.init(this);initData();}private void initData() {soundPath = "file:///android_asset/sound.mp3";}// 点击事件public void onFix(View view) {switch (view.getId()) {case R.id.btn_normal:voiceChange(MODE_NORMAL, soundPath); // 真实开发中,必须子线程  JNI线程(很多坑)break;case R.id.btn_luoli:voiceChange(MODE_LUOLI, soundPath);break;case R.id.btn_dashu:voiceChange(MODE_DASHU, soundPath);break;case R.id.btn_jingsong:voiceChange(MODE_JINGSONG, soundPath);break;case R.id.btn_gaoguai:voiceChange(MODE_GAOGUAI, soundPath);break;case R.id.btn_kongling:voiceChange(MODE_KONGLING, soundPath);break;}}@Overrideprotected void onDestroy() {super.onDestroy();FMOD.close();}private void voiceChange(int modeNormal, String path) {if (isPlaying) {Toast.makeText(this, "正在播放,请稍后!", Toast.LENGTH_SHORT).show();return;}isPlaying = true;new Thread(() -> voiceChangeNative(modeNormal, path)).start();}/*** 给C++(native)层调用的函数* JNI 调用 Java函数的时候,忽略掉 私有、公开 等** @param msg*/private void playerEnd(String msg) {isPlaying = false;runOnUiThread(() -> Toast.makeText(this, msg, Toast.LENGTH_SHORT).show());}/*** 变声native层方法** @param modeNormal* @param path*/public native void voiceChangeNative(int modeNormal, String path);
}
  1. 编写native层头文件(声明文件)

1)导入需要使用到的头文件

#include <jni.h>
#include <fmod.hpp>
#include <string>
#include <android/log.h>

2)定义log宏

#define TAG "NDK"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)

3)定义变声模式宏

#undef com_whine_MainActivity_MODE_NORMAL
#define com_whine_MainActivity_MODE_NORMAL 0L
#undef com_whine_MainActivity_MODE_LUOLI
#define com_whine_MainActivity_MODE_LUOLI 1L
#undef com_whine_MainActivity_MODE_DASHU
#define com_whine_MainActivity_MODE_DASHU 2L
#undef com_whine_MainActivity_MODE_JINGSONG
#define com_whine_MainActivity_MODE_JINGSONG 3L
#undef com_whine_MainActivity_MODE_GAOGUAI
#define com_whine_MainActivity_MODE_GAOGUAI 4L
#undef com_whine_MainActivity_MODE_KONGLING
#define com_whine_MainActivity_MODE_KONGLING 5L

4)头文件完整代码

// TODO 第八步 编写头文件(声明文件)
#include <jni.h>
// 导入FMOD的头文件
#include <fmod.hpp>
#include <string>
// 导入log库
#include <android/log.h>extern "C" {// log宏
#define TAG "NDK"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)// 创建变声模式宏
#undef com_whine_MainActivity_MODE_NORMAL
#define com_whine_MainActivity_MODE_NORMAL 0L
#undef com_whine_MainActivity_MODE_LUOLI
#define com_whine_MainActivity_MODE_LUOLI 1L
#undef com_whine_MainActivity_MODE_DASHU
#define com_whine_MainActivity_MODE_DASHU 2L
#undef com_whine_MainActivity_MODE_JINGSONG
#define com_whine_MainActivity_MODE_JINGSONG 3L
#undef com_whine_MainActivity_MODE_GAOGUAI
#define com_whine_MainActivity_MODE_GAOGUAI 4L
#undef com_whine_MainActivity_MODE_KONGLING
#define com_whine_MainActivity_MODE_KONGLING 5L
JNIEXPORT void JNICALL Java_com_whine_MainActivity_voiceChangeNative(JNIEnv *, jobject, jint, jstring);
}
  1. 编写native层实现文件

1)转换Java层传递的声音路径jstring -> char *

const char *pathChar = env->GetStringUTFChars(path, NULL);

2)定义和初始化音效引擎系统

// 定义音效引擎系统 指针
System *system = 0;
// 声音
Sound *sound = 0;
// 音轨
Channel *channel = 0;
// 数字信号处理  指针
DSP *dsp = 0;
// 创建系统
System_Create(&system);
// 初始化系统 参数1:最大音轨数,参数2:系统初始化标记,参数3:额外数据
system->init(32, FMOD_INIT_NORMAL, 0);
// 创建声音  参数1:路径,  参数2:声音初始化标记, 参数3:额外数据, 参数4:声音指针
system->createSound(pathChar, FMOD_DEFAULT, 0, &sound);
// 播放声音,并赋值通道 参数1:声音,参数2:分组音轨,参数3:控制,参数4:通道
system->playSound(sound, 0, false, &channel);

3)判断变声模式,调节音效

switch (mode_normal) {case com_whine_MainActivity_MODE_NORMAL:resultChar = "原生,播放完毕!";break;case com_whine_MainActivity_MODE_LUOLI:resultChar = "萝莉,播放完毕!";// 音调高 -- 萝莉 2.0// 1.创建DSP类型的Pitch 音调条件system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);// 2.设置Pitch音调调节2.0dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 3.0f);// 3.添加音效进去 第一个音轨channel->addDSP(0, dsp);break;case com_whine_MainActivity_MODE_DASHU:resultChar = "大叔,播放完毕!";// 音调低 -- 大叔 0.7// 1.创建DSP类型的Pitch 音调条件system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);// 2.设置Pitch音调调节0.7dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.7f);// 3.添加音效进去 第一个音轨channel->addDSP(0, dsp);break;case com_whine_MainActivity_MODE_GAOGUAI:resultChar = "搞怪小黄人,播放完毕!";// 小黄人声音 频率快// 1.从音轨拿 当前 频率float frequency;channel->getFrequency(&frequency);// 2.加快频率加快  小黄人的声音channel->setFrequency(frequency * 1.5f);break;case com_whine_MainActivity_MODE_JINGSONG:resultChar = "惊悚,播放完毕!";// 惊悚音效:特点:很多声音的拼接// TODO 1.音调低// 音调低 -- 大叔 0.7// 1.创建DSP类型的Pitchsystem->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);// 2.设置Pitch音调调节0.7dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.7f);// 3.添加音效进去 第一个音轨channel->addDSP(0, dsp);// TODO 2.回声// 回音 ECHO// 1.创建DSP类型的ECHOsystem->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);// 2.设置回音 延时 to 5000.  Default = 500.dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 200);// 3.设置回音 衰减度 Default = 50   0 完全衰减了dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 10);// 4.添加音效进去 第二个音轨channel->addDSP(1, dsp);// TODO 3.颤抖// Tremolo 颤抖音// 1.创建DSP类型的Tremolosystem->createDSPByType(FMOD_DSP_TYPE_TREMOLO, &dsp);// 2.设置颤抖 正常 5,非常颤抖 20dsp->setParameterFloat(FMOD_DSP_TREMOLO_FREQUENCY, 20);// 3.dsp->setParameterFloat(FMOD_DSP_TREMOLO_SKEW, 0.8f);// 4.第三个音轨channel->addDSP(2, dsp);break;case com_whine_MainActivity_MODE_KONGLING:resultChar = "空灵,播放完毕!";// 回音 ECHO// 1.创建DSP类型的ECHO 音调条件system->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);// 2.设置回音 延时 to 5000.  Default = 500.dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 200);// 3.设置回音 衰减度 Default = 50   0 完全衰减了dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 10);// 4.添加音效进去 第一个音轨channel->addDSP(0, dsp);break;
}

4)检测声音是否播放完毕

bool isPlay = true;
while (isPlay) {// 如果真的播放完成了,音轨是知道的,内部会修改isPlayer=falsechannel->isPlaying(&isPlay);// 休眠100mSusleep(100 * 1000);
}

5)回收C层资源

sound->release();
system->close();
system->release();
env->ReleaseStringUTFChars(path, pathChar);

6)播放完毕,通知java层(调用Java层的播放完成方法)

jclass jClazz = env->GetObjectClass(thiz);
jmethodID methodId = env->GetMethodID(jClazz, "playerEnd", "(Ljava/lang/String;)V");
jstring value = env->NewStringUTF(resultChar);
env->CallVoidMethod(thiz, methodId, value);

7)实现文件完整代码

#include <unistd.h>
#include "whine.h"using namespace FMOD; // fmod的命名空间extern "C"
JNIEXPORT void JNICALL
Java_com_whine_MainActivity_voiceChangeNative(JNIEnv *env, jobject thiz, jint mode_normal,jstring path) {// 定义一个结果字符串返回char *resultChar = "播放完毕!";// 转换路径jstring -> char *const char *pathChar = env->GetStringUTFChars(path, NULL);// 定义音效引擎系统 指针System *system = 0;// 声音Sound *sound = 0;// 音轨Channel *channel = 0;// 数字信号处理  指针DSP *dsp = 0;// 创建系统System_Create(&system);// 初始化系统 参数1:最大音轨数,参数2:系统初始化标记,参数3:额外数据system->init(32, FMOD_INIT_NORMAL, 0);// 创建声音  参数1:路径,  参数2:声音初始化标记, 参数3:额外数据, 参数4:声音指针system->createSound(pathChar, FMOD_DEFAULT, 0, &sound);// 播放声音,并赋值通道 参数1:声音,参数2:分组音轨,参数3:控制,参数4:通道system->playSound(sound, 0, false, &channel);LOGI("init done");// 判断模式,增加特效switch (mode_normal) {case com_whine_MainActivity_MODE_NORMAL:resultChar = "原生,播放完毕!";break;case com_whine_MainActivity_MODE_LUOLI:resultChar = "萝莉,播放完毕!";// 音调高 -- 萝莉 2.0// 1.创建DSP类型的Pitch 音调条件system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);// 2.设置Pitch音调调节2.0dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 3.0f);// 3.添加音效进去 第一个音轨channel->addDSP(0, dsp);break;case com_whine_MainActivity_MODE_DASHU:resultChar = "大叔,播放完毕!";// 音调低 -- 大叔 0.7// 1.创建DSP类型的Pitch 音调条件system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);// 2.设置Pitch音调调节0.7dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.7f);// 3.添加音效进去 第一个音轨channel->addDSP(0, dsp);break;case com_whine_MainActivity_MODE_GAOGUAI:resultChar = "搞怪小黄人,播放完毕!";// 小黄人声音 频率快// 1.从音轨拿 当前 频率float frequency;channel->getFrequency(&frequency);// 2.加快频率加快  小黄人的声音channel->setFrequency(frequency * 1.5f);break;case com_whine_MainActivity_MODE_JINGSONG:resultChar = "惊悚,播放完毕!";// 惊悚音效:特点:很多声音的拼接// TODO 1.音调低// 音调低 -- 大叔 0.7// 1.创建DSP类型的Pitchsystem->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);// 2.设置Pitch音调调节0.7dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.7f);// 3.添加音效进去 第一个音轨channel->addDSP(0, dsp);// TODO 2.回声// 回音 ECHO// 1.创建DSP类型的ECHOsystem->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);// 2.设置回音 延时 to 5000.  Default = 500.dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 200);// 3.设置回音 衰减度 Default = 50   0 完全衰减了dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 10);// 4.添加音效进去 第二个音轨channel->addDSP(1, dsp);// TODO 3.颤抖// Tremolo 颤抖音// 1.创建DSP类型的Tremolosystem->createDSPByType(FMOD_DSP_TYPE_TREMOLO, &dsp);// 2.设置颤抖 正常 5,非常颤抖 20dsp->setParameterFloat(FMOD_DSP_TREMOLO_FREQUENCY, 20);// 3.dsp->setParameterFloat(FMOD_DSP_TREMOLO_SKEW, 0.8f);// 4.第三个音轨channel->addDSP(2, dsp);break;case com_whine_MainActivity_MODE_KONGLING:resultChar = "空灵,播放完毕!";// 回音 ECHO// 1.创建DSP类型的ECHO 音调条件system->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);// 2.设置回音 延时 to 5000.  Default = 500.dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 200);// 3.设置回音 衰减度 Default = 50   0 完全衰减了dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 10);// 4.添加音效进去 第一个音轨channel->addDSP(0, dsp);break;}// 等待播放完毕 再回收bool isPlay = true;while (isPlay) {// 如果真的播放完成了,音轨是知道的,内部会修改isPlayer=falsechannel->isPlaying(&isPlay);// 休眠100mSusleep(100 * 1000);}LOGI("play done");// 回收资源sound->release();system->close();system->release();env->ReleaseStringUTFChars(path, pathChar);// 通知java层jclass jClazz = env->GetObjectClass(thiz);jmethodID methodId = env->GetMethodID(jClazz, "playerEnd", "(Ljava/lang/String;)V");jstring value = env->NewStringUTF(resultChar);env->CallVoidMethod(thiz, methodId, value);
}

NDK JNI 变声器实现相关推荐

  1. 小哥哥你有98K吗?利用Python制作一款多功能变声器!

    前言 好吧,关于这句小哥哥你有98K吗?出自别人口中经常说的玩笑话,我也略懂一些游戏嘛.不过不常玩,废话不多说,开始咱们今天的教程,非常简单! 利用Python制作一款多功能变声器! 咱们首先登陆百度 ...

  2. 【毕业设计系列】033:基于MATLAB语言的实时变声器系统【含Matlab源码】

    Date:2022.5.5 文章目录 前言 1.算法原理 2.实现效果 2.1.男声变女声 2.2.女声变男声 2.3.男声变大叔声 3.部分matlab代码实现 前言 在大学毕业设计中,要求实现一个 ...

  3. 【毕业设计/Matlab系列】基于MATLAB语言的实时变声器系统

    Date:2022.5.5 文章目录 前言 1.算法原理 2.实现效果 2.1.男声变女声 2.2.女声变男声 2.3.男声变大叔声 3.部分matlab代码实现 前言 在大学毕业设计中,要求实现一个 ...

  4. 【AI实时变声器,声音甜甜的小姐姐背后竟是抠脚大汉】

    前言 这是一款基于AI算法的实时变声器,如果你不懂AI也没事,直接使用我提供的一键安装包 链接:https://pan.baidu.com/s/1f3X6JdBVOgeTNPf0B3CRKg 提取码: ...

  5. 语音变声器微信小程序源码下载支持多种音效

    如标题所示这就是一款变声器小程序源码 程序支持多种音效生成, 另外支持本地音频文件上传变声 也支持直接录音变声 当然啦,也是支持在线试听的这样可以方便查看每一种效果 好了,不多说了下面来看看小编的测试 ...

  6. 小程序源码:语音变声器微信小程序-多玩法安装简单

    如标题所示这就是一款变声器小程序源码 程序支持多种音效生成,如少女,鬼畜,恐怖,萝莉等等! 另外支持本地音频文件上传变声 也支持直接录音变声 最后支持观看激励视频保存音频文件 当然啦,也是支持在线试听 ...

  7. 语音变声器微信小程序源码_支持多种音效

    简介: 如标题所示这就是一款变声器小程序源码 程序支持多种音效生成,如少女,鬼畜,恐怖,萝莉等等! 另外支持本地音频文件上传变声 也支持直接录音变声 最后支持观看激励视频保存音频文件 当然啦,也是支持 ...

  8. 用python写一个变声器,要求导入mp4或者mp3文件,将视频中的声音变成女声

    用python写一个变声器,要求导入mp4或者mp3文件,将视频中的声音变成女声 1.安装 pydub 库 2.然后可以使用以下代码导入 mp4 或 mp3 文件并将其变成女声: 1.安装 pydub ...

  9. 时代拓灵变声器SDK集成调试过程

    时代拓灵变声器SDK集成调试过程 时代拓灵变声器SDK效果概述 时代拓灵变声器SDK通过"变调不变速"的方法,改变说话人基频的大小,同时保持语速和语义不变,即保持短时频谱包络(共振 ...

最新文章

  1. 2022-2028年中国顺丁橡胶行业发展模式分析及市场分析预测报告
  2. centos 7 lvs 负载均衡搭建部署
  3. 网络编程学习方法和图书推荐
  4. Arduino-一些函数
  5. XGB 调参基本方法
  6. php faker 中文,使用faker 生成中文测试数据
  7. StringBuilder详解
  8. 【项目管理】合同和采购
  9. jsp中动态include与静态include的区别
  10. 小米电视2+android,真实体验小米电视2方便安装第三方软件
  11. 基于SSM的Java图书管理系统
  12. c1语言实验报告怎么写,c语言程序设计实验报告模板
  13. catia二次开发c语言,CATIA二次开发1_VB语言基础语法
  14. C语言随机产生26个大写字母
  15. 微信铃声设置教程,怎么设置微信铃声?
  16. 阻碍改善设计的常见观念
  17. Windows cmd 进入其他盘
  18. python字典嵌套字典构造_浅析python 字典嵌套
  19. MySql 查询排名前10(包含并列)Sql
  20. 14.利用虚函数实现多态性来求正方体、球体和圆柱体的表面积和体积。

热门文章

  1. java反射进行字段类型判断_Java反射机制的讲解
  2. 算法之十大滤波算法详解
  3. Hi3518EV200的开发环境搭建,Hi3518E_V200R001C01SPC030
  4. 迅为恩智浦i.MX8MM开发平台虚拟机安装Ubuntu16.04系统
  5. win7 iE8 删除临时文件时 总提示Internet 控制面板已停止工作
  6. 关于input文本框只能输入数字的几种实用方法!!!
  7. 3.3 锚框 or 先验框
  8. 『IDEA』IDEA快捷键使用教程
  9. 计算机维修耗材管理,中国建设银行驻马店分行计算机耗材管理办法.doc
  10. Adobe Flash离线安装 (无需联网安装)