基于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文件的存储结构,就可以自己编写出来java编写的转换代码。下图是wav文件头的格式:

WAV文件头,44个字节

这里还是贴一下读写wav文件的代码(也是参考网上的代码,点此链接,做一点修改就可以了)。

WaveReader.java

1 packagecom.example.amrcodec;2

3 importjava.io.BufferedInputStream;4 importjava.io.File;5 importjava.io.FileInputStream;6 importjava.io.FileNotFoundException;7 importjava.io.IOException;8

9 importcom.example.amrcodec.InvalidWaveException;10

11 public classWaveReader {12 private static final int WAV_HEADER_CHUNK_ID = 0x52494646; //"RIFF"

13 private static final int WAV_FORMAT = 0x57415645; //"WAVE"

14 private static final int WAV_FORMAT_CHUNK_ID = 0x666d7420; //"fmt "

15 private static final int WAV_DATA_CHUNK_ID = 0x64617461; //"data"

16 private static final int STREAM_BUFFER_SIZE = 4096;17

18 privateFile mInFile;19 privateBufferedInputStream mInStream;20

21 private intmSampleRate;22 private intmChannels;23 private intmSampleBits;24 private intmFileSize;25 private intmDataSize;26

27

28 /**

29 * Constructor; initializes WaveReader to read from given file30 *31 *@parampath path to input file32 *@paramname name of input file33 */

34 publicWaveReader(String path, String name) {35 this.mInFile = new File(path + File.separator +name);36 }37

38 /**

39 * Constructor; initializes WaveReader to read from given file40 *41 *@paramfile handle to input file42 */

43 publicWaveReader(File file) {44 this.mInFile =file;45 }46

47 /**

48 * Open WAV file for reading49 *50 *@throwsFileNotFoundException if input file does not exist51 *@throwsInvalidWaveException if input file is not a valid WAVE file52 *@throwsIOException if I/O error occurred during file read53 */

54 public void openWave() throwsFileNotFoundException, InvalidWaveException, IOException {55 FileInputStream fileStream = newFileInputStream(mInFile);56 mInStream = newBufferedInputStream(fileStream, STREAM_BUFFER_SIZE);57

58 int headerId = readUnsignedInt(mInStream); //should be "RIFF"

59 if (headerId !=WAV_HEADER_CHUNK_ID) {60 throw new InvalidWaveException(String.format("Invalid WAVE header chunk ID: %d", headerId));61 }62 mFileSize = readUnsignedIntLE(mInStream); //length of header

63 int format = readUnsignedInt(mInStream); //should be "WAVE"

64 if (format !=WAV_FORMAT) {65 throw new InvalidWaveException("Invalid WAVE format");66 }67

68 int formatId = readUnsignedInt(mInStream); //should be "fmt "

69 if (formatId !=WAV_FORMAT_CHUNK_ID) {70 throw new InvalidWaveException("Invalid WAVE format chunk ID");71 }72 int formatSize =readUnsignedIntLE(mInStream);73 if (formatSize != 16) {74

75 }76 int audioFormat =readUnsignedShortLE(mInStream);77 if (audioFormat != 1) {78 throw new InvalidWaveException("Not PCM WAVE format");79 }80 mChannels =readUnsignedShortLE(mInStream);81 mSampleRate =readUnsignedIntLE(mInStream);82 int byteRate =readUnsignedIntLE(mInStream);83 int blockAlign =readUnsignedShortLE(mInStream);84 mSampleBits =readUnsignedShortLE(mInStream);85

86 int dataId =readUnsignedInt(mInStream);87 if (dataId !=WAV_DATA_CHUNK_ID) {88 throw new InvalidWaveException("Invalid WAVE data chunk ID");89 }90 mDataSize =readUnsignedIntLE(mInStream);91 }92

93 /**

94 * Get sample rate95 *96 *@returninput file's sample rate97 */

98 public intgetSampleRate() {99 returnmSampleRate;100 }101

102 /**

103 * Get number of channels104 *105 *@returnnumber of channels in input file106 */

107 public intgetChannels() {108 returnmChannels;109 }110

111 /**

112 * Get PCM format, S16LE or S8LE113 *114 *@returnnumber of bits per sample115 */

116 public intgetPcmFormat() {117 returnmSampleBits;118 }119

120 /**

121 * Get file size122 *123 *@returntotal input file size in bytes124 */

125 public intgetFileSize() {126 return mFileSize + 8;127 }128

129 /**

130 * Get input file's audio data size131 * Basically file size without headers included132 *133 *@returnaudio data size in bytes134 */

135 public intgetDataSize() {136 returnmDataSize;137 }138

139 /**

140 * Get input file length141 *142 *@returnlength of file in seconds143 */

144 public intgetLength() {145 if (mSampleRate == 0 || mChannels == 0 || (mSampleBits + 7) / 8 == 0) {146 return 0;147 } else{148 return mDataSize / (mSampleRate * mChannels * ((mSampleBits + 7) / 8));149 }150 }151

152 /**

153 * Read audio data from input file (mono)154 *155 *@paramdst mono audio data output buffer156 *@paramnumSamples number of samples to read157 *158 *@returnnumber of samples read159 *160 *@throwsIOException if file I/O error occurs161 */

162 public int read(short[] dst, int numSamples) throwsIOException {163 if (mChannels != 1) {164 return -1;165 }166

167 byte[] buf = new byte[numSamples * 2];168 int index = 0;169 int bytesRead = mInStream.read(buf, 0, numSamples * 2);170

171 for (int i = 0; i < bytesRead; i+=2) {172 dst[index] = byteToShortLE(buf[i], buf[i+1]);173 index++;174 }175

176 returnindex;177 }178

179 /**

180 * Read audio data from input file (stereo)181 *182 *@paramleft left channel audio output buffer183 *@paramright right channel audio output buffer184 *@paramnumSamples number of samples to read185 *186 *@returnnumber of samples read187 *188 *@throwsIOException if file I/O error occurs189 */

190 public int read(short[] left, short[] right, int numSamples) throwsIOException {191 if (mChannels != 2) {192 return -1;193 }194 byte[] buf = new byte[numSamples * 4];195 int index = 0;196 int bytesRead = mInStream.read(buf, 0, numSamples * 4);197

198 for (int i = 0; i < bytesRead; i+=2) {199 short val = byteToShortLE(buf[0], buf[i+1]);200 if (i % 4 == 0) {201 left[index] =val;202 } else{203 right[index] =val;204 index++;205 }206 }207

208 returnindex;209 }210

211 /**

212 * Close WAV file. WaveReader object cannot be used again following this call.213 *214 *@throwsIOException if I/O error occurred closing filestream215 */

216 public void closeWaveFile() throwsIOException {217 if (mInStream != null) {218 mInStream.close();219 }220 }221

222 private static short byteToShortLE(byte b1, byteb2) {223 return (short) (b1 & 0xFF | ((b2 & 0xFF) << 8));224 }225

226 private static int readUnsignedInt(BufferedInputStream in) throwsIOException {227 intret;228 byte[] buf = new byte[4];229 ret =in.read(buf);230 if (ret == -1) {231 return -1;232 } else{233 return (((buf[0] & 0xFF) << 24)234 | ((buf[1] & 0xFF) << 16)235 | ((buf[2] & 0xFF) << 8)236 | (buf[3] & 0xFF));237 }238 }239

240 private static int readUnsignedIntLE(BufferedInputStream in) throwsIOException {241 intret;242 byte[] buf = new byte[4];243 ret =in.read(buf);244 if (ret == -1) {245 return -1;246 } else{247 return (buf[0] & 0xFF

248 | ((buf[1] & 0xFF) << 8)249 | ((buf[2] & 0xFF) << 16)250 | ((buf[3] & 0xFF) << 24));251 }252 }253

254 private static short readUnsignedShortLE(BufferedInputStream in) throwsIOException {255 intret;256 byte[] buf = new byte[2];257 ret = in.read(buf, 0, 2);258 if (ret == -1) {259 return -1;260 } else{261 return byteToShortLE(buf[0], buf[1]);262 }263 }264 }

