androidベースのリアルタイムオーディオスペクトル計


前の段の実习、もともとc++をするつもりで、会社に着いて何のプロジェクトもないことを発见して、そこでandroidを転行して、书いた最初のプログラムは意外にも私に信号を処理して、私达は一心にコンピュータをやって、信号のものに接触したことがなくて、何も接触したことがなくて、それでは、各种の友达を探して、各种の知人、今考えて、言语に専念するのは间违っています、言語はツールであり、肝心なのはビジネスであり、アルゴリズムである.はい、くだらないことはあまり言わないで、プログラムに行って、注釈はすべてとても詳しくて、理解できるはずです.
音声を分析するのは、実は簡単で、フーリエ変換を運用して、音声信号を時間領域から周波数領域に変換します(プログラムは高速フーリエ変換で、比較的簡単です)、どうしてこのようにして、メリットが多くて、詳しく言わないで、会社の用途は携帯電話の音声の信号がある周波数の集中範囲を検出するためです.
最初のクラスは、複素数の計算で、加減算を使って、簡単です.
package com.mobao360.sunshine;
//        
public class Complex {
	public double real;
	public double image;
	
	//      
	public Complex() {
		// TODO Auto-generated constructor stub
		this.real = 0;
		this.image = 0;
	}

	public Complex(double real, double image){
		this.real = real;
		this.image = image;
	}
	
	public Complex(int real, int image) {
		Integer integer = real;
		this.real = integer.floatValue();
		integer = image;
		this.image = integer.floatValue();
	}
	
	public Complex(double real) {
		this.real = real;
		this.image = 0;
	}
	//  
	public Complex cc(Complex complex) {
		Complex tmpComplex = new Complex();
		tmpComplex.real = this.real * complex.real - this.image * complex.image;
		tmpComplex.image = this.real * complex.image + this.image * complex.real;
		return tmpComplex;
	}
	//  
	public Complex sum(Complex complex) {
		Complex tmpComplex = new Complex();
		tmpComplex.real = this.real + complex.real;
		tmpComplex.image = this.image + complex.image;
		return tmpComplex;
	}
	//  
	public Complex cut(Complex complex) {
		Complex tmpComplex = new Complex();
		tmpComplex.real = this.real - complex.real;
		tmpComplex.image = this.image - complex.image;
		return tmpComplex;
	}
	//        
	public int getIntValue(){
		int ret = 0;
		ret = (int) Math.round(Math.sqrt(this.real*this.real - this.image*this.image));
		return ret;
	}
}

このクラスには3つの機能があり、第一に、データを収集します.第二に、高速フーリエ計算を行う.第三に、絵を描く.
収集データはAudioRecordクラスで、ネット上でこのクラスを説明するのはとても多くて、構造クラスの各パラメータを明らかにすればいいです.
図面はSurfaceView Paint Canvasの3つのクラスで、本人もネットの達人のコードを参考にしています.
package com.mobao360.sunshine;

import java.util.ArrayList;
import java.lang.Short;


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.Rect;
import android.media.AudioRecord;
import android.util.Log;
import android.view.SurfaceView;

public class AudioProcess {
	public static final float pi= (float) 3.1415926;
	//                  
	private ArrayList<short[]> inBuf = new ArrayList<short[]>();//      
	private ArrayList<int[]> outBuf = new ArrayList<int[]>();//      
	private boolean isRecording = false;

	Context mContext;
	private int shift = 30;
	public int frequence = 0;
	
	private int length = 256;
	//y      
	public int rateY = 21;
	//y   
	public int baseLine = 0;
	//          
	public void initDraw(int rateY, int baseLine,Context mContext, int frequence){
		this.mContext = mContext;
		this.rateY = rateY;
		this.baseLine = baseLine;
		this.frequence = frequence;
	}
	//    
	public void start(AudioRecord audioRecord, int minBufferSize, SurfaceView sfvSurfaceView) {
		isRecording = true;
		new RecordThread(audioRecord, minBufferSize).start();
		new DrawThread(sfvSurfaceView).start();
	}
	//    
	public void stop(SurfaceView sfvSurfaceView){
		isRecording = false;
		inBuf.clear();
	}
	
	//    
	class RecordThread extends Thread{
		private AudioRecord audioRecord;
		private int minBufferSize;
		
		public RecordThread(AudioRecord audioRecord,int minBufferSize){
			this.audioRecord = audioRecord;
			this.minBufferSize = minBufferSize;
		}
		
