Android录制mp3格式文件
前言
最近做一个即时通信类的项目,由于要保证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_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_maxi_mp3record_mp3recorder.c include $(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() { @override public 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 block e.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; } // 获得声音的level public 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左右的样子吧。使用还是很方便的。如果有什么疑问或建议请留言哈。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
下一篇: 微博最核心的价值是什么 如何玩好微博营销