• 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  
  • 相关阅读:
    8.2 TensorFlow实现KNN与TensorFlow中的损失函数,优化函数
    8.3 TensorFlow BP神经网络构建与超参数的选取
    8.3 TensorFlow BP神经网络构建与超参数的选取
    聊一聊深度学习的weight initialization
    聊一聊深度学习的weight initialization
    聊一聊深度学习的activation function
    聊一聊深度学习的activation function
    Hadoop-2.7.4 八节点分布式集群安装
    Hadoop-2.7.4 八节点分布式集群安装
    Vue学习笔记(一) 入门
  • 原文地址:https://www.cnblogs.com/hanfeihanfei/p/5561093.html
Copyright © 2020-2023  润新知