summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Eric Laurent <elaurent@google.com> 2013-03-25 23:39:52 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2013-03-25 23:39:52 +0000
commitd8b50ab6fedf97d0667ba26a2a3d6eb6694e4add (patch)
tree902536fd62aec3a9b267c6aa631cb33a8221d7d9
parenta32c7e8f9df1612d690a34258c014be661dc66ca (diff)
parent5d3eb44a749ece55ce345f9d8ac608fc3715f9da (diff)
Merge "AudioService: rewrite sound effects loading" into jb-mr2-dev
-rw-r--r--media/java/android/media/AudioService.java356
1 files changed, 209 insertions, 147 deletions
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 18f3b1e43b06..38cdb8a71a8e 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -165,6 +165,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
private static final int MSG_PERSIST_SAFE_VOLUME_STATE = 28;
private static final int MSG_PROMOTE_RCC = 29;
private static final int MSG_BROADCAST_BT_CONNECTION_STATE = 30;
+ private static final int MSG_UNLOAD_SOUND_EFFECTS = 31;
// flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
@@ -1685,6 +1686,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
private static final String ASSET_FILE_VERSION = "1.0";
private static final String GROUP_TOUCH_SOUNDS = "touch_sounds";
+ private static final int SOUND_EFECTS_LOAD_TIMEOUT_MS = 5000;
+
+ class LoadSoundEffectReply {
+ public int mStatus = 1;
+ };
+
private void loadTouchSoundAssetDefaults() {
SOUND_EFFECT_FILES.add("Effect_Tick.ogg");
for (int i = 0; i < AudioManager.NUM_SOUND_EFFECTS; i++) {
@@ -1696,6 +1703,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
private void loadTouchSoundAssets() {
XmlResourceParser parser = null;
+ // only load assets once.
+ if (!SOUND_EFFECT_FILES.isEmpty()) {
+ return;
+ }
+
loadTouchSoundAssetDefaults();
try {
@@ -1765,14 +1777,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
/** @see AudioManager#playSoundEffect(int) */
public void playSoundEffect(int effectType) {
- sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
- effectType, -1, null, 0);
+ playSoundEffectVolume(effectType, -1.0f);
}
/** @see AudioManager#playSoundEffect(int, float) */
public void playSoundEffectVolume(int effectType, float volume) {
- loadSoundEffects();
- sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
+ sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_QUEUE,
effectType, (int) (volume * 1000), null, 0);
}
@@ -1781,116 +1791,20 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
* This method must be called at first when sound effects are enabled
*/
public boolean loadSoundEffects() {
- int status;
-
- loadTouchSoundAssets();
-
- synchronized (mSoundEffectsLock) {
- if (!mBootCompleted) {
- Log.w(TAG, "loadSoundEffects() called before boot complete");
- return false;
- }
-
- if (mSoundPool != null) {
- return true;
- }
- mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
-
- try {
- mSoundPoolCallBack = null;
- mSoundPoolListenerThread = new SoundPoolListenerThread();
- mSoundPoolListenerThread.start();
- // Wait for mSoundPoolCallBack to be set by the other thread
- mSoundEffectsLock.wait();
- } catch (InterruptedException e) {
- Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
- }
-
- if (mSoundPoolCallBack == null) {
- Log.w(TAG, "loadSoundEffects() could not create SoundPool listener or thread");
- if (mSoundPoolLooper != null) {
- mSoundPoolLooper.quit();
- mSoundPoolLooper = null;
- }
- mSoundPoolListenerThread = null;
- mSoundPool.release();
- mSoundPool = null;
- return false;
- }
- /*
- * poolId table: The value -1 in this table indicates that corresponding
- * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
- * Once loaded, the value in poolId is the sample ID and the same
- * sample can be reused for another effect using the same file.
- */
- int[] poolId = new int[SOUND_EFFECT_FILES.size()];
- for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
- poolId[fileIdx] = -1;
- }
- /*
- * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
- * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
- * this indicates we have a valid sample loaded for this effect.
- */
-
- int lastSample = 0;
- for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
- // Do not load sample if this effect uses the MediaPlayer
- if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
- continue;
- }
- if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
- String filePath = Environment.getRootDirectory()
- + SOUND_EFFECTS_PATH
- + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effect][0]);
- int sampleId = mSoundPool.load(filePath, 0);
- if (sampleId <= 0) {
- Log.w(TAG, "Soundpool could not load file: "+filePath);
- } else {
- SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
- poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
- lastSample = sampleId;
- }
- } else {
- SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
- }
- }
- // wait for all samples to be loaded
- if (lastSample != 0) {
- mSoundPoolCallBack.setLastSample(lastSample);
+ int attempts = 3;
+ LoadSoundEffectReply reply = new LoadSoundEffectReply();
+ synchronized (reply) {
+ sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, reply, 0);
+ while ((reply.mStatus == 1) && (attempts-- > 0)) {
try {
- mSoundEffectsLock.wait();
- status = mSoundPoolCallBack.status();
- } catch (java.lang.InterruptedException e) {
- Log.w(TAG, "Interrupted while waiting sound pool callback.");
- status = -1;
- }
- } else {
- status = -1;
- }
-
- if (mSoundPoolLooper != null) {
- mSoundPoolLooper.quit();
- mSoundPoolLooper = null;
- }
- mSoundPoolListenerThread = null;
- if (status != 0) {
- Log.w(TAG,
- "loadSoundEffects(), Error "
- + ((lastSample != 0) ? mSoundPoolCallBack.status() : -1)
- + " while loading samples");
- for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
- if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
- SOUND_EFFECT_FILES_MAP[effect][1] = -1;
- }
+ reply.wait(SOUND_EFECTS_LOAD_TIMEOUT_MS);
+ } catch (InterruptedException e) {
+ Log.w(TAG, "loadSoundEffects Interrupted while waiting sound pool loaded.");
}
-
- mSoundPool.release();
- mSoundPool = null;
}
}
- return (status == 0);
+ return (reply.mStatus == 0);
}
/**
@@ -1899,32 +1813,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
* sound effects are disabled.
*/
public void unloadSoundEffects() {
- synchronized (mSoundEffectsLock) {
- if (mSoundPool == null) {
- return;
- }
-
- mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS);
- mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT);
-
- int[] poolId = new int[SOUND_EFFECT_FILES.size()];
- for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
- poolId[fileIdx] = 0;
- }
-
- for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
- if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
- continue;
- }
- if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
- mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
- SOUND_EFFECT_FILES_MAP[effect][1] = -1;
- poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
- }
- }
- mSoundPool.release();
- mSoundPool = null;
- }
+ sendMsg(mAudioHandler, MSG_UNLOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, null, 0);
}
class SoundPoolListenerThread extends Thread {
@@ -1952,23 +1841,30 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
private final class SoundPoolCallback implements
android.media.SoundPool.OnLoadCompleteListener {
- int mStatus;
- int mLastSample;
+ int mStatus = 1; // 1 means neither error nor last sample loaded yet
+ List<Integer> mSamples = new ArrayList<Integer>();
public int status() {
return mStatus;
}
- public void setLastSample(int sample) {
- mLastSample = sample;
+ public void setSamples(int[] samples) {
+ for (int i = 0; i < samples.length; i++) {
+ // do not wait ack for samples rejected upfront by SoundPool
+ if (samples[i] > 0) {
+ mSamples.add(samples[i]);
+ }
+ }
}
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
synchronized (mSoundEffectsLock) {
- if (status != 0) {
- mStatus = status;
+ int i = mSamples.indexOf(sampleId);
+ if (i >= 0) {
+ mSamples.remove(i);
}
- if (sampleId == mLastSample) {
+ if ((status != 0) || mSamples. isEmpty()) {
+ mStatus = status;
mSoundEffectsLock.notify();
}
}
@@ -3404,11 +3300,163 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
Settings.Global.putInt(mContentResolver, Settings.Global.MODE_RINGER, ringerMode);
}
- private void playSoundEffect(int effectType, int volume) {
+ private boolean onLoadSoundEffects() {
+ int status;
+
+ synchronized (mSoundEffectsLock) {
+ if (!mBootCompleted) {
+ Log.w(TAG, "onLoadSoundEffects() called before boot complete");
+ return false;
+ }
+
+ if (mSoundPool != null) {
+ return true;
+ }
+
+ loadTouchSoundAssets();
+
+ mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
+ mSoundPoolCallBack = null;
+ mSoundPoolListenerThread = new SoundPoolListenerThread();
+ mSoundPoolListenerThread.start();
+ int attempts = 3;
+ while ((mSoundPoolCallBack == null) && (attempts-- > 0)) {
+ try {
+ // Wait for mSoundPoolCallBack to be set by the other thread
+ mSoundEffectsLock.wait(SOUND_EFECTS_LOAD_TIMEOUT_MS);
+ } catch (InterruptedException e) {
+ Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
+ }
+ }
+
+ if (mSoundPoolCallBack == null) {
+ Log.w(TAG, "onLoadSoundEffects() SoundPool listener or thread creation error");
+ if (mSoundPoolLooper != null) {
+ mSoundPoolLooper.quit();
+ mSoundPoolLooper = null;
+ }
+ mSoundPoolListenerThread = null;
+ mSoundPool.release();
+ mSoundPool = null;
+ return false;
+ }
+ /*
+ * poolId table: The value -1 in this table indicates that corresponding
+ * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
+ * Once loaded, the value in poolId is the sample ID and the same
+ * sample can be reused for another effect using the same file.
+ */
+ int[] poolId = new int[SOUND_EFFECT_FILES.size()];
+ for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
+ poolId[fileIdx] = -1;
+ }
+ /*
+ * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
+ * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
+ * this indicates we have a valid sample loaded for this effect.
+ */
+
+ int numSamples = 0;
+ for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
+ // Do not load sample if this effect uses the MediaPlayer
+ if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
+ continue;
+ }
+ if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
+ String filePath = Environment.getRootDirectory()
+ + SOUND_EFFECTS_PATH
+ + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effect][0]);
+ int sampleId = mSoundPool.load(filePath, 0);
+ if (sampleId <= 0) {
+ Log.w(TAG, "Soundpool could not load file: "+filePath);
+ } else {
+ SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
+ poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
+ numSamples++;
+ }
+ } else {
+ SOUND_EFFECT_FILES_MAP[effect][1] =
+ poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
+ }
+ }
+ // wait for all samples to be loaded
+ if (numSamples > 0) {
+ mSoundPoolCallBack.setSamples(poolId);
+
+ attempts = 3;
+ status = 1;
+ while ((status == 1) && (attempts-- > 0)) {
+ try {
+ mSoundEffectsLock.wait(SOUND_EFECTS_LOAD_TIMEOUT_MS);
+ status = mSoundPoolCallBack.status();
+ } catch (InterruptedException e) {
+ Log.w(TAG, "Interrupted while waiting sound pool callback.");
+ }
+ }
+ } else {
+ status = -1;
+ }
+
+ if (mSoundPoolLooper != null) {
+ mSoundPoolLooper.quit();
+ mSoundPoolLooper = null;
+ }
+ mSoundPoolListenerThread = null;
+ if (status != 0) {
+ Log.w(TAG,
+ "onLoadSoundEffects(), Error "+status+ " while loading samples");
+ for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
+ if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
+ SOUND_EFFECT_FILES_MAP[effect][1] = -1;
+ }
+ }
+
+ mSoundPool.release();
+ mSoundPool = null;
+ }
+ }
+ return (status == 0);
+ }
+
+ /**
+ * Unloads samples from the sound pool.
+ * This method can be called to free some memory when
+ * sound effects are disabled.
+ */
+ private void onUnloadSoundEffects() {
synchronized (mSoundEffectsLock) {
if (mSoundPool == null) {
return;
}
+
+ int[] poolId = new int[SOUND_EFFECT_FILES.size()];
+ for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
+ poolId[fileIdx] = 0;
+ }
+
+ for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
+ if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
+ continue;
+ }
+ if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
+ mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
+ SOUND_EFFECT_FILES_MAP[effect][1] = -1;
+ poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
+ }
+ }
+ mSoundPool.release();
+ mSoundPool = null;
+ }
+ }
+
+ private void onPlaySoundEffect(int effectType, int volume) {
+ synchronized (mSoundEffectsLock) {
+
+ onLoadSoundEffects();
+
+ if (mSoundPool == null) {
+ return;
+ }
float volFloat;
// use default if volume is not specified by caller
if (volume < 0) {
@@ -3418,7 +3466,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
- mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
+ mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1],
+ volFloat, volFloat, 0, 0, 1.0f);
} else {
MediaPlayer mediaPlayer = new MediaPlayer();
try {
@@ -3598,12 +3647,25 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
AudioSystem.setParameters("restarting=false");
break;
+ case MSG_UNLOAD_SOUND_EFFECTS:
+ onUnloadSoundEffects();
+ break;
+
case MSG_LOAD_SOUND_EFFECTS:
- loadSoundEffects();
+ //FIXME: onLoadSoundEffects() should be executed in a separate thread as it
+ // can take several dozens of milliseconds to complete
+ boolean loaded = onLoadSoundEffects();
+ if (msg.obj != null) {
+ LoadSoundEffectReply reply = (LoadSoundEffectReply)msg.obj;
+ synchronized (reply) {
+ reply.mStatus = loaded ? 0 : -1;
+ reply.notify();
+ }
+ }
break;
case MSG_PLAY_SOUND_EFFECT:
- playSoundEffect(msg.arg1, msg.arg2);
+ onPlaySoundEffect(msg.arg1, msg.arg2);
break;
case MSG_BTA2DP_DOCK_TIMEOUT:
@@ -4151,7 +4213,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
} else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
mBootCompleted = true;
- sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_NOOP,
+ sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE,
0, 0, null, 0);
mKeyguardManager =