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:=$(callmy-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.ccom_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文件对应你的包名建立即可。
packagecn.ctvonline.android.modules.project.widget; importjava.io.File; importjava.io.FileNotFoundException; importjava.io.FileOutputStream; importjava.io.IOException; importandroid.media.AudioFormat; importandroid.media.AudioRecord; importandroid.media.MediaRecorder; importandroid.os.Handler; /** *类功能描述:*MP3实时录制功能,可暂停,注意因踩用Native开发,不能混淆 */ publicclassMP3Recorder{ privateStringfilePath; privateintsampleRate; privatebooleanisRecording=false; privatebooleanisPause=false; privateHandlerhandler; /** *开始录音 */ publicstaticfinalintMSG_REC_STARTED=1; /** *结束录音 */ publicstaticfinalintMSG_REC_STOPPED=2; /** *暂停录音 */ publicstaticfinalintMSG_REC_PAUSE=3; /** *继续录音 */ publicstaticfinalintMSG_REC_RESTORE=4; /** *缓冲区挂了,采样率手机不支持 */ publicstaticfinalintMSG_ERROR_GET_MIN_BUFFERSIZE=-1; /** *创建文件时扑街了 */ publicstaticfinalintMSG_ERROR_CREATE_FILE=-2; /** *初始化录音器时扑街了 */ publicstaticfinalintMSG_ERROR_REC_START=-3; /** *录紧音的时候出错 */ publicstaticfinalintMSG_ERROR_AUDIO_RECORD=-4; /** *编码时挂了 */ publicstaticfinalintMSG_ERROR_AUDIO_ENCODE=-5; /** *写文件时挂了 */ publicstaticfinalintMSG_ERROR_WRITE_FILE=-6; /** *没法关闭文件流 */ publicstaticfinalintMSG_ERROR_CLOSE_FILE=-7; publicMP3Recorder(intsampleRate){ this.sampleRate=sampleRate; } publicvoidsetFilePath(StringfilePath){ this.filePath=filePath; } /** *开片 */ publicvoidstart(){ if(isRecording){ return; } newThread(){ @Override publicvoidrun(){ android.os.Process .setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); //根据定义好的几个配置,来获取合适的缓冲大小 finalintminBufferSize=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; } AudioRecordaudioRecord=newAudioRecord( MediaRecorder.AudioSource.MIC,sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT,minBufferSize*2); //5秒的缓冲 short[]buffer=newshort[sampleRate*(16/8)*1*5]; byte[]mp3buffer=newbyte[(int)(7200+buffer.length*2*1.25)]; FileOutputStreamoutput=null; try{ Filefile=createSDFile(filePath); output=newFileOutputStream(file); }catch(FileNotFoundExceptione){ if(handler!=null){ handler.sendEmptyMessage(MSG_ERROR_CREATE_FILE); } return; }catch(IOExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } MP3Recorder.init(sampleRate,1,sampleRate,32); isRecording=true;//录音状态 isPause=false;//录音状态 try{ try{ audioRecord.startRecording();//开启录音获取音频数据 if(mListener!=null){ mListener.wellPrepared(); } }catch(IllegalStateExceptione){ //不给录音... if(handler!=null){ handler.sendEmptyMessage(MSG_ERROR_REC_START); } return; } try{ //开始录音 if(handler!=null){ handler.sendEmptyMessage(MSG_REC_STARTED); } intreadSize=0; booleanpause=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; }elseif(readSize==0){ ; }else{ intencResult=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(IOExceptione){ if(handler!=null){ handler.sendEmptyMessage(MSG_ERROR_WRITE_FILE); } break; } } } /*--End--*/ } /*--录音完--*/ intflushResult=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(IOExceptione){ if(handler!=null){ handler.sendEmptyMessage(MSG_ERROR_WRITE_FILE); } } } try{ output.close(); }catch(IOExceptione){ 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(); } publicvoidstop(){ isRecording=false; } publicvoidpause(){ isPause=true; } publicvoidrestore(){ isPause=false; } publicbooleanisRecording(){ returnisRecording; } publicbooleanisPaus(){ if(!isRecording){ returnfalse; } returnisPause; } //获得声音的level publicintgetVoiceSize(intr,short[]buffer){ if(isRecording){ try{ longv=0; //将buffer内容取出,进行平方和运算 for(inti=0;i 创建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" staticlame_global_flags*glf=NULL; JNIEXPORTvoidJNICALLJava_com_maxi_mp3record_MP3Recorder_init( JNIEnv*env,jclasscls,jintinSamplerate,jintoutChannel, jintoutSamplerate,jintoutBitrate,jintquality){ 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); } JNIEXPORTjintJNICALLJava_com_maxi_mp3record_MP3Recorder_encode( JNIEnv*env,jclasscls,jshortArraybuffer_l,jshortArraybuffer_r, jintsamples,jbyteArraymp3buf){ jshort*j_buffer_l=(*env)->GetShortArrayElements(env,buffer_l,NULL); jshort*j_buffer_r=(*env)->GetShortArrayElements(env,buffer_r,NULL); constjsizemp3buf_size=(*env)->GetArrayLength(env,mp3buf); jbyte*j_mp3buf=(*env)->GetByteArrayElements(env,mp3buf,NULL); intresult=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); returnresult; } JNIEXPORTjintJNICALLJava_com_maxi_mp3record_MP3Recorder_flush( JNIEnv*env,jclasscls,jbyteArraymp3buf){ constjsizemp3buf_size=(*env)->GetArrayLength(env,mp3buf); jbyte*j_mp3buf=(*env)->GetByteArrayElements(env,mp3buf,NULL); intresult=lame_encode_flush(glf,j_mp3buf,mp3buf_size); (*env)->ReleaseByteArrayElements(env,mp3buf,j_mp3buf,0); returnresult; } JNIEXPORTvoidJNICALLJava_com_maxi_mp3record_MP3Recorder_close( JNIEnv*env,jclasscls){ lame_close(glf); glf=NULL; }com_maxi_mp3record_MP3Recorder.h
/*DONOTEDITTHISFILE-itismachinegenerated*/ #include/*Headerforclasscom_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 */ JNIEXPORTvoidJNICALLJava_com_maxi_mp3record_MP3Recorder_init (JNIEnv*,jclass,jint,jint,jint,jint,jint); /* *Class:com_maxi_mp3record_MP3Recorder *Method:encode *Signature:([S[SI[B)I */ JNIEXPORTjintJNICALLJava_com_maxi_mp3record_MP3Recorder_encode (JNIEnv*,jclass,jshortArray,jshortArray,jint,jbyteArray); /* *Class:com_maxi_mp3record_MP3Recorder *Method:flush *Signature:([B)I */ JNIEXPORTjintJNICALLJava_com_maxi_mp3record_MP3Recorder_flush (JNIEnv*,jclass,jbyteArray); /* *Class:com_maxi_mp3record_MP3Recorder *Method:close *Signature:()V */ JNIEXPORTvoidJNICALLJava_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文件下,没有自行创建,各个平台便都可以加载了。使用方法
MP3Recorderrecorder=newMP3Recorder(8000); recorder.setFilePath(voicePath);//录音保存目录 recorder.start();//开始录音 recorder.stop();//录音结束 recorder.getVoiceLevel()//这是我封装的获取音频振幅接口,大家可以用来录音的时候显示声音大小,数据自行调节。总结
之前一直用MediaRecorder录音,发现录出来的只能是amr、acc等格式,用lame转MP3感觉是不可行的。我试了没能成功,不知道具体是什么原因,所以大家有时间可以研究研究,没时间就不要尝试了。
Mp3lame录制出来的声音还是挺靠谱的(不过据听说iOS就有些莎莎声),然后录制出来的大小还是可以接受的,五秒钟的音频大概在20k左右的样子吧。使用还是很方便的。如果有什么疑问或建议请留言哈。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。