您的位置:首页 > 移动开发 > Android开发

Android录音时指针摆动的实现

2012-11-06 19:56 369 查看
本文中实现录音时候、指针摆动的功能主要是参考SoundRecorder的。主要是其中的VUMeter类,VUMeter是通过Recorder.getMaxAmplitude()的值计算,画出指针的偏移摆动。

下面直接上代码:

VUMeter类:

package hfut.geron.record;

import hfut.geron.memorybook.R;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;

public class VUMeter extends View {
static final float PIVOT_RADIUS = 3.5f;
static final float PIVOT_Y_OFFSET = 10f;
static final float SHADOW_OFFSET = 2.0f;
static final float DROPOFF_STEP = 0.18f;
static final float SURGE_STEP = 0.35f;
static final long ANIMATION_INTERVAL = 70;

Paint mPaint, mShadow;
float mCurrentAngle;

RecordHelper mRecorder;

public VUMeter(Context context) {
super(context);
init(context);
}

public VUMeter(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}

void init(Context context) {
Drawable background = context.getResources().getDrawable(R.drawable.record_bg2);
setBackgroundDrawable(background);

mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.WHITE);
mShadow = new Paint(Paint.ANTI_ALIAS_FLAG);
mShadow.setColor(Color.argb(60, 0, 0, 0));

mRecorder = null;

mCurrentAngle = 0;
}

public void setRecorder(RecordHelper recorder) {
mRecorder = recorder;
invalidate();
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

final float minAngle = (float)Math.PI/8;
final float maxAngle = (float)Math.PI*7/8;

float angle = minAngle;
if (mRecorder != null)
angle += (float)(maxAngle - minAngle)*mRecorder.getMaxAmplitude()/32768;

if (angle > mCurrentAngle)
mCurrentAngle = angle;
else
mCurrentAngle = Math.max(angle, mCurrentAngle - DROPOFF_STEP);

mCurrentAngle = Math.min(maxAngle, mCurrentAngle);

float w = getWidth();
float h = getHeight();
float pivotX = w/2;
float pivotY = h - PIVOT_RADIUS - PIVOT_Y_OFFSET;
float l = h*4/5;
float sin = (float) Math.sin(mCurrentAngle);
float cos = (float) Math.cos(mCurrentAngle);
float x0 = pivotX - l*cos;
float y0 = pivotY - l*sin;
canvas.drawLine(x0 + SHADOW_OFFSET, y0 + SHADOW_OFFSET, pivotX + SHADOW_OFFSET, pivotY + SHADOW_OFFSET, mShadow);
canvas.drawCircle(pivotX + SHADOW_OFFSET, pivotY + SHADOW_OFFSET, PIVOT_RADIUS, mShadow);
canvas.drawLine(x0, y0, pivotX, pivotY, mPaint);
canvas.drawCircle(pivotX, pivotY, PIVOT_RADIUS, mPaint);

if (mRecorder != null)
postInvalidateDelayed(ANIMATION_INTERVAL);
}
}

录音类:RecordHelper.java
package hfut.geron.record;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import android.content.Context;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaRecorder;
import android.util.Log;

