天天看點

08.Eclipse下Ndk開發(使用fmod實作QQ變聲功能)

(建立于2017/12/30)

1.編寫native

package org.fmod.example;

public class EffectUtils {
    
    //音效的類型
    public static final int MODE_NORMAL = 0;
    public static final int MODE_LUOLI = 1;
    public static final int MODE_DASHU = 2;
    public static final int MODE_JINGSONG = 3;
    public static final int MODE_GAOGUAI = 4;
    public static final int MODE_KONGLING = 5;

    /**
     * 音效處理
     * @param path
     * @param type
     */
    public native static void fix(String path,int type);
    
    static{
        System.loadLibrary("fmodL");
        System.loadLibrary("fmod");
        System.loadLibrary("qq_voice");
    }
}
           

2.進入src目錄下,使用指令行生成頭檔案

javah 包名+類名
           

3.建立項目根目錄下jni檔案夾,将頭檔案拷貝進去

4.下載下傳fmod源碼,複制api中lowlevel中的inc中的頭檔案到jni目錄下

5.複制fmod jar包到根目錄下libs檔案夾并添加依賴

6.複制需要的api平台的so檔案到jni目錄下

7.修改報錯的地方,比如頭檔案的路徑等問題

8.Android Tools -> Add native support 添加本地支援(在此之前,确認你的項目中已經配置了ndk相關的頭檔案目錄支援,參考前邊的筆記),填寫你要生成的

so庫的檔案名,确認和loadLibrary中加載的(如qq_voice)保持一緻

9.配置mk檔案:

Android.mk

LOCAL_PATH := $(call my-dir)

//編譯libfmod.so
include $(CLEAR_VARS)
LOCAL_MODULE := fmod
LOCAL_SRC_FILES := libfmod.so
include $(PREBUILT_SHARED_LIBRARY)

//編譯libfmodL.so
include $(CLEAR_VARS)
LOCAL_MODULE := fmodL
LOCAL_SRC_FILES := libfmodL.so
include $(PREBUILT_SHARED_LIBRARY)

//編譯我們自己寫的c代碼為so檔案,并添加上邊編譯的so支援
include $(CLEAR_VARS)
LOCAL_MODULE    := qq_voice
LOCAL_SRC_FILES := effect_fix.cpp
LOCAL_SHARED_LIBRARIES := fmod fmodL
//添加log支援
LOCAL_LDLIBS := -llog
//添加c++異常類支援
LOCAL_CPP_FEATURES := exceptions
include $(BUILD_SHARED_LIBRARY)
           

Application.mk

##支援C++異常處理,标準莫闆塊(針對string.h等頭檔案的處理,去掉了common.c之後,這個不再需要了,但是保留也沒問題)
APP_STL := gnustl_static
           

10.不斷的build project,直到所有異常解決編譯成功

部分源碼:

effect_fix.cpp

#include "org_fmod_example_EffectUtils.h"
#include "inc/fmod.hpp"
#include <stdlib.h>
#include <unistd.h>
#include <android/log.h>
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"renzhenming",FORMAT,##__VA_ARGS__)
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"renzhenming",FORMAT,##__VA_ARGS__)

#define MODE_NORMAL 0
#define MODE_LUOLI 1
#define MODE_DASHU 2
#define MODE_JINGSONG 3
#define MODE_GAOGUAI 4
#define MODE_KONGLING 5

using namespace FMOD;

