MediaPlayer来播放音频文件:
资源占用量较高、延迟时间较长、不支持多个音频同时播放等。
SoundPool来播放音乐:
1.SoundPool最大只能申请1M的内存空间,这就意味着我们只能使用一些很短的声音片段,而不是用它来播放歌曲或者游戏背景音乐(背景音乐可以考虑使用JetPlayer来播放)。
2.SoundPool提供了pause和stop方法,但这些方法建议最好不要轻易使用,因为有些时候它们可能会使你的程序莫名其妙的终止。还有些朋友反映它们不会立即中止播放声音,而是把缓冲区里的数据播放完才会停下来,也许会多播放一秒钟。
3.音频格式建议使用OGG格式。使用WAV格式的音频文件存放游戏音效,经过反复测试,在音效播放间隔较短的情况下会出现异常关闭的情况(有说法是SoundPool目前只对16bit的WAV文件有较好的支持)。后来将文件转成OGG格式,问题得到了解决。
4.在使用SoundPool播放音频的时候,如果在初始化中就调用播放函数进行播放音乐那么根本没有声音,不是因为没有执行,而是SoundPool需要一准备时间!囧。当然这个准备时间也很短,不会影响使用,只是程序一运行就播放会没有声音罢了。
对此我们对游戏开发中到底需要用什么来做进行了分析,总结就是SoundPool适合做特效声,其实播放背景音乐我感觉还是用MediaPlayer比较好。
注意:
这里要强调一下,调整音频是用这个音频管理器调用setStreamVolume()的方式去调整,而不是MediaPlayer.setVolue(intLeftVolume,int RightVolume);这个方法的两个参数也是调正左右声道而不是调节声音大小。
package com.example.zhangying.simplemusicplayer;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.SoundPool;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.HashMap;
/**
* 下面对以上进行总结:
*
* 一个SoundPool可以:
* 1.管理多个音频资源,通过load()函数,成功则返回非0的soundID;
* 2.同时播放多个音频,通过play()函数,成功则返回非0的streamID;
* 3.pause()、resume()和stop()等操作是针对streamID(播放流)的;
* 4.当设置为无限循环时,需要手动调用stop()来终止播放;
* 5.播放流的优先级(play()中的priority参数),只在同时播放数超过设定的最大数时起作用;
* 6.程序中不用考虑(play触发的)播放流的生命周期,无效的soundID/streamID不会导致程序错误。
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button button1;//使用MediaPlayer播放声音
private Button button2;//暂停MediaPlayer声音
private Button button3;//使用SoundPool播放声音
private Button button4;//暂停SoundPool声音
private TextView textView;//显示文本信息
private MediaPlayer mediaPlayer;//
/**
* SoundPool初始化的过程是异步的,也就是说对SoundPool初始化时系统会自动启动一个后台线程来完成初始化工作。
* 因此,并不影响前台其他程序的运行,但也带来了一个问题,调用初始化操作后不能立即播放,需要等待一下,否则 可能会出错。
* SoundPool可以同时播放多个音频文件,但MediaPlayer同一时间却只能播放一个
*/
private SoundPool soundPool;
private HashMap soundPoolMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initSounds();//初始化声音
setContentView(R.layout.activity_main);
initView();
initListener();
}
/**
* 初始化声音方法
*/
private void initSounds() {
/**
* 两个参数:1.上下文。2.音频资源的id(R.raw.)
*如果是播放指定sd卡上的音乐的话使用: mediaPlayer.setDataSource();
* 如果是应用中自带的音乐(如游戏中的音效),就不是sdcard中的音乐了,一般情况下会将音频资源存放在应用目录下的''res\raw\'下,并通过create()方法返回一个MediaPlayer对象。
*/
mediaPlayer = MediaPlayer.create(this, R.raw.backsound);//初始化MediaPlayer
/**
*public SoundPool(int maxStream, int streamType, int srcQuality)
*maxStream —— 同时播放的流的最大数量
*streamType —— 流的类型,一般为STREAM_MUSIC(具体在AudioManager类中列出)
*srcQuality —— 采样率转化质量,当前无效果,使用0作为默认值,它表示音质,它也可以设置为100
*/
soundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);//
soundPoolMap = new HashMap();
/**
* API中指出,其中第三个参数priority参数目前没有效果,建议设置为1。
* soundPool.load(this, R.raw.dingdong, 1);加载音频资源
* 一个SoundPool能同时管理多个音频,所以可以通过多次调用load函数来记载,如果记载成功将返回一个非0的soundID ,用于播放时指定特定的音频。
* 需要注意的是,
*流的加载过程是一个将音频解压为原始16位PCM数据的过程,由一个后台线程来进行处理异步,所以初始化后不能立即播放,需要等待一点时间。
*
*/
soundPoolMap.put(1, soundPool.load(this, R.raw.dingdong, 1));
}
private void initListener() {
button1.setOnClickListener(this);
button2.setOnClickListener(this);
button3.setOnClickListener(this);
button4.setOnClickListener(this);
}
private void initView() {
textView = (TextView) findViewById(R.id.textView);
button1 = (Button) findViewById(R.id.button1);
button2 = (Button) findViewById(R.id.button2);
button3 = (Button) findViewById(R.id.button3);
button4 = (Button) findViewById(R.id.button4);
}
/**
* 使用soundPool播放音乐的方法
* leftVolume left volume value (range = 0.0 to 1.0)
* rightVolume right volume value (range = 0.0 to 1.0)
*/
public void playSound(int sound, int loop) {
AudioManager mgr = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
float streamVolume = mgr.getStreamVolume(AudioManager.STREAM_MUSIC);//当前音量
float streamMaxVolume = mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);//最大音量(获得当前手机最大铃声)
float volume = streamVolume / streamMaxVolume;
/**
* final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
*播放指定音频的音效,并返回一个streamID 。
*priority —— 流的优先级,值越大优先级高,影响当同时播放数量超出了最大支持数时SoundPool对该流的处理;
*loop —— 循环播放的次数,0为值播放一次,-1为无限循环,其他值为播放loop+1次(例如,3为一共播放4次).
*rate —— 播放的速率,范围0.5-2.0(0.5为一半速率,1.0为正常速率,2.0为两倍速率)
* volume设置为1的话应该是最大的音量了。
*/
soundPool.play(soundPoolMap.get(sound), volume, volume, 1, loop, 1f);//播放声音
}
@Override
public void onClick(View v) {
if (v == button1) {
textView.setText("使用MediaPlayer播放声音");
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();//播放音乐
}
} else if (v == button2) {
textView.setText("暂停MediaPlayer声音");
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
} else if (v == button3) {//点击了使用SoundPool播放声音按钮
textView.setText("点击了使用SoundPool播放声音按钮");
this.playSound(1, 0);//使用SoundPool播放声音按钮
} else if (v == button4) {
textView.setText("点击了暂停SoundPool声音按钮");
soundPool.pause(1);//暂停SoundPool声音
}
}
}
布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="没有任何声音" />
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="使用MediaPlayer播放声音" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="暂停MediaPlayer声音" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="使用SoundPool播放声音"/>
<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="暂停SoundPool声音"/>
</LinearLayout>