├── assets
└── test.pcm
├── ic_launcher-web.png
├── res
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xhdpi
│ └── ic_launcher.png
├── drawable-xxhdpi
│ └── ic_launcher.png
├── values
│ ├── strings.xml
│ └── styles.xml
└── layout
│ └── activity_main.xml
├── README.md
├── gen
└── com
│ └── asia
│ └── pcm2amr
│ ├── BuildConfig.java
│ └── R.java
├── .settings
└── org.eclipse.jdt.core.prefs
├── .classpath
├── project.properties
├── proguard-project.txt
├── .project
├── AndroidManifest.xml
└── src
├── com
└── asia
│ └── pcm2amr
│ ├── TransferThread.java
│ ├── AmrEncoder.java
│ ├── ToastUtil.java
│ └── MainActivity.java
└── android
└── media
└── AmrInputStream.java
/assets/test.pcm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dev-Wiki/Pcm2Amr/HEAD/assets/test.pcm
--------------------------------------------------------------------------------
/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dev-Wiki/Pcm2Amr/HEAD/ic_launcher-web.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dev-Wiki/Pcm2Amr/HEAD/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dev-Wiki/Pcm2Amr/HEAD/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dev-Wiki/Pcm2Amr/HEAD/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dev-Wiki/Pcm2Amr/HEAD/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pcm2Amr
2 |
3 | 具体文章参见此处:[Android中Pcm转Amr](http://blog.devwiki.net/index.php/2015/06/23/android-audio-pcm-to-amr.html)
4 |
--------------------------------------------------------------------------------
/gen/com/asia/pcm2amr/BuildConfig.java:
--------------------------------------------------------------------------------
1 | /** Automatically generated file. DO NOT MODIFY */
2 | package com.asia.pcm2amr;
3 |
4 | public final class BuildConfig {
5 | public final static boolean DEBUG = true;
6 | }
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
3 | org.eclipse.jdt.core.compiler.compliance=1.6
4 | org.eclipse.jdt.core.compiler.source=1.6
5 |
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Pcm2Amr
5 | Hello world!
6 | 开始转换
7 | 请稍候
8 | 文件正在转换中,请稍候…
9 | 转码成功!
10 | AMR文件在SD卡根目录下,名为:test.amr
11 |
12 |
13 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-21
15 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
11 |
12 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Pcm2Amr
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
12 |
13 |
18 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/com/asia/pcm2amr/TransferThread.java:
--------------------------------------------------------------------------------
1 | package com.asia.pcm2amr;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 |
6 | import android.content.Context;
7 | import android.os.Environment;
8 |
9 | public class TransferThread extends Thread{
10 |
11 | private TransferCallback callback;
12 | private Context context;
13 | public TransferThread(Context context, TransferCallback callback){
14 | this.callback = callback;
15 | this.context = context;
16 | }
17 |
18 | @Override
19 | public void run() {
20 | transfer();
21 | }
22 |
23 | private void transfer(){
24 | String rootPath = Environment.getExternalStorageDirectory().getPath();
25 | String amrPath = rootPath + "/test.amr";
26 | try {
27 | InputStream pcmStream = context.getAssets().open("test.pcm");
28 | AmrEncoder.pcm2Amr(pcmStream, amrPath);
29 | callback.onSuccess();
30 | } catch (IOException e) {
31 | callback.onFailed();
32 | e.printStackTrace();
33 | }
34 | }
35 |
36 |
37 | public static interface TransferCallback{
38 |
39 | void onSuccess();
40 |
41 | void onFailed();
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/com/asia/pcm2amr/AmrEncoder.java:
--------------------------------------------------------------------------------
1 | package com.asia.pcm2amr;
2 |
3 | import java.io.FileInputStream;
4 | import java.io.FileNotFoundException;
5 | import java.io.FileOutputStream;
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.io.OutputStream;
9 |
10 | import android.media.AmrInputStream;
11 |
12 | public class AmrEncoder {
13 |
14 | public static void pcm2Amr(String pcmPath , String amrPath) {
15 | FileInputStream fis;
16 | try {
17 | fis = new FileInputStream(pcmPath);
18 | pcm2Amr(fis, amrPath);
19 | fis.close();
20 | } catch (FileNotFoundException e1) {
21 | e1.printStackTrace();
22 | } catch (IOException e) {
23 | e.printStackTrace();
24 | }
25 | }
26 |
27 | public static void pcm2Amr(InputStream pcmStream, String amrPath) {
28 | try {
29 | AmrInputStream ais = new AmrInputStream(pcmStream);
30 | OutputStream out = new FileOutputStream(amrPath);
31 | byte[] buf = new byte[4096];
32 | int len = -1;
33 | /*
34 | * 下面的AMR的文件头,缺少这几个字节是不行的
35 | */
36 | out.write(0x23);
37 | out.write(0x21);
38 | out.write(0x41);
39 | out.write(0x4D);
40 | out.write(0x52);
41 | out.write(0x0A);
42 | while((len = ais.read(buf)) >0){
43 | out.write(buf,0,len);
44 | }
45 | out.close();
46 | ais.close();
47 | } catch (FileNotFoundException e) {
48 | e.printStackTrace();
49 | } catch (IOException e) {
50 | e.printStackTrace();
51 | }
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/gen/com/asia/pcm2amr/R.java:
--------------------------------------------------------------------------------
1 | /* AUTO-GENERATED FILE. DO NOT MODIFY.
2 | *
3 | * This class was automatically generated by the
4 | * aapt tool from the resource data it found. It
5 | * should not be modified by hand.
6 | */
7 |
8 | package com.asia.pcm2amr;
9 |
10 | public final class R {
11 | public static final class attr {
12 | }
13 | public static final class drawable {
14 | public static final int ic_launcher=0x7f020000;
15 | }
16 | public static final class id {
17 | public static final int btn_start=0x7f060000;
18 | public static final int hint=0x7f060001;
19 | }
20 | public static final class layout {
21 | public static final int activity_main=0x7f030000;
22 | }
23 | public static final class string {
24 | public static final int app_name=0x7f040000;
25 | public static final int btn_start=0x7f040002;
26 | public static final int hello_world=0x7f040001;
27 | public static final int success_hint=0x7f040005;
28 | public static final int transfer_result=0x7f040006;
29 | public static final int transfer_wait_message=0x7f040004;
30 | public static final int transfer_wait_title=0x7f040003;
31 | }
32 | public static final class style {
33 | /**
34 | Base application theme, dependent on API level. This theme is replaced
35 | by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
36 |
37 |
38 | Theme customizations available in newer API levels can go in
39 | res/values-vXX/styles.xml, while customizations related to
40 | backward-compatibility can go here.
41 |
42 | */
43 | public static final int AppBaseTheme=0x7f050000;
44 | /** Application theme.
45 | All customizations that are NOT specific to a particular API-level can go here.
46 | */
47 | public static final int AppTheme=0x7f050001;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/com/asia/pcm2amr/ToastUtil.java:
--------------------------------------------------------------------------------
1 | package com.asia.pcm2amr;
2 |
3 | import android.content.Context;
4 | import android.content.res.Resources.NotFoundException;
5 | import android.widget.Toast;
6 |
7 | /**
8 | * Toast工具类
9 | * @author Asia
10 | *
11 | */
12 | public class ToastUtil {
13 | //工具类私有化构造器,防止实例化
14 | private ToastUtil(){}
15 |
16 | //控制是否显示Toast
17 | private static boolean isShow = true;
18 |
19 | private static String getShowInfo(Context context, int id){
20 | String info = "";
21 | try {
22 | info = context.getResources().getString(id);
23 | } catch (NotFoundException e) {
24 | e.printStackTrace();
25 | }
26 | return info;
27 | }
28 |
29 | /**
30 | * 显示短时间的Toast
31 | * @param context 上下文
32 | * @param info 要显示的信息
33 | */
34 | public static void showShort(Context context, String info){
35 | if (isShow) {
36 | Toast.makeText(context, info, Toast.LENGTH_SHORT).show();
37 | }
38 | }
39 |
40 | /**
41 | * 显示短时间的Toast
42 | * @param context 上下文
43 | * @param id 要显示的文字资源id
44 | */
45 | public static void showShort(Context context, int id){
46 | if (isShow) {
47 | String info = getShowInfo(context, id);
48 | Toast.makeText(context, info, Toast.LENGTH_SHORT).show();
49 | }
50 | }
51 |
52 | /**
53 | * 显示长时间的Toast
54 | * @param context 上下文
55 | * @param info 要显示的信息
56 | */
57 | public static void showLong(Context context, String info){
58 | if (isShow) {
59 | Toast.makeText(context, info, Toast.LENGTH_LONG).show();
60 | }
61 | }
62 |
63 | /**
64 | * 显示长时间的Toast
65 | * @param context 上下文
66 | * @param id 要显示的文字资源id
67 | */
68 | public static void showLong(Context context, int id){
69 | if (isShow) {
70 | String info = getShowInfo(context, id);
71 | Toast.makeText(context, info, Toast.LENGTH_LONG).show();
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/com/asia/pcm2amr/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.asia.pcm2amr;
2 |
3 |
4 | import com.asia.pcm2amr.TransferThread.TransferCallback;
5 |
6 | import android.app.Activity;
7 | import android.app.ProgressDialog;
8 | import android.os.Bundle;
9 | import android.view.View;
10 | import android.view.View.OnClickListener;
11 | import android.widget.Button;
12 | import android.widget.TextView;
13 |
14 | public class MainActivity extends Activity implements OnClickListener{
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.activity_main);
20 |
21 | initView();
22 | }
23 |
24 | private TextView hintView;
25 | private Button startButton;
26 | private void initView() {
27 | startButton = (Button) findViewById(R.id.btn_start);
28 | startButton.setOnClickListener(this);
29 | hintView = (TextView) findViewById(R.id.hint);
30 | }
31 |
32 | @Override
33 | public void onClick(View v) {
34 | if (v.getId() == R.id.btn_start) {
35 | transferButtonClicked();
36 | }
37 | }
38 |
39 | private void transferButtonClicked(){
40 | showWaitDialog();
41 | startTransfer();
42 | }
43 |
44 | private ProgressDialog waitDialog;
45 | private void showWaitDialog(){
46 | waitDialog = new ProgressDialog(this);
47 | waitDialog.setTitle(getResources().getString(R.string.transfer_wait_title));
48 | waitDialog.setMessage(getResources().getString(R.string.transfer_wait_message));
49 | waitDialog.show();
50 | }
51 |
52 | private void startTransfer() {
53 | new TransferThread(this, new TransferCallback() {
54 |
55 | @Override
56 | public void onSuccess() {
57 | transferSuccess();
58 | }
59 |
60 | @Override
61 | public void onFailed() {
62 | }
63 | }).start();
64 | }
65 |
66 | private void transferSuccess() {
67 | runOnUiThread(new Runnable() {
68 |
69 | @Override
70 | public void run() {
71 | waitDialog.dismiss();
72 | hintView.setText(getResources().getString(R.string.transfer_result));
73 | ToastUtil.showShort(MainActivity.this, R.string.success_hint);
74 | }
75 | });
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/android/media/AmrInputStream.java:
--------------------------------------------------------------------------------
1 | package android.media;
2 |
3 | import java.io.InputStream;
4 | import java.io.IOException;
5 |
6 | /**
7 | * AmrInputStream
8 | *
9 | */
10 | public final class AmrInputStream extends InputStream {
11 |
12 | static {
13 | System.loadLibrary("media_jni");
14 | }
15 |
16 | private final static String TAG = "AmrInputStream";
17 |
18 | // frame is 20 msec at 8.000 khz
19 | private final static int SAMPLES_PER_FRAME = 8000 * 20 / 1000;
20 |
21 | // pcm input stream
22 | private InputStream mInputStream;
23 |
24 | // native handle
25 | private int mGae;
26 |
27 | // result amr stream
28 | private byte[] mBuf = new byte[SAMPLES_PER_FRAME * 2];
29 | private int mBufIn = 0;
30 | private int mBufOut = 0;
31 |
32 | // helper for bytewise read()
33 | private byte[] mOneByte = new byte[1];
34 |
35 | /**
36 | * Create a new AmrInputStream, which converts 16 bit PCM to AMR
37 | * @param inputStream InputStream containing 16 bit PCM.
38 | */
39 | public AmrInputStream(InputStream inputStream) {
40 | mInputStream = inputStream;
41 | mGae = GsmAmrEncoderNew();
42 | GsmAmrEncoderInitialize(mGae);
43 | }
44 |
45 | @Override
46 | public int read() throws IOException {
47 | int rtn = read(mOneByte, 0, 1);
48 | return rtn == 1 ? (0xff & mOneByte[0]) : -1;
49 | }
50 |
51 | @Override
52 | public int read(byte[] b) throws IOException {
53 | return read(b, 0, b.length);
54 | }
55 |
56 | @Override
57 | public int read(byte[] b, int offset, int length) throws IOException {
58 | if (mGae == 0) throw new IllegalStateException("not open");
59 |
60 | // local buffer of amr encoded audio empty
61 | if (mBufOut >= mBufIn) {
62 | // reset the buffer
63 | mBufOut = 0;
64 | mBufIn = 0;
65 |
66 | // fetch a 20 msec frame of pcm
67 | for (int i = 0; i < SAMPLES_PER_FRAME * 2; ) {
68 | int n = mInputStream.read(mBuf, i, SAMPLES_PER_FRAME * 2 - i);
69 | if (n == -1) return -1;
70 | i += n;
71 | }
72 |
73 | // encode it
74 | mBufIn = GsmAmrEncoderEncode(mGae, mBuf, 0, mBuf, 0);
75 | }
76 |
77 | // return encoded audio to user
78 | if (length > mBufIn - mBufOut) length = mBufIn - mBufOut;
79 | System.arraycopy(mBuf, mBufOut, b, offset, length);
80 | mBufOut += length;
81 |
82 | return length;
83 | }
84 |
85 | @Override
86 | public void close() throws IOException {
87 | try {
88 | if (mInputStream != null) mInputStream.close();
89 | } finally {
90 | mInputStream = null;
91 | try {
92 | if (mGae != 0) GsmAmrEncoderCleanup(mGae);
93 | } finally {
94 | try {
95 | if (mGae != 0) GsmAmrEncoderDelete(mGae);
96 | } finally {
97 | mGae = 0;
98 | }
99 | }
100 | }
101 | }
102 |
103 | @Override
104 | protected void finalize() throws Throwable {
105 | if (mGae != 0) {
106 | close();
107 | throw new IllegalStateException("someone forgot to close AmrInputStream");
108 | }
109 | }
110 |
111 | //
112 | // AudioRecord JNI interface
113 | //
114 | private static native int GsmAmrEncoderNew();
115 | private static native void GsmAmrEncoderInitialize(int gae);
116 | private static native int GsmAmrEncoderEncode(int gae,
117 | byte[] pcm, int pcmOffset, byte[] amr, int amrOffset) throws IOException;
118 | private static native void GsmAmrEncoderCleanup(int gae);
119 | private static native void GsmAmrEncoderDelete(int gae);
120 |
121 | }
122 |
--------------------------------------------------------------------------------