本文对Android音频播放做简单总结。

一.基本概念

(一)Android媒体格式

     Android提供了对常用音频和视频格式的支持,它所支持的音频格式有MP3,3GP,OGG,和 WAVE(.ave)等,支持的视频格式有3GP和MPEG-4等,通过Android API提供的方法,可以实现音 频与视频的播放。
     播放SD卡中的一个音频文件 ,注意权限的添加:
Android.permission.READ_EXTERNAL_STORAGE

(二)播放音频文件,一般使用的是:MediaPlayer类或SoundPool类。

二.MediaPlayer的使用总结

Media Player可以使用静态方法创建也可以使用无参构造的方法来创建。

(一)静态方法创建MediaPlayer对象

使用create方法来绑定资源

1.用于从资源ID对应的资源文件中装载音频,并返回新创建的MediaPlayer对象

MediaPlayer player=MediaPlayer.create(this,R.raw.d);
这里的资源文件是放在res/raw目录下的

2.用于根据指定的URI来装载音频,并返回新创建的MediaPlayer对象

MediaPlayer player=
MediaPlayer.create(this,Uri,pares(“http://www.music.baidu.com/sou nd/mylove.mp3”));

     但是,注意,这里的访问网络上的资源时,要在AndroidManifest.xml文件中授予该程序访问网络 的权限,具代码如下

(二)根据无参数的构造方法,创建MediaPlayer对象

用setDataSource()方法实现资源的绑定。
具体代码:

MediaPlayer player=new MediaPlayer(); 
player.setDataSource(“/sdcard/s.wav”); 
player.prepare();

使用构造方法创建的MediaPlayer必须预加载音频。才能去播放文件。

(三)MediaPlayer的常用方法

1.setDataSource() //设置 资源 
 2.start() throws IllegalStateException //开 始播放,如果没有预编译错过会报错 
 3.stop() //停 止播放 
 4.pause() //暂 停播放 
 5.prepare()//同步加载 
 6.prepareAsync()// 异步加载,完成后调用监听 
 7.isPlaying(); //本 地方法,判断播放状态 
 8.seekTo(int msec) //本地方法,跳转到时间点 
 9.int getDuration(); //本 地方法,获取音乐总长度 
 10.release() //释 放资源 
 11.reset() //重 置MediaPlayer 
 12.setLooping(boolean looping) //设 置循环 
 13.public native boolean isLooping(); //判 断循环状态 
 14.setVolume(float leftVolume, float rightVolume) 
 / /设置音量,左声道和右声道 
 15.setVolume(float volume) //设 置音量

同步与异步的区别 同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;异步是指进程不需 要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统 会通知进程进行处理,这样可以提高执行的效率。

(四)MediaPlayer的几个监听方式

1. 异步加载完成时回调监听

public void setOnPreparedListener(OnPreparedListener listener)

异步监听,一般在异步预加载之前就要设置好。

2.音乐播放完毕后回调监听

public void setOnCompletionListener(OnCompletionListener listener)

一般用于设置播放完毕后,播放下一首还是循环播放

3.当跳转完成时的监听

public void setOnSeekCompleteListener(OnSeekCompleteListener listener)

一般用于监听进度突然改变的值的变化

三.SoundPool的使用总结

     由于MediaPlayer占用资源较高,且不支持同时播放多个音频,所有Android为我们提供了另一个播放音频的类SounePool.SoundPool也就是音频池,它可以同时播放多个短促的音频,而且占用的资源少,例如游戏中的多个音频同时播放。
Sound Pool的音频文件也是放在res/raw/文件夹下面
使用SoundPool播放音频的步骤如下:

(一)创建SoundPool对象

new SoundPool(int maxStreams,int streamType,int srcQuality)
     其中,maxStreams用来指定可以容纳多少个音频,streamType用来指定声音类型,可以通过 AudioManager类提供的常量进行指定,通常使用STREAM_MUSIC; srcQuality参数用于指定音频的品质,0为默认

(二)加载所要播放的音频,四种方式

1. Public int load(Context context,int resid,int priority)

用于通过指定的资源来加载音频

2.Public int load(String path,int priority)

用于通过音频文件的路径来加载音频

3.Public int load(AssetFileDescriptor afd,int priority)

用于从AssetFileDescriptor所对应的文件中加载音频

4. Public int load(FileDescriptor fd,long offset,long length,int priority )

用于加载FileDescriptor对象中,从offset开始,长度为length的音频

(三)控制播放音频

play (int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)

6个参数的说明:

1)soundID 用于指定要播放的音频,该音频为通过load()方法返回的音频 
 2)leftVolume 用于指定左声道的音量,取值为0-1.0 
 3)rightVolume 用于指定左声道的音量,取值为0-1.0 
 4) Priority 用于指定播放音频的优先级,数值越大,优先级越高 
 5)Loop 用于设置循环次数0为不循环-1为循环 
 6)Rate 用于指定速率,1为正常,最小值为0.5 最高值为2

     上面就是Android音频使用的类和常用的方法,下面结合实际,做两个简单的示例。