public class RecordHelper implements OnCompletionListener, OnErrorListener{
private MediaRecorder mRecorder ;
private String record_path;
public static final int IDLE_STATE = 0; //空闲状态
public static final int RECORDING_STATE = 1; //录音状态

int mState = IDLE_STATE; //状态标示,默认是空闲状态

public static final int NO_ERROR = 0;
public static final int SDCARD_ACCESS_ERROR = 1;
public static final int INTERNAL_ERROR = 2;

public interface OnStateChangedListener {
public void onStateChanged(int state);
public void onError(int error);
}
OnStateChangedListener mOnStateChangedListener = null;
public void setOnStateChangedListener(OnStateChangedListener listener) {
mOnStateChangedListener = listener;
}

public void startRecording(int outputfileformat, String suffix, Context context) {
Log.d("Infor", "start...");
stopRecording();

if(CommonUtils.isSdCardAvailable()){
File audioRecPath = new File(CommonUtils.RECORD_PATH);
//判断存储音频的文件夹是否存在,如果不存在则进行创建操作
if(!audioRecPath.exists()){
audioRecPath.mkdir();

}

if(audioRecPath != null ){
//创建临时文件,以Record_开头
SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyyMMdd_hhmmss");
String record_name = "Record_"+sDateFormat.format(new java.util.Date());
File audioRecFile;
try {
audioRecFile = File.createTempFile(record_name, suffix, audioRecPath);

} catch (IOException e) {
// TODO Auto-generated catch block
setError(SDCARD_ACCESS_ERROR);
return;
}

mRecorder= new MediaRecorder();
//设置采样的音频源为麦克风
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置输出文件格式
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
//设置音频的编码方式
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
//设置输出文件
Log.d("Infor", audioRecFile.getAbsolutePath());
record_path= audioRecFile.getAbsolutePath();
mRecorder.setOutputFile(audioRecFile.getAbsolutePath());

try{
mRecorder.prepare();
}catch(IOException e){
setError(INTERNAL_ERROR);
mRecorder.reset();
mRecorder.release();
mRecorder = null;
return;
}
//开始录音
if(this.mState==RecordHelper.IDLE_STATE){
mRecorder.start();
setState(RECORDING_STATE);
}

}
}

else{
Log.d("Infor", "error,内存卡打不开。。。");
setError(SDCARD_ACCESS_ERROR);
}

}

public void stopRecording() {
if (mRecorder == null)
return;

mRecorder.stop();
mRecorder.release();
mRecorder = null;

setState(IDLE_STATE);
}

void setState(int state){//设置状态
if (state == mState)
return;
mState = state;
signalStateChanged(mState);
}

public int getMaxAmplitude() {//得到录音的振幅
if (mState != RECORDING_STATE)
return 0;
return mRecorder.getMaxAmplitude();
}

//实现对自定义接口OnStateChangedListener的两个方法调用的条件
private void signalStateChanged(int state) {
if (mOnStateChangedListener != null)
mOnStateChangedListener.onStateChanged(state);
}

private void setError(int error) {
if (mOnStateChangedListener != null)
mOnStateChangedListener.onError(error);
}

public String getPath(){
return record_path;
}

/*继承接口方法*/

public boolean onError(MediaPlayer mp, int what, int extra) {
// TODO Auto-generated method stub

return true;
}
public void onCompletion(MediaPlayer mp) {
// TODO Auto-generated method stub

}

}


RecordActivity:
package hfut.geron.memorybook.onebook;

import java.io.File;

import hfut.geron.memorybook.R;
import hfut.geron.record.CommonUtils;
import hfut.geron.record.RecordHelper;
import hfut.geron.record.RecordHelper.OnStateChangedListener;
import hfut.geron.record.VUMeter;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;

public class RecordActivity extends Activity implements OnClickListener, OnStateChangedListener {
private TextView record_text_time;
private ImageButton record_btn_begin,record_btn_end;
private VUMeter mVUMeter;//VUMeter类是通过mRecorder.getMaxAmplitude()的值计算,画出指针的偏移摆动
WakeLock mWakeLock;//用WakeLock请求休眠锁,让其不会休眠
private String time;
private RecordHelper mRecordHelper;//录音类,实现录音功能

private int s1=0;
private int s2=0;
private int m1=0;
private int m2=0;
private final static int RECORD_ISOVER = 1;
private final static int SDCARD_ACCESS_ERROR=2;

private final static int NOTES_RECORD=1;
private Handler time_handler= new Handler();
private Runnable runnable = new Runnable(){

public void run() {
// TODO Auto-generated method stub

if(s1==10){
s2++;
s1=0;
}
if(s2==6){
s2=0;
m1++;
}
if(m1==10){
m1=0;
m2++;
}

time=""+m2+m1+":"+s2+s1;
record_text_time = (TextView) findViewById(R.id.record_text_time);
record_text_time.setText(time);
s1++;
time_handler.postDelayed(runnable, 1000);

}

};

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
this.setContentView(R.layout.recordialog);

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK,
"SoundRecorder");//请求休眠锁

mRecordHelper = new RecordHelper();
mRecordHelper.setOnStateChangedListener(this);

mVUMeter = (VUMeter)this.findViewById(R.id.record_VUMeter);
mVUMeter.setRecorder(mRecordHelper);

