Android MP3录音实现

    给APP做语音功能,必须考虑到IOS和Android平台的通用性。wav录音质量高,文件太大,AAC和AMR格式在IOS平台却不支持,所以采用libmp3lame把AudioRecord音频流直接转换成MP3格式。

       声明一下,代码参考了http://blog.csdn.net/cboy017/article/details/8455629,这里只是借花献佛,把整个流程写得更详细。

       这里采用的是最新的lame-3.99.5.tar。

       可以去Lame官网下载,博文最后也有CSDN下载地址。官网地址:http://lame.sourceforge.net/

       如果你对JNI和NDK完全不熟悉的话,请看前一篇博文 Android NDK开发之入门教程

      先看一下项目文件目录:

       

       开始Coding吧!

 

1  新建项目AndroidLameMP3。

2  创建JNI目录。

3 下载lame-3.99.5.tar。

       解压,把子文件夹libmp3lame中的非.h和.c格式的文件删除后的剩余的所有文件和include下的lame.h放进一个新建的lame-3.99.5_libmp3lame文件夹中,最后把整个lame-3.99.5_libmp3lame文件夹拷贝到JNI目录下。

4  在com.example.lamemp3下创建MP3Recorder.class:

MP3Recorder.class

[java] view plain copy
 
  1. public class MP3Recorder {  
  2.   
  3.     private String mFilePath = null;  
  4.     private int sampleRate = 0;  
  5.     private boolean isRecording = false;  
  6.     private boolean isPause = false;  
  7.     private Handler handler = null;  
  8.   
  9.     /** 
  10.      * 开始录音 
  11.      */  
  12.     public static final int MSG_REC_STARTED = 1;  
  13.   
  14.     /** 
  15.      * 结束录音 
  16.      */  
  17.     public static final int MSG_REC_STOPPED = 2;  
  18.   
  19.     /** 
  20.      * 暂停录音 
  21.      */  
  22.     public static final int MSG_REC_PAUSE = 3;  
  23.   
  24.     /** 
  25.      * 继续录音 
  26.      */  
  27.     public static final int MSG_REC_RESTORE = 4;  
  28.   
  29.     /** 
  30.      * 缓冲区挂了,采样率手机不支持 
  31.      */  
  32.     public static final int MSG_ERROR_GET_MIN_BUFFERSIZE = -1;  
  33.   
  34.     /** 
  35.      * 创建文件时扑街了 
  36.      */  
  37.     public static final int MSG_ERROR_CREATE_FILE = -2;  
  38.   
  39.     /** 
  40.      * 初始化录音器时扑街了 
  41.      */  
  42.     public static final int MSG_ERROR_REC_START = -3;  
  43.   
  44.     /** 
  45.      * 录音的时候出错 
  46.      */  
  47.     public static final int MSG_ERROR_AUDIO_RECORD = -4;  
  48.   
  49.     /** 
  50.      * 编码时挂了 
  51.      */  
  52.     public static final int MSG_ERROR_AUDIO_ENCODE = -5;  
  53.   
  54.     /** 
  55.      * 写文件时挂了 
  56.      */  
  57.     public static final int MSG_ERROR_WRITE_FILE = -6;  
  58.   
  59.     /** 
  60.      * 没法关闭文件流 
  61.      */  
  62.     public static final int MSG_ERROR_CLOSE_FILE = -7;  
  63.   
  64.     public MP3Recorder() {  
  65.         this.sampleRate = 8000;  
  66.     }  
  67.   
  68.     /** 
  69.      * 开片 
  70.      */  
  71.     public void start() {  
  72.         if (isRecording) {  
  73.             return;  
  74.         }  
  75.   
  76.         new Thread() {  
  77.             @Override  
  78.             public void run() {  
  79.                 String fileDir = StorageUtil.getSDPath() + "LameMP3/Voice/";  
  80.   
  81.                 File dir = new File(fileDir);  
  82.                 if (!dir.exists()) {  
  83.                     dir.mkdirs();  
  84.                 }  
  85.   
  86.                 mFilePath = StorageUtil.getSDPath() + "LameMP3/Voice/"  
  87.                         + System.currentTimeMillis() + ".mp3";  
  88.   
  89.                 System.out.println(mFilePath);  
  90.                 android.os.Process  
  91.                         .setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);  
  92.                 // 根据定义好的几个配置,来获取合适的缓冲大小  
  93.                 final int minBufferSize = AudioRecord.getMinBufferSize(  
  94.                         sampleRate, AudioFormat.CHANNEL_IN_MONO,  
  95.                         AudioFormat.ENCODING_PCM_16BIT);  
  96.                 if (minBufferSize < 0) {  
  97.                     if (handler != null) {  
  98.                         handler.sendEmptyMessage(MSG_ERROR_GET_MIN_BUFFERSIZE);  
  99.                     }  
  100.                     return;  
  101.                 }  
  102.                 AudioRecord audioRecord = new AudioRecord(  
  103.                         MediaRecorder.AudioSource.MIC, sampleRate,  
  104.                         AudioFormat.CHANNEL_IN_MONO,  
  105.                         AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 2);  
  106.   
  107.                 // 5秒的缓冲  
  108.                 short[] buffer = new short[sampleRate * (16 / 8) * 1 * 5];  
  109.                 byte[] mp3buffer = new byte[(int) (7200 + buffer.length * 2 * 1.25)];  
  110.   
  111.                 FileOutputStream output = null;  
  112.                 try {  
  113.                     output = new FileOutputStream(new File(mFilePath));  
  114.                 } catch (FileNotFoundException e) {  
  115.                     if (handler != null) {  
  116.                         handler.sendEmptyMessage(MSG_ERROR_CREATE_FILE);  
  117.                     }  
  118.                     return;  
  119.                 }  
  120.                 MP3Recorder.init(sampleRate, 1, sampleRate, 32);  
  121.                 isRecording = true; // 录音状态  
  122.                 isPause = false; // 录音状态  
  123.                 try {  
  124.                     try {  
  125.                         audioRecord.startRecording(); // 开启录音获取音频数据  
  126.                     } catch (IllegalStateException e) {  
  127.                         // 不给录音...  
  128.                         if (handler != null) {  
  129.                             handler.sendEmptyMessage(MSG_ERROR_REC_START);  
  130.                         }  
  131.                         return;  
  132.                     }  
  133.   
  134.                     try {  
  135.                         // 开始录音  
  136.                         if (handler != null) {  
  137.                             handler.sendEmptyMessage(MSG_REC_STARTED);  
  138.                         }  
  139.   
  140.                         int readSize = 0;  
  141.                         boolean pause = false;  
  142.                         while (isRecording) {  
  143.                             /*--暂停--*/  
  144.                             if (isPause) {  
  145.                                 if (!pause) {  
  146.                                     handler.sendEmptyMessage(MSG_REC_PAUSE);  
  147.                                     pause = true;  
  148.                                 }  
  149.                                 continue;  
  150.                             }  
  151.                             if (pause) {  
  152.                                 handler.sendEmptyMessage(MSG_REC_RESTORE);  
  153.                                 pause = false;  
  154.                             }  
  155.                             /*--End--*/  
  156.                             /*--实时录音写数据--*/  
  157.                             readSize = audioRecord.read(buffer, 0,  
  158.                                     minBufferSize);  
  159.                             if (readSize < 0) {  
  160.                                 if (handler != null) {  
  161.                                     handler.sendEmptyMessage(MSG_ERROR_AUDIO_RECORD);  
  162.                                 }  
  163.                                 break;  
  164.                             } else if (readSize == 0) {  
  165.                                 ;  
  166.                             } else {  
  167.                                 int encResult = MP3Recorder.encode(buffer,  
  168.                                         buffer, readSize, mp3buffer);  
  169.                                 if (encResult < 0) {  
  170.                                     if (handler != null) {  
  171.                                         handler.sendEmptyMessage(MSG_ERROR_AUDIO_ENCODE);  
  172.                                     }  
  173.                                     break;  
  174.                                 }  
  175.                                 if (encResult != 0) {  
  176.                                     try {  
  177.                                         output.write(mp3buffer, 0, encResult);  
  178.                                     } catch (IOException e) {  
  179.                                         if (handler != null) {  
  180.                                             handler.sendEmptyMessage(MSG_ERROR_WRITE_FILE);  
  181.                                         }  
  182.                                         break;  
  183.                                     }  
  184.                                 }  
  185.                             }  
  186.                             /*--End--*/  
  187.                         }  
  188.                         /*--录音完--*/  
  189.                         int flushResult = MP3Recorder.flush(mp3buffer);  
  190.                         if (flushResult < 0) {  
  191.                             if (handler != null) {  
  192.                                 handler.sendEmptyMessage(MSG_ERROR_AUDIO_ENCODE);  
  193.                             }  
  194.                         }  
  195.                         if (flushResult != 0) {  
  196.                             try {  
  197.                                 output.write(mp3buffer, 0, flushResult);  
  198.                             } catch (IOException e) {  
  199.                                 if (handler != null) {  
  200.                                     handler.sendEmptyMessage(MSG_ERROR_WRITE_FILE);  
  201.                                 }  
  202.                             }  
  203.                         }  
  204.                         try {  
  205.                             output.close();  
  206.                         } catch (IOException e) {  
  207.                             if (handler != null) {  
  208.                                 handler.sendEmptyMessage(MSG_ERROR_CLOSE_FILE);  
  209.                             }  
  210.                         }  
  211.                         /*--End--*/  
  212.                     } finally {  
  213.                         audioRecord.stop();  
  214.                         audioRecord.release();  
  215.                     }  
  216.                 } finally {  
  217.                     MP3Recorder.close();  
  218.                     isRecording = false;  
  219.                 }  
  220.                 if (handler != null) {  
  221.                     handler.sendEmptyMessage(MSG_REC_STOPPED);  
  222.                 }  
  223.             }  
  224.         }.start();  
  225.     }  
  226.   
  227.     public void stop() {  
  228.         isRecording = false;  
  229.     }  
  230.   
  231.     public void pause() {  
  232.         isPause = true;  
  233.     }  
  234.   
  235.     public void restore() {  
  236.         isPause = false;  
  237.     }  
  238.   
  239.     public boolean isRecording() {  
  240.         return isRecording;  
  241.     }  
  242.   
  243.     public boolean isPaus() {  
  244.         if (!isRecording) {  
  245.             return false;  
  246.         }  
  247.         return isPause;  
  248.     }  
  249.   
  250.     public String getFilePath() {  
  251.         return mFilePath;  
  252.     }  
  253.   
  254.     /** 
  255.      * 录音状态管理 
  256.      *  
  257.      * @see RecMicToMp3#MSG_REC_STARTED 
  258.      * @see RecMicToMp3#MSG_REC_STOPPED 
  259.      * @see RecMicToMp3#MSG_REC_PAUSE 
  260.      * @see RecMicToMp3#MSG_REC_RESTORE 
  261.      * @see RecMicToMp3#MSG_ERROR_GET_MIN_BUFFERSIZE 
  262.      * @see RecMicToMp3#MSG_ERROR_CREATE_FILE 
  263.      * @see RecMicToMp3#MSG_ERROR_REC_START 
  264.      * @see RecMicToMp3#MSG_ERROR_AUDIO_RECORD 
  265.      * @see RecMicToMp3#MSG_ERROR_AUDIO_ENCODE 
  266.      * @see RecMicToMp3#MSG_ERROR_WRITE_FILE 
  267.      * @see RecMicToMp3#MSG_ERROR_CLOSE_FILE 
  268.      */  
  269.     public void setHandle(Handler handler) {  
  270.         this.handler = handler;  
  271.     }  
  272.   
  273.     /*--以下为Native部分--*/  
  274.     static {  
  275.         System.loadLibrary("mp3lame");  
  276.     }  
  277.   
  278.     /** 
  279.      * 初始化录制参数 
  280.      */  
  281.     public static void init(int inSamplerate, int outChannel,  
  282.             int outSamplerate, int outBitrate) {  
  283.         init(inSamplerate, outChannel, outSamplerate, outBitrate, 7);  
  284.     }  
  285.   
  286.     /** 
  287.      * 初始化录制参数 quality:0=很好很慢 9=很差很快 
  288.      */  
  289.     public native static void init(int inSamplerate, int outChannel,  
  290.             int outSamplerate, int outBitrate, int quality);  
  291.   
  292.     /** 
  293.      * 音频数据编码(PCM左进,PCM右进,MP3输出) 
  294.      */  
  295.     public native static int encode(short[] buffer_l, short[] buffer_r,  
  296.             int samples, byte[] mp3buf);  
  297.   
  298.     /** 
  299.      * 刷干净缓冲区 
  300.      */  
  301.     public native static int flush(byte[] mp3buf);  
  302.   
  303.     /** 
  304.      * 结束编码 
  305.      */  
  306.     public native static void close();  
  307. }  