		public void run(){
			try{
				short[] buffer = new short[minBufferSize];
				audioRecord.startRecording();
				while(isRecording){
					int res = audioRecord.read(buffer, 0, minBufferSize);
					synchronized (inBuf){
						inBuf.add(buffer);
					}
					//     2    
					length=up2int(res);
					short[]tmpBuf = new short[length];
					System.arraycopy(buffer, 0, tmpBuf, 0, length);
					
					Complex[]complexs = new Complex[length];
					int[]outInt = new int[length];
					for(int i=0;i < length; i++){
						Short short1 = tmpBuf[i];
						complexs[i] = new Complex(short1.doubleValue());
					}
					fft(complexs,length);
					for (int i = 0; i < length; i++) {
						outInt[i] = complexs[i].getIntValue();
					}
					synchronized (outBuf) {
						outBuf.add(outInt);
					}
				}
				audioRecord.stop();
			}catch (Exception e) {
				// TODO: handle exception
				Log.i("Rec E",e.toString());
			}
			
		}
	}

	//    
	class DrawThread extends Thread{
		//  
		private SurfaceView sfvSurfaceView;
		//        x    
		//  
		private Paint mPaint;
		private Paint tPaint;
		private Paint dashPaint;
		public DrawThread(SurfaceView sfvSurfaceView) {
			this.sfvSurfaceView = sfvSurfaceView;
			//      
			mPaint = new Paint();
			mPaint.setColor(Color.BLUE);
			mPaint.setStrokeWidth(2);
			mPaint.setAntiAlias(true);
			
			tPaint = new Paint();
			tPaint.setColor(Color.YELLOW);
			tPaint.setStrokeWidth(1);
			tPaint.setAntiAlias(true);
			
			//   
			dashPaint = new Paint();
			dashPaint.setStyle(Paint.Style.STROKE);
			dashPaint.setColor(Color.GRAY);
			Path path = new Path();
	        path.moveTo(0, 10);
	        path.lineTo(480,10); 
	        PathEffect effects = new DashPathEffect(new float[]{5,5,5,5},1);
	        dashPaint.setPathEffect(effects);
		}
		
		@SuppressWarnings("unchecked")
		public void run() {
			while (isRecording) {
				ArrayList<int[]>buf = new ArrayList<int[]>();
				synchronized (outBuf) {
					if (outBuf.size() == 0) {
						continue;
					}
					buf = (ArrayList<int[]>)outBuf.clone();
					outBuf.clear();
				}
				//  ArrayList  short      
				for(int i = 0; i < buf.size(); i++){
					int[]tmpBuf = buf.get(i);
					SimpleDraw(tmpBuf, rateY, baseLine);
				}
				
			}
		}
		
		/** 
         *        
         *  
         * @param start 
         *            X       (  ) 
         * @param buffer 
         *                 
         * @param rate 
         *            Y          
         * @param baseLine 
         *            Y     
         */ 

		private void SimpleDraw(int[] buffer, int rate, int baseLine){
			Canvas canvas = sfvSurfaceView.getHolder().lockCanvas(
					new Rect(0, 0, buffer.length,sfvSurfaceView.getHeight()));
			canvas.drawColor(Color.BLACK);
			canvas.drawText("   ", 0, 3, 2, 15, tPaint);
			canvas.drawText("  (0,0)", 0, 7, 5, baseLine + 15, tPaint);
			canvas.drawText("  (HZ)", 0, 6, sfvSurfaceView.getWidth() - 50, baseLine + 30, tPaint);
			canvas.drawLine(shift, 20, shift, baseLine, tPaint);
			canvas.drawLine(shift, baseLine, sfvSurfaceView.getWidth(), baseLine, tPaint);
			canvas.save();
			canvas.rotate(30, shift, 20);
			canvas.drawLine(shift, 20, shift, 30, tPaint);
			canvas.rotate(-60, shift, 20);
			canvas.drawLine(shift, 20, shift, 30, tPaint);
			canvas.rotate(30, shift, 20);
			canvas.rotate(30, sfvSurfaceView.getWidth()-1, baseLine);
			canvas.drawLine(sfvSurfaceView.getWidth() - 1, baseLine, sfvSurfaceView.getWidth() - 11, baseLine, tPaint);
			canvas.rotate(-60, sfvSurfaceView.getWidth()-1, baseLine);
			canvas.drawLine(sfvSurfaceView.getWidth() - 1, baseLine, sfvSurfaceView.getWidth() - 11, baseLine, tPaint);
			canvas.restore();
			//tPaint.setStyle(Style.STROKE);
			for(int index = 64; index <= 512; index = index + 64){
				canvas.drawLine(shift + index, baseLine, shift + index, 40, dashPaint);
				String str = String.valueOf(frequence / 1024 * index);
				canvas.drawText( str, 0, str.length(), shift + index - 15, baseLine + 15, tPaint);
			}
			int y;
			for(int i = 0; i < buffer.length; i = i + 1){
				y = baseLine - buffer[i] / rateY ;
				canvas.drawLine(2*i + shift, baseLine, 2*i +shift, y, mPaint);
			}
			sfvSurfaceView.getHolder().unlockCanvasAndPost(canvas);
		}
	}
	
