diff options
author | Dave Sparks <davidsparks@android.com> | 2009-12-08 08:10:42 -0800 |
---|---|---|
committer | Dave Sparks <davidsparks@android.com> | 2009-12-23 09:11:29 -0800 |
commit | f6e43bf29084760b39257b2273e0f04c2815fdc5 (patch) | |
tree | f9690ab4d0caeb527bf13392f6a88e4002a1a1cb | |
parent | fddd8f96f34b622cbdd0d891f8a7f5b7e18fdbf8 (diff) | |
download | frameworks_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.java | 110 | ||||
-rw-r--r-- | media/jni/soundpool/SoundPool.cpp | 29 | ||||
-rw-r--r-- | media/jni/soundpool/SoundPool.h | 19 | ||||
-rw-r--r-- | media/jni/soundpool/SoundPoolThread.cpp | 54 | ||||
-rw-r--r-- | media/jni/soundpool/SoundPoolThread.h | 25 | ||||
-rw-r--r-- | media/jni/soundpool/android_media_SoundPool.cpp | 41 |
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; |