5  在JNI文件夹下创建com_example_lamemp3_MP3Recorder.h头文件,在里面定义几个方法,然后在
com_example_lamemp3_MP3Recorder.c中实现。

com_example_lamemp3_MP3Recorder.h:

[cpp] view plain copy
 
  1. /* DO NOT EDIT THIS FILE - it is machine generated */  
  2. #include <jni.h>  
  3. /* Header for class com_kubility_demo_MP3Recorder */  
  4.   
  5. #ifndef _Included_com_example_lamemp3_MP3Recorder  
  6. #define _Included_com_example_lamemp3_MP3Recorder  
  7. #ifdef __cplusplus  
  8. extern "C" {  
  9. #endif  
  10. /* 
  11.  * Class:     com_kubility_demo_MP3Recorder 
  12.  * Method:    init 
  13.  * Signature: (IIIII)V 
  14.  */  
  15. JNIEXPORT void JNICALL Java_com_example_lamemp3_MP3Recorder_init  
  16.   (JNIEnv *, jclass, jint, jint, jint, jint, jint);  
  17.   
  18. /* 
  19.  * Class:     com_kubility_demo_MP3Recorder 
  20.  * Method:    encode 
  21.  * Signature: ([S[SI[B)I 
  22.  */  
  23. JNIEXPORT jint JNICALL Java_com_example_lamemp3_MP3Recorder_encode  
  24.   (JNIEnv *, jclass, jshortArray, jshortArray, jint, jbyteArray);  
  25.   
  26. /* 
  27.  * Class:     com_kubility_demo_MP3Recorder 
  28.  * Method:    flush 
  29.  * Signature: ([B)I 
  30.  */  
  31. JNIEXPORT jint JNICALL Java_com_example_lamemp3_MP3Recorder_flush  
  32.   (JNIEnv *, jclass, jbyteArray);  
  33.   
  34. /* 
  35.  * Class:     com_kubility_demo_MP3Recorder 
  36.  * Method:    close 
  37.  * Signature: ()V 
  38.  */  
  39. JNIEXPORT void JNICALL Java_com_example_lamemp3_MP3Recorder_close  
  40.   (JNIEnv *, jclass);  
  41.   
  42. #ifdef __cplusplus  
  43. }  
  44. #endif  
  45. #endif  