JNIEXPORT void JNICALL Java_org_fmod_example_EffectUtils_fix
  (JNIEnv *env, jclass jcls, jstring path_jstr, jint type){

    System            *system;
    FMOD_RESULT       result;
    Sound             *sound;
    Channel           *channel;
    DSP               *dsp;
    float             frequency = 0;
    bool isPlaying = true;
    const char *media_path = env->GetStringUTFChars(path_jstr,NULL);
    try {
            //初始化
            result = System_Create(&system);
            result = system->init(32, FMOD_INIT_NORMAL, NULL);
            //建立聲音

            //這裡進坑了,寫作下邊的樣子是無法播放的,正确的方式是第二行 因為sound如果是null,那麼,播放的時候的sound也就是null,怎麼播放呢
            //result = system->createSound(media_path, FMOD_DEFAULT, 0, NULL);
            LOGI("%s",media_path);
            system->createSound(media_path, FMOD_DEFAULT, NULL, &sound);
            switch(type){
                case MODE_NORMAL:
                    //原生播放
                    result = system->playSound(sound, 0, false, &channel);
                    break;

                case MODE_LUOLI:
                    //dsp -- 音效
                    //FMOD_DSP_TYPE_PITCHSHIFT 提升或者降低音調用的一種音效
                    system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT,&dsp);
                    //設定音調的參數
                    dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH,2);
                    //播放(播放和添加channel的順序必須是先播放,再添加,否則音效無法生效)
                    //猜測原因
                    //playSound之後并不是立即播放,而是在system->update();之後開始播放,
                    //playSound的作用是創造了一個sound載體一個channel載體,然後在這個載體基礎上添加一系列的效果,是以,隻能現有載體
                    //效果才有放置的位置,是以需要先play再添加
                    result = system->playSound(sound, 0, false, &channel);
                    //添加到channel
                    channel->addDSP(0,dsp);

                    break;
                case MODE_DASHU:
                    //dsp -- 音效
                    //FMOD_DSP_TYPE_PITCHSHIFT 提升或者降低音調用的一種音效
                    system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT,&dsp);
                    //設定音調的參數
                    dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH,0.6);
                    //播放(播放和添加channel的順序必須是先播放,再添加,否則音效無法生效)
                    result = system->playSound(sound, 0, false, &channel);
                    //添加到channel
                    channel->addDSP(0,dsp);

                    break;
                case MODE_GAOGUAI:
                    //提高說話的速度
                    result = system->playSound(sound, 0, false, &channel);
                    channel->getFrequency(&frequency);\
                    frequency = frequency*1.5;
                    channel->setFrequency(frequency);
                    break;
                case MODE_KONGLING:
                    system->createDSPByType(FMOD_DSP_TYPE_ECHO,&dsp);
                    dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY,300);
                    dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK,2);
                    result = system->playSound(sound, 0, false, &channel);
                    channel->addDSP(0,dsp);
                    break;
                case MODE_JINGSONG:
                    //緊張
                    system->createDSPByType(FMOD_DSP_TYPE_ECHO,&dsp);
                    dsp->setParameterFloat(FMOD_DSP_TREMOLO_SKEW,0.5);
                    result = system->playSound(sound, 0, false, &channel);
                    channel->addDSP(0,dsp);
                    break;
                default:
                    break;
            }
            while(isPlaying){
                channel->isPlaying(&isPlaying);
                //<unistd.h>
                //機關微秒,不斷的睡眠保證聲音播放完成之後再釋放資源
                usleep(1000*1000);
            }
            LOGI("%s","播放完成");
            system->update();
    } catch(...){
        LOGE("%s","發生異常");
        goto end;
    }
    goto end;

    end:
    env->ReleaseStringUTFChars(path_jstr,media_path);
    result = sound->release();
    system->close();
    system->release();
}
           

QQActivity.java

package org.fmod.example;

import java.io.File;

import com.renzhenming.qq_voice.R;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;

public class QQActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
         org.fmod.FMOD.init(this);
        setContentView(R.layout.activity_main);
    }
    
    public void mFix(View view){
        String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separatorChar + "singing.wav";
        Log.d("renzhenming", path);
        
        switch (view.getId()) {
        case R.id.btn_record:
            EffectUtils.fix(path, EffectUtils.MODE_NORMAL);
            Log.d("renzhenming", "MODE_NORMAL");
            break;
        case R.id.btn_luoli:
            EffectUtils.fix(path, EffectUtils.MODE_LUOLI);
            Log.d("renzhenming", "MODE_LUOLI");
            break;
        case R.id.btn_dashu:
            EffectUtils.fix(path, EffectUtils.MODE_DASHU);
            Log.d("renzhenming", "MODE_DASHU");
            break;
        case R.id.btn_jingsong:
            EffectUtils.fix(path, EffectUtils.MODE_JINGSONG);
            break;  
            
        case R.id.btn_gaoguai:
            EffectUtils.fix(path, EffectUtils.MODE_GAOGUAI);
            break;  
            
        case R.id.btn_kongling:
            EffectUtils.fix(path, EffectUtils.MODE_KONGLING);
            break;  
        default:
            break;
        }
        
        
    }
    
    @Override
    protected void onDestroy(){    
        org.fmod.FMOD.close();      
        super.onDestroy();
    }
}
           

EffectUtils.java

package org.fmod.example;

public class EffectUtils {
    
    //音效的類型
    public static final int MODE_NORMAL = 0;
    public static final int MODE_LUOLI = 1;
    public static final int MODE_DASHU = 2;
    public static final int MODE_JINGSONG = 3;
    public static final int MODE_GAOGUAI = 4;
    public static final int MODE_KONGLING = 5;

    /**
     * 音效處理
     * @param path
     * @param type
     */
    public native static void fix(String path,int type);
    
    static{
        System.loadLibrary("fmodL");
        System.loadLibrary("fmod");
        System.loadLibrary("qq_voice");
    }
}
           

程式運作界面

08.Eclipse下Ndk開發(使用fmod實作QQ變聲功能)

18404062.png