From 1c5251777028531d69ba7fa1c6e2e1ae318c3b72 Mon Sep 17 00:00:00 2001 From: Sergey Shambir Date: Sat, 22 Jun 2013 00:01:00 +0400 Subject: [PATCH] CocosDenshion: added effects customization for 7 platforms. 1. Pitch/pan/gain support on iOS, Mac, Linux+FMOD, Android+SoundPool; 2. Unified OpenAL code with pitch/pan/gain on Linux, Tizen, Blackberry, Native Client; 3. Bug fixed: rewindBackgroundMusic() no longer stops music on unified OpenAL engine. 4. Optional mp3 support for Linux+OpenAL (OPENAL_MP3 make option); 5. Pan/gain (not pitch) support for Android+OpenES. 6. Reorganized CocosDenshion tests in TestCpp. Known issues: 1. No support for windows, emscrippten, marmalade. 2. No pitch effect on Android+OpenES. It requires backend redesign: http://code.google.com/p/android/issues/detail?id=24592 --- CocosDenshion/android/SimpleAudioEngine.cpp | 6 +- .../android/jni/SimpleAudioEngineJni.cpp | 6 +- .../android/jni/SimpleAudioEngineJni.h | 5 +- CocosDenshion/android/opensl/OpenSLEngine.cpp | 77 +- CocosDenshion/android/opensl/OpenSLEngine.h | 6 +- .../opensl/SimpleAudioEngineOpenSL.cpp | 10 +- .../android/opensl/SimpleAudioEngineOpenSL.h | 2 +- .../blackberry/SimpleAudioEngine.cpp | 599 --------- CocosDenshion/include/SimpleAudioEngine.h | 14 +- CocosDenshion/openal/OpenALDecoder.cpp | 443 +++++++ CocosDenshion/openal/OpenALDecoder.h | 62 + .../SimpleAudioEngineOpenAL.cpp | 1150 ++++++++--------- CocosDenshion/proj.blackberry/.project | 4 +- CocosDenshion/proj.linux/Makefile | 8 +- CocosDenshion/proj.nacl/Makefile | 3 +- CocosDenshion/proj.tizen/.project | 12 +- CocosDenshion/tizen/OspPlayer.cpp | 246 ---- CocosDenshion/tizen/OspPlayer.h | 65 - CocosDenshion/tizen/SimpleAudioEngine.cpp | 294 ----- .../src/org/cocos2dx/lib/Cocos2dxHelper.java | 6 +- .../src/org/cocos2dx/lib/Cocos2dxSound.java | 76 +- .../CocosDenshionTest/CocosDenshionTest.cpp | 485 ++++--- .../CocosDenshionTest/CocosDenshionTest.h | 21 +- samples/Cpp/TestCpp/proj.tizen/.cproject | 94 +- 24 files changed, 1587 insertions(+), 2107 deletions(-) delete mode 100644 CocosDenshion/blackberry/SimpleAudioEngine.cpp create mode 100644 CocosDenshion/openal/OpenALDecoder.cpp create mode 100644 CocosDenshion/openal/OpenALDecoder.h rename CocosDenshion/{linux => openal}/SimpleAudioEngineOpenAL.cpp (75%) delete mode 100644 CocosDenshion/tizen/OspPlayer.cpp delete mode 100644 CocosDenshion/tizen/OspPlayer.h delete mode 100644 CocosDenshion/tizen/SimpleAudioEngine.cpp mode change 100644 => 100755 cocos2dx/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java mode change 100644 => 100755 cocos2dx/platform/android/java/src/org/cocos2dx/lib/Cocos2dxSound.java diff --git a/CocosDenshion/android/SimpleAudioEngine.cpp b/CocosDenshion/android/SimpleAudioEngine.cpp index 8ac594400e..33743719c1 100644 --- a/CocosDenshion/android/SimpleAudioEngine.cpp +++ b/CocosDenshion/android/SimpleAudioEngine.cpp @@ -80,7 +80,7 @@ SimpleAudioEngine::SimpleAudioEngine() { LOGD("i9100 model\nSwitch to OpenSLES"); s_bI9100 = true; - } + } methodInfo.env->ReleaseStringUTFChars(jstr, deviceModel); methodInfo.env->DeleteLocalRef(jstr); @@ -198,11 +198,11 @@ unsigned int SimpleAudioEngine::playEffect(const char* pszFilePath, bool bLoop, std::string fullPath = getFullPathWithoutAssetsPrefix(pszFilePath); if (s_bI9100) { - return SimpleAudioEngineOpenSL::sharedEngine()->playEffect(fullPath.c_str(), bLoop); + return SimpleAudioEngineOpenSL::sharedEngine()->playEffect(fullPath.c_str(), bLoop, pitch, pan, gain); } else { - return playEffectJNI(fullPath.c_str(), bLoop); + return playEffectJNI(fullPath.c_str(), bLoop, pitch, pan, gain); } } diff --git a/CocosDenshion/android/jni/SimpleAudioEngineJni.cpp b/CocosDenshion/android/jni/SimpleAudioEngineJni.cpp index 829b911896..8e06b3ccac 100644 --- a/CocosDenshion/android/jni/SimpleAudioEngineJni.cpp +++ b/CocosDenshion/android/jni/SimpleAudioEngineJni.cpp @@ -250,20 +250,20 @@ extern "C" methodInfo.env->DeleteLocalRef(methodInfo.classID); } - unsigned int playEffectJNI(const char* path, bool bLoop) + unsigned int playEffectJNI(const char* path, bool bLoop, float pitch, float pan, float gain) { // int playEffect(String) JniMethodInfo methodInfo; int ret = 0; - if (! getStaticMethodInfo(methodInfo, "playEffect", "(Ljava/lang/String;Z)I")) + if (! getStaticMethodInfo(methodInfo, "playEffect", "(Ljava/lang/String;ZFFF)I")) { return ret; } jstring stringArg = methodInfo.env->NewStringUTF(path); - ret = methodInfo.env->CallStaticIntMethod(methodInfo.classID, methodInfo.methodID, stringArg, bLoop); + ret = methodInfo.env->CallStaticIntMethod(methodInfo.classID, methodInfo.methodID, stringArg, bLoop, pitch, pan, gain); methodInfo.env->DeleteLocalRef(stringArg); methodInfo.env->DeleteLocalRef(methodInfo.classID); diff --git a/CocosDenshion/android/jni/SimpleAudioEngineJni.h b/CocosDenshion/android/jni/SimpleAudioEngineJni.h index d3e27b4ba6..e016e60e71 100644 --- a/CocosDenshion/android/jni/SimpleAudioEngineJni.h +++ b/CocosDenshion/android/jni/SimpleAudioEngineJni.h @@ -14,7 +14,8 @@ extern "C" extern bool isBackgroundMusicPlayingJNI(); extern float getBackgroundMusicVolumeJNI(); extern void setBackgroundMusicVolumeJNI(float volume); - extern unsigned int playEffectJNI(const char* path, bool bLoop); + extern unsigned int playEffectJNI(const char* path, bool bLoop, + float pitch, float pan, float gain); extern void stopEffectJNI(unsigned int nSoundId); extern void endJNI(); extern float getEffectsVolumeJNI(); @@ -28,4 +29,4 @@ extern "C" extern void stopAllEffectsJNI(); } -#endif // __SIMPLE_AUDIO_ENGINE_JNI__ \ No newline at end of file +#endif // __SIMPLE_AUDIO_ENGINE_JNI__ diff --git a/CocosDenshion/android/opensl/OpenSLEngine.cpp b/CocosDenshion/android/opensl/OpenSLEngine.cpp index b22be2e65b..095d56bff4 100644 --- a/CocosDenshion/android/opensl/OpenSLEngine.cpp +++ b/CocosDenshion/android/opensl/OpenSLEngine.cpp @@ -130,14 +130,50 @@ extern "C" { #define MAX_VOLUME_MILLIBEL 0 #define RANGE_VOLUME_MILLIBEL 4000 -struct AudioPlayer +class AudioPlayer { +public: SLDataSource audioSrc; SLObjectItf fdPlayerObject; SLPlayItf fdPlayerPlay; SLSeekItf fdPlayerSeek; - SLVolumeItf fdPlayerVolume; -} musicPlayer; /* for background music */ + SLVolumeItf fdPlayerVolume; + SLPlaybackRateItf fdPlaybackRate; + + /// Applies global effects volume, takes effect gain into account. + /// @param volume In range 0..1. + void applyEffectsVolume(float volume) + { + SLmillibel finalVolume = int (RANGE_VOLUME_MILLIBEL * (volume * _gain)) + MIN_VOLUME_MILLIBEL; + SLresult result = (*fdPlayerVolume)->SetVolumeLevel(fdPlayerVolume, finalVolume); + assert(SL_RESULT_SUCCESS == result); + } + + void applyParameters(bool isLooping, float pitch, float pan, float gain, float effectsVolume) + { + SLresult result = (*fdPlayerSeek)->SetLoop(fdPlayerSeek, (SLboolean) isLooping, 0, SL_TIME_UNKNOWN); + assert(SL_RESULT_SUCCESS == result); + + SLpermille stereo = SLpermille(1000 * pan); + result = (*fdPlayerVolume)->EnableStereoPosition(fdPlayerVolume, SL_BOOLEAN_TRUE); + assert(SL_RESULT_SUCCESS == result); + result = (*fdPlayerVolume)->SetStereoPosition(fdPlayerVolume, stereo); + assert(SL_RESULT_SUCCESS == result); + + SLpermille playbackRate = SLpermille(1000 * pitch); + if (fdPlaybackRate) + result = (*fdPlaybackRate)->SetRate(fdPlaybackRate, playbackRate); + assert(SL_RESULT_SUCCESS == result); + + _gain = gain; + applyEffectsVolume(effectsVolume); + } + +private: + float _gain; +}; + +static AudioPlayer s_musicPlayer; /* for background music */ typedef map* > EffectList; typedef pair* > Effect; @@ -275,6 +311,10 @@ bool createAudioPlayerBySource(AudioPlayer* player) result = (*(player->fdPlayerObject))->GetInterface(player->fdPlayerObject, getInterfaceID("SL_IID_SEEK"), &(player->fdPlayerSeek)); assert(SL_RESULT_SUCCESS == result); + // get the playback rate interface + result = (*(player->fdPlayerObject))->GetInterface(player->fdPlayerObject, getInterfaceID("SL_IID_PLAYBACKRATE"), &(player->fdPlaybackRate)); + assert(SL_RESULT_SUCCESS == result); + return true; } @@ -317,6 +357,7 @@ void destroyAudioPlayer(AudioPlayer * player) player->fdPlayerPlay = NULL; player->fdPlayerSeek = NULL; player->fdPlayerVolume = NULL; + player->fdPlaybackRate = NULL; } } @@ -368,7 +409,7 @@ void OpenSLEngine::createEngine(void* pHandle) void OpenSLEngine::closeEngine() { // destroy background players - destroyAudioPlayer(&musicPlayer); + destroyAudioPlayer(&s_musicPlayer); // destroy effect players vector* vec; @@ -432,13 +473,6 @@ void PlayOverEvent(SLPlayItf caller, void* pContext, SLuint32 playEvent) } } -void setSingleEffectVolume(AudioPlayer* player, SLmillibel volume) -{ - SLresult result; - result = (*(player->fdPlayerVolume))->SetVolumeLevel(player->fdPlayerVolume, volume); - assert(result == SL_RESULT_SUCCESS); -} - int getSingleEffectState(AudioPlayer * player) { SLuint32 state = 0; @@ -500,7 +534,7 @@ bool OpenSLEngine::recreatePlayer(const char* filename) assert(SL_RESULT_SUCCESS == result); // set volume - setSingleEffectVolume(newPlayer, _effectVolume); + newPlayer->applyEffectsVolume(_effectVolume); setSingleEffectState(newPlayer, SL_PLAYSTATE_STOPPED); setSingleEffectState(newPlayer, SL_PLAYSTATE_PLAYING); @@ -525,8 +559,8 @@ unsigned int OpenSLEngine::preloadEffect(const char * filename) return FILE_NOT_FOUND; } - // set the new player's volume as others' - setSingleEffectVolume(player, _effectVolume); + // set the new player's volume as others' + player->applyEffectsVolume(_effectVolume); vector* vec = new vector; vec->push_back(player); @@ -641,9 +675,9 @@ void OpenSLEngine::resumeAllEffects() } } -void OpenSLEngine::setEffectLooping(unsigned int effectID, bool isLooping) +void OpenSLEngine::setEffectParameters(unsigned int effectID, bool isLooping, + float pitch, float pan, float gain) { - SLresult result; vector* vec = sharedList()[effectID]; assert(NULL != vec); @@ -653,17 +687,15 @@ void OpenSLEngine::setEffectLooping(unsigned int effectID, bool isLooping) if (player && player->fdPlayerSeek) { - result = (*(player->fdPlayerSeek))->SetLoop(player->fdPlayerSeek, (SLboolean) isLooping, 0, SL_TIME_UNKNOWN); - assert(SL_RESULT_SUCCESS == result); + player->applyParameters(isLooping, pitch, pan, gain, _effectVolume); } } void OpenSLEngine::setEffectsVolume(float volume) { assert(volume <= 1.0f && volume >= 0.0f); - _effectVolume = int (RANGE_VOLUME_MILLIBEL * volume) + MIN_VOLUME_MILLIBEL; - - SLresult result; + _effectVolume = volume; + EffectList::iterator p; AudioPlayer * player; for (p = sharedList().begin() ; p != sharedList().end() ; ++ p) @@ -672,8 +704,7 @@ void OpenSLEngine::setEffectsVolume(float volume) for (vector::iterator iter = vec->begin() ; iter != vec->end() ; ++ iter) { player = *iter; - result = (*(player->fdPlayerVolume))->SetVolumeLevel(player->fdPlayerVolume, _effectVolume); - assert(SL_RESULT_SUCCESS == result); + player->applyEffectsVolume(_effectVolume); } } } diff --git a/CocosDenshion/android/opensl/OpenSLEngine.h b/CocosDenshion/android/opensl/OpenSLEngine.h index 45f8123668..8a38b83d26 100644 --- a/CocosDenshion/android/opensl/OpenSLEngine.h +++ b/CocosDenshion/android/opensl/OpenSLEngine.h @@ -62,7 +62,7 @@ public: void resumeAllEffects(); - void setEffectLooping(unsigned int effectID, bool isLooping); + void setEffectParameters(unsigned int effectID, bool isLooping, float pitch, float pan, float gain); void setEffectsVolume(float volume); @@ -70,7 +70,7 @@ public: private: SLmillibel _musicVolume; - SLmillibel _effectVolume; + float _effectVolume; }; -#endif \ No newline at end of file +#endif diff --git a/CocosDenshion/android/opensl/SimpleAudioEngineOpenSL.cpp b/CocosDenshion/android/opensl/SimpleAudioEngineOpenSL.cpp index 134084b462..f491f1bd3f 100644 --- a/CocosDenshion/android/opensl/SimpleAudioEngineOpenSL.cpp +++ b/CocosDenshion/android/opensl/SimpleAudioEngineOpenSL.cpp @@ -86,7 +86,8 @@ void SimpleAudioEngineOpenSL::setEffectsVolume(float volume) s_pOpenSL->setEffectsVolume(volume); } -unsigned int SimpleAudioEngineOpenSL::playEffect(const char* pszFilePath, bool bLoop) +unsigned int SimpleAudioEngineOpenSL::playEffect(const char* pszFilePath, bool bLoop, + float pitch, float pan, float gain) { unsigned int soundID = s_pOpenSL->preloadEffect(pszFilePath); @@ -94,19 +95,18 @@ unsigned int SimpleAudioEngineOpenSL::playEffect(const char* pszFilePath, bool b { if (s_pOpenSL->getEffectState(soundID) == PLAYSTATE_PLAYING) { - // recreate an effect player + // recreate an effect player. if (s_pOpenSL->recreatePlayer(pszFilePath)) { - s_pOpenSL->setEffectLooping(soundID, bLoop); + s_pOpenSL->setEffectParameters(soundID, bLoop, pitch, pan, gain); } } else { s_pOpenSL->setEffectState(soundID, PLAYSTATE_STOPPED); s_pOpenSL->setEffectState(soundID, PLAYSTATE_PLAYING); - s_pOpenSL->setEffectLooping(soundID, bLoop); + s_pOpenSL->setEffectParameters(soundID, bLoop, pitch, pan, gain); } - } return soundID; diff --git a/CocosDenshion/android/opensl/SimpleAudioEngineOpenSL.h b/CocosDenshion/android/opensl/SimpleAudioEngineOpenSL.h index 31d44bcd25..8f5059e9d7 100644 --- a/CocosDenshion/android/opensl/SimpleAudioEngineOpenSL.h +++ b/CocosDenshion/android/opensl/SimpleAudioEngineOpenSL.h @@ -19,7 +19,7 @@ public: void setEffectsVolume(float volume); - unsigned int playEffect(const char* pszFilePath, bool bLoop = false); + unsigned int playEffect(const char* pszFilePath, bool bLoop, float pitch, float pan, float gain); void pauseEffect(unsigned int nSoundId); diff --git a/CocosDenshion/blackberry/SimpleAudioEngine.cpp b/CocosDenshion/blackberry/SimpleAudioEngine.cpp deleted file mode 100644 index 8cef36b209..0000000000 --- a/CocosDenshion/blackberry/SimpleAudioEngine.cpp +++ /dev/null @@ -1,599 +0,0 @@ -/**************************************************************************** -Copyright (c) 2010 cocos2d-x.org - -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. -****************************************************************************/ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "SimpleAudioEngine.h" -#include "cocos2d.h" -USING_NS_CC; - -using namespace std; - -namespace CocosDenshion -{ - struct soundData { - ALuint buffer; - ALuint source; - bool isLooped; - }; - - typedef map EffectsMap; - EffectsMap s_effects; - - typedef enum { - PLAYING, - STOPPED, - PAUSED, - } playStatus; - - static float s_volume = 1.0f; - static float s_effectVolume = 1.0f; - - struct backgroundMusicData { - ALuint buffer; - ALuint source; - }; - typedef map BackgroundMusicsMap; - BackgroundMusicsMap s_backgroundMusics; - - static ALuint s_backgroundSource = AL_NONE; - - static SimpleAudioEngine *s_engine = 0; - - static int checkALError(const char *funcName) - { - int err = alGetError(); - - if (err != AL_NO_ERROR) - { - switch (err) - { - case AL_INVALID_NAME: - fprintf(stderr, "AL_INVALID_NAME in %s\n", funcName); - break; - - case AL_INVALID_ENUM: - fprintf(stderr, "AL_INVALID_ENUM in %s\n", funcName); - break; - - case AL_INVALID_VALUE: - fprintf(stderr, "AL_INVALID_VALUE in %s\n", funcName); - break; - - case AL_INVALID_OPERATION: - fprintf(stderr, "AL_INVALID_OPERATION in %s\n", funcName); - break; - - case AL_OUT_OF_MEMORY: - fprintf(stderr, "AL_OUT_OF_MEMORY in %s\n", funcName); - break; - } - } - - return err; - } - - static void stopBackground(bool bReleaseData) - { - alSourceStop(s_backgroundSource); - - if (bReleaseData) - { - for (BackgroundMusicsMap::iterator it = s_backgroundMusics.begin(); it != s_backgroundMusics.end(); ++it) - { - if (it->second->source == s_backgroundSource) - { - alDeleteBuffers(1, &it->second->buffer); - checkALError("stopBackground"); - alDeleteSources(1, &it->second->source); - checkALError("stopBackground"); - delete it->second; - s_backgroundMusics.erase(it); - break; - } - } - } - - s_backgroundSource = AL_NONE; - } - - static void setBackgroundVolume(float volume) - { - alSourcef(s_backgroundSource, AL_GAIN, volume); - } - - SimpleAudioEngine::SimpleAudioEngine() - { - alutInit(0, 0); - } - - SimpleAudioEngine::~SimpleAudioEngine() - { - alutExit(); - } - - SimpleAudioEngine* SimpleAudioEngine::sharedEngine() - { - if (!s_engine) - s_engine = new SimpleAudioEngine(); - - return s_engine; - } - - void SimpleAudioEngine::end() - { - checkALError("end"); - - // clear all the sounds - EffectsMap::const_iterator end = s_effects.end(); - for (EffectsMap::iterator it = s_effects.begin(); it != end; it++) - { - alSourceStop(it->second->source); - checkALError("end"); - alDeleteBuffers(1, &it->second->buffer); - checkALError("end"); - alDeleteSources(1, &it->second->source); - checkALError("end"); - delete it->second; - } - s_effects.clear(); - - // and the background too - stopBackground(true); - - for (BackgroundMusicsMap::iterator it = s_backgroundMusics.begin(); it != s_backgroundMusics.end(); ++it) - { - alSourceStop(it->second->source); - checkALError("end"); - alDeleteBuffers(1, &it->second->buffer); - checkALError("end"); - alDeleteSources(1, &it->second->source); - checkALError("end"); - delete it->second; - } - s_backgroundMusics.clear(); - } - - // - // OGG support - // - static bool isOGGFile(const char *pszFilePath) - { - FILE *file; - OggVorbis_File ogg_file; - int result; - - file = fopen(pszFilePath, "rb"); - result = ov_test(file, &ogg_file, 0, 0); - ov_clear(&ogg_file); - - return (result == 0); - } - - static ALuint createBufferFromOGG(const char *pszFilePath) - { - ALuint buffer; - OggVorbis_File ogg_file; - vorbis_info* info; - ALenum format; - int result; - int section; - unsigned int size = 0; - - if (ov_fopen(pszFilePath, &ogg_file) < 0) - { - ov_clear(&ogg_file); - fprintf(stderr, "Could not open OGG file %s\n", pszFilePath); - return -1; - } - - info = ov_info(&ogg_file, -1); - - if (info->channels == 1) - format = AL_FORMAT_MONO16; - else - format = AL_FORMAT_STEREO16; - - // size = #samples * #channels * 2 (for 16 bit) - unsigned int data_size = ov_pcm_total(&ogg_file, -1) * info->channels * 2; - char* data = new char[data_size]; - - while (size < data_size) - { - result = ov_read(&ogg_file, data + size, data_size - size, 0, 2, 1, §ion); - if (result > 0) - { - size += result; - } - else if (result < 0) - { - delete [] data; - fprintf(stderr, "OGG file problem %s\n", pszFilePath); - return -1; - } - else - { - break; - } - } - - if (size == 0) - { - delete [] data; - fprintf(stderr, "Unable to read OGG data\n"); - return -1; - } - - // clear al errors - checkALError("createBufferFromOGG"); - - // Load audio data into a buffer. - alGenBuffers(1, &buffer); - - if (checkALError("createBufferFromOGG") != AL_NO_ERROR) - { - fprintf(stderr, "Couldn't generate a buffer for OGG file\n"); - delete [] data; - return buffer; - } - - alBufferData(buffer, format, data, data_size, info->rate); - checkALError("createBufferFromOGG"); - - delete [] data; - ov_clear(&ogg_file); - - return buffer; - } - - - // - // background audio - // - void SimpleAudioEngine::preloadBackgroundMusic(const char* pszFilePath) - { - // Changing file path to full path - std::string fullPath = FileUtils::sharedFileUtils()->fullPathForFilename(pszFilePath); - - BackgroundMusicsMap::const_iterator it = s_backgroundMusics.find(fullPath); - if (it == s_backgroundMusics.end()) - { - ALuint buffer = AL_NONE; - if (isOGGFile(fullPath.data())) - { - buffer = createBufferFromOGG(fullPath.data()); - } - else - { - buffer = alutCreateBufferFromFile(fullPath.data()); - } - - checkALError("preloadBackgroundMusic"); - - if (buffer == AL_NONE) - { - fprintf(stderr, "Error loading file: '%s'\n", fullPath.data()); - alDeleteBuffers(1, &buffer); - return; - } - - ALuint source = AL_NONE; - alGenSources(1, &source); - checkALError("preloadBackgroundMusic"); - - alSourcei(source, AL_BUFFER, buffer); - checkALError("preloadBackgroundMusic"); - - backgroundMusicData* data = new backgroundMusicData(); - data->buffer = buffer; - data->source = source; - s_backgroundMusics.insert(BackgroundMusicsMap::value_type(fullPath, data)); - } - } - - void SimpleAudioEngine::playBackgroundMusic(const char* pszFilePath, bool bLoop) - { - if (s_backgroundSource != AL_NONE) - stopBackgroundMusic(false); - - // Changing file path to full path - std::string fullPath = FileUtils::sharedFileUtils()->fullPathForFilename(pszFilePath); - - BackgroundMusicsMap::const_iterator it = s_backgroundMusics.find(fullPath); - if (it == s_backgroundMusics.end()) - { - preloadBackgroundMusic(fullPath.c_str()); - it = s_backgroundMusics.find(fullPath); - } - - if (it != s_backgroundMusics.end()) - { - s_backgroundSource = it->second->source; - alSourcei(s_backgroundSource, AL_LOOPING, bLoop ? AL_TRUE : AL_FALSE); - alSourcePlay(s_backgroundSource); - checkALError("playBackgroundMusic"); - } - } - - void SimpleAudioEngine::stopBackgroundMusic(bool bReleaseData) - { - stopBackground(bReleaseData); - } - - void SimpleAudioEngine::pauseBackgroundMusic() - { - ALint state; - alGetSourcei(s_backgroundSource, AL_SOURCE_STATE, &state); - if (state == AL_PLAYING) - alSourcePause(s_backgroundSource); - checkALError("pauseBackgroundMusic"); - } - - void SimpleAudioEngine::resumeBackgroundMusic() - { - ALint state; - alGetSourcei(s_backgroundSource, AL_SOURCE_STATE, &state); - if (state == AL_PAUSED) - alSourcePlay(s_backgroundSource); - checkALError("resumeBackgroundMusic"); - } - - void SimpleAudioEngine::rewindBackgroundMusic() - { - alSourceRewind(s_backgroundSource); - checkALError("rewindBackgroundMusic"); - } - - bool SimpleAudioEngine::willPlayBackgroundMusic() - { - return true; - } - - bool SimpleAudioEngine::isBackgroundMusicPlaying() - { - ALint play_status; - alGetSourcei(s_backgroundSource, AL_SOURCE_STATE, &play_status); - - return (play_status == AL_PLAYING); - } - - float SimpleAudioEngine::getBackgroundMusicVolume() - { - return s_volume; - } - - void SimpleAudioEngine::setBackgroundMusicVolume(float volume) - { - if (s_volume != volume && volume >= -0.0001 && volume <= 1.0001) - { - s_volume = volume; - - setBackgroundVolume(volume); - } - } - - // - // Effect audio (using OpenAL) - // - float SimpleAudioEngine::getEffectsVolume() - { - return s_effectVolume; - } - - void SimpleAudioEngine::setEffectsVolume(float volume) - { - if (volume != s_effectVolume) - { - EffectsMap::const_iterator end = s_effects.end(); - for (EffectsMap::const_iterator it = s_effects.begin(); it != end; it++) - { - alSourcef(it->second->source, AL_GAIN, volume); - } - - s_effectVolume = volume; - } - } - - unsigned int SimpleAudioEngine::playEffect(const char* pszFilePath, bool bLoop, - float pitch, float pan, float gain) - { - // Changing file path to full path - std::string fullPath = FileUtils::sharedFileUtils()->fullPathForFilename(pszFilePath); - - EffectsMap::iterator iter = s_effects.find(fullPath); - - if (iter == s_effects.end()) - { - preloadEffect(fullPath.c_str()); - - // let's try again - iter = s_effects.find(fullPath); - if (iter == s_effects.end()) - { - fprintf(stderr, "could not find play sound %s\n", fullPath.c_str()); - return -1; - } - } - - checkALError("playEffect"); - iter->second->isLooped = bLoop; - alSourcei(iter->second->source, AL_LOOPING, iter->second->isLooped ? AL_TRUE : AL_FALSE); - alSourcePlay(iter->second->source); - checkALError("playEffect"); - - return iter->second->source; - } - - void SimpleAudioEngine::stopEffect(unsigned int nSoundId) - { - alSourceStop(nSoundId); - checkALError("stopEffect"); - } - - void SimpleAudioEngine::preloadEffect(const char* pszFilePath) - { - // Changing file path to full path - std::string fullPath = FileUtils::sharedFileUtils()->fullPathForFilename(pszFilePath); - - EffectsMap::iterator iter = s_effects.find(fullPath); - - // check if we have this already - if (iter == s_effects.end()) - { - ALuint buffer; - ALuint source; - soundData *data = new soundData; - string path = fullPath; - - checkALError("preloadEffect"); - - if (isOGGFile(path.data())) - { - buffer = createBufferFromOGG(path.data()); - } - else - { - buffer = alutCreateBufferFromFile(path.data()); - checkALError("preloadEffect"); - } - - if (buffer == AL_NONE) - { - fprintf(stderr, "Error loading file: '%s'\n", path.data()); - alDeleteBuffers(1, &buffer); - return; - } - - alGenSources(1, &source); - - if (checkALError("preloadEffect") != AL_NO_ERROR) - { - alDeleteBuffers(1, &buffer); - return; - } - - alSourcei(source, AL_BUFFER, buffer); - checkALError("preloadEffect"); - - data->isLooped = false; - data->buffer = buffer; - data->source = source; - - s_effects.insert(EffectsMap::value_type(fullPath, data)); - } - } - - void SimpleAudioEngine::unloadEffect(const char* pszFilePath) - { - // Changing file path to full path - std::string fullPath = FileUtils::sharedFileUtils()->fullPathForFilename(pszFilePath); - - EffectsMap::iterator iter = s_effects.find(fullPath); - - if (iter != s_effects.end()) - { - checkALError("unloadEffect"); - - alSourceStop(iter->second->source); - checkALError("unloadEffect"); - - alDeleteSources(1, &iter->second->source); - checkALError("unloadEffect"); - - alDeleteBuffers(1, &iter->second->buffer); - checkALError("unloadEffect"); - delete iter->second; - - s_effects.erase(iter); - } - } - - void SimpleAudioEngine::pauseEffect(unsigned int nSoundId) - { - ALint state; - alGetSourcei(nSoundId, AL_SOURCE_STATE, &state); - if (state == AL_PLAYING) - alSourcePause(nSoundId); - checkALError("pauseEffect"); - } - - void SimpleAudioEngine::pauseAllEffects() - { - EffectsMap::iterator iter = s_effects.begin(); - ALint state; - while (iter != s_effects.end()) - { - alGetSourcei(iter->second->source, AL_SOURCE_STATE, &state); - if (state == AL_PLAYING) - alSourcePause(iter->second->source); - checkALError("pauseAllEffects"); - ++iter; - } - } - - void SimpleAudioEngine::resumeEffect(unsigned int nSoundId) - { - ALint state; - alGetSourcei(nSoundId, AL_SOURCE_STATE, &state); - if (state == AL_PAUSED) - alSourcePlay(nSoundId); - checkALError("resumeEffect"); - } - - void SimpleAudioEngine::resumeAllEffects() - { - EffectsMap::iterator iter = s_effects.begin(); - ALint state; - while (iter != s_effects.end()) - { - alGetSourcei(iter->second->source, AL_SOURCE_STATE, &state); - if (state == AL_PAUSED) - alSourcePlay(iter->second->source); - checkALError("resumeAllEffects"); - ++iter; - } - } - - void SimpleAudioEngine::stopAllEffects() - { - EffectsMap::iterator iter = s_effects.begin(); - - if (iter != s_effects.end()) - { - checkALError("stopAllEffects"); - alSourceStop(iter->second->source); - checkALError("stopAllEffects"); - } - } - -} diff --git a/CocosDenshion/include/SimpleAudioEngine.h b/CocosDenshion/include/SimpleAudioEngine.h index b07533ca76..4bd62cfac3 100644 --- a/CocosDenshion/include/SimpleAudioEngine.h +++ b/CocosDenshion/include/SimpleAudioEngine.h @@ -155,7 +155,7 @@ public: /** @brief Play sound effect @param pszFilePath The path of the effect file,or the FileName of T_SoundResInfo - @bLoop Whether to loop the effect playing, default value is false + @param bLoop Whether to loop the effect playing, default value is false */ unsigned int playEffect(const char* pszFilePath, bool bLoop) { return this->playEffect(pszFilePath, bLoop, 1.0, 0.0, 1.0); @@ -168,10 +168,14 @@ public: /** @brief Play sound effect with a file path, pitch, pan and gain @param pszFilePath The path of the effect file,or the FileName of T_SoundResInfo - @bLoop Whether to loop the effect playing, default value is false - @pitch Frequency, normal value is 1.0. Will also change effect play time. - @pan Stereo effect, in range [-1..1] where -1 enables only left channel. - @gain Volume, normal value is 1.0. Works for range [0..1]. + @param bLoop Whether to loop the effect playing, default value is false + @param pitch Frequency, normal value is 1.0. Will also change effect play time. + @param pan Stereo effect, in range [-1..1] where -1 enables only left channel. + @param gain Volume, normal value is 1. Works for range [0..1]. + + @note Full support is under development, now there are limitations: + - no pitch effect on Samsung Galaxy S2 with OpenSL backend enabled; + - no pitch/pan/gain on emscrippten, win32, marmalade. */ unsigned int playEffect(const char* pszFilePath, bool bLoop, float pitch, float pan, float gain); diff --git a/CocosDenshion/openal/OpenALDecoder.cpp b/CocosDenshion/openal/OpenALDecoder.cpp new file mode 100644 index 0000000000..5e96d50c34 --- /dev/null +++ b/CocosDenshion/openal/OpenALDecoder.cpp @@ -0,0 +1,443 @@ +#include "OpenALDecoder.h" +#include +#include +#include + +#if CC_TARGET_PLATFORM == CC_PLATFORM_TIZEN +#include +#include +#include +using namespace Tizen::Base; +using namespace Tizen::Base::Collection; +using namespace Tizen::Media; +#endif + +#ifndef DISABLE_VORBIS +#include +#endif + +#ifdef ENABLE_MPG123 +#include +#endif + +namespace CocosDenshion { + +static int checkALError(const char *funcName) +{ + int err = alGetError(); + + if (err != AL_NO_ERROR) + { + switch (err) + { + case AL_INVALID_NAME: + fprintf(stderr, "AL_INVALID_NAME in %s\n", funcName); + break; + + case AL_INVALID_ENUM: + fprintf(stderr, "AL_INVALID_ENUM in %s\n", funcName); + break; + + case AL_INVALID_VALUE: + fprintf(stderr, "AL_INVALID_VALUE in %s\n", funcName); + break; + + case AL_INVALID_OPERATION: + fprintf(stderr, "AL_INVALID_OPERATION in %s\n", funcName); + break; + + case AL_OUT_OF_MEMORY: + fprintf(stderr, "AL_OUT_OF_MEMORY in %s\n", funcName); + break; + } + } + + return err; +} + +class AlutDecoder : public OpenALDecoder +{ + bool decode(OpenALFile &file, ALuint &result) + { + if (!ensureFileIsMemoryMapped(file)) + return false; + result = alutCreateBufferFromFileImage(file.mappedFile, file.fileSize); + if (AL_NONE == result) + return false; + return true; + } + + bool acceptsFormat(Format format) const + { + return Wav == format || Raw == format; + } +}; + +class DataRaii +{ +public: + char *data; + size_t size; + + DataRaii() : data(0), size(0) {} + ~DataRaii() { delete [] data; } +}; + +#ifdef ENABLE_MPG123 +class Mpg123Decoder : public OpenALDecoder +{ +private: + mpg123_handle *handle; + +public: + class MpgOpenRaii + { + public: + mpg123_handle *handle; + + MpgOpenRaii(mpg123_handle *handle) : handle(handle) {} + ~MpgOpenRaii() { mpg123_close(handle); } + }; + + bool getInfo(ALenum &format, ALsizei &freq, ALsizei &size) const + { + int channels = 0; + int encoding = 0; + long rate = 0; + if (MPG123_OK != mpg123_getformat(handle, &rate, &channels, &encoding)) + return false; + size = mpg123_length(handle); + if (size == MPG123_ERR) + return false; + freq = rate; + if (encoding == MPG123_ENC_UNSIGNED_8) { + if (channels == 1) + format = AL_FORMAT_MONO8; + else + format = AL_FORMAT_STEREO8; + } else { + if (channels == 1) + format = AL_FORMAT_MONO16; + else + format = AL_FORMAT_STEREO16; + } + return true; + } + + bool decode(OpenALFile &file, ALuint &result) + { + if (MPG123_OK != mpg123_open_fd(handle, fileno(file.file))) + return false; + MpgOpenRaii raii(handle); + ALenum format = AL_NONE; + ALsizei freq = 0; + ALsizei size = 0; + if (!getInfo(format, freq, size)) + return false; + DataRaii pcm; + pcm.size = size; + if (format == AL_FORMAT_MONO16 || format == AL_FORMAT_STEREO16) + pcm.size *= 2; + pcm.data = new char[pcm.size]; + size_t done = 0; + if (MPG123_DONE != mpg123_read(handle, (unsigned char*)pcm.data, pcm.size, &done)) + return false; + CCLOG("MP3 BUFFER SIZE: %ld, FORMAT %i.", (long)done, (int)format); + return initALBuffer(result, format, pcm.data, done, freq); + } + + bool acceptsFormat(Format format) const + { + return Mp3 == format; + } + + Mpg123Decoder() + : handle(mpg123_new(NULL, NULL)) + { + if (MPG123_OK != mpg123_format(handle, 44100, MPG123_MONO | MPG123_STEREO, + MPG123_ENC_UNSIGNED_8 | MPG123_ENC_SIGNED_16)) + CCLOG("ERROR (CocosDenshion): cannot set specified mpg123 format."); + } + + ~Mpg123Decoder() + { + mpg123_delete(handle); + } +}; +#endif + +#ifndef DISABLE_VORBIS +class VorbisDecoder : public OpenALDecoder +{ + class OggRaii + { + public: + OggVorbis_File file; + + ~OggRaii() { ov_clear(&file); } + }; + +public: + bool decode(OpenALFile &file, ALuint &result) + { + OggRaii ogg; + int status = ov_test(file.file, &ogg.file, 0, 0); + if (status != 0) { + ov_clear(&ogg.file); + return false; + } + status = ov_test_open(&ogg.file); + if (status != 0) { + fprintf(stderr, "Could not open OGG file '%s'\n", file.debugName.c_str()); + return false; + } + // As vorbis documentation says, we should not fclose() file + // after successful opening by vorbis functions. + file.file = NULL; + vorbis_info *info = ov_info(&ogg.file, -1); + ALenum format = (info->channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; + + DataRaii pcm; + pcm.size = ov_pcm_total(&ogg.file, -1) * info->channels * 2; + pcm.data = new char[pcm.size]; + + size_t size = 0; + int section = 0; + while (size < pcm.size) { + status = ov_read(&ogg.file, pcm.data + size, pcm.size - size, 0, 2, 1, §ion); + if (status > 0) { + size += status; + } else if (status < 0) { + fprintf(stderr, "OGG file decoding stopped, file '%s'\n", file.debugName.c_str()); + return false; + } else { + break; + } + } + if (size == 0) { + fprintf(stderr, "Unable to read OGG data from '%s'\n", file.debugName.c_str()); + return false; + } + return initALBuffer(result, format, pcm.data, pcm.size, info->rate); + } + + bool acceptsFormat(Format format) const + { + return Vorbis == format; + } +}; +#endif + +#if CC_TARGET_PLATFORM == CC_PLATFORM_TIZEN +class TizenDecoder : public OpenALDecoder +{ +public: + static TizenDecoder *create(Format format) + { + TizenDecoder *decoder = new TizenDecoder(format); + if (decoder && !decoder->init()) { + delete decoder; + decoder = NULL; + } + return decoder; + } + + bool decode(OpenALFile &file, ALuint &result) + { + if (!ensureFileIsMemoryMapped(file)) + return false; + ByteBuffer inputBuffer; + inputBuffer.Construct(/*capacity*/ file.fileSize); + inputBuffer.SetArray((const byte*)file.mappedFile, 0, file.fileSize); + inputBuffer.Flip(); + ByteBuffer pcm; + pcm.Construct(/*capacity*/ 2 * file.fileSize); + + AudioSampleType sampleType = AUDIO_TYPE_NONE; + AudioChannelType channelType = AUDIO_CHANNEL_TYPE_NONE; + int sampleRate = 0; + if (E_SUCCESS != m_decoder.Probe(inputBuffer, sampleType, channelType, sampleRate)) + return false; + while (inputBuffer.GetRemaining()) { + auto ret = m_decoder.Decode(inputBuffer, pcm); + if (ret == E_OUT_OF_MEMORY) { + pcm.ExpandCapacity(2 * pcm.GetCapacity()); + } else if (IsFailed(ret)) { + AppLogTag("CocosDenshion(TizenDecoder)", "failed to decode file '%s', supported format is %s.", file.debugName.c_str(), getCodecName()); + return false; + } + } + + return initALBuffer(result, getALFormat(sampleType, channelType), + pcm.GetPointer(), pcm.GetPosition(), sampleRate); + } + + bool acceptsFormat(Format format) const + { + return m_format == format; + } + +private: + TizenDecoder(Format format) + : m_format(format) + { + } + + bool init() + { + HashMap option; + option.Construct(); + option.Add(*(new Integer(MEDIA_PROPERTY_AUDIO_CHANNEL_TYPE)), *(new Integer(AUDIO_CHANNEL_TYPE_NONE))); + option.Add(*(new Integer(MEDIA_PROPERTY_AUDIO_SAMPLE_RATE)), *(new Integer(44100))); + + result r = m_decoder.Construct(getCodecType()); + if (IsFailed(r)) + return false; + else + AppLogTag("CocosDenshion", "Tizen device supports audio format %s.", getCodecName()); + return true; + } + + ALenum getALFormat(AudioSampleType sampleType, AudioChannelType channelType) + { + if (sampleType == AUDIO_TYPE_PCM_U8) { + if (channelType == AUDIO_CHANNEL_TYPE_MONO) + return AL_FORMAT_MONO8; + return AL_FORMAT_STEREO8; + } + if (sampleType == AUDIO_TYPE_PCM_S16_LE) { + if (channelType == AUDIO_CHANNEL_TYPE_MONO) + return AL_FORMAT_MONO16; + return AL_FORMAT_STEREO16; + } + AppLogTag("CocosDenshion(TizenDecoder)", "unsuppored sampleType=%d.", sampleType, channelType); + return AL_NONE; + } + + CodecType getCodecType() const + { + switch (m_format) { + case Mp3: + return CODEC_MP3; + case Vorbis: + return CODEC_VORBIS; + case Flac: + return CODEC_FLAC; + case Midi: + return CODEC_MIDI; + case Aac: + return CODEC_AAC; + default: + break; + } + return CODEC_UNKNOWN; + } + + const char *getCodecName() const + { + switch (m_format) { + case Mp3: + return "mp3"; + case Vorbis: + return "vorbis"; + case Flac: + return "flac"; + case Midi: + return "midi"; + case Aac: + return "aac"; + default: + break; + } + return "unknown"; + } + + Format m_format; + AudioDecoder m_decoder; +}; +#endif + +std::vector OpenALDecoder::m_decoders; + +void OpenALDecoder::installDecoders() +{ +#if CC_TARGET_PLATFORM == CC_PLATFORM_TIZEN + addDecoder(TizenDecoder::create(Mp3)); + addDecoder(TizenDecoder::create(Vorbis)); + addDecoder(TizenDecoder::create(Flac)); + addDecoder(TizenDecoder::create(Midi)); + addDecoder(TizenDecoder::create(Aac)); +#else +#if !defined(DISABLE_VORBIS) + addDecoder(new VorbisDecoder()); +#endif +#if defined(ENABLE_MPG123) + addDecoder(new Mpg123Decoder()); +#endif +#endif + addDecoder(new AlutDecoder()); +} + +void OpenALDecoder::addDecoder(OpenALDecoder *decoder) +{ + if (decoder) + m_decoders.push_back(decoder); +} + +bool OpenALDecoder::initALBuffer(ALuint &result, ALenum format, + const ALvoid *data, ALsizei size, ALsizei freq) +{ + // Load audio data into a buffer. + alGenBuffers(1, &result); + + if (checkALError("initALBuffer:alGenBuffers") != AL_NO_ERROR) + { + fprintf(stderr, "Couldn't generate OpenAL buffer\n"); + return false; + } + + alBufferData(result, format, data, size, freq); + checkALError("initALBuffer:alBufferData"); + return true; +} + +/// Ensures that mmap() called (and calls if it wasn't) +/// @return False if file cannot be memory mapped. +bool OpenALDecoder::ensureFileIsMemoryMapped(OpenALFile &file) +{ + if (!file.file) + return false; + if (file.mappedFile != NULL) + return true; + + const int fd = fileno(file.file); + struct stat fileStats; + if (0 != fstat(fd, &fileStats)) + return false; + file.fileSize = fileStats.st_size; + file.mappedFile = ::mmap(NULL, file.fileSize, PROT_READ, MAP_PRIVATE, fd, 0); + if (file.mappedFile != MAP_FAILED) + return true; + file.mappedFile = NULL; + return false; +} + +const std::vector &OpenALDecoder::getDecoders() +{ + return m_decoders; +} + +void OpenALFile::clear() +{ + if (mappedFile) { + ::munmap(mappedFile, fileSize); + mappedFile = 0; + fileSize = 0; + } + if (file) { + fclose(file); + file = 0; + } +} + +} // namespace CocosDenshion diff --git a/CocosDenshion/openal/OpenALDecoder.h b/CocosDenshion/openal/OpenALDecoder.h new file mode 100644 index 0000000000..8754abc27a --- /dev/null +++ b/CocosDenshion/openal/OpenALDecoder.h @@ -0,0 +1,62 @@ +#ifndef COCOSDENSHION_OPENALLOADER_H +#define COCOSDENSHION_OPENALLOADER_H + +#include +#include +#include +#include +#include "cocos2d.h" + +#if CC_TARGET_PLATFORM == CC_PLATFORM_TIZEN +#define DISABLE_VORBIS +#endif + +namespace CocosDenshion { + +struct OpenALFile +{ + std::string debugName; ///< For logging. + FILE *file; + void *mappedFile; ///< Reserved by decoders. + size_t fileSize; ///< Reserved by decoders. + + OpenALFile() : file(0), mappedFile(0), fileSize(0) {} + ~OpenALFile() { clear(); } + + void clear(); +}; + +class OpenALDecoder +{ +public: + enum Format { + Mp3 = 0, + Vorbis, + Wav, + Raw, + Flac, + Midi, + Aac + }; + + virtual ~OpenALDecoder() {} + + /// Returns true if such format is supported and decoding was successful. + virtual bool decode(OpenALFile &file, ALuint &result) = 0; + virtual bool acceptsFormat(Format format) const = 0; + + static const std::vector &getDecoders(); + static void installDecoders(); + +protected: + static void addDecoder(OpenALDecoder *decoder); + bool initALBuffer(ALuint &result, ALenum format, + const ALvoid* data, ALsizei size, ALsizei freq); + bool ensureFileIsMemoryMapped(OpenALFile &file); + + static std::vector m_decoders; +}; + +} // namespace CocosDenshion + +#endif // COCOSDENSHION_OPENALLOADER_H diff --git a/CocosDenshion/linux/SimpleAudioEngineOpenAL.cpp b/CocosDenshion/openal/SimpleAudioEngineOpenAL.cpp similarity index 75% rename from CocosDenshion/linux/SimpleAudioEngineOpenAL.cpp rename to CocosDenshion/openal/SimpleAudioEngineOpenAL.cpp index 182f223b74..5fb87ec19a 100644 --- a/CocosDenshion/linux/SimpleAudioEngineOpenAL.cpp +++ b/CocosDenshion/openal/SimpleAudioEngineOpenAL.cpp @@ -1,618 +1,532 @@ -/**************************************************************************** -Copyright (c) 2010 cocos2d-x.org - -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. -****************************************************************************/ - -#include -#include -#include -#include - -#include -#include -#include -#include - -#ifndef DISABLE_VORBIS -#include -#endif - -#include "SimpleAudioEngine.h" -#include "cocos2d.h" -USING_NS_CC; - -using namespace std; - -namespace CocosDenshion -{ - struct soundData { - ALuint buffer; - ALuint source; - bool isLooped; - float pitch; - float pan; - float gain; - }; - - typedef map EffectsMap; - EffectsMap s_effects; - - typedef enum { - PLAYING, - STOPPED, - PAUSED, - } playStatus; - - static float s_volume = 1.0f; - static float s_effectVolume = 1.0f; - - struct backgroundMusicData { - ALuint buffer; - ALuint source; - }; - typedef map BackgroundMusicsMap; - BackgroundMusicsMap s_backgroundMusics; - - static ALuint s_backgroundSource = AL_NONE; - - static SimpleAudioEngine *s_engine = 0; - - static int checkALError(const char *funcName) - { - int err = alGetError(); - - if (err != AL_NO_ERROR) - { - switch (err) - { - case AL_INVALID_NAME: - fprintf(stderr, "AL_INVALID_NAME in %s\n", funcName); - break; - - case AL_INVALID_ENUM: - fprintf(stderr, "AL_INVALID_ENUM in %s\n", funcName); - break; - - case AL_INVALID_VALUE: - fprintf(stderr, "AL_INVALID_VALUE in %s\n", funcName); - break; - - case AL_INVALID_OPERATION: - fprintf(stderr, "AL_INVALID_OPERATION in %s\n", funcName); - break; - - case AL_OUT_OF_MEMORY: - fprintf(stderr, "AL_OUT_OF_MEMORY in %s\n", funcName); - break; - } - } - - return err; - } - - static void stopBackground(bool bReleaseData) - { - alSourceStop(s_backgroundSource); - - if (bReleaseData) - { - for (BackgroundMusicsMap::iterator it = s_backgroundMusics.begin(); it != s_backgroundMusics.end(); ++it) - { - if (it->second->source == s_backgroundSource) - { - alDeleteBuffers(1, &it->second->buffer); - checkALError("stopBackground:alDeleteBuffers"); - alDeleteSources(1, &it->second->source); - checkALError("stopBackground:alDeleteSources"); - delete it->second; - s_backgroundMusics.erase(it); - break; - } - } - } - - s_backgroundSource = AL_NONE; - } - - static void setBackgroundVolume(float volume) - { - alSourcef(s_backgroundSource, AL_GAIN, volume); - } - - SimpleAudioEngine::SimpleAudioEngine() - { - alutInit(0, 0); - checkALError("SimpleAudioEngine:alutInit"); - } - - SimpleAudioEngine::~SimpleAudioEngine() - { - alutExit(); - } - - SimpleAudioEngine* SimpleAudioEngine::sharedEngine() - { - if (!s_engine) - s_engine = new SimpleAudioEngine(); - - return s_engine; - } - - void SimpleAudioEngine::end() - { - checkALError("end:init"); - - // clear all the sounds - EffectsMap::const_iterator end = s_effects.end(); - for (EffectsMap::iterator it = s_effects.begin(); it != end; it++) - { - alSourceStop(it->second->source); - checkALError("end:alSourceStop"); - alDeleteBuffers(1, &it->second->buffer); - checkALError("end:alDeleteBuffers"); - alDeleteSources(1, &it->second->source); - checkALError("end:alDeleteSources"); - delete it->second; - } - s_effects.clear(); - - // and the background too - stopBackground(true); - - for (BackgroundMusicsMap::iterator it = s_backgroundMusics.begin(); it != s_backgroundMusics.end(); ++it) - { - alSourceStop(it->second->source); - checkALError("end:alSourceStop"); - alDeleteBuffers(1, &it->second->buffer); - checkALError("end:alDeleteBuffers"); - alDeleteSources(1, &it->second->source); - checkALError("end:alDeleteSources"); - delete it->second; - } - s_backgroundMusics.clear(); - } - -#ifndef DISABLE_VORBIS - - // - // OGG support - // - static bool isOGGFile(const char *pszFilePath) - { - FILE *file; - OggVorbis_File ogg_file; - int result; - - file = fopen(pszFilePath, "rb"); - result = ov_test(file, &ogg_file, 0, 0); - ov_clear(&ogg_file); - - return (result == 0); - } - - static ALuint createBufferFromOGG(const char *pszFilePath) - { - ALuint buffer; - OggVorbis_File ogg_file; - vorbis_info* info; - ALenum format; - int result; - int section; - unsigned int size = 0; - - if (ov_fopen(pszFilePath, &ogg_file) < 0) - { - ov_clear(&ogg_file); - fprintf(stderr, "Could not open OGG file %s\n", pszFilePath); - return -1; - } - - info = ov_info(&ogg_file, -1); - - if (info->channels == 1) - format = AL_FORMAT_MONO16; - else - format = AL_FORMAT_STEREO16; - - // size = #samples * #channels * 2 (for 16 bit) - unsigned int data_size = ov_pcm_total(&ogg_file, -1) * info->channels * 2; - char* data = new char[data_size]; - - while (size < data_size) - { - result = ov_read(&ogg_file, data + size, data_size - size, 0, 2, 1, §ion); - if (result > 0) - { - size += result; - } - else if (result < 0) - { - delete [] data; - fprintf(stderr, "OGG file problem %s\n", pszFilePath); - return -1; - } - else - { - break; - } - } - - if (size == 0) - { - delete [] data; - fprintf(stderr, "Unable to read OGG data\n"); - return -1; - } - - // clear al errors - checkALError("createBufferFromOGG:init"); - - // Load audio data into a buffer. - alGenBuffers(1, &buffer); - - if (checkALError("createBufferFromOGG:alGenBuffers") != AL_NO_ERROR) - { - fprintf(stderr, "Couldn't generate a buffer for OGG file\n"); - delete [] data; - return buffer; - } - - alBufferData(buffer, format, data, data_size, info->rate); - checkALError("createBufferFromOGG:alBufferData"); - - delete [] data; - ov_clear(&ogg_file); - - return buffer; - } -#endif - - // - // background audio - // - void SimpleAudioEngine::preloadBackgroundMusic(const char* pszFilePath) - { - // Changing file path to full path - std::string fullPath = FileUtils::sharedFileUtils()->fullPathForFilename(pszFilePath); - - BackgroundMusicsMap::const_iterator it = s_backgroundMusics.find(fullPath); - if (it == s_backgroundMusics.end()) - { - ALuint buffer = AL_NONE; -#ifndef DISABLE_VORBIS - if (isOGGFile(fullPath.data())) - { - buffer = createBufferFromOGG(fullPath.data()); - } - else -#endif - buffer = alutCreateBufferFromFile(fullPath.data()); - - - checkALError("preloadBackgroundMusic:createBuffer"); - - if (buffer == AL_NONE) - { - fprintf(stderr, "Error loading file: '%s'\n", fullPath.data()); - alDeleteBuffers(1, &buffer); - return; - } - - ALuint source = AL_NONE; - alGenSources(1, &source); - checkALError("preloadBackgroundMusic:alGenSources"); - - alSourcei(source, AL_BUFFER, buffer); - checkALError("preloadBackgroundMusic:alSourcei"); - - backgroundMusicData* data = new backgroundMusicData(); - data->buffer = buffer; - data->source = source; - s_backgroundMusics.insert(BackgroundMusicsMap::value_type(fullPath, data)); - } - } - - void SimpleAudioEngine::playBackgroundMusic(const char* pszFilePath, bool bLoop) - { - if (s_backgroundSource != AL_NONE) - stopBackgroundMusic(false); - - // Changing file path to full path - std::string fullPath = FileUtils::sharedFileUtils()->fullPathForFilename(pszFilePath); - - BackgroundMusicsMap::const_iterator it = s_backgroundMusics.find(fullPath); - if (it == s_backgroundMusics.end()) - { - preloadBackgroundMusic(fullPath.c_str()); - it = s_backgroundMusics.find(fullPath); - } - - if (it != s_backgroundMusics.end()) - { - s_backgroundSource = it->second->source; - alSourcei(s_backgroundSource, AL_LOOPING, bLoop ? AL_TRUE : AL_FALSE); - alSourcePlay(s_backgroundSource); - checkALError("playBackgroundMusic:alSourcePlay"); - } - } - - void SimpleAudioEngine::stopBackgroundMusic(bool bReleaseData) - { - stopBackground(bReleaseData); - } - - void SimpleAudioEngine::pauseBackgroundMusic() - { - ALint state; - alGetSourcei(s_backgroundSource, AL_SOURCE_STATE, &state); - if (state == AL_PLAYING) - alSourcePause(s_backgroundSource); - checkALError("pauseBackgroundMusic:alSourcePause"); - } - - void SimpleAudioEngine::resumeBackgroundMusic() - { - ALint state; - alGetSourcei(s_backgroundSource, AL_SOURCE_STATE, &state); - if (state == AL_PAUSED) - alSourcePlay(s_backgroundSource); - checkALError("resumeBackgroundMusic:alSourcePlay"); - } - - void SimpleAudioEngine::rewindBackgroundMusic() - { - alSourceRewind(s_backgroundSource); - checkALError("rewindBackgroundMusic:alSourceRewind"); - } - - bool SimpleAudioEngine::willPlayBackgroundMusic() - { - return true; - } - - bool SimpleAudioEngine::isBackgroundMusicPlaying() - { - ALint play_status; - alGetSourcei(s_backgroundSource, AL_SOURCE_STATE, &play_status); - - return (play_status == AL_PLAYING); - } - - float SimpleAudioEngine::getBackgroundMusicVolume() - { - return s_volume; - } - - void SimpleAudioEngine::setBackgroundMusicVolume(float volume) - { - if (s_volume != volume && volume >= -0.0001 && volume <= 1.0001) - { - s_volume = volume; - - setBackgroundVolume(volume); - } - } - - // - // Effect audio (using OpenAL) - // - float SimpleAudioEngine::getEffectsVolume() - { - return s_effectVolume; - } - - void SimpleAudioEngine::setEffectsVolume(float volume) - { - if (volume != s_effectVolume) - { - EffectsMap::const_iterator end = s_effects.end(); - for (EffectsMap::const_iterator it = s_effects.begin(); it != end; it++) - { - alSourcef(it->second->source, AL_GAIN, volume); - } - - s_effectVolume = volume; - } - } - - unsigned int SimpleAudioEngine::playEffect(const char* pszFilePath, bool bLoop, - float pitch, float pan, float gain) - { - std::string fullPath = FileUtils::sharedFileUtils()->fullPathForFilename(pszFilePath); - - EffectsMap::iterator iter = s_effects.find(fullPath); - - if (iter == s_effects.end()) - { - preloadEffect(fullPath.c_str()); - - // let's try again - iter = s_effects.find(fullPath); - if (iter == s_effects.end()) - { - fprintf(stderr, "could not find play sound %s\n", fullPath.c_str()); - return -1; - } - } - - checkALError("playEffect:init"); - - soundData &d = *iter->second; - d.isLooped = bLoop; - d.pitch = pitch; - d.pan = pan; - d.gain = gain; - alSourcei(d.source, AL_LOOPING, d.isLooped ? AL_TRUE : AL_FALSE); - alSourcef(d.source, AL_GAIN, d.gain); - alSourcef(d.source, AL_PITCH, d.pitch); - float sourcePosAL[] = {d.pan, 0.0f, 0.0f};//Set position - just using left and right panning - alSourcefv(d.source, AL_POSITION, sourcePosAL); - alSourcePlay(d.source); - checkALError("playEffect:alSourcePlay"); - - return d.source; - } - - void SimpleAudioEngine::stopEffect(unsigned int nSoundId) - { - alSourceStop(nSoundId); - checkALError("stopEffect:alSourceStop"); - } - - void SimpleAudioEngine::preloadEffect(const char* pszFilePath) - { - // Changing file path to full path - std::string fullPath = FileUtils::sharedFileUtils()->fullPathForFilename(pszFilePath); - - EffectsMap::iterator iter = s_effects.find(fullPath); - - // check if we have this already - if (iter == s_effects.end()) - { - ALuint buffer; - ALuint source; - soundData *data = new soundData; - string path = fullPath; - - checkALError("preloadEffect:init"); -#ifndef DISABLE_VORBIS - if (isOGGFile(path.data())) - { - buffer = createBufferFromOGG(path.data()); - } - else -#endif - { - buffer = alutCreateBufferFromFile(path.data()); - checkALError("preloadEffect:createBufferFromFile"); - } - - if (buffer == AL_NONE) - { - fprintf(stderr, "Error loading file: '%s'\n", path.data()); - alDeleteBuffers(1, &buffer); - return; - } - - alGenSources(1, &source); - - if (checkALError("preloadEffect:alGenSources") != AL_NO_ERROR) - { - alDeleteBuffers(1, &buffer); - return; - } - - alSourcei(source, AL_BUFFER, buffer); - checkALError("preloadEffect:alSourcei"); - - data->isLooped = false; - data->buffer = buffer; - data->source = source; - - s_effects.insert(EffectsMap::value_type(fullPath, data)); - } - } - - void SimpleAudioEngine::unloadEffect(const char* pszFilePath) - { - // Changing file path to full path - std::string fullPath = FileUtils::sharedFileUtils()->fullPathForFilename(pszFilePath); - - EffectsMap::iterator iter = s_effects.find(fullPath); - - if (iter != s_effects.end()) - { - checkALError("unloadEffect:init"); - - alSourceStop(iter->second->source); - checkALError("unloadEffect:alSourceStop"); - - alDeleteSources(1, &iter->second->source); - checkALError("unloadEffect:DeletSources"); - - alDeleteBuffers(1, &iter->second->buffer); - checkALError("unloadEffect:alDeleteBuffers"); - delete iter->second; - - s_effects.erase(iter); - } - } - - void SimpleAudioEngine::pauseEffect(unsigned int nSoundId) - { - ALint state; - alGetSourcei(nSoundId, AL_SOURCE_STATE, &state); - if (state == AL_PLAYING) - alSourcePause(nSoundId); - checkALError("pauseEffect:alSourcePause"); - } - - void SimpleAudioEngine::pauseAllEffects() - { - EffectsMap::iterator iter = s_effects.begin(); - ALint state; - while (iter != s_effects.end()) - { - alGetSourcei(iter->second->source, AL_SOURCE_STATE, &state); - if (state == AL_PLAYING) - alSourcePause(iter->second->source); - checkALError("pauseAllEffects:alSourcePause"); - ++iter; - } - } - - void SimpleAudioEngine::resumeEffect(unsigned int nSoundId) - { - ALint state; - alGetSourcei(nSoundId, AL_SOURCE_STATE, &state); - if (state == AL_PAUSED) - alSourcePlay(nSoundId); - checkALError("resumeEffect:alSourcePlay"); - } - - void SimpleAudioEngine::resumeAllEffects() - { - EffectsMap::iterator iter = s_effects.begin(); - ALint state; - while (iter != s_effects.end()) - { - alGetSourcei(iter->second->source, AL_SOURCE_STATE, &state); - if (state == AL_PAUSED) - alSourcePlay(iter->second->source); - checkALError("resumeAllEffects:alSourcePlay"); - ++iter; - } - } - - void SimpleAudioEngine::stopAllEffects() - { - EffectsMap::iterator iter = s_effects.begin(); - - if (iter != s_effects.end()) - { - checkALError("stopAllEffects:init"); - alSourceStop(iter->second->source); - checkALError("stopAllEffects:alSourceStop"); - } - } - -} +/**************************************************************************** +Copyright (c) 2010 cocos2d-x.org + +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. +****************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include "OpenALDecoder.h" +#include "SimpleAudioEngine.h" + +#ifdef ENABLE_MPG123 +#include +#endif + +USING_NS_CC; + +using namespace std; + +namespace CocosDenshion +{ + struct soundData { + ALuint buffer; + ALuint source; + bool isLooped; + float pitch; + float pan; + float gain; + }; + + typedef map EffectsMap; + EffectsMap s_effects; + + typedef enum { + PLAYING, + STOPPED, + PAUSED, + } playStatus; + + static float s_volume = 1.0f; + static float s_effectVolume = 1.0f; + + struct backgroundMusicData { + ALuint buffer; + ALuint source; + }; + typedef map BackgroundMusicsMap; + BackgroundMusicsMap s_backgroundMusics; + + static ALuint s_backgroundSource = AL_NONE; + + static SimpleAudioEngine *s_engine = 0; + + static int checkALError(const char *funcName) + { + int err = alGetError(); + + if (err != AL_NO_ERROR) + { + switch (err) + { + case AL_INVALID_NAME: + fprintf(stderr, "AL_INVALID_NAME in %s\n", funcName); + break; + + case AL_INVALID_ENUM: + fprintf(stderr, "AL_INVALID_ENUM in %s\n", funcName); + break; + + case AL_INVALID_VALUE: + fprintf(stderr, "AL_INVALID_VALUE in %s\n", funcName); + break; + + case AL_INVALID_OPERATION: + fprintf(stderr, "AL_INVALID_OPERATION in %s\n", funcName); + break; + + case AL_OUT_OF_MEMORY: + fprintf(stderr, "AL_OUT_OF_MEMORY in %s\n", funcName); + break; + } + } + + return err; + } + + static void stopBackground(bool bReleaseData) + { + alSourceStop(s_backgroundSource); + + if (bReleaseData) + { + for (BackgroundMusicsMap::iterator it = s_backgroundMusics.begin(); it != s_backgroundMusics.end(); ++it) + { + if (it->second->source == s_backgroundSource) + { + alDeleteBuffers(1, &it->second->buffer); + checkALError("stopBackground:alDeleteBuffers"); + alDeleteSources(1, &it->second->source); + checkALError("stopBackground:alDeleteSources"); + delete it->second; + s_backgroundMusics.erase(it); + break; + } + } + } + + s_backgroundSource = AL_NONE; + } + + static void setBackgroundVolume(float volume) + { + alSourcef(s_backgroundSource, AL_GAIN, volume); + } + + SimpleAudioEngine::SimpleAudioEngine() + { + alutInit(0, 0); +#ifdef ENABLE_MPG123 + mpg123_init(); +#endif + checkALError("SimpleAudioEngine:alutInit"); + OpenALDecoder::installDecoders(); + } + + SimpleAudioEngine::~SimpleAudioEngine() + { +#ifdef ENABLE_MPG123 + mpg123_exit(); +#endif + alutExit(); + } + + SimpleAudioEngine* SimpleAudioEngine::sharedEngine() + { + if (!s_engine) + s_engine = new SimpleAudioEngine(); + + return s_engine; + } + + void SimpleAudioEngine::end() + { + checkALError("end:init"); + + // clear all the sounds + EffectsMap::const_iterator end = s_effects.end(); + for (EffectsMap::iterator it = s_effects.begin(); it != end; it++) + { + alSourceStop(it->second->source); + checkALError("end:alSourceStop"); + alDeleteBuffers(1, &it->second->buffer); + checkALError("end:alDeleteBuffers"); + alDeleteSources(1, &it->second->source); + checkALError("end:alDeleteSources"); + delete it->second; + } + s_effects.clear(); + + // and the background too + stopBackground(true); + + for (BackgroundMusicsMap::iterator it = s_backgroundMusics.begin(); it != s_backgroundMusics.end(); ++it) + { + alSourceStop(it->second->source); + checkALError("end:alSourceStop"); + alDeleteBuffers(1, &it->second->buffer); + checkALError("end:alDeleteBuffers"); + alDeleteSources(1, &it->second->source); + checkALError("end:alDeleteSources"); + delete it->second; + } + s_backgroundMusics.clear(); + } + + // + // background audio + // + void SimpleAudioEngine::preloadBackgroundMusic(const char* pszFilePath) + { + // Changing file path to full path + std::string fullPath = FileUtils::sharedFileUtils()->fullPathForFilename(pszFilePath); + + BackgroundMusicsMap::const_iterator it = s_backgroundMusics.find(fullPath); + if (it == s_backgroundMusics.end()) + { + ALuint buffer = AL_NONE; + bool success = false; + OpenALFile file; + file.debugName = pszFilePath; + file.file = fopen(fullPath.c_str(), "rb"); + if (!file.file) { + fprintf(stderr, "Cannot read file: '%s'\n", fullPath.data()); + return; + } + + /// TODO: check that there are no AL buffer leaks. + const std::vector &decoders = OpenALDecoder::getDecoders(); + for (size_t i = 0, n = decoders.size(); !success && i < n; ++i) + success = decoders[i]->decode(file, buffer); + file.clear(); + + ALuint source = AL_NONE; + alGenSources(1, &source); + checkALError("preloadBackgroundMusic:alGenSources"); + + alSourcei(source, AL_BUFFER, buffer); + checkALError("preloadBackgroundMusic:alSourcei"); + + backgroundMusicData* data = new backgroundMusicData(); + data->buffer = buffer; + data->source = source; + s_backgroundMusics.insert(BackgroundMusicsMap::value_type(fullPath, data)); + } + } + + void SimpleAudioEngine::playBackgroundMusic(const char* pszFilePath, bool bLoop) + { + if (s_backgroundSource != AL_NONE) + stopBackgroundMusic(false); + + // Changing file path to full path + std::string fullPath = FileUtils::sharedFileUtils()->fullPathForFilename(pszFilePath); + + BackgroundMusicsMap::const_iterator it = s_backgroundMusics.find(fullPath); + if (it == s_backgroundMusics.end()) + { + preloadBackgroundMusic(fullPath.c_str()); + it = s_backgroundMusics.find(fullPath); + } + + if (it != s_backgroundMusics.end()) + { + s_backgroundSource = it->second->source; + alSourcei(s_backgroundSource, AL_LOOPING, bLoop ? AL_TRUE : AL_FALSE); + alSourcePlay(s_backgroundSource); + checkALError("playBackgroundMusic:alSourcePlay"); + } + } + + void SimpleAudioEngine::stopBackgroundMusic(bool bReleaseData) + { + ALint state; + alGetSourcei(s_backgroundSource, AL_SOURCE_STATE, &state); + if (state == AL_PLAYING) + stopBackground(bReleaseData); + } + + void SimpleAudioEngine::pauseBackgroundMusic() + { + ALint state; + alGetSourcei(s_backgroundSource, AL_SOURCE_STATE, &state); + if (state == AL_PLAYING) + alSourcePause(s_backgroundSource); + checkALError("pauseBackgroundMusic:alSourcePause"); + } + + void SimpleAudioEngine::resumeBackgroundMusic() + { + ALint state; + alGetSourcei(s_backgroundSource, AL_SOURCE_STATE, &state); + if (state == AL_PAUSED) + alSourcePlay(s_backgroundSource); + checkALError("resumeBackgroundMusic:alSourcePlay"); + } + + void SimpleAudioEngine::rewindBackgroundMusic() + { + ALint state; + alGetSourcei(s_backgroundSource, AL_SOURCE_STATE, &state); + if (state == AL_PLAYING) + { + alSourceRewind(s_backgroundSource); + alSourcePlay(s_backgroundSource); + } + checkALError("rewindBackgroundMusic:alSourceRewind"); + } + + bool SimpleAudioEngine::willPlayBackgroundMusic() + { + return true; + } + + bool SimpleAudioEngine::isBackgroundMusicPlaying() + { + ALint play_status; + alGetSourcei(s_backgroundSource, AL_SOURCE_STATE, &play_status); + + return (play_status == AL_PLAYING); + } + + float SimpleAudioEngine::getBackgroundMusicVolume() + { + return s_volume; + } + + void SimpleAudioEngine::setBackgroundMusicVolume(float volume) + { + if (s_volume != volume && volume >= -0.0001 && volume <= 1.0001) + { + s_volume = volume; + + setBackgroundVolume(volume); + } + } + + // + // Effect audio (using OpenAL) + // + float SimpleAudioEngine::getEffectsVolume() + { + return s_effectVolume; + } + + void SimpleAudioEngine::setEffectsVolume(float volume) + { + if (volume != s_effectVolume) + { + EffectsMap::const_iterator end = s_effects.end(); + for (EffectsMap::const_iterator it = s_effects.begin(); it != end; it++) + { + alSourcef(it->second->source, AL_GAIN, volume * it->second->gain); + } + + s_effectVolume = volume; + } + } + + unsigned int SimpleAudioEngine::playEffect(const char* pszFilePath, bool bLoop, + float pitch, float pan, float gain) + { + std::string fullPath = FileUtils::sharedFileUtils()->fullPathForFilename(pszFilePath); + + EffectsMap::iterator iter = s_effects.find(fullPath); + + if (iter == s_effects.end()) + { + preloadEffect(fullPath.c_str()); + + // let's try again + iter = s_effects.find(fullPath); + if (iter == s_effects.end()) + { + fprintf(stderr, "could not find play sound %s\n", fullPath.c_str()); + return -1; + } + } + + checkALError("playEffect:init"); + + soundData &d = *iter->second; + d.isLooped = bLoop; + d.pitch = pitch; + d.pan = pan; + d.gain = gain; + alSourcei(d.source, AL_LOOPING, d.isLooped ? AL_TRUE : AL_FALSE); + alSourcef(d.source, AL_GAIN, s_effectVolume * d.gain); + alSourcef(d.source, AL_PITCH, d.pitch); + float sourcePosAL[] = {d.pan, 0.0f, 0.0f};//Set position - just using left and right panning + alSourcefv(d.source, AL_POSITION, sourcePosAL); + alSourcePlay(d.source); + checkALError("playEffect:alSourcePlay"); + + return d.source; + } + + void SimpleAudioEngine::stopEffect(unsigned int nSoundId) + { + alSourceStop(nSoundId); + checkALError("stopEffect:alSourceStop"); + } + + void SimpleAudioEngine::preloadEffect(const char* pszFilePath) + { + // Changing file path to full path + std::string fullPath = FileUtils::sharedFileUtils()->fullPathForFilename(pszFilePath); + + EffectsMap::iterator iter = s_effects.find(fullPath); + + // check if we have this already + if (iter == s_effects.end()) + { + ALuint buffer = AL_NONE; + ALuint source = AL_NONE; + soundData *data = new soundData; + + checkALError("preloadEffect:init"); + OpenALFile file; + file.debugName = pszFilePath; + file.file = fopen(fullPath.c_str(), "rb"); + if (!file.file) { + fprintf(stderr, "Cannot read file: '%s'\n", fullPath.data()); + return; + } + + bool success = false; + /// TODO: check that there are no AL buffer leaks. + const std::vector &decoders = OpenALDecoder::getDecoders(); + for (size_t i = 0, n = decoders.size(); !success && i < n; ++i) + success = decoders[i]->decode(file, buffer); + file.clear(); + + alGenSources(1, &source); + + if (checkALError("preloadEffect:alGenSources") != AL_NO_ERROR) + { + alDeleteBuffers(1, &buffer); + return; + } + + alSourcei(source, AL_BUFFER, buffer); + checkALError("preloadEffect:alSourcei"); + + data->isLooped = false; + data->buffer = buffer; + data->source = source; + data->pitch = 1.0; + data->pan = 0.0; + data->gain = 1.0; + + s_effects.insert(EffectsMap::value_type(fullPath, data)); + } + } + + void SimpleAudioEngine::unloadEffect(const char* pszFilePath) + { + // Changing file path to full path + std::string fullPath = FileUtils::sharedFileUtils()->fullPathForFilename(pszFilePath); + + EffectsMap::iterator iter = s_effects.find(fullPath); + + if (iter != s_effects.end()) + { + checkALError("unloadEffect:init"); + + alSourceStop(iter->second->source); + checkALError("unloadEffect:alSourceStop"); + + alDeleteSources(1, &iter->second->source); + checkALError("unloadEffect:DeletSources"); + + alDeleteBuffers(1, &iter->second->buffer); + checkALError("unloadEffect:alDeleteBuffers"); + delete iter->second; + + s_effects.erase(iter); + } + } + + void SimpleAudioEngine::pauseEffect(unsigned int nSoundId) + { + ALint state; + alGetSourcei(nSoundId, AL_SOURCE_STATE, &state); + if (state == AL_PLAYING) + alSourcePause(nSoundId); + checkALError("pauseEffect:alSourcePause"); + } + + void SimpleAudioEngine::pauseAllEffects() + { + EffectsMap::iterator iter = s_effects.begin(); + ALint state; + while (iter != s_effects.end()) + { + alGetSourcei(iter->second->source, AL_SOURCE_STATE, &state); + if (state == AL_PLAYING) + alSourcePause(iter->second->source); + checkALError("pauseAllEffects:alSourcePause"); + ++iter; + } + } + + void SimpleAudioEngine::resumeEffect(unsigned int nSoundId) + { + ALint state; + alGetSourcei(nSoundId, AL_SOURCE_STATE, &state); + if (state == AL_PAUSED) + alSourcePlay(nSoundId); + checkALError("resumeEffect:alSourcePlay"); + } + + void SimpleAudioEngine::resumeAllEffects() + { + EffectsMap::iterator iter = s_effects.begin(); + ALint state; + while (iter != s_effects.end()) + { + alGetSourcei(iter->second->source, AL_SOURCE_STATE, &state); + if (state == AL_PAUSED) + alSourcePlay(iter->second->source); + checkALError("resumeAllEffects:alSourcePlay"); + ++iter; + } + } + + void SimpleAudioEngine::stopAllEffects() + { + EffectsMap::iterator iter = s_effects.begin(); + + if (iter != s_effects.end()) + { + checkALError("stopAllEffects:init"); + alSourceStop(iter->second->source); + checkALError("stopAllEffects:alSourceStop"); + } + } +} diff --git a/CocosDenshion/proj.blackberry/.project b/CocosDenshion/proj.blackberry/.project index d181a71795..59595cad8e 100644 --- a/CocosDenshion/proj.blackberry/.project +++ b/CocosDenshion/proj.blackberry/.project @@ -80,9 +80,9 @@ - blackberry + openal 2 - PARENT-1-PROJECT_LOC/blackberry + PARENT-1-PROJECT_LOC/openal include diff --git a/CocosDenshion/proj.linux/Makefile b/CocosDenshion/proj.linux/Makefile index 6510a4b9f4..82dc89a6d6 100644 --- a/CocosDenshion/proj.linux/Makefile +++ b/CocosDenshion/proj.linux/Makefile @@ -4,9 +4,15 @@ INCLUDES += -I.. -I../include ##Using OpenAL ifeq ($(OPENAL),1) -SOURCES = ../linux/SimpleAudioEngineOpenAL.cpp +SOURCES = ../openal/OpenALDecoder.cpp \ + ../openal/SimpleAudioEngineOpenAL.cpp SHAREDLIBS += -lopenal -lalut +ifeq ($(OPENAL_MP3),1) +DEFINES += -DENABLE_MPG123 +SHAREDLIBS += -lmpg123 +endif + ifneq ($(NOVORBIS),1) SHAREDLIBS += -logg -lvorbis -lvorbisfile else diff --git a/CocosDenshion/proj.nacl/Makefile b/CocosDenshion/proj.nacl/Makefile index 7988e4b5d2..75cda4add0 100644 --- a/CocosDenshion/proj.nacl/Makefile +++ b/CocosDenshion/proj.nacl/Makefile @@ -2,7 +2,8 @@ COCOS_ROOT = ../.. INCLUDES = -I../include -SOURCES = ../blackberry/SimpleAudioEngine.cpp +SOURCES = ../openal/OpenALDecoder.cpp \ + ../openal/SimpleAudioEngine.cpp include $(COCOS_ROOT)/cocos2dx/proj.nacl/cocos2dx.mk diff --git a/CocosDenshion/proj.tizen/.project b/CocosDenshion/proj.tizen/.project index 3f44ed4404..94f0701e24 100644 --- a/CocosDenshion/proj.tizen/.project +++ b/CocosDenshion/proj.tizen/.project @@ -87,19 +87,19 @@ - src/OspPlayer.cpp + src/OpenALDecoder.cpp 1 - PARENT-1-PROJECT_LOC/tizen/OspPlayer.cpp + PARENT-1-PROJECT_LOC/openal/OpenALDecoder.cpp - src/OspPlayer.h + src/OpenALDecoder.h 1 - PARENT-1-PROJECT_LOC/tizen/OspPlayer.h + PARENT-1-PROJECT_LOC/openal/OpenALDecoder.h - src/SimpleAudioEngine.cpp + src/SimpleAudioEngineOpenAL.cpp 1 - PARENT-1-PROJECT_LOC/tizen/SimpleAudioEngine.cpp + PARENT-1-PROJECT_LOC/openal/SimpleAudioEngineOpenAL.cpp diff --git a/CocosDenshion/tizen/OspPlayer.cpp b/CocosDenshion/tizen/OspPlayer.cpp deleted file mode 100644 index c3b12d4ca9..0000000000 --- a/CocosDenshion/tizen/OspPlayer.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/**************************************************************************** -Copyright (c) 2013 cocos2d-x.org -Copyright (c) 2013 Lee, Jae-Hong - -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. -****************************************************************************/ - -#include "OspPlayer.h" - -using namespace Tizen::Media; - -OspPlayer::OspPlayer() - : __pPlayer(null) - , _soundID(0) -{ - Initialize(); -} - -OspPlayer::~OspPlayer() -{ - Close(); -} - -result -OspPlayer::Initialize() -{ - result r = E_SUCCESS; - - __pPlayer = new (std::nothrow) Player(); - if (__pPlayer == null) - { - AppLogException("pPlyaer = new (std::nothrow) Player() has failed"); - return E_FAILURE; - } - - r = __pPlayer->Construct(*this, null); - if (IsFailed(r)) - { - AppLog("pPlayer->Construct has failed\n"); - return E_FAILURE; - } - - return r; -} - -void -OspPlayer::Open(const char* pFileName, unsigned int uId) -{ - result r = E_SUCCESS; - - Close(); - - r = __pPlayer->OpenFile(pFileName); - if (IsFailed(r)) - { - AppLog("pPlayer->OpenFile has failed\n"); - } - - _soundID = uId; -} - -void -OspPlayer::Play(bool bLoop) -{ - result r = E_SUCCESS; - - r = __pPlayer->SetLooping(bLoop); - if (IsFailed(r)) - { - AppLog("pPlayer->SetLooping has failed\n"); - return; - } - - r = __pPlayer->Play(); - if (IsFailed(r)) - { - AppLog("pPlayer->Play has failed\n"); - } -} - -void -OspPlayer::Pause() -{ - result r = E_SUCCESS; - - r = __pPlayer->Pause(); - if (IsFailed(r)) - { - AppLog("pPlayer->Pause has failed\n"); - } -} - -void -OspPlayer::Stop() -{ - result r = E_SUCCESS; - - r = __pPlayer->Stop(); - if (IsFailed(r)) - { - AppLog("pPlayer->Stop has failed\n"); - } -} - -void -OspPlayer::Resume() -{ - result r = E_SUCCESS; - - if (__pPlayer->GetState() == PLAYER_STATE_PAUSED) - { - r = __pPlayer->Play(); - if (IsFailed(r)) - { - AppLog("pPlayer->Play has failed\n"); - } - } -} - -void -OspPlayer::Rewind() -{ - result r = E_SUCCESS; - - r = __pPlayer->SeekTo(0); - if (IsFailed(r)) - { - AppLog("pPlayer->SeekTo has failed\n"); - } -} - -bool -OspPlayer::IsPlaying() -{ - PlayerState state = __pPlayer->GetState(); - - return (state == PLAYER_STATE_PLAYING) ? true : false; -} - -void -OspPlayer::Close() -{ - result r = E_SUCCESS; - - if (__pPlayer->GetState() == PLAYER_STATE_PLAYING) - { - r = __pPlayer->Stop(); - if (IsFailed(r)) - { - AppLog("pPlayer->Stop has failed\n"); - } - } - - r = __pPlayer->Close(); - if (IsFailed(r)) - { - AppLog("pPlayer->Close has failed\n"); - } -} - -unsigned int -OspPlayer::GetSoundID() -{ - return _soundID; -} - -void -OspPlayer::SetVolume(int volume) -{ - result r = E_SUCCESS; - - if (volume > 100 || volume < 0) - { - return; - } - - r = __pPlayer->SetVolume(volume); - if (IsFailed(r)) - { - AppLog("pPlayer->SetVolume has failed\n"); - } -} - -int -OspPlayer::GetVolume() -{ - return __pPlayer->GetVolume(); -} - -void -OspPlayer::OnPlayerOpened(result r) -{ -} - -void -OspPlayer::OnPlayerEndOfClip(void) -{ -} - -void -OspPlayer::OnPlayerBuffering(int percent) -{ -} - -void -OspPlayer::OnPlayerErrorOccurred(Tizen::Media::PlayerErrorReason r) -{ -} - -void -OspPlayer::OnPlayerInterrupted(void) -{ -} - -void -OspPlayer::OnPlayerReleased(void) -{ -} - -void -OspPlayer::OnPlayerSeekCompleted(result r) -{ -} - -void -OspPlayer::OnPlayerAudioFocusChanged(void) -{ -} diff --git a/CocosDenshion/tizen/OspPlayer.h b/CocosDenshion/tizen/OspPlayer.h deleted file mode 100644 index 621ca6879e..0000000000 --- a/CocosDenshion/tizen/OspPlayer.h +++ /dev/null @@ -1,65 +0,0 @@ -/**************************************************************************** -Copyright (c) 2013 cocos2d-x.org -Copyright (c) 2013 Lee, Jae-Hong - -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. -****************************************************************************/ - -#ifndef _OSP_PLAYER_H_ -#define _OSP_PLAYER_H_ - -#include - -class OspPlayer : public Tizen::Media::IPlayerEventListener -{ -public: - OspPlayer(); - ~OspPlayer(); - - result Initialize(); - void Open(const char* pFileName, unsigned int uId); - void Play(bool bLoop); - void Pause(); - void Stop(); - void Resume(); - void Rewind(); - bool IsPlaying(); - void SetVolume(int volume); - int GetVolume(); - void Close(); - unsigned int GetSoundID(); - -protected: - void OnPlayerOpened(result r); - void OnPlayerEndOfClip(void); - void OnPlayerBuffering(int percent); - void OnPlayerErrorOccurred(Tizen::Media::PlayerErrorReason r); - void OnPlayerInterrupted(void); - void OnPlayerReleased(void); - void OnPlayerSeekCompleted(result r); - void OnPlayerAudioFocusChanged(void); - -private: - Tizen::Media::Player* __pPlayer; - unsigned int _soundID; -}; - -#endif // _OSP_PLAYER_H_ diff --git a/CocosDenshion/tizen/SimpleAudioEngine.cpp b/CocosDenshion/tizen/SimpleAudioEngine.cpp deleted file mode 100644 index 7d6e950bf2..0000000000 --- a/CocosDenshion/tizen/SimpleAudioEngine.cpp +++ /dev/null @@ -1,294 +0,0 @@ -/**************************************************************************** -Copyright (c) 2013 cocos2d-x.org -Copyright (c) 2013 Lee, Jae-Hong - -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. -****************************************************************************/ - -#include "SimpleAudioEngine.h" -#include "OspPlayer.h" -#include "cocos2d.h" -#include - -USING_NS_CC; -using namespace std; - -namespace CocosDenshion { - -typedef map EffectList; -typedef pair Effect; - -static std::string _FullPath(const char * szPath); -static unsigned int _Hash(const char *key); - -#define BREAK_IF(cond) if (cond) break; - -static EffectList& sharedList() -{ - static EffectList s_List; - return s_List; -} - -static OspPlayer& sharedMusic() -{ - static OspPlayer s_Music; - return s_Music; -} - -SimpleAudioEngine::SimpleAudioEngine() -{ -} - -SimpleAudioEngine::~SimpleAudioEngine() -{ -} - -SimpleAudioEngine* SimpleAudioEngine::sharedEngine() -{ - static SimpleAudioEngine s_SharedEngine; - return &s_SharedEngine; -} - -void SimpleAudioEngine::end() -{ - sharedMusic().Close(); - - EffectList::iterator p = sharedList().begin(); - while (p != sharedList().end()) - { - delete p->second; - p->second = NULL; - p++; - } - sharedList().clear(); -} - -void SimpleAudioEngine::preloadBackgroundMusic(const char* pszFilePath) -{ -} - -void SimpleAudioEngine::playBackgroundMusic(const char* pszFilePath, bool bLoop) -{ - if (!pszFilePath) - { - return; - } - - sharedMusic().Open(_FullPath(pszFilePath).c_str(), _Hash(pszFilePath)); - sharedMusic().Play(bLoop); -} - -void SimpleAudioEngine::stopBackgroundMusic(bool bReleaseData) -{ - if (bReleaseData) - { - sharedMusic().Close(); - } - else - { - sharedMusic().Stop(); - } -} - -void SimpleAudioEngine::pauseBackgroundMusic() -{ - sharedMusic().Pause(); -} - -void SimpleAudioEngine::resumeBackgroundMusic() -{ - sharedMusic().Resume(); -} - -void SimpleAudioEngine::rewindBackgroundMusic() -{ - sharedMusic().Rewind(); -} - -bool SimpleAudioEngine::willPlayBackgroundMusic() -{ - return false; -} - -bool SimpleAudioEngine::isBackgroundMusicPlaying() -{ - return sharedMusic().IsPlaying(); -} - -float SimpleAudioEngine::getBackgroundMusicVolume() -{ - return float(sharedMusic().GetVolume()) / 100.f; -} - -void SimpleAudioEngine::setBackgroundMusicVolume(float volume) -{ - sharedMusic().SetVolume(int(volume * 100)); -} - -float SimpleAudioEngine::getEffectsVolume() -{ - EffectList::iterator iter; - iter = sharedList().begin(); - if (iter != sharedList().end()) - { - return float(iter->second->GetVolume()) / 100.f; - } -} - -void SimpleAudioEngine::setEffectsVolume(float volume) -{ - EffectList::iterator iter; - for (iter = sharedList().begin(); iter != sharedList().end(); iter++) - { - iter->second->SetVolume(int(volume * 100)); - } -} - -unsigned int SimpleAudioEngine::playEffect(const char* pszFilePath, bool bLoop, - float pitch, float pan, float gain) -{ - unsigned int nRet = _Hash(pszFilePath); - - preloadEffect(pszFilePath); - - EffectList::iterator p = sharedList().find(nRet); - if (p != sharedList().end()) - { - p->second->Play(bLoop); - } - - return nRet; -} - -void SimpleAudioEngine::stopEffect(unsigned int nSoundId) -{ - EffectList::iterator p = sharedList().find(nSoundId); - if (p != sharedList().end()) - { - p->second->Stop(); - } -} - -void SimpleAudioEngine::preloadEffect(const char* pszFilePath) -{ - int nRet = 0; - do - { - BREAK_IF(! pszFilePath); - - nRet = _Hash(pszFilePath); - - BREAK_IF(sharedList().end() != sharedList().find(nRet)); - - sharedList().insert(Effect(nRet, new (std::nothrow) OspPlayer())); - OspPlayer * pPlayer = sharedList()[nRet]; - pPlayer->Open(_FullPath(pszFilePath).c_str(), nRet); - - BREAK_IF(nRet == pPlayer->GetSoundID()); - - sharedList().erase(nRet); - nRet = 0; - } - while (0); -} - -void SimpleAudioEngine::unloadEffect(const char* pszFilePath) -{ - unsigned nId = _Hash(pszFilePath); - - EffectList::iterator p = sharedList().find(nId); - if (p != sharedList().end()) - { - delete p->second; - p->second = NULL; - sharedList().erase(nId); - } -} - -void SimpleAudioEngine::pauseEffect(unsigned int nSoundId) -{ - EffectList::iterator p = sharedList().find(nSoundId); - if (p != sharedList().end()) - { - p->second->Pause(); - } -} - -void SimpleAudioEngine::pauseAllEffects() -{ - EffectList::iterator iter; - for (iter = sharedList().begin(); iter != sharedList().end(); iter++) - { - iter->second->Pause(); - } -} - -void SimpleAudioEngine::resumeEffect(unsigned int nSoundId) -{ - EffectList::iterator p = sharedList().find(nSoundId); - if (p != sharedList().end()) - { - p->second->Resume(); - } -} - -void SimpleAudioEngine::resumeAllEffects() -{ - EffectList::iterator iter; - for (iter = sharedList().begin(); iter != sharedList().end(); iter++) - { - iter->second->Resume(); - } -} - -void SimpleAudioEngine::stopAllEffects() -{ - EffectList::iterator iter; - for (iter = sharedList().begin(); iter != sharedList().end(); iter++) - { - iter->second->Stop(); - } -} - -////////////////////////////////////////////////////////////////////////// -// static function -////////////////////////////////////////////////////////////////////////// - -static std::string _FullPath(const char * szPath) -{ - return FileUtils::sharedFileUtils()->fullPathForFilename(szPath); -} - -unsigned int _Hash(const char *key) -{ - unsigned int len = strlen(key); - const char *end=key+len; - unsigned int hash; - - for (hash = 0; key < end; key++) - { - hash *= 16777619; - hash ^= (unsigned int) (unsigned char) toupper(*key); - } - return (hash); -} - -} diff --git a/cocos2dx/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java b/cocos2dx/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java old mode 100644 new mode 100755 index 59afdedf20..0a17d590a9 --- a/cocos2dx/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java +++ b/cocos2dx/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java @@ -77,7 +77,7 @@ public class Cocos2dxHelper { Cocos2dxHelper.nativeSetAssetManager(sAssetManager); Cocos2dxBitmap.setContext(pContext); - Cocos2dxETCLoader.setContext(pContext); + Cocos2dxETCLoader.setContext(pContext); } // =========================================================== @@ -173,8 +173,8 @@ public class Cocos2dxHelper { Cocos2dxHelper.sCocos2dSound.preloadEffect(path); } - public static int playEffect(final String path, final boolean isLoop) { - return Cocos2dxHelper.sCocos2dSound.playEffect(path, isLoop); + public static int playEffect(final String path, final boolean isLoop, final float pitch, final float pan, final float gain) { + return Cocos2dxHelper.sCocos2dSound.playEffect(path, isLoop, pitch, pan, gain); } public static void resumeEffect(final int soundId) { diff --git a/cocos2dx/platform/android/java/src/org/cocos2dx/lib/Cocos2dxSound.java b/cocos2dx/platform/android/java/src/org/cocos2dx/lib/Cocos2dxSound.java old mode 100644 new mode 100755 index b0fcfbd3bc..faf350f08e --- a/cocos2dx/platform/android/java/src/org/cocos2dx/lib/Cocos2dxSound.java +++ b/cocos2dx/platform/android/java/src/org/cocos2dx/lib/Cocos2dxSound.java @@ -57,9 +57,9 @@ public class Cocos2dxSound { private final HashMap> mPathStreamIDsMap = new HashMap>(); private final HashMap mPathSoundIDMap = new HashMap(); - + private final ArrayList mEffecToPlayWhenLoadedArray = new ArrayList(); - + private int mStreamIdSyn; private Semaphore mSemaphore; @@ -83,11 +83,11 @@ public class Cocos2dxSound { private void initData() { this.mSoundPool = new SoundPool(Cocos2dxSound.MAX_SIMULTANEOUS_STREAMS_DEFAULT, AudioManager.STREAM_MUSIC, Cocos2dxSound.SOUND_QUALITY); - this.mSoundPool.setOnLoadCompleteListener(new OnLoadCompletedListener()); - + this.mSoundPool.setOnLoadCompleteListener(new OnLoadCompletedListener()); + this.mLeftVolume = 0.5f; this.mRightVolume = 0.5f; - + this.mSemaphore = new Semaphore(0, true); } @@ -135,13 +135,15 @@ public class Cocos2dxSound { } } - public int playEffect(final String pPath, final boolean pLoop) { + public int playEffect(final String pPath, final boolean pLoop, float pitch, float pan, float gain){ Integer soundID = this.mPathSoundIDMap.get(pPath); int streamID = Cocos2dxSound.INVALID_STREAM_ID; if (soundID != null) { + // parameters; pan = -1 for left channel, 1 for right channel, 0 for both channels + // play sound - streamID = this.doPlayEffect(pPath, soundID.intValue(), pLoop); + streamID = this.doPlayEffect(pPath, soundID.intValue(), pLoop, pitch, pan, gain); } else { // the effect is not prepared soundID = this.preloadEffect(pPath); @@ -149,16 +151,17 @@ public class Cocos2dxSound { // can not preload effect return Cocos2dxSound.INVALID_SOUND_ID; } - + // only allow one playEffect at a time, or the semaphore will not work correctly synchronized(this.mSoundPool) { // add this effect into mEffecToPlayWhenLoadedArray, and it will be played when loaded completely - mEffecToPlayWhenLoadedArray.add(new SoundInfoForLoadedCompleted(pPath, soundID.intValue(), pLoop)); - + mEffecToPlayWhenLoadedArray.add(new SoundInfoForLoadedCompleted(pPath, soundID.intValue(), pLoop, + pitch, pan, gain)); + try { // wait OnloadedCompleteListener to set streamID this.mSemaphore.acquire(); - + streamID = this.mStreamIdSyn; } catch(Exception e) { return Cocos2dxSound.INVALID_SOUND_ID; @@ -259,7 +262,7 @@ public class Cocos2dxSound { this.mLeftVolume = 0.5f; this.mRightVolume = 0.5f; - + this.initData(); } @@ -284,10 +287,18 @@ public class Cocos2dxSound { return soundID; } - - private int doPlayEffect(final String pPath, final int soundId, final boolean pLoop) { + + private float clamp(float value, float min, float max) { + return Math.max(min, (Math.min(value, max))); + } + + private int doPlayEffect(final String pPath, final int soundId, final boolean pLoop, float pitch, float pan, float gain) { + float leftVolume = this.mLeftVolume * gain * (1.0f - this.clamp(pan, 0.0f, 1.0f)); + float rightVolume = this.mRightVolume * gain * (1.0f - this.clamp(-pan, 0.0f, 1.0f)); + float soundRate = this.clamp(SOUND_RATE * pitch, 0.5f, 2.0f); + // play sound - int streamID = this.mSoundPool.play(soundId, this.mLeftVolume, this.mRightVolume, Cocos2dxSound.SOUND_PRIORITY, pLoop ? -1 : 0, Cocos2dxSound.SOUND_RATE); + int streamID = this.mSoundPool.play(soundId, this.clamp(leftVolume, 0.0f, 1.0f), this.clamp(rightVolume, 0.0f, 1.0f), Cocos2dxSound.SOUND_PRIORITY, pLoop ? -1 : 0, soundRate); // record stream id ArrayList streamIDs = this.mPathStreamIDsMap.get(pPath); @@ -296,26 +307,33 @@ public class Cocos2dxSound { this.mPathStreamIDsMap.put(pPath, streamIDs); } streamIDs.add(streamID); - + return streamID; } // =========================================================== // Inner and Anonymous Classes // =========================================================== - + public class SoundInfoForLoadedCompleted { public int soundID; - public boolean isLoop; - public String path; - - public SoundInfoForLoadedCompleted(String path, int soundId, boolean isLoop) { - this.path = path; - this.soundID = soundId; - this.isLoop = isLoop; - } + public boolean isLoop; + public float pitch; + public float pan; + public float gain; + public String path; + + public SoundInfoForLoadedCompleted(String path, int soundId, boolean isLoop, + float pitch, float pan, float gain) { + this.path = path; + this.soundID = soundId; + this.isLoop = isLoop; + this.pitch = pitch; + this.pan = pan; + this.gain = gain; + } } - + public class OnLoadCompletedListener implements SoundPool.OnLoadCompleteListener { @Override @@ -326,8 +344,8 @@ public class Cocos2dxSound { for ( SoundInfoForLoadedCompleted info : mEffecToPlayWhenLoadedArray) { if (sampleId == info.soundID) { // set the stream id which will be returned by playEffect() - mStreamIdSyn = doPlayEffect(info.path, info.soundID, info.isLoop); - + mStreamIdSyn = doPlayEffect(info.path, info.soundID, info.isLoop, info.pitch, info.pan, info.gain); + // remove it from array, because we will break here // so it is safe to do mEffecToPlayWhenLoadedArray.remove(info); @@ -338,7 +356,7 @@ public class Cocos2dxSound { } else { mStreamIdSyn = Cocos2dxSound.INVALID_SOUND_ID; } - + mSemaphore.release(); } } diff --git a/samples/Cpp/TestCpp/Classes/CocosDenshionTest/CocosDenshionTest.cpp b/samples/Cpp/TestCpp/Classes/CocosDenshionTest/CocosDenshionTest.cpp index b2889ad9e1..25fb48d228 100644 --- a/samples/Cpp/TestCpp/Classes/CocosDenshionTest/CocosDenshionTest.cpp +++ b/samples/Cpp/TestCpp/Classes/CocosDenshionTest/CocosDenshionTest.cpp @@ -1,84 +1,238 @@ #include "CocosDenshionTest.h" #include "cocos2d.h" #include "SimpleAudioEngine.h" +#include "GUI/CCControlExtension/CCControlSlider.h" // android effect only support ogg -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) +#if (CC_TARGET_PLATFORM == CC_PLATFOR_ANDROID) #define EFFECT_FILE "effect2.ogg" -#elif( CC_TARGET_PLATFORM == CC_PLATFORM_MARMALADE) +#elif( CC_TARGET_PLATFORM == CC_PLATFOR_MARMALADE) #define EFFECT_FILE "effect1.raw" #else #define EFFECT_FILE "effect1.wav" -#endif // CC_PLATFORM_ANDROID +#endif // CC_PLATFOR_ANDROID -#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) +#if (CC_TARGET_PLATFORM == CC_PLATFOR_WIN32) #define MUSIC_FILE "music.mid" -#elif (CC_TARGET_PLATFORM == CC_PLATFORM_BLACKBERRY || CC_TARGET_PLATFORM == CC_PLATFORM_LINUX ) +#elif (CC_TARGET_PLATFORM == CC_PLATFOR_BLACKBERRY || CC_TARGET_PLATFORM == CC_PLATFOR_LINUX ) #define MUSIC_FILE "background.ogg" #else #define MUSIC_FILE "background.mp3" -#endif // CC_PLATFORM_WIN32 +#endif // CC_PLATFOR_WIN32 USING_NS_CC; using namespace CocosDenshion; #define LINE_SPACE 40 -CocosDenshionTest::CocosDenshionTest() -: _itmeMenu(NULL), -_beginPos(PointZero), -_soundId(0) +class Button : public Node, public TargetedTouchDelegate { - std::string testItems[] = { - "play background music", - "stop background music", - "pause background music", - "resume background music", - "rewind background music", - "is background music playing", - "play effect", - "play effect repeatly", - "stop effect", - "unload effect", - "add background music volume", - "sub background music volume", - "add effects volume", - "sub effects volume", - "pause effect", - "resume effect", - "pause all effects", - "resume all effects", - "stop all effects" - }; - - // add menu items for tests - _itmeMenu = Menu::create(); - - _testCount = sizeof(testItems) / sizeof(testItems[0]); - - for (int i = 0; i < _testCount; ++i) +public: + static Button *createWithSprite(const char *filePath) { -//#if (CC_TARGET_PLATFORM == CC_PLATFORM_MARMALADE) -// LabelBMFont* label = LabelBMFont::create(testItems[i].c_str(), "fonts/arial16.fnt"); -//#else - LabelTTF* label = LabelTTF::create(testItems[i].c_str(), "Arial", 24); -//#endif - MenuItemLabel* pMenuItem = MenuItemLabel::create(label, CC_CALLBACK_1(CocosDenshionTest::menuCallback, this)); - - _itmeMenu->addChild(pMenuItem, i + 10000); - pMenuItem->setPosition( ccp( VisibleRect::center().x, (VisibleRect::top().y - (i + 1) * LINE_SPACE) )); + auto b = new Button(); + if (b && !b->initSpriteButton(filePath)) { + delete b; + b = NULL; + } + return b; } - _itmeMenu->setContentSize(CCSizeMake(VisibleRect::getVisibleRect().size.width, (_testCount + 1) * LINE_SPACE)); - _itmeMenu->setPosition(PointZero); - addChild(_itmeMenu); + static Button *createWithText(const char *text) + { + auto b = new Button(); + if (b && !b->initTextButton(text)) { + delete b; + b = NULL; + } + return b; + } + + ~Button() + { + Director::sharedDirector()->getTouchDispatcher()->removeDelegate(this); + } + + void onTriggered(const std::function &onTriggered) + { + _onTriggered = onTriggered; + } + +private: + Button() + : _child(NULL) + { + Director::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 100, true); + } + + bool initSpriteButton(const char *filePath) + { + _child = Sprite::create(filePath); + addChild(_child); + return true; + } + + bool initTextButton(const char *text) + { + _child = LabelTTF::create(text, "Arial", 16); + addChild(_child); + return true; + } + + bool touchHits(Touch *pTouch) + { + const Rect area(0, 0, _child->getContentSize().width, _child->getContentSize().height); + return area.containsPoint(_child->convertToNodeSpace(pTouch->getLocation())); + } + + virtual bool ccTouchBegan(Touch *pTouch, Event *pEvent) + { + CC_UNUSED_PARAM(pEvent); + const bool hits = touchHits(pTouch); + if (hits) + scaleButtonTo(0.9); + return hits; + } + + virtual void ccTouchEnded(Touch *pTouch, Event *pEvent) + { + CC_UNUSED_PARAM(pEvent); + const bool hits = touchHits(pTouch); + if (hits && _onTriggered) + _onTriggered(); + scaleButtonTo(1); + } + + virtual void ccTouchCancelled(Touch *pTouch, Event *pEvent) + { + CC_UNUSED_PARAM(pEvent); + scaleButtonTo(1); + } + + void scaleButtonTo(float scale) + { + auto action = ScaleTo::create(0.1, scale); + action->setTag(900); + stopActionByTag(900); + runAction(action); + } + + Node *_child; + std::function _onTriggered; +}; + +class AudioSlider : public Node +{ +public: + enum Direction { + Vertical, + Horizontal + }; + + static AudioSlider *create(Direction direction) + { + auto ret = new AudioSlider(direction); + if (ret && !ret->init()) { + delete ret; + ret = NULL; + } + return ret; + } + + float getValue() const + { + return _slider->getValue(); + } + + void setValue(float minValue, float maxValue, float value) + { + _slider->setMinimumValue(minValue); + _slider->setMaximumValue(maxValue); + _slider->setValue(value); + + char buffer[32]; + + sprintf(buffer, "%.2f", minValue); + if (!_lblMinValue) { + _lblMinValue = LabelTTF::create(buffer, "Arial", 8); + addChild(_lblMinValue); + if (_direction == Vertical) + _lblMinValue->setPosition(ccp(12.0, -50.0)); + else + _lblMinValue->setPosition(ccp(-50, 12.0)); + } else { + _lblMinValue->setString(buffer); + } + + sprintf(buffer, "%.2f", maxValue); + if (!_lblMaxValue) { + _lblMaxValue = LabelTTF::create(buffer, "Arial", 8); + addChild(_lblMaxValue); + if (_direction == Vertical) + _lblMaxValue->setPosition(ccp(12.0, 50.0)); + else + _lblMaxValue->setPosition(ccp(50, 12.0)); + } else { + _lblMaxValue->setString(buffer); + } + } + +private: + AudioSlider(Direction direction) + : _direction(direction) + , _slider(NULL) + , _lblMinValue(NULL) + , _lblMaxValue(NULL) + { + } + + bool init() + { + _slider = extension::ControlSlider::create("extensions/sliderTrack.png","extensions/sliderProgress.png" ,"extensions/sliderThumb.png"); + _slider->setScale(0.5); + if (_direction == Vertical) + _slider->setRotation(-90.0); + addChild(_slider); + return true; + } + + Direction _direction; + extension::ControlSlider *_slider; + LabelTTF *_lblMinValue; + LabelTTF *_lblMaxValue; +}; + +CocosDenshionTest::CocosDenshionTest() +: _soundId(0), +_musicVolume(1), +_effectsVolume(1), +_sliderPitch(NULL), +_sliderPan(NULL), +_sliderGain(NULL), +_sliderEffectsVolume(NULL), +_sliderMusicVolume(NULL) +{ + addButtons(); + addSliders(); + schedule(schedule_selector(CocosDenshionTest::updateVolumes)); + +// SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic(); + +// std::string testItems[] = { +// "unload effect", +// "pause effect", +// "resume effect", +// "pause all effects", +// "resume all effects", +// "stop all effects" +// }; setTouchEnabled(true); // preload background music and effect SimpleAudioEngine::sharedEngine()->preloadBackgroundMusic( MUSIC_FILE ); SimpleAudioEngine::sharedEngine()->preloadEffect( EFFECT_FILE ); - + // set default volume SimpleAudioEngine::sharedEngine()->setEffectsVolume(0.5); SimpleAudioEngine::sharedEngine()->setBackgroundMusicVolume(0.5); @@ -95,128 +249,167 @@ void CocosDenshionTest::onExit() SimpleAudioEngine::sharedEngine()->end(); } -void CocosDenshionTest::menuCallback(Object * pSender) +void CocosDenshionTest::addButtons() { - // get the userdata, it's the index of the menu item clicked - MenuItem* pMenuItem = (MenuItem *)(pSender); - int nIdx = pMenuItem->getZOrder() - 10000; - - switch(nIdx) - { - // play background music - case 0: + LabelTTF *lblMusic = LabelTTF::create("Control Music", "Arial", 24); + addChildAt(lblMusic, 0.25, 1); + Button *btnPlay = Button::createWithText("play"); + btnPlay->onTriggered([]() { SimpleAudioEngine::sharedEngine()->playBackgroundMusic(MUSIC_FILE, true); - break; - // stop background music - case 1: + }); + addChildAt(btnPlay, 0.1, 0.7); + + Button *btnStop = Button::createWithText("stop"); + btnStop->onTriggered([]() { SimpleAudioEngine::sharedEngine()->stopBackgroundMusic(); - break; - // pause background music - case 2: - SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic(); - break; - // resume background music - case 3: - SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic(); - break; - // rewind background music - case 4: + }); + addChildAt(btnStop, 0.25, 0.7); + + Button *btnRewindMusic = Button::createWithText("rewind"); + btnRewindMusic->onTriggered([]() { SimpleAudioEngine::sharedEngine()->rewindBackgroundMusic(); - break; - // is background music playing - case 5: + }); + addChildAt(btnRewindMusic, 0.4, 0.7); + + Button *btnPause = Button::createWithText("pause"); + btnPause->onTriggered([]() { + SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic(); + }); + addChildAt(btnPause, 0.1, 0.55); + + Button *btnResumeMusic = Button::createWithText("resume"); + btnResumeMusic->onTriggered([]() { + SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic(); + }); + addChildAt(btnResumeMusic, 0.25, 0.55); + + Button *btnIsPlayingMusic = Button::createWithText("is playing"); + btnIsPlayingMusic->onTriggered([]() { if (SimpleAudioEngine::sharedEngine()->isBackgroundMusicPlaying()) - { CCLOG("background music is playing"); - } else - { CCLOG("background music is not playing"); - } - break; - // play effect - case 6: - _soundId = SimpleAudioEngine::sharedEngine()->playEffect(EFFECT_FILE); - break; - // play effect - case 7: - _soundId = SimpleAudioEngine::sharedEngine()->playEffect(EFFECT_FILE, true); - break; - // stop effect - case 8: + }); + addChildAt(btnIsPlayingMusic, 0.4, 0.55); + + LabelTTF *lblSound = LabelTTF::create("Control Effects", "Arial", 24); + addChildAt(lblSound, 0.75, 1); + + Button *btnPlayEffect = Button::createWithText("play"); + btnPlayEffect->onTriggered([this]() { + const float pitch = _sliderPitch->getValue(); + const float pan = _sliderPan->getValue(); + const float gain = _sliderGain->getValue(); + _soundId = SimpleAudioEngine::sharedEngine()->playEffect(EFFECT_FILE, false, pitch, pan, gain); + }); + addChildAt(btnPlayEffect, 0.6, 0.9); + + Button *btnPlayEffectInLoop = Button::createWithText("play in loop"); + btnPlayEffectInLoop->onTriggered([this]() { + const float pitch = _sliderPitch->getValue(); + const float pan = _sliderPan->getValue(); + const float gain = _sliderGain->getValue(); + _soundId = SimpleAudioEngine::sharedEngine()->playEffect(EFFECT_FILE, true, pitch, pan, gain); + }); + addChildAt(btnPlayEffectInLoop, 0.75, 0.9); + + Button *btnStopEffect = Button::createWithText("stop"); + btnStopEffect->onTriggered([this]() { SimpleAudioEngine::sharedEngine()->stopEffect(_soundId); - break; - // unload effect - case 9: + }); + addChildAt(btnStopEffect, 0.9, 0.9); + + Button *btnUnloadEffect = Button::createWithText("unload"); + btnUnloadEffect->onTriggered([this]() { SimpleAudioEngine::sharedEngine()->unloadEffect(EFFECT_FILE); - break; - // add bakcground music volume - case 10: - SimpleAudioEngine::sharedEngine()->setBackgroundMusicVolume(SimpleAudioEngine::sharedEngine()->getBackgroundMusicVolume() + 0.1f); - break; - // sub backgroud music volume - case 11: - SimpleAudioEngine::sharedEngine()->setBackgroundMusicVolume(SimpleAudioEngine::sharedEngine()->getBackgroundMusicVolume() - 0.1f); - break; - // add effects volume - case 12: - SimpleAudioEngine::sharedEngine()->setEffectsVolume(SimpleAudioEngine::sharedEngine()->getEffectsVolume() + 0.1f); - break; - // sub effects volume - case 13: - SimpleAudioEngine::sharedEngine()->setEffectsVolume(SimpleAudioEngine::sharedEngine()->getEffectsVolume() - 0.1f); - break; - case 14: + }); + addChildAt(btnUnloadEffect, 0.6, 0.8); + + Button *btnPauseEffect = Button::createWithText("pause"); + btnPauseEffect->onTriggered([this]() { SimpleAudioEngine::sharedEngine()->pauseEffect(_soundId); - break; - case 15: + }); + addChildAt(btnPauseEffect, 0.75, 0.8); + + Button *btnResumeEffect = Button::createWithText("resume"); + btnResumeEffect->onTriggered([this]() { SimpleAudioEngine::sharedEngine()->resumeEffect(_soundId); - break; - case 16: + }); + addChildAt(btnResumeEffect, 0.9, 0.8); + + Button *btnPauseAll = Button::createWithText("pause all"); + btnPauseAll->onTriggered([this]() { SimpleAudioEngine::sharedEngine()->pauseAllEffects(); - break; - case 17: + }); + addChildAt(btnPauseAll, 0.6, 0.7); + + Button *btnResumeAll = Button::createWithText("resume all"); + btnResumeAll->onTriggered([this]() { SimpleAudioEngine::sharedEngine()->resumeAllEffects(); - break; - case 18: + }); + addChildAt(btnResumeAll, 0.75, 0.7); + + Button *btnStopAll = Button::createWithText("stop all"); + btnStopAll->onTriggered([this]() { SimpleAudioEngine::sharedEngine()->stopAllEffects(); - break; - } - + }); + addChildAt(btnStopAll, 0.9, 0.7); } -void CocosDenshionTest::ccTouchesBegan(Set *pTouches, Event *pEvent) +void CocosDenshionTest::addSliders() { - Touch* touch = (Touch*)pTouches->anyObject(); + auto lblPitch = LabelTTF::create("Pitch", "Arial", 14); + addChildAt(lblPitch, 0.67, 0.6); + _sliderPitch = AudioSlider::create(AudioSlider::Horizontal); + _sliderPitch->setValue(0.5, 2, 1); + addChildAt(_sliderPitch, 0.85, 0.6); - _beginPos = touch->getLocation(); + auto lblPan = LabelTTF::create("Pan", "Arial", 14); + addChildAt(lblPan, 0.67, 0.5); + _sliderPan = AudioSlider::create(AudioSlider::Horizontal); + _sliderPan->setValue(-1, 1, 0); + addChildAt(_sliderPan, 0.85, 0.5); + + auto lblGain = LabelTTF::create("Gain", "Arial", 14); + addChildAt(lblGain, 0.67, 0.4); + _sliderGain = AudioSlider::create(AudioSlider::Horizontal); + _sliderGain->setValue(0, 1, 1); + addChildAt(_sliderGain, 0.85, 0.4); + + auto lblEffectsVolume = LabelTTF::create("Effects Volume", "Arial", 14); + addChildAt(lblEffectsVolume, 0.62, 0.3); + _sliderEffectsVolume = AudioSlider::create(AudioSlider::Horizontal); + _sliderEffectsVolume->setValue(0, 1, 1); + addChildAt(_sliderEffectsVolume, 0.85, 0.3); + + auto lblMusicVolume = LabelTTF::create("Music Volume", "Arial", 14); + addChildAt(lblMusicVolume, 0.12, 0.3); + _sliderMusicVolume = AudioSlider::create(AudioSlider::Horizontal); + _sliderMusicVolume->setValue(0, 1, 1); + addChildAt(_sliderMusicVolume, 0.35, 0.3); } -void CocosDenshionTest::ccTouchesMoved(Set *pTouches, Event *pEvent) +void CocosDenshionTest::addChildAt(Node *node, float percentageX, float percentageY) { - Touch* touch = (Touch*)pTouches->anyObject(); + const Size size = VisibleRect::getVisibleRect().size; + node->setPosition(Point(percentageX * size.width, percentageY * size.height)); + addChild(node); +} - Point touchLocation = touch->getLocation(); - float nMoveY = touchLocation.y - _beginPos.y; - - Point curPos = _itmeMenu->getPosition(); - Point nextPos = ccp(curPos.x, curPos.y + nMoveY); - - if (nextPos.y < 0.0f) - { - _itmeMenu->setPosition(PointZero); - return; +void CocosDenshionTest::updateVolumes(float) +{ + const float musicVolume = _sliderMusicVolume->getValue(); + if (fabs(musicVolume - _musicVolume) > 0.001) { + _musicVolume = musicVolume; + SimpleAudioEngine::sharedEngine()->setBackgroundMusicVolume(_musicVolume); } - if (nextPos.y > ((_testCount + 1)* LINE_SPACE - VisibleRect::getVisibleRect().size.height)) - { - _itmeMenu->setPosition(ccp(0, ((_testCount + 1)* LINE_SPACE - VisibleRect::getVisibleRect().size.height))); - return; + const float effectsVolume = _sliderEffectsVolume->getValue(); + if (fabs(effectsVolume - _effectsVolume) > 0.001) { + _effectsVolume = effectsVolume; + SimpleAudioEngine::sharedEngine()->setEffectsVolume(_effectsVolume); } - - _itmeMenu->setPosition(nextPos); - _beginPos = touchLocation; } void CocosDenshionTestScene::runThisTest() diff --git a/samples/Cpp/TestCpp/Classes/CocosDenshionTest/CocosDenshionTest.h b/samples/Cpp/TestCpp/Classes/CocosDenshionTest/CocosDenshionTest.h index 324ac0455d..5de4d66165 100644 --- a/samples/Cpp/TestCpp/Classes/CocosDenshionTest/CocosDenshionTest.h +++ b/samples/Cpp/TestCpp/Classes/CocosDenshionTest/CocosDenshionTest.h @@ -3,22 +3,31 @@ #include "../testBasic.h" +class AudioSlider; + class CocosDenshionTest : public Layer { public: CocosDenshionTest(void); ~CocosDenshionTest(void); - void menuCallback(Object * pSender); - virtual void ccTouchesMoved(Set *pTouches, Event *pEvent); - virtual void ccTouchesBegan(Set *pTouches, Event *pEvent); virtual void onExit(); private: - Menu* _itmeMenu; - Point _beginPos; - int _testCount; + void addButtons(); + void addSliders(); + void addChildAt(Node *node, float percentageX, float percentageY); + + void updateVolumes(float); + unsigned int _soundId; + float _musicVolume; + float _effectsVolume; + AudioSlider *_sliderPitch; + AudioSlider *_sliderPan; + AudioSlider *_sliderGain; + AudioSlider *_sliderEffectsVolume; + AudioSlider *_sliderMusicVolume; }; class CocosDenshionTestScene : public TestScene diff --git a/samples/Cpp/TestCpp/proj.tizen/.cproject b/samples/Cpp/TestCpp/proj.tizen/.cproject index c4be844364..5a349243e4 100644 --- a/samples/Cpp/TestCpp/proj.tizen/.cproject +++ b/samples/Cpp/TestCpp/proj.tizen/.cproject @@ -25,7 +25,7 @@ - + @@ -70,7 +70,7 @@ - + @@ -105,7 +105,7 @@ - + - + @@ -218,7 +220,7 @@ - + @@ -260,7 +262,7 @@ - + @@ -294,7 +296,7 @@ - + - + @@ -406,7 +408,7 @@ - + - + - + - + @@ -541,7 +543,7 @@ - + - + - + @@ -605,7 +607,7 @@ - + @@ -682,7 +684,7 @@ - + - + - + @@ -746,7 +748,7 @@ - + @@ -803,12 +805,17 @@ + + + + + - + @@ -817,13 +824,8 @@ - + - - - - -