zoukankan      html  css  js  c++  java
  • 我的Android进阶之旅------>Android实现音乐示波器、均衡器、重低音和音场功能

    本实例来自于《疯狂Android讲义》。要实现详细的功能,须要了解下面API:
    • MediaPlayer  媒体播放器
    • Visualizer 频谱
    • Equalizer 均衡器
    • BassBoost 重低音控制器
    • PresetReverb 预设音场控制器
    • Paint 画图

    来看下效果示意图,例如以下所看到的

    竖状波形图

    块状波形图

    曲线波形图


    调节均衡器、重低音

    选择音场


    以下来看详细的实现代码    
    MediaPlayerTest.java
    package com.oyp.media;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import android.app.Activity;
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Paint.Style;
    import android.graphics.Rect;
    import android.media.AudioManager;
    import android.media.MediaPlayer;
    import android.media.audiofx.BassBoost;
    import android.media.audiofx.Equalizer;
    import android.media.audiofx.PresetReverb;
    import android.media.audiofx.Visualizer;
    import android.os.Bundle;
    import android.view.Gravity;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.AdapterView;
    import android.widget.ArrayAdapter;
    import android.widget.LinearLayout;
    import android.widget.SeekBar;
    import android.widget.Spinner;
    import android.widget.TextView;
    
    public class MediaPlayerTest extends Activity
    {
    	// 定义播放声音的MediaPlayer
    	private MediaPlayer mPlayer;
    	// 定义系统的频谱
    	private Visualizer mVisualizer;	
    	// 定义系统的均衡器
    	private Equalizer mEqualizer;
    	// 定义系统的重低音控制器
    	private BassBoost mBass;
    	// 定义系统的预设音场控制器
    	private PresetReverb mPresetReverb;
    	private LinearLayout layout;
    	private List<Short> reverbNames = new ArrayList<Short>();
    	private List<String> reverbVals = new ArrayList<String>();
    
    	@Override
    	public void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		//设置音频流 - STREAM_MUSIC:音乐回放即媒体音量
    		setVolumeControlStream(AudioManager.STREAM_MUSIC);
    		layout = new LinearLayout(this);//代码创建布局
    		layout.setOrientation(LinearLayout.VERTICAL);//设置为线性布局-上下排列
    		setContentView(layout);//将布局加入到 Activity
    		// 创建MediaPlayer对象,并加入音频
    		// 音频路径为  res/raw/beautiful.mp3
    		mPlayer = MediaPlayer.create(this, R.raw.beautiful);
    		// 初始化示波器
    		setupVisualizer();
    		// 初始化均衡控制器
    		setupEqualizer();
    		// 初始化重低音控制器
    		setupBassBoost();
    		// 初始化预设音场控制器
    		setupPresetReverb();
    		// 开发播放音乐
    		mPlayer.start();
    	}
    	/**
    	 * 初始化频谱
    	 */
    	private void setupVisualizer()
    	{
    		// 创建MyVisualizerView组件,用于显示波形图
    		final MyVisualizerView mVisualizerView =
    			new MyVisualizerView(this);
    		mVisualizerView.setLayoutParams(new ViewGroup.LayoutParams(
    			ViewGroup.LayoutParams.MATCH_PARENT,
    			(int) (120f * getResources().getDisplayMetrics().density)));
    		// 将MyVisualizerView组件加入到layout容器中
    		layout.addView(mVisualizerView);
    		// 以MediaPlayer的AudioSessionId创建Visualizer
    		// 相当于设置Visualizer负责显示该MediaPlayer的音频数据
    		mVisualizer = new Visualizer(mPlayer.getAudioSessionId());
    		//设置须要转换的音乐内容长度,专业的说这就是採样,该採样值一般为2的指数倍,如64,128,256,512,1024。
    		mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
    		// 为mVisualizer设置监听器
    		/*
    		 * Visualizer.setDataCaptureListener(OnDataCaptureListener listener, int rate, boolean waveform, boolean fft
    		 * 	
    		 * 		listener,表监听函数。匿名内部类实现该接口,该接口须要实现两个函数	
    		 		rate, 表示採样的周期,即隔多久採样一次,联系前文就是隔多久採样128个数据
    				iswave,是波形信号
    				isfft。是FFT信号,表示是获取波形信号还是频域信号
    			
    		 */
    		mVisualizer.setDataCaptureListener(
    			new Visualizer.OnDataCaptureListener()
    			{
    				//这个回调应该採集的是高速傅里叶变换有关的数据
    				@Override
    				public void onFftDataCapture(Visualizer visualizer,
    					byte[] fft, int samplingRate)
    				{
    				}
    				 //这个回调应该採集的是波形数据
    				@Override
    				public void onWaveFormDataCapture(Visualizer visualizer,
    					byte[] waveform, int samplingRate)
    				{
    					// 用waveform波形数据更新mVisualizerView组件
    					mVisualizerView.updateVisualizer(waveform);
    				}
    			}, Visualizer.getMaxCaptureRate() / 2, true, false);
    		mVisualizer.setEnabled(true);
    	}
    	
    	/**
    	 * 初始化均衡控制器
    	 */
    	private void setupEqualizer()
    	{
    		// 以MediaPlayer的AudioSessionId创建Equalizer
    		// 相当于设置Equalizer负责控制该MediaPlayer
    		mEqualizer = new Equalizer(0, mPlayer.getAudioSessionId());
    		// 启用均衡控制效果
    		mEqualizer.setEnabled(true);
    		TextView eqTitle = new TextView(this);
    		eqTitle.setText("均衡器:");
    		layout.addView(eqTitle);
    		// 获取均衡控制器支持最小值和最大值
    		final short minEQLevel = mEqualizer.getBandLevelRange()[0];//第一个下标为最低的限度范围
    		short maxEQLevel = mEqualizer.getBandLevelRange()[1];  // 第二个下标为最高的限度范围
    		// 获取均衡控制器支持的全部频率
    		short brands = mEqualizer.getNumberOfBands();
    		for (short i = 0; i < brands; i++)
    		{
    			TextView eqTextView = new TextView(this);
    			// 创建一个TextView。用于显示频率
    			eqTextView.setLayoutParams(new ViewGroup.LayoutParams(
    				ViewGroup.LayoutParams.MATCH_PARENT,
    				ViewGroup.LayoutParams.WRAP_CONTENT));
    			eqTextView.setGravity(Gravity.CENTER_HORIZONTAL);
    			// 设置该均衡控制器的频率
    			eqTextView.setText((mEqualizer.getCenterFreq(i) / 1000)
    				+ " Hz");
    			layout.addView(eqTextView);
    			// 创建一个水平排列组件的LinearLayout
    			LinearLayout tmpLayout = new LinearLayout(this);
    			tmpLayout.setOrientation(LinearLayout.HORIZONTAL);
    			// 创建显示均衡控制器最小值的TextView
    			TextView minDbTextView = new TextView(this);
    			minDbTextView.setLayoutParams(new ViewGroup.LayoutParams(
    				ViewGroup.LayoutParams.WRAP_CONTENT,
    				ViewGroup.LayoutParams.WRAP_CONTENT));
    			// 显示均衡控制器的最小值
    			minDbTextView.setText((minEQLevel / 100) + " dB");
    			// 创建显示均衡控制器最大值的TextView
    			TextView maxDbTextView = new TextView(this);
    			maxDbTextView.setLayoutParams(new ViewGroup.LayoutParams(
    				ViewGroup.LayoutParams.WRAP_CONTENT,
    				ViewGroup.LayoutParams.WRAP_CONTENT));
    			// 显示均衡控制器的最大值			
    			maxDbTextView.setText((maxEQLevel / 100) + " dB");
    			LinearLayout.LayoutParams layoutParams = new 
    				LinearLayout.LayoutParams(
    				ViewGroup.LayoutParams.MATCH_PARENT,
    				ViewGroup.LayoutParams.WRAP_CONTENT);
    			layoutParams.weight = 1;
    			// 定义SeekBar做为调整工具
    			SeekBar bar = new SeekBar(this);
    			bar.setLayoutParams(layoutParams);
    			bar.setMax(maxEQLevel - minEQLevel);
    			bar.setProgress(mEqualizer.getBandLevel(i));
    			final short brand = i;
    			// 为SeekBar的拖动事件设置事件监听器
    			bar.setOnSeekBarChangeListener(new SeekBar
    				.OnSeekBarChangeListener()
    			{
    				@Override
    				public void onProgressChanged(SeekBar seekBar,
    					int progress, boolean fromUser)
    				{
    					// 设置该频率的均衡值
    					mEqualizer.setBandLevel(brand,
    						(short) (progress + minEQLevel));
    				}
    				@Override
    				public void onStartTrackingTouch(SeekBar seekBar)
    				{
    				}
    				@Override
    				public void onStopTrackingTouch(SeekBar seekBar)
    				{
    				}
    			});
    			// 使用水平排列组件的LinearLayout“盛装”3个组件
    			tmpLayout.addView(minDbTextView);
    			tmpLayout.addView(bar);
    			tmpLayout.addView(maxDbTextView);
    			// 将水平排列组件的LinearLayout加入到myLayout容器中
    			layout.addView(tmpLayout);
    		}
    	}
    
    	/**
    	 * 初始化重低音控制器
    	 */
    	private void setupBassBoost()
    	{
    		// 以MediaPlayer的AudioSessionId创建BassBoost
    		// 相当于设置BassBoost负责控制该MediaPlayer
    		mBass = new BassBoost(0, mPlayer.getAudioSessionId());
    		// 设置启用重低音效果
    		mBass.setEnabled(true);
    		TextView bbTitle = new TextView(this);
    		bbTitle.setText("重低音:");
    		layout.addView(bbTitle);
    		// 使用SeekBar做为重低音的调整工具 
    		SeekBar bar = new SeekBar(this);
    		// 重低音的范围为0~1000
    		bar.setMax(1000);
    		bar.setProgress(0);
    		// 为SeekBar的拖动事件设置事件监听器
    		bar.setOnSeekBarChangeListener(new SeekBar
    			.OnSeekBarChangeListener()
    		{
    			@Override
    			public void onProgressChanged(SeekBar seekBar
    				, int progress, boolean fromUser)
    			{
    				// 设置重低音的强度
    				mBass.setStrength((short) progress);
    			}
    			@Override
    			public void onStartTrackingTouch(SeekBar seekBar)
    			{
    			}
    			@Override
    			public void onStopTrackingTouch(SeekBar seekBar)
    			{
    			}
    		});
    		layout.addView(bar);
    	}
    
    	/**
    	 * 初始化预设音场控制器
    	 */
    	private void setupPresetReverb()
    	{
    		// 以MediaPlayer的AudioSessionId创建PresetReverb
    		// 相当于设置PresetReverb负责控制该MediaPlayer
    		mPresetReverb = new PresetReverb(0,
    			mPlayer.getAudioSessionId());
    		// 设置启用预设音场控制
    		mPresetReverb.setEnabled(true);
    		TextView prTitle = new TextView(this);
    		prTitle.setText("音场");
    		layout.addView(prTitle);
    		// 获取系统支持的全部预设音场
    		for (short i = 0; i < mEqualizer.getNumberOfPresets(); i++)
    		{
    			reverbNames.add(i);
    			reverbVals.add(mEqualizer.getPresetName(i));
    		}
    		// 使用Spinner做为音场选择工具
    		Spinner sp = new Spinner(this);
    		sp.setAdapter(new ArrayAdapter<String>(MediaPlayerTest.this,
    			android.R.layout.simple_spinner_item, reverbVals));
    		// 为Spinner的列表项选中事件设置监听器
    		sp.setOnItemSelectedListener(new Spinner
    			.OnItemSelectedListener()
    		{
    			@Override
    			public void onItemSelected(AdapterView<?> arg0
    				, View arg1, int arg2, long arg3)
    			{
    				// 设定音场
    				mPresetReverb.setPreset(reverbNames.get(arg2));
    			}
    
    			@Override
    			public void onNothingSelected(AdapterView<?> arg0)
    			{
    			}
    		});
    		layout.addView(sp);
    	}
    
    	@Override
    	protected void onPause()
    	{
    		super.onPause();
    		if (isFinishing() && mPlayer != null)
    		{
    			// 释放全部对象
    			mVisualizer.release();
    			mEqualizer.release();
    			mPresetReverb.release();
    			mBass.release();
    			mPlayer.release();
    			mPlayer = null;
    		}
    	}
    	/**
    	 * 依据Visualizer传来的数据动态绘制波形效果,分别为:
    	 * 块状波形、柱状波形、曲线波形
    	 */
    	private static class MyVisualizerView extends View
    	{
    		// bytes数组保存了波形抽样点的值
    		private byte[] bytes;
    		private float[] points;
    		private Paint paint = new Paint();
    		private Rect rect = new Rect();
    		private byte type = 0;
    		public MyVisualizerView(Context context)
    		{
    			super(context);
    			bytes = null;
    			// 设置画笔的属性
    			paint.setStrokeWidth(1f);
    			paint.setAntiAlias(true);//抗锯齿
    			paint.setColor(Color.YELLOW);//画笔颜色
    			paint.setStyle(Style.FILL);
    		}
    
    		public void updateVisualizer(byte[] ftt)
    		{
    			bytes = ftt;
    			// 通知该组件重绘自己。
    			invalidate();
    		}
    		
    		@Override
    		public boolean onTouchEvent(MotionEvent me)
    		{
    			// 当用户触碰该组件时,切换波形类型
    			if(me.getAction() != MotionEvent.ACTION_DOWN)
    			{
    				return false;
    			}
    			type ++;
    			if(type >= 3)
    			{
    				type = 0;
    			}
    			return true;
    		}
    
    		@Override
    		protected void onDraw(Canvas canvas)
    		{
    			super.onDraw(canvas);
    			if (bytes == null)
    			{
    				return;
    			}
    			// 绘制白色背景
    			canvas.drawColor(Color.WHITE);			
    			// 使用rect对象记录该组件的宽度和高度
    			rect.set(0,0,getWidth(),getHeight());
    			switch(type)
    			{
    				// -------绘制块状的波形图-------
    				case 0:	
    					for (int i = 0; i < bytes.length - 1; i++)
    					{
    						float left = getWidth() * i / (bytes.length - 1);
    						// 依据波形值计算该矩形的高度		
    						float top = rect.height()-(byte)(bytes[i+1]+128)
    							* rect.height() / 128;
    						float right = left + 1;
    						float bottom = rect.height();
    						canvas.drawRect(left, top, right, bottom, paint);
    					}
    					break;
    				// -------绘制柱状的波形图(每隔18个抽样点绘制一个矩形)-------
    				case 1:
    					for (int i = 0; i < bytes.length - 1; i += 18)
    					{
    						float left = rect.width()*i/(bytes.length - 1);
    						// 依据波形值计算该矩形的高度
    						float top = rect.height()-(byte)(bytes[i+1]+128)
    							* rect.height() / 128;
    						float right = left + 6;
    						float bottom = rect.height();
    						canvas.drawRect(left, top, right, bottom, paint);
    					}
    					break;
    				// -------绘制曲线波形图-------
    				case 2:
    					// 假设point数组还未初始化
    					if (points == null || points.length < bytes.length * 4)
    					{
    						points = new float[bytes.length * 4];
    					}
    					for (int i = 0; i < bytes.length - 1; i++)
    					{
    						// 计算第i个点的x坐标
    						points[i * 4] = rect.width()*i/(bytes.length - 1);
    						// 依据bytes[i]的值(波形点的值)计算第i个点的y坐标
    						points[i * 4 + 1] = (rect.height() / 2)
    							+ ((byte) (bytes[i] + 128)) * 128
    							/ (rect.height() / 2);
    						// 计算第i+1个点的x坐标
    						points[i * 4 + 2] = rect.width() * (i + 1)
    							/ (bytes.length - 1);
    						// 依据bytes[i+1]的值(波形点的值)计算第i+1个点的y坐标
    						points[i * 4 + 3] = (rect.height() / 2)
    							+ ((byte) (bytes[i + 1] + 128)) * 128
    							/ (rect.height() / 2);
    					}
    					// 绘制波形曲线
    					canvas.drawLines(points, paint);
    					break;
    			}
    		}
    	}	
    }

    AndroidManifest.xml
    <?xml version="1.0" encoding="utf-8"?>
    <manifest
    	xmlns:android="http://schemas.android.com/apk/res/android"
    	package="com.oyp.media"
    	android:versionCode="1"
    	android:versionName="1.0">
    	<uses-sdk android:minSdkVersion="10" 
    	    android:targetSdkVersion="17"/>
    	<!-- 使用音场效果必要的权限 -->
    	<uses-permission android:name="android.permission.RECORD_AUDIO" />
    	 <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
    	
    	<application
    		android:icon="@drawable/ic_launcher"
    		android:label="@string/app_name">
    		<activity
    			android:name=".MediaPlayerTest"
    			android:label="@string/app_name">
    			<intent-filter>
    				<action android:name="android.intent.action.MAIN" />
    				<category android:name="android.intent.category.LAUNCHER" />
    			</intent-filter>
    		</activity>
    	</application>
    </manifest>

    PS:请在真机环境下执行此程序,假设在模拟器下执行。可能会报错:
    java.lang.RuntimeException: Cannot initialize Visualizer engine, error: -4



                                ====================================================================================

      作者:欧阳鹏  欢迎转载,与人分享是进步的源泉!

      转载请保留原文地址http://blog.csdn.net/ouyang_peng

    ====================================================================================

     


  • 相关阅读:
    Spring MVC 迁移项目搭建运行
    linux 安装 nginx
    linux 安装 redis
    linux 安装 jdk
    存储过程之游标插入数据
    存储过程之基础语法
    AES加密解密,自定义加密规则记录
    idea破解记录
    Mysql-explain之Using temporary和Using filesort解决方案
    C#多线程学习笔记(朝夕eleven) Task启动方式、Task阻塞、Task.Delay()、多线程异常处理、任务取消、多线程的临时变量、共享数据的lock、Task返回值
  • 原文地址:https://www.cnblogs.com/mfmdaoyou/p/6723574.html
Copyright © 2011-2022 走看看