FMOD的集成与运用

集成第三方 so库的步骤

  • 1、到官网 下载对应的库文件
  • 2、找到对应cpu架构的 库文件
  • 3、把头文件、库文件放到项目对应目录里边
  • 4、在gradle(指定CPU架构,Cmake CPU环境变量值)、cmake(第三方库文件链接到项目库文件里边)
  • 5、在java、jni里边运用第三方库的api

记住了:Android便是linux的二次封装,所以假如要移植linux的 库到手机端,库是通用的。直接把 .so 库放到项目对应目录里边就好

------------------手机cpu架构指令集----------------------------------
arm64-v8a		实体手机 基本上便是 arm64-v8a
armeabi-v7a
armeabi
x86				模拟器,WindowsPhone(已经关闭)

FMOD 简介

fmod 是一个音效引擎库 www.fmod.com

游戏引擎 cocos2d unity3d 等 都是默许集成了 fmod来做音效, 由于 fmod是音效引擎库(C库)

FMOD 的运用类似于你玩 调音师软件的流程,只是调音师能够直接运用,而FMOD经过api,让你能实现类似于调音师的功用 开端调音 pitch 腔调调理 默许:1.0 2.0萝莉 0.8大叔 0.5老头 tremolo 颤腔调理 默许:5 20.0明显颤抖了

模拟信号的声响是无法保存到电脑里边的,所以科学家就把 模拟信号转换成了数字信号,以便对数字信号的声响进行存储和处理(DSP-digital signal process 数字信号处理)

开端集成

整体流程概括

  1. 资源文件集成
  2. CMakeLists.txt 配置文件修正
    1. 导入头文件
    2. 导入库文件(C++的环境变量)
    3. 链接第三方库 到 咱们自己的库(libvoicechange.so)里边
  3. app/build.gradle 配置文件修正
    1. 指定CPU架构 Cmake中的本地库,例如:libnative-lib.so libvoicechange.so
    2. 指定CPU的架构 apk/lib/渠道
    3. 引进 app下 libs文件夹里边的 fmod.jar库

首先把对应的文件集成到项目目录里

Android音频库的集成与使用

修正 CMakeLists.txt

cmake_minimum_required(VERSION 3.22.1) # 表明最低支撑的cmake版本
project("voicechange")
# TODO 第一步:导入头文件
include_directories(inc) # CMakeLists.txt 目录下的inc文件夹 放头文件
# TODO 第二步:导入库文件(C++的环境变量)- 最新的办法,以前是 add_xxx(已淘汰)6.0以下才能够
# 环境变量的添加是追加的办法的:%JAVA_HOME%;%ANDROID_HOME%;path1;path2;
# CMAKE_SOURCE_DIR:取得当时CmakeLists.txt的途径
# CMAKE_ANDROID_ARCH_ABI:主动获取 四大渠道架构值  也能够在gradle里边进行指定
# 这个set的写法就相当于  path = path + CMAKE_CXX_FLAGS;
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")
# 批量导入 一切的C++文件
#file(GLOB allCPP *.c *.h *.cpp)
# TODO 库名的规则: lib + 称号 + .so   (lib .so 体系主动拼接的)
add_library( # Sets the name of the library.
        voicechange  #libvoicechange.so
        SHARED #表明动态库    STATIC 表明静态库
        native-lib.cpp
        # ${allCPP}  # 批量导入
        )
# 相当于 var log-lib = log (liblog.so 库的途径)
find_library( # Sets the name of the path variable.
        m-lib # 变量名
        # Specifies the name of the NDK library that
        # you want CMake to locate.
        # 这里就相当于 定位到ndk的 liblog.so 库的途径
        log  #查找NDK的日志打印库
        )
# TODO 第三步:链接第三方库 到 咱们自己的库(libvoicechange.so)里边
target_link_libraries( # Specifies the target library.
        voicechange
        # Links the target library to the log library
        # included in the NDK.
        ${m-lib} # 链接m-lib库 到 咱们自己的库(libvoicechange.so)里边
        fmod
        fmodL
        )

修正 app/build.gradle

android {
    ...
    defaultConfig {
        ...
        // TODO 第四步:指定CPU架构 Cmake中的本地库,例如:libnative-lib.so libvoicechange.so
        externalNativeBuild{
            cmake{
                // cppFlags "" // 这样写,默许是支撑四大渠道
                // 指定CPU架构是armeabi-v7a、x86
                // 指定CMakeLists.txt里边的环境变量CMAKE_ANDROID_ARCH_ABI的值
                // 【留意:这里只指定本地库到armeabi-v7a、x86】 这句代码 还不能决议apk生成
                abiFilters ("armeabi-v7a", "x86")
            }
            // TODO 第五步:指定CPU的架构  apk/lib/渠道
            // 下面代码不写,默许是四大CPU架构渠道
            ndk{
                // 指定CPU架构是armeabi-v7a、x86
                // 【留意:这里只指定编译一切库到armeabi-v7a、x86进apk】
                abiFilters ("armeabi-v7a", "x86")
            }
        }
    }
    ...
    externalNativeBuild {
        cmake {
            path file('src/main/cpp/CMakeLists.txt')
            version '3.22.1'
        }
    }
    ...
}
dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    ...
    // 引进 app下 libs文件夹里边的 fmod.jar库
    implementation files('libs/fmod.jar')
    ...
}

