Android ndk開發:fmod語音學習(二)

鋸齒流沙發表於2017-12-26

在上一篇文章中,介紹以及搭建了FMOD的Android示例,這篇文章是在上一篇文章的基礎上寫的。所以建議首先讀一下我的這篇文章《Android ndk開發:fmod語音學習(一)》

本文使用FMOD來實現變聲效果,直接使用《Android ndk開發:fmod語音學習(一)》這篇文章建立的FMOD的Android示例上新增變聲效果。

fmod.png

上圖就是專案的介面,可以看到,本節要實現的變聲效果,包括大叔蘿莉等等。接下來實戰開始。

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型別。

fmod.png

DSP設定音效也有很多種

fmod.png

這些音效和型別都有註釋說明,使用者根據需求去做不一樣的變聲效果。

關於開始播放聲音:實際上使用system->update();才開始播放聲音。

        while(isplay){
		channel->isPlaying(&isplay);
		usleep(1000*1000);
	}
	env->ReleaseStringUTFChars(path_jstr,path);
	sound->release();
	system->close();
	system->release();
複製程式碼

每隔一秒鐘訪問下是否播放結束,直到聲音播放結束才釋放記憶體。

執行專案,體驗變聲效果。

相關文章