record_text_time = (TextView)this.findViewById(R.id.record_text_time);
record_btn_begin = (ImageButton)this.findViewById(R.id.record_btn_begin);

record_btn_end= (ImageButton)this.findViewById(R.id.record_btn_end);
record_btn_begin.setOnClickListener(this);
record_btn_end.setOnClickListener(this);
record_btn_begin.setId(1);
record_btn_end.setId(2);

}
public void onClick(View v) {
// TODO Auto-generated method stub
int id=v.getId();
switch(id){
case 1: //录音开始
time_handler.post(runnable);
Log.d("Infor", "录音开始...");

mRecordHelper.startRecording(MediaRecorder.OutputFormat.DEFAULT,
".amr", this);
record_btn_end.setClickable(true);
record_btn_begin.setClickable(false);
break;
case 2: //录音结束
record_btn_begin.setClickable(true);
time_handler.removeCallbacks(runnable);
s1=s2=m1=m2=0;
Log.d("Infor", "录音关闭...");
mRecordHelper.stopRecording();
showDialog(RECORD_ISOVER);
break;
}
}

public void onStateChanged(int state) {
// TODO Auto-generated method stub
Log.d("Infor", "录音状态发生改变");
if (state == RecordHelper.RECORDING_STATE) {
mWakeLock.acquire(); // 录音的时候让其不休眠

} else {
if (mWakeLock.isHeld())
mWakeLock.release();
}

}
public void onError(int error) {
// TODO Auto-generated method stub
Log.d("Infor", "error:"+error);
this.showDialog(error);
}
@Override
protected Dialog onCreateDialog(int id) {
// TODO Auto-generated method stub

switch(id){
case RECORD_ISOVER:
Log.d("Infor", "here");
return CommonUtils.getDialog(this, "提示", "确定要保存该录音音频文件吗?", 0,
"是", "否", new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
Log.d("Infor", "保存该录音音频");
Toast.makeText(RecordActivity.this, "录音文件保存成功...", Toast.LENGTH_SHORT).show();

Intent intent = new Intent(RecordActivity.this,NoteActivity.class);
intent.putExtra("recordpath", mRecordHelper.getPath());
RecordActivity.this.setResult(NOTES_RECORD, intent);
RecordActivity.this.finish();
}
}, new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub

//删除之前的录音文件
Log.d("Infor", "取消保存该录音音频");
record_text_time.setText("00:00");
Log.d("Infor", "PATH:"+mRecordHelper.getPath());
File file = new File(mRecordHelper.getPath());
if(file.exists()){
Log.d("Infor", "存在");
try{
file.delete();
}catch(Exception e){

Log.d("Infor", "删除不了");
}
}
}
});

case SDCARD_ACCESS_ERROR:

return CommonUtils.getDialog(this, "提示", "无法读取内存卡..", 0, "是", null, new DialogInterface.OnClickListener(){

public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
RecordActivity.this.finish();
}

}, null);

default:
return null;
}
}
}


结果截图:



要点:

1、该程序中使用了用WakeLock请求休眠锁,让其不会休眠。这样能保证录音过程中屏幕常亮。

2、该程序中主要使用VUMeter,自定义该类,VUMeter类是通过mRecorder.getMaxAmplitude()的值计算,画出指针的偏移摆动。

3、RecordHelper类中定义了一个接口OnStateChangedListener ,定义如下

public interface OnStateChangedListener {

         public void onStateChanged(int state);

         public void onError(int error);

     }

并在RecordHelper类中实例化该接口并set,如下:

     OnStateChangedListener mOnStateChangedListener = null;

         public void setOnStateChangedListener(OnStateChangedListener listener) {

               mOnStateChangedListener = listener;

     }

定义在何种情况下使用该接口中的两个方法:

 //实现对自定义接口OnStateChangedListener的两个方法调用的条件
   private void signalStateChanged(int state) {
       if (mOnStateChangedListener != null)
           mOnStateChangedListener.onStateChanged(state);
   }
   
   private void setError(int error) {
       if (mOnStateChangedListener != null)
           mOnStateChangedListener.onError(error);
   }

在RecordActivity类中就可以继承该接口

 class RecordActivity extends Activity implements OnStateChangedListener{}



mRecordHelper = new RecordHelper();

mRecordHelper.setOnStateChangedListener(this);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: