Android练手——分贝计

基础实现方法以及图片素材来自大黑博客: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()这个方法来获取最大的振幅,因此本项目的问题也就迎刃而解了,下面看具体的代码实现


扫描二维码关注公众号,回复: 1495655 查看本文章

Android源码:

manifest.xml文件
两个执行访问权限别忘记添加:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission 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



猜你喜欢

转载自blog.csdn.net/qq_17475155/article/details/51594680
今日推荐