四.使用MediaPlayer来控制文件的播放和停止

程序功能实现:
1.能播放固定的音频文件资源
2.能控制音频文件的暂停和停止,开始
3.能够使用进度拖动条来控制音频文件的播放
     对于其他的一些复杂功能,暂时没有去实现。
这里的使用的是使用的静态方法和无参的构造方法来控制音频文件,实际应用中只使用一种就可以了。
     本程序上面两个按钮和下面两个按钮不能混用,一次程序运行中只能使用静态或者无参的对象来控制音频文件,这里只为了对比功能才放到一起的。

(一)xml文件的设计

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.lwz.mediaplayer.MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="音频播放"
        android:textSize="20sp" />

    <SeekBar
        android:id="@+id/main_sb"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/main_tv_pro"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:text="00:00/00:00"
        android:textSize="15sp" />

    <Button
        android:id="@+id/main_btn_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="start"
        android:text="播放" />

    <Button
        android:id="@+id/main_btn_stop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="stop"
        android:text="停止" />

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#f00" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=" 动态使用音频对象" />

    <Button
        android:id="@+id/main_btn_start2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="start2"
        android:text="播放" />

    <Button
        android:id="@+id/main_btn_stop2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="stop2"
        android:text="停止" />

</LinearLayout>

(二)java代码的设计

package com.lwz.mediaplayer;
import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;

/***
 * 音频文件的使用
 * 静态方法:播放的是程序本身的音频文件在/src/raw/文件夹下
 * 动态方法:播放的是存储器的音频文件
 */
