Android利用Fmod仿QQ变声音效

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

我们要用到core下的东西,将core文件下的inclib文件拷贝到项目中。

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开发资料+面试架构资料 免费分享 点击链接 即可领取

《Android架构师必备学习资源免费领取(架构视频+面试专题文档+学习笔记)》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值