NDK开发QQ语音变声

一、下载fmod三方引擎sdk

fmod官网下载Engine -> Android下的sdk

image-20220127202810611

二、定义原生方法

2.1、拷贝sdk

新建AndroidStudio Native工程

1,拷贝sdk的lib包到androidstudio工程的lib目录下,并右键add as library

2,拷贝inc头文件到main/cpp目录下

image-20220127202810611

2.2、绘制页面

在MainActivity的xml中绘制几个按钮如下,ui可以完全自定义。

image-20220127202810611

2.3、定义点击事件

MainActivity定义点击事件如下

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

//        tempCreateFilePath()
    }

    fun tempCreateFilePath() {
        val file = getExternalFilesDir("/zhanglei.m4a")
        if (file?.exists() == false) {
            file.mkdirs()
        }
    }

    fun mFix(btn: View) {
        val file = getExternalFilesDir("/zhanglei.m4a")
        val path = file?.path.toString()
        if (!File(path).exists()) {
            Toast.makeText(this, "文件不存在", Toast.LENGTH_SHORT).show()
            return;
        }
        lifecycleScope.launch(Dispatchers.IO) {
            when (btn.id) {
                R.id.btn_normal -> EffectUtils.fix(path, EffectUtils.MODE_NORMAL)
                R.id.btn_luoli -> EffectUtils.fix(path, EffectUtils.MODE_LUOLI)
                R.id.btn_dashu -> EffectUtils.fix(path, EffectUtils.MODE_DASHU)
                R.id.btn_jingsong -> EffectUtils.fix(path, EffectUtils.MODE_JINGSONG)
                R.id.btn_gaoguai -> EffectUtils.fix(path, EffectUtils.MODE_GAOGUAI)
                R.id.btn_kongling -> EffectUtils.fix(path, EffectUtils.MODE_KONGLING)
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        FMOD.close()
    }
}

2.4、配置kotlin携程

这里用到了携程需要配置Gradle

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
implementation "androidx.activity:activity-ktx:1.1.0"

2.5、抽出Native工具类

定义Native方法fix方法的工具类EffectUtils

object EffectUtils {

    //音效的类型
    const val MODE_NORMAL = 0
    const val MODE_LUOLI = 1
    const val MODE_DASHU = 2
    const val MODE_JINGSONG = 3
    const val MODE_GAOGUAI = 4
    const val MODE_KONGLING = 5

    external fun fix(path: String, type: Int)

    init {
        System.loadLibrary("qqvoicechange")
    }
}

2.6、拷贝音频文件到SD卡目录

拷贝一段你录制的音频文件到SD卡app/file/目录下,我这里测试.mp3、.m4a都可以。也可以使用文末demo中的录音文件。

三、实现native方法

3.1、配置CMakeLists.txt文件

cmake_minimum_required(VERSION 3.18.1)

project("qqvoicechange")

set(libs_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../libs)

add_library(fmod
        SHARED
        IMPORTED)

set_target_properties(fmod
        PROPERTIES
        IMPORTED_LOCATION ${libs_DIR}/${ANDROID_ABI}/libfmod.so
        )

add_library(fmodL
        SHARED
        IMPORTED)

set_target_properties(fmodL
        PROPERTIES
        IMPORTED_LOCATION ${libs_DIR}/${ANDROID_ABI}/libfmodL.so
        )


add_library(
        qqvoicechange
        SHARED
        native-lib.cpp)

#头文件
target_include_directories(qqvoicechange PRIVATE ./inc/)

target_link_libraries(
        qqvoicechange
        fmod
        fmodL
        log)

3.2、实现fix方法

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

#include <android/log.h>

#define LOGI(FORMAT, ...) __android_log_print(ANDROID_LOG_INFO,"jason",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,"jason",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;

extern "C"
JNIEXPORT void JNICALL
Java_com_example_qqvoicechange_EffectUtils_fix(JNIEnv *env, jobject thiz, jstring path_jstr,
                                                  jint type) {

    const char *path_cstr = env->GetStringUTFChars(path_jstr, nullptr);
    LOGI("%s", path_cstr);
    System *system;
    Sound *sound;
    Channel *channel;
    DSP *dsp;
    float frequency = 0;
    bool playing = true;
    try {
        //初始化
        System_Create(&system);
        system->init(32, FMOD_INIT_NORMAL, nullptr);
        //创建声音
        system->createSound(path_cstr, FMOD_DEFAULT, nullptr, &sound);

        switch (type) {
            //原生播放
            case MODE_NORMAL:
                system->playSound(sound, nullptr, false, &channel);
                LOGI("%s", "fix normal");
                break;
            case MODE_LUOLI:
                //萝莉
                //DSP digital signal process
                //dsp -> 音效 创建fmod中预定义好的音效
                //FMOD_DSP_TYPE_PITCHSHIFT dsp,提升或者降低音调用的一种音效
                system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
                //设置音调的参数
                dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 2.5);
                system->playSound(sound, nullptr, false, &channel);
                //添加到channel
                channel->addDSP(0, dsp);
                LOGI("%s","fix luoli");
                break;

            case MODE_DASHU:
                //大叔
                system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
                //设置音调的参数
                dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.8);
                system->playSound(sound, nullptr, false, &channel);
                //添加到channel
                channel->addDSP(0, dsp);
                LOGI("%s","fix dashu");
                break;

            case MODE_GAOGUAI:
                //搞怪
                //提高说话的速度
                system->playSound(sound, nullptr, false, &channel);
                channel->getFrequency(&frequency);
                frequency = frequency * 1.6;
                channel->setFrequency(frequency);
                LOGI("%s","fix gaoguai");
                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, nullptr, false, &channel);
                channel->addDSP(0, dsp);
                LOGI("%s","fix kongling");
                break;
        }
    } catch (...) {
        LOGE("%s", "发生异常");
        goto end;
    }

    system->update();

    //单位是微秒
    //每秒钟判断下是否在播放
    while (playing) {
        channel->isPlaying(&playing);
        usleep(1000 * 1000);
    }
    goto end;
    end:
    //释放资源
    sound->release();
    system->close();
    system->release();
    env->ReleaseStringUTFChars(path_jstr, path_cstr);
}

四、运行效果

由于博客不支持MP4视频的展示,这里把demo和演示效果压缩放到一块了

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流星雨在线

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值