/*
* 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_*/