研究錄音是源于即時通訊的項目。寫出一個即時通訊很簡單,但是寫好一個即時通訊就不是一件容易的事,比如聊天中語音的加入。接下來就來描述一下自己對語音的見解和處理方式。
首先寫到語音,當然首當其沖的是運用到網(wǎng)上百分之八九十的處理方案MediaRecorder,這個也是我首先用到的方式,主要是由于以前寫過接入環(huán)信的項目,它里面所提供的就是MediaRecorder。大概的來介紹一下這個類的幾個常用方法吧。
Initial:初始狀態(tài),當使用new()方法創(chuàng)建一個MediaRecorder對象或者調(diào)用了reset()方法時,該MediaRecorder對象處于Initial狀態(tài)。在設(shè)定視頻源或者音頻源之后將轉(zhuǎn)換為Initialized狀態(tài)。另外,在除Released狀態(tài)外的其它狀態(tài)通過調(diào)用reset()方法都可以使MediaRecorder進入該狀態(tài)。
Initialized:已初始化狀態(tài),可以通過在Initial狀態(tài)調(diào)用setAudioSource()或setVideoSource()方法進入該狀態(tài)。在這個狀態(tài)可以通過setOutputFormat()方法設(shè)置輸出格式,此時MediaRecorder轉(zhuǎn)換為DataSourceConfigured狀態(tài)。另外,通過reset()方法進入Initial狀態(tài)。
DataSourceConfigured:數(shù)據(jù)源配置狀態(tài),這期間可以設(shè)定編碼方式、輸出文件、屏幕旋轉(zhuǎn)、預(yù)覽顯示等等??梢栽贗nitialized狀態(tài)通過setOutputFormat()方法進入該狀態(tài)。另外,可以通過reset()方法回到Initial狀態(tài),或者通過prepare()方法到達Prepared狀態(tài)。
Prepared:就緒狀態(tài),在DataSourceConfigured狀態(tài)通過prepare()方法進入該狀態(tài)。在這個狀態(tài)可以通過start()進入錄制狀態(tài)。另外,可以通過reset()方法回到Initialized狀態(tài)。
Recording:錄制狀態(tài),可以在Prepared狀態(tài)通過調(diào)用start()方法進入該狀態(tài)。另外,它可以通過stop()方法或reset()方法回到Initial狀態(tài)。
Released:釋放狀態(tài)(官方文檔給出的詞叫做Idle state 空閑狀態(tài)),可以通過在Initial狀態(tài)調(diào)用release()方法來進入這個狀態(tài),這時將會釋放所有和MediaRecorder對象綁定的資源。
Error:錯誤狀態(tài),當錯誤發(fā)生的時候進入這個狀態(tài),它可以通過reset()方法進入Initial狀態(tài)
這個MediaRecorder的主要是有停供的方法來進行編碼語音,好處自然就是方便,另外一個就是它所編譯的語音文件體積非常小。大概有AAC和ARM用得比較多,環(huán)信使用的就是ARM的編碼方式。其實我覺得差別不是很大,都不是特別好的錄音,對于項目要求不高的可以考慮下,下面我也會粘貼出工具類,方便使用(EaseVoiceRecorder),對了,忘了說,它還提供通道設(shè)置setAudioChannels ?1是單聲道 2是多聲道;setAudioSamplingRate采樣率,網(wǎng)上說的是越大越好,但是我選了很多中個人覺得16000比較適中。
importjava.io.File;
importjava.io.IOException;
importjava.util.Date;
importandroid.content.Context;
importandroid.content.pm.PackageManager;
importandroid.media.MediaRecorder;
importandroid.os.Handler;
importandroid.os.SystemClock;
importandroid.text.format.Time;
importandroid.util.Log;
importcom.lvgou.distribution.driect.entity.EMError;
importcom.lvgou.distribution.utils.PathUtil;
publicclassEaseVoiceRecorder{
MediaRecorderrecorder;
staticfinalStringPREFIX="voice";
staticfinalStringEXTENSION=".mp3";
Stringuid;
privatebooleanisRecording=false;
privatelongstartTime=-4;
privateStringvoiceFilePath=null;
privateStringvoiceFileName=null;
privateFilefile;
privateHandlerhandler;
privateContextmContext;
//? ? public EaseVoiceRecorder(Handler handler) {
//? ? ? ? this.handler = handler;
//? ? }
publicEaseVoiceRecorder(){
}
/**
* @param appContext
* @param userId? ? 傳入userId 用于標示 名稱
* @return
*/
publicStringstartRecording(ContextappContext,StringuserId){
mContext=appContext;
file=null;
startTime=-4;
try{
// need to create recorder every time, otherwise, will got exception
// from setOutputFile when try to reuse
if(recorder!=null){
recorder.release();
recorder=null;
}
recorder=newMediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//? ? ? ? ? ? recorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);
//? ? ? ? ? ? recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
//? ? ? ? ? ? recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
//? ? ? ? ? ? recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
/*//? ? ? ? ? 方案一
recorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setAudioChannels(2); // MONO
recorder.setAudioSamplingRate(16000); // 8000Hz*/
//? ? ? ? ? 方案二
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
recorder.setAudioChannels(2);
recorder.setAudioSamplingRate(16000);
/*//? ? ? ? ? ? 方案三
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);*/
//? ? ? ? ? ? recorder.setAudioEncodingBitRate(64); // seems if change this to
// 128, still got same file
// size.
// one easy way is to use temp file
// file = File.createTempFile(PREFIX + userId, EXTENSION,
// User.getVoicePath());
voiceFileName=getVoiceFileName(userId);
voiceFilePath=PathUtil.getInstance().getVoicePath()+"/"+voiceFileName;
file=newFile(voiceFilePath);
recorder.setOutputFile(file.getAbsolutePath());
recorder.prepare();
isRecording=true;
recorder.start();
}catch(IOExceptione){
}
newThread(newRunnable(){
@Override
publicvoidrun(){
try{
while(isRecording){
//? ? ? ? ? ? ? ? ? ? ? ? android.os.Message msg = new android.os.Message();
//? ? ? ? ? ? ? ? ? ? ? ? msg.what = recorder.getMaxAmplitude() * 13 / 0x7FFF;
//? ? ? ? ? ? ? ? ? ? ? ? handler.sendMessage(msg);
SystemClock.sleep(100);
}
}catch(Exceptione){
// from the crash report website, found one NPE crash from
// one android 4.0.4 htc phone
// maybe handler is null for some reason
}
}
}).start();
startTime=newDate().getTime();
returnfile==null?null:file.getAbsolutePath();
}
/**
* stop the recoding
*
* @return seconds of the voice recorded
*/
publicvoiddiscardRecording(){
if(recorder!=null){
try{
recorder.stop();
recorder.release();
recorder=null;
if(file!=null&&file.exists()&&!file.isDirectory()){
file.delete();
}
}catch(IllegalStateExceptione){
}catch(RuntimeExceptione){
}
isRecording=false;
}
}
publicintgetRatio(){
if(recorder!=null){
intratio=recorder.getMaxAmplitude()/600;
returnratio;
}
return-1;
}
publicintstopRecoding(){
if(recorder!=null){
isRecording=false;
if(startTime>-1){
try{
recorder.stop();
recorder.release();
}catch(Exceptione){
e.printStackTrace();
}
recorder=null;
}
intseconds=(int)(newDate().getTime()-startTime)/1000;
if(seconds>0){
if(file==null||!file.exists()||!file.isFile()){
returnEMError.FILE_INVALID;
}
if(file.length()==0){
file.delete();
returnEMError.FILE_INVALID;
}
}
returnseconds;
}
return0;
}
protectedvoidfinalize()throwsThrowable{
super.finalize();
if(recorder!=null){
recorder.release();
}
}
privateStringgetVoiceFileName(Stringuid){
Timenow=newTime();
now.setToNow();
this.uid=uid;
returnuid+now.toString().substring(0,15)+EXTENSION;
}
publicbooleanisRecording(){
returnisRecording;
}
publicStringgetVoiceFilePath(){
returnvoiceFilePath;
}
publicStringgetVoiceTargetFilePath(){
Timenow=newTime();
now.setToNow();
returnPathUtil.getInstance().getVoicePath()+"/"+uid+now.toString().substring(0,15)+".mp3";
}
publicStringgetVoiceFileName(){
returnvoiceFileName;
}
}
EaseVoiceRecorder.java
體積這么小而且這么方便,很大的一個缺點,也是我放棄的理由,就是錄下的音質(zhì)不太好,總感覺被什么籠罩著在。因此就開始尋求其它的解決方式,接下來就引用到了AudioRecord。主要是因為ios也是在使用這個所以就嘗試著加入它來試試。大概描述一下簡單工作流程:
1.創(chuàng)建一個數(shù)據(jù)流。
2.構(gòu)造一個AudioRecord對象,其中需要的最小錄音緩存buffer大小可以通過getMinBufferSize方法得到。如果buffer容量過小,將導(dǎo)致對象構(gòu)造的失敗。
3.初始化一個buffer,該buffer大于等于AudioRecord對象用于寫聲音數(shù)據(jù)的buffer大小。
4.開始錄音。
5.從AudioRecord中讀取聲音數(shù)據(jù)到初始化buffer,將buffer中數(shù)據(jù)導(dǎo)入數(shù)據(jù)流。
6.停止錄音。
7.關(guān)閉數(shù)據(jù)流。
首先遇到的問題是錄音下來的東西是不能播放的,就是PCM格式的音頻文件,也就是通常遇到的raw文件。簡單的來說就是裸音。如果需要播放的話,必須要給裸數(shù)據(jù)加上頭文件。這樣得到的就是WAV格式的音頻的文件,也是是電腦上經(jīng)常使用的無損音質(zhì)了,但是對于這么好的音質(zhì),引來的第一大問題就是文件體積比較大簡單來說錄音10秒大概就有四百多KB的大小。這可能對于即時通訊的項目是不行的,先粘貼出工具類先,方便大家其它地方有用到。
importandroid.media.AudioFormat;
importandroid.media.AudioRecord;
importandroid.os.Environment;
importandroid.text.format.Time;
importandroid.util.Log;
importjava.io.File;
importjava.io.FileInputStream;
importjava.io.FileNotFoundException;
importjava.io.FileOutputStream;
importjava.io.IOException;
importjava.util.Date;
importstaticcom.lvgou.distribution.driect.AudioFileFunc.isSdcardExit;
/**
* Created by Administrator on 2017/6/12.
*/
publicclassChatAudioRecord{
// 緩沖區(qū)字節(jié)大小
privateintbufferSizeInBytes=0;
//AudioName裸音頻數(shù)據(jù)文件 ,麥克風
privateStringAudioName="";
//NewAudioName可播放的音頻文件
privateStringNewAudioName="";
privateAudioRecordaudioRecord;
privatebooleanisRecord=false;// 設(shè)置正在錄制的狀態(tài)
publicbooleanisRecording(){
returnisRecord;
}
privatestaticChatAudioRecordmInstance;
privateChatAudioRecord(){
}
publicsynchronizedstaticChatAudioRecordgetInstance(){
if(mInstance==null)
mInstance=newChatAudioRecord();
returnmInstance;
}
publicStringgetVoiceFilePath(){
/* try {
execute(new File(NewAudioName),myuid);
Log.e("lkhfkhsdfg", "--------"+NewAudioName );
return NewAudioName;
} catch (Exception e) {
e.printStackTrace();
Log.e("lkhfkhsdfg", "---------"+e );
}
return "";*/
//? ? ? ? writeMP3();
returnNewAudioName;
}
privateStringmyuid="";
publicintstartRecordAndFile(Stringuid){
//判斷是否有外部存儲設(shè)備sdcard
if(isSdcardExit()){
if(isRecord){
returnErrorCode.E_STATE_RECODING;
}else{
if(audioRecord==null)
myuid=uid;
creatAudioRecord(uid);
audioRecord.startRecording();
// 讓錄制狀態(tài)為true
isRecord=true;
// 開啟音頻文件寫入線程
newThread(newAudioRecordThread()).start();
startTime=newDate().getTime();
returnErrorCode.SUCCESS;
}
}else{
returnErrorCode.E_NOSDCARD;
}
}
privatelongstartTime=-4;
publicintstopRecordAndFile(){
close();
intseconds=(int)(newDate().getTime()-startTime)/1000;
returnseconds;
}
publiclonggetRecordFileSize(){
returnAudioFileFunc.getFileSize(NewAudioName);
}
privatevoidclose(){
if(audioRecord!=null){
System.out.println("stopRecord");
isRecord=false;//停止文件寫入
audioRecord.stop();
audioRecord.release();//釋放資源
audioRecord=null;
}
}
privateStringuid="";
staticfinalStringEXTENSION=".mp3";
privateStringgetVoiceFileName(Stringuid,Stringnamess){
Timenow=newTime();
now.setToNow();
this.uid=uid;
returnuid+now.toString().substring(0,15)+namess;
}
privatefinalstaticStringAUDIO_RAW_FILENAME="RawAudio.raw";
privatefinalstaticStringAUDIO_WAV_FILENAME="FinalAudio.wav";
publicfinalstaticStringAUDIO_AMR_FILENAME="FinalAudio.amr";
publicStringgetRawFilePath(StringuserId){
StringmAudioRawPath="";
if(isSdcardExit()){
StringfileBasePath=Environment.getExternalStorageDirectory().getAbsolutePath();
mAudioRawPath=fileBasePath+"/"+getVoiceFileName(userId,AUDIO_RAW_FILENAME);
}
returnmAudioRawPath;
}
/**
* 獲取編碼后的WAV格式音頻文件路徑
* @return
*/
publicStringgetWavFilePath(StringuserId){
StringmAudioWavPath="";
if(isSdcardExit()){
StringfileBasePath=Environment.getExternalStorageDirectory().getAbsolutePath();
mAudioWavPath=fileBasePath+"/"+getVoiceFileName(userId,AUDIO_WAV_FILENAME);
}
returnmAudioWavPath;
}
privatevoidcreatAudioRecord(StringuserId){
// 獲取音頻文件路徑
AudioName=getRawFilePath(userId);
//? ? ? ? voiceFilePath= voiceFilePath = PathUtil.getInstance().getVoicePath() + "/" + voiceFileName;
//? ? ? ? NewAudioName = AudioFileFunc.getWavFilePath();
NewAudioName=getWavFilePath(userId);
// 獲得緩沖區(qū)字節(jié)大小
bufferSizeInBytes=AudioRecord.getMinBufferSize(AudioFileFunc.AUDIO_SAMPLE_RATE,
AudioFormat.CHANNEL_IN_STEREO,AudioFormat.ENCODING_PCM_16BIT);
// 創(chuàng)建AudioRecord對象
audioRecord=newAudioRecord(AudioFileFunc.AUDIO_INPUT,AudioFileFunc.AUDIO_SAMPLE_RATE,
AudioFormat.CHANNEL_IN_STEREO,AudioFormat.ENCODING_PCM_16BIT,bufferSizeInBytes);
}
classAudioRecordThreadimplementsRunnable{
@Override
publicvoidrun(){
writeDateTOFile();//往文件中寫入裸數(shù)據(jù)
copyWaveFile(AudioName,NewAudioName);//給裸數(shù)據(jù)加上頭文件
//? ? ? ? ? ? writeMP3();
}
}
/**
* 這里將數(shù)據(jù)寫入文件,但是并不能播放,因為AudioRecord獲得的音頻是原始的裸音頻,
* 如果需要播放就必須加入一些格式或者編碼的頭信息。但是這樣的好處就是你可以對音頻的 裸數(shù)據(jù)進行處理,比如你要做一個愛說話的TOM
* 貓在這里就進行音頻的處理,然后重新封裝 所以說這樣得到的音頻比較容易做一些音頻的處理。
*/
privatevoidwriteDateTOFile(){
// new一個byte數(shù)組用來存一些字節(jié)數(shù)據(jù),大小為緩沖區(qū)大小
byte[]audiodata=newbyte[bufferSizeInBytes];
FileOutputStreamfos=null;
intreadsize=0;
try{
Filefile=newFile(AudioName);
if(file.exists()){
file.delete();
}
fos=newFileOutputStream(file);// 建立一個可存取字節(jié)的文件
}catch(Exceptione){
e.printStackTrace();
}
while(isRecord==true){
readsize=audioRecord.read(audiodata,0,bufferSizeInBytes);
if(AudioRecord.ERROR_INVALID_OPERATION!=readsize&&fos!=null){
try{
fos.write(audiodata);
}catch(IOExceptione){
e.printStackTrace();
}
}
}
try{
if(fos!=null)
fos.close();// 關(guān)閉寫入流
}catch(IOExceptione){
e.printStackTrace();
}
}
// 這里得到可播放的音頻文件
privatevoidcopyWaveFile(StringinFilename,StringoutFilename){
FileInputStreamin=null;
FileOutputStreamout=null;
longtotalAudioLen=0;
longtotalDataLen=totalAudioLen+36;
longlongSampleRate=AudioFileFunc.AUDIO_SAMPLE_RATE;
intchannels=2;
longbyteRate=16*AudioFileFunc.AUDIO_SAMPLE_RATE*channels/8;
byte[]data=newbyte[bufferSizeInBytes];
try{
in=newFileInputStream(inFilename);
out=newFileOutputStream(outFilename);
totalAudioLen=in.getChannel().size();
totalDataLen=totalAudioLen+36;
WriteWaveFileHeader(out,totalAudioLen,totalDataLen,
longSampleRate,channels,byteRate);
while(in.read(data)!=-1){
out.write(data);
}
in.close();
out.close();
}catch(FileNotFoundExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}
}
/**
* 這里提供一個頭信息。插入這些信息就可以得到可以播放的文件。
* 為我為啥插入這44個字節(jié),這個還真沒深入研究,不過你隨便打開一個wav
* 音頻的文件,可以發(fā)現(xiàn)前面的頭文件可以說基本一樣哦。每種格式的文件都有
* 自己特有的頭文件。
*/
privatevoidWriteWaveFileHeader(FileOutputStreamout,longtotalAudioLen,
longtotalDataLen,longlongSampleRate,intchannels,longbyteRate)
throwsIOException{
byte[]header=newbyte[44];
header[0]='R';// RIFF/WAVE header
header[1]='I';
header[2]='F';
header[3]='F';
header[4]=(byte)(totalDataLen&0xff);
header[5]=(byte)((totalDataLen>>8)&0xff);
header[6]=(byte)((totalDataLen>>16)&0xff);
header[7]=(byte)((totalDataLen>>24)&0xff);
header[8]='W';
header[9]='A';
header[10]='V';
header[11]='E';
header[12]='f';// 'fmt ' chunk
header[13]='m';
header[14]='t';
header[15]=' ';
header[16]=16;// 4 bytes: size of 'fmt ' chunk
header[17]=0;
header[18]=0;
header[19]=0;
header[20]=1;// format = 1
header[21]=0;
header[22]=(byte)channels;
header[23]=0;
header[24]=(byte)(longSampleRate&0xff);
header[25]=(byte)((longSampleRate>>8)&0xff);
header[26]=(byte)((longSampleRate>>16)&0xff);
header[27]=(byte)((longSampleRate>>24)&0xff);
header[28]=(byte)(byteRate&0xff);
header[29]=(byte)((byteRate>>8)&0xff);
header[30]=(byte)((byteRate>>16)&0xff);
header[31]=(byte)((byteRate>>24)&0xff);
header[32]=(byte)(2*16/8);// block align
header[33]=0;
header[34]=16;// bits per sample
header[35]=0;
header[36]='d';
header[37]='a';
header[38]='t';
header[39]='a';
header[40]=(byte)(totalAudioLen&0xff);
header[41]=(byte)((totalAudioLen>>8)&0xff);
header[42]=(byte)((totalAudioLen>>16)&0xff);
header[43]=(byte)((totalAudioLen>>24)&0xff);
out.write(header,0,44);
}
}
ChatAudioRecord.java
接下來就來處理體積大這個問題,首先我用到的是lame,就是去網(wǎng)上找到mp3lame.so庫,然后修改它的mk文件,將其引入到項目中,其實也就是在錄音的過程中將音頻文件的編碼改成mp3格式。于是就在github上找了相關(guān)的文章
https://github.com/search?q=android+mp3+&type=Repositories&ref=searchresults
https://github.com/yhirano/Mp3VoiceRecorderSampleForAndroid
關(guān)于使用,只需要項目中的libmp3lame.so文件,和com.uraroji.garage.android.lame包下的SimpleLame.java文件和RecMicToMp3.java文件,注意SimpleLame.java必須放在com.uraroji.garage.android.lame包下。
但是對于按照它的操作出現(xiàn)的問題就是
這一下就尷尬了,然后在網(wǎng)上找了一些解決方式,后來報錯就是找不到so庫的錯,接著我又將so庫復(fù)制到其它的幾個文件夾里面還是相同的問題,我然后就去修改那些.h和.c文件。希望在這里找到一些解藥,對于一個多年沒接觸C語言的人來說,那個里面的調(diào)用著實讓我看不懂。因此就放棄了這條路。
接著就是VoAACEncorder,這個方式,我也記不得是在github上哪個項目弄下來的。首先是它的demo可以運行,并且音質(zhì)也很不錯,基本就是AAC的音頻格式,并且這個格式的體積并不大,我錄了120秒的音頻文件大概就只有470kb左右的大小吧,當我看到這里的時候認定自己選擇的就是這個,接下來就想方設(shè)法的研究出來然后插入到自己項目中。主要的就是jni中so庫的試用,github里的項目是ec編寫的,但是對于現(xiàn)在這個as稱道的時代,還是一個一個復(fù)制吧。就兩個so文件libAacEncoder.so和libVoAACEncoder.so。方法也就那幾個,我也粘貼出util.
首先出現(xiàn)的問題就和lame后面那個一樣,找不到so庫,
這對于一個使用jni不熟練的人來說確實是個難題。其實這個問題的原因就是那個java文件中的native方法,native標識的方法就是調(diào)用C語言的地方,由于沒有找到指定的位子。但是,對于標識指定位子的文件就是so庫里面的mk文件里面標識的,這個對于我們來說是不方便編輯和更改的,但是我們可以更改外部編寫有native方法文件的包名啊。按照mk文件里的包名進行重新創(chuàng)造。首先在我的一臺測試機上運行成功,也能正常錄音,但是當我換一臺測試機的時候,又報了一個錯,就是缺少64位的so庫。這個問題就很尷尬了啊,提供的是armeabi里面的so啊,這個應(yīng)該是32位和64位通用的啊,然后我發(fā)現(xiàn)我還有一些其它的文件夾,安卓在尋找so文件的時候是會優(yōu)先尋找和自己匹配的文件夾下的so,沒有找打就會報錯。接著我就引入了armeabi-v7a包,在這個下面把armeabi里面的so文件拷貝過來,這個也就是處理兼容性問題的地方。然后就能順利運行和錄音了。但是還是存在一個缺點,對于即時通訊的項目是錄音完成后馬上發(fā)送的,這個錄音的結(jié)構(gòu)來說是邊錄邊轉(zhuǎn)碼的,很有可能在你沒轉(zhuǎn)碼成功的情況下就進行發(fā)送,這就導(dǎo)致服務(wù)器那邊出現(xiàn)找不到文件的錯誤。我的處理方式是在錄音完成后會停頓一秒,然后再進行發(fā)送,這樣就大大降低了發(fā)送失敗的次數(shù)。但偶爾還是會存在失敗,接著我就在失敗返回監(jiān)聽的方法里面再次將失敗了的文件重新發(fā)送,如果還是失敗,那么我就告訴用戶失敗了,在消息后面加一個紅色邊框里面是感嘆號,讓用戶自己點擊后重新發(fā)送,目前這樣的處理方式是沒有出現(xiàn)過失敗的了。
importandroid.text.format.Time;
importandroid.util.Log;
importcom.lvgou.distribution.utils.PathUtil;
importjava.util.Date;
importlwx.linin.aac.VoAAC;
/**
* Created by Administrator on 2017/6/14.
*/
publicclassACCVoiceRecorder{
privateintsampleRateInHz=16000;
privateVoAACaac;
privateStringfileName;
staticfinalStringEXTENSION=".mp3";
privateStringuid="";
privatelongstartTime=-4;
privatebooleanisRecording=false;//是否正在錄音
privateStringvoiceFilePath;
publicACCVoiceRecorder(){
}
publicStringgetVoiceFilePath(){
returnvoiceFilePath;
}
publicvoidstartRecording(StringuserId){
fileName=getVoiceFileName(userId);
voiceFilePath=PathUtil.getInstance().getVoicePath()+"/"+fileName;
Log.e("aslkdfhakshfd","------------"+voiceFilePath);
aac=newVoAAC(voiceFilePath);
aac.sampleRateInHz(sampleRateInHz);
aac.start();
startTime=newDate().getTime();
isRecording=true;
}
privateStringgetVoiceFileName(Stringuid){
Timenow=newTime();
now.setToNow();
this.uid=uid;
returnuid+now.toString().substring(0,15)+EXTENSION;
}
publicintstopRecoding(){
intseconds=(int)(newDate().getTime()-startTime)/1000;
aac.stop();
isRecording=false;
returnseconds;
}
publicvoiddiscardRecording(){
stopRecoding();
}
publicbooleanisRecording(){
returnisRecording;
}
publicStringgetVoiceFileName(){
returnfileName;
}
}
csdn項目地址:http://blog.csdn.net/greatdaocaoren/article/details/73433527