前言

最近做一个即时通信类的项目,由于要保证pc端,iOS端和Android端的通用性,最终统一为MP3格式,一直担心MP3格式会不会很大,但是实测还是可以接受的。下面来看看具体步骤:

工具

MP3格式是用一个开源项目转的,MP3lame,由于该项目用到了jni,所以需要大家配置好ndk环境,环境配置在此就不多说了,大家可以自行百度,最新的应该很好配置。

创建jni

拷贝文件

下载好后(我下载的是3.98.4版本)打开,找到libmp3lame文件,将里面的.h和.c拷贝下来,在自己的工程里创建jni文件夹,在jni文件夹下新建一个文件夹(我的命名为lame-3.98.4_libmp3lame,后面会用到),将刚才拷贝的文件复制进去,然后再把include文件夹里的lame.h也拷贝进去。

创建Android.mk

在jni中创建文件,Android.mk

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LAME_LIBMP3_DIR := lame-3.98.4_libmp3lameLOCAL_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_maxi_mp3record_MP3Recorder.cinclude $(BUILD_SHARED_LIBRARY)

**注意:**LAME_LIBMP3_DIR := lame-3.98.4_libmp3lame 需要将其改为你的项目中的文件名,即上面说的jni下新建的文件夹。
大家应该看到了最后一句的com_maxi_mp3record_MP3Recorder.c
很明显这是我自己创建的.c文件。用来调用mp3lame中的接口的,对应着我java中的com.maxi.mp3record.MP3Recorder.java。咱们先创建java文件。

创建MP3Recorder.java

对应你的包名建一个MP3Recorder.java文件,该文件是java文件对应你的包名建立即可。