View Code

WaveWriter.java

1 packagecom.example.amrcodec;2

3

4 importjava.io.BufferedOutputStream;5 importjava.io.File;6 importjava.io.FileOutputStream;7 importjava.io.IOException;8 importjava.io.RandomAccessFile;9

10 public classWaveWriter11 {12 private static final int OUTPUT_STREAM_BUFFER = 16384;13

14 privateFile mOutFile;15 privateBufferedOutputStream mOutStream;16

17 private intmSampleRate;18 private intmChannels;19 private intmSampleBits;20

21 intmBytesWritten;22

23

24 /**

25 * Constructor; initializes WaveWriter with file name and path26 *27 *@parampath output file path28 *@paramname output file name29 *@paramsampleRate output sample rate30 *@paramchannels number of channels31 *@paramsampleBits number of bits per sample (S8LE, S16LE)32 */

33 public WaveWriter(String path, String name, int sampleRate, intchannels,34 intsampleBits) {35 this.mOutFile = new File(path + File.separator +name);36

37 this.mSampleRate =sampleRate;38 this.mChannels =channels;39 this.mSampleBits =sampleBits;40

41 this.mBytesWritten = 0;42 }43

44 /**

45 * Constructor; initializes WaveWriter with file name and path46 *47 *@paramfile output file handle48 *@paramsampleRate output sample rate49 *@paramchannels number of channels50 *@paramsampleBits number of bits per sample (S8LE, S16LE)51 */

52 public WaveWriter(File file, int sampleRate, int channels, intsampleBits) {53 this.mOutFile =file;54

55 this.mSampleRate =sampleRate;56 this.mChannels =channels;57 this.mSampleBits =sampleBits;58

59 this.mBytesWritten = 0;60 }61

62 /**

63 * Create output WAV file64 *65 *@returnwhether file creation succeeded66 *67 *@throwsIOException if file I/O error occurs allocating header68 */

69 public boolean createWaveFile() throwsIOException {70 if(mOutFile.exists()) {71 mOutFile.delete();72

73 }74

75 System.out.println("marker1!");76 if(mOutFile.createNewFile()) {77

78 FileOutputStream fileStream = newFileOutputStream(mOutFile);79 mOutStream = newBufferedOutputStream(fileStream, OUTPUT_STREAM_BUFFER);80 //write 44 bytes of space for the header

81 mOutStream.write(new byte[44]); //只是把头空出来,在文件流关闭之前写入相关数据即可。

82 return true;83 }84

85 return false;86 }87

88 /**

89 * Write audio data to output file (mono). Does90 * nothing if output file is not mono channel.91 *92 *@paramlittleendian mono audio data input buffer93 *@paramoffset offset into src buffer94 *@paramlength buffer size in number of samples95 *96 *@throwsIOException if file I/O error occurs97 */

98 public void write(byte[] littleendian, int offset, int length) throwsIOException {99 if (mChannels != 1) {100 return;101 }102 if (offset >length) {103 throw new IndexOutOfBoundsException(String.format("offset %d is greater than length %d", offset, length));104 }105 for (int i = offset; i < length; i++) {106 writeUnsignedBYTELE(mOutStream, littleendian[i]);107 mBytesWritten += 1;108 }109 }110

111 /**

112 * Write audio data to output file (stereo). Does113 * nothing if output file is not stereo channel.114 *115 *@paramleft left channel audio data buffer116 *@paramright right channel audio data buffer117 *@paramoffset offset into left/right buffers118 *@paramlength buffer size in number of samples119 *120 *@throwsIOException if file I/O error occurs121 */

122 public void write(short[] left, short[] right, int offset, int length) throwsIOException {123 if (mChannels != 2) {124 return;125 }126 if (offset >length) {127 throw new IndexOutOfBoundsException(String.format("offset %d is greater than length %d", offset, length));128 }129 for (int i = offset; i < length; i++) {130 writeUnsignedShortLE(mOutStream, left[i]);131 writeUnsignedShortLE(mOutStream, right[i]);132 mBytesWritten += 4;133 }134 }135

136 /**

137 * Close output WAV file and write WAV header. WaveWriter138 * cannot be used again following this call.139 *140 *@throwsIOException if file I/O error occurs writing WAV header141 */

142 public void closeWaveFile() throwsIOException {143 if (mOutStream != null) {144 this.mOutStream.flush();145 this.mOutStream.close();146 }147 writeWaveHeader();148 }149

150 private void writeWaveHeader() throwsIOException {151 //rewind to beginning of the file

152 RandomAccessFile file = new RandomAccessFile(this.mOutFile, "rw");153 file.seek(0);154

155 int bytesPerSec = (mSampleBits + 7) / 8;156

157 file.writeBytes("RIFF"); //WAV chunk header

158 file.writeInt(Integer.reverseBytes(mBytesWritten + 36)); //WAV chunk size

159 file.writeBytes("WAVE"); //WAV format

160

161 file.writeBytes("fmt "); //format subchunk header

162 file.writeInt(Integer.reverseBytes(16)); //format subchunk size

163 file.writeShort(Short.reverseBytes((short) 1)); //audio format

164 file.writeShort(Short.reverseBytes((short) mChannels)); //number of channels

165 file.writeInt(Integer.reverseBytes(mSampleRate)); //sample rate

166 file.writeInt(Integer.reverseBytes(mSampleRate * mChannels * bytesPerSec)); //byte rate

167 file.writeShort(Short.reverseBytes((short) (mChannels * bytesPerSec))); //block align

168 file.writeShort(Short.reverseBytes((short) mSampleBits)); //bits per sample

169

170 file.writeBytes("data"); //data subchunk header

171 file.writeInt(Integer.reverseBytes(mBytesWritten)); //data subchunk size

172 System.out.println("写入数据长度为:"+mBytesWritten);173 file.close();174 //file = null;

175 }176

177 private static void writeUnsignedShortLE(BufferedOutputStream stream,shortsample)178 throwsIOException {179 //write already writes the lower order byte of this short

180 stream.write(sample);181 //stream.write((sample >> 8));

182 }183 /*写一个字节的数据到输出流,原来的writeUnsignedShortLE是写两个字节,是有问题的,双声道的未作测试*/

184 private static void writeUnsignedBYTELE(BufferedOutputStream stream,bytesample)185 throwsIOException {186 //write already writes the lower order byte of this short

187 stream.write(sample);188 }189 }

