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


    目录
    相关文章
    |
    4天前
    |
    开发工具 Android开发 iOS开发
    探索Android与iOS开发的差异与挑战
    【7月更文挑战第11天】在移动应用开发的广阔天地中,Android和iOS两大平台如同双子星座般耀眼,各自拥有独特的开发生态和用户群体。本文将深入分析这两个平台的显著差异,从技术架构到开发工具,再到市场定位,揭示它们之间的异同。通过比较,我们不仅能够更好地理解各自的优势和局限,还能洞察未来移动应用开发的趋势。
    |
    2天前
    |
    存储 移动开发 Android开发
    使用kotlin Jetpack Compose框架开发安卓app, webview中h5如何访问手机存储上传文件
    在Kotlin和Jetpack Compose中,集成WebView以支持HTML5页面访问手机存储及上传音频文件涉及关键步骤:1) 添加`READ_EXTERNAL_STORAGE`和`WRITE_EXTERNAL_STORAGE`权限,考虑Android 11的分区存储;2) 配置WebView允许JavaScript和文件访问,启用`javaScriptEnabled`、`allowFileAccess`等设置;3) HTML5页面使用`<input type="file">`让用户选择文件,利用File API;
    |
    5天前
    |
    开发者 Kotlin Android开发
    Kotlin协程在Android开发中的应用
    【7月更文挑战第10天】Kotlin协程简化了Android异步编程,提供轻量级并发。挂起函数让异步代码看起来同步,不阻塞线程,便于管理。在项目中,添加Kotlin和协程依赖,如`kotlinx.coroutines-core`和`kotlinx-coroutines-android`。使用`CoroutineScope`和`launch`处理耗时任务,如网络请求,避免主线程阻塞。挂起函数和调度器控制执行上下文,适应不同任务需求。
    |
    23小时前
    |
    Android开发 Kotlin
    kotlin开发安卓app,如何让布局自适应系统传统导航和全面屏导航
    使用`navigationBarsPadding()`修饰符实现界面自适应,自动处理底部导航栏的内边距,再加上`.padding(bottom = 10.dp)`设定内容与屏幕底部的距离,以完成全面的布局适配。示例代码采用Kotlin。
    30 15
    |
    1天前
    |
    存储 API Android开发
    kotlin开发安卓app,使用webivew 触发 onShowFileChooser, 但只能触发一次,第二次无法触发,是怎么回事。 如何解决
    在Android WebView开发中,`onShowFileChooser`方法用于开启文件选择。当用户只能选择一次文件可能是因为未正确处理选择回调。解决此问题需确保:1) 实现`WebChromeClient`并覆写`onShowFileChooser`;2) 用户选择文件后调用`ValueCallback.onReceiveValue`传递URI;3) 传递结果后将`ValueCallback`设为`null`以允许再次选择。下面是一个Kotlin示例,展示如何处理文件选择和结果回调。别忘了在Android 6.0+动态请求存储权限,以及在Android 10+处理分区存储。
    |
    4天前
    |
    Java 开发工具 Android开发
    探索Android与iOS开发的差异与挑战
    【7月更文挑战第11天】在移动应用开发的广阔天地中,Android和iOS两大平台各领风骚。本文将深入探讨这两个平台的开发差异,从编程语言、用户界面设计到开发工具等方面进行比较,并分析开发者面临的挑战。通过对比分析,旨在为开发者提供一个全面的视角,帮助他们更好地选择适合自己项目需求的开发平台。
    8 0
    |
    6天前
    |
    搜索推荐 Android开发 iOS开发
    探索Android与iOS开发的差异:平台特性与用户体验的对比分析
    【7月更文挑战第9天】在移动应用开发的浩瀚海洋中,Android和iOS两大操作系统如同两座灯塔,指引着开发者们的航向。本文将深入探讨这两个平台在开发环境、用户界面设计、性能优化以及市场策略上的根本差异。我们将通过比较分析,揭示各自平台的独特优势和潜在挑战,为开发者提供决策支持,同时也为用户体验的提升指明方向。
    |
    2月前
    |
    存储 Java 开发工具
    Android开发的技术与开发流程
    Android开发的技术与开发流程
    284 1
    |
    2月前
    |
    SQL API Android开发
    展望2022:Android 开发最新技术动向
    展望2022:Android 开发最新技术动向
    237 0
    展望2022:Android 开发最新技术动向
    |
    存储 缓存 前端开发
    MAD,现代安卓开发技术:Android 领域开发方式的重大变革~(2)
    MAD,现代安卓开发技术:Android 领域开发方式的重大变革~(2)
    MAD,现代安卓开发技术:Android 领域开发方式的重大变革~(2)