| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef SOUNDPOOL_H_ |
| #define SOUNDPOOL_H_ |
| |
| #include <utils/threads.h> |
| #include <utils/List.h> |
| #include <utils/Vector.h> |
| #include <utils/KeyedVector.h> |
| #include <media/AudioTrack.h> |
| #include <cutils/atomic.h> |
| |
| #include <nativehelper/jni.h> |
| |
| namespace android { |
| |
| static const int IDLE_PRIORITY = -1; |
| |
| // forward declarations |
| class SoundEvent; |
| class SoundPoolThread; |
| class SoundPool; |
| |
| // for queued events |
| class SoundPoolEvent { |
| public: |
| SoundPoolEvent(int msg, int arg1=0, int arg2=0) : |
| mMsg(msg), mArg1(arg1), mArg2(arg2) {} |
| int mMsg; |
| int mArg1; |
| int mArg2; |
| }; |
| |
| // JNI for calling back Java SoundPool object |
| extern void android_soundpool_SoundPool_notify(jobject ref, const SoundPoolEvent *event); |
| |
| // tracks samples used by application |
| class Sample : public RefBase { |
| public: |
| enum sample_state { UNLOADED, LOADING, READY, UNLOADING }; |
| Sample(int sampleID, const char* url); |
| Sample(int sampleID, int fd, int64_t offset, int64_t length); |
| ~Sample(); |
| int sampleID() { return mSampleID; } |
| int numChannels() { return mNumChannels; } |
| int sampleRate() { return mSampleRate; } |
| int format() { return mFormat; } |
| size_t size() { return mSize; } |
| int state() { return mState; } |
| uint8_t* data() { return static_cast<uint8_t*>(mData->pointer()); } |
| void doLoad(); |
| void startLoad() { mState = LOADING; } |
| sp<IMemory> getIMemory() { return mData; } |
| |
| // hack |
| void init(int numChannels, int sampleRate, int format, size_t size, sp<IMemory> data ) { |
| mNumChannels = numChannels; mSampleRate = sampleRate; mFormat = format; mSize = size; mData = data; } |
| |
| private: |
| void init(); |
| |
| size_t mSize; |
| volatile int32_t mRefCount; |
| uint16_t mSampleID; |
| uint16_t mSampleRate; |
| uint8_t mState : 3; |
| uint8_t mNumChannels : 2; |
| uint8_t mFormat : 2; |
| int mFd; |
| int64_t mOffset; |
| int64_t mLength; |
| char* mUrl; |
| sp<IMemory> mData; |
| }; |
| |
| // stores pending events for stolen channels |
| class SoundEvent |
| { |
| public: |
| SoundEvent() : mChannelID(0), mLeftVolume(0), mRightVolume(0), |
| mPriority(IDLE_PRIORITY), mLoop(0), mRate(0) {} |
| void set(const sp<Sample>& sample, int channelID, float leftVolume, |
| float rightVolume, int priority, int loop, float rate); |
| sp<Sample> sample() { return mSample; } |
| int channelID() { return mChannelID; } |
| float leftVolume() { return mLeftVolume; } |
| float rightVolume() { return mRightVolume; } |
| int priority() { return mPriority; } |
| int loop() { return mLoop; } |
| float rate() { return mRate; } |
| void clear() { mChannelID = 0; mSample.clear(); } |
| |
| protected: |
| sp<Sample> mSample; |
| int mChannelID; |
| float mLeftVolume; |
| float mRightVolume; |
| int mPriority; |
| int mLoop; |
| float mRate; |
| }; |
| |
| // for channels aka AudioTracks |
| class SoundChannel : public SoundEvent { |
| public: |
| enum state { IDLE, RESUMING, STOPPING, PAUSED, PLAYING }; |
| SoundChannel() : mAudioTrack(0), mState(IDLE), mNumChannels(1), mPos(0), mToggle(0) {} |
| ~SoundChannel(); |
| void init(SoundPool* soundPool); |
| void play(const sp<Sample>& sample, int channelID, float leftVolume, float rightVolume, |
| int priority, int loop, float rate); |
| void setVolume_l(float leftVolume, float rightVolume); |
| void setVolume(float leftVolume, float rightVolume); |
| void stop_l(); |
| void stop(); |
| void pause(); |
| void resume(); |
| void setRate(float rate); |
| int state() { return mState; } |
| void setPriority(int priority) { mPriority = priority; } |
| void setLoop(int loop); |
| int numChannels() { return mNumChannels; } |
| void clearNextEvent() { mNextEvent.clear(); } |
| void nextEvent(); |
| int nextChannelID() { return mNextEvent.channelID(); } |
| void dump(); |
| |
| private: |
| static void callback(int event, void* user, void *info); |
| void process(int event, void *info); |
| |
| SoundPool* mSoundPool; |
| AudioTrack* mAudioTrack; |
| SoundEvent mNextEvent; |
| Mutex mLock; |
| int mState; |
| int mNumChannels; |
| int mPos; |
| int mAudioBufferSize; |
| unsigned long mToggle; |
| }; |
| |
| // application object for managing a pool of sounds |
| class SoundPool { |
| friend class SoundPoolThread; |
| friend class SoundChannel; |
| public: |
| SoundPool(jobject soundPoolRef, int maxChannels, int streamType, int srcQuality); |
| ~SoundPool(); |
| int load(const char* url, int priority); |
| int load(int fd, int64_t offset, int64_t length, int priority); |
| bool unload(int sampleID); |
| int play(int sampleID, float leftVolume, float rightVolume, int priority, |
| int loop, float rate); |
| void pause(int channelID); |
| void resume(int channelID); |
| void stop(int channelID); |
| void setVolume(int channelID, float leftVolume, float rightVolume); |
| void setPriority(int channelID, int priority); |
| void setLoop(int channelID, int loop); |
| void setRate(int channelID, float rate); |
| int streamType() const { return mStreamType; } |
| int srcQuality() const { return mSrcQuality; } |
| |
| // called from SoundPoolThread |
| void sampleLoaded(int sampleID); |
| |
| // called from AudioTrack thread |
| void done(SoundChannel* channel); |
| |
| private: |
| SoundPool() {} // no default constructor |
| bool startThreads(); |
| void doLoad(sp<Sample>& sample); |
| inline void notify(const SoundPoolEvent* event) { |
| android_soundpool_SoundPool_notify(mSoundPoolRef, event); |
| } |
| sp<Sample> findSample(int sampleID) { return mSamples.valueFor(sampleID); } |
| SoundChannel* findChannel (int channelID); |
| SoundChannel* findNextChannel (int channelID); |
| SoundChannel* allocateChannel(int priority); |
| void moveToFront(SoundChannel* channel); |
| void dump(); |
| |
| // restart thread |
| void addToRestartList(SoundChannel* channel); |
| static int beginThread(void* arg); |
| int run(); |
| void quit(); |
| |
| jobject mSoundPoolRef; |
| Mutex mLock; |
| Mutex mRestartLock; |
| Condition mCondition; |
| SoundPoolThread* mDecodeThread; |
| SoundChannel* mChannelPool; |
| List<SoundChannel*> mChannels; |
| List<SoundChannel*> mRestart; |
| DefaultKeyedVector< int, sp<Sample> > mSamples; |
| int mMaxChannels; |
| int mStreamType; |
| int mSrcQuality; |
| int mAllocated; |
| int mNextSampleID; |
| int mNextChannelID; |
| bool mQuit; |
| }; |
| |
| } // end namespace android |
| |
| #endif /*SOUNDPOOL_H_*/ |