|
@@ -5,44 +5,47 @@ import android.content.res.TypedArray;
|
|
|
import android.graphics.Canvas;
|
|
|
import android.graphics.Color;
|
|
|
import android.graphics.Paint;
|
|
|
-import android.media.MediaPlayer;
|
|
|
+import android.graphics.Path;
|
|
|
+import android.graphics.RectF;
|
|
|
import android.media.audiofx.Visualizer;
|
|
|
import android.util.AttributeSet;
|
|
|
import android.view.View;
|
|
|
|
|
|
+import com.cooleshow.base.utils.LOG;
|
|
|
import com.cooleshow.base.utils.SizeUtils;
|
|
|
import com.cooleshow.musicmerge.R;
|
|
|
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
|
|
/**
|
|
|
- * author by LiuGuo
|
|
|
+ *
|
|
|
* on 2021/4/13
|
|
|
* 自定义组件:音乐频谱显示组件
|
|
|
* API-> setMediaPlayer() 外层设置路径 播放之后 自动显示频谱效果
|
|
|
*/
|
|
|
public class MusicFrequencyView extends View {
|
|
|
private static final String TAG = "MusicFrequencyView";
|
|
|
- private int LUMP_COUNT = 60;
|
|
|
- private int LUMP_WIDTH = SizeUtils.dp2px(5);
|
|
|
+ private int LUMP_COUNT = 120;
|
|
|
+ private int LUMP_WIDTH = SizeUtils.dp2px(1.5f);
|
|
|
+ ;
|
|
|
private int LUMP_SPACE = SizeUtils.dp2px(3);
|
|
|
private int LUMP_SIZE = LUMP_WIDTH + LUMP_SPACE;
|
|
|
private int LUMP_MAX_HEIGHT = SizeUtils.dp2px(80);
|
|
|
- private int LUMP_MIN_HEIGHT = SizeUtils.dp2px(5);
|
|
|
- private float SCALE = 1.0f;
|
|
|
+ private int LUMP_MIN_HEIGHT = SizeUtils.dp2px(3);
|
|
|
+ private float SCALE = 0.95f;
|
|
|
private int widthsize;
|
|
|
private int heightsize;
|
|
|
- private Paint paint;
|
|
|
private Visualizer visualizer;
|
|
|
private int itemColor = Color.WHITE;
|
|
|
private Paint paint1;
|
|
|
- private float mi;
|
|
|
+ protected RectF mRect;
|
|
|
|
|
|
public MusicFrequencyView(Context context, @Nullable AttributeSet attrs) {
|
|
|
super(context, attrs);
|
|
|
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.musicView);
|
|
|
-// itemColor = ta.getColor(R.styleable.musicView_itemColor, Color.parseColor("#269EFE"));
|
|
|
itemColor = ta.getColor(R.styleable.musicView_itemColor, Color.WHITE);
|
|
|
+ mRect = new RectF();
|
|
|
+ waveData = new float[LUMP_COUNT];
|
|
|
}
|
|
|
|
|
|
|
|
@@ -56,36 +59,37 @@ public class MusicFrequencyView extends View {
|
|
|
|
|
|
|
|
|
@Override
|
|
|
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
|
|
+ super.onLayout(changed, left, top, right, bottom);
|
|
|
+ mRect.set(0, 0, getWidth(), getHeight());
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
protected void onDraw(Canvas canvas) {
|
|
|
super.onDraw(canvas);
|
|
|
-
|
|
|
-// Log.e(TAG, "" + heightsize + "==" + widthsize);
|
|
|
- if (waveData != null) {
|
|
|
-// Log.e(TAG, "waveData" + waveData.length);
|
|
|
- }
|
|
|
- for (int i = 0; i < LUMP_COUNT; i++) {
|
|
|
- if (waveData == null) {
|
|
|
- canvas.drawRect((LUMP_WIDTH + LUMP_SPACE) * i,
|
|
|
- LUMP_MAX_HEIGHT - LUMP_MIN_HEIGHT,
|
|
|
- (LUMP_WIDTH + LUMP_SPACE) * i + LUMP_WIDTH,
|
|
|
- LUMP_MAX_HEIGHT,
|
|
|
- paint1);
|
|
|
- continue;
|
|
|
- }
|
|
|
- drawLump(canvas, i, false);
|
|
|
+ //因为外部宽度是写死的,不调整坐标算法,最后一个可能绘制不全,暂时不绘制最后一个吧
|
|
|
+ for (int i = 1; i <= LUMP_COUNT-1; i++) {
|
|
|
+// canvas.drawLine(mRect.width() * i / LUMP_COUNT, mRect.height() / 2, mRect.width() * i / LUMP_COUNT, -LUMP_MIN_HEIGHT + mRect.height() / 2 - SCALE * waveData[i], paint1);
|
|
|
+// canvas.drawLine(mRect.width() * i / LUMP_COUNT, mRect.height() / 2, mRect.width() * i / LUMP_COUNT, LUMP_MIN_HEIGHT + mRect.height() / 2 + SCALE * waveData[i], paint1);
|
|
|
+ canvas.drawLine(mRect.width() * i / LUMP_COUNT, -LUMP_MIN_HEIGHT + mRect.height() / 2 - SCALE * waveData[i-1], mRect.width() * i / LUMP_COUNT, LUMP_MIN_HEIGHT + mRect.height() / 2 + SCALE * waveData[i - 1], paint1);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private Path getPath(float startX, float startY, float endX, float endY, Paint paint) {
|
|
|
+ Path path = new Path();
|
|
|
+ paint.setStrokeCap(Paint.Cap.SQUARE);
|
|
|
+ path.moveTo(startY, startY);
|
|
|
+ path.lineTo(endX / 2, endY / 2);
|
|
|
+ paint.setStrokeCap(Paint.Cap.ROUND);
|
|
|
+ path.lineTo(endX, endY);
|
|
|
+ return path;
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
private Visualizer.OnDataCaptureListener dataCaptureListener = new Visualizer.OnDataCaptureListener() {
|
|
|
@Override
|
|
|
public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) {
|
|
|
-// Log.i("xiaozhu", "waveform" + waveform.length);
|
|
|
- long v = 0;
|
|
|
- for (int i = 0; i < waveform.length; i++) {
|
|
|
- v += Math.pow(waveform[i], 2);
|
|
|
- }
|
|
|
- double volume = 10 * Math.log10(v / (double) waveform.length);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -94,7 +98,7 @@ public class MusicFrequencyView extends View {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- private byte[] waveData;
|
|
|
+ private float[] waveData;
|
|
|
|
|
|
private void setWaveData(byte[] fft) {
|
|
|
waveData = readyData(fft);
|
|
@@ -103,61 +107,59 @@ public class MusicFrequencyView extends View {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 绘制矩形条
|
|
|
+ * 预处理数据
|
|
|
+ *
|
|
|
+ * @return
|
|
|
*/
|
|
|
- private void drawLump(Canvas canvas, int i, boolean reversal) {
|
|
|
- int minus = reversal ? -1 : 1;
|
|
|
-
|
|
|
- if (waveData[i] < 0) {
|
|
|
-// LOG.i(TAG, "waveData[i] < 0 data: %s" + waveData[i]);
|
|
|
- }
|
|
|
- float top = (LUMP_MAX_HEIGHT - (LUMP_MIN_HEIGHT + waveData[i] * SCALE) * minus);
|
|
|
+ private float[] readyData(byte[] fft) {
|
|
|
+ float[] model = new float[fft.length / 2 + 1];
|
|
|
+ model[0] = (float) Math.abs(fft[1]);
|
|
|
+ int j = 1;
|
|
|
|
|
|
+ for (int i = 2; i < LUMP_COUNT * 2; ) {
|
|
|
|
|
|
- canvas.drawLine(LUMP_SIZE * i, LUMP_MAX_HEIGHT,
|
|
|
- LUMP_SIZE * i, top, paint1);
|
|
|
-// canvas.drawRect(LUMP_SIZE * i,
|
|
|
-// top,
|
|
|
-// LUMP_SIZE * i + LUMP_WIDTH,
|
|
|
-// LUMP_MAX_HEIGHT,
|
|
|
-// paint1);
|
|
|
+ model[j] = (float) Math.hypot(fft[i], fft[i + 1]);
|
|
|
+ i += 2;
|
|
|
+ j++;
|
|
|
+ model[j] = Math.abs(model[j]);
|
|
|
+ }
|
|
|
+ LOG.i(TAG,"model:"+model.length);
|
|
|
+ float[] results = hananianWindow(model, 2);
|
|
|
+ LOG.i(TAG,"results:"+results.length);
|
|
|
+ return results;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 预处理数据
|
|
|
- *
|
|
|
- * @return
|
|
|
- */
|
|
|
- private byte[] readyData(byte[] fft) {
|
|
|
- byte[] newData = new byte[LUMP_COUNT];
|
|
|
- byte abs;
|
|
|
- for (int i = 0; i < LUMP_COUNT; i++) {
|
|
|
- abs = (byte) Math.abs(fft[i]);
|
|
|
- //描述:Math.abs -128时越界
|
|
|
- newData[i] = abs < 0 ? 127 : abs;
|
|
|
+ public static float[] hananianWindow(float[] data, int windowSize) {
|
|
|
+ float[] windowData = new float[data.length - windowSize + 1];
|
|
|
+ for (int i = 0; i < windowData.length; i++) {
|
|
|
+ float sum = 0;
|
|
|
+ for (int j = 0; j < windowSize; j++) {
|
|
|
+ sum += data[i + j];
|
|
|
+ }
|
|
|
+ windowData[i] = sum / windowSize;
|
|
|
}
|
|
|
- return newData;
|
|
|
+ return windowData;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 只需传入 mediaPlayer 即可
|
|
|
*
|
|
|
- * @param mediaPlayer
|
|
|
+ * @param
|
|
|
*/
|
|
|
public void setMediaPlayer(final int audioSessionId) {
|
|
|
- try{
|
|
|
+ try {
|
|
|
if (visualizer == null) {
|
|
|
visualizer = new Visualizer(audioSessionId);
|
|
|
|
|
|
int captureSize = Visualizer.getCaptureSizeRange()[1];
|
|
|
- int captureRate = Visualizer.getMaxCaptureRate() * 3 / 4;
|
|
|
+ int captureRate = Visualizer.getMaxCaptureRate() / 2;
|
|
|
// 3:设置参数
|
|
|
visualizer.setCaptureSize(captureSize);
|
|
|
- visualizer.setDataCaptureListener(dataCaptureListener, captureRate, true, true);
|
|
|
+ visualizer.setDataCaptureListener(dataCaptureListener, captureRate, false, true);
|
|
|
visualizer.setScalingMode(Visualizer.SCALING_MODE_NORMALIZED);
|
|
|
visualizer.setEnabled(true);
|
|
|
}
|
|
|
- }catch (Exception e){
|
|
|
+ } catch (Exception e) {
|
|
|
e.printStackTrace();
|
|
|
}
|
|
|
}
|
|
@@ -188,25 +190,13 @@ public class MusicFrequencyView extends View {
|
|
|
* 初始化画笔
|
|
|
*/
|
|
|
void init() {
|
|
|
- setLayerType(LAYER_TYPE_SOFTWARE, null); //部分手机不显示阴影效果 配合setShadowLayer
|
|
|
- paint = new Paint();
|
|
|
- paint.setAntiAlias(true);
|
|
|
- paint.setColor(itemColor);
|
|
|
- paint.setStyle(Paint.Style.FILL);
|
|
|
- paint.setFilterBitmap(true);
|
|
|
- paint.setShadowLayer(2, 9, 5, Color.parseColor("#55000000"));
|
|
|
- paint.setStrokeWidth(heightsize / 100f);
|
|
|
-
|
|
|
-
|
|
|
paint1 = new Paint();
|
|
|
- paint1.setAntiAlias(true);
|
|
|
+ paint1.setStrokeWidth(LUMP_WIDTH);
|
|
|
paint1.setColor(itemColor);
|
|
|
paint1.setStrokeCap(Paint.Cap.ROUND);
|
|
|
- paint1.setTextAlign(Paint.Align.LEFT);
|
|
|
+ paint1.setAntiAlias(true);
|
|
|
paint1.setStyle(Paint.Style.FILL);
|
|
|
-// paint1.setStrokeWidth(heightsize / 100f);
|
|
|
- paint1.setStrokeWidth(LUMP_WIDTH);
|
|
|
-
|
|
|
-
|
|
|
+ paint1.setStrokeJoin(Paint.Join.BEVEL);
|
|
|
+// paint1.setMaskFilter(new BlurMaskFilter(5, BlurMaskFilter.Blur.SOLID));
|
|
|
}
|
|
|
}
|