View Code

C.amr编解码部分和图形界面部分

编解码部分的代码主要就是参考test文件夹下的c代码,照着写成JAVA的代码就可以了,难点就是JAVA文件流的读写了。

AMR文件存储结构

arm2wav

1 packagecom.example.amrcodec;2

3 importjava.io.File;4 importjava.io.FileInputStream;5 importjava.io.IOException;6

7 importcom.example.amrcodec.decode.AmrDecInterface;8

9 public classamr2wav {10 /*From WmfDecBytesPerFrame in dec_input_format_tab.cpp*/

11 int sizes[] = { 12, 13, 15, 17, 19, 20, 26, 31, 5, 6, 5, 5, 0, 0, 0, 0};12 private static int mNativeAmrDecoder = 0; //the pointer to the native13 //amr-nb decoder14

15

16

17 //读取amr文件头的6个字节

18

19

20 publicfileInfor[] convertamr(File input, File output) {21

22 byte[] header = new byte[6];23 int fileSize = (int) input.length();24 System.out.println(fileSize);25 fileInfor[] finfor = new fileInfor[2];26 /*amr文件输入信息,这里我们测试的是单声道的文件,文件头开始是“#AMR!/n”*/

27

28 finfor[0] = newfileInfor();29 finfor[1] = newfileInfor();30 finfor[0].fileType = "amr";31 finfor[0].fileSize =fileSize;32 finfor[0].sampleRate = 8000;33 finfor[0].bitsPerSample = 16;34 finfor[0].channels = 1;35

36 FileInputStream in = null;37 int count = 0;38 try{39 in = newFileInputStream(input);40 count = in.read(header, 0, 6);41 } catch(Exception e) {42

43 e.printStackTrace();44 System.out.println("读入文件错误!");45 returnfinfor;46 }47 System.out.println("开始创建文件!");48 if (count != 6 || header[0] != '#' || header[1] != '!'

49 || header[2] != 'A' || header[3] != 'M' || header[4] != 'R'

50 || header[5] != '\n') {51 System.out.println("BAD HEADER"); //检查文件头是否是由#!AMR/n开始的

52 }53 mNativeAmrDecoder =AmrDecInterface.initDecamr();54 System.out.println("开始创建文件1!");55 try{56 //创建WaveWriter对象

57

58

59 finfor[1].fileType = "wav";60 finfor[1].sampleRate = 8000;61 finfor[1].bitsPerSample = 16;62 finfor[1].channels = 1;63

64 WaveWriter wav = new WaveWriter(output, finfor[1].sampleRate,65 finfor[1].channels, finfor[1].bitsPerSample);66

67 boolean flag =wav.createWaveFile();68 if (!flag) {69 System.out.println("Failed to createWaveFile.");70 in.close();71 returnfinfor;72 }73 int counter = 0;74

75 while (true) {76 byte[] buffer = new byte[500];77

78 if (in == null) {79 break;80 }81 //读入模式字节

82 int n = in.read(buffer, 0, 1);83 if (n != 1)84 break;85 //按照模式字节显示的数据包的大小来读数据

86 int size = sizes[(buffer[0] >> 3) & 0x0f];87 if (size <= 0)88 break;89 n = in.read(buffer, 1, 1 *size);90 if (n !=size)91 break;92

93 short[] outbuffer = new short[160];94 counter++;95 System.out.println(counter);96

97 //System.out.println("开始写入wav文件!");

98 AmrDecInterface.Decodeamr(mNativeAmrDecoder, buffer, outbuffer,99 0);100

101 byte littleendian[] = new byte[320];102 int j = 0;103 for (int i = 0; i < 160; i++) {104 littleendian[j] = (byte) (outbuffer[i] >> 0 & 0xff);105 littleendian[j + 1] = (byte) (outbuffer[i] >> 8 & 0xff);106 j = j + 2;107 }108

109 wav.write(littleendian, 0, 320);110 }111 finfor[1].fileSize = wav.mBytesWritten+44; //wav文件大小

112

113 wav.closeWaveFile();114 in.close();115 System.out.println("wav文件写完!");116 AmrDecInterface.exitDecamr(mNativeAmrDecoder);117

118 } catch(IOException e) {119

120 }121 returnfinfor;122

123 }124

125 }

