Android利用Fmod仿QQ变声音效

作者: 小小混世魔王 | 来源:发表于2019-05-07 15:16 被阅读42次

    看到QQ一些变声音效,这些声音效果可以采用SoundTouch,Fmod去处理。这篇文章我们用Fmod去实现变声音效的处理。fmod官网https://www.fmod.com/,fmod Ex 声音系统是为游戏提供音频引擎。多路输出,多路输入,支持类型广。
    在使用之前了解一些关于声音的概念,一般声音的采样率是44.1khz,就是每秒钟采集44100个sample,一个sample(采样解析度)有8-bit ,16-bit(声音品质好)。声道分为单声道和立体声(多声道),硬件的扬声器只能播放PCM数据,通过对音频的解码(解压缩过程)获取到音频解码数据输送到扬声器播放。
    下面我们就来看看Fmod的使用。下载Fmod https://www.fmod.com/download选择Android,解压出来的文件夹如下

    fmod_dir.png 我们要用到core下的东西,将core文件下的inc与lib文件拷贝到项目中。 fmod_引入.png
    CMakeLists.txt的配置
    include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)
    
    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/native-lib.cpp)
    
    
    add_library(libfmod
            SHARED
            IMPORTED)
    
    set_target_properties(libfmod
            PROPERTIES
            IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libfmod.so)
    
    
    find_library( # Sets the name of the path variable.
            log-lib
            log)
    
    target_link_libraries( # Specifies the target library.
            native-lib libfmod
            ${log-lib})
    

    build.gradle配置

    android {
         ..........
        defaultConfig {
           ..........
            externalNativeBuild {
                cmake {
                    cppFlags "-fexceptions"
                    abiFilters "armeabi","x86","x86_64"
                }
            }
        }
        ..........
        externalNativeBuild {
            cmake {
                path "CMakeLists.txt"
            }
        }
        sourceSets {
            main {
                jniLibs.srcDirs = ['src/main/jniLibs']
            }
        }
    }
    
    dependencies {
        ..........
        implementation files('libs/fmod.jar')
    }
    
    

    fmod使用前调用 org.fmod.FMOD.init(this);进行FMOD的初始化

    /**
     * 播放声音
     */
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_youyangbo_fmod_FmodUtils_play(JNIEnv *env, jclass type, jstring path_) {
        const char *path = env->GetStringUTFChars(path_, 0);
        //创建对象
        System *system;
        System_Create(&system);
    
        //初始化
        void *extradriverdata;
        system->init(32, FMOD_INIT_NORMAL, extradriverdata);
    
        //創建一個聲音
        Sound *sound;
        system->createSound(path, FMOD_DEFAULT, 0, &sound);
    
        //播放声音
        Channel *channel = 0;
        system->playSound(sound, 0, false, &channel);
        system->update();
        //是否播放
        bool isplaying = true;
        //阻塞线程
        while (isplaying) {
            channel->isPlaying(&isplaying);
            usleep(1000*1000);
        }
        //释放资源
        sound->release();
        system->close();
        system->release();
        env->ReleaseStringUTFChars(path_, path);
    }
    }
    

    到这里我们就可以简单的播放一个声音啦。system->playSound(sound, 0, false, &channel);可以看到我们在播放声音时传了一个Channel这里并没有进行赋值, 在playSound()函数里面会给选择一个闲置的Channel用来播放声音。我们保存Channel就是为了后面对声音的操作。
    怎样利用Fmod进行变声,Fmod提供了很多DSP(数字信号处理的一个单元)类型,我们根据不同的类型创建不同的DSP,然后把我们创建好的DSP添加到Channel(音轨)中达到对声音的改变。

    MainActivity.java
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
        static {
    
            try {
                System.loadLibrary("fmodD");
            } catch (UnsatisfiedLinkError e) {
            }
            try {
                System.loadLibrary("fmod");
            } catch (UnsatisfiedLinkError e) {
            }
            System.loadLibrary("native-lib");
    
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            findViewById(R.id.tv_original).setOnClickListener(this);
            findViewById(R.id.tv_lolita).setOnClickListener(this);
            findViewById(R.id.tv_dashu).setOnClickListener(this);
            findViewById(R.id.tv_thriller).setOnClickListener(this);
            findViewById(R.id.tv_gaoguai).setOnClickListener(this);
            findViewById(R.id.tv_kong_ling).setOnClickListener(this);
            findViewById(R.id.tv_mantuntun).setOnClickListener(this);
    
            org.fmod.FMOD.init(this);
    
        }
        public static final String  path = "file:///android_asset/el.mp3";
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.tv_original:
                    FmodUtils.play(path);
                    break;
                case R.id.tv_lolita:
                    FmodUtils.changeOfVoice(path, FmodUtils.ChangeOfVoice.valueOf("LOLITA").getType());
                    break;
                case R.id.tv_dashu:
                    FmodUtils.changeOfVoice(path, FmodUtils.ChangeOfVoice.valueOf("DASHU").getType());
                    break;
                case R.id.tv_thriller:
    
                    FmodUtils.changeOfVoice(path, FmodUtils.ChangeOfVoice.valueOf("THRILLER").getType());
                    break;
    
                case R.id.tv_gaoguai:
                    FmodUtils.changeOfVoice(path, FmodUtils.ChangeOfVoice.valueOf("GAOGUAI").getType());
                    break;
                case R.id.tv_kong_ling:
                    FmodUtils.changeOfVoice(path, FmodUtils.ChangeOfVoice.valueOf("KONG_LING").getType());
                    break;
                case R.id.tv_mantuntun:
                    FmodUtils.changeOfVoice(path, FmodUtils.ChangeOfVoice.valueOf("MANTUNTUN").getType());
                    break;
            }
        }
    
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            //同步FMOD的生命周期
            org.fmod.FMOD.close();
        }
    }
    
    
    FmodUtils.java
    public class FmodUtils {
    
        enum ChangeOfVoice
        {
            ORIGINAL(0),  //原声
            LOLITA(1), //萝莉
            DASHU(2),  //大叔
            THRILLER(3), //惊悚
            GAOGUAI(4),  //搞怪
            KONG_LING(5), //空灵
            MANTUNTUN(6); //满吞吞
    
            int type;
            ChangeOfVoice(int i) {
                type = i;
            }
    
            public int getType() {
                return type;
            }
        }
    
        public static native void play(String path);
    
        public static native void changeOfVoice(String path, int original);
    }
    
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_youyangbo_fmod_FmodUtils_changeOfVoice(JNIEnv *env, jclass type, jstring path_,
                                                    jint effect_type) {
        const char *path = env->GetStringUTFChars(path_, 0);
        //创建对象
        System *system;
        System_Create(&system);
    
        //初始化
        void *extradriverdata;
        system->init(32, FMOD_INIT_NORMAL, extradriverdata);
    
        //創建一個聲音
        Sound *sound;
        system->createSound(path, FMOD_DEFAULT, 0, &sound);
    
        // FMOD_RESULT F_API createDSPByType         (FMOD_DSP_TYPE type, DSP **dsp);
        DSP *dsp;
    
        //播放声音
        Channel *channel = 0;
        system->playSound(sound, 0, false, &channel);
        switch (effect_type) {
            case ORIGINAL:
    
                break;
            case LOLITA:
                //  FMOD_DSP_TYPE_PITCHSHIFT 对音频提高八度 达到萝莉效果
                system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
                dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 2.0);
                channel->addDSP(0, dsp);
                break;
            case DASHU:
                //降低八度
                system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
                dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.5);
                channel->addDSP(0, dsp);
                break;
            case THRILLER:
                //声音抖动
                system->createDSPByType(FMOD_DSP_TYPE_TREMOLO, &dsp);
                dsp->setParameterFloat(FMOD_DSP_TREMOLO_SKEW, 0.5);
                channel->addDSP(0, dsp);
    
                break;
            case GAOGUAI:
                //加快声音的播放速度
                float frequency;
                channel->getFrequency(&frequency);
                channel->setFrequency(frequency*1.5);
                break;
            case KONG_LING:
                //添加回声
                system->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);
                //声音延迟
                dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 300);
                //回声次数
                dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 3);
                channel->addDSP(0, dsp);
    
                break;
            case MANTUNTUN:
                float frequency_m;
                channel->getFrequency(&frequency_m);
                channel->setFrequency(frequency_m*0.8);
                break;
    
        }
    
        system->update();
        //是否播放
        bool isplaying = true;
        //阻塞线程
        while (isplaying) {
            channel->isPlaying(&isplaying);
            usleep(1000*1000);
        }
        //释放资源
        sound->release();
        system->close();
        system->release();
        env->ReleaseStringUTFChars(path_, path);
    }
    

    介绍一下关于DSP的类型

    typedef enum
    {
        FMOD_DSP_TYPE_UNKNOWN, // 不知类型
        FMOD_DSP_TYPE_MIXER,   //不需要任何输入,并将它们混合在一起
        FMOD_DSP_TYPE_OSCILLATOR,   //振荡器 
        FMOD_DSP_TYPE_LOWPASS,    //低通滤波
        FMOD_DSP_TYPE_ITLOWPASS,   //特低通滤波
        FMOD_DSP_TYPE_HIGHPASS, //高通滤波
        FMOD_DSP_TYPE_ECHO,    //回声
        FMOD_DSP_TYPE_FADER,   //衰减器
        FMOD_DSP_TYPE_FLANGE,   //轮缘  对声音产生法兰效应
        FMOD_DSP_TYPE_DISTORTION, //扭曲
        FMOD_DSP_TYPE_NORMALIZE,   //归一化
        FMOD_DSP_TYPE_LIMITER,    //限幅器
        FMOD_DSP_TYPE_PARAMEQ,   //参数化
        FMOD_DSP_TYPE_PITCHSHIFT,   //频移
        FMOD_DSP_TYPE_CHORUS,     //合唱
        FMOD_DSP_TYPE_VSTPLUGIN,
        FMOD_DSP_TYPE_WINAMPPLUGIN, //温普拉普林
        FMOD_DSP_TYPE_ITECHO,    //特回声
        FMOD_DSP_TYPE_COMPRESSOR,    //压缩机
        FMOD_DSP_TYPE_SFXREVERB,   //SFX混响器
        FMOD_DSP_TYPE_LOWPASS_SIMPLE,  //低通简单
        FMOD_DSP_TYPE_DELAY,     //延迟
        FMOD_DSP_TYPE_TREMOLO,    //颤音
        FMOD_DSP_TYPE_LADSPAPLUGIN,    //拉斯帕拉金/
        FMOD_DSP_TYPE_SEND, 
        FMOD_DSP_TYPE_RETURN,
        FMOD_DSP_TYPE_HIGHPASS_SIMPLE,//高通简单
        FMOD_DSP_TYPE_PAN,
        FMOD_DSP_TYPE_THREE_EQ,   //三重均衡器
        FMOD_DSP_TYPE_FFT,     //快速傅立叶变换
        FMOD_DSP_TYPE_LOUDNESS_METER,   //劳氏流量计
        FMOD_DSP_TYPE_ENVELOPEFOLLOWER,
        FMOD_DSP_TYPE_CONVOLUTIONREVERB,   //卷积混响
        FMOD_DSP_TYPE_CHANNELMIX,    //通道混合
        FMOD_DSP_TYPE_TRANSCEIVER,
        FMOD_DSP_TYPE_OBJECTPAN,
        FMOD_DSP_TYPE_MULTIBAND_EQ,   //多频带EQ
    
        FMOD_DSP_TYPE_MAX,
        FMOD_DSP_TYPE_FORCEINT = 65536    /* Makes sure this enum is signed 32bit. */
    } FMOD_DSP_TYPE;
    

    关于对音频音效的处理上面已经实现QQ变音效果。音频这块的东西后面会在音视频文中具体涉及到,包括音频的编解码,OpenSL ES 音频播放。

    相关文章

      网友评论

        本文标题:Android利用Fmod仿QQ变声音效

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