一.点击录音时
1.首先是判断是否有外部存储,再去判断外部存储的内存是否够用;
如果没有外部存储或者外部存储的内存不够用,那么不会录音,会提示错误;
①判断外部存储内存是否够用
//判断存储空间是否够用
public boolean diskSpaceAvailable() {
StatFs fs = new StatFs(mSDCardDirectory.getAbsolutePath());
// keep one free block
return fs.getAvailableBlocks() > 1;
}
其中
File mSDCardDirectory = Environment.getExternalStorageDirectory();
②判断是否有外部存储器:
Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)
2.正常录音时,首先是获取音乐播放的焦点,确保我们在录音时不会被其他音源骚扰;
//获取音乐播放的焦点
stopAudioPlayback();
//获取音乐播放的焦点,确保我们没有录制音乐播放的背景
private void stopAudioPlayback() {
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
}
3.判断音频:
需要根据不同的音频设置不同的比特率,该比特率和存储器的剩余大小有关,然后开始录音;
//设置比特率
mRemainingTimeCalculator.setBitRate(BITRATE_3GPP);
//开始录音
mRecorder.startRecording(MediaRecorder.OutputFormat.THREE_GPP, ".3gpp",this);
4.录音
录音调用的是Recorder中的startRecording()方法
①.首先是停止录音和播放,即MediaRecorder和MediaPlayer的停止以及资源释放;
public void stop() {
stopRecording();
stopPlayback();
}
public void stopRecording() {
if (mRecorder == null)
return;
mRecorder.stop();
mRecorder.release();
mRecorder = null;
mSampleLength = (int)( (System.currentTimeMillis() - mSampleStart)/1000 );
setState(IDLE_STATE);
}
public void stopPlayback() {
if (mPlayer == null) // we were not in playback
return;
mPlayer.stop();
mPlayer.release();
mPlayer = null;
setState(IDLE_STATE);
}
②判断是否有录音文件,如果没有的话,创建一个文件来保存录音
if (mSampleFile == null) {
File sampleDir = Environment.getExternalStorageDirectory();
if (!sampleDir.canWrite()) // Workaround for broken sdcard support on the device.
sampleDir = new File("/sdcard/sdcard");
try {
mSampleFile = File.createTempFile(SAMPLE_PREFIX, extension, sampleDir);
} catch (IOException e) {
setError(SDCARD_ACCESS_ERROR);
return;
}
}
③初始化MediaRecorder,即给MediaRecorder设置参数
mRecorder = new MediaRecorder();
//设置声音来源,一般传入 MediaRecorder. AudioSource.MIC参数指定录制来自麦克风的声音。
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置所录制的音视频文件的格式。
mRecorder.setOutputFormat(outputfileformat);
//设置所录制的声音的编码格式。
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
//设置录制的音频文件的保存位置。
mRecorder.setOutputFile(mSampleFile.getAbsolutePath());
④录音前的准备
try {
//准备录音
mRecorder.prepare();
} catch(IOException exception) {
setError(INTERNAL_ERROR);
mRecorder.reset();
mRecorder.release();
mRecorder = null;
return;
}
⑤开始录音
try {
//开始录音
mRecorder.start();
} catch (RuntimeException exception) {
AudioManager audioMngr = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
boolean isInCall = ((audioMngr.getMode() == AudioManager.MODE_IN_CALL) ||
(audioMngr.getMode() == AudioManager.MODE_IN_COMMUNICATION));
if (isInCall) {
setError(IN_CALL_RECORD_ERROR);
} else {
setError(INTERNAL_ERROR);
}
mRecorder.reset();
mRecorder.release();
mRecorder = null;
return;
}
mSampleStart = System.currentTimeMillis();
setState(RECORDING_STATE);
二.setState()方法用于Recorder与SoundRecorder交互
private void setState(int state) {
if (state == mState)
return;
mState = state;
signalStateChanged(mState);
}
private void signalStateChanged(int state) {
if (mOnStateChangedListener != null)
mOnStateChangedListener.onStateChanged(state);
}
这个mOnStateChangedListener就是SoundRecorder;
public void onStateChanged(int state) {
if (state == Recorder.PLAYING_STATE || state == Recorder.RECORDING_STATE) {
mSampleInterrupted = false;
mErrorUiMessage = null;
//录音时屏幕常亮
mWakeLock.acquire(); // we don't want to go to sleep while recording or playing
} else {
if (mWakeLock.isHeld())
mWakeLock.release();
}
updateUi();
}
通过updateUi()来改变视图
//更新录音的状态来更新UI
private void updateUi() {
Resources res = getResources();
switch (mRecorder.state()) {
case Recorder.IDLE_STATE:
if (mRecorder.sampleLength() == 0) {
mRecordButton.setEnabled(true);
mRecordButton.setFocusable(true);
mPlayButton.setEnabled(false);
mPlayButton.setFocusable(false);
mStopButton.setEnabled(false);
mStopButton.setFocusable(false);
mRecordButton.requestFocus();
mStateMessage1.setVisibility(View.INVISIBLE);
mStateLED.setVisibility(View.INVISIBLE);
mStateMessage2.setVisibility(View.INVISIBLE);
mExitButtons.setVisibility(View.INVISIBLE);
mVUMeter.setVisibility(View.VISIBLE);
mStateProgressBar.setVisibility(View.INVISIBLE);
setTitle(res.getString(R.string.record_your_message));
} else {
mRecordButton.setEnabled(true);
mRecordButton.setFocusable(true);
mPlayButton.setEnabled(true);
mPlayButton.setFocusable(true);
mStopButton.setEnabled(false);
mStopButton.setFocusable(false);
mStateMessage1.setVisibility(View.INVISIBLE);
mStateLED.setVisibility(View.INVISIBLE);
mStateMessage2.setVisibility(View.INVISIBLE);
mExitButtons.setVisibility(View.VISIBLE);
mVUMeter.setVisibility(View.INVISIBLE);
mStateProgressBar.setVisibility(View.INVISIBLE);
setTitle(res.getString(R.string.message_recorded));
}
if (mSampleInterrupted) {
mStateMessage2.setVisibility(View.VISIBLE);
mStateMessage2.setText(res.getString(R.string.recording_stopped));
mStateLED.setVisibility(View.INVISIBLE);
}
if (mErrorUiMessage != null) {
mStateMessage1.setText(mErrorUiMessage);
mStateMessage1.setVisibility(View.VISIBLE);
}
break;
case Recorder.RECORDING_STATE:
mRecordButton.setEnabled(false);
mRecordButton.setFocusable(false);
mPlayButton.setEnabled(false);
mPlayButton.setFocusable(false);
mStopButton.setEnabled(true);
mStopButton.setFocusable(true);
mStateMessage1.setVisibility(View.VISIBLE);
mStateLED.setVisibility(View.VISIBLE);
mStateLED.setImageResource(R.drawable.recording_led);
mStateMessage2.setVisibility(View.VISIBLE);
mStateMessage2.setText(res.getString(R.string.recording));
mExitButtons.setVisibility(View.INVISIBLE);
mVUMeter.setVisibility(View.VISIBLE);
mStateProgressBar.setVisibility(View.INVISIBLE);
setTitle(res.getString(R.string.record_your_message));
break;
case Recorder.PLAYING_STATE:
mRecordButton.setEnabled(true);
mRecordButton.setFocusable(true);
mPlayButton.setEnabled(false);
mPlayButton.setFocusable(false);
mStopButton.setEnabled(true);
mStopButton.setFocusable(true);
mStateMessage1.setVisibility(View.INVISIBLE);
mStateLED.setVisibility(View.INVISIBLE);
mStateMessage2.setVisibility(View.INVISIBLE);
mExitButtons.setVisibility(View.VISIBLE);
mVUMeter.setVisibility(View.INVISIBLE);
mStateProgressBar.setVisibility(View.VISIBLE);
setTitle(res.getString(R.string.review_message));
break;
}
//更新录音时间
updateTimerView();
//刷新录音音量界面
mVUMeter.invalidate();
}
三.录音时间的刷新
录音时间的刷新,调用的是updateTimerView()方法;
private void updateTimerView() {
Resources res = getResources();
int state = mRecorder.state();
boolean ongoing = state == Recorder.RECORDING_STATE || state == Recorder.PLAYING_STATE;
long time = ongoing ? mRecorder.progress() : mRecorder.sampleLength();
String timeStr = String.format(mTimerFormat, time/60, time%60);
mTimerView.setText(timeStr);
if (state == Recorder.PLAYING_STATE) {
mStateProgressBar.setProgress((int)(100*time/mRecorder.sampleLength()));
} else if (state == Recorder.RECORDING_STATE) {
updateTimeRemaining();
}
if (ongoing)
//通过递归的方法更新view
mHandler.postDelayed(mUpdateTimer, 1000);
}
其中time根据Recorder中的录音时间来计算的:
public int progress() {
if (mState == RECORDING_STATE || mState == PLAYING_STATE)
return (int) ((System.currentTimeMillis() - mSampleStart)/1000);
return 0;
}
然后将这个time进行格式化解析,再然后设置给mTimerView显示;
如果是正在录音的话,还需要更新剩余的录音时间:
private void updateTimeRemaining() {
long t = mRemainingTimeCalculator.timeRemaining();
if (t <= 0) {
mSampleInterrupted = true;
int limit = mRemainingTimeCalculator.currentLowerLimit();
switch (limit) {
case RemainingTimeCalculator.DISK_SPACE_LIMIT:
mErrorUiMessage
= getResources().getString(R.string.storage_is_full);
break;
case RemainingTimeCalculator.FILE_SIZE_LIMIT:
mErrorUiMessage
= getResources().getString(R.string.max_length_reached);
break;
default:
mErrorUiMessage = null;
break;
}
mRecorder.stop();
return;
}
Resources res = getResources();
String timeStr = "";
if (t < 60)
timeStr = String.format(res.getString(R.string.sec_available), t);
else if (t < 540)
timeStr = String.format(res.getString(R.string.min_available), t/60 + 1);
mStateMessage1.setText(timeStr);
}
当剩余时间t<0时,停止录音;
否则的话,以相应的格式来更新自己;
通过递归的方法更新录音时间和剩余时间,当非录音时或者非播放时停止;
二.点击停止录音时:
mRecorder.stop();
停止录音和播放,并且释放资源,并设置状态为IDLE_STATE;此时会根据录音时长mSampleLength来判断是否需要弹出保存录音的view;
此时如果点击保存,会保存录音;点击删除会删除录音;
三.保存录音
private void saveSample() {
if (mRecorder.sampleLength() == 0)
return;
Uri uri = null;
try {
uri = this.addToMediaDB(mRecorder.sampleFile());
} catch(UnsupportedOperationException ex) { // Database manipulation failure
return;
}
if (uri == null) {
return;
}
setResult(RESULT_OK, new Intent().setData(uri)
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION));
}
将录音文件添加进数据库,返回一个Uri给activity;
//将录音文件添加到数据库
private Uri addToMediaDB(File file) {
Resources res = getResources();
ContentValues cv = new ContentValues();
long current = System.currentTimeMillis();
long modDate = file.lastModified();
Date date = new Date(current);
SimpleDateFormat formatter = new SimpleDateFormat(
res.getString(R.string.audio_db_title_format));
String title = formatter.format(date);
long sampleLengthMillis = mRecorder.sampleLength() * 1000L;
// Lets label the recorded audio file as NON-MUSIC so that the file
// won't be displayed automatically, except for in the playlist.
cv.put(MediaStore.Audio.Media.IS_MUSIC, "0");
cv.put(MediaStore.Audio.Media.TITLE, title);
cv.put(MediaStore.Audio.Media.DATA, file.getAbsolutePath());
cv.put(MediaStore.Audio.Media.DATE_ADDED, (int) (current / 1000));
cv.put(MediaStore.Audio.Media.DATE_MODIFIED, (int) (modDate / 1000));
cv.put(MediaStore.Audio.Media.DURATION, sampleLengthMillis);
cv.put(MediaStore.Audio.Media.MIME_TYPE, mRequestedType);
cv.put(MediaStore.Audio.Media.ARTIST,
res.getString(R.string.audio_db_artist_name));
cv.put(MediaStore.Audio.Media.ALBUM,
res.getString(R.string.audio_db_album_name));
Log.d(TAG, "Inserting audio record: " + cv.toString());
ContentResolver resolver = getContentResolver();
Uri base = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Log.d(TAG, "ContentURI: " + base);
Uri result = resolver.insert(base, cv);
if (result == null) {
new AlertDialog.Builder(this)
.setTitle(R.string.app_name)
.setMessage(R.string.error_mediadb_new_record)
.setPositiveButton(R.string.button_ok, null)
.setCancelable(false)
.show();
return null;
}
if (getPlaylistId(res) == -1) {
createPlaylist(res, resolver);
}
int audioId = Integer.valueOf(result.getLastPathSegment());
addToPlaylist(resolver, audioId, getPlaylistId(res));
// Notify those applications such as Music listening to the
// scanner events that a recorded audio file just created.
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
return result;
}
如果添加失败,会弹出对话框;
添加成功后,将该录音添加至播放列表,并且发送广播通知去扫描;