public class MainActivity extends Activity implements SeekBar.OnSeekBarChangeListener {
    //定义2个音频播放的对象
    MediaPlayer mediaPlayer;
    MediaPlayer mediaPlayer2;
    //定义布局内的按钮控件
    Button start;
    Button stop;
    Button start2;
    Button stop2;
    SeekBar seekBar;
    TextView tv_time;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //实例化布局内的控件
        start = (Button) findViewById(R.id.main_btn_start);
        stop = (Button) findViewById(R.id.main_btn_stop);
        start2 = (Button) findViewById(R.id.main_btn_start2);
        stop2 = (Button) findViewById(R.id.main_btn_stop2);
        seekBar = (SeekBar) findViewById(R.id.main_sb);
        tv_time = (TextView) findViewById(R.id.main_tv_pro);
        //设置停止按钮点无效
        stop.setEnabled(false);
        stop2.setEnabled(false);
        //给SeekBar设置监听事件
        seekBar.setOnSeekBarChangeListener(this);
    }

    public void start(View v) {


        if (mediaPlayer == null) {
            //如果音频文件对象是空的就实例化数据
            mediaPlayer = MediaPlayer.create(this, R.raw.arcs);
            //设置可以点击停止
            stop.setEnabled(true);
        }
        //如果还没有开始播放,就播放音频,显示“暂停”
        if (!mediaPlayer.isPlaying()) {
            mediaPlayer.start();
            start.setText("暂停");
            //开始计算
            startTime();
            //设置进度条的最大值
            seekBar.setMax(mediaPlayer.getDuration() / 1000);
            //设置一直循环
            mediaPlayer.setLooping(true);
        } else {
            //如果正在显示“暂停”时,单击暂停,就暂停音频,显示“播放”
            mediaPlayer.pause();
            start.setText("播放");
            //取消定时器
            timer.cancel();
        }
    }

    public void start2(View v) {
        if (mediaPlayer2 == null) {
            //如果音频文件对象是空的就实例化数据
            //动态使用音频对象和静态使用使用区别的,这里直接new出来
            mediaPlayer2 = new MediaPlayer();
            try {
                //给音频对象设置资源地址
                mediaPlayer2.setDataSource(Environment.getExternalStorageDirectory().getAbsolutePath() + "/laolang.mp3");
                //这里要预加载
                mediaPlayer2.prepare();
            } catch (IOException e) {
                e.printStackTrace();
            }
            //设置可以点击停止
            stop2.setEnabled(true);
        }
        //如果还没有开始播放,就播放音频,显示“暂停”
        if (!mediaPlayer2.isPlaying()) {
            mediaPlayer2.start();
            start2.setText("暂停");
            //开始计算
            startTime();
            //设置进度条的最大值
            seekBar.setMax(mediaPlayer2.getDuration() / 1000);
            //设置一直循环
            mediaPlayer2.setLooping(true);
        } else {
            //如果正在显示“暂停”时,单击暂停,就暂停音频,显示“播放”
            mediaPlayer2.pause();
            start2.setText("播放");
            //取消定时器
            timer.cancel();
        }

    }

    /**
     * 时间开始跑
     * 这里设置一个Timer对象来控制时间
     */
    Timer timer;
    //转换时间的一个对象
    SimpleDateFormat format = new SimpleDateFormat("mm:ss");

    private void startTime() {
        timer = new Timer();
        //一秒之后每秒执行一次的任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                //这里要在主线程执行
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        //进度条每次加一
                        seekBar.setProgress(seekBar.getProgress() + 1);
                        //获取当前播放的时间,单位毫秒秒
                        int time = seekBar.getProgress() * 1000;
                        //把显示进度的文字显示到屏幕上
                        if(mediaPlayer2!=null)
                        tv_time.setText(format.format(new Date(time)) + "/" + format.format(new Date(mediaPlayer2.getDuration())));
                        if(mediaPlayer!=null)
                        tv_time.setText(format.format(new Date(time)) + "/" + format.format(new Date(mediaPlayer.getDuration())));
                        //结束后重新开始,进度条
                        if (seekBar.getProgress() == seekBar.getMax()) {
                            seekBar.setProgress(0);
                        }
                    }
                });

            }
        }, 1000, 1000);


    }

    public void stop(View v) {
        //这里是停止不是暂停
        recyle();

    }

    public void stop2(View v) {
        //这里是停止不是暂停
        recyle2();

    }

    /**
     * 彻底释放资源
     */
    public void recyle() {
        //判断非空才需做处理
        if (mediaPlayer != null) {
            //保证音频停止
            if (mediaPlayer.isPlaying()) {
                mediaPlayer.stop();
            }
            //回收资源
            mediaPlayer.release();
            //设置对象为空
            mediaPlayer = null;
            //设置显示文本为播放
            start.setText("播放");
            //设置禁止点击"停止"
            stop.setEnabled(false);
        }
        //取消时间计数器
        timer.cancel();
        //设置进度条的位置为开始的地方
        seekBar.setProgress(0);
    }

    /**
     * 彻底释放资源
     */
    public void recyle2() {
        //判断非空才需做处理
        if (mediaPlayer2 != null) {
            //保证音频停止
            if (mediaPlayer2.isPlaying()) {
                mediaPlayer2.stop();
            }
            //回收资源
            mediaPlayer2.release();
            //设置对象为空
            mediaPlayer2 = null;
            //设置显示文本为播放
            start2.setText("播放");
            //设置禁止点击"停止"
            stop2.setEnabled(false);
        }
        //取消时间计数器
        timer.cancel();
        //设置进度条的位置为开始的地方
        seekBar.setProgress(0);
    }

    @Override
    protected void onDestroy() {
        //页面关闭前先彻底释放音频的资源
        recyle();
        recyle2();
        super.onDestroy();

    }

    //定义一个布尔值判断是否是用户的操作的进度条
    boolean fromUser;

    //当进度条发送生改变时回调
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        this.fromUser = fromUser;
    }

    //进度条操作开始时回调
    @Override
    public void onStartTrackingTouch(SeekBar seekar) {
        fromUser = false;
    }

    //进度条操作停止时回调
    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        //如果是用户拖动的进度条才做对应的时间跳转
        if (fromUser) {
            //把音频播放时间设置到拖动的位置
            int time = seekBar.getProgress() * 1000;
            if (mediaPlayer != null) {
                mediaPlayer.seekTo(time);
            }


        }
    }
}

