123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- /****************************************************************************
- Copyright (c) 2014-2017 Chukong Technologies Inc.
- http://www.cocos2d-x.org
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- ****************************************************************************/
- #if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
- #define LOG_TAG "cocos2d-x debug info"
- #include "audio/android/AudioEngine-inl.h"
- #include <unistd.h>
- // for native asset manager
- #include <sys/types.h>
- #include <android/asset_manager.h>
- #include <android/asset_manager_jni.h>
- #include <unordered_map>
- #include "platform/android/jni/JniHelper.h"
- #include <android/log.h>
- #include <jni.h>
- #include "audio/include/AudioEngine.h"
- #include "base/CCDirector.h"
- #include "base/CCScheduler.h"
- #include "base/CCEventDispatcher.h"
- #include "base/CCEventType.h"
- #include "base/CCEventListenerCustom.h"
- #include "platform/android/CCFileUtils-android.h"
- #include "platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h"
- #include "audio/android/IAudioPlayer.h"
- #include "audio/android/ICallerThreadUtils.h"
- #include "audio/android/AudioPlayerProvider.h"
- #include "audio/android/cutils/log.h"
- #include "audio/android/UrlAudioPlayer.h"
- using namespace cocos2d;
- using namespace cocos2d::experimental;
- #define DELAY_TIME_TO_REMOVE 0.5f
- class CallerThreadUtils : public ICallerThreadUtils
- {
- public:
- virtual void performFunctionInCallerThread(const std::function<void()>& func)
- {
- Director::getInstance()->getScheduler()->performFunctionInCocosThread(func);
- };
- virtual std::thread::id getCallerThreadId()
- {
- return _tid;
- };
- void setCallerThreadId(std::thread::id tid)
- {
- _tid = tid;
- };
- private:
- std::thread::id _tid;
- };
- static CallerThreadUtils __callerThreadUtils;
- static int fdGetter(const std::string& url, off_t* start, off_t* length)
- {
- int fd = -1;
- if (cocos2d::FileUtilsAndroid::getObbFile() != nullptr)
- {
- fd = getObbAssetFileDescriptorJNI(url.c_str(), start, length);
- }
- else
- {
- auto asset = AAssetManager_open(cocos2d::FileUtilsAndroid::getAssetManager(), url.c_str(), AASSET_MODE_UNKNOWN);
- // open asset as file descriptor
- fd = AAsset_openFileDescriptor(asset, start, length);
- AAsset_close(asset);
- }
- if (fd <= 0)
- {
- ALOGE("Failed to open file descriptor for '%s'", url.c_str());
- }
- return fd;
- };
- //====================================================
- AudioEngineImpl::AudioEngineImpl()
- : _engineObject(nullptr)
- , _engineEngine(nullptr)
- , _outputMixObject(nullptr)
- , _audioPlayerProvider(nullptr)
- , _onPauseListener(nullptr)
- , _onResumeListener(nullptr)
- , _audioIDIndex(0)
- , _lazyInitLoop(true)
- {
- __callerThreadUtils.setCallerThreadId(std::this_thread::get_id());
- }
- AudioEngineImpl::~AudioEngineImpl()
- {
- if (_audioPlayerProvider != nullptr)
- {
- delete _audioPlayerProvider;
- _audioPlayerProvider = nullptr;
- }
- if (_outputMixObject)
- {
- (*_outputMixObject)->Destroy(_outputMixObject);
- }
- if (_engineObject)
- {
- (*_engineObject)->Destroy(_engineObject);
- }
- if (_onPauseListener != nullptr)
- {
- Director::getInstance()->getEventDispatcher()->removeEventListener(_onPauseListener);
- }
- if (_onResumeListener != nullptr)
- {
- Director::getInstance()->getEventDispatcher()->removeEventListener(_onResumeListener);
- }
- }
- bool AudioEngineImpl::init()
- {
- bool ret = false;
- do{
- // create engine
- auto result = slCreateEngine(&_engineObject, 0, nullptr, 0, nullptr, nullptr);
- if(SL_RESULT_SUCCESS != result){ ERRORLOG("create opensl engine fail"); break; }
- // realize the engine
- result = (*_engineObject)->Realize(_engineObject, SL_BOOLEAN_FALSE);
- if(SL_RESULT_SUCCESS != result){ ERRORLOG("realize the engine fail"); break; }
- // get the engine interface, which is needed in order to create other objects
- result = (*_engineObject)->GetInterface(_engineObject, SL_IID_ENGINE, &_engineEngine);
- if(SL_RESULT_SUCCESS != result){ ERRORLOG("get the engine interface fail"); break; }
- // create output mix
- const SLInterfaceID outputMixIIDs[] = {};
- const SLboolean outputMixReqs[] = {};
- result = (*_engineEngine)->CreateOutputMix(_engineEngine, &_outputMixObject, 0, outputMixIIDs, outputMixReqs);
- if(SL_RESULT_SUCCESS != result){ ERRORLOG("create output mix fail"); break; }
- // realize the output mix
- result = (*_outputMixObject)->Realize(_outputMixObject, SL_BOOLEAN_FALSE);
- if(SL_RESULT_SUCCESS != result){ ERRORLOG("realize the output mix fail"); break; }
- _audioPlayerProvider = new AudioPlayerProvider(_engineEngine, _outputMixObject, getDeviceSampleRate(), getDeviceAudioBufferSizeInFrames(), fdGetter, &__callerThreadUtils);
- _onPauseListener = Director::getInstance()->getEventDispatcher()->addCustomEventListener(EVENT_COME_TO_BACKGROUND, CC_CALLBACK_1(AudioEngineImpl::onEnterBackground, this));
- _onResumeListener = Director::getInstance()->getEventDispatcher()->addCustomEventListener(EVENT_COME_TO_FOREGROUND, CC_CALLBACK_1(AudioEngineImpl::onEnterForeground, this));
- ret = true;
- }while (false);
- return ret;
- }
- void AudioEngineImpl::onEnterBackground(EventCustom* event)
- {
- // _audioPlayerProvider->pause() pauses AudioMixer and PcmAudioService,
- // but UrlAudioPlayers could not be paused.
- if (_audioPlayerProvider != nullptr)
- {
- _audioPlayerProvider->pause();
- }
- // pause UrlAudioPlayers which are playing.
- for (auto&& e : _audioPlayers)
- {
- auto player = e.second;
- if (dynamic_cast<UrlAudioPlayer*>(player) != nullptr
- && player->getState() == IAudioPlayer::State::PLAYING)
- {
- _urlAudioPlayersNeedResume.push_back(player);
- player->pause();
- }
- }
- }
- void AudioEngineImpl::onEnterForeground(EventCustom* event)
- {
- // _audioPlayerProvider->resume() resumes AudioMixer and PcmAudioService,
- // but UrlAudioPlayers could not be resumed.
- if (_audioPlayerProvider != nullptr)
- {
- _audioPlayerProvider->resume();
- }
- // resume UrlAudioPlayers
- for (auto&& player : _urlAudioPlayersNeedResume)
- {
- player->resume();
- }
- _urlAudioPlayersNeedResume.clear();
- }
- int AudioEngineImpl::play2d(const std::string &filePath ,bool loop ,float volume)
- {
- ALOGV("play2d, _audioPlayers.size=%d", (int)_audioPlayers.size());
- auto audioId = AudioEngine::INVALID_AUDIO_ID;
- do
- {
- if (_engineEngine == nullptr || _audioPlayerProvider == nullptr)
- break;
- auto fullPath = FileUtils::getInstance()->fullPathForFilename(filePath);
- audioId = _audioIDIndex++;
- auto player = _audioPlayerProvider->getAudioPlayer(fullPath);
- if (player != nullptr)
- {
- player->setId(audioId);
- _audioPlayers.insert(std::make_pair(audioId, player));
- player->setPlayEventCallback([this, player](IAudioPlayer::State state){
- if (state != IAudioPlayer::State::OVER && state != IAudioPlayer::State::STOPPED)
- {
- ALOGV("Ignore state: %d", static_cast<int>(state));
- return;
- }
- int id = player->getId();
- std::string filePath = *AudioEngine::_audioIDInfoMap[id].filePath;
- ALOGV("Removing player id=%d, state:%d", id, (int)state);
- AudioEngine::remove(id);
- _audioPlayers.erase(id);
- auto iter = _callbackMap.find(id);
- if (iter != _callbackMap.end())
- {
- if (state == IAudioPlayer::State::OVER)
- {
- iter->second(id, filePath);
- }
- _callbackMap.erase(iter);
- }
- });
- player->setLoop(loop);
- player->setVolume(volume);
- player->play();
- }
- else
- {
- ALOGE("Oops, player is null ...");
- return AudioEngine::INVALID_AUDIO_ID;
- }
- AudioEngine::_audioIDInfoMap[audioId].state = AudioEngine::AudioState::PLAYING;
-
- } while (0);
- return audioId;
- }
- void AudioEngineImpl::setVolume(int audioID,float volume)
- {
- auto iter = _audioPlayers.find(audioID);
- if (iter != _audioPlayers.end())
- {
- auto player = iter->second;
- player->setVolume(volume);
- }
- }
- void AudioEngineImpl::setLoop(int audioID, bool loop)
- {
- auto iter = _audioPlayers.find(audioID);
- if (iter != _audioPlayers.end())
- {
- auto player = iter->second;
- player->setLoop(loop);
- }
- }
- void AudioEngineImpl::pause(int audioID)
- {
- auto iter = _audioPlayers.find(audioID);
- if (iter != _audioPlayers.end())
- {
- auto player = iter->second;
- player->pause();
- }
- }
- void AudioEngineImpl::resume(int audioID)
- {
- auto iter = _audioPlayers.find(audioID);
- if (iter != _audioPlayers.end())
- {
- auto player = iter->second;
- player->resume();
- }
- }
- void AudioEngineImpl::stop(int audioID)
- {
- auto iter = _audioPlayers.find(audioID);
- if (iter != _audioPlayers.end())
- {
- auto player = iter->second;
- player->stop();
- }
- }
- void AudioEngineImpl::stopAll()
- {
- if (_audioPlayers.empty())
- {
- return;
- }
- // Create a temporary vector for storing all players since
- // p->stop() will trigger _audioPlayers.erase,
- // and it will cause a crash as it's already in for loop
- std::vector<IAudioPlayer*> players;
- players.reserve(_audioPlayers.size());
- for (const auto& e : _audioPlayers)
- {
- players.push_back(e.second);
- }
- for (auto p : players)
- {
- p->stop();
- }
- }
- float AudioEngineImpl::getDuration(int audioID)
- {
- auto iter = _audioPlayers.find(audioID);
- if (iter != _audioPlayers.end())
- {
- auto player = iter->second;
- return player->getDuration();
- }
- return 0.0f;
- }
- float AudioEngineImpl::getCurrentTime(int audioID)
- {
- auto iter = _audioPlayers.find(audioID);
- if (iter != _audioPlayers.end())
- {
- auto player = iter->second;
- return player->getPosition();
- }
- return 0.0f;
- }
- bool AudioEngineImpl::setCurrentTime(int audioID, float time)
- {
- auto iter = _audioPlayers.find(audioID);
- if (iter != _audioPlayers.end())
- {
- auto player = iter->second;
- return player->setPosition(time);
- }
- return false;
- }
- void AudioEngineImpl::setFinishCallback(int audioID, const std::function<void (int, const std::string &)> &callback)
- {
- _callbackMap[audioID] = callback;
- }
- void AudioEngineImpl::preload(const std::string& filePath, const std::function<void(bool)>& callback)
- {
- if (_audioPlayerProvider != nullptr)
- {
- std::string fullPath = FileUtils::getInstance()->fullPathForFilename(filePath);
- _audioPlayerProvider->preloadEffect(fullPath, [callback](bool succeed, PcmData data){
- if (callback != nullptr)
- {
- callback(succeed);
- }
- });
- }
- else
- {
- if (callback != nullptr)
- {
- callback(false);
- }
- }
- }
- void AudioEngineImpl::uncache(const std::string& filePath)
- {
- if (_audioPlayerProvider != nullptr)
- {
- std::string fullPath = FileUtils::getInstance()->fullPathForFilename(filePath);
- _audioPlayerProvider->clearPcmCache(fullPath);
- }
- }
- void AudioEngineImpl::uncacheAll()
- {
- if (_audioPlayerProvider != nullptr)
- {
- _audioPlayerProvider->clearAllPcmCaches();
- }
- }
- #endif
|