通过Android录音进行简单音频分析

Posted 小妖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过Android录音进行简单音频分析相关的知识,希望对你有一定的参考价值。

android录音有MediaRecorder和AudioRecord两种方式,前者使用方便,可以直接生成录音文件,但是录音格式为aac和amr等等,都经过压缩处理,不方便进行音频分析。

而用AudioRecord可以得到PCM编码的原音频数据,可以用FFT对数据进行处理,简单分析声音的频率。

1.AndroidRecord录音

    private static final String FILE_NAME = "MainMicRecord";
    private static final int SAMPLE_RATE = 44100;//Hz,采样频率
    private static final double FREQUENCY = 500; //Hz,标准频率(这里分析的是500Hz)
    private static final double RESOLUTION = 10; //Hz,误差
    private static final long RECORD_TIME = 2000;
    private File mSampleFile;
    private int bufferSize=0;
    private AudioRecord mAudioRecord;

    private void startRecord() {
        try {
            mSampleFile = new File(getFilesDir()+"/"+FILE_NAME);
            if(mSampleFile.exists()){
                if(!mSampleFile.delete()){
                    return;
                }
            }
            if(!mSampleFile.createNewFile()){
                return;
            }
        } catch(IOException e) {
            return;
        }
        //为了方便,这里只录制单声道
       //如果是双声道,得到的数据是一左一右,注意数据的保存和处理
        bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE,
                AudioFormat.CHANNEL_IN_MONO,
                AudioFormat.ENCODING_PCM_16BIT);
        mAudioRecord = new AudioRecord(MediaRecorder.Audiosource.MIC,SAMPLE_RATE,
                AudioFormat.CHANNEL_IN_MONO,
                AudioFormat.ENCODING_PCM_16BIT,
                bufferSize);
        mAudioRecord.startRecording();
        new Thread(new AudioRecordThread()).start();
    }

    private class  AudioRecordThread implements Runnable{
        @Override
        public void run() {
            //将录音数据写入文件
            short[] audiodata = new short[bufferSize/2];
            DataOutputStream fos = null;
            try {
                fos = new DataOutputStream( new FileOutputStream(mSampleFile));
                int readSize;
                while (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING){
                    readSize = mAudioRecord.read(audiodata,0,audiodata.length);
                    if(AudioRecord.ERROR_INVALID_OPERATION != readSize){
                        for(int i = 0;i<readSize;i++){
                            fos.writeShort(audiodata[i]);
                            fos.flush();
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if(fos!=null){
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                //在这里release
                mAudioRecord.release();
                mAudioRecord = null;
            }
        }
    };

    //在这里stop的时候先不要release
    private void stopRecording() {
        mAudioRecord.stop();
    }

    //对录音文件进行分析
    private void frequencyAnalyse(){
        if(mSampleFile == null){return;
        }
        try {
            DataInputStream inputStream = new DataInputStream(new FileInputStream(mSampleFile));
            //16bit采样,因此用short[]
            //如果是8bit采样,这里直接用byte[]
            //从文件中读出一段数据,这里长度是SAMPLE_RATE,也就是1s采样的数据
            short[] buffer=new short[SAMPLE_RATE];
            for(int i = 0;i<buffer.length;i++){
                buffer[i] = inputStream.readShort();
            }
            short[] data = new short[FFT.FFT_N];

            //为了数据稳定,在这里FFT分析只取最后的FFT_N个数据
            System.arraycopy(buffer, buffer.length - FFT.FFT_N,
                    data, 0, FFT.FFT_N);

            //FFT分析得到频率
            double frequence = FFT.GetFrequency(data);
            if(Math.abs(frequence - FREQUENCY)<RESOLUTION){
                //测试通过
            }else{
                //测试失败
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

 

2.FFT实现

参考:http://introcs.cs.princeton.edu/java/97data/FFT.java.html

(1)复数类

 1 public class Complex {
 2 
 3     public double real, imag;
 4 
 5     public Complex(double real,double im){
 6         this.real = real;
 7         this.imag = im;
 8     }
 9 
10     public Complex(){
11         this(0,0);
12     }
13 
14     public Complex(Complex c){
15         this(c.real,c.imag);
16     }
17 
18     @Override
19     public String toString() {
20         return "("+this.real+"+"+this.imag +"i)";
21     }
22 
23     //加法
24     public final Complex add(Complex c){
25         return new Complex(this.real+c.real,this.imag +c.imag);
26     }
27 
28     //减法
29     public final Complex minus(Complex c){
30         return new Complex(this.real-c.real,this.imag -c.imag);
31     }
32 
33     //求模值
34     public final double getMod(){
35         return Math.sqrt(this.real * this.real+this.imag * this.imag);
36     }
37 
38     //乘法
39     public final Complex multiply(Complex c){
40         return new Complex(
41                 this.real*c.real - this.imag *c.imag,
42                 this.real*c.imag + this.imag *c.real);
43     }
44 }
View Code

(2)FFT求最大频率

public class FFT {
    public static final int FFT_N = 4096;
    public static final int SAMPLE_RATE = 44100; //HZ

    //快速傅里叶变换
    public static Complex[] getFFT(Complex[] data){
        int N = data.length;
        if(N==1){
            return new Complex[]{data[0]};
        }
        if(N%2 != 0){
            throw new RuntimeException("N is not a power of 2");
        }

        //fft of even/odd terms
        Complex[] even = new Complex[N/2];
        Complex[] odd = new Complex[N/2];
        for(int k = 0;k<N/2;k++){
            even[k] = data[2*k];
            odd[k] = data[2*k+1];
        }
        Complex[] q=  getFFT(even);
        Complex[] r = getFFT(odd);

        Complex[] y = new Complex[N];
        for (int k = 0;k<N/2;k++){
            double kth = -2*k*Math.PI/N;
            Complex wk = new Complex(Math.cos(kth), Math.sin(kth));
            y[k] = q[k].add(wk.multiply(r[k]));
            y[k+N/2] = q[k].minus(wk.multiply(r[k]));
        }

        return y;
    }

    //================================================================
    public static double GetFrequency(short[] data){
        Log.i("FFT","GetFrequency");
        if(data.length<FFT_N){
            throw new RuntimeException("Data length lower than "+FFT_N);
        }
        Complex[]  f = new Complex[FFT_N];
        for(int i=0;i<FFT_N;i++){
            f[i] = new Complex(data[i],0); //实部为正弦波FFT_N点采样,赋值为1
                                                //虚部为0
        }

        f = getFFT(f);                                        //进行快速福利叶变换
//        String str = "";
//        for(int i = 0;i<FFT_N;i++){
//            str+=f[i].toString()+" ";
//        }
//        Log.i("FFT","fft: "+str);
        double[]  s = new double[FFT_N/2];
//        str = "";
        for(int i=0;i<FFT_N/2;i++){
            s[i] = f[i].getMod();
//            str += ""+s[i]+" ";
        }
//        Log.i("FFT","s: "+str);

        int fmax=0;
        for(int i=1;i<FFT_N/2;i++){  //利用FFT的对称性,只取前一半进行处理
            if(s[i]>s[fmax])
                fmax=i;                          //计算最大频率的序号值
        }
//        Log.i("FFT","max index:"+fmax+" fft:"+f[fmax]+" s:"+s[fmax]);
        double fre = fmax*(double)SAMPLE_RATE / FFT_N;
        Log.i("FFT","fre:"+fre);
        return fre;
    }
}

 

以上是关于通过Android录音进行简单音频分析的主要内容,如果未能解决你的问题,请参考以下文章

Android 音频源码分析——AudioFlinger

Android 音频源码分析——AudioFlinger

Android 音频源码分析——AudioTrack设备选择

Android 音频源码分析——AudioTrack设备选择

Android 音频源码分析——audioserver启动

需要一个简单的录音示例