在上一篇文章中,介绍以及搭建了FMOD的Android示例,这篇文章是在上一篇文章的基础上写的。所以建议首先读一下我的这篇文章。
本文使用FMOD来实现变声效果,直接使用《Android ndk开发:fmod语音学习(一)》这篇文章建立的FMOD的Android示例上添加变声效果。
上图就是项目的界面,可以看到,本节要实现的变声效果,包括大叔、萝莉等等。接下来实战开始。
1、编写本地方法
VoiceUtils.java
public class VoiceUtils {
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;
/**
*
* @param path 语言的路径
* @param type 声音类型类型
*/
public native static void fix(String path, int type);
static {
System.loadLibrary("fmodL");
System.loadLibrary("fmod");
System.loadLibrary("Voice");
}
}
2、使用Javah 编译头文件
org_fmod_example_VoiceUtils.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class org_fmod_example_VoiceUtils */
#ifndef _Included_org_fmod_example_VoiceUtils
#define _Included_org_fmod_example_VoiceUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: org_fmod_example_VoiceUtils
* Method: fix
* Signature: (Ljava/lang/String;I)V
*/
JNIEXPORT void JNICALL Java_org_fmod_example_VoiceUtils_fix
(JNIEnv *, jclass, jstring, jint);
#ifdef __cplusplus
}
#endif
#endif
3、新建fix.cpp文件
4、更改Android.mk文件
LOCAL_PATH := $(call my-dir)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/*.cpp
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 := Voice
LOCAL_SRC_FILES := fix.cpp
LOCAL_SHARED_LIBRARIES := fmod fmodL
include $(BUILD_SHARED_LIBRARY)
主要将LOCAL_SRC_FILES指向fix.cpp
5、新建Activity,实现页面逻辑
布局:
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
android:orientation="vertical" >
android:id="@+id/yuan_sheng"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="原声"/>
android:id="@+id/luo_li"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="萝莉"/>
android:id="@+id/da_shu"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="大叔"/>
android:id="@+id/jing_song"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="惊悚"/>
android:id="@+id/gao_guai"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="搞怪"/>
android:id="@+id/kong_ling"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="空灵"/>
VoiceActivity:
public class VoiceActivity extends Activity{
private String path;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
FMOD.init(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.voice_layout);
this.findViewById(R.id.yuan_sheng).setOnClickListener(mClickListener);
this.findViewById(R.id.luo_li).setOnClickListener(mClickListener);
this.findViewById(R.id.da_shu).setOnClickListener(mClickListener);
this.findViewById(R.id.jing_song).setOnClickListener(mClickListener);
this.findViewById(R.id.gao_guai).setOnClickListener(mClickListener);
this.findViewById(R.id.kong_ling).setOnClickListener(mClickListener);
path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separatorChar + "testvoice.wav";
}
/**
* 点击监听器
*/
private OnClickListener mClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.yuan_sheng:
VoiceUtils.fix(path, VoiceUtils.MODE_NORMAL);
break;
case R.id.luo_li:
VoiceUtils.fix(path, VoiceUtils.MODE_LUOLI);
break;
case R.id.da_shu:
VoiceUtils.fix(path, VoiceUtils.MODE_DASHU);
break;
case R.id.jing_song:
VoiceUtils.fix(path, VoiceUtils.MODE_JINGSONG);
break;
case R.id.gao_guai:
VoiceUtils.fix(path, VoiceUtils.MODE_GAOGUAI);
break;
case R.id.kong_ling:
VoiceUtils.fix(path, VoiceUtils.MODE_KONGLING);
break;
default:
break;
}
}
};
@Override
protected void onDestroy() {
super.onDestroy();
FMOD.close();
};
}
根据按钮点击来调用本地方法。
6、fix.cpp实现头文件方法
这是最重要的一步,根据type类型来实现不同的变声效果。
#include "inc/fmod.hpp"
#include "common.h"
#include
#include
#include "org_fmod_example_VoiceUtils.h";
#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_VoiceUtils_fix
(JNIEnv *env, jclass jcls, jstring path_jstr, jint type){
System *system;
Sound *sound;
Channel *channel = 0;
bool isplay = true;
DSP *dsp;
float frequency = 0;
// char *path = env->GetStringUTFChars(path_jstr,NULL);
const char* path = env->GetStringUTFChars(path_jstr,NULL);
System_Create(&system);//创建
system->init(32, FMOD_INIT_NORMAL, NULL);//初始化
//创建声音
system->createSound(path, FMOD_DEFAULT, 0, &sound);
switch(type){
case MODE_NORMAL:
//原生播放
system->playSound(sound, 0, false, &channel);
break;
case MODE_LUOLI:
/**
* DSP:digital signal process
* FMOD_DSP_TYPE_PITCHSHIFT dsp,提升或者降低音调用的一种音效
*/
system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT,&dsp);
//设置音调的参数,创建fmod中预定义好的音效
dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH,2.0);
system->playSound(sound, 0, false, &channel);
//添加到channel
channel->addDSP(0,dsp);
break;
case MODE_DASHU:
// (FMOD_DSP_TYPE type, DSP **dsp);
system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT,&dsp);
dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH,0.5);
// (Sound *sound, ChannelGroup *channelgroup, bool paused, Channel **channel);
system->playSound(sound,0,false,&channel);
// (int index, DSP *dsp);
channel->addDSP(0,dsp);
break;
case MODE_JINGSONG:
system->createDSPByType(FMOD_DSP_TYPE_TREMOLO,&dsp);
//-1.0~1.0
dsp->setParameterFloat(FMOD_DSP_TREMOLO_SKEW,0.3);
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 * 1.6;
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;
}
system->update();
//单位是微秒,每秒钟判断下是否在播放
while(isplay){
channel->isPlaying(&isplay);
usleep(1000*1000);
}
env->ReleaseStringUTFChars(path_jstr,path);
sound->release();
system->close();
system->release();
}
using namespace FMOD使用命名空间FMOD, 主要是方便命名空间FMOD的方法调用,不需要每次调用都要加上FMOD::。基本上读者可以看注释就明白使用了,无需多言。
实现变声,主要是使用DSP,给DSP设置不同的参数类型可以得到不同的音效。最后将 DSP添加到channel声道中。
打开fmod_dsp_effects.h文件可以看到又非常多DSP类型。
DSP设置音效也有很多种
这些音效和类型都有注释说明,用户根据需求去做不一样的变声效果。
关于开始播放声音:实际上使用system->update();才开始播放声音。
while(isplay){
channel->isPlaying(&isplay);
usleep(1000*1000);
}
env->ReleaseStringUTFChars(path_jstr,path);
sound->release();
system->close();
system->release();
每隔一秒钟访问下是否播放结束,直到声音播放结束才释放内存。
运行项目,体验变声效果。