AMR编解码库的实现

  基于opencore-amr实现amr-nb编码和解码,在Android上完成wav文件与amr文件格式的相互转换。wav和amr文件读写部分主要参考了opencore中的test文件夹下的例子,以及  IOS音频格式之AMR和WAV互转(更新支持amrv7s)

 

1、opencore-amr的下载和编译

  参考链接:Using OpenCORE AMR NB and WB Codecs(linux下的编译), GitHub代码SourgeForge代码

  编译之后我们可以得到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 <jni.h>
 2 #include <interf_dec.h>
 3  
 4 #ifndef _Included_com_example_amrcodec_decode_AmrDecInterface
 5 #define _Included_com_example_amrcodec_decode_AmrDecInterface
 6 #ifdef __cplusplus
 7 extern "C" {
 8 #endif
 9 /*
10  * Class:     com_example_amr_dec_decode_AmrDecInterface
11  * Method:    initamr
12  * Signature: ()V
13  */
14 JNIEXPORT int JNICALL Java_com_example_amrcodec_decode_AmrDecInterface_initDecamr
15 (JNIEnv *env, jobject ccc) {
16  
17     return (jint) Decoder_Interface_init();
18 }
19  
20 /*
21  * Class:     com_example_amr_dec_decode_AmrDecInterface
22  * Method:    exitamr
23  * Signature: ()V
24  */
25 JNIEXPORT int JNICALL Java_com_example_amrcodec_decode_AmrDecInterface_exitDecamr
26 (JNIEnv *env, jobject lll,jint* nativePointer) {
27  
28     Decoder_Interface_exit(nativePointer);
29  
30 }
31  
32 /*
33  * Class:     com_example_amr_dec_decode_AmrDecInterface
34  * Method:    Decodeamr
35  * Signature: ([B[B)V
36  */
37 JNIEXPORT void JNICALL Java_com_example_amrcodec_decode_AmrDecInterface_Decodeamr
38 (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     short outBuf[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 __cplusplus
55 }
56 #endif
57 #endif
View Code

编码部分JNI cpp代码:(多出来的枚举型很讨厌)

 1 /* DO NOT EDIT THIS FILE - it is machine generated */
 2 #include <jni.h>
 3 #include<interf_enc.h>
 4 #include<string.h>
 5 /* Header for class com_example_amr_dec_encode_AmrEncInterface */
 6  
 7 #ifndef _Included_com_example_amrcodec_encode_AmrEncInterface
 8 #define _Included_com_example_amrcodec_encode_AmrEncInterface
 9 #ifdef __cplusplus
10 extern "C" {
11 #endif
12 /*
13  * Class:     com_example_amr_dec_encode_AmrEncInterface
14  * Method:    initEncamr
15  * Signature: (I)I
16  */
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_AmrEncInterface
24  * Method:    exitEncamr
25  * Signature: (I)V
26  */
27 JNIEXPORT void JNICALL Java_com_example_amrcodec_encode_AmrEncInterface_exitEncamr
28 (JNIEnv *env, jobject job1, jint* nativePointer) {
29  
30     Encoder_Interface_exit(nativePointer);
31  
32 }
33  
34 /*
35  * Class:     com_example_amr_dec_encode_AmrEncInterface
36  * Method:    Encodeamr
37  * Signature: (ILcom/example/amr_dec/encode/AmrEncInterface/Mode;[S[BI)I
38  */
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     int encodeLength;
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     return encodeLength;
80  
81 }
82  
83 #ifdef __cplusplus
84 }
85 #endif
86 #endif
View Code

JAVA 本地函数部分代码(可以合在一起写,也可以放到其他文件里面去,不一定要单独写一个类)

 1 package com.example.amrcodec.encode;
 2  
 3  
 4 public class AmrEncInterface {
 5     //本地函数的声明
 6          
 7     public enum Mode{
 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(int ini);
22         public static native void exitEncamr(int encode);
23         public static native int Encodeamr(int gae, Mode mode, short[] in, byte[] outbuffer, int froceSpeech);
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  class AmrDecInterface{
 5      
 6     public static native int initDecamr();
 7     public static native void exitDecamr(int decode);
 8     public static native int Decodeamr(int gae, byte[] in, short[] outbuffer, int unused);
 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 package com.example.amrcodec;
  2 
  3 import java.io.BufferedInputStream;
  4 import java.io.File;
  5 import java.io.FileInputStream;
  6 import java.io.FileNotFoundException;
  7 import java.io.IOException;
  8 
  9 import com.example.amrcodec.InvalidWaveException;
 10 
 11 public class WaveReader {
 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     private File mInFile;
 19     private BufferedInputStream mInStream;
 20 
 21     private int mSampleRate;
 22     private int mChannels;
 23     private int mSampleBits;
 24     private int mFileSize;
 25     private int mDataSize;
 26 
 27 
 28     /**
 29      * Constructor; initializes WaveReader to read from given file
 30      *
 31      * @param path  path to input file
 32      * @param name  name of input file
 33      */
 34     public WaveReader(String path, String name) {
 35         this.mInFile = new File(path + File.separator + name);
 36     }
 37 
 38     /**
 39      * Constructor; initializes WaveReader to read from given file
 40      *
 41      * @param file  handle to input file
 42      */
 43     public WaveReader(File file) {
 44         this.mInFile = file;
 45     }
 46 
 47     /**
 48      * Open WAV file for reading
 49      *
 50      * @throws FileNotFoundException if input file does not exist
 51      * @throws InvalidWaveException if input file is not a valid WAVE file
 52      * @throws IOException if I/O error occurred during file read
 53      */
 54     public void openWave() throws FileNotFoundException, InvalidWaveException, IOException {
 55         FileInputStream fileStream = new FileInputStream(mInFile);
 56         mInStream = new BufferedInputStream(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 rate
 95      *
 96      * @return input file's sample rate
 97      */
 98     public int getSampleRate() {
 99         return mSampleRate;
100     }
101 
102     /**
103      * Get number of channels
104      *
105      * @return number of channels in input file
106      */
107     public int getChannels() {
108         return mChannels;
109     }
110 
111     /**
112      * Get PCM format, S16LE or S8LE
113      *
114      * @return number of bits per sample
115      */
116     public int getPcmFormat() {
117         return mSampleBits;
118     }
119     
120     /**
121      * Get file size
122      *
123      * @return total input file size in bytes
124      */
125     public int getFileSize() {
126         return mFileSize + 8;
127     }
128 
129     /**
130      * Get input file's audio data size
131      * Basically file size without headers included
132      *
133      * @return audio data size in bytes
134      */
135     public int getDataSize() {
136         return mDataSize;
137     }
138 
139     /**
140      * Get input file length
141      *
142      * @return length of file in seconds
143      */
144     public int getLength() {
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      * @param dst  mono audio data output buffer
156      * @param numSamples  number of samples to read
157      *
158      * @return number of samples read
159      *
160      * @throws IOException if file I/O error occurs
161      */
162     public int read(short[] dst, int numSamples) throws IOException {
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         return index;
177     }
178 
179     /**
180      * Read audio data from input file (stereo)
181      *
182      * @param left  left channel audio output buffer
183      * @param right  right channel audio output buffer
184      * @param numSamples  number of samples to read
185      *
186      * @return number of samples read
187      *
188      * @throws IOException if file I/O error occurs
189      */
190     public int read(short[] left, short[] right, int numSamples) throws IOException {
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         return index;
209     }
210 
211     /**
212      * Close WAV file. WaveReader object cannot be used again following this call.
213      *
214      * @throws IOException if I/O error occurred closing filestream
215      */
216     public void closeWaveFile() throws IOException {
217         if (mInStream != null) {
218             mInStream.close();
219         }
220     }
221     
222     private static short byteToShortLE(byte b1, byte b2) {
223         return (short) (b1 & 0xFF | ((b2 & 0xFF) << 8));
224     }
225 
226     private static int readUnsignedInt(BufferedInputStream in) throws IOException {
227         int ret;
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) throws IOException {
241         int ret;
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) throws IOException {
255         int ret;
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 package com.example.amrcodec;
  2 
  3 
  4 import java.io.BufferedOutputStream;
  5 import java.io.File;
  6 import java.io.FileOutputStream;
  7 import java.io.IOException;
  8 import java.io.RandomAccessFile;
  9 
 10 public class WaveWriter
 11 {
 12     private static final int OUTPUT_STREAM_BUFFER = 16384;
 13 
 14     private File mOutFile;
 15     private BufferedOutputStream mOutStream;
 16 
 17     private int mSampleRate;
 18     private int mChannels;
 19     private int mSampleBits;
 20 
 21     int mBytesWritten;
 22 
 23 
 24     /**
 25      * Constructor; initializes WaveWriter with file name and path
 26      *
 27      * @param path  output file path
 28      * @param name  output file name
 29      * @param sampleRate  output sample rate
 30      * @param channels  number of channels
 31      * @param sampleBits  number of bits per sample (S8LE, S16LE)
 32      */
 33     public WaveWriter(String path, String name, int sampleRate, int channels,
 34             int sampleBits) {
 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 path
 46      *
 47      * @param file  output file handle
 48      * @param sampleRate  output sample rate
 49      * @param channels  number of channels
 50      * @param sampleBits  number of bits per sample (S8LE, S16LE)
 51      */
 52     public WaveWriter(File file, int sampleRate, int channels, int sampleBits) {
 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 file
 64      *
 65      * @return whether file creation succeeded
 66      *
 67      * @throws IOException if file I/O error occurs allocating header
 68      */
 69     public boolean createWaveFile() throws IOException {
 70         if (mOutFile.exists()) {
 71             mOutFile.delete();
 72       
 73         }
 74     
 75         System.out.println("marker1!");
 76         if (mOutFile.createNewFile()) {
 77                         
 78             FileOutputStream fileStream = new FileOutputStream(mOutFile);
 79             mOutStream = new BufferedOutputStream(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). Does
 90      * nothing if output file is not mono channel.
 91      *
 92      * @param littleendian  mono audio data input buffer
 93      * @param offset offset into src buffer
 94      * @param length  buffer size in number of samples
 95      *
 96      * @throws IOException if file I/O error occurs
 97      */
 98     public void write(byte[] littleendian, int offset, int length) throws IOException {
 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). Does
113      * nothing if output file is not stereo channel.
114      *
115      * @param left  left channel audio data buffer
116      * @param right  right channel audio data buffer
117      * @param offset  offset into left/right buffers
118      * @param length  buffer size in number of samples
119      *
120      * @throws IOException if file I/O error occurs
121      */
122     public void write(short[] left, short[] right, int offset, int length) throws IOException {
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. WaveWriter
138      * cannot be used again following this call.
139      *
140      * @throws IOException if file I/O error occurs writing WAV header
141      */
142     public void closeWaveFile() throws IOException {
143         if (mOutStream != null) {
144             this.mOutStream.flush();
145             this.mOutStream.close();
146         }
147         writeWaveHeader();
148     }
149 
150     private void writeWaveHeader() throws IOException {
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,short sample)
178             throws IOException {
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,byte sample)
185             throws IOException {
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文件来说,Header是"#AMR!/n",双声道的是不一样的,具体查阅RFC 4867     RTP Payload Format for AMR and AMR-WB

 

AMR文件存储结构

 

arm2wav

 

  1 package com.example.amrcodec;
  2 
  3 import java.io.File;
  4 import java.io.FileInputStream;
  5 import java.io.IOException;
  6 
  7 import com.example.amrcodec.decode.AmrDecInterface;
  8 
  9 public class amr2wav {
 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 native
 13                                             // amr-nb decoder
 14   
 15     
 16     
 17     // 读取amr文件头的6个字节
 18 
 19     
 20     public fileInfor[] 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] = new fileInfor();
 29         finfor[1] = new fileInfor();
 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 = new FileInputStream(input);
 40             count = in.read(header, 0, 6);
 41         } catch (Exception e) {
 42 
 43             e.printStackTrace();
 44             System.out.println("读入文件错误!");
 45             return finfor;
 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                 return finfor;
 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         return  finfor;
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 class wav2amr {
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,magic
18                                                                     // words!
19  
20     private static int mNativeAmrEncoder = 0; // the pointer to the native
21                                                 // amr-nb encoder
22  
23     public fileInfor[] convertwav(File input, File output)
24             throws FileNotFoundException, InvalidWaveException, IOException {
25  
26         fileInfor[] finfor = new fileInfor[2];
27         finfor[0] = new fileInfor();
28         finfor[1] = new fileInfor();
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 = new FileOutputStream(output);
38         mOutStream = new BufferedOutputStream(fileStream, OUTPUT_STREAM_BUFFER);
39  
40         WaveReader wav = new WaveReader(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             return finfor;
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             int readCount;
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         return finfor;
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 class MainActivity extends Activity {
 15 
 16     private Button mybutton;
 17     //private TextView mytextView;
 18     private Button mybutton2;
 19     //private TextView mytextView2;
 20     private TextView mytextView3;
 21     private TextView mytextView4;
 22     private TextView mytextView5;
 23     private TextView mytextView6;
 24     private File sdCardDir = Environment.getExternalStorageDirectory();  //SD 卡的路径
 25 
 26     @Override
 27     protected void onCreate(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(new Button.OnClickListener() {
 44 
 45             @Override
 46             public void onClick(View v) {
 47                 // TODO Auto-generated method stub
 48 
 49                 
 50                 amr2wav aa = new amr2wav();
 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(new Button.OnClickListener() {
 67 
 68             @Override
 69             public void onClick(View v) {
 70                 // TODO Auto-generated method stub
 71                 
 72                 wav2amr bb = new wav2amr();
 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     @Override
104     public boolean 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 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 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     <LinearLayout
10         android:layout_width="match_parent"
11         android:layout_height="wrap_content"
12         android:orientation="vertical" >
13 
14         <Button
15             android:id="@+id/mybutton1"
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     </LinearLayout>
22 
23     <LinearLayout
24         android:layout_width="match_parent"
25         android:layout_height="wrap_content"
26         android:orientation="horizontal" >
27 
28         <TextView
29             android:id="@+id/mytextview3"
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         <TextView
38             android:id="@+id/mytextview4"
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     </LinearLayout>
46 
47     <LinearLayout
48         android:layout_width="match_parent"
49         android:layout_height="wrap_content"
50         android:orientation="vertical" >
51 
52         <Button
53             android:id="@+id/mybutton2"
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     </LinearLayout>
60 
61     <LinearLayout
62         android:layout_width="match_parent"
63         android:layout_height="wrap_content"
64         android:orientation="horizontal" >
65 
66         <TextView
67             android:id="@+id/mytextview5"
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         <TextView
76             android:id="@+id/mytextview6"
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     </LinearLayout>
84 
85 </LinearLayout>
View Code

AndroidManifest.xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 3     package="com.example.amrcodec"
 4     android:versionCode="1"
 5     android:versionName="1.0" >
 6 
 7     <uses-sdk
 8         android:minSdkVersion="8"
 9         android:targetSdkVersion="18" />
10 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
11     <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
12     <application
13         android:allowBackup="true"
14         android:icon="@drawable/ic_launcher"
15         android:label="@string/app_name"
16         android:theme="@style/AppTheme" >
17         <activity
18             android:name="com.example.amrcodec.MainActivity"
19             android:label="@string/app_name" >
20             <intent-filter>
21                 <action android:name="android.intent.action.MAIN" />
22 
23                 <category android:name="android.intent.category.LAUNCHER" />
24             </intent-filter>
25         </activity>
26     </application>
27 
28 </manifest>
View Code

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

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

 

3、Android apk  demo的生成

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

 

posted @ 2014-03-28 17:34  bigoffer  阅读(11128)  评论(6编辑  收藏  举报