使用fmod实现变声功能

新建一个ndk项目,将下载的fmod库添加到相应目录:


image.png

1.在app模块的build.gradle,配置implementation fileTree(dir: 'libs', include: ['*.jar']),使它生效
2.把对应的fmod库放在jniLibs下,在CMakeLists.txt,修改CMAKE_CXX_FLAGS

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")

使这些库能被我们自己的库链接到
3.把头文件路径包含进来

include_directories(${CMAKE_SOURCE_DIR}/inc)

完整的CMakeLists.txt文件如下:

cmake_minimum_required(VERSION 3.22.1)
# Declares and names the project.
project("cmake")
set(CMAKE_VERBOSE_MAKEFILE on)
set(BUILD_SHARED_LIBS on)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")
include_directories(${CMAKE_SOURCE_DIR}/inc)
add_library( # Sets the name of the library.
        voice
        # Sets the library as a shared library.
        SHARED
        # Provides a relative path to your source file(s).
        native-lib.cpp)
find_library( # Sets the name of the path variable.
        log-lib
        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)
target_link_libraries( # Specifies the target library.
        voice
        fmod
        fmodL
        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})
image.png

同时这里abiFilters指定的cpu架构要对应
布局文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/sample_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <LinearLayout
        android:layout_width="match_parent"
        android:orientation="vertical"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_height="wrap_content">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="原声"
            android:onClick="onClick"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
        
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="萝莉"
            android:onClick="onClick"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="惊悚"
            android:onClick="onClick"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="大叔"
            android:onClick="onClick"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="搞怪"
            android:onClick="onClick"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="空灵"
            android:onClick="onClick"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity文件如下:

package com.example.cmake
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.example.cmake.databinding.ActivityMainBinding
import org.fmod.FMOD

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.sampleText.text = stringFromJNI()
        FMOD.init(this)  // 这里要初始化
    }

    fun onClick(view: View) {
        val t = view as TextView
        val text = t.text.toString()
        var flag = 0
        //变音
        when (text) {
            "萝莉" -> flag = 1
            "惊悚" -> flag = 2
            "大叔" -> flag = 3
            "搞怪" -> flag = 4
            "空灵" -> flag = 5
        }
        Thread { playSound(flag) }.start()
    }

    override fun onDestroy() {
        super.onDestroy()
        FMOD.close()  //onDestroy时关闭
    }

    private external fun playSound(flag: Int)

    /**
     * A native method that is implemented by the 'cmake' native library,
     * which is packaged with this application.
     */
    private external fun stringFromJNI(): String

    companion object {
        init {
            System.loadLibrary("voice")
        }
    }
}

录一个音频,放到assets下


image.png

然后在cpp文件里实现native的playSound函数:

#include <jni.h>
#include <string>
#include "fmod.hpp"
#include <android/log.h>
#include <unistd.h>
#define TAG "TEST"
#define LOGE(format, ...) __android_log_print(ANDROID_LOG_ERROR,TAG,format,##__VA_ARGS__)

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_cmake_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

void FMOD_Main(int flag) {
    FMOD::System *system = 0;
    FMOD::Sound *sound = 0;
    FMOD::Channel *channel = 0;
    FMOD::ChannelGroup *mastergroup = 0;
    FMOD::DSP *dsp = 0;
    /*
       Create a System object and initialize
   */
    System_Create(&system);
    system->init(32, FMOD_INIT_NORMAL, NULL);
    system->getMasterChannelGroup(&mastergroup);
    system->createSound("file:///android_asset/me.wav", FMOD_DEFAULT, 0, &sound);
    system->playSound(sound, 0, false, &channel);
    switch (flag) {
        //萝莉
        case 1:
            //创建音频处理DSP 类型 为音频提高
            system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
            //设置dsp大小
            dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 2.0);
            //添加一个处理单元到通道
            channel->addDSP(0, dsp);
            break;
            //惊悚
        case 2:
            //创建音频处理DSP 类型 为颤抖
            system->createDSPByType(FMOD_DSP_TYPE_TREMOLO, &dsp);
            //设置低频震旦器频率
            dsp->setParameterFloat(FMOD_DSP_TREMOLO_FREQUENCY, 20);
            //设置声音歪斜
            dsp->setParameterFloat(FMOD_DSP_TREMOLO_SKEW, 0.5);
            //添加一个处理单元到通道
            channel->addDSP(0, dsp);
            break;
            //大叔
        case 3:
            //创建音频处理DSP 类型 为音频提高
            system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
            //设置dsp大小
            dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.5);
            //添加一个处理单元到通道
            channel->addDSP(0, dsp);
            break;
            //搞怪
        case 4: {
            float mfrequency = 0;
            //得到频率
            channel->getFrequency(&mfrequency);
            channel->setFrequency(mfrequency * 2);
            break;
        }
            //空灵
        case 5 : {
            //创建音频处理DSP 类型 为音频提高
            system->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);
            //设置dsp大小  延迟声
            dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 300);
            //回音音量
            dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 20);
            //添加一个处理单元到通道
            channel->addDSP(0, dsp);
            break;
        }
        default: {

        }
    }
    system->update();
    unsigned int duration = 0;
    //得到声音
    sound->getLength(&duration, FMOD_TIMEUNIT_MS);
    //线程休眠 以微秒所以要转
    usleep(duration * 1000);
    sound->release();
    system->close();
    system->release();
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_cmake_MainActivity_playSound(JNIEnv *env, jobject thiz, jint flag) {
    FMOD_Main(flag);
}

运行之后,查看效果。
完整代码:https://gitee.com/sunshuo1092373331/fmod.git

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容