My Android advanced tutorial ------) Android implements music oscilloscope, balancer, bass, and audio field functions
This example is from crazy Android handout. To implement specific functions, you need to understand the following APIs: MediaPlayer Media Player Visualizer spectrum Equalizer balancer BassBoost bass controller PresetReverb preset sound field controller painting
The following figure shows the effect.
Portrait Waveform
Block Waveform
Curve Waveform
Adjust the balancer and bass
Select audio field
The specific implementation code MediaPlayerTest. java is as follows:
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
{
// Define MediaPlayer to play sound
private MediaPlayer mPlayer;
// Define the spectrum of the system
private Visualizer mVisualizer;
// Define the equalizer of the system
private Equalizer mEqualizer;
// Define the subwoofer controller of the system
private BassBoost mBass;
// Define the preset sound field controller of the system
private PresetReverb mPresetReverb;
private LinearLayout layout;
private List reverbNames = new ArrayList ();
private List reverbVals = new ArrayList ();
@Override
public void onCreate (Bundle savedInstanceState)
{
super.onCreate (savedInstanceState);
// Set audio stream-STREAM_MUSIC: music playback is the media volume
setVolumeControlStream (AudioManager.STREAM_MUSIC);
layout = new LinearLayout (this); // The code creates a layout
layout.setOrientation (LinearLayout.VERTICAL); // Set to linear layout-arranged up and down
setContentView (layout); // Add layout to Activity
// Create MediaPlayer object and add audio
// The audio path is res / raw / beautiful.mp3
mPlayer = MediaPlayer.create (this, R.raw.beautiful);
// Initialize the oscilloscope
setupVisualizer ();
// Initialize the balance controller
setupEqualizer ();
// Initialize the subwoofer controller
setupBassBoost ();
// Initialize the preset sound field controller
setupPresetReverb ();
// Develop and play music
mPlayer.start ();
}
/ **
* Initialize the spectrum
* /
private void setupVisualizer ()
{
// Create a MyVisualizerView component to display the waveform
final MyVisualizerView mVisualizerView =
new MyVisualizerView (this);
mVisualizerView.setLayoutParams (new ViewGroup.LayoutParams (
ViewGroup.LayoutParams.MATCH_PARENT,
(int) (120f * getResources (). getDisplayMetrics (). density)));
// Add the MyVisualizerView component to the layout container
layout.addView (mVisualizerView);
// Create Visualizer with MediaPlayer's AudioSessionId
// Equivalent to setting Visualizer to display the audio data of this MediaPlayer
mVisualizer = new Visualizer (mPlayer.getAudioSessionId ());
// Set the length of the music content that needs to be converted. Professionally speaking, this is sampling. The sampling value is generally an exponential multiple of 2, such as 64,128,256,512,1024
mVisualizer.setCaptureSize (Visualizer.getCaptureSizeRange () [1]);
// Set the listener for mVisualizer
/ *
* Visualizer.setDataCaptureListener (OnDataCaptureListener listener, int rate, boolean waveform, boolean fft
*
* listener, table listener function, anonymous inner class implements this interface, the interface needs to implement two functions
rate, indicates the sampling period, that is, how often to sample once, contact the above is how often to sample 128 data
iswave, is a waveform signal
isfft, which is an FFT signal, indicating whether to obtain a waveform signal or a frequency domain signal
The
* /
mVisualizer.setDataCaptureListener (
new Visualizer.OnDataCaptureListener ()
{
// This callback should collect data related to fast Fourier transform
@Override
public void onFftDataCapture (Visualizer visualizer,
byte [] fft, int samplingRate)
{
}
// This callback should collect waveform data
@Override
public void onWaveFormDataCapture (Visualizer visualizer,
byte [] waveform, int samplingRate)
{
// Update mVisualizerView component with waveform data
mVisualizerView.updateVisualizer (waveform);
}
}, Visualizer.getMaxCaptureRate () / 2, true, false);
mVisualizer.setEnabled (true);
}
The
/ **
* Initialize the balance controller
* /
private void setupEqualizer ()
{
// Create Equalizer with MediaPlayer's AudioSessionId
// Equivalent to setting Equalizer to control the MediaPlayer
mEqualizer = new Equalizer (0, mPlayer.getAudioSessionId ());
// Enable the balance control effect
mEqualizer.setEnabled (true);
TextView eqTitle = new TextView (this);
eqTitle.setText (Equalizer :);
layout.addView (eqTitle);
// Get the minimum and maximum value supported by the balance controller
final short minEQLevel = mEqualizer.getBandLevelRange () [0]; // The first subscript is the lowest limit range
short maxEQLevel = mEqualizer.getBandLevelRange () [1]; // The second subscript is the highest limit range
// Get all frequencies supported by the balance controller
short brands = mEqualizer.getNumberOfBands ();
for (short i = 0; i <brands; i ++)
{
TextView eqTextView = new TextView (this);
// Create a TextView to display the frequency
eqTextView.setLayoutParams (new ViewGroup.LayoutParams (
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
eqTextView.setGravity (Gravity.CENTER_HORIZONTAL);
// Set the frequency of the equalization controller
eqTextView.setText ((mEqualizer.getCenterFreq (i) / 1000)
+ Hz);
layout.addView (eqTextView);
// Create a LinearLayout that arranges components horizontally
LinearLayout tmpLayout = new LinearLayout (this);
tmpLayout.setOrientation (LinearLayout.HORIZONTAL);
// Create a TextView that displays the minimum value of the equalization controller
TextView minDbTextView = new TextView (this);
minDbTextView.setLayoutParams (new ViewGroup.LayoutParams (
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
// Display the minimum value of the equalization controller
minDbTextView.setText ((minEQLevel / 100) + dB);
// Create a TextView that displays the maximum value of the equalization controller
TextView maxDbTextView = new TextView (this);
maxDbTextView.setLayoutParams (new ViewGroup.LayoutParams (
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
// Display the maximum value of the balance controller
maxDbTextView.setText ((maxEQLevel / 100) + dB);
LinearLayout.LayoutParams layoutParams = new
LinearLayout.LayoutParams (
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.weight = 1;
// Define SeekBar as an adjustment tool
SeekBar bar = new SeekBar (this);
bar.setLayoutParams (layoutParams);
bar.setMax (maxEQLevel-minEQLevel);
bar.setProgress (mEqualizer.getBandLevel (i));
final short brand = i;
// Set event listener for SeekBar's drag event
bar.setOnSeekBarChangeListener (new SeekBar
.OnSeekBarChangeListener ()
{
@Override
public void onProgressChanged (SeekBar seekBar,
int progress, boolean fromUser)
{
// Set the balance value of this frequency
mEqualizer.setBandLevel (brand,
(short) (progress + minEQLevel));
}
@Override
public void onStartTrackingTouch (SeekBar seekBar)
{
}
@Override
public void onStopTrackingTouch (SeekBar seekBar)
{
}
});
// "Layout" 3 components using LinearLayout with horizontally arranged components
tmpLayout.addView (minDbTextView);
tmpLayout.addView (bar);
tmpLayout.addView (maxDbTextView);
// Add the LinearLayout of horizontally arranged components to the myLayout container
layout.addView (tmpLayout);
}
}
/ **
* Initialize the subwoofer controller
* /
private void setupBassBoost ()
{
// Create a BassBoost with AudioSessionId of MediaPlayer
// Equivalent to setting BassBoost to control the MediaPlayer
mBass = new BassBoost (0, mPlayer.getAudioSessionId ());
// Set to enable subwoofer effect
mBass.setEnabled (true);
TextView bbTitle = new TextView (this);
bbTitle.setText (subwoofer :);
layout.addView (bbTitle);
// Use SeekBar as a subwoofer adjustment tool
SeekBar bar = new SeekBar (this);
// The range of subwoofer is 0 ~ 1000
bar.setMax (1000);
bar.setProgress (0);
// Set event listener for SeekBar's drag event
bar.setOnSeekBarChangeListener (new SeekBar
.OnSeekBarChangeListener ()
{
@Override
public void onProgressChanged (SeekBar seekBar
, int progress, boolean fromUser)
{
// Set the intensity of the subwoofer
mBass.setStrength ((short) progress);
}
@Override
public void onStartTrackingTouch (SeekBar seekBar)
{
}
@Override
public void onStopTrackingTouch (SeekBar seekBar)
{
}
});
layout.addView (bar);
}
/ **
* Initialize preset sound field controller
* /
private void setupPresetReverb ()
{
// Create PresetReverb with MediaSession's AudioSessionId
// Equivalent to setting PresetReverb to control the MediaPlayer
mPresetReverb = new PresetReverb (0,
mPlayer.getAudioSessionId ());
// Set to enable preset sound field control
mPresetReverb.setEnabled (true);
TextView prTitle = new TextView (this);
prTitle.setText (sound field);
layout.addView (prTitle);
// Get all preset sound fields supported by the system
for (short i = 0; i <mEqualizer.getNumberOfPresets (); i ++)
{
reverbNames.add (i);
reverbVals.add (mEqualizer.getPresetName (i));
}
// Use Spinner as a sound field selection tool
Spinner sp = new Spinner (this);
sp.setAdapter (new ArrayAdapter (MediaPlayerTest.this,
android.R.layout.simple_spinner_item, reverbVals));
// Set the listener for the selected event of Spinner's list item
sp.setOnItemSelectedListener (new Spinner
.OnItemSelectedListener ()
{
@Override
public void onItemSelected (AdapterView arg0
, View arg1, int arg2, long arg3)
{
// Set the sound field
mPresetReverb.setPreset (reverbNames.get (arg2));
}
@Override
public void onNothingSelected (AdapterView arg0)
{
}
});
layout.addView (sp);
}
@Override
protected void onPause ()
{
super.onPause ();
if (isFinishing () && mPlayer! = null)
{
// release all objects
mVisualizer.release ();
mEqualizer.release ();
mPresetReverb.release ();
mBass.release ();
mPlayer.release ();
mPlayer = null;
}
}
/ **
* Dynamically draw waveform effects based on data from Visualizer, respectively:
* Block waveform, column waveform, curve waveform
* /
private static class MyVisualizerView extends View
{
// The bytes array holds the values of waveform sampling points
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;
// Set the properties of the brush
paint.setStrokeWidth (1f);
paint.setAntiAlias (true); // Anti-aliasing
paint.setColor (Color.YELLOW); // Paint color
paint.setStyle (Style.FILL);
}
public void updateVisualizer (byte [] ftt)
{
bytes = ftt;
// Notify the component to redraw itself.
invalidate ();
}
The
@Override
public boolean onTouchEvent (MotionEvent me)
{
// When the user touches the component, switch the waveform type
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;
}
// draw a white background
canvas.drawColor (Color.WHITE);
// Use the rect object to record the width and height of the component
rect.set (0,0, getWidth (), getHeight ());
switch (type)
{
// ------- Draw block-shaped waveform diagram -------
case 0:
for (int i = 0; i <bytes.length-1; i ++)
{
float left = getWidth () * i / (bytes.length-1);
// Calculate the height of the rectangle based on the waveform value
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;
// ------- Draw a columnar waveform (draw a rectangle every 18 sampling points) -------
case 1:
for (int i = 0; i <bytes.length-1; i + = 18)
{
float left = rect.width () * i / (bytes.length-1);
// Calculate the height of the rectangle based on the waveform value
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;
// ------- Draw curve waveform graph -------
case 2:
// If the point array has not been initialized
if (points == null || points.length <bytes.length * 4)
{
points = new float [bytes.length * 4];
}
for (int i = 0; i <bytes.length-1; i ++)
{
// Calculate the x coordinate of the i-th point
points [i * 4] = rect.width () * i / (bytes.length-1);
// Calculate the y coordinate of the ith point based on the value of bytes [i] (the value of the waveform point)
points [i * 4 + 1] = (rect.height () / 2)
+ ((byte) (bytes [i] + 128)) * 128
/ (rect.height () / 2);
// Calculate the x coordinate of the i + 1th point
points [i * 4 + 2] = rect.width () * (i + 1)
/ (bytes.length-1);
// Calculate the y coordinate of the i + 1th point based on the value of bytes [i + 1] (the value of the waveform point)
points [i * 4 + 3] = (rect.height () / 2)
+ ((byte) (bytes [i + 1] + 128)) * 128
/ (rect.height () / 2);
}
// Draw waveform curve
canvas.drawLines (points, paint);
break;
}
}
}
}
AndroidManifest. xml
PS: run this program in the real machine environment. If it is run in the simulator, an error may be reported:
java.lang.RuntimeException: Cannot initialize Visualizer engine, error: -4