在上一篇文章中,介紹以及搭建了FMOD的Android示例,這篇文章是在上一篇文章的基礎上寫的。所以建議首先讀一下我的這篇文章《Android ndk開發:fmod語音學習(一)》。
本文使用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 <jni.h>
/* 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,實現頁面邏輯
佈局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
android:orientation="vertical" >
<Button
android:id="@+id/yuan_sheng"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="原聲"/>
<Button
android:id="@+id/luo_li"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="蘿莉"/>
<Button
android:id="@+id/da_shu"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="大叔"/>
<Button
android:id="@+id/jing_song"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="驚悚"/>
<Button
android:id="@+id/gao_guai"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="搞怪"/>
<Button
android:id="@+id/kong_ling"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="空靈"/>
</LinearLayout>
複製程式碼
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 <stdlib.h>
#include <unistd.h>
#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();
複製程式碼
每隔一秒鐘訪問下是否播放結束,直到聲音播放結束才釋放記憶體。
執行專案,體驗變聲效果。