基于opencore-amr实现amr-nb编码和解码,在Android上完成wav文件与amr文件格式的相互转换。wav和amr文件读写部分主要参考了opencore中的test文件夹下的例子,以及 IOS音频格式之AMR和WAV互转(更新支持amrv7s)。
1、opencore-amr的下载和编译
编译之后我们可以得到interf_enc.h、interf_dec.h、wrapper.cpp这些与接口有关的文件,通过这些文件相对容易写出JNI接口函数。
在linux下的编译可以得到so文件,但是是基于X86的,想要得到Arm 架构下的 so文件,需要用到ndk编译。
test文件夹下的编解码例子可以自己尝试一下,对后面编写java代码很有帮助。
2、Eclipse下Android工程建立
主要有两点:一是JAVA本地函数通过JNI调用opencore的代码(so封装、调用),二是转码相关的代码。首先需要将opencore编解码的cpp文件放在工程jni目录下,或者其他地方,然后需要编写Android.mk文件,将需要的代码包含进来.
A. JNI 接口cpp文件编写
解码和编码部分分开写,也可以合在一起写,重点是在JNI cpp文件中完成了数据类型的映射和接口函数的重写。写完之后也要包含到Android.mk文件中去。执行ndk-build就可以在lib文件夹下生成so文件。
解码部分JNI cpp代码:
1 #include
2 #include
3
4 #ifndef _Included_com_example_amrcodec_decode_AmrDecInterface5 #define _Included_com_example_amrcodec_decode_AmrDecInterface
6 #ifdef __cplusplus7 extern "C"{8 #endif
9 /*
10 * Class: com_example_amr_dec_decode_AmrDecInterface11 * Method: initamr12 * Signature: ()V13 */
14 JNIEXPORT intJNICALL Java_com_example_amrcodec_decode_AmrDecInterface_initDecamr15 (JNIEnv *env, jobject ccc) {16
17 return(jint) Decoder_Interface_init();18 }19
20 /*
21 * Class: com_example_amr_dec_decode_AmrDecInterface22 * Method: exitamr23 * Signature: ()V24 */
25 JNIEXPORT intJNICALL Java_com_example_amrcodec_decode_AmrDecInterface_exitDecamr26 (JNIEnv *env, jobject lll,jint*nativePointer) {27
28 Decoder_Interface_exit(nativePointer);29
30 }31
32 /*
33 * Class: com_example_amr_dec_decode_AmrDecInterface34 * Method: Decodeamr35 * Signature: ([B[B)V36 */
37 JNIEXPORT voidJNICALL Java_com_example_amrcodec_decode_AmrDecInterface_Decodeamr38 (JNIEnv *env, jobject obj, jint* nativePointer,jbyteArray in, jshortArray out, jint bfi)39 {40 jsize inLen = env->GetArrayLength(in); //这里主要就是数据类型的映射
41 jbyte inBuf[inLen];42 env->GetByteArrayRegion(in, 0, inLen, inBuf);43
44 jsize outLen = env->GetArrayLength(out);45 shortoutBuf[outLen];46
47 Decoder_Interface_Decode(nativePointer, (const unsigned char*) inBuf, (short*) outBuf, bfi);48
49 //env->ReleaseByteArrayElements(in, inBuf, JNI_ABORT);//no need - GetByteArrayRegion handles this
50 env->SetShortArrayRegion(out, 0, outLen, outBuf); //释放
51
52 }53
54 #ifdef __cplusplus55 }56 #endif
57 #endif
View Code
编码部分JNI cpp代码:(多出来的枚举型很讨厌)
1 /*DO NOT EDIT THIS FILE - it is machine generated*/
2 #include
3 #include
4 #include
5 /*Header for class com_example_amr_dec_encode_AmrEncInterface*/
6
7 #ifndef _Included_com_example_amrcodec_encode_AmrEncInterface8 #define _Included_com_example_amrcodec_encode_AmrEncInterface
9 #ifdef __cplusplus10 extern "C"{11 #endif
12 /*
13 * Class: com_example_amr_dec_encode_AmrEncInterface14 * Method: initEncamr15 * Signature: (I)I16 */
17 JNIEXPORT jint JNICALL Java_com_example_amrcodec_encode_AmrEncInterface_initEncamr(18 JNIEnv *env, jobject job, jint dtxState) {19
20 return(jint) Encoder_Interface_init(dtxState);21 }22 /*
23 * Class: com_example_amr_dec_encode_AmrEncInterface24 * Method: exitEncamr25 * Signature: (I)V26 */
27 JNIEXPORT voidJNICALL Java_com_example_amrcodec_encode_AmrEncInterface_exitEncamr28 (JNIEnv *env, jobject job1, jint*nativePointer) {29
30 Encoder_Interface_exit(nativePointer);31
32 }33
34 /*
35 * Class: com_example_amr_dec_encode_AmrEncInterface36 * Method: Encodeamr37 * Signature: (ILcom/example/amr_dec/encode/AmrEncInterface/Mode;[S[BI)I38 */
39 JNIEXPORT jint JNICALL Java_com_example_amrcodec_encode_AmrEncInterface_Encodeamr(40 JNIEnv *env, jobject job2, jint*nativePointer, jobject mode,41 jshortArray speech, jbyteArray out, jint forceSpeech) {42
43 jsize inLen = env->GetArrayLength(speech);44 jshort inBuf[inLen];45 env->GetShortArrayRegion(speech, 0, inLen, inBuf);46 jclass jcz = env->GetObjectClass(mode);47 jmethodID getNameMethod = env->GetMethodID(jcz, "name",48 "()Ljava/lang/String;");49 jstring modevalue = (jstring) env->CallObjectMethod(mode, getNameMethod);50 const char * valueNative = env->GetStringUTFChars(modevalue, 0);51 /*为了使用C++中定义的模式,需要做以下的映射*/
52 Mode cmode ;53 if(((strcmp(valueNative, "MR475") == 0)))54 cmode =MR475;55 if(((strcmp(valueNative, "MR515") == 0)))56 cmode =MR515;57 if(((strcmp(valueNative, "MR59") == 0)))58 cmode =MR59;59 if(((strcmp(valueNative, "MR67") == 0)))60 cmode =MR67;61 if(((strcmp(valueNative, "MR74") == 0)))62 cmode =MR74;63 if(((strcmp(valueNative, "MR795") == 0)))64 cmode =MR795;65 if(((strcmp(valueNative, "MR102") == 0)))66 cmode =MR102;67 if(((strcmp(valueNative, "MR122") == 0)))68 cmode =MR122;69
70 jsize outLen = env->GetArrayLength(out);71 jbyte outBuf[outLen];72 intencodeLength;73
74 encodeLength = Encoder_Interface_Encode(nativePointer, cmode, (const short*) inBuf,75 (unsigned char*) outBuf, forceSpeech);76
77 //env->ReleaseByteArrayElements(in, inBuf, JNI_ABORT);//no need - GetByteArrayRegion handles this
78 env->SetByteArrayRegion(out, 0, outLen, outBuf);79 returnencodeLength;80
81 }82
83 #ifdef __cplusplus84 }85 #endif
86 #endif
View Code
JAVA 本地函数部分代码(可以合在一起写,也可以放到其他文件里面去,不一定要单独写一个类)
1 package com.example.amrcodec.encode;2
3
4 public classAmrEncInterface {5 //本地函数的声明
6
7 public enumMode{8 MR475 ,/*4.75 kbps*/
9 MR515, /*5.15 kbps*/
10 MR59, /*5.90 kbps*/
11 MR67, /*6.70 kbps*/
12 MR74, /*7.40 kbps*/
13 MR795, /*7.95 kbps*/
14 MR102, /*10.2 kbps*/
15 MR122, /*12.2 kbps*/
16 MRDTX, /*DTX*/
17 N_MODES /*Not Used*/
18 }19
20
21 public static native int initEncamr(intini);22 public static native void exitEncamr(intencode);23 public static native int Encodeamr(int gae, Mode mode, short[] in, byte[] outbuffer, intfroceSpeech);24 static{25
26 System.loadLibrary("amr_dec");27
28 }29
30
31 }
View Code
1 package com.example.amrcodec.decode;2
3 //本地函数的声明
4 public classAmrDecInterface{5
6 public static native intinitDecamr();7 public static native void exitDecamr(intdecode);8 public static native int Decodeamr(int gae, byte[] in, short[] outbuffer, intunused);9 static{10
11 System.loadLibrary("amr_dec");12
13 }14
15
16 }
View Code
B.Wav文件的读写
关于wav和amr文件转换的c或者c++的代码有很多,别人做的java的相关函数也有一些,通过参考这些文件同时搞清楚amr文件和wav文件的存储结构,就可