基础实现方法以及图片素材来自大黑博客:http://blog.csdn.net/halibobo1998/article/details/51594655,博主基于此做了一些小小的修改和原理的推导~
博主是通信工程学院的学生,不过很不幸对通信一点都不感冒-。-!,从大一开始就走上了编程这条不归路,前几天实验考试被老师质疑是不是通信工程学院的学生咋BPSK都不知道,甚伤我心,为了证明我还是知道一点专业知识的,正好这两天预习光纤通信涉及dB(分贝),不是数据库啊!,所以就写一个Android上的分贝计,当然,在进行开发之前dB的背景知识以及相关公式必须得了解~,我也查了很多资料,下面是小米所介绍的一些背景知识!
度量声音强度,大家最熟悉的单位就是分贝(decibel,缩写为dB)。这是一个无纲量的相对单位,计算公式如下:
分子是测量值的声压,分母是参考值的声压(20微帕,人类所能听到的最小声压)。因此日常中说道声音强度是多少多少分贝时,都是默认了一个很小的参考值的。
而Android设备传感器可以提供的物理量是场的幅值(amplitude),常用下列公式计算分贝值:
从SDK中读取了某段音频数据的振幅后,取最大振幅或平均振幅(可以用平方和平均,或绝对值的和平均),代入上述公式的A1。
具体实现的原理:通过麦克风进行录音,然后通过对一段时间内的录音源文件的幅度进行分析判断,其中Android的SDK直接给我们提供了getMaxAmplitude()这个方法来获取最大的振幅,因此本项目的问题也就迎刃而解了,下面看具体的代码实现
manifest.xml文件
两个执行访问权限别忘记添加:
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
android:name="android.permission.RECORD_AUDIO"/>
FileUtil.java文件,这里主要是文件的创建、删除、路径查询等方法,使用的是第三方写好的代码,直接贴上来了:
public class FileUtil {
private static final String TAG = "FileUtil";
public static final String LOCAL = "Test";
public static final String LOCAL_PATH = Environment.getExternalStorageDirectory().getPath() + File.separator;
/**
* 录音文件目录
*/
public static final String REC_PATH = LOCAL_PATH + LOCAL + File.separator;
/**
* 自动在SD卡创建相关的目录
*/
static {
File dirRootFile = new File(LOCAL_PATH);
if (!dirRootFile.exists()) {
dirRootFile.mkdirs();
}
File recFile = new File(REC_PATH);
if (!recFile.exists()) {
recFile.mkdirs();
}
}
/**
* 判断是否存在存储空间 *
*
* @return
*/
public static boolean isExitSDCard() {
return Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED);
}
private static boolean hasFile(String fileName) {
File f = createFile(fileName);
return null != f && f.exists();
}
public static File createFile(String fileName) {
File myCaptureFile = new File(REC_PATH + fileName);
if (myCaptureFile.exists()) {
myCaptureFile.delete();
}
try {
myCaptureFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
return myCaptureFile;
}
}
Calculator.java文件,用于记录最新的dB值,便于图形的计算绘制。
public class Calculator {
public static float dbstart = 0; //初始值记录
public static float dblast = dbstart; //最新值
public static void setDbCount(float dbValue) {
dbstart = dblast + (dbValue - dblast) * 0.2f; //最新值赋予以及保留两位小数
dblast = dbstart;
}
}
View.java文件,分贝计的绘制,参考小黑的绘制方法。
public class View extends ImageView{
private float scaleWidth, scaleHeight;
private int newWidth, newHeight;
private Matrix mMatrix = new Matrix();
private Bitmap indicatorBitmap;
private Paint paint = new Paint();
static final long ANIMATION_INTERVAL = 100;
public View(Context context) {
super(context);
}
public View(Context context, AttributeSet attrs) {
super(context, attrs);
}
private void init() {
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.noise_index);
int bitmapWidth = myBitmap.getWidth();
int bitmapHeight = myBitmap.getHeight();
newWidth = getWidth();
newHeight = getHeight();
scaleWidth = ((float) newWidth) /(float) bitmapWidth; // 获取缩放比例
scaleHeight = ((float) newHeight) /(float) bitmapHeight; //获取缩放比例
mMatrix.postScale(scaleWidth, scaleHeight); //设置mMatrix的缩放比例
indicatorBitmap = Bitmap.createBitmap(myBitmap, 0, 0, bitmapWidth, bitmapHeight, mMatrix,true); //获取同等和背景宽高的指针图的bitmap
paint = new Paint();
paint.setTextSize(55);
paint.setAntiAlias(true);
paint.setTextAlign(Paint.Align.CENTER); //抗锯齿
paint.setColor(Color.WHITE);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (indicatorBitmap == null) {
init();
}
float currentAngle = getAngle(Calculator.dbstart);
mMatrix.setRotate(currentAngle, newWidth / 2, newHeight * 215 / 460); //片相对位置
canvas.drawBitmap(indicatorBitmap, mMatrix, paint);
postInvalidateDelayed(ANIMATION_INTERVAL);
canvas.drawText((int)Calculator.dbstart +" DB", newWidth/2,newHeight*36/46, paint); //图片相对位置
}
private float getAngle(float db){
return(db-85)*5/3; }
}
接下来就是MainActivity.java文件的编写了
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final int START_RECORD = 0;
private Button mBtnStart, mBtnStop;
private File mFileRec;
private Thread mThread;
private MediaRecorder mMediaRecorder;
private MyHandle mHandle;
public boolean isRecording = false;
private boolean isListener = false;
private boolean isThreading = true;
private float volume;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandle = new MyHandle();
initView();
}
private void initView() {
mBtnStart = (Button) findViewById(R.id.btn_start);
mBtnStop = (Button) findViewById(R.id.btn_stop);
mBtnStart.setOnClickListener(this);
mBtnStop.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_start:
isListener = true;
isRecording = true;
isThreading = true;
Message msg = new Message();
msg.what = START_RECORD;
mHandle.sendMessage(msg);
break;
case R.id.btn_stop:
isListener = false;
isThreading = false;
isRecording = false;
mMediaRecorder.reset();
mFileRec.delete();
Calculator.dbstart = 0;
break;
}
}
private void beginstart() {
isListener = true;
mFileRec = FileUtil.createFile("test.amr");
}
private void startRecord(File mFileRec) {
try {
mMediaRecorder = new MediaRecorder();
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setOutputFile(mFileRec.getAbsolutePath());
mMediaRecorder.prepare();
mMediaRecorder.start();
isRecording = true;
} catch (IOException e) {
e.printStackTrace();
}
mThread = new Thread(new Runnable() {
@Override
public void run() {
while (isThreading) {
try {
if (isListener) {
volume = mMediaRecorder.getMaxAmplitude();
if (volume > 0 && volume < 1000000) {
Calculator.setDbCount(20 * (float) (Math.log10(volume)));
}
}
Log.v("activity", "db = " + Calculator.dbstart);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
isListener = false;
}
}
}
});
mThread.start();
}
class MyHandle extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case START_RECORD:
beginstart();
if (mFileRec != null) {
startRecord(mFileRec);
} else {
Toast.makeText(getApplicationContext(), "创建文件失败", Toast.LENGTH_LONG).show();
}
break;
}
super.handleMessage(msg);
}
}
}
运行后的效果图:
源码:https://github.com/weizainiunai/dBCalculator