Android 实现多个Audio文件的顺序播放

最近开发Android通讯录播报号码时需用到播放多个连续音频文件,用传统的MediaManager无法满足复杂性需求,而 SoundPool则满足,于是写了一个SoundPool的管理类SoundManager管理音频的播放。

代码如下:

package com.yous365.android.util;

import org.xml.sax.SAXException;

import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Handler;
import android.util.Log;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import javax.xml.parsers.ParserConfigurationException;

public class SoundManager {

    private SoundPool mSoundPool;

    private HashMap<String, Integer> mSoundPoolMap;

    private AudioManager mAudioManager;

    private Context mContext;

    private Handler mHandler = new Handler();

    private Vector<Integer> mKillSoundQueue = new Vector<Integer>();

    private VoicePhoneNumberUtil util;

    private long delay = 1000;

    private long seperateTime = 700;

    private float rate = 1.0f;

    private String locale;

    static private SoundManager _instance;

    /**
     * Requests the instance of the Sound Manager and creates it if it does not
     * exist.
     *
     * @return Returns the single instance of the SoundManager
     */
    static synchronized public SoundManager getInstance() {
        if (_instance == null)
            _instance = new SoundManager();
        return _instance;
    }

    /**
     *
     */
    private SoundManager() {
        util = VoicePhoneNumberUtil.getInstance();
    }

    /**
     * Initialises the storage for the sounds
     *
     * @param theContext The Application context
     */

    public void initSounds(Context theContext) {
        mContext = theContext;
        mSoundPool = new SoundPool(1,
                AudioManager.STREAM_MUSIC, 0);
        mSoundPoolMap = new HashMap<String, Integer>();
        mAudioManager = (AudioManager) mContext
                .getSystemService(Context.AUDIO_SERVICE);
    }

    /**
     * Add a new Sound to the SoundPool
     *
     * @param key - The Sound Index for Retrieval
     * @param SoundID - The Android ID for the Sound asset.
     */

    public void addSound(String key, int SoundID) {
        mSoundPoolMap.put(key,
                mSoundPool.load(mContext, SoundID, 1));
    }
    /**
     *
     * @param key the key we need to get the sound later
     * @param afd  the fie store in the asset
     */
    public void addSound(String key, AssetFileDescriptor afd) {
        mSoundPoolMap.put(key, mSoundPool.load(
                afd.getFileDescriptor(),
                afd.getStartOffset(), afd.getLength(), 1));
    }
   
    /**
     * play the sound loaded to the SoundPool by the key we set
     * @param key  the key in the map
     */
    public void playSound(String key) {

        int streamVolume = mAudioManager
                .getStreamVolume(AudioManager.STREAM_MUSIC);
        streamVolume = streamVolume
                / mAudioManager
                        .getStreamMaxVolume(AudioManager.STREAM_MUSIC);

        int soundId = mSoundPool.play(
                mSoundPoolMap.get(key), streamVolume,
                streamVolume, 1, 0, rate);
        mKillSoundQueue.add(soundId);
        // schedule the current sound to stop after set milliseconds
        mHandler.postDelayed(new Runnable() {
            public void run() {
                if (!mKillSoundQueue.isEmpty()) {
                    mSoundPool.stop(mKillSoundQueue
                            .firstElement());
                }
            }
        }, delay);

    }
   
    /**
     *
     * @param key  the key in the map
     */
    public void playLoopedSound(String key) {

        int streamVolume = mAudioManager
                .getStreamVolume(AudioManager.STREAM_MUSIC);
        streamVolume = streamVolume
                / mAudioManager
                        .getStreamMaxVolume(AudioManager.STREAM_MUSIC);
       
        int soundId = mSoundPool.play(
                mSoundPoolMap.get(key), streamVolume,
                streamVolume, 1, -1, rate);

        mKillSoundQueue.add(soundId);
        // schedule the current sound to stop after set milliseconds
        mHandler.postDelayed(new Runnable() {
            public void run() {
                if (!mKillSoundQueue.isEmpty()) {
                    mSoundPool.stop(mKillSoundQueue
                            .firstElement());
                }
            }
        }, delay);
    }
   
  /**
   * play the sounds have loaded in SoundPool
   * @param keys the files key stored in the map
   * @throws InterruptedException
   */
    public void playMutilSounds(String keys[])
            throws InterruptedException {
        int streamVolume = mAudioManager
                .getStreamVolume(AudioManager.STREAM_MUSIC);
        streamVolume = streamVolume
                / mAudioManager
                        .getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        for (String key : keys) {
            Log.d("playMutilSounds", key);
            if (mSoundPoolMap.containsKey(key)) {
                int soundId = mSoundPool.play(
                        mSoundPoolMap.get(key),
                        streamVolume, streamVolume, 1, 0,
                        rate);
                //sleep for a while for SoundPool play
                Thread.sleep(seperateTime);
                mKillSoundQueue.add(soundId);
            }

        }

        // schedule the current sound to stop after set milliseconds
        mHandler.postDelayed(new Runnable() {
            public void run() {
                if (!mKillSoundQueue.isEmpty()) {
                    mSoundPool.stop(mKillSoundQueue
                            .firstElement());
                }
            }
        }, delay);

    }

    /**
     * Loads the various sound assets
     * @param locale the load to load audio files
     */

    public void loadSounds(String locale)
            throws SAXException, IOException,
            ParserConfigurationException {
        Log.d("load locale", locale);
        this.locale = locale;
        AssetFileDescriptor afd;
        Map<String, String> audioFiles = util
                .getAudioFileConfig(locale,
                        mContext.getAssets());

        for (String key : audioFiles.keySet()) {
            afd = mContext.getAssets().openFd(
                    audioFiles.get(key));
            addSound(key, afd);
        }

    }

    /**
     * Stop a Sound
     *
     * @param index - index of the sound to be stopped
     */
    public void stopSound(int index) {
        mSoundPool.stop(mSoundPoolMap.get(index));
    }

    /**
     * Deallocates the resources and Instance of SoundManager
     */
    public void cleanup() {
        mSoundPool.release();
        mSoundPool = null;
        mSoundPoolMap.clear();
        mAudioManager.unloadSoundEffects();
        _instance = null;

    }

    /**
     * unload all resource in the sound pool
     * support for user change VoiceLanguage or Locale or user close the voice function !
     */

    public void unloadAllSoundsIn() {
        if (mSoundPoolMap.size() > 0) {
            for (String key : mSoundPoolMap.keySet()) {
                mSoundPool.unload(mSoundPoolMap.get(key));
            }
        }
        mKillSoundQueue.clear();
        mSoundPoolMap.clear();
    }

    /**
     * set the speed of soundPool
     *
     * @param i i<0 means slow i= 0 means normal i>0 means fast
     */
    public void setVoiceSpeed(int i) {
        if (i > 0) {
            rate = 1.2f;
        }
        else if (i < 0) {
            rate = 0.8f;
        }
        else {
            rate = 1.0f;
        }

    }

    /**
     * set the delay after one number's sound have played
     *
     * @param i i<0 means short i= 0 means normal i>0 means long
     */
    public void setVoiceDelay(int i) {
        if (i > 0) {
            seperateTime = 700;
        }
        else if (i < 0) {
            seperateTime = 400;
        }
        else {
            seperateTime = 500;
        }

    }

    public String getLocale() {
        return locale;
    }

    public void setLocale(String locale) {
        this.locale = locale;
    }

}