Android ndk开发:fmod语音学习(二)

在上一篇文章中,介绍以及搭建了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();
复制代码

每隔一秒钟访问下是否播放结束,直到声音播放结束才释放内存。

运行项目,体验变声效果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值