View Code

wav2amr

1 package com.example.amrcodec;2

3 import java.io.BufferedOutputStream;4 import java.io.File;5 import java.io.FileNotFoundException;6 import java.io.FileOutputStream;7 import java.io.IOException;8

9 import com.example.amrcodec.encode.AmrEncInterface;10 import com.example.amrcodec.encode.AmrEncInterface.Mode;11

12 public classwav2amr {13

14 Mode req_mode = Mode.MR475; //先指定编码速率,后面再改

15 int dtx = 0;16 int PCM_FRAME_SIZE = 160; //8khz 8000*0.02=160

17 byte[] header = new byte[] { '#', '!', 'A', 'M', 'R', '\n' }; //header,magic18 //words!

19

20 private static int mNativeAmrEncoder = 0; //the pointer to the native21 //amr-nb encoder

22

23 publicfileInfor[] convertwav(File input, File output)24 throws FileNotFoundException, InvalidWaveException, IOException {25

26 fileInfor[] finfor = new fileInfor[2];27 finfor[0] = newfileInfor();28 finfor[1] = newfileInfor();29

30 int fileSize = (int) input.length();31 System.out.println(fileSize);32 finfor[0].fileType = "wav";33 finfor[0].fileSize =fileSize;34 int OUTPUT_STREAM_BUFFER = 16384;35 /*File output stream*/

36 BufferedOutputStream mOutStream;37 FileOutputStream fileStream = newFileOutputStream(output);38 mOutStream = newBufferedOutputStream(fileStream, OUTPUT_STREAM_BUFFER);39

40 WaveReader wav = newWaveReader(input);41 wav.openWave();42 finfor[0].sampleRate =wav.getSampleRate();43 finfor[0].bitsPerSample =wav.getPcmFormat();44 finfor[0].channels =wav.getChannels();45

46 finfor[1].fileType = "amr";47 finfor[1].sampleRate = 8000;48 finfor[1].bitsPerSample = 16;49 finfor[1].channels = 1;50 mNativeAmrEncoder =AmrEncInterface.initEncamr(dtx);51 if (header.length != 6) {52 System.out.println("BAD HEADER!");53 mOutStream.close();54 returnfinfor;55

56 }57 mOutStream.write(header, 0, 6); //write the header

58 int counter = 0;59 int bytecount = 0;60 while (true) {61 counter++;62 System.out.println(counter);63

64 short[] speech = new short[160];65 byte[] outbuf = new byte[500];66 intreadCount;67 int channels = wav.getChannels();//这里我们只测试单声道的,也先不考虑PCM每样点编码比特数68 //int mSampleBits = wav.getPcmFormat();//获取PCM帧每样点比特数

69 int inputSize = channels * PCM_FRAME_SIZE; //每次读取的大小160(单声道)

70 readCount =wav.read(speech, inputSize);71 if (readCount != 160) { //跳过有问题的帧

72 System.out.println("READ FILE ERROR!");73 break;74 }75 int outLength =AmrEncInterface.Encodeamr(mNativeAmrEncoder,76 req_mode, speech, outbuf, 0);77 //System.out.println(outLength);//这个长度是固定的32byte帧长

78 mOutStream.write(outbuf, 0, outLength);79 bytecount +=outLength;80

81 }82 finfor[1].fileSize = bytecount + 6;83 wav.closeWaveFile();84 mOutStream.close();85 AmrEncInterface.exitEncamr(mNativeAmrEncoder);86

87 returnfinfor;88

89 }90

91 }

