Android平台音頻信號FFT的實現


轉載請標明出處:http://blog.csdn.net/sctu_vroy/article/details/45871823

功能:加載本地SD卡中moveDsp文件夾中的音頻文件(包括錄音獲取文件和MP3文件),播放實時FFT,繪制出信號的時域和頻域波形。

設計步驟:
第一步:頁面布局,編寫錄音工具類URecorder(設置錄音屬性)和接口IVoiceManager
[java]  view plain copy
 
  1. public class URecorder implements IVoiceManager{  
  2.     private static final String TAG = "URecorder";  
  3.     private Context context = null;  
  4.     private String path = null;  
  5.     private MediaRecorder mRecorder = null;  
  6.     public URecorder(Context context, String path) {  
  7.         this.context = context;  
  8.         this.path = path;  
  9.         mRecorder = new MediaRecorder();  
  10.     }  
  11.   
  12.     @Override  
  13.     public boolean start() {  
  14.         //設置音源為Micphone      
  15.         mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);      
  16.         //設置封裝格式      
  17.         mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);      
  18.         mRecorder.setOutputFile(path);      
  19.         //設置編碼格式      
  20.         mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);      
  21.         try {      
  22.             mRecorder.prepare();      
  23.         } catch (IOException e) {      
  24.             Log.e(TAG, "prepare() failed");      
  25.         }      
  26.         //錄音    
  27.         mRecorder.start();      
  28.         return false;  
  29.     }  
  30.     @Override  
  31.     public boolean stop() {  
  32.         mRecorder.stop();      
  33.         mRecorder.release();      
  34.         mRecorder = null;     
  35.         return false;  
  36.     }  
  37. }  
[java]  view plain copy
 
  1. public interface IVoiceManager {  
  2.     public boolean start();  
  3.     public boolean stop();  
  4. }  
第二步:繼承父類View,編寫自定義繪圖類VisualizerView(顯示時域波形)和VisualizerFFTView(顯示頻域波形(頻譜)),重寫onDraw()方法
時域波形顯示類:
[java]  view plain copy
 
  1. public class VisualizerView extends View {  
  2.     private byte[] mBytes;    
  3.     private float[] mPoints;    
  4.     private Rect mRect = new Rect();    
  5.     private Paint mForePaint = new Paint();    
  6.     public VisualizerView(Context context) {  
  7.         super(context);  
  8.         init();  
  9.     }  
  10.     /** 
  11.      * 初始化 
  12.      */  
  13.     private void init() {  
  14.         mBytes = null;    
  15.         mForePaint.setStrokeWidth(1f);    
  16.         mForePaint.setAntiAlias(true);    
  17.         mForePaint.setColor(Color.GREEN);  
  18.     }  
  19.     public void updateVisualizer(byte[] waveForm)    
  20.     {    
  21.         mBytes = waveForm;    
  22.         invalidate();    
  23.     }  
  24.     @Override  
  25.     protected void onDraw(Canvas canvas) {  
  26.         super.onDraw(canvas);     
  27.         if (mBytes == null)    
  28.         {    
  29.             return;    
  30.         }    
  31.         if (mPoints == null || mPoints.length < mBytes.length * 4)    
  32.         {    
  33.             mPoints = new float[mBytes.length * 4];    
  34.         }    
  35.         mRect.set(0, 0, getWidth(), getHeight());    
  36.         //繪制波形    
  37.          for (int i = 0; i < mBytes.length - 1; i++) {    
  38.              mPoints[i * 4] = mRect.width() * i / (mBytes.length - 1);    
  39.              mPoints[i * 4 + 1] = mRect.height() / 2    
  40.              + ((byte) (mBytes[i] + 128)) * (mRect.height() / 2) / 128;    
  41.              mPoints[i * 4 + 2] = mRect.width() * (i + 1) / (mBytes.length - 1);    
  42.              mPoints[i * 4 + 3] = mRect.height() / 2    
  43.              + ((byte) (mBytes[i + 1] + 128)) * (mRect.height() / 2) / 128;    
  44.          }     
  45.         canvas.drawLines(mPoints, mForePaint);  
  46.     }  
  47. }  
