zoukankan      html  css  js  c++  java
  • 通过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;
        }
    }
  • 相关阅读:
    LintCode Update Bits
    LintCode Flip Bits
    LintCode Wood Cut
    LintCode Sqrt(x)
    LintCode Binary Search
    LintCode Subarray Sum Closest
    Huffman编码(Huffman树)
    DFS应用——查找强分支
    DFS应用——遍历有向图+判断有向图是否有圈
    DFS应用——找出无向图的割点
  • 原文地址:https://www.cnblogs.com/tt2015-sz/p/5616534.html
Copyright © 2011-2022 走看看