View Code

activity这部分的内容写的比较简单就两个按钮,几个TextView 布局、美化也没怎么做。

Mainactivity

1 package com.example.amrcodec;2

3 import java.io.File;4 import java.io.FileNotFoundException;5 import java.io.IOException;6

7 import android.os.Bundle;8 import android.os.Environment;9 import android.app.Activity;10 import android.view.Menu;11 import android.view.View;12 import android.widget.*;13

14 public classMainActivity extends Activity {15

16 privateButton mybutton;17 //private TextView mytextView;

18 privateButton mybutton2;19 //private TextView mytextView2;

20 privateTextView mytextView3;21 privateTextView mytextView4;22 privateTextView mytextView5;23 privateTextView mytextView6;24 private File sdCardDir = Environment.getExternalStorageDirectory(); //SD 卡的路径

25

26 @Override27 protected voidonCreate(Bundle savedInstanceState) {28 super.onCreate(savedInstanceState);29 setContentView(R.layout.activity_main);30

31 mybutton =(Button) findViewById(R.id.mybutton1);32 //mytextView = (TextView) findViewById(R.id.mytextview1);

33 mybutton2 =(Button) findViewById(R.id.mybutton2);34 //mytextView2 = (TextView) findViewById(R.id.mytextview2);

35 mytextView3 =(TextView) findViewById(R.id.mytextview3);36 mytextView4 =(TextView) findViewById(R.id.mytextview4);37 mytextView5 =(TextView) findViewById(R.id.mytextview5);38 mytextView6 =(TextView) findViewById(R.id.mytextview6);39 mytextView3.setText(" "+'\n'+" "+'\n'+" "+'\n'+" "+'\n'+" "+'\n');40 mytextView4.setText(" "+'\n'+" "+'\n'+" "+'\n'+" "+'\n'+" "+'\n');41 mytextView5.setText(" "+'\n'+" "+'\n'+" "+'\n'+" "+'\n'+" "+'\n');42 mytextView6.setText(" "+'\n'+" "+'\n'+" "+'\n'+" "+'\n'+" "+'\n');43 mybutton.setOnClickListener(newButton.OnClickListener() {44

45 @Override46 public voidonClick(View v) {47 //TODO Auto-generated method stub

48

49

50 amr2wav aa = newamr2wav();51 System.out.println("创建对象");52 fileInfor[] displayInfor = new fileInfor[2];53 File amrFile = new File(sdCardDir,"test.amr"); //待转换的文件

54

55

56 File output11 = new File(sdCardDir,"testout.wav"); //输出的转换文件

57

58 displayInfor =aa.convertamr(amrFile,output11);59 mytextView3.setText("输入:"+'\n'+"文件格式:"+displayInfor[0].fileType+'\n'+"文件大小:"+displayInfor[0].fileSize+'\n'+"采样频率:"+displayInfor[0].sampleRate+'\n'+"编码比特:"+displayInfor[0].bitsPerSample+'\n'+"声道数:"+displayInfor[0].channels);60 mytextView4.setText("输出:"+'\n'+"文件格式:"+displayInfor[1].fileType+'\n'+"文件大小:"+displayInfor[1].fileSize+'\n'+"采样频率:"+displayInfor[1].sampleRate+'\n'+"编码比特:"+displayInfor[1].bitsPerSample+'\n'+"声道数:"+displayInfor[1].channels);61

62 }63

64 });65

66 mybutton2.setOnClickListener(newButton.OnClickListener() {67

68 @Override69 public voidonClick(View v) {70 //TODO Auto-generated method stub

71

72 wav2amr bb = newwav2amr();73 System.out.println("创建对象");74 File wavFile = new File(sdCardDir,"test.wav"); //待转换的文件

75

76

77 File output12 = new File(sdCardDir,"testout.amr"); //输出的转换文件

78 fileInfor[] displayInfor = new fileInfor[2];79 try{80 displayInfor =bb.convertwav(wavFile,output12);81 mytextView5.setText("输入:"+'\n'+"文件格式:"+displayInfor[0].fileType+'\n'+"文件大小:"+displayInfor[0].fileSize+'\n'+"采样频率:"+displayInfor[0].sampleRate+'\n'+"编码比特:"+displayInfor[0].bitsPerSample+'\n'+"声道数:"+displayInfor[0].channels);82 mytextView6.setText("输出:"+'\n'+"文件格式:"+displayInfor[1].fileType+'\n'+"文件大小:"+displayInfor[1].fileSize+'\n'+"采样频率:"+displayInfor[1].sampleRate+'\n'+"编码比特:"+displayInfor[1].bitsPerSample+'\n'+"声道数:"+displayInfor[1].channels);83

84 } catch(FileNotFoundException e) {85 //TODO Auto-generated catch block

86 e.printStackTrace();87 } catch(InvalidWaveException e) {88 //TODO Auto-generated catch block

89 e.printStackTrace();90 } catch(IOException e) {91 //TODO Auto-generated catch block

92 e.printStackTrace();93 }94

95 }96

97 });98

99

100 }101

102

103 @Override104 publicboolean onCreateOptionsMenu(Menu menu) {105 //Inflate the menu; this adds items to the action bar if it is present.

106 getMenuInflater().inflate(R.menu.main, menu);107 return true;108 }109

110 }

