版權(quán)聲明:轉(zhuǎn)載必須注明本文轉(zhuǎn)自張鵬輝的博客: http://blog.csdn.net/qingtiangg
大家好扒腕,這是我從業(yè)以來第一篇博客,給大家拜個晚年浊吏,祝大家雞年大吉斯辰,幸福美滿、事業(yè)有成帖旨。好了請容許我嘮叨幾萬字箕昭。首先自我介紹下
我叫張鵬輝大家可以叫我道長,我是老司······哦不解阅,我是新人請大家多多關(guān)照落竹。 一直都是看別人的博客成長(復(fù)制、黏貼)货抄,總想自己寫點什么述召,發(fā)出來,讓大家去點評蟹地,給建議互相成長积暖。這也是我的初衷,也就有了這個博客的誕生怪与。呀酸。。在下目前從事移動開發(fā)所以目前以移動端內(nèi)容來發(fā)布琼梆,記錄一些日常開發(fā)中碰到的坑性誉,和項目中一些模塊拿出來開源互相學(xué)習(xí)(幾萬字略。茎杂。错览。我用意念給你們介紹完了,就當(dāng)你們了解我了)
閑話不多說了開擼煌往,如題今天請跟貧道一步步來實現(xiàn)球球語音變聲功能
分析
首先我們來看下QQ語音變聲效果倾哺,如下圖所示:
點擊發(fā)送語音在點擊相應(yīng)的生效發(fā)出去就變聲了轧邪,沒玩過的可以打開QQ試下,那么他們是怎么做到讓聲音變聲呢羞海!看官們接著往下看忌愚。
首先我們知道,錄制人聲到手機(jī)却邓,這些音頻數(shù)據(jù)是由二進(jìn)制十六進(jìn)制組成硕糊,如果我們自己去用算法處理這些數(shù)據(jù)這個難度是比較大的。
那么怎么辦腊徙?简十??慶幸吧我們Android開發(fā)中有個NDK的東西撬腾,簡單來說就是我們在java層調(diào)用c/c++的一些強(qiáng)大的開源庫螟蝙,(這里不講NDK基礎(chǔ)如果想了解請谷歌一下)。
網(wǎng)上有很多開源的音頻處理庫民傻。我不知道騰訊是用的那個胰默,但我們今天用到的是FMOD音頻引擎,如果你做過游戲開發(fā)相信你一定接觸過漓踢,這里附上FMOD官網(wǎng)鏈接大家可以去簡單的看下介紹牵署,國內(nèi)FMOD的中文資料幾乎沒有,所以還是建議你去官網(wǎng)了解彭雾,點我進(jìn)fmod官網(wǎng)碟刺,這里就不詳細(xì)介紹了,他的主要功能就是對音頻進(jìn)行處理薯酝,比如你玩賽車類游戲你的引擎轟鳴聲半沽,會隨著你的速度變化而變化,游戲中有多個視角吴菠,比如車內(nèi)切換到車外者填,你聽到的聲音是不是不一樣的,再比如我們當(dāng)年玩的cs游戲做葵,你附近的敵人在你不同角度開槍占哟,你聽到的聲音也是不一樣的,(D鹗浮Uズ酢!暴露年齡了瘫筐。蜜暑。。)
或許我們來個圖更好理解:
聽到腳步聲或槍聲策肝,就可以判斷出敵人在你什么位置肛捍,不同的位置方位明顯聽到的聲音不同
開始
好的大體環(huán)境介紹完了隐绵,接下來我們看代碼。
首先我們新建eclipse工程‘’mysound‘’(這里不要問我為什么不用Android Studio開發(fā)as現(xiàn)在多火白竞痢依许!,因為ndk開發(fā)as還是有些問題的缀蹄,有些坑我也沒解決峭跳,我能怎么辦?我也很絕望呀 袍患。當(dāng)然了以后我主要還是以as去寫坦康,如果今天的項目你能移植到as里面請聯(lián)系我?⒏丁)
首先我們導(dǎo)入一下FMOD的so動態(tài)庫和一些需要用到的頭文件诡延。
今天的所有代碼和用到的庫我都會放到github上,所以不用擔(dān)心這些文件去哪找古胆。
1.接了下我們將工程轉(zhuǎn)成C/C++工程 Add Native Support
之后會給你生成android.mk文件 我們來配置一下里面的文件肆良,引入提供的預(yù)編譯的so庫把他們放到j(luò)ni目錄下,還有他自己的函數(shù)的實現(xiàn)也要引入看下面
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := fmod
LOCAL_SRC_FILES := libfmod.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := fmodL
LOCAL_SRC_FILES := libfmodL.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := sound
LOCAL_SRC_FILES :=effect_fix.cpp
LOCAL_SHARED_LIBRARIES := fmod fmodL
LOCAL_LDLIBS := -llog
LOCAL_CPP_FEATURES :=exceptions
include $(BUILD_SHARED_LIBRARY)
LOCAL_MODULE := sound 自己的動態(tài)庫名切記要和System.loadLibrary("sound");一致這坑我踩過 逸绎,fmod fmodL這是2個預(yù)編譯的庫
如果你引入的是官網(wǎng)提供的庫可能會出現(xiàn)一些路徑的問題改一下就好我的已經(jīng)改了惹恃。
劃重點 FMOD 用到了標(biāo)準(zhǔn)模板庫STL,說用這玩意會變慢棺牧?巫糙,但我沒感覺出來
安卓ndk里面已經(jīng)有了一個最小的c++運行庫,想用那么我們就需要另外的配置
怎么配置呢颊乘?看ndk提供的文檔 在我們下載的ndk目錄里就有解決方法APP_STL
2.創(chuàng)建Application.mk 支持C++異常處理参淹,標(biāo)準(zhǔn)模板塊
里面就一句話 :
APP_STL := gnustl_static
3.配置完我們來寫java代碼
布局我就不在這貼出來了幾個按鈕,看關(guān)鍵代碼
我們先來個工具類Utils
public class Utils {
//音效類型 大叔蘿莉什么的類型
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;
/**
* 音效處理傳2個值
* @param path 音頻文件路徑
* @param type 音頻文件類型
*/
public native static void fix(String path,int type);
//加載動態(tài)庫
//要和你android.mk文件一致
static{
System.loadLibrary("fmodL");
System.loadLibrary("fmod");
System.loadLibrary("sound");
}
}
接下來我們生成頭文件
然后到項目F5刷新一下src目錄下會多出個.h的文件把它拖到j(luò)ni目錄下
繼續(xù)在jni目錄下創(chuàng)建一個c++源文件 supersound.cpp代碼
#include "inc/fmod.hpp"
#include <stdlib.h>
#include <unistd.h>
#include "org_fmod_example_Utils.h"
#include <android/log.h>
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"zph",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"zph",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_Utils_fix(JNIEnv *env,
jclass jcls, jstring path_jstr, jint type) {
//初始化
System *system;
Sound *sound;
DSP *dsp;
bool playing = true;
Channel *channel;
float frequency = 0;
System_Create(&system);
system->init(32, FMOD_INIT_NORMAL, NULL);//32通道
const char* path_cstr = env->GetStringUTFChars(path_jstr, NULL);
try {
//創(chuàng)建聲音
system->createSound(path_cstr, FMOD_DEFAULT, NULL, &sound);
switch (type) {
case MODE_NORMAL:
//原生播放
LOGI("%s", path_cstr);
system->playSound(sound, 0, false, &channel);
LOGI("%s", "fix normal");
break;
case MODE_LUOLI:
//loli
//DSP digital singal process
//dsp->音效
//dsp 提升或者降低音調(diào)的一種音效
system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
//設(shè)置音調(diào)的參數(shù)
dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 8.0);
//添加進(jìn)到channel
system->playSound(sound, 0, false, &channel);
channel->addDSP(0, dsp);
break;
case MODE_DASHU:
//大叔
system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
//設(shè)置音調(diào)的參數(shù)
dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.8);
//添加進(jìn)到channel
system->playSound(sound, 0, false, &channel);
channel->addDSP(0, dsp);
break;
case MODE_JINGSONG:
//驚悚
system->createDSPByType(FMOD_DSP_TYPE_TREMOLO, &dsp);
dsp->setParameterFloat(FMOD_DSP_TREMOLO_SKEW, 5);
system->playSound(sound, 0, false, &channel);
channel->addDSP(0, dsp);
break;
case MODE_GAOGUAI:
//搞怪
//提高說話的速度
system->playSound(sound, 0, false, &channel);
channel->getFrequency(&frequency);
frequency = frequency * 2;
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, 20);
system->playSound(sound, 0, false, &channel);
channel->addDSP(0, dsp);
break;
default:
break;
}
} catch (...) {
LOGE("%s", "發(fā)生異常");
goto end;
}
system->update();
//釋放資源
//單位是微妙
//每秒鐘判斷下是否是播放
while (playing) {
channel->isPlaying(&playing);
usleep(1000 * 1000);
}
goto end;
end: env->ReleaseStringUTFChars(path_jstr, path_cstr);
sound->release();
system->close();
system->release();
}
舉個例子比如在我們播放一段mp3的時候里面又人聲乏悄,樂器聲等浙值,如果我想對其中一個鋼琴的聲音加一個特效
那么怎么加呢!這就用到它了Channel *channel; 一般是32個通道修改完以后混音
好了到這里關(guān)鍵代碼已經(jīng)寫完檩小,我們可以測試玩玩了.
項目源碼下載地址:
https://github.com/CN-ZPH/MySound.git
覺得不錯請點一個star蛤开呐!
有問題下面留言評論,我看到會回復(fù)规求。