package cn.ctvonline.android.modules.project.widget;import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Handler;/*** <b>类功能描述:</b><div style="margin-left:40px;margin-top:-10px">* MP3实时录制功能,可暂停,注意因踩用Native开发,不能混淆*/
public class MP3Recorder {private String filePath;private int sampleRate;private boolean isRecording = false;private boolean isPause = false;private Handler handler;/*** 开始录音*/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(int sampleRate) {this.sampleRate = sampleRate;}public void setFilePath(String filePath) {this.filePath = filePath;}/*** 开片*/public void start() {if (isRecording) {return;}new Thread() {@Overridepublic void run() {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 {File file = createSDFile(filePath);output = new FileOutputStream(file);} catch (FileNotFoundException e) {if (handler != null) {handler.sendEmptyMessage(MSG_ERROR_CREATE_FILE);}return;} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}MP3Recorder.init(sampleRate, 1, sampleRate, 32);isRecording = true; // 录音状态isPause = false; // 录音状态try {try {audioRecord.startRecording(); // 开启录音获取音频数据if (mListener != null) {mListener.wellPrepared();}} 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);voiceLevel = getVoiceSize(readSize, buffer);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;}// 获得声音的levelpublic int getVoiceSize(int r, short[] buffer) {if (isRecording) {try {long v = 0;// 将 buffer 内容取出,进行平方和运算for (int i = 0; i < buffer.length; i++) {v += buffer[i] * buffer[i];}// 平方和除以数据总长度,得到音量大小。double mean = v / (double) r;double volume = 10 * Math.log10(mean);return (((int) volume / 10) - 1);} catch (Exception e) {// TODO Auto-generated catch block}}return 1;}/*** 在SD卡上创建文件* * @throws IOException*/public static File createSDFile(String fileName) throws IOException {File file = new File(fileName);if (!isFileExists(file))if (file.isDirectory()) {file.mkdirs();} else {file.createNewFile();}return file;}private int voiceLevel;public int getVoiceLevel() {return voiceLevel;}public interface AudioStageListener {void wellPrepared();}public AudioStageListener mListener;public void setOnAudioStageListener(AudioStageListener listener) {mListener = listener;}/*** 录音状态管理* * @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();
}

创建c文件

在创建c文件,创建在jni下,命名就按你的java文件所在的包名命名”.”替换为“_”。例如:com_maxi_mp3record_MP3Recorder.c。当然还得有头文件:com_maxi_mp3record_MP3Recorder.h。

com_maxi_mp3record_MP3Recorder.c

#include "lame-3.98.4_libmp3lame/lame.h"
#include "com_maxi_mp3record_MP3Recorder.h"static lame_global_flags *glf = NULL;JNIEXPORT void JNICALL Java_com_maxi_mp3record_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_maxi_mp3record_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_maxi_mp3record_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_maxi_mp3record_MP3Recorder_close(JNIEnv *env, jclass cls) {lame_close(glf);glf = NULL;
}

com_maxi_mp3record_MP3Recorder.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_maxi_mp3record_MP3Recorder */#ifndef _Included_com_maxi_mp3record_MP3Recorder
#define _Included_com_maxi_mp3record_MP3Recorder
#ifdef __cplusplus
extern "C" {
#endif
/** Class:     com_maxi_mp3record_MP3Recorder* Method:    init* Signature: (IIIII)V*/
JNIEXPORT void JNICALL Java_com_maxi_mp3record_MP3Recorder_init(JNIEnv *, jclass, jint, jint, jint, jint, jint);/** Class:     com_maxi_mp3record_MP3Recorder* Method:    encode* Signature: ([S[SI[B)I*/
JNIEXPORT jint JNICALL Java_com_maxi_mp3record_MP3Recorder_encode(JNIEnv *, jclass, jshortArray, jshortArray, jint, jbyteArray);/** Class:     com_maxi_mp3record_MP3Recorder* Method:    flush* Signature: ([B)I*/
JNIEXPORT jint JNICALL Java_com_maxi_mp3record_MP3Recorder_flush(JNIEnv *, jclass, jbyteArray);/** Class:     com_maxi_mp3record_MP3Recorder* Method:    close* Signature: ()V*/
JNIEXPORT void JNICALL Java_com_maxi_mp3record_MP3Recorder_close(JNIEnv *, jclass);#ifdef __cplusplus
}
#endif
#endif

这俩文件别只复制不看内容啊,里面可都是连接java和c的接口,所以不能有差错。举个例子吧,#ifndef _Included_com_maxi_mp3record_MP3Recorder,这个就得替换成你对应的包名,里面所有都得替换成你自己的程序对应的包名。

编译ndk

创建Application.mk在你的项目文件的jni下,在里面写入:
APP_ABI := all
如果不加,NDK只会编译“armeabi”,然而安卓有很多不同类型的处理器,所以我们不止需要arm。相信你们搞到现在了肯定ndk都配置好了,然后打开终端,进入到你的项目(找到你的ndk目录下的ndk-bulid,最好把它添加到环境变量里,对于以后编译比较方便,在此默认你没添加环境变量),执行ndk-bulid。稍等片刻你会发现你的项目里多了一个obj文件夹,obj文件夹下会生成”arm64-v8a”、”armeabi”、”armeabi-v7a”、”mips”、”mips64”、”x86”、”x86_64”。打开它,各个文件夹下会有一个libmp3lame.so。ok,没错那就是你要的“滑板鞋”。将它放入你的libs文件下,没有自行创建,各个平台便都可以加载了。

使用方法

MP3Recorder recorder = new MP3Recorder(8000);
recorder.setFilePath(voicePath);//录音保存目录
recorder.start();//开始录音
recorder.stop();//录音结束
recorder.getVoiceLevel()//这是我封装的获取音频振幅接口,大家可以用来录音的时候显示声音大小,数据自行调节。

总结

之前一直用MediaRecorder录音,发现录出来的只能是amr、acc等格式,用lame转MP3感觉是不可行的。我试了没能成功,不知道具体是什么原因,所以大家有时间可以研究研究,没时间就不要尝试了。
Mp3lame录制出来的声音还是挺靠谱的(不过据听说iOS就有些莎莎声),然后录制出来的大小还是可以接受的,五秒钟的音频大概在20k左右的样子吧。使用还是很方便的。如果有什么疑问或建议请留言哈。

Android录制mp3格式相关推荐

  1. android mp3数据格式,Android录制mp3格式文件

    前言 最近做一个即时通信类的项目,由于要保证pc端,iOS端和Android端的通用性,最终统一为MP3格式,一直担心MP3格式会不会很大,但是实测还是可以接受的.下面来看看具体步骤: 工具 MP3格 ...

  2. android录音MP3格式文件

    项目有录音功能,本来是录制的arm格式的文件,需求让改成MP3格式,折腾半天发现android本身做不到这一点,只能借助ndk来完成,下面是ndk的配置级MP3格式文件的录制 一.ndk介绍及下载:h ...

  3. android 录音的格式,Android录音mp3格式实例详解