View Code

layout(全部采用线性布局)

1

2 xmlns:tools="http://schemas.android.com/tools"

3 android:layout_width="match_parent"

4 android:layout_height="match_parent"

5 android:background="#FFFFFF"

6 android:orientation="vertical"

7 tools:context=".MainActivity" >

8

9

11 android:layout_height="wrap_content"

12 android:orientation="vertical" >

13

14

16 android:layout_width="match_parent"

17 android:layout_height="0dp"

18 android:layout_weight="1"

19 android:text="@string/str1"

20 android:textColor="#0033ff" />

21

22

23

25 android:layout_height="wrap_content"

26 android:orientation="horizontal" >

27

28

30 android:layout_width="match_parent"

31 android:layout_height="wrap_content"

32 android:layout_gravity="left"

33 android:layout_weight="1"

34 android:textColor="#ff6600"

35 android:textSize="15sp" />

36

37

39 android:layout_width="match_parent"

40 android:layout_height="wrap_content"

41 android:layout_gravity="left"

42 android:layout_weight="1"

43 android:textColor="#ff6600"

44 android:textSize="15sp" />

45

46

47

49 android:layout_height="wrap_content"

50 android:orientation="vertical" >

51

52

54 android:layout_width="match_parent"

55 android:layout_height="0dp"

