天天看點

Android Api Demos登頂之路(九十五)Media-->AudioFx

/*
 * 這個demon示範了在進行音頻播放時如何使用Visualizer和Equalizer類為音頻定制
 * 示波器和均衡器。
 */
public class MainActivity extends Activity {
    // 定義示波器界面的高度(機關為dip)
    private static final float VISUALIZER_HEIGHT_DIP = f;
    // 定義一個媒體播放器
    private MediaPlayer mMediaPlay;
    // 定義示波器
    private Visualizer mVisualizer;
    // 定義均衡器
    private Equalizer mEqualizer;

    private LinearLayout mLinearLayout;
    // 定義示波器的顯示界面(這是一個自定義的内部類)
    private VisualizerView mVisualizerView;
    private TextView mStatusTextView;

    private class VisualizerView extends View {
        // 定義一個位元組數組用于接收Visualizer在采樣時擷取到的位元組資料
        private byte[] mBytes;
        // 定義一個浮點數組,用于表示每段示波線段的兩個端點
        private float[] mPoints;
        // 定義示波器的顯示區域
        private Rect mRect = new Rect();
        private Paint mPaint;

        public VisualizerView(Context context) {
            super(context);
            // 清空mBytes該變量由Visualizer在采樣後指派,是以要確定在未被指派前後狀态為空
            mBytes = null;

            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setStrokeWidth(f);
            mPaint.setColor(Color.rgb(, , ));
        }

        // 更新資料,為mBytes指派
        public void updateVisualizer(byte[] bytes) {
            mBytes = bytes;
            invalidate();
        }

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);

            if (mBytes == null) {
                return;
            }
            // 将采樣得到的資料個數作為x軸刻度的計算依據,每個位元組值的大小作為y軸刻度的計算依據建立坐标系
            // 第i個點處的波動圖示由第i個點和第i+1個點的連線組成,是以每個示波線段由四個點組成,而到連到
            // 最後一個點處結束,每段小波由兩個點組成,每個點由兩個值組成,是以必須確定mPoints的長度為(mBytes.length-1)
            if (mPoints == null || mPoints.length < (mBytes.length - ) * ) {
                mPoints = new float[(mBytes.length - ) * ];
            }

            // 繪圖區域的大小為整個示波器視圖界面
            mRect.set(, , getWidth(), getHeight());
            // 坐标系的刻度為x為采樣資料的個數,y軸為每個位元組資料的大小,以視圖的左側中心為圓點
            for (int i = ; i < mBytes.length - ; i++) {
                mPoints[i * ] = mRect.width() * i / (mBytes.length - );
                // byte的聚會範圍為-127到128,是以将y軸的刻度定義為半高/128
                // (byte)(mBytes[i]+128)變換一下會使波形更平滑一些
                mPoints[i *  + ] = mRect.height() / 
                        + (byte) (mBytes[i] + ) * (mRect.height() / ) / ;
                /*
                 * System.out.println("未轉換:"+mBytes[i]);
                 * System.out.println("轉換後:"+(byte)(mBytes[i]+128));
                 */
                mPoints[i *  + ] = mRect.width() * (i + )
                        / (mBytes.length - );
                mPoints[i *  + ] = mRect.height() / 
                        + (byte) (mBytes[i + ] + ) * (mRect.height() / )
                        / ;
            }
            // System.out.println(mPoints.length);
            canvas.drawLines(mPoints, mPaint);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 設定該Activity中音量控制鍵控制的音頻流為音樂回放,即媒體音量
        setVolumeControlStream(AudioManager.STREAM_MUSIC);

        mStatusTextView = new TextView(this);
        mLinearLayout = new LinearLayout(this);
        mLinearLayout.setOrientation(LinearLayout.VERTICAL);
        mLinearLayout.addView(mStatusTextView);

        setContentView(mLinearLayout);

        // 建立媒體播放器,并指定播放的音頻
        mMediaPlay = MediaPlayer.create(this, R.raw.test_cbr);

        // 設定示波器的界面并傳遞資料
        setupVisualizerFxAndUi();
        // 設定均衡器的界面并傳遞資料
        setupEqualizerFxAndUi();

        // 隻有當示波器真正接收資料時才将其設定為可用
        mVisualizer.setEnabled(true);

