Android NDK 导入 C库,开发流程,以导入fmod库为例,简单实现变声器效果
image.png
1、导入fmod
导入fmod头文件、so库、jar
image.png
2、配置Cmake文件
#cmake_minimum_required(VERSION 3.4.1) # 最低支持Cmake版本
cmake_minimum_required(VERSION 3.10.2)# 最低支持Cmake版本
# Declares and names the project.
project("jni_02")
#NDK集成C库流程
#1、导入C库头文件 inc
include_directories("inc")
#2、配置总库,也就是最终打包到APK里的库
file(GLOB allCPP *.c *.h *.cpp)
add_library(
native-lib # Sets the name of the library 给最终打包到APK的C库起名称 native-lib
SHARED # Sets the library as a shared library.//设置库为动态共享库
${allCPP} # Provides a relative path to your source file(s).//导入所有源文件
)
#3、导入库文件 .so ,CMAKE_ANDROID_ARCH_ABI会自动匹配当前的CPU架构
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")
#这种方式是一个个添加
#add_library( # Sets the name of the library.
# jni_01
# # Sets the library as a shared library.
# SHARED
# # Provides a relative path to your source file(s).
# native-lib.cpp extern-lib.cpp)
#4、链接具体的库,到我们的 libnative-lib.so总库
target_link_libraries(
native-lib
#${log-varAAAA}
log # 自动寻找 # 具体的库 链接到 libnative-lib.so里面去
fmod # 具体的库 链接到 libnative-lib.so里面去
fmodL # 具体的库 链接到 libnative-lib.so里面去
)
3、 配置gradle的cpu架构
externalNativeBuild {
cmake {
cppFlags "" // 默认五大平台
// 指定CPU架构,Cmake的本地库, 例如:native-lib ---> armeabi-v7a
// abiFilters "armeabi-v7a"
}
}
//
// // 指定CPU架构,打入APK lib/CPU平台
// ndk {
// abiFilters "armeabi-v7a"
// }
-
关于cpu架构
CPU架构模式:
以前七个平台
arm64-v8a 如果你现在去买 华为 小米
armeabi-v7a 2015 小米4
armeabi 2020 ~ 2011 就快没有了,越来越少了
x86 x86_64 模拟器 ,WindowPhone如果知道自己手机的 CPU架构模式:
adb shell getprop ro.product.cpu.abi
armeabi-v7a
4、例子
以上环境配置完成,即可调用C库完成简单的变声器
(1)生成头文件
生成我们自己的头文件,并引入fmod的头文件,fmod.hpp。javah 包名即可生成头文件,并且会把Java中的常量对应生成C中的宏
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <fmod.hpp> // TODO 最后一步 FMOD的头文件,必须导入,才能使用功能
#include <string>
/* Header for class com_it_jni_02_MainActivity */
#ifndef _Included_com_it_jni_02_MainActivity
#define _Included_com_it_jni_02_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
#undef com_it_jni_02_MainActivity_MODE_LUOLI
#define com_it_jni_02_MainActivity_MODE_LUOLI 1L
#undef com_it_jni_02_MainActivity_MODE_DASHU
#define com_it_jni_02_MainActivity_MODE_DASHU 2L
/*
* Class: com_it_jni_02_MainActivity
* Method: playNative
* Signature: (I)V
*/
JNIEXPORT void JNICALL
Java_com_it_jni_102_MainActivity_playNative
(JNIEnv
*, jobject, jint,jstring);
#ifdef __cplusplus
}
#endif
#endif
(2)编写cpp代码
#include <jni.h>
#include <string>
#include <unistd.h>
#include "com_it_jni_02_MainActivity.h"
using namespace FMOD; // fmod的命名空间
extern "C"
JNIEXPORT void JNICALL
/**
*
* @param env
* @param thiz
* @param playType 播放类型
* @param path 播放路径
*/
Java_com_it_jni_102_MainActivity_playNative(JNIEnv *env, jobject thiz, jint playType,
jstring path) {
char *content = "播放完毕啦";
//播放路径
const char *playPath = env->GetStringUTFChars(path, NULL);
//音效系统指针
System *system = 0;
//声音
Sound *sound = 0;
//通道
Channel *channel = 0;
//DSP:digital signal process == 数字信号处理 指针
DSP *dsp = 0;
//创建系统
System_Create(&system);
//初始化最大音轨数
system->init(32, FMOD_INIT_NORMAL, 0);
//创建声音,传入路径 playPath
system->createSound(playPath, FMOD_DEFAULT, 0, &sound);
//播放声音
system->playSound(sound, 0, false, &channel);
switch (playType) {
//萝莉声音
case com_it_jni_02_MainActivity_MODE_LUOLI:
//1、创建dsp
system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
//2、设置Pitch音调调节2.0
dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 2.0f);
//3、添加音效到音轨
channel->addDSP(0, dsp);
content = "萝莉播放完成";
break;
//大叔声音
case com_it_jni_02_MainActivity_MODE_DASHU:
//1、创建dsp
system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
//2、设置Pitch音调调节2.0
dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.5f);
//3、添加音效到音轨
channel->addDSP(0, dsp);
content = "大叔播放完成";
break;
}
bool isPlaying = true;
//等待播放完毕
while (isPlaying) {
channel->isPlaying(&isPlaying);
usleep(1000 * 1000);
}
//JNI中使用 记得回收
sound->release();
system->close();
system->release();
env->ReleaseStringUTFChars(path, playPath);
//C++调用Java 通知播放完成
//获取jclass
jclass j_class = env->GetObjectClass(thiz);
jmethodID jmethod_Id = env->GetMethodID(j_class, "playFinish", "(Ljava/lang/String;)V");
jstring j_string = env->NewStringUTF(content);
env->CallVoidMethod(thiz, jmethod_Id, j_string);
}
(3)编写Java代码
package com.it.jni_02;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.it.jni_02.databinding.ActivityMainBinding;
import org.fmod.FMOD;
public class MainActivity extends AppCompatActivity {
//萝莉声音
private static final int MODE_LUOLI = 1; //
//大叔声音
private static final int MODE_DASHU = 2; //
private static final String path = "file:///android_asset/daxiaa.m4a";
private static final String TAG = "MainActivity";
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
FMOD.init(this);
binding.btnLuoli.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
playNative(MODE_LUOLI, path);
}
});
binding.btnDashu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
playNative(MODE_DASHU, path);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
FMOD.close();
}
/**
* c++回调java 播放完成
*
* @param msg
*/
void playFinish(String msg) {
Log.d(TAG, "playFinish: " + msg);
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}
/**
* 调用native函数,播放声音
*
* @param playType
*/
native void playNative(int playType, String path);
}
网友评论