	/**
	 *       iint 2    .  iint=320 ,  256
	 * @param iint
	 * @return
	 */
	private int up2int(int iint) {
		int ret = 1;
		while (ret<=iint) {
			ret = ret << 1;
		}
		return ret>>1;
	}
	
	//       
	public void fft(Complex[] xin,int N)
	{
	    int f,m,N2,nm,i,k,j,L;//L:    
	    float p;
	    int e2,le,B,ip;
	    Complex w = new Complex();
	    Complex t = new Complex();
	    N2 = N / 2;//         ,     m              
	    f = N;//f             
	    for(m = 1; (f = f / 2) != 1; m++);                             //         
	    nm = N - 2;
	    j = N2;
	    /******    ——    ******/
	    for(i = 1; i <= nm; i++)
	    {
	        if(i < j)//      
	        {
	            t = xin[j];
	            xin[j] = xin[i];
	            xin[i] = t;
	        }
	        k = N2;
	        while(j >= k)
	        {
	            j = j - k;
	            k = k / 2;
	        }
	        j = j + k;
	    }
	    /******       ******/
	    for(L=1; L<=m; L++)                                    //  1   m 
	    {
	    	e2 = (int) Math.pow(2, L);
	        //e2=(int)2.pow(L);
	        le=e2+1;
	        B=e2/2;
	        for(j=0;j<B;j++)                                    //j 0 2^(L-1)-1
	        {
	            p=2*pi/e2;
	            w.real = Math.cos(p * j);
	            //w.real=Math.cos((double)p*j);                                   //  W
	            w.image = Math.sin(p*j) * -1;
	            //w.imag = -sin(p*j);
	            for(i=j;i<N;i=i+e2)                                //           
	            {
	                ip=i+B;                                           //          2^(L-1)
	                t=xin[ip].cc(w);
	                xin[ip] = xin[i].cut(t);
	                xin[i] = xin[i].sum(t);
	            }
	        }
	    }
	}
}

しゅプログラム
package com.mobao360.sunshine;

import java.util.ArrayList;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ZoomControls;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;

public class AudioMaker extends Activity {
    /** Called when the activity is first created. */
    static  int frequency = 8000;//     
    static final int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;  
    static final int audioEncodeing = AudioFormat.ENCODING_PCM_16BIT; 
    static final int yMax = 50;//Y          
    static final int yMin = 1;//Y          
	
	int minBufferSize;//            
	AudioRecord audioRecord;//  
	AudioProcess audioProcess = new AudioProcess();//  
	