頻譜顯示類:
[java]  view plain copy
 
  1. public class VisualizerFFTView extends View {  
  2.     private byte[] mBytes;    
  3.     private float[] mPoints;    
  4.     private Rect mRect = new Rect();    
  5.     private Paint mForePaint = new Paint();    
  6.     private int mSpectrumNum = 48;        
  7.     public VisualizerFFTView(Context context) {  
  8.         super(context);  
  9.         init();  
  10.     }  
  11.     /** 
  12.      * 初始化 
  13.      */  
  14.     private void init() {  
  15.         mBytes = null;    
  16.         mForePaint.setStrokeWidth(8f);    
  17.         mForePaint.setAntiAlias(true);    
  18.         mForePaint.setColor(Color.rgb(0, 128, 255));  
  19.     }     
  20.     public void updateVisualizer(byte[] fft)    
  21.     {    
  22.         byte[] model = new byte[fft.length / 2 + 1];    
  23.         model[0] = (byte) Math.abs(fft[0]);  
  24.         for (int i = 2, j = 1; j < mSpectrumNum;)    
  25.         {    
  26.             model[j] = (byte) Math.hypot(fft[i], fft[i + 1]);    
  27.             i += 2;    
  28.             j++;    
  29.         }    
  30.         mBytes = model;    
  31.         invalidate();    
  32.     }     
  33.     @Override  
  34.     protected void onDraw(Canvas canvas) {  
  35.         super.onDraw(canvas);         
  36.         if (mBytes == null)    
  37.         {    
  38.             return;    
  39.         }    
  40.         if (mPoints == null || mPoints.length < mBytes.length * 4)    
  41.         {    
  42.             mPoints = new float[mBytes.length * 4];    
  43.         }    
  44.         mRect.set(0, 0, getWidth(), getHeight());    
  45.         //繪制頻譜    
  46.         final int baseX = mRect.width()/mSpectrumNum;    
  47.         final int height = mRect.height();   
  48.         for (int i = 0; i < mSpectrumNum ; i++)    
  49.         {    
  50.             if (mBytes[i] < 0)    
  51.             {    
  52.                 mBytes[i] = 127;    
  53.             }                 
  54.             final int xi = baseX*i + baseX/2;              
  55.             mPoints[i * 4] = xi;    
  56.             mPoints[i * 4 + 1] = height;             
  57.             mPoints[i * 4 + 2] = xi;    
  58.             mPoints[i * 4 + 3] = height - mBytes[i];    
  59.         }    
  60.         canvas.drawLines(mPoints, mForePaint);  
  61.     }  
  62. }  
第三步:通過URI新建一個MediaPlayer對象,其他方式當執行getAudioSessionId()時將為null
[java]  view plain copy
 
  1. //uri = Uri.parse(AudioPath);  // 解析錄音文件路徑到uri  
  2. uri = Uri.parse(Mp3Path);  // 解析MP3文件路徑到uri  
  3. mMedia = MediaPlayer.create(this, uri);  // 實例化mMedia對象,並通過uri將資源文件加載到該對象  

調用Android SDK 2.3以上版本中一個工具包android.media.audiofx.Visualizer,程序需要做的就是實例化一個Visualizer對象,通過獲得一個實例化的音頻媒體類對象的SessionId,並設置該對象的需要轉換的音樂內容長度和采樣率。最后為visualizer設置監聽器setDataCaptureListener(OnDataCaptureListener listener, rate, iswave, isfft),當采樣得到的數據長度達到之前設置的內容長度后,將會觸發兩個函數,在這兩個函數中即可分別得到音頻信號的時域信號數據以及經過快速傅里葉變換(FFT)處理的頻域信號數據,均為字節數組形式(即:byte[])。

[java]  view plain copy
 
  1. mWaveView = new VisualizerView(this);   // 創建VisualizerView對象  
  2. mFFtView = new VisualizerFFTView(this);   // 創建VisualizerFFTView對象  
  3. final int maxCR = Visualizer.getMaxCaptureRate();   // 獲取最大采樣率  
  4. mVisualizer = new Visualizer(mMedia.getAudioSessionId());   // 實例化mVisualizer  
  5. mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);   // 設置內容長度為1024  
  6. mVisualizer.setDataCaptureListener(    
  7.       new Visualizer.OnDataCaptureListener()    
  8.       {    
  9.           public void onWaveFormDataCapture(Visualizer visualizer,    
  10.                          byte[] waveform, int samplingRate)    
  11.           {    
  12.                 mWaveView.updateVisualizer(waveform);  // 更新時域波形數據  
  13.           }    
  14.           public void onFftDataCapture(Visualizer visualizer,    
  15.                          byte[] fft, int samplingRate)    
  16.           {    
  17.                 mFFtView.updateVisualizer(fft);  // 更新頻域波形數據  
  18.            }    
  19.        }, maxCR / 2, true, true);   // 采樣速率為512MHz,設置同時獲取時域、頻域波形數據  
需要注意的是:停止播放時,除了release播放類對象外,還要釋放Visualizer對象。
音頻信號FFT效果圖:

源碼下載鏈接:


免責聲明!

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



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