AudioRecoder 錄音

packagecom.ldm.mediarecorder.activity;

importandroid.Manifest;

importandroid.content.pm.PackageManager;

importandroid.media.AudioFormat;

importandroid.media.AudioManager;

importandroid.media.AudioRecord;

importandroid.media.AudioTrack;

importandroid.media.MediaRecorder;

importandroid.os.AsyncTask;

importandroid.os.Build;

importandroid.os.Environment;

importandroid.os.Handler;

importandroid.os.Message;

importandroid.support.annotation.NonNull;

importandroid.support.v4.app.ActivityCompat;

importandroid.support.v4.content.ContextCompat;

importandroid.util.Log;

importandroid.view.View;

importandroid.widget.AdapterView;

importandroid.widget.Button;

importandroid.widget.ListView;

importandroid.widget.Toast;

importcom.ldm.mediarecorder.R;

importcom.ldm.mediarecorder.adapter.AudioAdapter;

importcom.ldm.mediarecorder.base.BaseActivity;

importcom.ldm.mediarecorder.base.Constant;

importcom.ldm.mediarecorder.model.FileBean;

importjava.io.BufferedInputStream;

importjava.io.DataInputStream;

importjava.io.File;

importjava.io.FileInputStream;

importjava.io.FileOutputStream;

importjava.io.IOException;

importjava.util.ArrayList;

importjava.util.List;

importjava.util.concurrent.ExecutorService;

importjava.util.concurrent.Executors;

/**

* 該實例中,我們使用AudioRecord類來完成我們的音頻錄制程序

* AudioRecord類,我們可以使用三種不同的read方法來完成錄制工作,

* 每種方法都有其實用的場合

* 一、實例化一個AudioRecord類我們需要傳入幾種參數

* 1、AudioSource:這里可以是MediaRecorder.AudioSource.MIC

* 2、SampleRateInHz:錄制頻率,可以為8000hz或者11025hz等,不同的硬件設備這個值不同

* 3、ChannelConfig:錄制通道,可以為AudioFormat.CHANNEL_CONFIGURATION_MONO和AudioFormat.CHANNEL_CONFIGURATION_STEREO

* 4、AudioFormat:錄制編碼格式,可以為AudioFormat.ENCODING_16BIT和8BIT,其中16BIT的仿真性比8BIT好,但是需要消耗更多的電量和存儲空間

* 5、BufferSize:錄制緩沖大小:可以通過getMinBufferSize來獲取

* 這樣我們就可以實例化一個AudioRecord對象了

* 二、創建一個文件,用于保存錄制的內容

* 同上篇

* 三、打開一個輸出流,指向創建的文件

* DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)))

* 四、現在就可以開始錄制了,我們需要創建一個字節數組來存儲從AudioRecorder中返回的音頻數據,但是

* 注意,我們定義的數組要小于定義AudioRecord時指定的那個BufferSize

* short[]buffer = new short[BufferSize/4];

* startRecording();

* 然后一個循環,調用AudioRecord的read方法實現讀取

* 另外使用MediaPlayer是無法播放使用AudioRecord錄制的音頻的,為了實現播放,我們需要

* 使用AudioTrack類來實現

* AudioTrack類允許我們播放原始的音頻數據

*

*

* 一、實例化一個AudioTrack同樣要傳入幾個參數

* 1、StreamType:在AudioManager中有幾個常量,其中一個是STREAM_MUSIC;

* 2、SampleRateInHz:最好和AudioRecord使用的是同一個值

* 3、ChannelConfig:同上

* 4、AudioFormat:同上

* 5、BufferSize:通過AudioTrack的靜態方法getMinBufferSize來獲取

* 6、Mode:可以是AudioTrack.MODE_STREAM和MODE_STATIC,關于這兩種不同之處,可以查閱文檔

* 二、打開一個輸入流,指向剛剛錄制內容保存的文件,然后開始播放,邊讀取邊播放

*

* 實現時,音頻的錄制和播放分別使用兩個AsyncTask來完成

*/

