细说JNI与NDK(三)ndk 配置说明

细说JNI与NDK专题目录:

细说JNI与NDK(一) 初体验
细说JNI与NDK(二)基本操作)
细说JNI与NDK(三)ndk 配置说明
细说JNI与NDK(四)动态和静态注册
细说JNI与NDK(五)JNI 线程
细说JNI与NDK(六)静态缓存,异常捕获,内置函数
细说JNI与NDK(七)Parcel底层JNI思想与OpenCV简单对比

Cmake 配置使用说明(简化版)

  • cmake_minimum_required(VERSION 3.4.1)

最低支持的cmake版本

  • include_directories(src/main/cpp/include)

include_directories 导入头文件

  • aux_source_directory 定义文件夹路径别名

aux_source_directory(${CMAKE_SOURCE_DIR}/src/main/cpp my_source_path)
aux_source_directory(${CMAKE_SOURCE_DIR}/src/main/cpp/bzip2 SOURCES)

${CMAKE_SOURCE_DIR} 这个就是你的CmakeList.txt 所在的目录位置,通常我们会根据他,进行相对位置,进行配置信息。
aux_source_directory 他的意思就是给你这个目录起一个变量名替代,因为路径太长,所以相当与起一个别名,简化配置。
除了这种配置我们还有其他方式 file ( Glob ...)

  • file ( Glob ...) 不推荐

file(GLOB allCpp *.h *.cpp *.c)
全局引入文件,并起别名

  • add_library

用第一种:分别对应不同库路径配置方法

add_library(
        native-lib 
        SHARED 
        ${my_source_path} ${SOURCES} )

native-lib 对应的实际上是libnative-lib.so lib自定义名字.so 这种规则
SHARED 是动态库.so---->对应的静态库STATIC 静态库.a
然后候命可以跟着很多不同库文件夹路径。

#方法1
aux_source_directory(${CMAKE_SOURCE_DIR}/src/main/cpp my_source_path)
aux_source_directory(${CMAKE_SOURCE_DIR}/src/main/cpp/bzip2 SOURCES)
add_library(native-lib SHARED ${my_source_path} ${SOURCES} )

不推荐第二种:file(GLOB) 的方式

# 方法2
#file(GLOB allCpp *.h *.cpp *.c)
#add_library(native-lib SHARED ${allCpp} )

简单的项目还好,引入的库比较少,如果是多个项目,不好替换,随时增删的情况,不好分类管理,建议用第一种

  • find_library 这个多数情况可以简化操作,在target_link_libraries里面

find_library( 
        log-lib # Sets the name of the path variable.
        log# Specifies the name of the NDK library that you want CMake to locate.     
        )

看注释也知道,给log-lib 是个变量别名
log----> 在本地ndk配置的中动态查找liblog.so

  • set_target_properties

set_target_properties(lib_opencv
        PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jni/${ANDROID_ABI}/libopencv_java3.so)

我们在开发自己的库或者C层的时候,会用到第三方成形的so库,所以set_target_properties 的使用就比较重要
规则简单
lib_opencv(用到的so库名)
IMPORTED_LOCATION(固定引入方式)
具体的路径${ANDROID_ABI} 比较有意思,是配合gradle 打包配置,可以循环判断,对应平台的so引入
由于版本有区别,和${CMAKE_ANDROID_ARCH_ABI} 是一样的

同理,和set_target_properties,对应的还有 set 方式

  • set 作用和目的类似set_target_properties

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_CXX_FLAGS}/src/main/${CMAKE_ANDROID_ARCH_ABI}")

CMAKE_CXX_FLAGS 这个就相当于,我们在windows或者mac配置path一样,这个是系统变量基本的,我们自定义的进行追加,追加标记就是-L 后面跟路径就好了,批量操作set更合适

回忆下mac path:xxxxx
回一下windows %path%;%xxxxx%
是不是瞬间就有感觉理解了

  • target_link_libraries 编译的步骤中,链接

target_link_libraries( # Specifies the target library.
        native-lib
        log #${log-lib}
        jnigraphics
        lib_opencv)

log #${log-lib}是不是吧find_library 去掉就行了,就是不要别名,是不是简洁很多

  • set(CMAKE_VERBOSE_MAKEFILE on) 调试打印cmake

