diff --git a/cocos/audio/AudioEngine.cpp b/cocos/audio/AudioEngine.cpp index f4f7d98767a0..46f3483c0cfc 100644 --- a/cocos/audio/AudioEngine.cpp +++ b/cocos/audio/AudioEngine.cpp @@ -1,7 +1,8 @@ /**************************************************************************** Copyright (c) 2014-2016 Chukong Technologies Inc. Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - + Copyright (c) 2018 HALX99. + http://www.cocos2d-x.org Permission is hereby granted, free of charge, to any person obtaining a copy @@ -32,7 +33,7 @@ #include "base/ccUtils.h" #if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID -#include "audio/android/AudioEngine-inl.h" +#include "audio/android/AudioEngineImpl.h" #elif CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC #include "audio/apple/AudioEngine-inl.h" #elif CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 diff --git a/cocos/audio/CMakeLists.txt b/cocos/audio/CMakeLists.txt index ae58b430cf6d..c3d763ac33d4 100644 --- a/cocos/audio/CMakeLists.txt +++ b/cocos/audio/CMakeLists.txt @@ -33,80 +33,31 @@ if(WINDOWS) elseif(ANDROID) set(COCOS_AUDIO_PLATFORM_HEADER - audio/android/PcmAudioService.h - audio/android/AudioBufferProvider.h - audio/android/IAudioPlayer.h - audio/android/AudioResampler.h - audio/android/AudioDecoder.h - audio/android/AudioResamplerPublic.h - audio/android/AudioMixer.h - audio/android/tinysndfile.h - audio/android/mp3reader.h - audio/android/AudioMixerOps.h - audio/android/cutils/bitops.h - audio/android/cutils/log.h - audio/android/audio.h - audio/android/AudioPlayerProvider.h - audio/android/utils/Utils.h - audio/android/utils/Errors.h - audio/android/utils/Compat.h audio/android/ccdandroidUtils.h - audio/android/AudioDecoderOgg.h - audio/android/Track.h - audio/android/OpenSLHelper.h - audio/android/PcmAudioPlayer.h - audio/android/AssetFd.h - audio/android/PcmBufferProvider.h - audio/android/CCThreadPool.h - audio/android/audio_utils/include/audio_utils/minifloat.h - audio/android/audio_utils/include/audio_utils/primitives.h - audio/android/audio_utils/include/audio_utils/format.h - audio/android/audio_utils/private/private.h - audio/android/ICallerThreadUtils.h - audio/android/AudioDecoderWav.h - audio/android/AudioDecoderProvider.h - audio/android/UrlAudioPlayer.h - audio/android/AudioDecoderSLES.h - audio/android/AudioDecoderMp3.h - audio/android/PcmData.h audio/android/jni/cddandroidAndroidJavaEngine.h - audio/android/AudioMixerController.h - audio/android/AudioResamplerCubic.h - audio/android/AudioEngine-inl.h - audio/android/IVolumeProvider.h - + audio/android/utils/Utils.h + audio/android/AudioEngineImpl.h + audio/android/AudioCache.h + audio/android/AudioDecoder.h + audio/android/AudioDecoderManager.h + audio/android/AudioDecoderMp3.h + audio/android/AudioDecoderOgg.h + audio/android/AudioPlayer.h + audio/android/log.h ) set(COCOS_AUDIO_PLATFORM_SRC audio/android/cddSimpleAudioEngine.cpp audio/android/ccdandroidUtils.cpp audio/android/jni/cddandroidAndroidJavaEngine.cpp - audio/android/AudioEngine-inl.cpp - audio/android/CCThreadPool.cpp - audio/android/AssetFd.cpp + audio/android/utils/Utils.cpp + audio/android/AudioEngineImpl.cpp + audio/android/AudioCache.cpp audio/android/AudioDecoder.cpp - audio/android/AudioDecoderProvider.cpp - audio/android/AudioDecoderSLES.cpp - audio/android/AudioDecoderOgg.cpp + audio/android/AudioDecoderManager.cpp audio/android/AudioDecoderMp3.cpp - audio/android/AudioDecoderWav.cpp - audio/android/AudioPlayerProvider.cpp - audio/android/AudioResampler.cpp - audio/android/AudioResamplerCubic.cpp - audio/android/PcmBufferProvider.cpp - audio/android/PcmAudioPlayer.cpp - audio/android/UrlAudioPlayer.cpp - audio/android/PcmData.cpp - audio/android/AudioMixerController.cpp - audio/android/AudioMixer.cpp - audio/android/PcmAudioService.cpp - audio/android/Track.cpp - audio/android/audio_utils/format.c - audio/android/audio_utils/minifloat.cpp - audio/android/audio_utils/primitives.c - audio/android/utils/Utils.cpp - audio/android/mp3reader.cpp - audio/android/tinysndfile.cpp + audio/android/AudioDecoderOgg.cpp + audio/android/AudioPlayer.cpp ) elseif(LINUX) diff --git a/cocos/audio/android/Android.mk b/cocos/audio/android/Android.mk index f071924899d9..9e0f3446ae5c 100644 --- a/cocos/audio/android/Android.mk +++ b/cocos/audio/android/Android.mk @@ -7,33 +7,15 @@ LOCAL_MODULE := audioengine_static LOCAL_MODULE_FILENAME := libaudioengine -LOCAL_SRC_FILES := AudioEngine-inl.cpp \ +LOCAL_SRC_FILES := AudioEngineImpl.cpp \ ../AudioEngine.cpp \ - CCThreadPool.cpp \ - AssetFd.cpp \ AudioDecoder.cpp \ - AudioDecoderProvider.cpp \ - AudioDecoderSLES.cpp \ AudioDecoderOgg.cpp \ AudioDecoderMp3.cpp \ - AudioDecoderWav.cpp \ - AudioPlayerProvider.cpp \ - AudioResampler.cpp \ - AudioResamplerCubic.cpp \ - PcmBufferProvider.cpp \ - PcmAudioPlayer.cpp \ - UrlAudioPlayer.cpp \ - PcmData.cpp \ - AudioMixerController.cpp \ - AudioMixer.cpp \ - PcmAudioService.cpp \ - Track.cpp \ - audio_utils/format.c \ - audio_utils/minifloat.cpp \ - audio_utils/primitives.c \ - utils/Utils.cpp \ - mp3reader.cpp \ - tinysndfile.cpp + AudioCache.cpp \ + AudioDecoderManager.cpp \ + AudioPlayer.cpp \ + utils/Utils.cpp LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../include @@ -43,12 +25,14 @@ LOCAL_EXPORT_LDLIBS := -lOpenSLES LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include \ $(LOCAL_PATH)/../.. \ $(LOCAL_PATH)/../../platform/android \ - $(LOCAL_PATH)/../../../external/android-specific \ - $(LOCAL_PATH)/../../../external/android-specific/tremolo - -LOCAL_STATIC_LIBRARIES += libvorbisidec libpvmp3dec + $(LOCAL_PATH)/../../../external/android-specific/OggDecoder/include \ + $(LOCAL_PATH)/../../../external/android-specific/MP3Decoder/include \ + $(LOCAL_PATH)/../../../external/android-specific/OpenalSoft/include + +LOCAL_SHARED_LIBRARIES += mpg123_shared openal_shared +LOCAL_WHOLE_STATIC_LIBRARIES += cocos_ogg_static cocos_vorbis_static cocos_vorbisfile_static include $(BUILD_STATIC_LIBRARY) - + #SimpleAudioEngine include $(CLEAR_VARS) @@ -69,5 +53,6 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include \ include $(BUILD_STATIC_LIBRARY) -$(call import-module,android-specific/tremolo) -$(call import-module,android-specific/pvmp3dec) +$(call import-module,android-specific/OggDecoder/prebuilt) +$(call import-module,android-specific/MP3Decoder/prebuilt) +$(call import-module,android-specific/OpenalSoft/prebuilt) diff --git a/cocos/audio/android/AudioBufferProvider.h b/cocos/audio/android/AudioBufferProvider.h deleted file mode 100644 index cf9a3c0974c1..000000000000 --- a/cocos/audio/android/AudioBufferProvider.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include "audio/android/utils/Errors.h" - -namespace cocos2d { namespace experimental { -// ---------------------------------------------------------------------------- - -class AudioBufferProvider -{ -public: - - // FIXME merge with AudioTrackShared::Buffer, AudioTrack::Buffer, and AudioRecord::Buffer - // and rename getNextBuffer() to obtainBuffer() - struct Buffer { - Buffer() : raw(NULL), frameCount(0) { } - union { - void* raw; - short* i16; - int8_t* i8; - }; - size_t frameCount; - }; - - virtual ~AudioBufferProvider() {} - - // value representing an invalid presentation timestamp - static const int64_t kInvalidPTS = 0x7FFFFFFFFFFFFFFFLL; // is too painful - - // pts is the local time when the next sample yielded by getNextBuffer - // will be rendered. - // Pass kInvalidPTS if the PTS is unknown or not applicable. - // On entry: - // buffer != NULL - // buffer->raw unused - // buffer->frameCount maximum number of desired frames - // On successful return: - // status NO_ERROR - // buffer->raw non-NULL pointer to buffer->frameCount contiguous available frames - // buffer->frameCount number of contiguous available frames at buffer->raw, - // 0 < buffer->frameCount <= entry value - // On error return: - // status != NO_ERROR - // buffer->raw NULL - // buffer->frameCount 0 - virtual status_t getNextBuffer(Buffer* buffer, int64_t pts = kInvalidPTS) = 0; - - // Release (a portion of) the buffer previously obtained by getNextBuffer(). - // It is permissible to call releaseBuffer() multiple times per getNextBuffer(). - // On entry: - // buffer->frameCount number of frames to release, must be <= number of frames - // obtained but not yet released - // buffer->raw unused - // On return: - // buffer->frameCount 0; implementation MUST set to zero - // buffer->raw undefined; implementation is PERMITTED to set to any value, - // so if caller needs to continue using this buffer it must - // keep track of the pointer itself - virtual void releaseBuffer(Buffer* buffer) = 0; -}; - -// ---------------------------------------------------------------------------- -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioCache.cpp b/cocos/audio/android/AudioCache.cpp new file mode 100644 index 000000000000..de857f95ae9d --- /dev/null +++ b/cocos/audio/android/AudioCache.cpp @@ -0,0 +1,383 @@ +/**************************************************************************** + Copyright (c) 2014-2017 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + Copyright (c) 2018 HALX99. + + 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. + ****************************************************************************/ + +#define LOG_TAG "AudioCache" + +#include "platform/CCPlatformConfig.h" + +#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID + +#include "audio/android/AudioCache.h" +#include +#include "base/CCDirector.h" +#include "base/CCScheduler.h" +#include +#include +#include "audio/android/AudioDecoderManager.h" +#include "audio/android/AudioDecoder.h" +#include "audio/android/log.h" + +#define VERY_VERY_VERBOSE_LOGGING +#ifdef VERY_VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +#else +#define ALOGVV(...) do{} while(false) +#endif + +namespace { +unsigned int __idIndex = 0; +} + +#define INVALID_AL_BUFFER_ID 0xFFFFFFFF +#define PCMDATA_CACHEMAXSIZE 1048576 + +using namespace cocos2d; +using namespace cocos2d::experimental; + +AudioCache::AudioCache() +: _totalFrames(0) +, _framesRead(0) +, _format(-1) +, _duration(0.0f) +, _alBufferId(INVALID_AL_BUFFER_ID) +, _pcmData(nullptr) +, _queBufferFrames(0) +, _state(State::INITIAL) +, _isDestroyed(std::make_shared(false)) +, _id(++__idIndex) +, _isLoadingFinished(false) +, _isSkipReadDataTask(false) +{ + ALOGVV("AudioCache() %p, id=%u", this, _id); + for (int i = 0; i < QUEUEBUFFER_NUM; ++i) + { + _queBuffers[i] = nullptr; + _queBufferSize[i] = 0; + } +} + +AudioCache::~AudioCache() +{ + ALOGVV("~AudioCache() %p, id=%u, begin", this, _id); + *_isDestroyed = true; + while (!_isLoadingFinished) + { + if (_isSkipReadDataTask) + { + ALOGV("id=%u, Skip read data task, don't continue to wait!", _id); + break; + } + ALOGVV("id=%u, waiting readData thread to finish ...", _id); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + //wait for the 'readDataTask' task to exit + _readDataTaskMutex.lock(); + _readDataTaskMutex.unlock(); + + if (_pcmData) + { + if (_state == State::READY) + { + if (_alBufferId != INVALID_AL_BUFFER_ID && alIsBuffer(_alBufferId)) + { + ALOGV("~AudioCache(id=%u), delete buffer: %u", _id, _alBufferId); + alDeleteBuffers(1, &_alBufferId); + _alBufferId = INVALID_AL_BUFFER_ID; + } + } + else + { + ALOGW("AudioCache (%p), id=%u, buffer isn't ready, state=%d", this, _id, (int)_state); + } + + free(_pcmData); + } + + if (_queBufferFrames > 0) + { + for (int index = 0; index < QUEUEBUFFER_NUM; ++index) + { + free(_queBuffers[index]); + } + } + ALOGVV("~AudioCache() %p, id=%u, end", this, _id); +} + +void AudioCache::readDataTask(unsigned int selfId) +{ + //Note: It's in sub thread + ALOGVV("readDataTask begin, cache id=%u", selfId); + + _readDataTaskMutex.lock(); + _state = State::LOADING; + + AudioDecoder* decoder = AudioDecoderManager::createDecoder(_fileFullPath.c_str()); + do + { + if (decoder == nullptr || !decoder->open(_fileFullPath.c_str())) + break; + + const uint32_t originalTotalFrames = decoder->getTotalFrames(); + const uint32_t bytesPerFrame = decoder->getBytesPerFrame(); + const uint32_t sampleRate = decoder->getSampleRate(); + const uint32_t channelCount = decoder->getChannelCount(); + + uint32_t totalFrames = originalTotalFrames; + uint32_t dataSize = totalFrames * bytesPerFrame; + uint32_t remainingFrames = totalFrames; + uint32_t adjustFrames = 0; + + _format = channelCount > 1 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16; + _sampleRate = (ALsizei)sampleRate; + _duration = 1.0f * totalFrames / sampleRate; + _totalFrames = totalFrames; + + if (dataSize <= PCMDATA_CACHEMAXSIZE) + { + uint32_t framesRead = 0; + const uint32_t framesToReadOnce = (std::min)(totalFrames, static_cast(sampleRate * QUEUEBUFFER_TIME_STEP * QUEUEBUFFER_NUM)); + + std::vector adjustFrameBuf; + + if (decoder->seek(totalFrames)) + { + char* tmpBuf = (char*)malloc(framesToReadOnce * bytesPerFrame); + adjustFrameBuf.reserve(framesToReadOnce * bytesPerFrame); + + // Adjust total frames by setting position to the end of frames and try to read more data. + // This is a workaround for https://github.com/cocos2d/cocos2d-x/issues/16938 + do + { + framesRead = decoder->read(framesToReadOnce, tmpBuf); + if (framesRead > 0) + { + adjustFrames += framesRead; + adjustFrameBuf.insert(adjustFrameBuf.end(), tmpBuf, tmpBuf + framesRead * bytesPerFrame); + } + + } while (framesRead > 0); + + if (adjustFrames > 0) + { + ALOGV("Orignal total frames: %u, adjust frames: %u, current total frames: %u", totalFrames, adjustFrames, totalFrames + adjustFrames); + totalFrames += adjustFrames; + _totalFrames = remainingFrames = totalFrames; + } + + // Reset dataSize + dataSize = totalFrames * bytesPerFrame; + + free(tmpBuf); + } + // Reset to frame 0 + BREAK_IF_ERR_LOG(!decoder->seek(0), "AudioDecoder::seek(0) failed!"); + + _pcmData = (char*)malloc(dataSize); + memset(_pcmData, 0x00, dataSize); + + if (adjustFrames > 0) + { + memcpy(_pcmData + (dataSize - adjustFrameBuf.size()), adjustFrameBuf.data(), adjustFrameBuf.size()); + } + + alGenBuffers(1, &_alBufferId); + auto alError = alGetError(); + if (alError != AL_NO_ERROR) { + ALOGE("%s: attaching audio to buffer fail: %x", __FUNCTION__, alError); + break; + } + + if (*_isDestroyed) + break; + + framesRead = decoder->readFixedFrames((std::min)(framesToReadOnce, remainingFrames), _pcmData + _framesRead * bytesPerFrame); + _framesRead += framesRead; + remainingFrames -= framesRead; + + if (*_isDestroyed) + break; + + uint32_t frames = 0; + while (!*_isDestroyed && _framesRead < originalTotalFrames) + { + frames = (std::min)(framesToReadOnce, remainingFrames); + if (_framesRead + frames > originalTotalFrames) + { + frames = originalTotalFrames - _framesRead; + } + framesRead = decoder->read(frames, _pcmData + _framesRead * bytesPerFrame); + if (framesRead == 0) + break; + _framesRead += framesRead; + remainingFrames -= framesRead; + } + + if (*_isDestroyed) + break; + + if (_framesRead < originalTotalFrames) + { + memset(_pcmData + _framesRead * bytesPerFrame, 0x00, (totalFrames - _framesRead) * bytesPerFrame); + } + ALOGV("pcm buffer was loaded successfully, total frames: %u, total read frames: %u, adjust frames: %u, remainingFrames: %u", totalFrames, _framesRead, adjustFrames, remainingFrames); + + _framesRead += adjustFrames; + + alBufferData(_alBufferId, _format, _pcmData, (ALsizei)dataSize, (ALsizei)sampleRate); + + _state = State::READY; + } + else + { + _queBufferFrames = sampleRate * QUEUEBUFFER_TIME_STEP; + BREAK_IF_ERR_LOG(_queBufferFrames == 0, "_queBufferFrames == 0"); + + const uint32_t queBufferBytes = _queBufferFrames * bytesPerFrame; + + for (int index = 0; index < QUEUEBUFFER_NUM; ++index) + { + _queBuffers[index] = (char*)malloc(queBufferBytes); + _queBufferSize[index] = queBufferBytes; + + decoder->readFixedFrames(_queBufferFrames, _queBuffers[index]); + } + + _state = State::READY; + } + + } while (false); + + if (decoder != nullptr) + { + decoder->close(); + } + + AudioDecoderManager::destroyDecoder(decoder); + + if (_state != State::READY) + { + _state = State::FAILED; + if (_alBufferId != INVALID_AL_BUFFER_ID && alIsBuffer(_alBufferId)) + { + ALOGV("readDataTask failed, delete buffer: %u", _alBufferId); + alDeleteBuffers(1, &_alBufferId); + _alBufferId = INVALID_AL_BUFFER_ID; + } + } + + //FIXME: Why to invoke play callback first? Should it be after 'load' callback? + invokingPlayCallbacks(); + invokingLoadCallbacks(); + + _isLoadingFinished = true; + _readDataTaskMutex.unlock(); + ALOGVV("readDataTask end, cache id=%u", selfId); +} + +void AudioCache::addPlayCallback(const std::function& callback) +{ + std::lock_guard lk(_playCallbackMutex); + switch (_state) + { + case State::INITIAL: + case State::LOADING: + _playCallbacks.push_back(callback); + break; + + case State::READY: + // If state is failure, we still need to invoke the callback + // since the callback will set the 'AudioPlayer::_removeByAudioEngine' flag to true. + case State::FAILED: + callback(); + break; + + default: + ALOGE("Invalid state: %d", (int)_state); + break; + } +} + +void AudioCache::invokingPlayCallbacks() +{ + std::lock_guard lk(_playCallbackMutex); + + for (auto&& cb : _playCallbacks) + { + cb(); + } + + _playCallbacks.clear(); +} + +void AudioCache::addLoadCallback(const std::function& callback) +{ + switch (_state) + { + case State::INITIAL: + case State::LOADING: + _loadCallbacks.push_back(callback); + break; + + case State::READY: + callback(true); + break; + case State::FAILED: + callback(false); + break; + + default: + ALOGE("Invalid state: %d", (int)_state); + break; + } +} + +void AudioCache::invokingLoadCallbacks() +{ + if (*_isDestroyed) + { + ALOGV("AudioCache (%p) was destroyed, don't invoke preload callback ...", this); + return; + } + + auto isDestroyed = _isDestroyed; + auto scheduler = Director::getInstance()->getScheduler(); + scheduler->performFunctionInCocosThread([&, isDestroyed](){ + if (*isDestroyed) + { + ALOGV("invokingLoadCallbacks perform in cocos thread, AudioCache (%p) was destroyed!", this); + return; + } + + for (auto&& cb : _loadCallbacks) + { + cb(_state == State::READY); + } + + _loadCallbacks.clear(); + }); +} + +#endif diff --git a/cocos/audio/android/AudioCache.h b/cocos/audio/android/AudioCache.h new file mode 100644 index 000000000000..6901541b98d5 --- /dev/null +++ b/cocos/audio/android/AudioCache.h @@ -0,0 +1,121 @@ +/**************************************************************************** + Copyright (c) 2014-2017 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + Copyright (c) 2018 HALX99. + + 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. + ****************************************************************************/ + +#pragma once + +#include "platform/CCPlatformConfig.h" + +#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID + +#include +#include +#include +#include +#ifdef OPENAL_PLAIN_INCLUDES +#include +#else +#include +#endif +#include "platform/CCPlatformMacros.h" +#include "audio/android/AudioMacros.h" + +NS_CC_BEGIN +namespace experimental{ + +class AudioEngineImpl; +class AudioPlayer; + +class CC_DLL AudioCache +{ +public: + + enum class State + { + INITIAL, + LOADING, + READY, + FAILED + }; + + AudioCache(); + ~AudioCache(); + + void addPlayCallback(const std::function& callback); + + void addLoadCallback(const std::function& callback); + +protected: + void setSkipReadDataTask(bool isSkip) { _isSkipReadDataTask = isSkip; }; + void readDataTask(unsigned int selfId); + + void invokingPlayCallbacks(); + + void invokingLoadCallbacks(); + + //pcm data related stuff + ALenum _format; + ALsizei _sampleRate; + float _duration; + uint32_t _totalFrames; + uint32_t _framesRead; + + /*Cache related stuff; + * Cache pcm data when sizeInBytes less than PCMDATA_CACHEMAXSIZE + */ + ALuint _alBufferId; + char* _pcmData; + + /*Queue buffer related stuff + * Streaming in OpenAL when sizeInBytes greater then PCMDATA_CACHEMAXSIZE + */ + char* _queBuffers[QUEUEBUFFER_NUM]; + ALsizei _queBufferSize[QUEUEBUFFER_NUM]; + uint32_t _queBufferFrames; + + std::mutex _playCallbackMutex; + std::vector< std::function > _playCallbacks; + + // loadCallbacks doesn't need mutex since it's invoked only in Cocos thread. + std::vector< std::function > _loadCallbacks; + + std::mutex _readDataTaskMutex; + + State _state; + + std::shared_ptr _isDestroyed; + std::string _fileFullPath; + unsigned int _id; + bool _isLoadingFinished; + bool _isSkipReadDataTask; + + friend class AudioEngineImpl; + friend class AudioPlayer; +}; + +} +NS_CC_END + +#endif diff --git a/cocos/audio/android/AudioDecoder.cpp b/cocos/audio/android/AudioDecoder.cpp index 5f14e83fd5c0..371c4b193d4f 100644 --- a/cocos/audio/android/AudioDecoder.cpp +++ b/cocos/audio/android/AudioDecoder.cpp @@ -1,294 +1,94 @@ /**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#define LOG_TAG "AudioDecoder" + Copyright (c) 2016-2017 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + Copyright (c) 2018 HALX99. + + 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 "audio/android/AudioDecoder.h" -#include "audio/android/AudioResampler.h" -#include "audio/android/PcmBufferProvider.h" -#include "audio/android/AudioResampler.h" +#include "audio/android/AudioMacros.h" +#include "platform/CCFileUtils.h" -#include -#include -#include +#define LOG_TAG "AudioDecoder" +#include "audio/android/log.h" namespace cocos2d { namespace experimental { -size_t AudioDecoder::fileRead(void* ptr, size_t size, size_t nmemb, void* datasource) -{ - AudioDecoder* thiz = (AudioDecoder*)datasource; - ssize_t toReadBytes = std::min((ssize_t)(thiz->_fileData.getSize() - thiz->_fileCurrPos), (ssize_t)(nmemb * size)); - if (toReadBytes > 0) - { - memcpy(ptr, (unsigned char*) thiz->_fileData.getBytes() + thiz->_fileCurrPos, toReadBytes); - thiz->_fileCurrPos += toReadBytes; - } - // ALOGD("File size: %d, After fileRead _fileCurrPos %d", (int)thiz->_fileData.getSize(), thiz->_fileCurrPos); - return toReadBytes; -} - -int AudioDecoder::fileSeek(void* datasource, int64_t offset, int whence) -{ - AudioDecoder* thiz = (AudioDecoder*)datasource; - if (whence == SEEK_SET) - thiz->_fileCurrPos = offset; - else if (whence == SEEK_CUR) - thiz->_fileCurrPos = thiz->_fileCurrPos + offset; - else if (whence == SEEK_END) - thiz->_fileCurrPos = thiz->_fileData.getSize(); - return 0; -} - -int AudioDecoder::fileClose(void* datasource) -{ - return 0; -} - -long AudioDecoder::fileTell(void* datasource) -{ - AudioDecoder* thiz = (AudioDecoder*)datasource; - return (long) thiz->_fileCurrPos; -} - AudioDecoder::AudioDecoder() - : _fileCurrPos(0), _sampleRate(-1) -{ - auto pcmBuffer = std::make_shared>(); - pcmBuffer->reserve(4096); - _result.pcmBuffer = pcmBuffer; -} - -AudioDecoder::~AudioDecoder() -{ - ALOGV("~AudioDecoder() %p", this); -} - -bool AudioDecoder::init(const std::string &url, int sampleRate) -{ - _url = url; - _sampleRate = sampleRate; - return true; -} - -bool AudioDecoder::start() -{ - auto oldTime = clockNow(); - auto nowTime = oldTime; - bool ret; - do + : _isOpened(false) + , _totalFrames(0) + , _bytesPerFrame(0) + , _sampleRate(0) + , _channelCount(0) { - ret = decodeToPcm(); - if (!ret) - { - ALOGE("decodeToPcm (%s) failed!", _url.c_str()); - break; - } - - nowTime = clockNow(); - ALOGD("Decoding (%s) to pcm data wasted %fms", _url.c_str(), intervalInMS(oldTime, nowTime)); - oldTime = nowTime; - - ret = resample(); - if (!ret) - { - ALOGE("resample (%s) failed!", _url.c_str()); - break; - } - - nowTime = clockNow(); - ALOGD("Resampling (%s) wasted %fms", _url.c_str(), intervalInMS(oldTime, nowTime)); - oldTime = nowTime; - ret = interleave(); - if (!ret) - { - ALOGE("interleave (%s) failed!", _url.c_str()); - break; - } - - nowTime = clockNow(); - ALOGD("Interleave (%s) wasted %fms", _url.c_str(), intervalInMS(oldTime, nowTime)); - - } while(false); - - ALOGV_IF(!ret, "%s returns false, decode (%s)", __FUNCTION__, _url.c_str()); - return ret; -} + } -bool AudioDecoder::resample() -{ - if (_result.sampleRate == _sampleRate) + AudioDecoder::~AudioDecoder() { - ALOGI("No need to resample since the sample rate (%d) of the decoded pcm data is the same as the device output sample rate", - _sampleRate); - return true; } - ALOGV("Resample: %d --> %d", _result.sampleRate, _sampleRate); - - auto r = _result; - PcmBufferProvider provider; - provider.init(r.pcmBuffer->data(), r.numFrames, r.pcmBuffer->size() / r.numFrames); - - const int outFrameRate = _sampleRate; - int outputChannels = 2; - size_t outputFrameSize = outputChannels * sizeof(int32_t); - size_t outputFrames = ((int64_t) r.numFrames * outFrameRate) / r.sampleRate; - size_t outputSize = outputFrames * outputFrameSize; - void *outputVAddr = malloc(outputSize); - auto resampler = AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT, r.numChannels, outFrameRate, - AudioResampler::MED_QUALITY); - resampler->setSampleRate(r.sampleRate); - resampler->setVolume(AudioResampler::UNITY_GAIN_FLOAT, AudioResampler::UNITY_GAIN_FLOAT); - - memset(outputVAddr, 0, outputSize); - - ALOGV("resample() %zu output frames", outputFrames); - - std::vector Ovalues; - - if (Ovalues.empty()) + bool AudioDecoder::isOpened() const { - Ovalues.push_back(outputFrames); + return _isOpened; } - for (size_t i = 0, j = 0; i < outputFrames;) + + uint32_t AudioDecoder::readFixedFrames(uint32_t framesToRead, char* pcmBuf) { - size_t thisFrames = Ovalues[j++]; - if (j >= Ovalues.size()) + uint32_t framesRead = 0; + uint32_t framesReadOnce = 0; + do { - j = 0; - } - if (thisFrames == 0 || thisFrames > outputFrames - i) + framesReadOnce = read(framesToRead - framesRead, pcmBuf + framesRead * _bytesPerFrame); + framesRead += framesReadOnce; + } while (framesReadOnce != 0 && framesRead < framesToRead); + + if (framesRead < framesToRead) { - thisFrames = outputFrames - i; + memset(pcmBuf + framesRead * _bytesPerFrame, 0x00, (framesToRead - framesRead) * _bytesPerFrame); } - int outFrames = resampler->resample((int *) outputVAddr + outputChannels * i, thisFrames, - &provider); - ALOGV("outFrames: %d", outFrames); - i += thisFrames; - } - - ALOGV("resample() complete"); - - resampler->reset(); - - ALOGV("reset() complete"); - - delete resampler; - resampler = nullptr; - - // mono takes left channel only (out of stereo output pair) - // stereo and multichannel preserve all channels. - - int channels = r.numChannels; - int32_t *out = (int32_t *) outputVAddr; - int16_t *convert = (int16_t *) malloc(outputFrames * channels * sizeof(int16_t)); - const int volumeShift = 12; // shift requirement for Q4.27 to Q.15 - // round to half towards zero and saturate at int16 (non-dithered) - const int roundVal = (1 << (volumeShift - 1)) - 1; // volumePrecision > 0 + return framesRead; + } - for (size_t i = 0; i < outputFrames; i++) + uint32_t AudioDecoder::getTotalFrames() const { - for (int j = 0; j < channels; j++) - { - int32_t s = out[i * outputChannels + j] + roundVal; // add offset here - if (s < 0) - { - s = (s + 1) >> volumeShift; // round to 0 - if (s < -32768) - { - s = -32768; - } - } else - { - s = s >> volumeShift; - if (s > 32767) - { - s = 32767; - } - } - convert[i * channels + j] = int16_t(s); - } + return _totalFrames; } - // Reset result - _result.numFrames = outputFrames; - _result.sampleRate = outFrameRate; - - auto buffer = std::make_shared>(); - buffer->reserve(_result.numFrames * _result.bitsPerSample / 8); - buffer->insert(buffer->end(), (char *) convert, - (char *) convert + outputFrames * channels * sizeof(int16_t)); - _result.pcmBuffer = buffer; - - ALOGV("pcm buffer size: %d", (int)_result.pcmBuffer->size()); - - free(convert); - free(outputVAddr); - return true; -} - -//----------------------------------------------------------------- -bool AudioDecoder::interleave() -{ - if (_result.numChannels == 2) + uint32_t AudioDecoder::getBytesPerFrame() const { - ALOGI("Audio channel count is 2, no need to interleave"); - return true; + return _bytesPerFrame; } - else if (_result.numChannels == 1) - { - // If it's a mono audio, try to compose a fake stereo buffer - size_t newBufferSize = _result.pcmBuffer->size() * 2; - auto newBuffer = std::make_shared>(); - newBuffer->reserve(newBufferSize); - size_t totalFrameSizeInBytes = (size_t) (_result.numFrames * _result.bitsPerSample / 8); - - for (size_t i = 0; i < totalFrameSizeInBytes; i += 2) - { - // get one short value - char byte1 = _result.pcmBuffer->at(i); - char byte2 = _result.pcmBuffer->at(i + 1); - // push two short value - for (int j = 0; j < 2; ++j) - { - newBuffer->push_back(byte1); - newBuffer->push_back(byte2); - } - } - _result.numChannels = 2; - _result.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - _result.pcmBuffer = newBuffer; - return true; + uint32_t AudioDecoder::getSampleRate() const + { + return _sampleRate; } - ALOGE("Audio channel count (%d) is wrong, interleave only supports converting mono to stereo!", _result.numChannels); - return false; -} + uint32_t AudioDecoder::getChannelCount() const + { + return _channelCount; + } }} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioDecoder.h b/cocos/audio/android/AudioDecoder.h index 46ad515b1f0a..4041c384daf2 100644 --- a/cocos/audio/android/AudioDecoder.h +++ b/cocos/audio/android/AudioDecoder.h @@ -1,64 +1,121 @@ /**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ + Copyright (c) 2016-2017 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + Copyright (c) 2018 HALX99. + + 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. + ****************************************************************************/ #pragma once -#include "audio/android/OpenSLHelper.h" -#include "audio/android/PcmData.h" -#include "base/CCData.h" +#include + +// #include "vorbis/vorbisfile.h" namespace cocos2d { namespace experimental { +/** + * @brief The class for decoding compressed audio file to PCM buffer. + */ class AudioDecoder { public: - AudioDecoder(); - virtual ~AudioDecoder(); + static const uint32_t INVALID_FRAME_INDEX = UINT32_MAX; + + /** + * @brief Opens an audio file specified by a file path. + * @return true if succeed, otherwise false. + */ + virtual bool open(const char* path) = 0; + + /** + * @brief Checks whether decoder has opened file successfully. + * @return true if succeed, otherwise false. + */ + virtual bool isOpened() const; + + /** + * @brief Closes opened audio file. + * @note The method will also be automatically invoked in the destructor. + */ + virtual void close() = 0; - virtual bool init(const std::string &url, int sampleRate); + /** + * @brief Reads audio frames of PCM format. + * @param framesToRead The number of frames excepted to be read. + * @param pcmBuf The buffer to hold the frames to be read, its size should be >= |framesToRead| * _bytesPerFrame. + * @return The number of frames actually read, it's probably less than 'framesToRead'. Returns 0 means reach the end of file. + */ + virtual uint32_t read(uint32_t framesToRead, char* pcmBuf) = 0; - bool start(); + /** + * @brief Reads fixed audio frames of PCM format. + * @param framesToRead The number of frames excepted to be read. + * @param pcmBuf The buffer to hold the frames to be read, its size should be >= |framesToRead| * _bytesPerFrame. + * @return The number of frames actually read, it's probably less than |framesToRead|. Returns 0 means reach the end of file. + * @note The different between |read| and |readFixedFrames| is |readFixedFrames| will do multiple reading operations if |framesToRead| frames + * isn't filled entirely, while |read| just does reading operation once whatever |framesToRead| is or isn't filled entirely. + * If current position reaches the end of frames, the return value may smaller than |framesToRead| and the remaining + * buffer in |pcmBuf| will be set with silence data (0x00). + */ + virtual uint32_t readFixedFrames(uint32_t framesToRead, char* pcmBuf); - inline PcmData getResult() - { return _result; }; + /** + * @brief Sets frame offest to be read. + * @param frameOffset The frame offest to be set. + * @return true if succeed, otherwise false + */ + virtual bool seek(uint32_t frameOffset) = 0; + + /** + * @brief Tells the current frame offset. + * @return The current frame offset. + */ + virtual uint32_t tell() const = 0; + + /** Gets total frames of current audio.*/ + virtual uint32_t getTotalFrames() const; + + /** Gets bytes per frame of current audio.*/ + virtual uint32_t getBytesPerFrame() const; + + /** Gets sample rate of current audio.*/ + virtual uint32_t getSampleRate() const; + + /** Gets the channel count of current audio. + * @note Currently we only support 1 or 2 channels. + */ + virtual uint32_t getChannelCount() const; protected: - virtual bool decodeToPcm() = 0; - bool resample(); - bool interleave(); - - static size_t fileRead(void* ptr, size_t size, size_t nmemb, void* datasource); - static int fileSeek(void* datasource, int64_t offset, int whence); - static int fileClose(void* datasource); - static long fileTell(void* datasource); - - std::string _url; - PcmData _result; - int _sampleRate; - Data _fileData; - size_t _fileCurrPos; + AudioDecoder(); + virtual ~AudioDecoder(); + + bool _isOpened; + uint32_t _totalFrames; + uint32_t _bytesPerFrame; + uint32_t _sampleRate; + uint32_t _channelCount; + + friend class AudioDecoderManager; }; }} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AssetFd.cpp b/cocos/audio/android/AudioDecoderManager.cpp similarity index 58% rename from cocos/audio/android/AssetFd.cpp rename to cocos/audio/android/AudioDecoderManager.cpp index fc8303142f73..94887a4d3076 100644 --- a/cocos/audio/android/AssetFd.cpp +++ b/cocos/audio/android/AudioDecoderManager.cpp @@ -1,6 +1,7 @@ /**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. +Copyright (c) 2016-2017 Chukong Technologies Inc. Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. +Copyright (c) 2018 HALX99. http://www.cocos2d-x.org @@ -23,26 +24,50 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ****************************************************************************/ -#define LOG_TAG "AssetFd" -#include "audio/android/cutils/log.h" -#include "audio/android/AssetFd.h" +#include "audio/android/AudioDecoderManager.h" +#include "audio/android/AudioDecoderOgg.h" +#include "audio/android/AudioDecoderMp3.h" +#include "audio/android/AudioMacros.h" +#include "platform/CCFileUtils.h" +#include "mpg123.h" + +#define LOG_TAG "AudioDecoderManager" +#include "audio/android/log.h" namespace cocos2d { namespace experimental { -AssetFd::AssetFd(int assetFd) - : _assetFd(assetFd) +static bool __mp3Inited = false; + +bool AudioDecoderManager::init() +{ + return true; +} + +void AudioDecoderManager::destroy() { + AudioDecoderMp3::destroy(); } -AssetFd::~AssetFd() +AudioDecoder* AudioDecoderManager::createDecoder(const char* path) { - ALOGV("~AssetFd: %d", _assetFd); - if (_assetFd > 0) + std::string suffix = FileUtils::getInstance()->getFileExtension(path); + if (suffix == ".ogg") + { + return new (std::nothrow) AudioDecoderOgg(); + } + else if (suffix == ".mp3") { - ::close(_assetFd); - _assetFd = 0; + return new (std::nothrow) AudioDecoderMp3(); } -}; + + return nullptr; +} + +void AudioDecoderManager::destroyDecoder(AudioDecoder* decoder) +{ + delete decoder; +} }} // namespace cocos2d { namespace experimental { + diff --git a/cocos/audio/android/AssetFd.h b/cocos/audio/android/AudioDecoderManager.h similarity index 82% rename from cocos/audio/android/AssetFd.h rename to cocos/audio/android/AudioDecoderManager.h index aedb8c8ac2ad..073657148a6c 100644 --- a/cocos/audio/android/AssetFd.h +++ b/cocos/audio/android/AudioDecoderManager.h @@ -1,6 +1,7 @@ /**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. +Copyright (c) 2016-2017 Chukong Technologies Inc. Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. +Copyright (c) 2018 HALX99. http://www.cocos2d-x.org @@ -22,21 +23,20 @@ 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. ****************************************************************************/ -#pragma once -#include +#pragma once namespace cocos2d { namespace experimental { -class AssetFd +class AudioDecoder; + +class AudioDecoderManager { public: - AssetFd(int assetFd); - ~AssetFd(); - - inline int getFd() const { return _assetFd; }; -private: - int _assetFd; + static bool init(); + static void destroy(); + static AudioDecoder* createDecoder(const char* path); + static void destroyDecoder(AudioDecoder* decoder); }; }} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioDecoderMp3.cpp b/cocos/audio/android/AudioDecoderMp3.cpp index 136c562fa733..b25c69524613 100644 --- a/cocos/audio/android/AudioDecoderMp3.cpp +++ b/cocos/audio/android/AudioDecoderMp3.cpp @@ -1,83 +1,243 @@ /**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#define LOG_TAG "AudioDecoderMp3" + Copyright (c) 2016-2017 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + Copyright (c) 2018 HALX99. + + 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 "audio/android/AudioDecoderMp3.h" -#include "audio/android/mp3reader.h" +#include "audio/android/AudioMacros.h" #include "platform/CCFileUtils.h" +#include "mpg123.h" + +#include "platform/android/CCFileUtils-android.h" +#include +#include +#include +#include +#include + +#define LOG_TAG "AudioDecoderMp3" +#include "audio/android/log.h" namespace cocos2d { namespace experimental { -AudioDecoderMp3::AudioDecoderMp3() -{ - ALOGV("Create AudioDecoderMp3"); -} + static bool __mp3Inited = false; + + static AAsset* mpg123_open_r(const std::string& url) + { + AAssetManager* asMgr = FileUtilsAndroid::getAssetManager(); + AAsset* asset = AAssetManager_open(FileUtilsAndroid::getAssetManager(), url.c_str(), AASSET_MODE_UNKNOWN); + // open asset as file descriptor + return asset; + }; + + static ssize_t mpg123_read_r(void * handle, void * buffer, size_t count) + { + return AAsset_read((AAsset*)handle, buffer, count); + } + + static off_t mpg123_lseek_r(void * handle, off_t offset, int whence) + { + return AAsset_seek((AAsset*)handle, offset, whence); + } + + void mpg123_close_r(void* handle) { + AAsset_close((AAsset*)handle); + } + + bool AudioDecoderMp3::lazyInit() + { + bool ret = true; + if (!__mp3Inited) + { + int error = mpg123_init(); + if (error == MPG123_OK) + { + __mp3Inited = true; + } + else + { + ALOGE("Basic setup goes wrong: %s", mpg123_plain_strerror(error)); + ret = false; + } + } + return ret; + } + + void AudioDecoderMp3::destroy() + { + if (__mp3Inited) + { + mpg123_exit(); + __mp3Inited = false; + } + } -AudioDecoderMp3::~AudioDecoderMp3() -{ + AudioDecoderMp3::AudioDecoderMp3() + : _mpg123handle(nullptr) + { + lazyInit(); + } -} + AudioDecoderMp3::~AudioDecoderMp3() + { + close(); + } -bool AudioDecoderMp3::decodeToPcm() -{ - _fileData = FileUtils::getInstance()->getDataFromFile(_url); - if (_fileData.isNull()) + bool AudioDecoderMp3::open(const char* path) { + std::string fullPath = FileUtils::getInstance()->fullPathForFilename(path); + + long rate = 0; + int error = MPG123_OK; + int mp3Encoding = 0; + int channel = 0; + do + { + _mpg123handle = mpg123_new(nullptr, &error); + if (nullptr == _mpg123handle) + { + ALOGE("Basic setup goes wrong: %s", mpg123_plain_strerror(error)); + break; + } + + if(fullPath[0] != '/') { + off_t start = 0, length = 0; + std::string relativePath; + size_t position = fullPath.find("assets/"); + if (0 == position) + { + // "assets/" is at the beginning of the path and we don't want it + relativePath = fullPath.substr(strlen("assets/")); + } else + { + relativePath = fullPath; + } + auto stream = mpg123_open_r(relativePath); + if(stream == nullptr) { + ALOGE("Trouble with mpg123(1): %s\n", strerror(errno) ); + break; + } + + mpg123_replace_reader_handle(_mpg123handle, mpg123_read_r, mpg123_lseek_r, mpg123_close_r); + + if (mpg123_open_handle(_mpg123handle, stream) != MPG123_OK + || mpg123_getformat(_mpg123handle, &rate, &channel, &mp3Encoding) != MPG123_OK) + { + ALOGE("Trouble with mpg123(2): %s\n", mpg123_strerror(_mpg123handle) ); + break; + } + } + else { + if (mpg123_open(_mpg123handle, fullPath.c_str()) != MPG123_OK + || mpg123_getformat(_mpg123handle, &rate, &channel, &mp3Encoding) != MPG123_OK) + { + ALOGE("Trouble with mpg123(2): %s\n", mpg123_strerror(_mpg123handle) ); + break; + } + } + + _channelCount = channel; + _sampleRate = rate; + + if (mp3Encoding == MPG123_ENC_SIGNED_16) + { + _bytesPerFrame = 2 * _channelCount; + } + else if (mp3Encoding == MPG123_ENC_FLOAT_32) + { + _bytesPerFrame = 4 * _channelCount; + } + else + { + ALOGE("Bad encoding: 0x%x!\n", mp3Encoding); + break; + } + + /* Ensure that this output format will not change (it could, when we allow it). */ + mpg123_format_none(_mpg123handle); + mpg123_format(_mpg123handle, rate, channel, mp3Encoding); + /* Ensure that we can get accurate length by call mpg123_length */ + mpg123_scan(_mpg123handle); + + _totalFrames = mpg123_length(_mpg123handle); + + _isOpened = true; + return true; + } while (false); + + if (_mpg123handle != nullptr) + { + mpg123_close(_mpg123handle); + mpg123_delete(_mpg123handle); + _mpg123handle = nullptr; + } return false; } - mp3_callbacks callbacks; - callbacks.read = AudioDecoder::fileRead; - callbacks.seek = AudioDecoder::fileSeek; - callbacks.close = AudioDecoder::fileClose; - callbacks.tell = AudioDecoder::fileTell; + void AudioDecoderMp3::close() + { + if (isOpened()) + { + if (_mpg123handle != nullptr) + { + mpg123_close(_mpg123handle); + mpg123_delete(_mpg123handle); + + _mpg123handle = nullptr; + } + _isOpened = false; + } + } - int numChannels = 0; - int sampleRate = 0; - int numFrames = 0; - - if (EXIT_SUCCESS == decodeMP3(&callbacks, this, *_result.pcmBuffer, &numChannels, &sampleRate, &numFrames) - && numChannels > 0 && sampleRate > 0 && numFrames > 0) + uint32_t AudioDecoderMp3::read(uint32_t framesToRead, char* pcmBuf) + { + int bytesToRead = framesToRead * _bytesPerFrame; + size_t bytesRead = 0; + int err = mpg123_read(_mpg123handle, (unsigned char*)pcmBuf, bytesToRead, &bytesRead); + if (err == MPG123_ERR) + { + ALOGE("Trouble with mpg123: %s\n", mpg123_strerror(_mpg123handle) ); + return 0; + } + + return static_cast(bytesRead / _bytesPerFrame); + } + + bool AudioDecoderMp3::seek(uint32_t frameOffset) { - _result.numChannels = numChannels; - _result.sampleRate = sampleRate; - _result.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; - _result.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; - _result.channelMask = numChannels == 1 ? SL_SPEAKER_FRONT_CENTER : (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT); - _result.endianness = SL_BYTEORDER_LITTLEENDIAN; - _result.numFrames = numFrames; - _result.duration = 1.0f * numFrames / sampleRate; - - std::string info = _result.toString(); - ALOGI("Original audio info: %s, total size: %d", info.c_str(), (int)_result.pcmBuffer->size()); - return true; + off_t offset = mpg123_seek(_mpg123handle, frameOffset, SEEK_SET); + //ALOGD("mpg123_seek return: %d", (int)offset); + if (offset >= 0 && offset == frameOffset) + { + return true; + } + return false; } - ALOGE("Decode MP3 (%s) failed, channels: %d, rate: %d, frames: %d", _url.c_str(), numChannels, sampleRate, numFrames); - return false; -} + uint32_t AudioDecoderMp3::tell() const + { + return static_cast(mpg123_tell(_mpg123handle)); + } -}} // namespace cocos2d { namespace experimental { \ No newline at end of file +}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioDecoderMp3.h b/cocos/audio/android/AudioDecoderMp3.h index 3def5a202dc5..8439e5cbabe4 100644 --- a/cocos/audio/android/AudioDecoderMp3.h +++ b/cocos/audio/android/AudioDecoderMp3.h @@ -1,43 +1,87 @@ /**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ + Copyright (c) 2016-2017 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + Copyright (c) 2018 HALX99. + + 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. + ****************************************************************************/ #pragma once #include "audio/android/AudioDecoder.h" +struct mpg123_handle_struct; + namespace cocos2d { namespace experimental { +/** + * @brief The class for decoding compressed audio file to PCM buffer. + */ class AudioDecoderMp3 : public AudioDecoder { +public: + /** + * @brief Opens an audio file specified by a file path. + * @return true if succeed, otherwise false. + */ + virtual bool open(const char* path) override; + + /** + * @brief Closes opened audio file. + * @note The method will also be automatically invoked in the destructor. + */ + virtual void close() override; + + /** + * @brief Reads audio frames of PCM format. + * @param framesToRead The number of frames excepted to be read. + * @param pcmBuf The buffer to hold the frames to be read, its size should be >= |framesToRead| * _bytesPerFrame. + * @return The number of frames actually read, it's probably less than 'framesToRead'. Returns 0 means reach the end of file. + */ + virtual uint32_t read(uint32_t framesToRead, char* pcmBuf) override; + + /** + * @brief Sets frame offest to be read. + * @param frameOffset The frame offest to be set. + * @return true if succeed, otherwise false + */ + virtual bool seek(uint32_t frameOffset) override; + + /** + * @brief Tells the current frame offset. + * @return The current frame offset. + */ + virtual uint32_t tell() const override; + protected: + AudioDecoderMp3(); - virtual ~AudioDecoderMp3(); + ~AudioDecoderMp3(); + + static bool lazyInit(); + static void destroy(); - virtual bool decodeToPcm() override; + struct mpg123_handle_struct* _mpg123handle; - friend class AudioDecoderProvider; + friend class AudioDecoderManager; }; -}} // namespace cocos2d { namespace experimental { \ No newline at end of file +}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioDecoderOgg.cpp b/cocos/audio/android/AudioDecoderOgg.cpp index 15f033636b72..460e8362f604 100644 --- a/cocos/audio/android/AudioDecoderOgg.cpp +++ b/cocos/audio/android/AudioDecoderOgg.cpp @@ -1,113 +1,156 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#define LOG_TAG "AudioDecoderOgg" - -#include "audio/android/AudioDecoderOgg.h" -#include "platform/CCFileUtils.h" - -namespace cocos2d { namespace experimental { - -AudioDecoderOgg::AudioDecoderOgg() -{ - ALOGV("Create AudioDecoderOgg"); -} - -AudioDecoderOgg::~AudioDecoderOgg() -{ - -} - -int AudioDecoderOgg::fseek64Wrap(void* datasource, ogg_int64_t off, int whence) -{ - return AudioDecoder::fileSeek(datasource, (long)off, whence); -} - -bool AudioDecoderOgg::decodeToPcm() -{ - _fileData = FileUtils::getInstance()->getDataFromFile(_url); - if (_fileData.isNull()) - { - return false; - } - - ov_callbacks callbacks; - callbacks.read_func = AudioDecoder::fileRead; - callbacks.seek_func = AudioDecoderOgg::fseek64Wrap; - callbacks.close_func = AudioDecoder::fileClose; - callbacks.tell_func = AudioDecoder::fileTell; - - _fileCurrPos = 0; - - OggVorbis_File vf; - int ret = ov_open_callbacks(this, &vf, NULL, 0, callbacks); - if (ret != 0) - { - ALOGE("Open file error, file: %s, ov_open_callbacks return %d", _url.c_str(), ret); - return false; - } - // header - auto vi = ov_info(&vf, -1); - - uint32_t pcmSamples = (uint32_t) ov_pcm_total(&vf, -1); - - uint32_t bufferSize = pcmSamples * vi->channels * sizeof(short); - char* pcmBuffer = (char*)malloc(bufferSize); - memset(pcmBuffer, 0, bufferSize); - - int currentSection = 0; - long curPos = 0; - long readBytes = 0; - - do - { - readBytes = ov_read(&vf, pcmBuffer + curPos, 4096, ¤tSection); - curPos += readBytes; - } while (readBytes > 0); - - if (curPos > 0) - { - _result.pcmBuffer->insert(_result.pcmBuffer->end(), pcmBuffer, pcmBuffer + bufferSize); - _result.numChannels = vi->channels; - _result.sampleRate = vi->rate; - _result.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; - _result.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; - _result.channelMask = vi->channels == 1 ? SL_SPEAKER_FRONT_CENTER : (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT); - _result.endianness = SL_BYTEORDER_LITTLEENDIAN; - _result.numFrames = pcmSamples; - _result.duration = 1.0f * pcmSamples / vi->rate; - } - else - { - ALOGE("ov_read returns 0 byte!"); - } - - ov_clear(&vf); - free(pcmBuffer); - - return (curPos > 0); -} - -}} // namespace cocos2d { namespace experimental { \ No newline at end of file +/**************************************************************************** + Copyright (c) 2016-2017 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + Copyright (c) 2018 HALX99. + + 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 "audio/android/AudioDecoderOgg.h" +#include "audio/android/AudioMacros.h" +#include "platform/CCFileUtils.h" + +#include "platform/android/CCFileUtils-android.h" +#include +#include +#include +#include +#include + +#define LOG_TAG "AudioDecoderOgg" +#include "audio/android/log.h" + +namespace cocos2d { namespace experimental { + + static AAsset* ov_fopen_r(const char* path) + { + AAssetManager* asMgr = FileUtilsAndroid::getAssetManager(); + AAsset* asset = AAssetManager_open(FileUtilsAndroid::getAssetManager(), path, AASSET_MODE_UNKNOWN); + return asset; + }; + + static size_t ov_fread_r(void* buffer, size_t element_size, size_t element_count, void* handle) + { + return AAsset_read((AAsset*)handle, buffer, element_size * element_count); + } + + static int ov_fseek_r(void * handle, ogg_int64_t offset, int whence) + { + auto n = AAsset_seek((AAsset*)handle, offset, whence); + return n >= 0 ? 0 : -1; + } + + static long ov_ftell_r(void * handle) + { + return AAsset_seek((AAsset*)handle, 0, SEEK_CUR); + } + + static int ov_fclose_r(void* handle) { + AAsset_close((AAsset*)handle); + return 0; + } + + AudioDecoderOgg::AudioDecoderOgg() + { + } + + AudioDecoderOgg::~AudioDecoderOgg() + { + close(); + } + + bool AudioDecoderOgg::open(const char* path) + { + std::string fullPath = FileUtils::getInstance()->fullPathForFilename(path); + int iret = -1; + if(fullPath[0] != '/') { + off_t start = 0, length = 0; + std::string relativePath; + size_t position = fullPath.find("assets/"); + if (0 == position) + { + // "assets/" is at the beginning of the path and we don't want it + relativePath = fullPath.substr(strlen("assets/")); + } else + { + relativePath = fullPath; + } + auto stream = ov_fopen_r(relativePath.c_str()); + if(stream == nullptr) { + ALOGE("Trouble with ogg(1): %s\n", strerror(errno) ); + return false; + } + + static ov_callbacks OV_CALLBACKS_AASSET = { + ov_fread_r, + ov_fseek_r, + ov_fclose_r, + ov_ftell_r + }; + + iret = ov_open_callbacks(stream, &_vf, nullptr, 0, OV_CALLBACKS_AASSET); + } + else { + iret = ov_fopen(fullPath.c_str(), &_vf); + } + + if (0 == iret) + { + // header + vorbis_info* vi = ov_info(&_vf, -1); + _sampleRate = static_cast(vi->rate); + _channelCount = vi->channels; + _bytesPerFrame = vi->channels * sizeof(short); + _totalFrames = static_cast(ov_pcm_total(&_vf, -1)); + _isOpened = true; + return true; + } + return false; + } + + void AudioDecoderOgg::close() + { + if (isOpened()) + { + ov_clear(&_vf); + _isOpened = false; + } + } + + uint32_t AudioDecoderOgg::read(uint32_t framesToRead, char* pcmBuf) + { + int currentSection = 0; + int bytesToRead = framesToRead * _bytesPerFrame; + long bytesRead = ov_read(&_vf, pcmBuf, bytesToRead, 0, 2, 1, ¤tSection); + return static_cast(bytesRead / _bytesPerFrame); + } + + bool AudioDecoderOgg::seek(uint32_t frameOffset) + { + return 0 == ov_pcm_seek(&_vf, frameOffset); + } + + uint32_t AudioDecoderOgg::tell() const + { + return static_cast(ov_pcm_tell(const_cast(&_vf))); + } + +}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioDecoderOgg.h b/cocos/audio/android/AudioDecoderOgg.h index 4ad5166e65d9..021687be4d29 100644 --- a/cocos/audio/android/AudioDecoderOgg.h +++ b/cocos/audio/android/AudioDecoderOgg.h @@ -1,46 +1,83 @@ /**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ + Copyright (c) 2016-2017 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + Copyright (c) 2018 HALX99. + + 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. + ****************************************************************************/ #pragma once #include "audio/android/AudioDecoder.h" -#include "Tremolo/ivorbisfile.h" +#include "vorbis/vorbisfile.h" namespace cocos2d { namespace experimental { +/** + * @brief The class for decoding compressed audio file to PCM buffer. + */ class AudioDecoderOgg : public AudioDecoder { +public: + /** + * @brief Opens an audio file specified by a file path. + * @return true if succeed, otherwise false. + */ + virtual bool open(const char* path) override; + + /** + * @brief Closes opened audio file. + * @note The method will also be automatically invoked in the destructor. + */ + virtual void close() override; + + /** + * @brief Reads audio frames of PCM format. + * @param framesToRead The number of frames excepted to be read. + * @param pcmBuf The buffer to hold the frames to be read, its size should be >= |framesToRead| * _bytesPerFrame. + * @return The number of frames actually read, it's probably less than 'framesToRead'. Returns 0 means reach the end of file. + */ + virtual uint32_t read(uint32_t framesToRead, char* pcmBuf) override; + + /** + * @brief Sets frame offest to be read. + * @param frameOffset The frame offest to be set. + * @return true if succeed, otherwise false + */ + virtual bool seek(uint32_t frameOffset) override; + + /** + * @brief Tells the current frame offset. + * @return The current frame offset. + */ + virtual uint32_t tell() const override; + protected: AudioDecoderOgg(); - virtual ~AudioDecoderOgg(); + ~AudioDecoderOgg(); - static int fseek64Wrap(void* datasource, ogg_int64_t off, int whence); - virtual bool decodeToPcm() override; + OggVorbis_File _vf; - friend class AudioDecoderProvider; + friend class AudioDecoderManager; }; -}} // namespace cocos2d { namespace experimental { \ No newline at end of file +}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioDecoderProvider.cpp b/cocos/audio/android/AudioDecoderProvider.cpp deleted file mode 100644 index cedf52b20e6d..000000000000 --- a/cocos/audio/android/AudioDecoderProvider.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************** - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - - 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. - ****************************************************************************/ - -#define LOG_TAG "AudioDecoderProvider" - -#include "audio/android/AudioDecoderProvider.h" -#include "audio/android/AudioDecoderSLES.h" -#include "audio/android/AudioDecoderOgg.h" -#include "audio/android/AudioDecoderMp3.h" -#include "audio/android/AudioDecoderWav.h" -#include "platform/CCFileUtils.h" - -namespace cocos2d { namespace experimental { - -AudioDecoder* AudioDecoderProvider::createAudioDecoder(SLEngineItf engineItf, const std::string &url, int bufferSizeInFrames, int sampleRate, const FdGetterCallback &fdGetterCallback) -{ - AudioDecoder* decoder = nullptr; - std::string extension = FileUtils::getInstance()->getFileExtension(url); - ALOGV("url:%s, extension:%s", url.c_str(), extension.c_str()); - if (extension == ".ogg") - { - decoder = new AudioDecoderOgg(); - if (!decoder->init(url, sampleRate)) - { - delete decoder; - decoder = nullptr; - } - } - else if (extension == ".mp3") - { - decoder = new AudioDecoderMp3(); - if (!decoder->init(url, sampleRate)) - { - delete decoder; - decoder = nullptr; - } - } - else if (extension == ".wav") - { - decoder = new AudioDecoderWav(); - if (!decoder->init(url, sampleRate)) - { - delete decoder; - decoder = nullptr; - } - } - else - { - auto slesDecoder = new AudioDecoderSLES(); - if (slesDecoder->init(engineItf, url, bufferSizeInFrames, sampleRate, fdGetterCallback)) - { - decoder = slesDecoder; - } - else - { - delete slesDecoder; - } - } - - return decoder; -} - -void AudioDecoderProvider::destroyAudioDecoder(AudioDecoder** decoder) -{ - if (decoder != nullptr && *decoder != nullptr) - { - delete (*decoder); - (*decoder) = nullptr; - } -} - -}} // namespace cocos2d { namespace experimental { \ No newline at end of file diff --git a/cocos/audio/android/AudioDecoderProvider.h b/cocos/audio/android/AudioDecoderProvider.h deleted file mode 100644 index 9b2e7e1cb433..000000000000 --- a/cocos/audio/android/AudioDecoderProvider.h +++ /dev/null @@ -1,41 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#pragma once - -#include "audio/android/OpenSLHelper.h" - -namespace cocos2d { namespace experimental { - -class AudioDecoder; - -class AudioDecoderProvider -{ -public: - static AudioDecoder* createAudioDecoder(SLEngineItf engineItf, const std::string &url, int bufferSizeInFrames, int sampleRate, const FdGetterCallback &fdGetterCallback); - static void destroyAudioDecoder(AudioDecoder** decoder); -}; - -}} // namespace cocos2d { namespace experimental { \ No newline at end of file diff --git a/cocos/audio/android/AudioDecoderSLES.cpp b/cocos/audio/android/AudioDecoderSLES.cpp deleted file mode 100644 index bbcc9382b243..000000000000 --- a/cocos/audio/android/AudioDecoderSLES.cpp +++ /dev/null @@ -1,646 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#define LOG_TAG "AudioDecoderSLES" - -#include "audio/android/AudioDecoderSLES.h" -#include "platform/CCFileUtils.h" - -namespace cocos2d { namespace experimental { - -/* Explicitly requesting SL_IID_ANDROIDSIMPLEBUFFERQUEUE and SL_IID_PREFETCHSTATUS -* on the UrlAudioPlayer object for decoding, SL_IID_METADATAEXTRACTION for retrieving the -* format of the decoded audio */ -#define NUM_EXPLICIT_INTERFACES_FOR_PLAYER 3 - -/* Size of the decode buffer queue */ -#define NB_BUFFERS_IN_QUEUE 4 - -/* size of the struct to retrieve the PCM format metadata values: the values we're interested in - * are SLuint32, but it is saved in the data field of a SLMetadataInfo, hence the larger size. - * Nate that this size is queried and displayed at l.452 for demonstration/test purposes. - * */ -#define PCM_METADATA_VALUE_SIZE 32 - -/* used to detect errors likely to have occurred when the OpenSL ES framework fails to open - * a resource, for instance because a file URI is invalid, or an HTTP server doesn't respond. - */ -#define PREFETCHEVENT_ERROR_CANDIDATE (SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE) - -//----------------------------------------------------------------- - -static std::mutex __SLPlayerMutex; - -static int toBufferSizeInBytes(int bufferSizeInFrames, int sampleSize, int channelCount) -{ - return bufferSizeInFrames * sampleSize * channelCount; -} - -static int BUFFER_SIZE_IN_BYTES = 0; - -static void checkMetaData(int index, const char *key) -{ - if (index != -1) - { - ALOGV("Key %s is at index %d", key, index); - } - else - { - ALOGE("Unable to find key %s", key); - } -} - -class SLAudioDecoderCallbackProxy -{ -public: - //----------------------------------------------------------------- - /* Callback for "prefetch" events, here used to detect audio resource opening errors */ - static void prefetchEventCallback(SLPrefetchStatusItf caller, void *context, SLuint32 event) - { - AudioDecoderSLES *thiz = reinterpret_cast(context); - thiz->prefetchCallback(caller, event); - } - - static void decPlayCallback(SLAndroidSimpleBufferQueueItf queueItf, void *context) - { - AudioDecoderSLES *thiz = reinterpret_cast(context); - thiz->decodeToPcmCallback(queueItf); - } - - static void decProgressCallback(SLPlayItf caller, void *context, SLuint32 event) - { - AudioDecoderSLES *thiz = reinterpret_cast(context); - thiz->decodeProgressCallback(caller, event); - } -}; - -AudioDecoderSLES::AudioDecoderSLES() - : _engineItf(nullptr), _playObj(nullptr), _formatQueried(false), - _prefetchError(false), _counter(0), _numChannelsKeyIndex(-1), _sampleRateKeyIndex(-1), - _bitsPerSampleKeyIndex(-1), _containerSizeKeyIndex(-1), _channelMaskKeyIndex(-1), - _endiannessKeyIndex(-1), _eos(false), _bufferSizeInFrames(-1), - _assetFd(0), _fdGetterCallback(nullptr), _isDecodingCallbackInvoked(false) -{ - ALOGV("Create AudioDecoderSLES"); -} - -AudioDecoderSLES::~AudioDecoderSLES() -{ - { - std::lock_guard lk(__SLPlayerMutex); - SL_DESTROY_OBJ(_playObj); - } - ALOGV("After destroying SL play object"); - if (_assetFd > 0) - { - ALOGV("Closing assetFd: %d", _assetFd); - ::close(_assetFd); - _assetFd = 0; - } - free(_pcmData); -} - -bool AudioDecoderSLES::init(SLEngineItf engineItf, const std::string &url, int bufferSizeInFrames, int sampleRate, const FdGetterCallback &fdGetterCallback) -{ - if (AudioDecoder::init(url, sampleRate)) - { - _engineItf = engineItf; - _bufferSizeInFrames = bufferSizeInFrames; - _fdGetterCallback = fdGetterCallback; - - BUFFER_SIZE_IN_BYTES = toBufferSizeInBytes(bufferSizeInFrames, 2, 2); - _pcmData = (char*) malloc(NB_BUFFERS_IN_QUEUE * BUFFER_SIZE_IN_BYTES); - memset(_pcmData, 0x00, NB_BUFFERS_IN_QUEUE * BUFFER_SIZE_IN_BYTES); - return true; - } - - return false; -} - -bool AudioDecoderSLES::decodeToPcm() -{ - SLresult result; - - /* Objects this application uses: one audio player */ - SLObjectItf player; - - /* Interfaces for the audio player */ - SLAndroidSimpleBufferQueueItf decBuffQueueItf; - SLPrefetchStatusItf prefetchItf; - SLPlayItf playItf; - SLMetadataExtractionItf mdExtrItf; - - /* Source of audio data for the decoding */ - SLDataSource decSource; - - // decUri & locFd should be defined here - SLDataLocator_URI decUri; - SLDataLocator_AndroidFD locFd; - - /* Data sink for decoded audio */ - SLDataSink decDest; - SLDataLocator_AndroidSimpleBufferQueue decBuffQueue; - SLDataFormat_PCM pcm; - - SLboolean required[NUM_EXPLICIT_INTERFACES_FOR_PLAYER]; - SLInterfaceID iidArray[NUM_EXPLICIT_INTERFACES_FOR_PLAYER]; - - /* Initialize arrays required[] and iidArray[] */ - for (int i = 0; i < NUM_EXPLICIT_INTERFACES_FOR_PLAYER; i++) - { - required[i] = SL_BOOLEAN_FALSE; - iidArray[i] = SL_IID_NULL; - } - - /* ------------------------------------------------------ */ - /* Configuration of the player */ - - /* Request the AndroidSimpleBufferQueue interface */ - required[0] = SL_BOOLEAN_TRUE; - iidArray[0] = SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - /* Request the PrefetchStatus interface */ - required[1] = SL_BOOLEAN_TRUE; - iidArray[1] = SL_IID_PREFETCHSTATUS; - /* Request the PrefetchStatus interface */ - required[2] = SL_BOOLEAN_TRUE; - iidArray[2] = SL_IID_METADATAEXTRACTION; - - SLDataFormat_MIME formatMime = {SL_DATAFORMAT_MIME, nullptr, SL_CONTAINERTYPE_UNSPECIFIED}; - decSource.pFormat = &formatMime; - - if (_url[0] != '/') - { - off_t start = 0, length = 0; - std::string relativePath; - size_t position = _url.find("assets/"); - - if (0 == position) - { - // "assets/" is at the beginning of the path and we don't want it - relativePath = _url.substr(strlen("assets/")); - } else - { - relativePath = _url; - } - - _assetFd = _fdGetterCallback(relativePath, &start, &length); - - if (_assetFd <= 0) - { - ALOGE("Failed to open file descriptor for '%s'", _url.c_str()); - return false; - } - - // configure audio source - locFd = {SL_DATALOCATOR_ANDROIDFD, _assetFd, start, length}; - - decSource.pLocator = &locFd; - } - else - { - decUri = {SL_DATALOCATOR_URI, (SLchar *) _url.c_str()}; - decSource.pLocator = &decUri; - } - - /* Setup the data sink */ - decBuffQueue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - decBuffQueue.numBuffers = NB_BUFFERS_IN_QUEUE; - /* set up the format of the data in the buffer queue */ - pcm.formatType = SL_DATAFORMAT_PCM; - // FIXME valid value required but currently ignored - pcm.numChannels = 2; - pcm.samplesPerSec = SL_SAMPLINGRATE_44_1; - pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; - pcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; - pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; - - decDest.pLocator = (void *) &decBuffQueue; - decDest.pFormat = (void *) &pcm; - - { - std::lock_guard lk(__SLPlayerMutex); - /* Create the audio player */ - result = (*_engineItf)->CreateAudioPlayer(_engineItf, &player, &decSource, &decDest, - NUM_EXPLICIT_INTERFACES_FOR_PLAYER, iidArray, - required); - SL_RETURN_VAL_IF_FAILED(result, false, "CreateAudioPlayer failed"); - - _playObj = player; - /* Realize the player in synchronous mode. */ - result = (*player)->Realize(player, SL_BOOLEAN_FALSE); - SL_RETURN_VAL_IF_FAILED(result, false, "Realize failed"); - } - - /* Get the play interface which is implicit */ - result = (*player)->GetInterface(player, SL_IID_PLAY, (void *) &playItf); - SL_RETURN_VAL_IF_FAILED(result, false, "GetInterface SL_IID_PLAY failed"); - - /* Set up the player callback to get events during the decoding */ - // FIXME currently ignored - result = (*playItf)->SetMarkerPosition(playItf, 2000); - SL_RETURN_VAL_IF_FAILED(result, false, "SetMarkerPosition failed"); - - result = (*playItf)->SetPositionUpdatePeriod(playItf, 500); - SL_RETURN_VAL_IF_FAILED(result, false, "SetPositionUpdatePeriod failed"); - result = (*playItf)->SetCallbackEventsMask(playItf, - SL_PLAYEVENT_HEADATMARKER | - SL_PLAYEVENT_HEADATNEWPOS | SL_PLAYEVENT_HEADATEND); - SL_RETURN_VAL_IF_FAILED(result, false, "SetCallbackEventsMask failed"); - result = (*playItf)->RegisterCallback(playItf, SLAudioDecoderCallbackProxy::decProgressCallback, - this); - SL_RETURN_VAL_IF_FAILED(result, false, "RegisterCallback failed"); - ALOGV("Play callback registered"); - - - /* Get the buffer queue interface which was explicitly requested */ - result = (*player)->GetInterface(player, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - (void *) &decBuffQueueItf); - SL_RETURN_VAL_IF_FAILED(result, false, "GetInterface SL_IID_ANDROIDSIMPLEBUFFERQUEUE failed"); - - /* Get the prefetch status interface which was explicitly requested */ - result = (*player)->GetInterface(player, SL_IID_PREFETCHSTATUS, (void *) &prefetchItf); - SL_RETURN_VAL_IF_FAILED(result, false, "GetInterface SL_IID_PREFETCHSTATUS failed"); - - /* Get the metadata extraction interface which was explicitly requested */ - result = (*player)->GetInterface(player, SL_IID_METADATAEXTRACTION, (void *) &mdExtrItf); - SL_RETURN_VAL_IF_FAILED(result, false, "GetInterface SL_IID_METADATAEXTRACTION failed"); - - /* ------------------------------------------------------ */ - /* Initialize the callback and its context for the decoding buffer queue */ - _decContext.playItf = playItf; - _decContext.metaItf = mdExtrItf; - _decContext.pDataBase = (int8_t *) _pcmData; - _decContext.pData = _decContext.pDataBase; - _decContext.size = NB_BUFFERS_IN_QUEUE * BUFFER_SIZE_IN_BYTES; - - result = (*decBuffQueueItf)->RegisterCallback(decBuffQueueItf, - SLAudioDecoderCallbackProxy::decPlayCallback, - this); - SL_RETURN_VAL_IF_FAILED(result, false, "decBuffQueueItf RegisterCallback failed"); - - /* Enqueue buffers to map the region of memory allocated to store the decoded data */ -// ALOGV("Enqueueing buffer "); - for (int i = 0; i < NB_BUFFERS_IN_QUEUE; i++) - { - result = (*decBuffQueueItf)->Enqueue(decBuffQueueItf, _decContext.pData, - BUFFER_SIZE_IN_BYTES); - SL_RETURN_VAL_IF_FAILED(result, false, "Enqueue failed"); - _decContext.pData += BUFFER_SIZE_IN_BYTES; - } - - _decContext.pData = _decContext.pDataBase; - - /* ------------------------------------------------------ */ - /* Initialize the callback for prefetch errors, if we can't open the resource to decode */ - result = (*prefetchItf)->RegisterCallback(prefetchItf, - SLAudioDecoderCallbackProxy::prefetchEventCallback, - this); - SL_RETURN_VAL_IF_FAILED(result, false, "prefetchItf RegisterCallback failed"); - - result = (*prefetchItf)->SetCallbackEventsMask(prefetchItf, PREFETCHEVENT_ERROR_CANDIDATE); - SL_RETURN_VAL_IF_FAILED(result, false, "prefetchItf SetCallbackEventsMask failed"); - - /* ------------------------------------------------------ */ - /* Prefetch the data so we can get information about the format before starting to decode */ - /* 1/ cause the player to prefetch the data */ - result = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PAUSED); - SL_RETURN_VAL_IF_FAILED(result, false, "SetPlayState SL_PLAYSTATE_PAUSED failed"); - - /* 2/ block until data has been prefetched */ - SLuint32 prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW; - SLuint32 timeOutIndex = 1000; //cjh time out prefetching after 2s - while ((prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA) && (timeOutIndex > 0) && - !_prefetchError) - { - std::this_thread::sleep_for(std::chrono::milliseconds(2)); - (*prefetchItf)->GetPrefetchStatus(prefetchItf, &prefetchStatus); - timeOutIndex--; - } - if (timeOutIndex == 0 || _prefetchError) - { - ALOGE("Failure to prefetch data in time, exiting"); - SL_RETURN_VAL_IF_FAILED(SL_RESULT_CONTENT_NOT_FOUND, false, - "Failure to prefetch data in time"); - } - - /* ------------------------------------------------------ */ - /* Display duration */ - SLmillisecond durationInMsec = SL_TIME_UNKNOWN; - result = (*playItf)->GetDuration(playItf, &durationInMsec); - SL_RETURN_VAL_IF_FAILED(result, false, "GetDuration failed"); - - if (durationInMsec == SL_TIME_UNKNOWN) - { - ALOGV("Content duration is unknown"); - } else - { - ALOGV("Content duration is %dms", (int)durationInMsec); - } - - /* ------------------------------------------------------ */ - /* Display the metadata obtained from the decoder */ - // This is for test / demonstration purposes only where we discover the key and value sizes - // of a PCM decoder. An application that would want to directly get access to those values - // can make assumptions about the size of the keys and their matching values (all SLuint32) - SLuint32 itemCount; - result = (*mdExtrItf)->GetItemCount(mdExtrItf, &itemCount); - SLuint32 i, keySize, valueSize; - SLMetadataInfo *keyInfo, *value; - for (i = 0; i < itemCount; i++) - { - keyInfo = nullptr; - keySize = 0; - value = nullptr; - valueSize = 0; - result = (*mdExtrItf)->GetKeySize(mdExtrItf, i, &keySize); - SL_RETURN_VAL_IF_FAILED(result, false, "GetKeySize(%d) failed", (int)i); - - result = (*mdExtrItf)->GetValueSize(mdExtrItf, i, &valueSize); - SL_RETURN_VAL_IF_FAILED(result, false, "GetValueSize(%d) failed", (int)i); - - keyInfo = (SLMetadataInfo *) malloc(keySize); - if (nullptr != keyInfo) - { - result = (*mdExtrItf)->GetKey(mdExtrItf, i, keySize, keyInfo); - - SL_RETURN_VAL_IF_FAILED(result, false, "GetKey(%d) failed", (int)i); - - ALOGV("key[%d] size=%d, name=%s, value size=%d", - (int)i, (int)keyInfo->size, keyInfo->data, (int)valueSize); - /* find out the key index of the metadata we're interested in */ - if (!strcmp((char *) keyInfo->data, ANDROID_KEY_PCMFORMAT_NUMCHANNELS)) - { - _numChannelsKeyIndex = i; - } else if (!strcmp((char *) keyInfo->data, ANDROID_KEY_PCMFORMAT_SAMPLERATE)) - { - _sampleRateKeyIndex = i; - } else if (!strcmp((char *) keyInfo->data, ANDROID_KEY_PCMFORMAT_BITSPERSAMPLE)) - { - _bitsPerSampleKeyIndex = i; - } else if (!strcmp((char *) keyInfo->data, ANDROID_KEY_PCMFORMAT_CONTAINERSIZE)) - { - _containerSizeKeyIndex = i; - } else if (!strcmp((char *) keyInfo->data, ANDROID_KEY_PCMFORMAT_CHANNELMASK)) - { - _channelMaskKeyIndex = i; - } else if (!strcmp((char *) keyInfo->data, ANDROID_KEY_PCMFORMAT_ENDIANNESS)) - { - _endiannessKeyIndex = i; - } - free(keyInfo); - } - } - - checkMetaData(_numChannelsKeyIndex, ANDROID_KEY_PCMFORMAT_NUMCHANNELS); - checkMetaData(_sampleRateKeyIndex, ANDROID_KEY_PCMFORMAT_SAMPLERATE); - checkMetaData(_bitsPerSampleKeyIndex, ANDROID_KEY_PCMFORMAT_BITSPERSAMPLE); - checkMetaData(_containerSizeKeyIndex, ANDROID_KEY_PCMFORMAT_CONTAINERSIZE); - checkMetaData(_channelMaskKeyIndex, ANDROID_KEY_PCMFORMAT_CHANNELMASK); - checkMetaData(_endiannessKeyIndex, ANDROID_KEY_PCMFORMAT_ENDIANNESS); - - /* ------------------------------------------------------ */ - /* Start decoding */ - result = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PLAYING); - SL_RETURN_VAL_IF_FAILED(result, false, "SetPlayState SL_PLAYSTATE_PLAYING failed"); - - ALOGV("Starting to decode"); - - /* Decode until the end of the stream is reached */ - { - std::unique_lock autoLock(_eosLock); - while (!_eos) - { - _eosCondition.wait(autoLock); - } - } - ALOGV("EOS signaled"); - - /* ------------------------------------------------------ */ - /* End of decoding */ - - /* Stop decoding */ - result = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED); - SL_RETURN_VAL_IF_FAILED(result, false, "SetPlayState SL_PLAYSTATE_STOPPED failed"); - - ALOGV("Stopped decoding"); - - /* Destroy the UrlAudioPlayer object */ - { - std::lock_guard lk(__SLPlayerMutex); - SL_DESTROY_OBJ(_playObj); - } - - ALOGV("After destroy player ..."); - - _result.numFrames = - _result.pcmBuffer->size() / _result.numChannels / (_result.bitsPerSample / 8); - - std::string info = _result.toString(); - ALOGI("Original audio info: %s, total size: %d", info.c_str(), (int)_result.pcmBuffer->size()); - return true; -} - -//----------------------------------------------------------------- -void AudioDecoderSLES::signalEos() -{ - std::unique_lock autoLock(_eosLock); - _eos = true; - _eosCondition.notify_one(); -} - -void AudioDecoderSLES::queryAudioInfo() -{ - if (_formatQueried) - { - return; - } - - SLresult result; - /* Get duration in callback where we use the callback context for the SLPlayItf*/ - SLmillisecond durationInMsec = SL_TIME_UNKNOWN; - result = (*_decContext.playItf)->GetDuration(_decContext.playItf, &durationInMsec); - SL_RETURN_IF_FAILED(result, "decodeProgressCallback,GetDuration failed"); - - if (durationInMsec == SL_TIME_UNKNOWN) - { - ALOGV("Content duration is unknown (in dec callback)"); - } else - { - ALOGV("Content duration is %dms (in dec callback)", (int)durationInMsec); - _result.duration = durationInMsec / 1000.0f; - } - - /* used to query metadata values */ - SLMetadataInfo pcmMetaData; - - result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _sampleRateKeyIndex, - PCM_METADATA_VALUE_SIZE, &pcmMetaData); - - SL_RETURN_IF_FAILED(result, "%s GetValue _sampleRateKeyIndex failed", __FUNCTION__); - // Note: here we could verify the following: - // pcmMetaData->encoding == SL_CHARACTERENCODING_BINARY - // pcmMetaData->size == sizeof(SLuint32) - // but the call was successful for the PCM format keys, so those conditions are implied - - _result.sampleRate = *((SLuint32 *) pcmMetaData.data); - result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _numChannelsKeyIndex, - PCM_METADATA_VALUE_SIZE, &pcmMetaData); - SL_RETURN_IF_FAILED(result, "%s GetValue _numChannelsKeyIndex failed", __FUNCTION__); - - _result.numChannels = *((SLuint32 *) pcmMetaData.data); - - result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _bitsPerSampleKeyIndex, - PCM_METADATA_VALUE_SIZE, &pcmMetaData); - SL_RETURN_IF_FAILED(result, "%s GetValue _bitsPerSampleKeyIndex failed", __FUNCTION__) - _result.bitsPerSample = *((SLuint32 *) pcmMetaData.data); - - result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _containerSizeKeyIndex, - PCM_METADATA_VALUE_SIZE, &pcmMetaData); - SL_RETURN_IF_FAILED(result, "%s GetValue _containerSizeKeyIndex failed", __FUNCTION__) - _result.containerSize = *((SLuint32 *) pcmMetaData.data); - - result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _channelMaskKeyIndex, - PCM_METADATA_VALUE_SIZE, &pcmMetaData); - SL_RETURN_IF_FAILED(result, "%s GetValue _channelMaskKeyIndex failed", __FUNCTION__) - _result.channelMask = *((SLuint32 *) pcmMetaData.data); - - result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _endiannessKeyIndex, - PCM_METADATA_VALUE_SIZE, &pcmMetaData); - SL_RETURN_IF_FAILED(result, "%s GetValue _endiannessKeyIndex failed", __FUNCTION__) - _result.endianness = *((SLuint32 *) pcmMetaData.data); - - _formatQueried = true; -} - -void AudioDecoderSLES::prefetchCallback(SLPrefetchStatusItf caller, SLuint32 event) -{ - SLpermille level = 0; - SLresult result; - result = (*caller)->GetFillLevel(caller, &level); - SL_RETURN_IF_FAILED(result, "GetFillLevel failed"); - - SLuint32 status; - //ALOGV("PrefetchEventCallback: received event %u", event); - result = (*caller)->GetPrefetchStatus(caller, &status); - - SL_RETURN_IF_FAILED(result, "GetPrefetchStatus failed"); - - if ((PREFETCHEVENT_ERROR_CANDIDATE == (event & PREFETCHEVENT_ERROR_CANDIDATE)) - && (level == 0) && (status == SL_PREFETCHSTATUS_UNDERFLOW)) - { - ALOGV("PrefetchEventCallback: Error while prefetching data, exiting"); - _prefetchError = true; - signalEos(); - } -} - -/* Callback for "playback" events, i.e. event happening during decoding */ -void AudioDecoderSLES::decodeProgressCallback(SLPlayItf caller, SLuint32 event) -{ - if (SL_PLAYEVENT_HEADATEND & event) - { - ALOGV("SL_PLAYEVENT_HEADATEND"); - if (!_isDecodingCallbackInvoked) - { - queryAudioInfo(); - - for (int i = 0; i < NB_BUFFERS_IN_QUEUE; ++i) - { - _result.pcmBuffer->insert(_result.pcmBuffer->end(), _decContext.pData, - _decContext.pData + BUFFER_SIZE_IN_BYTES); - - /* Increase data pointer by buffer size */ - _decContext.pData += BUFFER_SIZE_IN_BYTES; - } - } - signalEos(); - } -} - -//----------------------------------------------------------------- -/* Callback for decoding buffer queue events */ -void AudioDecoderSLES::decodeToPcmCallback(SLAndroidSimpleBufferQueueItf queueItf) -{ - _isDecodingCallbackInvoked = true; - ALOGV("%s ...", __FUNCTION__); - _counter++; - SLresult result; - // FIXME: ?? - if (_counter % 1000 == 0) - { - SLmillisecond msec; - result = (*_decContext.playItf)->GetPosition(_decContext.playItf, &msec); - SL_RETURN_IF_FAILED(result, "%s, GetPosition failed", __FUNCTION__); - ALOGV("%s called (iteration %d): current position=%d ms", __FUNCTION__, _counter, (int)msec); - } - - _result.pcmBuffer->insert(_result.pcmBuffer->end(), _decContext.pData, - _decContext.pData + BUFFER_SIZE_IN_BYTES); - - result = (*queueItf)->Enqueue(queueItf, _decContext.pData, BUFFER_SIZE_IN_BYTES); - SL_RETURN_IF_FAILED(result, "%s, Enqueue failed", __FUNCTION__); - - /* Increase data pointer by buffer size */ - _decContext.pData += BUFFER_SIZE_IN_BYTES; - - if (_decContext.pData >= _decContext.pDataBase + (NB_BUFFERS_IN_QUEUE * BUFFER_SIZE_IN_BYTES)) - { - _decContext.pData = _decContext.pDataBase; - } - - // Note: adding a sleep here or any sync point is a way to slow down the decoding, or - // synchronize it with some other event, as the OpenSL ES framework will block until the - // buffer queue callback return to proceed with the decoding. - -#if 0 - /* Example: buffer queue state display */ - SLAndroidSimpleBufferQueueState decQueueState; - result =(*queueItf)->GetState(queueItf, &decQueueState); - SL_RETURN_IF_FAILED(result, "decQueueState.GetState failed"); - - ALOGV("DecBufferQueueCallback now has _decContext.pData=%p, _decContext.pDataBase=%p, queue: " - "count=%u playIndex=%u, count: %d", - _decContext.pData, _decContext.pDataBase, decQueueState.count, decQueueState.index, _counter); -#endif - -#if 0 - /* Example: display position in callback where we use the callback context for the SLPlayItf*/ - SLmillisecond posMsec = SL_TIME_UNKNOWN; - result = (*_decContext.playItf)->GetPosition(_decContext.playItf, &posMsec); - SL_RETURN_IF_FAILED(result, "decodeToPcmCallback,GetPosition2 failed"); - - if (posMsec == SL_TIME_UNKNOWN) { - ALOGV("Content position is unknown (in dec callback)"); - } else { - ALOGV("Content position is %ums (in dec callback)", - posMsec); - } -#endif - - queryAudioInfo(); -} - -}} // namespace cocos2d { namespace experimental { \ No newline at end of file diff --git a/cocos/audio/android/AudioDecoderSLES.h b/cocos/audio/android/AudioDecoderSLES.h deleted file mode 100644 index 30795ab73600..000000000000 --- a/cocos/audio/android/AudioDecoderSLES.h +++ /dev/null @@ -1,97 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#pragma once - -#include "audio/android/AudioDecoder.h" -#include -#include - -namespace cocos2d { namespace experimental { - -class AudioDecoderSLES : public AudioDecoder -{ -protected: - AudioDecoderSLES(); - virtual ~AudioDecoderSLES(); - - bool init(SLEngineItf engineItf, const std::string &url, int bufferSizeInFrames, int sampleRate, const FdGetterCallback &fdGetterCallback); - virtual bool decodeToPcm() override; - -private: - void queryAudioInfo(); - - void signalEos(); - void decodeToPcmCallback(SLAndroidSimpleBufferQueueItf queueItf); - void prefetchCallback(SLPrefetchStatusItf caller, SLuint32 event); - void decodeProgressCallback(SLPlayItf caller, SLuint32 event); - - SLEngineItf _engineItf; - SLObjectItf _playObj; - /* Local storage for decoded audio data */ - char* _pcmData; - - /* we only want to query / display the PCM format once */ - bool _formatQueried; - /* Used to signal prefetching failures */ - bool _prefetchError; - - /* to display the number of decode iterations */ - int _counter; - - /* metadata key index for the PCM format information we want to retrieve */ - int _numChannelsKeyIndex; - int _sampleRateKeyIndex; - int _bitsPerSampleKeyIndex; - int _containerSizeKeyIndex; - int _channelMaskKeyIndex; - int _endiannessKeyIndex; - - /* to signal to the test app the end of the stream to decode has been reached */ - bool _eos; - std::mutex _eosLock; - std::condition_variable _eosCondition; - - /* Structure for passing information to callback function */ - typedef struct CallbackCntxt_ - { - SLPlayItf playItf; - SLMetadataExtractionItf metaItf; - SLuint32 size; - SLint8 *pDataBase; // Base address of local audio data storage - SLint8 *pData; // Current address of local audio data storage - } CallbackCntxt; - - CallbackCntxt _decContext; - int _bufferSizeInFrames; - int _assetFd; - FdGetterCallback _fdGetterCallback; - bool _isDecodingCallbackInvoked; - - friend class SLAudioDecoderCallbackProxy; - friend class AudioDecoderProvider; -}; - -}} // namespace cocos2d { namespace experimental { \ No newline at end of file diff --git a/cocos/audio/android/AudioDecoderWav.cpp b/cocos/audio/android/AudioDecoderWav.cpp deleted file mode 100644 index a89526ced179..000000000000 --- a/cocos/audio/android/AudioDecoderWav.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#define LOG_TAG "AudioDecoderWav" - -#include "audio/android/AudioDecoderWav.h" -#include "audio/android/tinysndfile.h" -#include "platform/CCFileUtils.h" - -namespace cocos2d { namespace experimental { - -AudioDecoderWav::AudioDecoderWav() -{ - ALOGV("Create AudioDecoderWav"); -} - -AudioDecoderWav::~AudioDecoderWav() -{ - -} - -void* AudioDecoderWav::onWavOpen(const char* path, void* user) -{ - return user; -} - -int AudioDecoderWav::onWavSeek(void* datasource, long offset, int whence) -{ - return AudioDecoder::fileSeek(datasource, (int64_t) offset, whence); -} - -int AudioDecoderWav::onWavClose(void* datasource) -{ - return 0; -} - -bool AudioDecoderWav::decodeToPcm() -{ - _fileData = FileUtils::getInstance()->getDataFromFile(_url); - if (_fileData.isNull()) - { - return false; - } - - SF_INFO info; - - snd_callbacks cb; - cb.open = onWavOpen; - cb.read = AudioDecoder::fileRead; - cb.seek = onWavSeek; - cb.close = onWavClose; - cb.tell = AudioDecoder::fileTell; - - SNDFILE* handle = NULL; - bool ret = false; - do - { - handle = sf_open_read(_url.c_str(), &info, &cb, this); - if (handle == nullptr) - break; - - if (info.frames == 0) - break; - - ALOGD("wav info: frames: %d, samplerate: %d, channels: %d, format: %d", info.frames, info.samplerate, info.channels, info.format); - size_t bufSize = sizeof(short) * info.frames * info.channels; - unsigned char* buf = (unsigned char*)malloc(bufSize); - sf_count_t readFrames = sf_readf_short(handle, (short*)buf, info.frames); - assert(readFrames == info.frames); - - _result.pcmBuffer->insert(_result.pcmBuffer->end(), buf, buf + bufSize); - _result.numChannels = info.channels; - _result.sampleRate = info.samplerate; - _result.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; - _result.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; - _result.channelMask = _result.numChannels == 1 ? SL_SPEAKER_FRONT_CENTER : (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT); - _result.endianness = SL_BYTEORDER_LITTLEENDIAN; - _result.numFrames = info.frames; - _result.duration = 1.0f * info.frames / _result.sampleRate; - - free(buf); - ret = true; - } while (false); - - if (handle != NULL) - sf_close(handle); - - return ret; -} - -}} // namespace cocos2d { namespace experimental { \ No newline at end of file diff --git a/cocos/audio/android/AudioDecoderWav.h b/cocos/audio/android/AudioDecoderWav.h deleted file mode 100644 index 41d57e6bf0db..000000000000 --- a/cocos/audio/android/AudioDecoderWav.h +++ /dev/null @@ -1,47 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#pragma once - -#include "audio/android/AudioDecoder.h" - -namespace cocos2d { namespace experimental { - -class AudioDecoderWav : public AudioDecoder -{ -protected: - AudioDecoderWav(); - virtual ~AudioDecoderWav(); - - virtual bool decodeToPcm() override; - - static void* onWavOpen(const char* path, void* user); - static int onWavSeek(void* datasource, long offset, int whence); - static int onWavClose(void* datasource); - - friend class AudioDecoderProvider; -}; - -}} // namespace cocos2d { namespace experimental { \ No newline at end of file diff --git a/cocos/audio/android/AudioEngine-inl.cpp b/cocos/audio/android/AudioEngine-inl.cpp deleted file mode 100644 index 81bd20f3358a..000000000000 --- a/cocos/audio/android/AudioEngine-inl.cpp +++ /dev/null @@ -1,495 +0,0 @@ -/**************************************************************************** - Copyright (c) 2014-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - - http://www.cocos2d-x.org - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ -#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID - -#define LOG_TAG "AudioEngineImpl" - -#include "audio/android/AudioEngine-inl.h" - -#include -// for native asset manager -#include -#include -#include - -#include -#include "platform/android/jni/JniHelper.h" -#include -#include -#include "audio/include/AudioEngine.h" -#include "base/CCDirector.h" -#include "base/CCScheduler.h" -#include "base/CCEventDispatcher.h" -#include "base/CCEventType.h" -#include "base/CCEventListenerCustom.h" -#include "base/ccUTF8.h" -#include "platform/android/CCFileUtils-android.h" -#include "platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h" - -#include "audio/android/IAudioPlayer.h" -#include "audio/android/ICallerThreadUtils.h" -#include "audio/android/AudioPlayerProvider.h" -#include "audio/android/cutils/log.h" -#include "audio/android/UrlAudioPlayer.h" - -using namespace cocos2d; -using namespace cocos2d::experimental; - -// Audio focus values synchronized with which in cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxActivity.java -static const int AUDIOFOCUS_GAIN = 0; -static const int AUDIOFOCUS_LOST = 1; -static const int AUDIOFOCUS_LOST_TRANSIENT = 2; -static const int AUDIOFOCUS_LOST_TRANSIENT_CAN_DUCK = 3; - -static int __currentAudioFocus = AUDIOFOCUS_GAIN; -static AudioEngineImpl* __impl = nullptr; - -class CallerThreadUtils : public ICallerThreadUtils -{ -public: - virtual void performFunctionInCallerThread(const std::function& func) - { - Director::getInstance()->getScheduler()->performFunctionInCocosThread(func); - }; - - virtual std::thread::id getCallerThreadId() - { - return _tid; - }; - - void setCallerThreadId(std::thread::id tid) - { - _tid = tid; - }; - -private: - std::thread::id _tid; -}; - -static CallerThreadUtils __callerThreadUtils; - -static int fdGetter(const std::string& url, off_t* start, off_t* length) -{ - int fd = -1; - if (cocos2d::FileUtilsAndroid::getObbFile() != nullptr) - { - fd = getObbAssetFileDescriptorJNI(url.c_str(), start, length); - } - else - { - auto asset = AAssetManager_open(cocos2d::FileUtilsAndroid::getAssetManager(), url.c_str(), AASSET_MODE_UNKNOWN); - // open asset as file descriptor - fd = AAsset_openFileDescriptor(asset, start, length); - AAsset_close(asset); - } - - if (fd <= 0) - { - ALOGE("Failed to open file descriptor for '%s'", url.c_str()); - } - - return fd; -}; - -//==================================================== -AudioEngineImpl::AudioEngineImpl() - : _engineObject(nullptr) - , _engineEngine(nullptr) - , _outputMixObject(nullptr) - , _audioPlayerProvider(nullptr) - , _onPauseListener(nullptr) - , _onResumeListener(nullptr) - , _audioIDIndex(0) - , _lazyInitLoop(true) -{ - __callerThreadUtils.setCallerThreadId(std::this_thread::get_id()); - __impl = this; -} - -AudioEngineImpl::~AudioEngineImpl() -{ - if (_audioPlayerProvider != nullptr) - { - delete _audioPlayerProvider; - _audioPlayerProvider = nullptr; - } - - if (_outputMixObject) - { - (*_outputMixObject)->Destroy(_outputMixObject); - } - if (_engineObject) - { - (*_engineObject)->Destroy(_engineObject); - } - - if (_onPauseListener != nullptr) - { - Director::getInstance()->getEventDispatcher()->removeEventListener(_onPauseListener); - } - - if (_onResumeListener != nullptr) - { - Director::getInstance()->getEventDispatcher()->removeEventListener(_onResumeListener); - } - - __impl = nullptr; -} - -bool AudioEngineImpl::init() -{ - bool ret = false; - do{ - - // create engine - auto result = slCreateEngine(&_engineObject, 0, nullptr, 0, nullptr, nullptr); - if(SL_RESULT_SUCCESS != result){ ERRORLOG("create opensl engine fail"); break; } - - // realize the engine - result = (*_engineObject)->Realize(_engineObject, SL_BOOLEAN_FALSE); - if(SL_RESULT_SUCCESS != result){ ERRORLOG("realize the engine fail"); break; } - - // get the engine interface, which is needed in order to create other objects - result = (*_engineObject)->GetInterface(_engineObject, SL_IID_ENGINE, &_engineEngine); - if(SL_RESULT_SUCCESS != result){ ERRORLOG("get the engine interface fail"); break; } - - // create output mix - const SLInterfaceID outputMixIIDs[] = {}; - const SLboolean outputMixReqs[] = {}; - result = (*_engineEngine)->CreateOutputMix(_engineEngine, &_outputMixObject, 0, outputMixIIDs, outputMixReqs); - if(SL_RESULT_SUCCESS != result){ ERRORLOG("create output mix fail"); break; } - - // realize the output mix - result = (*_outputMixObject)->Realize(_outputMixObject, SL_BOOLEAN_FALSE); - if(SL_RESULT_SUCCESS != result){ ERRORLOG("realize the output mix fail"); break; } - - _audioPlayerProvider = new AudioPlayerProvider(_engineEngine, _outputMixObject, getDeviceSampleRate(), getDeviceAudioBufferSizeInFrames(), fdGetter, &__callerThreadUtils); - - _onPauseListener = Director::getInstance()->getEventDispatcher()->addCustomEventListener(EVENT_COME_TO_BACKGROUND, CC_CALLBACK_1(AudioEngineImpl::onEnterBackground, this)); - - _onResumeListener = Director::getInstance()->getEventDispatcher()->addCustomEventListener(EVENT_COME_TO_FOREGROUND, CC_CALLBACK_1(AudioEngineImpl::onEnterForeground, this)); - - ret = true; - }while (false); - - return ret; -} - -void AudioEngineImpl::onEnterBackground(EventCustom* event) -{ - // _audioPlayerProvider->pause() pauses AudioMixer and PcmAudioService, - // but UrlAudioPlayers could not be paused. - if (_audioPlayerProvider != nullptr) - { - _audioPlayerProvider->pause(); - } - - // pause UrlAudioPlayers which are playing. - for (auto&& e : _audioPlayers) - { - auto player = e.second; - if (dynamic_cast(player) != nullptr - && player->getState() == IAudioPlayer::State::PLAYING) - { - _urlAudioPlayersNeedResume.emplace(e.first, player); - player->pause(); - } - } -} - -void AudioEngineImpl::onEnterForeground(EventCustom* event) -{ - // _audioPlayerProvider->resume() resumes AudioMixer and PcmAudioService, - // but UrlAudioPlayers could not be resumed. - if (_audioPlayerProvider != nullptr) - { - _audioPlayerProvider->resume(); - } - - // resume UrlAudioPlayers - for (auto&& iter : _urlAudioPlayersNeedResume) - { - iter.second->resume(); - } - _urlAudioPlayersNeedResume.clear(); -} - -void AudioEngineImpl::setAudioFocusForAllPlayers(bool isFocus) -{ - for (const auto& e : _audioPlayers) - { - e.second->setAudioFocus(isFocus); - } -} - -int AudioEngineImpl::play2d(const std::string &filePath ,bool loop ,float volume) -{ - ALOGV("play2d, _audioPlayers.size=%d", (int)_audioPlayers.size()); - auto audioId = AudioEngine::INVALID_AUDIO_ID; - - do - { - if (_engineEngine == nullptr || _audioPlayerProvider == nullptr) - break; - - auto fullPath = FileUtils::getInstance()->fullPathForFilename(filePath); - - audioId = _audioIDIndex++; - - auto player = _audioPlayerProvider->getAudioPlayer(fullPath); - if (player != nullptr) - { - player->setId(audioId); - _audioPlayers.insert(std::make_pair(audioId, player)); - - player->setPlayEventCallback([this, player, filePath](IAudioPlayer::State state){ - - if (state != IAudioPlayer::State::OVER && state != IAudioPlayer::State::STOPPED) - { - ALOGV("Ignore state: %d", static_cast(state)); - return; - } - - int id = player->getId(); - - ALOGV("Removing player id=%d, state:%d", id, (int)state); - - AudioEngine::remove(id); - if (_audioPlayers.find(id) != _audioPlayers.end()) - { - _audioPlayers.erase(id); - } - if (_urlAudioPlayersNeedResume.find(id) != _urlAudioPlayersNeedResume.end()) - { - _urlAudioPlayersNeedResume.erase(id); - } - - auto iter = _callbackMap.find(id); - if (iter != _callbackMap.end()) - { - if (state == IAudioPlayer::State::OVER) - { - iter->second(id, filePath); - } - _callbackMap.erase(iter); - } - }); - - player->setLoop(loop); - player->setVolume(volume); - player->setAudioFocus(__currentAudioFocus == AUDIOFOCUS_GAIN); - player->play(); - } - else - { - ALOGE("Oops, player is null ..."); - return AudioEngine::INVALID_AUDIO_ID; - } - - AudioEngine::_audioIDInfoMap[audioId].state = AudioEngine::AudioState::PLAYING; - - } while (0); - - return audioId; -} - -void AudioEngineImpl::setVolume(int audioID,float volume) -{ - auto iter = _audioPlayers.find(audioID); - if (iter != _audioPlayers.end()) - { - auto player = iter->second; - player->setVolume(volume); - } -} - -void AudioEngineImpl::setLoop(int audioID, bool loop) -{ - auto iter = _audioPlayers.find(audioID); - if (iter != _audioPlayers.end()) - { - auto player = iter->second; - player->setLoop(loop); - } -} - -void AudioEngineImpl::pause(int audioID) -{ - auto iter = _audioPlayers.find(audioID); - if (iter != _audioPlayers.end()) - { - auto player = iter->second; - player->pause(); - } -} - -void AudioEngineImpl::resume(int audioID) -{ - auto iter = _audioPlayers.find(audioID); - if (iter != _audioPlayers.end()) - { - auto player = iter->second; - player->resume(); - } -} - -void AudioEngineImpl::stop(int audioID) -{ - auto iter = _audioPlayers.find(audioID); - if (iter != _audioPlayers.end()) - { - auto player = iter->second; - player->stop(); - } -} - -void AudioEngineImpl::stopAll() -{ - if (_audioPlayers.empty()) - { - return; - } - - // Create a temporary vector for storing all players since - // p->stop() will trigger _audioPlayers.erase, - // and it will cause a crash as it's already in for loop - std::vector players; - players.reserve(_audioPlayers.size()); - - for (const auto& e : _audioPlayers) - { - players.push_back(e.second); - } - - for (auto p : players) - { - p->stop(); - } -} - -float AudioEngineImpl::getDuration(int audioID) -{ - auto iter = _audioPlayers.find(audioID); - if (iter != _audioPlayers.end()) - { - auto player = iter->second; - return player->getDuration(); - } - return 0.0f; -} - -float AudioEngineImpl::getCurrentTime(int audioID) -{ - auto iter = _audioPlayers.find(audioID); - if (iter != _audioPlayers.end()) - { - auto player = iter->second; - return player->getPosition(); - } - return 0.0f; -} - -bool AudioEngineImpl::setCurrentTime(int audioID, float time) -{ - auto iter = _audioPlayers.find(audioID); - if (iter != _audioPlayers.end()) - { - auto player = iter->second; - return player->setPosition(time); - } - return false; -} - -void AudioEngineImpl::setFinishCallback(int audioID, const std::function &callback) -{ - _callbackMap[audioID] = callback; -} - -void AudioEngineImpl::preload(const std::string& filePath, const std::function& callback) -{ - if (_audioPlayerProvider != nullptr) - { - std::string fullPath = FileUtils::getInstance()->fullPathForFilename(filePath); - _audioPlayerProvider->preloadEffect(fullPath, [callback](bool succeed, PcmData data){ - if (callback != nullptr) - { - callback(succeed); - } - }); - } - else - { - if (callback != nullptr) - { - callback(false); - } - } -} - -void AudioEngineImpl::uncache(const std::string& filePath) -{ - if (_audioPlayerProvider != nullptr) - { - std::string fullPath = FileUtils::getInstance()->fullPathForFilename(filePath); - _audioPlayerProvider->clearPcmCache(fullPath); - } -} - -void AudioEngineImpl::uncacheAll() -{ - if (_audioPlayerProvider != nullptr) - { - _audioPlayerProvider->clearAllPcmCaches(); - } -} - -// It's invoked from javaactivity-android.cpp -void cocos_audioengine_focus_change(int focusChange) -{ - if (focusChange < AUDIOFOCUS_GAIN || focusChange > AUDIOFOCUS_LOST_TRANSIENT_CAN_DUCK) - { - CCLOGERROR("cocos_audioengine_focus_change: unknown value: %d", focusChange); - return; - } - CCLOG("cocos_audioengine_focus_change: %d", focusChange); - __currentAudioFocus = focusChange; - - if (__impl == nullptr) - { - CCLOGWARN("cocos_audioengine_focus_change: AudioEngineImpl isn't ready!"); - return; - } - - if (__currentAudioFocus == AUDIOFOCUS_GAIN) - { - __impl->setAudioFocusForAllPlayers(true); - } - else - { - __impl->setAudioFocusForAllPlayers(false); - } -} - -#endif diff --git a/cocos/audio/android/AudioEngineImpl.cpp b/cocos/audio/android/AudioEngineImpl.cpp new file mode 100644 index 000000000000..943f41a10901 --- /dev/null +++ b/cocos/audio/android/AudioEngineImpl.cpp @@ -0,0 +1,509 @@ +/**************************************************************************** + Copyright (c) 2014-2017 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + Copyright (c) 2018 HALX99. + + 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. + ****************************************************************************/ +#define LOG_TAG "AudioEngine-Android" + +#include "platform/CCPlatformConfig.h" + +#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID +#include "audio/android/log.h" +#include "audio/android/AudioEngineImpl.h" + +#ifdef OPENAL_PLAIN_INCLUDES +#include "alc.h" +#include "alext.h" +#else +#include "AL/alc.h" +#include "AL/alext.h" +#endif +#include "audio/include/AudioEngine.h" +#include "base/CCDirector.h" +#include "base/CCScheduler.h" +#include "platform/CCFileUtils.h" +#include "audio/android/AudioDecoderManager.h" + +// log, CCLOG aren't threadsafe, since we uses sub threads for parsing pcm data, threadsafe log output +// is needed. Define the following macros (ALOGV, ALOGD, ALOGI, ALOGW, ALOGE) for threadsafe log output. + +using namespace cocos2d; +using namespace cocos2d::experimental; + +static ALCdevice *s_ALDevice = nullptr; +static ALCcontext *s_ALContext = nullptr; + +AudioEngineImpl::AudioEngineImpl() +: _lazyInitLoop(true) +, _currentAudioID(0) +, _scheduler(nullptr) +{ + +} + +AudioEngineImpl::~AudioEngineImpl() +{ + if (_scheduler != nullptr) + { + _scheduler->unschedule(CC_SCHEDULE_SELECTOR(AudioEngineImpl::update), this); + } + + if (s_ALContext) { + alDeleteSources(MAX_AUDIOINSTANCES, _alSources); + + _audioCaches.clear(); + + alcMakeContextCurrent(nullptr); + alcDestroyContext(s_ALContext); + s_ALContext = nullptr; + } + + if (s_ALDevice) { + alcCloseDevice(s_ALDevice); + s_ALDevice = nullptr; + } + + AudioDecoderManager::destroy(); +} + +bool AudioEngineImpl::init() +{ + bool ret = false; + do{ + s_ALDevice = alcOpenDevice(nullptr); + + if (s_ALDevice) { + alGetError(); + s_ALContext = alcCreateContext(s_ALDevice, nullptr); + alcMakeContextCurrent(s_ALContext); + + alGenSources(MAX_AUDIOINSTANCES, _alSources); + auto alError = alGetError(); + if(alError != AL_NO_ERROR) + { + ALOGE("%s:generating sources failed! error = %x\n", __FUNCTION__, alError); + break; + } + + for (int i = 0; i < MAX_AUDIOINSTANCES; ++i) { + _alSourceUsed[_alSources[i]] = false; + } + + _scheduler = Director::getInstance()->getScheduler(); + ret = AudioDecoderManager::init(); + ALOGI("OpenAL was initialized successfully!"); + } + }while (false); + + return ret; +} + +AudioCache* AudioEngineImpl::preload(const std::string& filePath, std::function callback) +{ + AudioCache* audioCache = nullptr; + + auto it = _audioCaches.find(filePath); + if (it == _audioCaches.end()) { + audioCache = &_audioCaches[filePath]; + audioCache->_fileFullPath = FileUtils::getInstance()->fullPathForFilename(filePath); + unsigned int cacheId = audioCache->_id; + auto isCacheDestroyed = audioCache->_isDestroyed; + AudioEngine::addTask([audioCache, cacheId, isCacheDestroyed](){ + if (*isCacheDestroyed) + { + ALOGV("AudioCache (id=%u) was destroyed, no need to launch readDataTask.", cacheId); + audioCache->setSkipReadDataTask(true); + return; + } + audioCache->readDataTask(cacheId); + }); + } + else { + audioCache = &it->second; + } + + if (audioCache && callback) + { + audioCache->addLoadCallback(callback); + } + return audioCache; +} + +int AudioEngineImpl::play2d(const std::string &filePath ,bool loop ,float volume) +{ + if (s_ALDevice == nullptr) { + return AudioEngine::INVALID_AUDIO_ID; + } + + bool sourceFlag = false; + ALuint alSource = 0; + for (int i = 0; i < MAX_AUDIOINSTANCES; ++i) { + alSource = _alSources[i]; + + if ( !_alSourceUsed[alSource]) { + sourceFlag = true; + break; + } + } + if(!sourceFlag){ + return AudioEngine::INVALID_AUDIO_ID; + } + + auto player = new (std::nothrow) AudioPlayer; + if (player == nullptr) { + return AudioEngine::INVALID_AUDIO_ID; + } + + player->_alSource = alSource; + player->_loop = loop; + player->_volume = volume; + + auto audioCache = preload(filePath, nullptr); + if (audioCache == nullptr) { + delete player; + return AudioEngine::INVALID_AUDIO_ID; + } + + player->setCache(audioCache); + _threadMutex.lock(); + _audioPlayers[++_currentAudioID] = player; + _threadMutex.unlock(); + + _alSourceUsed[alSource] = true; + + audioCache->addPlayCallback(std::bind(&AudioEngineImpl::_play2d,this,audioCache,_currentAudioID)); + + if (_lazyInitLoop) { + _lazyInitLoop = false; + _scheduler->schedule(CC_SCHEDULE_SELECTOR(AudioEngineImpl::update), this, 0.05f, false); + } + + return _currentAudioID; +} + +void AudioEngineImpl::_play2d(AudioCache *cache, int audioID) +{ + //Note: It may bn in sub thread or main thread :( + if (!*cache->_isDestroyed && cache->_state == AudioCache::State::READY) + { + _threadMutex.lock(); + auto playerIt = _audioPlayers.find(audioID); + if (playerIt != _audioPlayers.end() && playerIt->second->play2d()) { + _scheduler->performFunctionInCocosThread([audioID](){ + + if (AudioEngine::_audioIDInfoMap.find(audioID) != AudioEngine::_audioIDInfoMap.end()) { + AudioEngine::_audioIDInfoMap[audioID].state = AudioEngine::AudioState::PLAYING; + } + }); + } + _threadMutex.unlock(); + } + else + { + ALOGD("AudioEngineImpl::_play2d, cache was destroyed or not ready!"); + auto iter = _audioPlayers.find(audioID); + if (iter != _audioPlayers.end()) + { + iter->second->_removeByAudioEngine = true; + } + } +} + +void AudioEngineImpl::setVolume(int audioID,float volume) +{ + auto player = _audioPlayers[audioID]; + player->_volume = volume; + + if (player->_ready) { + alSourcef(_audioPlayers[audioID]->_alSource, AL_GAIN, volume); + + auto error = alGetError(); + if (error != AL_NO_ERROR) { + ALOGE("%s: audio id = %d, error = %x", __FUNCTION__,audioID,error); + } + } +} + +void AudioEngineImpl::setLoop(int audioID, bool loop) +{ + auto player = _audioPlayers[audioID]; + + if (player->_ready) { + if (player->_streamingSource) { + player->setLoop(loop); + } else { + if (loop) { + alSourcei(player->_alSource, AL_LOOPING, AL_TRUE); + } else { + alSourcei(player->_alSource, AL_LOOPING, AL_FALSE); + } + + auto error = alGetError(); + if (error != AL_NO_ERROR) { + ALOGE("%s: audio id = %d, error = %x", __FUNCTION__,audioID,error); + } + } + } + else { + player->_loop = loop; + } +} + +bool AudioEngineImpl::pause(int audioID) +{ + bool ret = true; + alSourcePause(_audioPlayers[audioID]->_alSource); + + auto error = alGetError(); + if (error != AL_NO_ERROR) { + ret = false; + ALOGE("%s: audio id = %d, error = %x\n", __FUNCTION__,audioID,error); + } + + return ret; +} + +bool AudioEngineImpl::resume(int audioID) +{ + bool ret = true; + alSourcePlay(_audioPlayers[audioID]->_alSource); + + auto error = alGetError(); + if (error != AL_NO_ERROR) { + ret = false; + ALOGE("%s: audio id = %d, error = %x\n", __FUNCTION__,audioID,error); + } + + return ret; +} + +void AudioEngineImpl::stop(int audioID) +{ + auto player = _audioPlayers[audioID]; + player->destroy(); + //Note: Don't set the flag to false here, it should be set in 'update' function. + // Otherwise, the state got from alSourceState may be wrong +// _alSourceUsed[player->_alSource] = false; + + // Call 'update' method to cleanup immediately since the schedule may be cancelled without any notification. + update(0.0f); +} + +void AudioEngineImpl::stopAll() +{ + for(auto&& player : _audioPlayers) + { + player.second->destroy(); + } + //Note: Don't set the flag to false here, it should be set in 'update' function. + // Otherwise, the state got from alSourceState may be wrong +// for(int index = 0; index < MAX_AUDIOINSTANCES; ++index) +// { +// _alSourceUsed[_alSources[index]] = false; +// } + + // Call 'update' method to cleanup immediately since the schedule may be cancelled without any notification. + update(0.0f); +} + +float AudioEngineImpl::getDuration(int audioID) +{ + auto player = _audioPlayers[audioID]; + if(player->_ready){ + return player->_audioCache->_duration; + } else { + return AudioEngine::TIME_UNKNOWN; + } +} + +float AudioEngineImpl::getCurrentTime(int audioID) +{ + float ret = 0.0f; + auto player = _audioPlayers[audioID]; + if(player->_ready){ + if (player->_streamingSource) { + ret = player->getTime(); + } else { + alGetSourcef(player->_alSource, AL_SEC_OFFSET, &ret); + + auto error = alGetError(); + if (error != AL_NO_ERROR) { + ALOGE("%s, audio id:%d,error code:%x", __FUNCTION__,audioID,error); + } + } + } + + return ret; +} + +bool AudioEngineImpl::setCurrentTime(int audioID, float time) +{ + bool ret = false; + auto player = _audioPlayers[audioID]; + + do { + if (!player->_ready) { + break; + } + + if (player->_streamingSource) { + ret = player->setTime(time); + break; + } + else { + if (player->_audioCache->_framesRead != player->_audioCache->_totalFrames && + (time * player->_audioCache->_sampleRate) > player->_audioCache->_framesRead) { + ALOGE("%s: audio id = %d", __FUNCTION__,audioID); + break; + } + + alSourcef(player->_alSource, AL_SEC_OFFSET, time); + + auto error = alGetError(); + if (error != AL_NO_ERROR) { + ALOGE("%s: audio id = %d, error = %x", __FUNCTION__,audioID,error); + } + ret = true; + } + } while (0); + + return ret; +} + +void AudioEngineImpl::setFinishCallback(int audioID, const std::function &callback) +{ + _audioPlayers[audioID]->_finishCallbak = callback; +} + +void AudioEngineImpl::update(float dt) +{ + int audioID; + AudioPlayer* player; + ALuint alSource; + +// ALOGV("AudioPlayer count: %d", (int)_audioPlayers.size()); + _threadMutex.lock(); + + for (auto it = _audioPlayers.begin(); it != _audioPlayers.end(); ) { + audioID = it->first; + player = it->second; + alSource = player->_alSource; + + if (player->_removeByAudioEngine) + { + AudioEngine::remove(audioID); + + it = _audioPlayers.erase(it); + delete player; + _alSourceUsed[alSource] = false; + } + else if (player->_ready && player->isFinished()) { + + std::string filePath; + if (player->_finishCallbak) { + auto& audioInfo = AudioEngine::_audioIDInfoMap[audioID]; + filePath = *audioInfo.filePath; + } + + AudioEngine::remove(audioID); + + it = _audioPlayers.erase(it); + + if (player->_finishCallbak) { +#if !defined(_MSC_VER) || _MSC_VER >= 1900 + _finishCallbacks.push_back([finishCallback = std::move(player->_finishCallbak), audioID, filePath = std::move(filePath)] { + finishCallback(audioID, filePath); //FIXME: callback will delay 50ms + }); +#else + auto finishCallback = std::move(player->_finishCallbak); + _finishCallbacks.push_back([=]{ + finishCallback(audioID, filePath); //FIXME: callback will delay 50ms + }); +#endif + } + delete player; + _alSourceUsed[alSource] = false; + } + else{ + ++it; + } + } + + if(_audioPlayers.empty()) { + _lazyInitLoop = true; + _scheduler->unschedule(CC_SCHEDULE_SELECTOR(AudioEngineImpl::update), this); + } + _threadMutex.unlock(); + + if (!_finishCallbacks.empty()) { + for (auto& finishCallback : _finishCallbacks) + finishCallback(); + _finishCallbacks.clear(); + } +} + +void AudioEngineImpl::uncache(const std::string &filePath) +{ + _audioCaches.erase(filePath); +} + +void AudioEngineImpl::uncacheAll() +{ + _audioCaches.clear(); +} + +// It's invoked from javaactivity-android.cpp +// Audio focus values synchronized with which in cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxActivity.java +static const int AUDIOFOCUS_GAIN = 0; +static const int AUDIOFOCUS_LOST = 1; +static const int AUDIOFOCUS_LOST_TRANSIENT = 2; +static const int AUDIOFOCUS_LOST_TRANSIENT_CAN_DUCK = 3; +void cocos_audioengine_focus_change(int focusChange) +{ + if (focusChange < AUDIOFOCUS_GAIN || focusChange > AUDIOFOCUS_LOST_TRANSIENT_CAN_DUCK) + { + CCLOGERROR("cocos_audioengine_focus_change: unknown value: %d", focusChange); + return; + } + CCLOG("cocos_audioengine_focus_change: %d", focusChange); + + // TODO: + //~ __currentAudioFocus = focusChange; + + //~ if (__impl == nullptr) + //~ { + //~ CCLOGWARN("cocos_audioengine_focus_change: AudioEngineImpl isn't ready!"); + //~ return; + //~ } + + //~ if (__currentAudioFocus == AUDIOFOCUS_GAIN) + //~ { + //~ __impl->setAudioFocusForAllPlayers(true); + //~ } + //~ else + //~ { + //~ __impl->setAudioFocusForAllPlayers(false); + //~ } +} + +#endif diff --git a/cocos/audio/android/AudioEngine-inl.h b/cocos/audio/android/AudioEngineImpl.h similarity index 60% rename from cocos/audio/android/AudioEngine-inl.h rename to cocos/audio/android/AudioEngineImpl.h index 9b68d43d25b3..530cf0a50cc2 100644 --- a/cocos/audio/android/AudioEngine-inl.h +++ b/cocos/audio/android/AudioEngineImpl.h @@ -1,6 +1,7 @@ /**************************************************************************** - Copyright (c) 2014-2016 Chukong Technologies Inc. + Copyright (c) 2014-2017 Chukong Technologies Inc. Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + Copyright (c) 2018 HALX99. http://www.cocos2d-x.org @@ -21,36 +22,29 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ****************************************************************************/ -#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID + ****************************************************************************/ +#pragma once +#ifndef __AUDIO_ENGINE_IMPL_H_ +#define __AUDIO_ENGINE_IMPL_H_ -#ifndef __AUDIO_ENGINE_INL_H_ -#define __AUDIO_ENGINE_INL_H_ +#include "platform/CCPlatformConfig.h" +#include "base/CCScheduler.h" +#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID -#include -#include -#include #include -#include "base/CCRef.h" -#include "base/ccUtils.h" - -#define MAX_AUDIOINSTANCES 24 -#define ERRORLOG(msg) log("fun:%s,line:%d,msg:%s",__func__,__LINE__,#msg) +#include "base/CCRef.h" +#include "audio/android/AudioCache.h" +#include "audio/android/AudioPlayer.h" NS_CC_BEGIN -class EventCustom; -class EventListener; +class Scheduler; namespace experimental { +#define MAX_AUDIOINSTANCES 32 -class IAudioPlayer; -class AudioPlayerProvider; - -class AudioEngineImpl; - -class AudioEngineImpl : public cocos2d::Ref +class CC_DLL AudioEngineImpl : public cocos2d::Ref { public: AudioEngineImpl(); @@ -60,8 +54,8 @@ class AudioEngineImpl : public cocos2d::Ref int play2d(const std::string &fileFullPath ,bool loop ,float volume); void setVolume(int audioID,float volume); void setLoop(int audioID, bool loop); - void pause(int audioID); - void resume(int audioID); + bool pause(int audioID); + bool resume(int audioID); void stop(int audioID); void stopAll(); float getDuration(int audioID); @@ -71,39 +65,34 @@ class AudioEngineImpl : public cocos2d::Ref void uncache(const std::string& filePath); void uncacheAll(); - void preload(const std::string& filePath, const std::function& callback); + AudioCache* preload(const std::string& filePath, std::function callback); + void update(float dt); - void setAudioFocusForAllPlayers(bool isFocus); private: + void _play2d(AudioCache *cache, int audioID); - void onEnterBackground(EventCustom* event); - void onEnterForeground(EventCustom* event); + ALuint _alSources[MAX_AUDIOINSTANCES]; - // engine interfaces - SLObjectItf _engineObject; - SLEngineItf _engineEngine; + //source,used + std::unordered_map _alSourceUsed; - // output mix interfaces - SLObjectItf _outputMixObject; + //filePath,bufferInfo + std::unordered_map _audioCaches; //audioID,AudioInfo - std::unordered_map _audioPlayers; - std::unordered_map> _callbackMap; + std::unordered_map _audioPlayers; + std::mutex _threadMutex; - // UrlAudioPlayers which need to resumed while entering foreground - std::unordered_map _urlAudioPlayersNeedResume; + //finish callbacks + std::vector> _finishCallbacks; - AudioPlayerProvider* _audioPlayerProvider; - EventListener* _onPauseListener; - EventListener* _onResumeListener; - - int _audioIDIndex; - bool _lazyInitLoop; -}; -#endif // __AUDIO_ENGINE_INL_H_ - } + int _currentAudioID; + Scheduler* _scheduler; +}; +} NS_CC_END - +#endif // __AUDIO_ENGINE_INL_H_ #endif + diff --git a/cocos/audio/android/AudioMacros.h b/cocos/audio/android/AudioMacros.h new file mode 100644 index 000000000000..70a435bf25bb --- /dev/null +++ b/cocos/audio/android/AudioMacros.h @@ -0,0 +1,65 @@ +/**************************************************************************** + Copyright (c) 2016-2017 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + Copyright (c) 2018 HALX99. + + 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. + ****************************************************************************/ + +#pragma once + +#define QUEUEBUFFER_NUM (3) +#define QUEUEBUFFER_TIME_STEP (0.05f) + +// log, CCLOG aren't threadsafe, since we uses sub threads for parsing pcm data, threadsafe log output +// is needed. Define the following macros (ALOGV, ALOGD, ALOGI, ALOGW, ALOGE) for threadsafe log output. + +//FIXME:Move the definition of the following macros to a separated file. + +#define QUOTEME_(x) #x +#define QUOTEME(x) QUOTEME_(x) + +#if defined(COCOS2D_DEBUG) && COCOS2D_DEBUG > 0 +#define CHECK_AL_ERROR_DEBUG() \ +do { \ + GLenum __error = alGetError(); \ + if (__error) { \ + ALOGE("OpenAL error 0x%04X in %s %s %d\n", __error, __FILE__, __FUNCTION__, __LINE__); \ + } \ +} while (false) +#else +#define CHECK_AL_ERROR_DEBUG() +#endif + +#define BREAK_IF(condition) \ + if (!!(condition)) { \ + break; \ + } + +#define BREAK_IF_ERR_LOG(condition, fmt, ...) \ + if (!!(condition)) { \ + ALOGE("(" QUOTEME(condition) ") failed, message: " fmt, ##__VA_ARGS__); \ + break; \ + } + +#if !defined(int) +#define int int +#endif diff --git a/cocos/audio/android/AudioMixer.cpp b/cocos/audio/android/AudioMixer.cpp deleted file mode 100644 index 2b9e1baacf55..000000000000 --- a/cocos/audio/android/AudioMixer.cpp +++ /dev/null @@ -1,2101 +0,0 @@ -/* -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#define LOG_TAG "AudioMixer" -#define LOG_NDEBUG 1 - -#include -#include -#include -#include -#include - -#include "audio/android/audio.h" -#include "audio/android/audio_utils/include/audio_utils/primitives.h" - -#include "audio/android/AudioMixerOps.h" -#include "audio/android/AudioMixer.h" - -// The FCC_2 macro refers to the Fixed Channel Count of 2 for the legacy integer mixer. -#ifndef FCC_2 -#define FCC_2 2 -#endif - -// Look for MONO_HACK for any Mono hack involving legacy mono channel to -// stereo channel conversion. - -/* VERY_VERY_VERBOSE_LOGGING will show exactly which process hook and track hook is - * being used. This is a considerable amount of log spam, so don't enable unless you - * are verifying the hook based code. - */ -//#define VERY_VERY_VERBOSE_LOGGING -#ifdef VERY_VERY_VERBOSE_LOGGING -#define ALOGVV ALOGV -//define ALOGVV printf // for test-mixer.cpp -#else -#define ALOGVV(a...) do { } while (0) -#endif - -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) -#endif - -// TODO: Move these macro/inlines to a header file. -template -static inline -T max(const T& x, const T& y) { - return x > y ? x : y; -} - -// Set kUseNewMixer to true to use the new mixer engine always. Otherwise the -// original code will be used for stereo sinks, the new mixer for multichannel. -static const bool kUseNewMixer = false; - -// Set kUseFloat to true to allow floating input into the mixer engine. -// If kUseNewMixer is false, this is ignored or may be overridden internally -// because of downmix/upmix support. -static const bool kUseFloat = false; - -// Set to default copy buffer size in frames for input processing. -static const size_t kCopyBufferFrameCount = 256; - -namespace cocos2d { namespace experimental { - -// ---------------------------------------------------------------------------- - -template -T min(const T& a, const T& b) -{ - return a < b ? a : b; -} - -// ---------------------------------------------------------------------------- - -// Ensure mConfiguredNames bitmask is initialized properly on all architectures. -// The value of 1 << x is undefined in C when x >= 32. - -AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTracks) - : mTrackNames(0), mConfiguredNames((maxNumTracks >= 32 ? 0 : 1 << maxNumTracks) - 1), - mSampleRate(sampleRate) -{ - ALOGVV("AudioMixer constructed, frameCount: %d, sampleRate: %d", (int)frameCount, (int)sampleRate); - ALOG_ASSERT(maxNumTracks <= MAX_NUM_TRACKS, "maxNumTracks %u > MAX_NUM_TRACKS %u", - maxNumTracks, MAX_NUM_TRACKS); - - // AudioMixer is not yet capable of more than 32 active track inputs - ALOG_ASSERT(32 >= MAX_NUM_TRACKS, "bad MAX_NUM_TRACKS %d", MAX_NUM_TRACKS); - - pthread_once(&sOnceControl, &sInitRoutine); - - mState.enabledTracks= 0; - mState.needsChanged = 0; - mState.frameCount = frameCount; - mState.hook = process__nop; - mState.outputTemp = NULL; - mState.resampleTemp = NULL; -//cjh mState.mLog = &mDummyLog; - // mState.reserved - - // FIXME Most of the following initialization is probably redundant since - // tracks[i] should only be referenced if (mTrackNames & (1 << i)) != 0 - // and mTrackNames is initially 0. However, leave it here until that's verified. - track_t* t = mState.tracks; - for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) { - t->resampler = NULL; -//cjh t->downmixerBufferProvider = NULL; -// t->mReformatBufferProvider = NULL; -// t->mTimestretchBufferProvider = NULL; - t++; - } - -} - -AudioMixer::~AudioMixer() -{ - track_t* t = mState.tracks; - for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) { - delete t->resampler; -//cjh delete t->downmixerBufferProvider; -// delete t->mReformatBufferProvider; -// delete t->mTimestretchBufferProvider; - t++; - } - delete [] mState.outputTemp; - delete [] mState.resampleTemp; -} - -//cjh void AudioMixer::setLog(NBLog::Writer *log) -//{ -// mState.mLog = log; -//} - -static inline audio_format_t selectMixerInFormat(audio_format_t inputFormat __unused) { - return kUseFloat && kUseNewMixer ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; -} - -int AudioMixer::getTrackName(audio_channel_mask_t channelMask, - audio_format_t format, int sessionId) -{ - if (!isValidPcmTrackFormat(format)) { - ALOGE("AudioMixer::getTrackName invalid format (%#x)", format); - return -1; - } - uint32_t names = (~mTrackNames) & mConfiguredNames; - if (names != 0) { - int n = __builtin_ctz(names); - ALOGV("add track (%d)", n); - // assume default parameters for the track, except where noted below - track_t* t = &mState.tracks[n]; - t->needs = 0; - - // Integer volume. - // Currently integer volume is kept for the legacy integer mixer. - // Will be removed when the legacy mixer path is removed. - t->volume[0] = UNITY_GAIN_INT; - t->volume[1] = UNITY_GAIN_INT; - t->prevVolume[0] = UNITY_GAIN_INT << 16; - t->prevVolume[1] = UNITY_GAIN_INT << 16; - t->volumeInc[0] = 0; - t->volumeInc[1] = 0; - t->auxLevel = 0; - t->auxInc = 0; - t->prevAuxLevel = 0; - - // Floating point volume. - t->mVolume[0] = UNITY_GAIN_FLOAT; - t->mVolume[1] = UNITY_GAIN_FLOAT; - t->mPrevVolume[0] = UNITY_GAIN_FLOAT; - t->mPrevVolume[1] = UNITY_GAIN_FLOAT; - t->mVolumeInc[0] = 0.; - t->mVolumeInc[1] = 0.; - t->mAuxLevel = 0.; - t->mAuxInc = 0.; - t->mPrevAuxLevel = 0.; - - // no initialization needed - // t->frameCount - t->channelCount = audio_channel_count_from_out_mask(channelMask); - t->enabled = false; - ALOGV_IF(audio_channel_mask_get_bits(channelMask) != AUDIO_CHANNEL_OUT_STEREO, - "Non-stereo channel mask: %d\n", channelMask); - t->channelMask = channelMask; - t->sessionId = sessionId; - // setBufferProvider(name, AudioBufferProvider *) is required before enable(name) - t->bufferProvider = NULL; - t->buffer.raw = NULL; - // no initialization needed - // t->buffer.frameCount - t->hook = NULL; - t->in = NULL; - t->resampler = NULL; - t->sampleRate = mSampleRate; - // setParameter(name, TRACK, MAIN_BUFFER, mixBuffer) is required before enable(name) - t->mainBuffer = NULL; - t->auxBuffer = NULL; - t->mInputBufferProvider = NULL; -//cjh t->mReformatBufferProvider = NULL; -// t->downmixerBufferProvider = NULL; -// t->mPostDownmixReformatBufferProvider = NULL; -// t->mTimestretchBufferProvider = NULL; - t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT; - t->mFormat = format; - t->mMixerInFormat = selectMixerInFormat(format); - t->mDownmixRequiresFormat = AUDIO_FORMAT_INVALID; // no format required - t->mMixerChannelMask = audio_channel_mask_from_representation_and_bits( - AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO); - t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask); - ALOGVV("t->mMixerChannelCount: %d", t->mMixerChannelCount); - t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT; - // Check the downmixing (or upmixing) requirements. - status_t status = t->prepareForDownmix(); - if (status != OK) { - ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask); - return -1; - } - // prepareForDownmix() may change mDownmixRequiresFormat - ALOGVV("mMixerFormat:%#x mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat); - t->prepareForReformat(); - mTrackNames |= 1 << n; - ALOGVV("getTrackName return: %d", TRACK0 + n); - return TRACK0 + n; - } - ALOGE("AudioMixer::getTrackName out of available tracks"); - return -1; -} - -void AudioMixer::invalidateState(uint32_t mask) -{ - if (mask != 0) { - mState.needsChanged |= mask; - mState.hook = process__validate; - } - } - -// Called when channel masks have changed for a track name -// TODO: Fix DownmixerBufferProvider not to (possibly) change mixer input format, -// which will simplify this logic. -bool AudioMixer::setChannelMasks(int name, - audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) { - track_t &track = mState.tracks[name]; - ALOGVV("AudioMixer::setChannelMask ..."); - if (trackChannelMask == track.channelMask - && mixerChannelMask == track.mMixerChannelMask) { - ALOGVV("No need to change channel mask ..."); - return false; // no need to change - } - // always recompute for both channel masks even if only one has changed. - const uint32_t trackChannelCount = audio_channel_count_from_out_mask(trackChannelMask); - const uint32_t mixerChannelCount = audio_channel_count_from_out_mask(mixerChannelMask); - const bool mixerChannelCountChanged = track.mMixerChannelCount != mixerChannelCount; - - ALOG_ASSERT((trackChannelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) - && trackChannelCount - && mixerChannelCount); - track.channelMask = trackChannelMask; - track.channelCount = trackChannelCount; - track.mMixerChannelMask = mixerChannelMask; - track.mMixerChannelCount = mixerChannelCount; - - // channel masks have changed, does this track need a downmixer? - // update to try using our desired format (if we aren't already using it) - const audio_format_t prevDownmixerFormat = track.mDownmixRequiresFormat; - const status_t status = mState.tracks[name].prepareForDownmix(); - ALOGE_IF(status != OK, - "prepareForDownmix error %d, track channel mask %#x, mixer channel mask %#x", - status, track.channelMask, track.mMixerChannelMask); - - if (prevDownmixerFormat != track.mDownmixRequiresFormat) { - track.prepareForReformat(); // because of downmixer, track format may change! - } - - if (track.resampler && mixerChannelCountChanged) { - // resampler channels may have changed. - const uint32_t resetToSampleRate = track.sampleRate; - delete track.resampler; - track.resampler = NULL; - track.sampleRate = mSampleRate; // without resampler, track rate is device sample rate. - // recreate the resampler with updated format, channels, saved sampleRate. - track.setResampler(resetToSampleRate /*trackSampleRate*/, mSampleRate /*devSampleRate*/); - } - return true; -} - -void AudioMixer::track_t::unprepareForDownmix() { - ALOGV("AudioMixer::unprepareForDownmix(%p)", this); - - mDownmixRequiresFormat = AUDIO_FORMAT_INVALID; -//cjh if (downmixerBufferProvider != NULL) { -// // this track had previously been configured with a downmixer, delete it -// ALOGV(" deleting old downmixer"); -// delete downmixerBufferProvider; -// downmixerBufferProvider = NULL; -// reconfigureBufferProviders(); -// } else - { - ALOGV(" nothing to do, no downmixer to delete"); - } -} - -status_t AudioMixer::track_t::prepareForDownmix() -{ - ALOGV("AudioMixer::prepareForDownmix(%p) with mask 0x%x", - this, channelMask); - - // discard the previous downmixer if there was one - unprepareForDownmix(); - // MONO_HACK Only remix (upmix or downmix) if the track and mixer/device channel masks - // are not the same and not handled internally, as mono -> stereo currently is. - if (channelMask == mMixerChannelMask - || (channelMask == AUDIO_CHANNEL_OUT_MONO - && mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO)) { - return NO_ERROR; - } - // DownmixerBufferProvider is only used for position masks. -//cjh if (audio_channel_mask_get_representation(channelMask) -// == AUDIO_CHANNEL_REPRESENTATION_POSITION -// && DownmixerBufferProvider::isMultichannelCapable()) { -// DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(channelMask, -// mMixerChannelMask, -// AUDIO_FORMAT_PCM_16_BIT /* TODO: use mMixerInFormat, now only PCM 16 */, -// sampleRate, sessionId, kCopyBufferFrameCount); -// -// if (pDbp->isValid()) { // if constructor completed properly -// mDownmixRequiresFormat = AUDIO_FORMAT_PCM_16_BIT; // PCM 16 bit required for downmix -// downmixerBufferProvider = pDbp; -// reconfigureBufferProviders(); -// return NO_ERROR; -// } -// delete pDbp; -// } -// -// // Effect downmixer does not accept the channel conversion. Let's use our remixer. -// RemixBufferProvider* pRbp = new RemixBufferProvider(channelMask, -// mMixerChannelMask, mMixerInFormat, kCopyBufferFrameCount); -// // Remix always finds a conversion whereas Downmixer effect above may fail. -// downmixerBufferProvider = pRbp; -// reconfigureBufferProviders(); - return NO_ERROR; -} - -void AudioMixer::track_t::unprepareForReformat() { - ALOGV("AudioMixer::unprepareForReformat(%p)", this); - bool requiresReconfigure = false; -//cjh if (mReformatBufferProvider != NULL) { -// delete mReformatBufferProvider; -// mReformatBufferProvider = NULL; -// requiresReconfigure = true; -// } -// if (mPostDownmixReformatBufferProvider != NULL) { -// delete mPostDownmixReformatBufferProvider; -// mPostDownmixReformatBufferProvider = NULL; -// requiresReconfigure = true; -// } - if (requiresReconfigure) { - reconfigureBufferProviders(); - } -} - -status_t AudioMixer::track_t::prepareForReformat() -{ - ALOGV("AudioMixer::prepareForReformat(%p) with format %#x", this, mFormat); - // discard previous reformatters - unprepareForReformat(); - // only configure reformatters as needed - const audio_format_t targetFormat = mDownmixRequiresFormat != AUDIO_FORMAT_INVALID - ? mDownmixRequiresFormat : mMixerInFormat; - bool requiresReconfigure = false; -//cjh if (mFormat != targetFormat) { -// mReformatBufferProvider = new ReformatBufferProvider( -// audio_channel_count_from_out_mask(channelMask), -// mFormat, -// targetFormat, -// kCopyBufferFrameCount); -// requiresReconfigure = true; -// } -// if (targetFormat != mMixerInFormat) { -// mPostDownmixReformatBufferProvider = new ReformatBufferProvider( -// audio_channel_count_from_out_mask(mMixerChannelMask), -// targetFormat, -// mMixerInFormat, -// kCopyBufferFrameCount); -// requiresReconfigure = true; -// } - if (requiresReconfigure) { - reconfigureBufferProviders(); - } - ALOGVV("prepareForReformat return ..."); - return NO_ERROR; -} - -void AudioMixer::track_t::reconfigureBufferProviders() -{ - bufferProvider = mInputBufferProvider; -//cjh if (mReformatBufferProvider) { -// mReformatBufferProvider->setBufferProvider(bufferProvider); -// bufferProvider = mReformatBufferProvider; -// } -// if (downmixerBufferProvider) { -// downmixerBufferProvider->setBufferProvider(bufferProvider); -// bufferProvider = downmixerBufferProvider; -// } -// if (mPostDownmixReformatBufferProvider) { -// mPostDownmixReformatBufferProvider->setBufferProvider(bufferProvider); -// bufferProvider = mPostDownmixReformatBufferProvider; -// } -// if (mTimestretchBufferProvider) { -// mTimestretchBufferProvider->setBufferProvider(bufferProvider); -// bufferProvider = mTimestretchBufferProvider; -// } -} - -void AudioMixer::deleteTrackName(int name) -{ - ALOGV("AudioMixer::deleteTrackName(%d)", name); - name -= TRACK0; - ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name); - ALOGV("deleteTrackName(%d)", name); - track_t& track(mState.tracks[ name ]); - if (track.enabled) { - track.enabled = false; - invalidateState(1< AudioMixer::UNITY_GAIN_FLOAT) { - newVolume = AudioMixer::UNITY_GAIN_FLOAT; - } - break; - } - } - - // set floating point volume ramp - if (ramp != 0) { - // when the ramp completes, *pPrevVolume is set to *pSetVolume, so there - // is no computational mismatch; hence equality is checked here. - ALOGD_IF(*pPrevVolume != *pSetVolume, "previous float ramp hasn't finished," - " prev:%f set_to:%f", *pPrevVolume, *pSetVolume); - const float inc = (newVolume - *pPrevVolume) / ramp; // could be inf, nan, subnormal - const float maxv = max(newVolume, *pPrevVolume); // could be inf, cannot be nan, subnormal - - if (isnormal(inc) // inc must be a normal number (no subnormals, infinite, nan) - && maxv + inc != maxv) { // inc must make forward progress - *pVolumeInc = inc; - // ramp is set now. - // Note: if newVolume is 0, then near the end of the ramp, - // it may be possible that the ramped volume may be subnormal or - // temporarily negative by a small amount or subnormal due to floating - // point inaccuracies. - } else { - ramp = 0; // ramp not allowed - } - } - - // compute and check integer volume, no need to check negative values - // The integer volume is limited to "unity_gain" to avoid wrapping and other - // audio artifacts, so it never reaches the range limit of U4.28. - // We safely use signed 16 and 32 bit integers here. - const float scaledVolume = newVolume * AudioMixer::UNITY_GAIN_INT; // not neg, subnormal, nan - const int32_t intVolume = (scaledVolume >= (float)AudioMixer::UNITY_GAIN_INT) ? - AudioMixer::UNITY_GAIN_INT : (int32_t)scaledVolume; - - // set integer volume ramp - if (ramp != 0) { - // integer volume is U4.12 (to use 16 bit multiplies), but ramping uses U4.28. - // when the ramp completes, *pIntPrevVolume is set to *pIntSetVolume << 16, so there - // is no computational mismatch; hence equality is checked here. - ALOGD_IF(*pIntPrevVolume != *pIntSetVolume << 16, "previous int ramp hasn't finished," - " prev:%d set_to:%d", *pIntPrevVolume, *pIntSetVolume << 16); - const int32_t inc = ((intVolume << 16) - *pIntPrevVolume) / ramp; - - if (inc != 0) { // inc must make forward progress - *pIntVolumeInc = inc; - } else { - ramp = 0; // ramp not allowed - } - } - - // if no ramp, or ramp not allowed, then clear float and integer increments - if (ramp == 0) { - *pVolumeInc = 0; - *pPrevVolume = newVolume; - *pIntVolumeInc = 0; - *pIntPrevVolume = intVolume << 16; - } - *pSetVolume = newVolume; - *pIntSetVolume = intVolume; - return true; -} - -void AudioMixer::setParameter(int name, int target, int param, void *value) -{ - name -= TRACK0; - ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name); - track_t& track = mState.tracks[name]; - - int valueInt = static_cast(reinterpret_cast(value)); - int32_t *valueBuf = reinterpret_cast(value); - - switch (target) { - - case TRACK: - switch (param) { - case CHANNEL_MASK: { - const audio_channel_mask_t trackChannelMask = - static_cast(valueInt); - if (setChannelMasks(name, trackChannelMask, track.mMixerChannelMask)) { - ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", trackChannelMask); - invalidateState(1 << name); - } - } break; - case MAIN_BUFFER: - if (track.mainBuffer != valueBuf) { - track.mainBuffer = valueBuf; - ALOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf); - invalidateState(1 << name); - } - break; - case AUX_BUFFER: - if (track.auxBuffer != valueBuf) { - track.auxBuffer = valueBuf; - ALOGV("setParameter(TRACK, AUX_BUFFER, %p)", valueBuf); - invalidateState(1 << name); - } - break; - case FORMAT: { - audio_format_t format = static_cast(valueInt); - if (track.mFormat != format) { - ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format); - track.mFormat = format; - ALOGV("setParameter(TRACK, FORMAT, %#x)", format); - track.prepareForReformat(); - invalidateState(1 << name); - } - } break; - // FIXME do we want to support setting the downmix type from AudioMixerController? - // for a specific track? or per mixer? - /* case DOWNMIX_TYPE: - break */ - case MIXER_FORMAT: { - audio_format_t format = static_cast(valueInt); - if (track.mMixerFormat != format) { - track.mMixerFormat = format; - ALOGV("setParameter(TRACK, MIXER_FORMAT, %#x)", format); - } - } break; - case MIXER_CHANNEL_MASK: { - const audio_channel_mask_t mixerChannelMask = - static_cast(valueInt); - if (setChannelMasks(name, track.channelMask, mixerChannelMask)) { - ALOGV("setParameter(TRACK, MIXER_CHANNEL_MASK, %#x)", mixerChannelMask); - invalidateState(1 << name); - } - } break; - default: - LOG_ALWAYS_FATAL("setParameter track: bad param %d", param); - } - break; - - case RESAMPLE: - switch (param) { - case SAMPLE_RATE: - ALOG_ASSERT(valueInt > 0, "bad sample rate %d", valueInt); - if (track.setResampler(uint32_t(valueInt), mSampleRate)) { - ALOGV("setParameter(RESAMPLE, SAMPLE_RATE, %u)", - uint32_t(valueInt)); - invalidateState(1 << name); - } - break; - case RESET: - track.resetResampler(); - invalidateState(1 << name); - break; - case REMOVE: - delete track.resampler; - track.resampler = NULL; - track.sampleRate = mSampleRate; - invalidateState(1 << name); - break; - default: - LOG_ALWAYS_FATAL("setParameter resample: bad param %d", param); - } - break; - - case RAMP_VOLUME: - case VOLUME: - switch (param) { - case AUXLEVEL: - if (setVolumeRampVariables(*reinterpret_cast(value), - target == RAMP_VOLUME ? mState.frameCount : 0, - &track.auxLevel, &track.prevAuxLevel, &track.auxInc, - &track.mAuxLevel, &track.mPrevAuxLevel, &track.mAuxInc)) { - ALOGV("setParameter(%s, AUXLEVEL: %04x)", - target == VOLUME ? "VOLUME" : "RAMP_VOLUME", track.auxLevel); - invalidateState(1 << name); - } - break; - default: - if ((unsigned)param >= VOLUME0 && (unsigned)param < VOLUME0 + MAX_NUM_VOLUMES) { - if (setVolumeRampVariables(*reinterpret_cast(value), - target == RAMP_VOLUME ? mState.frameCount : 0, - &track.volume[param - VOLUME0], &track.prevVolume[param - VOLUME0], - &track.volumeInc[param - VOLUME0], - &track.mVolume[param - VOLUME0], &track.mPrevVolume[param - VOLUME0], - &track.mVolumeInc[param - VOLUME0])) { - ALOGV("setParameter(%s, VOLUME%d: %04x)", - target == VOLUME ? "VOLUME" : "RAMP_VOLUME", param - VOLUME0, - track.volume[param - VOLUME0]); - invalidateState(1 << name); - } - } else { - LOG_ALWAYS_FATAL("setParameter volume: bad param %d", param); - } - } - break; - case TIMESTRETCH: - switch (param) { - case PLAYBACK_RATE: { - const AudioPlaybackRate *playbackRate = - reinterpret_cast(value); - ALOGW_IF(!isAudioPlaybackRateValid(*playbackRate), - "bad parameters speed %f, pitch %f",playbackRate->mSpeed, - playbackRate->mPitch); - if (track.setPlaybackRate(*playbackRate)) { - ALOGV("setParameter(TIMESTRETCH, PLAYBACK_RATE, STRETCH_MODE, FALLBACK_MODE " - "%f %f %d %d", - playbackRate->mSpeed, - playbackRate->mPitch, - playbackRate->mStretchMode, - playbackRate->mFallbackMode); - // invalidateState(1 << name); - } - } break; - default: - LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param); - } - break; - - default: - LOG_ALWAYS_FATAL("setParameter: bad target %d", target); - } -} - -bool AudioMixer::track_t::setResampler(uint32_t trackSampleRate, uint32_t devSampleRate) -{ - if (trackSampleRate != devSampleRate || resampler != NULL) { - if (sampleRate != trackSampleRate) { - sampleRate = trackSampleRate; - if (resampler == NULL) { - ALOGV("Creating resampler from track %d Hz to device %d Hz", - trackSampleRate, devSampleRate); - AudioResampler::src_quality quality; - // force lowest quality level resampler if use case isn't music or video - // FIXME this is flawed for dynamic sample rates, as we choose the resampler - // quality level based on the initial ratio, but that could change later. - // Should have a way to distinguish tracks with static ratios vs. dynamic ratios. -//cjh if (isMusicRate(trackSampleRate)) { - quality = AudioResampler::DEFAULT_QUALITY; -//cjh } else { -// quality = AudioResampler::DYN_LOW_QUALITY; -// } - - // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer - // but if none exists, it is the channel count (1 for mono). - const int resamplerChannelCount = false/*downmixerBufferProvider != NULL*/ - ? mMixerChannelCount : channelCount; - ALOGVV("Creating resampler:" - " format(%#x) channels(%d) devSampleRate(%u) quality(%d)\n", - mMixerInFormat, resamplerChannelCount, devSampleRate, quality); - resampler = AudioResampler::create( - mMixerInFormat, - resamplerChannelCount, - devSampleRate, quality); - resampler->setLocalTimeFreq(sLocalTimeFreq); - } - return true; - } - } - return false; -} - -bool AudioMixer::track_t::setPlaybackRate(const AudioPlaybackRate &playbackRate) -{ -//cjh if ((mTimestretchBufferProvider == NULL && -// fabs(playbackRate.mSpeed - mPlaybackRate.mSpeed) < AUDIO_TIMESTRETCH_SPEED_MIN_DELTA && -// fabs(playbackRate.mPitch - mPlaybackRate.mPitch) < AUDIO_TIMESTRETCH_PITCH_MIN_DELTA) || -// isAudioPlaybackRateEqual(playbackRate, mPlaybackRate)) { -// return false; -// } - mPlaybackRate = playbackRate; -// if (mTimestretchBufferProvider == NULL) { -// // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer -// // but if none exists, it is the channel count (1 for mono). -// const int timestretchChannelCount = downmixerBufferProvider != NULL -// ? mMixerChannelCount : channelCount; -// mTimestretchBufferProvider = new TimestretchBufferProvider(timestretchChannelCount, -// mMixerInFormat, sampleRate, playbackRate); -// reconfigureBufferProviders(); -// } else { -// reinterpret_cast(mTimestretchBufferProvider) -// ->setPlaybackRate(playbackRate); -// } - return true; -} - -/* Checks to see if the volume ramp has completed and clears the increment - * variables appropriately. - * - * FIXME: There is code to handle int/float ramp variable switchover should it not - * complete within a mixer buffer processing call, but it is preferred to avoid switchover - * due to precision issues. The switchover code is included for legacy code purposes - * and can be removed once the integer volume is removed. - * - * It is not sufficient to clear only the volumeInc integer variable because - * if one channel requires ramping, all channels are ramped. - * - * There is a bit of duplicated code here, but it keeps backward compatibility. - */ -inline void AudioMixer::track_t::adjustVolumeRamp(bool aux, bool useFloat) -{ - if (useFloat) { - for (uint32_t i = 0; i < MAX_NUM_VOLUMES; i++) { - if ((mVolumeInc[i] > 0 && mPrevVolume[i] + mVolumeInc[i] >= mVolume[i]) || - (mVolumeInc[i] < 0 && mPrevVolume[i] + mVolumeInc[i] <= mVolume[i])) { - volumeInc[i] = 0; - prevVolume[i] = volume[i] << 16; - mVolumeInc[i] = 0.; - mPrevVolume[i] = mVolume[i]; - } else { - //ALOGV("ramp: %f %f %f", mVolume[i], mPrevVolume[i], mVolumeInc[i]); - prevVolume[i] = u4_28_from_float(mPrevVolume[i]); - } - } - } else { - for (uint32_t i = 0; i < MAX_NUM_VOLUMES; i++) { - if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) || - ((volumeInc[i]<0) && (((prevVolume[i]+volumeInc[i])>>16) <= volume[i]))) { - volumeInc[i] = 0; - prevVolume[i] = volume[i] << 16; - mVolumeInc[i] = 0.; - mPrevVolume[i] = mVolume[i]; - } else { - //ALOGV("ramp: %d %d %d", volume[i] << 16, prevVolume[i], volumeInc[i]); - mPrevVolume[i] = float_from_u4_28(prevVolume[i]); - } - } - } - /* TODO: aux is always integer regardless of output buffer type */ - if (aux) { - if (((auxInc>0) && (((prevAuxLevel+auxInc)>>16) >= auxLevel)) || - ((auxInc<0) && (((prevAuxLevel+auxInc)>>16) <= auxLevel))) { - auxInc = 0; - prevAuxLevel = auxLevel << 16; - mAuxInc = 0.; - mPrevAuxLevel = mAuxLevel; - } else { - //ALOGV("aux ramp: %d %d %d", auxLevel << 16, prevAuxLevel, auxInc); - } - } -} - -size_t AudioMixer::getUnreleasedFrames(int name) const -{ - name -= TRACK0; - if (uint32_t(name) < MAX_NUM_TRACKS) { - return mState.tracks[name].getUnreleasedFrames(); - } - return 0; -} - -void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider) -{ - name -= TRACK0; - ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name); - - if (mState.tracks[name].mInputBufferProvider == bufferProvider) { - return; // don't reset any buffer providers if identical. - } -//cjh if (mState.tracks[name].mReformatBufferProvider != NULL) { -// mState.tracks[name].mReformatBufferProvider->reset(); -// } else if (mState.tracks[name].downmixerBufferProvider != NULL) { -// mState.tracks[name].downmixerBufferProvider->reset(); -// } else if (mState.tracks[name].mPostDownmixReformatBufferProvider != NULL) { -// mState.tracks[name].mPostDownmixReformatBufferProvider->reset(); -// } else if (mState.tracks[name].mTimestretchBufferProvider != NULL) { -// mState.tracks[name].mTimestretchBufferProvider->reset(); -// } - - mState.tracks[name].mInputBufferProvider = bufferProvider; - mState.tracks[name].reconfigureBufferProviders(); -} - - -void AudioMixer::process(int64_t pts) -{ - mState.hook(&mState, pts); -} - - -void AudioMixer::process__validate(state_t* state, int64_t pts) -{ - ALOGW_IF(!state->needsChanged, - "in process__validate() but nothing's invalid"); - - uint32_t changed = state->needsChanged; - state->needsChanged = 0; // clear the validation flag - - // recompute which tracks are enabled / disabled - uint32_t enabled = 0; - uint32_t disabled = 0; - while (changed) { - const int i = 31 - __builtin_clz(changed); - const uint32_t mask = 1<tracks[i]; - (t.enabled ? enabled : disabled) |= mask; - } - state->enabledTracks &= ~disabled; - state->enabledTracks |= enabled; - - // compute everything we need... - int countActiveTracks = 0; - // TODO: fix all16BitsStereNoResample logic to - // either properly handle muted tracks (it should ignore them) - // or remove altogether as an obsolete optimization. - bool all16BitsStereoNoResample = true; - bool resampling = false; - bool volumeRamp = false; - uint32_t en = state->enabledTracks; - while (en) { - const int i = 31 - __builtin_clz(en); - en &= ~(1<tracks[i]; - uint32_t n = 0; - // FIXME can overflow (mask is only 3 bits) - n |= NEEDS_CHANNEL_1 + t.channelCount - 1; - if (t.doesResample()) { - n |= NEEDS_RESAMPLE; - } - if (t.auxLevel != 0 && t.auxBuffer != NULL) { - n |= NEEDS_AUX; - } - - if (t.volumeInc[0]|t.volumeInc[1]) { - volumeRamp = true; - } else if (!t.doesResample() && t.volumeRL == 0) { - n |= NEEDS_MUTE; - } - t.needs = n; - - if (n & NEEDS_MUTE) { - t.hook = track__nop; - } else { - if (n & NEEDS_AUX) { - all16BitsStereoNoResample = false; - } - if (n & NEEDS_RESAMPLE) { - all16BitsStereoNoResample = false; - resampling = true; - t.hook = getTrackHook(TRACKTYPE_RESAMPLE, t.mMixerChannelCount, - t.mMixerInFormat, t.mMixerFormat); - ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2, - "Track %d needs downmix + resample", i); - } else { - if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){ - t.hook = getTrackHook( - (t.mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO // TODO: MONO_HACK - && t.channelMask == AUDIO_CHANNEL_OUT_MONO) - ? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE, - t.mMixerChannelCount, - t.mMixerInFormat, t.mMixerFormat); - all16BitsStereoNoResample = false; - } - if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){ - t.hook = getTrackHook(TRACKTYPE_NORESAMPLE, t.mMixerChannelCount, - t.mMixerInFormat, t.mMixerFormat); - ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2, - "Track %d needs downmix", i); - } - } - } - } - - // select the processing hooks - state->hook = process__nop; - if (countActiveTracks > 0) { - if (resampling) { - if (!state->outputTemp) { - state->outputTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount]; - } - if (!state->resampleTemp) { - state->resampleTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount]; - } - state->hook = process__genericResampling; - } else { - if (state->outputTemp) { - delete [] state->outputTemp; - state->outputTemp = NULL; - } - if (state->resampleTemp) { - delete [] state->resampleTemp; - state->resampleTemp = NULL; - } - state->hook = process__genericNoResampling; - if (all16BitsStereoNoResample && !volumeRamp) { - if (countActiveTracks == 1) { - const int i = 31 - __builtin_clz(state->enabledTracks); - track_t& t = state->tracks[i]; - if ((t.needs & NEEDS_MUTE) == 0) { - // The check prevents a muted track from acquiring a process hook. - // - // This is dangerous if the track is MONO as that requires - // special case handling due to implicit channel duplication. - // Stereo or Multichannel should actually be fine here. - state->hook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK, - t.mMixerChannelCount, t.mMixerInFormat, t.mMixerFormat); - } - } - } - } - } - - ALOGV("mixer configuration change: %d activeTracks (%08x) " - "all16BitsStereoNoResample=%d, resampling=%d, volumeRamp=%d", - countActiveTracks, state->enabledTracks, - all16BitsStereoNoResample, resampling, volumeRamp); - - state->hook(state, pts); - - // Now that the volume ramp has been done, set optimal state and - // track hooks for subsequent mixer process - if (countActiveTracks > 0) { - bool allMuted = true; - uint32_t en = state->enabledTracks; - while (en) { - const int i = 31 - __builtin_clz(en); - en &= ~(1<tracks[i]; - if (!t.doesResample() && t.volumeRL == 0) { - t.needs |= NEEDS_MUTE; - t.hook = track__nop; - } else { - allMuted = false; - } - } - if (allMuted) { - state->hook = process__nop; - } else if (all16BitsStereoNoResample) { - if (countActiveTracks == 1) { - const int i = 31 - __builtin_clz(state->enabledTracks); - track_t& t = state->tracks[i]; - // Muted single tracks handled by allMuted above. - state->hook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK, - t.mMixerChannelCount, t.mMixerInFormat, t.mMixerFormat); - } - } - } -} - - -void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, - int32_t* temp, int32_t* aux) -{ - ALOGVV("track__genericResample\n"); - t->resampler->setSampleRate(t->sampleRate); - - // ramp gain - resample to temp buffer and scale/mix in 2nd step - if (aux != NULL) { - // always resample with unity gain when sending to auxiliary buffer to be able - // to apply send level after resampling - t->resampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT); - memset(temp, 0, outFrameCount * t->mMixerChannelCount * sizeof(int32_t)); - t->resampler->resample(temp, outFrameCount, t->bufferProvider); - if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc)) { - volumeRampStereo(t, out, outFrameCount, temp, aux); - } else { - volumeStereo(t, out, outFrameCount, temp, aux); - } - } else { - if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) { - t->resampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT); - memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t)); - t->resampler->resample(temp, outFrameCount, t->bufferProvider); - volumeRampStereo(t, out, outFrameCount, temp, aux); - } - - // constant gain - else { - t->resampler->setVolume(t->mVolume[0], t->mVolume[1]); - t->resampler->resample(out, outFrameCount, t->bufferProvider); - } - } -} - -void AudioMixer::track__nop(track_t* t __unused, int32_t* out __unused, - size_t outFrameCount __unused, int32_t* temp __unused, int32_t* aux __unused) -{ -} - -void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, - int32_t* aux) -{ - int32_t vl = t->prevVolume[0]; - int32_t vr = t->prevVolume[1]; - const int32_t vlInc = t->volumeInc[0]; - const int32_t vrInc = t->volumeInc[1]; - - //ALOGD("[0] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", - // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], - // (vl + vlInc*frameCount)/65536.0f, frameCount); - - // ramp volume - if (CC_UNLIKELY(aux != NULL)) { - int32_t va = t->prevAuxLevel; - const int32_t vaInc = t->auxInc; - int32_t l; - int32_t r; - - do { - l = (*temp++ >> 12); - r = (*temp++ >> 12); - *out++ += (vl >> 16) * l; - *out++ += (vr >> 16) * r; - *aux++ += (va >> 17) * (l + r); - vl += vlInc; - vr += vrInc; - va += vaInc; - } while (--frameCount); - t->prevAuxLevel = va; - } else { - do { - *out++ += (vl >> 16) * (*temp++ >> 12); - *out++ += (vr >> 16) * (*temp++ >> 12); - vl += vlInc; - vr += vrInc; - } while (--frameCount); - } - t->prevVolume[0] = vl; - t->prevVolume[1] = vr; - t->adjustVolumeRamp(aux != NULL); -} - -void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, - int32_t* aux) -{ - const int16_t vl = t->volume[0]; - const int16_t vr = t->volume[1]; - - if (CC_UNLIKELY(aux != NULL)) { - const int16_t va = t->auxLevel; - do { - int16_t l = (int16_t)(*temp++ >> 12); - int16_t r = (int16_t)(*temp++ >> 12); - out[0] = mulAdd(l, vl, out[0]); - int16_t a = (int16_t)(((int32_t)l + r) >> 1); - out[1] = mulAdd(r, vr, out[1]); - out += 2; - aux[0] = mulAdd(a, va, aux[0]); - aux++; - } while (--frameCount); - } else { - do { - int16_t l = (int16_t)(*temp++ >> 12); - int16_t r = (int16_t)(*temp++ >> 12); - out[0] = mulAdd(l, vl, out[0]); - out[1] = mulAdd(r, vr, out[1]); - out += 2; - } while (--frameCount); - } -} - -void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, - int32_t* temp __unused, int32_t* aux) -{ - ALOGVV("track__16BitsStereo\n"); - const int16_t *in = static_cast(t->in); - - if (CC_UNLIKELY(aux != NULL)) { - int32_t l; - int32_t r; - // ramp gain - if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc)) { - int32_t vl = t->prevVolume[0]; - int32_t vr = t->prevVolume[1]; - int32_t va = t->prevAuxLevel; - const int32_t vlInc = t->volumeInc[0]; - const int32_t vrInc = t->volumeInc[1]; - const int32_t vaInc = t->auxInc; - // ALOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", - // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], - // (vl + vlInc*frameCount)/65536.0f, frameCount); - - do { - l = (int32_t)*in++; - r = (int32_t)*in++; - *out++ += (vl >> 16) * l; - *out++ += (vr >> 16) * r; - *aux++ += (va >> 17) * (l + r); - vl += vlInc; - vr += vrInc; - va += vaInc; - } while (--frameCount); - - t->prevVolume[0] = vl; - t->prevVolume[1] = vr; - t->prevAuxLevel = va; - t->adjustVolumeRamp(true); - } - - // constant gain - else { - const uint32_t vrl = t->volumeRL; - const int16_t va = (int16_t)t->auxLevel; - do { - uint32_t rl = *reinterpret_cast(in); - int16_t a = (int16_t)(((int32_t)in[0] + in[1]) >> 1); - in += 2; - out[0] = mulAddRL(1, rl, vrl, out[0]); - out[1] = mulAddRL(0, rl, vrl, out[1]); - out += 2; - aux[0] = mulAdd(a, va, aux[0]); - aux++; - } while (--frameCount); - } - } else { - // ramp gain - if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) { - int32_t vl = t->prevVolume[0]; - int32_t vr = t->prevVolume[1]; - const int32_t vlInc = t->volumeInc[0]; - const int32_t vrInc = t->volumeInc[1]; - - // ALOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", - // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], - // (vl + vlInc*frameCount)/65536.0f, frameCount); - - do { - *out++ += (vl >> 16) * (int32_t) *in++; - *out++ += (vr >> 16) * (int32_t) *in++; - vl += vlInc; - vr += vrInc; - } while (--frameCount); - - t->prevVolume[0] = vl; - t->prevVolume[1] = vr; - t->adjustVolumeRamp(false); - } - - // constant gain - else { - const uint32_t vrl = t->volumeRL; - do { - uint32_t rl = *reinterpret_cast(in); - in += 2; - out[0] = mulAddRL(1, rl, vrl, out[0]); - out[1] = mulAddRL(0, rl, vrl, out[1]); - out += 2; - } while (--frameCount); - } - } - t->in = in; -} - -void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, - int32_t* temp __unused, int32_t* aux) -{ - ALOGVV("track__16BitsMono\n"); - const int16_t *in = static_cast(t->in); - - if (CC_UNLIKELY(aux != NULL)) { - // ramp gain - if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc)) { - int32_t vl = t->prevVolume[0]; - int32_t vr = t->prevVolume[1]; - int32_t va = t->prevAuxLevel; - const int32_t vlInc = t->volumeInc[0]; - const int32_t vrInc = t->volumeInc[1]; - const int32_t vaInc = t->auxInc; - - // ALOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", - // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], - // (vl + vlInc*frameCount)/65536.0f, frameCount); - - do { - int32_t l = *in++; - *out++ += (vl >> 16) * l; - *out++ += (vr >> 16) * l; - *aux++ += (va >> 16) * l; - vl += vlInc; - vr += vrInc; - va += vaInc; - } while (--frameCount); - - t->prevVolume[0] = vl; - t->prevVolume[1] = vr; - t->prevAuxLevel = va; - t->adjustVolumeRamp(true); - } - // constant gain - else { - const int16_t vl = t->volume[0]; - const int16_t vr = t->volume[1]; - const int16_t va = (int16_t)t->auxLevel; - do { - int16_t l = *in++; - out[0] = mulAdd(l, vl, out[0]); - out[1] = mulAdd(l, vr, out[1]); - out += 2; - aux[0] = mulAdd(l, va, aux[0]); - aux++; - } while (--frameCount); - } - } else { - // ramp gain - if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) { - int32_t vl = t->prevVolume[0]; - int32_t vr = t->prevVolume[1]; - const int32_t vlInc = t->volumeInc[0]; - const int32_t vrInc = t->volumeInc[1]; - - // ALOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", - // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], - // (vl + vlInc*frameCount)/65536.0f, frameCount); - - do { - int32_t l = *in++; - *out++ += (vl >> 16) * l; - *out++ += (vr >> 16) * l; - vl += vlInc; - vr += vrInc; - } while (--frameCount); - - t->prevVolume[0] = vl; - t->prevVolume[1] = vr; - t->adjustVolumeRamp(false); - } - // constant gain - else { - const int16_t vl = t->volume[0]; - const int16_t vr = t->volume[1]; - do { - int16_t l = *in++; - out[0] = mulAdd(l, vl, out[0]); - out[1] = mulAdd(l, vr, out[1]); - out += 2; - } while (--frameCount); - } - } - t->in = in; -} - -// no-op case -void AudioMixer::process__nop(state_t* state, int64_t pts) -{ - ALOGVV("process__nop\n"); - uint32_t e0 = state->enabledTracks; - while (e0) { - // process by group of tracks with same output buffer to - // avoid multiple memset() on same buffer - uint32_t e1 = e0, e2 = e0; - int i = 31 - __builtin_clz(e1); - { - track_t& t1 = state->tracks[i]; - e2 &= ~(1<tracks[i]; - if (CC_UNLIKELY(t2.mainBuffer != t1.mainBuffer)) { - e1 &= ~(1<frameCount * t1.mMixerChannelCount - * audio_bytes_per_sample(t1.mMixerFormat)); - } - - while (e1) { - i = 31 - __builtin_clz(e1); - e1 &= ~(1<tracks[i]; - size_t outFrames = state->frameCount; - while (outFrames) { - t3.buffer.frameCount = outFrames; - int64_t outputPTS = calculateOutputPTS( - t3, pts, state->frameCount - outFrames); - t3.bufferProvider->getNextBuffer(&t3.buffer, outputPTS); - if (t3.buffer.raw == NULL) break; - outFrames -= t3.buffer.frameCount; - t3.bufferProvider->releaseBuffer(&t3.buffer); - } - } - } - } -} - -// generic code without resampling -void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts) -{ - ALOGVV("process__genericNoResampling\n"); - int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32))); - - // acquire each track's buffer - uint32_t enabledTracks = state->enabledTracks; - uint32_t e0 = enabledTracks; - while (e0) { - const int i = 31 - __builtin_clz(e0); - e0 &= ~(1<tracks[i]; - t.buffer.frameCount = state->frameCount; - t.bufferProvider->getNextBuffer(&t.buffer, pts); - t.frameCount = t.buffer.frameCount; - t.in = t.buffer.raw; - } - - e0 = enabledTracks; - while (e0) { - // process by group of tracks with same output buffer to - // optimize cache use - uint32_t e1 = e0, e2 = e0; - int j = 31 - __builtin_clz(e1); - track_t& t1 = state->tracks[j]; - e2 &= ~(1<tracks[j]; - if (CC_UNLIKELY(t2.mainBuffer != t1.mainBuffer)) { - e1 &= ~(1<tracks[i]; - size_t outFrames = BLOCKSIZE; - int32_t *aux = NULL; - if (CC_UNLIKELY(t.needs & NEEDS_AUX)) { - aux = t.auxBuffer + numFrames; - } - while (outFrames) { - // t.in == NULL can happen if the track was flushed just after having - // been enabled for mixing. - if (t.in == NULL) { - enabledTracks &= ~(1< outFrames)?outFrames:t.frameCount; - if (inFrames > 0) { - t.hook(&t, outTemp + (BLOCKSIZE - outFrames) * t.mMixerChannelCount, - inFrames, state->resampleTemp, aux); - t.frameCount -= inFrames; - outFrames -= inFrames; - if (CC_UNLIKELY(aux != NULL)) { - aux += inFrames; - } - } - if (t.frameCount == 0 && outFrames) { - t.bufferProvider->releaseBuffer(&t.buffer); - t.buffer.frameCount = (state->frameCount - numFrames) - - (BLOCKSIZE - outFrames); - int64_t outputPTS = calculateOutputPTS( - t, pts, numFrames + (BLOCKSIZE - outFrames)); - t.bufferProvider->getNextBuffer(&t.buffer, outputPTS); - t.in = t.buffer.raw; - if (t.in == NULL) { - enabledTracks &= ~(1<((uint8_t*)out - + BLOCKSIZE * t1.mMixerChannelCount - * audio_bytes_per_sample(t1.mMixerFormat)); - numFrames += BLOCKSIZE; - } while (numFrames < state->frameCount); - } - - // release each track's buffer - e0 = enabledTracks; - while (e0) { - const int i = 31 - __builtin_clz(e0); - e0 &= ~(1<tracks[i]; - t.bufferProvider->releaseBuffer(&t.buffer); - } -} - - -// generic code with resampling -void AudioMixer::process__genericResampling(state_t* state, int64_t pts) -{ - ALOGVV("process__genericResampling\n"); - // this const just means that local variable outTemp doesn't change - int32_t* const outTemp = state->outputTemp; - size_t numFrames = state->frameCount; - - uint32_t e0 = state->enabledTracks; - while (e0) { - // process by group of tracks with same output buffer - // to optimize cache use - uint32_t e1 = e0, e2 = e0; - int j = 31 - __builtin_clz(e1); - track_t& t1 = state->tracks[j]; - e2 &= ~(1<tracks[j]; - if (CC_UNLIKELY(t2.mainBuffer != t1.mainBuffer)) { - e1 &= ~(1<frameCount); - while (e1) { - const int i = 31 - __builtin_clz(e1); - e1 &= ~(1<tracks[i]; - int32_t *aux = NULL; - if (CC_UNLIKELY(t.needs & NEEDS_AUX)) { - aux = t.auxBuffer; - } - - // this is a little goofy, on the resampling case we don't - // acquire/release the buffers because it's done by - // the resampler. - if (t.needs & NEEDS_RESAMPLE) { - t.resampler->setPTS(pts); - t.hook(&t, outTemp, numFrames, state->resampleTemp, aux); - } else { - - size_t outFrames = 0; - - while (outFrames < numFrames) { - t.buffer.frameCount = numFrames - outFrames; - int64_t outputPTS = calculateOutputPTS(t, pts, outFrames); - t.bufferProvider->getNextBuffer(&t.buffer, outputPTS); - t.in = t.buffer.raw; - // t.in == NULL can happen if the track was flushed just after having - // been enabled for mixing. - if (t.in == NULL) break; - - if (CC_UNLIKELY(aux != NULL)) { - aux += outFrames; - } - t.hook(&t, outTemp + outFrames * t.mMixerChannelCount, t.buffer.frameCount, - state->resampleTemp, aux); - outFrames += t.buffer.frameCount; - t.bufferProvider->releaseBuffer(&t.buffer); - } - } - } - convertMixerFormat(out, t1.mMixerFormat, - outTemp, t1.mMixerInFormat, numFrames * t1.mMixerChannelCount); - } -} - -// one track, 16 bits stereo without resampling is the most common case -void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, - int64_t pts) -{ - ALOGVV("process__OneTrack16BitsStereoNoResampling\n"); - // This method is only called when state->enabledTracks has exactly - // one bit set. The asserts below would verify this, but are commented out - // since the whole point of this method is to optimize performance. - //ALOG_ASSERT(0 != state->enabledTracks, "no tracks enabled"); - const int i = 31 - __builtin_clz(state->enabledTracks); - //ALOG_ASSERT((1 << i) == state->enabledTracks, "more than 1 track enabled"); - const track_t& t = state->tracks[i]; - - AudioBufferProvider::Buffer& b(t.buffer); - - int32_t* out = t.mainBuffer; - float *fout = reinterpret_cast(out); - size_t numFrames = state->frameCount; - - const int16_t vl = t.volume[0]; - const int16_t vr = t.volume[1]; - const uint32_t vrl = t.volumeRL; - while (numFrames) { - b.frameCount = numFrames; - int64_t outputPTS = calculateOutputPTS(t, pts, out - t.mainBuffer); - t.bufferProvider->getNextBuffer(&b, outputPTS); - const int16_t *in = b.i16; - - // in == NULL can happen if the track was flushed just after having - // been enabled for mixing. - if (in == NULL || (((uintptr_t)in) & 3)) { - memset(out, 0, numFrames - * t.mMixerChannelCount * audio_bytes_per_sample(t.mMixerFormat)); - ALOGE_IF((((uintptr_t)in) & 3), - "process__OneTrack16BitsStereoNoResampling: misaligned buffer" - " %p track %d, channels %d, needs %08x, volume %08x vfl %f vfr %f", - in, i, t.channelCount, t.needs, vrl, t.mVolume[0], t.mVolume[1]); - return; - } - size_t outFrames = b.frameCount; - - switch (t.mMixerFormat) { - case AUDIO_FORMAT_PCM_FLOAT: - do { - uint32_t rl = *reinterpret_cast(in); - in += 2; - int32_t l = mulRL(1, rl, vrl); - int32_t r = mulRL(0, rl, vrl); - *fout++ = float_from_q4_27(l); - *fout++ = float_from_q4_27(r); - // Note: In case of later int16_t sink output, - // conversion and clamping is done by memcpy_to_i16_from_float(). - } while (--outFrames); - break; - case AUDIO_FORMAT_PCM_16_BIT: - if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN_INT || uint32_t(vr) > UNITY_GAIN_INT)) { - // volume is boosted, so we might need to clamp even though - // we process only one track. - do { - uint32_t rl = *reinterpret_cast(in); - in += 2; - int32_t l = mulRL(1, rl, vrl) >> 12; - int32_t r = mulRL(0, rl, vrl) >> 12; - // clamping... - l = clamp16(l); - r = clamp16(r); - *out++ = (r<<16) | (l & 0xFFFF); - } while (--outFrames); - } else { - do { - uint32_t rl = *reinterpret_cast(in); - in += 2; - int32_t l = mulRL(1, rl, vrl) >> 12; - int32_t r = mulRL(0, rl, vrl) >> 12; - *out++ = (r<<16) | (l & 0xFFFF); - } while (--outFrames); - } - break; - default: - LOG_ALWAYS_FATAL("bad mixer format: %d", t.mMixerFormat); - } - numFrames -= b.frameCount; - t.bufferProvider->releaseBuffer(&b); - } -} - -int64_t AudioMixer::calculateOutputPTS(const track_t& t, int64_t basePTS, - int outputFrameIndex) -{ - if (AudioBufferProvider::kInvalidPTS == basePTS) { - return AudioBufferProvider::kInvalidPTS; - } - - return basePTS + ((outputFrameIndex * sLocalTimeFreq) / t.sampleRate); -} - -/*static*/ uint64_t AudioMixer::sLocalTimeFreq; -/*static*/ pthread_once_t AudioMixer::sOnceControl = PTHREAD_ONCE_INIT; - -/*static*/ void AudioMixer::sInitRoutine() -{ -//cjh LocalClock lc; -// sLocalTimeFreq = lc.getLocalFreq(); // for the resampler -// -// DownmixerBufferProvider::init(); // for the downmixer -} - -/* TODO: consider whether this level of optimization is necessary. - * Perhaps just stick with a single for loop. - */ - -// Needs to derive a compile time constant (constexpr). Could be targeted to go -// to a MONOVOL mixtype based on MAX_NUM_VOLUMES, but that's an unnecessary complication. -#define MIXTYPE_MONOVOL(mixtype) (mixtype == MIXTYPE_MULTI ? MIXTYPE_MULTI_MONOVOL : \ - mixtype == MIXTYPE_MULTI_SAVEONLY ? MIXTYPE_MULTI_SAVEONLY_MONOVOL : mixtype) - -/* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration) - * TO: int32_t (Q4.27) or float - * TI: int32_t (Q4.27) or int16_t (Q0.15) or float - * TA: int32_t (Q4.27) - */ -template -static void volumeRampMulti(uint32_t channels, TO* out, size_t frameCount, - const TI* in, TA* aux, TV *vol, const TV *volinc, TAV *vola, TAV volainc) -{ - switch (channels) { - case 1: - volumeRampMulti(out, frameCount, in, aux, vol, volinc, vola, volainc); - break; - case 2: - volumeRampMulti(out, frameCount, in, aux, vol, volinc, vola, volainc); - break; - case 3: - volumeRampMulti(out, - frameCount, in, aux, vol, volinc, vola, volainc); - break; - case 4: - volumeRampMulti(out, - frameCount, in, aux, vol, volinc, vola, volainc); - break; - case 5: - volumeRampMulti(out, - frameCount, in, aux, vol, volinc, vola, volainc); - break; - case 6: - volumeRampMulti(out, - frameCount, in, aux, vol, volinc, vola, volainc); - break; - case 7: - volumeRampMulti(out, - frameCount, in, aux, vol, volinc, vola, volainc); - break; - case 8: - volumeRampMulti(out, - frameCount, in, aux, vol, volinc, vola, volainc); - break; - } -} - -/* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration) - * TO: int32_t (Q4.27) or float - * TI: int32_t (Q4.27) or int16_t (Q0.15) or float - * TA: int32_t (Q4.27) - */ -template -static void volumeMulti(uint32_t channels, TO* out, size_t frameCount, - const TI* in, TA* aux, const TV *vol, TAV vola) -{ - switch (channels) { - case 1: - volumeMulti(out, frameCount, in, aux, vol, vola); - break; - case 2: - volumeMulti(out, frameCount, in, aux, vol, vola); - break; - case 3: - volumeMulti(out, frameCount, in, aux, vol, vola); - break; - case 4: - volumeMulti(out, frameCount, in, aux, vol, vola); - break; - case 5: - volumeMulti(out, frameCount, in, aux, vol, vola); - break; - case 6: - volumeMulti(out, frameCount, in, aux, vol, vola); - break; - case 7: - volumeMulti(out, frameCount, in, aux, vol, vola); - break; - case 8: - volumeMulti(out, frameCount, in, aux, vol, vola); - break; - } -} - -/* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration) - * USEFLOATVOL (set to true if float volume is used) - * ADJUSTVOL (set to true if volume ramp parameters needs adjustment afterwards) - * TO: int32_t (Q4.27) or float - * TI: int32_t (Q4.27) or int16_t (Q0.15) or float - * TA: int32_t (Q4.27) - */ -template -void AudioMixer::volumeMix(TO *out, size_t outFrames, - const TI *in, TA *aux, bool ramp, AudioMixer::track_t *t) -{ - if (USEFLOATVOL) { - if (ramp) { - volumeRampMulti(t->mMixerChannelCount, out, outFrames, in, aux, - t->mPrevVolume, t->mVolumeInc, &t->prevAuxLevel, t->auxInc); - if (ADJUSTVOL) { - t->adjustVolumeRamp(aux != NULL, true); - } - } else { - volumeMulti(t->mMixerChannelCount, out, outFrames, in, aux, - t->mVolume, t->auxLevel); - } - } else { - if (ramp) { - volumeRampMulti(t->mMixerChannelCount, out, outFrames, in, aux, - t->prevVolume, t->volumeInc, &t->prevAuxLevel, t->auxInc); - if (ADJUSTVOL) { - t->adjustVolumeRamp(aux != NULL); - } - } else { - volumeMulti(t->mMixerChannelCount, out, outFrames, in, aux, - t->volume, t->auxLevel); - } - } -} - -/* This process hook is called when there is a single track without - * aux buffer, volume ramp, or resampling. - * TODO: Update the hook selection: this can properly handle aux and ramp. - * - * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration) - * TO: int32_t (Q4.27) or float - * TI: int32_t (Q4.27) or int16_t (Q0.15) or float - * TA: int32_t (Q4.27) - */ -template -void AudioMixer::process_NoResampleOneTrack(state_t* state, int64_t pts) -{ - ALOGVV("process_NoResampleOneTrack\n"); - // CLZ is faster than CTZ on ARM, though really not sure if true after 31 - clz. - const int i = 31 - __builtin_clz(state->enabledTracks); - ALOG_ASSERT((1 << i) == state->enabledTracks, "more than 1 track enabled"); - track_t *t = &state->tracks[i]; - const uint32_t channels = t->mMixerChannelCount; - TO* out = reinterpret_cast(t->mainBuffer); - TA* aux = reinterpret_cast(t->auxBuffer); - const bool ramp = t->needsRamp(); - - for (size_t numFrames = state->frameCount; numFrames; ) { - AudioBufferProvider::Buffer& b(t->buffer); - // get input buffer - b.frameCount = numFrames; - const int64_t outputPTS = calculateOutputPTS(*t, pts, state->frameCount - numFrames); - t->bufferProvider->getNextBuffer(&b, outputPTS); - const TI *in = reinterpret_cast(b.raw); - - // in == NULL can happen if the track was flushed just after having - // been enabled for mixing. - if (in == NULL || (((uintptr_t)in) & 3)) { - memset(out, 0, numFrames - * channels * audio_bytes_per_sample(t->mMixerFormat)); - ALOGE_IF((((uintptr_t)in) & 3), "process_NoResampleOneTrack: bus error: " - "buffer %p track %p, channels %d, needs %#x", - in, t, t->channelCount, t->needs); - return; - } - - const size_t outFrames = b.frameCount; - volumeMix::value, false> ( - out, outFrames, in, aux, ramp, t); - - out += outFrames * channels; - if (aux != NULL) { - aux += channels; - } - numFrames -= b.frameCount; - - // release buffer - t->bufferProvider->releaseBuffer(&b); - } - if (ramp) { - t->adjustVolumeRamp(aux != NULL, is_same::value); - } -} - -/* This track hook is called to do resampling then mixing, - * pulling from the track's upstream AudioBufferProvider. - * - * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration) - * TO: int32_t (Q4.27) or float - * TI: int32_t (Q4.27) or int16_t (Q0.15) or float - * TA: int32_t (Q4.27) - */ -template -void AudioMixer::track__Resample(track_t* t, TO* out, size_t outFrameCount, TO* temp, TA* aux) -{ - ALOGVV("track__Resample\n"); - t->resampler->setSampleRate(t->sampleRate); - const bool ramp = t->needsRamp(); - if (ramp || aux != NULL) { - // if ramp: resample with unity gain to temp buffer and scale/mix in 2nd step. - // if aux != NULL: resample with unity gain to temp buffer then apply send level. - - t->resampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT); - memset(temp, 0, outFrameCount * t->mMixerChannelCount * sizeof(TO)); - t->resampler->resample((int32_t*)temp, outFrameCount, t->bufferProvider); - - volumeMix::value, true>( - out, outFrameCount, temp, aux, ramp, t); - - } else { // constant volume gain - t->resampler->setVolume(t->mVolume[0], t->mVolume[1]); - t->resampler->resample((int32_t*)out, outFrameCount, t->bufferProvider); - } -} - -/* This track hook is called to mix a track, when no resampling is required. - * The input buffer should be present in t->in. - * - * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration) - * TO: int32_t (Q4.27) or float - * TI: int32_t (Q4.27) or int16_t (Q0.15) or float - * TA: int32_t (Q4.27) - */ -template -void AudioMixer::track__NoResample(track_t* t, TO* out, size_t frameCount, - TO* temp __unused, TA* aux) -{ - ALOGVV("track__NoResample\n"); - const TI *in = static_cast(t->in); - - volumeMix::value, true>( - out, frameCount, in, aux, t->needsRamp(), t); - - // MIXTYPE_MONOEXPAND reads a single input channel and expands to NCHAN output channels. - // MIXTYPE_MULTI reads NCHAN input channels and places to NCHAN output channels. - in += (MIXTYPE == MIXTYPE_MONOEXPAND) ? frameCount : frameCount * t->mMixerChannelCount; - t->in = in; -} - -/* The Mixer engine generates either int32_t (Q4_27) or float data. - * We use this function to convert the engine buffers - * to the desired mixer output format, either int16_t (Q.15) or float. - */ -void AudioMixer::convertMixerFormat(void *out, audio_format_t mixerOutFormat, - void *in, audio_format_t mixerInFormat, size_t sampleCount) -{ - switch (mixerInFormat) { - case AUDIO_FORMAT_PCM_FLOAT: - switch (mixerOutFormat) { - case AUDIO_FORMAT_PCM_FLOAT: - memcpy(out, in, sampleCount * sizeof(float)); // MEMCPY. TODO optimize out - break; - case AUDIO_FORMAT_PCM_16_BIT: - memcpy_to_i16_from_float((int16_t*)out, (float*)in, sampleCount); - break; - default: - LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat); - break; - } - break; - case AUDIO_FORMAT_PCM_16_BIT: - switch (mixerOutFormat) { - case AUDIO_FORMAT_PCM_FLOAT: - memcpy_to_float_from_q4_27((float*)out, (int32_t*)in, sampleCount); - break; - case AUDIO_FORMAT_PCM_16_BIT: - // two int16_t are produced per iteration - ditherAndClamp((int32_t*)out, (int32_t*)in, sampleCount >> 1); - break; - default: - LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat); - break; - } - break; - default: - LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat); - break; - } -} - -/* Returns the proper track hook to use for mixing the track into the output buffer. - */ -AudioMixer::hook_t AudioMixer::getTrackHook(int trackType, uint32_t channelCount, - audio_format_t mixerInFormat, audio_format_t mixerOutFormat __unused) -{ - if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) { - switch (trackType) { - case TRACKTYPE_NOP: - return track__nop; - case TRACKTYPE_RESAMPLE: - return track__genericResample; - case TRACKTYPE_NORESAMPLEMONO: - return track__16BitsMono; - case TRACKTYPE_NORESAMPLE: - return track__16BitsStereo; - default: - LOG_ALWAYS_FATAL("bad trackType: %d", trackType); - break; - } - } - LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS); - switch (trackType) { - case TRACKTYPE_NOP: - return track__nop; - case TRACKTYPE_RESAMPLE: - switch (mixerInFormat) { - case AUDIO_FORMAT_PCM_FLOAT: - return (AudioMixer::hook_t) - track__Resample; - case AUDIO_FORMAT_PCM_16_BIT: - return (AudioMixer::hook_t)\ - track__Resample; - default: - LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat); - break; - } - break; - case TRACKTYPE_NORESAMPLEMONO: - switch (mixerInFormat) { - case AUDIO_FORMAT_PCM_FLOAT: - return (AudioMixer::hook_t) - track__NoResample; - case AUDIO_FORMAT_PCM_16_BIT: - return (AudioMixer::hook_t) - track__NoResample; - default: - LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat); - break; - } - break; - case TRACKTYPE_NORESAMPLE: - switch (mixerInFormat) { - case AUDIO_FORMAT_PCM_FLOAT: - return (AudioMixer::hook_t) - track__NoResample; - case AUDIO_FORMAT_PCM_16_BIT: - return (AudioMixer::hook_t) - track__NoResample; - default: - LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat); - break; - } - break; - default: - LOG_ALWAYS_FATAL("bad trackType: %d", trackType); - break; - } - return NULL; -} - -/* Returns the proper process hook for mixing tracks. Currently works only for - * PROCESSTYPE_NORESAMPLEONETRACK, a mix involving one track, no resampling. - * - * TODO: Due to the special mixing considerations of duplicating to - * a stereo output track, the input track cannot be MONO. This should be - * prevented by the caller. - */ -AudioMixer::process_hook_t AudioMixer::getProcessHook(int processType, uint32_t channelCount, - audio_format_t mixerInFormat, audio_format_t mixerOutFormat) -{ - if (processType != PROCESSTYPE_NORESAMPLEONETRACK) { // Only NORESAMPLEONETRACK - LOG_ALWAYS_FATAL("bad processType: %d", processType); - return NULL; - } - if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) { - return process__OneTrack16BitsStereoNoResampling; - } - LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS); - switch (mixerInFormat) { - case AUDIO_FORMAT_PCM_FLOAT: - switch (mixerOutFormat) { - case AUDIO_FORMAT_PCM_FLOAT: - return process_NoResampleOneTrack; - case AUDIO_FORMAT_PCM_16_BIT: - return process_NoResampleOneTrack; - default: - LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat); - break; - } - break; - case AUDIO_FORMAT_PCM_16_BIT: - switch (mixerOutFormat) { - case AUDIO_FORMAT_PCM_FLOAT: - return process_NoResampleOneTrack; - case AUDIO_FORMAT_PCM_16_BIT: - return process_NoResampleOneTrack; - default: - LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat); - break; - } - break; - default: - LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat); - break; - } - return NULL; -} - -// ---------------------------------------------------------------------------- -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioMixer.h b/cocos/audio/android/AudioMixer.h deleted file mode 100644 index dc2080d71f36..000000000000 --- a/cocos/audio/android/AudioMixer.h +++ /dev/null @@ -1,389 +0,0 @@ -/* -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#pragma once - -#include -#include -#include - -#include "audio/android/AudioBufferProvider.h" -#include "audio/android/AudioResamplerPublic.h" - -#include "audio/android/AudioResampler.h" -#include "audio/android/audio.h" - -// FIXME This is actually unity gain, which might not be max in future, expressed in U.12 -#define MAX_GAIN_INT AudioMixer::UNITY_GAIN_INT - -namespace cocos2d { namespace experimental { - -// ---------------------------------------------------------------------------- - -class AudioMixer -{ -public: - AudioMixer(size_t frameCount, uint32_t sampleRate, - uint32_t maxNumTracks = MAX_NUM_TRACKS); - - /*virtual*/ ~AudioMixer(); // non-virtual saves a v-table, restore if sub-classed - - - // This mixer has a hard-coded upper limit of 32 active track inputs. - // Adding support for > 32 tracks would require more than simply changing this value. - static const uint32_t MAX_NUM_TRACKS = 32; - // maximum number of channels supported by the mixer - - // This mixer has a hard-coded upper limit of 8 channels for output. - static const uint32_t MAX_NUM_CHANNELS = 8; - static const uint32_t MAX_NUM_VOLUMES = 2; // stereo volume only - // maximum number of channels supported for the content - static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = AUDIO_CHANNEL_COUNT_MAX; - - static const uint16_t UNITY_GAIN_INT = 0x1000; - static const CONSTEXPR float UNITY_GAIN_FLOAT = 1.0f; - - enum { // names - - // track names (MAX_NUM_TRACKS units) - TRACK0 = 0x1000, - - // 0x2000 is unused - - // setParameter targets - TRACK = 0x3000, - RESAMPLE = 0x3001, - RAMP_VOLUME = 0x3002, // ramp to new volume - VOLUME = 0x3003, // don't ramp - TIMESTRETCH = 0x3004, - - // set Parameter names - // for target TRACK - CHANNEL_MASK = 0x4000, - FORMAT = 0x4001, - MAIN_BUFFER = 0x4002, - AUX_BUFFER = 0x4003, - DOWNMIX_TYPE = 0X4004, - MIXER_FORMAT = 0x4005, // AUDIO_FORMAT_PCM_(FLOAT|16_BIT) - MIXER_CHANNEL_MASK = 0x4006, // Channel mask for mixer output - // for target RESAMPLE - SAMPLE_RATE = 0x4100, // Configure sample rate conversion on this track name; - // parameter 'value' is the new sample rate in Hz. - // Only creates a sample rate converter the first time that - // the track sample rate is different from the mix sample rate. - // If the new sample rate is the same as the mix sample rate, - // and a sample rate converter already exists, - // then the sample rate converter remains present but is a no-op. - RESET = 0x4101, // Reset sample rate converter without changing sample rate. - // This clears out the resampler's input buffer. - REMOVE = 0x4102, // Remove the sample rate converter on this track name; - // the track is restored to the mix sample rate. - // for target RAMP_VOLUME and VOLUME (8 channels max) - // FIXME use float for these 3 to improve the dynamic range - VOLUME0 = 0x4200, - VOLUME1 = 0x4201, - AUXLEVEL = 0x4210, - // for target TIMESTRETCH - PLAYBACK_RATE = 0x4300, // Configure timestretch on this track name; - // parameter 'value' is a pointer to the new playback rate. - }; - - - // For all APIs with "name": TRACK0 <= name < TRACK0 + MAX_NUM_TRACKS - - // Allocate a track name. Returns new track name if successful, -1 on failure. - // The failure could be because of an invalid channelMask or format, or that - // the track capacity of the mixer is exceeded. - int getTrackName(audio_channel_mask_t channelMask, - audio_format_t format, int sessionId); - - // Free an allocated track by name - void deleteTrackName(int name); - - // Enable or disable an allocated track by name - void enable(int name); - void disable(int name); - - void setParameter(int name, int target, int param, void *value); - - void setBufferProvider(int name, AudioBufferProvider* bufferProvider); - void process(int64_t pts); - - uint32_t trackNames() const { return mTrackNames; } - - size_t getUnreleasedFrames(int name) const; - - static inline bool isValidPcmTrackFormat(audio_format_t format) { - switch (format) { - case AUDIO_FORMAT_PCM_8_BIT: - case AUDIO_FORMAT_PCM_16_BIT: - case AUDIO_FORMAT_PCM_24_BIT_PACKED: - case AUDIO_FORMAT_PCM_32_BIT: - case AUDIO_FORMAT_PCM_FLOAT: - return true; - default: - return false; - } - } - -private: - - enum { - // FIXME this representation permits up to 8 channels - NEEDS_CHANNEL_COUNT__MASK = 0x00000007, - }; - - enum { - NEEDS_CHANNEL_1 = 0x00000000, // mono - NEEDS_CHANNEL_2 = 0x00000001, // stereo - - // sample format is not explicitly specified, and is assumed to be AUDIO_FORMAT_PCM_16_BIT - - NEEDS_MUTE = 0x00000100, - NEEDS_RESAMPLE = 0x00001000, - NEEDS_AUX = 0x00010000, - }; - - struct state_t; - struct track_t; - - typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, - int32_t* aux); - static const int BLOCKSIZE = 16; // 4 cache lines - - struct track_t { - uint32_t needs; - - // TODO: Eventually remove legacy integer volume settings - union { - int16_t volume[MAX_NUM_VOLUMES]; // U4.12 fixed point (top bit should be zero) - int32_t volumeRL; - }; - - int32_t prevVolume[MAX_NUM_VOLUMES]; - - // 16-byte boundary - - int32_t volumeInc[MAX_NUM_VOLUMES]; - int32_t auxInc; - int32_t prevAuxLevel; - - // 16-byte boundary - - int16_t auxLevel; // 0 <= auxLevel <= MAX_GAIN_INT, but signed for mul performance - uint16_t frameCount; - - uint8_t channelCount; // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK) - uint8_t unused_padding; // formerly format, was always 16 - uint16_t enabled; // actually bool - audio_channel_mask_t channelMask; - - // actual buffer provider used by the track hooks, see DownmixerBufferProvider below - // for how the Track buffer provider is wrapped by another one when dowmixing is required - AudioBufferProvider* bufferProvider; - - // 16-byte boundary - - mutable AudioBufferProvider::Buffer buffer; // 8 bytes - - hook_t hook; - const void* in; // current location in buffer - - // 16-byte boundary - - AudioResampler* resampler; - uint32_t sampleRate; - int32_t* mainBuffer; - int32_t* auxBuffer; - - // 16-byte boundary - - /* Buffer providers are constructed to translate the track input data as needed. - * - * TODO: perhaps make a single PlaybackConverterProvider class to move - * all pre-mixer track buffer conversions outside the AudioMixer class. - * - * 1) mInputBufferProvider: The AudioTrack buffer provider. - * 2) mReformatBufferProvider: If not NULL, performs the audio reformat to - * match either mMixerInFormat or mDownmixRequiresFormat, if the downmixer - * requires reformat. For example, it may convert floating point input to - * PCM_16_bit if that's required by the downmixer. - * 3) downmixerBufferProvider: If not NULL, performs the channel remixing to match - * the number of channels required by the mixer sink. - * 4) mPostDownmixReformatBufferProvider: If not NULL, performs reformatting from - * the downmixer requirements to the mixer engine input requirements. - * 5) mTimestretchBufferProvider: Adds timestretching for playback rate - */ - AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider. -//cjh PassthruBufferProvider* mReformatBufferProvider; // provider wrapper for reformatting. -// PassthruBufferProvider* downmixerBufferProvider; // wrapper for channel conversion. -// PassthruBufferProvider* mPostDownmixReformatBufferProvider; -// PassthruBufferProvider* mTimestretchBufferProvider; - - int32_t sessionId; - - audio_format_t mMixerFormat; // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT) - audio_format_t mFormat; // input track format - audio_format_t mMixerInFormat; // mix internal format AUDIO_FORMAT_PCM_(FLOAT|16_BIT) - // each track must be converted to this format. - audio_format_t mDownmixRequiresFormat; // required downmixer format - // AUDIO_FORMAT_PCM_16_BIT if 16 bit necessary - // AUDIO_FORMAT_INVALID if no required format - - float mVolume[MAX_NUM_VOLUMES]; // floating point set volume - float mPrevVolume[MAX_NUM_VOLUMES]; // floating point previous volume - float mVolumeInc[MAX_NUM_VOLUMES]; // floating point volume increment - - float mAuxLevel; // floating point set aux level - float mPrevAuxLevel; // floating point prev aux level - float mAuxInc; // floating point aux increment - - audio_channel_mask_t mMixerChannelMask; - uint32_t mMixerChannelCount; - - AudioPlaybackRate mPlaybackRate; - - bool needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; } - bool setResampler(uint32_t trackSampleRate, uint32_t devSampleRate); - bool doesResample() const { return resampler != NULL; } - void resetResampler() { if (resampler != NULL) resampler->reset(); } - void adjustVolumeRamp(bool aux, bool useFloat = false); - size_t getUnreleasedFrames() const { return resampler != NULL ? - resampler->getUnreleasedFrames() : 0; }; - - status_t prepareForDownmix(); - void unprepareForDownmix(); - status_t prepareForReformat(); - void unprepareForReformat(); - bool setPlaybackRate(const AudioPlaybackRate &playbackRate); - void reconfigureBufferProviders(); - }; - - typedef void (*process_hook_t)(state_t* state, int64_t pts); - - // pad to 32-bytes to fill cache line - struct state_t { - uint32_t enabledTracks; - uint32_t needsChanged; - size_t frameCount; - process_hook_t hook; // one of process__*, never NULL - int32_t *outputTemp; - int32_t *resampleTemp; -//cjh NBLog::Writer* mLog; - int32_t reserved[1]; - // FIXME allocate dynamically to save some memory when maxNumTracks < MAX_NUM_TRACKS - track_t tracks[MAX_NUM_TRACKS] __attribute__((aligned(32))); - }; - - // bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc. - uint32_t mTrackNames; - - // bitmask of configured track names; ~0 if maxNumTracks == MAX_NUM_TRACKS, - // but will have fewer bits set if maxNumTracks < MAX_NUM_TRACKS - const uint32_t mConfiguredNames; - - const uint32_t mSampleRate; - -//cjh NBLog::Writer mDummyLog; -public: -//cjh void setLog(NBLog::Writer* log); -private: - state_t mState __attribute__((aligned(32))); - - // Call after changing either the enabled status of a track, or parameters of an enabled track. - // OK to call more often than that, but unnecessary. - void invalidateState(uint32_t mask); - - bool setChannelMasks(int name, - audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask); - - static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, - int32_t* aux); - static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); - static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, - int32_t* aux); - static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, - int32_t* aux); - static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, - int32_t* aux); - static void volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, - int32_t* aux); - - static void process__validate(state_t* state, int64_t pts); - static void process__nop(state_t* state, int64_t pts); - static void process__genericNoResampling(state_t* state, int64_t pts); - static void process__genericResampling(state_t* state, int64_t pts); - static void process__OneTrack16BitsStereoNoResampling(state_t* state, - int64_t pts); - - static int64_t calculateOutputPTS(const track_t& t, int64_t basePTS, - int outputFrameIndex); - - static uint64_t sLocalTimeFreq; - static pthread_once_t sOnceControl; - static void sInitRoutine(); - - /* multi-format volume mixing function (calls template functions - * in AudioMixerOps.h). The template parameters are as follows: - * - * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration) - * USEFLOATVOL (set to true if float volume is used) - * ADJUSTVOL (set to true if volume ramp parameters needs adjustment afterwards) - * TO: int32_t (Q4.27) or float - * TI: int32_t (Q4.27) or int16_t (Q0.15) or float - * TA: int32_t (Q4.27) - */ - template - static void volumeMix(TO *out, size_t outFrames, - const TI *in, TA *aux, bool ramp, AudioMixer::track_t *t); - - // multi-format process hooks - template - static void process_NoResampleOneTrack(state_t* state, int64_t pts); - - // multi-format track hooks - template - static void track__Resample(track_t* t, TO* out, size_t frameCount, - TO* temp __unused, TA* aux); - template - static void track__NoResample(track_t* t, TO* out, size_t frameCount, - TO* temp __unused, TA* aux); - - static void convertMixerFormat(void *out, audio_format_t mixerOutFormat, - void *in, audio_format_t mixerInFormat, size_t sampleCount); - - // hook types - enum { - PROCESSTYPE_NORESAMPLEONETRACK, - }; - enum { - TRACKTYPE_NOP, - TRACKTYPE_RESAMPLE, - TRACKTYPE_NORESAMPLE, - TRACKTYPE_NORESAMPLEMONO, - }; - - // functions for determining the proper process and track hooks. - static process_hook_t getProcessHook(int processType, uint32_t channelCount, - audio_format_t mixerInFormat, audio_format_t mixerOutFormat); - static hook_t getTrackHook(int trackType, uint32_t channelCount, - audio_format_t mixerInFormat, audio_format_t mixerOutFormat); -}; - -// ---------------------------------------------------------------------------- -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioMixerController.cpp b/cocos/audio/android/AudioMixerController.cpp deleted file mode 100644 index 4ffb323c097d..000000000000 --- a/cocos/audio/android/AudioMixerController.cpp +++ /dev/null @@ -1,351 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#define LOG_TAG "AudioMixerController" - -#include "audio/android/AudioMixerController.h" -#include "audio/android/AudioMixer.h" -#include "audio/android/Track.h" -#include "audio/android/OpenSLHelper.h" - -#include -#include - -namespace cocos2d { namespace experimental { - -AudioMixerController::AudioMixerController(int bufferSizeInFrames, int sampleRate, int channelCount) - : _bufferSizeInFrames(bufferSizeInFrames) - , _sampleRate(sampleRate) - , _channelCount(channelCount) - , _mixer(nullptr) - , _isPaused(false) - , _isMixingFrame(false) -{ - ALOGV("In the constructor of AudioMixerController!"); - - _mixingBuffer.size = (size_t) bufferSizeInFrames * 2 * channelCount; - // Don't use posix_memalign since it was added from API 16, it will crash on Android 2.3 - // Therefore, for a workaround, we uses memalign here. - _mixingBuffer.buf = memalign(32, _mixingBuffer.size); - memset(_mixingBuffer.buf, 0, _mixingBuffer.size); -} - -AudioMixerController::~AudioMixerController() -{ - destroy(); - - if (_mixer != nullptr) - { - delete _mixer; - _mixer = nullptr; - } - - free(_mixingBuffer.buf); -} - -bool AudioMixerController::init() -{ - _mixer = new (std::nothrow) AudioMixer(_bufferSizeInFrames, _sampleRate); - return _mixer != nullptr; -} - -bool AudioMixerController::addTrack(Track* track) -{ - ALOG_ASSERT(track != nullptr, "Shouldn't pass nullptr to addTrack"); - bool ret = false; - - std::lock_guard lk(_activeTracksMutex); - - auto iter = std::find(_activeTracks.begin(), _activeTracks.end(), track); - if (iter == _activeTracks.end()) - { - _activeTracks.push_back(track); - ret = true; - } - - return ret; -} - -template -static void removeItemFromVector(std::vector& v, T item) -{ - auto iter = std::find(v.begin(), v.end(), item); - if (iter != v.end()) - { - v.erase(iter); - } -} - -void AudioMixerController::initTrack(Track* track, std::vector& tracksToRemove) -{ - if (track->isInitialized()) - return; - - uint32_t channelMask = audio_channel_out_mask_from_count(2); - int32_t name = _mixer->getTrackName(channelMask, AUDIO_FORMAT_PCM_16_BIT, - AUDIO_SESSION_OUTPUT_MIX); - if (name < 0) - { - // If we could not get the track name, it means that there're MAX_NUM_TRACKS tracks - // So ignore the new track. - tracksToRemove.push_back(track); - } - else - { - _mixer->setBufferProvider(name, track); - _mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, - _mixingBuffer.buf); - _mixer->setParameter( - name, - AudioMixer::TRACK, - AudioMixer::MIXER_FORMAT, - (void *) (uintptr_t) AUDIO_FORMAT_PCM_16_BIT); - _mixer->setParameter( - name, - AudioMixer::TRACK, - AudioMixer::FORMAT, - (void *) (uintptr_t) AUDIO_FORMAT_PCM_16_BIT); - _mixer->setParameter( - name, - AudioMixer::TRACK, - AudioMixer::MIXER_CHANNEL_MASK, - (void *) (uintptr_t) channelMask); - _mixer->setParameter( - name, - AudioMixer::TRACK, - AudioMixer::CHANNEL_MASK, - (void *) (uintptr_t) channelMask); - - track->setName(name); - _mixer->enable(name); - - std::lock_guard lk(track->_volumeDirtyMutex); - gain_minifloat_packed_t volume = track->getVolumeLR(); - float lVolume = float_from_gain(gain_minifloat_unpack_left(volume)); - float rVolume = float_from_gain(gain_minifloat_unpack_right(volume)); - - _mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &lVolume); - _mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &rVolume); - - track->setVolumeDirty(false); - track->setInitialized(true); - } -} - -void AudioMixerController::mixOneFrame() -{ - _isMixingFrame = true; - _activeTracksMutex.lock(); - - auto mixStart = clockNow(); - - std::vector tracksToRemove; - tracksToRemove.reserve(_activeTracks.size()); - - // FOR TESTING BEGIN -// Track* track = _activeTracks[0]; -// -// AudioBufferProvider::Buffer buffer; -// buffer.frameCount = _bufferSizeInFrames; -// status_t r = track->getNextBuffer(&buffer); -//// ALOG_ASSERT(buffer.frameCount == _mixing->size / 2, "buffer.frameCount:%d, _mixing->size/2:%d", buffer.frameCount, _mixing->size/2); -// if (r == NO_ERROR) -// { -// ALOGV("getNextBuffer succeed ..."); -// memcpy(_mixing->buf, buffer.raw, _mixing->size); -// } -// if (buffer.raw == nullptr) -// { -// ALOGV("Play over ..."); -// tracksToRemove.push_back(track); -// } -// else -// { -// track->releaseBuffer(&buffer); -// } -// -// _mixing->state = BufferState::FULL; -// _activeTracksMutex.unlock(); - // FOR TESTING END - - Track::State state; - // set up the tracks. - for (auto&& track : _activeTracks) - { - state = track->getState(); - - if (state == Track::State::PLAYING) - { - initTrack(track, tracksToRemove); - - int name = track->getName(); - ALOG_ASSERT(name >= 0); - - std::lock_guard lk(track->_volumeDirtyMutex); - - if (track->isVolumeDirty()) - { - gain_minifloat_packed_t volume = track->getVolumeLR(); - float lVolume = float_from_gain(gain_minifloat_unpack_left(volume)); - float rVolume = float_from_gain(gain_minifloat_unpack_right(volume)); - - ALOGV("Track (name: %d)'s volume is dirty, update volume to L: %f, R: %f", name, lVolume, rVolume); - - _mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &lVolume); - _mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &rVolume); - - track->setVolumeDirty(false); - } - } - else if (state == Track::State::RESUMED) - { - initTrack(track, tracksToRemove); - - if (track->getPrevState() == Track::State::PAUSED) - { - _mixer->enable(track->getName()); - track->setState(Track::State::PLAYING); - } - else - { - ALOGW("Previous state (%d) isn't PAUSED, couldn't resume!", static_cast(track->getPrevState())); - } - } - else if (state == Track::State::PAUSED) - { - initTrack(track, tracksToRemove); - - if (track->getPrevState() == Track::State::PLAYING || track->getPrevState() == Track::State::RESUMED) - { - _mixer->disable(track->getName()); - } - else - { - ALOGW("Previous state (%d) isn't PLAYING, couldn't pause!", static_cast(track->getPrevState())); - } - } - else if (state == Track::State::STOPPED) - { - if (track->isInitialized()) - { - _mixer->deleteTrackName(track->getName()); - } - else - { - ALOGV("Track (%p) hasn't been initialized yet!", track); - } - tracksToRemove.push_back(track); - } - - if (track->isPlayOver()) - { - if (track->isLoop()) - { - track->reset(); - } - else - { - ALOGV("Play over ..."); - _mixer->deleteTrackName(track->getName()); - tracksToRemove.push_back(track); - track->setState(Track::State::OVER); - } - } - } - - bool hasAvailableTracks = _activeTracks.size() - tracksToRemove.size() > 0; - - if (hasAvailableTracks) - { - ALOGV_IF(_activeTracks.size() > 8, "More than 8 active tracks: %d", (int) _activeTracks.size()); - _mixer->process(AudioBufferProvider::kInvalidPTS); - } - else - { - ALOGV("Doesn't have enough tracks: %d, %d", (int) _activeTracks.size(), (int) tracksToRemove.size()); - } - - // Remove stopped or playover tracks for active tracks container - for (auto&& track : tracksToRemove) - { - removeItemFromVector(_activeTracks, track); - - if (track != nullptr && track->onStateChanged != nullptr) - { - track->onStateChanged(Track::State::DESTROYED); - } - else - { - ALOGE("track (%p) was released ...", track); - } - } - - _activeTracksMutex.unlock(); - - auto mixEnd = clockNow(); - float mixInterval = intervalInMS(mixStart, mixEnd); - ALOGV_IF(mixInterval > 1.0f, "Mix a frame waste: %fms", mixInterval); - - _isMixingFrame = false; -} - -void AudioMixerController::destroy() -{ - while (_isMixingFrame) - { - usleep(10); - } - usleep(2000); // Wait for more 2ms -} - -void AudioMixerController::pause() -{ - _isPaused = true; -} - -void AudioMixerController::resume() -{ - _isPaused = false; -} - -bool AudioMixerController::hasPlayingTacks() -{ - std::lock_guard lk (_activeTracksMutex); - if (_activeTracks.empty()) - return false; - - for (auto&& track : _activeTracks) - { - Track::State state = track->getState(); - if (state == Track::State::IDLE || state == Track::State::PLAYING || state == Track::State::RESUMED) - { - return true; - } - } - - return false; -} - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioMixerController.h b/cocos/audio/android/AudioMixerController.h deleted file mode 100644 index ce2097010f4d..000000000000 --- a/cocos/audio/android/AudioMixerController.h +++ /dev/null @@ -1,88 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#pragma once - -#include "audio/android/utils/Errors.h" - -#include -#include -#include -#include -#include - -namespace cocos2d { namespace experimental { - -class Track; -class AudioMixer; - -class AudioMixerController -{ -public: - - struct OutputBuffer - { - void* buf; - size_t size; - }; - - AudioMixerController(int bufferSizeInFrames, int sampleRate, int channelCount); - - ~AudioMixerController(); - - bool init(); - - bool addTrack(Track* track); - bool hasPlayingTacks(); - - void pause(); - void resume(); - inline bool isPaused() const { return _isPaused; }; - - void mixOneFrame(); - - inline OutputBuffer* current() { return &_mixingBuffer; } - -private: - void destroy(); - void initTrack(Track* track, std::vector& tracksToRemove); - -private: - int _bufferSizeInFrames; - int _sampleRate; - int _channelCount; - - AudioMixer* _mixer; - - std::mutex _activeTracksMutex; - std::vector _activeTracks; - - OutputBuffer _mixingBuffer; - - std::atomic_bool _isPaused; - std::atomic_bool _isMixingFrame; -}; - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioMixerOps.h b/cocos/audio/android/AudioMixerOps.h deleted file mode 100644 index 1e74daeb269f..000000000000 --- a/cocos/audio/android/AudioMixerOps.h +++ /dev/null @@ -1,450 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "audio/android/cutils/log.h" - -namespace cocos2d { namespace experimental { - -/* Behavior of is_same<>::value is true if the types are identical, - * false otherwise. Identical to the STL std::is_same. - */ -template -struct is_same -{ - static const bool value = false; -}; - -template -struct is_same // partial specialization -{ - static const bool value = true; -}; - - -/* MixMul is a multiplication operator to scale an audio input signal - * by a volume gain, with the formula: - * - * O(utput) = I(nput) * V(olume) - * - * The output, input, and volume may have different types. - * There are 27 variants, of which 14 are actually defined in an - * explicitly templated class. - * - * The following type variables and the underlying meaning: - * - * Output type TO: int32_t (Q4.27) or int16_t (Q.15) or float [-1,1] - * Input signal type TI: int32_t (Q4.27) or int16_t (Q.15) or float [-1,1] - * Volume type TV: int32_t (U4.28) or int16_t (U4.12) or float [-1,1] - * - * For high precision audio, only the = - * needs to be accelerated. This is perhaps the easiest form to do quickly as well. - * - * A generic version is NOT defined to catch any mistake of using it. - */ - -template -TO MixMul(TI value, TV volume); - -template <> -inline int32_t MixMul(int16_t value, int16_t volume) { - return value * volume; -} - -template <> -inline int32_t MixMul(int32_t value, int16_t volume) { - return (value >> 12) * volume; -} - -template <> -inline int32_t MixMul(int16_t value, int32_t volume) { - return value * (volume >> 16); -} - -template <> -inline int32_t MixMul(int32_t value, int32_t volume) { - return (value >> 12) * (volume >> 16); -} - -template <> -inline float MixMul(float value, int16_t volume) { - static const float norm = 1. / (1 << 12); - return value * volume * norm; -} - -template <> -inline float MixMul(float value, int32_t volume) { - static const float norm = 1. / (1 << 28); - return value * volume * norm; -} - -template <> -inline int16_t MixMul(float value, int16_t volume) { - return clamp16_from_float(MixMul(value, volume)); -} - -template <> -inline int16_t MixMul(float value, int32_t volume) { - return clamp16_from_float(MixMul(value, volume)); -} - -template <> -inline float MixMul(int16_t value, int16_t volume) { - static const float norm = 1. / (1 << (15 + 12)); - return static_cast(value) * static_cast(volume) * norm; -} - -template <> -inline float MixMul(int16_t value, int32_t volume) { - static const float norm = 1. / (1ULL << (15 + 28)); - return static_cast(value) * static_cast(volume) * norm; -} - -template <> -inline int16_t MixMul(int16_t value, int16_t volume) { - return clamp16(MixMul(value, volume) >> 12); -} - -template <> -inline int16_t MixMul(int32_t value, int16_t volume) { - return clamp16(MixMul(value, volume) >> 12); -} - -template <> -inline int16_t MixMul(int16_t value, int32_t volume) { - return clamp16(MixMul(value, volume) >> 12); -} - -template <> -inline int16_t MixMul(int32_t value, int32_t volume) { - return clamp16(MixMul(value, volume) >> 12); -} - -/* Required for floating point volume. Some are needed for compilation but - * are not needed in execution and should be removed from the final build by - * an optimizing compiler. - */ -template <> -inline float MixMul(float value, float volume) { - return value * volume; -} - -template <> -inline float MixMul(int16_t value, float volume) { - static const float float_from_q_15 = 1. / (1 << 15); - return value * volume * float_from_q_15; -} - -template <> -inline int32_t MixMul(int32_t value, float volume) { - LOG_ALWAYS_FATAL("MixMul Runtime Should not be here"); - return value * volume; -} - -template <> -inline int32_t MixMul(int16_t value, float volume) { - LOG_ALWAYS_FATAL("MixMul Runtime Should not be here"); - static const float u4_12_from_float = (1 << 12); - return value * volume * u4_12_from_float; -} - -template <> -inline int16_t MixMul(int16_t value, float volume) { - LOG_ALWAYS_FATAL("MixMul Runtime Should not be here"); - return clamp16_from_float(MixMul(value, volume)); -} - -template <> -inline int16_t MixMul(float value, float volume) { - return clamp16_from_float(value * volume); -} - -/* - * MixAccum is used to add into an accumulator register of a possibly different - * type. The TO and TI types are the same as MixMul. - */ - -template -inline void MixAccum(TO *auxaccum, TI value) { - if (!is_same::value) { - LOG_ALWAYS_FATAL("MixAccum type not properly specialized: %zu %zu\n", - sizeof(TO), sizeof(TI)); - } - *auxaccum += value; -} - -template<> -inline void MixAccum(float *auxaccum, int16_t value) { - static const float norm = 1. / (1 << 15); - *auxaccum += norm * value; -} - -template<> -inline void MixAccum(float *auxaccum, int32_t value) { - static const float norm = 1. / (1 << 27); - *auxaccum += norm * value; -} - -template<> -inline void MixAccum(int32_t *auxaccum, int16_t value) { - *auxaccum += value << 12; -} - -template<> -inline void MixAccum(int32_t *auxaccum, float value) { - *auxaccum += clampq4_27_from_float(value); -} - -/* MixMulAux is just like MixMul except it combines with - * an accumulator operation MixAccum. - */ - -template -inline TO MixMulAux(TI value, TV volume, TA *auxaccum) { - MixAccum(auxaccum, value); - return MixMul(value, volume); -} - -/* MIXTYPE is used to determine how the samples in the input frame - * are mixed with volume gain into the output frame. - * See the volumeRampMulti functions below for more details. - */ -enum { - MIXTYPE_MULTI, - MIXTYPE_MONOEXPAND, - MIXTYPE_MULTI_SAVEONLY, - MIXTYPE_MULTI_MONOVOL, - MIXTYPE_MULTI_SAVEONLY_MONOVOL, -}; - -/* - * The volumeRampMulti and volumeRamp functions take a MIXTYPE - * which indicates the per-frame mixing and accumulation strategy. - * - * MIXTYPE_MULTI: - * NCHAN represents number of input and output channels. - * TO: int32_t (Q4.27) or float - * TI: int32_t (Q4.27) or int16_t (Q0.15) or float - * TV: int32_t (U4.28) or int16_t (U4.12) or float - * vol: represents a volume array. - * - * This accumulates into the out pointer. - * - * MIXTYPE_MONOEXPAND: - * Single input channel. NCHAN represents number of output channels. - * TO: int32_t (Q4.27) or float - * TI: int32_t (Q4.27) or int16_t (Q0.15) or float - * TV: int32_t (U4.28) or int16_t (U4.12) or float - * Input channel count is 1. - * vol: represents volume array. - * - * This accumulates into the out pointer. - * - * MIXTYPE_MULTI_SAVEONLY: - * NCHAN represents number of input and output channels. - * TO: int16_t (Q.15) or float - * TI: int32_t (Q4.27) or int16_t (Q0.15) or float - * TV: int32_t (U4.28) or int16_t (U4.12) or float - * vol: represents a volume array. - * - * MIXTYPE_MULTI_SAVEONLY does not accumulate into the out pointer. - * - * MIXTYPE_MULTI_MONOVOL: - * Same as MIXTYPE_MULTI, but uses only volume[0]. - * - * MIXTYPE_MULTI_SAVEONLY_MONOVOL: - * Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0]. - * - */ - -template -inline void volumeRampMulti(TO* out, size_t frameCount, - const TI* in, TA* aux, TV *vol, const TV *volinc, TAV *vola, TAV volainc) -{ -#ifdef ALOGVV - ALOGVV("volumeRampMulti, MIXTYPE:%d\n", MIXTYPE); -#endif - if (aux != NULL) { - do { - TA auxaccum = 0; - switch (MIXTYPE) { - case MIXTYPE_MULTI: - for (int i = 0; i < NCHAN; ++i) { - *out++ += MixMulAux(*in++, vol[i], &auxaccum); - vol[i] += volinc[i]; - } - break; - case MIXTYPE_MONOEXPAND: - for (int i = 0; i < NCHAN; ++i) { - *out++ += MixMulAux(*in, vol[i], &auxaccum); - vol[i] += volinc[i]; - } - in++; - break; - case MIXTYPE_MULTI_SAVEONLY: - for (int i = 0; i < NCHAN; ++i) { - *out++ = MixMulAux(*in++, vol[i], &auxaccum); - vol[i] += volinc[i]; - } - break; - case MIXTYPE_MULTI_MONOVOL: - for (int i = 0; i < NCHAN; ++i) { - *out++ += MixMulAux(*in++, vol[0], &auxaccum); - } - vol[0] += volinc[0]; - break; - case MIXTYPE_MULTI_SAVEONLY_MONOVOL: - for (int i = 0; i < NCHAN; ++i) { - *out++ = MixMulAux(*in++, vol[0], &auxaccum); - } - vol[0] += volinc[0]; - break; - default: - LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); - break; - } - auxaccum /= NCHAN; - *aux++ += MixMul(auxaccum, *vola); - vola[0] += volainc; - } while (--frameCount); - } else { - do { - switch (MIXTYPE) { - case MIXTYPE_MULTI: - for (int i = 0; i < NCHAN; ++i) { - *out++ += MixMul(*in++, vol[i]); - vol[i] += volinc[i]; - } - break; - case MIXTYPE_MONOEXPAND: - for (int i = 0; i < NCHAN; ++i) { - *out++ += MixMul(*in, vol[i]); - vol[i] += volinc[i]; - } - in++; - break; - case MIXTYPE_MULTI_SAVEONLY: - for (int i = 0; i < NCHAN; ++i) { - *out++ = MixMul(*in++, vol[i]); - vol[i] += volinc[i]; - } - break; - case MIXTYPE_MULTI_MONOVOL: - for (int i = 0; i < NCHAN; ++i) { - *out++ += MixMul(*in++, vol[0]); - } - vol[0] += volinc[0]; - break; - case MIXTYPE_MULTI_SAVEONLY_MONOVOL: - for (int i = 0; i < NCHAN; ++i) { - *out++ = MixMul(*in++, vol[0]); - } - vol[0] += volinc[0]; - break; - default: - LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); - break; - } - } while (--frameCount); - } -} - -template -inline void volumeMulti(TO* out, size_t frameCount, - const TI* in, TA* aux, const TV *vol, TAV vola) -{ -#ifdef ALOGVV - ALOGVV("volumeMulti MIXTYPE:%d\n", MIXTYPE); -#endif - if (aux != NULL) { - do { - TA auxaccum = 0; - switch (MIXTYPE) { - case MIXTYPE_MULTI: - for (int i = 0; i < NCHAN; ++i) { - *out++ += MixMulAux(*in++, vol[i], &auxaccum); - } - break; - case MIXTYPE_MONOEXPAND: - for (int i = 0; i < NCHAN; ++i) { - *out++ += MixMulAux(*in, vol[i], &auxaccum); - } - in++; - break; - case MIXTYPE_MULTI_SAVEONLY: - for (int i = 0; i < NCHAN; ++i) { - *out++ = MixMulAux(*in++, vol[i], &auxaccum); - } - break; - case MIXTYPE_MULTI_MONOVOL: - for (int i = 0; i < NCHAN; ++i) { - *out++ += MixMulAux(*in++, vol[0], &auxaccum); - } - break; - case MIXTYPE_MULTI_SAVEONLY_MONOVOL: - for (int i = 0; i < NCHAN; ++i) { - *out++ = MixMulAux(*in++, vol[0], &auxaccum); - } - break; - default: - LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); - break; - } - auxaccum /= NCHAN; - *aux++ += MixMul(auxaccum, vola); - } while (--frameCount); - } else { - do { - switch (MIXTYPE) { - case MIXTYPE_MULTI: - for (int i = 0; i < NCHAN; ++i) { - *out++ += MixMul(*in++, vol[i]); - } - break; - case MIXTYPE_MONOEXPAND: - for (int i = 0; i < NCHAN; ++i) { - *out++ += MixMul(*in, vol[i]); - } - in++; - break; - case MIXTYPE_MULTI_SAVEONLY: - for (int i = 0; i < NCHAN; ++i) { - *out++ = MixMul(*in++, vol[i]); - } - break; - case MIXTYPE_MULTI_MONOVOL: - for (int i = 0; i < NCHAN; ++i) { - *out++ += MixMul(*in++, vol[0]); - } - break; - case MIXTYPE_MULTI_SAVEONLY_MONOVOL: - for (int i = 0; i < NCHAN; ++i) { - *out++ = MixMul(*in++, vol[0]); - } - break; - default: - LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); - break; - } - } while (--frameCount); - } -} - -}} // namespace cocos2d { namespace experimental { - diff --git a/cocos/audio/android/AudioPlayer.cpp b/cocos/audio/android/AudioPlayer.cpp new file mode 100644 index 000000000000..a8de096652a8 --- /dev/null +++ b/cocos/audio/android/AudioPlayer.cpp @@ -0,0 +1,386 @@ +/**************************************************************************** + Copyright (c) 2014-2017 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + Copyright (c) 2018 HALX99. + + 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. + ****************************************************************************/ + +#define LOG_TAG "AudioPlayer" + +#include "platform/CCPlatformConfig.h" + +#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID +#include "audio/android/log.h" +#include "audio/android/AudioPlayer.h" +#include "audio/android/AudioCache.h" +#include "platform/CCFileUtils.h" +#include "audio/android/AudioDecoderManager.h" +#include "audio/android/Audiodecoder.h" + +#define VERY_VERY_VERBOSE_LOGGING +#ifdef VERY_VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +#else +#define ALOGVV(...) do{} while(false) +#endif + + +using namespace cocos2d; +using namespace cocos2d::experimental; + +namespace { +unsigned int __idIndex = 0; +} + +AudioPlayer::AudioPlayer() +: _audioCache(nullptr) +, _finishCallbak(nullptr) +, _isDestroyed(false) +, _removeByAudioEngine(false) +, _ready(false) +, _currTime(0.0f) +, _streamingSource(false) +, _rotateBufferThread(nullptr) +, _timeDirty(false) +, _isRotateThreadExited(false) +, _id(++__idIndex) +{ + memset(_bufferIds, 0, sizeof(_bufferIds)); +} + +AudioPlayer::~AudioPlayer() +{ + ALOGVV("~AudioPlayer() (%p), id=%u", this, _id); + destroy(); + + if (_streamingSource) + { + alDeleteBuffers(QUEUEBUFFER_NUM, _bufferIds); + } +} + +void AudioPlayer::destroy() +{ + if (_isDestroyed) + return; + + ALOGVV("AudioPlayer::destroy begin, id=%u", _id); + + _isDestroyed = true; + + do + { + if (_audioCache != nullptr) + { + if (_audioCache->_state == AudioCache::State::INITIAL) + { + ALOGV("AudioPlayer::destroy, id=%u, cache isn't ready!", _id); + break; + } + + while (!_audioCache->_isLoadingFinished) + { + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + } + + // Wait for play2d to be finished. + _play2dMutex.lock(); + _play2dMutex.unlock(); + + if (_streamingSource) + { + if (_rotateBufferThread != nullptr) + { + while (!_isRotateThreadExited) + { + _sleepCondition.notify_one(); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + + if (_rotateBufferThread->joinable()) { + _rotateBufferThread->join(); + } + + delete _rotateBufferThread; + _rotateBufferThread = nullptr; + ALOGVV("rotateBufferThread exited!"); + } + } + } while(false); + + ALOGVV("Before alSourceStop"); + alSourceStop(_alSource); CHECK_AL_ERROR_DEBUG(); + ALOGVV("Before alSourcei"); + alSourcei(_alSource, AL_BUFFER, 0); CHECK_AL_ERROR_DEBUG(); + + _removeByAudioEngine = true; + + _ready = false; + ALOGVV("AudioPlayer::destroy end, id=%u", _id); +} + +void AudioPlayer::setCache(AudioCache* cache) +{ + _audioCache = cache; +} + +bool AudioPlayer::play2d() +{ + std::unique_lock lck(_play2dMutex); + ALOGV("AudioPlayer::play2d, _alSource: %u, player id=%u", _alSource, _id); + + if (_isDestroyed) + return false; + + /*********************************************************************/ + /* Note that it may be in sub thread or in main thread. **/ + /*********************************************************************/ + bool ret = false; + do + { + if (_audioCache->_state != AudioCache::State::READY) + { + ALOGE("alBuffer isn't ready for play!"); + break; + } + + alSourcei(_alSource, AL_BUFFER, 0);CHECK_AL_ERROR_DEBUG(); + alSourcef(_alSource, AL_PITCH, 1.0f);CHECK_AL_ERROR_DEBUG(); + alSourcef(_alSource, AL_GAIN, _volume);CHECK_AL_ERROR_DEBUG(); + alSourcei(_alSource, AL_LOOPING, AL_FALSE);CHECK_AL_ERROR_DEBUG(); + + if (_audioCache->_queBufferFrames == 0) + { + if (_loop) { + alSourcei(_alSource, AL_LOOPING, AL_TRUE); + CHECK_AL_ERROR_DEBUG(); + } + } + else + { + alGenBuffers(QUEUEBUFFER_NUM, _bufferIds); + + auto alError = alGetError(); + if (alError == AL_NO_ERROR) + { + for (int index = 0; index < QUEUEBUFFER_NUM; ++index) + { + alBufferData(_bufferIds[index], _audioCache->_format, _audioCache->_queBuffers[index], _audioCache->_queBufferSize[index], _audioCache->_sampleRate); + } + CHECK_AL_ERROR_DEBUG(); + } + else + { + ALOGE("%s:alGenBuffers error code:%x", __FUNCTION__,alError); + break; + } + _streamingSource = true; + } + + { + std::unique_lock lk(_sleepMutex); + if (_isDestroyed) + break; + + if (_streamingSource) + { + // To continuously stream audio from a source without interruption, buffer queuing is required. + alSourceQueueBuffers(_alSource, QUEUEBUFFER_NUM, _bufferIds); + CHECK_AL_ERROR_DEBUG(); + _rotateBufferThread = new std::thread(&AudioPlayer::rotateBufferThread, this, _audioCache->_queBufferFrames * QUEUEBUFFER_NUM + 1); + } + else + { + alSourcei(_alSource, AL_BUFFER, _audioCache->_alBufferId); + CHECK_AL_ERROR_DEBUG(); + } + + alSourcePlay(_alSource); + } + + auto alError = alGetError(); + if (alError != AL_NO_ERROR) + { + ALOGE("%s:alSourcePlay error code:%x", __FUNCTION__,alError); + break; + } + + ALint state; + alGetSourcei(_alSource, AL_SOURCE_STATE, &state); + if (state != AL_PLAYING) + { + ALOGE("state isn't playing, %d, %s, cache id=%u, player id=%u", state, _audioCache->_fileFullPath.c_str(), _audioCache->_id, _id); + } + assert(state == AL_PLAYING); + _ready = true; + ret = true; + } while (false); + + if (!ret) + { + _removeByAudioEngine = true; + } + + return ret; +} + +void AudioPlayer::rotateBufferThread(int offsetFrame) +{ + char* tmpBuffer = nullptr; + AudioDecoder* decoder = AudioDecoderManager::createDecoder(_audioCache->_fileFullPath.c_str()); + long long rotateSleepTime = static_cast(QUEUEBUFFER_TIME_STEP * 1000) / 2; + do + { + BREAK_IF(decoder == nullptr || !decoder->open(_audioCache->_fileFullPath.c_str())); + + uint32_t framesRead = 0; + const uint32_t framesToRead = _audioCache->_queBufferFrames; + const uint32_t bufferSize = framesToRead * decoder->getBytesPerFrame(); + tmpBuffer = (char*)malloc(bufferSize); + memset(tmpBuffer, 0, bufferSize); + + if (offsetFrame != 0) { + decoder->seek(offsetFrame); + } + + ALint sourceState; + ALint bufferProcessed = 0; + bool needToExitThread = false; + + while (!_isDestroyed) { + alGetSourcei(_alSource, AL_SOURCE_STATE, &sourceState); + if (sourceState == AL_PLAYING) { + alGetSourcei(_alSource, AL_BUFFERS_PROCESSED, &bufferProcessed); + while (bufferProcessed > 0) { + bufferProcessed--; + if (_timeDirty) { + _timeDirty = false; + offsetFrame = _currTime * decoder->getSampleRate(); + decoder->seek(offsetFrame); + } + else { + _currTime += QUEUEBUFFER_TIME_STEP; + if (_currTime > _audioCache->_duration) { + if (_loop) { + _currTime = 0.0f; + } else { + _currTime = _audioCache->_duration; + } + } + } + + framesRead = decoder->readFixedFrames(framesToRead, tmpBuffer); + + if (framesRead == 0) { + if (_loop) { + decoder->seek(0); + framesRead = decoder->readFixedFrames(framesToRead, tmpBuffer); + } else { + needToExitThread = true; + break; + } + } + /* + While the source is playing, alSourceUnqueueBuffers can be called to remove buffers which have + already played. Those buffers can then be filled with new data or discarded. New or refilled + buffers can then be attached to the playing source using alSourceQueueBuffers. As long as there is + always a new buffer to play in the queue, the source will continue to play. + */ + ALuint bid; + alSourceUnqueueBuffers(_alSource, 1, &bid); + alBufferData(bid, _audioCache->_format, tmpBuffer, framesRead * decoder->getBytesPerFrame(), decoder->getSampleRate()); + alSourceQueueBuffers(_alSource, 1, &bid); + } + } + + std::unique_lock lk(_sleepMutex); + if (_isDestroyed || needToExitThread) { + break; + } + + _sleepCondition.wait_for(lk,std::chrono::milliseconds(rotateSleepTime)); + } + + } while(false); + + ALOGV("Exit rotate buffer thread ..."); + if (decoder != nullptr) + { + decoder->close(); + } + AudioDecoderManager::destroyDecoder(decoder); + free(tmpBuffer); + _isRotateThreadExited = true; + ALOGV("%s exited.\n", __FUNCTION__); +} + +bool AudioPlayer::isFinished() const +{ + ALint sourceState; + alGetSourcei(_alSource, AL_SOURCE_STATE, &sourceState); + /* Make sure the source hasn't underrun */ + if (sourceState != AL_PLAYING && sourceState != AL_PAUSED) + { + if (!_streamingSource || _isRotateThreadExited) + return true; + + ALint queued; + + /* If no buffers are queued, playback is finished */ + alGetSourcei(_alSource, AL_BUFFERS_QUEUED, &queued); + if (queued == 0) + return true; + + alSourcePlay(_alSource); + if (alGetError() != AL_NO_ERROR) + { + ALOGE("Error restarting playback\n"); + return true; + } + } + return false; +} + +bool AudioPlayer::setLoop(bool loop) +{ + if (!_isDestroyed ) { + _loop = loop; + return true; + } + + return false; +} + +bool AudioPlayer::setTime(float time) +{ + if (!_isDestroyed && time >= 0.0f && time < _audioCache->_duration) { + + _currTime = time; + _timeDirty = true; + + return true; + } + return false; +} + +#endif diff --git a/cocos/audio/android/AudioPlayer.h b/cocos/audio/android/AudioPlayer.h new file mode 100644 index 000000000000..5617dff5561e --- /dev/null +++ b/cocos/audio/android/AudioPlayer.h @@ -0,0 +1,100 @@ +/**************************************************************************** + Copyright (c) 2014-2017 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + Copyright (c) 2018 HALX99. + + 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. + ****************************************************************************/ + +#pragma once + +#include "platform/CCPlatformConfig.h" + +#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID + +#include +#include +#include +#include +#ifdef OPENAL_PLAIN_INCLUDES +#include +#else +#include +#endif +#include "platform/CCPlatformMacros.h" + +NS_CC_BEGIN +namespace experimental{ + +class AudioCache; +class AudioEngineImpl; + +class CC_DLL AudioPlayer +{ +public: + AudioPlayer(); + ~AudioPlayer(); + + void destroy(); + + //queue buffer related stuff + bool setTime(float time); + float getTime() { return _currTime;} + bool setLoop(bool loop); + + bool isFinished() const; +protected: + void setCache(AudioCache* cache); + void rotateBufferThread(int offsetFrame); + bool play2d(); + + AudioCache* _audioCache; + + float _volume; + bool _loop; + std::function _finishCallbak; + + bool _isDestroyed; + bool _removeByAudioEngine; + bool _ready; + ALuint _alSource; + + //play by circular buffer + float _currTime; + bool _streamingSource; + ALuint _bufferIds[3]; + std::thread* _rotateBufferThread; + std::condition_variable _sleepCondition; + std::mutex _sleepMutex; + bool _timeDirty; + bool _isRotateThreadExited; + + std::mutex _play2dMutex; + + unsigned int _id; + friend class AudioEngineImpl; +}; + +} +NS_CC_END + +#endif //CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID + diff --git a/cocos/audio/android/AudioPlayerProvider.cpp b/cocos/audio/android/AudioPlayerProvider.cpp deleted file mode 100644 index e42506482f52..000000000000 --- a/cocos/audio/android/AudioPlayerProvider.cpp +++ /dev/null @@ -1,519 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#define LOG_TAG "AudioPlayerProvider" - -#include "audio/android/AudioPlayerProvider.h" -#include "audio/android/UrlAudioPlayer.h" -#include "audio/android/PcmAudioPlayer.h" -#include "audio/android/AudioDecoder.h" -#include "audio/android/AudioDecoderProvider.h" -#include "audio/android/AudioMixerController.h" -#include "audio/android/PcmAudioService.h" -#include "audio/android/CCThreadPool.h" -#include "audio/android/ICallerThreadUtils.h" -#include "audio/android/utils/Utils.h" - -#include -#include -#include // for std::find_if - -namespace cocos2d { namespace experimental { - -static int getSystemAPILevel() -{ - static int __systemApiLevel = -1; - if (__systemApiLevel > 0) - { - return __systemApiLevel; - } - - int apiLevel = getSDKVersion(); - if (apiLevel > 0) - { - ALOGD("Android API level: %d", apiLevel); - } - else - { - ALOGE("Fail to get Android API level!"); - } - __systemApiLevel = apiLevel; - return apiLevel; -} - -struct AudioFileIndicator -{ - std::string extension; - int smallSizeIndicator; -}; - -static AudioFileIndicator __audioFileIndicator[] = { - {"default", 128000}, // If we could not handle the audio format, return default value, the position should be first. - {".wav", 1024000}, - {".ogg", 128000}, - {".mp3", 160000} -}; - -AudioPlayerProvider::AudioPlayerProvider(SLEngineItf engineItf, SLObjectItf outputMixObject, - int deviceSampleRate, int bufferSizeInFrames, - const FdGetterCallback &fdGetterCallback, - ICallerThreadUtils* callerThreadUtils) - : _engineItf(engineItf), _outputMixObject(outputMixObject), - _deviceSampleRate(deviceSampleRate), _bufferSizeInFrames(bufferSizeInFrames), - _fdGetterCallback(fdGetterCallback), _callerThreadUtils(callerThreadUtils), - _pcmAudioService(nullptr), _mixController(nullptr), - _threadPool(ThreadPool::newCachedThreadPool(1, 8, 5, 2, 2)) -{ - ALOGI("deviceSampleRate: %d, bufferSizeInFrames: %d", _deviceSampleRate, _bufferSizeInFrames); - if (getSystemAPILevel() >= 17) - { - _mixController = new (std::nothrow) AudioMixerController(_bufferSizeInFrames, _deviceSampleRate, 2); - _mixController->init(); - _pcmAudioService = new (std::nothrow) PcmAudioService(engineItf, outputMixObject); - _pcmAudioService->init(_mixController, 2, deviceSampleRate, bufferSizeInFrames * 2); - } - - ALOG_ASSERT(callerThreadUtils != nullptr, "Caller thread utils parameter should not be nullptr!"); -} - -AudioPlayerProvider::~AudioPlayerProvider() -{ - ALOGV("~AudioPlayerProvider()"); - UrlAudioPlayer::stopAll(); - - SL_SAFE_DELETE(_pcmAudioService); - SL_SAFE_DELETE(_mixController); - SL_SAFE_DELETE(_threadPool); -} - -IAudioPlayer *AudioPlayerProvider::getAudioPlayer(const std::string &audioFilePath) -{ - // Pcm data decoding by OpenSLES API only supports in API level 17 and later. - if (getSystemAPILevel() < 17) - { - AudioFileInfo info = getFileInfo(audioFilePath); - if (info.isValid()) - { - return createUrlAudioPlayer(info); - } - - return nullptr; - } - - IAudioPlayer *player = nullptr; - - _pcmCacheMutex.lock(); - auto iter = _pcmCache.find(audioFilePath); - if (iter != _pcmCache.end()) - {// Found pcm cache means it was used to be a PcmAudioService - PcmData pcmData = iter->second; - _pcmCacheMutex.unlock(); - player = obtainPcmAudioPlayer(audioFilePath, pcmData); - ALOGV_IF(player == nullptr, "%s, %d: player is nullptr, path: %s", __FUNCTION__, __LINE__, audioFilePath.c_str()); - } - else - { - _pcmCacheMutex.unlock(); - // Check audio file size to determine to use a PcmAudioService or UrlAudioPlayer, - // generally PcmAudioService is used for playing short audio like game effects while - // playing background music uses UrlAudioPlayer - AudioFileInfo info = getFileInfo(audioFilePath); - if (info.isValid()) - { - if (isSmallFile(info)) - { - // Put an empty lambda to preloadEffect since we only want the future object to get PcmData - auto pcmData = std::make_shared(); - auto isSucceed = std::make_shared(false); - auto isReturnFromCache = std::make_shared(false); - auto isPreloadFinished = std::make_shared(false); - - std::thread::id threadId = std::this_thread::get_id(); - - void* infoPtr = &info; - std::string url = info.url; - preloadEffect(info, [infoPtr, url, threadId, pcmData, isSucceed, isReturnFromCache, isPreloadFinished](bool succeed, PcmData data){ - // If the callback is in the same thread as caller's, it means that we found it - // in the cache - *isReturnFromCache = std::this_thread::get_id() == threadId; - *pcmData = data; - *isSucceed = succeed; - *isPreloadFinished = true; - ALOGV("FileInfo (%p), Set isSucceed flag: %d, path: %s", infoPtr, succeed, url.c_str()); - }, true); - - if (!*isReturnFromCache && !*isPreloadFinished) - { - std::unique_lock lk(_preloadWaitMutex); - // Wait for 2 seconds for the decoding in sub thread finishes. - ALOGV("FileInfo (%p), Waiting preload (%s) to finish ...", &info, audioFilePath.c_str()); - _preloadWaitCond.wait_for(lk, std::chrono::seconds(2)); - ALOGV("FileInfo (%p), Waitup preload (%s) ...", &info, audioFilePath.c_str()); - } - - if (*isSucceed) - { - if (pcmData->isValid()) - { - player = obtainPcmAudioPlayer(info.url, *pcmData); - ALOGV_IF(player == nullptr, "%s, %d: player is nullptr, path: %s", __FUNCTION__, __LINE__, audioFilePath.c_str()); - } - else - { - ALOGE("pcm data is invalid, path: %s", audioFilePath.c_str()); - } - } - else - { - ALOGE("FileInfo (%p), preloadEffect (%s) failed", &info, audioFilePath.c_str()); - } - } - else - { - player = createUrlAudioPlayer(info); - ALOGV_IF(player == nullptr, "%s, %d: player is nullptr, path: %s", __FUNCTION__, __LINE__, audioFilePath.c_str()); - } - } - else - { - ALOGE("File info is invalid, path: %s", audioFilePath.c_str()); - } - } - - ALOGV_IF(player == nullptr, "%s, %d return nullptr", __FUNCTION__, __LINE__); - return player; -} - -void AudioPlayerProvider::preloadEffect(const std::string &audioFilePath, const PreloadCallback& cb) -{ - // Pcm data decoding by OpenSLES API only supports in API level 17 and later. - if (getSystemAPILevel() < 17) - { - PcmData data; - cb(true, data); - return; - } - - _pcmCacheMutex.lock(); - auto&& iter = _pcmCache.find(audioFilePath); - if (iter != _pcmCache.end()) - { - ALOGV("preload return from cache: (%s)", audioFilePath.c_str()); - _pcmCacheMutex.unlock(); - cb(true, iter->second); - return; - } - _pcmCacheMutex.unlock(); - - auto info = getFileInfo(audioFilePath); - preloadEffect(info, [this, cb, audioFilePath](bool succeed, PcmData data){ - - _callerThreadUtils->performFunctionInCallerThread([this, succeed, data, cb](){ - cb(succeed, data); - }); - - }, false); -} - -// Used internally -void AudioPlayerProvider::preloadEffect(const AudioFileInfo &info, const PreloadCallback& cb, bool isPreloadInPlay2d) -{ - PcmData pcmData; - - if (!info.isValid()) - { - cb(false, pcmData); - return; - } - - if (isSmallFile(info)) - { - std::string audioFilePath = info.url; - - // 1. First time check, if it wasn't in the cache, goto 2 step - _pcmCacheMutex.lock(); - auto&& iter = _pcmCache.find(audioFilePath); - if (iter != _pcmCache.end()) - { - ALOGV("1. Return pcm data from cache, url: %s", info.url.c_str()); - _pcmCacheMutex.unlock(); - cb(true, iter->second); - return; - } - _pcmCacheMutex.unlock(); - - { - // 2. Check whether the audio file is being preloaded, if it has been removed from map just now, - // goto step 3 - std::lock_guard lk(_preloadCallbackMutex); - - auto&& preloadIter = _preloadCallbackMap.find(audioFilePath); - if (preloadIter != _preloadCallbackMap.end()) - { - ALOGV("audio (%s) is being preloaded, add to callback vector!", audioFilePath.c_str()); - PreloadCallbackParam param; - param.callback = cb; - param.isPreloadInPlay2d = isPreloadInPlay2d; - preloadIter->second.push_back(std::move(param)); - return; - } - - // 3. Check it in cache again. If it has been removed from map just now, the file is in - // the cache absolutely. - _pcmCacheMutex.lock(); - auto&& iter = _pcmCache.find(audioFilePath); - if (iter != _pcmCache.end()) - { - ALOGV("2. Return pcm data from cache, url: %s", info.url.c_str()); - _pcmCacheMutex.unlock(); - cb(true, iter->second); - return; - } - _pcmCacheMutex.unlock(); - - PreloadCallbackParam param; - param.callback = cb; - param.isPreloadInPlay2d = isPreloadInPlay2d; - std::vector callbacks; - callbacks.push_back(std::move(param)); - _preloadCallbackMap.insert(std::make_pair(audioFilePath, std::move(callbacks))); - } - - _threadPool->pushTask([this, audioFilePath](int tid) { - ALOGV("AudioPlayerProvider::preloadEffect: (%s)", audioFilePath.c_str()); - PcmData d; - AudioDecoder* decoder = AudioDecoderProvider::createAudioDecoder(_engineItf, audioFilePath, _bufferSizeInFrames, _deviceSampleRate, _fdGetterCallback); - bool ret = decoder != nullptr && decoder->start(); - if (ret) - { - d = decoder->getResult(); - std::lock_guard lk(_pcmCacheMutex); - _pcmCache.insert(std::make_pair(audioFilePath, d)); - } - else - { - ALOGE("decode (%s) failed!", audioFilePath.c_str()); - } - - ALOGV("decode %s", (ret ? "succeed" : "failed")); - - std::lock_guard lk(_preloadCallbackMutex); - auto&& preloadIter = _preloadCallbackMap.find(audioFilePath); - if (preloadIter != _preloadCallbackMap.end()) - { - auto&& params = preloadIter->second; - ALOGV("preload (%s) callback count: %d", audioFilePath.c_str(), (int)params.size()); - PcmData result = decoder->getResult(); - for (auto&& param : params) - { - param.callback(ret, result); - if (param.isPreloadInPlay2d) - { - _preloadWaitCond.notify_one(); - } - } - _preloadCallbackMap.erase(preloadIter); - } - - AudioDecoderProvider::destroyAudioDecoder(&decoder); - }); - } - else - { - ALOGV("File (%s) is too large, ignore preload!", info.url.c_str()); - cb(true, pcmData); - } -} - -AudioPlayerProvider::AudioFileInfo AudioPlayerProvider::getFileInfo( - const std::string &audioFilePath) -{ - AudioFileInfo info; - long fileSize = 0; - off_t start = 0, length = 0; - int assetFd = -1; - - if (audioFilePath[0] != '/') - { - std::string relativePath; - size_t position = audioFilePath.find("assets/"); - - if (0 == position) - { - // "assets/" is at the beginning of the path and we don't want it - relativePath = audioFilePath.substr(strlen("assets/")); - } - else - { - relativePath = audioFilePath; - } - - assetFd = _fdGetterCallback(relativePath, &start, &length); - - if (assetFd <= 0) - { - ALOGE("Failed to open file descriptor for '%s'", audioFilePath.c_str()); - return info; - } - - fileSize = length; - } - else - { - FILE *fp = fopen(audioFilePath.c_str(), "rb"); - if (fp != nullptr) - { - fseek(fp, 0, SEEK_END); - fileSize = ftell(fp); - fclose(fp); - } - else - { - return info; - } - } - - info.url = audioFilePath; - info.assetFd = std::make_shared(assetFd); - info.start = start; - info.length = fileSize; - - ALOGV("(%s) file size: %ld", audioFilePath.c_str(), fileSize); - - return info; -} - -bool AudioPlayerProvider::isSmallFile(const AudioFileInfo &info) -{ - //TODO: If file size is smaller than 100k, we think it's a small file. This value should be set by developers. - AudioFileInfo &audioFileInfo = const_cast(info); - size_t judgeCount = sizeof(__audioFileIndicator) / sizeof(__audioFileIndicator[0]); - size_t pos = audioFileInfo.url.rfind("."); - std::string extension; - if (pos != std::string::npos) - { - extension = audioFileInfo.url.substr(pos); - } - auto iter = std::find_if(std::begin(__audioFileIndicator), std::end(__audioFileIndicator), - [&extension](const AudioFileIndicator &judge) -> bool { - return judge.extension == extension; - }); - - if (iter != std::end(__audioFileIndicator)) - { -// ALOGV("isSmallFile: found: %s: ", iter->extension.c_str()); - return info.length < iter->smallSizeIndicator; - } - -// ALOGV("isSmallFile: not found return default value"); - return info.length < __audioFileIndicator[0].smallSizeIndicator; -} - -void AudioPlayerProvider::clearPcmCache(const std::string &audioFilePath) -{ - std::lock_guard lk(_pcmCacheMutex); - auto iter = _pcmCache.find(audioFilePath); - if (iter != _pcmCache.end()) - { - ALOGV("clear pcm cache: (%s)", audioFilePath.c_str()); - _pcmCache.erase(iter); - } - else - { - ALOGW("Couldn't find the pcm cache: (%s)", audioFilePath.c_str()); - } -} - -void AudioPlayerProvider::clearAllPcmCaches() -{ - std::lock_guard lk(_pcmCacheMutex); - _pcmCache.clear(); -} - -PcmAudioPlayer *AudioPlayerProvider::obtainPcmAudioPlayer(const std::string &url, - const PcmData &pcmData) -{ - PcmAudioPlayer *pcmPlayer = nullptr; - if (pcmData.isValid()) - { - pcmPlayer = new(std::nothrow) PcmAudioPlayer(_mixController, _callerThreadUtils); - if (pcmPlayer != nullptr) - { - pcmPlayer->prepare(url, pcmData); - } - } - else - { - ALOGE("obtainPcmAudioPlayer failed, pcmData isn't valid!"); - } - return pcmPlayer; -} - -UrlAudioPlayer *AudioPlayerProvider::createUrlAudioPlayer( - const AudioPlayerProvider::AudioFileInfo &info) -{ - if (info.url.empty()) - { - ALOGE("createUrlAudioPlayer failed, url is empty!"); - return nullptr; - } - - SLuint32 locatorType = info.assetFd->getFd() > 0 ? SL_DATALOCATOR_ANDROIDFD : SL_DATALOCATOR_URI; - auto urlPlayer = new (std::nothrow) UrlAudioPlayer(_engineItf, _outputMixObject, _callerThreadUtils); - bool ret = urlPlayer->prepare(info.url, locatorType, info.assetFd, info.start, info.length); - if (!ret) - { - SL_SAFE_DELETE(urlPlayer); - } - return urlPlayer; -} - -void AudioPlayerProvider::pause() -{ - if (_mixController != nullptr) - { - _mixController->pause(); - } - - if (_pcmAudioService != nullptr) - { - _pcmAudioService->pause(); - } -} - -void AudioPlayerProvider::resume() -{ - if (_mixController != nullptr) - { - _mixController->resume(); - } - - if (_pcmAudioService != nullptr) - { - _pcmAudioService->resume(); - } -} - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioPlayerProvider.h b/cocos/audio/android/AudioPlayerProvider.h deleted file mode 100644 index 8b21984c3009..000000000000 --- a/cocos/audio/android/AudioPlayerProvider.h +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#pragma once - -#include "audio/android/IAudioPlayer.h" -#include "audio/android/OpenSLHelper.h" -#include "audio/android/PcmData.h" - -#include -#include -#include - -namespace cocos2d { namespace experimental { -// Manage PcmAudioPlayer& UrlAudioPlayer - -class PcmAudioPlayer; -class PcmAudioService; -class UrlAudioPlayer; -class AudioMixerController; -class ICallerThreadUtils; -class AssetFd; -class ThreadPool; - -class AudioPlayerProvider -{ -public: - AudioPlayerProvider(SLEngineItf engineItf, SLObjectItf outputMixObject, int deviceSampleRate, - int bufferSizeInFrames, const FdGetterCallback &fdGetterCallback, - ICallerThreadUtils* callerThreadUtils); - - virtual ~AudioPlayerProvider(); - - IAudioPlayer *getAudioPlayer(const std::string &audioFilePath); - - typedef std::function PreloadCallback; - void preloadEffect(const std::string &audioFilePath, const PreloadCallback& cb); - - void clearPcmCache(const std::string &audioFilePath); - - void clearAllPcmCaches(); - - void pause(); - - void resume(); - -private: - - struct AudioFileInfo - { - std::string url; - std::shared_ptr assetFd; - off_t start; - off_t length; - - AudioFileInfo() - : assetFd(nullptr), start(0), length(0) - { }; - - inline bool isValid() const - { - return !url.empty() && length > 0; - } - }; - - PcmAudioPlayer *obtainPcmAudioPlayer(const std::string &url, const PcmData &pcmData); - - UrlAudioPlayer *createUrlAudioPlayer(const AudioFileInfo &info); - - void preloadEffect(const AudioFileInfo &info, const PreloadCallback& cb, bool isPreloadInPlay2d); - - AudioFileInfo getFileInfo(const std::string &audioFilePath); - - bool isSmallFile(const AudioFileInfo &info); - -private: - SLEngineItf _engineItf; - SLObjectItf _outputMixObject; - int _deviceSampleRate; - int _bufferSizeInFrames; - FdGetterCallback _fdGetterCallback; - ICallerThreadUtils* _callerThreadUtils; - - std::unordered_map _pcmCache; - std::mutex _pcmCacheMutex; - - struct PreloadCallbackParam - { - PreloadCallback callback; - bool isPreloadInPlay2d; - }; - - std::unordered_map> _preloadCallbackMap; - std::mutex _preloadCallbackMutex; - - std::mutex _preloadWaitMutex; - std::condition_variable _preloadWaitCond; - - PcmAudioService* _pcmAudioService; - AudioMixerController *_mixController; - - ThreadPool* _threadPool; -}; - -}} // namespace cocos2d { namespace experimental { - diff --git a/cocos/audio/android/AudioResampler.cpp b/cocos/audio/android/AudioResampler.cpp deleted file mode 100644 index ab1228936018..000000000000 --- a/cocos/audio/android/AudioResampler.cpp +++ /dev/null @@ -1,788 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "AudioResampler" -//#define LOG_NDEBUG 0 - -#include -#include -#include -#include -#include -#include "audio/android/cutils/log.h" -#include "audio/android/utils/Utils.h" -//#include -#include "audio/android/audio_utils/include/audio_utils/primitives.h" -#include "audio/android/AudioResampler.h" -//#include "audio/android/AudioResamplerSinc.h" -#include "audio/android/AudioResamplerCubic.h" - - -//#include "AudioResamplerDyn.h" - -//cjh #ifdef __arm__ -// #define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1 -//#endif - - - -namespace cocos2d { namespace experimental { - -// ---------------------------------------------------------------------------- - -class AudioResamplerOrder1 : public AudioResampler { -public: - AudioResamplerOrder1(int inChannelCount, int32_t sampleRate) : - AudioResampler(inChannelCount, sampleRate, LOW_QUALITY), mX0L(0), mX0R(0) { - } - virtual size_t resample(int32_t* out, size_t outFrameCount, - AudioBufferProvider* provider); -private: - // number of bits used in interpolation multiply - 15 bits avoids overflow - static const int kNumInterpBits = 15; - - // bits to shift the phase fraction down to avoid overflow - static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits; - - void init() {} - size_t resampleMono16(int32_t* out, size_t outFrameCount, - AudioBufferProvider* provider); - size_t resampleStereo16(int32_t* out, size_t outFrameCount, - AudioBufferProvider* provider); -#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 - void AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, - size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, - uint32_t &phaseFraction, uint32_t phaseIncrement); - void AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, - size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, - uint32_t &phaseFraction, uint32_t phaseIncrement); -#endif // ASM_ARM_RESAMP1 - - static inline int32_t Interp(int32_t x0, int32_t x1, uint32_t f) { - return x0 + (((x1 - x0) * (int32_t)(f >> kPreInterpShift)) >> kNumInterpBits); - } - static inline void Advance(size_t* index, uint32_t* frac, uint32_t inc) { - *frac += inc; - *index += (size_t)(*frac >> kNumPhaseBits); - *frac &= kPhaseMask; - } - int mX0L; - int mX0R; -}; - -/*static*/ -const double AudioResampler::kPhaseMultiplier = 1L << AudioResampler::kNumPhaseBits; - -bool AudioResampler::qualityIsSupported(src_quality quality) -{ - switch (quality) { - case DEFAULT_QUALITY: - case LOW_QUALITY: - case MED_QUALITY: - case HIGH_QUALITY: - case VERY_HIGH_QUALITY: - return true; - default: - return false; - } -} - -// ---------------------------------------------------------------------------- - -static pthread_once_t once_control = PTHREAD_ONCE_INIT; -static AudioResampler::src_quality defaultQuality = AudioResampler::DEFAULT_QUALITY; - -void AudioResampler::init_routine() -{ - // int resamplerQuality = getSystemProperty("af.resampler.quality"); - // if (resamplerQuality > 0) { - // defaultQuality = (src_quality) resamplerQuality; - // ALOGD("forcing AudioResampler quality to %d", defaultQuality); - // if (defaultQuality < DEFAULT_QUALITY || defaultQuality > VERY_HIGH_QUALITY) { - // defaultQuality = DEFAULT_QUALITY; - // } - // } -} - -uint32_t AudioResampler::qualityMHz(src_quality quality) -{ - switch (quality) { - default: - case DEFAULT_QUALITY: - case LOW_QUALITY: - return 3; - case MED_QUALITY: - return 6; - case HIGH_QUALITY: - return 20; - case VERY_HIGH_QUALITY: - return 34; -// case DYN_LOW_QUALITY: -// return 4; -// case DYN_MED_QUALITY: -// return 6; -// case DYN_HIGH_QUALITY: -// return 12; - } -} - -static const uint32_t maxMHz = 130; // an arbitrary number that permits 3 VHQ, should be tunable -static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; -static uint32_t currentMHz = 0; - -AudioResampler* AudioResampler::create(audio_format_t format, int inChannelCount, - int32_t sampleRate, src_quality quality) { - - bool atFinalQuality; - if (quality == DEFAULT_QUALITY) { - // read the resampler default quality property the first time it is needed - int ok = pthread_once(&once_control, init_routine); - if (ok != 0) { - ALOGE("%s pthread_once failed: %d", __func__, ok); - } - quality = defaultQuality; - atFinalQuality = false; - } else { - atFinalQuality = true; - } - - /* if the caller requests DEFAULT_QUALITY and af.resampler.property - * has not been set, the target resampler quality is set to DYN_MED_QUALITY, - * and allowed to "throttle" down to DYN_LOW_QUALITY if necessary - * due to estimated CPU load of having too many active resamplers - * (the code below the if). - */ - if (quality == DEFAULT_QUALITY) { -//cjh quality = DYN_MED_QUALITY; - } - - // naive implementation of CPU load throttling doesn't account for whether resampler is active - pthread_mutex_lock(&mutex); - for (;;) { - uint32_t deltaMHz = qualityMHz(quality); - uint32_t newMHz = currentMHz + deltaMHz; - if ((qualityIsSupported(quality) && newMHz <= maxMHz) || atFinalQuality) { - ALOGV("resampler load %u -> %u MHz due to delta +%u MHz from quality %d", - currentMHz, newMHz, deltaMHz, quality); - currentMHz = newMHz; - break; - } - // not enough CPU available for proposed quality level, so try next lowest level - switch (quality) { - default: - case LOW_QUALITY: - atFinalQuality = true; - break; - case MED_QUALITY: - quality = LOW_QUALITY; - break; - case HIGH_QUALITY: - quality = MED_QUALITY; - break; - case VERY_HIGH_QUALITY: - quality = HIGH_QUALITY; - break; -// case DYN_LOW_QUALITY: -// atFinalQuality = true; -// break; -// case DYN_MED_QUALITY: -// quality = DYN_LOW_QUALITY; -// break; -// case DYN_HIGH_QUALITY: -// quality = DYN_MED_QUALITY; -// break; - } - } - pthread_mutex_unlock(&mutex); - - AudioResampler* resampler; - - switch (quality) { - default: - case LOW_QUALITY: - ALOGV("Create linear Resampler"); - LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT, "invalid pcm format"); - resampler = new (std::nothrow) AudioResamplerOrder1(inChannelCount, sampleRate); - break; - case MED_QUALITY: - ALOGV("Create cubic Resampler"); - LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT, "invalid pcm format"); - resampler = new (std::nothrow) AudioResamplerCubic(inChannelCount, sampleRate); - break; - case HIGH_QUALITY: - ALOGV("Create HIGH_QUALITY sinc Resampler"); - LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT, "invalid pcm format"); - ALOG_ASSERT(false, "HIGH_QUALITY isn't supported"); - // Cocos2d-x only uses MED_QUALITY, so we could remove Sinc relative files -// resampler = new (std::nothrow) AudioResamplerSinc(inChannelCount, sampleRate); - break; - case VERY_HIGH_QUALITY: - ALOGV("Create VERY_HIGH_QUALITY sinc Resampler = %d", quality); - LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT, "invalid pcm format"); - // Cocos2d-x only uses MED_QUALITY, so we could remove Sinc relative files -// resampler = new (std::nothrow) AudioResamplerSinc(inChannelCount, sampleRate, quality); - ALOG_ASSERT(false, "VERY_HIGH_QUALITY isn't supported"); - break; - } - - // initialize resampler - resampler->init(); - return resampler; -} - -AudioResampler::AudioResampler(int inChannelCount, - int32_t sampleRate, src_quality quality) : - mChannelCount(inChannelCount), - mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0), - mPhaseFraction(0), mLocalTimeFreq(0), - mPTS(AudioBufferProvider::kInvalidPTS), mQuality(quality) { - - const int maxChannels = 2;//cjh quality < DYN_LOW_QUALITY ? 2 : 8; - if (inChannelCount < 1 - || inChannelCount > maxChannels) { - LOG_ALWAYS_FATAL("Unsupported sample format %d quality %d channels", - quality, inChannelCount); - } - if (sampleRate <= 0) { - LOG_ALWAYS_FATAL("Unsupported sample rate %d Hz", sampleRate); - } - - // initialize common members - mVolume[0] = mVolume[1] = 0; - mBuffer.frameCount = 0; -} - -AudioResampler::~AudioResampler() { - pthread_mutex_lock(&mutex); - src_quality quality = getQuality(); - uint32_t deltaMHz = qualityMHz(quality); - int32_t newMHz = currentMHz - deltaMHz; - ALOGV("resampler load %u -> %d MHz due to delta -%u MHz from quality %d", - currentMHz, newMHz, deltaMHz, quality); - LOG_ALWAYS_FATAL_IF(newMHz < 0, "negative resampler load %d MHz", newMHz); - currentMHz = newMHz; - pthread_mutex_unlock(&mutex); -} - -void AudioResampler::setSampleRate(int32_t inSampleRate) { - mInSampleRate = inSampleRate; - mPhaseIncrement = (uint32_t)((kPhaseMultiplier * inSampleRate) / mSampleRate); -} - -void AudioResampler::setVolume(float left, float right) { - // TODO: Implement anti-zipper filter - // convert to U4.12 for internal integer use (round down) - // integer volume values are clamped to 0 to UNITY_GAIN. - mVolume[0] = u4_12_from_float(clampFloatVol(left)); - mVolume[1] = u4_12_from_float(clampFloatVol(right)); -} - -void AudioResampler::setLocalTimeFreq(uint64_t freq) { - mLocalTimeFreq = freq; -} - -void AudioResampler::setPTS(int64_t pts) { - mPTS = pts; -} - -int64_t AudioResampler::calculateOutputPTS(int outputFrameIndex) { - - if (mPTS == AudioBufferProvider::kInvalidPTS) { - return AudioBufferProvider::kInvalidPTS; - } else { - return mPTS + ((outputFrameIndex * mLocalTimeFreq) / mSampleRate); - } -} - -void AudioResampler::reset() { - mInputIndex = 0; - mPhaseFraction = 0; - mBuffer.frameCount = 0; -} - -// ---------------------------------------------------------------------------- - -size_t AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount, - AudioBufferProvider* provider) { - - // should never happen, but we overflow if it does - // ALOG_ASSERT(outFrameCount < 32767); - - // select the appropriate resampler - switch (mChannelCount) { - case 1: - return resampleMono16(out, outFrameCount, provider); - case 2: - return resampleStereo16(out, outFrameCount, provider); - default: - LOG_ALWAYS_FATAL("invalid channel count: %d", mChannelCount); - return 0; - } -} - -size_t AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount, - AudioBufferProvider* provider) { - - int32_t vl = mVolume[0]; - int32_t vr = mVolume[1]; - - size_t inputIndex = mInputIndex; - uint32_t phaseFraction = mPhaseFraction; - uint32_t phaseIncrement = mPhaseIncrement; - size_t outputIndex = 0; - size_t outputSampleCount = outFrameCount * 2; - size_t inFrameCount = getInFrameCountRequired(outFrameCount); - - // ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d", - // outFrameCount, inputIndex, phaseFraction, phaseIncrement); - - while (outputIndex < outputSampleCount) { - - // buffer is empty, fetch a new one - while (mBuffer.frameCount == 0) { - mBuffer.frameCount = inFrameCount; - provider->getNextBuffer(&mBuffer, - calculateOutputPTS(outputIndex / 2)); - if (mBuffer.raw == NULL) { - goto resampleStereo16_exit; - } - - // ALOGE("New buffer fetched: %d frames", mBuffer.frameCount); - if (mBuffer.frameCount > inputIndex) break; - - inputIndex -= mBuffer.frameCount; - mX0L = mBuffer.i16[mBuffer.frameCount*2-2]; - mX0R = mBuffer.i16[mBuffer.frameCount*2-1]; - provider->releaseBuffer(&mBuffer); - // mBuffer.frameCount == 0 now so we reload a new buffer - } - - int16_t *in = mBuffer.i16; - - // handle boundary case - while (inputIndex == 0) { - // ALOGE("boundary case"); - out[outputIndex++] += vl * Interp(mX0L, in[0], phaseFraction); - out[outputIndex++] += vr * Interp(mX0R, in[1], phaseFraction); - Advance(&inputIndex, &phaseFraction, phaseIncrement); - if (outputIndex == outputSampleCount) { - break; - } - } - - // process input samples - // ALOGE("general case"); - -#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 - if (inputIndex + 2 < mBuffer.frameCount) { - int32_t* maxOutPt; - int32_t maxInIdx; - - maxOutPt = out + (outputSampleCount - 2); // 2 because 2 frames per loop - maxInIdx = mBuffer.frameCount - 2; - AsmStereo16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr, - phaseFraction, phaseIncrement); - } -#endif // ASM_ARM_RESAMP1 - - while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) { - out[outputIndex++] += vl * Interp(in[inputIndex*2-2], - in[inputIndex*2], phaseFraction); - out[outputIndex++] += vr * Interp(in[inputIndex*2-1], - in[inputIndex*2+1], phaseFraction); - Advance(&inputIndex, &phaseFraction, phaseIncrement); - } - - // ALOGE("loop done - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex); - - // if done with buffer, save samples - if (inputIndex >= mBuffer.frameCount) { - inputIndex -= mBuffer.frameCount; - - // ALOGE("buffer done, new input index %d", inputIndex); - - mX0L = mBuffer.i16[mBuffer.frameCount*2-2]; - mX0R = mBuffer.i16[mBuffer.frameCount*2-1]; - provider->releaseBuffer(&mBuffer); - - // verify that the releaseBuffer resets the buffer frameCount - // ALOG_ASSERT(mBuffer.frameCount == 0); - } - } - - // ALOGE("output buffer full - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex); - -resampleStereo16_exit: - // save state - mInputIndex = inputIndex; - mPhaseFraction = phaseFraction; - return outputIndex / 2 /* channels for stereo */; -} - -size_t AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount, - AudioBufferProvider* provider) { - - int32_t vl = mVolume[0]; - int32_t vr = mVolume[1]; - - size_t inputIndex = mInputIndex; - uint32_t phaseFraction = mPhaseFraction; - uint32_t phaseIncrement = mPhaseIncrement; - size_t outputIndex = 0; - size_t outputSampleCount = outFrameCount * 2; - size_t inFrameCount = getInFrameCountRequired(outFrameCount); - - // ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d", - // outFrameCount, inputIndex, phaseFraction, phaseIncrement); - while (outputIndex < outputSampleCount) { - // buffer is empty, fetch a new one - while (mBuffer.frameCount == 0) { - mBuffer.frameCount = inFrameCount; - provider->getNextBuffer(&mBuffer, - calculateOutputPTS(outputIndex / 2)); - if (mBuffer.raw == NULL) { - mInputIndex = inputIndex; - mPhaseFraction = phaseFraction; - goto resampleMono16_exit; - } - // ALOGE("New buffer fetched: %d frames", mBuffer.frameCount); - if (mBuffer.frameCount > inputIndex) break; - - inputIndex -= mBuffer.frameCount; - mX0L = mBuffer.i16[mBuffer.frameCount-1]; - provider->releaseBuffer(&mBuffer); - // mBuffer.frameCount == 0 now so we reload a new buffer - } - int16_t *in = mBuffer.i16; - - // handle boundary case - while (inputIndex == 0) { - // ALOGE("boundary case"); - int32_t sample = Interp(mX0L, in[0], phaseFraction); - out[outputIndex++] += vl * sample; - out[outputIndex++] += vr * sample; - Advance(&inputIndex, &phaseFraction, phaseIncrement); - if (outputIndex == outputSampleCount) { - break; - } - } - - // process input samples - // ALOGE("general case"); - -#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 - if (inputIndex + 2 < mBuffer.frameCount) { - int32_t* maxOutPt; - int32_t maxInIdx; - - maxOutPt = out + (outputSampleCount - 2); - maxInIdx = (int32_t)mBuffer.frameCount - 2; - AsmMono16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr, - phaseFraction, phaseIncrement); - } -#endif // ASM_ARM_RESAMP1 - - while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) { - int32_t sample = Interp(in[inputIndex-1], in[inputIndex], - phaseFraction); - out[outputIndex++] += vl * sample; - out[outputIndex++] += vr * sample; - Advance(&inputIndex, &phaseFraction, phaseIncrement); - } - - - // ALOGE("loop done - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex); - - // if done with buffer, save samples - if (inputIndex >= mBuffer.frameCount) { - inputIndex -= mBuffer.frameCount; - - // ALOGE("buffer done, new input index %d", inputIndex); - - mX0L = mBuffer.i16[mBuffer.frameCount-1]; - provider->releaseBuffer(&mBuffer); - - // verify that the releaseBuffer resets the buffer frameCount - // ALOG_ASSERT(mBuffer.frameCount == 0); - } - } - - // ALOGE("output buffer full - outputIndex=%d, inputIndex=%d", outputIndex, inputIndex); - -resampleMono16_exit: - // save state - mInputIndex = inputIndex; - mPhaseFraction = phaseFraction; - return outputIndex; -} - -#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 - -/******************************************************************* -* -* AsmMono16Loop -* asm optimized monotonic loop version; one loop is 2 frames -* Input: -* in : pointer on input samples -* maxOutPt : pointer on first not filled -* maxInIdx : index on first not used -* outputIndex : pointer on current output index -* out : pointer on output buffer -* inputIndex : pointer on current input index -* vl, vr : left and right gain -* phaseFraction : pointer on current phase fraction -* phaseIncrement -* Output: -* outputIndex : -* out : updated buffer -* inputIndex : index of next to use -* phaseFraction : phase fraction for next interpolation -* -*******************************************************************/ -__attribute__((noinline)) -void AudioResamplerOrder1::AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, - size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, - uint32_t &phaseFraction, uint32_t phaseIncrement) -{ - (void)maxOutPt; // remove unused parameter warnings - (void)maxInIdx; - (void)outputIndex; - (void)out; - (void)inputIndex; - (void)vl; - (void)vr; - (void)phaseFraction; - (void)phaseIncrement; - (void)in; -#define MO_PARAM5 "36" // offset of parameter 5 (outputIndex) - - asm( - "stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n" - // get parameters - " ldr r6, [sp, #" MO_PARAM5 " + 20]\n" // &phaseFraction - " ldr r6, [r6]\n" // phaseFraction - " ldr r7, [sp, #" MO_PARAM5 " + 8]\n" // &inputIndex - " ldr r7, [r7]\n" // inputIndex - " ldr r8, [sp, #" MO_PARAM5 " + 4]\n" // out - " ldr r0, [sp, #" MO_PARAM5 " + 0]\n" // &outputIndex - " ldr r0, [r0]\n" // outputIndex - " add r8, r8, r0, asl #2\n" // curOut - " ldr r9, [sp, #" MO_PARAM5 " + 24]\n" // phaseIncrement - " ldr r10, [sp, #" MO_PARAM5 " + 12]\n" // vl - " ldr r11, [sp, #" MO_PARAM5 " + 16]\n" // vr - - // r0 pin, x0, Samp - - // r1 in - // r2 maxOutPt - // r3 maxInIdx - - // r4 x1, i1, i3, Out1 - // r5 out0 - - // r6 frac - // r7 inputIndex - // r8 curOut - - // r9 inc - // r10 vl - // r11 vr - - // r12 - // r13 sp - // r14 - - // the following loop works on 2 frames - - "1:\n" - " cmp r8, r2\n" // curOut - maxCurOut - " bcs 2f\n" - -#define MO_ONE_FRAME \ - " add r0, r1, r7, asl #1\n" /* in + inputIndex */\ - " ldrsh r4, [r0]\n" /* in[inputIndex] */\ - " ldr r5, [r8]\n" /* out[outputIndex] */\ - " ldrsh r0, [r0, #-2]\n" /* in[inputIndex-1] */\ - " bic r6, r6, #0xC0000000\n" /* phaseFraction & ... */\ - " sub r4, r4, r0\n" /* in[inputIndex] - in[inputIndex-1] */\ - " mov r4, r4, lsl #2\n" /* <<2 */\ - " smulwt r4, r4, r6\n" /* (x1-x0)*.. */\ - " add r6, r6, r9\n" /* phaseFraction + phaseIncrement */\ - " add r0, r0, r4\n" /* x0 - (..) */\ - " mla r5, r0, r10, r5\n" /* vl*interp + out[] */\ - " ldr r4, [r8, #4]\n" /* out[outputIndex+1] */\ - " str r5, [r8], #4\n" /* out[outputIndex++] = ... */\ - " mla r4, r0, r11, r4\n" /* vr*interp + out[] */\ - " add r7, r7, r6, lsr #30\n" /* inputIndex + phaseFraction>>30 */\ - " str r4, [r8], #4\n" /* out[outputIndex++] = ... */ - - MO_ONE_FRAME // frame 1 - MO_ONE_FRAME // frame 2 - - " cmp r7, r3\n" // inputIndex - maxInIdx - " bcc 1b\n" - "2:\n" - - " bic r6, r6, #0xC0000000\n" // phaseFraction & ... - // save modified values - " ldr r0, [sp, #" MO_PARAM5 " + 20]\n" // &phaseFraction - " str r6, [r0]\n" // phaseFraction - " ldr r0, [sp, #" MO_PARAM5 " + 8]\n" // &inputIndex - " str r7, [r0]\n" // inputIndex - " ldr r0, [sp, #" MO_PARAM5 " + 4]\n" // out - " sub r8, r0\n" // curOut - out - " asr r8, #2\n" // new outputIndex - " ldr r0, [sp, #" MO_PARAM5 " + 0]\n" // &outputIndex - " str r8, [r0]\n" // save outputIndex - - " ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}\n" - ); -} - -/******************************************************************* -* -* AsmStereo16Loop -* asm optimized stereo loop version; one loop is 2 frames -* Input: -* in : pointer on input samples -* maxOutPt : pointer on first not filled -* maxInIdx : index on first not used -* outputIndex : pointer on current output index -* out : pointer on output buffer -* inputIndex : pointer on current input index -* vl, vr : left and right gain -* phaseFraction : pointer on current phase fraction -* phaseIncrement -* Output: -* outputIndex : -* out : updated buffer -* inputIndex : index of next to use -* phaseFraction : phase fraction for next interpolation -* -*******************************************************************/ -__attribute__((noinline)) -void AudioResamplerOrder1::AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, - size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, - uint32_t &phaseFraction, uint32_t phaseIncrement) -{ - (void)maxOutPt; // remove unused parameter warnings - (void)maxInIdx; - (void)outputIndex; - (void)out; - (void)inputIndex; - (void)vl; - (void)vr; - (void)phaseFraction; - (void)phaseIncrement; - (void)in; -#define ST_PARAM5 "40" // offset of parameter 5 (outputIndex) - asm( - "stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}\n" - // get parameters - " ldr r6, [sp, #" ST_PARAM5 " + 20]\n" // &phaseFraction - " ldr r6, [r6]\n" // phaseFraction - " ldr r7, [sp, #" ST_PARAM5 " + 8]\n" // &inputIndex - " ldr r7, [r7]\n" // inputIndex - " ldr r8, [sp, #" ST_PARAM5 " + 4]\n" // out - " ldr r0, [sp, #" ST_PARAM5 " + 0]\n" // &outputIndex - " ldr r0, [r0]\n" // outputIndex - " add r8, r8, r0, asl #2\n" // curOut - " ldr r9, [sp, #" ST_PARAM5 " + 24]\n" // phaseIncrement - " ldr r10, [sp, #" ST_PARAM5 " + 12]\n" // vl - " ldr r11, [sp, #" ST_PARAM5 " + 16]\n" // vr - - // r0 pin, x0, Samp - - // r1 in - // r2 maxOutPt - // r3 maxInIdx - - // r4 x1, i1, i3, out1 - // r5 out0 - - // r6 frac - // r7 inputIndex - // r8 curOut - - // r9 inc - // r10 vl - // r11 vr - - // r12 temporary - // r13 sp - // r14 - - "3:\n" - " cmp r8, r2\n" // curOut - maxCurOut - " bcs 4f\n" - -#define ST_ONE_FRAME \ - " bic r6, r6, #0xC0000000\n" /* phaseFraction & ... */\ -\ - " add r0, r1, r7, asl #2\n" /* in + 2*inputIndex */\ -\ - " ldrsh r4, [r0]\n" /* in[2*inputIndex] */\ - " ldr r5, [r8]\n" /* out[outputIndex] */\ - " ldrsh r12, [r0, #-4]\n" /* in[2*inputIndex-2] */\ - " sub r4, r4, r12\n" /* in[2*InputIndex] - in[2*InputIndex-2] */\ - " mov r4, r4, lsl #2\n" /* <<2 */\ - " smulwt r4, r4, r6\n" /* (x1-x0)*.. */\ - " add r12, r12, r4\n" /* x0 - (..) */\ - " mla r5, r12, r10, r5\n" /* vl*interp + out[] */\ - " ldr r4, [r8, #4]\n" /* out[outputIndex+1] */\ - " str r5, [r8], #4\n" /* out[outputIndex++] = ... */\ -\ - " ldrsh r12, [r0, #+2]\n" /* in[2*inputIndex+1] */\ - " ldrsh r0, [r0, #-2]\n" /* in[2*inputIndex-1] */\ - " sub r12, r12, r0\n" /* in[2*InputIndex] - in[2*InputIndex-2] */\ - " mov r12, r12, lsl #2\n" /* <<2 */\ - " smulwt r12, r12, r6\n" /* (x1-x0)*.. */\ - " add r12, r0, r12\n" /* x0 - (..) */\ - " mla r4, r12, r11, r4\n" /* vr*interp + out[] */\ - " str r4, [r8], #4\n" /* out[outputIndex++] = ... */\ -\ - " add r6, r6, r9\n" /* phaseFraction + phaseIncrement */\ - " add r7, r7, r6, lsr #30\n" /* inputIndex + phaseFraction>>30 */ - - ST_ONE_FRAME // frame 1 - ST_ONE_FRAME // frame 1 - - " cmp r7, r3\n" // inputIndex - maxInIdx - " bcc 3b\n" - "4:\n" - - " bic r6, r6, #0xC0000000\n" // phaseFraction & ... - // save modified values - " ldr r0, [sp, #" ST_PARAM5 " + 20]\n" // &phaseFraction - " str r6, [r0]\n" // phaseFraction - " ldr r0, [sp, #" ST_PARAM5 " + 8]\n" // &inputIndex - " str r7, [r0]\n" // inputIndex - " ldr r0, [sp, #" ST_PARAM5 " + 4]\n" // out - " sub r8, r0\n" // curOut - out - " asr r8, #2\n" // new outputIndex - " ldr r0, [sp, #" ST_PARAM5 " + 0]\n" // &outputIndex - " str r8, [r0]\n" // save outputIndex - - " ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, pc}\n" - ); -} - -#endif // ASM_ARM_RESAMP1 - - -// ---------------------------------------------------------------------------- - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioResampler.h b/cocos/audio/android/AudioResampler.h deleted file mode 100644 index ee9185bb5296..000000000000 --- a/cocos/audio/android/AudioResampler.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include - -#include "audio/android/AudioBufferProvider.h" - -//#include -//#include - -//#include -//#include -#include -#include "audio/android/audio.h" - -namespace cocos2d { namespace experimental { - - -class AudioResampler { -public: - // Determines quality of SRC. - // LOW_QUALITY: linear interpolator (1st order) - // MED_QUALITY: cubic interpolator (3rd order) - // HIGH_QUALITY: fixed multi-tap FIR (e.g. 48KHz->44.1KHz) - // NOTE: high quality SRC will only be supported for - // certain fixed rate conversions. Sample rate cannot be - // changed dynamically. - enum src_quality { - DEFAULT_QUALITY=0, - LOW_QUALITY=1, - MED_QUALITY=2, - HIGH_QUALITY=3, - VERY_HIGH_QUALITY=4, - }; - - static const CONSTEXPR float UNITY_GAIN_FLOAT = 1.0f; - - static AudioResampler* create(audio_format_t format, int inChannelCount, - int32_t sampleRate, src_quality quality=DEFAULT_QUALITY); - - virtual ~AudioResampler(); - - virtual void init() = 0; - virtual void setSampleRate(int32_t inSampleRate); - virtual void setVolume(float left, float right); - virtual void setLocalTimeFreq(uint64_t freq); - - // set the PTS of the next buffer output by the resampler - virtual void setPTS(int64_t pts); - - // Resample int16_t samples from provider and accumulate into 'out'. - // A mono provider delivers a sequence of samples. - // A stereo provider delivers a sequence of interleaved pairs of samples. - // - // In either case, 'out' holds interleaved pairs of fixed-point Q4.27. - // That is, for a mono provider, there is an implicit up-channeling. - // Since this method accumulates, the caller is responsible for clearing 'out' initially. - // - // For a float resampler, 'out' holds interleaved pairs of float samples. - // - // Multichannel interleaved frames for n > 2 is supported for quality DYN_LOW_QUALITY, - // DYN_MED_QUALITY, and DYN_HIGH_QUALITY. - // - // Returns the number of frames resampled into the out buffer. - virtual size_t resample(int32_t* out, size_t outFrameCount, - AudioBufferProvider* provider) = 0; - - virtual void reset(); - virtual size_t getUnreleasedFrames() const { return mInputIndex; } - - // called from destructor, so must not be virtual - src_quality getQuality() const { return mQuality; } - -protected: - // number of bits for phase fraction - 30 bits allows nearly 2x downsampling - static const int kNumPhaseBits = 30; - - // phase mask for fraction - static const uint32_t kPhaseMask = (1LU<(outFrameCount)*mInSampleRate + (mSampleRate - 1))/mSampleRate; - // - // The double precision equivalent (float may not be precise enough): - // ceil(static_cast(outFrameCount) * mInSampleRate / mSampleRate); - // - // this relies on the fact that the mPhaseIncrement is rounded down from - // #phases * mInSampleRate/mSampleRate and the fact that Sum(Floor(x)) <= Floor(Sum(x)). - // http://www.proofwiki.org/wiki/Sum_of_Floors_Not_Greater_Than_Floor_of_Sums - // - // (so long as double precision is computed accurately enough to be considered - // greater than or equal to the Floor(x) value in int32_t arithmetic; thus this - // will not necessarily hold for floats). - // - // TODO: - // Greater accuracy and a tight bound is obtained by: - // 1) subtract and adjust for the current state of the AudioBufferProvider buffer. - // 2) using the exact integer formula where (ignoring 64b casting) - // inFrameCount = (mPhaseIncrement * (outFrameCount - 1) + mPhaseFraction) / phaseWrapLimit; - // phaseWrapLimit is the wraparound (1 << kNumPhaseBits), if not specified explicitly. - // - inline size_t getInFrameCountRequired(size_t outFrameCount) { - return (static_cast(outFrameCount)*mInSampleRate - + (mSampleRate - 1))/mSampleRate; - } - - inline float clampFloatVol(float volume) { - if (volume > UNITY_GAIN_FLOAT) { - return UNITY_GAIN_FLOAT; - } else if (volume >= 0.) { - return volume; - } - return 0.; // NaN or negative volume maps to 0. - } - -private: - const src_quality mQuality; - - // Return 'true' if the quality level is supported without explicit request - static bool qualityIsSupported(src_quality quality); - - // For pthread_once() - static void init_routine(); - - // Return the estimated CPU load for specific resampler in MHz. - // The absolute number is irrelevant, it's the relative values that matter. - static uint32_t qualityMHz(src_quality quality); -}; - -// ---------------------------------------------------------------------------- -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioResamplerCubic.cpp b/cocos/audio/android/AudioResamplerCubic.cpp deleted file mode 100644 index 2b85321245c7..000000000000 --- a/cocos/audio/android/AudioResamplerCubic.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "AudioResamplerCubic" - -#include -#include -#include -#include "audio/android/cutils/log.h" - -#include "audio/android/AudioResampler.h" -#include "audio/android/AudioResamplerCubic.h" - -namespace cocos2d { namespace experimental { -// ---------------------------------------------------------------------------- - -void AudioResamplerCubic::init() { - memset(&left, 0, sizeof(state)); - memset(&right, 0, sizeof(state)); -} - -size_t AudioResamplerCubic::resample(int32_t* out, size_t outFrameCount, - AudioBufferProvider* provider) { - - // should never happen, but we overflow if it does - // ALOG_ASSERT(outFrameCount < 32767); - - // select the appropriate resampler - switch (mChannelCount) { - case 1: - return resampleMono16(out, outFrameCount, provider); - case 2: - return resampleStereo16(out, outFrameCount, provider); - default: - LOG_ALWAYS_FATAL("invalid channel count: %d", mChannelCount); - return 0; - } -} - -size_t AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount, - AudioBufferProvider* provider) { - - int32_t vl = mVolume[0]; - int32_t vr = mVolume[1]; - - size_t inputIndex = mInputIndex; - uint32_t phaseFraction = mPhaseFraction; - uint32_t phaseIncrement = mPhaseIncrement; - size_t outputIndex = 0; - size_t outputSampleCount = outFrameCount * 2; - size_t inFrameCount = getInFrameCountRequired(outFrameCount); - - // fetch first buffer - if (mBuffer.frameCount == 0) { - mBuffer.frameCount = inFrameCount; - provider->getNextBuffer(&mBuffer, mPTS); - if (mBuffer.raw == NULL) { - return 0; - } - // ALOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount); - } - int16_t *in = mBuffer.i16; - - while (outputIndex < outputSampleCount) { - int32_t sample; - int32_t x; - - // calculate output sample - x = phaseFraction >> kPreInterpShift; - out[outputIndex++] += vl * interp(&left, x); - out[outputIndex++] += vr * interp(&right, x); - // out[outputIndex++] += vr * in[inputIndex*2]; - - // increment phase - phaseFraction += phaseIncrement; - uint32_t indexIncrement = (phaseFraction >> kNumPhaseBits); - phaseFraction &= kPhaseMask; - - // time to fetch another sample - while (indexIncrement--) { - - inputIndex++; - if (inputIndex == mBuffer.frameCount) { - inputIndex = 0; - provider->releaseBuffer(&mBuffer); - mBuffer.frameCount = inFrameCount; - provider->getNextBuffer(&mBuffer, - calculateOutputPTS(outputIndex / 2)); - if (mBuffer.raw == NULL) { - goto save_state; // ugly, but efficient - } - in = mBuffer.i16; - // ALOGW("New buffer: offset=%p, frames=%d", mBuffer.raw, mBuffer.frameCount); - } - - // advance sample state - advance(&left, in[inputIndex*2]); - advance(&right, in[inputIndex*2+1]); - } - } - -save_state: - // ALOGW("Done: index=%d, fraction=%u", inputIndex, phaseFraction); - mInputIndex = inputIndex; - mPhaseFraction = phaseFraction; - return outputIndex / 2 /* channels for stereo */; -} - -size_t AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount, - AudioBufferProvider* provider) { - - int32_t vl = mVolume[0]; - int32_t vr = mVolume[1]; - - size_t inputIndex = mInputIndex; - uint32_t phaseFraction = mPhaseFraction; - uint32_t phaseIncrement = mPhaseIncrement; - size_t outputIndex = 0; - size_t outputSampleCount = outFrameCount * 2; - size_t inFrameCount = getInFrameCountRequired(outFrameCount); - - // fetch first buffer - if (mBuffer.frameCount == 0) { - mBuffer.frameCount = inFrameCount; - provider->getNextBuffer(&mBuffer, mPTS); - if (mBuffer.raw == NULL) { - return 0; - } - // ALOGW("New buffer: offset=%p, frames=%d", mBuffer.raw, mBuffer.frameCount); - } - int16_t *in = mBuffer.i16; - - while (outputIndex < outputSampleCount) { - int32_t sample; - int32_t x; - - // calculate output sample - x = phaseFraction >> kPreInterpShift; - sample = interp(&left, x); - out[outputIndex++] += vl * sample; - out[outputIndex++] += vr * sample; - - // increment phase - phaseFraction += phaseIncrement; - uint32_t indexIncrement = (phaseFraction >> kNumPhaseBits); - phaseFraction &= kPhaseMask; - - // time to fetch another sample - while (indexIncrement--) { - - inputIndex++; - if (inputIndex == mBuffer.frameCount) { - inputIndex = 0; - provider->releaseBuffer(&mBuffer); - mBuffer.frameCount = inFrameCount; - provider->getNextBuffer(&mBuffer, - calculateOutputPTS(outputIndex / 2)); - if (mBuffer.raw == NULL) { - goto save_state; // ugly, but efficient - } - // ALOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount); - in = mBuffer.i16; - } - - // advance sample state - advance(&left, in[inputIndex]); - } - } - -save_state: - // ALOGW("Done: index=%d, fraction=%u", inputIndex, phaseFraction); - mInputIndex = inputIndex; - mPhaseFraction = phaseFraction; - return outputIndex; -} - -// ---------------------------------------------------------------------------- -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioResamplerCubic.h b/cocos/audio/android/AudioResamplerCubic.h deleted file mode 100644 index ef71b771f3d5..000000000000 --- a/cocos/audio/android/AudioResamplerCubic.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include "audio/android/AudioResampler.h" -#include "audio/android/AudioBufferProvider.h" - -namespace cocos2d { namespace experimental { -// ---------------------------------------------------------------------------- - -class AudioResamplerCubic : public AudioResampler { -public: - AudioResamplerCubic(int inChannelCount, int32_t sampleRate) : - AudioResampler(inChannelCount, sampleRate, MED_QUALITY) { - } - virtual size_t resample(int32_t* out, size_t outFrameCount, - AudioBufferProvider* provider); -private: - // number of bits used in interpolation multiply - 14 bits avoids overflow - static const int kNumInterpBits = 14; - - // bits to shift the phase fraction down to avoid overflow - static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits; - typedef struct { - int32_t a, b, c, y0, y1, y2, y3; - } state; - void init(); - size_t resampleMono16(int32_t* out, size_t outFrameCount, - AudioBufferProvider* provider); - size_t resampleStereo16(int32_t* out, size_t outFrameCount, - AudioBufferProvider* provider); - static inline int32_t interp(state* p, int32_t x) { - return (((((p->a * x >> 14) + p->b) * x >> 14) + p->c) * x >> 14) + p->y1; - } - static inline void advance(state* p, int16_t in) { - p->y0 = p->y1; - p->y1 = p->y2; - p->y2 = p->y3; - p->y3 = in; - p->a = (3 * (p->y1 - p->y2) - p->y0 + p->y3) >> 1; - p->b = (p->y2 << 1) + p->y0 - (((5 * p->y1 + p->y3)) >> 1); - p->c = (p->y2 - p->y0) >> 1; - } - state left, right; -}; - -// ---------------------------------------------------------------------------- -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioResamplerPublic.h b/cocos/audio/android/AudioResamplerPublic.h deleted file mode 100644 index 68f31ba60e68..000000000000 --- a/cocos/audio/android/AudioResamplerPublic.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -namespace cocos2d { namespace experimental { - -// AUDIO_RESAMPLER_DOWN_RATIO_MAX is the maximum ratio between the original -// audio sample rate and the target rate when downsampling, -// as permitted in the audio framework, e.g. AudioTrack and AudioFlinger. -// In practice, it is not recommended to downsample more than 6:1 -// for best audio quality, even though the audio framework permits a larger -// downsampling ratio. -// TODO: replace with an API -#define AUDIO_RESAMPLER_DOWN_RATIO_MAX 256 - -// AUDIO_RESAMPLER_UP_RATIO_MAX is the maximum suggested ratio between the original -// audio sample rate and the target rate when upsampling. It is loosely enforced by -// the system. One issue with large upsampling ratios is the approximation by -// an int32_t of the phase increments, making the resulting sample rate inexact. -#define AUDIO_RESAMPLER_UP_RATIO_MAX 65536 - -// AUDIO_TIMESTRETCH_SPEED_MIN and AUDIO_TIMESTRETCH_SPEED_MAX define the min and max time stretch -// speeds supported by the system. These are enforced by the system and values outside this range -// will result in a runtime error. -// Depending on the AudioPlaybackRate::mStretchMode, the effective limits might be narrower than -// the ones specified here -// AUDIO_TIMESTRETCH_SPEED_MIN_DELTA is the minimum absolute speed difference that might trigger a -// parameter update -#define AUDIO_TIMESTRETCH_SPEED_MIN 0.01f -#define AUDIO_TIMESTRETCH_SPEED_MAX 20.0f -#define AUDIO_TIMESTRETCH_SPEED_NORMAL 1.0f -#define AUDIO_TIMESTRETCH_SPEED_MIN_DELTA 0.0001f - -// AUDIO_TIMESTRETCH_PITCH_MIN and AUDIO_TIMESTRETCH_PITCH_MAX define the min and max time stretch -// pitch shifting supported by the system. These are not enforced by the system and values -// outside this range might result in a pitch different than the one requested. -// Depending on the AudioPlaybackRate::mStretchMode, the effective limits might be narrower than -// the ones specified here. -// AUDIO_TIMESTRETCH_PITCH_MIN_DELTA is the minimum absolute pitch difference that might trigger a -// parameter update -#define AUDIO_TIMESTRETCH_PITCH_MIN 0.25f -#define AUDIO_TIMESTRETCH_PITCH_MAX 4.0f -#define AUDIO_TIMESTRETCH_PITCH_NORMAL 1.0f -#define AUDIO_TIMESTRETCH_PITCH_MIN_DELTA 0.0001f - - -//Determines the current algorithm used for stretching -enum AudioTimestretchStretchMode : int32_t { - AUDIO_TIMESTRETCH_STRETCH_DEFAULT = 0, - AUDIO_TIMESTRETCH_STRETCH_SPEECH = 1, - //TODO: add more stretch modes/algorithms -}; - -//Limits for AUDIO_TIMESTRETCH_STRETCH_SPEECH mode -#define TIMESTRETCH_SONIC_SPEED_MIN 0.1f -#define TIMESTRETCH_SONIC_SPEED_MAX 6.0f - -//Determines behavior of Timestretch if current algorithm can't perform -//with current parameters. -// FALLBACK_CUT_REPEAT: (internal only) for speed <1.0 will truncate frames -// for speed > 1.0 will repeat frames -// FALLBACK_MUTE: will set all processed frames to zero -// FALLBACK_FAIL: will stop program execution and log a fatal error -enum AudioTimestretchFallbackMode : int32_t { - AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT = -1, - AUDIO_TIMESTRETCH_FALLBACK_DEFAULT = 0, - AUDIO_TIMESTRETCH_FALLBACK_MUTE = 1, - AUDIO_TIMESTRETCH_FALLBACK_FAIL = 2, -}; - -struct AudioPlaybackRate { - float mSpeed; - float mPitch; - enum AudioTimestretchStretchMode mStretchMode; - enum AudioTimestretchFallbackMode mFallbackMode; -}; - -static const AudioPlaybackRate AUDIO_PLAYBACK_RATE_DEFAULT = { - AUDIO_TIMESTRETCH_SPEED_NORMAL, - AUDIO_TIMESTRETCH_PITCH_NORMAL, - AUDIO_TIMESTRETCH_STRETCH_DEFAULT, - AUDIO_TIMESTRETCH_FALLBACK_DEFAULT -}; - -static inline bool isAudioPlaybackRateEqual(const AudioPlaybackRate &pr1, - const AudioPlaybackRate &pr2) { - return fabs(pr1.mSpeed - pr2.mSpeed) < AUDIO_TIMESTRETCH_SPEED_MIN_DELTA && - fabs(pr1.mPitch - pr2.mPitch) < AUDIO_TIMESTRETCH_PITCH_MIN_DELTA && - pr2.mStretchMode == pr2.mStretchMode && - pr2.mFallbackMode == pr2.mFallbackMode; -} - -static inline bool isAudioPlaybackRateValid(const AudioPlaybackRate &playbackRate) { - if (playbackRate.mFallbackMode == AUDIO_TIMESTRETCH_FALLBACK_FAIL && - (playbackRate.mStretchMode == AUDIO_TIMESTRETCH_STRETCH_SPEECH || - playbackRate.mStretchMode == AUDIO_TIMESTRETCH_STRETCH_DEFAULT)) { - //test sonic specific constraints - return playbackRate.mSpeed >= TIMESTRETCH_SONIC_SPEED_MIN && - playbackRate.mSpeed <= TIMESTRETCH_SONIC_SPEED_MAX && - playbackRate.mPitch >= AUDIO_TIMESTRETCH_PITCH_MIN && - playbackRate.mPitch <= AUDIO_TIMESTRETCH_PITCH_MAX; - } else { - return playbackRate.mSpeed >= AUDIO_TIMESTRETCH_SPEED_MIN && - playbackRate.mSpeed <= AUDIO_TIMESTRETCH_SPEED_MAX && - playbackRate.mPitch >= AUDIO_TIMESTRETCH_PITCH_MIN && - playbackRate.mPitch <= AUDIO_TIMESTRETCH_PITCH_MAX; - } -} - -// TODO: Consider putting these inlines into a class scope - -// Returns the source frames needed to resample to destination frames. This is not a precise -// value and depends on the resampler (and possibly how it handles rounding internally). -// Nevertheless, this should be an upper bound on the requirements of the resampler. -// If srcSampleRate and dstSampleRate are equal, then it returns destination frames, which -// may not be true if the resampler is asynchronous. -static inline size_t sourceFramesNeeded( - uint32_t srcSampleRate, size_t dstFramesRequired, uint32_t dstSampleRate) { - // +1 for rounding - always do this even if matched ratio (resampler may use phases not ratio) - // +1 for additional sample needed for interpolation - return srcSampleRate == dstSampleRate ? dstFramesRequired : - size_t((uint64_t)dstFramesRequired * srcSampleRate / dstSampleRate + 1 + 1); -} - -// An upper bound for the number of destination frames possible from srcFrames -// after sample rate conversion. This may be used for buffer sizing. -static inline size_t destinationFramesPossible(size_t srcFrames, uint32_t srcSampleRate, - uint32_t dstSampleRate) { - if (srcSampleRate == dstSampleRate) { - return srcFrames; - } - uint64_t dstFrames = (uint64_t)srcFrames * dstSampleRate / srcSampleRate; - return dstFrames > 2 ? dstFrames - 2 : 0; -} - -static inline size_t sourceFramesNeededWithTimestretch( - uint32_t srcSampleRate, size_t dstFramesRequired, uint32_t dstSampleRate, - float speed) { - // required is the number of input frames the resampler needs - size_t required = sourceFramesNeeded(srcSampleRate, dstFramesRequired, dstSampleRate); - // to deliver this, the time stretcher requires: - return required * (double)speed + 1 + 1; // accounting for rounding dependencies -} - -// Identifies sample rates that we associate with music -// and thus eligible for better resampling and fast capture. -// This is somewhat less than 44100 to allow for pitch correction -// involving resampling as well as asynchronous resampling. -#define AUDIO_PROCESSING_MUSIC_RATE 40000 - -static inline bool isMusicRate(uint32_t sampleRate) { - return sampleRate >= AUDIO_PROCESSING_MUSIC_RATE; -} - -}} // namespace cocos2d { namespace experimental { - -// --------------------------------------------------------------------------- diff --git a/cocos/audio/android/CCThreadPool.cpp b/cocos/audio/android/CCThreadPool.cpp deleted file mode 100644 index bc9213dfe99b..000000000000 --- a/cocos/audio/android/CCThreadPool.cpp +++ /dev/null @@ -1,443 +0,0 @@ -/**************************************************************************** - Copyright (c) 2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - - 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. - - Inspired by https://github.com/vit-vit/CTPL - - ****************************************************************************/ - -#include "audio/android/CCThreadPool.h" -#include - - -#ifdef __ANDROID__ -#include -#define LOG_TAG "ThreadPool" -#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,__VA_ARGS__) -#else -#define LOGD(...) printf(__VA_ARGS__) -#endif - -namespace cocos2d { namespace experimental { - -#define DEFAULT_THREAD_POOL_MIN_NUM (4) -#define DEFAULT_THREAD_POOL_MAX_NUM (20) - -#define DEFAULT_SHRINK_INTERVAL (5.0f) -#define DEFAULT_SHRINK_STEP (2) -#define DEFAULT_STRETCH_STEP (2) - -static ThreadPool *__defaultThreadPool = nullptr; - -ThreadPool *ThreadPool::getDefaultThreadPool() -{ - if (__defaultThreadPool == nullptr) - { - __defaultThreadPool = newCachedThreadPool(DEFAULT_THREAD_POOL_MIN_NUM, - DEFAULT_THREAD_POOL_MAX_NUM, - DEFAULT_SHRINK_INTERVAL, DEFAULT_SHRINK_STEP, - DEFAULT_STRETCH_STEP); - } - - return __defaultThreadPool; -} - -void ThreadPool::destroyDefaultThreadPool() -{ - delete __defaultThreadPool; - __defaultThreadPool = nullptr; -} - -ThreadPool *ThreadPool::newCachedThreadPool(int minThreadNum, int maxThreadNum, int shrinkInterval, - int shrinkStep, int stretchStep) -{ - ThreadPool *pool = new(std::nothrow) ThreadPool(minThreadNum, maxThreadNum); - if (pool != nullptr) - { - pool->setFixedSize(false); - pool->setShrinkInterval(shrinkInterval); - pool->setShrinkStep(shrinkStep); - pool->setStretchStep(stretchStep); - } - return pool; -} - -ThreadPool *ThreadPool::newFixedThreadPool(int threadNum) -{ - ThreadPool *pool = new(std::nothrow) ThreadPool(threadNum, threadNum); - if (pool != nullptr) - { - pool->setFixedSize(true); - } - return pool; -} - -ThreadPool *ThreadPool::newSingleThreadPool() -{ - ThreadPool *pool = new(std::nothrow) ThreadPool(1, 1); - if (pool != nullptr) - { - pool->setFixedSize(true); - } - return pool; -} - -ThreadPool::ThreadPool(int minNum, int maxNum) - : _isDone(false), _isStop(false), _idleThreadNum(0), _minThreadNum(minNum), - _maxThreadNum(maxNum), _initedThreadNum(0), _shrinkInterval(DEFAULT_SHRINK_INTERVAL), - _shrinkStep(DEFAULT_SHRINK_STEP), _stretchStep(DEFAULT_STRETCH_STEP), - _isFixedSize(false) -{ - init(); -} - -// the destructor waits for all the functions in the queue to be finished -ThreadPool::~ThreadPool() -{ - stop(); -} - -// number of idle threads -int ThreadPool::getIdleThreadNum() const -{ - ThreadPool* thiz = const_cast(this); - std::lock_guard lk(thiz->_idleThreadNumMutex); - return _idleThreadNum; -} - -void ThreadPool::init() -{ - gettimeofday(&_lastShrinkTime, nullptr); - - _maxThreadNum = std::max(_minThreadNum, _maxThreadNum); - - _threads.resize(_maxThreadNum); - _abortFlags.resize(_maxThreadNum); - _idleFlags.resize(_maxThreadNum); - _initedFlags.resize(_maxThreadNum); - - for (int i = 0; i < _maxThreadNum; ++i) - { - _idleFlags[i] = std::make_shared>(false); - if (i < _minThreadNum) - { - _abortFlags[i] = std::make_shared>(false); - setThread(i); - _initedFlags[i] = std::make_shared>(true); - ++_initedThreadNum; - } - else - { - _abortFlags[i] = std::make_shared>(true); - _initedFlags[i] = std::make_shared>(false); - } - } -} - -bool ThreadPool::tryShrinkPool() -{ - LOGD("shrink pool, _idleThreadNum = %d \n", getIdleThreadNum()); - - struct timeval before; - gettimeofday(&before, nullptr); - - std::vector threadIDsToJoin; - int maxThreadNumToJoin = std::min(_initedThreadNum - _minThreadNum, _shrinkStep); - - for (int i = 0; i < _maxThreadNum; ++i) - { - if (threadIDsToJoin.size() >= maxThreadNumToJoin) - { - break; - } - - if (*_idleFlags[i]) - { - *_abortFlags[i] = true; - threadIDsToJoin.push_back(i); - } - } - - { - // stop the detached threads that were waiting - std::unique_lock lock(_mutex); - _cv.notify_all(); - } - - for (const auto& threadID : threadIDsToJoin) - { // wait for the computing threads to finish - if (_threads[threadID]->joinable()) - { - _threads[threadID]->join(); - } - - _threads[threadID].reset(); - *_initedFlags[threadID] = false; - --_initedThreadNum; - } - - struct timeval after; - gettimeofday(&after, nullptr); - - float seconds = (after.tv_sec - before.tv_sec) + (after.tv_usec - before.tv_usec) / 1000000.0f; - - LOGD("shrink %d threads, waste: %f seconds\n", (int) threadIDsToJoin.size(), seconds); - - if (_initedThreadNum <= _minThreadNum) - return true; - - return false; -} - -void ThreadPool::stretchPool(int count) -{ - struct timeval before; - gettimeofday(&before, nullptr); - - int oldThreadCount = _initedThreadNum; - int newThreadCount = 0; - - for (int i = 0; i < _maxThreadNum; ++i) - { - if (!*_initedFlags[i]) - { - *_abortFlags[i] = false; - setThread(i); - *_initedFlags[i] = true; - ++_initedThreadNum; - - if (++newThreadCount >= count) - { - break; - } - } - } - - if (newThreadCount > 0) - { - struct timeval after; - gettimeofday(&after, nullptr); - - float seconds = - (after.tv_sec - before.tv_sec) + (after.tv_usec - before.tv_usec) / 1000000.0f; - - LOGD("stretch pool from %d to %d, waste %f seconds\n", oldThreadCount, _initedThreadNum, - seconds); - } -} - -void ThreadPool::pushTask(const std::function& runnable, - TaskType type/* = DEFAULT*/) -{ - if (!_isFixedSize) - { - _idleThreadNumMutex.lock(); - int idleNum = _idleThreadNum; - _idleThreadNumMutex.unlock(); - - if (idleNum > _minThreadNum) - { - if (_taskQueue.empty()) - { - struct timeval now; - gettimeofday(&now, nullptr); - - float seconds = (now.tv_sec - _lastShrinkTime.tv_sec) + - (now.tv_usec - _lastShrinkTime.tv_usec) / 1000000.0f; - if (seconds > _shrinkInterval) - { - tryShrinkPool(); - _lastShrinkTime = now; - } - } - } - else if (idleNum == 0) - { - stretchPool(_stretchStep); - } - } - - auto callback = new(std::nothrow) std::function([runnable](int tid) { - runnable(tid); - }); - - Task task; - task.type = type; - task.callback = callback; - _taskQueue.push(std::move(task)); - - { - std::unique_lock lock(_mutex); - _cv.notify_one(); - } -} - -void ThreadPool::stopAllTasks() -{ - Task task; - while (_taskQueue.pop(task)) - { - delete task.callback; // empty the queue - } -} - -void ThreadPool::stopTasksByType(TaskType type) -{ - Task task; - - std::vector notStopTasks; - notStopTasks.reserve(_taskQueue.size()); - - while (_taskQueue.pop(task)) - { - if (task.type == type) - {// Delete the task from queue - delete task.callback; - } - else - {// If task type isn't match, push it into a vector, then insert to task queue again - notStopTasks.push_back(task); - } - } - - if (!notStopTasks.empty()) - { - for (const auto& t : notStopTasks) - { - _taskQueue.push(t); - } - } -} - -void ThreadPool::joinThread(int tid) -{ - if (tid < 0 || tid >= _threads.size()) - { - LOGD("Invalid thread id %d\n", tid); - return; - } - - // wait for the computing threads to finish - if (*_initedFlags[tid] && _threads[tid]->joinable()) - { - _threads[tid]->join(); - *_initedFlags[tid] = false; - --_initedThreadNum; - } -} - -int ThreadPool::getTaskNum() const -{ - return (int) _taskQueue.size(); -} - -void ThreadPool::setFixedSize(bool isFixedSize) -{ - _isFixedSize = isFixedSize; -} - -void ThreadPool::setShrinkInterval(int seconds) -{ - if (seconds >= 0) - _shrinkInterval = seconds; -} - -void ThreadPool::setShrinkStep(int step) -{ - if (step > 0) - _shrinkStep = step; -} - -void ThreadPool::setStretchStep(int step) -{ - if (step > 0) - _stretchStep = step; -} - -void ThreadPool::stop() -{ - if (_isDone || _isStop) - return; - _isDone = true; // give the waiting threads a command to finish - - { - std::unique_lock lock(_mutex); - _cv.notify_all(); // stop all waiting threads - } - - for (int i = 0, n = static_cast(_threads.size()); i < n; ++i) - { - joinThread(i); - } - // if there were no threads in the pool but some functors in the queue, the functors are not deleted by the threads - // therefore delete them here - stopAllTasks(); - _threads.clear(); - _abortFlags.clear(); -} - -void ThreadPool::setThread(int tid) -{ - std::shared_ptr> abort_ptr( - _abortFlags[tid]); // a copy of the shared ptr to the flag - auto f = [this, tid, abort_ptr/* a copy of the shared ptr to the abort */]() { - std::atomic& abort = *abort_ptr; - Task task; - bool isPop = _taskQueue.pop(task); - while (true) - { - while (isPop) - { // if there is anything in the queue - std::unique_ptr> func( - task.callback); // at return, delete the function even if an exception occurred - (*task.callback)(tid); - if (abort) - return; // the thread is wanted to stop, return even if the queue is not empty yet - else - isPop = _taskQueue.pop(task); - } - // the queue is empty here, wait for the next command - std::unique_lock lock(_mutex); - _idleThreadNumMutex.lock(); - ++_idleThreadNum; - _idleThreadNumMutex.unlock(); - - *_idleFlags[tid] = true; - _cv.wait(lock, [this, &task, &isPop, &abort]() { - isPop = _taskQueue.pop(task); - return isPop || _isDone || abort; - }); - *_idleFlags[tid] = false; - _idleThreadNumMutex.lock(); - --_idleThreadNum; - _idleThreadNumMutex.unlock(); - - if (!isPop) - return; // if the queue is empty and isDone == true or *flag then return - } - }; - _threads[tid].reset( - new(std::nothrow) std::thread(f)); // compiler may not support std::make_unique() -} - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/CCThreadPool.h b/cocos/audio/android/CCThreadPool.h deleted file mode 100644 index 6f20dc2833c6..000000000000 --- a/cocos/audio/android/CCThreadPool.h +++ /dev/null @@ -1,237 +0,0 @@ -/**************************************************************************** - Copyright (c) 2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - - 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. - - Inspired by https://github.com/vit-vit/CTPL - - ****************************************************************************/ -#pragma once - -//#include "platform/CCPlatformMacros.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cocos2d { namespace experimental { - -/** - * @addtogroup base - * @{ - */ - -class ThreadPool -{ -public: - - enum class TaskType - { - DEFAULT = 0, - NETWORK, - IO, - AUDIO, - USER = 1000 - }; - - /* - * Gets the default thread pool which is a cached thread pool with default parameters. - */ - static ThreadPool *getDefaultThreadPool(); - - /* - * Destroys the default thread pool - */ - static void destroyDefaultThreadPool(); - - /* - * Creates a cached thread pool - * @note The return value has to be delete while it doesn't needed - */ - static ThreadPool *newCachedThreadPool(int minThreadNum, int maxThreadNum, int shrinkInterval, - int shrinkStep, int stretchStep); - - /* - * Creates a thread pool with fixed thread count - * @note The return value has to be delete while it doesn't needed - */ - static ThreadPool *newFixedThreadPool(int threadNum); - - /* - * Creates a thread pool with only one thread in the pool, it could be used to execute multiply tasks serially in just one thread. - * @note The return value has to be delete while it doesn't needed - */ - static ThreadPool *newSingleThreadPool(); - - // the destructor waits for all the functions in the queue to be finished - ~ThreadPool(); - - /* Pushs a task to thread pool - * @param runnable The callback of the task executed in sub thread - * @param type The task type, it's TASK_TYPE_DEFAULT if this argument isn't assigned - * @note This function has to be invoked in cocos thread - */ - void pushTask(const std::function& runnable, TaskType type = TaskType::DEFAULT); - - // Stops all tasks, it will remove all tasks in queue - void stopAllTasks(); - - // Stops some tasks by type - void stopTasksByType(TaskType type); - - // Gets the minimum thread numbers - inline int getMinThreadNum() const - { return _minThreadNum; }; - - // Gets the maximum thread numbers - inline int getMaxThreadNum() const - { return _maxThreadNum; }; - - // Gets the number of idle threads - int getIdleThreadNum() const; - - // Gets the number of initialized threads - inline int getInitedThreadNum() const - { return _initedThreadNum; }; - - // Gets the task number - int getTaskNum() const; - - /* - * Trys to shrink pool - * @note This method is only available for cached thread pool - */ - bool tryShrinkPool(); - -private: - ThreadPool(int minNum, int maxNum); - - ThreadPool(const ThreadPool&); - - ThreadPool(ThreadPool&&); - - ThreadPool& operator=(const ThreadPool&); - - ThreadPool& operator=(ThreadPool&&); - - void init(); - - void stop(); - - void setThread(int tid); - - void joinThread(int tid); - - void setFixedSize(bool isFixedSize); - - void setShrinkInterval(int seconds); - - void setShrinkStep(int step); - - void setStretchStep(int step); - - void stretchPool(int count); - - std::vector> _threads; - std::vector>> _abortFlags; - std::vector>> _idleFlags; - std::vector>> _initedFlags; - - template - class ThreadSafeQueue - { - public: - bool push(T const& value) - { - std::unique_lock lock(this->mutex); - this->q.push(value); - return true; - } - - // deletes the retrieved element, do not use for non integral types - bool pop(T& v) - { - std::unique_lock lock(this->mutex); - if (this->q.empty()) - return false; - v = this->q.front(); - this->q.pop(); - return true; - } - - bool empty() const - { - auto thiz = const_cast(this); - std::unique_lock lock(thiz->mutex); - return this->q.empty(); - } - - size_t size() const - { - auto thiz = const_cast(this); - std::unique_lock lock(thiz->mutex); - return this->q.size(); - } - - private: - std::queue q; - std::mutex mutex; - }; - - struct Task - { - TaskType type; - std::function *callback; - }; - - ThreadSafeQueue _taskQueue; - std::atomic _isDone; - std::atomic _isStop; - - //FIXME: std::atomic isn't supported by ndk-r10e while compiling with `armeabi` arch. - // So using a mutex here instead. - int _idleThreadNum; // how many threads are waiting - std::mutex _idleThreadNumMutex; - - std::mutex _mutex; - std::condition_variable _cv; - - int _minThreadNum; - int _maxThreadNum; - int _initedThreadNum; - - struct timeval _lastShrinkTime; - float _shrinkInterval; - int _shrinkStep; - int _stretchStep; - bool _isFixedSize; -}; - -// end of base group -/// @} - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/IAudioPlayer.h b/cocos/audio/android/IAudioPlayer.h deleted file mode 100644 index c1e58c0203a5..000000000000 --- a/cocos/audio/android/IAudioPlayer.h +++ /dev/null @@ -1,89 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#pragma once - -#include - -namespace cocos2d { namespace experimental { - -class IAudioPlayer -{ -public: - enum class State - { - INVALID = 0, - INITIALIZED, - PLAYING, - PAUSED, - STOPPED, - OVER - }; - - using PlayEventCallback = std::function; - - virtual ~IAudioPlayer() - { }; - - virtual int getId() const = 0; - - virtual void setId(int id) = 0; - - virtual std::string getUrl() const = 0; - - virtual State getState() const = 0; - - virtual void play() = 0; - - virtual void pause() = 0; - - virtual void resume() = 0; - - virtual void stop() = 0; - - virtual void rewind() = 0; - - virtual void setVolume(float volume) = 0; - - virtual float getVolume() const = 0; - - virtual void setAudioFocus(bool isFocus) = 0; - - virtual void setLoop(bool isLoop) = 0; - - virtual bool isLoop() const = 0; - - virtual float getDuration() const = 0; - - virtual float getPosition() const = 0; - - virtual bool setPosition(float pos) = 0; - - // @note: STOPPED event is invoked in main thread - // OVER event is invoked in sub thread - virtual void setPlayEventCallback(const PlayEventCallback &playEventCallback) = 0; -}; - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/ICallerThreadUtils.h b/cocos/audio/android/ICallerThreadUtils.h deleted file mode 100644 index 54ac681799db..000000000000 --- a/cocos/audio/android/ICallerThreadUtils.h +++ /dev/null @@ -1,42 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ -#pragma once - -#include -#include - -namespace cocos2d { namespace experimental { - -class ICallerThreadUtils -{ -public: - virtual ~ICallerThreadUtils() - { }; - - virtual void performFunctionInCallerThread(const std::function& func) = 0; - virtual std::thread::id getCallerThreadId() = 0; -}; - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/IVolumeProvider.h b/cocos/audio/android/IVolumeProvider.h deleted file mode 100644 index 076ac65b260f..000000000000 --- a/cocos/audio/android/IVolumeProvider.h +++ /dev/null @@ -1,45 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ -#pragma once - -#include "audio/android/audio_utils/include/audio_utils/minifloat.h" - -namespace cocos2d { namespace experimental { - -class IVolumeProvider -{ -public: - // The provider implementation is responsible for validating that the return value is in range. - virtual gain_minifloat_packed_t getVolumeLR() = 0; - -protected: - IVolumeProvider() - { } - - virtual ~IVolumeProvider() - { } -}; - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/OpenSLHelper.h b/cocos/audio/android/OpenSLHelper.h deleted file mode 100644 index bc8131694e83..000000000000 --- a/cocos/audio/android/OpenSLHelper.h +++ /dev/null @@ -1,100 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#pragma once - -#include "audio/android/cutils/log.h" - -#include -#include -#include -#include - -#define SL_SAFE_DELETE(obj) \ - if ((obj) != nullptr) { delete (obj); (obj) = nullptr; } - -#define SL_DESTROY_OBJ(OBJ) \ - if ((OBJ) != nullptr) { \ - (*(OBJ))->Destroy(OBJ); \ - (OBJ) = nullptr; \ - } - -#define SL_RETURN_VAL_IF_FAILED(r, rval, ...) \ - if (r != SL_RESULT_SUCCESS) {\ - ALOGE(__VA_ARGS__); \ - return rval; \ - } - -#define SL_RETURN_IF_FAILED(r, ...) \ - if (r != SL_RESULT_SUCCESS) {\ - ALOGE(__VA_ARGS__); \ - return; \ - } - -#define SL_PRINT_ERROR_IF_FAILED(r, ...) \ - if (r != SL_RESULT_SUCCESS) {\ - ALOGE(__VA_ARGS__); \ - } - -typedef std::function FdGetterCallback; - - -// Copied from OpenSLES_AndroidMetadata.h in android-21 -// It's because android-10 doesn't contain this header file -/** - * Additional metadata keys to be used in SLMetadataExtractionItf: - * the ANDROID_KEY_PCMFORMAT_* keys follow the fields of the SLDataFormat_PCM struct, and as such - * all values corresponding to these keys are of SLuint32 type, and are defined as the fields - * of the same name in SLDataFormat_PCM. The exception is that sample rate is expressed here - * in Hz units, rather than in milliHz units. - */ -#ifndef ANDROID_KEY_PCMFORMAT_NUMCHANNELS -#define ANDROID_KEY_PCMFORMAT_NUMCHANNELS "AndroidPcmFormatNumChannels" -#endif - -#ifndef ANDROID_KEY_PCMFORMAT_SAMPLERATE -#define ANDROID_KEY_PCMFORMAT_SAMPLERATE "AndroidPcmFormatSampleRate" -#endif - -#ifndef ANDROID_KEY_PCMFORMAT_BITSPERSAMPLE -#define ANDROID_KEY_PCMFORMAT_BITSPERSAMPLE "AndroidPcmFormatBitsPerSample" -#endif - -#ifndef ANDROID_KEY_PCMFORMAT_CONTAINERSIZE -#define ANDROID_KEY_PCMFORMAT_CONTAINERSIZE "AndroidPcmFormatContainerSize" -#endif - -#ifndef ANDROID_KEY_PCMFORMAT_CHANNELMASK -#define ANDROID_KEY_PCMFORMAT_CHANNELMASK "AndroidPcmFormatChannelMask" -#endif - -#ifndef ANDROID_KEY_PCMFORMAT_ENDIANNESS -#define ANDROID_KEY_PCMFORMAT_ENDIANNESS "AndroidPcmFormatEndianness" -#endif - -#define clockNow() std::chrono::high_resolution_clock::now() -#define intervalInMS(oldTime, newTime) (static_cast(std::chrono::duration_cast((newTime) - (oldTime)).count()) / 1000.f) - -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) diff --git a/cocos/audio/android/PcmAudioPlayer.cpp b/cocos/audio/android/PcmAudioPlayer.cpp deleted file mode 100644 index 2fbba41b0d5d..000000000000 --- a/cocos/audio/android/PcmAudioPlayer.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#define LOG_TAG "PcmAudioPlayer" - -#include "audio/android/cutils/log.h" -#include "audio/android/PcmAudioPlayer.h" -#include "audio/android/AudioMixerController.h" -#include "audio/android/ICallerThreadUtils.h" - -namespace cocos2d { namespace experimental { - -PcmAudioPlayer::PcmAudioPlayer(AudioMixerController * controller, ICallerThreadUtils* callerThreadUtils) - : _id(-1) - , _track(nullptr) - , _playEventCallback(nullptr) - , _controller(controller) - , _callerThreadUtils(callerThreadUtils) -{ - ALOGV("PcmAudioPlayer constructor: %p", this); -} - -PcmAudioPlayer::~PcmAudioPlayer() -{ - ALOGV("In the destructor of PcmAudioPlayer (%p)", this); - delete _track; -} - -bool PcmAudioPlayer::prepare(const std::string &url, const PcmData &decResult) -{ - _url = url; - _decResult = decResult; - - _track = new (std::nothrow) Track(_decResult); - - std::thread::id callerThreadId = _callerThreadUtils->getCallerThreadId(); - - // @note The logic may cause this issue https://github.com/cocos2d/cocos2d-x/issues/17707 - // Assume that AudioEngine::stop(id) is invoked and the audio is played over meanwhile. - // Since State::OVER and State::DESTROYED are triggered in the audio mixing thread, it will - // call 'performFunctionInCallerThread' to post events to cocos's message queue. - // Therefore, the sequence in cocos's thread will be |STOP|OVER|DESTROYED|. - // Although, we remove the audio id in |STOPPED| callback, because it's asynchronous operation, - // |OVER| and |DESTROYED| callbacks will still be invoked in cocos's thread. - // HOW TO FIX: If the previous state is |STOPPED| and the current state - // is |OVER|, just skip to invoke |OVER| callback. - - _track->onStateChanged = [this, callerThreadId](Track::State state) { - // It maybe in sub thread - Track::State prevState = _track->getPrevState(); - auto func = [this, state, prevState](){ - // It's in caller's thread - if (state == Track::State::OVER && prevState != Track::State::STOPPED) - { - if (_playEventCallback != nullptr) - { - _playEventCallback(State::OVER); - } - } - else if (state == Track::State::STOPPED) - { - if (_playEventCallback != nullptr) - { - _playEventCallback(State::STOPPED); - } - } - else if (state == Track::State::DESTROYED) - { - delete this; - } - }; - - if (callerThreadId == std::this_thread::get_id()) - { // onStateChanged(Track::State::STOPPED) is in caller's (Cocos's) thread. - func(); - } - else - { // onStateChanged(Track::State::OVER) or onStateChanged(Track::State::DESTROYED) are in audio mixing thread. - _callerThreadUtils->performFunctionInCallerThread(func); - } - }; - - setVolume(1.0f); - - return true; -} - -void PcmAudioPlayer::rewind() -{ - ALOGW("PcmAudioPlayer::rewind isn't supported!"); -} - -void PcmAudioPlayer::setVolume(float volume) -{ - _track->setVolume(volume); -} - -float PcmAudioPlayer::getVolume() const -{ - return _track->getVolume(); -} - -void PcmAudioPlayer::setAudioFocus(bool isFocus) -{ - _track->setAudioFocus(isFocus); -} - -void PcmAudioPlayer::setLoop(bool isLoop) -{ - _track->setLoop(isLoop); -} - -bool PcmAudioPlayer::isLoop() const -{ - return _track->isLoop(); -} - -float PcmAudioPlayer::getDuration() const -{ - return _decResult.duration; -} - -float PcmAudioPlayer::getPosition() const -{ - return _track->getPosition(); -} - -bool PcmAudioPlayer::setPosition(float pos) -{ - return _track->setPosition(pos); -} - -void PcmAudioPlayer::setPlayEventCallback(const PlayEventCallback &playEventCallback) -{ - _playEventCallback = playEventCallback; -} - -void PcmAudioPlayer::play() -{ - // put track to AudioMixerController - ALOGV("PcmAudioPlayer (%p) play, url: %s", this, _url.c_str()); - _controller->addTrack(_track); - _track->setState(Track::State::PLAYING); -} - -void PcmAudioPlayer::pause() -{ - ALOGV("PcmAudioPlayer (%p) pause, url: %s", this, _url.c_str()); - _track->setState(Track::State::PAUSED); -} - -void PcmAudioPlayer::resume() -{ - ALOGV("PcmAudioPlayer (%p) resume, url: %s", this, _url.c_str()); - _track->setState(Track::State::RESUMED); -} - -void PcmAudioPlayer::stop() -{ - ALOGV("PcmAudioPlayer (%p) stop, url: %s", this, _url.c_str()); - _track->setState(Track::State::STOPPED); -} - -IAudioPlayer::State PcmAudioPlayer::getState() const -{ - IAudioPlayer::State state = State::INVALID; - - if (_track != nullptr) - { - switch (_track->getState()) - { - case Track::State::IDLE: - state = State::INITIALIZED; - break; - - case Track::State::PLAYING: - state = State::PLAYING; - break; - - case Track::State::RESUMED: - state = State::PLAYING; - break; - - case Track::State::PAUSED: - state = State::PAUSED; - break; - - case Track::State::STOPPED: - state = State::STOPPED; - break; - - case Track::State::OVER: - state = State::OVER; - break; - - default: - state = State::INVALID; - break; - } - } - return state; -} - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/PcmAudioPlayer.h b/cocos/audio/android/PcmAudioPlayer.h deleted file mode 100644 index aaacefc5a07b..000000000000 --- a/cocos/audio/android/PcmAudioPlayer.h +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ -#pragma once - -#include -#include "audio/android/IAudioPlayer.h" -#include "audio/android/PcmData.h" -#include "audio/android/Track.h" - -namespace cocos2d { namespace experimental { - -class ICallerThreadUtils; -class AudioMixerController; - -class PcmAudioPlayer : public IAudioPlayer -{ -public: - - bool prepare(const std::string &url, const PcmData &decResult); - - // Override Functions Begin - virtual int getId() const override { return _id; }; - - virtual void setId(int id) override { _id = id; }; - - virtual std::string getUrl() const override { return _url; }; - - virtual State getState() const override; - - virtual void play() override; - - virtual void pause() override; - - virtual void resume() override; - - virtual void stop() override; - - virtual void rewind() override; - - virtual void setVolume(float volume) override; - - virtual float getVolume() const override; - - virtual void setAudioFocus(bool isFocus) override; - - virtual void setLoop(bool isLoop) override; - - virtual bool isLoop() const override; - - virtual float getDuration() const override; - - virtual float getPosition() const override; - - virtual bool setPosition(float pos) override; - - virtual void setPlayEventCallback(const PlayEventCallback &playEventCallback) override; - - // Override Functions End - -private: - PcmAudioPlayer(AudioMixerController * controller, ICallerThreadUtils* callerThreadUtils); - virtual ~PcmAudioPlayer(); - -private: - int _id; - std::string _url; - PcmData _decResult; - Track* _track; - PlayEventCallback _playEventCallback; - AudioMixerController * _controller; - ICallerThreadUtils* _callerThreadUtils; - - friend class AudioPlayerProvider; -}; - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/PcmAudioService.cpp b/cocos/audio/android/PcmAudioService.cpp deleted file mode 100644 index e6aa23c848b2..000000000000 --- a/cocos/audio/android/PcmAudioService.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#define LOG_TAG "PcmAudioService" - -#include "audio/android/PcmAudioService.h" -#include "audio/android/AudioMixerController.h" - -namespace cocos2d { namespace experimental { - -static std::vector __silenceData; - -#define AUDIO_PLAYER_BUFFER_COUNT (2) - -class SLPcmAudioPlayerCallbackProxy -{ -public: - static void samplePlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) - { - PcmAudioService *thiz = reinterpret_cast(context); - thiz->bqFetchBufferCallback(bq); - } -}; - -PcmAudioService::PcmAudioService(SLEngineItf engineItf, SLObjectItf outputMixObject) - : _engineItf(engineItf), _outputMixObj(outputMixObject), _playObj(nullptr), - _playItf(nullptr), _volumeItf(nullptr), _bufferQueueItf(nullptr), _numChannels(-1), - _sampleRate(-1), _bufferSizeInBytes(0), _controller(nullptr), _isInitialised(false) -{ -} - -PcmAudioService::~PcmAudioService() -{ - ALOGV("PcmAudioServicee() (%p), before destroy play object", this); - SL_DESTROY_OBJ(_playObj); - ALOGV("PcmAudioServicee() end"); -} - -bool PcmAudioService::enqueue() -{ - if (_controller->hasPlayingTacks()) - { - if (_controller->isPaused()) - { - SLresult r = (*_bufferQueueItf)->Enqueue(_bufferQueueItf, __silenceData.data(), __silenceData.size()); - SL_RETURN_VAL_IF_FAILED(r, false, "enqueue silent data failed!"); - } - else - { - _controller->mixOneFrame(); - - auto current = _controller->current(); - ALOG_ASSERT(current != nullptr, "current buffer is nullptr ..."); - SLresult r = (*_bufferQueueItf)->Enqueue(_bufferQueueItf, current->buf, current->size); - SL_RETURN_VAL_IF_FAILED(r, false, "enqueue failed!"); - } - } - else - { - SLresult r = (*_bufferQueueItf)->Enqueue(_bufferQueueItf, __silenceData.data(), __silenceData.size()); - SL_RETURN_VAL_IF_FAILED(r, false, "enqueue silent data failed!"); - } - - return true; -} - -void PcmAudioService::bqFetchBufferCallback(SLAndroidSimpleBufferQueueItf bq) -{ - // FIXME: PcmAudioService instance may be destroyed, we need to find a way to wait... - // It's in sub thread - enqueue(); -} - -bool PcmAudioService::init(AudioMixerController* controller, int numChannels, int sampleRate, int bufferSizeInBytes) -{ - _controller = controller; - _numChannels = numChannels; - _sampleRate = sampleRate; - _bufferSizeInBytes = bufferSizeInBytes; - - SLuint32 channelMask = SL_SPEAKER_FRONT_CENTER; - - if (numChannels > 1) - { - channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - } - - SLDataFormat_PCM formatPcm = { - SL_DATAFORMAT_PCM, - (SLuint32) numChannels, - (SLuint32) sampleRate * 1000, - SL_PCMSAMPLEFORMAT_FIXED_16, - SL_PCMSAMPLEFORMAT_FIXED_16, - channelMask, - SL_BYTEORDER_LITTLEENDIAN - }; - - SLDataLocator_AndroidSimpleBufferQueue locBufQueue = { - SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, - AUDIO_PLAYER_BUFFER_COUNT - }; - SLDataSource source = {&locBufQueue, &formatPcm}; - - SLDataLocator_OutputMix locOutmix = { - SL_DATALOCATOR_OUTPUTMIX, - _outputMixObj - }; - SLDataSink sink = {&locOutmix, nullptr}; - - const SLInterfaceID ids[] = { - SL_IID_PLAY, - SL_IID_VOLUME, - SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - }; - - const SLboolean req[] = { - SL_BOOLEAN_TRUE, - SL_BOOLEAN_TRUE, - SL_BOOLEAN_TRUE, - }; - - SLresult r; - - r = (*_engineItf)->CreateAudioPlayer(_engineItf, &_playObj, &source, &sink, - sizeof(ids) / sizeof(ids[0]), ids, req); - SL_RETURN_VAL_IF_FAILED(r, false, "CreateAudioPlayer failed"); - - r = (*_playObj)->Realize(_playObj, SL_BOOLEAN_FALSE); - SL_RETURN_VAL_IF_FAILED(r, false, "Realize failed"); - - r = (*_playObj)->GetInterface(_playObj, SL_IID_PLAY, &_playItf); - SL_RETURN_VAL_IF_FAILED(r, false, "GetInterface SL_IID_PLAY failed"); - - r = (*_playObj)->GetInterface(_playObj, SL_IID_VOLUME, &_volumeItf); - SL_RETURN_VAL_IF_FAILED(r, false, "GetInterface SL_IID_VOLUME failed"); - - r = (*_playObj)->GetInterface(_playObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &_bufferQueueItf); - SL_RETURN_VAL_IF_FAILED(r, false, "GetInterface SL_IID_ANDROIDSIMPLEBUFFERQUEUE failed"); - - r = (*_bufferQueueItf)->RegisterCallback(_bufferQueueItf, - SLPcmAudioPlayerCallbackProxy::samplePlayerCallback, - this); - SL_RETURN_VAL_IF_FAILED(r, false, "_bufferQueueItf RegisterCallback failed"); - - if (__silenceData.empty()) - { - __silenceData.resize(_numChannels * _bufferSizeInBytes, 0x00); - } - - r = (*_bufferQueueItf)->Enqueue(_bufferQueueItf, __silenceData.data(), __silenceData.size()); - SL_RETURN_VAL_IF_FAILED(r, false, "_bufferQueueItf Enqueue failed"); - - r = (*_playItf)->SetPlayState(_playItf, SL_PLAYSTATE_PLAYING); - SL_RETURN_VAL_IF_FAILED(r, false, "SetPlayState failed"); - - _isInitialised = true; - return true; -} - -void PcmAudioService::pause() -{ - if (_isInitialised) - { - SLresult r = (*_playItf)->SetPlayState(_playItf, SL_PLAYSTATE_PAUSED); - SL_RETURN_IF_FAILED(r, "PcmAudioService::pause failed"); - } -} - -void PcmAudioService::resume() -{ - if (_isInitialised) - { - SLresult r = (*_playItf)->SetPlayState(_playItf, SL_PLAYSTATE_PLAYING); - SL_RETURN_IF_FAILED(r, "PcmAudioService::resume failed"); - } -} - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/PcmAudioService.h b/cocos/audio/android/PcmAudioService.h deleted file mode 100644 index 27559f2c7f43..000000000000 --- a/cocos/audio/android/PcmAudioService.h +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#pragma once - -#include "audio/android/IAudioPlayer.h" -#include "audio/android/OpenSLHelper.h" -#include "audio/android/PcmData.h" - -#include -#include - -namespace cocos2d { namespace experimental { - -class AudioMixerController; - -class PcmAudioService -{ -public: - inline int getChannelCount() const - { return _numChannels; }; - - inline int getSampleRate() const - { return _sampleRate; }; - -private: - PcmAudioService(SLEngineItf engineItf, SLObjectItf outputMixObject); - - virtual ~PcmAudioService(); - - bool init(AudioMixerController* controller, int numChannels, int sampleRate, int bufferSizeInBytes); - - bool enqueue(); - - void bqFetchBufferCallback(SLAndroidSimpleBufferQueueItf bq); - - void pause(); - void resume(); - -private: - SLEngineItf _engineItf; - SLObjectItf _outputMixObj; - - SLObjectItf _playObj; - SLPlayItf _playItf; - SLVolumeItf _volumeItf; - SLAndroidSimpleBufferQueueItf _bufferQueueItf; - - int _numChannels; - int _sampleRate; - int _bufferSizeInBytes; - bool _isInitialised; - - AudioMixerController* _controller; - - friend class SLPcmAudioPlayerCallbackProxy; - friend class AudioPlayerProvider; -}; - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/PcmBufferProvider.cpp b/cocos/audio/android/PcmBufferProvider.cpp deleted file mode 100644 index 4ed2011c1a91..000000000000 --- a/cocos/audio/android/PcmBufferProvider.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#define LOG_TAG "PcmBufferProvider" - -#include "audio/android/cutils/log.h" -#include "audio/android/PcmBufferProvider.h" - -//#define VERY_VERY_VERBOSE_LOGGING -#ifdef VERY_VERY_VERBOSE_LOGGING -#define ALOGVV ALOGV -#else -#define ALOGVV(a...) do { } while (0) -#endif - -namespace cocos2d { namespace experimental { - -PcmBufferProvider::PcmBufferProvider() - : _addr(nullptr) - , _numFrames(0) - , _frameSize(0) - , _nextFrame(0) - , _unrel(0) -{ - -} - -bool PcmBufferProvider::init(const void *addr, size_t frames, size_t frameSize) -{ - _addr = addr; - _numFrames = frames; - _frameSize = frameSize; - _nextFrame = 0; - _unrel = 0; - return true; -} - -status_t PcmBufferProvider::getNextBuffer(Buffer *buffer, - int64_t pts/* = kInvalidPTS*/) { - (void) pts; // suppress warning - size_t requestedFrames = buffer->frameCount; - if (requestedFrames > _numFrames - _nextFrame) { - buffer->frameCount = _numFrames - _nextFrame; - } - - ALOGVV("getNextBuffer() requested %zu frames out of %zu frames available," - " and returned %zu frames", - requestedFrames, (size_t) (_numFrames - _nextFrame), buffer->frameCount); - - _unrel = buffer->frameCount; - if (buffer->frameCount > 0) { - buffer->raw = (char *) _addr + _frameSize * _nextFrame; - return NO_ERROR; - } else { - buffer->raw = NULL; - return NOT_ENOUGH_DATA; - } -} - -void PcmBufferProvider::releaseBuffer(Buffer *buffer) { - if (buffer->frameCount > _unrel) { - ALOGVV("ERROR releaseBuffer() released %zu frames but only %zu available " - "to release", buffer->frameCount, _unrel); - _nextFrame += _unrel; - _unrel = 0; - } else { - ALOGVV("releaseBuffer() released %zu frames out of %zu frames available " - "to release", buffer->frameCount, _unrel); - _nextFrame += buffer->frameCount; - _unrel -= buffer->frameCount; - } - buffer->frameCount = 0; - buffer->raw = NULL; -} - -void PcmBufferProvider::reset() { - _nextFrame = 0; -} - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/PcmBufferProvider.h b/cocos/audio/android/PcmBufferProvider.h deleted file mode 100644 index e5f5a2a7d5eb..000000000000 --- a/cocos/audio/android/PcmBufferProvider.h +++ /dev/null @@ -1,52 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#pragma once - -#include "audio/android/AudioBufferProvider.h" - -#include -#include - -namespace cocos2d { namespace experimental { - -class PcmBufferProvider : public AudioBufferProvider -{ -public: - PcmBufferProvider(); - bool init(const void *addr, size_t frames, size_t frameSize); - virtual status_t getNextBuffer(Buffer *buffer, int64_t pts = kInvalidPTS) override ; - virtual void releaseBuffer(Buffer *buffer) override ; - void reset(); - -protected: - const void *_addr; // base address - size_t _numFrames; // total frames - size_t _frameSize; // size of each frame in bytes - size_t _nextFrame; // index of next frame to provide - size_t _unrel; // number of frames not yet released -}; - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/PcmData.cpp b/cocos/audio/android/PcmData.cpp deleted file mode 100644 index a3bd8ddaa3d3..000000000000 --- a/cocos/audio/android/PcmData.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#define LOG_TAG "PcmData" - -#include "audio/android/OpenSLHelper.h" -#include "audio/android/PcmData.h" - -namespace cocos2d { namespace experimental { - -PcmData::PcmData() -{ -// ALOGV("In the constructor of PcmData (%p)", this); - reset(); -} - -PcmData::~PcmData() -{ -// ALOGV("In the destructor of PcmData (%p)", this); -} - -PcmData::PcmData(const PcmData &o) -{ -// ALOGV("In the copy constructor of PcmData (%p)", this); - numChannels = o.numChannels; - sampleRate = o.sampleRate; - bitsPerSample = o.bitsPerSample; - containerSize = o.containerSize; - channelMask = o.channelMask; - endianness = o.endianness; - numFrames = o.numFrames; - duration = o.duration; - pcmBuffer = std::move(o.pcmBuffer); -} - -PcmData::PcmData(PcmData &&o) -{ -// ALOGV("In the move constructor of PcmData (%p)", this); - numChannels = o.numChannels; - sampleRate = o.sampleRate; - bitsPerSample = o.bitsPerSample; - containerSize = o.containerSize; - channelMask = o.channelMask; - endianness = o.endianness; - numFrames = o.numFrames; - duration = o.duration; - pcmBuffer = std::move(o.pcmBuffer); - o.reset(); -} - -PcmData &PcmData::operator=(const PcmData &o) -{ -// ALOGV("In the copy assignment of PcmData"); - numChannels = o.numChannels; - sampleRate = o.sampleRate; - bitsPerSample = o.bitsPerSample; - containerSize = o.containerSize; - channelMask = o.channelMask; - endianness = o.endianness; - numFrames = o.numFrames; - duration = o.duration; - pcmBuffer = o.pcmBuffer; - return *this; -} - -PcmData &PcmData::operator=(PcmData &&o) -{ -// ALOGV("In the move assignment of PcmData"); - numChannels = o.numChannels; - sampleRate = o.sampleRate; - bitsPerSample = o.bitsPerSample; - containerSize = o.containerSize; - channelMask = o.channelMask; - endianness = o.endianness; - numFrames = o.numFrames; - duration = o.duration; - pcmBuffer = std::move(o.pcmBuffer); - o.reset(); - return *this; -} - -void PcmData::reset() -{ - numChannels = -1; - sampleRate = -1; - bitsPerSample = -1; - containerSize = -1; - channelMask = -1; - endianness = -1; - numFrames = -1; - duration = -1.0f; - pcmBuffer = nullptr; -} - -bool PcmData::isValid() const -{ - return numChannels > 0 && sampleRate > 0 && bitsPerSample > 0 && containerSize > 0 - && numFrames > 0 && duration > 0 && pcmBuffer != nullptr; -} - -std::string PcmData::toString() const -{ - std::string ret; - char buf[256] = {0}; - - snprintf(buf, sizeof(buf), - "numChannels: %d, sampleRate: %d, bitPerSample: %d, containerSize: %d, " - "channelMask: %d, endianness: %d, numFrames: %d, duration: %f", - numChannels, sampleRate, bitsPerSample, containerSize, channelMask, endianness, - numFrames, duration - ); - - ret = buf; - return ret; -} - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/PcmData.h b/cocos/audio/android/PcmData.h deleted file mode 100644 index d0445e0cc0ae..000000000000 --- a/cocos/audio/android/PcmData.h +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#pragma once - -#include -#include -#include -#include - -namespace cocos2d { namespace experimental { - -struct PcmData -{ - std::shared_ptr> pcmBuffer; - int numChannels; - int sampleRate; - int bitsPerSample; - int containerSize; - int channelMask; - int endianness; - int numFrames; - float duration; // in seconds - - PcmData(); - - ~PcmData(); - - PcmData(const PcmData &o); - - PcmData(PcmData &&o); - - PcmData &operator=(const PcmData &o); - - PcmData &operator=(PcmData &&o); - - void reset(); - - bool isValid() const; - - std::string toString() const; -}; - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/Track.cpp b/cocos/audio/android/Track.cpp deleted file mode 100644 index d2f7c9be4da2..000000000000 --- a/cocos/audio/android/Track.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#define LOG_TAG "Track" - -#include "audio/android/cutils/log.h" -#include "audio/android/Track.h" - -#include - -namespace cocos2d { namespace experimental { - -Track::Track(const PcmData &pcmData) - : onStateChanged(nullptr) - , _pcmData(pcmData) - , _prevState(State::IDLE) - , _state(State::IDLE) - , _name(-1) - , _volume(1.0f) - , _isVolumeDirty(true) - , _isLoop(false) - , _isInitialized(false) - , _isAudioFocus(true) -{ - init(_pcmData.pcmBuffer->data(), _pcmData.numFrames, _pcmData.bitsPerSample / 8 * _pcmData.numChannels); -} - -Track::~Track() -{ - ALOGV("~Track(): %p", this); -} - -gain_minifloat_packed_t Track::getVolumeLR() -{ - float volume = _isAudioFocus ? _volume : 0.0f; - gain_minifloat_t v = gain_from_float(volume); - return gain_minifloat_pack(v, v); -} - -bool Track::setPosition(float pos) -{ - _nextFrame = (size_t) (pos * _numFrames / _pcmData.duration); - _unrel = 0; - return true; -} - -float Track::getPosition() const -{ - return _nextFrame * _pcmData.duration / _numFrames; -} - -void Track::setVolume(float volume) -{ - std::lock_guard lk(_volumeDirtyMutex); - if (fabs(_volume - volume) > 0.00001) - { - _volume = volume; - setVolumeDirty(true); - } -} - -float Track::getVolume() const -{ - return _volume; -} - -void Track::setAudioFocus(bool isFocus) -{ - _isAudioFocus = isFocus; - setVolumeDirty(true); -} - -void Track::setState(State state) -{ - std::lock_guard lk(_stateMutex); - if (_state != state) - { - _prevState = _state; - _state = state; - onStateChanged(_state); - } -}; - -}} // namespace cocos2d { namespace experimental { \ No newline at end of file diff --git a/cocos/audio/android/Track.h b/cocos/audio/android/Track.h deleted file mode 100644 index 09b8e2eb2f97..000000000000 --- a/cocos/audio/android/Track.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#pragma once - -#include "audio/android/PcmData.h" -#include "audio/android/IVolumeProvider.h" -#include "audio/android/PcmBufferProvider.h" - -#include -#include - -namespace cocos2d { namespace experimental { - -class Track : public PcmBufferProvider, public IVolumeProvider -{ -public: - enum class State - { - IDLE, - PLAYING, - RESUMED, - PAUSED, - STOPPED, - OVER, - DESTROYED - }; - - Track(const PcmData &pcmData); - virtual ~Track(); - - inline State getState() const { return _state; }; - void setState(State state); - - inline State getPrevState() const { return _prevState; }; - - inline bool isPlayOver() const { return _state == State::PLAYING && _nextFrame >= _numFrames;}; - inline void setName(int name) { _name = name; }; - inline int getName() const { return _name; }; - - void setVolume(float volume); - float getVolume() const; - - void setAudioFocus(bool isFocus); - - bool setPosition(float pos); - float getPosition() const; - - virtual gain_minifloat_packed_t getVolumeLR() override ; - - inline void setLoop(bool isLoop) { _isLoop = isLoop; }; - inline bool isLoop() const { return _isLoop; }; - - std::function onStateChanged; - -private: - inline bool isVolumeDirty() const - { return _isVolumeDirty; }; - - inline void setVolumeDirty(bool isDirty) - { _isVolumeDirty = isDirty; }; - - inline bool isInitialized() const - { return _isInitialized; }; - - inline void setInitialized(bool isInitialized) - { _isInitialized = isInitialized; }; - -private: - PcmData _pcmData; - State _prevState; - State _state; - std::mutex _stateMutex; - int _name; - float _volume; - bool _isVolumeDirty; - std::mutex _volumeDirtyMutex; - bool _isLoop; - bool _isInitialized; - bool _isAudioFocus; - - friend class AudioMixerController; -}; - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/UrlAudioPlayer.cpp b/cocos/audio/android/UrlAudioPlayer.cpp deleted file mode 100644 index ef073691c537..000000000000 --- a/cocos/audio/android/UrlAudioPlayer.cpp +++ /dev/null @@ -1,424 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#define LOG_TAG "UrlAudioPlayer" - -#include "audio/android/UrlAudioPlayer.h" -#include "audio/android/ICallerThreadUtils.h" - -#include -#include // for std::find - -namespace { - -std::mutex __playerContainerMutex; -std::vector __playerContainer; -std::once_flag __onceFlag; - -} - -namespace cocos2d { namespace experimental { - -class SLUrlAudioPlayerCallbackProxy -{ -public: - static void playEventCallback(SLPlayItf caller, void *context, SLuint32 playEvent) - { - UrlAudioPlayer *thiz = (UrlAudioPlayer *) context; - // We must use a mutex for the whole block of the following function invocation. - std::lock_guard lk(__playerContainerMutex); - auto iter = std::find(__playerContainer.begin(), __playerContainer.end(), thiz); - if (iter != __playerContainer.end()) - { - thiz->playEventCallback(caller, playEvent); - } - } -}; - -UrlAudioPlayer::UrlAudioPlayer(SLEngineItf engineItf, SLObjectItf outputMixObject, ICallerThreadUtils* callerThreadUtils) - : _engineItf(engineItf), _outputMixObj(outputMixObject), - _callerThreadUtils(callerThreadUtils), _id(-1), _assetFd(nullptr), - _playObj(nullptr), _playItf(nullptr), _seekItf(nullptr), _volumeItf(nullptr), - _volume(0.0f), _duration(0.0f), _isLoop(false), _isAudioFocus(true), _state(State::INVALID), - _playEventCallback(nullptr), _isDestroyed(std::make_shared(false)) -{ - std::call_once(__onceFlag, [](){ - __playerContainer.reserve(10); - }); - - __playerContainerMutex.lock(); - __playerContainer.push_back(this); - ALOGV("Current UrlAudioPlayer instance count: %d", (int)__playerContainer.size()); - __playerContainerMutex.unlock(); - - _callerThreadId = callerThreadUtils->getCallerThreadId(); -} - -UrlAudioPlayer::~UrlAudioPlayer() -{ - ALOGV("~UrlAudioPlayer(): %p", this); - - __playerContainerMutex.lock(); - - auto iter = std::find(__playerContainer.begin(), __playerContainer.end(), this); - if (iter != __playerContainer.end()) - { - __playerContainer.erase(iter); - } - - __playerContainerMutex.unlock(); -} - -void UrlAudioPlayer::playEventCallback(SLPlayItf caller, SLuint32 playEvent) -{ - // Note that it's on sub thread, please don't invoke OpenSLES API on sub thread - if (playEvent == SL_PLAYEVENT_HEADATEND) - { - std::shared_ptr isDestroyed = _isDestroyed; - - auto func = [this, isDestroyed](){ - // If it was destroyed, just return. - if (*isDestroyed) - { - ALOGV("The UrlAudioPlayer (%p) was destroyed!", this); - return; - } - - //Note that It's in the caller's thread (Cocos Thread) - // If state is already stopped, ignore the play over event. - - if (_state == State::STOPPED) - { - return; - } - - //fix issue#8965:AudioEngine can't looping audio on Android 2.3.x - if (isLoop()) - { - play(); - } - else - { - setState(State::OVER); - if (_playEventCallback != nullptr) - { - _playEventCallback(State::OVER); - } - - ALOGV("UrlAudioPlayer (%p) played over, destroy self ...", this); - destroy(); - delete this; - } - }; - - if (_callerThreadId == std::this_thread::get_id()) - { - func(); - } - else - { - _callerThreadUtils->performFunctionInCallerThread(func); - } - } -} - -void UrlAudioPlayer::setPlayEventCallback(const PlayEventCallback &playEventCallback) -{ - _playEventCallback = playEventCallback; -} - -void UrlAudioPlayer::stop() -{ - ALOGV("UrlAudioPlayer::stop (%p, %d)", this, getId()); - SLresult r = (*_playItf)->SetPlayState(_playItf, SL_PLAYSTATE_STOPPED); - SL_RETURN_IF_FAILED(r, "UrlAudioPlayer::stop failed"); - - if (_state == State::PLAYING || _state == State::PAUSED) - { - setLoop(false); - setState(State::STOPPED); - - if (_playEventCallback != nullptr) - { - _playEventCallback(State::STOPPED); - } - - destroy(); - delete this; - } - else - { - ALOGW("UrlAudioPlayer (%p, state:%d) isn't playing or paused, could not invoke stop!", this, static_cast(_state)); - } -} - -void UrlAudioPlayer::pause() -{ - if (_state == State::PLAYING) - { - SLresult r = (*_playItf)->SetPlayState(_playItf, SL_PLAYSTATE_PAUSED); - SL_RETURN_IF_FAILED(r, "UrlAudioPlayer::pause failed"); - setState(State::PAUSED); - } - else - { - ALOGW("UrlAudioPlayer (%p, state:%d) isn't playing, could not invoke pause!", this, static_cast(_state)); - } -} - -void UrlAudioPlayer::resume() -{ - if (_state == State::PAUSED) - { - SLresult r = (*_playItf)->SetPlayState(_playItf, SL_PLAYSTATE_PLAYING); - SL_RETURN_IF_FAILED(r, "UrlAudioPlayer::resume failed"); - setState(State::PLAYING); - } - else - { - ALOGW("UrlAudioPlayer (%p, state:%d) isn't paused, could not invoke resume!", this, static_cast(_state)); - } -} - -void UrlAudioPlayer::play() -{ - if (_state == State::INITIALIZED || _state == State::PAUSED) - { - SLresult r = (*_playItf)->SetPlayState(_playItf, SL_PLAYSTATE_PLAYING); - SL_RETURN_IF_FAILED(r, "UrlAudioPlayer::play failed"); - setState(State::PLAYING); - } - else - { - ALOGW("UrlAudioPlayer (%p, state:%d) isn't paused or initialized, could not invoke play!", this, static_cast(_state)); - } -} - -void UrlAudioPlayer::setVolumeToSLPlayer(float volume) -{ - int dbVolume = 2000 * log10(volume); - if (dbVolume < SL_MILLIBEL_MIN) - { - dbVolume = SL_MILLIBEL_MIN; - } - SLresult r = (*_volumeItf)->SetVolumeLevel(_volumeItf, dbVolume); - SL_RETURN_IF_FAILED(r, "UrlAudioPlayer::setVolumeToSLPlayer %d failed", dbVolume); -} - -void UrlAudioPlayer::setVolume(float volume) -{ - _volume = volume; - if (_isAudioFocus) - { - setVolumeToSLPlayer(_volume); - } -} - -float UrlAudioPlayer::getVolume() const -{ - return _volume; -} - -void UrlAudioPlayer::setAudioFocus(bool isFocus) -{ - _isAudioFocus = isFocus; - float volume = _isAudioFocus ? _volume : 0.0f; - setVolumeToSLPlayer(volume); -} - -float UrlAudioPlayer::getDuration() const -{ - if (_duration > 0) - { - return _duration; - } - - SLmillisecond duration; - SLresult r = (*_playItf)->GetDuration(_playItf, &duration); - SL_RETURN_VAL_IF_FAILED(r, 0.0f, "UrlAudioPlayer::getDuration failed"); - - if (duration == SL_TIME_UNKNOWN) - { - return -1.0f; - } - else - { - const_cast(this)->_duration = duration / 1000.0f; - - if (_duration <= 0) - { - return -1.0f; - } - } - return _duration; -} - -float UrlAudioPlayer::getPosition() const -{ - SLmillisecond millisecond; - SLresult r = (*_playItf)->GetPosition(_playItf, &millisecond); - SL_RETURN_VAL_IF_FAILED(r, 0.0f, "UrlAudioPlayer::getPosition failed"); - return millisecond / 1000.0f; -} - -bool UrlAudioPlayer::setPosition(float pos) -{ - SLmillisecond millisecond = 1000.0f * pos; - SLresult r = (*_seekItf)->SetPosition(_seekItf, millisecond, SL_SEEKMODE_ACCURATE); - SL_RETURN_VAL_IF_FAILED(r, false, "UrlAudioPlayer::setPosition %f failed", pos); - return true; -} - -bool UrlAudioPlayer::prepare(const std::string &url, SLuint32 locatorType, std::shared_ptr assetFd, int start, - int length) -{ - _url = url; - _assetFd = assetFd; - - const char* locatorTypeStr= "UNKNOWN"; - if (locatorType == SL_DATALOCATOR_ANDROIDFD) - locatorTypeStr = "SL_DATALOCATOR_ANDROIDFD"; - else if (locatorType == SL_DATALOCATOR_URI) - locatorTypeStr = "SL_DATALOCATOR_URI"; - else - { - ALOGE("Oops, invalid locatorType: %d", (int)locatorType); - return false; - } - - ALOGV("UrlAudioPlayer::prepare: %s, %s, %d, %d, %d", _url.c_str(), locatorTypeStr, _assetFd->getFd(), start, - length); - SLDataSource audioSrc; - - SLDataFormat_MIME formatMime = {SL_DATAFORMAT_MIME, nullptr, SL_CONTAINERTYPE_UNSPECIFIED}; - audioSrc.pFormat = &formatMime; - - //Note: locFd & locUri should be outside of the following if/else block - // Although locFd & locUri are only used inside if/else block, its lifecycle - // will be destroyed right after '}' block. And since we pass a pointer to - // 'audioSrc.pLocator=&locFd/&locUri', pLocator will point to an invalid address - // while invoking Engine::createAudioPlayer interface. So be care of change the position - // of these two variables. - SLDataLocator_AndroidFD locFd; - SLDataLocator_URI locUri; - - if (locatorType == SL_DATALOCATOR_ANDROIDFD) - { - locFd = {locatorType, _assetFd->getFd(), start, length}; - audioSrc.pLocator = &locFd; - } - else if (locatorType == SL_DATALOCATOR_URI) - { - locUri = {locatorType, (SLchar *) _url.c_str()}; - audioSrc.pLocator = &locUri; - ALOGV("locUri: locatorType: %d", (int)locUri.locatorType); - } - - // configure audio sink - SLDataLocator_OutputMix locOutmix = {SL_DATALOCATOR_OUTPUTMIX, _outputMixObj}; - SLDataSink audioSnk = {&locOutmix, nullptr}; - - // create audio player - const SLInterfaceID ids[3] = {SL_IID_SEEK, SL_IID_PREFETCHSTATUS, SL_IID_VOLUME}; - const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; - - SLresult result = (*_engineItf)->CreateAudioPlayer(_engineItf, &_playObj, &audioSrc, &audioSnk, - 3, ids, req); - SL_RETURN_VAL_IF_FAILED(result, false, "CreateAudioPlayer failed"); - - // realize the player - result = (*_playObj)->Realize(_playObj, SL_BOOLEAN_FALSE); - SL_RETURN_VAL_IF_FAILED(result, false, "Realize failed"); - - // get the play interface - result = (*_playObj)->GetInterface(_playObj, SL_IID_PLAY, &_playItf); - SL_RETURN_VAL_IF_FAILED(result, false, "GetInterface SL_IID_PLAY failed"); - - // get the seek interface - result = (*_playObj)->GetInterface(_playObj, SL_IID_SEEK, &_seekItf); - SL_RETURN_VAL_IF_FAILED(result, false, "GetInterface SL_IID_SEEK failed"); - - // get the volume interface - result = (*_playObj)->GetInterface(_playObj, SL_IID_VOLUME, &_volumeItf); - SL_RETURN_VAL_IF_FAILED(result, false, "GetInterface SL_IID_VOLUME failed"); - - result = (*_playItf)->RegisterCallback(_playItf, - SLUrlAudioPlayerCallbackProxy::playEventCallback, this); - SL_RETURN_VAL_IF_FAILED(result, false, "RegisterCallback failed"); - - result = (*_playItf)->SetCallbackEventsMask(_playItf, SL_PLAYEVENT_HEADATEND); - SL_RETURN_VAL_IF_FAILED(result, false, "SetCallbackEventsMask SL_PLAYEVENT_HEADATEND failed"); - - setState(State::INITIALIZED); - - setVolume(1.0f); - - return true; -} - -void UrlAudioPlayer::rewind() -{ -// Not supported currently. since cocos audio engine will new -> prepare -> play again. -} - -void UrlAudioPlayer::setLoop(bool isLoop) -{ - _isLoop = isLoop; - - SLboolean loopEnable = _isLoop ? SL_BOOLEAN_TRUE : SL_BOOLEAN_FALSE; - SLresult r = (*_seekItf)->SetLoop(_seekItf, loopEnable, 0, SL_TIME_UNKNOWN); - SL_RETURN_IF_FAILED(r, "UrlAudioPlayer::setLoop %d failed", _isLoop ? 1 : 0); -} - -bool UrlAudioPlayer::isLoop() const -{ - return _isLoop; -} - -void UrlAudioPlayer::stopAll() -{ - // To avoid break the for loop, we need to copy a new map - __playerContainerMutex.lock(); - auto temp = __playerContainer; - __playerContainerMutex.unlock(); - - for (auto&& player : temp) - { - player->stop(); - } -} - -void UrlAudioPlayer::destroy() -{ - if (!*_isDestroyed) - { - *_isDestroyed = true; - ALOGV("UrlAudioPlayer::destroy() %p", this); - SL_DESTROY_OBJ(_playObj); - ALOGV("UrlAudioPlayer::destroy end"); - } -} - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/UrlAudioPlayer.h b/cocos/audio/android/UrlAudioPlayer.h deleted file mode 100644 index 16885effa830..000000000000 --- a/cocos/audio/android/UrlAudioPlayer.h +++ /dev/null @@ -1,135 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#pragma once - -#include "audio/android/IAudioPlayer.h" -#include "audio/android/OpenSLHelper.h" -#include "audio/android/AssetFd.h" -#include -#include -#include -#include - -namespace cocos2d { namespace experimental { - -class ICallerThreadUtils; -class AssetFd; - -class UrlAudioPlayer : public IAudioPlayer -{ -public: - - // Override Functions Begin - virtual int getId() const override - { return _id; }; - - virtual void setId(int id) override - { _id = id; }; - - virtual std::string getUrl() const override - { return _url; }; - - virtual State getState() const override - { return _state; }; - - virtual void play() override; - - virtual void pause() override; - - virtual void resume() override; - - virtual void stop() override; - - virtual void rewind() override; - - virtual void setVolume(float volume) override; - - virtual float getVolume() const override; - - virtual void setAudioFocus(bool isFocus) override; - - virtual void setLoop(bool isLoop) override; - - virtual bool isLoop() const override; - - virtual float getDuration() const override; - - virtual float getPosition() const override; - - virtual bool setPosition(float pos) override; - - virtual void setPlayEventCallback(const PlayEventCallback &playEventCallback) override; - - // Override Functions EndOv - -private: - UrlAudioPlayer(SLEngineItf engineItf, SLObjectItf outputMixObject, ICallerThreadUtils* callerThreadUtils); - virtual ~UrlAudioPlayer(); - - bool prepare(const std::string &url, SLuint32 locatorType, std::shared_ptr assetFd, int start, int length); - - static void stopAll(); - - void destroy(); - - inline void setState(State state) - { _state = state; }; - - void playEventCallback(SLPlayItf caller, SLuint32 playEvent); - - void setVolumeToSLPlayer(float volume); - -private: - SLEngineItf _engineItf; - SLObjectItf _outputMixObj; - ICallerThreadUtils* _callerThreadUtils; - - int _id; - std::string _url; - - std::shared_ptr _assetFd; - - SLObjectItf _playObj; - SLPlayItf _playItf; - SLSeekItf _seekItf; - SLVolumeItf _volumeItf; - - float _volume; - float _duration; - bool _isLoop; - bool _isAudioFocus; - State _state; - - PlayEventCallback _playEventCallback; - - std::thread::id _callerThreadId; - std::shared_ptr _isDestroyed; - - friend class SLUrlAudioPlayerCallbackProxy; - friend class AudioPlayerProvider; -}; - -}} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/audio.h b/cocos/audio/android/audio.h deleted file mode 100644 index a7022141a5d9..000000000000 --- a/cocos/audio/android/audio.h +++ /dev/null @@ -1,504 +0,0 @@ -/**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -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. -****************************************************************************/ - -#pragma once - -// ---------------------------------------------------------------------------- - -#include -#include -#include "audio/android/cutils/bitops.h" - -#define PROPERTY_VALUE_MAX 256 -#define CONSTEXPR constexpr - - -#ifdef __cplusplus -# define CC_LIKELY( exp ) (__builtin_expect( !!(exp), true )) -# define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), false )) -#else -# define CC_LIKELY( exp ) (__builtin_expect( !!(exp), 1 )) -# define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 )) -#endif - - -/* special audio session values - * (XXX: should this be living in the audio effects land?) - */ -typedef enum { - /* session for effects attached to a particular output stream - * (value must be less than 0) - */ - AUDIO_SESSION_OUTPUT_STAGE = -1, - - /* session for effects applied to output mix. These effects can - * be moved by audio policy manager to another output stream - * (value must be 0) - */ - AUDIO_SESSION_OUTPUT_MIX = 0, - - /* application does not specify an explicit session ID to be used, - * and requests a new session ID to be allocated - * TODO use unique values for AUDIO_SESSION_OUTPUT_MIX and AUDIO_SESSION_ALLOCATE, - * after all uses have been updated from 0 to the appropriate symbol, and have been tested. - */ - AUDIO_SESSION_ALLOCATE = 0, -} audio_session_t; - -/* Audio sub formats (see enum audio_format). */ - -/* PCM sub formats */ -typedef enum { - /* All of these are in native byte order */ - AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, /* DO NOT CHANGE - PCM signed 16 bits */ - AUDIO_FORMAT_PCM_SUB_8_BIT = 0x2, /* DO NOT CHANGE - PCM unsigned 8 bits */ - AUDIO_FORMAT_PCM_SUB_32_BIT = 0x3, /* PCM signed .31 fixed point */ - AUDIO_FORMAT_PCM_SUB_8_24_BIT = 0x4, /* PCM signed 8.23 fixed point */ - AUDIO_FORMAT_PCM_SUB_FLOAT = 0x5, /* PCM single-precision floating point */ - AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED = 0x6, /* PCM signed .23 fixed point packed in 3 bytes */ -} audio_format_pcm_sub_fmt_t; - -/* The audio_format_*_sub_fmt_t declarations are not currently used */ - -/* MP3 sub format field definition : can use 11 LSBs in the same way as MP3 - * frame header to specify bit rate, stereo mode, version... - */ -typedef enum { - AUDIO_FORMAT_MP3_SUB_NONE = 0x0, -} audio_format_mp3_sub_fmt_t; - -/* AMR NB/WB sub format field definition: specify frame block interleaving, - * bandwidth efficient or octet aligned, encoding mode for recording... - */ -typedef enum { - AUDIO_FORMAT_AMR_SUB_NONE = 0x0, -} audio_format_amr_sub_fmt_t; - -/* AAC sub format field definition: specify profile or bitrate for recording... */ -typedef enum { - AUDIO_FORMAT_AAC_SUB_MAIN = 0x1, - AUDIO_FORMAT_AAC_SUB_LC = 0x2, - AUDIO_FORMAT_AAC_SUB_SSR = 0x4, - AUDIO_FORMAT_AAC_SUB_LTP = 0x8, - AUDIO_FORMAT_AAC_SUB_HE_V1 = 0x10, - AUDIO_FORMAT_AAC_SUB_SCALABLE = 0x20, - AUDIO_FORMAT_AAC_SUB_ERLC = 0x40, - AUDIO_FORMAT_AAC_SUB_LD = 0x80, - AUDIO_FORMAT_AAC_SUB_HE_V2 = 0x100, - AUDIO_FORMAT_AAC_SUB_ELD = 0x200, -} audio_format_aac_sub_fmt_t; - -/* VORBIS sub format field definition: specify quality for recording... */ -typedef enum { - AUDIO_FORMAT_VORBIS_SUB_NONE = 0x0, -} audio_format_vorbis_sub_fmt_t; - -/* Audio format consists of a main format field (upper 8 bits) and a sub format - * field (lower 24 bits). - * - * The main format indicates the main codec type. The sub format field - * indicates options and parameters for each format. The sub format is mainly - * used for record to indicate for instance the requested bitrate or profile. - * It can also be used for certain formats to give informations not present in - * the encoded audio stream (e.g. octet alignment for AMR). - */ -typedef enum { - AUDIO_FORMAT_INVALID = 0xFFFFFFFFUL, - AUDIO_FORMAT_DEFAULT = 0, - AUDIO_FORMAT_PCM = 0x00000000UL, /* DO NOT CHANGE */ - AUDIO_FORMAT_MP3 = 0x01000000UL, - AUDIO_FORMAT_AMR_NB = 0x02000000UL, - AUDIO_FORMAT_AMR_WB = 0x03000000UL, - AUDIO_FORMAT_AAC = 0x04000000UL, - AUDIO_FORMAT_HE_AAC_V1 = 0x05000000UL, /* Deprecated, Use AUDIO_FORMAT_AAC_HE_V1*/ - AUDIO_FORMAT_HE_AAC_V2 = 0x06000000UL, /* Deprecated, Use AUDIO_FORMAT_AAC_HE_V2*/ - AUDIO_FORMAT_VORBIS = 0x07000000UL, - AUDIO_FORMAT_OPUS = 0x08000000UL, - AUDIO_FORMAT_AC3 = 0x09000000UL, - AUDIO_FORMAT_E_AC3 = 0x0A000000UL, - AUDIO_FORMAT_DTS = 0x0B000000UL, - AUDIO_FORMAT_DTS_HD = 0x0C000000UL, - AUDIO_FORMAT_MAIN_MASK = 0xFF000000UL, - AUDIO_FORMAT_SUB_MASK = 0x00FFFFFFUL, - - /* Aliases */ - /* note != AudioFormat.ENCODING_PCM_16BIT */ - AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_16_BIT), - /* note != AudioFormat.ENCODING_PCM_8BIT */ - AUDIO_FORMAT_PCM_8_BIT = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_8_BIT), - AUDIO_FORMAT_PCM_32_BIT = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_32_BIT), - AUDIO_FORMAT_PCM_8_24_BIT = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_8_24_BIT), - AUDIO_FORMAT_PCM_FLOAT = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_FLOAT), - AUDIO_FORMAT_PCM_24_BIT_PACKED = (AUDIO_FORMAT_PCM | - AUDIO_FORMAT_PCM_SUB_24_BIT_PACKED), - AUDIO_FORMAT_AAC_MAIN = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_MAIN), - AUDIO_FORMAT_AAC_LC = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_LC), - AUDIO_FORMAT_AAC_SSR = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_SSR), - AUDIO_FORMAT_AAC_LTP = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_LTP), - AUDIO_FORMAT_AAC_HE_V1 = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_HE_V1), - AUDIO_FORMAT_AAC_SCALABLE = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_SCALABLE), - AUDIO_FORMAT_AAC_ERLC = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_ERLC), - AUDIO_FORMAT_AAC_LD = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_LD), - AUDIO_FORMAT_AAC_HE_V2 = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_HE_V2), - AUDIO_FORMAT_AAC_ELD = (AUDIO_FORMAT_AAC | - AUDIO_FORMAT_AAC_SUB_ELD), -} audio_format_t; - -/* For the channel mask for position assignment representation */ -enum { -/* These can be a complete audio_channel_mask_t. */ - AUDIO_CHANNEL_NONE = 0x0, - AUDIO_CHANNEL_INVALID = 0xC0000000, -/* These can be the bits portion of an audio_channel_mask_t - * with representation AUDIO_CHANNEL_REPRESENTATION_POSITION. - * Using these bits as a complete audio_channel_mask_t is deprecated. - */ - /* output channels */ - AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x1, - AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x2, - AUDIO_CHANNEL_OUT_FRONT_CENTER = 0x4, - AUDIO_CHANNEL_OUT_LOW_FREQUENCY = 0x8, - AUDIO_CHANNEL_OUT_BACK_LEFT = 0x10, - AUDIO_CHANNEL_OUT_BACK_RIGHT = 0x20, - AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x40, - AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x80, - AUDIO_CHANNEL_OUT_BACK_CENTER = 0x100, - AUDIO_CHANNEL_OUT_SIDE_LEFT = 0x200, - AUDIO_CHANNEL_OUT_SIDE_RIGHT = 0x400, - AUDIO_CHANNEL_OUT_TOP_CENTER = 0x800, - AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT = 0x1000, - AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER = 0x2000, - AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT = 0x4000, - AUDIO_CHANNEL_OUT_TOP_BACK_LEFT = 0x8000, - AUDIO_CHANNEL_OUT_TOP_BACK_CENTER = 0x10000, - AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT = 0x20000, -/* TODO: should these be considered complete channel masks, or only bits? */ - AUDIO_CHANNEL_OUT_MONO = AUDIO_CHANNEL_OUT_FRONT_LEFT, - AUDIO_CHANNEL_OUT_STEREO = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT), - AUDIO_CHANNEL_OUT_QUAD = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_BACK_LEFT | - AUDIO_CHANNEL_OUT_BACK_RIGHT), - AUDIO_CHANNEL_OUT_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD, - /* like AUDIO_CHANNEL_OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_* */ - AUDIO_CHANNEL_OUT_QUAD_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_SIDE_LEFT | - AUDIO_CHANNEL_OUT_SIDE_RIGHT), - AUDIO_CHANNEL_OUT_5POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_CENTER | - AUDIO_CHANNEL_OUT_LOW_FREQUENCY | - AUDIO_CHANNEL_OUT_BACK_LEFT | - AUDIO_CHANNEL_OUT_BACK_RIGHT), - AUDIO_CHANNEL_OUT_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1, - /* like AUDIO_CHANNEL_OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_* */ - AUDIO_CHANNEL_OUT_5POINT1_SIDE = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_CENTER | - AUDIO_CHANNEL_OUT_LOW_FREQUENCY | - AUDIO_CHANNEL_OUT_SIDE_LEFT | - AUDIO_CHANNEL_OUT_SIDE_RIGHT), - // matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND definition for 7.1 - AUDIO_CHANNEL_OUT_7POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_CENTER | - AUDIO_CHANNEL_OUT_LOW_FREQUENCY | - AUDIO_CHANNEL_OUT_BACK_LEFT | - AUDIO_CHANNEL_OUT_BACK_RIGHT | - AUDIO_CHANNEL_OUT_SIDE_LEFT | - AUDIO_CHANNEL_OUT_SIDE_RIGHT), - AUDIO_CHANNEL_OUT_ALL = (AUDIO_CHANNEL_OUT_FRONT_LEFT | - AUDIO_CHANNEL_OUT_FRONT_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_CENTER | - AUDIO_CHANNEL_OUT_LOW_FREQUENCY | - AUDIO_CHANNEL_OUT_BACK_LEFT | - AUDIO_CHANNEL_OUT_BACK_RIGHT | - AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER | - AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER | - AUDIO_CHANNEL_OUT_BACK_CENTER| - AUDIO_CHANNEL_OUT_SIDE_LEFT| - AUDIO_CHANNEL_OUT_SIDE_RIGHT| - AUDIO_CHANNEL_OUT_TOP_CENTER| - AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT| - AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER| - AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT| - AUDIO_CHANNEL_OUT_TOP_BACK_LEFT| - AUDIO_CHANNEL_OUT_TOP_BACK_CENTER| - AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), -/* These are bits only, not complete values */ - /* input channels */ - AUDIO_CHANNEL_IN_LEFT = 0x4, - AUDIO_CHANNEL_IN_RIGHT = 0x8, - AUDIO_CHANNEL_IN_FRONT = 0x10, - AUDIO_CHANNEL_IN_BACK = 0x20, - AUDIO_CHANNEL_IN_LEFT_PROCESSED = 0x40, - AUDIO_CHANNEL_IN_RIGHT_PROCESSED = 0x80, - AUDIO_CHANNEL_IN_FRONT_PROCESSED = 0x100, - AUDIO_CHANNEL_IN_BACK_PROCESSED = 0x200, - AUDIO_CHANNEL_IN_PRESSURE = 0x400, - AUDIO_CHANNEL_IN_X_AXIS = 0x800, - AUDIO_CHANNEL_IN_Y_AXIS = 0x1000, - AUDIO_CHANNEL_IN_Z_AXIS = 0x2000, - AUDIO_CHANNEL_IN_VOICE_UPLINK = 0x4000, - AUDIO_CHANNEL_IN_VOICE_DNLINK = 0x8000, -/* TODO: should these be considered complete channel masks, or only bits, or deprecated? */ - AUDIO_CHANNEL_IN_MONO = AUDIO_CHANNEL_IN_FRONT, - AUDIO_CHANNEL_IN_STEREO = (AUDIO_CHANNEL_IN_LEFT | AUDIO_CHANNEL_IN_RIGHT), - AUDIO_CHANNEL_IN_FRONT_BACK = (AUDIO_CHANNEL_IN_FRONT | AUDIO_CHANNEL_IN_BACK), - AUDIO_CHANNEL_IN_ALL = (AUDIO_CHANNEL_IN_LEFT | - AUDIO_CHANNEL_IN_RIGHT | - AUDIO_CHANNEL_IN_FRONT | - AUDIO_CHANNEL_IN_BACK| - AUDIO_CHANNEL_IN_LEFT_PROCESSED | - AUDIO_CHANNEL_IN_RIGHT_PROCESSED | - AUDIO_CHANNEL_IN_FRONT_PROCESSED | - AUDIO_CHANNEL_IN_BACK_PROCESSED| - AUDIO_CHANNEL_IN_PRESSURE | - AUDIO_CHANNEL_IN_X_AXIS | - AUDIO_CHANNEL_IN_Y_AXIS | - AUDIO_CHANNEL_IN_Z_AXIS | - AUDIO_CHANNEL_IN_VOICE_UPLINK | - AUDIO_CHANNEL_IN_VOICE_DNLINK), -}; -/* A channel mask per se only defines the presence or absence of a channel, not the order. - * But see AUDIO_INTERLEAVE_* below for the platform convention of order. - * - * audio_channel_mask_t is an opaque type and its internal layout should not - * be assumed as it may change in the future. - * Instead, always use the functions declared in this header to examine. - * - * These are the current representations: - * - * AUDIO_CHANNEL_REPRESENTATION_POSITION - * is a channel mask representation for position assignment. - * Each low-order bit corresponds to the spatial position of a transducer (output), - * or interpretation of channel (input). - * The user of a channel mask needs to know the context of whether it is for output or input. - * The constants AUDIO_CHANNEL_OUT_* or AUDIO_CHANNEL_IN_* apply to the bits portion. - * It is not permitted for no bits to be set. - * - * AUDIO_CHANNEL_REPRESENTATION_INDEX - * is a channel mask representation for index assignment. - * Each low-order bit corresponds to a selected channel. - * There is no platform interpretation of the various bits. - * There is no concept of output or input. - * It is not permitted for no bits to be set. - * - * All other representations are reserved for future use. - * - * Warning: current representation distinguishes between input and output, but this will not the be - * case in future revisions of the platform. Wherever there is an ambiguity between input and output - * that is currently resolved by checking the channel mask, the implementer should look for ways to - * fix it with additional information outside of the mask. - */ -typedef uint32_t audio_channel_mask_t; - -/* Maximum number of channels for all representations */ -#define AUDIO_CHANNEL_COUNT_MAX 30 - -/* log(2) of maximum number of representations, not part of public API */ -#define AUDIO_CHANNEL_REPRESENTATION_LOG2 2 - -/* Representations */ -typedef enum { - AUDIO_CHANNEL_REPRESENTATION_POSITION = 0, // must be zero for compatibility - // 1 is reserved for future use - AUDIO_CHANNEL_REPRESENTATION_INDEX = 2, - // 3 is reserved for future use -} audio_channel_representation_t; - -/* The return value is undefined if the channel mask is invalid. */ -static inline uint32_t audio_channel_mask_get_bits(audio_channel_mask_t channel) -{ - return channel & ((1 << AUDIO_CHANNEL_COUNT_MAX) - 1); -} - -/* The return value is undefined if the channel mask is invalid. */ -static inline audio_channel_representation_t audio_channel_mask_get_representation( - audio_channel_mask_t channel) -{ - // The right shift should be sufficient, but also "and" for safety in case mask is not 32 bits - return (audio_channel_representation_t) - ((channel >> AUDIO_CHANNEL_COUNT_MAX) & ((1 << AUDIO_CHANNEL_REPRESENTATION_LOG2) - 1)); -} - -/* Returns the number of channels from an output channel mask, - * used in the context of audio output or playback. - * If a channel bit is set which could _not_ correspond to an output channel, - * it is excluded from the count. - * Returns zero if the representation is invalid. - */ -static inline uint32_t audio_channel_count_from_out_mask(audio_channel_mask_t channel) -{ - uint32_t bits = audio_channel_mask_get_bits(channel); - switch (audio_channel_mask_get_representation(channel)) { - case AUDIO_CHANNEL_REPRESENTATION_POSITION: - // TODO: We can now merge with from_in_mask and remove anding - bits &= AUDIO_CHANNEL_OUT_ALL; - // fall through - case AUDIO_CHANNEL_REPRESENTATION_INDEX: - return popcount(bits); - default: - return 0; - } -} - -static inline bool audio_is_valid_format(audio_format_t format) -{ - switch (format & AUDIO_FORMAT_MAIN_MASK) { - case AUDIO_FORMAT_PCM: - switch (format) { - case AUDIO_FORMAT_PCM_16_BIT: - case AUDIO_FORMAT_PCM_8_BIT: - case AUDIO_FORMAT_PCM_32_BIT: - case AUDIO_FORMAT_PCM_8_24_BIT: - case AUDIO_FORMAT_PCM_FLOAT: - case AUDIO_FORMAT_PCM_24_BIT_PACKED: - return true; - default: - return false; - } - /* not reached */ - case AUDIO_FORMAT_MP3: - case AUDIO_FORMAT_AMR_NB: - case AUDIO_FORMAT_AMR_WB: - case AUDIO_FORMAT_AAC: - case AUDIO_FORMAT_HE_AAC_V1: - case AUDIO_FORMAT_HE_AAC_V2: - case AUDIO_FORMAT_VORBIS: - case AUDIO_FORMAT_OPUS: - case AUDIO_FORMAT_AC3: - case AUDIO_FORMAT_E_AC3: - case AUDIO_FORMAT_DTS: - case AUDIO_FORMAT_DTS_HD: - return true; - default: - return false; - } -} - -static inline bool audio_is_linear_pcm(audio_format_t format) -{ - return ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM); -} - -static inline size_t audio_bytes_per_sample(audio_format_t format) -{ - size_t size = 0; - - switch (format) { - case AUDIO_FORMAT_PCM_32_BIT: - case AUDIO_FORMAT_PCM_8_24_BIT: - size = sizeof(int32_t); - break; - case AUDIO_FORMAT_PCM_24_BIT_PACKED: - size = sizeof(uint8_t) * 3; - break; - case AUDIO_FORMAT_PCM_16_BIT: - size = sizeof(int16_t); - break; - case AUDIO_FORMAT_PCM_8_BIT: - size = sizeof(uint8_t); - break; - case AUDIO_FORMAT_PCM_FLOAT: - size = sizeof(float); - break; - default: - break; - } - return size; -} - -/* Not part of public API */ -static inline audio_channel_mask_t audio_channel_mask_from_representation_and_bits( - audio_channel_representation_t representation, uint32_t bits) -{ - return (audio_channel_mask_t) ((representation << AUDIO_CHANNEL_COUNT_MAX) | bits); -} - -/* Derive an output channel mask for position assignment from a channel count. - * This is to be used when the content channel mask is unknown. The 1, 2, 4, 5, 6, 7 and 8 channel - * cases are mapped to the standard game/home-theater layouts, but note that 4 is mapped to quad, - * and not stereo + FC + mono surround. A channel count of 3 is arbitrarily mapped to stereo + FC - * for continuity with stereo. - * Returns the matching channel mask, - * or AUDIO_CHANNEL_NONE if the channel count is zero, - * or AUDIO_CHANNEL_INVALID if the channel count exceeds that of the - * configurations for which a default output channel mask is defined. - */ -static inline audio_channel_mask_t audio_channel_out_mask_from_count(uint32_t channel_count) -{ - uint32_t bits; - switch (channel_count) { - case 0: - return AUDIO_CHANNEL_NONE; - case 1: - bits = AUDIO_CHANNEL_OUT_MONO; - break; - case 2: - bits = AUDIO_CHANNEL_OUT_STEREO; - break; - case 3: - bits = AUDIO_CHANNEL_OUT_STEREO | AUDIO_CHANNEL_OUT_FRONT_CENTER; - break; - case 4: // 4.0 - bits = AUDIO_CHANNEL_OUT_QUAD; - break; - case 5: // 5.0 - bits = AUDIO_CHANNEL_OUT_QUAD | AUDIO_CHANNEL_OUT_FRONT_CENTER; - break; - case 6: // 5.1 - bits = AUDIO_CHANNEL_OUT_5POINT1; - break; - case 7: // 6.1 - bits = AUDIO_CHANNEL_OUT_5POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER; - break; - case 8: - bits = AUDIO_CHANNEL_OUT_7POINT1; - break; - // FIXME FCC_8 - default: - return AUDIO_CHANNEL_INVALID; - } - return audio_channel_mask_from_representation_and_bits( - AUDIO_CHANNEL_REPRESENTATION_POSITION, bits); -} - diff --git a/cocos/audio/android/audio_utils/format.c b/cocos/audio/android/audio_utils/format.c deleted file mode 100644 index 3c0b2310852e..000000000000 --- a/cocos/audio/android/audio_utils/format.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* #define LOG_NDEBUG 0 */ -#define LOG_TAG "audio_utils_format" - -#include "audio/android/cutils/log.h" -#include "audio/android/audio_utils/include/audio_utils/primitives.h" -#include "audio/android/audio_utils/include/audio_utils/format.h" -#include "audio/android/audio.h" - -void memcpy_by_audio_format(void *dst, audio_format_t dst_format, - const void *src, audio_format_t src_format, size_t count) -{ - /* default cases for error falls through to fatal log below. */ - if (dst_format == src_format) { - switch (dst_format) { - case AUDIO_FORMAT_PCM_16_BIT: - case AUDIO_FORMAT_PCM_FLOAT: - case AUDIO_FORMAT_PCM_8_BIT: - case AUDIO_FORMAT_PCM_24_BIT_PACKED: - case AUDIO_FORMAT_PCM_32_BIT: - case AUDIO_FORMAT_PCM_8_24_BIT: - memcpy(dst, src, count * audio_bytes_per_sample(dst_format)); - return; - default: - break; - } - } - switch (dst_format) { - case AUDIO_FORMAT_PCM_16_BIT: - switch (src_format) { - case AUDIO_FORMAT_PCM_FLOAT: - memcpy_to_i16_from_float((int16_t*)dst, (float*)src, count); - return; - case AUDIO_FORMAT_PCM_8_BIT: - memcpy_to_i16_from_u8((int16_t*)dst, (uint8_t*)src, count); - return; - case AUDIO_FORMAT_PCM_24_BIT_PACKED: - memcpy_to_i16_from_p24((int16_t*)dst, (uint8_t*)src, count); - return; - case AUDIO_FORMAT_PCM_32_BIT: - memcpy_to_i16_from_i32((int16_t*)dst, (int32_t*)src, count); - return; - case AUDIO_FORMAT_PCM_8_24_BIT: - memcpy_to_i16_from_q8_23((int16_t*)dst, (int32_t*)src, count); - return; - default: - break; - } - break; - case AUDIO_FORMAT_PCM_FLOAT: - switch (src_format) { - case AUDIO_FORMAT_PCM_16_BIT: - memcpy_to_float_from_i16((float*)dst, (int16_t*)src, count); - return; - case AUDIO_FORMAT_PCM_8_BIT: - memcpy_to_float_from_u8((float*)dst, (uint8_t*)src, count); - return; - case AUDIO_FORMAT_PCM_24_BIT_PACKED: - memcpy_to_float_from_p24((float*)dst, (uint8_t*)src, count); - return; - case AUDIO_FORMAT_PCM_32_BIT: - memcpy_to_float_from_i32((float*)dst, (int32_t*)src, count); - return; - case AUDIO_FORMAT_PCM_8_24_BIT: - memcpy_to_float_from_q8_23((float*)dst, (int32_t*)src, count); - return; - default: - break; - } - break; - case AUDIO_FORMAT_PCM_8_BIT: - switch (src_format) { - case AUDIO_FORMAT_PCM_16_BIT: - memcpy_to_u8_from_i16((uint8_t*)dst, (int16_t*)src, count); - return; - case AUDIO_FORMAT_PCM_FLOAT: - memcpy_to_u8_from_float((uint8_t*)dst, (float*)src, count); - return; - default: - break; - } - break; - case AUDIO_FORMAT_PCM_24_BIT_PACKED: - switch (src_format) { - case AUDIO_FORMAT_PCM_16_BIT: - memcpy_to_p24_from_i16((uint8_t*)dst, (int16_t*)src, count); - return; - case AUDIO_FORMAT_PCM_FLOAT: - memcpy_to_p24_from_float((uint8_t*)dst, (float*)src, count); - return; - default: - break; - } - break; - case AUDIO_FORMAT_PCM_32_BIT: - switch (src_format) { - case AUDIO_FORMAT_PCM_16_BIT: - memcpy_to_i32_from_i16((int32_t*)dst, (int16_t*)src, count); - return; - case AUDIO_FORMAT_PCM_FLOAT: - memcpy_to_i32_from_float((int32_t*)dst, (float*)src, count); - return; - default: - break; - } - break; - case AUDIO_FORMAT_PCM_8_24_BIT: - switch (src_format) { - case AUDIO_FORMAT_PCM_16_BIT: - memcpy_to_q8_23_from_i16((int32_t*)dst, (int16_t*)src, count); - return; - case AUDIO_FORMAT_PCM_FLOAT: - memcpy_to_q8_23_from_float_with_clamp((int32_t*)dst, (float*)src, count); - return; - case AUDIO_FORMAT_PCM_24_BIT_PACKED: { - memcpy_to_q8_23_from_p24((int32_t *)dst, (uint8_t *)src, count); - return; - } - default: - break; - } - break; - default: - break; - } - LOG_ALWAYS_FATAL("invalid src format %#x for dst format %#x", - src_format, dst_format); -} - -size_t memcpy_by_index_array_initialization_from_channel_mask(int8_t *idxary, size_t arysize, - audio_channel_mask_t dst_channel_mask, audio_channel_mask_t src_channel_mask) -{ - const audio_channel_representation_t src_representation = - audio_channel_mask_get_representation(src_channel_mask); - const audio_channel_representation_t dst_representation = - audio_channel_mask_get_representation(dst_channel_mask); - const uint32_t src_bits = audio_channel_mask_get_bits(src_channel_mask); - const uint32_t dst_bits = audio_channel_mask_get_bits(dst_channel_mask); - - switch (src_representation) { - case AUDIO_CHANNEL_REPRESENTATION_POSITION: - switch (dst_representation) { - case AUDIO_CHANNEL_REPRESENTATION_POSITION: - return memcpy_by_index_array_initialization(idxary, arysize, - dst_bits, src_bits); - case AUDIO_CHANNEL_REPRESENTATION_INDEX: - return memcpy_by_index_array_initialization_dst_index(idxary, arysize, - dst_bits, src_bits); - default: - return 0; - } - break; - case AUDIO_CHANNEL_REPRESENTATION_INDEX: - switch (dst_representation) { - case AUDIO_CHANNEL_REPRESENTATION_POSITION: - return memcpy_by_index_array_initialization_src_index(idxary, arysize, - dst_bits, src_bits); - case AUDIO_CHANNEL_REPRESENTATION_INDEX: - return memcpy_by_index_array_initialization(idxary, arysize, - dst_bits, src_bits); - default: - return 0; - } - break; - default: - return 0; - } -} diff --git a/cocos/audio/android/audio_utils/include/audio_utils/format.h b/cocos/audio/android/audio_utils/include/audio_utils/format.h deleted file mode 100644 index b39f4ae85704..000000000000 --- a/cocos/audio/android/audio_utils/include/audio_utils/format.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef COCOS_AUDIO_FORMAT_H -#define COCOS_AUDIO_FORMAT_H - -#include -#include -#include "audio/android/audio.h" - -__BEGIN_DECLS - -/* Copy buffers with conversion between buffer sample formats. - * - * dst Destination buffer - * dst_format Destination buffer format - * src Source buffer - * src_format Source buffer format - * count Number of samples to copy - * - * Allowed format conversions are given by either case 1 or 2 below: - * - * 1) One of src_format or dst_format is AUDIO_FORMAT_PCM_16_BIT or - * AUDIO_FORMAT_PCM_FLOAT, and the other format type is one of: - * - * AUDIO_FORMAT_PCM_16_BIT - * AUDIO_FORMAT_PCM_FLOAT - * AUDIO_FORMAT_PCM_8_BIT - * AUDIO_FORMAT_PCM_24_BIT_PACKED - * AUDIO_FORMAT_PCM_32_BIT - * AUDIO_FORMAT_PCM_8_24_BIT - * - * 2) Both dst_format and src_format are identical and of the list given - * in (1). This is a straight copy. - * - * The destination and source buffers must be completely separate if the destination - * format size is larger than the source format size. These routines call functions - * in primitives.h, so descriptions of detailed behavior can be reviewed there. - * - * Logs a fatal error if dst or src format is not allowed by the conversion rules above. - */ -void memcpy_by_audio_format(void *dst, audio_format_t dst_format, - const void *src, audio_format_t src_format, size_t count); - - -/* This function creates an index array for converting audio data with different - * channel position and index masks, used by memcpy_by_index_array(). - * Returns the number of array elements required. - * This may be greater than idxcount, so the return value should be checked - * if idxary size is less than 32. Returns zero if the input masks are unrecognized. - * - * Note that idxary is a caller allocated array - * of at least as many channels as present in the dst_mask. - * - * Parameters: - * idxary Updated array of indices of channels in the src frame for the dst frame - * idxcount Number of caller allocated elements in idxary - * dst_mask Bit mask corresponding to destination channels present - * src_mask Bit mask corresponding to source channels present - */ -size_t memcpy_by_index_array_initialization_from_channel_mask(int8_t *idxary, size_t arysize, - audio_channel_mask_t dst_channel_mask, audio_channel_mask_t src_channel_mask); - -__END_DECLS - -#endif // COCOS_AUDIO_FORMAT_H diff --git a/cocos/audio/android/audio_utils/include/audio_utils/minifloat.h b/cocos/audio/android/audio_utils/include/audio_utils/minifloat.h deleted file mode 100644 index 26be8eb0b1ac..000000000000 --- a/cocos/audio/android/audio_utils/include/audio_utils/minifloat.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef COCOS_AUDIO_MINIFLOAT_H -#define COCOS_AUDIO_MINIFLOAT_H - -#include -#include - -__BEGIN_DECLS - -/* A single gain expressed as minifloat */ -typedef uint16_t gain_minifloat_t; - -/* A pair of gain_minifloat_t packed into a single word */ -typedef uint32_t gain_minifloat_packed_t; - -/* The nominal range of a gain, expressed as a float */ -#define GAIN_FLOAT_ZERO 0.0f -#define GAIN_FLOAT_UNITY 1.0f - -/* Unity gain expressed as a minifloat */ -#define GAIN_MINIFLOAT_UNITY 0xE000 - -/* Pack a pair of gain_mini_float_t into a combined gain_minifloat_packed_t */ -static inline gain_minifloat_packed_t gain_minifloat_pack(gain_minifloat_t left, - gain_minifloat_t right) -{ - return (right << 16) | left; -} - -/* Unpack a gain_minifloat_packed_t into the two gain_minifloat_t components */ -static inline gain_minifloat_t gain_minifloat_unpack_left(gain_minifloat_packed_t packed) -{ - return packed & 0xFFFF; -} - -static inline gain_minifloat_t gain_minifloat_unpack_right(gain_minifloat_packed_t packed) -{ - return packed >> 16; -} - -/* A pair of unity gains expressed as a gain_minifloat_packed_t */ -#define GAIN_MINIFLOAT_PACKED_UNITY gain_minifloat_pack(GAIN_MINIFLOAT_UNITY, GAIN_MINIFLOAT_UNITY) - -/* Convert a float to the internal representation used for gains. - * The nominal range [0.0, 1.0], but the hard range is [0.0, 2.0). - * Negative and underflow values are converted to 0.0, - * and values larger than the hard maximum are truncated to the hard maximum. - * - * Minifloats are ordered, and standard comparisons may be used between them - * in the gain_minifloat_t representation. - * - * Details on internal representation of gains, based on mini-floats: - * The nominal maximum is 1.0 and the hard maximum is 1 ULP less than 2.0, or +6 dB. - * The minimum non-zero value is approximately 1.9e-6 or -114 dB. - * Negative numbers, infinity, and NaN are not supported. - * There are 13 significand bits specified, 1 implied hidden bit, 3 exponent bits, - * and no sign bit. Denormals are supported. - */ -gain_minifloat_t gain_from_float(float f); - -/* Convert the internal representation used for gains to float */ -float float_from_gain(gain_minifloat_t gain); - -__END_DECLS - -#endif // COCOS_AUDIO_MINIFLOAT_H diff --git a/cocos/audio/android/audio_utils/include/audio_utils/primitives.h b/cocos/audio/android/audio_utils/include/audio_utils/primitives.h deleted file mode 100644 index 1421fd989002..000000000000 --- a/cocos/audio/android/audio_utils/include/audio_utils/primitives.h +++ /dev/null @@ -1,959 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef COCOS_AUDIO_PRIMITIVES_H -#define COCOS_AUDIO_PRIMITIVES_H - -#include -#include -#include - -__BEGIN_DECLS - -/* The memcpy_* conversion routines are designed to work in-place on same dst as src - * buffers only if the types shrink on copy, with the exception of memcpy_to_i16_from_u8(). - * This allows the loops to go upwards for faster cache access (and may be more flexible - * for future optimization later). - */ - -/** - * Dither and clamp pairs of 32-bit input samples (sums) to 16-bit output samples (out). - * Each 32-bit input sample can be viewed as a signed fixed-point Q19.12 of which the - * .12 fraction bits are dithered and the 19 integer bits are clamped to signed 16 bits. - * Alternatively the input can be viewed as Q4.27, of which the lowest .12 of the fraction - * is dithered and the remaining fraction is converted to the output Q.15, with clamping - * on the 4 integer guard bits. - * - * For interleaved stereo, c is the number of sample pairs, - * and out is an array of interleaved pairs of 16-bit samples per channel. - * For mono, c is the number of samples / 2, and out is an array of 16-bit samples. - * The name "dither" is a misnomer; the current implementation does not actually dither - * but uses truncation. This may change. - * The out and sums buffers must either be completely separate (non-overlapping), or - * they must both start at the same address. Partially overlapping buffers are not supported. - */ -void ditherAndClamp(int32_t* out, const int32_t *sums, size_t c); - -/* Expand and copy samples from unsigned 8-bit offset by 0x80 to signed 16-bit. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must either be completely separate (non-overlapping), or - * they must both start at the same address. Partially overlapping buffers are not supported. - */ -void memcpy_to_i16_from_u8(int16_t *dst, const uint8_t *src, size_t count); - -/* Shrink and copy samples from signed 16-bit to unsigned 8-bit offset by 0x80. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must either be completely separate (non-overlapping), or - * they must both start at the same address. Partially overlapping buffers are not supported. - * The conversion is done by truncation, without dithering, so it loses resolution. - */ -void memcpy_to_u8_from_i16(uint8_t *dst, const int16_t *src, size_t count); - -/* Copy samples from float to unsigned 8-bit offset by 0x80. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must either be completely separate (non-overlapping), or - * they must both start at the same address. Partially overlapping buffers are not supported. - * The conversion is done by truncation, without dithering, so it loses resolution. - */ -void memcpy_to_u8_from_float(uint8_t *dst, const float *src, size_t count); - -/* Shrink and copy samples from signed 32-bit fixed-point Q0.31 to signed 16-bit Q0.15. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must either be completely separate (non-overlapping), or - * they must both start at the same address. Partially overlapping buffers are not supported. - * The conversion is done by truncation, without dithering, so it loses resolution. - */ -void memcpy_to_i16_from_i32(int16_t *dst, const int32_t *src, size_t count); - -/* Shrink and copy samples from single-precision floating-point to signed 16-bit. - * Each float should be in the range -1.0 to 1.0. Values outside that range are clamped, - * refer to clamp16_from_float(). - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must either be completely separate (non-overlapping), or - * they must both start at the same address. Partially overlapping buffers are not supported. - * The conversion is done by truncation, without dithering, so it loses resolution. - */ -void memcpy_to_i16_from_float(int16_t *dst, const float *src, size_t count); - -/* Copy samples from signed fixed-point 32-bit Q4.27 to single-precision floating-point. - * The nominal output float range is [-1.0, 1.0] if the fixed-point range is - * [0xf8000000, 0x07ffffff]. The full float range is [-16.0, 16.0]. Note the closed range - * at 1.0 and 16.0 is due to rounding on conversion to float. See float_from_q4_27() for details. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must either be completely separate (non-overlapping), or - * they must both start at the same address. Partially overlapping buffers are not supported. - */ -void memcpy_to_float_from_q4_27(float *dst, const int32_t *src, size_t count); - -/* Copy samples from signed fixed-point 16 bit Q0.15 to single-precision floating-point. - * The output float range is [-1.0, 1.0) for the fixed-point range [0x8000, 0x7fff]. - * No rounding is needed as the representation is exact. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must be completely separate. - */ -void memcpy_to_float_from_i16(float *dst, const int16_t *src, size_t count); - -/* Copy samples from unsigned fixed-point 8 bit to single-precision floating-point. - * The output float range is [-1.0, 1.0) for the fixed-point range [0x00, 0xFF]. - * No rounding is needed as the representation is exact. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must be completely separate. - */ -void memcpy_to_float_from_u8(float *dst, const uint8_t *src, size_t count); - -/* Copy samples from signed fixed-point packed 24 bit Q0.23 to single-precision floating-point. - * The packed 24 bit input is stored in native endian format in a uint8_t byte array. - * The output float range is [-1.0, 1.0) for the fixed-point range [0x800000, 0x7fffff]. - * No rounding is needed as the representation is exact. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must be completely separate. - */ -void memcpy_to_float_from_p24(float *dst, const uint8_t *src, size_t count); - -/* Copy samples from signed fixed-point packed 24 bit Q0.23 to signed fixed point 16 bit Q0.15. - * The packed 24 bit output is stored in native endian format in a uint8_t byte array. - * The data is truncated without rounding. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must either be completely separate (non-overlapping), or - * they must both start at the same address. Partially overlapping buffers are not supported. - */ -void memcpy_to_i16_from_p24(int16_t *dst, const uint8_t *src, size_t count); - -/* Copy samples from signed fixed-point packed 24 bit Q0.23 to signed fixed-point 32-bit Q0.31. - * The packed 24 bit input is stored in native endian format in a uint8_t byte array. - * The output data range is [0x80000000, 0x7fffff00] at intervals of 0x100. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must be completely separate. - */ -void memcpy_to_i32_from_p24(int32_t *dst, const uint8_t *src, size_t count); - -/* Copy samples from signed fixed point 16 bit Q0.15 to signed fixed-point packed 24 bit Q0.23. - * The packed 24 bit output is assumed to be a native-endian uint8_t byte array. - * The output data range is [0x800000, 0x7fff00] (not full). - * Nevertheless there is no DC offset on the output, if the input has no DC offset. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must be completely separate. - */ -void memcpy_to_p24_from_i16(uint8_t *dst, const int16_t *src, size_t count); - -/* Copy samples from single-precision floating-point to signed fixed-point packed 24 bit Q0.23. - * The packed 24 bit output is assumed to be a native-endian uint8_t byte array. - * The data is clamped and rounded to nearest, ties away from zero. See clamp24_from_float() - * for details. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must either be completely separate (non-overlapping), or - * they must both start at the same address. Partially overlapping buffers are not supported. - */ -void memcpy_to_p24_from_float(uint8_t *dst, const float *src, size_t count); - -/* Copy samples from signed fixed-point 32-bit Q8.23 to signed fixed-point packed 24 bit Q0.23. - * The packed 24 bit output is assumed to be a native-endian uint8_t byte array. - * The data is clamped to the range is [0x800000, 0x7fffff]. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must be completely separate. - */ -void memcpy_to_p24_from_q8_23(uint8_t *dst, const int32_t *src, size_t count); - -/* Shrink and copy samples from signed 32-bit fixed-point Q0.31 - * to signed fixed-point packed 24 bit Q0.23. - * The packed 24 bit output is assumed to be a native-endian uint8_t byte array. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must either be completely separate (non-overlapping), or - * they must both start at the same address. Partially overlapping buffers are not supported. - * The conversion is done by truncation, without dithering, so it loses resolution. - */ -void memcpy_to_p24_from_i32(uint8_t *dst, const int32_t *src, size_t count); - -/* Copy samples from signed fixed point 16-bit Q0.15 to signed fixed-point 32-bit Q8.23. - * The output data range is [0xff800000, 0x007fff00] at intervals of 0x100. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must be completely separate. - */ -void memcpy_to_q8_23_from_i16(int32_t *dst, const int16_t *src, size_t count); - -/* Copy samples from single-precision floating-point to signed fixed-point 32-bit Q8.23. - * This copy will clamp the Q8.23 representation to [0xff800000, 0x007fffff] even though there - * are guard bits available. Fractional lsb is rounded to nearest, ties away from zero. - * See clamp24_from_float() for details. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must either be completely separate (non-overlapping), or - * they must both start at the same address. Partially overlapping buffers are not supported. - */ -void memcpy_to_q8_23_from_float_with_clamp(int32_t *dst, const float *src, size_t count); - -/* Copy samples from signed fixed point packed 24-bit Q0.23 to signed fixed-point 32-bit Q8.23. - * The output data range is [0xff800000, 0x007fffff]. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must be completely separate. - */ -void memcpy_to_q8_23_from_p24(int32_t *dst, const uint8_t *src, size_t count); - -/* Copy samples from single-precision floating-point to signed fixed-point 32-bit Q4.27. - * The conversion will use the full available Q4.27 range, including guard bits. - * Fractional lsb is rounded to nearest, ties away from zero. - * See clampq4_27_from_float() for details. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must either be completely separate (non-overlapping), or - * they must both start at the same address. Partially overlapping buffers are not supported. - */ -void memcpy_to_q4_27_from_float(int32_t *dst, const float *src, size_t count); - -/* Copy samples from signed fixed-point 32-bit Q8.23 to signed fixed point 16-bit Q0.15. - * The data is clamped, and truncated without rounding. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must either be completely separate (non-overlapping), or - * they must both start at the same address. Partially overlapping buffers are not supported. - */ -void memcpy_to_i16_from_q8_23(int16_t *dst, const int32_t *src, size_t count); - -/* Copy samples from signed fixed-point 32-bit Q8.23 to single-precision floating-point. - * The nominal output float range is [-1.0, 1.0) for the fixed-point - * range [0xff800000, 0x007fffff]. The maximum output float range is [-256.0, 256.0). - * No rounding is needed as the representation is exact for nominal values. - * Rounding for overflow values is to nearest, ties to even. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must either be completely separate (non-overlapping), or - * they must both start at the same address. Partially overlapping buffers are not supported. - */ -void memcpy_to_float_from_q8_23(float *dst, const int32_t *src, size_t count); - -/* Copy samples from signed fixed point 16-bit Q0.15 to signed fixed-point 32-bit Q0.31. - * The output data range is [0x80000000, 0x7fff0000] at intervals of 0x10000. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must be completely separate. - */ -void memcpy_to_i32_from_i16(int32_t *dst, const int16_t *src, size_t count); - -/* Copy samples from single-precision floating-point to signed fixed-point 32-bit Q0.31. - * If rounding is needed on truncation, the fractional lsb is rounded to nearest, - * ties away from zero. See clamp32_from_float() for details. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must either be completely separate (non-overlapping), or - * they must both start at the same address. Partially overlapping buffers are not supported. - */ -void memcpy_to_i32_from_float(int32_t *dst, const float *src, size_t count); - -/* Copy samples from signed fixed-point 32-bit Q0.31 to single-precision floating-point. - * The float range is [-1.0, 1.0] for the fixed-point range [0x80000000, 0x7fffffff]. - * Rounding is done according to float_from_i32(). - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of samples to copy - * The destination and source buffers must either be completely separate (non-overlapping), or - * they must both start at the same address. Partially overlapping buffers are not supported. - */ -void memcpy_to_float_from_i32(float *dst, const int32_t *src, size_t count); - -/* Downmix pairs of interleaved stereo input 16-bit samples to mono output 16-bit samples. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of stereo frames to downmix - * The destination and source buffers must be completely separate (non-overlapping). - * The current implementation truncates the mean rather than dither, but this may change. - */ -void downmix_to_mono_i16_from_stereo_i16(int16_t *dst, const int16_t *src, size_t count); - -/* Upmix mono input 16-bit samples to pairs of interleaved stereo output 16-bit samples by - * duplicating. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of mono samples to upmix - * The destination and source buffers must be completely separate (non-overlapping). - */ -void upmix_to_stereo_i16_from_mono_i16(int16_t *dst, const int16_t *src, size_t count); - -/* Downmix pairs of interleaved stereo input float samples to mono output float samples - * by averaging the stereo pair together. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of stereo frames to downmix - * The destination and source buffers must be completely separate (non-overlapping), - * or they must both start at the same address. - */ -void downmix_to_mono_float_from_stereo_float(float *dst, const float *src, size_t count); - -/* Upmix mono input float samples to pairs of interleaved stereo output float samples by - * duplicating. - * Parameters: - * dst Destination buffer - * src Source buffer - * count Number of mono samples to upmix - * The destination and source buffers must be completely separate (non-overlapping). - */ -void upmix_to_stereo_float_from_mono_float(float *dst, const float *src, size_t count); - -/* Return the total number of non-zero 32-bit samples */ -size_t nonZeroMono32(const int32_t *samples, size_t count); - -/* Return the total number of non-zero 16-bit samples */ -size_t nonZeroMono16(const int16_t *samples, size_t count); - -/* Return the total number of non-zero stereo frames, where a frame is considered non-zero - * if either of its constituent 32-bit samples is non-zero - */ -size_t nonZeroStereo32(const int32_t *frames, size_t count); - -/* Return the total number of non-zero stereo frames, where a frame is considered non-zero - * if either of its constituent 16-bit samples is non-zero - */ -size_t nonZeroStereo16(const int16_t *frames, size_t count); - -/* Copy frames, selecting source samples based on a source channel mask to fit - * the destination channel mask. Unmatched channels in the destination channel mask - * are zero filled. Unmatched channels in the source channel mask are dropped. - * Channels present in the channel mask are represented by set bits in the - * uint32_t value and are matched without further interpretation. - * Parameters: - * dst Destination buffer - * dst_mask Bit mask corresponding to destination channels present - * src Source buffer - * src_mask Bit mask corresponding to source channels present - * sample_size Size of each sample in bytes. Must be 1, 2, 3, or 4. - * count Number of frames to copy - * The destination and source buffers must be completely separate (non-overlapping). - * If the sample size is not in range, the function will abort. - */ -void memcpy_by_channel_mask(void *dst, uint32_t dst_mask, - const void *src, uint32_t src_mask, size_t sample_size, size_t count); - -/* Copy frames, selecting source samples based on an index array (idxary). - * The idxary[] consists of dst_channels number of elements. - * The ith element if idxary[] corresponds the ith destination channel. - * A non-negative value is the channel index in the source frame. - * A negative index (-1) represents filling with 0. - * - * Example: Swapping L and R channels for stereo streams - * idxary[0] = 1; - * idxary[1] = 0; - * - * Example: Copying a mono source to the front center 5.1 channel - * idxary[0] = -1; - * idxary[1] = -1; - * idxary[2] = 0; - * idxary[3] = -1; - * idxary[4] = -1; - * idxary[5] = -1; - * - * This copy allows swizzling of channels or replication of channels. - * - * Parameters: - * dst Destination buffer - * dst_channels Number of destination channels per frame - * src Source buffer - * src_channels Number of source channels per frame - * idxary Array of indices representing channels in the source frame - * sample_size Size of each sample in bytes. Must be 1, 2, 3, or 4. - * count Number of frames to copy - * The destination and source buffers must be completely separate (non-overlapping). - * If the sample size is not in range, the function will abort. - */ -void memcpy_by_index_array(void *dst, uint32_t dst_channels, - const void *src, uint32_t src_channels, - const int8_t *idxary, size_t sample_size, size_t count); - -/* Prepares an index array (idxary) from channel masks, which can be later - * used by memcpy_by_index_array(). Returns the number of array elements required. - * This may be greater than idxcount, so the return value should be checked - * if idxary size is less than 32. Note that idxary is a caller allocated array - * of at least as many channels as present in the dst_mask. - * Channels present in the channel mask are represented by set bits in the - * uint32_t value and are matched without further interpretation. - * - * This function is typically used for converting audio data with different - * channel position masks. - * - * Parameters: - * idxary Updated array of indices of channels in the src frame for the dst frame - * idxcount Number of caller allocated elements in idxary - * dst_mask Bit mask corresponding to destination channels present - * src_mask Bit mask corresponding to source channels present - */ -size_t memcpy_by_index_array_initialization(int8_t *idxary, size_t idxcount, - uint32_t dst_mask, uint32_t src_mask); - -/* Prepares an index array (idxary) from channel masks, which can be later - * used by memcpy_by_index_array(). Returns the number of array elements required. - * - * For a source channel index mask, the source channels will map to the destination - * channels as if counting the set bits in dst_mask in order from lsb to msb - * (zero bits are ignored). The ith bit of the src_mask corresponds to the - * ith SET bit of dst_mask and the ith destination channel. Hence, a zero ith - * bit of the src_mask indicates that the ith destination channel plays silence. - * - * Parameters: - * idxary Updated array of indices of channels in the src frame for the dst frame - * idxcount Number of caller allocated elements in idxary - * dst_mask Bit mask corresponding to destination channels present - * src_mask Bit mask corresponding to source channels present - */ -size_t memcpy_by_index_array_initialization_src_index(int8_t *idxary, size_t idxcount, - uint32_t dst_mask, uint32_t src_mask); - -/* Prepares an index array (idxary) from channel mask bits, which can be later - * used by memcpy_by_index_array(). Returns the number of array elements required. - * - * This initialization is for a destination channel index mask from a positional - * source mask. - * - * For an destination channel index mask, the input channels will map - * to the destination channels, with the ith SET bit in the source bits corresponding - * to the ith bit in the destination bits. If there is a zero bit in the middle - * of set destination bits (unlikely), the corresponding source channel will - * be dropped. - * - * Parameters: - * idxary Updated array of indices of channels in the src frame for the dst frame - * idxcount Number of caller allocated elements in idxary - * dst_mask Bit mask corresponding to destination channels present - * src_mask Bit mask corresponding to source channels present - */ -size_t memcpy_by_index_array_initialization_dst_index(int8_t *idxary, size_t idxcount, - uint32_t dst_mask, uint32_t src_mask); - -/** - * Clamp (aka hard limit or clip) a signed 32-bit sample to 16-bit range. - */ -static inline int16_t clamp16(int32_t sample) -{ - if ((sample>>15) ^ (sample>>31)) - sample = 0x7FFF ^ (sample>>31); - return sample; -} - -/* - * Convert a IEEE 754 single precision float [-1.0, 1.0) to int16_t [-32768, 32767] - * with clamping. Note the open bound at 1.0, values within 1/65536 of 1.0 map - * to 32767 instead of 32768 (early clamping due to the smaller positive integer subrange). - * - * Values outside the range [-1.0, 1.0) are properly clamped to -32768 and 32767, - * including -Inf and +Inf. NaN will generally be treated either as -32768 or 32767, - * depending on the sign bit inside NaN (whose representation is not unique). - * Nevertheless, strictly speaking, NaN behavior should be considered undefined. - * - * Rounding of 0.5 lsb is to even (default for IEEE 754). - */ -static inline int16_t clamp16_from_float(float f) -{ - /* Offset is used to expand the valid range of [-1.0, 1.0) into the 16 lsbs of the - * floating point significand. The normal shift is 3<<22, but the -15 offset - * is used to multiply by 32768. - */ - static const float offset = (float)(3 << (22 - 15)); - /* zero = (0x10f << 22) = 0x43c00000 (not directly used) */ - static const int32_t limneg = (0x10f << 22) /*zero*/ - 32768; /* 0x43bf8000 */ - static const int32_t limpos = (0x10f << 22) /*zero*/ + 32767; /* 0x43c07fff */ - - union { - float f; - int32_t i; - } u; - - u.f = f + offset; /* recenter valid range */ - /* Now the valid range is represented as integers between [limneg, limpos]. - * Clamp using the fact that float representation (as an integer) is an ordered set. - */ - if (u.i < limneg) - u.i = -32768; - else if (u.i > limpos) - u.i = 32767; - return u.i; /* Return lower 16 bits, the part of interest in the significand. */ -} - -/* - * Convert a IEEE 754 single precision float [-1.0, 1.0) to uint8_t [0, 0xff] - * with clamping. Note the open bound at 1.0, values within 1/128 of 1.0 map - * to 255 instead of 256 (early clamping due to the smaller positive integer subrange). - * - * Values outside the range [-1.0, 1.0) are properly clamped to 0 and 255, - * including -Inf and +Inf. NaN will generally be treated either as 0 or 255, - * depending on the sign bit inside NaN (whose representation is not unique). - * Nevertheless, strictly speaking, NaN behavior should be considered undefined. - * - * Rounding of 0.5 lsb is to even (default for IEEE 754). - */ -static inline uint8_t clamp8_from_float(float f) -{ - /* Offset is used to expand the valid range of [-1.0, 1.0) into the 16 lsbs of the - * floating point significand. The normal shift is 3<<22, but the -7 offset - * is used to multiply by 128. - */ - static const float offset = (float)((3 << (22 - 7)) + 1 /* to cancel -1.0 */); - /* zero = (0x11f << 22) = 0x47c00000 */ - static const int32_t limneg = (0x11f << 22) /*zero*/; - static const int32_t limpos = (0x11f << 22) /*zero*/ + 255; /* 0x47c000ff */ - - union { - float f; - int32_t i; - } u; - - u.f = f + offset; /* recenter valid range */ - /* Now the valid range is represented as integers between [limneg, limpos]. - * Clamp using the fact that float representation (as an integer) is an ordered set. - */ - if (u.i < limneg) - return 0; - if (u.i > limpos) - return 255; - return u.i; /* Return lower 8 bits, the part of interest in the significand. */ -} - -/* Convert a single-precision floating point value to a Q0.23 integer value, stored in a - * 32 bit signed integer (technically stored as Q8.23, but clamped to Q0.23). - * - * Rounds to nearest, ties away from 0. - * - * Values outside the range [-1.0, 1.0) are properly clamped to -8388608 and 8388607, - * including -Inf and +Inf. NaN values are considered undefined, and behavior may change - * depending on hardware and future implementation of this function. - */ -static inline int32_t clamp24_from_float(float f) -{ - static const float scale = (float)(1 << 23); - static const float limpos = 0x7fffff / (float)(1 << 23); - static const float limneg = -0x800000 / (float)(1 << 23); - - if (f <= limneg) { - return -0x800000; - } else if (f >= limpos) { - return 0x7fffff; - } - f *= scale; - /* integer conversion is through truncation (though int to float is not). - * ensure that we round to nearest, ties away from 0. - */ - return f > 0 ? f + 0.5 : f - 0.5; -} - -/* Convert a signed fixed-point 32-bit Q8.23 value to a Q0.23 integer value, - * stored in a 32-bit signed integer (technically stored as Q8.23, but clamped to Q0.23). - * - * Values outside the range [-0x800000, 0x7fffff] are clamped to that range. - */ -static inline int32_t clamp24_from_q8_23(int32_t ival) -{ - static const int32_t limpos = 0x7fffff; - static const int32_t limneg = -0x800000; - if (ival < limneg) { - return limneg; - } else if (ival > limpos) { - return limpos; - } else { - return ival; - } -} - -/* Convert a single-precision floating point value to a Q4.27 integer value. - * Rounds to nearest, ties away from 0. - * - * Values outside the range [-16.0, 16.0) are properly clamped to -2147483648 and 2147483647, - * including -Inf and +Inf. NaN values are considered undefined, and behavior may change - * depending on hardware and future implementation of this function. - */ -static inline int32_t clampq4_27_from_float(float f) -{ - static const float scale = (float)(1UL << 27); - static const float limpos = 16.; - static const float limneg = -16.; - - if (f <= limneg) { - return -0x80000000; /* or 0x80000000 */ - } else if (f >= limpos) { - return 0x7fffffff; - } - f *= scale; - /* integer conversion is through truncation (though int to float is not). - * ensure that we round to nearest, ties away from 0. - */ - return f > 0 ? f + 0.5 : f - 0.5; -} - -/* Convert a single-precision floating point value to a Q0.31 integer value. - * Rounds to nearest, ties away from 0. - * - * Values outside the range [-1.0, 1.0) are properly clamped to -2147483648 and 2147483647, - * including -Inf and +Inf. NaN values are considered undefined, and behavior may change - * depending on hardware and future implementation of this function. - */ -static inline int32_t clamp32_from_float(float f) -{ - static const float scale = (float)(1UL << 31); - static const float limpos = 1.; - static const float limneg = -1.; - - if (f <= limneg) { - return -0x80000000; /* or 0x80000000 */ - } else if (f >= limpos) { - return 0x7fffffff; - } - f *= scale; - /* integer conversion is through truncation (though int to float is not). - * ensure that we round to nearest, ties away from 0. - */ - return f > 0 ? f + 0.5 : f - 0.5; -} - -/* Convert a signed fixed-point 32-bit Q4.27 value to single-precision floating-point. - * The nominal output float range is [-1.0, 1.0] if the fixed-point range is - * [0xf8000000, 0x07ffffff]. The full float range is [-16.0, 16.0]. - * - * Note the closed range at 1.0 and 16.0 is due to rounding on conversion to float. - * In more detail: if the fixed-point integer exceeds 24 bit significand of single - * precision floating point, the 0.5 lsb in the significand conversion will round - * towards even, as per IEEE 754 default. - */ -static inline float float_from_q4_27(int32_t ival) -{ - /* The scale factor is the reciprocal of the fractional bits. - * - * Since the scale factor is a power of 2, the scaling is exact, and there - * is no rounding due to the multiplication - the bit pattern is preserved. - * However, there may be rounding due to the fixed-point to float conversion, - * as described above. - */ - static const float scale = 1. / (float)(1UL << 27); - - return ival * scale; -} - -/* Convert an unsigned fixed-point 32-bit U4.28 value to single-precision floating-point. - * The nominal output float range is [0.0, 1.0] if the fixed-point range is - * [0x00000000, 0x10000000]. The full float range is [0.0, 16.0]. - * - * Note the closed range at 1.0 and 16.0 is due to rounding on conversion to float. - * In more detail: if the fixed-point integer exceeds 24 bit significand of single - * precision floating point, the 0.5 lsb in the significand conversion will round - * towards even, as per IEEE 754 default. - */ -static inline float float_from_u4_28(uint32_t uval) -{ - static const float scale = 1. / (float)(1UL << 28); - - return uval * scale; -} - -/* Convert an unsigned fixed-point 16-bit U4.12 value to single-precision floating-point. - * The nominal output float range is [0.0, 1.0] if the fixed-point range is - * [0x0000, 0x1000]. The full float range is [0.0, 16.0). - */ -static inline float float_from_u4_12(uint16_t uval) -{ - static const float scale = 1. / (float)(1UL << 12); - - return uval * scale; -} - -/* Convert a single-precision floating point value to a U4.28 integer value. - * Rounds to nearest, ties away from 0. - * - * Values outside the range [0, 16.0] are properly clamped to [0, 4294967295] - * including -Inf and +Inf. NaN values are considered undefined, and behavior may change - * depending on hardware and future implementation of this function. - */ -static inline uint32_t u4_28_from_float(float f) -{ - static const float scale = (float)(1 << 28); - static const float limpos = 0xffffffffUL / (float)(1 << 28); - - if (f <= 0.) { - return 0; - } else if (f >= limpos) { - return 0xffffffff; - } - /* integer conversion is through truncation (though int to float is not). - * ensure that we round to nearest, ties away from 0. - */ - return f * scale + 0.5; -} - -/* Convert a single-precision floating point value to a U4.12 integer value. - * Rounds to nearest, ties away from 0. - * - * Values outside the range [0, 16.0) are properly clamped to [0, 65535] - * including -Inf and +Inf. NaN values are considered undefined, and behavior may change - * depending on hardware and future implementation of this function. - */ -static inline uint16_t u4_12_from_float(float f) -{ - static const float scale = (float)(1 << 12); - static const float limpos = 0xffff / (float)(1 << 12); - - if (f <= 0.) { - return 0; - } else if (f >= limpos) { - return 0xffff; - } - /* integer conversion is through truncation (though int to float is not). - * ensure that we round to nearest, ties away from 0. - */ - return f * scale + 0.5; -} - -/* Convert a signed fixed-point 16-bit Q0.15 value to single-precision floating-point. - * The output float range is [-1.0, 1.0) for the fixed-point range - * [0x8000, 0x7fff]. - * - * There is no rounding, the conversion and representation is exact. - */ -static inline float float_from_i16(int16_t ival) -{ - /* The scale factor is the reciprocal of the nominal 16 bit integer - * half-sided range (32768). - * - * Since the scale factor is a power of 2, the scaling is exact, and there - * is no rounding due to the multiplication - the bit pattern is preserved. - */ - static const float scale = 1. / (float)(1UL << 15); - - return ival * scale; -} - -/* Convert an unsigned fixed-point 8-bit U0.8 value to single-precision floating-point. - * The nominal output float range is [-1.0, 1.0) if the fixed-point range is - * [0x00, 0xff]. - */ -static inline float float_from_u8(uint8_t uval) -{ - static const float scale = 1. / (float)(1UL << 7); - - return ((int)uval - 128) * scale; -} - -/* Convert a packed 24bit Q0.23 value stored native-endian in a uint8_t ptr - * to a signed fixed-point 32 bit integer Q0.31 value. The output Q0.31 range - * is [0x80000000, 0x7fffff00] for the fixed-point range [0x800000, 0x7fffff]. - * Even though the output range is limited on the positive side, there is no - * DC offset on the output, if the input has no DC offset. - * - * Avoid relying on the limited output range, as future implementations may go - * to full range. - */ -static inline int32_t i32_from_p24(const uint8_t *packed24) -{ - /* convert to 32b */ - return (packed24[0] << 8) | (packed24[1] << 16) | (packed24[2] << 24); -} - -/* Convert a 32-bit Q0.31 value to single-precision floating-point. - * The output float range is [-1.0, 1.0] for the fixed-point range - * [0x80000000, 0x7fffffff]. - * - * Rounding may occur in the least significant 8 bits for large fixed point - * values due to storage into the 24-bit floating-point significand. - * Rounding will be to nearest, ties to even. - */ -static inline float float_from_i32(int32_t ival) -{ - static const float scale = 1. / (float)(1UL << 31); - - return ival * scale; -} - -/* Convert a packed 24bit Q0.23 value stored native endian in a uint8_t ptr - * to single-precision floating-point. The output float range is [-1.0, 1.0) - * for the fixed-point range [0x800000, 0x7fffff]. - * - * There is no rounding, the conversion and representation is exact. - */ -static inline float float_from_p24(const uint8_t *packed24) -{ - return float_from_i32(i32_from_p24(packed24)); -} - -/* Convert a 24-bit Q8.23 value to single-precision floating-point. - * The nominal output float range is [-1.0, 1.0) for the fixed-point - * range [0xff800000, 0x007fffff]. The maximum float range is [-256.0, 256.0). - * - * There is no rounding in the nominal range, the conversion and representation - * is exact. For values outside the nominal range, rounding is to nearest, ties to even. - */ -static inline float float_from_q8_23(int32_t ival) -{ - static const float scale = 1. / (float)(1UL << 23); - - return ival * scale; -} - -/** - * Multiply-accumulate 16-bit terms with 32-bit result: return a + in*v. - */ -static inline -int32_t mulAdd(int16_t in, int16_t v, int32_t a) -{ -#if defined(__arm__) && !defined(__thumb__) - int32_t out; - asm( "smlabb %[out], %[in], %[v], %[a] \n" - : [out]"=r"(out) - : [in]"%r"(in), [v]"r"(v), [a]"r"(a) - : ); - return out; -#else - return a + in * (int32_t)v; -#endif -} - -/** - * Multiply 16-bit terms with 32-bit result: return in*v. - */ -static inline -int32_t mul(int16_t in, int16_t v) -{ -#if defined(__arm__) && !defined(__thumb__) - int32_t out; - asm( "smulbb %[out], %[in], %[v] \n" - : [out]"=r"(out) - : [in]"%r"(in), [v]"r"(v) - : ); - return out; -#else - return in * (int32_t)v; -#endif -} - -/** - * Similar to mulAdd, but the 16-bit terms are extracted from a 32-bit interleaved stereo pair. - */ -static inline -int32_t mulAddRL(int left, uint32_t inRL, uint32_t vRL, int32_t a) -{ -#if defined(__arm__) && !defined(__thumb__) - int32_t out; - if (left) { - asm( "smlabb %[out], %[inRL], %[vRL], %[a] \n" - : [out]"=r"(out) - : [inRL]"%r"(inRL), [vRL]"r"(vRL), [a]"r"(a) - : ); - } else { - asm( "smlatt %[out], %[inRL], %[vRL], %[a] \n" - : [out]"=r"(out) - : [inRL]"%r"(inRL), [vRL]"r"(vRL), [a]"r"(a) - : ); - } - return out; -#else - if (left) { - return a + (int16_t)(inRL&0xFFFF) * (int16_t)(vRL&0xFFFF); - } else { - return a + (int16_t)(inRL>>16) * (int16_t)(vRL>>16); - } -#endif -} - -/** - * Similar to mul, but the 16-bit terms are extracted from a 32-bit interleaved stereo pair. - */ -static inline -int32_t mulRL(int left, uint32_t inRL, uint32_t vRL) -{ -#if defined(__arm__) && !defined(__thumb__) - int32_t out; - if (left) { - asm( "smulbb %[out], %[inRL], %[vRL] \n" - : [out]"=r"(out) - : [inRL]"%r"(inRL), [vRL]"r"(vRL) - : ); - } else { - asm( "smultt %[out], %[inRL], %[vRL] \n" - : [out]"=r"(out) - : [inRL]"%r"(inRL), [vRL]"r"(vRL) - : ); - } - return out; -#else - if (left) { - return (int16_t)(inRL&0xFFFF) * (int16_t)(vRL&0xFFFF); - } else { - return (int16_t)(inRL>>16) * (int16_t)(vRL>>16); - } -#endif -} - -__END_DECLS - -#endif // COCOS_AUDIO_PRIMITIVES_H diff --git a/cocos/audio/android/audio_utils/minifloat.cpp b/cocos/audio/android/audio_utils/minifloat.cpp deleted file mode 100644 index 81fa18de0d94..000000000000 --- a/cocos/audio/android/audio_utils/minifloat.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "audio/android/audio_utils/include/audio_utils/minifloat.h" - -#define EXPONENT_BITS 3 -#define EXPONENT_MAX ((1 << EXPONENT_BITS) - 1) -#define EXCESS ((1 << EXPONENT_BITS) - 2) - -#define MANTISSA_BITS 13 -#define MANTISSA_MAX ((1 << MANTISSA_BITS) - 1) -#define HIDDEN_BIT (1 << MANTISSA_BITS) -#define ONE_FLOAT ((float) (1 << (MANTISSA_BITS + 1))) - -#define MINIFLOAT_MAX ((EXPONENT_MAX << MANTISSA_BITS) | MANTISSA_MAX) - -#if EXPONENT_BITS + MANTISSA_BITS != 16 -#error EXPONENT_BITS and MANTISSA_BITS must sum to 16 -#endif - -extern "C" { - -gain_minifloat_t gain_from_float(float v) -{ - if (std::isnan(v) || v <= 0.0f) - { - return 0; - } - if (v >= 2.0f) - { - return MINIFLOAT_MAX; - } - int exp; - float r = frexpf(v, &exp); - if ((exp += EXCESS) > EXPONENT_MAX) - { - return MINIFLOAT_MAX; - } - if (-exp >= MANTISSA_BITS) - { - return 0; - } - int mantissa = (int) (r * ONE_FLOAT); - return exp > 0 ? (exp << MANTISSA_BITS) | (mantissa & ~HIDDEN_BIT) : - (mantissa >> (1 - exp)) & MANTISSA_MAX; -} - -float float_from_gain(gain_minifloat_t a) -{ - int mantissa = a & MANTISSA_MAX; - int exponent = (a >> MANTISSA_BITS) & EXPONENT_MAX; - return ldexpf((exponent > 0 ? HIDDEN_BIT | mantissa : mantissa << 1) / ONE_FLOAT, - exponent - EXCESS); -} - -} // extern "C" { diff --git a/cocos/audio/android/audio_utils/primitives.c b/cocos/audio/android/audio_utils/primitives.c deleted file mode 100644 index 269c20e80037..000000000000 --- a/cocos/audio/android/audio_utils/primitives.c +++ /dev/null @@ -1,526 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "audio/android/cutils/bitops.h" /* for popcount() */ -#include "audio/android/audio_utils/include/audio_utils/primitives.h" -#include "audio/android/audio_utils/private/private.h" - -void ditherAndClamp(int32_t* out, const int32_t *sums, size_t c) -{ - size_t i; - for (i=0 ; i> 12; - int32_t nr = r >> 12; - l = clamp16(nl); - r = clamp16(nr); - *out++ = (r<<16) | (l & 0xFFFF); - } -} - -void memcpy_to_i16_from_u8(int16_t *dst, const uint8_t *src, size_t count) -{ - dst += count; - src += count; - while (count--) { - *--dst = (int16_t)(*--src - 0x80) << 8; - } -} - -void memcpy_to_u8_from_i16(uint8_t *dst, const int16_t *src, size_t count) -{ - while (count--) { - *dst++ = (*src++ >> 8) + 0x80; - } -} - -void memcpy_to_u8_from_float(uint8_t *dst, const float *src, size_t count) -{ - while (count--) { - *dst++ = clamp8_from_float(*src++); - } -} - -void memcpy_to_i16_from_i32(int16_t *dst, const int32_t *src, size_t count) -{ - while (count--) { - *dst++ = *src++ >> 16; - } -} - -void memcpy_to_i16_from_float(int16_t *dst, const float *src, size_t count) -{ - while (count--) { - *dst++ = clamp16_from_float(*src++); - } -} - -void memcpy_to_float_from_q4_27(float *dst, const int32_t *src, size_t count) -{ - while (count--) { - *dst++ = float_from_q4_27(*src++); - } -} - -void memcpy_to_float_from_i16(float *dst, const int16_t *src, size_t count) -{ - while (count--) { - *dst++ = float_from_i16(*src++); - } -} - -void memcpy_to_float_from_u8(float *dst, const uint8_t *src, size_t count) -{ - while (count--) { - *dst++ = float_from_u8(*src++); - } -} - -void memcpy_to_float_from_p24(float *dst, const uint8_t *src, size_t count) -{ - while (count--) { - *dst++ = float_from_p24(src); - src += 3; - } -} - -void memcpy_to_i16_from_p24(int16_t *dst, const uint8_t *src, size_t count) -{ - while (count--) { -#ifdef HAVE_BIG_ENDIAN - *dst++ = src[1] | (src[0] << 8); -#else - *dst++ = src[1] | (src[2] << 8); -#endif - src += 3; - } -} - -void memcpy_to_i32_from_p24(int32_t *dst, const uint8_t *src, size_t count) -{ - while (count--) { -#ifdef HAVE_BIG_ENDIAN - *dst++ = (src[2] << 8) | (src[1] << 16) | (src[0] << 24); -#else - *dst++ = (src[0] << 8) | (src[1] << 16) | (src[2] << 24); -#endif - src += 3; - } -} - -void memcpy_to_p24_from_i16(uint8_t *dst, const int16_t *src, size_t count) -{ - while (count--) { -#ifdef HAVE_BIG_ENDIAN - *dst++ = *src >> 8; - *dst++ = *src++; - *dst++ = 0; -#else - *dst++ = 0; - *dst++ = *src; - *dst++ = *src++ >> 8; -#endif - } -} - -void memcpy_to_p24_from_float(uint8_t *dst, const float *src, size_t count) -{ - while (count--) { - int32_t ival = clamp24_from_float(*src++); - -#ifdef HAVE_BIG_ENDIAN - *dst++ = ival >> 16; - *dst++ = ival >> 8; - *dst++ = ival; -#else - *dst++ = ival; - *dst++ = ival >> 8; - *dst++ = ival >> 16; -#endif - } -} - -void memcpy_to_p24_from_q8_23(uint8_t *dst, const int32_t *src, size_t count) -{ - while (count--) { - int32_t ival = clamp24_from_q8_23(*src++); - -#ifdef HAVE_BIG_ENDIAN - *dst++ = ival >> 16; - *dst++ = ival >> 8; - *dst++ = ival; -#else - *dst++ = ival; - *dst++ = ival >> 8; - *dst++ = ival >> 16; -#endif - } -} - -void memcpy_to_p24_from_i32(uint8_t *dst, const int32_t *src, size_t count) -{ - while (count--) { - int32_t ival = *src++ >> 8; - -#ifdef HAVE_BIG_ENDIAN - *dst++ = ival >> 16; - *dst++ = ival >> 8; - *dst++ = ival; -#else - *dst++ = ival; - *dst++ = ival >> 8; - *dst++ = ival >> 16; -#endif - } -} - -void memcpy_to_q8_23_from_i16(int32_t *dst, const int16_t *src, size_t count) -{ - while (count--) { - *dst++ = (int32_t)*src++ << 8; - } -} - -void memcpy_to_q8_23_from_float_with_clamp(int32_t *dst, const float *src, size_t count) -{ - while (count--) { - *dst++ = clamp24_from_float(*src++); - } -} - -void memcpy_to_q8_23_from_p24(int32_t *dst, const uint8_t *src, size_t count) -{ - while (count--) { -#ifdef HAVE_BIG_ENDIAN - *dst++ = (int8_t)src[0] << 16 | src[1] << 8 | src[2]; -#else - *dst++ = (int8_t)src[2] << 16 | src[1] << 8 | src[0]; -#endif - src += 3; - } -} - -void memcpy_to_q4_27_from_float(int32_t *dst, const float *src, size_t count) -{ - while (count--) { - *dst++ = clampq4_27_from_float(*src++); - } -} - -void memcpy_to_i16_from_q8_23(int16_t *dst, const int32_t *src, size_t count) -{ - while (count--) { - *dst++ = clamp16(*src++ >> 8); - } -} - -void memcpy_to_float_from_q8_23(float *dst, const int32_t *src, size_t count) -{ - while (count--) { - *dst++ = float_from_q8_23(*src++); - } -} - -void memcpy_to_i32_from_i16(int32_t *dst, const int16_t *src, size_t count) -{ - while (count--) { - *dst++ = (int32_t)*src++ << 16; - } -} - -void memcpy_to_i32_from_float(int32_t *dst, const float *src, size_t count) -{ - while (count--) { - *dst++ = clamp32_from_float(*src++); - } -} - -void memcpy_to_float_from_i32(float *dst, const int32_t *src, size_t count) -{ - while (count--) { - *dst++ = float_from_i32(*src++); - } -} - -void downmix_to_mono_i16_from_stereo_i16(int16_t *dst, const int16_t *src, size_t count) -{ - while (count--) { - *dst++ = (int16_t)(((int32_t)src[0] + (int32_t)src[1]) >> 1); - src += 2; - } -} - -void upmix_to_stereo_i16_from_mono_i16(int16_t *dst, const int16_t *src, size_t count) -{ - while (count--) { - int32_t temp = *src++; - dst[0] = temp; - dst[1] = temp; - dst += 2; - } -} - -void downmix_to_mono_float_from_stereo_float(float *dst, const float *src, size_t frames) -{ - while (frames--) { - *dst++ = (src[0] + src[1]) * 0.5; - src += 2; - } -} - -void upmix_to_stereo_float_from_mono_float(float *dst, const float *src, size_t frames) -{ - while (frames--) { - float temp = *src++; - dst[0] = temp; - dst[1] = temp; - dst += 2; - } -} - -size_t nonZeroMono32(const int32_t *samples, size_t count) -{ - size_t nonZero = 0; - while (count-- > 0) { - if (*samples++ != 0) { - nonZero++; - } - } - return nonZero; -} - -size_t nonZeroMono16(const int16_t *samples, size_t count) -{ - size_t nonZero = 0; - while (count-- > 0) { - if (*samples++ != 0) { - nonZero++; - } - } - return nonZero; -} - -size_t nonZeroStereo32(const int32_t *frames, size_t count) -{ - size_t nonZero = 0; - while (count-- > 0) { - if (frames[0] != 0 || frames[1] != 0) { - nonZero++; - } - frames += 2; - } - return nonZero; -} - -size_t nonZeroStereo16(const int16_t *frames, size_t count) -{ - size_t nonZero = 0; - while (count-- > 0) { - if (frames[0] != 0 || frames[1] != 0) { - nonZero++; - } - frames += 2; - } - return nonZero; -} - -/* - * C macro to do channel mask copying independent of dst/src sample type. - * Don't pass in any expressions for the macro arguments here. - */ -#define copy_frame_by_mask(dst, dmask, src, smask, count, zero) \ -{ \ - uint32_t bit, ormask; \ - while ((count)--) { \ - ormask = (dmask) | (smask); \ - while (ormask) { \ - bit = ormask & -ormask; /* get lowest bit */ \ - ormask ^= bit; /* remove lowest bit */ \ - if ((dmask) & bit) { \ - *(dst)++ = (smask) & bit ? *(src)++ : (zero); \ - } else { /* source channel only */ \ - ++(src); \ - } \ - } \ - } \ -} - -void memcpy_by_channel_mask(void *dst, uint32_t dst_mask, - const void *src, uint32_t src_mask, size_t sample_size, size_t count) -{ -#if 0 - /* alternate way of handling memcpy_by_channel_mask by using the idxary */ - int8_t idxary[32]; - uint32_t src_channels = popcount(src_mask); - uint32_t dst_channels = - memcpy_by_index_array_initialization(idxary, 32, dst_mask, src_mask); - - memcpy_by_idxary(dst, dst_channels, src, src_channels, idxary, sample_size, count); -#else - if (dst_mask == src_mask) { - memcpy(dst, src, sample_size * popcount(dst_mask) * count); - return; - } - switch (sample_size) { - case 1: { - uint8_t *udst = (uint8_t*)dst; - const uint8_t *usrc = (const uint8_t*)src; - - copy_frame_by_mask(udst, dst_mask, usrc, src_mask, count, 0); - } break; - case 2: { - uint16_t *udst = (uint16_t*)dst; - const uint16_t *usrc = (const uint16_t*)src; - - copy_frame_by_mask(udst, dst_mask, usrc, src_mask, count, 0); - } break; - case 3: { /* could be slow. use a struct to represent 3 bytes of data. */ - uint8x3_t *udst = (uint8x3_t*)dst; - const uint8x3_t *usrc = (const uint8x3_t*)src; - static const uint8x3_t zero; /* tricky - we use this to zero out a sample */ - - copy_frame_by_mask(udst, dst_mask, usrc, src_mask, count, zero); - } break; - case 4: { - uint32_t *udst = (uint32_t*)dst; - const uint32_t *usrc = (const uint32_t*)src; - - copy_frame_by_mask(udst, dst_mask, usrc, src_mask, count, 0); - } break; - default: - abort(); /* illegal value */ - break; - } -#endif -} - -/* - * C macro to do copying by index array, to rearrange samples - * within a frame. This is independent of src/dst sample type. - * Don't pass in any expressions for the macro arguments here. - */ -#define copy_frame_by_idx(dst, dst_channels, src, src_channels, idxary, count, zero) \ -{ \ - unsigned i; \ - int index; \ - while ((count)--) { \ - for (i = 0; i < (dst_channels); ++i) { \ - index = (idxary)[i]; \ - *(dst)++ = index < 0 ? (zero) : (src)[index]; \ - } \ - (src) += (src_channels); \ - } \ -} - -void memcpy_by_index_array(void *dst, uint32_t dst_channels, - const void *src, uint32_t src_channels, - const int8_t *idxary, size_t sample_size, size_t count) -{ - switch (sample_size) { - case 1: { - uint8_t *udst = (uint8_t*)dst; - const uint8_t *usrc = (const uint8_t*)src; - - copy_frame_by_idx(udst, dst_channels, usrc, src_channels, idxary, count, 0); - } break; - case 2: { - uint16_t *udst = (uint16_t*)dst; - const uint16_t *usrc = (const uint16_t*)src; - - copy_frame_by_idx(udst, dst_channels, usrc, src_channels, idxary, count, 0); - } break; - case 3: { /* could be slow. use a struct to represent 3 bytes of data. */ - uint8x3_t *udst = (uint8x3_t*)dst; - const uint8x3_t *usrc = (const uint8x3_t*)src; - static const uint8x3_t zero; - - copy_frame_by_idx(udst, dst_channels, usrc, src_channels, idxary, count, zero); - } break; - case 4: { - uint32_t *udst = (uint32_t*)dst; - const uint32_t *usrc = (const uint32_t*)src; - - copy_frame_by_idx(udst, dst_channels, usrc, src_channels, idxary, count, 0); - } break; - default: - abort(); /* illegal value */ - break; - } -} - -size_t memcpy_by_index_array_initialization(int8_t *idxary, size_t idxcount, - uint32_t dst_mask, uint32_t src_mask) -{ - size_t n = 0; - int srcidx = 0; - uint32_t bit, ormask = src_mask | dst_mask; - - while (ormask && n < idxcount) { - bit = ormask & -ormask; /* get lowest bit */ - ormask ^= bit; /* remove lowest bit */ - if (src_mask & dst_mask & bit) { /* matching channel */ - idxary[n++] = srcidx++; - } else if (src_mask & bit) { /* source channel only */ - ++srcidx; - } else { /* destination channel only */ - idxary[n++] = -1; - } - } - return n + popcount(ormask & dst_mask); -} - -size_t memcpy_by_index_array_initialization_src_index(int8_t *idxary, size_t idxcount, - uint32_t dst_mask, uint32_t src_mask) { - size_t dst_count = popcount(dst_mask); - if (idxcount == 0) { - return dst_count; - } - if (dst_count > idxcount) { - dst_count = idxcount; - } - - size_t src_idx, dst_idx; - for (src_idx = 0, dst_idx = 0; dst_idx < dst_count; ++dst_idx) { - if (src_mask & 1) { - idxary[dst_idx] = src_idx++; - } else { - idxary[dst_idx] = -1; - } - src_mask >>= 1; - } - return dst_idx; -} - -size_t memcpy_by_index_array_initialization_dst_index(int8_t *idxary, size_t idxcount, - uint32_t dst_mask, uint32_t src_mask) { - size_t src_idx, dst_idx; - size_t dst_count = __builtin_popcount(dst_mask); - size_t src_count = __builtin_popcount(src_mask); - if (idxcount == 0) { - return dst_count; - } - if (dst_count > idxcount) { - dst_count = idxcount; - } - for (src_idx = 0, dst_idx = 0; dst_idx < dst_count; ++src_idx) { - if (dst_mask & 1) { - idxary[dst_idx++] = src_idx < src_count ? (signed)src_idx : -1; - } - dst_mask >>= 1; - } - return dst_idx; -} diff --git a/cocos/audio/android/audio_utils/private/private.h b/cocos/audio/android/audio_utils/private/private.h deleted file mode 100644 index d10d1eac0ff9..000000000000 --- a/cocos/audio/android/audio_utils/private/private.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_AUDIO_PRIVATE_H -#define ANDROID_AUDIO_PRIVATE_H - -#include - -__BEGIN_DECLS - -/* Defines not necessary for external use but kept here to be common - * to the audio_utils library. - */ - -/* struct representation of 3 bytes for packed PCM 24 bit data. - * The naming follows the ARM NEON convention. - */ -typedef struct {uint8_t c[3];} __attribute__((__packed__)) uint8x3_t; - -__END_DECLS - -#endif /*ANDROID_AUDIO_PRIVATE_H*/ diff --git a/cocos/audio/android/ccdandroidUtils.cpp b/cocos/audio/android/ccdandroidUtils.cpp index 1146c61c6b3b..1bcd0a2d8a9f 100644 --- a/cocos/audio/android/ccdandroidUtils.cpp +++ b/cocos/audio/android/ccdandroidUtils.cpp @@ -1,7 +1,6 @@ /**************************************************************************** Copyright (c) 2010-2012 cocos2d-x.org -Copyright (c) 2013-2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. +Copyright (c) 2013-2017 Chukong Technologies Inc. http://www.cocos2d-x.org diff --git a/cocos/audio/android/ccdandroidUtils.h b/cocos/audio/android/ccdandroidUtils.h index 1edab8d4a715..1424f2e44908 100644 --- a/cocos/audio/android/ccdandroidUtils.h +++ b/cocos/audio/android/ccdandroidUtils.h @@ -1,7 +1,6 @@ /**************************************************************************** Copyright (c) 2010-2012 cocos2d-x.org -Copyright (c) 2013-2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. +Copyright (c) 2013-2017 Chukong Technologies Inc. http://www.cocos2d-x.org diff --git a/cocos/audio/android/cddSimpleAudioEngine.cpp b/cocos/audio/android/cddSimpleAudioEngine.cpp index c7c6b626ca3c..d4cbb12bb942 100644 --- a/cocos/audio/android/cddSimpleAudioEngine.cpp +++ b/cocos/audio/android/cddSimpleAudioEngine.cpp @@ -1,7 +1,6 @@ /**************************************************************************** Copyright (c) 2010-2012 cocos2d-x.org -Copyright (c) 2013-2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. +Copyright (c) 2013-2017 Chukong Technologies Inc. http://www.cocos2d-x.org diff --git a/cocos/audio/android/cutils/bitops.h b/cocos/audio/android/cutils/bitops.h deleted file mode 100644 index 33168b4a2bfa..000000000000 --- a/cocos/audio/android/cutils/bitops.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef COCOS_CUTILS_BITOPS_H -#define COCOS_CUTILS_BITOPS_H - -#include -#include -#include -#include - -__BEGIN_DECLS - -static inline int popcount(unsigned int x) -{ - return __builtin_popcount(x); -} - -static inline int popcountl(unsigned long x) -{ - return __builtin_popcountl(x); -} - -static inline int popcountll(unsigned long long x) -{ - return __builtin_popcountll(x); -} - -__END_DECLS - -#endif /* COCOS_CUTILS_BITOPS_H */ diff --git a/cocos/audio/android/jni/cddandroidAndroidJavaEngine.cpp b/cocos/audio/android/jni/cddandroidAndroidJavaEngine.cpp index 8ddf48919f04..4fbd5b145983 100644 --- a/cocos/audio/android/jni/cddandroidAndroidJavaEngine.cpp +++ b/cocos/audio/android/jni/cddandroidAndroidJavaEngine.cpp @@ -1,7 +1,6 @@ /**************************************************************************** Copyright (c) 2010-2012 cocos2d-x.org -Copyright (c) 2013-2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. +Copyright (c) 2013-2017 Chukong Technologies Inc. http://www.cocos2d-x.org diff --git a/cocos/audio/android/jni/cddandroidAndroidJavaEngine.h b/cocos/audio/android/jni/cddandroidAndroidJavaEngine.h index 120ba2c378e0..76869d6edcfa 100644 --- a/cocos/audio/android/jni/cddandroidAndroidJavaEngine.h +++ b/cocos/audio/android/jni/cddandroidAndroidJavaEngine.h @@ -1,7 +1,6 @@ /**************************************************************************** Copyright (c) 2010-2012 cocos2d-x.org -Copyright (c) 2013-2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. +Copyright (c) 2013-2017 Chukong Technologies Inc. http://www.cocos2d-x.org @@ -23,8 +22,8 @@ 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 __CDDANDROIDANDROIDJAVAENGINE_H__ -#define __CDDANDROIDANDROIDJAVAENGINE_H__ +#ifndef __CDDANDRIODANDROIDJAVAENGINE_H__ +#define __CDDANDRIODANDROIDJAVAENGINE_H__ #include "audio/include/SimpleAudioEngine.h" #include "platform/android/jni/JniHelper.h" @@ -37,131 +36,27 @@ namespace CocosDenshion { AndroidJavaEngine(); ~AndroidJavaEngine(); - /* - * @brief Preload background music. - * @param filePath The path of the background music file. - */ virtual void preloadBackgroundMusic(const char* filePath); - - /* - * @brief Play background music. - * @param filePath The path of the background music file,or the FileName of T_SoundResInfo. - * @param loop Whether the background music loop or not. - */ virtual void playBackgroundMusic(const char* filePath, bool loop); - - /* - * @brief Stop playing background music. - * @param releaseData If release the background music data or not.As default value is false. - */ virtual void stopBackgroundMusic(bool releaseData); - - /* - * @brief Pause playing background music. - */ virtual void pauseBackgroundMusic(); - - /* - * @brief Resume playing background music. - */ virtual void resumeBackgroundMusic(); - - /* - * @brief Rewind playing background music - */ virtual void rewindBackgroundMusic(); - - /* - * @brief Indicates whether any background music can be played or not. - * @return true if background music can be played; otherwise false. - */ virtual bool willPlayBackgroundMusic(); - - /* - * @brief Indicates whether the background music is playing. - * @return true if the background music is playing; otherwise false. - */ virtual bool isBackgroundMusicPlaying(); - - /* - * @brief Get the volume of the background music. - * @return the range of 0.0 as the minimum and 1.0 as the maximum. - */ virtual float getBackgroundMusicVolume(); - - /* - * @brief Set the volume of the background music. - * @param volume must be range of 0.0 as the minimum and 1.0 as the maximum. - */ virtual void setBackgroundMusicVolume(float volume); - - /* - * @brief Get the volume of the effects. - * @return the range of 0.0 as the minimum and 1.0 as the maximum. - */ virtual float getEffectsVolume(); - - /* - * @brief Set the volume of sound effects. - * @param volume must be range of 0.0 as the minimum and 1.0 as the maximum. - */ virtual void setEffectsVolume(float volume); - - /* - * @brief Play sound effect with a file path, pitch, pan, and gain. - * @param filePath The path of the effect file. - * @param loop Determines whether to loop the effect playing or not. The default value is false. - * @param pitch Fequency, normal value is 1.0. will also change effect play time. - * @param pan Stereo effect, in the range of [-1..1] where -1 enables only left channel. - * @param gain Volume, in the range of [0..1]. The normal value is 1. - * @return The sound id. - */ virtual unsigned int playEffect(const char* filePath, bool loop = false, float pitch = 1.0f, float pan = 0.0f, float gain = 1.0f); - - /* - * @brief Pause playing sound effect. - * @param soundId The return value of function playEffect. - */ virtual void pauseEffect(unsigned int soundId); - - /* - * @brief Pause all playing sound effect. - */ virtual void pauseAllEffects(); - - /* - * @brief Resume playing sound effect. - * @param soundId The return value of function playEffect. - */ virtual void resumeEffect(unsigned int soundId); - - /* - * @brief Resume all playing sound effects. - */ virtual void resumeAllEffects(); - - /* - * @brief Stop playing sound effect. - * @param soundId The return value of function playEffect. - */ virtual void stopEffect(unsigned int soundId); - - /* - * @brief Stop all playing sound effects. - */ virtual void stopAllEffects(); - - /* - * @brief Preload a compressed audio file. - * @param filePath The path of the effect file. - */ virtual void preloadEffect(const char* filePath); - - /* - * @brief Unload the preloaded effect from internal buffer. - * @param filePath The path of the effect file. - */ virtual void unloadEffect(const char* filePath); private : @@ -172,4 +67,4 @@ namespace CocosDenshion { } } -#endif //__CDDANDROIDANDROIDJAVAENGINE_H__ +#endif //__CDDANDRIODANDROIDJAVAENGINE_H__ diff --git a/cocos/audio/android/cutils/log.h b/cocos/audio/android/log.h similarity index 99% rename from cocos/audio/android/cutils/log.h rename to cocos/audio/android/log.h index 3e505b0f0f89..985276c25a0e 100644 --- a/cocos/audio/android/cutils/log.h +++ b/cocos/audio/android/log.h @@ -24,9 +24,8 @@ // threads and/or multiple processes so long as the operating system // supports O_APPEND. These calls have mutex-protected data structures // and so are NOT reentrant. Do not use LOG in a signal handler. -// -#ifndef COCOS_CUTILS_LOG_H -#define COCOS_CUTILS_LOG_H +// +#pragma once #include #include @@ -565,4 +564,3 @@ int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fm } #endif -#endif /* COCOS_CUTILS_LOG_H */ diff --git a/cocos/audio/android/mp3reader.cpp b/cocos/audio/android/mp3reader.cpp deleted file mode 100644 index 15a5ef9165af..000000000000 --- a/cocos/audio/android/mp3reader.cpp +++ /dev/null @@ -1,542 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#define LOG_TAG "mp3reader" - -#include -#include -#include -#include -#include -#include "audio/android/cutils/log.h" - -#include "pvmp3decoder_api.h" -#include "audio/android/mp3reader.h" - -using namespace std; - -static uint32_t U32_AT(const uint8_t *ptr) { - return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; -} - -static bool parseHeader( - uint32_t header, size_t *frame_size, - uint32_t *out_sampling_rate = NULL, uint32_t *out_channels = NULL , - uint32_t *out_bitrate = NULL, uint32_t *out_num_samples = NULL) { - *frame_size = 0; - - if (out_sampling_rate) { - *out_sampling_rate = 0; - } - - if (out_channels) { - *out_channels = 0; - } - - if (out_bitrate) { - *out_bitrate = 0; - } - - if (out_num_samples) { - *out_num_samples = 1152; - } - - if ((header & 0xffe00000) != 0xffe00000) { - return false; - } - - unsigned version = (header >> 19) & 3; - - if (version == 0x01) { - return false; - } - - unsigned layer = (header >> 17) & 3; - - if (layer == 0x00) { - return false; - } - - unsigned bitrate_index = (header >> 12) & 0x0f; - - if (bitrate_index == 0 || bitrate_index == 0x0f) { - // Disallow "free" bitrate. - return false; - } - - unsigned sampling_rate_index = (header >> 10) & 3; - - if (sampling_rate_index == 3) { - return false; - } - - static const int kSamplingRateV1[] = { 44100, 48000, 32000 }; - int sampling_rate = kSamplingRateV1[sampling_rate_index]; - if (version == 2 /* V2 */) { - sampling_rate /= 2; - } else if (version == 0 /* V2.5 */) { - sampling_rate /= 4; - } - - unsigned padding = (header >> 9) & 1; - - if (layer == 3) { - // layer I - - static const int kBitrateV1[] = { - 32, 64, 96, 128, 160, 192, 224, 256, - 288, 320, 352, 384, 416, 448 - }; - - static const int kBitrateV2[] = { - 32, 48, 56, 64, 80, 96, 112, 128, - 144, 160, 176, 192, 224, 256 - }; - - int bitrate = - (version == 3 /* V1 */) - ? kBitrateV1[bitrate_index - 1] - : kBitrateV2[bitrate_index - 1]; - - if (out_bitrate) { - *out_bitrate = bitrate; - } - - *frame_size = (12000 * bitrate / sampling_rate + padding) * 4; - - if (out_num_samples) { - *out_num_samples = 384; - } - } else { - // layer II or III - - static const int kBitrateV1L2[] = { - 32, 48, 56, 64, 80, 96, 112, 128, - 160, 192, 224, 256, 320, 384 - }; - - static const int kBitrateV1L3[] = { - 32, 40, 48, 56, 64, 80, 96, 112, - 128, 160, 192, 224, 256, 320 - }; - - static const int kBitrateV2[] = { - 8, 16, 24, 32, 40, 48, 56, 64, - 80, 96, 112, 128, 144, 160 - }; - - int bitrate; - if (version == 3 /* V1 */) { - bitrate = (layer == 2 /* L2 */) - ? kBitrateV1L2[bitrate_index - 1] - : kBitrateV1L3[bitrate_index - 1]; - - if (out_num_samples) { - *out_num_samples = 1152; - } - } else { - // V2 (or 2.5) - - bitrate = kBitrateV2[bitrate_index - 1]; - if (out_num_samples) { - *out_num_samples = (layer == 1 /* L3 */) ? 576 : 1152; - } - } - - if (out_bitrate) { - *out_bitrate = bitrate; - } - - if (version == 3 /* V1 */) { - *frame_size = 144000 * bitrate / sampling_rate + padding; - } else { - // V2 or V2.5 - size_t tmp = (layer == 1 /* L3 */) ? 72000 : 144000; - *frame_size = tmp * bitrate / sampling_rate + padding; - } - } - - if (out_sampling_rate) { - *out_sampling_rate = sampling_rate; - } - - if (out_channels) { - int channel_mode = (header >> 6) & 3; - - *out_channels = (channel_mode == 3) ? 1 : 2; - } - - return true; -} - -// Mask to extract the version, layer, sampling rate parts of the MP3 header, -// which should be same for all MP3 frames. -static const uint32_t kMask = 0xfffe0c00; - -static ssize_t sourceReadAt(mp3_callbacks *callback, void* source, off64_t offset, void *data, size_t size) { - int retVal = callback->seek(source, offset, SEEK_SET); - if (retVal != EXIT_SUCCESS) { - return 0; - } else { - return callback->read(data, 1, size, source); - } -} - -// Resync to next valid MP3 frame in the file. -static bool resync( - mp3_callbacks *callback, void* source, uint32_t match_header, - off64_t *inout_pos, uint32_t *out_header) { - - if (*inout_pos == 0) { - // Skip an optional ID3 header if syncing at the very beginning - // of the datasource. - - for (;;) { - uint8_t id3header[10]; - int retVal = sourceReadAt(callback, source, *inout_pos, id3header, - sizeof(id3header)); - if (retVal < (ssize_t)sizeof(id3header)) { - // If we can't even read these 10 bytes, we might as well bail - // out, even if there _were_ 10 bytes of valid mp3 audio data... - return false; - } - - if (memcmp("ID3", id3header, 3)) { - break; - } - - // Skip the ID3v2 header. - - size_t len = - ((id3header[6] & 0x7f) << 21) - | ((id3header[7] & 0x7f) << 14) - | ((id3header[8] & 0x7f) << 7) - | (id3header[9] & 0x7f); - - len += 10; - - *inout_pos += len; - - ALOGV("skipped ID3 tag, new starting offset is %lld (0x%016llx)", - (long long)*inout_pos, (long long)*inout_pos); - } - - } - - off64_t pos = *inout_pos; - bool valid = false; - - const int32_t kMaxReadBytes = 1024; - const int32_t kMaxBytesChecked = 128 * 1024; - uint8_t buf[kMaxReadBytes]; - ssize_t bytesToRead = kMaxReadBytes; - ssize_t totalBytesRead = 0; - ssize_t remainingBytes = 0; - bool reachEOS = false; - uint8_t *tmp = buf; - - do { - if (pos >= (off64_t)(*inout_pos + kMaxBytesChecked)) { - // Don't scan forever. - ALOGV("giving up at offset %lld", (long long)pos); - break; - } - - if (remainingBytes < 4) { - if (reachEOS) { - break; - } else { - memcpy(buf, tmp, remainingBytes); - bytesToRead = kMaxReadBytes - remainingBytes; - - /* - * The next read position should start from the end of - * the last buffer, and thus should include the remaining - * bytes in the buffer. - */ - totalBytesRead = sourceReadAt(callback, source, pos + remainingBytes, - buf + remainingBytes, bytesToRead); - - if (totalBytesRead <= 0) { - break; - } - reachEOS = (totalBytesRead != bytesToRead); - remainingBytes += totalBytesRead; - tmp = buf; - continue; - } - } - - uint32_t header = U32_AT(tmp); - - if (match_header != 0 && (header & kMask) != (match_header & kMask)) { - ++pos; - ++tmp; - --remainingBytes; - continue; - } - - size_t frame_size; - uint32_t sample_rate, num_channels, bitrate; - if (!parseHeader( - header, &frame_size, - &sample_rate, &num_channels, &bitrate)) { - ++pos; - ++tmp; - --remainingBytes; - continue; - } - - // ALOGV("found possible 1st frame at %lld (header = 0x%08x)", (long long)pos, header); - // We found what looks like a valid frame, - // now find its successors. - - off64_t test_pos = pos + frame_size; - - valid = true; - const int FRAME_MATCH_REQUIRED = 3; - for (int j = 0; j < FRAME_MATCH_REQUIRED; ++j) { - uint8_t tmp[4]; - ssize_t retval = sourceReadAt(callback, source, test_pos, tmp, sizeof(tmp)); - if (retval < (ssize_t)sizeof(tmp)) { - valid = false; - break; - } - - uint32_t test_header = U32_AT(tmp); - - ALOGV("subsequent header is %08x", test_header); - - if ((test_header & kMask) != (header & kMask)) { - valid = false; - break; - } - - size_t test_frame_size; - if (!parseHeader(test_header, &test_frame_size)) { - valid = false; - break; - } - - ALOGV("found subsequent frame #%d at %lld", j + 2, (long long)test_pos); - test_pos += test_frame_size; - } - - if (valid) { - *inout_pos = pos; - - if (out_header != NULL) { - *out_header = header; - } - } else { - ALOGV("no dice, no valid sequence of frames found."); - } - - ++pos; - ++tmp; - --remainingBytes; - } while (!valid); - - return valid; -} - -Mp3Reader::Mp3Reader() : mSource(NULL), mCallback(NULL) { -} - -// Initialize the MP3 reader. -bool Mp3Reader::init(mp3_callbacks *callback, void* source) { - - mSource = source; - mCallback = callback; - // Open the file. - // mFp = fopen(file, "rb"); - // if (mFp == NULL) return false; - - // Sync to the first valid frame. - off64_t pos = 0; - uint32_t header; - bool success = resync(callback, source, 0 /*match_header*/, &pos, &header); - if (!success) - { - ALOGE("%s, resync failed", __FUNCTION__); - return false; - } - - mCurrentPos = pos; - mFixedHeader = header; - - size_t frame_size; - return parseHeader(header, &frame_size, &mSampleRate, - &mNumChannels, &mBitrate); -} - -// Get the next valid MP3 frame. -bool Mp3Reader::getFrame(void *buffer, uint32_t *size) { - - size_t frame_size; - uint32_t bitrate; - uint32_t num_samples; - uint32_t sample_rate; - for (;;) { - ssize_t n = sourceReadAt(mCallback, mSource, mCurrentPos, buffer, 4); - if (n < 4) { - return false; - } - - uint32_t header = U32_AT((const uint8_t *)buffer); - - if ((header & kMask) == (mFixedHeader & kMask) - && parseHeader( - header, &frame_size, &sample_rate, NULL /*out_channels*/, - &bitrate, &num_samples)) { - break; - } - - // Lost sync. - off64_t pos = mCurrentPos; - if (!resync(mCallback, mSource, mFixedHeader, &pos, NULL /*out_header*/)) { - // Unable to resync. Signalling end of stream. - return false; - } - - mCurrentPos = pos; - - // Try again with the new position. - } - ssize_t n = sourceReadAt(mCallback, mSource, mCurrentPos, buffer, frame_size); - if (n < (ssize_t)frame_size) { - return false; - } - - *size = frame_size; - mCurrentPos += frame_size; - return true; -} - -// Close the MP3 reader. -void Mp3Reader::close() { - assert(mCallback != NULL); - mCallback->close(mSource); -} - -Mp3Reader::~Mp3Reader() { -} - -enum { - kInputBufferSize = 10 * 1024, - kOutputBufferSize = 4608 * 2, -}; - -int decodeMP3(mp3_callbacks* cb, void* source, std::vector& pcmBuffer, int* numChannels, int* sampleRate, int* numFrames) -{ - // Initialize the config. - tPVMP3DecoderExternal config; - config.equalizerType = flat; - config.crcEnabled = false; - - // Allocate the decoder memory. - uint32_t memRequirements = pvmp3_decoderMemRequirements(); - void *decoderBuf = malloc(memRequirements); - assert(decoderBuf != NULL); - - // Initialize the decoder. - pvmp3_InitDecoder(&config, decoderBuf); - - // Open the input file. - Mp3Reader mp3Reader; - bool success = mp3Reader.init(cb, source); - if (!success) { - ALOGE("mp3Reader.init: Encountered error reading\n"); - free(decoderBuf); - return EXIT_FAILURE; - } - - // Open the output file. - // SF_INFO sfInfo; - // memset(&sfInfo, 0, sizeof(SF_INFO)); - // sfInfo.channels = mp3Reader.getNumChannels(); - // sfInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; - // sfInfo.samplerate = mp3Reader.getSampleRate(); - // SNDFILE *handle = sf_open(argv[2], SFM_WRITE, &sfInfo); - // if (handle == NULL) { - // ALOGE("Encountered error writing %s\n", argv[2]); - // mp3Reader.close(); - // free(decoderBuf); - // return EXIT_FAILURE; - // } - - // Allocate input buffer. - uint8_t *inputBuf = static_cast(malloc(kInputBufferSize)); - assert(inputBuf != NULL); - - // Allocate output buffer. - int16_t *outputBuf = static_cast(malloc(kOutputBufferSize)); - assert(outputBuf != NULL); - - // Decode loop. - int retVal = EXIT_SUCCESS; - while (1) { - // Read input from the file. - uint32_t bytesRead; - bool success = mp3Reader.getFrame(inputBuf, &bytesRead); - if (!success) break; - - *numChannels = mp3Reader.getNumChannels(); - *sampleRate = mp3Reader.getSampleRate(); - - // Set the input config. - config.inputBufferCurrentLength = bytesRead; - config.inputBufferMaxLength = 0; - config.inputBufferUsedLength = 0; - config.pInputBuffer = inputBuf; - config.pOutputBuffer = outputBuf; - config.outputFrameSize = kOutputBufferSize / sizeof(int16_t); - - ERROR_CODE decoderErr; - decoderErr = pvmp3_framedecoder(&config, decoderBuf); - if (decoderErr != NO_DECODING_ERROR) { - ALOGE("Decoder encountered error=%d", decoderErr); - retVal = EXIT_FAILURE; - break; - } - - pcmBuffer.insert(pcmBuffer.end(), (char*)outputBuf, ((char*)outputBuf) + config.outputFrameSize * 2); - *numFrames += config.outputFrameSize / mp3Reader.getNumChannels(); - } - - // Close input reader and output writer. - mp3Reader.close(); - // sf_close(handle); - - // Free allocated memory. - free(inputBuf); - free(outputBuf); - free(decoderBuf); - - return retVal; -} diff --git a/cocos/audio/android/mp3reader.h b/cocos/audio/android/mp3reader.h deleted file mode 100644 index 7c9e0a7a5978..000000000000 --- a/cocos/audio/android/mp3reader.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MP3READER_H_ -#define MP3READER_H_ - -typedef struct { - size_t (*read) (void *ptr, size_t size, size_t nmemb, void *datasource); - int (*seek) (void *datasource, int64_t offset, int whence); - int (*close) (void *datasource); - long (*tell) (void *datasource); -} mp3_callbacks; - -class Mp3Reader { -public: - Mp3Reader(); - bool init(mp3_callbacks *callback, void* source); - bool getFrame(void *buffer, uint32_t *size); - uint32_t getSampleRate() { return mSampleRate;} - uint32_t getNumChannels() { return mNumChannels;} - void close(); - ~Mp3Reader(); -private: - void *mSource; - mp3_callbacks* mCallback; - uint32_t mFixedHeader; - off64_t mCurrentPos; - uint32_t mSampleRate; - uint32_t mNumChannels; - uint32_t mBitrate; -}; - -int decodeMP3(mp3_callbacks* cb, void* source, std::vector& pcmBuffer, int* numChannels, int* sampleRate, int* numFrames); - - -#endif /* MP3READER_H_ */ diff --git a/cocos/audio/android/tinysndfile.cpp b/cocos/audio/android/tinysndfile.cpp deleted file mode 100644 index a7b66300f09e..000000000000 --- a/cocos/audio/android/tinysndfile.cpp +++ /dev/null @@ -1,518 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "tinysndfile" - -#include "audio/android/tinysndfile.h" -#include "audio/android/audio_utils/include/audio_utils/primitives.h" -#include "audio/android/cutils/log.h" - -// #ifdef HAVE_STDERR -// #include -// #endif - -#include -#include - -#ifndef HAVE_STDERR -#define HAVE_STDERR -#endif - -#define WAVE_FORMAT_PCM 1 -#define WAVE_FORMAT_IEEE_FLOAT 3 -#define WAVE_FORMAT_EXTENSIBLE 0xFFFE - -static snd_callbacks __defaultCallback; -static int __inited = 0; - -struct SNDFILE_ { - uint8_t *temp; // realloc buffer used for shrinking 16 bits to 8 bits and byte-swapping - void *stream; - size_t bytesPerFrame; - size_t remaining; // frames unread for SFM_READ, frames written for SFM_WRITE - SF_INFO info; - snd_callbacks callback; -}; - -static unsigned little2u(unsigned char *ptr) -{ - return (ptr[1] << 8) + ptr[0]; -} - -static unsigned little4u(unsigned char *ptr) -{ - return (ptr[3] << 24) + (ptr[2] << 16) + (ptr[1] << 8) + ptr[0]; -} - -static int isLittleEndian(void) -{ - static const short one = 1; - return *((const char *) &one) == 1; -} - -// "swab" conflicts with OS X -static void my_swab(short *ptr, size_t numToSwap) -{ - while (numToSwap > 0) { - *ptr = little2u((unsigned char *) ptr); - --numToSwap; - ++ptr; - } -} - -static void* open_func(const char* path, void* user) -{ - return fopen(path, "rb"); -} - -static size_t read_func(void *ptr, size_t size, size_t nmemb, void* datasource) -{ - return fread(ptr, size, nmemb, (FILE*)datasource); -} - -static int seek_func(void* datasource, long offset, int whence) -{ - return fseek((FILE*)datasource, offset, whence); -} - -static int close_func(void* datasource) -{ - return fclose((FILE*)datasource); -} - -static long tell_func(void* datasource) -{ - return ftell((FILE*)datasource); -} - -static void lazyInit() -{ - if (__inited == 0) - { - __defaultCallback.open = open_func; - __defaultCallback.read = read_func; - __defaultCallback.seek = seek_func; - __defaultCallback.close = close_func; - __defaultCallback.tell = tell_func; - __inited = 1; - } -} - -SNDFILE *sf_open_read(const char *path, SF_INFO *info, snd_callbacks* cb, void* user) -{ - lazyInit(); - - if (path == NULL || info == NULL) { -#ifdef HAVE_STDERR - ALOGE("path=%p info=%p\n", path, info); -#endif - return NULL; - } - - SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE)); - handle->temp = NULL; - - handle->info.format = SF_FORMAT_WAV; - if (cb != NULL) { - handle->callback = *cb; - } else { - handle->callback = __defaultCallback; - } - - void* stream = handle->callback.open(path, user); - if (stream == NULL) { -#ifdef HAVE_STDERR - ALOGE("fopen %s failed errno %d\n", path, errno); -#endif - free(handle); - return NULL; - } - handle->stream = stream; - - // don't attempt to parse all valid forms, just the most common ones - unsigned char wav[12]; - size_t actual; - unsigned riffSize; - size_t remaining; - int hadFmt = 0; - int hadData = 0; - long dataTell = 0L; - - actual = handle->callback.read(wav, sizeof(char), sizeof(wav), stream); - if (actual < 12) { -#ifdef HAVE_STDERR - ALOGE("actual %zu < 44\n", actual); -#endif - goto close; - } - if (memcmp(wav, "RIFF", 4)) { -#ifdef HAVE_STDERR - ALOGE("wav != RIFF\n"); -#endif - goto close; - } - riffSize = little4u(&wav[4]); - if (riffSize < 4) { -#ifdef HAVE_STDERR - ALOGE("riffSize %u < 4\n", riffSize); -#endif - goto close; - } - if (memcmp(&wav[8], "WAVE", 4)) { -#ifdef HAVE_STDERR - ALOGE("missing WAVE\n"); -#endif - goto close; - } - remaining = riffSize - 4; - - while (remaining >= 8) { - unsigned char chunk[8]; - actual = handle->callback.read(chunk, sizeof(char), sizeof(chunk), stream); - if (actual != sizeof(chunk)) { -#ifdef HAVE_STDERR - ALOGE("actual %zu != %zu\n", actual, sizeof(chunk)); -#endif - goto close; - } - remaining -= 8; - unsigned chunkSize = little4u(&chunk[4]); - if (chunkSize > remaining) { -#ifdef HAVE_STDERR - ALOGE("chunkSize %u > remaining %zu\n", chunkSize, remaining); -#endif - goto close; - } - if (!memcmp(&chunk[0], "fmt ", 4)) { - if (hadFmt) { -#ifdef HAVE_STDERR - ALOGE("multiple fmt\n"); -#endif - goto close; - } - if (chunkSize < 2) { -#ifdef HAVE_STDERR - ALOGE("chunkSize %u < 2\n", chunkSize); -#endif - goto close; - } - unsigned char fmt[40]; - actual = handle->callback.read(fmt, sizeof(char), 2, stream); - if (actual != 2) { -#ifdef HAVE_STDERR - ALOGE("actual %zu != 2\n", actual); -#endif - goto close; - } - unsigned format = little2u(&fmt[0]); - size_t minSize = 0; - switch (format) { - case WAVE_FORMAT_PCM: - case WAVE_FORMAT_IEEE_FLOAT: - minSize = 16; - break; - case WAVE_FORMAT_EXTENSIBLE: - minSize = 40; - break; - default: -#ifdef HAVE_STDERR - ALOGE("unsupported format %u\n", format); -#endif - goto close; - } - if (chunkSize < minSize) { -#ifdef HAVE_STDERR - ALOGE("chunkSize %u < minSize %zu\n", chunkSize, minSize); -#endif - goto close; - } - actual = handle->callback.read(&fmt[2], sizeof(char), minSize - 2, stream); - if (actual != minSize - 2) { -#ifdef HAVE_STDERR - ALOGE("actual %zu != %zu\n", actual, minSize - 16); -#endif - goto close; - } - if (chunkSize > minSize) { - handle->callback.seek(stream, (long) (chunkSize - minSize), SEEK_CUR); - } - unsigned channels = little2u(&fmt[2]); - // FIXME FCC_8 - if (channels != 1 && channels != 2 && channels != 4 && channels != 6 && channels != 8) { -#ifdef HAVE_STDERR - ALOGE("unsupported channels %u\n", channels); -#endif - goto close; - } - unsigned samplerate = little4u(&fmt[4]); - if (samplerate == 0) { -#ifdef HAVE_STDERR - ALOGE("samplerate %u == 0\n", samplerate); -#endif - goto close; - } - // ignore byte rate - // ignore block alignment - unsigned bitsPerSample = little2u(&fmt[14]); - if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && - bitsPerSample != 32) { -#ifdef HAVE_STDERR - ALOGE("bitsPerSample %u != 8 or 16 or 24 or 32\n", bitsPerSample); -#endif - goto close; - } - unsigned bytesPerFrame = (bitsPerSample >> 3) * channels; - handle->bytesPerFrame = bytesPerFrame; - handle->info.samplerate = samplerate; - handle->info.channels = channels; - switch (bitsPerSample) { - case 8: - handle->info.format |= SF_FORMAT_PCM_U8; - break; - case 16: - handle->info.format |= SF_FORMAT_PCM_16; - break; - case 24: - handle->info.format |= SF_FORMAT_PCM_24; - break; - case 32: - if (format == WAVE_FORMAT_IEEE_FLOAT) - handle->info.format |= SF_FORMAT_FLOAT; - else - handle->info.format |= SF_FORMAT_PCM_32; - break; - } - hadFmt = 1; - } else if (!memcmp(&chunk[0], "data", 4)) { - if (!hadFmt) { -#ifdef HAVE_STDERR - ALOGE("data not preceded by fmt\n"); -#endif - goto close; - } - if (hadData) { -#ifdef HAVE_STDERR - ALOGE("multiple data\n"); -#endif - goto close; - } - handle->remaining = chunkSize / handle->bytesPerFrame; - handle->info.frames = handle->remaining; - dataTell = handle->callback.tell(stream); - if (chunkSize > 0) { - handle->callback.seek(stream, (long) chunkSize, SEEK_CUR); - } - hadData = 1; - } else if (!memcmp(&chunk[0], "fact", 4)) { - // ignore fact - if (chunkSize > 0) { - handle->callback.seek(stream, (long) chunkSize, SEEK_CUR); - } - } else { - // ignore unknown chunk -#ifdef HAVE_STDERR - ALOGE("ignoring unknown chunk %c%c%c%c\n", - chunk[0], chunk[1], chunk[2], chunk[3]); -#endif - if (chunkSize > 0) { - handle->callback.seek(stream, (long) chunkSize, SEEK_CUR); - } - } - remaining -= chunkSize; - } - if (remaining > 0) { -#ifdef HAVE_STDERR - ALOGE("partial chunk at end of RIFF, remaining %zu\n", remaining); -#endif - goto close; - } - if (!hadData) { -#ifdef HAVE_STDERR - ALOGE("missing data\n"); -#endif - goto close; - } - (void) handle->callback.seek(stream, dataTell, SEEK_SET); - *info = handle->info; - return handle; - -close: - free(handle); - handle->callback.close(stream); - return NULL; -} - -void sf_close(SNDFILE *handle) -{ - if (handle == NULL) - return; - free(handle->temp); - (void) handle->callback.close(handle->stream); - free(handle); -} - -sf_count_t sf_readf_short(SNDFILE *handle, short *ptr, sf_count_t desiredFrames) -{ - if (handle == NULL || ptr == NULL || !handle->remaining || - desiredFrames <= 0) { - return 0; - } - if (handle->remaining < (size_t) desiredFrames) { - desiredFrames = handle->remaining; - } - // does not check for numeric overflow - size_t desiredBytes = desiredFrames * handle->bytesPerFrame; - size_t actualBytes; - void *temp = NULL; - unsigned format = handle->info.format & SF_FORMAT_SUBMASK; - if (format == SF_FORMAT_PCM_32 || format == SF_FORMAT_FLOAT || format == SF_FORMAT_PCM_24) { - temp = malloc(desiredBytes); - actualBytes = handle->callback.read(temp, sizeof(char), desiredBytes, handle->stream); - } else { - actualBytes = handle->callback.read(ptr, sizeof(char), desiredBytes, handle->stream); - } - size_t actualFrames = actualBytes / handle->bytesPerFrame; - handle->remaining -= actualFrames; - switch (format) { - case SF_FORMAT_PCM_U8: - memcpy_to_i16_from_u8(ptr, (unsigned char *) ptr, actualFrames * handle->info.channels); - break; - case SF_FORMAT_PCM_16: - if (!isLittleEndian()) - my_swab(ptr, actualFrames * handle->info.channels); - break; - case SF_FORMAT_PCM_32: - memcpy_to_i16_from_i32(ptr, (const int *) temp, actualFrames * handle->info.channels); - free(temp); - break; - case SF_FORMAT_FLOAT: - memcpy_to_i16_from_float(ptr, (const float *) temp, actualFrames * handle->info.channels); - free(temp); - break; - case SF_FORMAT_PCM_24: - memcpy_to_i16_from_p24(ptr, (const uint8_t *) temp, actualFrames * handle->info.channels); - free(temp); - break; - default: - memset(ptr, 0, actualFrames * handle->info.channels * sizeof(short)); - break; - } - return actualFrames; -} - -/* -sf_count_t sf_readf_float(SNDFILE *handle, float *ptr, sf_count_t desiredFrames) -{ - if (handle == NULL || ptr == NULL || !handle->remaining || - desiredFrames <= 0) { - return 0; - } - if (handle->remaining < (size_t) desiredFrames) { - desiredFrames = handle->remaining; - } - // does not check for numeric overflow - size_t desiredBytes = desiredFrames * handle->bytesPerFrame; - size_t actualBytes; - void *temp = NULL; - unsigned format = handle->info.format & SF_FORMAT_SUBMASK; - if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8 || format == SF_FORMAT_PCM_24) { - temp = malloc(desiredBytes); - actualBytes = handle->callback.read(temp, sizeof(char), desiredBytes, handle->stream); - } else { - actualBytes = handle->callback.read(ptr, sizeof(char), desiredBytes, handle->stream); - } - size_t actualFrames = actualBytes / handle->bytesPerFrame; - handle->remaining -= actualFrames; - switch (format) { - case SF_FORMAT_PCM_U8: -#if 0 - // TODO - implement - memcpy_to_float_from_u8(ptr, (const unsigned char *) temp, - actualFrames * handle->info.channels); -#endif - free(temp); - break; - case SF_FORMAT_PCM_16: - memcpy_to_float_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels); - free(temp); - break; - case SF_FORMAT_PCM_32: - memcpy_to_float_from_i32(ptr, (const int *) ptr, actualFrames * handle->info.channels); - break; - case SF_FORMAT_FLOAT: - break; - case SF_FORMAT_PCM_24: - memcpy_to_float_from_p24(ptr, (const uint8_t *) temp, actualFrames * handle->info.channels); - free(temp); - break; - default: - memset(ptr, 0, actualFrames * handle->info.channels * sizeof(float)); - break; - } - return actualFrames; -} - -sf_count_t sf_readf_int(SNDFILE *handle, int *ptr, sf_count_t desiredFrames) -{ - if (handle == NULL || ptr == NULL || !handle->remaining || - desiredFrames <= 0) { - return 0; - } - if (handle->remaining < (size_t) desiredFrames) { - desiredFrames = handle->remaining; - } - // does not check for numeric overflow - size_t desiredBytes = desiredFrames * handle->bytesPerFrame; - void *temp = NULL; - unsigned format = handle->info.format & SF_FORMAT_SUBMASK; - size_t actualBytes; - if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8 || format == SF_FORMAT_PCM_24) { - temp = malloc(desiredBytes); - actualBytes = handle->callback.read(temp, sizeof(char), desiredBytes, handle->stream); - } else { - actualBytes = handle->callback.read(ptr, sizeof(char), desiredBytes, handle->stream); - } - size_t actualFrames = actualBytes / handle->bytesPerFrame; - handle->remaining -= actualFrames; - switch (format) { - case SF_FORMAT_PCM_U8: -#if 0 - // TODO - implement - memcpy_to_i32_from_u8(ptr, (const unsigned char *) temp, - actualFrames * handle->info.channels); -#endif - free(temp); - break; - case SF_FORMAT_PCM_16: - memcpy_to_i32_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels); - free(temp); - break; - case SF_FORMAT_PCM_32: - break; - case SF_FORMAT_FLOAT: - memcpy_to_i32_from_float(ptr, (const float *) ptr, actualFrames * handle->info.channels); - break; - case SF_FORMAT_PCM_24: - memcpy_to_i32_from_p24(ptr, (const uint8_t *) temp, actualFrames * handle->info.channels); - free(temp); - break; - default: - memset(ptr, 0, actualFrames * handle->info.channels * sizeof(int)); - break; - } - return actualFrames; -} - */ diff --git a/cocos/audio/android/tinysndfile.h b/cocos/audio/android/tinysndfile.h deleted file mode 100644 index ca7e76513188..000000000000 --- a/cocos/audio/android/tinysndfile.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// This is a C library for reading and writing PCM .wav files. It is -// influenced by other libraries such as libsndfile and audiofile, except is -// much smaller and has an Apache 2.0 license. -// The API should be familiar to clients of similar libraries, but there is -// no guarantee that it will stay exactly source-code compatible with other libraries. - -#include -#include - -__BEGIN_DECLS - -// visible to clients -typedef int sf_count_t; - -typedef struct { - sf_count_t frames; - int samplerate; - int channels; - int format; -} SF_INFO; - -// opaque to clients -typedef struct SNDFILE_ SNDFILE; - -// Format -#define SF_FORMAT_TYPEMASK 1 -#define SF_FORMAT_WAV 1 -#define SF_FORMAT_SUBMASK 14 -#define SF_FORMAT_PCM_16 2 -#define SF_FORMAT_PCM_U8 4 -#define SF_FORMAT_FLOAT 6 -#define SF_FORMAT_PCM_32 8 -#define SF_FORMAT_PCM_24 10 - -typedef struct { - void* (*open)(const char* path, void* user); - size_t (*read) (void* ptr, size_t size, size_t nmemb, void* datasource); - int (*seek) (void* datasource, long offset, int whence); - int (*close) (void* datasource); - long (*tell) (void* datasource); -} snd_callbacks; - -// Open stream -SNDFILE *sf_open_read(const char *path, SF_INFO *info, snd_callbacks* cb, void* user); - -// Close stream -void sf_close(SNDFILE *handle); - -// Read interleaved frames and return actual number of frames read -sf_count_t sf_readf_short(SNDFILE *handle, short *ptr, sf_count_t desired); -/* -sf_count_t sf_readf_float(SNDFILE *handle, float *ptr, sf_count_t desired); -sf_count_t sf_readf_int(SNDFILE *handle, int *ptr, sf_count_t desired); -*/ - -__END_DECLS diff --git a/cocos/audio/android/utils/Compat.h b/cocos/audio/android/utils/Compat.h deleted file mode 100644 index 512090eee041..000000000000 --- a/cocos/audio/android/utils/Compat.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef COCOS_LIB_UTILS_COMPAT_H -#define COCOS_LIB_UTILS_COMPAT_H - -#include - -#if defined(__APPLE__) - -/* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */ - -typedef off_t off64_t; - -static inline off64_t lseek64(int fd, off64_t offset, int whence) { - return lseek(fd, offset, whence); -} - -static inline ssize_t pread64(int fd, void* buf, size_t nbytes, off64_t offset) { - return pread(fd, buf, nbytes, offset); -} - -static inline ssize_t pwrite64(int fd, const void* buf, size_t nbytes, off64_t offset) { - return pwrite(fd, buf, nbytes, offset); -} - -#endif /* __APPLE__ */ - -#if defined(_WIN32) -#define O_CLOEXEC O_NOINHERIT -#define O_NOFOLLOW 0 -#define DEFFILEMODE 0666 -#endif /* _WIN32 */ - -#if defined(_WIN32) -#define ZD "%ld" -#define ZD_TYPE long -#else -#define ZD "%zd" -#define ZD_TYPE ssize_t -#endif - -/* - * Needed for cases where something should be constexpr if possible, but not - * being constexpr is fine if in pre-C++11 code (such as a const static float - * member variable). - */ -#if __cplusplus >= 201103L -#define CONSTEXPR constexpr -#else -#define CONSTEXPR -#endif - -/* - * TEMP_FAILURE_RETRY is defined by some, but not all, versions of - * . (Alas, it is not as standard as we'd hoped!) So, if it's - * not already defined, then define it here. - */ -#ifndef TEMP_FAILURE_RETRY -/* Used to retry syscalls that can return EINTR. */ -#define TEMP_FAILURE_RETRY(exp) ({ \ - typeof (exp) _rc; \ - do { \ - _rc = (exp); \ - } while (_rc == -1 && errno == EINTR); \ - _rc; }) -#endif - -#if defined(_WIN32) -#define OS_PATH_SEPARATOR '\\' -#else -#define OS_PATH_SEPARATOR '/' -#endif - -#endif /* COCOS_LIB_UTILS_COMPAT_H */ diff --git a/cocos/audio/android/utils/Errors.h b/cocos/audio/android/utils/Errors.h deleted file mode 100644 index a305cf52056e..000000000000 --- a/cocos/audio/android/utils/Errors.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef COCOS_ERRORS_H -#define COCOS_ERRORS_H - -#include -#include - -namespace cocos2d { namespace experimental { - -// use this type to return error codes -#ifdef _WIN32 -typedef int status_t; -#else -typedef int32_t status_t; -#endif - -/* the MS C runtime lacks a few error codes */ - -/* - * Error codes. - * All error codes are negative values. - */ - -// Win32 #defines NO_ERROR as well. It has the same value, so there's no -// real conflict, though it's a bit awkward. -#ifdef _WIN32 -# undef NO_ERROR -#endif - -enum { - OK = 0, // Everything's swell. - NO_ERROR = 0, // No errors. - - UNKNOWN_ERROR = (-2147483647-1), // INT32_MIN value - - NO_MEMORY = -ENOMEM, - INVALID_OPERATION = -ENOSYS, - BAD_VALUE = -EINVAL, - BAD_TYPE = (UNKNOWN_ERROR + 1), - NAME_NOT_FOUND = -ENOENT, - PERMISSION_DENIED = -EPERM, - NO_INIT = -ENODEV, - ALREADY_EXISTS = -EEXIST, - DEAD_OBJECT = -EPIPE, - FAILED_TRANSACTION = (UNKNOWN_ERROR + 2), -#if !defined(_WIN32) - BAD_INDEX = -EOVERFLOW, - NOT_ENOUGH_DATA = -ENODATA, - WOULD_BLOCK = -EWOULDBLOCK, - TIMED_OUT = -ETIMEDOUT, - UNKNOWN_TRANSACTION = -EBADMSG, -#else - BAD_INDEX = -E2BIG, - NOT_ENOUGH_DATA = (UNKNOWN_ERROR + 3), - WOULD_BLOCK = (UNKNOWN_ERROR + 4), - TIMED_OUT = (UNKNOWN_ERROR + 5), - UNKNOWN_TRANSACTION = (UNKNOWN_ERROR + 6), -#endif - FDS_NOT_ALLOWED = (UNKNOWN_ERROR + 7), - UNEXPECTED_NULL = (UNKNOWN_ERROR + 8), -}; - -// Restore define; enumeration is in "android" namespace, so the value defined -// there won't work for Win32 code in a different namespace. -#ifdef _WIN32 -# define NO_ERROR 0L -#endif - -}} // namespace cocos2d { namespace experimental { - -// --------------------------------------------------------------------------- - -#endif // COCOS_ERRORS_H diff --git a/cocos/audio/android/utils/Utils.cpp b/cocos/audio/android/utils/Utils.cpp index aaccb1247b93..0ef5771be5b3 100644 --- a/cocos/audio/android/utils/Utils.cpp +++ b/cocos/audio/android/utils/Utils.cpp @@ -1,6 +1,5 @@ /**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. +Copyright (c) 2016-2017 Chukong Technologies Inc. http://www.cocos2d-x.org diff --git a/cocos/audio/android/utils/Utils.h b/cocos/audio/android/utils/Utils.h index 90ae035a4da3..8979bb39dafb 100644 --- a/cocos/audio/android/utils/Utils.h +++ b/cocos/audio/android/utils/Utils.h @@ -1,6 +1,5 @@ /**************************************************************************** -Copyright (c) 2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. +Copyright (c) 2016-2017 Chukong Technologies Inc. http://www.cocos2d-x.org