實現錄音功能

我們知道AndroidApI提供了MediaRecorder和AudioRecord兩個類給開發(fā)者來很方便地實現音視頻的錄制(前者可以實現音頻和視頻的錄制,后者只能實現音頻的錄制)。這兩個類都提供了start()和stop()方法用于開始和結束音頻或視頻的錄制,但令人費解的是這兩個類都沒有提供pause()方法用于暫停錄制音視頻,因為在實際應用當中,暫停錄制的功能是非常有必要的,暫不清楚Google工程師們在設計API時是如何考量的而沒有添加這個方法,可能另有玄機吧。那既然android自身沒有提供這樣一個方法,就只有我們自己來實現了,那么問題就來了,就是到底如何實現音頻錄制的暫停方法呢?別急,先講一下我在工作中所遇到的需求,如下:需實現音頻錄制的暫停功能,并且生成的音頻文件格式必須是m4a格式。為什么項目中音頻文件一定要采用m4a格式的呢?有以下幾點原因:

1. 錄制相同時間的音頻,使用m4a格式存儲的文件的大小要比使用其它格式類型存儲的文件的大小要小(通過實驗多次,在相同采樣率16000的情況下,一般錄制5分鐘的音頻,采用m4a格式存儲的音頻文件只有1.2Mb,而采用arm、mp3及其它格式的一般都有2-5Mb),這樣當用戶需要下載或上傳錄制的音頻文件時,可以節(jié)省流量,并且相同壓縮率的前提下,m4a格式音頻的音質相比其它格式的也更高;

2.產品同時擁有Android客戶端和iOS客戶端,那為了避免使用Android客戶端的用戶錄制的音頻上傳到服務器之后,使用ios客戶端的用戶下載下來發(fā)生無法播放的問題,我們需統一錄制音頻的存儲格式。由于Iphone手機官方推薦的音頻格式是m4a且對m4a格式的音頻文件支持度較高,再綜合第一點來看,于是我們選擇m4a格式作為音頻文件的存儲格式。

好了,解釋了為什么音頻錄制文件必須使用m4a存儲格式之后,接下來我們來解決如何實現音頻的錄制的暫停功能。前面講了,Android SDK

API提供了MediaRecorder和AudioRecord兩個類來完成音視頻的錄制方法,我們看下它們兩者之間的特點和區(qū)別:

MediaRecorder:

特性:該類集成了錄音、編碼和壓縮等功能,可根據設置的編碼格式的參數直接生成各種格式的音頻文件(如arm、

mp3或m4a等),由于集成度較高,因此使用起來簡單,但靈活度不高,不能實現像AudioRecord那樣進行音

頻的實時處理。

AudioRecord:

特性:該類錄制的音頻為原始的PCM二進制音頻數據,沒有文件頭和文件尾,生成的PCM文件不能直接使用

Mediaplayer播放,只能使用AudioTrack播放。使用AudioRecord可以實現邊錄邊播的音頻實時處理。

了解了這兩個類的特性之后,起初我決定使用MediaRecorder類來解決錄制暫停的問題,具體的思路如下:

(1)每次觸發(fā)開始錄制和暫停錄制音頻的事件時都單獨保存一個m4a格式的音頻文件,直到最后觸發(fā)停止錄制音頻的事件時,將之前錄制的若干m4a格式的音頻文件合并成一個文件。如圖下:

這種方法比較好理解,也容易想到,不過在實現過程中遇到了一個技術難點,那就是多個m4a格式的音頻文件的合并并不是簡單地將文件的內容拷貝到一個文件中,而是要通過分析每一個m4a格式的音頻文件,計算出每個文件頭的結構大小,并將文件頭去掉,再將文件進行拷貝合并。通過查閱資料,發(fā)現m4a格式的音頻文件頭是由多個包含關系的ATOM結構組成,且每個不同的m4a格式的音頻文件的文件頭的大小都不一樣,這樣使得多個m4a文件頭文件解析和合并變得較為復雜,若有多個m4a文件需要合并,那么會變得較為耗時。再者,對于沒有足夠音視頻文件解析和編解碼經驗的開發(fā)者來講,要精準地得解析一個m4a文件,挑戰(zhàn)性太大(網上這方面的資料也寥寥無幾),有興趣的讀者可以進行深入研究。