56 android:layout_weight="1"

57 android:text="@string/str2"

58 android:textColor="#0033ff" />

59

60

61

63 android:layout_height="wrap_content"

64 android:orientation="horizontal" >

65

66

68 android:layout_width="match_parent"

69 android:layout_height="wrap_content"

70 android:layout_gravity="left"

71 android:layout_weight="1"

72 android:textColor="#ff6600"

73 android:textSize="15sp" />

74

75

77 android:layout_width="match_parent"

78 android:layout_height="wrap_content"

79 android:layout_gravity="left"

80 android:layout_weight="1"

81 android:textColor="#ff6600"

82 android:textSize="15sp" />

83

84

85

View Code

AndroidManifest.xml

1 <?xml version="1.0" encoding="utf-8"?>

2

3 package="com.example.amrcodec"

4 android:versionCode="1"

5 android:versionName="1.0" >

6

7

9 android:targetSdkVersion="18" />

10

11

12

14 android:icon="@drawable/ic_launcher"

15 android:label="@string/app_name"

16 android:theme="@style/AppTheme" >

17

19 android:label="@string/app_name" >

20

21

22

23

24

25

26

27

28

View Code

增加这两行是为了读写SD卡目录的文件。很关键,不然在Android虚拟机中是没有权限读写SD卡的。

3、Android apk  demo的生成

做这个demo只是为了验证接口可行、so文件可调用,所以尽量做了简化。为了测试,需要在SD卡目录下放置需要转码的amr 和 wav文件,测试文件可以从这里下载AMR Test Files 、WAV Test Files,选择单声道(mono)某一码率的文件,输出的文件也在SD卡目  录下。demo apk转码的界面  如下图所示,显示了输入输出文件的一些基本信息。

