Skip to content

AndroidHensen/NDKVoice

Folders and files

NameName
Last commit message
Last commit date

Latest commit

author
AndroidHensen
Sep 2, 2017
dfa9b2b · Sep 2, 2017

History

1 Commit
Sep 2, 2017
Sep 2, 2017
Sep 2, 2017
Sep 2, 2017
Sep 2, 2017
Sep 2, 2017
Sep 2, 2017
Sep 2, 2017
Sep 2, 2017
Sep 2, 2017

Repository files navigation

[toc]

项目演示

这里写图片描述

源码下载

Github:https://github.com/AndroidHensen/NDKVoice

环境配置

  1. 你的Android Studio在SDKManager中下载好ndk-build和cmake
  2. 通过fmod的官网,在DownLoad页面中,找到Android平台的源码进行下载,这里需要注意使用VPN或者联系客服

项目开始

1、创建一个新的项目,勾选包含C++

这里写图片描述

2、勾选C++11和C++的依赖库

这里写图片描述

3、找到下载好的fmod目录下,在api/lowlevel/lib中,将所有文件拷贝到你的项目的libs目录下

这里写图片描述

4、让项目添加对fmod.jar包的支持,fmod.jar右键Add As Libs

这里写图片描述

5、找到下载好的fmod目录下,在api/lowlevel/inc中,将所有文件拷贝到你的项目的cpp(jni folder)目录下

这里写图片描述

拷贝好后如下图

这里写图片描述

6、准备好一个音频文件放到assets文件夹中

这里写图片描述

7、配置app的gradle文件中的cmake和ndk

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.3"
    defaultConfig {
        applicationId "com.handsome.ndkvoice"
        minSdkVersion 16
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11 -frtti -fexceptions"
                abiFilters "armeabi","arm64-v8a","armeabi-v7a","x86"
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets.main {
        jniLibs.srcDirs = ['libs']
        jni.srcDirs = []
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

代码编写

1、我们创建一个Utils类,编写我们的fmod变声处理

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");
    }
}

2、通过终端,javah我们的文件

这里写图片描述

3、将创建好的.h文件放进我们的cpp目录下,并创建sound.cpp

这里写图片描述

4、编写sound.cpp代码

#include "inc/fmod.hpp"
#include <stdlib.h>
#include <unistd.h>
#include  "com_handsome_ndkvoice_Utils.h"

#include <jni.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_com_handsome_ndkvoice_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;
    //音频地址
    const char* path_cstr = env->GetStringUTFChars(path_jstr, NULL);

    System_Create(&system);
	system->init(32, FMOD_INIT_NORMAL, NULL);

	try {
		//创建声音
		system->createSound(path_cstr, FMOD_DEFAULT, NULL, &sound);
		switch (type) {
            case MODE_NORMAL:
                //原生播放
                system->playSound(sound, 0, false, &channel);
                break;
            case MODE_LUOLI:
                //提升或者降低音调的一种音效
                system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
                //设置音调的参数
                dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 1.8);
                //添加进到channel,添加进轨道
                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, 0.8);
                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;
            }
	} catch (...) {
		LOGE("%s", "发生异常");
		goto end;
	}
	system->update();

	//单位是微妙
	//每秒钟判断下是否是播放
	while (playing) {
		channel->isPlaying(&playing);
        usleep(1000);
	}
	goto end;

    //释放资源
    end: env->ReleaseStringUTFChars(path_jstr, path_cstr);
	sound->release();
	system->close();
	system->release();
}

5、配置cmake文件

cmake_minimum_required(VERSION 3.4.1)


find_library( # Sets the name of the path variable.
              log-lib
              log )

set(distribution_DIR ${CMAKE_SOURCE_DIR}/libs)

add_library( fmod
             SHARED
             IMPORTED )
set_target_properties( fmod
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/${ANDROID_ABI}/libfmod.so )
add_library( fmodL
             SHARED
             IMPORTED )
set_target_properties( fmodL
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/${ANDROID_ABI}/libfmodL.so )
add_library( sound
             SHARED
             src/main/cpp/sound.cpp )