FMOD api的运用

#include <jni.h>
#include <string>
// xxx.h: C的头文件
// xxx.hpp:C++的头文件
// xxx.h ----- xxx.c  一般对应的早期版本
// xxx.hpp -----xxx.cpp  C++ NDK  今后基本上 C++多
// 声明 c的头文件,cpp的实现,这样的组合也是能够的。由于C++是兼容C的
#include <fmod.hpp>
#include <unistd.h>
using namespace FMOD;
#include "common_log.h"
#undef TYPE_YUANSHENG
#define TYPE_YUANSHENG 0
#undef TYPE_DASHU
#define TYPE_DASHU 1
#undef TYPE_LUOLI
#define TYPE_LUOLI 2
#undef TYPE_JINGSONG
#define TYPE_JINGSONG 3
#undef TYPE_GAOGUAI
#define TYPE_GAOGUAI 4
#undef TYPE_KONGLING
#define TYPE_KONGLING 5
extern "C"
JNIEXPORT void JNICALL
Java_com_hehe_voicechange_MainActivity_fmodPlaySound(JNIEnv *env, jobject thiz, jint type, jstring path) {
    const char * _path = env->GetStringUTFChars(path, NULL);
    // 声明指针
    System * system;
    Sound * sound;
    Channel * channel;
    DSP * dsp;
    // TODO 第一步 创立体系
    System_Create(&system);
    // TODO 第二步 体系的初始化 
	//参1:最大音轨数,参2:体系初始化标记,参3:额定数据
    system->init(32, FMOD_INIT_NORMAL, 0);
    // TODO 第三步 创立声响 
	//参1:途径,参2:声响的初始化标记,参3:额定数据,参4:声响指针
    system->createSound(_path, FMOD_DEFAULT, 0, &sound);
    // TODO 第四步 播映声响  音轨  声响
	//参1:声响,参2:分组音轨,参3:操控,参4:通道
    system->playSound(sound, 0, false, &channel);
    // TODO 第五步 添加特效
    //pitch   腔调调理  默许:1.0  2.0萝莉  0.8大叔  0.5老头
    //tremolo 颤腔调理  默许:5    20.0明显颤抖了
    //ECHO    回音
    jstring content = nullptr;
    float frequency = 1.0f;
    switch (type) {
        case TYPE_DASHU:
            content = env->NewStringUTF("大叔音效播映完结");
            // 1、创立数字信号处理对象
            system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
            // 2、设置腔调值 0.8
            dsp->setParameterFloat(0, 0.8f);
            // 3、把音效添加到音轨里边去
            channel->addDSP(0, dsp); 
            break;
        case TYPE_LUOLI:
            content = env->NewStringUTF("萝莉音效播映完结");
            system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
            dsp->setParameterFloat(0, 2.0f);
            channel->addDSP(0, dsp);
            break;
        case TYPE_GAOGUAI:
            // 搞怪小黄人音效,便是把音轨的频率调理了快慢
            content = env->NewStringUTF("搞怪音效播映完结");
            channel->getFrequency(&frequency); //获取当时帧率
            channel->setFrequency(frequency*1.5f);// 修正帧率
            break;
        case TYPE_KONGLING:
            system->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);
            dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 200);// 回音延时 默许500
            dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 10);// 回音衰减度 默许50
            channel->addDSP(0, dsp);
            break;
        case TYPE_JINGSONG:
            // ECHO    回音
            system->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);
            dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 300);// 回音延时 默许500
            dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 10);// 回音衰减度 默许50
            channel->addDSP(0, dsp); //音轨一
            // pitch   腔调调理  默许:1.0  2.0萝莉  0.8大叔  0.5老头
            system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
            dsp->setParameterFloat(0, 0.8f);
            channel->addDSP(1, dsp);//音轨二
            // tremolo 颤腔调理  默许:5    20.0明显颤抖了
            system->createDSPByType(FMOD_DSP_TYPE_TREMOLO, &dsp);
            dsp->setParameterFloat(0, 20.0f);
            channel->addDSP(2, dsp);//音轨三
    }
    bool isPlaying = 1;
    while (isPlaying){
        channel->isPlaying(&isPlaying);
        usleep(1000*30); // 休眠30毫秒
    }
    printf("播映结束");
    if (content){ // 通知java办法
        jclass mainCls = env->GetObjectClass(thiz);
        jmethodID mid = env->GetMethodID(mainCls, "soundEnd", "(Ljava/lang/String;)V");
        env->CallVoidMethod(thiz, mid, content);
    }
    // 好习惯:开释资源
    dsp->release();
    sound->release();
    system->close();
    system->release();
    env->ReleaseStringUTFChars(path, _path);
}