MusicFrequencyView.java 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. package com.cooleshow.musicmerge.widget;
  2. import android.content.Context;
  3. import android.content.res.TypedArray;
  4. import android.graphics.Canvas;
  5. import android.graphics.Color;
  6. import android.graphics.Paint;
  7. import android.graphics.Path;
  8. import android.graphics.RectF;
  9. import android.media.audiofx.Visualizer;
  10. import android.util.AttributeSet;
  11. import android.view.View;
  12. import com.cooleshow.base.utils.LOG;
  13. import com.cooleshow.base.utils.SizeUtils;
  14. import com.cooleshow.musicmerge.R;
  15. import androidx.annotation.Nullable;
  16. /**
  17. *
  18. * on 2021/4/13
  19. * 自定义组件:音乐频谱显示组件
  20. * API-> setMediaPlayer() 外层设置路径 播放之后 自动显示频谱效果
  21. */
  22. public class MusicFrequencyView extends View {
  23. private static final String TAG = "MusicFrequencyView";
  24. private int LUMP_COUNT = 120;
  25. private int LUMP_WIDTH = SizeUtils.dp2px(1.5f);
  26. ;
  27. private int LUMP_SPACE = SizeUtils.dp2px(3);
  28. private int LUMP_SIZE = LUMP_WIDTH + LUMP_SPACE;
  29. private int LUMP_MAX_HEIGHT = SizeUtils.dp2px(80);
  30. private int LUMP_MIN_HEIGHT = SizeUtils.dp2px(3);
  31. private float SCALE = 0.95f;
  32. private int widthsize;
  33. private int heightsize;
  34. private Visualizer visualizer;
  35. private int itemColor = Color.WHITE;
  36. private Paint paint1;
  37. protected RectF mRect;
  38. public MusicFrequencyView(Context context, @Nullable AttributeSet attrs) {
  39. super(context, attrs);
  40. TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.musicView);
  41. itemColor = ta.getColor(R.styleable.musicView_itemColor, Color.WHITE);
  42. mRect = new RectF();
  43. waveData = new float[LUMP_COUNT];
  44. }
  45. @Override
  46. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  47. widthsize = MeasureSpec.getSize(widthMeasureSpec);
  48. heightsize = MeasureSpec.getSize(heightMeasureSpec);
  49. setMeasuredDimension(widthsize, heightsize);
  50. init();
  51. }
  52. @Override
  53. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  54. super.onLayout(changed, left, top, right, bottom);
  55. mRect.set(0, 0, getWidth(), getHeight());
  56. }
  57. @Override
  58. protected void onDraw(Canvas canvas) {
  59. super.onDraw(canvas);
  60. //因为外部宽度是写死的,不调整坐标算法,最后一个可能绘制不全,暂时不绘制最后一个吧
  61. for (int i = 1; i <= LUMP_COUNT-1; i++) {
  62. // 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);
  63. // 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);
  64. 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);
  65. }
  66. }
  67. private Path getPath(float startX, float startY, float endX, float endY, Paint paint) {
  68. Path path = new Path();
  69. paint.setStrokeCap(Paint.Cap.SQUARE);
  70. path.moveTo(startY, startY);
  71. path.lineTo(endX / 2, endY / 2);
  72. paint.setStrokeCap(Paint.Cap.ROUND);
  73. path.lineTo(endX, endY);
  74. return path;
  75. }
  76. private Visualizer.OnDataCaptureListener dataCaptureListener = new Visualizer.OnDataCaptureListener() {
  77. @Override
  78. public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) {
  79. }
  80. @Override
  81. public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
  82. setWaveData(fft);
  83. }
  84. };
  85. private float[] waveData;
  86. private void setWaveData(byte[] fft) {
  87. waveData = readyData(fft);
  88. // LOG.i(TAG, "setWaveData:" + waveData);
  89. invalidate();
  90. }
  91. /**
  92. * 预处理数据
  93. *
  94. * @return
  95. */
  96. private float[] readyData(byte[] fft) {
  97. float[] model = new float[fft.length / 2 + 1];
  98. model[0] = (float) Math.abs(fft[1]);
  99. int j = 1;
  100. for (int i = 2; i < LUMP_COUNT * 2; ) {
  101. model[j] = (float) Math.hypot(fft[i], fft[i + 1]);
  102. i += 2;
  103. j++;
  104. model[j] = Math.abs(model[j]);
  105. }
  106. // LOG.i(TAG,"model:"+model.length);
  107. float[] results = hananianWindow(model, 2);
  108. // LOG.i(TAG,"results:"+results.length);
  109. return results;
  110. }
  111. public static float[] hananianWindow(float[] data, int windowSize) {
  112. float[] windowData = new float[data.length - windowSize + 1];
  113. for (int i = 0; i < windowData.length; i++) {
  114. float sum = 0;
  115. for (int j = 0; j < windowSize; j++) {
  116. sum += data[i + j];
  117. }
  118. windowData[i] = sum / windowSize;
  119. }
  120. return windowData;
  121. }
  122. /**
  123. * 只需传入 mediaPlayer 即可
  124. *
  125. * @param
  126. */
  127. public void setMediaPlayer(final int audioSessionId) {
  128. try {
  129. if (visualizer == null) {
  130. visualizer = new Visualizer(audioSessionId);
  131. int captureSize = Visualizer.getCaptureSizeRange()[1];
  132. int captureRate = Visualizer.getMaxCaptureRate() / 2;
  133. // 3:设置参数
  134. visualizer.setCaptureSize(captureSize);
  135. visualizer.setDataCaptureListener(dataCaptureListener, captureRate, false, true);
  136. visualizer.setScalingMode(Visualizer.SCALING_MODE_NORMALIZED);
  137. visualizer.setEnabled(true);
  138. }
  139. } catch (Exception e) {
  140. e.printStackTrace();
  141. }
  142. }
  143. public void setVisualizerEnable(boolean isEnable) {
  144. if (visualizer != null) {
  145. visualizer.setEnabled(isEnable);
  146. }
  147. }
  148. public void release() {
  149. if (visualizer != null) {
  150. visualizer.release();
  151. }
  152. }
  153. /**
  154. * 设置频率数据
  155. *
  156. * @param data
  157. */
  158. public void setMusicData(int[] data) {
  159. }
  160. /**
  161. * 初始化画笔
  162. */
  163. void init() {
  164. paint1 = new Paint();
  165. paint1.setStrokeWidth(LUMP_WIDTH);
  166. paint1.setColor(itemColor);
  167. paint1.setStrokeCap(Paint.Cap.ROUND);
  168. paint1.setAntiAlias(true);
  169. paint1.setStyle(Paint.Style.FILL);
  170. paint1.setStrokeJoin(Paint.Join.BEVEL);
  171. // paint1.setMaskFilter(new BlurMaskFilter(5, BlurMaskFilter.Blur.SOLID));
  172. }
  173. }