include_directories(src/main/cpp/inc)

target_link_libraries( sound fmod fmodL
                       ${log-lib} )

6、编写我们的Activity,对每个类型处理点击事件并调用本地方法

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private ExecutorService fixedThreadPool;
    private PlayerThread playerThread;
    private String path = "file:///android_asset/hensen.mp3";
    private int type;

    private LinearLayout normal, luoli, dashu, jingsong, gaoguai, kongling;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        normal = (LinearLayout) findViewById(R.id.normal);
        luoli = (LinearLayout) findViewById(R.id.luoli);
        dashu = (LinearLayout) findViewById(R.id.dashu);
        jingsong = (LinearLayout) findViewById(R.id.jingsong);
        gaoguai = (LinearLayout) findViewById(R.id.gaoguai);
        kongling = (LinearLayout) findViewById(R.id.kongling);
        normal.setOnClickListener(this);
        luoli.setOnClickListener(this);
        dashu.setOnClickListener(this);
        jingsong.setOnClickListener(this);
        gaoguai.setOnClickListener(this);
        kongling.setOnClickListener(this);

        fixedThreadPool = Executors.newFixedThreadPool(1);
        FMOD.init(this);
    }

    class PlayerThread implements Runnable {
        @Override
        public void run() {
            Utils.fix(path, type);
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.normal:
                type = Utils.MODE_NORMAL;
                break;
            case R.id.luoli:
                type = Utils.MODE_LUOLI;
                break;
            case R.id.dashu:
                type = Utils.MODE_DASHU;
                break;
            case R.id.jingsong:
                type = Utils.MODE_JINGSONG;
                break;
            case R.id.gaoguai:
                type = Utils.MODE_GAOGUAI;
                break;
            case R.id.kongling:
                type = Utils.MODE_KONGLING;
                break;
        }

        playerThread = new PlayerThread();
        fixedThreadPool.execute(playerThread);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        FMOD.close();
    }
}

过程分析

  • cmake文件的分析
  • 变声类型的分析
  • 实现变声代码过程的分析

cmake文件分析

1、声明cmake版本

cmake_minimum_required(VERSION 3.4.1)

2、设置一个路径变量,指向我们的libs目录,也就是fmod.jar和so库的目录

set(distribution_DIR ${CMAKE_SOURCE_DIR}/libs)

3、添加我们需要的fmod、fmodL、sound的库

add_library( fmod
             SHARED
             IMPORTED )
set_target_properties( fmod
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/${ANDROID_ABI}/libfmod.so )
add_library( fmodL
             SHARED
             IMPORTED )
set_target_properties( fmodL
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/${ANDROID_ABI}/libfmodL.so )
add_library( sound
             SHARED
             src/main/cpp/sound.cpp )

这里是通过我们刚才设置的libs目录下,和对应的Gradle设置的架构进行匹配,找到libfmod.so文件

${distribution_DIR}/${ANDROID_ABI}/libfmod.so

Gradle设置的架构在这个地方

这里写图片描述

变声类型的分析

  • 原声:直接播放音频文件
  • 萝莉:对音频提高八度
  • 大叔:对音频减低八度
  • 惊悚:增加音频的颤音
  • 搞笑:增加音频的播放速度
  • 空灵:增加音频的回音

实现变声代码过程的分析

这里的实现主要是阅读fmod下载的例子,播放声音的代码和增加音效的代码来编写的

这里写图片描述

大概的流程

  1. 创建fmod的系统:System_Create(&system);
  2. 系统进行初始化:system->init(32, FMOD_INIT_NORMAL, NULL);
  3. 创建音频:system->createSound(path_cstr, FMOD_DEFAULT, NULL, &sound);
  4. 创建不同类型的音效:system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
  5. 播放声音:system->playSound(sound, 0, false, &channel);
  6. 将音效加入声音中:channel->addDSP(0, dsp);
  7. 关闭资源:end部分

About

【博客】AndroidStudio+CMake实现QQ变声效果

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published