通過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;
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM