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


    目录
    相关文章
    |
    6月前
    |
    移动开发 前端开发 Android开发
    【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
    【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
    1147 12
    【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
    |
    6月前
    |
    移动开发 JavaScript 应用服务中间件
    【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
    【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
    893 5
    【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
    |
    6月前
    |
    移动开发 Rust JavaScript
    【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
    【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
    1036 4
    【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
    |
    7月前
    |
    开发工具 Android开发
    X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
    X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
    817 11
    X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
    |
    6月前
    |
    移动开发 Android开发
    【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
    【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
    337 0
    |
    7月前
    |
    Java 开发工具 Maven
    【01】完整的安卓二次商业实战-详细的初级步骤同步项目和gradle配置以及开发思路-优雅草伊凡
    【01】完整的安卓二次商业实战-详细的初级步骤同步项目和gradle配置以及开发思路-优雅草伊凡
    952 6
    |
    9月前
    |
    移动开发 Java 编译器
    Kotlin与Jetpack Compose:Android开发生态的演进与架构思考
    本文从资深Android工程师视角深入分析Kotlin与Jetpack Compose在Android系统中的技术定位。Kotlin通过空安全、协程等特性解决了Java在移动开发中的痛点,成为Android官方首选语言。Jetpack Compose则引入声明式UI范式,通过重组机制实现高效UI更新。两者结合不仅提升开发效率,更为跨平台战略和现代架构模式提供技术基础,代表了Android开发生态的根本性演进。
    393 0
    |
    存储 Java 开发工具
    Android开发的技术与开发流程
    Android开发的技术与开发流程
    691 1
    |
    编解码 Android开发 iOS开发
    安卓与iOS开发:平台差异下的技术创新之路
    在数字时代的浪潮中,移动应用开发如同两股潮流——安卓与iOS,各自携带着独特的技术生态和文化基因。本文将深入探讨这两大平台的开发环境、编程语言和工具的差异,以及它们如何塑造了不同的用户体验和技术趋势。通过比较分析,我们旨在揭示跨平台开发的可能性和挑战,同时探索未来技术创新的方向。让我们一起跟随代码的足迹,穿越安卓的开放草原和iOS的精密园林,发现那些隐藏在平台差异之下的创新机遇。
    231 28
    |
    安全 Android开发 Swift
    安卓与iOS开发:平台差异与技术选择
    【8月更文挑战第26天】 在移动应用开发的广阔天地中,安卓和iOS两大平台各占一方。本文旨在探索这两个系统在开发过程中的不同之处,并分析开发者如何根据项目需求选择合适的技术栈。通过深入浅出的对比,我们将揭示各自平台的优势与挑战,帮助开发者做出更明智的决策。
    321 5