上述方法行不通,于是只好作罷,后來又想到了另外一種方法,也是我解決問題的最終方案,具體的思路如下:

(2)由于使用AudioRecord類提供的方法錄制的音頻是原始的PCM格式的二進制數據,該格式的文件沒有文件頭信息,那么我們在進行文件合并時就就無需解析文件結構去掉對應的文件頭,這樣就變成了二進制數據地簡單拷貝和合并。我在這里實現的方式是在錄制音頻的過程中采用邊錄制邊寫入的方式不斷地向同一個文件寫入錄制的二進制音頻數據。當觸發(fā)暫停錄音事件時,停止錄制停止寫入二進制數據,當觸發(fā)繼續(xù)錄音事件時,則繼續(xù)錄制和向文件中寫入數據。最后停止寫入數據時,將PCM二進制音頻文件編碼成m4a格式的音頻文件。如圖下:

上面方法描述中,實現邊錄制邊寫入的功能倒比較簡單,關鍵難點是如何將PCM二進制數據編碼成目標的m4a格式的音頻數據,要實現音視頻的編解碼,一般都是使用第三方開源的編解碼庫,比較著名的有FFMpeg和Speex,這些庫都提供了錄制、轉換以及流化音視頻的完整解決方案,不過在此我的需求只是需要簡單地實現編碼工作,使用這些開源庫體積太大,有點殺雞用牛刀的感覺。因此,通過研究和查閱資料,我在github上找到了一個非常有用的編解碼開源項目android-aac-enc(地址:https://github.com/timsu/android-aac-enc),該開源項目能完美地實現將原始的pcm格式的二進制數據編碼成m4a格式的數據文件,相比于FFmpeg庫,這個庫有以下幾點優(yōu)點:

1. aac-enc庫的體積比FFmpeg庫的體積更??;

2. 相比FFMpeg, aac-enc實現格式轉換更加簡單和快速;

3. aac-enc比FFmpeg需要編譯更少的底層的代碼。

該開源項目使用起來也非常地簡單,通過分析其示例代碼我們可以通過以下四個步驟來實現音頻的編碼工作,代碼如下:

/**

* 1.初始化編碼配置

*

* 32000 : 音頻的比特率

* 2 : 音頻的聲道

* sampleRateInHz : 音頻采樣率

* 16 :音頻數據格式,PCM 16位每個樣本

* FileUtils.getAAcFilePath(mAudioRecordFileName) : aac音頻文件的存儲路徑

*/

encoder.init(32000, 2, sampleRateInHz, 16, FileUtils.

getAAcFilePath(mAudioRecordFileName));

/**

* 2.對二進制代碼進行編碼

*

* b :需要編碼的二進制音頻流

*/

encoder.encode(b);

/**

* 3. 從pcm二進制數據轉aac音頻文件編碼完成

*

*/

encoder.uninit();

/**

* 4. 將aac文件轉碼成m4a文件

*

* FileUtils.getAAcFilePath(mAudioRecordFileName) :需要編碼的aac文件路徑

* FileUtils.getM4aFilePath(mAudioRecordFileName) :編碼成m4a文件的目標路徑

*/

new AACToM4A().convert(mContext, FileUtils.getAAcFilePath(mAudioRecordFileName),

FileUtils.getM4aFilePath(mAudioRecordFileName));

使用起來是不是很簡單方便,我們無需對音頻文件格式和文件頭進行判斷和解析,只需要通過該開源項目封裝的api方法直接調用就可以很快速的將原始的二進制PCM音頻數據轉換成m4a格式的音頻數據文件。感興趣的讀者可以去研究一下該項目的源碼,了解一下其內部的實現,這里暫且不深入探究。

基本上明確好思路和編碼的實現方法后,接下來就是具體的實現過程了,我們將依據上面的思路和方法來實現一個具有暫停功能的音頻錄制Demo

如何使用AudioRecord類來實現音頻的錄制,這方面的資料很多,讀者可以先學習,簡單地入一下門。接下來我們先運行一下Demo,來看一下效果圖

(1)初始界面
(2)正在錄制界面
(3)暫停界面

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

(4)播放界面
?????????? (5)暫停播放界面?? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

粗略看了Demo的運行效果圖后,接下來我們就要來實現,這里由于要使用aac-encode項目來實現音頻的編碼,則需將該項目以library的形式集成到我們的Demo中,做完該項工作后,我們就可以在Demo工程? 中寫其它相關的邏輯代碼了,下面看一下實現demo的關鍵代碼,首先是RecordAct.Java文件中的代碼,該類為主界面類,主要實現了界面的初始化、音頻的錄制和音頻播放的功能,具體的代碼如下:

publicclassRecordActextendsActivityimplementsOnClickListener{

/**

*?Status:錄音初始狀態(tài)

*/

privatestaticfinalintSTATUS_PREPARE?=0;

/**

*?Status:正在錄音中

*/

privatestaticfinalintSTATUS_RECORDING?=1;

/**

*?Status:暫停錄音

*/

privatestaticfinalintSTATUS_PAUSE?=2;

/**

*?Status:播放初始狀態(tài)

*/

privatestaticfinalintSTATUS_PLAY_PREPARE?=3;

/**

*?Status:播放中

*/

privatestaticfinalintSTATUS_PLAY_PLAYING?=4;

/**

*?Status:播放暫停

*/

privatestaticfinalintSTATUS_PLAY_PAUSE?=5;

privateintstatus?=?STATUS_PREPARE;

/**

*?錄音時間

*/

privateTextView?tvRecordTime;

/**

*?錄音按鈕

*/

privateImageView?btnRecord;//?錄音按鈕

privatePopupWindow?popAddWindow;

/**

*?試聽界面

*/

privateLinearLayout?layoutListen;

/**

*?錄音長度

*/

privateTextView?tvLength;

privateTextView?recordContinue;

/**

*?重置按鈕

*/

privateView?resetRecord;

/**

*?結束錄音

*/

privateView?recordOver;

privateImageView?audioRecordNextImage;

privateTextView?audioRecordNextText;

/**

*?音頻播放進度

*/

privateTextView?tvPosition;

longstartTime?=0;

/**

*?最大錄音長度

*/

privatestaticfinalintMAX_LENGTH?=300*1000;

privateHandler?handler?=newHandler();

privateRunnable?runnable;

/**

*?音頻錄音的總長度

*/

privatestaticintvoiceLength;

/**

*?音頻錄音幫助類

*/

privateAudioRecordUtils?mRecordUtils;

/**

*?播放進度條

*/

privateSeekBar?seekBar;

/**

*?音頻播放類

*/

privatePlayer?player;

/**

*?錄音文件名

*/

privateString?audioRecordFileName;

@Override

protectedvoidonCreate(Bundle?savedInstanceState)?{

//?TODO?Auto-generated?method?stub

super.onCreate(savedInstanceState);

setContentView(R.layout.pop_add_record);

initView();

}

publicvoidinitView(){

//音頻錄音的文件名稱

audioRecordFileName?=?TimeUtils.getTimestamp();

//初始化音頻錄音對象

mRecordUtils?=newAudioRecordUtils(this,audioRecordFileName);

View?view?=?LayoutInflater.from(this).inflate(R.layout.pop_add_record,null);

tvRecordTime?=?(TextView)findViewById(R.id.tv_time);

btnRecord?=?(ImageView)findViewById(R.id.iv_btn_record);

btnRecord.setOnClickListener(this);

recordContinue?=?(TextView)findViewById(R.id.record_continue_txt);

resetRecord?=?findViewById(R.id.btn_record_reset);

recordOver?=?findViewById(R.id.btn_record_complete);

resetRecord.setOnClickListener(this);

recordOver.setOnClickListener(this);

audioRecordNextImage?=?(ImageView)findViewById(R.id.recrod_complete_img);

audioRecordNextText?=?(TextView)findViewById(R.id.record_complete_txt);

layoutListen?=?(LinearLayout)findViewById(R.id.layout_listen);

tvLength?=?(TextView)findViewById(R.id.tv_length);

tvPosition?=?(TextView)findViewById(R.id.tv_position);

seekBar?=?(SeekBar)findViewById(R.id.seekbar_play);

seekBar.setOnSeekBarChangeListener(newSeekBarChangeEvent());

seekBar.setEnabled(false);

player?=newPlayer(seekBar,?tvPosition);

player.setMyPlayerCallback(newMyPlayerCallback()?{

@Override

publicvoidonPrepared()?{

seekBar.setEnabled(true);

}

@Override

publicvoidonCompletion()?{

status?=?STATUS_PLAY_PREPARE;

seekBar.setEnabled(false);

seekBar.setProgress(0);

tvPosition.setText("00:00");

recordContinue.setBackgroundResource(R.drawable.record_audio_play);

}

});

popAddWindow?=newPopupWindow(view,?LayoutParams.MATCH_PARENT,

LayoutParams.MATCH_PARENT);

popAddWindow.setFocusable(true);

popAddWindow.setAnimationStyle(R.style.pop_anim);

popAddWindow.setBackgroundDrawable(newBitmapDrawable());

}

publicvoidhandleRecord(){

switch(status){

caseSTATUS_PREPARE:

mRecordUtils.startRecord();

btnRecord.setBackgroundResource(R.drawable.record_round_red_bg);

status?=?STATUS_RECORDING;

voiceLength?=0;

timing();

break;

caseSTATUS_RECORDING:

pauseAudioRecord();

resetRecord.setVisibility(View.VISIBLE);

recordOver.setVisibility(View.VISIBLE);

btnRecord.setBackgroundResource(R.drawable.record_round_blue_bg);

recordContinue.setVisibility(View.VISIBLE);

status?=?STATUS_PAUSE;

break;

caseSTATUS_PAUSE:

mRecordUtils.startRecord();

resetRecord.setVisibility(View.INVISIBLE);

recordOver.setVisibility(View.INVISIBLE);

btnRecord.setBackgroundResource(R.drawable.record_round_red_bg);

recordContinue.setVisibility(View.INVISIBLE);

status?=?STATUS_RECORDING;

timing();

break;

caseSTATUS_PLAY_PREPARE:

player.playUrl(FileUtils.getM4aFilePath(audioRecordFileName));

recordContinue.setBackgroundResource(R.drawable.record_audio_play_pause);

status?=?STATUS_PLAY_PLAYING;

break;

caseSTATUS_PLAY_PLAYING:

player.pause();

recordContinue.setBackgroundResource(R.drawable.record_audio_play);

status?=?STATUS_PLAY_PAUSE;

break;

caseSTATUS_PLAY_PAUSE:

player.play();

recordContinue.setBackgroundResource(R.drawable.record_audio_play_pause);

status?=?STATUS_PLAY_PLAYING;

break;

}

}

/**

*?暫停錄音

*/

publicvoidpauseAudioRecord(){

mRecordUtils.pauseRecord();

if(handler?!=null&&?runnable?!=null)?{

handler.removeCallbacks(runnable);

runnable?=null;

}

}

/**

*?停止錄音

*/

publicvoidstopAudioRecord(){

pauseAudioRecord();

mRecordUtils.stopRecord();

status?=?STATUS_PLAY_PREPARE;

showListen();

}

/**

*?重新錄音參數初始化

*/

@SuppressLint("NewApi")

publicvoidresetAudioRecord(){

//停止播放音頻

player.stop();

pauseAudioRecord();

mRecordUtils.reRecord();

status?=?STATUS_PREPARE;

voiceLength?=0;

tvRecordTime.setTextColor(Color.WHITE);

tvRecordTime.setText(TimeUtils.convertMilliSecondToMinute2(voiceLength));

recordContinue.setText(R.string.record_continue);

recordContinue.setBackground(null);

recordContinue.setVisibility(View.GONE);

layoutListen.setVisibility(View.GONE);

tvRecordTime.setVisibility(View.VISIBLE);

audioRecordNextImage.setImageResource(R.drawable.btn_record_icon_complete);

audioRecordNextText.setText(R.string.record_over);

btnRecord.setBackgroundResource(R.drawable.record_round_blue_bg);

resetRecord.setVisibility(View.INVISIBLE);

recordOver.setVisibility(View.INVISIBLE);

}

/**

*?計時功能

*/

privatevoidtiming()?{

runnable?=newRunnable()?{

@Override

publicvoidrun()?{

voiceLength?+=100;

if(voiceLength?>=?(MAX_LENGTH?-10*1000))?{

tvRecordTime.setTextColor(getResources().getColor(

R.color.red_n));

}else{

tvRecordTime.setTextColor(Color.WHITE);

}

if(voiceLength?>?MAX_LENGTH)?{

stopAudioRecord();

}else{

tvRecordTime.setText(TimeUtils.convertMilliSecondToMinute2(voiceLength));

handler.postDelayed(this,100);

}

}

};

handler.postDelayed(runnable,100);

}

@Override

publicvoidonClick(View?v)?{

//?TODO?Auto-generated?method?stub

switch(v.getId())?{

caseR.id.iv_btn_record:

handleRecord();

break;

caseR.id.btn_record_reset:

resetAudioRecord();

break;

caseR.id.btn_record_complete:

stopAudioRecord();

break;

default:

break;

}

}

/**

*?顯示播放界面

*/

privatevoidshowListen()?{

layoutListen.setVisibility(View.VISIBLE);

tvLength.setText(TimeUtils.convertMilliSecondToMinute2(voiceLength));

tvRecordTime.setVisibility(View.GONE);

resetRecord.setVisibility(View.VISIBLE);

recordOver.setVisibility(View.INVISIBLE);

recordContinue.setVisibility(View.VISIBLE);

seekBar.setProgress(0);

tvPosition.setText("00:00");

btnRecord.setBackgroundResource(R.drawable.record_round_blue_bg);

recordContinue.setText(null);

recordContinue.setBackgroundResource(R.drawable.record_audio_play);

}

/**

*

*?SeekBar進度條改變事件監(jiān)聽類

*/

classSeekBarChangeEventimplementsSeekBar.OnSeekBarChangeListener?{

intprogress;

@Override

publicvoidonProgressChanged(SeekBar?seekBar,intprogress,

booleanfromUser)?{

if(null!=?player?&&?player.mediaPlayer?!=null)?{

this.progress?=?progress?*?player.mediaPlayer.getDuration()

/?seekBar.getMax();

tvPosition.setText(TimeUtils

.convertMilliSecondToMinute2(player.currentPosition));

}

}

@Override

publicvoidonStartTrackingTouch(SeekBar?seekBar)?{

}

@Override

publicvoidonStopTrackingTouch(SeekBar?seekBar)?{

if(player.mediaPlayer?!=null)?{

player.mediaPlayer.seekTo(progress);

}

}

}

@Override

protectedvoidonDestroy()?{

//?TODO?Auto-generated?method?stub

super.onDestroy();

player.stop();

}

}

上面代碼注釋比較清楚,且好理解,因此不多分析,讀者自行學習。下面再來看一下AudioRecordUtils類的代碼,該類是音頻錄制功能的主要實現代碼,里面簡單地封裝了開始錄音、暫停錄音、停止錄音和重新錄音幾個方法,在開發(fā)中只要調用就行,來看看具體的實現代碼,如下:

[java]view plaincopy

publicclassAudioRecordUtils?{

privatefinalintaudioSource?=?MediaRecorder.AudioSource.MIC;

//?設置音頻采樣率,44100是目前的標準,但是某些設備仍然支持22050,16000,11025

privatefinalintsampleRateInHz?=16000;

//?設置音頻的錄制的聲道CHANNEL_IN_STEREO為雙聲道,CHANNEL_CONFIGURATION_MONO為單聲道

privatefinalintchannelConfig?=?AudioFormat.CHANNEL_IN_STEREO;

//?音頻數據格式:PCM?16位每個樣本。保證設備支持。PCM?8位每個樣本。不一定能得到設備支持。

privatefinalintaudioFormat?=?AudioFormat.ENCODING_PCM_16BIT;

privateintinBufSize?=0;

privateAudioRecord?audioRecord;

privateAACEncoder?encoder?=null;

privateProgressDialog?mProgressDialog?=null;

privatebooleanisRecord?=false;

privateContext?mContext;

/**

*?錄制的音頻文件名稱

*/

privateString?mAudioRecordFileName;

privatestaticfinalintRECORDED_INIT_DELETE?=0;

privatestaticfinalintRECORDED_COMPLETED_DELETE?=1;

publicAudioRecordUtils(Context?context,String?audioRecordFileName){

mContext?=?context;

mAudioRecordFileName?=?audioRecordFileName;

initAudioRecord();

}

/**

*?初始化對象

*/

privatevoidinitAudioRecord(){

inBufSize?=?AudioRecord.getMinBufferSize(

sampleRateInHz,

channelConfig,

audioFormat);

audioRecord??=newAudioRecord(

audioSource,

sampleRateInHz,

channelConfig,

audioFormat,

inBufSize);

encoder?=newAACEncoder();

deleteAllFiles(RECORDED_INIT_DELETE);

mProgressDialog?=newProgressDialog(mContext);

mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);

mProgressDialog.setCanceledOnTouchOutside(false);

mProgressDialog.setCancelable(false);

mProgressDialog.setTitle("提示");

mProgressDialog.setMessage("正在保存錄音,請耐心等候......");

}

/**

*?開始錄音

*/

publicvoidstartRecord(){

newAudioRecordTask().execute();

}

/**

*?暫停錄音

*/

publicvoidpauseRecord(){

isRecord?=false;

}

/**

*?停止錄音

*/

publicvoidstopRecord(){

newAudioEncoderTask().execute();

}

/**

*?重新錄制

*/

publicvoidreRecord(){

//重新錄制時,刪除錄音文件夾中的全部文件

deleteAllFiles(RECORDED_INIT_DELETE);

}

privatevoidencodeAudio(){

try{

//讀取錄制的pcm音頻文件

DataInputStream?mDataInputStream?=newDataInputStream(newFileInputStream(

FileUtils.getPcmFilePath(mAudioRecordFileName)));

byte[]?b?=newbyte[(int)newFile(FileUtils.

getPcmFilePath(mAudioRecordFileName)).length()];

mDataInputStream.read(b);

//初始化編碼配置

encoder.init(32000,2,?sampleRateInHz,16,?FileUtils.

getAAcFilePath(mAudioRecordFileName));

//對二進制代碼進行編碼

encoder.encode(b);

//編碼完成

encoder.uninit();

//關閉流

mDataInputStream.close();

try{

//將aac文件轉碼成m4a文件

newAACToM4A().convert(mContext,?FileUtils.getAAcFilePath(mAudioRecordFileName),

FileUtils.getM4aFilePath(mAudioRecordFileName));

}catch(IOException?e)?{

Log.e("ERROR","error?converting",?e);

}

deleteAllFiles(RECORDED_COMPLETED_DELETE);

}catch(FileNotFoundException?e)?{

//?TODO?Auto-generated?catch?block

e.printStackTrace();

}catch(IOException?e1)?{

//?TODO?Auto-generated?catch?block

e1.printStackTrace();

}

}

classAudioRecordTaskextendsAsyncTask{

@Override

protectedVoid?doInBackground(Void...?params)?{

//?TODO?Auto-generated?method?stub

if(audioRecord?==null){

initAudioRecord();

}

RandomAccessFile?mRandomAccessFile?=null;

try{

mRandomAccessFile?=newRandomAccessFile(newFile(

FileUtils.getPcmFilePath(mAudioRecordFileName)),"rw");

byte[]?b?=newbyte[inBufSize/4];

//開始錄制音頻

audioRecord.startRecording();

//判斷是否正在錄制

isRecord?=true;

while(isRecord){

audioRecord.read(b,0,?b.length);

//向文件中追加內容

mRandomAccessFile.seek(mRandomAccessFile.length());

mRandomAccessFile.write(b,0,?b.length);

}

//停止錄制

audioRecord.stop();

mRandomAccessFile.close();

}catch(FileNotFoundException?e)?{

//?TODO?Auto-generated?catch?block

e.printStackTrace();

}catch(IOException?e)?{

//?TODO?Auto-generated?catch?block

e.printStackTrace();

}

returnnull;

}

}

classAudioEncoderTaskextendsAsyncTask{

@Override

protectedvoidonPreExecute()?{

//?TODO?Auto-generated?method?stub

super.onPreExecute();

if(mProgressDialog?!=null&&?!mProgressDialog.isShowing()){

mProgressDialog.show();

}

}

@Override

protectedLong?doInBackground(Void...?params)?{

//?TODO?Auto-generated?method?stub

encodeAudio();

returnnull;

}

@Override

protectedvoidonPostExecute(Long?result)?{

//?TODO?Auto-generated?method?stub

super.onPostExecute(result);

if(mProgressDialog.isShowing()){

mProgressDialog.cancel();

mProgressDialog.dismiss();

}

}

}

/**

*?清空音頻錄制文件夾中的所有文件

*?@param?isRecorded

*/

publicvoiddeleteAllFiles(intisRecorded){

File[]?files?=newFile(FileUtils.getAudioRecordFilePath()).listFiles();

switch(isRecorded)?{

caseRECORDED_INIT_DELETE:

for(File?file:?files){

file.delete();

}

break;

caseRECORDED_COMPLETED_DELETE:

for(File?file:?files){

if(!file.getName().equals(mAudioRecordFileName?+?Constants.M4A_SUFFIX)){

file.delete();

}

}

break;

default:

break;

}

}

}

上面代碼關鍵處都有注釋,讀者可自行學習。自此,我們基本熟悉了實現能夠暫停錄音功能的關鍵代碼,代碼沒有全部貼出,想要完整的Demo可在文章末尾下載來仔細研究。最后我再補充一點,就是若讀者對錄制的音頻格式沒有嚴格的要求話,如錄制的音頻格式是arm格式,則沒有必要考慮到音頻的編解碼問題,因為arm格式的音頻文件的文件頭信息固定是6個字節(jié)的大小,那這種情況讀者可以采用文章開頭所說的第一種方法,就是每次點擊暫停事件都錄制成一個arm文件,在最后合并的時候,只需要去掉第2至n個文件的前6個字節(jié),然后進行文件的拷貝合并就行。

demo 下載地址?

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

相關閱讀更多精彩內容

友情鏈接更多精彩內容