set(CMAKE_VERBOSE_MAKEFILE on)
message("zcw before")

message(STATUS ${SOURCES})

message(STATUS "zcw after")

有时候对于复杂的脚本,出问题,我们需要定位,这个是后类似log信息就很重了。

好了,基本上上面都掌握的话,加上前面的基础ndk配置和引入第三方native库我们就不棘手了。

gradle 配置说明

我们需要了解的cpu架构模型:
arm64-v8a
armeabi-v7a
armeabi
x86 x86_64

我们一般兼容大多数手机,x86在windows上的模拟器比较多,所以对于正常运行的apk的话,我们兼容arm所有的类型就好。

但是开发的期间,我们手里有的是某一个,为了开发效率,我们选择一个对应测试机的cpu进行调试,用命令查询当前手机的cpu类型

 adb shell getprop  会查到所有的属性,所以我们过滤cpu型prop

 adb shell getprop ro.product.cpu.abi

可以开心的配置了

externalNativeBuild/cmake,ndk配置

defaultconfig/externalNativeBuild/cmake.

externalNativeBuild {
            cmake {
                cppFlags "-std=c++11"
                abiFilters "armeabi-v7a"
// 过时了
//                arguments "-DANDROID_STL=gnustl_static"
//                替换
                arguments "-DANDROID_STL=c++_static"

            }
        }

注意,我写的这个参数arguments ,google 官方相关的库可能会用到,编译失败,不要怕,仔细看信息,让你更新语法,因为可能我的ndk更新了,所以需要部分调整,所以遇到编译失败先仔细看输出信息。
cppFlags 默认的四大平台

defaultconfig/ndk , 指定cpu架构,打进apk的lib架构

ndk {
            abiFilters 'armeabi-v7a'

        }

实战练习-Fmod

这个引入的脚本CmakeList.txt 和过程理论结束了,我们来实践一下。
.QQ语言变声,这个功能是不是挺有意思,所谓的女装大佬,大佬女装。
还有QQmusic里面有那么多的音效DSP,DST等。底层用了音效库。
我们就引入fmod库进行demo一下。

native 核心

#include "fmod_demo.h"
#include <android/log.h>