com_example_lamemp3_MP3Recorder.c:

[cpp] view plain copy
 
  1. #include "lame-3.99.5_libmp3lame/lame.h"  
  2. #include "com_example_lamemp3_MP3Recorder.h"  
  3.   
  4. static lame_global_flags *glf = NULL;  
  5.   
  6. JNIEXPORT void JNICALL Java_com_example_lamemp3_MP3Recorder_init(  
  7.         JNIEnv *env, jclass cls, jint inSamplerate, jint outChannel,  
  8.         jint outSamplerate, jint outBitrate, jint quality) {  
  9.     if (glf != NULL) {  
  10.         lame_close(glf);  
  11.         glf = NULL;  
  12.     }  
  13.     glf = lame_init();  
  14.     lame_set_in_samplerate(glf, inSamplerate);  
  15.     lame_set_num_channels(glf, outChannel);  
  16.     lame_set_out_samplerate(glf, outSamplerate);  
  17.     lame_set_brate(glf, outBitrate);  
  18.     lame_set_quality(glf, quality);  
  19.     lame_init_params(glf);  
  20. }  
  21.   
  22. JNIEXPORT jint JNICALL Java_com_example_lamemp3_MP3Recorder_encode(  
  23.         JNIEnv *env, jclass cls, jshortArray buffer_l, jshortArray buffer_r,  
  24.         jint samples, jbyteArray mp3buf) {  
  25.     jshort* j_buffer_l = (*env)->GetShortArrayElements(env, buffer_l, NULL);  
  26.   
  27.     jshort* j_buffer_r = (*env)->GetShortArrayElements(env, buffer_r, NULL);  
  28.   
  29.     const jsize mp3buf_size = (*env)->GetArrayLength(env, mp3buf);  
  30.     jbyte* j_mp3buf = (*env)->GetByteArrayElements(env, mp3buf, NULL);  
  31.   
  32.     int result = lame_encode_buffer(glf, j_buffer_l, j_buffer_r,  
  33.             samples, j_mp3buf, mp3buf_size);  
  34.   
  35.     (*env)->ReleaseShortArrayElements(env, buffer_l, j_buffer_l, 0);  
  36.     (*env)->ReleaseShortArrayElements(env, buffer_r, j_buffer_r, 0);  
  37.     (*env)->ReleaseByteArrayElements(env, mp3buf, j_mp3buf, 0);  
  38.   
  39.     return result;  
  40. }  
  41.   
  42. JNIEXPORT jint JNICALL Java_com_example_lamemp3_MP3Recorder_flush(  
  43.         JNIEnv *env, jclass cls, jbyteArray mp3buf) {  
  44.     const jsize mp3buf_size = (*env)->GetArrayLength(env, mp3buf);  
  45.     jbyte* j_mp3buf = (*env)->GetByteArrayElements(env, mp3buf, NULL);  
  46.   
  47.     int result = lame_encode_flush(glf, j_mp3buf, mp3buf_size);  
  48.   
  49.     (*env)->ReleaseByteArrayElements(env, mp3buf, j_mp3buf, 0);  
  50.   
  51.     return result;  
  52. }  
  53.   
  54. JNIEXPORT void JNICALL Java_com_example_lamemp3_MP3Recorder_close(  
  55.         JNIEnv *env, jclass cls) {  
  56.     lame_close(glf);  
  57.     glf = NULL;  
  58. }  

