Android应用开发那些事Android ndk 开发优秀案例

Android利用Fmod仿QQ变声音效

2019-05-07  本文已影响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 音频播放。

上一篇下一篇

猜你喜欢

热点阅读