#define LOG_TAG "native_zcw"

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
using namespace FMOD;
extern "C"
JNIEXPORT void JNICALL
Java_top_zcwfeng_jni_FmodVoiceActivity_voiceChangeNative(JNIEnv *env, jobject thiz, jint mode,
                                                         jstring path) {
    char *_content = "默认:播放结束";
    const char *_path = env->GetStringUTFChars(path, NULL);

    //Fmod 音效引擎
    System *system = 0;
    // fmod 声音
    Sound *sound = 0;
    // 通道 音轨
    Channel *channel = 0;
    // digital signal process 数字信号处理
    DSP *dsp = 0;
    // ① 创建系统
    System_Create(&system);
    // ② 初始化
    system->init(32, FMOD_INIT_NORMAL, 0);
    // ③ 创建声音
    system->createSound(_path, FMOD_DEFAULT, 0, &sound);
    // ④ 播放声音
    system->playSound(sound, 0, false, &channel);

    switch (mode) {
        case top_zcwfeng_jni_FmodVoiceActivity_MODE_NORMAL:
            _content = "原生:播放完毕";
            break;
        case top_zcwfeng_jni_FmodVoiceActivity_MODE_LUOLI:
            _content = "萝莉:播放完毕";
            //1.创建DSP类型 类型是 Pitch 音调调节 默认正常:1.0    0.5 ~ 2.0
            system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
            // 2.设置Pitch音调调节为:2.0,音调很高就是萝莉了
            dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 2.0f);
            // 3.添加音效进去 参数一:0 是因为只有一个dsp
            channel->addDSP(0, dsp);
            break;
        case top_zcwfeng_jni_FmodVoiceActivity_MODE_DASHU:
            _content = "大叔:播放完毕";
            // 1.创建DSP类型是Pitch 音调调节 默认正常:1.0  0.5 ~ 2.0
            system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
            // 2.设置Pitch音调调节为:2.0,音调很高就是萝莉了
            dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.5f);
            // 3.添加音效进去 参数一:0 是因为只有一个dsp
            channel->addDSP(0, dsp);
            break;
        case top_zcwfeng_jni_FmodVoiceActivity_MODE_GAOGUAI:
            _content = "搞怪 小黄人:播放完毕";
            // 1.从通道里面拿频率, 原始频率
            float frequency;
            channel->getFrequency(&frequency);
            // 2.在原来的频率上更改
            channel->setFrequency(frequency * 1.3f);
            break;
        case top_zcwfeng_jni_FmodVoiceActivity_MODE_JINGSONG:
            _content = "惊悚音 播放完毕";
            //大叔
            system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
            dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.7f);
            channel->addDSP(0, dsp);
            // 回声 搞点回声
            system->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);
            dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 400); // 延时的回音
            dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 40); // 默认:50  0完全衰减了
            channel->addDSP(1, dsp);
            // 颤抖 Tremolo
            system->createDSPByType(FMOD_DSP_TYPE_TREMOLO, &dsp);
            dsp->setParameterFloat(FMOD_DSP_TREMOLO_FREQUENCY, 0.8f);
            dsp->setParameterFloat(FMOD_DSP_TREMOLO_SKEW, 0.8f);
            channel->addDSP(2, dsp);
            break;
        case top_zcwfeng_jni_FmodVoiceActivity_MODE_KONGLING:
            _content = "空灵:播放完毕";
            //回音 ECHO
            system->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);
            // 延迟声音
            dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 150);
            // 默认:50  0完全衰减了
            dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 20);
            channel->addDSP(0, dsp);
            break;
    }
    bool isPlay = true;
    while (isPlay) {
        channel->isPlaying(&isPlay);
        usleep(1000 * 1000);
    }

    sound->release();
    system->close();
    system->release();
    env->ReleaseStringUTFChars(path,_path);

    // 调用java弹出播放提示 char* ---> jstring  ---> String(Java)
    jclass clazz = env->GetObjectClass(thiz);
    jmethodID jmethodId = env->GetMethodID(clazz,"playEnd", "(Ljava/lang/String;)V");
    jstring str = env->NewStringUTF(_content);
    env->CallVoidMethod(thiz,jmethodId,str);
}
  • 创建过程
    ① 创建系统
    ② 初始化
    ③ 创建声音
    ④ 播放声音
  • 音效
    根据音轨调用api即可
    思路,创建dsp,然后更改dsp参数,加入到channel中

kotlin 代码

class FmodVoiceActivity : AppCompatActivity() {
    private val MODE_NORMAL = 0 // 正常
    private val MODE_LUOLI = 1 // 萝莉
    private val MODE_DASHU = 2 // 大叔
    private val MODE_JINGSONG = 3 // 惊悚
    private val MODE_GAOGUAI = 4 // 搞怪
    private val MODE_KONGLING = 5 // 空灵
    // 播放的路径
    private val PATH = "file:///android_asset/test.m4a"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_fmod_voice)
        FMOD.init(this);
    }
    // 六个 点击事件
    fun onFix(view: View) {
        when (view.id) {
            R.id.btn_normal -> voiceChangeNative(MODE_NORMAL, PATH)
            R.id.btn_luoli -> voiceChangeNative(MODE_LUOLI, PATH)
            R.id.btn_dashu -> voiceChangeNative(MODE_DASHU, PATH)
            R.id.btn_jingsong -> voiceChangeNative(MODE_JINGSONG, PATH)
            R.id.btn_gaoguai -> voiceChangeNative(MODE_GAOGUAI, PATH)
            R.id.btn_kongling -> voiceChangeNative(MODE_KONGLING, PATH)
        }
    }

    private external fun voiceChangeNative(mode:Int,path:String)

    private fun playEnd(nativeMessageContent:String){
        Toast.makeText(this,""+nativeMessageContent,Toast.LENGTH_SHORT).show()
    }

    override fun onDestroy() {
        super.onDestroy()
        FMOD.close();
    }

}

效果类似qq的这种变声

fennu4.jpeg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,198评论 6 514
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,334评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,643评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,495评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,502评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,156评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,743评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,659评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,200评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,282评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,424评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,107评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,789评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,264评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,390评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,798评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,435评论 2 359