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


    目录
    相关文章
    |
    18天前
    |
    开发框架 前端开发 Android开发
    安卓与iOS开发中的跨平台策略
    在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
    |
    23天前
    |
    搜索推荐 Android开发 开发者
    探索安卓开发中的自定义视图:打造个性化UI组件
    【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
    |
    5天前
    |
    搜索推荐 前端开发 API
    探索安卓开发中的自定义视图:打造个性化用户界面
    在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
    33 19
    |
    9天前
    |
    Java 调度 Android开发
    安卓与iOS开发中的线程管理差异解析
    在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
    |
    25天前
    |
    Android开发 Swift iOS开发
    探索安卓与iOS开发的差异和挑战
    【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。
    |
    25天前
    |
    存储 API 开发工具
    探索安卓开发:从基础到进阶
    【10月更文挑战第37天】在这篇文章中,我们将一起探索安卓开发的奥秘。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和建议。我们将从安卓开发的基础开始,逐步深入到更复杂的主题,如自定义组件、性能优化等。最后,我们将通过一个代码示例来展示如何实现一个简单的安卓应用。让我们一起开始吧!
    |
    6天前
    |
    Java Android开发 开发者
    探索安卓开发:构建你的第一个“Hello World”应用
    在安卓开发的浩瀚海洋中,每个新手都渴望扬帆起航。本文将作为你的指南针,引领你通过创建一个简单的“Hello World”应用,迈出安卓开发的第一步。我们将一起搭建开发环境、了解基本概念,并编写第一行代码。就像印度圣雄甘地所说:“你必须成为你希望在世界上看到的改变。”让我们一起开始这段旅程,成为我们想要见到的开发者吧!
    13 0
    |
    27天前
    |
    XML 存储 Java
    探索安卓开发之旅:从新手到专家
    【10月更文挑战第35天】在数字化时代,安卓应用的开发成为了一个热门话题。本文旨在通过浅显易懂的语言,带领初学者了解安卓开发的基础知识,同时为有一定经验的开发者提供进阶技巧。我们将一起探讨如何从零开始构建第一个安卓应用,并逐步深入到性能优化和高级功能的实现。无论你是编程新手还是希望提升技能的开发者,这篇文章都将为你提供有价值的指导和灵感。
    |
    26天前
    |
    存储 XML JSON
    探索安卓开发:从新手到专家的旅程
    【10月更文挑战第36天】在这篇文章中,我们将一起踏上一段激动人心的旅程,从零基础开始,逐步深入安卓开发的奥秘。无论你是编程新手,还是希望扩展技能的老手,这里都有适合你的知识宝藏等待发掘。通过实际的代码示例和深入浅出的解释,我们将解锁安卓开发的关键技能,让你能够构建自己的应用程序,甚至贡献于开源社区。准备好了吗?让我们开始吧!
    26 2
    |
    27天前
    |
    Android开发
    布谷语音软件开发:android端语音软件搭建开发教程
    语音软件搭建android端语音软件开发教程!