java amr wav_AMR编解码库的实现相关推荐

  1. Android amr语音编解码解惑 【转http://blog.csdn.net/xyz_lmn/article/category/922246】

    Android amr语音编解码解惑 androidAndroidARMarm声音采集 关于android中的语音压缩编码,今天算是好好的研究了一下,有了小小的心得: 首先关于采集到得声音源的格式是P ...

  2. Emoji表情编解码库XXL-EMOJI

    2019独角兽企业重金招聘Python工程师标准>>> <Emoji表情编解码库XXL-EMOJI> 一.简介 1.1 概述 XXL-EMOJI 是一个灵活可扩展的Emo ...

  3. Java实现BASE64编解码

    Java实现BASE64编解码 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs BASE64和其它类似的编码算法通经常使用于转换二进制数据为文本数据,其目 ...

  4. Java对base64编解码总结

    概述 java对base64编解码的通用处理方法. 关于base64编码Encode和Decode编码的几种方式 Base64是一种能将任意Binary资料用64种字元组合成字串的方法,而这个Bina ...

  5. 【Windows Esp32】基于 libjpeg-9e 编解码库的视频播放器

    目录 一.音视频基础 1.1.图像编码 1.2.视频编码 1.3.AVI 文件结构 二.TF卡基础 三.Windows上播放音视频 3.1.在 Windows 下使用 vs2019 编译 libjpe ...

  6. 一些常见的AAC编解码库

    一些AAC编解码库 - FAAC, FAAD/FAAD2    ---编码只有AAC LC - neroAACenc ---非商业可以使用(提供编码工具) - FFmpeg's native AAC ...

  7. 移植Opus音频编解码库到FreeScale iMX6q(飞凌嵌入式的OKMX6Q-C开发板)平台

    移植Opus音频编解码库到FreeScale iMX6q(飞凌嵌入式的OKMX6Q-C开发板)平台 交叉编译器 使用飞凌提供的最新版交叉编译工具链,fsl-imx-x11-glibc-x86_64-m ...

  8. RK-MPP硬件编解码库介绍和使用

    一.下载RK-MPP硬件编解码库 下载链接:https://github.com/rockchip-linux/mpp 二.RK-MPP库介绍         1.资料来源:MPP 开发参考.pdf ...

  9. Java版流媒体编解码和图像处理(JavaCPP+FFmpeg)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos FFmpeg.JavaCPP.JavaCV的关系 先简 ...

最新文章

  1. java微信菜单获取openid_微信二次开发点击菜单openId的获取
  2. wp7——sqlite数据库操作 from:http://blog.csdn.net/wp_lijin/article/details/7370790
  3. visio二次开发___事件篇___事件分类
  4. 七、压缩与解压缩命令
  5. 克服大数据技能差距的策略
  6. python中output使用_python-02.输入Input/输出Output
  7. SAP Spartacus delivery mode页面设计
  8. [css] 你对视网膜(Retina)分辨率有了解吗?有没有在实际中使用过?
  9. ast.literal_eval(转)
  10. Qt工作笔记-QSort的基本使用
  11. 数据结构之树和二叉树的定义和性质
  12. deny后加to do还是doing_become to do还是doing
  13. STM32CubeMX的安装
  14. java servlet spring_带着新人简单看看servlet到springmvc
  15. 洛谷4316绿豆蛙的归宿
  16. 关于Clipboard剪切板获取值为NULL问题解决方法
  17. 黑客入侵Windows XP的几个常用方法
  18. matlab中zeros()函数与ones()函数用法
  19. 自己画的STM32老是烧掉原因分析
  20. SAP库存--历史库存相关数据,以及库存变化对应表的数据变化,可以用于库龄分析报表逻辑设计。

热门文章

  1. 2018-2019-2 20189201 《网络攻防实践》第六周作业
  2. unity给自己的模型加上镜面效果
  3. 腾讯精选 T89 格雷编码
  4. php根据目录 创建文件,php创建文件目录,及删除目录和文件
  5. Angular4.0 动画
  6. m4a怎么转换成mp4
  7. 浅谈学习掌握linux系统的优势
  8. NSX-T 系列:第 23部分 - 备份与恢复
  9. 金山快盘关闭了,大家有什么网盘推荐?
  10. 黑马程序员,黑马论坛----黑马.Net 10期 史无前例的就业速度12天就业71.43%