闲话不多说了开撸,如题今天请跟贫道一步步来实现球球语音变声功能
分析
首先我们来看下QQ语音变声效果,如下图所示:

点击发送语音在点击相应的生效发出去就变声了,没玩过的可以打开QQ试下,那么他们是怎么做到让声音变声呢!看官们接着往下看。
首先我们知道,录制人声到手机,这些音频数据是由二进制十六进制组成,如果我们自己去用算法处理这些数据这个难度是比较大的。
那么怎么办???庆幸吧我们Android开发中有个NDK的东西,简单来说就是我们在java层调用c/c++的一些强大的开源库,(这里不讲NDK基础如果想了解请谷歌一下)。
网上有很多开源的音频处理库。我不知道腾讯是用的那个,但我们今天用到的是FMOD音频引擎,如果你做过游戏开发相信你一定接触过,这里附上FMOD官网链接大家可以去简单的看下介绍,国内FMOD的中文资料几乎没有,所以还是建议你去官网了解,点我进fmod官网,这里就不详细介绍了,他的主要功能就是对音频进行处理,比如你玩赛车类游戏你的引擎轰鸣声,会随着你的速度变化而变化,游戏中有多个视角,比如车内切换到车外,你听到的声音是不是不一样的,再比如我们当年玩的cs游戏,你附近的敌人在你不同角度开枪,你听到的声音也是不一样的,(!!!暴露年龄了。。。)
或许我们来个图更好理解:

听到脚步声或枪声,就可以判断出敌人在你什么位置,不同的位置方位明显听到的声音不同
开始
好的大体环境介绍完了,接下来我们看代码。
首先我们新建eclipse工程‘’mysound‘’(这里不要问我为什么不用Android Studio开发as现在多火啊!,因为ndk开发as还是有些问题的,有些坑我也没解决,我能怎么办?我也很绝望呀 。当然了以后我主要还是以as去写,如果今天的项目你能移植到as里面请联系我!)
首先我们导入一下FMOD的so动态库和一些需要用到的头文件。

今天的所有代码和用到的库我都会放到github上,所以不用担心这些文件去哪找。
1.接了下我们将工程转成C/C++工程 Add Native Support

之后会给你生成android.mk文件 我们来配置一下里面的文件,引入提供的预编译的so库把他们放到jni目录下,还有他自己的函数的实现也要引入看下面
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := fmod
LOCAL_SRC_FILES := libfmod.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := fmodL
LOCAL_SRC_FILES := libfmodL.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := sound
LOCAL_SRC_FILES :=effect_fix.cpp
LOCAL_SHARED_LIBRARIES := fmod fmodL
LOCAL_LDLIBS := -llog
LOCAL_CPP_FEATURES :=exceptions
include $(BUILD_SHARED_LIBRARY)
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
LOCAL_MODULE := sound 自己的动态库名切记要和System.loadLibrary(“sound”);一致这坑我踩过 ,fmod fmodL这是2个预编译的库 如果你引入的是官网提供的库可能会出现一些路径的问题改一下就好我的已经改了。
划重点 FMOD 用到了标准模板库STL,说用这玩意会变慢?,但我没感觉出来 安卓ndk里面已经有了一个最小的c++运行库,想用那么我们就需要另外的配置
怎么配置呢?看ndk提供的文档 在我们下载的ndk目录里就有解决方法APP_STL


2.创建Application.mk 支持C++异常处理,标准模板块
里面就一句话 :
APP_STL := gnustl_static
3.配置完我们来写java代码
布局我就不在这贴出来了几个按钮,看关键代码
我们先来个工具类Utils
public class Utils {
public static final int MODE_NORMAL=0;
public static final int MODE_LUOLI=1;
public static final int MODE_DASHU=2;
public static final int MODE_JINGSONG=3;
public static final int MODE_GAOGUAI=4;
public static final int MODE_KONGLING=5;
/**
* 音效处理传2个值
* @param path 音频文件路径
* @param type 音频文件类型
*/
public native static void fix(String path,int type);
static{
System.loadLibrary("fmodL");
System.loadLibrary("fmod");
System.loadLibrary("sound");
}
}
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
接下来我们生成头文件

然后到项目F5刷新一下src目录下会多出个.h的文件把它拖到jni目录下 继续在jni目录下创建一个c++源文件 supersound.cpp代码
#include "inc/fmod.hpp"
#include <stdlib.h>
#include <unistd.h>
#include "org_fmod_example_Utils.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_org_fmod_example_Utils_fix(JNIEnv *env,
jclass jcls, jstring path_jstr, jint type) {
System *system;
Sound *sound;
DSP *dsp;
bool playing = true;
Channel *channel;
float frequency = 0;
System_Create(&system);
system->init(32, FMOD_INIT_NORMAL, NULL);
const char* path_cstr = env->GetStringUTFChars(path_jstr, NULL);
try {
system->createSound(path_cstr, FMOD_DEFAULT, NULL, &sound);
switch (type) {
case MODE_NORMAL:
LOGI("%s", path_cstr);
system->playSound(sound, 0, false, &channel);
LOGI("%s", "fix normal");
break;
case MODE_LUOLI:
system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 8.0);
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, 5);
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;
default:
break;
}
} catch (...) {
LOGE("%s", "发生异常");
goto end;
}
system->update();
while (playing) {
channel->isPlaying(&playing);
usleep(1000 * 1000);
}
goto end;
end: env->ReleaseStringUTFChars(path_jstr, path_cstr);
sound->release();
system->close();
system->release();
}
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
-
105
-
106
举个例子比如在我们播放一段mp3的时候里面又人声,乐器声等,如果我想对其中一个钢琴的声音加一个特效

那么怎么加呢!这就用到它了Channel *channel; 一般是32个通道修改完以后混音
好了到这里关键代码已经写完,我们可以测试玩玩了.
项目下载:https://github.com/CN-ZPH