| /* |
| * 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. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "SoundPool" |
| #include <utils/Log.h> |
| |
| #include <algorithm> |
| #include <thread> |
| |
| #include "SoundPool.h" |
| |
| namespace android |
| { |
| |
| // 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; |
| |
| // kUseApiLock = true prior to R. |
| // Set to true to prevent multiple users access internal to the SoundPool API. |
| // Set to false to make the SoundPool methods weakly consistent. When set to false, |
| // only AutoPause and AutoResume are locked, which are the only two methods that |
| // require API level locking for consistency. |
| static constexpr bool kUseApiLock = false; |
| |
| namespace { |
| // Check input arguments to SoundPool - return "true" to reject request. |
| |
| bool checkVolume(float *leftVolume, float *rightVolume) |
| { |
| if (*leftVolume != std::clamp(*leftVolume, 0.f, 1.f) || |
| *rightVolume != std::clamp(*rightVolume, 0.f, 1.f)) { |
| ALOGI("volume l=%f r=%f out of (0.f, 1.f) bounds, using 1.f", *leftVolume, *rightVolume); |
| // for backward compatibility use 1.f. |
| *leftVolume = *rightVolume = 1.f; |
| } |
| return false; |
| } |
| |
| bool checkRate(float *rate) |
| { |
| if (*rate != std::clamp(*rate, 0.125f, 8.f)) { |
| ALOGI("rate %f out of (0.125f, 8.f) bounds, clamping", *rate); |
| // for backward compatibility just clamp |
| *rate = std::clamp(*rate, 0.125f, 8.f); |
| } |
| return false; |
| } |
| |
| bool checkPriority(int32_t *priority) |
| { |
| if (*priority < 0) { |
| ALOGI("negative priority %d, should be >= 0.", *priority); |
| // for backward compatibility, ignore. |
| } |
| return false; |
| } |
| |
| bool checkLoop(int32_t *loop) |
| { |
| if (*loop < -1) { |
| ALOGI("loop %d, should be >= -1", *loop); |
| *loop = -1; |
| } |
| return false; |
| } |
| |
| } // namespace |
| |
| SoundPool::SoundPool(int32_t maxStreams, const audio_attributes_t* attributes) |
| : mStreamManager(maxStreams, kStreamManagerThreads, attributes) |
| { |
| 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); |
| } |
| |
| SoundPool::~SoundPool() |
| { |
| ALOGV("%s()", __func__); |
| } |
| |
| 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); |
| } |
| |
| bool SoundPool::unload(int32_t soundID) |
| { |
| ALOGV("%s(%d)", __func__, soundID); |
| auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr; |
| return mSoundManager.unload(soundID); |
| } |
| |
| 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; |
| } |
| |
| void SoundPool::autoPause() |
| { |
| ALOGV("%s()", __func__); |
| auto apiLock = std::make_unique<std::lock_guard<std::mutex>>(mApiLock); |
| mStreamManager.forEach([](soundpool::Stream *stream) { stream->autoPause(); }); |
| } |
| |
| void SoundPool::autoResume() |
| { |
| ALOGV("%s()", __func__); |
| auto apiLock = std::make_unique<std::lock_guard<std::mutex>>(mApiLock); |
| mStreamManager.forEach([](soundpool::Stream *stream) { stream->autoResume(); }); |
| } |
| |
| void SoundPool::mute(bool muting) |
| { |
| ALOGV("%s(%d)", __func__, muting); |
| auto apiLock = std::make_unique<std::lock_guard<std::mutex>>(mApiLock); |
| mStreamManager.forEach([=](soundpool::Stream *stream) { stream->mute(muting); }); |
| } |
| |
| void SoundPool::pause(int32_t streamID) |
| { |
| ALOGV("%s(%d)", __func__, streamID); |
| auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr; |
| if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) { |
| stream->pause(streamID); |
| } |
| } |
| |
| void SoundPool::resume(int32_t streamID) |
| { |
| ALOGV("%s(%d)", __func__, streamID); |
| auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr; |
| if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) { |
| stream->resume(streamID); |
| } |
| } |
| |
| void SoundPool::stop(int32_t streamID) |
| { |
| ALOGV("%s(%d)", __func__, streamID); |
| auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr; |
| soundpool::Stream* stream = mStreamManager.findStream(streamID); |
| if (stream != nullptr && stream->requestStop(streamID)) { |
| mStreamManager.moveToRestartQueue(stream); |
| } |
| } |
| |
| void SoundPool::setVolume(int32_t streamID, float leftVolume, float rightVolume) |
| { |
| ALOGV("%s(%d, %f %f)", __func__, streamID, leftVolume, rightVolume); |
| if (checkVolume(&leftVolume, &rightVolume)) return; |
| auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr; |
| if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) { |
| stream->setVolume(streamID, leftVolume, rightVolume); |
| } |
| } |
| |
| void SoundPool::setPriority(int32_t streamID, int32_t priority) |
| { |
| ALOGV("%s(%d, %d)", __func__, streamID, priority); |
| if (checkPriority(&priority)) return; |
| auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr; |
| if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) { |
| stream->setPriority(streamID, priority); |
| } |
| } |
| |
| void SoundPool::setLoop(int32_t streamID, int32_t loop) |
| { |
| ALOGV("%s(%d, %d)", __func__, streamID, loop); |
| if (checkLoop(&loop)) return; |
| auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr; |
| if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) { |
| stream->setLoop(streamID, loop); |
| } |
| } |
| |
| void SoundPool::setRate(int32_t streamID, float rate) |
| { |
| ALOGV("%s(%d, %f)", __func__, streamID, rate); |
| if (checkRate(&rate)) return; |
| auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr; |
| if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) { |
| stream->setRate(streamID, rate); |
| } |
| } |
| |
| void SoundPool::setCallback(SoundPoolCallback* callback, void* user) |
| { |
| ALOGV("%s(%p, %p)", __func__, callback, user); |
| auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr; |
| mSoundManager.setCallback(this, callback, user); |
| } |
| |
| void* SoundPool::getUserData() const |
| { |
| ALOGV("%s()", __func__); |
| auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr; |
| return mSoundManager.getUserData(); |
| } |
| |
| } // end namespace android |