知識
不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價值,我們在追求其視覺表現(xiàn)的同時,更側(cè)重于功能的便捷,營銷的便利,運(yùn)營的高效,讓網(wǎng)站成為營銷工具,讓軟件能切實(shí)提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序?yàn)楹笃谏壧峁┍憬莸闹С郑?
AndroidNDK開發(fā)(六)——使用開源LAME轉(zhuǎn)碼mp3
發(fā)表時間:2020-10-19
發(fā)布人:葵宇科技
瀏覽次數(shù):62
轉(zhuǎn)載請注明出處:http://blog.csdn.net/allen315410/article/details/42456661
在本專欄的前面幾篇博客中講述了一些Android NDK開辟的基本,大年夜情況搭建一向到應(yīng)用JNI進(jìn)行Java端和C端代碼的互相調(diào)用,并且的講解的Demo也是很簡單易懂的,信賴控制前面博客的大年夜部分內(nèi)容,就可以著手在實(shí)際項(xiàng)目中應(yīng)用JNI進(jìn)行NDK開辟了,那么既然基本過了,接下來我在這里測驗(yàn)測驗(yàn)去應(yīng)用真實(shí)項(xiàng)目中去。我們知道,C說話因?yàn)楦咝?,并且又是最早期的高等編程之一,一向存活至今?0年了,所以很多用C開辟出來高效類庫是可以被復(fù)竽暌姑的,如許不僅做到高效力,并且削減了項(xiàng)目開辟周期。在這里我找到了一個關(guān)于音頻文件轉(zhuǎn)碼的最常用的類庫——LAME,這篇博客就是基于LAME開辟一個wav音頻文件轉(zhuǎn)碼成mp3音頻文件的小項(xiàng)目。
一、LAME簡介
LAME是今朝最好的MP3編碼引擎。LAME編碼出來的MP3音色純厚、空間寬廣、低音清楚、細(xì)節(jié)表示優(yōu)勝,它獨(dú)創(chuàng)的心理音響模型技巧包管了CD音頻還原的┞鋒實(shí)性,合營VBR和ABR參數(shù),音質(zhì)幾乎可以媲美CD音頻,但文件體積卻異常小。對于一個免費(fèi)引擎,LAME的優(yōu)勢不問可知。關(guān)于LAME的介紹可以在百度百科,維誹謗科中找到,我在這里不再贅述了,然則要知道LAME可以贊助我們將wav無損音頻文件轉(zhuǎn)碼成mp3這種體積相對較小的音頻格式文件。
注:關(guān)于wav和mp3是一系列的音頻編解碼算法,具體我也不是特別清跋扈它的厲害之處和實(shí)現(xiàn)道理,想要深刻懂得音頻編碼還須要找一些相干的材料文檔來讀,然則不清跋扈這些也不妨礙我們開辟如許的一個項(xiàng)目。
LAME的源碼是托管到sourceforge.net上的,我們開辟一個基于LAME的項(xiàng)目,就不得不下載其源碼用于編譯。
LAME主頁:http://lame.sourceforge.net/
二、編寫本處所法
開辟這個轉(zhuǎn)碼項(xiàng)目標(biāo)時刻,做一下簡單的處理,編寫兩個簡單的Native辦法,一個是convertmp3(String wav,String mp3),這個辦法是重要的Native辦法,目標(biāo)是將大年夜Java層獲取到的兩個音頻文件的路徑已字符串情勢傳遞給C端,C端拿到這兩個路徑,就可以進(jìn)行讀寫和編解碼操作了,別的一個Native辦法是getLameVersion(),該辦法是獲取LAME的版本號的,返回值為字符串,用處是校驗(yàn)在本地是否成功編譯.so文件。源碼如下
/** * wav轉(zhuǎn)換成mp3的本處所法 * * @param wav * @param mp3 */ public native void convertmp3(String wav, String mp3); /** * 獲取LAME的版本信息 * * @return */ public native String getLameVersion();
三、編譯頭文件
經(jīng)由過程前面幾篇博客進(jìn)修,我們知道在Java層中定義完我們須要的Native辦法后,須要應(yīng)用JDK供給的javah敕令編譯C文件中須要的辦法簽名,具體做法是1,如不雅你應(yīng)用的是jdk1.6及以下版本,將目次切換到工程目次下的classes目次;2,如不雅你應(yīng)用的jdk1.7版本,請將目次切換到工程目次下的src目次,履行如下的javah敕令:、
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := convert LOCAL_SRC_FILES := convert.c bitstream.c fft.c id3tag.c mpglib_interface.c presets.c quantize.c reservoir.c tables.c util.c VbrTag.c encoder.c gain_analysis.c lame.c newmdct.c psymodel.c quantize_pvt.c set_get.c takehiro.c vbrquantize.c version.c LOCAL_LDLIBS += -llog include $(BUILD_SHARED_LIBRARY)Application.mk
[img]http://img.blog.csdn.net/20150106143406183
好!看到?jīng)]有報錯的話,就解釋我們的辦法簽名文件以及生成了,在src目次下找到這個辦法簽名的文件,在工程目次下新建一個jni的目次,將辦法簽名文件拷貝進(jìn)jni目次之中,我們先打開這個文件看看內(nèi)容好了。
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_example_lame_MainActivity */ #ifndef _Included_com_example_lame_MainActivity #define _Included_com_example_lame_MainActivity #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_lame_MainActivity * Method: convertmp3 * Signature: (Ljava/lang/String;Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_example_lame_MainActivity_convertmp3 (JNIEnv *, jobject, jstring, jstring); /* * Class: com_example_lame_MainActivity * Method: getLameVersion * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_lame_MainActivity_getLameVersion (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
項(xiàng)目進(jìn)行到這里就要顯得特別重要也特別當(dāng)心了,因?yàn)榇藭r須要引用開源庫LAME到本項(xiàng)目中并且還要從新編譯成實(shí)用于本平臺的庫。
1,將下載來的LAME源碼解壓到本地,打開解壓后的目次,找到libmp3lame目次,將該目次下的所有的文件都拷貝到j(luò)ni目次下。
2,剔除不須要的文件目次。例如i386這個目次要刪除,還要刪除幾個非.h,.c作為擴(kuò)大名的文件,已經(jīng)Linux下的批處理文件,因?yàn)檫@些文件都是Android平臺下非須要的。
3,惹人lame.h頭文件。在LAME解壓目次下找到include目次,將其下的lame.h頭文件拷貝到j(luò)ni目次下,如不雅這個目次沒有被惹人,會報如下的缺點(diǎn)
4,修改util.h的源碼。在JNI目次下找到util.h文件,在574行找到ieee754_float32_t數(shù)據(jù)類型,將其修改為float類型,因?yàn)閕eee754_float32_t是Linux或者是Unix下支撐的數(shù)據(jù) 類型,在Android下并不支撐。如不雅不修改,則編譯源碼的時刻會報如下缺點(diǎn)
[img]http://img.blog.csdn.net/20150106145254829
五、編寫本地C說話代碼,實(shí)現(xiàn)音頻文件的轉(zhuǎn)碼
關(guān)于怎么編寫這段C說話代碼,確切是有些難度,須要必定的C說話常識,并且還要包管可以或許看懂lame.h文件中供給的API和注釋,在這里我就不在說清楚明了,因?yàn)檫@個工作說起來那就復(fù)雜多了,單單就一篇博客是將不完全的,下面貼出C說話代碼,代碼中供給了較為詳盡的注釋,帶著注釋去看代碼,便于懂得的!
#include<stdio.h> #include<jni.h> #include<malloc.h> #include<string.h> #include<lame.h> #include"com_example_lame_MainActivity.h" #include<android/log.h> #define LOG_TAG "System.out.c" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) /** * 返回值 char* 這個代表char數(shù)組的首地址 * Jstring2CStr 把java中的jstring的類型轉(zhuǎn)化成一個c說話中的char 字符串 */ char* Jstring2CStr(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = (*env)->FindClass(env, "java/lang/String"); //String jstring strencode = (*env)->NewStringUTF(env, "GB2312"); // 獲得一個java字符串 "GB2312" jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B"); //[ String.getBytes("gb2312"); jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid, strencode); // String .getByte("GB2312"); jsize alen = (*env)->GetArrayLength(env, barr); // byte數(shù)組的長度 jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE); if (alen > 0) { rtn = (char*) malloc(alen + 1); //"\0" memcpy(rtn, ba, alen); rtn[alen] = 0; } (*env)->ReleaseByteArrayElements(env, barr, ba, 0); // return rtn; } int flag = 0; /** * wav轉(zhuǎn)換mp3 */ JNIEXPORT void JNICALL Java_com_example_lame_MainActivity_convertmp3 (JNIEnv * env, jobject obj, jstring jwav, jstring jmp3) { char* cwav =Jstring2CStr(env,jwav) ; char* cmp3=Jstring2CStr(env,jmp3); LOGI("wav = %s", cwav); LOGI("mp3 = %s", cmp3); //1.打開 wav,MP3文件 FILE* fwav = fopen(cwav,"rb"); FILE* fmp3 = fopen(cmp3,"wb"); short int wav_buffer[8192*2]; unsigned char mp3_buffer[8192]; //1.初始化lame的編碼器 lame_t lame = lame_init(); //2. 設(shè)置lame mp3編碼的采樣率 lame_set_in_samplerate(lame , 44100); lame_set_num_channels(lame,2); // 3. 設(shè)置MP3的編碼方法 lame_set_VBR(lame, vbr_default); lame_init_params(lame); LOGI("lame init finish"); int read ; int write; //代表讀了若干個次 和寫了若干次 int total=0; // 當(dāng)前讀的wav文件的byte數(shù)量 do{ if(flag==404){ return; } read = fread(wav_buffer,sizeof(short int)*2, 8192,fwav); total += read* sizeof(short int)*2; LOGI("converting ....%d", total); publishJavaProgress(env,obj,total); // 調(diào)用java代碼 完成進(jìn)度條的更新 if(read!=0){ write = lame_encode_buffer_interleaved(lame,wav_buffer,read,mp3_buffer,8192); //把轉(zhuǎn)化后的mp3數(shù)據(jù)寫到文件里 fwrite(mp3_buffer,sizeof(unsigned char),write,fmp3); } if(read==0){ lame_encode_flush(lame,mp3_buffer,8192); } }while(read!=0); LOGI("convert finish"); lame_close(lame); fclose(fwav); fclose(fmp3); } /** * 調(diào)用java代碼 更新法度榜樣的進(jìn)度條 */ void publishJavaProgress(JNIEnv * env, jobject obj, jint progress) { // 1.找到j(luò)ava的MainActivity的class jclass clazz = (*env)->FindClass(env, "com/example/lame/MainActivity"); if (clazz == 0) { LOGI("can't find clazz"); } LOGI(" find clazz"); //2 找到class 瑯綾擎的辦法定義 jmethodID methodid = (*env)->GetMethodID(env, clazz, "setConvertProgress","(I)V"); if (methodid == 0) { LOGI("can't find methodid"); } LOGI(" find methodid"); //3 .調(diào)用辦法 (*env)->CallVoidMethod(env, obj, methodid, progress); } /** * 獲取LAME的版本號 */ JNIEXPORT jstring JNICALL Java_com_example_lame_MainActivity_getLameVersion( JNIEnv * env, jobject obj) { return (*env)->NewStringUTF(env, get_lame_version()); }膳綾擎新建了一個convert.c的C源文件,源碼中起首引用一些相干的類庫來應(yīng)用,例如<stdio.h><jni.h><malloc.h><string.h>這幾個是C的標(biāo)準(zhǔn)類庫,代碼中有須要就惹人。接下來,我們還須要引用上述第三主題中編譯好的辦法簽名文件com_example_lame_MainActivity.h,這個文件定義了Native辦法的簽名,便利編譯器可以或許找到相對應(yīng)的Native辦法,還有一個異常重要的頭文件lame.h,這個項(xiàng)目頂用的就是LAME,所以這個lame.h的頭文件是必須引用上的,最后就是log.h的惹人和定義了,都是固定的內(nèi)容,看源碼就好了。
六、編寫設(shè)備文件和交叉編譯
完成上述步調(diào)以及源碼的編寫之后,剩下的就是設(shè)備文件了。起首看看Android.mk文件,這個就麻煩了棘因?yàn)樯啪c擎我們將LAME全部源碼文件全部拷貝到j(luò)ni目次下了,這些文件都是要從新編譯的,所以我們須要在Android.mk文件的LOCAL_SRC_FILES這個字段上,將所以的源碼文件都要設(shè)備上去,不僅包含我們自定義的c文件,更有LAME中的c源碼文件,因?yàn)長AME包含的文件太多了,所以在編寫個LOCAL_SRC_FILES時要格外當(dāng)心,寫錯一個就有可能編碼不經(jīng)由過程。下面是我的設(shè)備籌劃:
APP_PLATFORM := android-8cygwin下交叉編譯:
[img]http://img.blog.csdn.net/20150106153948528
好!看到上圖底部的提示,解釋我們的.so是編譯好了,因?yàn)槲募^多,編譯是須要花費(fèi)一點(diǎn)時光的,并且膳綾擎報錯了很多的warnning警告提示,然則沒緊要,可以忽視,只要不是報錯,那這個工程依然可以運(yùn)行。
七、在Java層編寫調(diào)用C端的代碼
在編寫Java寫調(diào)用代碼之前,我們推敲到因?yàn)榫幗獯a是個異常耗時的操作,所以這個操作是不克不及夠放在主線程中履行的,必須得開啟新的線程,又是因?yàn)檫@是一個耗時操作,所認(rèn)為了優(yōu)勝的用戶體驗(yàn),我們須要在轉(zhuǎn)碼的過程中添加一個進(jìn)度條對話框,提示一個轉(zhuǎn)碼的過程,關(guān)于這個進(jìn)度條的進(jìn)度問題也是交給C說話實(shí)現(xiàn)的,我們在Java中只須要定義一個如許的設(shè)置進(jìn)度的辦法,在C說話中回調(diào)這個java辦法,將進(jìn)度數(shù)據(jù)傳遞給Java中的進(jìn)度條對話框即可。
LAME下載:http://sourceforge.net/projects/lame/files/lame/3.99/
public class MainActivity extends Activity { static { System.loadLibrary("convert"); } private EditText et_wav; private EditText et_mp3; private ProgressDialog pd; /** * wav轉(zhuǎn)換成mp3的本處所法 * * @param wav * @param mp3 */ public native void convertmp3(String wav, String mp3); /** * 獲取LAME的版本信息 * * @return */ public native String getLameVersion(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); et_wav = (EditText) this.findViewById(R.id.et_wav); et_mp3 = (EditText) this.findViewById(R.id.et_mp3); pd = new ProgressDialog(this); } /** * wav轉(zhuǎn)換mp3 */ public void convert(View view) { final String mp3Path = et_mp3.getText().toString().trim(); final String wavPath = et_wav.getText().toString().trim(); File file = new File(wavPath); int size = (int) file.length(); System.out.println("文件大年夜小 " + size); if ("".equals(mp3Path) || "".equals(wavPath)) { Toast.makeText(MainActivity.this, "路徑不克不及為空", 1).show(); return; } pd.setMessage("轉(zhuǎn)換中...."); pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pd.setMax(size); // 設(shè)置進(jìn)度條的最大年夜值 pd.setCancelable(false); pd.show(); // 轉(zhuǎn)碼是個耗時的操作,所以這里須要開啟新線程去履行 new Thread() { @Override public void run() { convertmp3(wavPath, mp3Path); pd.dismiss(); } }.start(); } /** * 設(shè)置進(jìn)度條的進(jìn)度,供給給C說話調(diào)用 * * @param progress */ public void setConvertProgress(int progress) { pd.setProgress(progress); } /** * 獲取LAME的版本號 */ public void getVersion(View view) { Toast.makeText(MainActivity.this, getLameVersion(), 0).show(); } }留意須要添加權(quán)限兩條:
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
八、測試
好吧,寫到這里總算是一個項(xiàng)目完成了,下面要進(jìn)行一下測試了,先開一個arm的模仿器,然后運(yùn)行一下工程。1,測試版本號
[img]http://img.blog.csdn.net/20150106155024713
[img]http://img.blog.csdn.net/20150106144735500
2,測試wav轉(zhuǎn)mp3
今朝LAME的最新版本是3.99.5,不克不及連上SourceForge主頁的同伙可以點(diǎn)擊csdn下載,我已經(jīng)上傳,地址是:http://download.csdn.net/detail/lee_tianya/8332357
[img]http://img.blog.csdn.net/20150106155234099
好,再來看看sdcard下的原wav音頻文件和轉(zhuǎn)碼后的mp3文件
[img]http://img.blog.csdn.net/20150106155411412
大年夜上圖可以看到我們的轉(zhuǎn)碼是可以應(yīng)用的,原wav文件確切轉(zhuǎn)碼成了mp3文件,并且大年夜小只有原文件的1/10了,導(dǎo)出這個mp3文件,用音頻播放器播放一下,也是可以聽得,文件并沒有破壞。怎么樣?項(xiàng)目做完了,你有沒有興趣嘗嘗呢?
源碼請在這里下載
四、將LAME的源碼導(dǎo)入jni目次
相關(guān)案例查看更多
相關(guān)閱讀
- 網(wǎng)站建設(shè)公司哪家好
- python開發(fā)小程序
- 網(wǎng)站排名
- 網(wǎng)站建設(shè)快速優(yōu)化
- 報廢車管理系統(tǒng)
- 昆明小程序設(shè)計
- 服務(wù)器
- 模版信息
- 小程序被騙
- 昆明小程序代建
- 云南網(wǎng)站建設(shè)價格
- 網(wǎng)站收錄
- 區(qū)塊鏈
- 云南小程序開發(fā)公司
- 云南網(wǎng)站建設(shè)一條龍
- 前端
- 云南網(wǎng)站建設(shè)
- 海南小程序制作公司
- 云南小程序代建
- 汽車報廢
- 小程序開發(fā)平臺前十名
- 網(wǎng)絡(luò)公司
- 支付寶小程序被騙
- 小程序開發(fā)聯(lián)系方式
- 報廢車回收管理系統(tǒng)
- 網(wǎng)站建設(shè)公司地址
- .net網(wǎng)站
- 北京小程序制作
- 汽車報廢管理系統(tǒng)
- 云南軟件設(shè)計