新建一个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