Android 錄音MediaRecorder到AudioRecord

研究錄音是源于即時通訊的項目。寫出一個即時通訊很簡單,但是寫好一個即時通訊就不是一件容易的事,比如聊天中語音的加入。接下來就來描述一下自己對語音的見解和處理方式。

首先寫到語音,當然首當其沖的是運用到網(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容