package com.linroid.sky31radio.view; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.media.audiofx.Visualizer; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import com.linroid.sky31radio.R; import timber.log.Timber; /** * Created by linroid on 1/22/15. */ public class EqualizerView extends View{ Visualizer visualizer; Paint wavePaint; float waveDividerWidth; float waveWidth; byte[] waveformBytes; byte[] fftBytes; float[] mWavePoints; float[] mFFTPoints; RectF mWaveRect; Rect mFFTRect; public EqualizerView(Context context) { super(context); init(context, null); } public EqualizerView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public EqualizerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, AttributeSet attrs) { TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.EqualizerView); Resources res = getResources(); waveWidth = ta.getDimensionPixelOffset(R.styleable.EqualizerView_waveWidth, TypedValue.complexToDimensionPixelSize(32, res.getDisplayMetrics())); waveDividerWidth = ta.getDimensionPixelSize(R.styleable.EqualizerView_waveDividerWidth, TypedValue.complexToDimensionPixelSize(32, res.getDisplayMetrics())); int waveColor = ta.getColor(R.styleable.EqualizerView_waveColor, Color.parseColor("#77FFFFFF")); wavePaint = new Paint(Paint.ANTI_ALIAS_FLAG); wavePaint.setStrokeWidth(waveWidth); wavePaint.setAntiAlias(true); wavePaint.setColor(waveColor); wavePaint.setStrokeWidth(waveWidth); mWaveRect = new RectF(); mFFTRect = new Rect(); } public void setWaveColor(int color){ wavePaint.setColor(color); invalidate(); } public void linkPlayer(int sessionId){ Timber.d("linkPlayer, session id: %d", sessionId); visualizer = new Visualizer(sessionId); if(!visualizer.getEnabled()){ visualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]); } visualizer.setDataCaptureListener(dataCaptureListener, Visualizer.getMaxCaptureRate() / 2, true, true); visualizer.setEnabled(true); } @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); if(visualizer!=null){ visualizer.setEnabled(enabled); } } public void release(){ if(visualizer != null){ visualizer.setEnabled(false); visualizer.release(); visualizer = null; } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // drawWave(canvas); drawFFT(canvas); } private void drawFFT(Canvas canvas) { if (waveformBytes == null) { return; } if (mFFTPoints == null || fftBytes.length < fftBytes.length * 4) { mFFTPoints = new float[fftBytes.length * 4]; } int mDivisions = (int) waveDividerWidth; mWaveRect.set(0, 0, getWidth(), getHeight()); for (int i = 0; i < fftBytes.length / mDivisions; i++) { mFFTPoints[i * 4] = i * 4 * mDivisions; mFFTPoints[i * 4 + 2] = i * 4 * mDivisions; byte rfk = fftBytes[mDivisions * i]; byte ifk = fftBytes[mDivisions * i + 1]; float magnitude = (rfk * rfk + ifk * ifk); int dbValue = (int) (10 * Math.log10(magnitude)); mFFTPoints[i * 4 + 1] = mWaveRect.height(); mFFTPoints[i * 4 + 3] = mWaveRect.height() - (dbValue * 8 - 10); } canvas.drawLines(mFFTPoints, wavePaint); } int waveCount; int perWaveBytesCount; int perWaveBytesSum; private void drawWave(Canvas canvas) { if (waveformBytes == null) { return; } if (mWavePoints == null || mWavePoints.length < waveformBytes.length * 4) { mWavePoints = new float[waveformBytes.length * 4]; } mWaveRect.set(0, 0, getWidth(), getHeight()); waveCount = (int) (mWaveRect.width() / (waveDividerWidth+waveWidth)); perWaveBytesCount = (mWavePoints.length/ waveCount); // if(waveformBytes.length < waveCount){ // return; // } for (int i = 0; i < waveCount; i++) { //x0 mWavePoints[i*4] = (waveDividerWidth+waveWidth) * i; //y0 mWavePoints[i*4+1] = mWaveRect.bottom; //x1 mWavePoints[i*4+2] = mWavePoints[i*4]; //y1 perWaveBytesSum = 0; for(int j= perWaveBytesCount * i; j< perWaveBytesCount *(i+1)&&j<waveformBytes.length ; j++){ perWaveBytesSum += waveformBytes[j]; } mWavePoints[i*4+3] = mWaveRect.bottom - ((perWaveBytesSum/(perWaveBytesCount*1.0f)) / 256f) * mWaveRect.height(); // Timber.d("[%f,%f] [%f,%f]",mWavePoints[i*4], mWavePoints[i*4+1], mWavePoints[i*4+2],mWavePoints[i*4+3]); } canvas.drawLines(mWavePoints, wavePaint); } Visualizer.OnDataCaptureListener dataCaptureListener = new Visualizer.OnDataCaptureListener() { @Override public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) { updateVisualizerWave(waveform); } @Override public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) { updateVisualizerFFT(fft); } }; private void updateVisualizerFFT(byte[] data) { fftBytes = data; } private void updateVisualizerWave(byte[] data) { waveformBytes = data; ViewCompat.postInvalidateOnAnimation(this); } }