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;
}
}
}