    Android录音支持的格式有amr.aac,但这两种音频格式在跨平台上表现并不好. MP3显然才是跨平台的最佳选择. 项目地址 实现思路概述 在分析代码前,我们需要明确几个问题 1. 如何最终生成M ...

  4. android录制mp3 github,GitHub - understanding/AndroidMP3Recorder: 为Android提供MP3录音功能[持续维护版]...

    AndroidMP3Recorder 项目介绍 本项目Fork自AndroidMP3Recorder,由于原作者停止维护,此项目是我fork下主分支来维护的.相比原项目,此项目支持以下功能: 修复部分 ...

  5. android录制mp3 github,GitHub - duantao561/AndroidMP3Recorder-1: 为Android提供MP3录音功能[RETIRED]...

    AndroidMP3Recorder[RETIRED] 停止维护声明 因个人精力原因,无法顾及到本库的维护和更新. 需要的同学请自行fork进行改进,谢谢 ==> 实现思路讲解:Android ...

  6. android录制mp3 github,GitHub - xj999/AndroidMP3Recorder: 为Android提供MP3录音功能

    AndroidMP3Recorder 为Android提供MP3录音功能 1. 目录简介 library: MP3录音实现源码库 sample : 使用范例 2. 使用方法 集成到项目中 depend ...

  7. android 网络mp3格式,Android-使用MediaPlayer播放网络音频并且缓存

    需求 接口中返回一个List,其中部分对象有音频文件的url.对有url的item视图中添加一个播放按钮.点击按钮播放音频,再次点击暂停或停止.点击其他item播放新的音频.点击item进入二级页面, ...

  8. android 录制mp4格式,Android系统如何录制屏幕(录制成mp4格式)

    不管是教学,还是为了演示,如果能将Android手机(或平板)的屏幕录制成视频文件,那是一件非常酷的事(iOS8已经提供了这一功能,能通过OS X直接在Mac上录制iPad.iPhone的屏幕,win ...

  9. android录制mp3音频,Android MP3录制,波形显示,音频权限兼容与播放

    我又来掀桌子了(ノಠ益ಠ)ノ彡┻━┻,这次是MP3录制和显示声音波形的一个故事. 先看看效果┑( ̄Д  ̄)┍ 2 1 MP3录制的逻辑过程就忽略了(真的不是因为懒(.・・)ノ),这里用的是开源的And ...

最新文章

  1. 手动增加swap空间
  2. java经典50题_JAVA经典算法50题(3)【面试+工作】
  3. Qt中的模型视图设计模式
  4. boost::shared_lock相关的测试程序
  5. const常量与define宏定义的区别
  6. 基于随机响应机制的本地差分隐私【谷歌】论文笔记
  7. 开源android手写批注,浅析各阅读平台手写批注 智器阅读显身手
  8. 年底各类大型年会晚会必用的高清丝绸海报设计背景来喽!
  9. hdu2074java
  10. 【语言处理与Python】5.7如何确定一个词的分类
  11. ConstraintLayout使用汇总
  12. amd k14主板参数_R5 1400配什么主板好?R5-1400主板搭配与参数详解 (全文)
  13. 时间序列计量经济学模型
  14. wordpress基础插件开发3-add_action()案例研究
  15. deactive(Deactive breakpoint)
  16. Jupyter Notebook又一懒人神器,拖拽生成Python代码!
  17. Windows10 LSTC 2021输入法无法使用的问题
  18. 无法打开登录所请求的数据库database登录失败用户登录失败
  19. Oracle Golden Gate
  20. 《华为数据之道》-第8章 打造“清洁数据”的质量综合管理能力

热门文章

  1. 蚂蚁区块链李杰力:开放联盟链2020年支持数字积分
  2. 台式计算机不同处理器型号,cpu型号怎么看 教你通过CPU型号识别电脑性能高低【图文】...
  3. 蓝牙协议分析(3)_蓝牙低功耗(BLE)协议栈介绍
  4. 四相八拍步进c语言程序,5线四相八拍步进电机的C51演示程序
  5. 转载 半步八拍(单双八拍)步进电机
  6. 财务共享中心,是要白刀子进去 红刀子出来
  7. mysql自增ID重置
  8. 9个专业分享生信技能的公众号
  9. iOS CoreAnimation专题——技巧篇(二)CAShapeLayer with Bezier Path - Layer世界的神奇画笔
  10. mysql中无穷大如何表示_python如何表示无穷大