SimpleAudioEngine:Fixed thread safety problem on android.

This commit is contained in:
WenhaiLin 2015-06-09 15:07:35 +08:00
parent a364d6ecfc
commit 6fec9c40b0
1 changed files with 27 additions and 57 deletions

View File

@ -29,15 +29,14 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.Semaphore; import java.util.concurrent.ConcurrentHashMap;
import com.chukong.cocosplay.client.CocosPlayClient;
import android.content.Context; import android.content.Context;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.SoundPool; import android.media.SoundPool;
import android.util.Log; import android.util.Log;
import com.chukong.cocosplay.client.CocosPlayClient;
public class Cocos2dxSound { public class Cocos2dxSound {
// =========================================================== // ===========================================================
// Constants // Constants
@ -61,10 +60,8 @@ public class Cocos2dxSound {
private final HashMap<String, Integer> mPathSoundIDMap = new HashMap<String, Integer>(); private final HashMap<String, Integer> mPathSoundIDMap = new HashMap<String, Integer>();
private final ArrayList<SoundInfoForLoadedCompleted> mEffecToPlayWhenLoadedArray = new ArrayList<SoundInfoForLoadedCompleted>(); private ConcurrentHashMap<Integer, SoundInfoForLoadedCompleted> mPlayWhenLoadedEffects =
new ConcurrentHashMap<Integer, SoundInfoForLoadedCompleted>();
private int mStreamIdSyn;
private Semaphore mSemaphore;
private static final int MAX_SIMULTANEOUS_STREAMS_DEFAULT = 5; private static final int MAX_SIMULTANEOUS_STREAMS_DEFAULT = 5;
private static final int MAX_SIMULTANEOUS_STREAMS_I9100 = 3; private static final int MAX_SIMULTANEOUS_STREAMS_I9100 = 3;
@ -86,7 +83,7 @@ public class Cocos2dxSound {
} }
private void initData() { private void initData() {
if (Cocos2dxHelper.getDeviceModel().indexOf("GT-I9100") != -1) { if (Cocos2dxHelper.getDeviceModel().contains("GT-I9100")) {
this.mSoundPool = new SoundPool(Cocos2dxSound.MAX_SIMULTANEOUS_STREAMS_I9100, AudioManager.STREAM_MUSIC, Cocos2dxSound.SOUND_QUALITY); this.mSoundPool = new SoundPool(Cocos2dxSound.MAX_SIMULTANEOUS_STREAMS_I9100, AudioManager.STREAM_MUSIC, Cocos2dxSound.SOUND_QUALITY);
} }
else { else {
@ -97,22 +94,8 @@ public class Cocos2dxSound {
this.mLeftVolume = 0.5f; this.mLeftVolume = 0.5f;
this.mRightVolume = 0.5f; this.mRightVolume = 0.5f;
this.mSemaphore = new Semaphore(0, true);
} }
// ===========================================================
// Getter & Setter
// ===========================================================
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
// ===========================================================
// Methods
// ===========================================================
public int preloadEffect(final String path) { public int preloadEffect(final String path) {
if (CocosPlayClient.isEnabled() && !CocosPlayClient.isDemo()) { if (CocosPlayClient.isEnabled() && !CocosPlayClient.isDemo()) {
CocosPlayClient.updateAssets(path); CocosPlayClient.updateAssets(path);
@ -149,6 +132,8 @@ public class Cocos2dxSound {
} }
} }
private static int LOAD_TIME_OUT = 500;
public int playEffect(final String path, final boolean loop, float pitch, float pan, float gain){ public int playEffect(final String path, final boolean loop, float pitch, float pan, float gain){
Integer soundID = this.mPathSoundIDMap.get(path); Integer soundID = this.mPathSoundIDMap.get(path);
int streamID = Cocos2dxSound.INVALID_STREAM_ID; int streamID = Cocos2dxSound.INVALID_STREAM_ID;
@ -157,7 +142,7 @@ public class Cocos2dxSound {
// parameters; pan = -1 for left channel, 1 for right channel, 0 for both channels // parameters; pan = -1 for left channel, 1 for right channel, 0 for both channels
// play sound // play sound
streamID = this.doPlayEffect(path, soundID.intValue(), loop, pitch, pan, gain); streamID = this.doPlayEffect(path, soundID, loop, pitch, pan, gain);
} else { } else {
// the effect is not prepared // the effect is not prepared
soundID = this.preloadEffect(path); soundID = this.preloadEffect(path);
@ -166,21 +151,19 @@ public class Cocos2dxSound {
return Cocos2dxSound.INVALID_SOUND_ID; return Cocos2dxSound.INVALID_SOUND_ID;
} }
// only allow one playEffect at a time, or the semaphore will not work correctly SoundInfoForLoadedCompleted info = new SoundInfoForLoadedCompleted(path, loop, pitch, pan, gain);
synchronized(this.mSoundPool) { mPlayWhenLoadedEffects.putIfAbsent(soundID, info);
// add this effect into mEffecToPlayWhenLoadedArray, and it will be played when loaded completely
mEffecToPlayWhenLoadedArray.add(new SoundInfoForLoadedCompleted(path, soundID.intValue(), loop,
pitch, pan, gain));
synchronized(info) {
try { try {
// wait OnloadedCompleteListener to set streamID info.wait(LOAD_TIME_OUT);
this.mSemaphore.acquire(); }
catch (Exception e) {
streamID = this.mStreamIdSyn; e.printStackTrace();
} catch(Exception e) {
return Cocos2dxSound.INVALID_SOUND_ID;
} }
} }
streamID = info.effectID;
mPlayWhenLoadedEffects.remove(soundID);
} }
return streamID; return streamID;
@ -280,7 +263,7 @@ public class Cocos2dxSound {
this.mSoundPool.release(); this.mSoundPool.release();
this.mPathStreamIDsMap.clear(); this.mPathStreamIDsMap.clear();
this.mPathSoundIDMap.clear(); this.mPathSoundIDMap.clear();
this.mEffecToPlayWhenLoadedArray.clear(); this.mPlayWhenLoadedEffects.clear();
this.mLeftVolume = 0.5f; this.mLeftVolume = 0.5f;
this.mRightVolume = 0.5f; this.mRightVolume = 0.5f;
@ -346,21 +329,20 @@ public class Cocos2dxSound {
// =========================================================== // ===========================================================
public class SoundInfoForLoadedCompleted { public class SoundInfoForLoadedCompleted {
public int soundID;
public boolean isLoop; public boolean isLoop;
public float pitch; public float pitch;
public float pan; public float pan;
public float gain; public float gain;
public String path; public String path;
public int effectID;
public SoundInfoForLoadedCompleted(String path, int soundId, boolean isLoop, public SoundInfoForLoadedCompleted(String path, boolean isLoop, float pitch, float pan, float gain) {
float pitch, float pan, float gain) {
this.path = path; this.path = path;
this.soundID = soundId;
this.isLoop = isLoop; this.isLoop = isLoop;
this.pitch = pitch; this.pitch = pitch;
this.pan = pan; this.pan = pan;
this.gain = gain; this.gain = gain;
effectID = Cocos2dxSound.INVALID_SOUND_ID;
} }
} }
@ -370,26 +352,14 @@ public class Cocos2dxSound {
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) { public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
if (status == 0) if (status == 0)
{ {
// only play effect that are in mEffecToPlayWhenLoadedArray SoundInfoForLoadedCompleted info = mPlayWhenLoadedEffects.get(sampleId);
for ( SoundInfoForLoadedCompleted info : mEffecToPlayWhenLoadedArray) { if (info != null) {
if (sampleId == info.soundID) { info.effectID = doPlayEffect(info.path, sampleId, info.isLoop, info.pitch, info.pan, info.gain);
// set the stream id which will be returned by playEffect() synchronized (info) {
mStreamIdSyn = doPlayEffect(info.path, info.soundID, info.isLoop, info.pitch, info.pan, info.gain); info.notifyAll();
/*
* Remove it from array, because we will break here.
* So it is safe to do.
*/
mEffecToPlayWhenLoadedArray.remove(info);
break;
} }
} }
} else {
mStreamIdSyn = Cocos2dxSound.INVALID_SOUND_ID;
} }
mSemaphore.release();
} }
} }
} }