通过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 }
(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 音频源码分析——AudioTrack设备选择