Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 011a3dfd authored by Android (Google) Code Review's avatar Android (Google) Code Review
Browse files

Merge change I9120be33

* changes:
  Add OnLoadCompleteListener to SoundPool.
parents cf4fc626 f6e43bf2
Loading
Loading
Loading
Loading
+104 −6
Original line number Diff line number Diff line
@@ -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);
@@ -358,6 +387,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.
     *
@@ -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(); }
}
+24 −5
Original line number Diff line number Diff line
@@ -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;
}


+14 −5
Original line number Diff line number Diff line
@@ -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
+30 −24
Original line number Diff line number Diff line
@@ -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);

    // 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));
    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
+9 −16
Original line number Diff line number Diff line
@@ -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();

    Mutex                   mLock;
    Condition               mCondition;
    Vector<SoundPoolMsg>    mMsgQueue;
    SoundPool*              mSoundPool;
    MessageQueue                mMessages;
    bool                    mRunning;
};

} // end namespace android
Loading