美文网首页
Android NDK的使用实例——使用fmod模仿QQ变声

Android NDK的使用实例——使用fmod模仿QQ变声

作者: 自然like | 来源:发表于2017-12-31 10:48 被阅读98次
    image

    概述

    本篇记录的内容在上一篇 Android NDK的使用实例——fmod example 的基础上继续开发。这次要使用fmod模仿QQ变声的效果。

    4.jpg

    页面布局

    几个按钮,就不贴布局代码了


    2.jpeg

    编写native方法

    NdkUtil.java

    package org.fmod.example;
    
    /**
     * NDK 工具类
     * Created by lex on 2017/12/30.
     */
    public class NdkUtil {
        static {
            System.loadLibrary("native-lib");
        }
    
        public static final int MODE_PLAY = 0;
        public static final int MODE_LUOLI = 1;
        public static final int MODE_DASHU = 2;
        public static final int MODE_JINGSONG = 3;
        public static final int MODE_GAOGUAI = 4;
        public static final int MODE_KONGLING = 5;
    
        /**
         * 播放
         */
        public native static final void play(int mode);
    }
    

    官方Demo解读

    首先看下官方的Demo,这里把 effects.cpp 复制到工程里面,添加编译到 CMakeLists.txt 里面。

    add_library( # Sets the name of the library.
                 native-lib
    
                 # Sets the library as a shared library.
                 SHARED
    
                 # Provides a relative path to your source file(s).
                 src/main/cpp/effects.cpp
                 src/main/cpp/common.cpp
                 src/main/cpp/common_platform.cpp
                 src/main/cpp/native-lib.cpp )
    

    跑起来是这样的效果。


    724493-2c52f28161bbfe31.jpeg

    其中A、B、C、D按钮分别响应低通滤波lowpass、高通滤波highpass、回音echo、法兰flange的变声效果。点击后可以听到音频的发生了变声的效果。

    以下是 effects.cpp 源代码,做了注释和删减部分。通过不断判断与测试,抽出必要的代码,以达到自己想要的效果。

    #include "inc/fmod.hpp"
    #include "common.h"
    
    int FMOD_Main()
    {
        // 初始化变声需要的相关变量
        FMOD::System       *system        = 0;
        FMOD::Sound        *sound         = 0;
        FMOD::Channel      *channel       = 0;
        FMOD::ChannelGroup *mastergroup   = 0; 
        FMOD::DSP          *dsplowpass    = 0;
        FMOD::DSP          *dsphighpass   = 0;
        FMOD::DSP          *dspecho       = 0;
        FMOD::DSP          *dspflange     = 0;
        FMOD_RESULT         result;
        unsigned int        version;
        void               *extradriverdata = 0;
    
        Common_Init(&extradriverdata);
    
        /*
            Create a System object and initialize
            创建一个系统对象
        */
        result = FMOD::System_Create(&system);
        // 初始化相关数据
        result = system->init(32, FMOD_INIT_NORMAL, extradriverdata);
        // 创建一个声音
        result = system->createSound(Common_MediaPath("dream.m4a"), FMOD_DEFAULT, 0, &sound);
        // 播放声音
        result = system->playSound(sound, 0, false, &channel);
    
        /*
            Create some effects to play with
            创建不同的音效
        */
        result = system->createDSPByType(FMOD_DSP_TYPE_LOWPASS, &dsplowpass);
        result = system->createDSPByType(FMOD_DSP_TYPE_HIGHPASS, &dsphighpass);
        result = system->createDSPByType(FMOD_DSP_TYPE_ECHO, &dspecho);
        result = system->createDSPByType(FMOD_DSP_TYPE_FLANGE, &dspflange);
    
        /*
            Add them to the master channel group.  Each time an effect is added (to position 0) it pushes the others down the list.
        */
        result = mastergroup->addDSP(0, dsplowpass);
        result = mastergroup->addDSP(0, dsphighpass);
        result = mastergroup->addDSP(0, dspecho);
        result = mastergroup->addDSP(0, dspflange);
    
        /*
            By default, bypass all effects.  This means let the original signal go through without processing.
            It will sound 'dry' until effects are enabled by the user.
        */
        result = dsplowpass->setBypass(true);
        result = dsphighpass->setBypass(true);
        result = dspecho->setBypass(true);
        result = dspflange->setBypass(true);
    
        /*
            Main loop
        */
        do
        {
            Common_Update();
    
            // 判断不同按钮的点击状态,以进行不同的变声效果处理
            if (Common_BtnPress(BTN_MORE))
            {
                bool paused;
    
                result = channel->getPaused(&paused);
    
                paused = !paused;
    
                result = channel->setPaused(paused);
            }
    
            if (Common_BtnPress(BTN_ACTION1))
            {
                bool bypass;
    
                result = dsplowpass->getBypass(&bypass);
    
                bypass = !bypass;
    
                result = dsplowpass->setBypass(bypass);
            }
    
            if (Common_BtnPress(BTN_ACTION2))
            {
                bool bypass;
    
                result = dsphighpass->getBypass(&bypass);
    
                bypass = !bypass;
    
                result = dsphighpass->setBypass(bypass);
            }
    
            if (Common_BtnPress(BTN_ACTION3))
            {
                bool bypass;
    
                result = dspecho->getBypass(&bypass);
    
                bypass = !bypass;
    
                result = dspecho->setBypass(bypass);
            }
    
            if (Common_BtnPress(BTN_ACTION4))
            {
                bool bypass;
    
                result = dspflange->getBypass(&bypass);
    
                bypass = !bypass;
    
                result = dspflange->setBypass(bypass);
            }
    
            result = system->update();
    
            // 线程休眠时间
            Common_Sleep(50);
        } while (!Common_BtnPress(BTN_QUIT));
    
        /*
            Shut down
            关闭移除数字信号处理器
        */
        result = mastergroup->removeDSP(dsplowpass);
        result = mastergroup->removeDSP(dsphighpass);
        result = mastergroup->removeDSP(dspecho);
        result = mastergroup->removeDSP(dspflange);
        
        // 释放资源
        result = dsplowpass->release();
        result = dsphighpass->release();
        result = dspecho->release();
        result = dspflange->release();
    
        result = sound->release();
        result = system->close();
        result = system->release();
    
        Common_Close();
    
        return 0;
    }
    

    实现变声效果

    描述声音特性的三个要素,包括响度、音色、音调。响度由振幅决定,音色由声波的波形决定,音调由频率决定。这里的变声效果有几种,原理也是对音频的不同属性做处理,当然,fmod对音效处理的类型做的更丰富。
    1、萝莉:女高音,提高声音的频率
    2、大叔:男低音,降低声音的频率
    3、惊悚:声音添加了颤抖的效果
    4、搞怪:加快了声音的播放速度 frequency
    5、空灵:做了回声 echo 的音效

    这里先用手机录制了一段声音文件 dream.m4a,复制到 assets 目录下,那么这段音频的路径就是

    file:///android_asset/dream.m4a
    

    这段音频数据是由二进制十六进制组成,那么我们要对这些数据进行处理?那得多复杂。我们可以借助于 fmod 来实现。以下是处理变声的代码

    #include <jni.h>
    #include <string>
    #include <unistd.h>
    #include <android/log.h>
    #include "inc/fmod.hpp"
    
    #define  LOG_TAG    "lex"
    #define LOGI(FORMAT, ...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,FORMAT,__VA_ARGS__)
    #define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,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_org_fmod_example_NdkUtil_play(JNIEnv *env, jclass type, jint mode) {
    
        // 播放声音、处理变声所需要的一些变量
        System *system;
        Sound *sound;
        Channel *channel;
        DSP *dsp;
        bool isPlaying = true;
        float frequency = 0F;
        // 播放文件的路径
        const char *path = "file:///android_asset/dream.m4a";
    
        // 初始化
        System_Create(&system);
        system->init(32, FMOD_INIT_NORMAL, NULL);
    
        // 创建声音
        system->createSound(path, FMOD_DEFAULT, NULL, &sound);
    
        switch (mode) {
            case MODE_NORMAL:
                // 原声播放
                system->playSound(sound, 0, false, &channel);
                break;
            case MODE_LUOLI:
                // 萝莉音效
                // FMOD_DSP_TYPE_PITCHSHIFT,提高或者降低音调的类型
                system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
                // 设置音调的参数
                dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 2.5);
    
                system->playSound(sound, 0, false, &channel);
                // 添加到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.5);
    
                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 * 1.5F;
                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;
            default:
                channel = NULL;
                break;
        }
    
        system->update();
    
        // 单位是微秒
        while (isPlaying && channel != NULL) {
            channel->isPlaying(&isPlaying);
            usleep(100 * 1000);
        }
        goto end;
    
        // 释放资源
        end:
        sound->release();
        system->close();
        system->release();
    }
    

    源码已经上传至 GitHub

    体会

    也是参考了很多资料来实现这个效果。官网文档也并不会很清晰的告诉你如何使用,甚至很多时候需要去推断。

    C/C++开源的世界真是非常的庞大,拥有着非常多优秀算法的开源项目,fmod 就是其中之一。通常我们做Android使用到网络上的开源库比较多的是UI、网络、数据库、架构之类的。对于算法类的确实并不多。利用NDK进入C/C++犹如打开了另一扇大门,继续探索前进。


    感谢

    Android NDK开发之旅27 使用fmod模仿QQ变声特效
    仿QQ语音变声功能实现

    相关文章

      网友评论

          本文标题:Android NDK的使用实例——使用fmod模仿QQ变声

          本文链接:https://www.haomeiwen.com/subject/rxjkgxtx.html