6  创建Android.mk,注意把com_example_lamemp3_MP3Recorder.c添加进去。

[cpp] view plain copy
 
  1. LOCAL_PATH := $(call my-dir)  
  2.   
  3. include $(CLEAR_VARS)  
  4.   
  5. LAME_LIBMP3_DIR := lame-3.99.5_libmp3lame  
  6.   
  7. LOCAL_MODULE    := mp3lame  
  8. LOCAL_SRC_FILES := $(LAME_LIBMP3_DIR)/bitstream.c $(LAME_LIBMP3_DIR)/fft.c $(LAME_LIBMP3_DIR)/id3tag.c $(LAME_LIBMP3_DIR)/mpglib_interface.c $(LAME_LIBMP3_DIR)/presets.c $(LAME_LIBMP3_DIR)/quantize.c $(LAME_LIBMP3_DIR)/reservoir.c $(LAME_LIBMP3_DIR)/tables.c $(LAME_LIBMP3_DIR)/util.c $(LAME_LIBMP3_DIR)/VbrTag.c $(LAME_LIBMP3_DIR)/encoder.c $(LAME_LIBMP3_DIR)/gain_analysis.c $(LAME_LIBMP3_DIR)/lame.c $(LAME_LIBMP3_DIR)/newmdct.c $(LAME_LIBMP3_DIR)/psymodel.c $(LAME_LIBMP3_DIR)/quantize_pvt.c $(LAME_LIBMP3_DIR)/set_get.c $(LAME_LIBMP3_DIR)/takehiro.c $(LAME_LIBMP3_DIR)/vbrquantize.c $(LAME_LIBMP3_DIR)/version.c com_example_lamemp3_MP3Recorder.c  
  9.   
  10. include $(BUILD_SHARED_LIBRARY)  

