android 分贝计设计开发

2,479 阅读4分钟

基础实现方法以及图片素材来自大黑博客:http://blog.csdn.net/halibobo1998/article/details/51594655,博主基于此做了一些小小的修改和原理的推导~

博主是通信工程学院的学生,不过很不幸对通信一点都不感冒-。-!,从大一开始就走上了编程这条不归路,前几天实验考试被老师质疑是不是通信工程学院的学生咋BPSK都不知道,甚伤我心,为了证明我还是知道一点专业知识的,正好这两天预习光纤通信涉及dB(分贝),不是数据库啊!,所以就写一个Android上的分贝计,当然,在进行开发之前dB的背景知识以及相关公式必须得了解~,我也查了很多资料,下面是小米所介绍的一些背景知识!

度量声音强度,大家最熟悉的单位就是分贝(decibel,缩写为dB)。这是一个无纲量的相对单位,计算公式如下:

L_p=20 \log_{10}\left(\frac{p_{\mathrm{rms}}}{p_{\mathrm{ref}}}\right)\mbox{ dB}

分子是测量值的声压,分母是参考值的声压(20微帕,人类所能听到的最小声压)。因此日常中说道声音强度是多少多少分贝时,都是默认了一个很小的参考值的。

Android设备传感器可以提供的物理量是场的幅值(amplitude),常用下列公式计算分贝值:

L_\mathrm{dB} = 10 \log_{10} \bigg(\frac{A_1^2}{A_0^2}\bigg) = 20 \log_{10} \bigg(\frac{A_1}{A_0}\bigg). \,

从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