Android播放音乐时跳动的屏谱demo

时间:2022-11-26 02:02:14

Android实现播放音频时的跳动频谱,并实现可以调节的均衡器。

Main.java

  1. package com.wjq.audiofx;
  2. import android.app.Activity;
  3. import android.media.AudioManager;
  4. import android.media.MediaPlayer;
  5. import android.media.audiofx.Equalizer;
  6. import android.media.audiofx.Visualizer;
  7. import android.os.Bundle;
  8. import android.util.Log;
  9. import android.view.Gravity;
  10. import android.view.ViewGroup;
  11. import android.widget.LinearLayout;
  12. import android.widget.SeekBar;
  13. import android.widget.TextView;
  14. public class Main extends Activity {
  15. private static final String TAG = "AudioFxDemo";
  16. private static final float VISUALIZER_HEIGHT_DIP = 50f;
  17. private MediaPlayer mMediaPlayer;
  18. private Visualizer mVisualizer;
  19. private Equalizer mEqualizer;//均横器
  20. private LinearLayout mLinearLayout;
  21. VisualizerView mVisualizerView;
  22. private TextView mStatusTextView;
  23. @Override
  24. public void onCreate(Bundle icicle) {
  25. super.onCreate(icicle);
  26. setVolumeControlStream(AudioManager.STREAM_MUSIC);
  27. mStatusTextView = new TextView(this);
  28. mLinearLayout = new LinearLayout(this);
  29. mLinearLayout.setOrientation(LinearLayout.VERTICAL);
  30. mLinearLayout.addView(mStatusTextView);
  31. setContentView(mLinearLayout);
  32. // Create the MediaPlayer
  33. mMediaPlayer = MediaPlayer.create(this, R.raw.test_cbr);
  34. if( null == mMediaPlayer )
  35. return ;
  36. Log.d(TAG, "MediaPlayer audio session ID: " + mMediaPlayer.getAudioSessionId());
  37. setupVisualizerFxAndUI();
  38. setupEqualizerFxAndUI();
  39. // Make sure the visualizer is enabled only when you actually want to receive data, and
  40. // when it makes sense to receive data.
  41. mVisualizer.setEnabled(true);
  42. // When the stream ends, we don't need to collect any more data. We don't do this in
  43. // setupVisualizerFxAndUI because we likely want to have more, non-Visualizer related code
  44. // in this callback.
  45. mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
  46. public void onCompletion(MediaPlayer mediaPlayer) {
  47. mVisualizer.setEnabled(false);
  48. }
  49. });
  50. mMediaPlayer.start();
  51. mStatusTextView.setText("播放音频...");
  52. }
  53. /**
  54. * 通过mMediaPlayer返回的AudioSessionId创建一个优先级为0均衡器对象,并且通过频谱生成相应的UI和对应的事件
  55. */
  56. private void setupEqualizerFxAndUI() {
  57. mEqualizer = new Equalizer(0, mMediaPlayer.getAudioSessionId());
  58. // 启用均衡器
  59. mEqualizer.setEnabled(true);
  60. TextView eqTextView = new TextView(this);
  61. eqTextView.setText("均横器:");
  62. mLinearLayout.addView(eqTextView);
  63. // 通过均衡器得到其支持的频谱引擎
  64. short bands = mEqualizer.getNumberOfBands();
  65. // getBandLevelRange是一个数组,返回一组频谱等级数组,返回一组频谱等级数组
  66. // 第一个下标为最低的限度范围,第二个下标为最高的限度范围
  67. // 依次取出
  68. final short minEQLevel = mEqualizer.getBandLevelRange()[0];
  69. final short maxEQLevel = mEqualizer.getBandLevelRange()[1];
  70. for (short i = 0; i < bands; i++) {
  71. final short band = i;
  72. TextView freqTextView = new TextView(this);
  73. freqTextView.setLayoutParams(new ViewGroup.LayoutParams(
  74. ViewGroup.LayoutParams.FILL_PARENT,
  75. ViewGroup.LayoutParams.WRAP_CONTENT));
  76. freqTextView.setGravity(Gravity.CENTER_HORIZONTAL);
  77. freqTextView.setText((mEqualizer.getCenterFreq(band) / 1000) + " Hz");
  78. mLinearLayout.addView(freqTextView);
  79. LinearLayout row = new LinearLayout(this);
  80. row.setOrientation(LinearLayout.HORIZONTAL);
  81. TextView minDbTextView = new TextView(this);
  82. minDbTextView.setLayoutParams(new ViewGroup.LayoutParams(
  83. ViewGroup.LayoutParams.WRAP_CONTENT,
  84. ViewGroup.LayoutParams.WRAP_CONTENT));
  85. minDbTextView.setText((minEQLevel / 100) + " dB");
  86. TextView maxDbTextView = new TextView(this);
  87. maxDbTextView.setLayoutParams(new ViewGroup.LayoutParams(
  88. ViewGroup.LayoutParams.WRAP_CONTENT,
  89. ViewGroup.LayoutParams.WRAP_CONTENT));
  90. maxDbTextView.setText((maxEQLevel / 100) + " dB");
  91. LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
  92. ViewGroup.LayoutParams.FILL_PARENT,
  93. ViewGroup.LayoutParams.WRAP_CONTENT);
  94. layoutParams.weight = 1;
  95. SeekBar bar = new SeekBar(this);
  96. bar.setLayoutParams(layoutParams);
  97. bar.setMax(maxEQLevel - minEQLevel);
  98. bar.setProgress(mEqualizer.getBandLevel(band));
  99. bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
  100. public void onStopTrackingTouch(SeekBar seekBar) {}
  101. public void onStartTrackingTouch(SeekBar seekBar) {}
  102. public void onProgressChanged(SeekBar seekBar, int progress,
  103. boolean fromUser) {
  104. mEqualizer.setBandLevel(band, (short) (progress + minEQLevel));
  105. }
  106. });
  107. row.addView(minDbTextView);
  108. row.addView(bar);
  109. row.addView(maxDbTextView);
  110. mLinearLayout.addView(row);
  111. }
  112. }
  113. /**
  114. * 生成一个VisualizerView对象,使音频频谱的波段能够反映到 VisualizerView上
  115. */
  116. private void setupVisualizerFxAndUI() {
  117. mVisualizerView = new VisualizerView(this);
  118. mVisualizerView.setLayoutParams(new ViewGroup.LayoutParams(
  119. ViewGroup.LayoutParams.FILL_PARENT,
  120. (int)(VISUALIZER_HEIGHT_DIP * getResources().getDisplayMetrics().density)));
  121. mLinearLayout.addView(mVisualizerView);
  122. int sessId = mMediaPlayer.getAudioSessionId() ;
  123. mVisualizer = new Visualizer(sessId);
  124. // 参数必须是2的位数
  125. mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
  126. // 设置允许波形表示,并且捕获它
  127. mVisualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
  128. public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes,
  129. int samplingRate) {
  130. mVisualizerView.updateVisualizer(bytes);
  131. }
  132. public void onFftDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) {}
  133. }, Visualizer.getMaxCaptureRate() / 2, true, false);
  134. }
  135. @Override
  136. protected void onPause() {
  137. super.onPause();
  138. if (isFinishing() && mMediaPlayer != null) {
  139. mVisualizer.release();
  140. mEqualizer.release();
  141. mMediaPlayer.release();
  142. mMediaPlayer = null;
  143. }
  144. }
  145. }

