文章目录

  • 一、SoundPool简介
  • 二、SoundPool API概述
    • 2.1、SoundPool Java层API
  • 三、SoundPool用例
  • 四、SoundPool实例的构建
    • 4.1、Java层SoundPool的创建
    • 4.2、JNI层SoundPool的创建
    • 4.3、Native层SoundPool的构建
    • 4.4、StreamManager的初始化
  • 五、SoundPool资源的加载
    • 5.1、Java层SoundPool资源的加载
    • 5.2、JNI层资源的加载
    • 5.3、Native层资源的加载
  • 六、加载完成的回调函数
    • 6.1、Java层回调函数
      • 6.1.1、回调函数接口
      • 6.1.2、回调函数的注册
    • 6.2、JNI层的回调函数
    • 6.3、回调函数的调用
  • 七、SoundPool播放音频
    • 7.1、Java层播放音频
    • 7.2、JNI层播放音频
    • 7.3、Native层播放音频

一、SoundPool简介

    SoundPool可以用来给应用程序管理或者播放音频资源。一个SoundPool是指从应用程序或者文件系统中加载进内存的音频样本的集合。SoundPool利用MediaCodec服务来将音频资源解码成16-bit的PCM数据流,这允许应用程序发送压缩流,而不必在播放期间承受 CPU 加载和解压缩延迟。
    SoundPool加载的音频资源由于是加载进内存,所以要求尽可能的短。每个音频资源的大小被限制在1M,差不多相当于时长5.6s,采样率在44.1kHz的双声道音频资源。如果超过这个限制大小就会被裁剪。
    为了低延迟播放,SoundPool在创建的时候就设置了同一时间播放音频流的数目,如果超过了这个限制,SoundPool将会根据优先级(同优先级根据年龄)自动停止先前播放的音频流。

二、SoundPool API概述

2.1、SoundPool Java层API

Interface Description
release() 释放SoundPool使用的资源
release() 释放SoundPool使用的资源
load() 加载音频资源,并返回一个SoundId,一共重载了四种load方法
unload() 利用load得到的SoundId来卸载音频资源,与load对应
play() 播放SoundId对应的音频资源,并返回一个用于控制的StreamId
pause() 暂停StreamId对应的音频资源
resume() 恢复StreamId对应的音频资源,与pause对应
autoPause() 暂停所有当前SoundPool播放的音频流
autoResume() 恢复所有AutoPause暂停的音频流,与autoPause对应
stop() 停止StreamId对应的音频流,并且释放对应的Native层资源
setVolume() 设置声音大小,分为左右声道,对应的大小从0~1,还有一个重载方法setVolume,不区分左右声道,一起设置同一增益
setPriority() 设置当前streamId对应流的优先级
setLoop() 设置循环,0:不循环,-1:一直循环,others:循环的次数
setRate() 设置播放速度
setOnLoadCompleteListener() 设置加载完成后的回调函数,需要实现OnLoadCompleteListener接口

三、SoundPool用例

