Android 开发之 QQ变声功能实现

简介: Android 开发之 QQ变声功能实现

 1.简介

在QQ中我们使用到的一个功能就是变声,QQ是使用FMOD实现的,那么同样的我们也使用FMOD让自己的应用可以变音

2.FMOD简介

fmod Ex 声音系统是为游戏开发者准备的革命性音频引擎,有时候我们下载FMOD可能不是太方便下载,我已将他下载好了放在百度云盘中:

链接:https://pan.baidu.com/s/1TW3ctQd0o5bOVCx5gKL0hA 密码:x2o4

下载下来后解压我们看到解压目录如下:

image.png

3.搭建项目

  在开始之前,最好已经了解了NDK开发的基本知识,需要下载好ndk并配置好,我用的Android studio版本是2.3.2,所以安装了cmake。

新建项目,输入项目名称,勾选包含支持c++

image.png

一直next最后一步勾选c++支持如下:

image.png

点击finish项目就创建完成了。

4.项目配置

4.1 将api\lowlevel\lib的文件复制到项目lib文件夹下

image.png

并且将fmod.jar引入进入,项目中如下图所示:

image.png

4.2将api/lowlevl/inc目录下的文件复制到你项目中的cpp目录下

image.png

复制后如下图:

image.png

4.3 准备好将要变声的mp3文件,放到assets目录下,如果没有就新建一个,在这里我下载的mp3文件是大家耳熟能详的敢问路在何方,这样听起来效果更加明显。

image.png

4.4 在app下的build.gradle下,添加jnilib指向的文件夹

sourceSets.main {
    jniLibs.srcDirs = ['libs']
    jni.srcDirs = []
}

image.gif

5.代码编写

配置好后,就可以编写代码了,(这里Android版本不同或者其他按照第四部可能会出现其他的各种问题,大家可以可以和我一起交流,至于2.3之前的版本ndk对应的是.mk文件并不是cmake)

5.1新建一个工具类ChangeUtils,声明声音对应的类型,以QQ为例分为正常、大叔、萝莉等6中音效,定义改变声音的方法,传入声音路径和声音类型,代码如下:

public class ChangeUtils {
    //定义音效类型常量
    public static final  int zhengchang = 0;//正常
    public static final int luoli = 1;//萝莉
    public static final int dashu = 2;//大叔
    public static final int jingsong = 3;//惊悚
    public static final int gaoguai = 4;//搞怪
    public static final int kongling = 5;//空灵
    /**
     *
     * @param path
     * @param type
     */
    public native static void change(String path, int type);
    static {
        System.loadLibrary("fmodL");
        System.loadLibrary("fmod");
        System.loadLibrary("sound");
    }
}

image.gif

5.2生成对应的头文件

我们使用javah命令生成工具类对应的头文件

image.png

上面报错是因为在代码注释中有中文,所以我们可以给javah添加utf8,就不报错误了,命令如下:

image.png

执行成功后就生成了对应的头文件:

5.3将生成的头文件复制到cpp目录下,并在cpp目录下新建voice.cpp文件

我们可以根据下载实例中的下述文件编写voice的代码(个人能力有限,不造轮子)

voice.cpp的代码如下:

#include "inc/fmod.hpp"
#include <stdlib.h>
#include <unistd.h>
#include  "hlq_utils_ChangeUtils.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_hlq_utils_ChangeUtils_change(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();
}

image.gif

5.4配置cmake文件,声明cmake版本,设置一个路径变量,指向我们的libs目录,也就是fmod.jar和so库的目录

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

我的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/voice.cpp )
include_directories(src/main/cpp/inc)
target_link_libraries( sound fmod fmodL
                       ${log-lib} )

image.gif

5.5 最后一步

终于到了最后一步,那就是在页面放上几个按钮,给按钮添加监听事件,不同监听事件对应音效不同

在Activity中初始化FMOD

FMOD.init(this);

image.gif

结合工具类设置工具类型

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

image.gif

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

image.gif

这样就完成了变声效果,需要注意的事,点击播放后,需要重新退出才可以切换声音效果,感兴趣的可以自行扩展成语音对讲那种。

6.总结

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

    源码地址:https://github.com/huanglinqing123/ChangeVoice/tree/master


    目录
    相关文章
    |
    3天前
    |
    Java Android开发 iOS开发
    探索安卓与iOS开发的差异:平台特性与创新潜力
    在移动应用开发的广阔天地中,安卓和iOS两大平台各占据一方。本文深入剖析了这两个操作系统的开发环境、工具、语言及市场趋势,旨在为开发者提供一个全面的比较视角。文章将基于最新的行业报告、技术论坛讨论以及专家分析,详细阐述两个平台的技术架构差异、开发成本和用户体验设计的不同点。通过数据支持的论证,揭示安卓与iOS在创新潜力上的独特优势,并探讨它们如何塑造未来的移动应用生态。
    5 0
    |
    4天前
    |
    测试技术 数据库 Android开发
    基于Android的聊天软件的设计与实现-一个聊天软件开发起来没那么难不是?(4)
    基于Android的聊天软件的设计与实现-一个聊天软件开发起来没那么难不是?
    9 0
    |
    4天前
    |
    Android开发
    基于Android的聊天软件的设计与实现-一个聊天软件开发起来没那么难不是?(3)
    基于Android的聊天软件的设计与实现-一个聊天软件开发起来没那么难不是?
    9 0
    |
    4天前
    |
    关系型数据库 MySQL 数据库
    基于Android的聊天软件的设计与实现-一个聊天软件开发起来没那么难不是?(2)
    基于Android的聊天软件的设计与实现-一个聊天软件开发起来没那么难不是?
    9 0
    |
    4天前
    |
    SQL Java 数据库
    基于Android的聊天软件的设计与实现-一个聊天软件开发起来没那么难不是?(1)
    基于Android的聊天软件的设计与实现-一个聊天软件开发起来没那么难不是?
    14 0
    |
    5天前
    |
    Android开发
    android 开发中的日期加减处理
    android 开发中的日期加减处理
    8 0
    |
    5天前
    |
    Android开发
    Android 开发中跳转到评论页面
    Android 开发中跳转到评论页面
    12 0
    |
    10天前
    |
    编解码 开发工具 Android开发
    技术心得:打造自己的智能投屏体验——Android投屏开发入门
    技术心得:打造自己的智能投屏体验——Android投屏开发入门
    14 0
    |
    2月前
    |
    Java API 开发工具
    java与Android开发入门指南
    java与Android开发入门指南
    71 0
    |
    存储 SQL 关系型数据库
    Android数据库开发基础入门【附完整案例】
    Android数据库开发基础入门【附完整案例】
    464 0
    Android数据库开发基础入门【附完整案例】