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

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

com_example_lamemp3_MP3Recorder.h:

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

com_example_lamemp3_MP3Recorder.c:

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

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

[cpp] view plaincopy
  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3. LAME_LIBMP3_DIR := lame-3.99.5_libmp3lame
  4. LOCAL_MODULE    := mp3lame
  5. 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
  6. include $(BUILD_SHARED_LIBRARY)

7  AndroidManifest.xml添加权限。

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

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

[html] view plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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

Android MP3录音实现相关推荐

  1. Android实例-录音与回放(播放MP3)(XE8+小米2)

    结果: 1.增加ActionList中的Action时,需要跳到Master界面,不能在Android4Phonel界面下. 2.如果不打开权限的话,会提示"该设备不支持停止录音操作&quo ...

  2. Android Mp3格式录音,含有暂停,计时功能

    Mp3录音,边录边转: 博主前端时间做的项目,遇到关于android录音相关的问题.由于android系统自带的录音Audiorecord录制出来的文件格式为PCM,或者简单的加上WAV头文件,转换成 ...

  3. android java 录音放大_Android实现录音功能实现实例(MediaRecorder)

    本文介绍了Android实现录音的实例代码(MediaRecorder),分享给大家,具体如下: Android提供了两个API用于录音的实现:MediaRecorder 和 AudioRecord, ...

  4. Android MP3录音功能(能暂停,含源码,可编译)

    项目需求ios和Android客户端要使用录音功能,格式要求为mp3,一直百度都找不到MP3录音的源码,国外也只能找到相关说明,却也没有找到源码, 最多也就只找到编译好的so文件,也没有例子,使用说明 ...

  5. Android MP3录音功能

    项目需求ios和Android客户端要使用录音功能,格式要求为mp3,一直百度都找不到MP3录音的源码,国外也只能找到相关说明,却也没有找到源码, 最多也就只找到编译好的so文件,也没有例子,使用说明 ...

  6. 喜马拉雅xm格式转化mp3_怎样让mp3录音转文字?

    我们大多数的的音频格式是mp3格式的,比如说我们录制了一段音频但是我们又想把它转化成文字的形式保存,总不能是一字一字的在键盘上敲打下来吧!那么我们有没有什么简便的方法把mp3录音转文字呢?小编在这点上 ...

  7. 一款简单易用的mp3录音软件

    MP3 Audio Recorder Mac版是Mac平台上的一款录音应用.MP3 Audio Recorder Mac版是一款简单的录音软件,它可以通过系统内置或外接麦克风将音频文件录制成高品质的M ...

  8. Android 实时录音和回放,边录音边播放 (KTV回音效果)

    原文地址为: Android 实时录音和回放,边录音边播放 (KTV回音效果) 上一篇介绍了如何使用Mediarecorder来录音,以及播放录音.不过并没有达到我的目的,一边录音一边播放.今天就讲解 ...

  9. Android 麦克风录音动画

    Android 麦克风录音动画 源代码 RecordView.java R.drawable.voice_recording 源代码 RecordView.java import android.co ...

最新文章

  1. tcl c语言笔试题,TCL技术类笔试题目.doc
  2. 我的一些小想法(聊以一笑……)
  3. python 文本转json_在python中将文本文件转换为json
  4. 评分卡模型开发(一)--用户数据缺失值处理
  5. 关于ObjectProxy
  6. cad快看_有石CAD自动下单排版软件,让你的CAD都比别人强!
  7. 淘宝降价补差价规则是什么?有哪些规则?
  8. webpack 报错处理 Error: Cannot find module '@webassemblyjs/wast-parser'
  9. 超好用的浏览器兼容性测试工具,赶紧收藏!
  10. 综合Synthesis
  11. 数据质量监控Griffin——使用
  12. 单片机叮咚c语言,单片机试验19“叮咚”门铃
  13. Python_人人车_字体加密的破解
  14. c语言樱花飘落代码_用Python画一棵漂亮的樱花树,给女友惊喜
  15. Caffe源码(十):eltwise_layer 分析
  16. python脚本模式_python脚本怎么执行
  17. 上海市浦东新区37街镇最新版行政区域
  18. shape文件的读取和展示
  19. vue-awesome-swiper插件
  20. iptables防火墙详解及使用layer7阻止qq,酷狗,等P2P软件

热门文章

  1. MVC6 (ASP.NET5) 自定义TagHelper
  2. 第二节:垃圾回收期算法简介
  3. 使SSH不用输入密码
  4. 44 | 套路篇:网络性能优化的几个思路(下)
  5. 6.6 rsync:文件同步工具
  6. linux ftp下载文件_Linux系统中10个使用Wget命令下载文件示例
  7. java 3 9 2 6数字排序_GitHub - JourWon/sort-algorithm: 史上最全经典排序算法总结(Java实现)...
  8. 基于实时计算Flink版的场景解决方案demo
  9. 云原生:重新定义信息产业生态体系
  10. 阿里和浙大的“AI 训练师助手”是这样炼成的