From d5c023740f6ef6b5f68a6e8c051869df832b16e7 Mon Sep 17 00:00:00 2001 From: Dhilan007 Date: Wed, 3 Sep 2014 18:19:47 +0800 Subject: [PATCH] Implement logical codes of new audio engine on android. --- cocos/Android.mk | 1 + cocos/audio/android/Android.mk | 20 ++ cocos/audio/android/AudioEngine-inl.cpp | 371 ++++++++++++++++++++++++ cocos/audio/android/AudioEngine-inl.h | 111 +++++++ 4 files changed, 503 insertions(+) create mode 100644 cocos/audio/android/AudioEngine-inl.cpp create mode 100644 cocos/audio/android/AudioEngine-inl.h diff --git a/cocos/Android.mk b/cocos/Android.mk index 94c14a9824..0969f5be76 100644 --- a/cocos/Android.mk +++ b/cocos/Android.mk @@ -237,6 +237,7 @@ LOCAL_MODULE := cocos2dx_static LOCAL_MODULE_FILENAME := libcocos2d LOCAL_STATIC_LIBRARIES := cocostudio_static +LOCAL_STATIC_LIBRARIES += audioengine_static LOCAL_STATIC_LIBRARIES += cocos3d_static LOCAL_STATIC_LIBRARIES += cocosbuilder_static LOCAL_STATIC_LIBRARIES += spine_static diff --git a/cocos/audio/android/Android.mk b/cocos/audio/android/Android.mk index 3854fa79d7..bbd7725d9d 100644 --- a/cocos/audio/android/Android.mk +++ b/cocos/audio/android/Android.mk @@ -16,3 +16,23 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include \ $(LOCAL_PATH)/../../platform/android include $(BUILD_STATIC_LIBRARY) + +#new audio engine +include $(CLEAR_VARS) + +LOCAL_MODULE := audioengine_static + +LOCAL_MODULE_FILENAME := libaudioengine + +LOCAL_SRC_FILES := AudioEngine-inl.cpp \ + ../AudioEngine.cpp + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../include + +LOCAL_EXPORT_LDLIBS := -lOpenSLES + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include \ + $(LOCAL_PATH)/../.. \ + $(LOCAL_PATH)/../../platform/android + +include $(BUILD_STATIC_LIBRARY) diff --git a/cocos/audio/android/AudioEngine-inl.cpp b/cocos/audio/android/AudioEngine-inl.cpp new file mode 100644 index 0000000000..2a8de0a7f3 --- /dev/null +++ b/cocos/audio/android/AudioEngine-inl.cpp @@ -0,0 +1,371 @@ +/**************************************************************************** + Copyright (c) 2014 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 +#include "AudioEngine-inl.h" + +#include +// for native asset manager +#include +#include +#include + +#include "audio/include/AudioEngine.h" +#include "base/CCDirector.h" +#include "platform/android/CCFileUtilsAndroid.h" + +#include "platform/android/jni/JniHelper.h" +#include +#include + +#define MIN_VOLUME_MILLIBEL -7000 +#define RANGE_VOLUME_MILLIBEL 7000 + +using namespace cocos2d; + +void PlayOverEvent(SLPlayItf caller, void* context, SLuint32 playEvent) +{ + if (context && playEvent == SL_PLAYEVENT_HEADATEND) + { + AudioEngineImpl* engineImpl = (AudioEngineImpl*)context; + engineImpl->playerFinishCallback(caller,playEvent); + } +} + +AudioPlayer::AudioPlayer() + : _fdPlayerObject(nullptr) + , _finishCallback(nullptr) + , _duration(0.0f) +{ + +} + +AudioPlayer::~AudioPlayer() +{ + if (_fdPlayerObject) + { + (*_fdPlayerObject)->Destroy(_fdPlayerObject); + _fdPlayerObject = nullptr; + } +} + +bool AudioPlayer::init(SLEngineItf engineEngine, SLObjectItf outputMixObject,const std::string& fileFullPath, float volume, bool loop) +{ + bool ret = false; + + do + { + SLDataSource audioSrc; + + SLDataLocator_AndroidFD loc_fd; + SLDataLocator_URI loc_uri; + + SLDataFormat_MIME format_mime = {SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED}; + audioSrc.pFormat = &format_mime; + + if (fileFullPath[0] != '/'){ + std::string relativePath = ""; + + size_t position = fileFullPath.find("assets/"); + if (0 == position) { + // "assets/" is at the beginning of the path and we don't want it + relativePath += fileFullPath.substr(strlen("assets/")); + } else { + relativePath += fileFullPath; + } + + auto asset = AAssetManager_open(cocos2d::FileUtilsAndroid::getAssetManager(), relativePath.c_str(), AASSET_MODE_UNKNOWN); + + // open asset as file descriptor + off_t start, length; + int fd = AAsset_openFileDescriptor(asset, &start, &length); + assert(0 <= fd); + AAsset_close(asset); + + // configure audio source + loc_fd = {SL_DATALOCATOR_ANDROIDFD, fd, start, length}; + + audioSrc.pLocator = &loc_fd; + } + else{ + loc_uri = {SL_DATALOCATOR_URI , (SLchar*)fileFullPath.c_str()}; + audioSrc.pLocator = &loc_uri; + } + + // configure audio sink + SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject}; + SLDataSink audioSnk = {&loc_outmix, NULL}; + + // create audio player + const SLInterfaceID ids[3] = {SL_IID_SEEK, SL_IID_PREFETCHSTATUS, SL_IID_VOLUME}; + const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; + auto result = (*engineEngine)->CreateAudioPlayer(engineEngine, &_fdPlayerObject, &audioSrc, &audioSnk, 3, ids, req); + if(SL_RESULT_SUCCESS != result){ LOG_FUN break; } + + // realize the player + result = (*_fdPlayerObject)->Realize(_fdPlayerObject, SL_BOOLEAN_FALSE); + if(SL_RESULT_SUCCESS != result){ LOG_FUN break; } + + // get the play interface + result = (*_fdPlayerObject)->GetInterface(_fdPlayerObject, SL_IID_PLAY, &_fdPlayerPlay); + if(SL_RESULT_SUCCESS != result){ LOG_FUN break; } + + // get the seek interface + result = (*_fdPlayerObject)->GetInterface(_fdPlayerObject, SL_IID_SEEK, &_fdPlayerSeek); + if(SL_RESULT_SUCCESS != result){ LOG_FUN break; } + + // get the volume interface + result = (*_fdPlayerObject)->GetInterface(_fdPlayerObject, SL_IID_VOLUME, &_fdPlayerVolume); + if(SL_RESULT_SUCCESS != result){ LOG_FUN break; } + + if (loop){ + (*_fdPlayerSeek)->SetLoop(_fdPlayerSeek, SL_BOOLEAN_TRUE, 0, SL_TIME_UNKNOWN); + } + + (*_fdPlayerVolume)->SetVolumeLevel(_fdPlayerVolume, MIN_VOLUME_MILLIBEL + RANGE_VOLUME_MILLIBEL * volume); + + result = (*_fdPlayerPlay)->SetPlayState(_fdPlayerPlay, SL_PLAYSTATE_PLAYING); + if(SL_RESULT_SUCCESS != result){ LOG_FUN break; } + + ret = true; + } while (0); + + return ret; +} + +//==================================================== +AudioEngineImpl::AudioEngineImpl(AudioEngine* audioEngine) + : _audioEngine(audioEngine) + , nextAudioID(0) + , _engineObject(nullptr) + , _engineEngine(nullptr) + , _outputMixObject(nullptr) +{ + +} + +AudioEngineImpl::~AudioEngineImpl() +{ + if (_outputMixObject) + { + (*_outputMixObject)->Destroy(_outputMixObject); + } + if (_engineObject) + { + (*_engineObject)->Destroy(_engineObject); + } +} + +bool AudioEngineImpl::init() +{ + bool ret = false; + do{ + // create engine + auto result = slCreateEngine(&_engineObject, 0, nullptr, 0, nullptr, nullptr); + if(SL_RESULT_SUCCESS != result){ LOG_FUN break; } + + // realize the engine + result = (*_engineObject)->Realize(_engineObject, SL_BOOLEAN_FALSE); + if(SL_RESULT_SUCCESS != result){ LOG_FUN 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){ LOG_FUN break; } + + // create output mix + const SLInterfaceID outputMixIIDs[] = {}; + const SLboolean outputMixReqs[] = {}; + result = (*_engineEngine)->CreateOutputMix(_engineEngine, &_outputMixObject, 0, outputMixIIDs, outputMixReqs); + if(SL_RESULT_SUCCESS != result){ LOG_FUN break; } + + // realize the output mix + result = (*_outputMixObject)->Realize(_outputMixObject, SL_BOOLEAN_FALSE); + if(SL_RESULT_SUCCESS != result){ LOG_FUN break; } + + ret = true; + }while (false); + + return ret; +} + +int AudioEngineImpl::play2d(const std::string &fileFullPath ,bool loop ,float volume, AudioProfile* profile) +{ + auto audioId = AudioEngine::INVAILD_AUDIO_ID; + + do + { + if (_engineEngine == nullptr) + break; + + auto& player = _audioPlayers[nextAudioID]; + auto initPlayer = player.init( _engineEngine, _outputMixObject, fileFullPath, volume, loop); + if (!initPlayer){ + _audioPlayers.erase(nextAudioID); + log("%s,%d message:create player for %s fail", __func__, __LINE__, fileFullPath.c_str()); + break; + } + + audioId = nextAudioID++; + player._audioID = audioId; + + (*(player._fdPlayerPlay))->RegisterCallback(player._fdPlayerPlay, PlayOverEvent, (void*)this); + (*(player._fdPlayerPlay))->SetCallbackEventsMask(player._fdPlayerPlay, SL_PLAYEVENT_HEADATEND); + + if (profile) { + profile->lastPlayTime = utils::gettime(); + profile->audioIDs.push_back(audioId); + } + + _audioEngine->_audioInfos[audioId].state = AudioEngine::AudioState::PLAYING; + } while (0); + + return audioId; +} + +void AudioEngineImpl::playerFinishCallback(SLPlayItf caller, SLuint32 playEvent) +{ + auto itend = _audioPlayers.end(); + for (auto iter = _audioPlayers.begin(); iter != itend; ++iter) + { + if (iter->second._fdPlayerPlay == caller) + { + if (iter->second._finishCallback) + { + iter->second._finishCallback(iter->second._audioID, *_audioEngine->_audioInfos[iter->second._audioID].filePath); + } + _audioEngine->stop(iter->second._audioID); + break; + } + } +} + +void AudioEngineImpl::setVolume(int audioID,float volume) +{ + auto& player = _audioPlayers[audioID]; + auto result = (*player._fdPlayerVolume)->SetVolumeLevel(player._fdPlayerVolume, MIN_VOLUME_MILLIBEL + RANGE_VOLUME_MILLIBEL * volume); + if(SL_RESULT_SUCCESS != result){ + log("%s error:%lu",__func__, result); + } +} + +void AudioEngineImpl::setLoop(int audioID, bool loop) +{ + auto& player = _audioPlayers[audioID]; + SLboolean loopEnabled = SL_BOOLEAN_TRUE; + if (!loop){ + loopEnabled = SL_BOOLEAN_FALSE; + } + (*player._fdPlayerSeek)->SetLoop(player._fdPlayerSeek, loopEnabled, 0, SL_TIME_UNKNOWN); +} + +void AudioEngineImpl::pause(int audioID) +{ + auto& player = _audioPlayers[audioID]; + auto result = (*player._fdPlayerPlay)->SetPlayState(player._fdPlayerPlay, SL_PLAYSTATE_PAUSED); + if(SL_RESULT_SUCCESS != result){ + log("%s error:%lu",__func__, result); + } +} + +void AudioEngineImpl::resume(int audioID) +{ + auto& player = _audioPlayers[audioID]; + auto result = (*player._fdPlayerPlay)->SetPlayState(player._fdPlayerPlay, SL_PLAYSTATE_PLAYING); + if(SL_RESULT_SUCCESS != result){ + log("%s error:%lu",__func__, result); + } +} + +void AudioEngineImpl::stop(int audioID) +{ + auto& player = _audioPlayers[audioID]; + auto result = (*player._fdPlayerPlay)->SetPlayState(player._fdPlayerPlay, SL_PLAYSTATE_STOPPED); + if(SL_RESULT_SUCCESS != result){ + log("%s error:%lu",__func__, result); + } + + _audioPlayers.erase(audioID); +} + +void AudioEngineImpl::stopAll() +{ + auto itEnd = _audioPlayers.end(); + for (auto it = _audioPlayers.begin(); it != itEnd; ++it) + { + auto result = (*it->second._fdPlayerPlay)->SetPlayState(it->second._fdPlayerPlay, SL_PLAYSTATE_STOPPED); + } + _audioPlayers.clear(); +} + +float AudioEngineImpl::getDuration(int audioID) +{ + SLmillisecond duration; + auto& player = _audioPlayers[audioID]; + auto result = (*player._fdPlayerPlay)->GetDuration(player._fdPlayerPlay, &duration); + if (duration == SL_TIME_UNKNOWN){ + return AudioEngine::TIME_UNKNOWN; + } + else{ + player._duration = duration / 1000.0; + + if (player._duration <= 0) + { + return AudioEngine::TIME_UNKNOWN; + } + + return player._duration; + } +} + +float AudioEngineImpl::getCurrentTime(int audioID) +{ + SLmillisecond currPos; + auto& player = _audioPlayers[audioID]; + (*player._fdPlayerPlay)->GetPosition(player._fdPlayerPlay, &currPos); + + if (currPos > player._duration){ + float pos = currPos/1000.0f; + return fmod(pos, player._duration); + } + else { + return currPos / 1000.0f; + } +} + +bool AudioEngineImpl::setCurrentTime(int audioID, float time) +{ + auto& player = _audioPlayers[audioID]; + SLmillisecond pos = 1000 * time; + auto result = (*player._fdPlayerSeek)->SetPosition(player._fdPlayerSeek, pos, SL_SEEKMODE_ACCURATE); + if(SL_RESULT_SUCCESS != result){ + return false; + } + return true; +} + +void AudioEngineImpl::setFinishCallback(int audioID, const std::function &callback) +{ + _audioPlayers[audioID]._finishCallback = callback; +} + +#endif diff --git a/cocos/audio/android/AudioEngine-inl.h b/cocos/audio/android/AudioEngine-inl.h new file mode 100644 index 0000000000..2decd98fce --- /dev/null +++ b/cocos/audio/android/AudioEngine-inl.h @@ -0,0 +1,111 @@ +/**************************************************************************** + Copyright (c) 2014 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 + +#ifndef __AUDIO_ENGINE_INL_H_ +#define __AUDIO_ENGINE_INL_H_ + +#include +#include +#include +#include +#include "base/ccUtils.h" + +#define kMaxSources 24 + +#define LOG_FUN log("error: %s,%d",__func__,__LINE__); + +NS_CC_BEGIN + +class AudioEngineImpl; + +class AudioPlayer +{ +public: + AudioPlayer(); + ~AudioPlayer(); + + bool init(SLEngineItf engineEngine, SLObjectItf outputMixObject,const std::string& fileFullPath, float volume, bool loop); + +private: + + SLObjectItf _fdPlayerObject; + SLPlayItf _fdPlayerPlay; + SLSeekItf _fdPlayerSeek; + SLVolumeItf _fdPlayerVolume; + + float _duration; + int _audioID; + + std::function _finishCallback; + + friend class AudioEngineImpl; +}; + +class AudioEngine; +class AudioProfile; +class AudioEngineImpl +{ +public: + AudioEngineImpl(AudioEngine* audioEngine); + ~AudioEngineImpl(); + + bool init(); + int play2d(const std::string &fileFullPath ,bool loop ,float volume, AudioProfile* profile); + void setVolume(int audioID,float volume); + void setLoop(int audioID, bool loop); + void pause(int audioID); + void resume(int audioID); + void stop(int audioID); + void stopAll(); + float getDuration(int audioID); + float getCurrentTime(int audioID); + bool setCurrentTime(int audioID, float time); + void setFinishCallback(int audioID, const std::function &callback); + + void playerFinishCallback(SLPlayItf caller, SLuint32 playEvent); + + void uncache(const std::string& filePath){} + void uncacheAll(){} +private: + AudioEngine* _audioEngine; + + // engine interfaces + SLObjectItf _engineObject; + SLEngineItf _engineEngine; + + // output mix interfaces + SLObjectItf _outputMixObject; + + //audioID,AudioInfo + std::unordered_map _audioPlayers; + + int nextAudioID; +}; + +#endif // __AUDIO_ENGINE_INL_H_ + +NS_CC_END + +#endif