7  AndroidManifest.xml添加权限。

[html] view plain copy
 
  1. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
  2.  <uses-permission android:name="android.permission.RECORD_AUDIO" />  

8  创建Builder后(上篇中有详细介绍,这里不赘述),Clean,报错:

[html] view plain copy
 
  1. In file included from jni/lame-3.99.5_libmp3lame/bitstream.c:36:0:  
  2. jni/lame-3.99.5_libmp3lame/util.h:574:5: error: unknown type name 'ieee754_float32_t'  
  3. jni/lame-3.99.5_libmp3lame/util.h:574:40: error: unknown type name 'ieee754_float32_t'  
  4. make.exe: *** [obj/local/armeabi/objs/mp3lame/lame-3.99.5_libmp3lame/bitstream.o] Error 1  


打开util.h
把 574行的 extern ieee754_float32_t fast_log2(ieee754_float32_t x);替换成extern float fast_log2(float x);

再次Clean,又报错:

[html] view plain copy
 
  1. jni/lame-3.99.5_libmp3lame/fft.c:47:32: fatal error: vector/lame_intrin.h: No such file or directory  
  2. compilation terminated.  
  3. make.exe: *** [obj/local/armeabi/objs/mp3lame/lame-3.99.5_libmp3lame/fft.o] Error 1  

打开fft.c删除47行#include "vector/lame_intrin.h"

Clean还报错:

[html] view plain copy
 
  1. In file included from jni/lame-3.99.5_libmp3lame/presets.c:29:0:  
  2. jni/lame-3.99.5_libmp3lame/set_get.h:24:18: fatal error: lame.h: No such file or directory  
  3. compilation terminated.  
  4. make.exe: *** [obj/local/armeabi/objs/mp3lame/lame-3.99.5_libmp3lame/presets.o] Error 1  

打开set_get.h,删除24行,#include <lame.h>

再次Clean,成功:

[html] view plain copy
 
    1. [armeabi] Compile thumb  : mp3lame <= version.c  
    2. [armeabi] Compile thumb  : mp3lame <= com_example_lamemp3_MP3Recorder.c  
    3. [armeabi] SharedLibrary  : libmp3lame.so  
    4. [armeabi] Install        : libmp3lame.so => libs/armeabi/libmp3lame.so  
原文地址:https://www.cnblogs.com/hanfeihanfei/p/5561093.html