SoundPool的play方法用于播放音效文件,代码如下:
//frameworks/base/media/java/android/media/SoundPool.java
public class SoundPool extends PlayerBase {
public final int play(int soundID, float leftVolume, float rightVolume,
int priority, int loop, float rate) {
// FIXME: b/174876164 implement device id for soundpool
baseStart(0);
return _play(soundID, leftVolume, rightVolume, priority, loop, rate);
}
}
调用_play方法,_play是一个native方法,实现代码如下:
//frameworks/base/media/jni/soundpool/android_media_soundPool.cpp
static jint
android_media_SoundPool_play(JNIEnv *env, jobject thiz, jint sampleID,
jfloat leftVolume, jfloat rightVolume, jint priority, jint loop,
jfloat rate)
{
ALOGV("android_media_SoundPool_play\n");
auto soundPool = getSoundPool(env, thiz);
if (soundPool == nullptr) return 0;
return (jint) soundPool->play(sampleID, leftVolume, rightVolume, priority, loop, rate);
}
上面方法处理如下:
1、调用getSoundPool方法,获取SoundPool对象
2、调用SoundPool的play方法进行音效声音play
下面分别进行分析:
getSoundPool
调用getSoundPool方法,获取SoundPool对象:
//frameworks/base/media/jni/soundpool/android_media_soundPool.cpp
inline auto getSoundPool(JNIEnv *env, jobject thiz) {
return getSoundPoolManager().get(env, thiz);
}
调用getSoundPoolManager方法:
//frameworks/base/media/jni/soundpool/android_media_soundPool.cpp
auto& getSoundPoolManager() {
static ObjectManager<std::shared_ptr<SoundPool>> soundPoolManager(fields.mNativeContext);
return soundPoolManager;
}
SoundPool play
调用SoundPool的play方法进行音效声音play:
//frameworks/base/media/jni/SoundPool/SoundPool.cpp
int32_t SoundPool::play(int32_t soundID, float leftVolume, float rightVolume,
int32_t priority, int32_t loop, float rate)
{
ALOGV("%s(soundID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f)",
__func__, soundID, leftVolume, rightVolume, priority, loop, rate);
// New for R: check arguments to ensure track can be created.
// If SoundPool defers the creation of the AudioTrack to the StreamManager thread,
// the failure to create may not be visible to the caller, so this precheck is needed.
if (checkVolume(&leftVolume, &rightVolume)
|| checkPriority(&priority)
|| checkLoop(&loop)
|| checkRate(&rate)) return 0;
auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
const std::shared_ptr<soundpool::Sound> sound = mSoundManager.findSound(soundID); //通过soundID获取sound 对象
if (sound == nullptr || sound->getState() != soundpool::Sound::READY) { //判断sound状态
ALOGW("%s soundID %d not READY", __func__, soundID);
return 0;
}
const int32_t streamID = mStreamManager.queueForPlay(
sound, soundID, leftVolume, rightVolume, priority, loop, rate);
ALOGV("%s returned %d", __func__, streamID);
return streamID;
}
调用StreamManager的queueForPlay方法:
//frameworks/base/media/jni/SoundPool/StreamManager.cpp
int32_t StreamManager::queueForPlay(const std::shared_ptr<Sound> &sound,
int32_t soundID, float leftVolume, float rightVolume,
int32_t priority, int32_t loop, float rate)
{
ALOGV("%s(sound=%p, soundID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f)",
__func__, sound.get(), soundID, leftVolume, rightVolume, priority, loop, rate);
bool launchThread = false;
int32_t streamID = 0;
std::vector<std::any> garbage;
{ // for lock
std::unique_lock lock(mStreamManagerLock);
Stream *newStream = nullptr;
bool fromAvailableQueue = false;
ALOGV("%s: mStreamManagerLock lock acquired", __func__);
sanityCheckQueue_l();
// find an available stream, prefer one that has matching sound id.
if (mAvailableStreams.size() > 0) {
for (auto stream : mAvailableStreams) {
if (stream->getSoundID() == soundID) {
newStream = stream;
ALOGV("%s: found soundID %d in available queue", __func__, soundID);
break;
}
}
if (newStream == nullptr) {
ALOGV("%s: found stream in available queue", __func__);
newStream = *mAvailableStreams.begin();
}
newStream->setStopTimeNs(systemTime());
fromAvailableQueue = true;
}
// also look in the streams restarting (if the paired stream doesn't have a pending play)
if (newStream == nullptr || newStream->getSoundID() != soundID) {
for (auto [unused , stream] : mRestartStreams) {
if (!stream->getPairStream()->hasSound()) {
if (stream->getSoundID() == soundID) {
ALOGV("%s: found soundID %d in restart queue", __func__, soundID);
newStream = stream;
fromAvailableQueue = false;
break;
} else if (newStream == nullptr) {
ALOGV("%s: found stream in restart queue", __func__);
newStream = stream;
}
}
}
}
// no available streams, look for one to steal from the active list
if (newStream == nullptr) {
for (auto stream : mActiveStreams) {
if (stream->getPriority() <= priority) {
if (newStream == nullptr
|| newStream->getPriority() > stream->getPriority()) {
newStream = stream;
ALOGV("%s: found stream in active queue", __func__);
}
}
}
if (newStream != nullptr) { // we need to mute as it is still playing.
(void)newStream->requestStop(newStream->getStreamID());
}
}
// none found, look for a stream that is restarting, evict one.
if (newStream == nullptr) {
for (auto [unused, stream] : mRestartStreams) {
if (stream->getPairPriority() <= priority) {
ALOGV("%s: evict stream from restart queue", __func__);
newStream = stream;
break;
}
}
}
// DO NOT LOOK into mProcessingStreams as those are held by the StreamManager threads.
if (newStream == nullptr) {
ALOGD("%s: unable to find stream, returning 0", __func__);
return 0; // unable to find available stream
}
Stream *pairStream = newStream->getPairStream();
streamID = getNextIdForStream(pairStream);
ALOGV("%s: newStream:%p pairStream:%p, streamID:%d",
__func__, newStream, pairStream, streamID);
pairStream->setPlay(
streamID, sound, soundID, leftVolume, rightVolume, priority, loop, rate);
if (fromAvailableQueue && kPlayOnCallingThread) {
removeFromQueues_l(newStream);
mProcessingStreams.emplace(newStream);
lock.unlock();
if (Stream* nextStream = newStream->playPairStream(garbage)) {
lock.lock();
ALOGV("%s: starting streamID:%d", __func__, nextStream->getStreamID());
addToActiveQueue_l(nextStream);
} else {
lock.lock();
mAvailableStreams.insert(newStream);
streamID = 0;
}
mProcessingStreams.erase(newStream);
} else {
launchThread = moveToRestartQueue_l(newStream) && needMoreThreads_l();
}
sanityCheckQueue_l();
ALOGV("%s: mStreamManagerLock released", __func__);
} // lock
if (launchThread) {
const int32_t id = mThreadPool->launch([this](int32_t id) { run(id); });
(void)id; // avoid clang warning -Wunused-variable -Wused-but-marked-unused
ALOGV_IF(id != 0, "%s: launched thread %d", __func__, id);
}
ALOGV("%s: returning %d", __func__, streamID);
// garbage is cleared here outside mStreamManagerLock.
return streamID;
}
上面主要处理如下:
1、调用Stream的setPlay方法,设置Play相关变量。
2、调用playPairStream方法,播放配对流。
下面分别进行分析:
Stream setPlay
调用Stream的setPlay方法,设置Play相关变量:
//frameworks/base/media/jni/SoundPool/Stream.cpp
void Stream::setPlay(
int32_t streamID, const std::shared_ptr<Sound> &sound, int32_t soundID,
float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate)
{
std::lock_guard lock(mLock);
// We must be idle, or we must be repurposing a pending Stream.
LOG_ALWAYS_FATAL_IF(mState != IDLE && mAudioTrack != nullptr, "State %d must be IDLE", mState);
mSound = sound;
mSoundID = soundID;
mLeftVolume = leftVolume;
mRightVolume = rightVolume;
mPriority = priority;
mLoop = loop;
mRate = rate;
mState = PLAYING;
mAutoPaused = false; // New for R (consistent with Java API spec).
mStreamID = streamID; // prefer this to be the last, as it is an atomic sync point
}
Stream playPairStream
调用playPairStream方法,播放配对流:
//frameworks/base/media/jni/SoundPool/Stream.cpp
Stream* Stream::playPairStream(std::vector<std::any>& garbage) {
Stream* pairStream = getPairStream();
LOG_ALWAYS_FATAL_IF(pairStream == nullptr, "No pair stream!");
{
ALOGV("%s: track streamID: %d", __func__, (int)getStreamID());
// TODO: Do we really want to force a simultaneous synchronization between
// the stream and its pair?
// note locking order - the paired stream is obtained before the queued stream.
// we can invert the locking order, but it is slightly more optimal to do it this way.
std::lock_guard lockp(pairStream->mLock);
if (pairStream->mSound == nullptr) {
return nullptr; // no pair sound
}
{
std::lock_guard lock(mLock);
LOG_ALWAYS_FATAL_IF(mState != IDLE, "State: %d must be IDLE", mState);
// TODO: do we want a specific set() here?
pairStream->mAudioTrack = mAudioTrack;
pairStream->mSoundID = mSoundID; // optimization to reuse AudioTrack.
pairStream->mToggle = mToggle;
pairStream->mAutoPaused = mAutoPaused; // save autopause state
pairStream->mMuted = mMuted;
mAudioTrack.clear(); // the pair owns the audiotrack.
mSound.reset();
mSoundID = 0;
}
// TODO: do we need a specific play_l() anymore?
const int pairState = pairStream->mState;
pairStream->play_l(pairStream->mSound, pairStream->mStreamID,
pairStream->mLeftVolume, pairStream->mRightVolume, pairStream->mPriority,
pairStream->mLoop, pairStream->mRate, garbage);
if (pairStream->mState == IDLE) {
return nullptr; // AudioTrack error
}
if (pairState == PAUSED) { // reestablish pause
pairStream->mState = PAUSED;
pairStream->mAudioTrack->pause();
}
}
return pairStream;
}
调用Stream的play_l方法:
//frameworks/base/media/jni/SoundPool/Stream.cpp
sp<AudioTrack> mAudioTrack GUARDED_BY(mLock);
void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate,
std::vector<std::any>& garbage)
{
ALOGV("%s(%p)(soundID=%d, streamID=%d, leftVolume=%f, rightVolume=%f,"
" priority=%d, loop=%d, rate=%f)",
__func__, this, sound->getSoundID(), nextStreamID, leftVolume, rightVolume,
priority, loop, rate);
// initialize track
const audio_stream_type_t streamType =
AudioSystem::attributesToStreamType(*mStreamManager->getAttributes());
const int32_t channelCount = sound->getChannelCount();
const auto sampleRate = (uint32_t)lround(double(sound->getSampleRate()) * rate);
size_t frameCount = 0;
if (loop) {
const audio_format_t format = sound->getFormat();
const size_t frameSize = audio_is_linear_pcm(format)
? channelCount * audio_bytes_per_sample(format) : 1;
frameCount = sound->getSizeInBytes() / frameSize;
}
if (mAudioTrack != nullptr) {
if (mSoundID == sound->getSoundID()
&& mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) { //调用AudioTrack设置波特率
// Reuse the old track if the soundID matches.
// the sample rate may fail to change if the audio track is a fast track.
ALOGV("%s: reusing track %p for sound %d",
__func__, mAudioTrack.get(), sound->getSoundID());
} else {
// If reuse not possible, move mAudioTrack to garbage, set to nullptr.
garbage.emplace_back(std::move(mAudioTrack));
mAudioTrack.clear(); // move should have cleared the sp<>, but we clear just in case.
}
}
if (mAudioTrack == nullptr) {
// mToggle toggles each time a track is started on a given stream.
// This enables the detection of callbacks received from the old
// audio track while the new one is being started and avoids processing them with
// wrong audio audio buffer size (mAudioBufferSize)
auto toggle = mToggle ^ 1;
// NOLINTNEXTLINE(performance-no-int-to-ptr)
audio_channel_mask_t soundChannelMask = sound->getChannelMask();
// When sound contains a valid channel mask, use it as is.
// Otherwise, use stream count to calculate channel mask.
audio_channel_mask_t channelMask = soundChannelMask != AUDIO_CHANNEL_NONE
? soundChannelMask : audio_channel_out_mask_from_count(channelCount);
// do not create a new audio track if current track is compatible with sound parameters
android::content::AttributionSourceState attributionSource;
attributionSource.packageName = mStreamManager->getOpPackageName();
attributionSource.token = sp<BBinder>::make();
mCallback = sp<StreamCallback>::make(this, toggle),
// TODO b/182469354 make consistent with AudioRecord, add util for native source
mAudioTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
mCallback,
0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
AudioTrack::TRANSFER_DEFAULT,
nullptr /*offloadInfo*/, attributionSource,
mStreamManager->getAttributes(),
false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/); //创建AudioTrack
// Set caller name so it can be logged in destructor.
// MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL
mAudioTrack->setCallerName("soundpool");
if (status_t status = mAudioTrack->initCheck();
status != NO_ERROR) {
ALOGE("%s: error %d creating AudioTrack", __func__, status);
// TODO: should we consider keeping the soundID and reusing the old track?
mState = IDLE;
mSoundID = 0;
mSound.reset();
garbage.emplace_back(std::move(mAudioTrack)); // remove mAudioTrack.
mAudioTrack.clear(); // move should have cleared the sp<>, but we clear just in case.
return;
}
// From now on, AudioTrack callbacks received with previous toggle value will be ignored.
mToggle = toggle;
ALOGV("%s: using new track %p for sound %d",
__func__, mAudioTrack.get(), sound->getSoundID());
}
if (mMuted) {
mAudioTrack->setVolume(0.f, 0.f); //设置AudioTrack Volume
} else {
mAudioTrack->setVolume(leftVolume, rightVolume);
}
mAudioTrack->setLoop(0, frameCount, loop); //设置AudioTrack Loop
mAudioTrack->start(); //start AudioTrack播放
mSound = sound;
mSoundID = sound->getSoundID();
mPriority = priority;
mLoop = loop;
mLeftVolume = leftVolume;
mRightVolume = rightVolume;
mRate = rate;
mState = PLAYING;
mStopTimeNs = 0;
mStreamID = nextStreamID; // prefer this to be the last, as it is an atomic sync point
}
上面方法主要处理如下:
1、创建AudioTrack对象
2、调用AudioTrack的setSampleRate方法设置播放波特率
3、调用AudioTrack的setVolume方法,设置Track音量
4、调用AudioTrack的setLoop方法,设置音频循环播放
5、调用AudioTrack的start方法,播放Track
下面分别进行分析:
new AudioTrack
待更新
AudioTrack start
待更新