        // 當媒體播放完畢,示波器設定為不可用狀态
        mMediaPlay.setOnCompletionListener(new OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                mVisualizer.setEnabled(false);
            }
        });

        mMediaPlay.start();
        mStatusTextView.setText("Playing audio...");
    }

    @Override
    protected void onPause() {
        super.onPause();
        //如果該activity的程序結束且媒體播放器不為空
        if(isFinishing() && mMediaPlay!=null){
            mVisualizer.release();
            mEqualizer.release();
            mMediaPlay.release();
            mMediaPlay=null;
        }
    }

    private void setupEqualizerFxAndUi() {
        // 擷取均衡器,第一個參數表示優先級,預設為0,當有多個應用控制EQ時就需要通過優先級來判定
        // 第二個參數是音樂的sessionId.
        mEqualizer = new Equalizer(, mMediaPlay.getAudioSessionId());
        mEqualizer.setEnabled(true);

        TextView eqTextView = new TextView(this);
        eqTextView.setText("Equalizer:");
        mLinearLayout.addView(eqTextView);

        // 擷取目前Equalizer引擎所支援的控制頻率的标簽數目
        short bands = mEqualizer.getNumberOfBands();
        // 擷取最小的頻率
        final short minEqLevel = mEqualizer.getBandLevelRange()[];
        short maxEqLevel = mEqualizer.getBandLevelRange()[];

        for (short i = ; i < bands; i++) {
            final short band = i;

            // 定義并設定頻率标簽的顯示文本
            TextView freqTextView = new TextView(this);
            freqTextView.setLayoutParams(new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT));
            freqTextView.setGravity(Gravity.CENTER_HORIZONTAL);
            freqTextView.setText((mEqualizer.getCenterFreq(band)/)+" Hz");
            mLinearLayout.addView(freqTextView);

            LinearLayout row=new LinearLayout(this);
            row.setOrientation(LinearLayout.HORIZONTAL);

            TextView minDbTextView = new TextView(this);
            minDbTextView.setLayoutParams(new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT));
            minDbTextView.setText((minEqLevel / ) + " dB");

            TextView maxDbTextView = new TextView(this);
            maxDbTextView.setLayoutParams(new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT));
            maxDbTextView.setText((maxEqLevel / ) + " dB");

            LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
            //使SeekBar的寬度占據剩餘的全部寬度
            layoutParams.weight = ;
            SeekBar bar = new SeekBar(this);
            bar.setLayoutParams(layoutParams);
            bar.setMax(maxEqLevel - minEqLevel);
            bar.setProgress(mEqualizer.getBandLevel(band));

            bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                public void onProgressChanged(SeekBar seekBar, int progress,
                        boolean fromUser) {
                    //設定頻率的值了,需要提供标簽号,和設定的大小data
                    mEqualizer.setBandLevel(band, (short) (progress + minEqLevel));
                }

                public void onStartTrackingTouch(SeekBar seekBar) {}
                public void onStopTrackingTouch(SeekBar seekBar) {}
            });

            row.addView(minDbTextView);
            row.addView(bar);
            row.addView(maxDbTextView);

            mLinearLayout.addView(row);
        }

    }

    private void setupVisualizerFxAndUi() {
        // 将示波器界面添加到布局當中
        mVisualizerView = new VisualizerView(this);
        mVisualizerView.setLayoutParams(new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                (int) (VISUALIZER_HEIGHT_DIP * getResources()
                        .getDisplayMetrics().density)));
        mLinearLayout.addView(mVisualizerView);

        // 擷取到示波器
        mVisualizer = new Visualizer(mMediaPlay.getAudioSessionId());
        // 設定采樣的大小,getCaptureSizeRange()所傳回的數組裡面就兩個值
        // .數組[0]是最小值(128),數組[1]是最大值(1024)。
        mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[]);

        // 監聽不斷而來的所采集的資料。一共有4個參數,第一個是監聽者,第二個機關是毫赫茲,表示的是采集的頻率,
        // 第三個是是否采集波形,第四個是是否采集頻率
        mVisualizer.setDataCaptureListener(new OnDataCaptureListener() {
            // 回調應該采集的是波形資料
            @Override
            public void onWaveFormDataCapture(Visualizer visualizer,
                    byte[] waveform, int samplingRate) {
                mVisualizerView.updateVisualizer(waveform);
            }

            // 回調應該采集的是快速傅裡葉變換有關的資料
            @Override
            public void onFftDataCapture(Visualizer visualizer, byte[] fft,
                    int samplingRate) {

            }
        }, Visualizer.getMaxCaptureRate() / , true, false);
    }

}
           

繼續閱讀