Android MP3录音实现
2015-04-17 14:30
369 查看
给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开发之入门教程
先看一下项目文件目录:
1
2
3
解压,把子文件夹libmp3lame中的非.h和.c格式的文件删除后的剩余的所有文件和include下的lame.h放进一个新建的lame-3.99.5_libmp3lame文件夹中,最后把整个lame-3.99.5_libmp3lame文件夹拷贝到JNI目录下。
4
MP3Recorder.class
5
com_example_lamemp3_MP3Recorder.h:
6
7
8
打开util.h
把 574行的 extern ieee754_float32_t fast_log2(ieee754_float32_t x);替换成extern float fast_log2(float x);
"vector/lame_intrin.h"
Android
MP3 DEMO下载地址:http://download.csdn.net/detail/yalinfendou/8604759
声明一下,代码参考了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.classpublic class MP3Recorder { private String mFilePath = null; private int sampleRate = 0; private boolean isRecording = false; private boolean isPause = false; private Handler handler = null; /** * 开始录音 */ public static final int MSG_REC_STARTED = 1; /** * 结束录音 */ public static final int MSG_REC_STOPPED = 2; /** * 暂停录音 */ public static final int MSG_REC_PAUSE = 3; /** * 继续录音 */ public static final int MSG_REC_RESTORE = 4; /** * 缓冲区挂了,采样率手机不支持 */ public static final int MSG_ERROR_GET_MIN_BUFFERSIZE = -1; /** * 创建文件时扑街了 */ public static final int MSG_ERROR_CREATE_FILE = -2; /** * 初始化录音器时扑街了 */ public static final int MSG_ERROR_REC_START = -3; /** * 录音的时候出错 */ public static final int MSG_ERROR_AUDIO_RECORD = -4; /** * 编码时挂了 */ public static final int MSG_ERROR_AUDIO_ENCODE = -5; /** * 写文件时挂了 */ public static final int MSG_ERROR_WRITE_FILE = -6; /** * 没法关闭文件流 */ public static final int MSG_ERROR_CLOSE_FILE = -7; public MP3Recorder() { this.sampleRate = 8000; } /** * 开片 */ public void start() { if (isRecording) { return; } new Thread() { @Override public void run() { String fileDir = StorageUtil.getSDPath() + "LameMP3/Voice/"; File dir = new File(fileDir); if (!dir.exists()) { dir.mkdirs(); } mFilePath = StorageUtil.getSDPath() + "LameMP3/Voice/" + System.currentTimeMillis() + ".mp3"; System.out.println(mFilePath); android.os.Process .setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); // 根据定义好的几个配置,来获取合适的缓冲大小 final int minBufferSize = AudioRecord.getMinBufferSize( sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); if (minBufferSize < 0) { if (handler != null) { handler.sendEmptyMessage(MSG_ERROR_GET_MIN_BUFFERSIZE); } return; } AudioRecord audioRecord = new AudioRecord( MediaRecorder.AudioSource.MIC, sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 2); // 5秒的缓冲 short[] buffer = new short[sampleRate * (16 / 8) * 1 * 5]; byte[] mp3buffer = new byte[(int) (7200 + buffer.length * 2 * 1.25)]; FileOutputStream output = null; try { output = new FileOutputStream(new File(mFilePath)); } catch (FileNotFoundException e) { if (handler != null) { handler.sendEmptyMessage(MSG_ERROR_CREATE_FILE); } return; } MP3Recorder.init(sampleRate, 1, sampleRate, 32); isRecording = true; // 录音状态 isPause = false; // 录音状态 try { try { audioRecord.startRecording(); // 开启录音获取音频数据 } catch (IllegalStateException e) { // 不给录音... if (handler != null) { handler.sendEmptyMessage(MSG_ERROR_REC_START); } return; } try { // 开始录音 if (handler != null) { handler.sendEmptyMessage(MSG_REC_STARTED); } int readSize = 0; boolean pause = false; while (isRecording) { /*--暂停--*/ if (isPause) { if (!pause) { handler.sendEmptyMessage(MSG_REC_PAUSE); pause = true; } continue; } if (pause) { handler.sendEmptyMessage(MSG_REC_RESTORE); pause = false; } /*--End--*/ /*--实时录音写数据--*/ readSize = audioRecord.read(buffer, 0, minBufferSize); if (readSize < 0) { if (handler != null) { handler.sendEmptyMessage(MSG_ERROR_AUDIO_RECORD); } break; } else if (readSize == 0) { ; } else { int encResult = MP3Recorder.encode(buffer, buffer, readSize, mp3buffer); if (encResult < 0) { if (handler != null) { handler.sendEmptyMessage(MSG_ERROR_AUDIO_ENCODE); } break; } if (encResult != 0) { try { output.write(mp3buffer, 0, encResult); } catch (IOException e) { if (handler != null) { handler.sendEmptyMessage(MSG_ERROR_WRITE_FILE); } break; } } } /*--End--*/ } /*--录音完--*/ int flushResult = MP3Recorder.flush(mp3buffer); if (flushResult < 0) { if (handler != null) { handler.sendEmptyMessage(MSG_ERROR_AUDIO_ENCODE); } } if (flushResult != 0) { try { output.write(mp3buffer, 0, flushResult); } catch (IOException e) { if (handler != null) { handler.sendEmptyMessage(MSG_ERROR_WRITE_FILE); } } } try { output.close(); } catch (IOException e) { if (handler != null) { handler.sendEmptyMessage(MSG_ERROR_CLOSE_FILE); } } /*--End--*/ } finally { audioRecord.stop(); audioRecord.release(); } } finally { MP3Recorder.close(); isRecording = false; } if (handler != null) { handler.sendEmptyMessage(MSG_REC_STOPPED); } } }.start(); } public void stop() { isRecording = false; } public void pause() { isPause = true; } public void restore() { isPause = false; } public boolean isRecording() { return isRecording; } public boolean isPaus() { if (!isRecording) { return false; } return isPause; } public String getFilePath() { return mFilePath; } /** * 录音状态管理 * * @see RecMicToMp3#MSG_REC_STARTED * @see RecMicToMp3#MSG_REC_STOPPED * @see RecMicToMp3#MSG_REC_PAUSE * @see RecMicToMp3#MSG_REC_RESTORE * @see RecMicToMp3#MSG_ERROR_GET_MIN_BUFFERSIZE * @see RecMicToMp3#MSG_ERROR_CREATE_FILE * @see RecMicToMp3#MSG_ERROR_REC_START * @see RecMicToMp3#MSG_ERROR_AUDIO_RECORD * @see RecMicToMp3#MSG_ERROR_AUDIO_ENCODE * @see RecMicToMp3#MSG_ERROR_WRITE_FILE * @see RecMicToMp3#MSG_ERROR_CLOSE_FILE */ public void setHandle(Handler handler) { this.handler = handler; } /*--以下为Native部分--*/ static { System.loadLibrary("mp3lame"); } /** * 初始化录制参数 */ public static void init(int inSamplerate, int outChannel, int outSamplerate, int outBitrate) { init(inSamplerate, outChannel, outSamplerate, outBitrate, 7); } /** * 初始化录制参数 quality:0=很好很慢 9=很差很快 */ public native static void init(int inSamplerate, int outChannel, int outSamplerate, int outBitrate, int quality); /** * 音频数据编码(PCM左进,PCM右进,MP3输出) */ public native static int encode(short[] buffer_l, short[] buffer_r, int samples, byte[] mp3buf); /** * 刷干净缓冲区 */ public native static int flush(byte[] mp3buf); /** * 结束编码 */ public native static void close(); }
5
在JNI文件夹下创建com_example_lamemp3_MP3Recorder.h头文件,在里面定义几个方法,然后在
com_example_lamemp3_MP3Recorder.c中实现。
com_example_lamemp3_MP3Recorder.h:/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_kubility_demo_MP3Recorder */ #ifndef _Included_com_example_lamemp3_MP3Recorder #define _Included_com_example_lamemp3_MP3Recorder #ifdef __cplusplus extern "C" { #endif /* * Class: com_kubility_demo_MP3Recorder * Method: init * Signature: (IIIII)V */ JNIEXPORT void JNICALL Java_com_example_lamemp3_MP3Recorder_init (JNIEnv *, jclass, jint, jint, jint, jint, jint); /* * Class: com_kubility_demo_MP3Recorder * Method: encode * Signature: ([S[SI[B)I */ JNIEXPORT jint JNICALL Java_com_example_lamemp3_MP3Recorder_encode (JNIEnv *, jclass, jshortArray, jshortArray, jint, jbyteArray); /* * Class: com_kubility_demo_MP3Recorder * Method: flush * Signature: ([B)I */ JNIEXPORT jint JNICALL Java_com_example_lamemp3_MP3Recorder_flush (JNIEnv *, jclass, jbyteArray); /* * Class: com_kubility_demo_MP3Recorder * Method: close * Signature: ()V */ JNIEXPORT void JNICALL Java_com_example_lamemp3_MP3Recorder_close (JNIEnv *, jclass); #ifdef __cplusplus } #endif #endifcom_example_lamemp3_MP3Recorder.c:
#include "lame-3.99.5_libmp3lame/lame.h" #include "com_example_lamemp3_MP3Recorder.h" static lame_global_flags *glf = NULL; JNIEXPORT void JNICALL Java_com_example_lamemp3_MP3Recorder_init( JNIEnv *env, jclass cls, jint inSamplerate, jint outChannel, jint outSamplerate, jint outBitrate, jint quality) { if (glf != NULL) { lame_close(glf); glf = NULL; } glf = lame_init(); lame_set_in_samplerate(glf, inSamplerate); lame_set_num_channels(glf, outChannel); lame_set_out_samplerate(glf, outSamplerate); lame_set_brate(glf, outBitrate); lame_set_quality(glf, quality); lame_init_params(glf); } JNIEXPORT jint JNICALL Java_com_example_lamemp3_MP3Recorder_encode( JNIEnv *env, jclass cls, jshortArray buffer_l, jshortArray buffer_r, jint samples, jbyteArray mp3buf) { jshort* j_buffer_l = (*env)->GetShortArrayElements(env, buffer_l, NULL); jshort* j_buffer_r = (*env)->GetShortArrayElements(env, buffer_r, NULL); const jsize mp3buf_size = (*env)->GetArrayLength(env, mp3buf); jbyte* j_mp3buf = (*env)->GetByteArrayElements(env, mp3buf, NULL); int result = lame_encode_buffer(glf, j_buffer_l, j_buffer_r, samples, j_mp3buf, mp3buf_size); (*env)->ReleaseShortArrayElements(env, buffer_l, j_buffer_l, 0); (*env)->ReleaseShortArrayElements(env, buffer_r, j_buffer_r, 0); (*env)->ReleaseByteArrayElements(env, mp3buf, j_mp3buf, 0); return result; } JNIEXPORT jint JNICALL Java_com_example_lamemp3_MP3Recorder_flush( JNIEnv *env, jclass cls, jbyteArray mp3buf) { const jsize mp3buf_size = (*env)->GetArrayLength(env, mp3buf); jbyte* j_mp3buf = (*env)->GetByteArrayElements(env, mp3buf, NULL); int result = lame_encode_flush(glf, j_mp3buf, mp3buf_size); (*env)->ReleaseByteArrayElements(env, mp3buf, j_mp3buf, 0); return result; } JNIEXPORT void JNICALL Java_com_example_lamemp3_MP3Recorder_close( JNIEnv *env, jclass cls) { lame_close(glf); glf = NULL; }
6
创建Android.mk,注意把com_example_lamemp3_MP3Recorder.c添加进去。
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LAME_LIBMP3_DIR := lame-3.99.5_libmp3lame LOCAL_MODULE := mp3lame 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 include $(BUILD_SHARED_LIBRARY)
7
AndroidManifest.xml添加权限。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
8
创建Builder后(上篇中有详细介绍,这里不赘述),Clean,报错:
In file included from jni/lame-3.99.5_libmp3lame/bitstream.c:36:0: jni/lame-3.99.5_libmp3lame/util.h:574:5: error: unknown type name 'ieee754_float32_t' jni/lame-3.99.5_libmp3lame/util.h:574:40: error: unknown type name 'ieee754_float32_t' 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,又报错:
jni/lame-3.99.5_libmp3lame/fft.c:47:32: fatal error: vector/lame_intrin.h: No such file or directory compilation terminated. 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还报错:
In file included from jni/lame-3.99.5_libmp3lame/presets.c:29:0: jni/lame-3.99.5_libmp3lame/set_get.h:24:18: fatal error: lame.h: No such file or directory compilation terminated. make.exe: *** [obj/local/armeabi/objs/mp3lame/lame-3.99.5_libmp3lame/presets.o] Error 1打开set_get.h,删除24行,#include <lame.h>
再次Clean,成功:
[armeabi] Compile thumb : mp3lame <= version.c [armeabi] Compile thumb : mp3lame <= com_example_lamemp3_MP3Recorder.c [armeabi] SharedLibrary : libmp3lame.so [armeabi] Install : libmp3lame.so => libs/armeabi/libmp3lame.so
录音后文件发送至iPhone,播放OK……
lame-3.99.5下载地址:http://download.csdn.net/detail/yalinfendou/8604729Android
MP3 DEMO下载地址:http://download.csdn.net/detail/yalinfendou/8604759
相关文章推荐
- Android MP3录音实现
- Android MP3录音实现
- Android MP3录音实现
- Android 使用AudioRecord而实现录音暂停以及wav文件转mp3文件
- Android MP3录音实现
- 利用libmp3lame实现在Android上录音MP3文件示例
- 浅谈Android实现伴奏录音合成MP3
- Android编程实现通话录音功能的方法
- Android开发--WebView简单录音功能的实现
- Android 录音实现方法、仿微信语音、麦克风录音、发送语音
- 如何使Android录音实现内录功能
- 如何使Android录音实现内录功能
- Android使用AudioRecord实现暂停录音功能实例代码
- Android MediaRecoder实现电话录音,其中出现录制成功但没有声音
- android 实现电话录音(窃听)
- [Android] 录音与播放录音实现
- android 语音即时通讯之录音、播放实现
- android语音即时通讯之录音、播放功能实现代码
- Android实现录音功能实现实例(MediaRecorder)
- Android实训案例(七)——四大组件之中的一个Service初步了解,实现通话录音功能,抽调接口