VisualizerView.java

  1. package com.wjq.audiofx;
  2. import android.content.Context;
  3. import android.graphics.Canvas;
  4. import android.graphics.Color;
  5. import android.graphics.Paint;
  6. import android.graphics.Rect;
  7. import android.view.View;
  8. public class VisualizerView extends View {
  9. private byte[] mBytes;
  10. private float[] mPoints;
  11. //矩形区域
  12. private Rect mRect = new Rect();
  13. // 画笔
  14. private Paint mForePaint = new Paint();
  15. // 初始化画笔
  16. private void init() {
  17. mBytes = null;
  18. mForePaint.setStrokeWidth(1f);
  19. mForePaint.setAntiAlias(true);
  20. mForePaint.setColor(Color.GREEN);
  21. }
  22. public VisualizerView(Context context) {
  23. super(context);
  24. init();
  25. }
  26. public void updateVisualizer(byte[] bytes) {
  27. mBytes = bytes;
  28. invalidate();
  29. }
  30. @Override
  31. protected void onDraw(Canvas canvas) {
  32. super.onDraw(canvas);
  33. if (mBytes == null) {
  34. return;
  35. }
  36. if (mPoints == null || mPoints.length < mBytes.length * 4) {
  37. mPoints = new float[mBytes.length * 4];
  38. }
  39. mRect.set(0, 0, getWidth(), getHeight());
  40. for (int i = 0; i < mBytes.length - 1; i++) {
  41. mPoints[i * 4] = mRect.width() * i / (mBytes.length - 1);
  42. mPoints[i * 4 + 1] = mRect.height() / 2
  43. + ((byte) (mBytes[i] + 128)) * (mRect.height() / 2) / 128;
  44. mPoints[i * 4 + 2] = mRect.width() * (i + 1) / (mBytes.length - 1);
  45. mPoints[i * 4 + 3] = mRect.height() / 2
  46. + ((byte) (mBytes[i + 1] + 128)) * (mRect.height() / 2) / 128;
  47. }
  48. canvas.drawLines(mPoints, mForePaint);
  49. }
  50. }