fun testSoundPool() {soundPool = SoundPool.Builder().setAudioAttributes(AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build()).build()sourceId = soundPool.load(context, R.raw.testaudio, 1)soundPool.setOnLoadCompleteListener{ _, _, status ->if (status == 0) {//non-zero streamID if successful, zero if failedsoundPool.play(sourceId, 1F, 1F, 0, -1, 1F);isPlaying = true} else {Log.e(TAG, "LoadSound Failed")}
}

四、SoundPool实例的构建

4.1、Java层SoundPool的创建

     private SoundPool(int maxStreams, AudioAttributes attributes) {super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL);// do native setupif (native_setup(new WeakReference<SoundPool>(this),maxStreams, attributes, getCurrentOpPackageName()) != 0) {throw new RuntimeException("Native setup failed");}mAttributes = attributes;// FIXME: b/174876164 implement session id for soundpoolbaseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE);}/*** Sets the {@link AudioAttributes}. For examples, game applications will use attributes* built with usage information set to {@link AudioAttributes#USAGE_GAME}.* @param attributes a non-null* @return*/public Builder setAudioAttributes(AudioAttributes attributes)throws IllegalArgumentException {if (attributes == null) {throw new IllegalArgumentException("Invalid null AudioAttributes");}mAudioAttributes = attributes;return this;}public SoundPool build() {if (mAudioAttributes == null) {mAudioAttributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();}return new SoundPool(mMaxStreams, mAudioAttributes);}}

创建SundPool主要需要两个参数,mMaxStream和mAudioAttributes,mMaxStream可以通过setMaxStream来设置,不设置的话默认是1,mAudioAttribute主要是音频流的属性,最后还是通过JNI调用Native层的方法创建SoundPool对象。

4.2、JNI层SoundPool的创建

frameworks/base/media/jni/soundpool/android_media_SoundPool.cpp

static jint
android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef,jint maxChannels, jobject jaa, jstring opPackageName)
{if (jaa == nullptr) {ALOGE("Error creating SoundPool: invalid audio attributes");return -1;}audio_attributes_t *paa = nullptr;// read the AudioAttributes values// 将Java层的AudioAttribute转换到Native层对应的结构体中paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));const auto jtags =(jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);const char* tags = env->GetStringUTFChars(jtags, nullptr);// copying array size -1, char array for tags was calloc'd, no need to NULL-terminate itstrncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);env->ReleaseStringUTFChars(jtags, tags);paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);paa->content_type =(audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);paa->flags = (audio_flags_mask_t) env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);ALOGV("android_media_SoundPool_native_setup");ScopedUtfChars opPackageNameStr(env, opPackageName);auto *ap = new SoundPool(maxChannels, paa, opPackageNameStr.c_str());if (ap == nullptr) {return -1;}// save pointer to SoundPool C++ object in opaque field in Java objectenv->SetLongField(thiz, fields.mNativeContext, (jlong) ap);// set callback with weak referencejobject globalWeakRef = env->NewGlobalRef(weakRef);ap->setCallback(android_media_callback, globalWeakRef);// audio attributes were copied in SoundPool creationfree(paa);return 0;
}

这里的mMaxStream到了Native层成立对应的音频通道,构建Native层的SoundPool实例。

4.3、Native层SoundPool的构建

frameworks/base/media/jni/soundpool/SoundPool.cpp

// kManagerThreads = 1 historically.
// Not really necessary to have more than one, but it does speed things up by about
// 25% having 2 threads instead of 1 when playing many sounds.  Having many threads
// could starve other AudioFlinger clients with SoundPool activity. It may also cause
// issues with app loading, e.g. Camera.
static const size_t kStreamManagerThreads = std::thread::hardware_concurrency() >= 4 ? 2 : 1;SoundPool::SoundPool(int32_t maxStreams, const audio_attributes_t* attributes, const std::string& opPackageName): mStreamManager(maxStreams, kStreamManagerThreads, attributes, opPackageName)
{ALOGV("%s(maxStreams=%d, attr={ content_type=%d, usage=%d, flags=0x%x, tags=%s })",__func__, maxStreams,attributes->content_type, attributes->usage, attributes->flags, attributes->tags);
}

这里主要是将对应的参数传入mStreamManager初始化,StreamManagerSoundPool作用域中用来管理流的工具,可以通过StreamId在Java层访问。

4.4、StreamManager的初始化

StreamManager::StreamManager(int32_t streams, size_t threads, const audio_attributes_t* attributes,std::string opPackageName): StreamMap(streams), mAttributes(*attributes), mOpPackageName(std::move(opPackageName)), mLockStreamManagerStop(streams == 1 || kForceLockStreamManagerStop)
{ALOGV("%s(%d, %zu, ...)", __func__, streams, threads);forEach([this](Stream *stream) {stream->setStreamManager(this);if ((streamPosition(stream) & 1) == 0) { // put the first stream of pair as available.mAvailableStreams.insert(stream);}});mThreadPool = std::make_unique<ThreadPool>(std::min((size_t)streams,  // do not make more threads than streams to playstd::min(threads, (size_t)std::thread::hardware_concurrency())),"SoundPool_");
}

设置可用音频流mAvailableStreams是将每一对音频流的第一个音频流加入这个Set中,即(0,2,4,…),然后创建一个线程池,实际上由于kStreamManagerThreads的影响,线程池大小不会超过2。

创建完成SoundPool实例之后,将对应的指针传回到Java层,并将Java层刚开始传入的的弱引用升级成JNI层的全局引用以备后续使用,同时提供了对应的回调函数接口,至此结束了SoundPool Native层对象的创建。

五、SoundPool资源的加载

5.1、Java层SoundPool资源的加载

    /*** Load the sound from the specified APK resource.** Note that the extension is dropped. For example, if you want to load* a sound from the raw resource file "explosion.mp3", you would specify* "R.raw.explosion" as the resource ID. Note that this means you cannot* have both an "explosion.wav" and an "explosion.mp3" in the res/raw* directory.** @param context the application context* @param resId the resource ID* @param priority the priority of the sound. Currently has no effect. Use*                 a value of 1 for future compatibility.* @return a sound ID. This value can be used to play or unload the sound.*/public int load(Context context, int resId, int priority) {AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);int id = 0;if (afd != null) {id = _load(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength(), priority);try {afd.close();} catch (java.io.IOException ex) {//Log.d(TAG, "close failed:", ex);}}return id;}/*** Load the sound from an asset file descriptor.** @param afd an asset file descriptor* @param priority the priority of the sound. Currently has no effect. Use*                 a value of 1 for future compatibility.* @return a sound ID. This value can be used to play or unload the sound.*/public int load(AssetFileDescriptor afd, int priority) {if (afd != null) {long len = afd.getLength();if (len < 0) {throw new AndroidRuntimeException("no length for fd");}return _load(afd.getFileDescriptor(), afd.getStartOffset(), len, priority);} else {return 0;}}/*** Load the sound from a FileDescriptor.** This version is useful if you store multiple sounds in a single* binary. The offset specifies the offset from the start of the file* and the length specifies the length of the sound within the file.** @param fd a FileDescriptor object* @param offset offset to the start of the sound* @param length length of the sound* @param priority the priority of the sound. Currently has no effect. Use*                 a value of 1 for future compatibility.* @return a sound ID. This value can be used to play or unload the sound.*/public int load(FileDescriptor fd, long offset, long length, int priority) {return _load(fd, offset, length, priority);}

SoundPool资源的加载方式主要有三种,分别是

  1. 加载特定的APK资源:这里注意不区分文件扩展名,示例中所使用的方式
  2. 通过资源文件描述符加载
  3. 通过文件描述符加载

实际上三种方法最终都要获取对应的文件描述符来调用对应的方法。

5.2、JNI层资源的加载

static jint
android_media_SoundPool_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,jlong offset, jlong length, jint priority)
{ALOGV("android_media_SoundPool_load_FD");//将Java层的实例转换成Native层的实例SoundPool *ap = MusterSoundPool(env, thiz);if (ap == nullptr) return 0;return (jint) ap->load(jniGetFDFromFileDescriptor(env, fileDescriptor),int64_t(offset), int64_t(length), int(priority));
}

5.3、Native层资源的加载

int32_t SoundPool::load(int fd, int64_t offset, int64_t length, int32_t priority)
{ALOGV("%s(fd=%d, offset=%lld, length=%lld, priority=%d)",__func__, fd, (long long)offset, (long long)length, priority);auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;return mSoundManager.load(fd, offset, length, priority);
}

frameworks/base/media/jni/soundpool/SoundManager.cpp

int32_t SoundManager::load(int fd, int64_t offset, int64_t length, int32_t priority)
{ALOGV("%s(fd=%d, offset=%lld, length=%lld, priority=%d)",__func__, fd, (long long)offset, (long long)length, priority);int32_t soundID;{std::lock_guard lock(mSoundManagerLock);// mNextSoundID is always positive and does not "integer overflow"do {mNextSoundID = mNextSoundID == INT32_MAX ? 1 : mNextSoundID + 1;} while (findSound_l(mNextSoundID) != nullptr);soundID = mNextSoundID;auto sound = std::make_shared<Sound>(soundID, fd, offset, length);mSounds.emplace(soundID, sound);}// mDecoder->loadSound() must be called outside of mSoundManagerLock.// mDecoder->loadSound() may block on mDecoder message queue space;// the message queue emptying may block on SoundManager::findSound().//// It is theoretically possible that sound loads might decode out-of-order.mDecoder->loadSound(soundID);return soundID;
}

frameworks/base/media/jni/soundpool/SoundDecoder.cpp

void SoundDecoder::loadSound(int32_t soundID)
{ALOGV("%s(%d)", __func__, soundID);size_t pendingSounds;{std::unique_lock lock(mLock);while (mSoundIDs.size() == kMaxQueueSize) {if (mQuit) return;ALOGV("%s: waiting soundID: %d size: %zu", __func__, soundID, mSoundIDs.size());mQueueSpaceAvailable.wait(lock);}if (mQuit) return;mSoundIDs.push_back(soundID);mQueueDataAvailable.notify_one();ALOGV("%s: adding soundID: %d  size: %zu", __func__, soundID, mSoundIDs.size());pendingSounds = mSoundIDs.size();}// Launch threads as needed.  The "as needed" is weakly consistent as we release mLock.if (pendingSounds > mThreadPool->getActiveThreadCount()) {const int32_t id = mThreadPool->launch([this](int32_t id) { run(id); });(void)id; // avoid clang warning -Wunused-variable -Wused-but-marked-unusedALOGV_IF(id != 0, "%s: launched thread %d", __func__, id);}
}

开始解码

void SoundDecoder::run(int32_t id)
{ALOGV("%s(%d): entering", __func__, id);std::unique_lock lock(mLock);while (!mQuit) {if (mSoundIDs.size() == 0) {ALOGV("%s(%d): waiting", __func__, id);mQueueDataAvailable.wait_for(lock, std::chrono::duration<int32_t, std::milli>(kWaitTimeBeforeCloseMs));if (mSoundIDs.size() == 0) {break; // no new sound, exit this thread.}continue;}const int32_t soundID = mSoundIDs.front();mSoundIDs.pop_front();mQueueSpaceAvailable.notify_one();ALOGV("%s(%d): processing soundID: %d  size: %zu", __func__, id, soundID, mSoundIDs.size());lock.unlock();std::shared_ptr<Sound> sound = mSoundManager->findSound(soundID);status_t status = NO_INIT;if (sound.get() != nullptr) {status = sound->doLoad();}ALOGV("%s(%d): notifying loaded soundID:%d  status:%d", __func__, id, soundID, status);mSoundManager->notify(SoundPoolEvent(SoundPoolEvent::SOUND_LOADED, soundID, status));lock.lock();}ALOGV("%s(%d): exiting", __func__, id);
}

无论成功或失败,向上层发送SOUND_LOADED消息事件,并伴随status,主要通过status来判断加载是否成功。

六、加载完成的回调函数

6.1、Java层回调函数

Java层主要有一个OnLoadCompleteListener接口,如果用户有需要再加载完成后所作的操作(例如播放音频),可以实现这个接口。

6.1.1、回调函数接口

    public interface OnLoadCompleteListener {/*** Called when a sound has completed loading.** @param soundPool SoundPool object from the load() method* @param sampleId 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);}

6.1.2、回调函数的注册

    /*** Sets the callback hook for the OnLoadCompleteListener.*/public void setOnLoadCompleteListener(OnLoadCompleteListener listener) {if (listener == null) {mEventHandler.set(null);return;}Looper looper;if ((looper = Looper.myLooper()) != null) {mEventHandler.set(new EventHandler(looper, listener));} else if ((looper = Looper.getMainLooper()) != null) {mEventHandler.set(new EventHandler(looper, listener));} else {mEventHandler.set(null);}}

完成回调函数的创建之后调用SoundPool.setOnLoadCompleteListener方法,将SoundPool中的mEventHandler对象置为新的事件对象。

    // post event from native code to message handler@SuppressWarnings("unchecked")private static void postEventFromNative(Object ref, int msg, int arg1, int arg2, Object obj) {SoundPool soundPool = ((WeakReference<SoundPool>) ref).get();if (soundPool == null) {return;}Handler eventHandler = soundPool.mEventHandler.get();if (eventHandler == null) {return;}Message message = eventHandler.obtainMessage(msg, arg1, arg2, obj);eventHandler.sendMessage(message);}private final class EventHandler extends Handler {private final OnLoadCompleteListener mOnLoadCompleteListener;EventHandler(Looper looper, @NonNull OnLoadCompleteListener onLoadCompleteListener) {super(looper);mOnLoadCompleteListener = onLoadCompleteListener;}@Overridepublic void handleMessage(Message msg) {if (msg.what != SAMPLE_LOADED) {Log.e(TAG, "Unknown message type " + msg.what);return;}if (DEBUG) Log.d(TAG, "Sample " + msg.arg1 + " loaded");mOnLoadCompleteListener.onLoadComplete(SoundPool.this, msg.arg1, msg.arg2);}}

postEventFromNative是Native层需要调用的方法,当Native完成加载之后会调用该方法,该方法会发送SAMPLE_LOADED的Message给上层,EventHandler中处理该消息调用对应接口实现类中的onLoadComplete方法。

6.2、JNI层的回调函数

JNI层初始化时postEventFromNative的函数指针也被传入了JNI层用于后面加载完成后回调函数的调用

jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative","(Ljava/lang/Object;IIILjava/lang/Object;)V");if (fields.mPostEvent == nullptr) {ALOGE("Can't find android/media/SoundPool.postEventFromNative");return result;}
}

JNI层也有对应回调函数的指针,在SoundPool初始化时传入

//调用对应的Java层通知函数
static void android_media_callback(SoundPoolEvent event, SoundPool* soundPool, void* user)
{ALOGV("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,nullptr /* object */);
}static jint
android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef,jint maxChannels, jobject jaa, jstring opPackageName)
{...// set callback with weak referencejobject globalWeakRef = env->NewGlobalRef(weakRef);ap->setCallback(android_media_callback, globalWeakRef);//设置回调函数...
}

这里主要传入对应的调用Java层回调函数的函数指针,这个指针需要传入三个参数,SoundPoolEvent事件,SoundPool实例,与用户指针(Java层传入的SoundPool对象指针的引用)。

6.3、回调函数的调用

第四章最后我们可以知道加载完成之后,会构建新的SoundPoolEvent消息实体,注入到SoundManager中。

void SoundManager::notify(SoundPoolEvent event)
{mCallbackHandler.notify(event);
}

frameworks/base/media/jni/soundpool/SoundManager.h

    // CallbackHandler is used to manage notifications back to the app when a sound// has been loaded.  It uses a recursive lock to allow setting the callback// during the callback.class CallbackHandler {public:void setCallback(SoundPool *soundPool, SoundPoolCallback* callback, void* userData){std::lock_guard<std::recursive_mutex> lock(mCallbackLock);mSoundPool = soundPool;mCallback = callback;mUserData = userData;}void notify(SoundPoolEvent event) const{std::lock_guard<std::recursive_mutex> lock(mCallbackLock);if (mCallback != nullptr) {mCallback(event, mSoundPool, mUserData);// Note: mCallback may call setCallback().// so mCallback, mUserData may have changed.}}}

这里我们知道mCallback实际上对应的就是JNI层的函数android_media_callback的函数指针,这里通过对应的事件消息开始调用这个函数。android_media_callback实际上通过JNI层的CallStaticVoidMethod调用Java层的postEventFromNative方法,最终我们回到了postEventFromNative中调用onLoadComplete方法。

七、SoundPool播放音频

7.1、Java层播放音频

    /*** Play a sound from a sound ID.** Play the sound specified by the soundID. This is the value* returned by the load() function. Returns a non-zero streamID* if successful, zero if it fails. The streamID can be used to* further control playback. Note that calling play() may cause* another sound to stop playing if the maximum number of active* streams is exceeded. A loop value of -1 means loop forever,* a value of 0 means don't loop, other values indicate the* number of repeats, e.g. a value of 1 plays the audio twice.* The playback rate allows the application to vary the playback* rate (pitch) of the sound. A value of 1.0 means play back at* the original frequency. A value of 2.0 means play back twice* as fast, and a value of 0.5 means playback at half speed.** @param soundID a soundID returned by the load() function* @param leftVolume left volume value (range = 0.0 to 1.0)* @param rightVolume right volume value (range = 0.0 to 1.0)* @param priority stream priority (0 = lowest priority)* @param loop loop mode (0 = no loop, -1 = loop forever)* @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)* @return non-zero streamID if successful, zero if failed*/public final int play(int soundID, float leftVolume, float rightVolume,int priority, int loop, float rate) {// FIXME: b/174876164 implement device id for soundpoolbaseStart(0);return _play(soundID, leftVolume, rightVolume, priority, loop, rate);}

7.2、JNI层播放音频

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");SoundPool *ap = MusterSoundPool(env, thiz);if (ap == nullptr) return 0;return (jint) ap->play(sampleID, leftVolume, rightVolume, priority, loop, rate);
}

7.3、Native层播放音频

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);if (sound == nullptr || sound->getState() != soundpool::Sound::READY) {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;
}

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 lockstd::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 listif (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__);} // lockif (launchThread) {const int32_t id = mThreadPool->launch([this](int32_t id) { run(id); });(void)id; // avoid clang warning -Wunused-variable -Wused-but-marked-unusedALOGV_IF(id != 0, "%s: launched thread %d", __func__, id);}ALOGV("%s: returning %d", __func__, streamID);// garbage is cleared here outside mStreamManagerLock.return streamID;
}

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 statepairStream->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 pausepairStream->mState = PAUSED;pairStream->mAudioTrack->pause();}}return pairStream;
}
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 trackconst 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) {// 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.// The toggle is concatenated with the Stream address and passed to AudioTrack// as callback user data. 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)void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle);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 parametersandroid::content::AttributionSourceState attributionSource;attributionSource.packageName = mStreamManager->getOpPackageName();attributionSource.token = sp<BBinder>::make();// TODO b/182469354 make consistent with AudioRecord, add util for native sourcemAudioTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,staticCallback, userData,0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,AudioTrack::TRANSFER_DEFAULT,nullptr /*offloadInfo*/, attributionSource,mStreamManager->getAttributes(),false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/);// Set caller name so it can be logged in destructor.// MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOLmAudioTrack->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);} else {mAudioTrack->setVolume(leftVolume, rightVolume);}mAudioTrack->setLoop(0, frameCount, loop);mAudioTrack->start();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
}

这里主要是操作AudioTrack

SoundPool概述相关推荐

  1. Java 多线程概述

    多线程技术概述 1.线程与进程 进程:内存中运行的应用程序,每个进程都拥有一个独立的内存空间. 线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换.并发执行,一个进程最少有一个线程, ...

  2. 【SpringMVC】概述

    概述: SpringMVC:是基于spring的一个框架, 实际上就是spring的一个模块, 专门是做web开发的.                       理解是servlet的一个升级 Sp ...

  3. 梯度下降优化算法概述

    本文原文是 An overview of gradient descent optimization algorithms,同时作者也在 arXiv 上发了一篇同样内容的 论文. 本文结合了两者来翻译 ...

  4. Redis概述和基础

    Redis 1.NoSQL NoSQL = Not Only SQL(不仅仅是SQL) 泛指非关系型数据库的,随着web2.0互联网的诞生!传统的关系型数据库很难对付web2.0时代!尤其是超大规模的 ...

  5. OpenCL™(开放计算语言)概述

    OpenCL™(开放计算语言)概述 异构系统并行编程的开准 OpenCL™(开放计算语言)是一种开放的.免版税的标准,用于对超级计算机.云服务器.个人计算机.移动设备和嵌入式平台中的,各种加速器进行跨 ...

  6. 自动驾驶QNX,Linux,Autosar概述

    自动驾驶QNX,Linux,Autosar概述 QNX是一个分布式.嵌入式.可规模扩展的实时操作系统.遵循POSIX.1 (程序接口)和POSIX.2 (Shell和工具).部分遵循POSIX.1b( ...

  7. Tengine MLOps概述

    Tengine MLOps概述 大幅提高产业应用从云向边缘迁移的效率 MLOps Cloud Native 聚焦于提升云端的运营过程效率 MLOps Edge Native 聚焦于解决边缘应用开发及异 ...

  8. Tengine Web服务器概述

    Tengine Web服务器概述 Tengine是由淘宝网发起的Web服务器项目.在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性.目的是打造一个高效.安全的Web平台. 发展 ...

  9. Docker基本原理概述

    Docker基本原理概述 Docker是一个用于开发,交付和运行应用程序的开放平台.Docker能够将应用程序与基础架构分开,从而可以快速交付软件.借助Docker,可以以与管理应用程序相同的方式来管 ...

  10. HiCar SDK概述

    HiCar SDK概述 HUAWEI HiCar SDK 是 HUAWEI HiCar(以下简称 HiCar )为汽车硬件设备提供的软件开发工具包,为汽车硬件厂商接入 HiCar 提供应用 API 接 ...

最新文章

  1. Cell综述-建立因果关系:合成菌群在植物菌群研究中的机会
  2. 机器学习博士自曝:实验室「阉割」我的创造力,劝你别读
  3. 二进制字符串模5的值(有限状态机法)
  4. java验证码识别--2
  5. 6条 Tips 为你照亮 GitHub 寻宝之路
  6. seo黑帽劫持用的php,黑帽seo 论坛:黑帽seo防止网站被k的js劫持跳转代码
  7. Java并发编程包中atomic的实现原理
  8. 缺少using namespace std;
  9. saltstack的简单安装和配置
  10. 8 年经验面试官详解 Java 面试秘诀!
  11. 树莓派Python 3.x+TensorFlow 1.9.0
  12. oracle离散度,统计学基础知识之数据离散程度描述
  13. 谷歌、火狐浏览器驱动下载
  14. 现代软件工程_项目回顾模板
  15. 用于AB测试的减少方差方法总结和对比
  16. 百度指数批量查询器,百度指数
  17. java blowfish 算法_Java Blowfish对称密钥加密算法的实现和加密案例
  18. 日赚1.7亿!华为发布2020年度财报!附华为十大5G应用场景
  19. java 生成kml文件_如何使用kml文件在地图上绘制路径?
  20. 述职答辩提问环节一般可以问些什么_什么是述职。述职会问些什么问题。

热门文章

  1. 电子商业汇票知识问答题
  2. 用Docker在一台笔记本电脑上搭建一个具有10个节点7种角色的Hadoop集群(上)-快速上手Docker...
  3. 个人理财系统springboot项目开发(一)需求分析文档
  4. ArrayList的removeAll和retainAll方法
  5. 【已解决】Magisk提示需要修复运行环境怎么办? | 面具提示需要修复运行环境怎么办?
  6. java计算机毕业设计教务排课系统MyBatis+系统+LW文档+源码+调试部署
  7. pytorch实现textCNN
  8. 视频翻译软件有哪些?好用的视频翻译软件推荐
  9. sketch如何做设计稿交互_sketch交互点击视觉标注方法|sketch如何实现交互点击的视觉标注 - PS下...
  10. u盘文件名乱码linux,U盘文件名乱码的原因和解决办法