public classAudioRecordActivityextendsBaseActivity {

privatePlayTaskplayer;

privateButtonstart_tv;

privateListViewlistView;

//線程操作

privateExecutorServicemExecutorService;

//當前是否正在錄音

private volatile booleanisRecording;

//錄音開始時間與結束時間

private longstartTime,endTime;

//錄音所保存的文件

privateFilemAudioFile;

/**

* 文件列表數據

*/

privateListdataList;

privateAudioAdaptermAudioAdapter;

//private String mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/audio/";

privateStringmFilePath= Environment.getExternalStorageDirectory().getAbsolutePath()+"/zhangxinaaaaa/";

private byte[]mBuffer;

privateFileOutputStreammFileOutPutStream;

//文件流錄音API

privateAudioRecordmAudioRecord;

private volatile booleanisPlaying;

/**

* 配置AudioRecord

*/

private final intaudioSource= MediaRecorder.AudioSource.MIC;

private intfrequence=8000;//錄制頻率,單位hz.這里的值注意了,寫的不好,可能實例化AudioRecord對象的時候,會出錯。我開始寫成11025就不行。這取決于硬件設備

private intchannelConfig= AudioFormat.CHANNEL_CONFIGURATION_MONO;

private intaudioEncoding= AudioFormat.ENCODING_PCM_16BIT;

//緩存大小

private static final intBUFFER_SIZE=2048;

//計算AudioRecord內部buffer大小

private final intminBufferSize= AudioRecord.getMinBufferSize(frequence,channelConfig,audioEncoding);

//更新UI線程的Handler

privateHandlermHandler=newHandler() {

@Override

public voidhandleMessage(Message msg) {

super.handleMessage(msg);

switch(msg.what) {

caseConstant.RECORD_SUCCESS:

//錄音成功,展示數據

if(null==mAudioAdapter) {

mAudioAdapter=newAudioAdapter(AudioRecordActivity.this,dataList,R.layout.file_item_layout);

}

listView.setAdapter(mAudioAdapter);

break;

//錄音失敗

caseConstant.RECORD_FAIL:

showToastMsg(getString(R.string.record_fail));

break;

//錄音時間太短

caseConstant.RECORD_TOO_SHORT:

showToastMsg(getString(R.string.time_too_short));

break;

caseConstant.PLAY_COMPLETION:

showToastMsg(getString(R.string.play_over));

break;

caseConstant.PLAY_ERROR:

showToastMsg(getString(R.string.play_error));

break;

}

}

};

@Override

protected voidsetWindowView() {

setContentView(R.layout.activity_record);

dataList=newArrayList<>();

mExecutorService= Executors.newSingleThreadExecutor();

mBuffer=new byte[BUFFER_SIZE];

}

@Override

protected voidinitViews() {

this.start_tv= (Button) findViewById(R.id.start_tv);

this.listView= (ListView) findViewById(R.id.listview);

}

@Override

protected voidinitEvents() {

this.start_tv.setOnClickListener(this);

//點擊Item播放對應的聲音文件

this.listView.setOnItemClickListener(newAdapterView.OnItemClickListener() {

@Override

public voidonItemClick(AdapterView adapterView,View view, inti, longl) {

//使用AudioTrack播放聲音流文件

Toast.makeText(AudioRecordActivity.this,i +"",Toast.LENGTH_SHORT).show();

player=newPlayTask(dataList.get(i).getFile());

player.execute();

}

});

}

@Override

public voidonClick(View view) {

switch(view.getId()) {

//開始錄音操作

caseR.id.start_tv:

//正在錄音

if(isRecording) {

isRecording=false;

start_tv.setText(R.string.start_record);

//停止錄音

mExecutorService.submit(newRunnable() {

@Override

public voidrun() {

stopRecord();

}

});

}else{

isRecording=true;

start_tv.setText(R.string.stop_record);

//錄音操作

mExecutorService.submit(newRunnable() {

@Override

public voidrun() {

if(Build.VERSION.SDK_INT>22) {

//6.0以上權限管理

permissionForM();

}else{

//開始錄音

startRecord();

}

}

});

}

break;

}

}

/**

* 開始錄制

*/

private voidstartRecord() {

try{

//創建錄音文件,.m4a為MPEG-4音頻標準的文件的擴展名

mAudioFile=newFile(mFilePath+ System.currentTimeMillis() +".pcm");

//創建父文件夾

mAudioFile.getParentFile().mkdirs();

//創建文件

mAudioFile.createNewFile();

//創建文件輸出流

mFileOutPutStream=newFileOutputStream(mAudioFile);

//根據上面的設置參數初始化AudioRecord

mAudioRecord=newAudioRecord(audioSource,frequence,channelConfig,audioEncoding,Math.max(minBufferSize,BUFFER_SIZE));

//開始錄音

mAudioRecord.startRecording();

//記錄開始時間

startTime= System.currentTimeMillis();

short[] buffer =new short[BUFFER_SIZE];

while(isRecording) {

//r是實際讀取的數據長度,一般而言r會小于buffersize

intr =mAudioRecord.read(buffer,0,BUFFER_SIZE);

longv =0;

// 將 buffer 內容取出,進行平方和運算

for(inti =0;i < buffer.length;i++) {

v += buffer[i] * buffer[i];

}

// 平方和除以數據總長度,得到音量大小。

doublemean = v / (double) r;

doublevolume =10* Math.log10(mean);

Log.d("zhangxin","分貝值:"+ volume);

}

//寫入數據到文件

while(isRecording) {

intread =mAudioRecord.read(mBuffer,0,BUFFER_SIZE);

if(read >0) {

//保存到指定文件

mFileOutPutStream.write(mBuffer,0,read);

}

}

}catch(IOException e) {

mHandler.sendEmptyMessage(Constant.RECORD_FAIL);

}finally{

//? ? ? ? ? ? if (null != mAudioRecord) {

//? ? ? ? ? ? ? ? //釋放資源

//? ? ? ? ? ? ? ? mAudioRecord.release();

//? ? ? ? ? ? }

}

}

/**

*@description停止錄音

*

*/

private voidstopRecord() {

try{

//停止錄音

mAudioRecord.stop();

mAudioRecord.release();

mAudioRecord=null;

mFileOutPutStream.close();

//記錄時長

endTime= System.currentTimeMillis();

//錄音時間處理,比如只有大于2秒的錄音才算成功

inttime = (int) ((endTime-startTime) /1000);

if(time >=3) {

//錄音成功,添加數據

FileBean bean =newFileBean();

bean.setFile(mAudioFile);

bean.setFileLength(time);

dataList.add(bean);

//錄音成功,發Message

mHandler.sendEmptyMessage(Constant.RECORD_SUCCESS);

}else{

mAudioFile=null;

mHandler.sendEmptyMessage(Constant.RECORD_TOO_SHORT);

}

}catch(Exception e) {

mHandler.sendEmptyMessage(Constant.RECORD_FAIL);

}

}

@Override

protected voidonDestroy() {

super.onDestroy();

if(null!=mExecutorService) {

mExecutorService.shutdownNow();

}

}

/**

* 6.0以上版本手機權限處理

*/

private voidpermissionForM() {

if(ContextCompat.checkSelfPermission(this,

Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED|| ContextCompat.checkSelfPermission(this,

Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {

ActivityCompat.requestPermissions(this,

newString[]{Manifest.permission.RECORD_AUDIO,Manifest.permission.WRITE_EXTERNAL_STORAGE},

Constant.PERMISSIONS_REQUEST_FOR_AUDIO);

}else{

startRecord();

}

}

@Override

public voidonRequestPermissionsResult(intrequestCode,@NonNullString[] permissions,@NonNullint[] grantResults) {

if(requestCode == Constant.PERMISSIONS_REQUEST_FOR_AUDIO) {

if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {

startRecord();

}

return;

}

super.onRequestPermissionsResult(requestCode,permissions,grantResults);

}

/**

* 播放音頻

*/

public classPlayTaskextendsAsyncTask {

FileaudioFile;

publicPlayTask(File audioFile) {

this.audioFile= audioFile;

}

@Override

protectedVoiddoInBackground(Void... arg0) {

isPlaying=true;

intbufferSize = AudioTrack.getMinBufferSize(frequence,channelConfig,audioEncoding);

short[] buffer =new short[bufferSize /4];

try{

//定義輸入流,將音頻寫入到AudioTrack類中,實現播放

DataInputStream dis =newDataInputStream(newBufferedInputStream(newFileInputStream(audioFile)));

//實例AudioTrack

AudioTrack track =newAudioTrack(AudioManager.STREAM_MUSIC,frequence,channelConfig,audioEncoding,bufferSize,AudioTrack.MODE_STREAM);

//開始播放

track.play();

//由于AudioTrack播放的是流,所以,我們需要一邊播放一邊讀取

while(isPlaying&& dis.available() >0) {

inti =0;

while(dis.available() >0&& i < buffer.length) {

buffer[i] = dis.readShort();

i++;

}

//然后將數據寫入到AudioTrack中

track.write(buffer,0,buffer.length);

}

//播放結束

track.stop();

dis.close();

}catch(Exception e) {

//TODO: handle exception

}

return null;

}

}

}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,885評論 6 541
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,312評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,993評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,667評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,410評論 6 411
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,778評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,775評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,955評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,521評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,266評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,468評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,998評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,696評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,095評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,385評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,193評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,431評論 2 378

推薦閱讀更多精彩內容