summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Sparks <davidsparks@android.com>2009-12-08 08:10:42 -0800
committerDave Sparks <davidsparks@android.com>2009-12-23 09:11:29 -0800
commitf6e43bf29084760b39257b2273e0f04c2815fdc5 (patch)
treef9690ab4d0caeb527bf13392f6a88e4002a1a1cb
parentfddd8f96f34b622cbdd0d891f8a7f5b7e18fdbf8 (diff)
downloadframeworks_base-f6e43bf29084760b39257b2273e0f04c2815fdc5.zip
frameworks_base-f6e43bf29084760b39257b2273e0f04c2815fdc5.tar.gz
frameworks_base-f6e43bf29084760b39257b2273e0f04c2815fdc5.tar.bz2
Add OnLoadCompleteListener to SoundPool.
-rw-r--r--media/java/android/media/SoundPool.java110
-rw-r--r--media/jni/soundpool/SoundPool.cpp29
-rw-r--r--media/jni/soundpool/SoundPool.h19
-rw-r--r--media/jni/soundpool/SoundPoolThread.cpp54
-rw-r--r--media/jni/soundpool/SoundPoolThread.h25
-rw-r--r--media/jni/soundpool/android_media_SoundPool.cpp41
6 files changed, 215 insertions, 63 deletions
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 3803d9d..df21aa4 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -26,6 +26,10 @@ import android.content.Context;
import android.content.res.AssetFileDescriptor;
import java.io.IOException;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
/**
* The SoundPool class manages and plays audio resources for applications.
*
@@ -103,9 +107,20 @@ public class SoundPool
static { System.loadLibrary("soundpool"); }
private final static String TAG = "SoundPool";
+ private final static boolean DEBUG = false;
private int mNativeContext; // accessed by native methods
+ private EventHandler mEventHandler;
+ private OnLoadCompleteListener mOnLoadCompleteListener;
+
+ private final Object mLock;
+
+ // SoundPool messages
+ //
+ // must match SoundPool.h
+ private static final int SAMPLE_LOADED = 1;
+
/**
* Constructor. Constructs a SoundPool object with the following
* characteristics:
@@ -120,7 +135,23 @@ public class SoundPool
* @return a SoundPool object, or null if creation failed
*/
public SoundPool(int maxStreams, int streamType, int srcQuality) {
- native_setup(new WeakReference<SoundPool>(this), maxStreams, streamType, srcQuality);
+
+ // do native setup
+ if (native_setup(new WeakReference(this), maxStreams, streamType, srcQuality) != 0) {
+ throw new RuntimeException("Native setup failed");
+ }
+ mLock = new Object();
+
+ // setup message handler
+ Looper looper;
+ if ((looper = Looper.myLooper()) != null) {
+ mEventHandler = new EventHandler(this, looper);
+ } else if ((looper = Looper.getMainLooper()) != null) {
+ mEventHandler = new EventHandler(this, looper);
+ } else {
+ mEventHandler = null;
+ }
+
}
/**
@@ -145,12 +176,11 @@ public class SoundPool
ParcelFileDescriptor fd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
if (fd != null) {
id = _load(fd.getFileDescriptor(), 0, f.length(), priority);
- //Log.v(TAG, "close fd");
fd.close();
}
}
} catch (java.io.IOException e) {
- Log.d(TAG, "error loading " + path);
+ Log.e(TAG, "error loading " + path);
}
return id;
}
@@ -176,7 +206,6 @@ public class SoundPool
if (afd != null) {
id = _load(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength(), priority);
try {
- //Log.v(TAG, "close fd");
afd.close();
} catch (java.io.IOException ex) {
//Log.d(TAG, "close failed:", ex);
@@ -359,6 +388,76 @@ public class SoundPool
public native final void setRate(int streamID, float rate);
/**
+ * Interface definition for a callback to be invoked when all the
+ * sounds are loaded.
+ *
+ * @hide
+ */
+ public interface OnLoadCompleteListener
+ {
+ /**
+ * Called when a sound has completed loading.
+ *
+ * @param soundPool SoundPool object from the load() method
+ * @param soundPool the sample ID of the sound loaded.
+ * @param status the status of the load operation (0 = success)
+ */
+ public void onLoadComplete(SoundPool soundPool, int sampleId, int status);
+ }
+
+ /**
+ * Sets the callback hook for the OnLoadCompleteListener.
+ *
+ * @hide
+ */
+ public void setOnLoadCompleteListener(OnLoadCompleteListener listener)
+ {
+ synchronized(mLock) {
+ mOnLoadCompleteListener = listener;
+ }
+ }
+
+ private class EventHandler extends Handler
+ {
+ private SoundPool mSoundPool;
+
+ public EventHandler(SoundPool soundPool, Looper looper) {
+ super(looper);
+ mSoundPool = soundPool;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case SAMPLE_LOADED:
+ if (DEBUG) Log.d(TAG, "Sample " + msg.arg1 + " loaded");
+ synchronized(mLock) {
+ if (mOnLoadCompleteListener != null) {
+ mOnLoadCompleteListener.onLoadComplete(mSoundPool, msg.arg1, msg.arg2);
+ }
+ }
+ break;
+ default:
+ Log.e(TAG, "Unknown message type " + msg.what);
+ return;
+ }
+ }
+ }
+
+ // post event from native code to message handler
+ private static void postEventFromNative(Object weakRef, int msg, int arg1, int arg2, Object obj)
+ {
+ SoundPool soundPool = (SoundPool)((WeakReference)weakRef).get();
+ if (soundPool == null)
+ return;
+
+ if (soundPool.mEventHandler != null) {
+ Message m = soundPool.mEventHandler.obtainMessage(msg, arg1, arg2, obj);
+ soundPool.mEventHandler.sendMessage(m);
+ }
+ }
+
+ /**
* Release the SoundPool resources.
*
* Release all memory and native resources used by the SoundPool
@@ -367,8 +466,7 @@ public class SoundPool
*/
public native final void release();
- private native final void native_setup(Object mediaplayer_this,
- int maxStreams, int streamType, int srcQuality);
+ private native final int native_setup(Object weakRef, int maxStreams, int streamType, int srcQuality);
protected void finalize() { release(); }
}
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 70fba7e..e57f4a4 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -61,6 +61,9 @@ SoundPool::SoundPool(int maxChannels, int streamType, int srcQuality)
mNextSampleID = 0;
mNextChannelID = 0;
+ mCallback = 0;
+ mUserData = 0;
+
mChannelPool = new SoundChannel[mMaxChannels];
for (int i = 0; i < mMaxChannels; ++i) {
mChannelPool[i].init(this);
@@ -141,7 +144,7 @@ void SoundPool::quit()
bool SoundPool::startThreads()
{
- createThread(beginThread, this);
+ createThreadEtc(beginThread, this, "SoundPoolThread");
if (mDecodeThread == NULL)
mDecodeThread = new SoundPoolThread(this);
return mDecodeThread != NULL;
@@ -372,6 +375,21 @@ void SoundPool::done(SoundChannel* channel)
}
}
+void SoundPool::setCallback(SoundPoolCallback* callback, void* user)
+{
+ Mutex::Autolock lock(&mCallbackLock);
+ mCallback = callback;
+ mUserData = user;
+}
+
+void SoundPool::notify(SoundPoolEvent event)
+{
+ Mutex::Autolock lock(&mCallbackLock);
+ if (mCallback != NULL) {
+ mCallback(event, this, mUserData);
+ }
+}
+
void SoundPool::dump()
{
for (int i = 0; i < mMaxChannels; ++i) {
@@ -422,7 +440,7 @@ Sample::~Sample()
delete mUrl;
}
-void Sample::doLoad()
+status_t Sample::doLoad()
{
uint32_t sampleRate;
int numChannels;
@@ -439,19 +457,19 @@ void Sample::doLoad()
}
if (p == 0) {
LOGE("Unable to load sample: %s", mUrl);
- return;
+ return -1;
}
LOGV("pointer = %p, size = %u, sampleRate = %u, numChannels = %d",
p->pointer(), p->size(), sampleRate, numChannels);
if (sampleRate > kMaxSampleRate) {
LOGE("Sample rate (%u) out of range", sampleRate);
- return;
+ return - 1;
}
if ((numChannels < 1) || (numChannels > 2)) {
LOGE("Sample channel count (%d) out of range", numChannels);
- return;
+ return - 1;
}
//_dumpBuffer(p->pointer(), p->size());
@@ -464,6 +482,7 @@ void Sample::doLoad()
mNumChannels = numChannels;
mFormat = format;
mState = READY;
+ return 0;
}
diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h
index 94cd978..7a2d631 100644
--- a/media/jni/soundpool/SoundPool.h
+++ b/media/jni/soundpool/SoundPool.h
@@ -24,8 +24,6 @@
#include <media/AudioTrack.h>
#include <cutils/atomic.h>
-#include <nativehelper/jni.h>
-
namespace android {
static const int IDLE_PRIORITY = -1;
@@ -43,10 +41,11 @@ public:
int mMsg;
int mArg1;
int mArg2;
+ enum MessageType { INVALID, SAMPLE_LOADED };
};
-// JNI for calling back Java SoundPool object
-extern void android_soundpool_SoundPool_notify(jobject ref, const SoundPoolEvent *event);
+// callback function prototype
+typedef void SoundPoolCallback(SoundPoolEvent event, SoundPool* soundPool, void* user);
// tracks samples used by application
class Sample : public RefBase {
@@ -62,7 +61,7 @@ public:
size_t size() { return mSize; }
int state() { return mState; }
uint8_t* data() { return static_cast<uint8_t*>(mData->pointer()); }
- void doLoad();
+ status_t doLoad();
void startLoad() { mState = LOADING; }
sp<IMemory> getIMemory() { return mData; }
@@ -182,6 +181,10 @@ public:
// called from AudioTrack thread
void done(SoundChannel* channel);
+ // callback function
+ void setCallback(SoundPoolCallback* callback, void* user);
+ void* getUserData() { return mUserData; }
+
private:
SoundPool() {} // no default constructor
bool startThreads();
@@ -191,6 +194,7 @@ private:
SoundChannel* findNextChannel (int channelID);
SoundChannel* allocateChannel(int priority);
void moveToFront(SoundChannel* channel);
+ void notify(SoundPoolEvent event);
void dump();
// restart thread
@@ -214,6 +218,11 @@ private:
int mNextSampleID;
int mNextChannelID;
bool mQuit;
+
+ // callback
+ Mutex mCallbackLock;
+ SoundPoolCallback* mCallback;
+ void* mUserData;
};
} // end namespace android
diff --git a/media/jni/soundpool/SoundPoolThread.cpp b/media/jni/soundpool/SoundPoolThread.cpp
index 4e6798d..e32c794 100644
--- a/media/jni/soundpool/SoundPoolThread.cpp
+++ b/media/jni/soundpool/SoundPoolThread.cpp
@@ -22,50 +22,54 @@
namespace android {
-void SoundPoolThread::MessageQueue::write(SoundPoolMsg msg) {
- LOGV("MessageQueue::write - acquiring lock\n");
+void SoundPoolThread::write(SoundPoolMsg msg) {
Mutex::Autolock lock(&mLock);
- while (mQueue.size() >= maxMessages) {
- LOGV("MessageQueue::write - wait\n");
+ while (mMsgQueue.size() >= maxMessages) {
mCondition.wait(mLock);
}
- LOGV("MessageQueue::write - push message\n");
- mQueue.push(msg);
- mCondition.signal();
+
+ // if thread is quitting, don't add to queue
+ if (mRunning) {
+ mMsgQueue.push(msg);
+ mCondition.signal();
+ }
}
-const SoundPoolMsg SoundPoolThread::MessageQueue::read() {
- LOGV("MessageQueue::read - acquiring lock\n");
+const SoundPoolMsg SoundPoolThread::read() {
Mutex::Autolock lock(&mLock);
- while (mQueue.size() == 0) {
- LOGV("MessageQueue::read - wait\n");
+ while (mMsgQueue.size() == 0) {
mCondition.wait(mLock);
}
- SoundPoolMsg msg = mQueue[0];
- LOGV("MessageQueue::read - retrieve message\n");
- mQueue.removeAt(0);
+ SoundPoolMsg msg = mMsgQueue[0];
+ mMsgQueue.removeAt(0);
mCondition.signal();
return msg;
}
-void SoundPoolThread::MessageQueue::quit() {
+void SoundPoolThread::quit() {
Mutex::Autolock lock(&mLock);
- mQueue.clear();
- mQueue.push(SoundPoolMsg(SoundPoolMsg::KILL, 0));
- mCondition.signal();
- mCondition.wait(mLock);
+ if (mRunning) {
+ mRunning = false;
+ mMsgQueue.clear();
+ mMsgQueue.push(SoundPoolMsg(SoundPoolMsg::KILL, 0));
+ mCondition.signal();
+ mCondition.wait(mLock);
+ }
LOGV("return from quit");
}
SoundPoolThread::SoundPoolThread(SoundPool* soundPool) :
mSoundPool(soundPool)
{
- mMessages.setCapacity(maxMessages);
- createThread(beginThread, this);
+ mMsgQueue.setCapacity(maxMessages);
+ if (createThread(beginThread, this)) {
+ mRunning = true;
+ }
}
SoundPoolThread::~SoundPoolThread()
{
+ quit();
}
int SoundPoolThread::beginThread(void* arg) {
@@ -77,7 +81,7 @@ int SoundPoolThread::beginThread(void* arg) {
int SoundPoolThread::run() {
LOGV("run");
for (;;) {
- SoundPoolMsg msg = mMessages.read();
+ SoundPoolMsg msg = read();
LOGV("Got message m=%d, mData=%d", msg.mMessageType, msg.mData);
switch (msg.mMessageType) {
case SoundPoolMsg::KILL:
@@ -95,14 +99,16 @@ int SoundPoolThread::run() {
}
void SoundPoolThread::loadSample(int sampleID) {
- mMessages.write(SoundPoolMsg(SoundPoolMsg::LOAD_SAMPLE, sampleID));
+ write(SoundPoolMsg(SoundPoolMsg::LOAD_SAMPLE, sampleID));
}
void SoundPoolThread::doLoadSample(int sampleID) {
sp <Sample> sample = mSoundPool->findSample(sampleID);
+ status_t status = -1;
if (sample != 0) {
- sample->doLoad();
+ status = sample->doLoad();
}
+ mSoundPool->notify(SoundPoolEvent(SoundPoolEvent::SAMPLE_LOADED, sampleID, status));
}
} // end namespace android
diff --git a/media/jni/soundpool/SoundPoolThread.h b/media/jni/soundpool/SoundPoolThread.h
index 459a764..bbd35e0 100644
--- a/media/jni/soundpool/SoundPoolThread.h
+++ b/media/jni/soundpool/SoundPoolThread.h
@@ -27,7 +27,7 @@ namespace android {
class SoundPoolMsg {
public:
- enum MessageType { INVALID, KILL, LOAD_SAMPLE, PLAY_SAMPLE, SAMPLE_DONE };
+ enum MessageType { INVALID, KILL, LOAD_SAMPLE };
SoundPoolMsg() : mMessageType(INVALID), mData(0) {}
SoundPoolMsg(MessageType MessageType, int data) :
mMessageType(MessageType), mData(data) {}
@@ -45,29 +45,22 @@ public:
SoundPoolThread(SoundPool* SoundPool);
~SoundPoolThread();
void loadSample(int sampleID);
- void quit() { mMessages.quit(); }
+ void quit();
+ void write(SoundPoolMsg msg);
private:
static const size_t maxMessages = 5;
- class MessageQueue {
- public:
- void write(SoundPoolMsg msg);
- const SoundPoolMsg read();
- void setCapacity(size_t size) { mQueue.setCapacity(size); }
- void quit();
- private:
- Vector<SoundPoolMsg> mQueue;
- Mutex mLock;
- Condition mCondition;
- };
-
static int beginThread(void* arg);
int run();
void doLoadSample(int sampleID);
+ const SoundPoolMsg read();
- SoundPool* mSoundPool;
- MessageQueue mMessages;
+ Mutex mLock;
+ Condition mCondition;
+ Vector<SoundPoolMsg> mMsgQueue;
+ SoundPool* mSoundPool;
+ bool mRunning;
};
} // end namespace android
diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
index 1381db3..f6ea916 100644
--- a/media/jni/soundpool/android_media_SoundPool.cpp
+++ b/media/jni/soundpool/android_media_SoundPool.cpp
@@ -17,7 +17,7 @@
#include <stdio.h>
//#define LOG_NDEBUG 0
-#define LOG_TAG "SoundPool"
+#define LOG_TAG "SoundPool-JNI"
#include <utils/Log.h>
#include <nativehelper/jni.h>
@@ -29,6 +29,7 @@ using namespace android;
static struct fields_t {
jfieldID mNativeContext;
+ jmethodID mPostEvent;
jclass mSoundPoolClass;
} fields;
@@ -149,19 +150,29 @@ android_media_SoundPool_setRate(JNIEnv *env, jobject thiz, jint channelID,
ap->setRate(channelID, rate);
}
-static void
-android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz,
- jobject weak_this, jint maxChannels, jint streamType, jint srcQuality)
+static void android_media_callback(SoundPoolEvent event, SoundPool* soundPool, void* user)
+{
+ LOGV("callback: (%d, %d, %d, %p, %p)", event.mMsg, event.mArg1, event.mArg2, soundPool, user);
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ env->CallStaticVoidMethod(fields.mSoundPoolClass, fields.mPostEvent, user, event.mMsg, event.mArg1, event.mArg2, NULL);
+}
+
+static jint
+android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef, jint maxChannels, jint streamType, jint srcQuality)
{
LOGV("android_media_SoundPool_native_setup");
SoundPool *ap = new SoundPool(maxChannels, streamType, srcQuality);
if (ap == NULL) {
- jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
- return;
+ return -1;
}
// save pointer to SoundPool C++ object in opaque field in Java object
env->SetIntField(thiz, fields.mNativeContext, (int)ap);
+
+ // set callback with weak reference
+ jobject globalWeakRef = env->NewGlobalRef(weakRef);
+ ap->setCallback(android_media_callback, globalWeakRef);
+ return 0;
}
static void
@@ -170,6 +181,15 @@ android_media_SoundPool_release(JNIEnv *env, jobject thiz)
LOGV("android_media_SoundPool_release");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap != NULL) {
+
+ // release weak reference
+ jobject weakRef = (jobject) ap->getUserData();
+ if (weakRef != NULL) {
+ env->DeleteGlobalRef(weakRef);
+ }
+
+ // clear callback and native context
+ ap->setCallback(NULL, NULL);
env->SetIntField(thiz, fields.mNativeContext, 0);
delete ap;
}
@@ -224,7 +244,7 @@ static JNINativeMethod gMethods[] = {
(void *)android_media_SoundPool_setRate
},
{ "native_setup",
- "(Ljava/lang/Object;III)V",
+ "(Ljava/lang/Object;III)I",
(void*)android_media_SoundPool_native_setup
},
{ "release",
@@ -259,6 +279,13 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)
goto bail;
}
+ fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
+ "(Ljava/lang/Object;IIILjava/lang/Object;)V");
+ if (fields.mPostEvent == NULL) {
+ LOGE("Can't find android/media/SoundPool.postEventFromNative");
+ return -1;
+ }
+
if (AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
goto bail;