程序运行前要保证资源文件的存在;
这里音频文件的停止和退出页面一定要释放资源。
程序运行后的效果:

android 播放本地语音 android播放音频文件_android


点击播放后效果:

android 播放本地语音 android播放音频文件_Android音频播_02


播放时点击暂停,就停止音频播放,如图所示:

android 播放本地语音 android播放音频文件_Android音频播_03


上面的动态使用音频对象功能也是一样的。

这里就可以实现对音频文件的基本控制了。

五.SoundPool播放音频的简单示例

     本示例功能比较简单,根据五种小音频文件,对应的设置五个按钮来触发播放,程序中可以同时播放多个音频,比如上一个音频文件没有播放完,下一个音频文件也可以播放。

(一)xml布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="play"
        android:text="play" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="play1"
        android:text="play1" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="play2"
        android:text="play2" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="play3"
        android:text="play3" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="play4"
        android:text="play4" />


</LinearLayout>

(二)java代码文件

package com.lwz.soundpool;

import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

import java.util.HashMap;

/**
 * 使用SoundPool播放音频,可以同时播放多个音频
 * 但是主要是播放时间比较短的音频
 * 它的文件资源也是一般放在本地中
 */
public class MainActivity extends AppCompatActivity {
    SoundPool soundPool;
    //创建一个集合存放音频数据
    HashMap<Integer, Integer> map = new HashMap<>();

    /*
    创建SoundPool对象
    1. SoundPool(int maxStreams,int streamType,int srcQuality)
    2. //其中,maxStreams用来指定可以容纳多少个音频,streamType用来指定声音类型,可以通过
    AudioManager类提供的常量进行指定,通常使用STREAM_MUSIC; srcQuality参数用于指定
    音频的品质,0为默认
    * */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //创建音频对象
        soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
        //给集合设置数据
        map.put(1, soundPool.load(this, R.raw.chimes, 100));
        map.put(2, soundPool.load(this, R.raw.ding, 100));
        map.put(3, soundPool.load(this, R.raw.enter, 100));
        map.put(4, soundPool.load(this, R.raw.notify, 100));
        map.put(5, soundPool.load(this, R.raw.ringout, 100));

    }

    /*
    * play (int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
    2. 参数说明:
    3. 参数 描述
    4. soundID 用于指定要播放的音频,该音频为通过load()方法返回的音频
    5. leftVolume 用于指定左声道的音量,取值为0-1.0
    6. rightVolume 用于指定左声道的音量,取值为0-1.0
    7. priority 用于指定播放音频的优先级,数值越大,优先级越高
    8. loop 用于设置循环次数0为不循环-1为循环
    9. rate 用于指定速率,1为正常,最小值为0.5 最高值为2
    * */
    public void play(View v) {
        soundPool.play(map.get(1), 1, 1, 100, 2, 1);
    }

    public void play1(View v) {
        soundPool.play(map.get(2), 1, 1, 100, 2, 1);
    }

    public void play2(View v) {
        soundPool.play(map.get(3), 1, 1, 100, 2, 1);
    }

    public void play3(View v) {
        soundPool.play(map.get(4), 1, 1, 100, 2, 1);
    }

    public void play4(View v) {
        soundPool.play(map.get(5), 1, 1, 100, 2, 1);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        soundPool.release();
    }
}

程序运行后的结果:

android 播放本地语音 android播放音频文件_Android音频播_04


这里没有办法展示声音效果!

     对比这两个音频控制文件:
     MediaPlayer是可以播放长时间的音频文件,Sound Pool是用来播放短时间的音频文件的;
Media Player和SoundPool控制的音频文件都可以放在资源文件夹res/raw/下面,也可以使用存储器中的音频资源文件,但是SoundPool控制的文件一般比较下,放在/raw目录中即可。