Android音频FMOD库的集成与使用

2023年 8月 15日 61.5k 0

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 数字信号处理)

开始集成

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

图片.png

修改 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 libderryku.so
        externalNativeBuild{
            cmake{
                // cppFlags "" // 这样写,默认是支持四大平台

                // 指定CPU架构是armeabi-v7a
                // 指定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 
#include 

// xxx.h: C的头文件
// xxx.hpp:C++的头文件
// xxx.h ----- xxx.c  一般对应的早期版本
// xxx.hpp -----xxx.cpp  C++ NDK  以后基本上 C++多
// 声明 c的头文件,cpp的实现,这样的组合也是可以的。因为C++是兼容C的
#include 
#include 

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);
}

相关文章

服务器端口转发,带你了解服务器端口转发
服务器开放端口,服务器开放端口的步骤
产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
如何使用 WinGet 下载 Microsoft Store 应用
百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

发布评论