SoundPool簡單使用與頻繁播報同一語音被覆蓋問題

SoundPool對象

新版本已經不建議使用默認構造方法去實例化SoundPool對象了;

/*
* @deprecated use {@link SoundPool.Builder} instead to create and configure a
*     SoundPool instance
*/
public SoundPool(int maxStreams, int streamType, int srcQuality)

下面通過Builder模式構建SoundPool對象

SoundPool.Builder builder = new SoundPool.Builder();
//傳入單次最多播放音頻流數量,
builder.setMaxStreams(1);
//AudioAttributes封裝音頻屬性的類
AudioAttributes.Builder attrBuilder = new AudioAttributes.Builder();
//設置音頻流的媒體類型,
//因爲我後臺有廣告使用media播放,所以這類不用STREAM_MUSIC避免衝突
attrBuilder.setLegacyStreamType(AudioManager.STREAM_ALARM);
//設置AudioAttributes到構造器上
builder.setAudioAttributes(attrBuilder.build());
//構造出相應的對象
soundPool = builder.build();

加載音頻

// priority 優先級,看註解應該是爲將來兼容性考慮,直接設置1就行

//通過音頻路徑path加載
public int load(String path, int priority)
//通過資源raw加載
public int load(Context context, int resId, int priority)
//通過asset資源加載
public int load(AssetFileDescriptor afd, int priority)
//通過文件描述符加載,上述三個方法最終都是調用該方法進行具體實現
public int load(FileDescriptor fd, long offset, long length, int priority)

我這邊音頻比較多,還分國家的,所以我使用了asset,保持了目錄結構。

//sb對象是用StringBuilder拼接的文件目錄加文件名
AssetFileDescriptor assetFileDescriptor = activity.getResources().getAssets().openFd(sb.toString());
//此處獲取到的voiceId 需要保存,後面播放的時候進行使用
int voiceId = soundPool.load(assetFileDescriptor, 1);

注意 加載完成後不能立刻進行播放,否則播放不出聲音。

通過設置回調方法監聽加載音頻是否完成,來判斷是否進行音頻播報。

soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
	@Override
	public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
		//加載音頻完成
	}
});

音頻播報

只有一個播放函數,各個參數含義看註解

//第一個參數音頻id,是前面load方法返回值
//第二個參數leftVolume爲左聲道音量值(範圍= 0.0到1.0)
//第三個參數rightVolume爲右聲道音量值(範圍= 0.0到1.0)
//第四個參數priority 爲優先級
//第五個參數loop 爲音頻重複播放次數,0爲值播放一次,-1爲無限循環,其他值爲播放loop+1次
//第六個參數 rate爲播放的速率,範圍0.5-2.0(0.5爲一半速率,1.0爲正常速率,2.0爲兩倍速率)
soundPool.play(voiceId, 1, 1, 1, 0, 1);

播報被覆蓋問題

音頻播報會有一個問題:頻繁播放同一個voiceId的時候,上一次沒播放完,下一次再播放會中斷上次的播放再播放,要是太過頻繁會導致音頻反覆被覆蓋,播放體驗不佳。
解決辦法:
音頻流都是通過AudioManager進行管理的,發現AudioManager中有isMusicActive可以判斷AudioManager.STREAM_MUSIC是否在播放。

/**
 * Checks whether any music is active.
 *
 * @return true if any music tracks are active.
 */
public boolean isMusicActive() {
    return AudioSystem.isStreamActive(STREAM_MUSIC, 0);
}

由於我使用的不是AudioManager.STREAM_MUSIC,而是AudioManager.STREAM_ALARM。AudioManager中沒有相關方法,所以我們可以直接通過AudioSystem.isStreamActive()方法去實現。而AudioSystem是一個hide註解類,isStreamActive是一個靜態方法,所以我們需要使用反射。

private boolean isSoundPoolPlay() {
    //通過反射判斷STREAM_ALARM類型的音頻流是否在執行
    //目標方法:AudioSystem.isStreamActive(AudioManager.STREAM_ALARM, 0);
    Class<?> threadClazz = null;
    try {
        threadClazz = Class.forName("android.media.AudioSystem");
        Method method = threadClazz.getMethod("isStreamActive", int.class, int.class);
        return (Boolean) method.invoke(null, AudioManager.STREAM_ALARM, 0);
    } catch (ClassNotFoundException | NoSuchMethodException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return false;
}

資源釋放

最後再進行資源釋放

if(soundPool!=null){
    soundPool.autoPause();
    for (Integer voiceId: soundPoolMap.values()) {
	    //將前面通過load獲取到的voiceId進行unload
        soundPool.unload(voiceId);
    }
    soundPool.release();
    soundPool = null;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章