    Button btnStart,btnExit;  //      
    SurfaceView sfv;  //    
    ZoomControls zctlX,zctlY;//     
    Spinner spinner;//    
    ArrayList<String> list=new ArrayList<String>();
    ArrayAdapter<String>adapter;//       
    TextView tView;
    
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        initControl();
        }
    @Override
    protected void onDestroy(){
    	super.onDestroy();
    	android.os.Process.killProcess(android.os.Process.myPid());
    }
    
  //       
    private void initControl() {
    	//     
        tView = (TextView)this.findViewById(R.id.tvSpinner);
        spinner = (Spinner)this.findViewById(R.id.spinnerFre);
        String []ls =getResources().getStringArray(R.array.action);
        for(int i=0;i<ls.length;i++){
        	list.add(ls[i]);
        }
        adapter=new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item,list);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner.setAdapter(adapter);
        spinner.setPrompt("      ");
        spinner.setOnItemSelectedListener(new Spinner.OnItemSelectedListener(){
        	@SuppressWarnings("unchecked")
        	public void onItemSelected(AdapterView arg0,View agr1,int arg2,long arg3){
        		frequency = Integer.parseInt(adapter.getItem(arg2));
        		tView.setText("     :"+adapter.getItem(arg2)+"HZ");
        		Log.i("sunshine",String.valueOf(minBufferSize));
        		arg0.setVisibility(View.VISIBLE);
        	}
        	@SuppressWarnings("unchecked")
        	public void onNothingSelected(AdapterView arg0){
        		arg0.setVisibility(View.VISIBLE);
        	}
        });
        
        
        Context mContext = getApplicationContext();
        //  
        btnStart = (Button)this.findViewById(R.id.btnStart);
        btnExit = (Button)this.findViewById(R.id.btnExit);
        //      
        btnStart.setOnClickListener(new ClickEvent());
        btnExit.setOnClickListener(new ClickEvent());
        //     
        sfv = (SurfaceView)this.findViewById(R.id.SurfaceView01);
        //     
        audioProcess.initDraw(yMax/2, sfv.getHeight(),mContext,frequency);
        //    
        zctlY = (ZoomControls)this.findViewById(R.id.zctlY);
        zctlY.setOnZoomInClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                if(audioProcess.rateY - 5>yMin){
                	audioProcess.rateY = audioProcess.rateY - 5;  
	                setTitle("Y   "+String.valueOf(audioProcess.rateY)+" ");
                }else{
                	audioProcess.rateY = 1;
	                setTitle("    ");
                }
            }  
        });  
          
        zctlY.setOnZoomOutClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                if(audioProcess.rateY<yMax){
                	audioProcess.rateY = audioProcess.rateY + 5;      
	                setTitle("Y   "+String.valueOf(audioProcess.rateY)+" ");  
                }else {
                	setTitle("Y        ");
				}
            }  
        });
	}
    
    /**
     *       
     */
    class ClickEvent implements View.OnClickListener{
    	@Override
    	public void onClick(View v){
    		Button button = (Button)v;
    		if(button == btnStart){
    			if(button.getText().toString().equals("Start")){
        	        try {
            			//  
            	        minBufferSize = AudioRecord.getMinBufferSize(frequency, 
            	        		channelConfiguration, 
            	        		audioEncodeing);
            	        //minBufferSize = 2 * minBufferSize; 
            	        audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,frequency,
            	        		channelConfiguration,
            	        		audioEncodeing,
            	        		minBufferSize);
            			audioProcess.baseLine = sfv.getHeight()-100;
            			audioProcess.frequence = frequency;
            			audioProcess.start(audioRecord, minBufferSize, sfv);
            			Toast.makeText(AudioMaker.this, 
            					"              :"+String.valueOf(frequency), 
            					Toast.LENGTH_SHORT).show();
            			btnStart.setText(R.string.btn_exit);
            	        spinner.setEnabled(false);
    				} catch (Exception e) {
    					// TODO: handle exception
            			Toast.makeText(AudioMaker.this, 
            					"               "+String.valueOf(frequency)+",     ", 
            					Toast.LENGTH_SHORT).show();
    				}
        		}else if (button.getText().equals("Stop")) {
        			spinner.setEnabled(true);
    				btnStart.setText(R.string.btn_start);
    				audioProcess.stop(sfv);
    			}
    		}
    		else {
    			new AlertDialog.Builder(AudioMaker.this) 
    	         .setTitle("  ") 
    	         .setMessage("    ?") 
    	         .setPositiveButton("  ", new DialogInterface.OnClickListener() { 
    	        public void onClick(DialogInterface dialog, int whichButton) { 
    	        setResult(RESULT_OK);//       
 				AudioMaker.this.finish();
    	         finish(); 
    	         } 
    	         }) 
    	         .setNegativeButton("  ", new DialogInterface.OnClickListener() { 
    	        public void onClick(DialogInterface dialog, int whichButton) { 
    	         //       
    	         } 
    	         }) 
    	         .show();
			}
    		
    	}
    }
}

プログラムソースダウンロードアドレス:http://download.csdn.net/detail/sunshine_okey/3790484
詳細はコードを見て、何か書いた詳細は伝言することができます
初めて技術的な文章を書いたが,よく書けなかったので,みんなは責めないで,我慢して読んでください.