From ee532e37a4d8a401827e62f0f47b5bd2d68d59ad Mon Sep 17 00:00:00 2001 From: xiqinggong <243795168@qq.com> Date: Thu, 19 Dec 2024 14:52:53 +0800 Subject: [PATCH 1/4] 3.7 version OHOS adaptation 3.7 version OHOS adaptation --- .appveyor.yml | 37 + CMakeLists.txt | 92 +- cmake/Modules/CocosBuildHelpers.cmake | 4 +- cocos/CMakeLists.txt | 57 +- cocos/audio/AudioEngine.cpp | 46 +- cocos/audio/CMakeLists.txt | 34 + cocos/audio/include/AudioEngine.h | 18 +- cocos/audio/ohos/AssetFd.cpp | 45 + cocos/audio/ohos/AssetFd.h | 42 + cocos/audio/ohos/AudioBufferProvider.h | 62 + cocos/audio/ohos/AudioDecoder.cpp | 261 +++ cocos/audio/ohos/AudioDecoder.h | 62 + cocos/audio/ohos/AudioDecoderMp3.cpp | 76 + cocos/audio/ohos/AudioDecoderMp3.h | 41 + cocos/audio/ohos/AudioDecoderOgg.cpp | 101 + cocos/audio/ohos/AudioDecoderOgg.h | 44 + cocos/audio/ohos/AudioDecoderProvider.cpp | 78 + cocos/audio/ohos/AudioDecoderProvider.h | 40 + cocos/audio/ohos/AudioDecoderSLES.cpp | 292 +++ cocos/audio/ohos/AudioDecoderSLES.h | 96 + cocos/audio/ohos/AudioDecoderWav.cpp | 106 + cocos/audio/ohos/AudioDecoderWav.h | 46 + cocos/audio/ohos/AudioEngine-inl.cpp | 544 +++++ cocos/audio/ohos/AudioEngine-inl.h | 121 + cocos/audio/ohos/AudioMixer.cpp | 2039 +++++++++++++++++ cocos/audio/ohos/AudioMixer.h | 363 +++ cocos/audio/ohos/AudioMixerController.cpp | 306 +++ cocos/audio/ohos/AudioMixerController.h | 88 + cocos/audio/ohos/AudioMixerOps.h | 429 ++++ cocos/audio/ohos/AudioPlayerProvider.cpp | 477 ++++ cocos/audio/ohos/AudioPlayerProvider.h | 122 + cocos/audio/ohos/AudioResampler.cpp | 770 +++++++ cocos/audio/ohos/AudioResampler.h | 154 ++ cocos/audio/ohos/AudioResamplerCubic.cpp | 170 ++ cocos/audio/ohos/AudioResamplerCubic.h | 48 + cocos/audio/ohos/AudioResamplerPublic.h | 155 ++ cocos/audio/ohos/CCThreadPool.cpp | 376 +++ cocos/audio/ohos/CCThreadPool.h | 218 ++ cocos/audio/ohos/IAudioPlayer.h | 87 + cocos/audio/ohos/ICallerThreadUtils.h | 40 + cocos/audio/ohos/IVolumeProvider.h | 42 + cocos/audio/ohos/Macros.h | 415 ++++ cocos/audio/ohos/OpenSLHelper.h | 81 + cocos/audio/ohos/PcmAudioPlayer.cpp | 193 ++ cocos/audio/ohos/PcmAudioPlayer.h | 96 + cocos/audio/ohos/PcmAudioService.cpp | 159 ++ cocos/audio/ohos/PcmAudioService.h | 71 + cocos/audio/ohos/PcmBufferProvider.cpp | 102 + cocos/audio/ohos/PcmBufferProvider.h | 51 + cocos/audio/ohos/PcmData.cpp | 128 ++ cocos/audio/ohos/PcmData.h | 65 + cocos/audio/ohos/SimpleAudioEngine.cpp | 176 ++ cocos/audio/ohos/Track.cpp | 86 + cocos/audio/ohos/Track.h | 101 + cocos/audio/ohos/UrlAudioPlayer.cpp | 336 +++ cocos/audio/ohos/UrlAudioPlayer.h | 125 + cocos/audio/ohos/Value.h | 240 ++ cocos/audio/ohos/audio.h | 491 ++++ cocos/audio/ohos/audio_utils/AudioDef.h | 49 + .../audio/ohos/audio_utils/AudioEngine-ohos.h | 410 ++++ cocos/audio/ohos/audio_utils/RefCounted.cpp | 110 + cocos/audio/ohos/audio_utils/RefCounted.h | 106 + cocos/audio/ohos/audio_utils/Value.cpp | 847 +++++++ .../include/audio_utils/minifloat.h | 88 + .../include/audio_utils/primitives.h | 936 ++++++++ cocos/audio/ohos/audio_utils/minifloat.cpp | 47 + cocos/audio/ohos/audio_utils/primitives.cpp | 477 ++++ cocos/audio/ohos/cutils/bitops.h | 30 + cocos/audio/ohos/cutils/log.h | 567 +++++ cocos/audio/ohos/mp3reader.cpp | 497 ++++ cocos/audio/ohos/mp3reader.h | 33 + cocos/audio/ohos/tinysndfile.cpp | 502 ++++ cocos/audio/ohos/tinysndfile.h | 72 + cocos/audio/ohos/utils/Compat.h | 49 + cocos/audio/ohos/utils/Errors.h | 72 + cocos/audio/ohos/utils/Utils.cpp | 93 + cocos/audio/ohos/utils/Utils.h | 393 ++++ cocos/base/CCConsole.cpp | 5 +- cocos/base/CCDirector.cpp | 14 + cocos/base/CCDirector.h | 7 + cocos/base/CCEventMouse.h | 13 + cocos/base/CCRefPtr.h | 27 +- cocos/base/allocator/CCAllocatorMutex.h | 2 +- cocos/base/ccConfig.h | 3 +- cocos/cocos2d.h | 7 + cocos/network/WebSocket.cpp | 6 +- cocos/platform/CCApplication.h | 2 + cocos/platform/CCApplicationProtocol.h | 5 +- cocos/platform/CCDevice.h | 11 + cocos/platform/CCFileUtils.cpp | 16 + cocos/platform/CCFileUtils.h | 12 +- cocos/platform/CCGL.h | 2 + cocos/platform/CCGLView.cpp | 7 + cocos/platform/CCGLView.h | 7 + cocos/platform/CCImage.cpp | 2 + cocos/platform/CCPlatformConfig.h | 7 + cocos/platform/CCPlatformDefine.h | 2 + cocos/platform/CCPlatformMacros.h | 4 +- cocos/platform/CCStdC.h | 2 + cocos/platform/CMakeLists.txt | 39 + cocos/platform/ohos/CCApplication-ohos.cpp | 181 ++ cocos/platform/ohos/CCApplication-ohos.h | 114 + cocos/platform/ohos/CCCommon-ohos.cpp | 25 + cocos/platform/ohos/CCDevice-ohos.cpp | 105 + cocos/platform/ohos/CCFileUtils-ohos.cpp | 294 +++ cocos/platform/ohos/CCFileUtils-ohos.h | 158 ++ cocos/platform/ohos/CCGL-ohos.h | 45 + cocos/platform/ohos/CCGLViewImpl-ohos.cpp | 191 ++ cocos/platform/ohos/CCGLViewImpl-ohos.h | 51 + cocos/platform/ohos/CCImage.cpp | 274 +++ cocos/platform/ohos/CCJsAudioEngine.cpp | 586 +++++ cocos/platform/ohos/CCJsAudioEngine.h | 48 + cocos/platform/ohos/CCLogOhos.h | 121 + cocos/platform/ohos/CCPlatformDefine-ohos.h | 49 + cocos/platform/ohos/CCStdC-ohos.h | 30 + cocos/platform/ohos/CCTextBitmap.cpp | 179 ++ cocos/platform/ohos/CCTextBitmap.h | 73 + cocos/platform/ohos/JsAudioEngine.cpp | 579 +++++ cocos/platform/ohos/JsAudioEngine.h | 48 + .../ohos/libSysCapabilities/Readme.txt | 2 + .../platform/ohos/napi/WorkerMessageQueue.cpp | 20 + cocos/platform/ohos/napi/WorkerMessageQueue.h | 44 + .../platform/ohos/napi/common/native_common.h | 83 + .../ohos/napi/helper/JSRegisterUtils.h | 73 + .../ohos/napi/helper/Js_Cocos2dxHelper.cpp | 54 + .../ohos/napi/helper/Js_Cocos2dxHelper.h | 41 + .../platform/ohos/napi/helper/NapiHelper.cpp | 3 + cocos/platform/ohos/napi/helper/NapiHelper.h | 254 ++ .../ohos/napi/helper/NapiValueConverter.cpp | 89 + .../ohos/napi/helper/NapiValueConverter.h | 16 + .../platform/ohos/napi/modules/InputNapi.cpp | 185 ++ cocos/platform/ohos/napi/modules/InputNapi.h | 15 + .../platform/ohos/napi/modules/MouseNapi.cpp | 50 + cocos/platform/ohos/napi/modules/MouseNapi.h | 18 + .../ohos/napi/modules/RawFileUtils.cpp | 44 + .../platform/ohos/napi/modules/RawFileUtils.h | 84 + .../platform/ohos/napi/modules/SensorNapi.cpp | 63 + cocos/platform/ohos/napi/modules/SensorNapi.h | 14 + .../ohos/napi/modules/TouchesNapi.cpp | 46 + .../platform/ohos/napi/modules/TouchesNapi.h | 21 + .../ohos/napi/modules/VideoPlayerNapi.cpp | 45 + .../ohos/napi/modules/VideoPlayerNapi.h | 6 + .../ohos/napi/modules/WebViewNapi.cpp | 167 ++ .../platform/ohos/napi/modules/WebViewNapi.h | 15 + cocos/platform/ohos/napi/plugin_manager.cpp | 289 +++ cocos/platform/ohos/napi/plugin_manager.h | 64 + cocos/platform/ohos/napi/render/egl_core.cpp | 113 + cocos/platform/ohos/napi/render/egl_core.h | 32 + .../ohos/napi/render/plugin_render.cpp | 575 +++++ .../platform/ohos/napi/render/plugin_render.h | 101 + cocos/renderer/CCRenderer.cpp | 5 +- cocos/renderer/CCTexture2D.cpp | 4 +- cocos/renderer/CCTextureAtlas.cpp | 5 +- cocos/scripting/lua-bindings/CMakeLists.txt | 113 +- .../auto/lua_cocos2dx_audioengine_auto.cpp | 2 +- .../auto/lua_cocos2dx_audioengine_auto.hpp | 2 +- .../auto/lua_cocos2dx_controller_auto.cpp | 3 +- .../auto/lua_cocos2dx_controller_auto.hpp | 4 +- .../lua_cocos2dx_experimental_video_auto.cpp | 2 +- .../lua_cocos2dx_experimental_video_auto.hpp | 4 +- ...lua_cocos2dx_experimental_webview_auto.cpp | 2 +- ...lua_cocos2dx_experimental_webview_auto.hpp | 4 +- .../lua-bindings/manual/CCLuaEngine.cpp | 10 +- .../lua-bindings/manual/CCLuaStack.cpp | 8 +- .../lua_cocos2dx_audioengine_manual.cpp | 2 +- .../lua_cocos2dx_controller_manual.cpp | 4 +- .../lua_cocos2dx_controller_manual.hpp | 4 +- .../manual/network/Lua_web_socket.cpp | 2 +- .../manual/network/Lua_web_socket.h | 2 +- .../network/lua_cocos2dx_network_manual.cpp | 8 +- ...lua_cocos2dx_experimental_video_manual.cpp | 2 +- ...lua_cocos2dx_experimental_video_manual.hpp | 4 +- ...a_cocos2dx_experimental_webview_manual.cpp | 2 +- ...a_cocos2dx_experimental_webview_manual.hpp | 4 +- .../manual/ui/lua_cocos2dx_ui_manual.cpp | 2 +- ...lua_cocos2dx_experimental_video_manual.cpp | 2 +- ...lua_cocos2dx_experimental_video_manual.hpp | 2 +- .../script/cocos2d/Cocos2dConstants.lua | 1 + cocos/ui/CMakeLists.txt | 9 + cocos/ui/CocosGUI.h | 4 +- cocos/ui/UIEditBox/UIEditBoxImpl-common.cpp | 378 +++ cocos/ui/UIEditBox/UIEditBoxImpl-common.h | 144 ++ cocos/ui/UIEditBox/UIEditBoxImpl-ohos.cpp | 212 ++ cocos/ui/UIEditBox/UIEditBoxImpl-ohos.h | 68 + cocos/ui/UIHelper.cpp | 26 + cocos/ui/UIHelper.h | 10 + cocos/ui/UIVideoPlayer-ohos.cpp | 305 +++ cocos/ui/UIVideoPlayer-ohos.h | 7 + cocos/ui/UIVideoPlayer.h | 63 +- cocos/ui/UIWebView.cpp | 4 + cocos/ui/UIWebView.h | 2 +- cocos/ui/UIWebViewImpl-ohos.cpp | 209 ++ cocos/ui/UIWebViewImpl-ohos.h | 111 + .../Particle3D/PU/CCPUMaterialManager.cpp | 15 + .../Particle3D/PU/CCPUParticleSystem3D.h | 9 +- tests/cpp-tests/CMakeLists.txt | 145 +- tests/cpp-tests/Classes/AppDelegate.cpp | 24 +- tests/cpp-tests/Classes/AppDelegate.h | 7 + .../CocosDenshionTest/CocosDenshionTest.cpp | 2 +- .../NewAudioEngineTest/NewAudioEngineTest.cpp | 2 +- .../NewAudioEngineTest/NewAudioEngineTest.h | 2 +- .../CocoStudioGUITest/CocosGUIScene.cpp | 12 +- tests/cpp-tests/Classes/controller.cpp | 2 +- tests/cpp-tests/Classes/tests.h | 2 +- tests/cpp-tests/proj.ohos/.gitignore | 14 + tests/cpp-tests/proj.ohos/AppScope/app.json5 | 11 + .../resources/base/element/string.json | 8 + .../resources/base/media/app_icon.png | Bin 0 -> 6790 bytes tests/cpp-tests/proj.ohos/build-profile.json5 | 45 + tests/cpp-tests/proj.ohos/entry/.gitignore | 6 + .../proj.ohos/entry/build-profile.json5 | 42 + tests/cpp-tests/proj.ohos/entry/hvigorfile.ts | 6 + .../proj.ohos/entry/obfuscation-rules.txt | 18 + .../proj.ohos/entry/oh-package.json5 | 13 + .../entry/src/main/cpp/CMakeLists.txt | 42 + .../proj.ohos/entry/src/main/cpp/main.cpp | 36 + .../entry/src/main/cpp/napi_init.cpp | 54 + .../src/main/cpp/types/libentry/index.d.ts | 30 + .../main/cpp/types/libentry/oh-package.json5 | 6 + .../src/main/ets/MainAbility/MainAbility.ts | 80 + .../src/main/ets/components/CocosEditBox.ets | 82 + .../main/ets/components/CocosVideoPlayer.ets | 39 + .../src/main/ets/components/CocosWebview.ets | 43 + .../main/ets/components/TextInputDialog.ets | 33 + .../entry/src/main/ets/pages/Index.ets | 158 ++ .../entry/src/main/ets/workers/CocosWorker.ts | 79 + .../src/main/ets/workers/WorkerManager.ts | 34 + .../proj.ohos/entry/src/main/module.json5 | 70 + .../main/resources/base/element/color.json | 8 + .../main/resources/base/element/string.json | 20 + .../src/main/resources/base/media/icon.png | Bin 0 -> 6790 bytes .../resources/base/profile/main_pages.json | 5 + .../proj.ohos/hvigor/hvigor-config.json5 | 5 + tests/cpp-tests/proj.ohos/hvigorfile.ts | 2 + .../libSysCapabilities/BuildProfile.ets | 17 + .../libSysCapabilities/build-profile.json5 | 28 + .../merge_profile/default/module.json | 25 + .../libSysCapabilities/hvigorfile.ts | 6 + .../proj.ohos/libSysCapabilities/index.ts | 15 + .../libSysCapabilities/obfuscation-rules.txt | 18 + .../libSysCapabilities/oh-package.json5 | 10 + .../src/main/ets/common/Constants.ts | 22 + .../src/main/ets/common/GlobalContext.ts | 25 + .../main/ets/components/dialog/DialogMsg.ts | 47 + .../ets/components/dialog/DialogWorker.ts | 29 + .../ets/components/editbox/CocosEditBox.ts | 111 + .../ets/components/editbox/EditBoxMsg.ets | 194 ++ .../ets/components/videoplayer/VideoPlayer.ts | 79 + .../components/videoplayer/VideoPlayerMsg.ets | 145 ++ .../main/ets/components/webview/WebView.ts | 114 + .../ets/components/webview/WebViewMsg.ets | 245 ++ .../src/main/ets/entity/Result.ts | 16 + .../main/ets/entity/TextInputDialogEntity.ts | 7 + .../src/main/ets/entity/WorkerMsgEntity.ts | 141 ++ .../src/main/ets/napi/NapiHelper.ts | 118 + .../src/main/ets/preferences/Preferences.ts | 218 ++ .../main/ets/system/appJump/JumpManager.ts | 19 + .../main/ets/system/appJump/JumpManagerMsg.ts | 31 + .../system/application/ApplicationManager.ts | 54 + .../src/main/ets/system/device/DeviceUtils.ts | 164 ++ .../ets/system/sensor/AccelerometerUtils.ts | 55 + .../src/main/ets/utils/Logger.ts | 27 + .../src/main/ets/utils/StringUtils.ts | 8 + .../src/main/ets/utils/WorkerMsgUtils.ets | 42 + .../libSysCapabilities/src/main/module.json5 | 10 + tests/cpp-tests/proj.ohos/oh-package.json5 | 10 + tests/lua-tests/project/CMakeLists.txt | 53 +- .../lua-tests/project/Classes/AppDelegate.cpp | 20 +- tests/lua-tests/project/Classes/AppDelegate.h | 7 + tests/lua-tests/project/proj.ohos/.gitignore | 14 + .../project/proj.ohos/AppScope/app.json5 | 11 + .../resources/base/element/string.json | 8 + .../resources/base/media/app_icon.png | Bin 0 -> 6790 bytes .../project/proj.ohos/build-profile.json5 | 45 + .../project/proj.ohos/entry/.gitignore | 6 + .../proj.ohos/entry/build-profile.json5 | 42 + .../project/proj.ohos/entry/hvigorfile.ts | 6 + .../proj.ohos/entry/obfuscation-rules.txt | 18 + .../project/proj.ohos/entry/oh-package.json5 | 13 + .../entry/src/main/cpp/CMakeLists.txt | 77 + .../proj.ohos/entry/src/main/cpp/main.cpp | 36 + .../entry/src/main/cpp/napi_init.cpp | 54 + .../src/main/cpp/types/libentry/index.d.ts | 30 + .../main/cpp/types/libentry/oh-package.json5 | 6 + .../src/main/ets/MainAbility/MainAbility.ts | 80 + .../src/main/ets/components/CocosEditBox.ets | 82 + .../main/ets/components/CocosVideoPlayer.ets | 39 + .../src/main/ets/components/CocosWebview.ets | 43 + .../main/ets/components/TextInputDialog.ets | 33 + .../entry/src/main/ets/pages/Index.ets | 157 ++ .../entry/src/main/ets/workers/CocosWorker.ts | 79 + .../src/main/ets/workers/WorkerManager.ts | 34 + .../proj.ohos/entry/src/main/module.json5 | 70 + .../main/resources/base/element/color.json | 8 + .../main/resources/base/element/string.json | 20 + .../src/main/resources/base/media/icon.png | Bin 0 -> 6790 bytes .../resources/base/profile/main_pages.json | 5 + .../proj.ohos/hvigor/hvigor-config.json5 | 5 + .../lua-tests/project/proj.ohos/hvigorfile.ts | 6 + .../libSysCapabilities/BuildProfile.ets | 17 + .../libSysCapabilities/build-profile.json5 | 28 + .../merge_profile/default/module.json | 25 + .../libSysCapabilities/hvigorfile.ts | 6 + .../proj.ohos/libSysCapabilities/index.ts | 15 + .../libSysCapabilities/obfuscation-rules.txt | 18 + .../libSysCapabilities/oh-package.json5 | 11 + .../src/main/ets/common/Constants.ts | 22 + .../src/main/ets/common/GlobalContext.ts | 25 + .../main/ets/components/dialog/DialogMsg.ts | 47 + .../ets/components/dialog/DialogWorker.ts | 29 + .../ets/components/editbox/CocosEditBox.ts | 111 + .../ets/components/editbox/EditBoxMsg.ets | 194 ++ .../ets/components/videoplayer/VideoPlayer.ts | 79 + .../components/videoplayer/VideoPlayerMsg.ets | 145 ++ .../main/ets/components/webview/WebView.ts | 114 + .../ets/components/webview/WebViewMsg.ets | 246 ++ .../src/main/ets/entity/Result.ts | 16 + .../main/ets/entity/TextInputDialogEntity.ts | 7 + .../src/main/ets/entity/WorkerMsgEntity.ts | 141 ++ .../src/main/ets/napi/NapiHelper.ts | 119 + .../src/main/ets/preferences/Preferences.ts | 218 ++ .../main/ets/system/appJump/JumpManager.ts | 19 + .../main/ets/system/appJump/JumpManagerMsg.ts | 31 + .../system/application/ApplicationManager.ts | 55 + .../src/main/ets/system/device/DeviceUtils.ts | 164 ++ .../ets/system/sensor/AccelerometerUtils.ts | 55 + .../src/main/ets/utils/Logger.ts | 27 + .../src/main/ets/utils/StringUtils.ts | 8 + .../src/main/ets/utils/WorkerMsgUtils.ets | 42 + .../libSysCapabilities/src/main/module.json5 | 10 + .../project/proj.ohos/oh-package.json5 | 10 + .../AssetsManagerTest/AssetsManagerTest.lua | 2 +- .../CocosDenshionTest/CocosDenshionTest.lua | 2 + .../NewAudioEngineTest/NewAudioEngineTest.lua | 3 + .../src/Particle3DTest/Particle3DTest.lua | 2 +- .../src/Sprite3DTest/Sprite3DTest.lua | 4 +- tests/lua-tests/src/controller.lua | 15 +- tests/lua-tests/src/mainMenu.lua | 10 +- 338 files changed, 30552 insertions(+), 171 deletions(-) create mode 100644 .appveyor.yml create mode 100644 cocos/audio/ohos/AssetFd.cpp create mode 100644 cocos/audio/ohos/AssetFd.h create mode 100644 cocos/audio/ohos/AudioBufferProvider.h create mode 100644 cocos/audio/ohos/AudioDecoder.cpp create mode 100644 cocos/audio/ohos/AudioDecoder.h create mode 100644 cocos/audio/ohos/AudioDecoderMp3.cpp create mode 100644 cocos/audio/ohos/AudioDecoderMp3.h create mode 100644 cocos/audio/ohos/AudioDecoderOgg.cpp create mode 100644 cocos/audio/ohos/AudioDecoderOgg.h create mode 100644 cocos/audio/ohos/AudioDecoderProvider.cpp create mode 100644 cocos/audio/ohos/AudioDecoderProvider.h create mode 100644 cocos/audio/ohos/AudioDecoderSLES.cpp create mode 100644 cocos/audio/ohos/AudioDecoderSLES.h create mode 100644 cocos/audio/ohos/AudioDecoderWav.cpp create mode 100644 cocos/audio/ohos/AudioDecoderWav.h create mode 100644 cocos/audio/ohos/AudioEngine-inl.cpp create mode 100644 cocos/audio/ohos/AudioEngine-inl.h create mode 100644 cocos/audio/ohos/AudioMixer.cpp create mode 100644 cocos/audio/ohos/AudioMixer.h create mode 100644 cocos/audio/ohos/AudioMixerController.cpp create mode 100644 cocos/audio/ohos/AudioMixerController.h create mode 100644 cocos/audio/ohos/AudioMixerOps.h create mode 100644 cocos/audio/ohos/AudioPlayerProvider.cpp create mode 100644 cocos/audio/ohos/AudioPlayerProvider.h create mode 100644 cocos/audio/ohos/AudioResampler.cpp create mode 100644 cocos/audio/ohos/AudioResampler.h create mode 100644 cocos/audio/ohos/AudioResamplerCubic.cpp create mode 100644 cocos/audio/ohos/AudioResamplerCubic.h create mode 100644 cocos/audio/ohos/AudioResamplerPublic.h create mode 100644 cocos/audio/ohos/CCThreadPool.cpp create mode 100644 cocos/audio/ohos/CCThreadPool.h create mode 100644 cocos/audio/ohos/IAudioPlayer.h create mode 100644 cocos/audio/ohos/ICallerThreadUtils.h create mode 100644 cocos/audio/ohos/IVolumeProvider.h create mode 100644 cocos/audio/ohos/Macros.h create mode 100644 cocos/audio/ohos/OpenSLHelper.h create mode 100644 cocos/audio/ohos/PcmAudioPlayer.cpp create mode 100644 cocos/audio/ohos/PcmAudioPlayer.h create mode 100644 cocos/audio/ohos/PcmAudioService.cpp create mode 100644 cocos/audio/ohos/PcmAudioService.h create mode 100644 cocos/audio/ohos/PcmBufferProvider.cpp create mode 100644 cocos/audio/ohos/PcmBufferProvider.h create mode 100644 cocos/audio/ohos/PcmData.cpp create mode 100644 cocos/audio/ohos/PcmData.h create mode 100644 cocos/audio/ohos/SimpleAudioEngine.cpp create mode 100644 cocos/audio/ohos/Track.cpp create mode 100644 cocos/audio/ohos/Track.h create mode 100644 cocos/audio/ohos/UrlAudioPlayer.cpp create mode 100644 cocos/audio/ohos/UrlAudioPlayer.h create mode 100644 cocos/audio/ohos/Value.h create mode 100644 cocos/audio/ohos/audio.h create mode 100644 cocos/audio/ohos/audio_utils/AudioDef.h create mode 100644 cocos/audio/ohos/audio_utils/AudioEngine-ohos.h create mode 100644 cocos/audio/ohos/audio_utils/RefCounted.cpp create mode 100644 cocos/audio/ohos/audio_utils/RefCounted.h create mode 100644 cocos/audio/ohos/audio_utils/Value.cpp create mode 100644 cocos/audio/ohos/audio_utils/include/audio_utils/minifloat.h create mode 100644 cocos/audio/ohos/audio_utils/include/audio_utils/primitives.h create mode 100644 cocos/audio/ohos/audio_utils/minifloat.cpp create mode 100644 cocos/audio/ohos/audio_utils/primitives.cpp create mode 100644 cocos/audio/ohos/cutils/bitops.h create mode 100644 cocos/audio/ohos/cutils/log.h create mode 100644 cocos/audio/ohos/mp3reader.cpp create mode 100644 cocos/audio/ohos/mp3reader.h create mode 100644 cocos/audio/ohos/tinysndfile.cpp create mode 100644 cocos/audio/ohos/tinysndfile.h create mode 100644 cocos/audio/ohos/utils/Compat.h create mode 100644 cocos/audio/ohos/utils/Errors.h create mode 100644 cocos/audio/ohos/utils/Utils.cpp create mode 100644 cocos/audio/ohos/utils/Utils.h create mode 100644 cocos/platform/ohos/CCApplication-ohos.cpp create mode 100644 cocos/platform/ohos/CCApplication-ohos.h create mode 100644 cocos/platform/ohos/CCCommon-ohos.cpp create mode 100644 cocos/platform/ohos/CCDevice-ohos.cpp create mode 100644 cocos/platform/ohos/CCFileUtils-ohos.cpp create mode 100644 cocos/platform/ohos/CCFileUtils-ohos.h create mode 100644 cocos/platform/ohos/CCGL-ohos.h create mode 100644 cocos/platform/ohos/CCGLViewImpl-ohos.cpp create mode 100644 cocos/platform/ohos/CCGLViewImpl-ohos.h create mode 100644 cocos/platform/ohos/CCImage.cpp create mode 100644 cocos/platform/ohos/CCJsAudioEngine.cpp create mode 100644 cocos/platform/ohos/CCJsAudioEngine.h create mode 100644 cocos/platform/ohos/CCLogOhos.h create mode 100644 cocos/platform/ohos/CCPlatformDefine-ohos.h create mode 100644 cocos/platform/ohos/CCStdC-ohos.h create mode 100644 cocos/platform/ohos/CCTextBitmap.cpp create mode 100644 cocos/platform/ohos/CCTextBitmap.h create mode 100644 cocos/platform/ohos/JsAudioEngine.cpp create mode 100644 cocos/platform/ohos/JsAudioEngine.h create mode 100644 cocos/platform/ohos/libSysCapabilities/Readme.txt create mode 100644 cocos/platform/ohos/napi/WorkerMessageQueue.cpp create mode 100644 cocos/platform/ohos/napi/WorkerMessageQueue.h create mode 100644 cocos/platform/ohos/napi/common/native_common.h create mode 100644 cocos/platform/ohos/napi/helper/JSRegisterUtils.h create mode 100644 cocos/platform/ohos/napi/helper/Js_Cocos2dxHelper.cpp create mode 100644 cocos/platform/ohos/napi/helper/Js_Cocos2dxHelper.h create mode 100644 cocos/platform/ohos/napi/helper/NapiHelper.cpp create mode 100644 cocos/platform/ohos/napi/helper/NapiHelper.h create mode 100644 cocos/platform/ohos/napi/helper/NapiValueConverter.cpp create mode 100644 cocos/platform/ohos/napi/helper/NapiValueConverter.h create mode 100644 cocos/platform/ohos/napi/modules/InputNapi.cpp create mode 100644 cocos/platform/ohos/napi/modules/InputNapi.h create mode 100644 cocos/platform/ohos/napi/modules/MouseNapi.cpp create mode 100644 cocos/platform/ohos/napi/modules/MouseNapi.h create mode 100644 cocos/platform/ohos/napi/modules/RawFileUtils.cpp create mode 100644 cocos/platform/ohos/napi/modules/RawFileUtils.h create mode 100644 cocos/platform/ohos/napi/modules/SensorNapi.cpp create mode 100644 cocos/platform/ohos/napi/modules/SensorNapi.h create mode 100644 cocos/platform/ohos/napi/modules/TouchesNapi.cpp create mode 100644 cocos/platform/ohos/napi/modules/TouchesNapi.h create mode 100644 cocos/platform/ohos/napi/modules/VideoPlayerNapi.cpp create mode 100644 cocos/platform/ohos/napi/modules/VideoPlayerNapi.h create mode 100644 cocos/platform/ohos/napi/modules/WebViewNapi.cpp create mode 100644 cocos/platform/ohos/napi/modules/WebViewNapi.h create mode 100644 cocos/platform/ohos/napi/plugin_manager.cpp create mode 100644 cocos/platform/ohos/napi/plugin_manager.h create mode 100644 cocos/platform/ohos/napi/render/egl_core.cpp create mode 100644 cocos/platform/ohos/napi/render/egl_core.h create mode 100644 cocos/platform/ohos/napi/render/plugin_render.cpp create mode 100644 cocos/platform/ohos/napi/render/plugin_render.h create mode 100644 cocos/ui/UIEditBox/UIEditBoxImpl-common.cpp create mode 100644 cocos/ui/UIEditBox/UIEditBoxImpl-common.h create mode 100644 cocos/ui/UIEditBox/UIEditBoxImpl-ohos.cpp create mode 100644 cocos/ui/UIEditBox/UIEditBoxImpl-ohos.h create mode 100644 cocos/ui/UIVideoPlayer-ohos.cpp create mode 100644 cocos/ui/UIVideoPlayer-ohos.h create mode 100644 cocos/ui/UIWebViewImpl-ohos.cpp create mode 100644 cocos/ui/UIWebViewImpl-ohos.h create mode 100644 tests/cpp-tests/proj.ohos/.gitignore create mode 100644 tests/cpp-tests/proj.ohos/AppScope/app.json5 create mode 100644 tests/cpp-tests/proj.ohos/AppScope/resources/base/element/string.json create mode 100644 tests/cpp-tests/proj.ohos/AppScope/resources/base/media/app_icon.png create mode 100644 tests/cpp-tests/proj.ohos/build-profile.json5 create mode 100644 tests/cpp-tests/proj.ohos/entry/.gitignore create mode 100644 tests/cpp-tests/proj.ohos/entry/build-profile.json5 create mode 100644 tests/cpp-tests/proj.ohos/entry/hvigorfile.ts create mode 100644 tests/cpp-tests/proj.ohos/entry/obfuscation-rules.txt create mode 100644 tests/cpp-tests/proj.ohos/entry/oh-package.json5 create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/cpp/CMakeLists.txt create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/cpp/main.cpp create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/cpp/napi_init.cpp create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/cpp/types/libentry/index.d.ts create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/cpp/types/libentry/oh-package.json5 create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/ets/MainAbility/MainAbility.ts create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/ets/components/CocosEditBox.ets create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/ets/components/CocosVideoPlayer.ets create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/ets/components/CocosWebview.ets create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/ets/components/TextInputDialog.ets create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/ets/pages/Index.ets create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/ets/workers/CocosWorker.ts create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/ets/workers/WorkerManager.ts create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/module.json5 create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/resources/base/element/color.json create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/resources/base/element/string.json create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/resources/base/media/icon.png create mode 100644 tests/cpp-tests/proj.ohos/entry/src/main/resources/base/profile/main_pages.json create mode 100644 tests/cpp-tests/proj.ohos/hvigor/hvigor-config.json5 create mode 100644 tests/cpp-tests/proj.ohos/hvigorfile.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/BuildProfile.ets create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/build-profile.json5 create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/build/default/intermediates/merge_profile/default/module.json create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/hvigorfile.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/index.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/obfuscation-rules.txt create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/oh-package.json5 create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/common/Constants.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/common/GlobalContext.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/dialog/DialogMsg.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/dialog/DialogWorker.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/editbox/CocosEditBox.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/editbox/EditBoxMsg.ets create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayer.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayerMsg.ets create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/webview/WebView.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/webview/WebViewMsg.ets create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/entity/Result.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/entity/TextInputDialogEntity.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/entity/WorkerMsgEntity.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/napi/NapiHelper.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/preferences/Preferences.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/appJump/JumpManager.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/appJump/JumpManagerMsg.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/application/ApplicationManager.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/device/DeviceUtils.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/sensor/AccelerometerUtils.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/utils/Logger.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/utils/StringUtils.ts create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/utils/WorkerMsgUtils.ets create mode 100644 tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/module.json5 create mode 100644 tests/cpp-tests/proj.ohos/oh-package.json5 create mode 100644 tests/lua-tests/project/proj.ohos/.gitignore create mode 100644 tests/lua-tests/project/proj.ohos/AppScope/app.json5 create mode 100644 tests/lua-tests/project/proj.ohos/AppScope/resources/base/element/string.json create mode 100644 tests/lua-tests/project/proj.ohos/AppScope/resources/base/media/app_icon.png create mode 100644 tests/lua-tests/project/proj.ohos/build-profile.json5 create mode 100644 tests/lua-tests/project/proj.ohos/entry/.gitignore create mode 100644 tests/lua-tests/project/proj.ohos/entry/build-profile.json5 create mode 100644 tests/lua-tests/project/proj.ohos/entry/hvigorfile.ts create mode 100644 tests/lua-tests/project/proj.ohos/entry/obfuscation-rules.txt create mode 100644 tests/lua-tests/project/proj.ohos/entry/oh-package.json5 create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/cpp/CMakeLists.txt create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/cpp/main.cpp create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/cpp/napi_init.cpp create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/cpp/types/libentry/index.d.ts create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/cpp/types/libentry/oh-package.json5 create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/ets/MainAbility/MainAbility.ts create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/ets/components/CocosEditBox.ets create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/ets/components/CocosVideoPlayer.ets create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/ets/components/CocosWebview.ets create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/ets/components/TextInputDialog.ets create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/ets/pages/Index.ets create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/ets/workers/CocosWorker.ts create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/ets/workers/WorkerManager.ts create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/module.json5 create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/resources/base/element/color.json create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/resources/base/element/string.json create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/resources/base/media/icon.png create mode 100644 tests/lua-tests/project/proj.ohos/entry/src/main/resources/base/profile/main_pages.json create mode 100644 tests/lua-tests/project/proj.ohos/hvigor/hvigor-config.json5 create mode 100644 tests/lua-tests/project/proj.ohos/hvigorfile.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/BuildProfile.ets create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/build-profile.json5 create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/build/default/intermediates/merge_profile/default/module.json create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/hvigorfile.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/index.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/obfuscation-rules.txt create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/oh-package.json5 create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/common/Constants.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/common/GlobalContext.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/dialog/DialogMsg.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/dialog/DialogWorker.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/editbox/CocosEditBox.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/editbox/EditBoxMsg.ets create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayer.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayerMsg.ets create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/webview/WebView.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/webview/WebViewMsg.ets create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/entity/Result.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/entity/TextInputDialogEntity.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/entity/WorkerMsgEntity.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/napi/NapiHelper.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/preferences/Preferences.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/appJump/JumpManager.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/appJump/JumpManagerMsg.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/application/ApplicationManager.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/device/DeviceUtils.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/sensor/AccelerometerUtils.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/utils/Logger.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/utils/StringUtils.ts create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/utils/WorkerMsgUtils.ets create mode 100644 tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/module.json5 create mode 100644 tests/lua-tests/project/proj.ohos/oh-package.json5 diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 000000000000..f3002d0ff4c2 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,37 @@ +version: 1.0.{build} +skip_tags: true +skip_branch_with_pr: true +image: + - Visual Studio 2015 +environment: + PYTHON: "C:\\Python27" + PYTHON_VERSION: "2.7.13" + PYTHON_ARCH: "32" + matrix: + # - build_type: windows32_cmake_test + # - build_type: windows32_sln_test +# - build_type: android_lua_tests +# - build_type: android_cocos_new_test +# - build_type: android_cpp_empty_test +# - build_type: android_gen_libs + + +platform: + - x86 + +configuration: + - Release + + +branches: + except: + - v1 + - v2 + - v4-develop + - v3-doc + - v3.11_backup + - v35-for-tizen + +clone_depth: 1 + +test: off diff --git a/CMakeLists.txt b/CMakeLists.txt index fc7c0016c148..65826ca65ff1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,21 +45,30 @@ include(CocosBuildHelpers) message(${BUILDING_STRING}) -set(USE_WEBP_DEFAULT ON) +if(OHOS) + set(USE_WEBP_DEFAULT OFF) +else() + set(USE_WEBP_DEFAULT ON) +endif() if(WINRT OR WP8) set(USE_WEBP_DEFAULT OFF) endif() set(USE_PREBUILT_LIBS_DEFAULT ON) -if(MINGW) +if(MINGW AND NOT OHOS) set(USE_PREBUILT_LIBS_DEFAULT OFF) endif() set(BUILD_CPP_TESTS_DEFAULT ON) set(BUILD_LUA_LIBS_DEFAULT ON) set(BUILD_LUA_TESTS_DEFAULT ON) -set(BUILD_JS_LIBS_DEFAULT ON) -set(BUILD_JS_TESTS_DEFAULT ON) +if(OHOS) + set(BUILD_JS_LIBS_DEFAULT OFF) + set(BUILD_JS_TESTS_DEFAULT OFF) +else() + set(BUILD_JS_LIBS_DEFAULT ON) + set(BUILD_JS_TESTS_DEFAULT ON) +endif() # TODO: fix test samples for MSVC if(MSVC) set(BUILD_CPP_TESTS_DEFAULT OFF) @@ -86,8 +95,8 @@ option(BUILD_LUA_TESTS "Build TestLua samples" ${BUILD_LUA_TESTS_DEFAULT}) option(BUILD_JS_LIBS "Build js libraries" ${BUILD_JS_LIBS_DEFAULT}) option(BUILD_JS_TESTS "Build TestJS samples" ${BUILD_JS_TESTS_DEFAULT}) option(USE_PREBUILT_LIBS "Use prebuilt libraries in external directory" ${USE_PREBUILT_LIBS_DEFAULT}) - -if(USE_PREBUILT_LIBS AND MINGW) + +if(USE_PREBUILT_LIBS AND MINGW AND NOT OHOS) message(FATAL_ERROR "Prebuilt windows libs can't be used with mingw, please use packages.") endif() @@ -146,6 +155,9 @@ elseif(LINUX) elseif(ANDROID) ADD_DEFINITIONS (-DUSE_FILE32API) set(PLATFORM_FOLDER android) +elseif(OHOS) + ADD_DEFINITIONS (-DUSE_FILE32API) + set(PLATFORM_FOLDER ohos) else() message( FATAL_ERROR "Unsupported platform, CMake will exit" ) endif() @@ -214,6 +226,11 @@ if(LINUX OR MACOSX OR WINDOWS) endif(LINUX OR MACOSX OR WINDOWS) # Freetype required on all platforms +if(OHOS) + set(FREETYPE_INCLUDE_DIR_ft2build ${CMAKE_CURRENT_SOURCE_DIR}/external/freetype2/include/ohos) + set(FREETYPE_INCLUDE_DIR_freetype2 ${CMAKE_CURRENT_SOURCE_DIR}/external/freetype2/include/ohos/freetype2) + set(FREETYPE_LIBRARY ${CMAKE_CURRENT_SOURCE_DIR}/external/freetype2/prebuilt/ohos/libfreetype.a) +endif() cocos_find_package(Freetype FREETYPE REQUIRED) # WebP required if used @@ -223,6 +240,10 @@ endif(USE_WEBP) # Chipmunk if(USE_CHIPMUNK) + if(OHOS) + set(CHIPMUNK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/chipmunk/include/chipmunk) + set(CHIPMUNK_LIBRARY ${CMAKE_CURRENT_SOURCE_DIR}/external/chipmunk/prebuilt/ohos/libchipmunk.a) + endif() cocos_find_package(Chipmunk CHIPMUNK REQUIRED) add_definitions(-DCC_ENABLE_CHIPMUNK_INTEGRATION=1) if(IOS OR MACOSX) @@ -295,6 +316,10 @@ endif() message(STATUS "TinyXML2 include dirs: ${TinyXML2_INCLUDE_DIRS}") # libjpeg +if(OHOS) + set(JPEG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/jpeg/include/ohos) + set(JPEG_LIBRARY ${CMAKE_CURRENT_SOURCE_DIR}/external/jpeg/prebuilt/ohos/libjpeg.a) +endif() cocos_find_package(JPEG JPEG REQUIRED) cocos_find_package(ZLIB ZLIB REQUIRED) @@ -321,9 +346,47 @@ else() add_definitions(-DMINIZIP_FROM_SYSTEM) endif() +if(OHOS) + set(PNG_PNG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/png/include/ohos) + set(PNG_LIBRARY ${CMAKE_CURRENT_SOURCE_DIR}/external/png/prebuilt/ohos/libpng.a) +endif() cocos_find_package(PNG PNG REQUIRED) + +if(OHOS) + set(TIFF_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/tiff/include/ohos) + set(TIFF_LIBRARY ${CMAKE_CURRENT_SOURCE_DIR}/external/tiff/prebuilt/ohos/libtiff.a) +endif() cocos_find_package(TIFF TIFF REQUIRED) + +if(OHOS) + set(WEBSOCKETS_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/websockets/include/ohos) + set(WEBSOCKETS_LIBRARY ${CMAKE_CURRENT_SOURCE_DIR}/external/websockets/prebuilt/ohos/libwebsockets.a) +endif() cocos_find_package(WEBSOCKETS WEBSOCKETS REQUIRED) + +# openssl for web +if(OHOS) + set(OPENSSL_INCLUDE_DIR + ${CMAKE_CURRENT_SOURCE_DIR}/external/openssl + ${CMAKE_CURRENT_SOURCE_DIR}/external/openssl/include/ohos + ${CMAKE_CURRENT_SOURCE_DIR}/external/openssl/include/ohos/openssl + ) + set(OPENSSL_LIBRARY + ${CMAKE_CURRENT_SOURCE_DIR}/external/openssl/prebuilt/ohos/libcrypto.a + ${CMAKE_CURRENT_SOURCE_DIR}/external/openssl/prebuilt/ohos/libssl.a + ) +endif() +cocos_find_package(OPENSSL OPENSSL REQUIRED) + +if(OHOS) + set(CURL_INCLUDE_DIR + ${CMAKE_CURRENT_SOURCE_DIR}/external/curl + ${CMAKE_CURRENT_SOURCE_DIR}/external/curl/include + ${CMAKE_CURRENT_SOURCE_DIR}/external/curl/include/ohos + ${CMAKE_CURRENT_SOURCE_DIR}/external/curl/include/ohos/curl + ) + set(CURL_LIBRARY ${CMAKE_CURRENT_SOURCE_DIR}/external/curl/prebuilt/ohos/libcurl.a) +endif() cocos_find_package(CURL CURL REQUIRED) add_subdirectory(external/flatbuffers) @@ -336,23 +399,34 @@ add_subdirectory(external/xxhash) set(XXHASH_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/external/xxhash) set(XXHASH_LIBRARIES xxhash) +if(OHOS) + add_subdirectory(external/ohos-specific/pvmp3dec) + add_subdirectory(external/ohos-specific/tremolo) +endif() # libcocos2d.a add_subdirectory(cocos) # build cpp tests if(BUILD_CPP_TESTS) - add_subdirectory(tests/cpp-empty-test) - add_subdirectory(tests/cpp-tests) + if(NOT OHOS) + add_subdirectory(tests/cpp-empty-test) + endif() + add_subdirectory(tests/cpp-tests) endif(BUILD_CPP_TESTS) ## Scripting if(BUILD_LUA_LIBS) + if(OHOS) + add_subdirectory(external/lua/luajit) + endif() add_subdirectory(cocos/scripting/lua-bindings) # build lua tests if(BUILD_LUA_TESTS) add_subdirectory(tests/lua-tests/project) - add_subdirectory(tests/lua-empty-test/project) + if(NOT OHOS) + add_subdirectory(tests/lua-empty-test/project) + endif() endif(BUILD_LUA_TESTS) endif(BUILD_LUA_LIBS) diff --git a/cmake/Modules/CocosBuildHelpers.cmake b/cmake/Modules/CocosBuildHelpers.cmake index 932d808b3d30..d2a104c2c383 100644 --- a/cmake/Modules/CocosBuildHelpers.cmake +++ b/cmake/Modules/CocosBuildHelpers.cmake @@ -114,7 +114,7 @@ endfunction() #IOS = iOS #MACOSX = MacOS X #LINUX = Linux - +#OHOS = OpenHarmonyOS if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") if(WINRT) set(SYSTEM_STRING "Windows RT") @@ -139,6 +139,8 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(APPLE TRUE) set(SYSTEM_STRING "Mac OSX") endif() +elseif(OHOS) + set(SYSTEM_STRING "HarmonyOS Next") endif() if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") diff --git a/cocos/CMakeLists.txt b/cocos/CMakeLists.txt index 311fe2728dd2..9cfcc42f8577 100644 --- a/cocos/CMakeLists.txt +++ b/cocos/CMakeLists.txt @@ -62,7 +62,11 @@ include(editor-support/spine/CMakeLists.txt) set(COCOS_EDITOR_SUPPORT_SRC ${COCOS_EDITOR_SUPPORT_SRC} ${COCOS_SPINE_SRC}) endif(BUILD_EDITOR_SPINE) -include(../extensions/CMakeLists.txt) +if(OHOS) + include(${COCOS2DX_ROOT_PATH}/extensions/CMakeLists.txt) +else() + include(../extensions/CMakeLists.txt) +endif() set(COCOS_SRC cocos2d.cpp ${COCOS_2D_SRC} @@ -126,14 +130,63 @@ elseif(MACOSX OR APPLE) elseif(ANDROID) set(PLATFORM_SPECIFIC_LIBS GLESv2 log android) +elseif(OHOS) + FIND_LIBRARY(Drawing-lib native_drawing) + FIND_LIBRARY(libace-lib ace_ndk.z) + FIND_LIBRARY(libnapi-lib ace_napi.z) + FIND_LIBRARY(GLES-lib GLESv3) + FIND_LIBRARY(libuv-lib uv) + FIND_LIBRARY(rawfile-lib rawfile.z) + FIND_LIBRARY(EGL-lib EGL) + FIND_LIBRARY(OpenSl-lib OpenSLES) + find_library( hilog-lib hilog_ndk.z ) + + set(PLATFORM_SPECIFIC_LIBS + ${Drawing-lib} + ${libace-lib} + ${libnapi-lib} + ${GLES-lib} + ${libuv-lib} + ${rawfile-lib} + ${EGL-lib} + ${OpenSl-lib} + ${hilog-lib} + ) + + set(COCOS2DX_HEADER_PUBLIC + ${CLASSES_PATH} + ${COCOS2DX_ROOT_PATH}/cocos + ${COCOS2DX_ROOT_PATH}/cocos/math + ${COCOS2DX_ROOT_PATH}/cocos/platform + ${COCOS2DX_ROOT_PATH}/cocos/platform/ohos/napi/modules + ${COCOS2DX_ROOT_PATH}/cocos/platform/ohos + ${COCOS2DX_ROOT_PATH}/cocos/platform/ohos/napi/common + ${COCOS2DX_ROOT_PATH}/cocos/platform/ohos/napi + ${COCOS2DX_ROOT_PATH}/cocos/platform/ohos/napi/render + ) + target_include_directories(cocos2d PUBLIC ${COCOS2DX_HEADER_PUBLIC}) + target_compile_definitions(cocos2d PUBLIC -DUSE_FILE32API -DOpenHarmony) + target_compile_options(cocos2d PUBLIC -Wno-psabi) else() message( FATAL_ERROR "Unsupported platform, CMake will exit" ) endif() -foreach(pkg ZLIB MINIZIP JPEG PNG TIFF TinyXML2 FREETYPE WEBSOCKETS CURL FLATBUFFERS XXHASH) +if (OHOS) + target_link_libraries(cocos2d ${Drawing-lib} ${libace-lib} ${GLES-lib} ${libnapi-lib} ${libuv-lib} ${rawfile-lib} ${EGL-lib} ${hilog-lib} libohaudio.so libavplayer.so libnative_window.so libnative_buffer.so) +endif() + +foreach(pkg ZLIB MINIZIP JPEG PNG TIFF TinyXML2 FREETYPE WEBSOCKETS OPENSSL CURL FLATBUFFERS XXHASH) cocos_use_pkg(cocos2d ${pkg}) endforeach() +if(OHOS) +target_link_libraries(cocos2d + # ext_cpufeatures TBD need fixed? + ext_pvmp3dec + ext_tremolo + ) +endif(OHOS) + if(LINUX) set(glfw_other_linker_flags X11) endif(LINUX) diff --git a/cocos/audio/AudioEngine.cpp b/cocos/audio/AudioEngine.cpp index 252c8de835c1..3c66d0655263 100644 --- a/cocos/audio/AudioEngine.cpp +++ b/cocos/audio/AudioEngine.cpp @@ -24,7 +24,7 @@ #include "platform/CCPlatformConfig.h" -#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 #include "audio/include/AudioEngine.h" #include "platform/CCFileUtils.h" @@ -32,6 +32,8 @@ #if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID #include "android/AudioEngine-inl.h" +#elif CC_TARGET_PLATFORM == CC_PLATFORM_OHOS +#include "ohos/AudioEngine-inl.h" #elif CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC #include "apple/AudioEngine-inl.h" #elif CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 @@ -63,6 +65,10 @@ AudioEngineImpl* AudioEngine::_audioEngineImpl = nullptr; void AudioEngine::end() { +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + stopAll(); +#endif + delete _audioEngineImpl; _audioEngineImpl = nullptr; @@ -227,6 +233,11 @@ void AudioEngine::resumeAll() void AudioEngine::stop(int audioID) { +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + if(!_audioEngineImpl){ + return; + } +#endif auto it = _audioIDInfoMap.find(audioID); if (it != _audioIDInfoMap.end()){ _audioEngineImpl->stop(audioID); @@ -267,6 +278,21 @@ void AudioEngine::stopAll() void AudioEngine::uncache(const std::string &filePath) { if(_audioPathIDMap.find(filePath) != _audioPathIDMap.end()){ +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + auto lst = _audioPathIDMap[filePath]; + for (auto it = lst.begin() ; it != lst.end(); ++it) { + auto audioID = *it; + _audioEngineImpl->stop(audioID); + + auto itInfo = _audioIDInfoMap.find(audioID); + if (itInfo != _audioIDInfoMap.end()){ + if (itInfo->second.profileHelper) { + itInfo->second.profileHelper->audioIDs.remove(audioID); + } + _audioIDInfoMap.erase(audioID); + } + } +#else auto itEnd = _audioPathIDMap[filePath].end(); for (auto it = _audioPathIDMap[filePath].begin() ; it != itEnd; ++it) { auto audioID = *it; @@ -280,6 +306,7 @@ void AudioEngine::uncache(const std::string &filePath) _audioIDInfoMap.erase(audioID); } } +#endif _audioPathIDMap.erase(filePath); } @@ -415,4 +442,21 @@ AudioProfile* AudioEngine::getProfile(const std::string &name) } } +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) +void AudioEngine::preload(const std::string &filePath, std::function callback) { + + lazyInit(); + + if (_audioEngineImpl) { + if (!FileUtils::getInstance()->isFileExist(filePath)) { + if (callback) { + callback(false); + } + return; + } + + _audioEngineImpl->preload(filePath, callback); + } +} +#endif #endif diff --git a/cocos/audio/CMakeLists.txt b/cocos/audio/CMakeLists.txt index 27437db0d436..2e53a83653e6 100644 --- a/cocos/audio/CMakeLists.txt +++ b/cocos/audio/CMakeLists.txt @@ -43,6 +43,40 @@ elseif(MACOSX) ${COCOS_AUDIO_PLATFORM_SRC_C} PROPERTIES LANGUAGE C ) +elseif(OHOS) + set(COCOS_AUDIO_PLATFORM_SRC + audio/ohos/SimpleAudioEngine.cpp + audio/ohos/AudioEngine-inl.cpp + audio/ohos/CCThreadPool.cpp + audio/ohos/AssetFd.cpp + audio/ohos/AudioDecoder.cpp + audio/ohos/AudioDecoderProvider.cpp + audio/ohos/AudioDecoderSLES.cpp + audio/ohos/AudioDecoderOgg.cpp + audio/ohos/AudioDecoderMp3.cpp + audio/ohos/AudioDecoderWav.cpp + audio/ohos/AudioPlayerProvider.cpp + audio/ohos/AudioResampler.cpp + audio/ohos/AudioResamplerCubic.cpp + # audio/ohos/CCData.cpp + audio/ohos/PcmBufferProvider.cpp + audio/ohos/PcmAudioPlayer.cpp + audio/ohos/PcmData.cpp + audio/ohos/PcmAudioService.cpp + audio/ohos/UrlAudioPlayer.cpp + audio/ohos/AudioMixerController.cpp + audio/ohos/AudioMixer.cpp + audio/ohos/mp3reader.cpp + audio/ohos/tinysndfile.cpp + audio/ohos/Track.cpp + + audio/ohos/audio_utils/RefCounted.cpp + audio/ohos/audio_utils/Value.cpp + audio/ohos/audio_utils/minifloat.cpp + audio/ohos/audio_utils/primitives.cpp + audio/ohos/utils/Utils.cpp + # audio/ohos/utils/CCAudioFileUtilsOhos.cpp + ) endif() list(APPEND COCOS_AUDIO_SRC ${COCOS_AUDIO_PLATFORM_SRC}) diff --git a/cocos/audio/include/AudioEngine.h b/cocos/audio/include/AudioEngine.h index 6b3945fb613c..d43545638059 100644 --- a/cocos/audio/include/AudioEngine.h +++ b/cocos/audio/include/AudioEngine.h @@ -23,7 +23,7 @@ ****************************************************************************/ #include "platform/CCPlatformConfig.h" -#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 #ifndef __AUDIO_ENGINE_H_ #define __AUDIO_ENGINE_H_ @@ -281,6 +281,22 @@ class EXPORT_DLL AudioEngine */ static AudioProfile* getProfile(const std::string &profileName); +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + /**ohos + * Preload audio file. + * @param filePath The file path of an audio. + */ + static void preload(const std::string& filePath) { preload(filePath, nullptr); } + + /**ohos + * Preload audio file. + * @param filePath The file path of an audio. + * @param callback A callback which will be called after loading is finished. + */ + static void preload(const std::string& filePath, std::function callback); +#endif + + protected: static void remove(int audioID); diff --git a/cocos/audio/ohos/AssetFd.cpp b/cocos/audio/ohos/AssetFd.cpp new file mode 100644 index 000000000000..6a22eab51a2f --- /dev/null +++ b/cocos/audio/ohos/AssetFd.cpp @@ -0,0 +1,45 @@ +/**************************************************************************** +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 "AssetFd" + +#include "cutils/log.h" +#include "AssetFd.h" + +namespace cocos2d { namespace experimental { + + AssetFd::AssetFd(int assetFd) + : _assetFd(assetFd) { + } + + AssetFd::~AssetFd() { + ALOGV("~AssetFd: %d", _assetFd); + if (_assetFd > 0) { + ::close(_assetFd); + _assetFd = 0; + } + }; + + }} // namespace CocosDenshion diff --git a/cocos/audio/ohos/AssetFd.h b/cocos/audio/ohos/AssetFd.h new file mode 100644 index 000000000000..b2ba2e1ac7c8 --- /dev/null +++ b/cocos/audio/ohos/AssetFd.h @@ -0,0 +1,42 @@ +/**************************************************************************** +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 AssetFd { + public: + AssetFd(int assetFd); + ~AssetFd(); + + inline int getFd() const { return _assetFd; }; + + private: + int _assetFd; + }; + + }} // namespace CocosDenshion diff --git a/cocos/audio/ohos/AudioBufferProvider.h b/cocos/audio/ohos/AudioBufferProvider.h new file mode 100644 index 000000000000..ae8c2f9a6ad4 --- /dev/null +++ b/cocos/audio/ohos/AudioBufferProvider.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include "utils/Errors.h" + +namespace cocos2d { namespace experimental { +// ---------------------------------------------------------------------------- + + class AudioBufferProvider { + public: + // IDEA: 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 CocosDenshion diff --git a/cocos/audio/ohos/AudioDecoder.cpp b/cocos/audio/ohos/AudioDecoder.cpp new file mode 100644 index 000000000000..21275d8df2bd --- /dev/null +++ b/cocos/audio/ohos/AudioDecoder.cpp @@ -0,0 +1,261 @@ +/**************************************************************************** +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" + +#include "AudioDecoder.h" +#include "AudioResampler.h" +#include "PcmBufferProvider.h" + +#include +#include + +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 = static_cast(offset); + else if (whence == SEEK_CUR) + thiz->_fileCurrPos = static_cast(thiz->_fileCurrPos + offset); + else if (whence == SEEK_END) + thiz->_fileCurrPos = static_cast(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 { + 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) { + 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); + auto outputFrames = static_cast(((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()) { + Ovalues.push_back(static_cast(outputFrames)); + } + for (size_t i = 0, j = 0; i < outputFrames;) { + size_t thisFrames = Ovalues[j++]; + if (j >= Ovalues.size()) { + j = 0; + } + if (thisFrames == 0 || thisFrames > outputFrames - i) { + thisFrames = outputFrames - i; + } + int outFrames = static_cast(resampler->resample(static_cast(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 + + for (size_t i = 0; i < outputFrames; i++) { + 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); + } + } + + // Reset result + _result.numFrames = static_cast(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) { + ALOGI("Audio channel count is 2, no need to interleave"); + return true; + } 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; + } + + ALOGE("Audio channel count (%d) is wrong, interleave only supports converting mono to stereo!", _result.numChannels); + return false; + } + + } // namespace cocos2d { namespace experimental +} \ No newline at end of file diff --git a/cocos/audio/ohos/AudioDecoder.h b/cocos/audio/ohos/AudioDecoder.h new file mode 100644 index 000000000000..6cad3c90e7ab --- /dev/null +++ b/cocos/audio/ohos/AudioDecoder.h @@ -0,0 +1,62 @@ +/**************************************************************************** +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 "OpenSLHelper.h" +#include "PcmData.h" +#include "base/CCData.h" + +namespace cocos2d { + namespace experimental { + + class AudioDecoder { + public: + AudioDecoder(); + virtual ~AudioDecoder(); + + virtual bool init(const std::string &url, int sampleRate); + + bool start(); + + inline PcmData getResult() { return _result; }; + + 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); // NOLINT + + std::string _url; + PcmData _result; + int _sampleRate; + Data _fileData; + size_t _fileCurrPos; + }; + } // namespace experimental +} // namespace cocos2d { namespace experimental diff --git a/cocos/audio/ohos/AudioDecoderMp3.cpp b/cocos/audio/ohos/AudioDecoderMp3.cpp new file mode 100644 index 000000000000..bd334e186cec --- /dev/null +++ b/cocos/audio/ohos/AudioDecoderMp3.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +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" + +#include "AudioDecoderMp3.h" +#include "mp3reader.h" +#include "platform/CCFileUtils.h" + +namespace cocos2d { namespace experimental { + + AudioDecoderMp3::AudioDecoderMp3() { + ALOGV("Create AudioDecoderMp3"); + } + + AudioDecoderMp3::~AudioDecoderMp3() { + } + + bool AudioDecoderMp3::decodeToPcm() { + _fileData = FileUtils::getInstance()->getDataFromFile(_url); + if (_fileData.isNull()) { + return false; + } + + mp3_callbacks callbacks; + callbacks.read = AudioDecoder::fileRead; + callbacks.seek = AudioDecoder::fileSeek; + callbacks.close = AudioDecoder::fileClose; + callbacks.tell = AudioDecoder::fileTell; + + 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) { + _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; + } + + ALOGE("Decode MP3 (%s) failed, channels: %d, rate: %d, frames: %d", _url.c_str(), numChannels, sampleRate, numFrames); + return false; + } + + } // namespace cocos2d { namespace experimental +} \ No newline at end of file diff --git a/cocos/audio/ohos/AudioDecoderMp3.h b/cocos/audio/ohos/AudioDecoderMp3.h new file mode 100644 index 000000000000..4f0e8b88bee8 --- /dev/null +++ b/cocos/audio/ohos/AudioDecoderMp3.h @@ -0,0 +1,41 @@ +/**************************************************************************** +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 "AudioDecoder.h" + +namespace cocos2d { namespace experimental { + + class AudioDecoderMp3 : public AudioDecoder { + protected: + AudioDecoderMp3(); + virtual ~AudioDecoderMp3(); + + virtual bool decodeToPcm() override; + + friend class AudioDecoderProvider; + }; + + }} // namespace cocos2d { namespace experimental \ No newline at end of file diff --git a/cocos/audio/ohos/AudioDecoderOgg.cpp b/cocos/audio/ohos/AudioDecoderOgg.cpp new file mode 100644 index 000000000000..1fa5bfe3c120 --- /dev/null +++ b/cocos/audio/ohos/AudioDecoderOgg.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +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 "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 diff --git a/cocos/audio/ohos/AudioDecoderOgg.h b/cocos/audio/ohos/AudioDecoderOgg.h new file mode 100644 index 000000000000..86acf1cf2b74 --- /dev/null +++ b/cocos/audio/ohos/AudioDecoderOgg.h @@ -0,0 +1,44 @@ +/**************************************************************************** +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 "AudioDecoder.h" + +#include "Tremolo/ivorbisfile.h" + +namespace cocos2d { namespace experimental { + + class AudioDecoderOgg : public AudioDecoder { + protected: + AudioDecoderOgg(); + virtual ~AudioDecoderOgg(); + + static int fseek64Wrap(void *datasource, ogg_int64_t off, int whence); + virtual bool decodeToPcm() override; + + friend class AudioDecoderProvider; + }; + + }} // namespace cocos2d { namespace experimental \ No newline at end of file diff --git a/cocos/audio/ohos/AudioDecoderProvider.cpp b/cocos/audio/ohos/AudioDecoderProvider.cpp new file mode 100644 index 000000000000..9c79efdf9904 --- /dev/null +++ b/cocos/audio/ohos/AudioDecoderProvider.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You shall + not use Cocos Creator software for developing other software or tools that's + used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. + + 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 "AudioDecoderProvider.h" +#include "AudioDecoderMp3.h" +#include "AudioDecoderOgg.h" +#include "AudioDecoderSLES.h" +#include "AudioDecoderWav.h" +#include "platform/CCFileUtils.h" + +namespace cocos2d { namespace experimental { + + cocos2d::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); + ALOGE("url:%s, extension:%s, sampleRate:%d", url.c_str(), extension.c_str(), sampleRate); + 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/ohos/AudioDecoderProvider.h b/cocos/audio/ohos/AudioDecoderProvider.h new file mode 100644 index 000000000000..043889cb82da --- /dev/null +++ b/cocos/audio/ohos/AudioDecoderProvider.h @@ -0,0 +1,40 @@ +/**************************************************************************** +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 "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 diff --git a/cocos/audio/ohos/AudioDecoderSLES.cpp b/cocos/audio/ohos/AudioDecoderSLES.cpp new file mode 100644 index 000000000000..ef530455bce5 --- /dev/null +++ b/cocos/audio/ohos/AudioDecoderSLES.cpp @@ -0,0 +1,292 @@ +/**************************************************************************** +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 "AudioDecoderSLES.h" +#include "platform/CCFileUtils.h" + +#include +#include + +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; //NOLINT(bugprone-reserved-identifier, readability-identifier-naming) + + static int toBufferSizeInBytes(int bufferSizeInFrames, int sampleSize, int channelCount) { + return bufferSizeInFrames * sampleSize * channelCount; + } + + static int BUFFER_SIZE_IN_BYTES = 0; // NOLINT(readability-identifier-naming) + + static void checkMetaData(int index, const char *key) { + if (index != -1) { + ALOGV("Key %s is at index %d", key, index); + } else { + ALOGV("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) { + auto *thiz = reinterpret_cast(context); + thiz->prefetchCallback(caller, event); + } + + static void decPlayCallback(CCSLBufferQueueItf queueItf, void *context) { + auto *thiz = reinterpret_cast(context); + thiz->decodeToPcmCallback(queueItf); + } + + static void decProgressCallback(SLPlayItf caller, void *context, SLuint32 event) { + auto *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 = static_cast(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() { + // 当前oh不支持,直接返回true + 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 = *reinterpret_cast(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 = *reinterpret_cast(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 = *reinterpret_cast(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 = *reinterpret_cast(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 = *reinterpret_cast(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 = *reinterpret_cast(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) { + CC_UNUSED_PARAM(caller); + 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(CCSLBufferQueueItf queueItf) { + _isDecodingCallbackInvoked = true; + ALOGV("%s ...", __FUNCTION__); + _counter++; + SLresult result; + // IDEA: ?? + 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/ohos/AudioDecoderSLES.h b/cocos/audio/ohos/AudioDecoderSLES.h new file mode 100644 index 000000000000..18a63143ebf9 --- /dev/null +++ b/cocos/audio/ohos/AudioDecoderSLES.h @@ -0,0 +1,96 @@ +/**************************************************************************** +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 "AudioDecoder.h" +#include "utils/Compat.h" + +namespace cocos2d { namespace experimental { + + class AudioDecoderSLES : public AudioDecoder { + protected: + AudioDecoderSLES(); + ~AudioDecoderSLES() override; + + bool init(SLEngineItf engineItf, const std::string &url, int bufferSizeInFrames, int sampleRate, const FdGetterCallback &fdGetterCallback); + bool decodeToPcm() override; + + private: + void queryAudioInfo(); + + void signalEos(); + void decodeToPcmCallback(CCSLBufferQueueItf 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_ { //NOLINT(modernize-use-using, readability-identifier-naming) + 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/ohos/AudioDecoderWav.cpp b/cocos/audio/ohos/AudioDecoderWav.cpp new file mode 100644 index 000000000000..e02c31530d3d --- /dev/null +++ b/cocos/audio/ohos/AudioDecoderWav.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +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 "AudioDecoderWav.h" +#include "tinysndfile.h" +#include "platform/CCFileUtils.h" + +namespace cocos2d { namespace experimental { + using namespace sf; //NOLINT + AudioDecoderWav::AudioDecoderWav() { + ALOGV("Create AudioDecoderWav"); + } + + AudioDecoderWav::~AudioDecoderWav() = default; + + void *AudioDecoderWav::onWavOpen(const char * /*path*/, void *user) { + return user; + } + + int AudioDecoderWav::onWavSeek(void *datasource, long offset, int whence) { //NOLINT(google-runtime-int) + return AudioDecoder::fileSeek(datasource, static_cast(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 = nullptr; + 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(int16_t) * info.frames * info.channels; + auto *buf = static_cast(malloc(bufSize)); + sf_count_t readFrames = sf_readf_short(handle, reinterpret_cast(buf), info.frames); + CC_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 = static_cast(1.0F * info.frames / _result.sampleRate); //NOLINT + + free(buf); + ret = true; + } while (false); + + if (handle != nullptr) { + sf_close(handle); + } + + return ret; + } + + } }// namespace cocos2d { namespace experimental diff --git a/cocos/audio/ohos/AudioDecoderWav.h b/cocos/audio/ohos/AudioDecoderWav.h new file mode 100644 index 000000000000..e0a18f5db93f --- /dev/null +++ b/cocos/audio/ohos/AudioDecoderWav.h @@ -0,0 +1,46 @@ +/**************************************************************************** +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 "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/ohos/AudioEngine-inl.cpp b/cocos/audio/ohos/AudioEngine-inl.cpp new file mode 100644 index 000000000000..416f97a9f946 --- /dev/null +++ b/cocos/audio/ohos/AudioEngine-inl.cpp @@ -0,0 +1,544 @@ +/**************************************************************************** + 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. + ****************************************************************************/ +#include +#define LOG_TAG "AudioEngineImpl" + +#include "AudioEngine-inl.h" +#include + + +#include +#include +#include + + +#include "audio/include/AudioEngine.h" + +#include "platform/ohos/CCFileUtils-ohos.h" +#include "AudioDecoder.h" +#include "AudioDecoderProvider.h" +#include "AudioPlayerProvider.h" +#include "IAudioPlayer.h" +#include "ICallerThreadUtils.h" +#include "UrlAudioPlayer.h" +#include "cutils/log.h" + +#include "base/CCDirector.h" +#include "base/CCScheduler.h" +#include "base/CCEventDispatcher.h" +#include "base/CCEventType.h" +#include "base/CCEventListenerCustom.h" + +using namespace cocos2d; +using namespace cocos2d::experimental; //NOLINT + +namespace { + AudioEngineImpl *gAudioImpl = nullptr; + int outputSampleRate = 48000; + +// TODO(hack) : There is currently a bug in the opensles module, +// so openharmony must configure a fixed size, otherwise the callback will be suspended + int bufferSizeInFrames = 2048; + + + void getAudioInfo() { + + } +} // namespace + +class CallerThreadUtils : public ICallerThreadUtils { +public: + void performFunctionInCallerThread(const std::function &func) override { + cocos2d::Director::sharedDirector()->getScheduler()->performFunctionInCocosThread(func); + + }; + + std::thread::id getCallerThreadId() override { + return _tid; + }; + + void setCallerThreadId(std::thread::id tid) { + _tid = tid; + }; + +private: + std::thread::id _tid; +}; + +static CallerThreadUtils gCallerThreadUtils; + +static int fdGetter(const std::string &url, off_t *start, off_t *length) { + int fd = -1; + + RawFileDescriptor descriptor; + FileUtilsOhos *utils = dynamic_cast(FileUtils::getInstance()); + utils->getRawFileDescriptor(url, descriptor); + fd = descriptor.fd; + + if (fd <= 0) { + ALOGE("Failed to open file descriptor for '%{public}s'", url.c_str()); + } + + return fd; +}; + +//==================================================== +AudioEngineImpl::AudioEngineImpl() + : _engineObject(nullptr), + _engineEngine(nullptr), + _outputMixObject(nullptr), + _audioPlayerProvider(nullptr), + _onPauseListener(nullptr), + _onResumeListener(nullptr), + _audioIDIndex(0), + _lazyInitLoop(true) { + gCallerThreadUtils.setCallerThreadId(std::this_thread::get_id()); + gAudioImpl = this; + getAudioInfo(); +} + +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); + } + + gAudioImpl = nullptr; +} + +bool AudioEngineImpl::init() { + bool ret = false; + do { + // create engine + auto result = slCreateEngine(&_engineObject, 0, nullptr, 0, nullptr, nullptr); + if (SL_RESULT_SUCCESS != result) { + ALOGE("create opensl engine fail"); + break; + } + + // realize the engine + result = (*_engineObject)->Realize(_engineObject, SL_BOOLEAN_FALSE); + if (SL_RESULT_SUCCESS != result) { + ALOGE("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) { + ALOGE("get the engine interface fail"); + break; + } + + _audioPlayerProvider = new AudioPlayerProvider(_engineEngine, outputSampleRate, fdGetter, &gCallerThreadUtils); + + _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) { + ALOGE("play2d, _audioPlayers.size=%{public}d", (int)_audioPlayers.size()); + auto audioId = AudioEngine::INVALID_AUDIO_ID; + + do { + if (_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: %{public}d", static_cast(state)); + return; + } + + int id = player->getId(); + + ALOGE("Removing player id=%{public}d, state:%{public}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->play(); + } else { + ALOGE("Oops, player is null ..."); + return AudioEngine::INVALID_AUDIO_ID; + } + + AudioEngine::_audioIDInfoMap[audioId].state = AudioEngine::AudioState::PLAYING; + + } while (false); + + 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::rewindMusic(int audioID) { + stop(audioID); + auto iter = _audioPlayers.find(audioID); + if (iter != _audioPlayers.end()) { + auto *player = iter->second; + player->play(); + } +} + +bool AudioEngineImpl::isMusicPlaying(int audioID) +{ + auto iter = _audioPlayers.find(audioID); + if (iter != _audioPlayers.end()) { + auto *player = iter->second; + auto state = player->getState(); + return state == IAudioPlayer::State::PLAYING; + } + return false; +} + +float AudioEngineImpl::getMusicVolume(int audioID) +{ + auto iter = _audioPlayers.find(audioID); + if (iter != _audioPlayers.end()) { + auto *player = iter->second; + return player->getVolume(); + } + return 0; +} + + +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::getDurationFromFile(const std::string &filePath) { + if (_audioPlayerProvider != nullptr) { + auto fullPath = FileUtils::getInstance()->fullPathForFilename(filePath); + return _audioPlayerProvider->getDurationFromFile(fullPath); + } + return 0; +} + +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, const 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(); + } +} + +void AudioEngineImpl::onPause() { + if (_audioPlayerProvider != nullptr) { + _audioPlayerProvider->pause(); + } +} + +void AudioEngineImpl::onResume() { + if (_audioPlayerProvider != nullptr) { + _audioPlayerProvider->resume(); + } +} + +PCMHeader AudioEngineImpl::getPCMHeader(const char *url) { + PCMHeader header{}; + std::string fileFullPath = FileUtils::getInstance()->fullPathForFilename(url); + if (fileFullPath.empty()) { + ALOGD("file %{public}s does not exist or failed to load", url); + return header; + } + if (_audioPlayerProvider->getPcmHeader(url, header)) { + ALOGD("file %{public}s pcm data already cached", url); + return header; + } + + AudioDecoder *decoder = AudioDecoderProvider::createAudioDecoder(_engineEngine, fileFullPath, bufferSizeInFrames, outputSampleRate, fdGetter); + + if (decoder == nullptr) { + ALOGD("decode %{public}s failed, the file formate might not support", url); + return header; + } + if (!decoder->start()) { + ALOGD("[Audio Decoder] Decode failed %{public}s", url); + return header; + } + // Ready to decode + do { + PcmData data = decoder->getResult(); + header.bytesPerFrame = data.bitsPerSample / 8; + header.channelCount = data.numChannels; + header.dataFormat = AudioDataFormat::SIGNED_16; + header.sampleRate = data.sampleRate; + header.totalFrames = data.numFrames; + } while (false); + + AudioDecoderProvider::destroyAudioDecoder(&decoder); + return header; +} + +std::vector AudioEngineImpl::getOriginalPCMBuffer(const char *url, uint32_t channelID) { + std::string fileFullPath = FileUtils::getInstance()->fullPathForFilename(url); + std::vector pcmData; + if (fileFullPath.empty()) { + ALOGD("file %{public}s does not exist or failed to load", url); + return pcmData; + } + PcmData data; + if (_audioPlayerProvider->getPcmData(url, data)) { + ALOGD("file %{public}s pcm data already cached", url); + } else { + AudioDecoder *decoder = AudioDecoderProvider::createAudioDecoder(_engineEngine, fileFullPath, bufferSizeInFrames, outputSampleRate, fdGetter); + if (decoder == nullptr) { + ALOGD("decode %{public}s failed, the file formate might not support", url); + return pcmData; + } + if (!decoder->start()) { + ALOGD("[Audio Decoder] Decode failed %{public}s", url); + return pcmData; + } + data = decoder->getResult(); + _audioPlayerProvider->registerPcmData(url, data); + AudioDecoderProvider::destroyAudioDecoder(&decoder); + } + do { + const uint32_t channelCount = data.numChannels; + if (channelID >= channelCount) { + ALOGE("channelID invalid, total channel count is %{public}d but %{public}d is required", channelCount, channelID); + break; + } + // bytesPerSample = bitsPerSample / 8, according to 1 byte = 8 bits + const uint32_t bytesPerFrame = data.numChannels * data.bitsPerSample / 8; + const uint32_t numFrames = data.numFrames; + const uint32_t bytesPerChannelInFrame = bytesPerFrame / channelCount; + + pcmData.resize(bytesPerChannelInFrame * numFrames); + uint8_t *p = pcmData.data(); + char *tmpBuf = data.pcmBuffer->data(); // shared ptr + for (int itr = 0; itr < numFrames; itr++) { + memcpy(p, tmpBuf + itr * bytesPerFrame + channelID * bytesPerChannelInFrame, bytesPerChannelInFrame); + p += bytesPerChannelInFrame; + } + } while (false); + + return pcmData; +} diff --git a/cocos/audio/ohos/AudioEngine-inl.h b/cocos/audio/ohos/AudioEngine-inl.h new file mode 100644 index 000000000000..896573822601 --- /dev/null +++ b/cocos/audio/ohos/AudioEngine-inl.h @@ -0,0 +1,121 @@ +/**************************************************************************** + 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. + ****************************************************************************/ +#pragma once + + +#include +#include + +#include +#include "audio_utils/AudioDef.h" +#include "audio_utils/RefCounted.h" +//#include "base/Utils.h" +#include +#include + +#include "base/CCEventCustom.h" +#include "base/CCEventListener.h" + +#define MAX_AUDIOINSTANCES 13 + +#define ERRORLOG(msg) log("fun:%s,line:%d,msg:%s", __func__, __LINE__, #msg) + +namespace cocos2d { namespace experimental { + + class IAudioPlayer; + class AudioPlayerProvider; + + class AudioEngineImpl; + + class AudioEngineImpl : public RefCounted { + public: + AudioEngineImpl(); + ~AudioEngineImpl() override; + const int INVALID_AUDIO_ID = -1; + bool init(); + int play2d(const std::string &filePath, bool loop, float volume); + + void setVolume(int audioID, float volume); + void setLoop(int audioID, bool loop); + void pause(int audioID); + + void resume(int audioID); + + void stop(int audioID); + + void stopAll(); + float getDuration(int audioID); + float getDurationFromFile(const std::string &filePath); + float getCurrentTime(int audioID); + bool setCurrentTime(int audioID, float time); + void setFinishCallback(int audioID, const std::function &callback); + + void uncache(const std::string &filePath); + void uncacheAll(); + void preload(const std::string &filePath, const std::function &callback); + + void onResume(); + void onPause(); + + void rewindMusic(int audioID); + bool isMusicPlaying(int audioID); + + float getMusicVolume(int audioID); + + + void setAudioFocusForAllPlayers(bool isFocus); + + PCMHeader getPCMHeader(const char *url); + std::vector getOriginalPCMBuffer(const char *url, uint32_t channelID); + + private: + void onEnterBackground(cocos2d::EventCustom* event); + void onEnterForeground(cocos2d::EventCustom* event); + + static AudioEngineImpl *audioEngineImpl; + + // engine interfaces + SLObjectItf _engineObject; + SLEngineItf _engineEngine; + + // output mix interfaces + SLObjectItf _outputMixObject; + + //audioID,AudioInfo + std::unordered_map _audioPlayers; + std::unordered_map> _callbackMap; + + // UrlAudioPlayers which need to resumed while entering foreground + std::unordered_map _urlAudioPlayersNeedResume; + AudioPlayerProvider *_audioPlayerProvider; + cocos2d::EventListener* _onPauseListener; + cocos2d::EventListener* _onResumeListener; + + int _audioIDIndex; + + bool _lazyInitLoop; + }; + + }} // namespace cocos2d { namespace experimental diff --git a/cocos/audio/ohos/AudioMixer.cpp b/cocos/audio/ohos/AudioMixer.cpp new file mode 100644 index 000000000000..838f53efa388 --- /dev/null +++ b/cocos/audio/ohos/AudioMixer.cpp @@ -0,0 +1,2039 @@ +// clang-format off +#define LOG_TAG "AudioMixer" +#define LOG_NDEBUG 1 + +#include +#include +#include +#include +#include + +#include "audio.h" +#include "audio_utils/include/audio_utils/primitives.h" +#include "AudioMixerOps.h" +#include "AudioMixer.h" + + +// clang-format on +// 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 + +// REFINE: 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; //NOLINT + +// 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; //NOLINT + +// Set to default copy buffer size in frames for input processing. +static const size_t kCopyBufferFrameCount = 256; //NOLINT + +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 = nullptr; + mState.resampleTemp = nullptr; + //cjh mState.mLog = &mDummyLog; + // mState.reserved + + // IDEA: 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 = nullptr; + //cjh t->downmixerBufferProvider = nullptr; + // t->mReformatBufferProvider = nullptr; + // t->mTimestretchBufferProvider = nullptr; + 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 = nullptr; + t->buffer.raw = nullptr; + // no initialization needed + // t->buffer.frameCount + t->hook = nullptr; + t->in = nullptr; + t->resampler = nullptr; + t->sampleRate = mSampleRate; + // setParameter(name, TRACK, MAIN_BUFFER, mixBuffer) is required before enable(name) + t->mainBuffer = nullptr; + t->auxBuffer = nullptr; + t->mInputBufferProvider = nullptr; + //cjh t->mReformatBufferProvider = nullptr; + // t->downmixerBufferProvider = nullptr; + // t->mPostDownmixReformatBufferProvider = nullptr; + // t->mTimestretchBufferProvider = nullptr; + 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 +// REFINE: 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 = nullptr; + 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 != nullptr) { + // // this track had previously been configured with a downmixer, delete it + // ALOGV(" deleting old downmixer"); + // delete downmixerBufferProvider; + // downmixerBufferProvider = nullptr; + // 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 /* REFINE: 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 != nullptr) { + // delete mReformatBufferProvider; + // mReformatBufferProvider = nullptr; + // requiresReconfigure = true; + // } + // if (mPostDownmixReformatBufferProvider != nullptr) { + // delete mPostDownmixReformatBufferProvider; + // mPostDownmixReformatBufferProvider = nullptr; + // 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 << name); + } + // delete the resampler + delete track.resampler; + track.resampler = nullptr; + // delete the downmixer + mState.tracks[name].unprepareForDownmix(); + // delete the reformatter + mState.tracks[name].unprepareForReformat(); + // delete the timestretch provider + //cjh delete track.mTimestretchBufferProvider; + // track.mTimestretchBufferProvider = nullptr; + mTrackNames &= ~(1 << name); +} + +void AudioMixer::enable(int name) { + name -= TRACK0; + ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name); + track_t &track = mState.tracks[name]; + + if (!track.enabled) { + track.enabled = true; + ALOGV("enable(%d)", name); + invalidateState(1 << name); + } +} + +void AudioMixer::disable(int name) { + name -= TRACK0; + ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name); + track_t &track = mState.tracks[name]; + + if (track.enabled) { + track.enabled = false; + ALOGV("disable(%d)", name); + invalidateState(1 << name); + } +} + +/* Sets the volume ramp variables for the AudioMixer. + * + * The volume ramp variables are used to transition from the previous + * volume to the set volume. ramp controls the duration of the transition. + * Its value is typically one state framecount period, but may also be 0, + * meaning "immediate." + * + * IDEA: 1) Volume ramp is enabled only if there is a nonzero integer increment + * even if there is a nonzero floating point increment (in that case, the volume + * change is immediate). This restriction should be changed when the legacy mixer + * is removed (see #2). + * IDEA: 2) Integer volume variables are used for Legacy mixing and should be removed + * when no longer needed. + * + * @param newVolume set volume target in floating point [0.0, 1.0]. + * @param ramp number of frames to increment over. if ramp is 0, the volume + * should be set immediately. Currently ramp should not exceed 65535 (frames). + * @param pIntSetVolume pointer to the U4.12 integer target volume, set on return. + * @param pIntPrevVolume pointer to the U4.28 integer previous volume, set on return. + * @param pIntVolumeInc pointer to the U4.28 increment per output audio frame, set on return. + * @param pSetVolume pointer to the float target volume, set on return. + * @param pPrevVolume pointer to the float previous volume, set on return. + * @param pVolumeInc pointer to the float increment per output audio frame, set on return. + * @return true if the volume has changed, false if volume is same. + */ +static inline bool setVolumeRampVariables(float newVolume, int32_t ramp, + int16_t *pIntSetVolume, int32_t *pIntPrevVolume, int32_t *pIntVolumeInc, + float *pSetVolume, float *pPrevVolume, float *pVolumeInc) { + // check floating point volume to see if it is identical to the previously + // set volume. + // We do not use a tolerance here (and reject changes too small) + // as it may be confusing to use a different value than the one set. + // If the resulting volume is too small to ramp, it is a direct set of the volume. + if (newVolume == *pSetVolume) { + return false; + } + if (newVolume < 0) { + newVolume = 0; // should not have negative volumes + } else { + switch (fpclassify(newVolume)) { + case FP_SUBNORMAL: + case FP_NAN: + newVolume = 0; + break; + case FP_ZERO: + break; // zero volume is fine + case FP_INFINITE: + // Infinite volume could be handled consistently since + // floating point math saturates at infinities, + // but we limit volume to unity gain float. + // ramp = 0; break; + // + newVolume = AudioMixer::UNITY_GAIN_FLOAT; + break; + case FP_NORMAL: + default: + // Floating point does not have problems with overflow wrap + // that integer has. However, we limit the volume to + // unity gain here. + // REFINE: Revisit the volume limitation and perhaps parameterize. + if (newVolume > 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 >= static_cast(AudioMixer::UNITY_GAIN_INT)) ? AudioMixer::UNITY_GAIN_INT : static_cast(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)); + auto *valueBuf = reinterpret_cast(value); + + switch (target) { + case TRACK: + switch (param) { + case CHANNEL_MASK: { + const auto 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: { + auto 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; + // IDEA: 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: { + auto format = static_cast(valueInt); + if (track.mMixerFormat != format) { + track.mMixerFormat = format; + ALOGV("setParameter(TRACK, MIXER_FORMAT, %#x)", format); + } + } break; + case MIXER_CHANNEL_MASK: { + const auto 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(static_cast(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 = nullptr; + 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 (static_cast(param) >= VOLUME0 && static_cast(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 != nullptr) { + if (sampleRate != trackSampleRate) { + sampleRate = trackSampleRate; + if (resampler == nullptr) { + 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 + // IDEA: 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; + // } + + // REFINE: 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 != nullptr*/ + ? static_cast(mMixerChannelCount) + : static_cast(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 == nullptr && + // 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 == nullptr) { + // // REFINE: 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 != nullptr + // ? 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. + * + * IDEA: 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]); + } + } + } + /* REFINE: 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 (static_cast(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 != nullptr) { + // mState.tracks[name].mReformatBufferProvider->reset(); + // } else if (mState.tracks[name].downmixerBufferProvider != nullptr) { + // mState.tracks[name].downmixerBufferProvider->reset(); + // } else if (mState.tracks[name].mPostDownmixReformatBufferProvider != nullptr) { + // mState.tracks[name].mPostDownmixReformatBufferProvider->reset(); + // } else if (mState.tracks[name].mTimestretchBufferProvider != nullptr) { + // 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::setBufferSize(size_t size) { + mState.frameCount = size; +} + +void AudioMixer::process__validate(state_t *state, int64_t pts) {//NOLINT + 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 << i; + changed &= ~mask; + track_t &t = state->tracks[i]; + (t.enabled ? enabled : disabled) |= mask; + } + state->enabledTracks &= ~disabled; + state->enabledTracks |= enabled; + + // compute everything we need... + int countActiveTracks = 0; + // REFINE: 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 << i); + + countActiveTracks++; + track_t &t = state->tracks[i]; + uint32_t n = 0; + // IDEA: 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 != nullptr) { + 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 // REFINE: 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 = nullptr; + } + if (state->resampleTemp) { + delete[] state->resampleTemp; + state->resampleTemp = nullptr; + } + 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 << i); + track_t &t = state->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,//NOLINT + 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 != nullptr) { + // 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,//NOLINT + 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 != nullptr)) { + 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 != nullptr); +} + +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 != nullptr)) { + const int16_t va = t->auxLevel; + do { + auto l = static_cast(*temp++ >> 12); + auto r = static_cast(*temp++ >> 12); + out[0] = mulAdd(l, vl, out[0]); + auto a = static_cast((static_cast(l) + r) >> 1); + out[1] = mulAdd(r, vr, out[1]); + out += 2; + aux[0] = mulAdd(a, va, aux[0]); + aux++; + } while (--frameCount); + } else { + do { + auto l = static_cast(*temp++ >> 12); + auto r = static_cast(*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,//NOLINT + int32_t *temp __unused, int32_t *aux) { + ALOGVV("track__16BitsStereo\n"); + const auto *in = static_cast(t->in); + + if (CC_UNLIKELY(aux != nullptr)) { + 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 = static_cast(*in++); + r = static_cast(*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 auto va = t->auxLevel; + do { + uint32_t rl = *reinterpret_cast(in); + auto a = static_cast((static_cast(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) * static_cast(*in++); + *out++ += (vr >> 16) * static_cast(*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,//NOLINT + int32_t *temp __unused, int32_t *aux) { + ALOGVV("track__16BitsMono\n"); + const auto *in = static_cast(t->in); + + if (CC_UNLIKELY(aux != nullptr)) { + // 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 auto va = 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) {//NOLINT + 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; + uint32_t e2 = e0; + int i = 31 - __builtin_clz(e1); + { + track_t &t1 = state->tracks[i]; + e2 &= ~(1 << i); + while (e2) { + i = 31 - __builtin_clz(e2); + e2 &= ~(1 << i); + track_t &t2 = state->tracks[i]; + if (CC_UNLIKELY(t2.mainBuffer != t1.mainBuffer)) { + e1 &= ~(1 << i); + } + } + e0 &= ~(e1); + + memset(t1.mainBuffer, 0, state->frameCount * t1.mMixerChannelCount * audio_bytes_per_sample(t1.mMixerFormat)); + } + + while (e1) { + i = 31 - __builtin_clz(e1); + e1 &= ~(1 << i); + { + track_t &t3 = state->tracks[i]; + size_t outFrames = state->frameCount; + while (outFrames) { + t3.buffer.frameCount = outFrames; + int64_t outputPTS = calculateOutputPTS( + t3, pts, state->frameCount - outFrames);//NOLINT + t3.bufferProvider->getNextBuffer(&t3.buffer, outputPTS); + if (t3.buffer.raw == nullptr) break; + outFrames -= t3.buffer.frameCount; + t3.bufferProvider->releaseBuffer(&t3.buffer); + } + } + } + } +} + +// generic code without resampling +void AudioMixer::process__genericNoResampling(state_t *state, int64_t pts) {//NOLINT + 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 << i); + track_t &t = state->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; + uint32_t e2 = e0; + int j = 31 - __builtin_clz(e1); + track_t &t1 = state->tracks[j]; + e2 &= ~(1 << j); + while (e2) { + j = 31 - __builtin_clz(e2); + e2 &= ~(1 << j); + track_t &t2 = state->tracks[j]; + if (CC_UNLIKELY(t2.mainBuffer != t1.mainBuffer)) { + e1 &= ~(1 << j); + } + } + e0 &= ~(e1); + // this assumes output 16 bits stereo, no resampling + int32_t *out = t1.mainBuffer; + size_t numFrames = 0; + do { + memset(outTemp, 0, sizeof(outTemp)); + e2 = e1; + while (e2) { + const int i = 31 - __builtin_clz(e2); + e2 &= ~(1 << i); + track_t &t = state->tracks[i]; + size_t outFrames = BLOCKSIZE; + int32_t *aux = nullptr; + if (CC_UNLIKELY(t.needs & NEEDS_AUX)) { + aux = t.auxBuffer + numFrames; + } + while (outFrames) { + // t.in == nullptr can happen if the track was flushed just after having + // been enabled for mixing. + if (t.in == nullptr) { + enabledTracks &= ~(1 << i); + e1 &= ~(1 << i); + break; + } + size_t inFrames = (t.frameCount > 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 != nullptr)) { + 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));//NOLINT + t.bufferProvider->getNextBuffer(&t.buffer, outputPTS); + t.in = t.buffer.raw; + if (t.in == nullptr) { + enabledTracks &= ~(1 << i); + e1 &= ~(1 << i); + break; + } + t.frameCount = t.buffer.frameCount; + } + } + } + + convertMixerFormat(out, t1.mMixerFormat, outTemp, t1.mMixerInFormat, + BLOCKSIZE * t1.mMixerChannelCount); + // REFINE: fix ugly casting due to choice of out pointer type + out = reinterpret_cast(reinterpret_cast(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 << i); + track_t &t = state->tracks[i]; + t.bufferProvider->releaseBuffer(&t.buffer); + } +} + +// generic code with resampling +void AudioMixer::process__genericResampling(state_t *state, int64_t pts) {//NOLINT + 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; + uint32_t e2 = e0; + int j = 31 - __builtin_clz(e1); + track_t &t1 = state->tracks[j]; + e2 &= ~(1 << j); + while (e2) { + j = 31 - __builtin_clz(e2); + e2 &= ~(1 << j); + track_t &t2 = state->tracks[j]; + if (CC_UNLIKELY(t2.mainBuffer != t1.mainBuffer)) { + e1 &= ~(1 << j); + } + } + e0 &= ~(e1); + int32_t *out = t1.mainBuffer; + memset(outTemp, 0, sizeof(*outTemp) * t1.mMixerChannelCount * state->frameCount); + while (e1) { + const int i = 31 - __builtin_clz(e1); + e1 &= ~(1 << i); + track_t &t = state->tracks[i]; + int32_t *aux = nullptr; + 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 == nullptr can happen if the track was flushed just after having + // been enabled for mixing. + if (t.in == nullptr) break; + + if (CC_UNLIKELY(aux != nullptr)) { + 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,//NOLINT + 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; + auto *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 == nullptr can happen if the track was flushed just after having + // been enabled for mixing. + if (in == nullptr || (((uintptr_t)in) & 3)) {//NOLINT + 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 +} + +/* REFINE: 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 != nullptr, 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 != nullptr); + } + } 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. + * REFINE: 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) {//NOLINT + 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);//NOLINT + t->bufferProvider->getNextBuffer(&b, outputPTS); + const TI *in = reinterpret_cast(b.raw); + + // in == nullptr can happen if the track was flushed just after having + // been enabled for mixing. + if (in == nullptr || (((uintptr_t)in) & 3)) {//NOLINT + 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 != nullptr) { + aux += channels; + } + numFrames -= b.frameCount; + + // release buffer + t->bufferProvider->releaseBuffer(&b); + } + if (ramp) { + t->adjustVolumeRamp(aux != nullptr, 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) {//NOLINT + ALOGVV("track__Resample\n"); + t->resampler->setSampleRate(t->sampleRate); + const bool ramp = t->needsRamp(); + if (ramp || aux != nullptr) { + // if ramp: resample with unity gain to temp buffer and scale/mix in 2nd step. + // if aux != nullptr: 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);//NOLINT + + 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);//NOLINT + } +} + +/* 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,//NOLINT + 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. REFINE: optimize out + break; + case AUDIO_FORMAT_PCM_16_BIT: + memcpy_to_i16_from_float(static_cast(out), static_cast(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(static_cast(out), static_cast(in), sampleCount); + break; + case AUDIO_FORMAT_PCM_16_BIT: + // two int16_t are produced per iteration + ditherAndClamp(static_cast(out), static_cast(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 reinterpret_cast(track__Resample); + case AUDIO_FORMAT_PCM_16_BIT: + return static_cast(track__Resample); + default: + LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat); + break; + } + break; + case TRACKTYPE_NORESAMPLEMONO: + switch (mixerInFormat) { + case AUDIO_FORMAT_PCM_FLOAT: + return reinterpret_cast(track__NoResample); + case AUDIO_FORMAT_PCM_16_BIT: + return static_cast(track__NoResample); + default: + LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat); + break; + } + break; + case TRACKTYPE_NORESAMPLE: + switch (mixerInFormat) { + case AUDIO_FORMAT_PCM_FLOAT: + return reinterpret_cast(track__NoResample); + case AUDIO_FORMAT_PCM_16_BIT: + return static_cast(track__NoResample); + default: + LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat); + break; + } + break; + default: + LOG_ALWAYS_FATAL("bad trackType: %d", trackType); + break; + } + return nullptr; +} + +/* Returns the proper process hook for mixing tracks. Currently works only for + * PROCESSTYPE_NORESAMPLEONETRACK, a mix involving one track, no resampling. + * + * REFINE: 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 nullptr; + } + 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 nullptr; +} + +// ---------------------------------------------------------------------------- +}} // namespace cocos2d { namespace experimental diff --git a/cocos/audio/ohos/AudioMixer.h b/cocos/audio/ohos/AudioMixer.h new file mode 100644 index 000000000000..87958901afb3 --- /dev/null +++ b/cocos/audio/ohos/AudioMixer.h @@ -0,0 +1,363 @@ +#pragma once + +#include +#include +#include + +#include "AudioBufferProvider.h" +#include "AudioResamplerPublic.h" + +#include "AudioResampler.h" +#include "audio.h" +#include "utils/Compat.h" + +// IDEA: 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) + // IDEA: 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); + void setBufferSize(size_t size); + + 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 { + // IDEA: this representation permits up to 8 channels + NEEDS_CHANNEL_COUNT__MASK = 0x00000007, // NOLINT(bugprone-reserved-identifier) + }; + + 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); //NOLINT(modernize-use-using) + static const int BLOCKSIZE = 16; // 4 cache lines + + struct track_t { + uint32_t needs; + + // REFINE: 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. + * + * REFINE: 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 != nullptr; } + void resetResampler() const { + if (resampler != nullptr) resampler->reset(); + } + void adjustVolumeRamp(bool aux, bool useFloat = false); + size_t getUnreleasedFrames() const { return resampler != nullptr ? 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); // NOLINT(modernize-use-using) + + // 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]; + // IDEA: 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;// NOLINT(readability-identifier-naming) + + // 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;// NOLINT(readability-identifier-naming) + + const uint32_t mSampleRate;// NOLINT(readability-identifier-naming) + + //cjh NBLog::Writer mDummyLog; +public: + //cjh void setLog(NBLog::Writer* log); +private: + state_t mState __attribute__((aligned(32)));// NOLINT(readability-identifier-naming) + + // 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);// NOLINT(bugprone-reserved-identifier, readability-identifier-naming) + static void track__nop(track_t *t, int32_t *out, size_t numFrames, int32_t *temp, int32_t *aux);// NOLINT(bugprone-reserved-identifier, readability-identifier-naming) + static void track__16BitsStereo(track_t *t, int32_t *out, size_t numFrames, int32_t *temp, int32_t *aux);// NOLINT(bugprone-reserved-identifier, readability-identifier-naming) + static void track__16BitsMono(track_t *t, int32_t *out, size_t numFrames, int32_t *temp, int32_t *aux);// NOLINT(bugprone-reserved-identifier, readability-identifier-naming) + 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);// NOLINT(bugprone-reserved-identifier, readability-identifier-naming) + static void process__nop(state_t *state, int64_t pts);// NOLINT(bugprone-reserved-identifier, readability-identifier-naming) + static void process__genericNoResampling(state_t *state, int64_t pts);// NOLINT(bugprone-reserved-identifier, readability-identifier-naming) + static void process__genericResampling(state_t *state, int64_t pts);// NOLINT(bugprone-reserved-identifier, readability-identifier-naming) + static void process__OneTrack16BitsStereoNoResampling(state_t *state, int64_t pts);// NOLINT(bugprone-reserved-identifier, readability-identifier-naming) + + 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);// NOLINT(bugprone-reserved-identifier, readability-identifier-naming) + + // multi-format track hooks + template + static void track__Resample(track_t *t, TO *out, size_t frameCount, TO *temp __unused, TA *aux);// NOLINT(bugprone-reserved-identifier, readability-identifier-naming) + template + static void track__NoResample(track_t *t, TO *out, size_t frameCount, TO *temp __unused, TA *aux); // NOLINT(bugprone-reserved-identifier, readability-identifier-naming) + + 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/ohos/AudioMixerController.cpp b/cocos/audio/ohos/AudioMixerController.cpp new file mode 100644 index 000000000000..d8b3036dec4b --- /dev/null +++ b/cocos/audio/ohos/AudioMixerController.cpp @@ -0,0 +1,306 @@ +/**************************************************************************** +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 "AudioMixerController.h" +#include +#include "AudioMixer.h" +#include "OpenSLHelper.h" +#include "Track.h" + + +namespace cocos2d { namespace experimental { + +AudioMixerController::AudioMixerController(int sampleRate, int channelCount) +: _sampleRate(sampleRate), _channelCount(channelCount), _mixer(nullptr), _isPaused(false), _isMixingFrame(false) { + ALOGV("In the constructor of AudioMixerController!"); + // For OHAudio, bluetooth buffer size is 17832 + int32_t maxBufferSize = 17832; + _mixingBuffer.buf = memalign(32, maxBufferSize); +} + +AudioMixerController::~AudioMixerController() { + destroy(); + + if (_mixer != nullptr) { + delete _mixer; + _mixer = nullptr; + } + + free(_mixingBuffer.buf); +} + +void AudioMixerController::updateBufferSize(int bufferSize) { + _mixer->setBufferSize(bufferSize / _channelCount / 2); + _mixingBuffer.size = bufferSize; + + uint32_t channelMask = audio_channel_out_mask_from_count(_channelCount); + int32_t name = _mixer->getTrackName(channelMask, AUDIO_FORMAT_PCM_16_BIT, AUDIO_SESSION_OUTPUT_MIX); + _mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, + _mixingBuffer.buf); +} + +bool AudioMixerController::init(int bufferSizeInFrames) { + _mixingBuffer.size = (size_t)bufferSizeInFrames * 2 * _channelCount; + memset(_mixingBuffer.buf, 0, _mixingBuffer.size); + _bufferSizeInFrames = bufferSizeInFrames; + _mixer = new 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/ohos/AudioMixerController.h b/cocos/audio/ohos/AudioMixerController.h new file mode 100644 index 000000000000..02d20d8a63b5 --- /dev/null +++ b/cocos/audio/ohos/AudioMixerController.h @@ -0,0 +1,88 @@ +/**************************************************************************** +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 +#include +#include "utils/Errors.h" + + +namespace cocos2d { namespace experimental { + +class Track; +class AudioMixer; + +class AudioMixerController { +public: + struct OutputBuffer { + void *buf; + size_t size; + }; + + AudioMixerController(int sampleRate, int channelCount); + + void updateBufferSize(int bufferSize); + + bool init(int bufferSizeInFrames); + + ~AudioMixerController(); + + + 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/ohos/AudioMixerOps.h b/cocos/audio/ohos/AudioMixerOps.h new file mode 100644 index 000000000000..3084471c12ca --- /dev/null +++ b/cocos/audio/ohos/AudioMixerOps.h @@ -0,0 +1,429 @@ +#pragma once + +#include "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/ohos/AudioPlayerProvider.cpp b/cocos/audio/ohos/AudioPlayerProvider.cpp new file mode 100644 index 000000000000..6c8fb9d0e9e7 --- /dev/null +++ b/cocos/audio/ohos/AudioPlayerProvider.cpp @@ -0,0 +1,477 @@ +/**************************************************************************** +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. +****************************************************************************/ + +#include +#include "PcmData.h" +#include "audio_utils/AudioDef.h" +#include "cutils/log.h" +#define LOG_TAG "AudioPlayerProvider" + +#include // for std::find_if +#include +#include +#include +#include "platform/ohos/CCFileUtils-ohos.h" +#include "AudioDecoder.h" +#include "AudioDecoderProvider.h" +#include "AudioMixerController.h" +#include "AudioPlayerProvider.h" +#include "ICallerThreadUtils.h" +#include "PcmAudioPlayer.h" +#include "PcmAudioService.h" +#include "UrlAudioPlayer.h" +#include "utils/Utils.h" +#include "CCThreadPool.h" + + + +#include // for std::find_if +#include +#include + +namespace cocos2d { namespace experimental { + + static int getSystemAPILevel() { + // TODO(qgh): On the openharmony platform, pcm streaming must be used + return std::numeric_limits::max(); + } + + struct AudioFileIndicator { + std::string extension; + int smallSizeIndicator; + }; + + static AudioFileIndicator gAudioFileIndicator[] = { + {"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, int deviceSampleRate, + + const FdGetterCallback &fdGetterCallback, //NOLINT(modernize-pass-by-value) + ICallerThreadUtils *callerThreadUtils) +: _engineItf(engineItf), _deviceSampleRate(deviceSampleRate), _fdGetterCallback(fdGetterCallback), _callerThreadUtils(callerThreadUtils), _pcmAudioService(nullptr), _mixController(nullptr), _threadPool(LegacyThreadPool::newCachedThreadPool(1, 8, 5, 2, 2)) { + ALOGI("deviceSampleRate: %d", _deviceSampleRate); + if (getSystemAPILevel() >= 17) { + _mixController = new AudioMixerController(_deviceSampleRate, 2); + _pcmAudioService = new PcmAudioService(); + _pcmAudioService->init(_mixController, CHANNEL_NUMBERS, deviceSampleRate, &_bufferSizeInFrames); + _mixController->init(_bufferSizeInFrames); + } + + 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 dynamic_cast(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 = dynamic_cast(obtainPcmAudioPlayer(audioFilePath, pcmData)); + ALOGV_IF(player == nullptr, "%{public}s, %{public}d: player is nullptr, path: %{public}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 = std::move(data); + *isSucceed = succeed; + *isPreloadFinished = true; + ALOGV("FileInfo (%{public}p), Set isSucceed flag: %{public}d, path: %{public}s", infoPtr, succeed, url.c_str()); + }, + true); + + if (!*isReturnFromCache && !*isPreloadFinished) { + std::unique_lock lck(_preloadWaitMutex); + // Wait for 2 seconds for the decoding in sub thread finishes. + ALOGV("FileInfo (%{public}p), Waiting preload (%{public}s) to finish ...", &info, audioFilePath.c_str()); + _preloadWaitCond.wait_for(lck, std::chrono::seconds(2)); + ALOGV("FileInfo (%{public}p), Waitup preload (%{public}s) ...", &info, audioFilePath.c_str()); + } + + if (*isSucceed) { + if (pcmData->isValid()) { + player = dynamic_cast(obtainPcmAudioPlayer(info.url, *pcmData)); + ALOGV_IF(player == nullptr, "%{public}s, %{public}d: player is nullptr, path: %{public}s", __FUNCTION__, __LINE__, audioFilePath.c_str()); + } else { + ALOGE("pcm data is invalid, path: %{public}s", audioFilePath.c_str()); + } + } else { + ALOGE("FileInfo (%{public}p), preloadEffect (%{public}s) failed", &info, audioFilePath.c_str()); + } + } else { + player = dynamic_cast(createUrlAudioPlayer(info)); + ALOGV_IF(player == nullptr, "%{public}s, %{public}d: player is nullptr, path: %{public}s", __FUNCTION__, __LINE__, audioFilePath.c_str()); + } + } else { + ALOGE("File info is invalid, path: %{public}s", audioFilePath.c_str()); + } + } + + ALOGV_IF(player == nullptr, "%{public}s, %{public}d return nullptr", __FUNCTION__, __LINE__); + return player; + } + + void AudioPlayerProvider::preloadEffect(const std::string &audioFilePath, const PreloadCallback &callback) { + // Pcm data decoding by OpenSLES API only supports in API level 17 and later. + if (getSystemAPILevel() < 17) { + PcmData data; + callback(true, data); + return; + } + + _pcmCacheMutex.lock(); + auto &&iter = _pcmCache.find(audioFilePath); + if (iter != _pcmCache.end()) { + ALOGV("preload return from cache: (%{public}s)", audioFilePath.c_str()); + _pcmCacheMutex.unlock(); + callback(true, iter->second); + return; + } + _pcmCacheMutex.unlock(); + + auto info = getFileInfo(audioFilePath); + preloadEffect( + info, [this, callback, audioFilePath](bool succeed, const PcmData &data) { + _callerThreadUtils->performFunctionInCallerThread([this, succeed, data, callback]() { + callback(succeed, data); + }); + }, + false); + } + +// Used internally + void AudioPlayerProvider::preloadEffect(const AudioFileInfo &info, const PreloadCallback &callback, bool isPreloadInPlay2d) { + PcmData pcmData; + + if (!info.isValid()) { + callback(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: %{public}s", info.url.c_str()); + _pcmCacheMutex.unlock(); + callback(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 lck(_preloadCallbackMutex); + + auto &&preloadIter = _preloadCallbackMap.find(audioFilePath); + if (preloadIter != _preloadCallbackMap.end()) { + ALOGV("audio (%{public}s) is being preloaded, add to callback vector!", audioFilePath.c_str()); + PreloadCallbackParam param; + param.callback = callback; + 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: %{public}s", info.url.c_str()); + _pcmCacheMutex.unlock(); + callback(true, iter->second); + return; + } + _pcmCacheMutex.unlock(); + + PreloadCallbackParam param; + param.callback = callback; + 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: (%{public}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 lck(_pcmCacheMutex); + _pcmCache.insert(std::make_pair(audioFilePath, d)); + } else { + ALOGE("decode (%{public}s) failed!", audioFilePath.c_str()); + } + + ALOGV("decode %{public}s", (ret ? "succeed" : "failed")); + + std::lock_guard lck(_preloadCallbackMutex); + auto &&preloadIter = _preloadCallbackMap.find(audioFilePath); + if (preloadIter != _preloadCallbackMap.end()) { + auto &¶ms = preloadIter->second; + ALOGV("preload (%{public}s) callback count: %{public}d", audioFilePath.c_str(), (int)params.size()); + PcmData result = decoder->getResult(); + for (auto &¶m : params) { + param.callback(ret, result); + if (param.isPreloadInPlay2d) { + _preloadWaitCond.notify_one(); + } + } + _preloadCallbackMap.erase(preloadIter); + } + + AudioDecoderProvider::destroyAudioDecoder(&decoder); + }); + } else { + ALOGV("File (%{public}s) is too large, ignore preload!", info.url.c_str()); + callback(true, pcmData); + } + } + +AudioPlayerProvider::AudioFileInfo AudioPlayerProvider::getFileInfo(const std::string &audioFilePath) +{ + AudioFileInfo info; + long fileSize = 0; //NOLINT(google-runtime-int) + off_t start = 0; + int assetFd = -1; + + if(audioFilePath[0]!='/'){ + RawFileDescriptor descriptor; + FileUtilsOhos *utils = dynamic_cast(FileUtils::getInstance()); + bool result = utils->getRawFileDescriptor(audioFilePath , descriptor); + if(result != 1|| descriptor.fd <= 0){ + ALOGE("Failed to open file descriptor for '%s'", audioFilePath.c_str()); + return info; + } + assetFd = descriptor.fd; + start = descriptor.start; + fileSize = descriptor.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; + ALOGI("AudioPlayerProvide::getFileInfo(%{public}s) file size:%{public}ld,fd is %d",audioFilePath.c_str(), fileSize,assetFd); + return info; +} + +bool AudioPlayerProvider::isSmallFile(const AudioFileInfo &info) { //NOLINT(readability-convert-member-functions-to-static) + //REFINE: If file size is smaller than 100k, we think it's a small file. This value should be set by developers. + auto &audioFileInfo = const_cast(info); + if(audioFileInfo.url[0] == '/') { + // avplayer does not support playing audio files in sandbox path currently. + return true; + } + size_t judgeCount = sizeof(gAudioFileIndicator) / sizeof(gAudioFileIndicator[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(gAudioFileIndicator), std::end(gAudioFileIndicator), + [&extension](const AudioFileIndicator &judge) -> bool { + return judge.extension == extension; + }); + + if (iter != std::end(gAudioFileIndicator)) { + // ALOGV("isSmallFile: found: %s: ", iter->extension.c_str()); + return info.length < iter->smallSizeIndicator; + } + + // ALOGV("isSmallFile: not found return default value"); + return info.length < gAudioFileIndicator[0].smallSizeIndicator; +} + + float AudioPlayerProvider::getDurationFromFile(const std::string &filePath) { + std::lock_guard lck(_pcmCacheMutex); + auto iter = _pcmCache.find(filePath); + if (iter != _pcmCache.end()) { + return iter->second.duration; + } + return 0; + } + + void AudioPlayerProvider::clearPcmCache(const std::string &audioFilePath) { + std::lock_guard lck(_pcmCacheMutex); + auto iter = _pcmCache.find(audioFilePath); + if (iter != _pcmCache.end()) { + ALOGV("clear pcm cache: (%{public}s)", audioFilePath.c_str()); + _pcmCache.erase(iter); + } else { + ALOGW("Couldn't find the pcm cache: (%{public}s)", audioFilePath.c_str()); + } + } + + void AudioPlayerProvider::clearAllPcmCaches() { + std::lock_guard lck(_pcmCacheMutex); + _pcmCache.clear(); + } + + PcmAudioPlayer *AudioPlayerProvider::obtainPcmAudioPlayer(const std::string &url, + const PcmData &pcmData) { + PcmAudioPlayer *pcmPlayer = nullptr; + if (pcmData.isValid()) { + pcmPlayer = new 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; + } + + auto *urlPlayer = new (std::nothrow) UrlAudioPlayer(_callerThreadUtils); + bool ret = urlPlayer->prepare(info.url, info.assetFd, info.start, info.length); + + if (!ret) { + if (urlPlayer != nullptr) { + delete urlPlayer; + urlPlayer = nullptr; + } + } + 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(); + } + } + void AudioPlayerProvider::registerPcmData(const std::string &audioFilePath, PcmData &data) { + std::lock_guard lck(_pcmCacheMutex); + if (_pcmCache.find(audioFilePath) != _pcmCache.end()){ + ALOGE("file %{public}s pcm data is already cached.", audioFilePath.c_str()); + return; + } + _pcmCache.emplace(audioFilePath, data); + } + + bool AudioPlayerProvider::getPcmHeader(const std::string &audioFilePath, PCMHeader &header) { + std::lock_guard lck(_pcmCacheMutex); + auto &&iter = _pcmCache.find(audioFilePath); + if (iter != _pcmCache.end()) { + ALOGV("get pcm header from cache, url: %{public}s", audioFilePath.c_str()); + // On Android, all pcm buffer is resampled to sign16. + header.bytesPerFrame = iter->second.bitsPerSample / 8; + header.channelCount = iter->second.numChannels; + header.dataFormat = AudioDataFormat::SIGNED_16; + header.sampleRate = iter->second.sampleRate; + header.totalFrames = iter->second.numFrames; + return true; + } + return false; + } + bool AudioPlayerProvider::getPcmData(const std::string &audioFilePath, PcmData &data) { + std::lock_guard lck(_pcmCacheMutex); + auto &&iter = _pcmCache.find(audioFilePath); + if (iter != _pcmCache.end()) { + ALOGV("get pcm buffer from cache, url: %{public}s", audioFilePath.c_str()); + // On Android, all pcm buffer is resampled to sign16. + data = iter->second; + return true; + } + return false; + } + }} // namespace cocos2d { namespace experimental diff --git a/cocos/audio/ohos/AudioPlayerProvider.h b/cocos/audio/ohos/AudioPlayerProvider.h new file mode 100644 index 000000000000..de7771a97e94 --- /dev/null +++ b/cocos/audio/ohos/AudioPlayerProvider.h @@ -0,0 +1,122 @@ +/**************************************************************************** +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 "IAudioPlayer.h" +#include "OpenSLHelper.h" +#include "PcmData.h" +#include "audio_utils/AudioDef.h" + + +namespace cocos2d { namespace experimental { +// Manage PcmAudioPlayer& UrlAudioPlayer + + class PcmAudioPlayer; + class PcmAudioService; + class UrlAudioPlayer; + class AudioMixerController; + class ICallerThreadUtils; + class AssetFd; + class LegacyThreadPool; + + class AudioPlayerProvider { + public: + AudioPlayerProvider(SLEngineItf engineItf, int deviceSampleRate, + const FdGetterCallback &fdGetterCallback, ICallerThreadUtils *callerThreadUtils); + + virtual ~AudioPlayerProvider(); + bool isFileCached(const std::string &audioFilePath); + IAudioPlayer *getAudioPlayer(const std::string &audioFilePath); + bool getPcmHeader(const std::string &audioFilePath, PCMHeader &header); + bool getPcmData(const std::string &audioFilePath, PcmData &data); + using PreloadCallback = std::function; + void preloadEffect(const std::string &audioFilePath, const PreloadCallback &callback); + void registerPcmData(const std::string &audioFilePath, PcmData &data); + float getDurationFromFile(const std::string &filePath); + void clearPcmCache(const std::string &audioFilePath); + + void clearAllPcmCaches(); + + void pause(); + + void resume(); + + + struct AudioFileInfo { + std::string url; + std::shared_ptr assetFd; + off_t start{}; + off_t length; + + AudioFileInfo() + : assetFd(nullptr) {} + + 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 &callback, bool isPreloadInPlay2d); + + AudioFileInfo getFileInfo(const std::string &audioFilePath); + + bool isSmallFile(const AudioFileInfo &info); + + 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; + + LegacyThreadPool *_threadPool; + }; + + }} // namespace cocos2d { namespace experimental diff --git a/cocos/audio/ohos/AudioResampler.cpp b/cocos/audio/ohos/AudioResampler.cpp new file mode 100644 index 000000000000..026e04172aa2 --- /dev/null +++ b/cocos/audio/ohos/AudioResampler.cpp @@ -0,0 +1,770 @@ +#define LOG_TAG "AudioResampler" + +#include +#include +#include +#include +#include +#include "cutils/log.h" +#include "utils/Utils.h" +#include "AudioResampler.h" +#include "audio_utils/include/audio_utils/primitives.h" +#include "AudioResamplerCubic.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 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 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 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 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) { + // REFINE: 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/ohos/AudioResampler.h b/cocos/audio/ohos/AudioResampler.h new file mode 100644 index 000000000000..49abf8470f71 --- /dev/null +++ b/cocos/audio/ohos/AudioResampler.h @@ -0,0 +1,154 @@ +#pragma once + +#include +#include "AudioBufferProvider.h" +#include +#include "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 { // NOLINT(readability-identifier-naming) + 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; // NOLINT(readability-identifier-naming) + + // phase mask for fraction + static const uint32_t kPhaseMask = (1LU << kNumPhaseBits) - 1; // NOLINT(readability-identifier-naming) + + // multiplier to calculate fixed point phase increment + static const double kPhaseMultiplier; // NOLINT(readability-identifier-naming) + + AudioResampler(int inChannelCount, int32_t sampleRate, src_quality quality); + + // prevent copying + AudioResampler(const AudioResampler &); + AudioResampler &operator=(const AudioResampler &); + + int64_t calculateOutputPTS(int outputFrameIndex); + + + const int32_t mChannelCount;// NOLINT(readability-identifier-naming) + const int32_t mSampleRate;// NOLINT(readability-identifier-naming) + int32_t mInSampleRate;// NOLINT(readability-identifier-naming) + AudioBufferProvider::Buffer mBuffer;// NOLINT(readability-identifier-naming) + union { + int16_t mVolume[2];// NOLINT(readability-identifier-naming) + uint32_t mVolumeRL;// NOLINT(readability-identifier-naming) + }; + int16_t mTargetVolume[2];// NOLINT(readability-identifier-naming) + size_t mInputIndex;// NOLINT(readability-identifier-naming) + int32_t mPhaseIncrement;// NOLINT(readability-identifier-naming) + uint32_t mPhaseFraction;// NOLINT(readability-identifier-naming) + uint64_t mLocalTimeFreq;// NOLINT(readability-identifier-naming) + int64_t mPTS;// NOLINT(readability-identifier-naming) + + // returns the inFrameCount required to generate outFrameCount frames. + // + // Placed here to be a consistent for all resamplers. + // + // Right now, we use the upper bound without regards to the current state of the + // input buffer using integer arithmetic, as follows: + // + // (static_cast(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). + // + // REFINE: + // 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) const { + return (static_cast(outFrameCount) * mInSampleRate + (mSampleRate - 1)) / mSampleRate; + } + + inline float clampFloatVol(float volume) {//NOLINT(readability-identifier-naming, readability-convert-member-functions-to-static) + float ret = 0.0F; + if (volume > UNITY_GAIN_FLOAT) { + ret = UNITY_GAIN_FLOAT; + } else if (volume >= 0.) { + ret = volume; + } + return ret; // NaN or negative volume maps to 0. + } + +private: + const src_quality mQuality;// NOLINT(readability-identifier-naming) + + // Return 'true' if the quality level is supported without explicit request + static bool qualityIsSupported(src_quality quality); + + // For pthread_once() + static void init_routine(); // NOLINT(readability-identifier-naming) + + // 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/ohos/AudioResamplerCubic.cpp b/cocos/audio/ohos/AudioResamplerCubic.cpp new file mode 100644 index 000000000000..a04d8afadccd --- /dev/null +++ b/cocos/audio/ohos/AudioResamplerCubic.cpp @@ -0,0 +1,170 @@ +#define LOG_TAG "AudioResamplerCubic" + +#include +#include +#include +#include "cutils/log.h" + +#include "AudioResampler.h" +#include "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/ohos/AudioResamplerCubic.h b/cocos/audio/ohos/AudioResamplerCubic.h new file mode 100644 index 000000000000..8d692342d56d --- /dev/null +++ b/cocos/audio/ohos/AudioResamplerCubic.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include "AudioResampler.h" +#include "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/ohos/AudioResamplerPublic.h b/cocos/audio/ohos/AudioResamplerPublic.h new file mode 100644 index 000000000000..465e1153caf9 --- /dev/null +++ b/cocos/audio/ohos/AudioResamplerPublic.h @@ -0,0 +1,155 @@ +#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. +// REFINE: 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, + //REFINE: 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 && + pr1.mStretchMode == pr2.mStretchMode && + pr1.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; + } +} + +// REFINE: 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 ? static_cast(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/ohos/CCThreadPool.cpp b/cocos/audio/ohos/CCThreadPool.cpp new file mode 100644 index 000000000000..7e35a0802f79 --- /dev/null +++ b/cocos/audio/ohos/CCThreadPool.cpp @@ -0,0 +1,376 @@ +/**************************************************************************** + Copyright (c) 2016-2017 Chukong Technologies Inc. + Copyright (c) 2017-2022 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You shall + not use Cocos Creator software for developing other software or tools that's + used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. + + 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 "CCThreadPool.h" +#include +#include + + + +#define LOGD(...) printf(__VA_ARGS__) + + +#define TIME_MINUS(now, prev) (std::chrono::duration_cast((now) - (prev)).count() / 1000.0f) + +namespace cocos2d { namespace experimental { + +#define DEFAULT_THREAD_POOL_MIN_NUM (4) +#define DEFAULT_THREAD_POOL_MAX_NUM (20) + +#define DEFAULT_SHRINK_INTERVAL (5) +#define DEFAULT_SHRINK_STEP (2) +#define DEFAULT_STRETCH_STEP (2) + +LegacyThreadPool *LegacyThreadPool::_instance = nullptr; + +LegacyThreadPool *LegacyThreadPool::getDefaultThreadPool() { + if (LegacyThreadPool::_instance == nullptr) { + LegacyThreadPool::_instance = newCachedThreadPool(DEFAULT_THREAD_POOL_MIN_NUM, + DEFAULT_THREAD_POOL_MAX_NUM, + DEFAULT_SHRINK_INTERVAL, DEFAULT_SHRINK_STEP, + DEFAULT_STRETCH_STEP); + } + + return LegacyThreadPool::_instance; +} + +void LegacyThreadPool::destroyDefaultThreadPool() { + delete LegacyThreadPool::_instance; + LegacyThreadPool::_instance = nullptr; +} + +LegacyThreadPool *LegacyThreadPool::newCachedThreadPool(int minThreadNum, int maxThreadNum, int shrinkInterval, + int shrinkStep, int stretchStep) { + auto *pool = new LegacyThreadPool(minThreadNum, maxThreadNum); + if (pool != nullptr) { + pool->setFixedSize(false); + pool->setShrinkInterval(shrinkInterval); + pool->setShrinkStep(shrinkStep); + pool->setStretchStep(stretchStep); + } + return pool; +} + +LegacyThreadPool *LegacyThreadPool::newFixedThreadPool(int threadNum) { + auto *pool = new LegacyThreadPool(threadNum, threadNum); + if (pool != nullptr) { + pool->setFixedSize(true); + } + return pool; +} + +LegacyThreadPool *LegacyThreadPool::newSingleThreadPool() { + auto *pool = new LegacyThreadPool(1, 1); + if (pool != nullptr) { + pool->setFixedSize(true); + } + return pool; +} + +LegacyThreadPool::LegacyThreadPool(int minNum, int maxNum) +: _minThreadNum(minNum), + _maxThreadNum(maxNum) { + init(); +} + +// the destructor waits for all the functions in the queue to be finished +LegacyThreadPool::~LegacyThreadPool() { + stop(); +} + +// number of idle threads +int LegacyThreadPool::getIdleThreadNum() const { + auto *thiz = const_cast(this); + std::lock_guard lk(thiz->_idleThreadNumMutex); + return _idleThreadNum; +} + +void LegacyThreadPool::init() { + _lastShrinkTime = std::chrono::high_resolution_clock::now(); + + _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 LegacyThreadPool::tryShrinkPool() { + LOGD("shrink pool, _idleThreadNum = %d \n", getIdleThreadNum()); + + auto before = std::chrono::high_resolution_clock::now(); + + std::vector threadIDsToJoin; + int maxThreadNumToJoin = std::min(_initedThreadNum - _minThreadNum, _shrinkStep); + + for (int i = 0; i < _maxThreadNum; ++i) { + if ((int)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; + } + + auto after = std::chrono::high_resolution_clock::now(); + + float seconds = TIME_MINUS(after, before); + + LOGD("shrink %d threads, waste: %f seconds\n", (int)threadIDsToJoin.size(), seconds); + + return (_initedThreadNum <= _minThreadNum); +} + +void LegacyThreadPool::stretchPool(int count) { + auto before = std::chrono::high_resolution_clock::now(); + + 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) { + auto after = std::chrono::high_resolution_clock::now(); + float seconds = TIME_MINUS(after, before); + + LOGD("stretch pool from %d to %d, waste %f seconds\n", oldThreadCount, _initedThreadNum, + seconds); + } +} + +void LegacyThreadPool::pushTask(const std::function &runnable, + TaskType type /* = DEFAULT*/) { + if (!_isFixedSize) { + _idleThreadNumMutex.lock(); + int idleNum = _idleThreadNum; + _idleThreadNumMutex.unlock(); + + if (idleNum > _minThreadNum) { + if (_taskQueue.empty()) { + auto now = std::chrono::high_resolution_clock::now(); + float seconds = TIME_MINUS(now, _lastShrinkTime); + if (seconds > _shrinkInterval) { + tryShrinkPool(); + _lastShrinkTime = now; + } + } + } else if (idleNum == 0) { + stretchPool(_stretchStep); + } + } + + auto callback = new std::function([runnable](int tid) { + runnable(tid); + }); + + Task task; + task.type = type; + task.callback = callback; + _taskQueue.push(task); + + { + std::unique_lock lock(_mutex); + _cv.notify_one(); + } +} + +void LegacyThreadPool::stopAllTasks() { + Task task; + while (_taskQueue.pop(task)) { + delete task.callback; // empty the queue + } +} + +void LegacyThreadPool::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 LegacyThreadPool::joinThread(int tid) { + if (tid < 0 || tid >= (int)_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 LegacyThreadPool::getTaskNum() const { + return (int)_taskQueue.size(); +} + +void LegacyThreadPool::setFixedSize(bool isFixedSize) { + _isFixedSize = isFixedSize; +} + +void LegacyThreadPool::setShrinkInterval(int seconds) { + if (seconds >= 0) { + _shrinkInterval = static_cast(seconds); + } +} + +void LegacyThreadPool::setShrinkStep(int step) { + if (step > 0) { + _shrinkStep = step; + } +} + +void LegacyThreadPool::setStretchStep(int step) { + if (step > 0) { + _stretchStep = step; + } +} + +void LegacyThreadPool::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 LegacyThreadPool::setThread(int tid) { + std::shared_ptr> abortPtr( + _abortFlags[tid]); // a copy of the shared ptr to the flag + auto f = [this, tid, abortPtr /* a copy of the shared ptr to the abort */]() { + std::atomic &abort = *abortPtr; + 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 + } + + 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::thread(f)); // compiler may not support std::make_unique() +} + +}} // namespace cocos2d { namespace experimental diff --git a/cocos/audio/ohos/CCThreadPool.h b/cocos/audio/ohos/CCThreadPool.h new file mode 100644 index 000000000000..24d15b76c3f8 --- /dev/null +++ b/cocos/audio/ohos/CCThreadPool.h @@ -0,0 +1,218 @@ +/**************************************************************************** + Copyright (c) 2016-2017 Chukong Technologies Inc. + Copyright (c) 2017-2022 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You shall + not use Cocos Creator software for developing other software or tools that's + used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. + + 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 +#include +#include +#include +#include +#include "utils/Utils.h" + + +namespace cocos2d { namespace experimental { + +class LegacyThreadPool { +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 LegacyThreadPool *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 LegacyThreadPool *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 LegacyThreadPool *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 LegacyThreadPool *newSingleThreadPool(); + + // the destructor waits for all the functions in the queue to be finished + ~LegacyThreadPool(); + + /* 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: + LegacyThreadPool(int minNum, int maxNum); + + LegacyThreadPool(const LegacyThreadPool &); + + LegacyThreadPool(LegacyThreadPool &&) noexcept; + + LegacyThreadPool &operator=(const LegacyThreadPool &); + + LegacyThreadPool &operator=(LegacyThreadPool &&) noexcept; + + 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; + }; + + static LegacyThreadPool *_instance; + + ThreadSafeQueue _taskQueue; + std::atomic _isDone{false}; + std::atomic _isStop{false}; + + //IDEA: std::atomic isn't supported by ndk-r10e while compiling with `armeabi` arch. + // So using a mutex here instead. + int _idleThreadNum{0}; // how many threads are waiting + std::mutex _idleThreadNumMutex; + + std::mutex _mutex; + std::condition_variable _cv; + + int _minThreadNum{0}; + int _maxThreadNum{0}; + int _initedThreadNum{0}; + + std::chrono::time_point _lastShrinkTime; + float _shrinkInterval{5}; + int _shrinkStep{2}; + int _stretchStep{2}; + bool _isFixedSize{false}; +}; + +}} // namespace cocos2d { namespace experimental diff --git a/cocos/audio/ohos/IAudioPlayer.h b/cocos/audio/ohos/IAudioPlayer.h new file mode 100644 index 000000000000..ed14ca4be54e --- /dev/null +++ b/cocos/audio/ohos/IAudioPlayer.h @@ -0,0 +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. +****************************************************************************/ + +#pragma once + +#include +#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/ohos/ICallerThreadUtils.h b/cocos/audio/ohos/ICallerThreadUtils.h new file mode 100644 index 000000000000..c729752e8b6a --- /dev/null +++ b/cocos/audio/ohos/ICallerThreadUtils.h @@ -0,0 +1,40 @@ +/**************************************************************************** +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/ohos/IVolumeProvider.h b/cocos/audio/ohos/IVolumeProvider.h new file mode 100644 index 000000000000..a6d95a0db144 --- /dev/null +++ b/cocos/audio/ohos/IVolumeProvider.h @@ -0,0 +1,42 @@ +/**************************************************************************** +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_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/ohos/Macros.h b/cocos/audio/ohos/Macros.h new file mode 100644 index 000000000000..da6865a157f7 --- /dev/null +++ b/cocos/audio/ohos/Macros.h @@ -0,0 +1,415 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2010-2012 cocos2d-x.org + Copyright (c) 2011 Zynga Inc. + Copyright (c) 2013-2016 Chukong Technologies Inc. + Copyright (c) 2017-2022 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You shall + not use Cocos Creator software for developing other software or tools that's + used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. + + 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 // To include uint8_t, uint16_t and so on. + +#include +#define CC_ASSERT(cond) assert(cond) + +#define CC_AUDIO_SAFE_DELETE(p) do { if(p) { delete (p); (p) = 0; } } while(0) + +#if (CC_PLATFORM == CC_PLATFORM_WINDOWS) +#if defined(CC_STATIC) +#define CC_DLL +#else +#if defined(_USRDLL) +#define CC_DLL __declspec(dllexport) +#else /* use a DLL library */ +#define CC_DLL __declspec(dllimport) +#endif +#endif +#else +#define CC_DLL +#endif + +/** @def CC_DEGREES_TO_RADIANS + converts degrees to radians + */ +#define CC_DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__)*0.01745329252f) // PI / 180 + +/** @def CC_RADIANS_TO_DEGREES + converts radians to degrees + */ +#define CC_RADIANS_TO_DEGREES(__ANGLE__) ((__ANGLE__)*57.29577951f) // PI * 180 + +#ifndef FLT_EPSILON +#define FLT_EPSILON 1.192092896e-07F +#endif // FLT_EPSILON + +/** +Helper macros which converts 4-byte little/big endian +integral number to the machine native number representation + +It should work same as apples CFSwapInt32LittleToHost(..) +*/ + +/// when define returns true it means that our architecture uses big endian +#define CC_HOST_IS_BIG_ENDIAN (bool)(*(unsigned short *)"\0\xff" < 0x100) +#define CC_SWAP32(i) ((i & 0x000000ff) << 24 | (i & 0x0000ff00) << 8 | (i & 0x00ff0000) >> 8 | (i & 0xff000000) >> 24) +#define CC_SWAP16(i) ((i & 0x00ff) << 8 | (i & 0xff00) >> 8) +#define CC_SWAP_INT32_LITTLE_TO_HOST(i) ((CC_HOST_IS_BIG_ENDIAN == true) ? CC_SWAP32(i) : (i)) +#define CC_SWAP_INT16_LITTLE_TO_HOST(i) ((CC_HOST_IS_BIG_ENDIAN == true) ? CC_SWAP16(i) : (i)) +#define CC_SWAP_INT32_BIG_TO_HOST(i) ((CC_HOST_IS_BIG_ENDIAN == true) ? (i) : CC_SWAP32(i)) +#define CC_SWAP_INT16_BIG_TO_HOST(i) ((CC_HOST_IS_BIG_ENDIAN == true) ? (i) : CC_SWAP16(i)) + +// new callbacks based on C++11 +#define CC_CALLBACK_0(__selector__, __target__, ...) std::bind(&__selector__, __target__, ##__VA_ARGS__) +#define CC_CALLBACK_1(__selector__, __target__, ...) std::bind(&__selector__, __target__, std::placeholders::_1, ##__VA_ARGS__) +#define CC_CALLBACK_2(__selector__, __target__, ...) std::bind(&__selector__, __target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__) +#define CC_CALLBACK_3(__selector__, __target__, ...) std::bind(&__selector__, __target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__) + +// Generic macros + +#define CC_BREAK_IF(cond) \ + if (cond) break + +/** @def CC_DEPRECATED_ATTRIBUTE +* Only certain compilers support __attribute__((deprecated)). +*/ +#if defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))) +#define CC_DEPRECATED_ATTRIBUTE __attribute__((deprecated)) +#elif _MSC_VER >= 1400 //vs 2005 or higher +#define CC_DEPRECATED_ATTRIBUTE __declspec(deprecated) +#else +#define CC_DEPRECATED_ATTRIBUTE +#endif + +/** @def CC_DEPRECATED(...) +* Macro to mark things deprecated as of a particular version +* can be used with arbitrary parameters which are thrown away. +* e.g. CC_DEPRECATED(4.0) or CC_DEPRECATED(4.0, "not going to need this anymore") etc. +*/ +#define CC_DEPRECATED(...) CC_DEPRECATED_ATTRIBUTE + +#ifdef __GNUC__ +#define CC_UNUSED __attribute__((unused)) +#else +#define CC_UNUSED +#endif + +#define CC_UNUSED_PARAM(unusedparam) (void)unusedparam + +/** @def CC_FORMAT_PRINTF(formatPos, argPos) + * Only certain compiler support __attribute__((format)) + * + * @param formatPos 1-based position of format string argument. + * @param argPos 1-based position of first format-dependent argument. + */ +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define CC_FORMAT_PRINTF(formatPos, argPos) __attribute__((__format__(printf, formatPos, argPos))) +#elif defined(__has_attribute) +#if __has_attribute(format) + #define CC_FORMAT_PRINTF(formatPos, argPos) __attribute__((__format__(printf, formatPos, argPos))) + #else + #define CC_FORMAT_PRINTF(formatPos, argPos) + #endif // __has_attribute(format) +#else +#define CC_FORMAT_PRINTF(formatPos, argPos) +#endif + +// Initial compiler-related stuff to set. +#define CC_COMPILER_MSVC 1 +#define CC_COMPILER_CLANG 2 +#define CC_COMPILER_GNUC 3 + +// CPU Architecture +#define CC_CPU_UNKNOWN 0 +#define CC_CPU_X86 1 +#define CC_CPU_PPC 2 +#define CC_CPU_ARM 3 +#define CC_CPU_MIPS 4 + +// 32-bits or 64-bits CPU +#define CC_CPU_ARCH_32 1 +#define CC_CPU_ARCH_64 2 + +// Mode +#define CC_MODE_DEBUG 1 +#define CC_MODE_RELEASE 2 + +// Compiler type and version recognition +#if defined(_MSC_VER) +#define CC_COMPILER CC_COMPILER_MSVC +#elif defined(__clang__) +#define CC_COMPILER CC_COMPILER_CLANG +#elif defined(__GNUC__) +#define CC_COMPILER CC_COMPILER_GNUC +#else +#error "Unknown compiler. Abort!" +#endif + +#if INTPTR_MAX == INT32_MAX +#define CC_CPU_ARCH CC_CPU_ARCH_32 +#else +#define CC_CPU_ARCH CC_CPU_ARCH_64 +#endif + +#if defined(__arm64__) || defined(__aarch64__) +#define CC_ARCH_ARM64 1 +#else +#define CC_ARCH_ARM64 0 +#endif + +// CC_HAS_ARM64_FP16 set to 1 if the architecture provides an IEEE compliant ARM fp16 type +#if CC_ARCH_ARM64 +#ifndef CC_HAS_ARM64_FP16 + #if defined(__ARM_FP16_FORMAT_IEEE) + #define CC_HAS_ARM64_FP16 1 + #else + #define CC_HAS_ARM64_FP16 0 + #endif + #endif +#endif + +// CC_HAS_ARM64_FP16_VECTOR_ARITHMETIC set to 1 if the architecture supports Neon vector intrinsics for fp16. +#if CC_ARCH_ARM64 +#ifndef CC_HAS_ARM64_FP16_VECTOR_ARITHMETIC + #if defined(__ARM_FEATURE_FP16_VECTOR_ARITHMETIC) + #define CC_HAS_ARM64_FP16_VECTOR_ARITHMETIC 1 + #else + #define CC_HAS_ARM64_FP16_VECTOR_ARITHMETIC 0 + #endif + #endif +#endif + +// CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC set to 1 if the architecture supports Neon scalar intrinsics for fp16. +#if CC_ARCH_ARM64 +#ifndef CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC + #if defined(__ARM_FEATURE_FP16_SCALAR_ARITHMETIC) + #define CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC 1 + #else + #define CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC 0 + #endif + #endif +#endif + +// Disable MSVC warning +#if (CC_COMPILER == CC_COMPILER_MSVC) +#pragma warning(disable : 4251 4275 4819) + #ifndef _CRT_SECURE_NO_DEPRECATE + #define _CRT_SECURE_NO_DEPRECATE + #endif + #ifndef _SCL_SECURE_NO_DEPRECATE + #define _SCL_SECURE_NO_DEPRECATE + #endif +#endif + +#define CC_CACHELINE_SIZE 64 + +#if (CC_COMPILER == CC_COMPILER_MSVC) +// MSVC ENABLE/DISABLE WARNING DEFINITION + #define CC_DISABLE_WARNINGS() \ + __pragma(warning(push, 0)) + + #define CC_ENABLE_WARNINGS() \ + __pragma(warning(pop)) +#elif (CC_COMPILER == CC_COMPILER_GNUC) +// GCC ENABLE/DISABLE WARNING DEFINITION + #define CC_DISABLE_WARNINGS() \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wall\"") \ + _Pragma("clang diagnostic ignored \"-Wextra\"") \ + _Pragma("clang diagnostic ignored \"-Wtautological-compare\"") + + #define CC_ENABLE_WARNINGS() \ + _Pragma("GCC diagnostic pop") +#elif (CC_COMPILER == CC_COMPILER_CLANG) +// CLANG ENABLE/DISABLE WARNING DEFINITION + #define CC_DISABLE_WARNINGS() \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wall\"") \ + _Pragma("clang diagnostic ignored \"-Wextra\"") \ + _Pragma("clang diagnostic ignored \"-Wtautological-compare\"") + + #define CC_ENABLE_WARNINGS() \ + _Pragma("clang diagnostic pop") +#endif + +#define CC_DISALLOW_ASSIGN(TypeName) \ + TypeName &operator=(const TypeName &) = delete; \ + TypeName &operator=(TypeName &&) = delete; + +#define CC_DISALLOW_COPY_MOVE_ASSIGN(TypeName) \ + TypeName(const TypeName &) = delete; \ + TypeName(TypeName &&) = delete; \ + CC_DISALLOW_ASSIGN(TypeName) + +#if (CC_COMPILER == CC_COMPILER_MSVC) +#define CC_ALIGN(N) __declspec(align(N)) + #define CC_CACHE_ALIGN __declspec(align(CC_CACHELINE_SIZE)) + #define CC_PACKED_ALIGN(N) __declspec(align(N)) + + #define CC_ALIGNED_DECL(type, var, alignment) __declspec(align(alignment)) type var + + #define CC_READ_COMPILER_BARRIER() _ReadBarrier() + #define CC_WRITE_COMPILER_BARRIER() _WriteBarrier() + #define CC_COMPILER_BARRIER() _ReadWriteBarrier() + + #define CC_READ_MEMORY_BARRIER() MemoryBarrier() + #define CC_WRITE_MEMORY_BARRIER() MemoryBarrier() + #define CC_MEMORY_BARRIER() MemoryBarrier() + + #define CC_CPU_READ_MEMORY_BARRIER() \ + do { \ + __asm { lfence} \ + } while (0) + #define CC_CPU_WRITE_MEMORY_BARRIER() \ + do { \ + __asm { sfence} \ + } while (0) + #define CC_CPU_MEMORY_BARRIER() \ + do { \ + __asm { mfence} \ + } while (0) + +#elif (CC_COMPILER == CC_COMPILER_GNUC) || (CC_COMPILER == CC_COMPILER_CLANG) +#define CC_ALIGN(N) __attribute__((__aligned__((N)))) + #define CC_CACHE_ALIGN __attribute__((__aligned__((CC_CACHELINE_SIZE)))) + #define CC_PACKED_ALIGN(N) __attribute__((packed, aligned(N))) + + #define CC_ALIGNED_DECL(type, var, alignment) type var __attribute__((__aligned__(alignment))) + + #define CC_READ_COMPILER_BARRIER() \ + do { \ + __asm__ __volatile__("" \ + : \ + : \ + : "memory"); \ + } while (0) + #define CC_WRITE_COMPILER_BARRIER() \ + do { \ + __asm__ __volatile__("" \ + : \ + : \ + : "memory"); \ + } while (0) + #define CC_COMPILER_BARRIER() \ + do { \ + __asm__ __volatile__("" \ + : \ + : \ + : "memory"); \ + } while (0) + + #define CC_READ_MEMORY_BARRIER() \ + do { \ + __sync_synchronize(); \ + } while (0) + #define CC_WRITE_MEMORY_BARRIER() \ + do { \ + __sync_synchronize(); \ + } while (0) + #define CC_MEMORY_BARRIER() \ + do { \ + __sync_synchronize(); \ + } while (0) + + #define CC_CPU_READ_MEMORY_BARRIER() \ + do { \ + __asm__ __volatile__("lfence" \ + : \ + : \ + : "memory"); \ + } while (0) + #define CC_CPU_WRITE_MEMORY_BARRIER() \ + do { \ + __asm__ __volatile__("sfence" \ + : \ + : \ + : "memory"); \ + } while (0) + #define CC_CPU_MEMORY_BARRIER() \ + do { \ + __asm__ __volatile__("mfence" \ + : \ + : \ + : "memory"); \ + } while (0) + +#else +#error "Unsupported compiler!" +#endif + +/* Stack-alignment + If macro __CC_SIMD_ALIGN_STACK defined, means there requests + special code to ensure stack align to a 16-bytes boundary. + + Note: + This macro can only guarantee callee stack pointer (esp) align + to a 16-bytes boundary, but not that for frame pointer (ebp). + Because most compiler might use frame pointer to access to stack + variables, so you need to wrap those alignment required functions + with extra function call. + */ +#if defined(__INTEL_COMPILER) +// For intel's compiler, simply calling alloca seems to do the right + // thing. The size of the allocated block seems to be irrelevant. + #define CC_SIMD_ALIGN_STACK() _alloca(16) + #define CC_SIMD_ALIGN_ATTRIBUTE + +#elif (CC_CPU == CC_CPU_X86) && (CC_COMPILER == CC_COMPILER_GNUC || CC_COMPILER == CC_COMPILER_CLANG) && (CC_CPU_ARCH != CC_CPU_ARCH_64) +// mark functions with GCC attribute to force stack alignment to 16 bytes + #define CC_SIMD_ALIGN_ATTRIBUTE __attribute__((force_align_arg_pointer)) +#elif (CC_COMPILER == CC_COMPILER_MSVC) +// Fortunately, MSVC will align the stack automatically + #define CC_SIMD_ALIGN_ATTRIBUTE +#else +#define CC_SIMD_ALIGN_ATTRIBUTE +#endif + +// mode +#if (defined(_DEBUG) || defined(DEBUG)) && (CC_PLATFORM == CC_PLATFORM_WINDOWS) +#define CC_MODE CC_MODE_DEBUG +#else +#define CC_MODE CC_MODE_RELEASE +#endif + +#define CC_TOSTR(s) #s + +#if defined(__GNUC__) && __GNUC__ >= 4 +#define CC_PREDICT_TRUE(x) __builtin_expect(!!(x), 1) + #define CC_PREDICT_FALSE(x) __builtin_expect(!!(x), 0) +#else +#define CC_PREDICT_TRUE(x) (x) +#define CC_PREDICT_FALSE(x) (x) +#endif + +#if defined(_MSC_VER) +#define CC_FORCE_INLINE __forceinline +#elif defined(__GNUC__) || defined(__clang__) +#define CC_FORCE_INLINE inline __attribute__((always_inline)) +#else +#if defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +#define CC_FORCE_INLINE static inline +#elif +#define CC_FORCE_INLINE inline +#endif +#endif diff --git a/cocos/audio/ohos/OpenSLHelper.h b/cocos/audio/ohos/OpenSLHelper.h new file mode 100644 index 000000000000..a8b00d5f19b4 --- /dev/null +++ b/cocos/audio/ohos/OpenSLHelper.h @@ -0,0 +1,81 @@ + +#pragma once + +#include "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/ohos/PcmAudioPlayer.cpp b/cocos/audio/ohos/PcmAudioPlayer.cpp new file mode 100644 index 000000000000..2ed36cda71ff --- /dev/null +++ b/cocos/audio/ohos/PcmAudioPlayer.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +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 "PcmAudioPlayer.h" +#include "AudioMixerController.h" +#include "ICallerThreadUtils.h" +#include "cutils/log.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 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 (%{public}p) play, url: %{public}s", this, _url.c_str()); + _controller->addTrack(_track); + _track->setState(Track::State::PLAYING); + } + + void PcmAudioPlayer::pause() { + ALOGV("PcmAudioPlayer (%{public}p) pause, url: %{public}s", this, _url.c_str()); + _track->setState(Track::State::PAUSED); + } + + void PcmAudioPlayer::resume() { + ALOGV("PcmAudioPlayer (%{public}p) resume, url: %{public}s", this, _url.c_str()); + _track->setState(Track::State::RESUMED); + } + + void PcmAudioPlayer::stop() { + ALOGV("PcmAudioPlayer (%{public}p) stop, url: %{public}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/ohos/PcmAudioPlayer.h b/cocos/audio/ohos/PcmAudioPlayer.h new file mode 100644 index 000000000000..7574fe18cb0f --- /dev/null +++ b/cocos/audio/ohos/PcmAudioPlayer.h @@ -0,0 +1,96 @@ +/**************************************************************************** +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 "IAudioPlayer.h" +#include "PcmData.h" +#include "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 + + + 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/ohos/PcmAudioService.cpp b/cocos/audio/ohos/PcmAudioService.cpp new file mode 100644 index 000000000000..03ec8d9582c0 --- /dev/null +++ b/cocos/audio/ohos/PcmAudioService.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** +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 "CCPlatformMacros.h" +#include "PcmAudioService.h" +#include "AudioMixerController.h" +#include "utils/Compat.h" + +namespace cocos2d { namespace experimental { + + +static std::vector __silenceData;//NOLINT(bugprone-reserved-identifier, readability-identifier-naming) + +PcmAudioService::PcmAudioService() +: _controller(nullptr) { +} + +PcmAudioService::~PcmAudioService() { + ALOGV("PcmAudioService() (%p), before destroy play object", this); + if (_audioRenderer != nullptr) { + OH_AudioRenderer_Stop(_audioRenderer); + OH_AudioRenderer_Release(_audioRenderer); + } + + if (_builder != nullptr) { + OH_AudioStreamBuilder_Destroy(_builder); + } + ALOGV("PcmAudioService() end"); +} + +int32_t PcmAudioService::AudioRendererOnWriteData(OH_AudioRenderer* renderer, + void* userData, + void* buffer, + int32_t bufferLen) +{ + auto *thiz = reinterpret_cast(userData); + if (bufferLen != thiz->_bufferSizeInBytes) { + __silenceData.resize(bufferLen, 0x00); + thiz->_bufferSizeInBytes = bufferLen; + thiz->_controller->updateBufferSize(thiz->_bufferSizeInBytes); + } + + if (thiz->_controller->hasPlayingTacks()) { + if (thiz->_controller->isPaused()) { + memcpy(buffer, __silenceData.data(), bufferLen); + } else { + + thiz->_controller->mixOneFrame(); + auto *current = thiz->_controller->current(); + ALOG_ASSERT(current != nullptr, "current buffer is nullptr ..."); + memcpy(buffer, current->buf, current->size < bufferLen ? current->size : bufferLen); + } + } else { + memcpy(buffer, __silenceData.data(), bufferLen); + } + + return 0; +} + +int32_t PcmAudioService::AudioRendererOnInterrupt(OH_AudioRenderer* renderer, + void* userData, + OH_AudioInterrupt_ForceType type, + OH_AudioInterrupt_Hint hint) +{ + auto *thiz = reinterpret_cast(userData); + if (thiz->_audioRenderer != nullptr) { + if (hint == AUDIOSTREAM_INTERRUPT_HINT_RESUME) { + OH_AudioRenderer_Start(thiz->_audioRenderer); + } else if (hint == AUDIOSTREAM_INTERRUPT_HINT_PAUSE) { + OH_AudioRenderer_Pause(thiz->_audioRenderer); + } + } + return 0; +} + +bool PcmAudioService::init(AudioMixerController *controller, int numChannels, int sampleRate, int *bufferSizeInBytes) { + _controller = controller; + + OH_AudioStream_Result ret; + OH_AudioStream_Type type = AUDIOSTREAM_TYPE_RENDERER; + ret = OH_AudioStreamBuilder_Create(&_builder, type); + if (ret != AUDIOSTREAM_SUCCESS) { + return false; + } + + OH_AudioStreamBuilder_SetSamplingRate(_builder, sampleRate); + OH_AudioStreamBuilder_SetChannelCount(_builder, numChannels); + OH_AudioStreamBuilder_SetLatencyMode(_builder, AUDIOSTREAM_LATENCY_MODE_FAST); + OH_AudioStreamBuilder_SetRendererInfo(_builder, AUDIOSTREAM_USAGE_GAME); + + OH_AudioRenderer_Callbacks callbacks; + callbacks.OH_AudioRenderer_OnWriteData = AudioRendererOnWriteData; + callbacks.OH_AudioRenderer_OnInterruptEvent = AudioRendererOnInterrupt; + callbacks.OH_AudioRenderer_OnError = nullptr; + callbacks.OH_AudioRenderer_OnStreamEvent = nullptr; + ret = OH_AudioStreamBuilder_SetRendererCallback(_builder, callbacks, this); + if (ret != AUDIOSTREAM_SUCCESS) { + return false; + } + + ret = OH_AudioStreamBuilder_GenerateRenderer(_builder, &_audioRenderer); + if (ret != AUDIOSTREAM_SUCCESS) { + return false; + } + + int32_t buffer_size; + OH_AudioRenderer_GetFrameSizeInCallback(_audioRenderer, &buffer_size); + _bufferSizeInBytes = buffer_size * numChannels * 2; + *bufferSizeInBytes = buffer_size; + + if (__silenceData.empty()) { + __silenceData.resize(_bufferSizeInBytes, 0x00); + } + + ret = OH_AudioRenderer_Start(_audioRenderer); + if (ret != AUDIOSTREAM_SUCCESS) { + return false; + } + + return true; +} + +void PcmAudioService::pause() { + if (_audioRenderer != nullptr) { + OH_AudioRenderer_Pause(_audioRenderer); + } +} + +void PcmAudioService::resume() { + if (_audioRenderer != nullptr) { + OH_AudioRenderer_Start(_audioRenderer); + } +} + +}} // namespace cocos2d { namespace experimental diff --git a/cocos/audio/ohos/PcmAudioService.h b/cocos/audio/ohos/PcmAudioService.h new file mode 100644 index 000000000000..3f090d14dd7e --- /dev/null +++ b/cocos/audio/ohos/PcmAudioService.h @@ -0,0 +1,71 @@ +/**************************************************************************** +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 "IAudioPlayer.h" +#include "PcmData.h" + +#include +#include +#include "utils/Compat.h" +#include "cutils/log.h" +#include +#include + +namespace cocos2d { namespace experimental { + +class AudioMixerController; + +class PcmAudioService { +public: + inline int getChannelCount() const { return _numChannels; }; + + inline int getSampleRate() const { return _sampleRate; }; + + + PcmAudioService(); + + virtual ~PcmAudioService(); + + bool init(AudioMixerController* controller, int numChannels, int sampleRate, int* bufferSizeInBytes); + static int32_t AudioRendererOnWriteData(OH_AudioRenderer* renderer, void* userData, void* buffer, int32_t bufferLen); + static int32_t AudioRendererOnInterrupt(OH_AudioRenderer* renderer, void* userData, OH_AudioInterrupt_ForceType type, OH_AudioInterrupt_Hint hint); + + void pause(); + void resume(); + + int _numChannels; + int _sampleRate; + int _bufferSizeInBytes; + + AudioMixerController *_controller; + OH_AudioRenderer *_audioRenderer; + OH_AudioStreamBuilder *_builder; + + friend class AudioPlayerProvider; +}; + +}} // namespace cocos2d { namespace experimental diff --git a/cocos/audio/ohos/PcmBufferProvider.cpp b/cocos/audio/ohos/PcmBufferProvider.cpp new file mode 100644 index 000000000000..1e6a11425e7d --- /dev/null +++ b/cocos/audio/ohos/PcmBufferProvider.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +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 "PcmBufferProvider.h" +#include "cutils/log.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/ohos/PcmBufferProvider.h b/cocos/audio/ohos/PcmBufferProvider.h new file mode 100644 index 000000000000..d073b04a39b8 --- /dev/null +++ b/cocos/audio/ohos/PcmBufferProvider.h @@ -0,0 +1,51 @@ +/**************************************************************************** +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 "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/ohos/PcmData.cpp b/cocos/audio/ohos/PcmData.cpp new file mode 100644 index 000000000000..76df8d506dae --- /dev/null +++ b/cocos/audio/ohos/PcmData.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +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 "PcmData.h" +#include "OpenSLHelper.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/ohos/PcmData.h b/cocos/audio/ohos/PcmData.h new file mode 100644 index 000000000000..76157cdfc704 --- /dev/null +++ b/cocos/audio/ohos/PcmData.h @@ -0,0 +1,65 @@ +/**************************************************************************** +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/ohos/SimpleAudioEngine.cpp b/cocos/audio/ohos/SimpleAudioEngine.cpp new file mode 100644 index 000000000000..acebad887d70 --- /dev/null +++ b/cocos/audio/ohos/SimpleAudioEngine.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** +Copyright (c) 2010-2012 cocos2d-x.org +Copyright (c) 2013-2014 Chukong Technologies Inc. + +http://www.cocos2d-x.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +****************************************************************************/ + +#include +#include "../include/SimpleAudioEngine.h" +#include "platform/ohos/JsAudioEngine.h" +#include "platform/ohos/CCFileUtils-ohos.h" +#include "audio/include/AudioEngine.h" +#include "cutils/log.h" + +using namespace cocos2d::experimental; + +namespace CocosDenshion { + + static int _lastBackGroundAudioID = -1; + static char* _lastBackGroundFilename; + static int _lastEffectAudioID; + static char* _lastEffectFilename; + static bool useJsFunc = false; + static float effectsvolume = 1.0; + + static std::string getFullPathWithoutAssetsPrefix(const char* pszFilename) + { + std::string fullPath = cocos2d::FileUtils::sharedFileUtils()->fullPathForFilename(pszFilename); + size_t pos = fullPath.find("hap:/"); + if (pos == 0) + { + fullPath = fullPath.substr(strlen("hap:/")); + } + return fullPath; + } + + + static SimpleAudioEngine *s_pEngine = 0; + + SimpleAudioEngine* SimpleAudioEngine::getInstance() { + if (! s_pEngine) + { + s_pEngine = new SimpleAudioEngine(); + } + + return s_pEngine; + } + + void SimpleAudioEngine::end() { + cocos2d::experimental::AudioEngine::end(); + } + + SimpleAudioEngine::SimpleAudioEngine() { + } + + SimpleAudioEngine::~SimpleAudioEngine() { + } + + // Empty implementations. On Android, only subclasses are meant to be used + void SimpleAudioEngine::preloadBackgroundMusic(const char* pszFilePath) { + ALOGD("preloadBackgroundMusic start!"); + std::string uri(getFullPathWithoutAssetsPrefix(pszFilePath)); + + cocos2d::JsAudioEngine::preLoadBackgroundMusic(uri); + } + + void SimpleAudioEngine::playBackgroundMusic(const char* pszFilePath, bool bLoop) { + ALOGD("playBackgroundMusic: %s", pszFilePath); + std::string uri(getFullPathWithoutAssetsPrefix(pszFilePath)); + _lastBackGroundAudioID = cocos2d::experimental::AudioEngine::play2d(uri, bLoop, 0.5); + ALOGD("playBackgroundMusic %s end, and id:%d", uri.c_str(), _lastBackGroundAudioID); + } + + void SimpleAudioEngine::stopBackgroundMusic(bool bReleaseData) { + ALOGD("stopBackgroundMusic"); + cocos2d::experimental::AudioEngine::stop(_lastBackGroundAudioID); + ALOGD("stopBackgroundMusic end, and id:%d", _lastBackGroundAudioID); + } + + void SimpleAudioEngine::pauseBackgroundMusic() { + ALOGD("pauseBackgroundMusic"); + cocos2d::experimental::AudioEngine::pause(_lastBackGroundAudioID); + } + + void SimpleAudioEngine::resumeBackgroundMusic() { + ALOGD("resumeBackgroundMusic start!"); + cocos2d::experimental::AudioEngine::resume(_lastBackGroundAudioID); + } + + void SimpleAudioEngine::rewindBackgroundMusic() { + ALOGD("rewindBackgroundMusic"); + cocos2d::experimental::AudioEngine::setCurrentTime(_lastBackGroundAudioID, 0); + } + + bool SimpleAudioEngine::willPlayBackgroundMusic() { + return _lastBackGroundAudioID != -1; + } + + bool SimpleAudioEngine::isBackgroundMusicPlaying() { + return cocos2d::experimental::AudioEngine::getState(_lastBackGroundAudioID) == cocos2d::experimental::AudioEngine::AudioState::PLAYING; + } + + float SimpleAudioEngine::getBackgroundMusicVolume() { + return AudioEngine::getVolume(_lastBackGroundAudioID); + } + + void SimpleAudioEngine::setBackgroundMusicVolume(float volume) { + AudioEngine::setVolume(_lastBackGroundAudioID, volume); + } + + float SimpleAudioEngine::getEffectsVolume() { + return effectsvolume; + } + + void SimpleAudioEngine::setEffectsVolume(float volume) { + effectsvolume = volume; + } + + unsigned int SimpleAudioEngine::playEffect(const char* pszFilePath, bool bLoop, float pitch, float pan, float gain) { + std::string uri(getFullPathWithoutAssetsPrefix(pszFilePath)); + ALOGD("playEffect start! and uri:%s", uri.data()); + return AudioEngine::play2d(uri, bLoop); + } + + void SimpleAudioEngine::pauseEffect(unsigned int nSoundId) { + AudioEngine::stop(nSoundId); + } + + void SimpleAudioEngine::pauseAllEffects() { + AudioEngine::pauseAll(); + } + + void SimpleAudioEngine::resumeEffect(unsigned int nSoundId) { + cocos2d::experimental::AudioEngine::resume(nSoundId); + } + + void SimpleAudioEngine::resumeAllEffects() { + cocos2d::experimental::AudioEngine::resumeAll(); + } + + void SimpleAudioEngine::stopEffect(unsigned int nSoundId) { + AudioEngine::stop(nSoundId); + } + + void SimpleAudioEngine::stopAllEffects() { + cocos2d::experimental::AudioEngine::stopAll(); + } + + void SimpleAudioEngine::preloadEffect(const char* pszFilePath) { + std::string uri(getFullPathWithoutAssetsPrefix(pszFilePath)); + AudioEngine::preload(uri); + } + + void SimpleAudioEngine::unloadEffect(const char* pszFilePath) { + std::string uri(getFullPathWithoutAssetsPrefix(pszFilePath)); + AudioEngine::uncache(uri); + } +} diff --git a/cocos/audio/ohos/Track.cpp b/cocos/audio/ohos/Track.cpp new file mode 100644 index 000000000000..6e16290849b9 --- /dev/null +++ b/cocos/audio/ohos/Track.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +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 "cutils/log.h" +#include "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/ohos/Track.h b/cocos/audio/ohos/Track.h new file mode 100644 index 000000000000..7e6400d57e8f --- /dev/null +++ b/cocos/audio/ohos/Track.h @@ -0,0 +1,101 @@ +/**************************************************************************** +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 "PcmData.h" +#include "IVolumeProvider.h" +#include "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/ohos/UrlAudioPlayer.cpp b/cocos/audio/ohos/UrlAudioPlayer.cpp new file mode 100644 index 000000000000..48eed27c0754 --- /dev/null +++ b/cocos/audio/ohos/UrlAudioPlayer.cpp @@ -0,0 +1,336 @@ +/**************************************************************************** +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 "UrlAudioPlayer.h" +#include "ICallerThreadUtils.h" +#include +#include "CCPlatformMacros.h" +#include +#include // for std::find + +namespace { + +std::mutex __playerContainerMutex;//NOLINT(bugprone-reserved-identifier,readability-identifier-naming) +std::map __playerContainer;//NOLINT(bugprone-reserved-identifier,readability-identifier-naming) + +std::once_flag __onceFlag;//NOLINT(bugprone-reserved-identifier,readability-identifier-naming) + +} // namespace + +namespace cocos2d { namespace experimental { + +UrlAudioPlayer::UrlAudioPlayer(ICallerThreadUtils *callerThreadUtils) + : _callerThreadUtils(callerThreadUtils), _id(-1), _playObj(nullptr),_volume(0.0F), _duration(0.0F), _isLoop(false), _isAudioFocus(true), _state(State::INVALID), _playEventCallback(nullptr), _isDestroyed(std::make_shared(false)) +{ + _callerThreadId = callerThreadUtils->getCallerThreadId(); +} + +UrlAudioPlayer::~UrlAudioPlayer() +{ + __playerContainerMutex.lock(); + + auto it = __playerContainer.begin(); + while(it != __playerContainer.end()) + { + if(it->second == this) + { + it = __playerContainer.erase(it); + } + else + { + it++; + } + } + + __playerContainerMutex.unlock(); +} + +void UrlAudioPlayer::playEventCallback() { + 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; + } + + 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()); + int32_t status = OH_AVPlayer_Stop(_playObj); + if(status != 0) { + ALOGE("UrlAudioPlayer::stop failed"); + return; + } + + 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) { + int32_t status = OH_AVPlayer_Pause(_playObj); + if(status != 0) { + ALOGE("UrlAudioPlayer::pause failed"); + return; + } + 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) { + int32_t status = OH_AVPlayer_Play(_playObj); + if(status != 0) { + ALOGE("UrlAudioPlayer::resume failed"); + return; + } + 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) { + int32_t status = OH_AVPlayer_Play(_playObj); + if(status != 0) { + ALOGE("UrlAudioPlayer::play failed"); + return; + } + setState(State::PLAYING); + } else { + ALOGW("UrlAudioPlayer (%p, state:%d) isn't paused or initialized, could not invoke play!", this, static_cast(_state)); + } +} + +void UrlAudioPlayer::setVolumeToAVPlayer(float volume) +{ + int32_t status = OH_AVPlayer_SetVolume(_playObj, volume, volume); + if(status != 0) { + ALOGE("UrlAudioPlayer::setVolume %d failed", volume); + } +} + +void UrlAudioPlayer::setVolume(float volume) { + _volume = volume; + if (_isAudioFocus) { + setVolumeToAVPlayer(_volume); + } +} + +float UrlAudioPlayer::getVolume() const { + return _volume; +} + +void UrlAudioPlayer::setAudioFocus(bool isFocus) { + _isAudioFocus = isFocus; + float volume = _isAudioFocus ? _volume : 0.0F; + setVolumeToAVPlayer(volume); +} + +float UrlAudioPlayer::getDuration() const { + if (_duration > 0) { + return _duration; + } + + int32_t duration = 0; + int32_t status = OH_AVPlayer_GetDuration(_playObj, &duration); + if(status != 0) { + ALOGE("UrlAudioPlayer::getDuration failed"); + return -1.0F; + } + const_cast(this)->_duration = duration / 1000.0F; + if (_duration <= 0) { + return -1.0F; + } + return _duration; +} + +float UrlAudioPlayer::getPosition() const { + int32_t currentTime = 0; + int status = OH_AVPlayer_GetCurrentTime(_playObj, ¤tTime); + if(status != 0) { + ALOGE("UrlAudioPlayer::getPosition failed"); + return -1.0F; + } + return currentTime / 1000.0F; +} + +bool UrlAudioPlayer::setPosition(float pos) +{ + int32_t millisecond = 1000.0F * pos; + int status = OH_AVPlayer_Seek(_playObj, millisecond, AV_SEEK_NEXT_SYNC); + if(status != 0) { + ALOGE("UrlAudioPlayer::setPosition %f failed", pos); + return false; + } + return true; +} + +void UrlAudioPlayer::onInfo(OH_AVPlayer *player, AVPlayerOnInfoType type, int32_t extra) +{ + if (type == AV_INFO_TYPE_STATE_CHANGE) { + if (extra == AV_COMPLETED) { + auto it = __playerContainer.find(player); + if (it != __playerContainer.end()) + { + UrlAudioPlayer *audioPlayer = it->second; + audioPlayer->playEventCallback(); + } + } + } else if (type == AV_INFO_TYPE_INTERRUPT_EVENT) { + auto it = __playerContainer.find(player); + if (it != __playerContainer.end()) { + UrlAudioPlayer *audioPlayer = it->second; + if (extra == 1) { + audioPlayer->resume(); + } else if (extra == 2) { + audioPlayer->pause(); + } else { + ALOGV("UrlAudioPlayer was interrupted, hint type is %d", extra); + } + } + } +} + +void UrlAudioPlayer::onError(OH_AVPlayer *player, int32_t errorCode, const char *errorMsg) { + ALOGE("UrlAudioPlayer play failed, errorCode is: %d, errorMsg is %s", errorCode, errorMsg); +} + +bool UrlAudioPlayer::prepare(const std::string &url, std::shared_ptr assetFd, int32_t start, int32_t length) +{ + _url = url; + _assetFd = assetFd; + _playObj = OH_AVPlayer_Create(); + __playerContainerMutex.lock(); + __playerContainer[_playObj] = this; + __playerContainerMutex.unlock(); + + AVPlayerCallback callback; + callback.onInfo = this->onInfo; + callback.onError = this->onError; + OH_AVPlayer_SetPlayerCallback(_playObj, callback); + OH_AVPlayer_SetFDSource(_playObj, _assetFd->getFd(), start, length); + OH_AVPlayer_SetAudioRendererInfo(_playObj, OH_AudioStream_Usage::AUDIOSTREAM_USAGE_GAME); + OH_AVErrCode code = OH_AVPlayer_Prepare(_playObj); + + if (code == AV_ERR_OK) { + setState(State::INITIALIZED); + setVolume(1.0f); + return true; + } + + ALOGE("Oops, prepare error: %d", (int)code); + return false; +} + +void UrlAudioPlayer::rewind() { + // Not supported currently. since cocos audio engine will new -> prepare -> play again. +} + +void UrlAudioPlayer::setLoop(bool isLoop) { + _isLoop = isLoop; + int status = OH_AVPlayer_SetLooping(_playObj, _isLoop); + if(status != 0) { + ALOGE( "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(); + + auto it = temp.begin(); + while(it != temp.end()) + { + UrlAudioPlayer* thiz = it->second; + thiz->stop(); + it++; + } +} + +void UrlAudioPlayer::destroy() +{ + if (!*_isDestroyed) + { + *_isDestroyed = true; + OH_AVErrCode code = OH_AVPlayer_Release(_playObj); + if (code != AV_ERR_OK) { + ALOGE( "UrlAudioPlayer release failed, errcode is %d", code); + } + } +} + +}} // namespace cocos2d { namespace experimental diff --git a/cocos/audio/ohos/UrlAudioPlayer.h b/cocos/audio/ohos/UrlAudioPlayer.h new file mode 100644 index 000000000000..62e4d4262494 --- /dev/null +++ b/cocos/audio/ohos/UrlAudioPlayer.h @@ -0,0 +1,125 @@ +/**************************************************************************** +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 +#include "AssetFd.h" +#include "IAudioPlayer.h" +#include "cutils/log.h" +#include "multimedia/player_framework/avplayer.h" + +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; + + UrlAudioPlayer(ICallerThreadUtils *callerThreadUtils); + + virtual ~UrlAudioPlayer(); + + bool prepare(const std::string &url, std::shared_ptr assetFd, int32_t start, int32_t length); + + static void onInfo(OH_AVPlayer *player, AVPlayerOnInfoType type, int32_t extra); + + static void onError(OH_AVPlayer *player, int32_t errorCode, const char *errorMsg); + + static void stopAll(); + + void destroy(); + + inline void setState(State state) { _state = state; }; + + void playEventCallback(); + + void setVolumeToAVPlayer(float volume); + +private: + ICallerThreadUtils *_callerThreadUtils; + + int _id; + std::string _url; + + std::shared_ptr _assetFd; + + OH_AVPlayer *_playObj; + + float _volume; + float _duration; + bool _isLoop; + bool _isAudioFocus; + State _state; + + PlayEventCallback _playEventCallback; + + std::thread::id _callerThreadId; + std::shared_ptr _isDestroyed; + + friend class AudioPlayerProvider; +}; + +}} // namespace cocos2d { namespace experimental diff --git a/cocos/audio/ohos/Value.h b/cocos/audio/ohos/Value.h new file mode 100644 index 000000000000..bf5d8b21ebd9 --- /dev/null +++ b/cocos/audio/ohos/Value.h @@ -0,0 +1,240 @@ +/**************************************************************************** + Copyright (c) 2013-2016 Chukong Technologies Inc. + Copyright (c) 2017-2022 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You shall + not use Cocos Creator software for developing other software or tools that's + used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. + + 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 "platform/CCPlatformMacros.h" + + +namespace cocos2d { namespace experimental { + +class Value; + +using ValueVector = std::vector; +using ValueMap = std::unordered_map; +using ValueMapIntKey = std::unordered_map; + +extern const ValueVector VALUE_VECTOR_NULL; +extern const ValueMap VALUE_MAP_NULL; +extern const ValueMapIntKey VALUE_MAP_INT_KEY_NULL; + +/* + * This class is provide as a wrapper of basic types, such as int and bool. + */ +class Value { +public: + /** A predefined Value that has not value. */ + static const Value VALUE_NULL; + + /** Default constructor. */ + Value(); + + /** Create a Value by an unsigned char value. */ + explicit Value(unsigned char v); + + /** Create a Value by an integer value. */ + explicit Value(int v); + + /** Create a Value by an unsigned value. */ + explicit Value(unsigned int v); + + /** Create a Value by a float value. */ + explicit Value(float v); + + /** Create a Value by a double value. */ + explicit Value(double v); + + /** Create a Value by a bool value. */ + explicit Value(bool v); + + /** Create a Value by a char pointer. It will copy the chars internally. */ + explicit Value(const char *v); + + /** Create a Value by a string. */ + explicit Value(const std::string &v); + + /** Create a Value by a ValueVector object. */ + explicit Value(const ValueVector &v); + /** Create a Value by a ValueVector object. It will use std::move internally. */ + explicit Value(ValueVector &&v); + + /** Create a Value by a ValueMap object. */ + explicit Value(const ValueMap &v); + /** Create a Value by a ValueMap object. It will use std::move internally. */ + explicit Value(ValueMap &&v); + + /** Create a Value by a ValueMapIntKey object. */ + explicit Value(const ValueMapIntKey &v); + /** Create a Value by a ValueMapIntKey object. It will use std::move internally. */ + explicit Value(ValueMapIntKey &&v); + + /** Create a Value by another Value object. */ + Value(const Value &other); + /** Create a Value by a Value object. It will use std::move internally. */ + Value(Value &&other) noexcept; + + /** Destructor. */ + ~Value(); + + /** Assignment operator, assign from Value to Value. */ + Value &operator=(const Value &other); + /** Assignment operator, assign from Value to Value. It will use std::move internally. */ + Value &operator=(Value &&other) noexcept; + + /** Assignment operator, assign from unsigned char to Value. */ + Value &operator=(unsigned char v); + /** Assignment operator, assign from integer to Value. */ + Value &operator=(int v); + /** Assignment operator, assign from integer to Value. */ + Value &operator=(unsigned int v); + /** Assignment operator, assign from float to Value. */ + Value &operator=(float v); + /** Assignment operator, assign from double to Value. */ + Value &operator=(double v); + /** Assignment operator, assign from bool to Value. */ + Value &operator=(bool v); + /** Assignment operator, assign from char* to Value. */ + Value &operator=(const char *v); + /** Assignment operator, assign from string to Value. */ + Value &operator=(const std::string &v); + + /** Assignment operator, assign from ValueVector to Value. */ + Value &operator=(const ValueVector &v); + /** Assignment operator, assign from ValueVector to Value. */ + Value &operator=(ValueVector &&v); + + /** Assignment operator, assign from ValueMap to Value. */ + Value &operator=(const ValueMap &v); + /** Assignment operator, assign from ValueMap to Value. It will use std::move internally. */ + Value &operator=(ValueMap &&v); + + /** Assignment operator, assign from ValueMapIntKey to Value. */ + Value &operator=(const ValueMapIntKey &v); + /** Assignment operator, assign from ValueMapIntKey to Value. It will use std::move internally. */ + Value &operator=(ValueMapIntKey &&v); + + /** != operator overloading */ + bool operator!=(const Value &v); + /** != operator overloading */ + bool operator!=(const Value &v) const; + /** == operator overloading */ + bool operator==(const Value &v); + /** == operator overloading */ + bool operator==(const Value &v) const; + + /** Gets as a byte value. Will convert to unsigned char if possible, or will trigger assert error. */ + unsigned char asByte() const; + /** Gets as an integer value. Will convert to integer if possible, or will trigger assert error. */ + int asInt() const; + /** Gets as an unsigned value. Will convert to unsigned if possible, or will trigger assert error. */ + unsigned int asUnsignedInt() const; + /** Gets as a float value. Will convert to float if possible, or will trigger assert error. */ + float asFloat() const; + /** Gets as a double value. Will convert to double if possible, or will trigger assert error. */ + double asDouble() const; + /** Gets as a bool value. Will convert to bool if possible, or will trigger assert error. */ + bool asBool() const; + /** Gets as a string value. Will convert to string if possible, or will trigger assert error. */ + std::string asString() const; + + /** Gets as a ValueVector reference. Will convert to ValueVector if possible, or will trigger assert error. */ + ValueVector &asValueVector(); + /** Gets as a const ValueVector reference. Will convert to ValueVector if possible, or will trigger assert error. */ + const ValueVector &asValueVector() const; + + /** Gets as a ValueMap reference. Will convert to ValueMap if possible, or will trigger assert error. */ + ValueMap &asValueMap(); + /** Gets as a const ValueMap reference. Will convert to ValueMap if possible, or will trigger assert error. */ + const ValueMap &asValueMap() const; + + /** Gets as a ValueMapIntKey reference. Will convert to ValueMapIntKey if possible, or will trigger assert error. */ + ValueMapIntKey &asIntKeyMap(); + /** Gets as a const ValueMapIntKey reference. Will convert to ValueMapIntKey if possible, or will trigger assert error. */ + const ValueMapIntKey &asIntKeyMap() const; + + /** + * Checks if the Value is null. + * @return True if the Value is null, false if not. + */ + inline bool isNull() const { return _type == Type::NONE; } + + /** Value type wrapped by Value. */ + enum class Type { + /// no value is wrapped, an empty Value + NONE = 0, + /// wrap byte + BYTE, + /// wrap integer + INTEGER, + /// wrap unsigned + UNSIGNED, + /// wrap float + FLOAT, + /// wrap double + DOUBLE, + /// wrap bool + BOOLEAN, + /// wrap string + STRING, + /// wrap vector + VECTOR, + /// wrap ValueMap + MAP, + /// wrap ValueMapIntKey + INT_KEY_MAP + }; + + /** Gets the value type. */ + inline Type getType() const { return _type; } + + /** Gets the description of the class. */ + std::string getDescription() const; + +private: + void clear(); + void reset(Type type); + + union { + unsigned char byteVal; + int intVal; + unsigned int unsignedVal; + float floatVal; + double doubleVal; + bool boolVal; + + std::string *strVal; + ValueVector *vectorVal; + ValueMap *mapVal; + ValueMapIntKey *intKeyMapVal; + } _field; + + Type _type; +}; + +}} diff --git a/cocos/audio/ohos/audio.h b/cocos/audio/ohos/audio.h new file mode 100644 index 000000000000..cada3009e973 --- /dev/null +++ b/cocos/audio/ohos/audio.h @@ -0,0 +1,491 @@ +/**************************************************************************** +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 "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 + * REFINE: 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, + /* REFINE: 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, + /* REFINE: 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: + // REFINE: 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; + // IDEA: 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/ohos/audio_utils/AudioDef.h b/cocos/audio/ohos/audio_utils/AudioDef.h new file mode 100644 index 000000000000..51008475f08f --- /dev/null +++ b/cocos/audio/ohos/audio_utils/AudioDef.h @@ -0,0 +1,49 @@ +/**************************************************************************** + Copyright (c) 2016 Chukong Technologies Inc. + Copyright (c) 2017-2022 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You shall + not use Cocos Creator software for developing other software or tools that's + used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. + + 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 +enum class AudioDataFormat { + UNKNOWN = 0, + SIGNED_8, + UNSIGNED_8, + SIGNED_16, + UNSIGNED_16, + SIGNED_32, + UNSIGNED_32, + FLOAT_32, + FLOAT_64, +}; +struct PCMHeader { + uint32_t totalFrames{0}; + uint32_t bytesPerFrame{0}; + uint32_t sampleRate{0}; + uint32_t channelCount{0}; + AudioDataFormat dataFormat{AudioDataFormat::UNKNOWN}; +}; + +#define CHANNEL_NUMBERS 2 diff --git a/cocos/audio/ohos/audio_utils/AudioEngine-ohos.h b/cocos/audio/ohos/audio_utils/AudioEngine-ohos.h new file mode 100644 index 000000000000..939757952df0 --- /dev/null +++ b/cocos/audio/ohos/audio_utils/AudioEngine-ohos.h @@ -0,0 +1,410 @@ +/**************************************************************************** + Copyright (c) 2014-2016 Chukong Technologies Inc. + Copyright (c) 2017-2022 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You shall + not use Cocos Creator software for developing other software or tools that's + used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. + + 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 "AudioDef.h" +//#include "audio/include/Export.h" +#include "CCPlatformMacros.h" +#include "../Macros.h" +#include +#include +#include +#include +//#include "bindings/event/CustomEventTypes.h" +//#include "bindings/event/EventDispatcher.h" + +#ifdef ERROR + #undef ERROR +#endif // ERROR + +/** + * @addtogroup audio + * @{ + */ + +namespace cocos2d { namespace experimental { +/** + * @class AudioProfile + * + * @brief + * @js NA + */ +class AudioProfile { +public: + //Profile name can't be empty. + std::string name; + //The maximum number of simultaneous audio instance. + unsigned int maxInstances{}; + + /* Minimum delay in between sounds */ + double minDelay{}; + + /** + * Default constructor + * + * @lua new + */ + AudioProfile() = default; +}; + +class AudioEngineImpl; + +/** + * @class AudioEngine + * + * @brief Offers a interface to play audio. + * + * @note Make sure to call AudioEngine::end() when the audio engine is not needed anymore to release resources. + * @js NA + */ + +class AudioEngine { +public: + /** AudioState enum,all possible states of an audio instance.*/ + enum class AudioState { + ERROR = -1, + INITIALIZING, + PLAYING, + PAUSED + }; + + static const int INVALID_AUDIO_ID; + + static const float TIME_UNKNOWN; + + static bool lazyInit(); + + /** + * Release objects relating to AudioEngine. + * + * @warning It must be called before the application exit. + * @lua endToLua + */ + static void end(); + + /** + * Gets the default profile of audio instances. + * + * @return The default profile of audio instances. + */ + static AudioProfile *getDefaultProfile(); + + /** + * Play 2d sound. + * + * @param filePath The path of an audio file. + * @param loop Whether audio instance loop or not. + * @param volume Volume value (range from 0.0 to 1.0). + * @param profile A profile for audio instance. When profile is not specified, default profile will be used. + * @return An audio ID. It allows you to dynamically change the behavior of an audio instance on the fly. + * + * @see `AudioProfile` + */ + static int play2d(const std::string &filePath, bool loop = false, float volume = 1.0F, const AudioProfile *profile = nullptr); + + /** + * Sets whether an audio instance loop or not. + * + * @param audioID An audioID returned by the play2d function. + * @param loop Whether audio instance loop or not. + */ + static void setLoop(int audioID, bool loop); + + /** + * Checks whether an audio instance is loop. + * + * @param audioID An audioID returned by the play2d function. + * @return Whether or not an audio instance is loop. + */ + static bool isLoop(int audioID); + + /** + * Sets volume for an audio instance. + * + * @param audioID An audioID returned by the play2d function. + * @param volume Volume value (range from 0.0 to 1.0). + */ + static void setVolume(int audioID, float volume); + + /** + * sets volume factor for all audio instance + * @param factor, Volume factor(range from 0.0 to 1.0). + */ + static void setVolumeFactor(float factor); + + /** + * Gets the volume value of an audio instance. + * + * @param audioID An audioID returned by the play2d function. + * @return Volume value (range from 0.0 to 1.0). + */ + static float getVolume(int audioID); + + /** + * Pause an audio instance. + * + * @param audioID An audioID returned by the play2d function. + */ + static void pause(int audioID); + + /** Pause all playing audio instances. */ + static void pauseAll(); + + /** + * Resume an audio instance. + * + * @param audioID An audioID returned by the play2d function. + */ + static void resume(int audioID); + + /** Resume all suspended audio instances. */ + static void resumeAll(); + + /** + * Stop an audio instance. + * + * @param audioID An audioID returned by the play2d function. + */ + static void stop(int audioID); + + /** Stop all audio instances. */ + static void stopAll(); + + /** + * Sets the current playback position of an audio instance. + * + * @param audioID An audioID returned by the play2d function. + * @param time The offset in seconds from the start to seek to. + * @return + */ + static bool setCurrentTime(int audioID, float time); + + /** + * Gets the current playback position of an audio instance. + * + * @param audioID An audioID returned by the play2d function. + * @return The current playback position of an audio instance. + */ + static float getCurrentTime(int audioID); + + /** + * Gets the duration of an audio instance. + * + * @param audioID An audioID returned by the play2d function. + * @return The duration of an audio instance. + */ + static float getDuration(int audioID); + + /** + * Gets the duration of an audio file. + * + * @param filePath The path of an audio file. + * @return The duration of an audio file. + */ + static float getDurationFromFile(const std::string &filePath); + + /** + * Returns the state of an audio instance. + * + * @param audioID An audioID returned by the play2d function. + * @return The status of an audio instance. + */ + static AudioState getState(int audioID); + + /** + * Register a callback to be invoked when an audio instance has completed playing. + * + * @param audioID An audioID returned by the play2d function. + * @param callback + */ + static void setFinishCallback(int audioID, const std::function &callback); + + /** + * Gets the maximum number of simultaneous audio instance of AudioEngine. + */ + static int getMaxAudioInstance() { return static_cast(sMaxInstances); } + + /** + * Sets the maximum number of simultaneous audio instance for AudioEngine. + * + * @param maxInstances The maximum number of simultaneous audio instance. + */ + static bool setMaxAudioInstance(int maxInstances); + + /** + * Uncache the audio data from internal buffer. + * AudioEngine cache audio data on ios,mac, and oalsoft platform. + * + * @warning This can lead to stop related audio first. + * @param filePath Audio file path. + */ + static void uncache(const std::string &filePath); + + /** + * Uncache all audio data from internal buffer. + * + * @warning All audio will be stopped first. + */ + static void uncacheAll(); + + /** + * Gets the audio profile by id of audio instance. + * + * @param audioID An audioID returned by the play2d function. + * @return The audio profile. + */ + static AudioProfile *getProfile(int audioID); + + /** + * Gets an audio profile by name. + * + * @param profileName A name of audio profile. + * @return The audio profile. + */ + static AudioProfile *getProfile(const std::string &profileName); + + /** + * Preload audio file. + * @param filePath The file path of an audio. + */ + static void preload(const std::string &filePath) { preload(filePath, nullptr); } + + /** + * Preload audio file. + * @param filePath The file path of an audio. + * @param callback A callback which will be called after loading is finished. + */ + static void preload(const std::string &filePath, const std::function &callback); + + /** + * Gets playing audio count. + */ + static int getPlayingAudioCount(); + + /** + * Whether to enable playing audios + * @note If it's disabled, current playing audios will be stopped and the later 'preload', 'play2d' methods will take no effects. + */ + static void setEnabled(bool isEnabled); + /** + * Check whether AudioEngine is enabled. + */ + static bool isEnabled(); + + /** + * @brief Get the PCMHeader of audio + * + * @param url The file url of an audio. same as filePath + * @return PCMHeader of audio + */ + static PCMHeader getPCMHeader(const char *url); + + /** + * @brief Get the Buffer object + * + * @param channelID as there might be several channels at same time, select one to get buffer. + * Start from 0 + * @return PCM datas behave as a std::vector. You can check byte length in PCMHeader. + */ + static std::vector getOriginalPCMBuffer(const char *url, uint32_t channelID); + +protected: + static void addTask(const std::function &task); + static void remove(int audioID); + + static void pauseAll(std::vector *pausedAudioIDs); + static void resumeAll(std::vector *pausedAudioIDs); + + struct ProfileHelper { + AudioProfile profile; + + std::list audioIDs; + + std::chrono::high_resolution_clock::time_point lastPlayTime; + + ProfileHelper() = default; + }; + + struct AudioInfo { + const std::string *filePath; + ProfileHelper *profileHelper; + + float volume; + bool loop; + float duration; + AudioState state; + + AudioInfo(); + ~AudioInfo() = default; + + private: + AudioInfo(const AudioInfo &info); + AudioInfo(AudioInfo &&info) noexcept; + AudioInfo &operator=(const AudioInfo &info); + AudioInfo &operator=(AudioInfo &&info) noexcept; + }; + + //audioID,audioAttribute + static std::unordered_map sAudioIDInfoMap; + + //audio file path,audio IDs + static std::unordered_map> sAudioPathIDMap; + + //profileName,ProfileHelper + static std::unordered_map sAudioPathProfileHelperMap; + + static unsigned int sMaxInstances; + + static ProfileHelper *sDefaultProfileHelper; + + static AudioEngineImpl *sAudioEngineImpl; + + class AudioEngineThreadPool; + static AudioEngineThreadPool *sThreadPool; + + static bool sIsEnabled; + +private: + static float sVolumeFactor; + static uint32_t sOnPauseListenerID; + static uint32_t sOnResumeListenerID; + static std::vector sBreakAudioID; + + // TBD 前后台切换待修复 + static void onEnterBackground(); + static void onEnterForeground(); + + friend class AudioEngineImpl; +}; + +}} // namespace CocosDenshion + +// end group +/// @} diff --git a/cocos/audio/ohos/audio_utils/RefCounted.cpp b/cocos/audio/ohos/audio_utils/RefCounted.cpp new file mode 100644 index 000000000000..765bb22c8c4c --- /dev/null +++ b/cocos/audio/ohos/audio_utils/RefCounted.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** + Copyright (c) 2010-2012 cocos2d-x.org + Copyright (c) 2013-2016 Chukong Technologies Inc. + Copyright (c) 2017-2021 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You shall + not use Cocos Creator software for developing other software or tools that's + used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. + + 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 "RefCounted.h" + +#if CC_REF_LEAK_DETECTION + #include // std::find + #include "base/Log.h" + #include "base/std/container/list.h" +#endif + +namespace cocos2d { namespace experimental { + +#if CC_REF_LEAK_DETECTION +static void trackRef(RefCounted *ref); +static void untrackRef(RefCounted *ref); +#endif + +RefCounted::RefCounted() { // NOLINT(modernize-use-equals-default) +#if CC_REF_LEAK_DETECTION + trackRef(this); +#endif +} + +RefCounted::~RefCounted() { // NOLINT(modernize-use-equals-default) +#if CC_REF_LEAK_DETECTION + untrackRef(this); +#endif +} + +void RefCounted::addRef() { + ++_referenceCount; +} + +void RefCounted::release() { + CC_ASSERT(_referenceCount > 0); + --_referenceCount; + + if (_referenceCount == 0) { + delete this; + } +} + +unsigned int RefCounted::getRefCount() const { + return _referenceCount; +} + +#if CC_REF_LEAK_DETECTION + +static std::list __refAllocationList; + +void RefCounted::printLeaks() { + // Dump Ref object memory leaks + if (__refAllocationList.empty()) { + CC_LOG_INFO("[memory] All Ref objects successfully cleaned up (no leaks detected).\n"); + } else { + CC_LOG_INFO("[memory] WARNING: %d Ref objects still active in memory.\n", (int)__refAllocationList.size()); + + for (const auto &ref : __refAllocationList) { + CC_ASSERT(ref); + const char *type = typeid(*ref).name(); + CC_LOG_INFO("[memory] LEAK: Ref object '%s' still active with reference count %d.\n", (type ? type : ""), ref->getRefCount()); + } + } +} + +static void trackRef(RefCounted *ref) { + CC_ASSERT(ref); + + // Create memory allocation record. + __refAllocationList.push_back(ref); +} + +static void untrackRef(RefCounted *ref) { + auto iter = std::find(__refAllocationList.begin(), __refAllocationList.end(), ref); + if (iter == __refAllocationList.end()) { + CC_LOG_INFO("[memory] CORRUPTION: Attempting to free (%s) with invalid ref tracking record.\n", typeid(*ref).name()); + return; + } + + __refAllocationList.erase(iter); +} + +#endif // #if CC_REF_LEAK_DETECTION + +}} // namespace CocosDenshion diff --git a/cocos/audio/ohos/audio_utils/RefCounted.h b/cocos/audio/ohos/audio_utils/RefCounted.h new file mode 100644 index 000000000000..74cdd9ae808b --- /dev/null +++ b/cocos/audio/ohos/audio_utils/RefCounted.h @@ -0,0 +1,106 @@ +/**************************************************************************** + Copyright (c) 2010-2012 cocos2d-x.org + Copyright (c) 2013-2016 Chukong Technologies Inc. + Copyright (c) 2017-2022 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You shall + not use Cocos Creator software for developing other software or tools that's + used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. + + 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/CCPlatformMacros.h" + +#define CC_REF_LEAK_DETECTION 0 + +namespace cocos2d { namespace experimental { + +class RefCounted; + +/** + * Interface that defines how to clone an Ref. + */ +class Clonable { +public: + /** Returns a copy of the Ref. */ + virtual Clonable *clone() const = 0; + + virtual ~Clonable() = default; +}; + +/** + * Ref is used for reference count management. If a class inherits from Ref, + * then it is easy to be shared in different places. + */ +class RefCounted { +public: + virtual ~RefCounted(); + + /** + * Retains the ownership. + * + * This increases the Ref's reference count. + * + * @see release, autorelease + */ + void addRef(); + + /** + * Releases the ownership immediately. + * + * This decrements the Ref's reference count. + * + * If the reference count reaches 0 after the decrement, this Ref is + * destructed. + * + * @see retain, autorelease + */ + void release(); + + /** + * Returns the Ref's current reference count. + * + * @returns The Ref's reference count. + */ + unsigned int getRefCount() const; + +protected: + /** + * Constructor + * + * The Ref's reference count is 1 after construction. + */ + RefCounted(); + + /// count of references + unsigned int _referenceCount{0}; + + // Memory leak diagnostic data (only included when CC_REF_LEAK_DETECTION is defined and its value isn't zero) +#if CC_REF_LEAK_DETECTION +public: + static void printLeaks(); +#endif +}; + +using SCHEDULE_CB = void (RefCounted::*)(float); +#define CC_SCHEDULE_CALLBACK(cb) static_cast(&cb) + +}} // namespace CocosDenshion diff --git a/cocos/audio/ohos/audio_utils/Value.cpp b/cocos/audio/ohos/audio_utils/Value.cpp new file mode 100644 index 000000000000..88057eb1eba7 --- /dev/null +++ b/cocos/audio/ohos/audio_utils/Value.cpp @@ -0,0 +1,847 @@ +/**************************************************************************** + Copyright (c) 2013-2016 Chukong Technologies Inc. + Copyright (c) 2017-2022 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You shall + not use Cocos Creator software for developing other software or tools that's + used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. + + 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 "../Value.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "../utils/Utils.h" + +namespace cocos2d { namespace experimental { + +const ValueVector VALUE_VECTOR_NULL; +const ValueMap VALUE_MAP_NULL = {}; +const ValueMapIntKey VALUE_MAP_INT_KEY_NULL = {}; + +const Value Value::VALUE_NULL; + +Value::Value() +: _type(Type::NONE) { + memset(&_field, 0, sizeof(_field)); +} + +Value::Value(unsigned char v) +: _type(Type::BYTE) { + _field.byteVal = v; +} + +Value::Value(int v) +: _type(Type::INTEGER) { + _field.intVal = v; +} + +Value::Value(unsigned int v) +: _type(Type::UNSIGNED) { + _field.unsignedVal = v; +} + +Value::Value(float v) +: _type(Type::FLOAT) { + _field.floatVal = v; +} + +Value::Value(double v) +: _type(Type::DOUBLE) { + _field.doubleVal = v; +} + +Value::Value(bool v) +: _type(Type::BOOLEAN) { + _field.boolVal = v; +} + +Value::Value(const char *v) +: _type(Type::STRING) { + _field.strVal = new std::string(); + if (v) { + *_field.strVal = v; + } +} + +Value::Value(const std::string &v) +: _type(Type::STRING) { + _field.strVal = new std::string(); + *_field.strVal = v; +} + +Value::Value(const ValueVector &v) +: _type(Type::VECTOR) { + _field.vectorVal = new ValueVector(); + *_field.vectorVal = v; +} + +Value::Value(ValueVector &&v) +: _type(Type::VECTOR) { + _field.vectorVal = new ValueVector(); + *_field.vectorVal = std::move(v); +} + +Value::Value(const ValueMap &v) +: _type(Type::MAP) { + _field.mapVal = new ValueMap(); + *_field.mapVal = v; +} + +Value::Value(ValueMap &&v) +: _type(Type::MAP) { + _field.mapVal = new ValueMap(); + *_field.mapVal = std::move(v); +} + +Value::Value(const ValueMapIntKey &v) +: _type(Type::INT_KEY_MAP) { + _field.intKeyMapVal = new ValueMapIntKey(); + *_field.intKeyMapVal = v; +} + +Value::Value(ValueMapIntKey &&v) +: _type(Type::INT_KEY_MAP) { + _field.intKeyMapVal = new ValueMapIntKey(); + *_field.intKeyMapVal = std::move(v); +} + +Value::Value(const Value &other) //NOLINT(misc-no-recursion) +: _type(Type::NONE) { + *this = other; +} + +Value::Value(Value &&other) noexcept +: _type(Type::NONE) { + *this = std::move(other); +} + +Value::~Value() { + clear(); +} + +Value &Value::operator=(const Value &other) { //NOLINT(misc-no-recursion) + if (this != &other) { + reset(other._type); + + switch (other._type) { + case Type::BYTE: + _field.byteVal = other._field.byteVal; + break; + case Type::INTEGER: + _field.intVal = other._field.intVal; + break; + case Type::UNSIGNED: + _field.unsignedVal = other._field.unsignedVal; + break; + case Type::FLOAT: + _field.floatVal = other._field.floatVal; + break; + case Type::DOUBLE: + _field.doubleVal = other._field.doubleVal; + break; + case Type::BOOLEAN: + _field.boolVal = other._field.boolVal; + break; + case Type::STRING: + if (_field.strVal == nullptr) { + _field.strVal = new std::string(); + } + *_field.strVal = *other._field.strVal; + break; + case Type::VECTOR: + if (_field.vectorVal == nullptr) { + _field.vectorVal = new ValueVector(); + } + *_field.vectorVal = *other._field.vectorVal; + break; + case Type::MAP: + if (_field.mapVal == nullptr) { + _field.mapVal = new ValueMap(); + } + *_field.mapVal = *other._field.mapVal; + break; + case Type::INT_KEY_MAP: + if (_field.intKeyMapVal == nullptr) { + _field.intKeyMapVal = new ValueMapIntKey(); + } + *_field.intKeyMapVal = *other._field.intKeyMapVal; + break; + default: + break; + } + } + return *this; +} + +Value &Value::operator=(Value &&other) noexcept { + if (this != &other) { + clear(); + switch (other._type) { + case Type::BYTE: + _field.byteVal = other._field.byteVal; + break; + case Type::INTEGER: + _field.intVal = other._field.intVal; + break; + case Type::UNSIGNED: + _field.unsignedVal = other._field.unsignedVal; + break; + case Type::FLOAT: + _field.floatVal = other._field.floatVal; + break; + case Type::DOUBLE: + _field.doubleVal = other._field.doubleVal; + break; + case Type::BOOLEAN: + _field.boolVal = other._field.boolVal; + break; + case Type::STRING: + _field.strVal = other._field.strVal; + break; + case Type::VECTOR: + _field.vectorVal = other._field.vectorVal; + break; + case Type::MAP: + _field.mapVal = other._field.mapVal; + break; + case Type::INT_KEY_MAP: + _field.intKeyMapVal = other._field.intKeyMapVal; + break; + default: + break; + } + _type = other._type; + + memset(&other._field, 0, sizeof(other._field)); + other._type = Type::NONE; + } + + return *this; +} + +Value &Value::operator=(unsigned char v) { + reset(Type::BYTE); + _field.byteVal = v; + return *this; +} + +Value &Value::operator=(int v) { + reset(Type::INTEGER); + _field.intVal = v; + return *this; +} + +Value &Value::operator=(unsigned int v) { + reset(Type::UNSIGNED); + _field.unsignedVal = v; + return *this; +} + +Value &Value::operator=(float v) { + reset(Type::FLOAT); + _field.floatVal = v; + return *this; +} + +Value &Value::operator=(double v) { + reset(Type::DOUBLE); + _field.doubleVal = v; + return *this; +} + +Value &Value::operator=(bool v) { + reset(Type::BOOLEAN); + _field.boolVal = v; + return *this; +} + +Value &Value::operator=(const char *v) { + reset(Type::STRING); + *_field.strVal = v ? v : ""; + return *this; +} + +Value &Value::operator=(const std::string &v) { + reset(Type::STRING); + *_field.strVal = v; + return *this; +} + +Value &Value::operator=(const ValueVector &v) { + reset(Type::VECTOR); + *_field.vectorVal = v; + return *this; +} + +Value &Value::operator=(ValueVector &&v) { + reset(Type::VECTOR); + *_field.vectorVal = std::move(v); + return *this; +} + +Value &Value::operator=(const ValueMap &v) { + reset(Type::MAP); + *_field.mapVal = v; + return *this; +} + +Value &Value::operator=(ValueMap &&v) { + reset(Type::MAP); + *_field.mapVal = std::move(v); + return *this; +} + +Value &Value::operator=(const ValueMapIntKey &v) { + reset(Type::INT_KEY_MAP); + *_field.intKeyMapVal = v; + return *this; +} + +Value &Value::operator=(ValueMapIntKey &&v) { + reset(Type::INT_KEY_MAP); + *_field.intKeyMapVal = std::move(v); + return *this; +} + +bool Value::operator!=(const Value &v) { + return !(*this == v); +} +bool Value::operator!=(const Value &v) const { //NOLINT(misc-no-recursion) + return !(*this == v); +} + +bool Value::operator==(const Value &v) { + const auto &t = *this; + return t == v; +} +bool Value::operator==(const Value &v) const { //NOLINT(misc-no-recursion) + if (this == &v) { + return true; + } + + if (v._type != this->_type) { + return false; + } + + if (this->isNull()) { + return true; + } + + switch (_type) { + case Type::BYTE: return v._field.byteVal == this->_field.byteVal; + case Type::INTEGER: return v._field.intVal == this->_field.intVal; + case Type::UNSIGNED: return v._field.unsignedVal == this->_field.unsignedVal; + case Type::BOOLEAN: return v._field.boolVal == this->_field.boolVal; + case Type::STRING: return *v._field.strVal == *this->_field.strVal; + case Type::FLOAT: return std::abs(v._field.floatVal - this->_field.floatVal) <= FLT_EPSILON; + case Type::DOUBLE: return std::abs(v._field.doubleVal - this->_field.doubleVal) <= DBL_EPSILON; + case Type::VECTOR: { + const auto &v1 = *(this->_field.vectorVal); + const auto &v2 = *(v._field.vectorVal); + const auto size = v1.size(); + if (size == v2.size()) { + for (size_t i = 0; i < size; i++) { + if (v1[i] != v2[i]) { + return false; + } + } + return true; + } + return false; + } + case Type::MAP: { + const auto &map1 = *(this->_field.mapVal); + const auto &map2 = *(v._field.mapVal); + for (const auto &kvp : map1) { //NOLINT + auto it = map2.find(kvp.first); + if (it == map2.end() || it->second != kvp.second) { + return false; + } + } + return true; + } + case Type::INT_KEY_MAP: { + const auto &map1 = *(this->_field.intKeyMapVal); + const auto &map2 = *(v._field.intKeyMapVal); + for (const auto &kvp : map1) { //NOLINT + auto it = map2.find(kvp.first); + if (it == map2.end() || it->second != kvp.second) { + return false; + } + } + return true; + } + default: + break; + }; + + return false; +} + +/// Convert value to a specified type +unsigned char Value::asByte() const { + CC_ASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP); + + if (_type == Type::BYTE) { + return _field.byteVal; + } + + if (_type == Type::INTEGER) { + return static_cast(_field.intVal); + } + + if (_type == Type::UNSIGNED) { + return static_cast(_field.unsignedVal); + } + + if (_type == Type::STRING) { + return static_cast(atoi(_field.strVal->c_str())); + } + + if (_type == Type::FLOAT) { + return static_cast(_field.floatVal); + } + + if (_type == Type::DOUBLE) { + return static_cast(_field.doubleVal); + } + + if (_type == Type::BOOLEAN) { + return _field.boolVal ? 1 : 0; + } + + return 0; +} + +int Value::asInt() const { + CC_ASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP); + if (_type == Type::INTEGER) { + return _field.intVal; + } + + if (_type == Type::UNSIGNED) { + CC_ASSERT(_field.unsignedVal < INT_MAX); + return static_cast(_field.unsignedVal); + } + + if (_type == Type::BYTE) { + return _field.byteVal; + } + + if (_type == Type::STRING) { + return atoi(_field.strVal->c_str()); + } + + if (_type == Type::FLOAT) { + return static_cast(_field.floatVal); + } + + if (_type == Type::DOUBLE) { + return static_cast(_field.doubleVal); + } + + if (_type == Type::BOOLEAN) { + return _field.boolVal ? 1 : 0; + } + + return 0; +} + +unsigned int Value::asUnsignedInt() const { + CC_ASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP); + if (_type == Type::UNSIGNED) { + return _field.unsignedVal; + } + + if (_type == Type::INTEGER) { + CC_ASSERT(_field.intVal >= 0); + return static_cast(_field.intVal); + } + + if (_type == Type::BYTE) { + return static_cast(_field.byteVal); + } + + if (_type == Type::STRING) { + // NOTE: strtoul is required (need to augment on unsupported platforms) + return static_cast(strtoul(_field.strVal->c_str(), nullptr, 10)); + } + + if (_type == Type::FLOAT) { + return static_cast(_field.floatVal); + } + + if (_type == Type::DOUBLE) { + return static_cast(_field.doubleVal); + } + + if (_type == Type::BOOLEAN) { + return _field.boolVal ? 1U : 0U; + } + + return 0U; +} + +#define MAX_ITOA_BUFFER_SIZE 256 +double audioAtof(const char *str) { + if (str == nullptr) { + return 0.0; + } + + char buf[MAX_ITOA_BUFFER_SIZE]; + strncpy(buf, str, MAX_ITOA_BUFFER_SIZE); + + // strip string, only remain 7 numbers after '.' + char *dot = strchr(buf, '.'); + if (dot != nullptr && dot - buf + 8 < MAX_ITOA_BUFFER_SIZE) { + dot[8] = '\0'; + } + + return ::atof(buf); +} + +float Value::asFloat() const { + CC_ASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP); + if (_type == Type::FLOAT) { + return _field.floatVal; + } + + if (_type == Type::BYTE) { + return static_cast(_field.byteVal); + } + + if (_type == Type::STRING) { + return static_cast(audioAtof(_field.strVal->c_str())); + } + + if (_type == Type::INTEGER) { + return static_cast(_field.intVal); + } + + if (_type == Type::UNSIGNED) { + return static_cast(_field.unsignedVal); + } + + if (_type == Type::DOUBLE) { + return static_cast(_field.doubleVal); + } + + if (_type == Type::BOOLEAN) { + return _field.boolVal ? 1.0F : 0.0F; + } + + return 0.0F; +} + +double Value::asDouble() const { + CC_ASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP); + if (_type == Type::DOUBLE) { + return _field.doubleVal; + } + + if (_type == Type::BYTE) { + return static_cast(_field.byteVal); + } + + if (_type == Type::STRING) { + return static_cast(audioAtof(_field.strVal->c_str())); + } + + if (_type == Type::INTEGER) { + return static_cast(_field.intVal); + } + + if (_type == Type::UNSIGNED) { + return static_cast(_field.unsignedVal); + } + + if (_type == Type::FLOAT) { + return static_cast(_field.floatVal); + } + + if (_type == Type::BOOLEAN) { + return _field.boolVal ? 1.0 : 0.0; + } + + return 0.0; +} + +bool Value::asBool() const { + CC_ASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP); + if (_type == Type::BOOLEAN) { + return _field.boolVal; + } + + if (_type == Type::BYTE) { + return _field.byteVal != 0; + } + + if (_type == Type::STRING) { + return *_field.strVal != "0" || *_field.strVal != "false"; + } + + if (_type == Type::INTEGER) { + return _field.intVal != 0; + } + + if (_type == Type::UNSIGNED) { + return _field.unsignedVal != 0; + } + + if (_type == Type::FLOAT) { + return _field.floatVal != 0.0F; + } + + if (_type == Type::DOUBLE) { + return _field.doubleVal != 0.0; + } + + return false; +} + +std::string Value::asString() const { + CC_ASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP); + + if (_type == Type::STRING) { + return *_field.strVal; + } + + std::stringstream ret; + + switch (_type) { + case Type::BYTE: + ret << _field.byteVal; + break; + case Type::INTEGER: + ret << _field.intVal; + break; + case Type::UNSIGNED: + ret << _field.unsignedVal; + break; + case Type::FLOAT: + ret << std::fixed << std::setprecision(7) << _field.floatVal; + break; + case Type::DOUBLE: + ret << std::fixed << std::setprecision(16) << _field.doubleVal; + break; + case Type::BOOLEAN: + ret << (_field.boolVal ? "true" : "false"); + break; + default: + break; + } + return ret.str(); +} + +ValueVector &Value::asValueVector() { + CC_ASSERT(_type == Type::VECTOR); + return *_field.vectorVal; +} + +const ValueVector &Value::asValueVector() const { + CC_ASSERT(_type == Type::VECTOR); + return *_field.vectorVal; +} + +ValueMap &Value::asValueMap() { + CC_ASSERT(_type == Type::MAP); + return *_field.mapVal; +} + +const ValueMap &Value::asValueMap() const { + CC_ASSERT(_type == Type::MAP); + return *_field.mapVal; +} + +ValueMapIntKey &Value::asIntKeyMap() { + CC_ASSERT(_type == Type::INT_KEY_MAP); + return *_field.intKeyMapVal; +} + +const ValueMapIntKey &Value::asIntKeyMap() const { + CC_ASSERT(_type == Type::INT_KEY_MAP); + return *_field.intKeyMapVal; +} + +static std::string getTabs(int depth) { + std::string tabWidth; + + for (int i = 0; i < depth; ++i) { + tabWidth += "\t"; + } + + return tabWidth; +} + +static std::string visit(const Value &v, int depth); + +static std::string visitVector(const ValueVector &v, int depth) { //NOLINT[misc-no-recursion] + std::stringstream ret; + + if (depth > 0) { + ret << "\n"; + } + + ret << getTabs(depth) << "[\n"; + + int i = 0; + for (const auto &child : v) { + ret << getTabs(depth + 1) << i << ": " << visit(child, depth + 1); + ++i; + } + + ret << getTabs(depth) << "]\n"; + + return ret.str(); +} + +template +static std::string visitMap(const T &v, int depth) { //NOLINT[misc-no-recursion] + std::stringstream ret; + + if (depth > 0) { + ret << "\n"; + } + + ret << getTabs(depth) << "{\n"; + + for (auto iter = v.begin(); iter != v.end(); ++iter) { + ret << getTabs(depth + 1) << iter->first << ": "; + ret << visit(iter->second, depth + 1); + } + + ret << getTabs(depth) << "}\n"; + + return ret.str(); +} + +static std::string visit(const Value &v, int depth) { //NOLINT[misc-no-recursion] + std::stringstream ret; + + switch (v.getType()) { + case Value::Type::NONE: + case Value::Type::BYTE: + case Value::Type::INTEGER: + case Value::Type::UNSIGNED: + case Value::Type::FLOAT: + case Value::Type::DOUBLE: + case Value::Type::BOOLEAN: + case Value::Type::STRING: + ret << v.asString() << "\n"; + break; + case Value::Type::VECTOR: + ret << visitVector(v.asValueVector(), depth); + break; + case Value::Type::MAP: + ret << visitMap(v.asValueMap(), depth); + break; + case Value::Type::INT_KEY_MAP: + ret << visitMap(v.asIntKeyMap(), depth); + break; + default: + CC_ASSERT(false); + break; + } + + return ret.str(); +} + +std::string Value::getDescription() const { + std::string ret("\n"); + ret += visit(*this, 0); + return ret; +} + +void Value::clear() { + // Free memory the old value allocated + switch (_type) { + case Type::BYTE: + _field.byteVal = 0; + break; + case Type::INTEGER: + _field.intVal = 0; + break; + case Type::UNSIGNED: + _field.unsignedVal = 0U; + break; + case Type::FLOAT: + _field.floatVal = 0.0F; + break; + case Type::DOUBLE: + _field.doubleVal = 0.0; + break; + case Type::BOOLEAN: + _field.boolVal = false; + break; + case Type::STRING: + CC_AUDIO_SAFE_DELETE(_field.strVal); + break; + case Type::VECTOR: + CC_AUDIO_SAFE_DELETE(_field.vectorVal); + break; + case Type::MAP: + CC_AUDIO_SAFE_DELETE(_field.mapVal); + break; + case Type::INT_KEY_MAP: + CC_AUDIO_SAFE_DELETE(_field.intKeyMapVal); + break; + default: + break; + } + + _type = Type::NONE; +} + +void Value::reset(Type type) { + if (_type == type) { + return; + } + + clear(); + + // Allocate memory for the new value + switch (type) { + case Type::STRING: + _field.strVal = new std::string(); + break; + case Type::VECTOR: + _field.vectorVal = new ValueVector(); + break; + case Type::MAP: + _field.mapVal = new ValueMap(); + break; + case Type::INT_KEY_MAP: + _field.intKeyMapVal = new ValueMapIntKey(); + break; + default: + break; + } + + _type = type; +} + +}} // namespace CocosDenshion diff --git a/cocos/audio/ohos/audio_utils/include/audio_utils/minifloat.h b/cocos/audio/ohos/audio_utils/include/audio_utils/minifloat.h new file mode 100644 index 000000000000..63d8b3831fc5 --- /dev/null +++ b/cocos/audio/ohos/audio_utils/include/audio_utils/minifloat.h @@ -0,0 +1,88 @@ +/* + * 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 + + + +/* 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 + +#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) + +/* 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 v); + +/* Convert the internal representation used for gains to float */ +float float_from_gain(gain_minifloat_t a); + + +#endif // COCOS_AUDIO_MINIFLOAT_H diff --git a/cocos/audio/ohos/audio_utils/include/audio_utils/primitives.h b/cocos/audio/ohos/audio_utils/include/audio_utils/primitives.h new file mode 100644 index 000000000000..ea868ceece83 --- /dev/null +++ b/cocos/audio/ohos/audio_utils/include/audio_utils/primitives.h @@ -0,0 +1,936 @@ +/* + * 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. + */ + +#pragma once + + +#include +#include +#include + +extern "C" { +typedef struct __attribute__((__packed__)) { + uint8_t c[3]; +} uint8x3_t; +} + + + +/* 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 INT32_MIN; /* or 0x80000000 */ + } else if (f >= limpos) { + return INT32_MAX; + } + 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 INT32_MIN; /* or 0x80000000 */ + } else if (f >= limpos) { + return INT32_MAX; + } + 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 = 16.0f; + + if (f <= 0.) { + return 0; + } else if (f >= limpos) { + // return 0xffffffff; + return UINT32_MAX; + } + /* 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; + return UINT16_MAX; + } + /* 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); + } + 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); + } + return (int16_t)(inRL >> 16) * (int16_t)(vRL >> 16); +#endif +} diff --git a/cocos/audio/ohos/audio_utils/minifloat.cpp b/cocos/audio/ohos/audio_utils/minifloat.cpp new file mode 100644 index 000000000000..7b2b40971d5d --- /dev/null +++ b/cocos/audio/ohos/audio_utils/minifloat.cpp @@ -0,0 +1,47 @@ +#include +#include "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 + + + +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); +} + + diff --git a/cocos/audio/ohos/audio_utils/primitives.cpp b/cocos/audio/ohos/audio_utils/primitives.cpp new file mode 100644 index 000000000000..9c02f3247be7 --- /dev/null +++ b/cocos/audio/ohos/audio_utils/primitives.cpp @@ -0,0 +1,477 @@ +#include "include/audio_utils/primitives.h" +#include "../utils/Utils.h" +using namespace cocos2d::experimental::utils; + +void ditherAndClamp(int32_t *out, const int32_t *sums, size_t c) { + size_t i; + for (i = 0; i < c; i++) { + int32_t l = *sums++; + int32_t r = *sums++; + int32_t nl = l >> 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 = static_cast(*--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++ = static_cast(*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++ = static_cast(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++ = static_cast(*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++ = static_cast((static_cast(src[0]) + static_cast(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 dstMask, + const void *src, uint32_t srcMask, size_t sampleSize, 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 (dstMask == srcMask) { + memcpy(dst, src, sampleSize * popcount(dstMask) * count); + return; + } + switch (sampleSize) { + case 1: { + auto *udst = static_cast(dst); + const auto *usrc = static_cast(src); + + COPY_FRAME_BY_MASK(udst, dstMask, usrc, srcMask, count, 0); + } break; + case 2: { + auto *udst = static_cast(dst); + const auto *usrc = static_cast(src); + + COPY_FRAME_BY_MASK(udst, dstMask, usrc, srcMask, count, 0); + } break; + case 3: { /* could be slow. use a struct to represent 3 bytes of data. */ + auto *udst = static_cast(dst); + const auto *usrc = static_cast(src); + static const uint8x3_t ZERO{0,0,0}; /* tricky - we use this to zero out a sample */ + + COPY_FRAME_BY_MASK(udst, dstMask, usrc, srcMask, count, ZERO); + } break; + case 4: { + auto *udst = static_cast(dst); + const auto *usrc = static_cast(src); + + COPY_FRAME_BY_MASK(udst, dstMask, usrc, srcMask, 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 dstChannels, + const void *src, uint32_t srcChannels, + const int8_t *idxary, size_t sampleSize, size_t count) { + switch (sampleSize) { + case 1: { + auto *udst = static_cast(dst); + const auto *usrc = static_cast(src); + + COPY_FRAME_BY_IDX(udst, dstChannels, usrc, srcChannels, idxary, count, 0); // NOLINT + } break; + case 2: { + auto *udst = static_cast(dst); + const auto *usrc = static_cast(src); + + COPY_FRAME_BY_IDX(udst, dstChannels, usrc, srcChannels, idxary, count, 0); // NOLINT + } break; + case 3: { /* could be slow. use a struct to represent 3 bytes of data. */ + auto *udst = static_cast(dst); + const auto *usrc = static_cast(src); + static const uint8x3_t ZERO{0,0,0}; + + COPY_FRAME_BY_IDX(udst, dstChannels, usrc, srcChannels, idxary, count, ZERO); + } break; + case 4: { + auto *udst = static_cast(dst); + const auto *usrc = static_cast(src); + + COPY_FRAME_BY_IDX(udst, dstChannels, usrc, srcChannels, idxary, count, 0); + } break; + default: + abort(); /* illegal value */ + break; + } +} + +size_t memcpy_by_index_array_initialization(int8_t *idxary, size_t idxcount, + uint32_t dstMask, uint32_t srcMask) { + size_t n = 0; + int srcidx = 0; + uint32_t bit; + uint32_t ormask = srcMask | dstMask; + + while (ormask && n < idxcount) { + bit = ormask & -ormask; /* get lowest bit */ + ormask ^= bit; /* remove lowest bit */ + if (srcMask & dstMask & bit) { /* matching channel */ + idxary[n++] = srcidx++; + } else if (srcMask & bit) { /* source channel only */ + ++srcidx; + } else { /* destination channel only */ + idxary[n++] = -1; + } + } + return n + popcount(ormask & dstMask); +} + +size_t memcpy_by_index_array_initialization_src_index(int8_t *idxary, size_t idxcount, + uint32_t dstMask, uint32_t srcMask) { + size_t dstCount = popcount(dstMask); + if (idxcount == 0) { + return dstCount; + } + if (dstCount > idxcount) { + dstCount = idxcount; + } + + size_t srcIdx; + size_t dstIdx; + for (srcIdx = 0, dstIdx = 0; dstIdx < dstCount; ++dstIdx) { + if (srcMask & 1) { + idxary[dstIdx] = srcIdx++; + } else { + idxary[dstIdx] = -1; + } + srcMask >>= 1; + } + return dstIdx; +} + +size_t memcpy_by_index_array_initialization_dst_index(int8_t *idxary, size_t idxcount, + uint32_t dstMask, uint32_t srcMask) { + size_t srcIdx; + size_t dstIdx; + size_t dstCount = popcount(dstMask); + size_t srcCount = popcount(srcMask); + if (idxcount == 0) { + return dstCount; + } + if (dstCount > idxcount) { + dstCount = idxcount; + } + for (srcIdx = 0, dstIdx = 0; dstIdx < dstCount; ++srcIdx) { + if (dstMask & 1) { + idxary[dstIdx++] = srcIdx < srcCount ? static_cast(srcIdx) : -1; + } + dstMask >>= 1; + } + return dstIdx; +} diff --git a/cocos/audio/ohos/cutils/bitops.h b/cocos/audio/ohos/cutils/bitops.h new file mode 100644 index 000000000000..d32ec53b794c --- /dev/null +++ b/cocos/audio/ohos/cutils/bitops.h @@ -0,0 +1,30 @@ +#ifndef COCOS_CUTILS_BITOPS_H +#define COCOS_CUTILS_BITOPS_H + +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + +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); +} + +#ifdef __cplusplus +} +#endif + +#endif /* COCOS_CUTILS_BITOPS_H */ diff --git a/cocos/audio/ohos/cutils/log.h b/cocos/audio/ohos/cutils/log.h new file mode 100644 index 000000000000..edc29618de31 --- /dev/null +++ b/cocos/audio/ohos/cutils/log.h @@ -0,0 +1,567 @@ +// +// C/C++ logging functions. See the logging documentation for API details. +// +// We'd like these to be available from C code (in case we import some from +// somewhere), so this has a C interface. +// +// The output will be correct when the log file is shared between multiple +// 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 + + +#include +#include +#include +#include +#include +#include +#define APP_LOG_DOMAIN 0x0001 +#define APP_LOG_TAG "cocos" + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------- +/* + * Normally we strip ALOGV (VERBOSE messages) from release builds. + * You can modify this (for example with "#define LOG_NDEBUG 0" + * at the top of your source file) to change that behavior. + */ +#define CC_DEBUG 0 +#ifndef LOG_NDEBUG + #if defined(CC_DEBUG) && CC_DEBUG > 0 + #define LOG_NDEBUG 0 + #else + #define LOG_NDEBUG 1 + #endif +#endif + +/* + * This is the local tag used for the following simplified + * logging macros. You can change this preprocessor definition + * before using the other macros to change the tag. + */ +#ifndef LOG_TAG + #define LOG_TAG NULL +#endif + +// --------------------------------------------------------------------- + +#ifndef __predict_false + #define __predict_false(exp) __builtin_expect((exp) != 0, 0) +#endif + +/* + * -DLINT_RLOG in sources that you want to enforce that all logging + * goes to the radio log buffer. If any logging goes to any of the other + * log buffers, there will be a compile or link error to highlight the + * problem. This is not a replacement for a full audit of the code since + * this only catches compiled code, not ifdef'd debug code. Options to + * defining this, either temporarily to do a spot check, or permanently + * to enforce, in all the communications trees; We have hopes to ensure + * that by supplying just the radio log buffer that the communications + * teams will have their one-stop shop for triaging issues. + */ +#ifndef LINT_RLOG + + /* + * Simplified macro to send a verbose log message using the current LOG_TAG. + */ + #ifndef ALOGV + #define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) + #if LOG_NDEBUG + #define ALOGV(...) \ + do { \ + if (0) { \ + __ALOGV(__VA_ARGS__); \ + } \ + } while (0) + #else + #define ALOGV(...) __ALOGV(__VA_ARGS__) + #endif + #endif + + #ifndef ALOGV_IF + #if LOG_NDEBUG + #define ALOGV_IF(cond, ...) ((void)0) + #else + #define ALOGV_IF(cond, ...) \ + ((__predict_false(cond)) \ + ? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ + : (void)0) + #endif + #endif + + /* + * Simplified macro to send a debug log message using the current LOG_TAG. + */ + #ifndef ALOGD + #define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) + #endif + + #ifndef ALOGD_IF + #define ALOGD_IF(cond, ...) \ + ((__predict_false(cond)) \ + ? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ + : (void)0) + #endif + + /* + * Simplified macro to send an info log message using the current LOG_TAG. + */ + #ifndef ALOGI + #define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) + #endif + + #ifndef ALOGI_IF + #define ALOGI_IF(cond, ...) \ + ((__predict_false(cond)) \ + ? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \ + : (void)0) + #endif + + /* + * Simplified macro to send a warning log message using the current LOG_TAG. + */ + #ifndef ALOGW + #define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) + #endif + + #ifndef ALOGW_IF + #define ALOGW_IF(cond, ...) \ + ((__predict_false(cond)) \ + ? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \ + : (void)0) + #endif + + /* + * Simplified macro to send an error log message using the current LOG_TAG. + */ + #ifndef ALOGE + #define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) + #endif + + #ifndef ALOGE_IF + #define ALOGE_IF(cond, ...) \ + ((__predict_false(cond)) \ + ? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ + : (void)0) + #endif + + // --------------------------------------------------------------------- + + /* + * Conditional based on whether the current LOG_TAG is enabled at + * verbose priority. + */ + #ifndef IF_ALOGV + #if LOG_NDEBUG + #define IF_ALOGV() if (false) + #else + #define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG) + #endif + #endif + + /* + * Conditional based on whether the current LOG_TAG is enabled at + * debug priority. + */ + #ifndef IF_ALOGD + #define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG) + #endif + + /* + * Conditional based on whether the current LOG_TAG is enabled at + * info priority. + */ + #ifndef IF_ALOGI + #define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG) + #endif + + /* + * Conditional based on whether the current LOG_TAG is enabled at + * warn priority. + */ + #ifndef IF_ALOGW + #define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG) + #endif + + /* + * Conditional based on whether the current LOG_TAG is enabled at + * error priority. + */ + #ifndef IF_ALOGE + #define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG) + #endif + + // --------------------------------------------------------------------- + + /* + * Simplified macro to send a verbose system log message using the current LOG_TAG. + */ + #ifndef SLOGV + #define __SLOGV(...) \ + ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) + #if LOG_NDEBUG + #define SLOGV(...) \ + do { \ + if (0) { \ + __SLOGV(__VA_ARGS__); \ + } \ + } while (0) + #else + #define SLOGV(...) __SLOGV(__VA_ARGS__) + #endif + #endif + + #ifndef SLOGV_IF + #if LOG_NDEBUG + #define SLOGV_IF(cond, ...) ((void)0) + #else + #define SLOGV_IF(cond, ...) \ + ((__predict_false(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ + : (void)0) + #endif + #endif + + /* + * Simplified macro to send a debug system log message using the current LOG_TAG. + */ + #ifndef SLOGD + #define SLOGD(...) \ + ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) + #endif + + #ifndef SLOGD_IF + #define SLOGD_IF(cond, ...) \ + ((__predict_false(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ + : (void)0) + #endif + + /* + * Simplified macro to send an info system log message using the current LOG_TAG. + */ + #ifndef SLOGI + #define SLOGI(...) \ + ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) + #endif + + #ifndef SLOGI_IF + #define SLOGI_IF(cond, ...) \ + ((__predict_false(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \ + : (void)0) + #endif + + /* + * Simplified macro to send a warning system log message using the current LOG_TAG. + */ + #ifndef SLOGW + #define SLOGW(...) \ + ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) + #endif + + #ifndef SLOGW_IF + #define SLOGW_IF(cond, ...) \ + ((__predict_false(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \ + : (void)0) + #endif + + /* + * Simplified macro to send an error system log message using the current LOG_TAG. + */ + #ifndef SLOGE + #define SLOGE(...) \ + ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) + #endif + + #ifndef SLOGE_IF + #define SLOGE_IF(cond, ...) \ + ((__predict_false(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ + : (void)0) + #endif + +#endif /* !LINT_RLOG */ + +// --------------------------------------------------------------------- + +/* + * Simplified macro to send a verbose radio log message using the current LOG_TAG. + */ +#ifndef RLOGV + #define __RLOGV(...) \ + ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) + #if LOG_NDEBUG + #define RLOGV(...) \ + do { \ + if (0) { \ + __RLOGV(__VA_ARGS__); \ + } \ + } while (0) + #else + #define RLOGV(...) __RLOGV(__VA_ARGS__) + #endif +#endif + +#ifndef RLOGV_IF + #if LOG_NDEBUG + #define RLOGV_IF(cond, ...) ((void)0) + #else + #define RLOGV_IF(cond, ...) \ + ((__predict_false(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ + : (void)0) + #endif +#endif + +/* + * Simplified macro to send a debug radio log message using the current LOG_TAG. + */ +#ifndef RLOGD + #define RLOGD(...) \ + ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGD_IF + #define RLOGD_IF(cond, ...) \ + ((__predict_false(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ + : (void)0) +#endif + +/* + * Simplified macro to send an info radio log message using the current LOG_TAG. + */ +#ifndef RLOGI + #define RLOGI(...) \ + ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGI_IF + #define RLOGI_IF(cond, ...) \ + ((__predict_false(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \ + : (void)0) +#endif + +/* + * Simplified macro to send a warning radio log message using the current LOG_TAG. + */ +#ifndef RLOGW + #define RLOGW(...) \ + ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGW_IF + #define RLOGW_IF(cond, ...) \ + ((__predict_false(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \ + : (void)0) +#endif + +/* + * Simplified macro to send an error radio log message using the current LOG_TAG. + */ +#ifndef RLOGE + #define RLOGE(...) \ + ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGE_IF + #define RLOGE_IF(cond, ...) \ + ((__predict_false(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ + : (void)0) +#endif + +// --------------------------------------------------------------------- + +/* + * Log a fatal error. If the given condition fails, this stops program + * execution like a normal assertion, but also generating the given message. + * It is NOT stripped from release builds. Note that the condition test + * is -inverted- from the normal assert() semantics. + */ +#ifndef LOG_ALWAYS_FATAL_IF + +#define LOG_ALWAYS_FATAL_IF(cond, ...) + +#endif + +#ifndef LOG_ALWAYS_FATAL + +#define LOG_ALWAYS_FATAL(...) + +#endif + +/* + * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that + * are stripped out of release builds. + */ +#if LOG_NDEBUG + + #ifndef LOG_FATAL_IF + #define LOG_FATAL_IF(cond, ...) ((void)0) + #endif + #ifndef LOG_FATAL + #define LOG_FATAL(...) ((void)0) + #endif + +#else + + #ifndef LOG_FATAL_IF + #define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ##__VA_ARGS__) + #endif + #ifndef LOG_FATAL + #define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__) + #endif + +#endif + +/* + * Assertion that generates a log message when the assertion fails. + * Stripped out of release builds. Uses the current LOG_TAG. + */ +#ifndef ALOG_ASSERT + #define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__) +//#define ALOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond) +#endif + +// --------------------------------------------------------------------- + +/* + * Basic log message macro. + * + * Example: + * ALOG(LOG_WARN, NULL, "Failed with error %d", errno); + * + * The second argument may be NULL or "" to indicate the "global" tag. + */ +#ifndef ALOG + #define ALOG(priority, tag, ...) \ + ((void)OH_LOG_Print(LOG_APP, LOG_INFO, APP_LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) +#endif + +/* + * Log macro that allows you to specify a number for the priority. + */ +#ifndef LOG_PRI + #define LOG_PRI(priority, tag, ...) ((void)0) +#endif + +/* + * Log macro that allows you to pass in a varargs ("args" is a va_list). + */ +#ifndef LOG_PRI_VA + #define LOG_PRI_VA(priority, tag, fmt, args) \ + android_vprintLog(priority, NULL, tag, fmt, args) +#endif + +/* + * Conditional given a desired logging priority and tag. + */ +#ifndef IF_ALOG + #define IF_ALOG(priority, tag) \ + if (android_testLog(ANDROID_##priority, tag)) +#endif + +// --------------------------------------------------------------------- + +/* + * =========================================================================== + * + * The stuff in the rest of this file should not be used directly. + */ + +#define android_printLog(prio, tag, ...) \ + __android_log_print(prio, tag, __VA_ARGS__) + +#define android_vprintLog(prio, cond, tag, ...) \ + __android_log_vprint(prio, tag, __VA_ARGS__) + +/* XXX Macros to work around syntax errors in places where format string + * arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF + * (happens only in debug builds). + */ + +/* Returns 2nd arg. Used to substitute default value if caller's vararg list + * is empty. + */ +#define __android_second(dummy, second, ...) second + +/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise + * returns nothing. + */ +#define __android_rest(first, ...) , ##__VA_ARGS__ + +#define android_printAssert(cond, tag, ...) \ + __android_log_assert(cond, tag, \ + __android_second(0, ##__VA_ARGS__, NULL) __android_rest(__VA_ARGS__)) + +#define android_writeLog(prio, tag, text) \ + __android_log_write(prio, tag, text) + +#define android_bWriteLog(tag, payload, len) \ + __android_log_bwrite(tag, payload, len) +#define android_btWriteLog(tag, type, payload, len) \ + __android_log_btwrite(tag, type, payload, len) + +#define android_errorWriteLog(tag, subTag) \ + __android_log_error_write(tag, subTag, -1, NULL, 0) + +#define android_errorWriteWithInfoLog(tag, subTag, uid, data, dataLen) \ + __android_log_error_write(tag, subTag, uid, data, dataLen) + +/* + * IF_ALOG uses android_testLog, but IF_ALOG can be overridden. + * android_testLog will remain constant in its purpose as a wrapper + * for Android logging filter policy, and can be subject to + * change. It can be reused by the developers that override + * IF_ALOG as a convenient means to reimplement their policy + * over Android. + */ +#if LOG_NDEBUG /* Production */ + #define android_testLog(prio, tag) \ + (__android_log_is_loggable(prio, tag, ANDROID_LOG_DEBUG) != 0) +#else + #define android_testLog(prio, tag) \ + (__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE) != 0) +#endif + +/* + * Use the per-tag properties "log.tag." to generate a runtime + * result of non-zero to expose a log. prio is ANDROID_LOG_VERBOSE to + * ANDROID_LOG_FATAL. default_prio if no property. Undefined behavior if + * any other value. + */ +int __android_log_is_loggable(int prio, const char *tag, int default_prio); + +int __android_log_security(); /* Device Owner is present */ + +int __android_log_error_write(int tag, const char *subTag, int32_t uid, const char *data, + uint32_t dataLen); + +/* + * Send a simple string to the log. + */ +int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text); +int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...) +#if defined(__GNUC__) + __attribute__((__format__(printf, 4, 5))) +#endif + ; + +#ifdef __cplusplus +} +#endif + +#endif /* COCOS_CUTILS_LOG_H */ diff --git a/cocos/audio/ohos/mp3reader.cpp b/cocos/audio/ohos/mp3reader.cpp new file mode 100644 index 000000000000..be4c7c9fefea --- /dev/null +++ b/cocos/audio/ohos/mp3reader.cpp @@ -0,0 +1,497 @@ +#define LOG_TAG "mp3reader" + +#include +#include +#include +#include // Resolves that memset, memcpy aren't found while APP_PLATFORM >= 22 on Android +#include +#include "cutils/log.h" + +#include "mp3reader.h" +#include "pvmp3decoder_api.h" + +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/ohos/mp3reader.h b/cocos/audio/ohos/mp3reader.h new file mode 100644 index 000000000000..9efd317e26eb --- /dev/null +++ b/cocos/audio/ohos/mp3reader.h @@ -0,0 +1,33 @@ +#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/ohos/tinysndfile.cpp b/cocos/audio/ohos/tinysndfile.cpp new file mode 100644 index 000000000000..6abe11edcd9d --- /dev/null +++ b/cocos/audio/ohos/tinysndfile.cpp @@ -0,0 +1,502 @@ +#define LOG_TAG "tinysndfile" + +#include "tinysndfile.h" +#include "audio_utils/include/audio_utils/primitives.h" + +#include "cutils/log.h" + +#include +#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 + +namespace sf { + +static snd_callbacks sDefaultCallback; + +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() { + static const uint16_t ONE = 1; + return *(reinterpret_cast(&ONE)) == 1; +} + +// "swab" conflicts with OS X +static void my_swab(int16_t *ptr, size_t numToSwap) { //NOLINT(readability-identifier-naming) + while (numToSwap > 0) { + *ptr = static_cast(little2u(reinterpret_cast(ptr))); + --numToSwap; + ++ptr; + } +} + +static void *open_func(const char *path, void * /*user*/) { //NOLINT(readability-identifier-naming) + return fopen(path, "rb"); +} + +static size_t read_func(void *ptr, size_t size, size_t nmemb, void *datasource) { //NOLINT(readability-identifier-naming) + return fread(ptr, size, nmemb, static_cast(datasource)); +} + +static int seek_func(void *datasource, long offset, int whence) { //NOLINT(google-runtime-int,readability-identifier-naming) + return fseek(static_cast(datasource), offset, whence); +} + +static int close_func(void *datasource) { //NOLINT(readability-identifier-naming) + return fclose(static_cast(datasource)); +} + +static long tell_func(void *datasource) { //NOLINT(google-runtime-int,readability-identifier-naming) + return ftell(static_cast(datasource)); +} + +static void sf_lazy_init() { //NOLINT(readability-identifier-naming) + if (sInited == 0) { + sDefaultCallback.open = open_func; + sDefaultCallback.read = read_func; + sDefaultCallback.seek = seek_func; + sDefaultCallback.close = close_func; + sDefaultCallback.tell = tell_func; + sInited = 1; + } +} + +SNDFILE *sf_open_read(const char *path, SF_INFO *info, snd_callbacks *cb, void *user) { //NOLINT(readability-identifier-naming) + sf_lazy_init(); + + if (path == nullptr || info == nullptr) { +#ifdef HAVE_STDERR + ALOGE("path=%p info=%p\n", path, info); +#endif + return nullptr; + } + + auto *handle = static_cast(malloc(sizeof(SNDFILE))); + handle->temp = nullptr; + + handle->info.format = SF_FORMAT_WAV; + if (cb != nullptr) { + handle->callback = *cb; + } else { + handle->callback = sDefaultCallback; + } + + void *stream = handle->callback.open(path, user); + if (stream == nullptr) { +#ifdef HAVE_STDERR + ALOGE("fopen %s failed errno %d\n", path, errno); +#endif + free(handle); + return nullptr; + } + 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; //NOLINT(google-runtime-int) + + 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)) { //NOLINT(bugprone-suspicious-string-compare) +#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)) { //NOLINT(bugprone-suspicious-string-compare) +#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, static_cast(chunkSize - minSize), SEEK_CUR); //NOLINT(google-runtime-int) + } + unsigned channels = little2u(&fmt[2]); + // IDEA: 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 = static_cast(samplerate); + handle->info.channels = static_cast(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, static_cast(chunkSize), SEEK_CUR); //NOLINT(google-runtime-int) + } + hadData = 1; + } else if (!memcmp(&chunk[0], "fact", 4)) { + // ignore fact + if (chunkSize > 0) { + handle->callback.seek(stream, static_cast(chunkSize), SEEK_CUR); //NOLINT(google-runtime-int) + } + } 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, static_cast(chunkSize), SEEK_CUR); //NOLINT(google-runtime-int) + } + } + 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 nullptr; +} + +void sf_close(SNDFILE *handle) { //NOLINT(readability-identifier-naming) + if (handle == nullptr) { + return; + } + free(handle->temp); + (void)handle->callback.close(handle->stream); + free(handle); +} + +off_t sf_seek(SNDFILE *handle, int offset, int whence) { //NOLINT(readability-identifier-naming) + if (whence == SEEK_SET) { + assert(offset >= 0 && offset <= handle->info.frames); + } else if (whence == SEEK_CUR) { + offset += sf_tell(handle); + assert(offset >= 0 && offset <= handle->info.frames); + } else if (whence == SEEK_END) { + offset += handle->info.frames; + assert(offset >= 0 && offset <= handle->info.frames); + } else { + assert(false); // base whence value + } + handle->remaining = handle->info.frames - offset; + return offset; +} + +off_t sf_tell(SNDFILE *handle) { //NOLINT(readability-identifier-naming) + return handle->info.frames - handle->remaining; +} + +sf_count_t sf_readf_short(SNDFILE *handle, int16_t *ptr, sf_count_t desiredFrames) { //NOLINT(readability-identifier-naming) + if (handle == nullptr || ptr == nullptr || !handle->remaining || + desiredFrames <= 0) { + return 0; + } + if (handle->remaining < static_cast(desiredFrames)) { + desiredFrames = handle->remaining; + } + // does not check for numeric overflow + size_t desiredBytes = desiredFrames * handle->bytesPerFrame; + size_t actualBytes; + void *temp = nullptr; + 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, reinterpret_cast(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, static_cast(temp), actualFrames * handle->info.channels); + free(temp); + break; + case SF_FORMAT_FLOAT: + memcpy_to_i16_from_float(ptr, static_cast(temp), actualFrames * handle->info.channels); + free(temp); + break; + case SF_FORMAT_PCM_24: + memcpy_to_i16_from_p24(ptr, static_cast(temp), actualFrames * handle->info.channels); + free(temp); + break; + default: + memset(ptr, 0, actualFrames * handle->info.channels * sizeof(int16_t)); + break; + } + return actualFrames; +} + +/* +sf_count_t sf_readf_float(SNDFILE *handle, float *ptr, sf_count_t desiredFrames) +{ + if (handle == nullptr || ptr == nullptr || !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 = nullptr; + 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 + // REFINE: - 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 int16_t *) 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 == nullptr || ptr == nullptr || !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 = nullptr; + 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 + // REFINE: - 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 int16_t *) 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; +} + */ +} // namespace sf diff --git a/cocos/audio/ohos/tinysndfile.h b/cocos/audio/ohos/tinysndfile.h new file mode 100644 index 000000000000..13079e5f2365 --- /dev/null +++ b/cocos/audio/ohos/tinysndfile.h @@ -0,0 +1,72 @@ +#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 + +namespace sf { + +// visible to clients +using sf_count_t = int; + +using SF_INFO = struct { + sf_count_t frames; + int samplerate; + int channels; + int format; +}; + +// opaque to clients +using SNDFILE = struct 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 + +using snd_callbacks = 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); //NOLINT(google-runtime-int) + int (*close)(void *datasource); + long (*tell)(void *datasource); //NOLINT(google-runtime-int) +}; + +// Open stream +SNDFILE *sf_open_read(const char *path, SF_INFO *info, snd_callbacks *cb, void *user); //NOLINT(readability-identifier-naming) + +// Close stream +void sf_close(SNDFILE *handle); //NOLINT(readability-identifier-naming) + +// Read interleaved frames and return actual number of frames read +sf_count_t sf_readf_short(SNDFILE *handle, int16_t *ptr, sf_count_t desired); + +//NOLINT(readability-identifier-naming) +/* +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); +*/ + +off_t sf_seek(SNDFILE *handle, int offset, int whence); //NOLINT(readability-identifier-naming) +off_t sf_tell(SNDFILE *handle); //NOLINT(readability-identifier-naming) +static int sInited = 0; +static void sf_lazy_init(); //NOLINT(readability-identifier-naming) +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; +}; +} // namespace sf diff --git a/cocos/audio/ohos/utils/Compat.h b/cocos/audio/ohos/utils/Compat.h new file mode 100644 index 000000000000..c9650c133828 --- /dev/null +++ b/cocos/audio/ohos/utils/Compat.h @@ -0,0 +1,49 @@ +#ifndef COCOS_LIB_UTILS_COMPAT_H +#define COCOS_LIB_UTILS_COMPAT_H + +#include +#include + +#define ZD "%zd" +#define ZD_TYPE ssize_t + + +/* + * 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 + + +typedef SLOHBufferQueueItf CCSLBufferQueueItf; +#define CC_SL_IDD_BUFFER_QUEUE SL_IID_OH_BUFFERQUEUE +#define __unused + + +#endif /* COCOS_LIB_UTILS_COMPAT_H */ diff --git a/cocos/audio/ohos/utils/Errors.h b/cocos/audio/ohos/utils/Errors.h new file mode 100644 index 000000000000..dcbbb931a652 --- /dev/null +++ b/cocos/audio/ohos/utils/Errors.h @@ -0,0 +1,72 @@ +#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 CocosDenshion + +// --------------------------------------------------------------------------- + +#endif // COCOS_ERRORS_H diff --git a/cocos/audio/ohos/utils/Utils.cpp b/cocos/audio/ohos/utils/Utils.cpp new file mode 100644 index 000000000000..b0ff6e564c97 --- /dev/null +++ b/cocos/audio/ohos/utils/Utils.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** + Copyright (c) 2010 cocos2d-x.org + Copyright (c) 2013-2016 Chukong Technologies Inc. + Copyright (c) 2017-2022 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You shall + not use Cocos Creator software for developing other software or tools that's + used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. + + 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 "Utils.h" + +#if CC_PLATFORM == CC_PLATFORM_IOS || CC_PLATFORM == CC_PLATFORM_MACOS + #include +#endif + +#include +#include +#include + +#define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED + + +namespace cocos2d { namespace experimental { +namespace utils { + +#define MAX_ITOA_BUFFER_SIZE 256 +double atof(const char *str) { + if (str == nullptr) { + return 0.0; + } + + char buf[MAX_ITOA_BUFFER_SIZE]; + strncpy(buf, str, MAX_ITOA_BUFFER_SIZE); + + // strip string, only remain 7 numbers after '.' + char *dot = strchr(buf, '.'); + if (dot != nullptr && dot - buf + 8 < MAX_ITOA_BUFFER_SIZE) { + dot[8] = '\0'; + } + + return ::atof(buf); +} + +uint32_t nextPOT(uint32_t x) { + x = x - 1; + x = x | (x >> 1); + x = x | (x >> 2); + x = x | (x >> 4); + x = x | (x >> 8); + x = x | (x >> 16); + return x + 1; +} + +// painfully slow to execute, use with caution +std::string getStacktrace(uint32_t skip, uint32_t maxDepth) { + return "not support for 2dx"; +} + +} // namespace utils + +#if USE_MEMORY_LEAK_DETECTOR + + // Make sure GMemoryHook to be initialized first. + #if (CC_COMPILER == CC_COMPILER_MSVC) + #pragma warning(push) + #pragma warning(disable : 4073) + #pragma init_seg(lib) +MemoryHook GMemoryHook; + #pragma warning(pop) + #elif (CC_COMPILER == CC_COMPILER_GNUC || CC_COMPILER == CC_COMPILER_CLANG) +MemoryHook GMemoryHook __attribute__((init_priority(101))); + #endif + +#endif +}} // namespace cc diff --git a/cocos/audio/ohos/utils/Utils.h b/cocos/audio/ohos/utils/Utils.h new file mode 100644 index 000000000000..3766c3d7ed9a --- /dev/null +++ b/cocos/audio/ohos/utils/Utils.h @@ -0,0 +1,393 @@ +/**************************************************************************** + Copyright (c) 2010 cocos2d-x.org + Copyright (c) 2013-2016 Chukong Technologies Inc. + Copyright (c) 2017-2022 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You shall + not use Cocos Creator software for developing other software or tools that's + used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. + + 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 +#include +#include +#include +#include "platform/CCPlatformMacros.h" + +#include +#include +/** @file ccUtils.h +Misc free functions +*/ + +namespace cocos2d { namespace experimental { +namespace utils { +std::string getStacktrace(uint32_t skip = 0, uint32_t maxDepth = UINT32_MAX); + +/** + * Returns the Next Power of Two value. + * Examples: + * - If "value" is 15, it will return 16. + * - If "value" is 16, it will return 16. + * - If "value" is 17, it will return 32. + * @param value The value to get next power of two. + * @return Returns the next power of two value. + * @since v0.99.5 +*/ +uint32_t nextPOT(uint32_t x); + +/** + * Same to ::atof, but strip the string, remain 7 numbers after '.' before call atof. + * Why we need this? Because in android c++_static, atof ( and std::atof ) is unsupported for numbers have long decimal part and contain + * several numbers can approximate to 1 ( like 90.099998474121094 ), it will return inf. This function is used to fix this bug. + * @param str The string be to converted to double. + * @return Returns converted value of a string. + */ +double atof(const char *str); + +#pragma warning(disable : 4146) +template ::value && std::is_unsigned::value>> +inline T getLowestBit(T mask) { + return mask & (-mask); +} +#pragma warning(default : 4146) + +template ::value && std::is_unsigned::value>> +inline T clearLowestBit(T mask) { + return mask & (mask - 1); +} + +// v must be power of 2 +inline uint32_t getBitPosition(uint32_t v) { + if (!v) return 0; + uint32_t c = 32; + if (v & 0x0000FFFF) c -= 16; + if (v & 0x00FF00FF) c -= 8; + if (v & 0x0F0F0F0F) c -= 4; + if (v & 0x33333333) c -= 2; + if (v & 0x55555555) c -= 1; + return c; +} + +// v must be power of 2 +inline uint64_t getBitPosition(uint64_t v) { + if (!v) return 0; + uint64_t c = 64; + if (v & 0x00000000FFFFFFFFLL) c -= 32; + if (v & 0x0000FFFF0000FFFFLL) c -= 16; + if (v & 0x00FF00FF00FF00FFLL) c -= 8; + if (v & 0x0F0F0F0F0F0F0F0FLL) c -= 4; + if (v & 0x3333333333333333LL) c -= 2; + if (v & 0x5555555555555555LL) c -= 1; + return c; +} + +template ::value>> +inline size_t popcount(T mask) { + return std::bitset(mask).count(); +} + +template ::value>> +inline T alignTo(T size, T alignment) { + return ((size - 1) / alignment + 1) * alignment; +} + +template +constexpr uint32_t ALIGN_TO = ((size - 1) / alignment + 1) * alignment; + +template +inline uint32_t toUint(T value) { + static_assert(std::is_arithmetic::value, "T must be numeric"); + + CC_ASSERT(static_cast(value) <= static_cast(std::numeric_limits::max())); + + return static_cast(value); +} + +template +Map &mergeToMap(Map &outMap, const Map &inMap) { + for (const auto &e : inMap) { + outMap.emplace(e.first, e.second); + } + return outMap; +} + +namespace numext { + +template +CC_FORCE_INLINE Tgt bit_cast(const Src &src) { // NOLINT(readability-identifier-naming) + // The behaviour of memcpy is not specified for non-trivially copyable types + static_assert(std::is_trivially_copyable::value, "THIS_TYPE_IS_NOT_SUPPORTED"); + static_assert(std::is_trivially_copyable::value && std::is_default_constructible::value, + "THIS_TYPE_IS_NOT_SUPPORTED"); + static_assert(sizeof(Src) == sizeof(Tgt), "THIS_TYPE_IS_NOT_SUPPORTED"); + + Tgt tgt; + // Load src into registers first. This allows the memcpy to be elided by CUDA. + const Src staged = src; + memcpy(&tgt, &staged, sizeof(Tgt)); + return tgt; +} + +} // namespace numext + +// Following the Arm ACLE arm_neon.h should also include arm_fp16.h but not all +// compilers seem to follow this. We therefore include it explicitly. +// See also: https://bugs.llvm.org/show_bug.cgi?id=47955 +#if defined(CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC) + #include +#endif + +// Code from https://gitlab.com/libeigen/eigen/-/blob/master/Eigen/src/Core/arch/Default/Half.h#L586 +struct HalfRaw { + constexpr HalfRaw() : x(0) {} +#if defined(CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC) + explicit HalfRaw(uint16_t raw) : x(numext::bit_cast<__fp16>(raw)) { + } + __fp16 x; +#else + explicit constexpr HalfRaw(uint16_t raw) : x(raw) {} + uint16_t x; // NOLINT(modernize-use-default-member-init) +#endif +}; + +// Conversion routines, including fallbacks for the host or older CUDA. +// Note that newer Intel CPUs (Haswell or newer) have vectorized versions of +// these in hardware. If we need more performance on older/other CPUs, they are +// also possible to vectorize directly. + +CC_FORCE_INLINE HalfRaw rawUint16ToHalf(uint16_t x) { + // We cannot simply do a "return HalfRaw(x)" here, because HalfRaw is union type + // in the hip_fp16 header file, and that will trigger a compile error + // On the other hand, having anything but a return statement also triggers a compile error + // because this is constexpr function. + // Fortunately, since we need to disable EIGEN_CONSTEXPR for GPU anyway, we can get out + // of this catch22 by having separate bodies for GPU / non GPU +#if defined(CC_HAS_GPU_FP16) + HalfRaw h; + h.x = x; + return h; +#else + return HalfRaw(x); +#endif +} + +CC_FORCE_INLINE uint16_t rawHalfAsUint16(const HalfRaw &h) { + // HIP/CUDA/Default have a member 'x' of type uint16_t. + // For ARM64 native half, the member 'x' is of type __fp16, so we need to bit-cast. + // For SYCL, cl::sycl::half is _Float16, so cast directly. +#if defined(CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC) + return numext::bit_cast(h.x); +#else + return h.x; +#endif +} + +union float32_bits { + unsigned int u; + float f; +}; + +CC_FORCE_INLINE HalfRaw floatToHalf(float ff) { +#if defined(CC_HAS_FP16_C) + HalfRaw h; + #ifdef _MSC_VER + // MSVC does not have scalar instructions. + h.x = _mm_extract_epi16(_mm_cvtps_ph(_mm_set_ss(ff), 0), 0); + #else + h.x = _cvtss_sh(ff, 0); + #endif + return h; + +#elif defined(CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC) + HalfRaw h; + h.x = static_cast<__fp16>(ff); + return h; + +#else + float32_bits f; + f.f = ff; + + const float32_bits f32infty = {255 << 23}; + const float32_bits f16max = {(127 + 16) << 23}; + const float32_bits denorm_magic = {((127 - 15) + (23 - 10) + 1) << 23}; // NOLINT(readability-identifier-naming) + unsigned int sign_mask = 0x80000000U; // NOLINT + HalfRaw o; + o.x = static_cast(0x0U); + + unsigned int sign = f.u & sign_mask; + f.u ^= sign; + + // NOTE all the integer compares in this function can be safely + // compiled into signed compares since all operands are below + // 0x80000000. Important if you want fast straight SSE2 code + // (since there's no unsigned PCMPGTD). + + if (f.u >= f16max.u) { // result is Inf or NaN (all exponent bits set) + o.x = (f.u > f32infty.u) ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf + } else { // (De)normalized number or zero + if (f.u < (113 << 23)) { // resulting FP16 is subnormal or zero + // use a magic value to align our 10 mantissa bits at the bottom of + // the float. as long as FP addition is round-to-nearest-even this + // just works. + f.f += denorm_magic.f; + + // and one integer subtract of the bias later, we have our final float! + o.x = static_cast(f.u - denorm_magic.u); + } else { + unsigned int mant_odd = (f.u >> 13) & 1; // NOLINT(readability-identifier-naming) // resulting mantissa is odd + + // update exponent, rounding bias part 1 + // Equivalent to `f.u += ((unsigned int)(15 - 127) << 23) + 0xfff`, but + // without arithmetic overflow. + f.u += 0xc8000fffU; + // rounding bias part 2 + f.u += mant_odd; + // take the bits! + o.x = static_cast(f.u >> 13); + } + } + + o.x |= static_cast(sign >> 16); + return o; +#endif +} + +CC_FORCE_INLINE float halfToFloat(HalfRaw h) { +#if defined(CC_HAS_FP16_C) + #ifdef _MSC_VER + // MSVC does not have scalar instructions. + return _mm_cvtss_f32(_mm_cvtph_ps(_mm_set1_epi16(h.x))); + #else + return _cvtsh_ss(h.x); + #endif +#elif defined(CC_HAS_ARM64_FP16_SCALAR_ARITHMETIC) + return static_cast(h.x); +#else + const float32_bits magic = {113 << 23}; + const unsigned int shifted_exp = 0x7c00 << 13; // NOLINT(readability-identifier-naming) // exponent mask after shift + float32_bits o; + + o.u = (h.x & 0x7fff) << 13; // exponent/mantissa bits + unsigned int exp = shifted_exp & o.u; // just the exponent + o.u += (127 - 15) << 23; // exponent adjust + + // handle exponent special cases + if (exp == shifted_exp) { // Inf/NaN? + o.u += (128 - 16) << 23; // extra exp adjust + } else if (exp == 0) { // Zero/Denormal? + o.u += 1 << 23; // extra exp adjust + o.f -= magic.f; // renormalize + } + + o.u |= (h.x & 0x8000) << 16; // sign bit + return o.f; +#endif +} + +namespace array { + +/** + * @zh + * 移除首个指定的数组元素。判定元素相等时相当于于使用了 `Array.prototype.indexOf`。 + * @en + * Removes the first occurrence of a specific object from the array. + * Decision of the equality of elements is similar to `Array.prototype.indexOf`. + * @param array 数组。 + * @param value 待移除元素。 + */ +template +bool remove(std::vector &array, T value) { + auto iter = std::find(array.begin(), array.end(), value); + if (iter != array.end()) { + array.erase(iter); + return true; + } + return false; +} + +/** + * @zh + * 移除指定索引的数组元素。 + * @en + * Removes the array item at the specified index. + * @param array 数组。 + * @param index 待移除元素的索引。 + */ +template +bool removeAt(std::vector &array, int32_t index) { + if (index >= 0 && index < static_cast(array.size())) { + array.erase(array.begin() + index); + return true; + } + return false; +} + +/** + * @zh + * 移除指定索引的数组元素。 + * 此函数十分高效,但会改变数组的元素次序。 + * @en + * Removes the array item at the specified index. + * It's faster but the order of the array will be changed. + * @param array 数组。 + * @param index 待移除元素的索引。 + */ +template +bool fastRemoveAt(std::vector &array, int32_t index) { + const auto length = static_cast(array.size()); + if (index < 0 || index >= length) { + return false; + } + array[index] = array[length - 1]; + array.resize(length - 1); + return true; +} + +/** + * @zh + * 移除首个指定的数组元素。判定元素相等时相当于于使用了 `Array.prototype.indexOf`。 + * 此函数十分高效,但会改变数组的元素次序。 + * @en + * Removes the first occurrence of a specific object from the array. + * Decision of the equality of elements is similar to `Array.prototype.indexOf`. + * It's faster but the order of the array will be changed. + * @param array 数组。 + * @param value 待移除元素。 + */ +template +bool fastRemove(std::vector &array, T value) { + auto iter = std::find(array.begin(), array.end(), value); + if (iter != array.end()) { + *iter = array[array.size() - 1]; + array.resize(array.size() - 1); + return true; + } + return false; +} + +} // namespace array +} // namespace utils +}} // namespace cc diff --git a/cocos/base/CCConsole.cpp b/cocos/base/CCConsole.cpp index aa306ce6bce9..f6d1940ccc13 100644 --- a/cocos/base/CCConsole.cpp +++ b/cocos/base/CCConsole.cpp @@ -241,7 +241,10 @@ static void _log(const char *format, va_list args) strcat(buf, "\n"); #if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID - __android_log_print(ANDROID_LOG_DEBUG, "cocos2d-x debug info", "%s", buf); + __android_log_print(ANDROID_LOG_DEBUG, "cocos2d-x debug info", "%s", buf); + +#elif CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + OHOS_LOGI("cocos2d-x debug info %{public}s", buf); #elif CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_WINRT WCHAR wszBuf[MAX_LOG_LENGTH] = {0}; diff --git a/cocos/base/CCDirector.cpp b/cocos/base/CCDirector.cpp index 6351c82d76a2..1ea3021762aa 100644 --- a/cocos/base/CCDirector.cpp +++ b/cocos/base/CCDirector.cpp @@ -789,6 +789,20 @@ Vec2 Director::getVisibleOrigin() const } } +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) +Rect Director::getSafeAreaRect() const +{ + if (_openGLView) + { + return _openGLView->getSafeAreaRect(); + } + else + { + return Rect::ZERO; + } +} +#endif + // scene management void Director::runWithScene(Scene *scene) diff --git a/cocos/base/CCDirector.h b/cocos/base/CCDirector.h index 28cbdcfbf886..c13c7f203556 100644 --- a/cocos/base/CCDirector.h +++ b/cocos/base/CCDirector.h @@ -246,6 +246,13 @@ class CC_DLL Director : public Ref /** Returns visible origin coordinate of the OpenGL view in points. */ Vec2 getVisibleOrigin() const; +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + /**ohos + * Returns safe area rectangle of the OpenGL view in points. + */ + Rect getSafeAreaRect() const; +#endif + /** * Converts a screen coordinate to an OpenGL coordinate. * Useful to convert (multi) touch coordinates to the current layout (portrait or landscape). diff --git a/cocos/base/CCEventMouse.h b/cocos/base/CCEventMouse.h index 8bfc72e46399..641c8532b92c 100644 --- a/cocos/base/CCEventMouse.h +++ b/cocos/base/CCEventMouse.h @@ -64,6 +64,19 @@ class CC_DLL EventMouse : public Event MOUSE_SCROLL, }; + enum class MouseButton + { + BUTTON_UNSET = -1, + BUTTON_LEFT = 0, + BUTTON_RIGHT = 1, + BUTTON_MIDDLE = 2, + BUTTON_4 = 3, + BUTTON_5 = 4, + BUTTON_6 = 5, + BUTTON_7 = 6, + BUTTON_8 = 7 + }; + /** Constructor. * * @param mouseEventCode A given mouse event type. diff --git a/cocos/base/CCRefPtr.h b/cocos/base/CCRefPtr.h index 60f833f1be3f..7a01edcb121c 100644 --- a/cocos/base/CCRefPtr.h +++ b/cocos/base/CCRefPtr.h @@ -211,17 +211,21 @@ template class RefPtr inline bool operator > (const T * other) const { return _ptr > other; } inline bool operator > (typename std::remove_const::type * other) const { return _ptr > other; } - - inline bool operator > (const std::nullptr_t other) const { return _ptr > other; } - + // TODO + #if(CC_TARGET_PLATFORM != CC_PLATFORM_OHOS) + inline bool operator > (const std::nullptr_t other) const { return _ptr > other; } + #endif inline bool operator < (const RefPtr & other) const { return _ptr < other._ptr; } inline bool operator < (const T * other) const { return _ptr < other; } inline bool operator < (typename std::remove_const::type * other) const { return _ptr < other; } - - inline bool operator < (const std::nullptr_t other) const { return _ptr < other; } + + // TODO + #if(CC_TARGET_PLATFORM != CC_PLATFORM_OHOS) + inline bool operator < (const std::nullptr_t other) const { return _ptr < other; } + #endif inline bool operator >= (const RefPtr & other) const { return _ptr >= other._ptr; } @@ -229,9 +233,10 @@ template class RefPtr inline bool operator >= (const T * other) const { return _ptr >= other; } inline bool operator >= (typename std::remove_const::type * other) const { return _ptr >= other; } - - inline bool operator >= (const std::nullptr_t other) const { return _ptr >= other; } - + // TODO + #if(CC_TARGET_PLATFORM != CC_PLATFORM_OHOS) + inline bool operator >= (const std::nullptr_t other) const { return _ptr >= other; } + #endif inline bool operator <= (const RefPtr & other) const { return _ptr <= other._ptr; } @@ -239,8 +244,10 @@ template class RefPtr inline bool operator <= (typename std::remove_const::type * other) const { return _ptr <= other; } - inline bool operator <= (const std::nullptr_t other) const { return _ptr <= other; } - + // TODO + #if(CC_TARGET_PLATFORM != CC_PLATFORM_OHOS) + inline bool operator <= (const std::nullptr_t other) const { return _ptr <= other; } + #endif inline operator bool() const { return _ptr != nullptr; } diff --git a/cocos/base/allocator/CCAllocatorMutex.h b/cocos/base/allocator/CCAllocatorMutex.h index 48e52ce1a486..f5bf7ba814ee 100644 --- a/cocos/base/allocator/CCAllocatorMutex.h +++ b/cocos/base/allocator/CCAllocatorMutex.h @@ -29,7 +29,7 @@ #include "platform/CCPlatformMacros.h" -#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_LINUX +#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_LINUX || CC_TARGET_PLATFORM == CC_PLATFORM_OHOS #include "pthread.h" #define MUTEX pthread_mutex_t #define MUTEX_INIT(m) \ diff --git a/cocos/base/ccConfig.h b/cocos/base/ccConfig.h index 0d32b51f542a..81571b155c15 100644 --- a/cocos/base/ccConfig.h +++ b/cocos/base/ccConfig.h @@ -254,8 +254,7 @@ THE SOFTWARE. /** Use 3d physics integration API. */ #ifndef CC_USE_3D_PHYSICS -#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_LINUX || CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) -#define CC_USE_3D_PHYSICS 1 +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_LINUX || CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) #define CC_USE_3D_PHYSICS 1 #endif #endif diff --git a/cocos/cocos2d.h b/cocos/cocos2d.h index c63d32180930..7c5752b4fa2a 100644 --- a/cocos/cocos2d.h +++ b/cocos/cocos2d.h @@ -212,6 +212,13 @@ THE SOFTWARE. //Enhance modification end #endif // CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + #include "platform/ohos/CCApplication-ohos.h" + #include "platform/ohos/CCGLViewImpl-ohos.h" + #include "platform/ohos/CCGL-ohos.h" + #include "platform/ohos/CCStdC-ohos.h" +#endif // CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + #if (CC_TARGET_PLATFORM == CC_PLATFORM_BLACKBERRY) #include "platform/blackberry/CCApplication.h" #include "platform/blackberry/CCGLViewImpl.h" diff --git a/cocos/network/WebSocket.cpp b/cocos/network/WebSocket.cpp index 6885eef5088a..4df5dd627ce0 100644 --- a/cocos/network/WebSocket.cpp +++ b/cocos/network/WebSocket.cpp @@ -429,8 +429,10 @@ void WebSocket::onSubThreadStarted() info.port = CONTEXT_PORT_NO_LISTEN; info.protocols = _wsProtocols; -#ifndef LWS_NO_EXTENSIONS - info.extensions = libwebsocket_get_internal_extensions(); +#if (CC_TARGET_PLATFORM != CC_PLATFORM_OHOS) + #ifndef LWS_NO_EXTENSIONS + info.extensions = libwebsocket_get_internal_extensions(); + #endif #endif info.gid = -1; info.uid = -1; diff --git a/cocos/platform/CCApplication.h b/cocos/platform/CCApplication.h index 27e3d499c0bb..702b0e7419cb 100644 --- a/cocos/platform/CCApplication.h +++ b/cocos/platform/CCApplication.h @@ -41,6 +41,8 @@ THE SOFTWARE. #include "platform/winrt/CCApplication.h" #elif CC_TARGET_PLATFORM == CC_PLATFORM_LINUX #include "platform/linux/CCApplication-linux.h" +#elif CC_TARGET_PLATFORM == CC_PLATFORM_OHOS +#include "platform/ohos/CCApplication-ohos.h" #endif /// @endcond diff --git a/cocos/platform/CCApplicationProtocol.h b/cocos/platform/CCApplicationProtocol.h index ea69db3dd005..00f92168bd54 100644 --- a/cocos/platform/CCApplicationProtocol.h +++ b/cocos/platform/CCApplicationProtocol.h @@ -56,8 +56,9 @@ class CC_DLL ApplicationProtocol OS_NACL,/** Nacl */ OS_EMSCRIPTEN,/** Emscripten */ OS_TIZEN,/** Tizen */ - OS_WINRT,/** Windows Store Applications */ - OS_WP8/** Windows Phone Applications */ + OS_WINRT,/** Winrt */ + OS_WP8,/** WP8 */ + OS_OHOS/** OHOS */ }; /** diff --git a/cocos/platform/CCDevice.h b/cocos/platform/CCDevice.h index 84e655f58ee7..fca6e4a55d67 100644 --- a/cocos/platform/CCDevice.h +++ b/cocos/platform/CCDevice.h @@ -83,6 +83,17 @@ class CC_DLL Device */ static void setKeepScreenOn(bool keepScreenOn); +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + /**ohos + * Vibrate for the specified amount of time. + * If vibrate is not supported, then invoking this method has no effect. + * Some platforms limit to a maximum duration of 5 seconds. + * Duration is ignored on iOS due to API limitations. + * @param duration The duration in seconds. + */ + static void vibrate(float duration); +#endif + /** * Gets texture data for text. */ diff --git a/cocos/platform/CCFileUtils.cpp b/cocos/platform/CCFileUtils.cpp index b5b502decdd6..95e6621e6950 100644 --- a/cocos/platform/CCFileUtils.cpp +++ b/cocos/platform/CCFileUtils.cpp @@ -1408,5 +1408,21 @@ std::string FileUtils::getSuitableFOpen(const std::string& filenameUtf8) const } #endif +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS +std::string FileUtils::getFileExtension(const std::string& filePath) const +{ + std::string fileExtension; + size_t pos = filePath.find_last_of('.'); + if (pos != std::string::npos) + { + fileExtension = filePath.substr(pos, filePath.length()); + + std::transform(fileExtension.begin(), fileExtension.end(), fileExtension.begin(), ::tolower); + } + + return fileExtension; +} +#endif + NS_CC_END diff --git a/cocos/platform/CCFileUtils.h b/cocos/platform/CCFileUtils.h index 9766478566b2..2fb469e78a7b 100644 --- a/cocos/platform/CCFileUtils.h +++ b/cocos/platform/CCFileUtils.h @@ -351,7 +351,17 @@ class CC_DLL FileUtils * @return True if the file exists, false if not. */ virtual bool isFileExist(const std::string& filename) const; - + +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + /**ohos + * Gets filename extension is a suffix (separated from the base filename by a dot) in lower case. + * Examples of filename extensions are .png, .jpeg, .exe, .dmg and .txt. + * @param filePath The path of the file, it could be a relative or absolute path. + * @return suffix for filename in lower case or empty if a dot not found. + */ + virtual std::string getFileExtension(const std::string& filePath) const; +#endif + /** * Checks whether the path is an absolute path. * diff --git a/cocos/platform/CCGL.h b/cocos/platform/CCGL.h index 9c754d265902..fa137f56174c 100644 --- a/cocos/platform/CCGL.h +++ b/cocos/platform/CCGL.h @@ -35,6 +35,8 @@ THE SOFTWARE. #include "platform/ios/CCGL-ios.h" #elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID #include "platform/android/CCGL-android.h" +#elif CC_TARGET_PLATFORM == CC_PLATFORM_OHOS +#include "platform/ohos/CCGL-ohos.h" #elif CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 #include "platform/win32/CCGL-win32.h" #elif CC_TARGET_PLATFORM == CC_PLATFORM_WINRT diff --git a/cocos/platform/CCGLView.cpp b/cocos/platform/CCGLView.cpp index 4cf41201c76d..47fb14cb3b01 100644 --- a/cocos/platform/CCGLView.cpp +++ b/cocos/platform/CCGLView.cpp @@ -200,6 +200,13 @@ Rect GLView::getVisibleRect() const return ret; } +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS +Rect GLView::getSafeAreaRect() const +{ + return getVisibleRect(); +} +#endif + Size GLView::getVisibleSize() const { if (_resolutionPolicy == ResolutionPolicy::NO_BORDER) diff --git a/cocos/platform/CCGLView.h b/cocos/platform/CCGLView.h index 0bbea562ba6d..c5433bf6d4dd 100644 --- a/cocos/platform/CCGLView.h +++ b/cocos/platform/CCGLView.h @@ -232,6 +232,13 @@ class CC_DLL GLView : public Ref */ virtual Rect getVisibleRect() const; +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + /**ohos + * Gets safe area rectangle + */ + virtual Rect getSafeAreaRect() const; +#endif + /** * Set the design resolution size. * @param width Design resolution width. diff --git a/cocos/platform/CCImage.cpp b/cocos/platform/CCImage.cpp index ad85ccb88fc2..ee52941ec331 100644 --- a/cocos/platform/CCImage.cpp +++ b/cocos/platform/CCImage.cpp @@ -87,6 +87,8 @@ extern "C" #include "base/ZipUtils.h" #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) #include "android/CCFileUtils-android.h" +#else if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) +#include "ohos/CCFileUtils-ohos.h" #endif #define CC_GL_ATC_RGB_AMD 0x8C92 diff --git a/cocos/platform/CCPlatformConfig.h b/cocos/platform/CCPlatformConfig.h index 6531f0e54eaa..02d48c7dec2c 100644 --- a/cocos/platform/CCPlatformConfig.h +++ b/cocos/platform/CCPlatformConfig.h @@ -52,6 +52,7 @@ THE SOFTWARE. #define CC_PLATFORM_TIZEN 11 #define CC_PLATFORM_QT5 12 #define CC_PLATFORM_WINRT 13 +#define CC_PLATFORM_OHOS 14 // Determine target platform by compile environment macro. #define CC_TARGET_PLATFORM CC_PLATFORM_UNKNOWN @@ -134,6 +135,12 @@ THE SOFTWARE. #define CC_TARGET_PLATFORM CC_PLATFORM_WINRT #endif +// ohos +#if defined(OHOS) + #undef CC_TARGET_PLATFORM + #define CC_TARGET_PLATFORM CC_PLATFORM_OHOS +#endif + ////////////////////////////////////////////////////////////////////////// // post configure ////////////////////////////////////////////////////////////////////////// diff --git a/cocos/platform/CCPlatformDefine.h b/cocos/platform/CCPlatformDefine.h index 786714eb0c8d..5cda068b6561 100644 --- a/cocos/platform/CCPlatformDefine.h +++ b/cocos/platform/CCPlatformDefine.h @@ -35,6 +35,8 @@ THE SOFTWARE. #include "platform/ios/CCPlatformDefine-ios.h" #elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID #include "platform/android/CCPlatformDefine-android.h" +#elif CC_TARGET_PLATFORM == CC_PLATFORM_OHOS +#include "platform/ohos/CCPlatformDefine-ohos.h" #elif CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 #include "platform/win32/CCPlatformDefine-win32.h" #elif CC_TARGET_PLATFORM == CC_PLATFORM_WINRT diff --git a/cocos/platform/CCPlatformMacros.h b/cocos/platform/CCPlatformMacros.h index 48ecbeab1f6b..ca8dc1339916 100644 --- a/cocos/platform/CCPlatformMacros.h +++ b/cocos/platform/CCPlatformMacros.h @@ -85,13 +85,13 @@ CC_DEPRECATED_ATTRIBUTE static __TYPE__* node() \ * * @since v0.99.5 */ -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) #define CC_ENABLE_CACHE_TEXTURE_DATA 1 #else #define CC_ENABLE_CACHE_TEXTURE_DATA 0 #endif -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_EMSCRIPTEN) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) || (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_EMSCRIPTEN) /** Application will crash in glDrawElements function on some win32 computers and some android devices. * Indices should be bound again while drawing to avoid this bug. */ diff --git a/cocos/platform/CCStdC.h b/cocos/platform/CCStdC.h index f22fddff4b80..97de42430193 100644 --- a/cocos/platform/CCStdC.h +++ b/cocos/platform/CCStdC.h @@ -34,6 +34,8 @@ THE SOFTWARE. #include "platform/ios/CCStdC-ios.h" #elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID #include "platform/android/CCStdC-android.h" +#elif CC_TARGET_PLATFORM == CC_PLATFORM_OHOS +#include "platform/ohos/CCStdC-ohos.h" #elif CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 #include "platform/win32/CCStdC-win32.h" #elif CC_TARGET_PLATFORM == CC_PLATFORM_WINRT diff --git a/cocos/platform/CMakeLists.txt b/cocos/platform/CMakeLists.txt index 298a73296cc3..d392f16af765 100644 --- a/cocos/platform/CMakeLists.txt +++ b/cocos/platform/CMakeLists.txt @@ -59,6 +59,45 @@ set(COCOS_PLATFORM_SPECIFIC_SRC platform/android/jni/TouchesJni.cpp ) +elseif(OHOS) + +include_directories( + platform/ohos + platform/ohos/napi + platform/ohos/napi/modules + platform/ohos/napi/common + platform/ohos/napi/render + platform/ohos/napi/plugin_manager.h + platform/ohos/napi/helper/NapiValueConverter.h + platform/ohos/napi/helper/Js_Cocos2dxHelper.h + platform/ohos/napi/helper/NapiHelper.h +) + +set(COCOS_PLATFORM_SPECIFIC_SRC + platform/ohos/CCDevice-ohos.cpp + platform/ohos/CCGLViewImpl-ohos.cpp + platform/ohos/CCApplication-ohos.cpp + platform/ohos/CCCommon-ohos.cpp + platform/ohos/CCFileUtils-ohos.cpp + platform/ohos/CCTextBitmap.cpp + + platform/ohos/JsAudioEngine.cpp + + platform/ohos/napi/modules/RawFileUtils.cpp + platform/ohos/napi/modules/TouchesNapi.cpp + platform/ohos/napi/modules/InputNapi.cpp + platform/ohos/napi/modules/MouseNapi.cpp + platform/ohos/napi/modules/WebViewNapi.cpp + platform/ohos/napi/modules/SensorNapi.cpp + platform/ohos/napi/modules/VideoPlayerNapi.cpp + platform/ohos/napi/helper/NapiValueConverter.cpp + platform/ohos/napi/helper/Js_Cocos2dxHelper.cpp + platform/ohos/napi/helper/NapiHelper.cpp + platform/ohos/napi/render/egl_core.cpp + platform/ohos/napi/render/plugin_render.cpp + platform/ohos/napi/plugin_manager.cpp + platform/ohos/napi/WorkerMessageQueue.cpp +) endif() #leave andatory external stuff here also diff --git a/cocos/platform/ohos/CCApplication-ohos.cpp b/cocos/platform/ohos/CCApplication-ohos.cpp new file mode 100644 index 000000000000..a630b7b174d1 --- /dev/null +++ b/cocos/platform/ohos/CCApplication-ohos.cpp @@ -0,0 +1,181 @@ +#include "platform/CCPlatformConfig.h" +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS +#include "napi/helper/Js_Cocos2dxHelper.h" +#include "napi/helper/NapiHelper.h" +#include "napi/render/plugin_render.h" +#include "CCApplication.h" +#include "base/CCDirector.h" +#include "base/ccUtils.h" +#include "CCLogOhos.h" +#include + +// FIXME: using ndk-r10c will cause the next function could not be found. It may be a bug of ndk-r10c. +// Here is the workaround method to fix the problem. +#ifdef __aarch64__ +extern "C" size_t __ctype_get_mb_cur_max(void) { + return (size_t) sizeof(wchar_t); +} +#endif + +NS_CC_BEGIN + +// sharedApplication pointer +Application * Application::sm_pSharedApplication = nullptr; + +Application::Application() { + CCAssert(! sm_pSharedApplication, ""); + sm_pSharedApplication = this; +} + +Application::~Application() { + CCAssert(this == sm_pSharedApplication, ""); + sm_pSharedApplication = nullptr; +} + +int Application::run() { + // Initialize instance and cocos2d. + if (! applicationDidFinishLaunching()) + { + return 0; + } + + return -1; +} + +void Application::setAnimationInterval(double interval) +{ + OHOS_LOGD("setAnimationInterval param is [%{public}lf] =========", interval); + PluginRender::GetInstance()->changeFPS((uint64_t)(interval * 1000)); // s to ms +} + +////////////////////////////////////////////////////////////////////////// +// static member function +////////////////////////////////////////////////////////////////////////// +Application* Application::getInstance() { + CCAssert(sm_pSharedApplication, ""); + return sm_pSharedApplication; +} + +// @deprecated Use getInstance() instead +Application* Application::sharedApplication() { + return Application::getInstance(); +} + +const char * Application::getCurrentLanguageCode() { + static char code[3]={0}; + std::string systemLanguage = JSFunction::getFunction("DeviceUtils.getSystemLanguage").invoke(); + OHOS_LOGD("==========getCurrentLanguageCode is [%{public}s] =========",systemLanguage.c_str()); + strncpy(code, systemLanguage.c_str(), 2); + code[2]='\0'; + return code; +} + +LanguageType Application::getCurrentLanguage() { + const char* pLanguageName = getCurrentLanguageCode(); + LanguageType ret = LanguageType::ENGLISH; + + if (0 == strcmp("zh", pLanguageName)) + { + ret = LanguageType::CHINESE; + } + else if (0 == strcmp("en", pLanguageName)) + { + ret = LanguageType::ENGLISH; + } + else if (0 == strcmp("fr", pLanguageName)) + { + ret = LanguageType::FRENCH; + } + else if (0 == strcmp("it", pLanguageName)) + { + ret = LanguageType::ITALIAN; + } + else if (0 == strcmp("de", pLanguageName)) + { + ret = LanguageType::GERMAN; + } + else if (0 == strcmp("es", pLanguageName)) + { + ret = LanguageType::SPANISH; + } + else if (0 == strcmp("ru", pLanguageName)) + { + ret = LanguageType::RUSSIAN; + } + else if (0 == strcmp("nl", pLanguageName)) + { + ret = LanguageType::DUTCH; + } + else if (0 == strcmp("ko", pLanguageName)) + { + ret = LanguageType::KOREAN; + } + else if (0 == strcmp("ja", pLanguageName)) + { + ret = LanguageType::JAPANESE; + } + else if (0 == strcmp("hu", pLanguageName)) + { + ret = LanguageType::HUNGARIAN; + } + else if (0 == strcmp("pt", pLanguageName)) + { + ret = LanguageType::PORTUGUESE; + } + else if (0 == strcmp("ar", pLanguageName)) + { + ret = LanguageType::ARABIC; + } + else if (0 == strcmp("nb", pLanguageName)) + { + ret = LanguageType::NORWEGIAN; + } + else if (0 == strcmp("pl", pLanguageName)) + { + ret = LanguageType::POLISH; + } + else if (0 == strcmp("tr", pLanguageName)) + { + ret = LanguageType::TURKISH; + } + else if (0 == strcmp("uk", pLanguageName)) + { + ret = LanguageType::UKRAINIAN; + } + else if (0 == strcmp("ro", pLanguageName)) + { + ret = LanguageType::ROMANIAN; + } + else if (0 == strcmp("bg", pLanguageName)) + { + ret = LanguageType::BULGARIAN; + } + return ret; +} + +Application::Platform Application::getTargetPlatform() +{ + return Platform::OS_OHOS; +} + +std::string Application::getVersion() { + return JSFunction::getFunction("ApplicationManager.getVersionName").invoke(); +} + +bool Application::openURL(const std::string &url) { + try { + JSFunction::getFunction("JumpManager.openUrl").invoke(url); + } catch (std::exception& e) { + return false; + } + return true; +} + +void Application::applicationScreenSizeChanged(int newWidth, int newHeight) { + +} + +NS_CC_END + +#endif // CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + diff --git a/cocos/platform/ohos/CCApplication-ohos.h b/cocos/platform/ohos/CCApplication-ohos.h new file mode 100644 index 000000000000..38da8ee4e0ba --- /dev/null +++ b/cocos/platform/ohos/CCApplication-ohos.h @@ -0,0 +1,114 @@ +/**************************************************************************** +Copyright (c) 2010-2012 cocos2d-x.org +Copyright (c) 2013-2014 Chukong Technologies Inc. + +http://www.cocos2d-x.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +****************************************************************************/ + +#ifndef __CC_APPLICATION_OHOS_H__ +#define __CC_APPLICATION_OHOS_H__ + +#include "platform/CCApplicationProtocol.h" +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + +#include "platform/CCCommon.h" +#include "platform/CCApplicationProtocol.h" + +NS_CC_BEGIN + +class CC_DLL Application : public ApplicationProtocol +{ +public: + /** + * @js ctor + */ + Application(); + /** + * @js NA + * @lua NA + */ + virtual ~Application(); + + /** + @brief Callback by Director to limit FPS. + @param interval The time, expressed in seconds, between current frame and next. + */ + void setAnimationInterval(double interval); + + /** + @brief Run the message loop. + */ + int run(); + + /** + @brief Get current application instance. + @return Current application instance pointer. + */ + static Application* getInstance(); + + /** @deprecated Use getInstance() instead */ + CC_DEPRECATED_ATTRIBUTE static Application* sharedApplication(); + + /** + @brief Get current language config + @return Current language config + */ + virtual LanguageType getCurrentLanguage(); + + /** + @brief Get current language iso 639-1 code + @return Current language iso 639-1 code + */ + virtual const char * getCurrentLanguageCode(); + + /** + @brief Get target platform + */ + virtual Platform getTargetPlatform(); + + /** + @brief Get application version. + */ + virtual std::string getVersion(); + + /** + @brief Open url in default browser + @param String with url to open. + @return true if the resource located by the URL was successfully opened; otherwise false. + */ + virtual bool openURL(const std::string &url); + + /** + @brief This function will be called when the application screen size is changed. + @param new width + @param new height + */ + virtual void applicationScreenSizeChanged(int newWidth, int newHeight); + +protected: + static Application * sm_pSharedApplication; +}; + +NS_CC_END + +#endif // CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + +#endif // __CC_APPLICATION_OHOS_H__ diff --git a/cocos/platform/ohos/CCCommon-ohos.cpp b/cocos/platform/ohos/CCCommon-ohos.cpp new file mode 100644 index 000000000000..f617c23fead2 --- /dev/null +++ b/cocos/platform/ohos/CCCommon-ohos.cpp @@ -0,0 +1,25 @@ +#include "platform/CCPlatformConfig.h" +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + +#include "platform/CCCommon.h" +#include "platform/ohos/napi/helper/NapiHelper.h" +#include "CCLogOhos.h" +#include + +NS_CC_BEGIN + +#define MAX_LEN (cocos2d::kMaxLogLen + 1) + +void MessageBox(const char * pszMsg, const char * pszTitle) { + std::string msg(pszMsg); + std::string title(pszTitle); + JSFunction::getFunction("DiaLog.showDialog").invoke(msg, title); +} + +void LuaLog(const char * pszFormat) { + OHOS_LOGI("cocos2d-x debug info %{public}s", pszFormat); +} + +NS_CC_END + +#endif // CC_TARGET_PLATFORM == CC_PLATFORM_OHOS \ No newline at end of file diff --git a/cocos/platform/ohos/CCDevice-ohos.cpp b/cocos/platform/ohos/CCDevice-ohos.cpp new file mode 100644 index 000000000000..8c28002d7103 --- /dev/null +++ b/cocos/platform/ohos/CCDevice-ohos.cpp @@ -0,0 +1,105 @@ +#include "platform/CCPlatformConfig.h" +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS +#include "napi/helper/Js_Cocos2dxHelper.h" +#include "platform/CCDevice.h" +#include "base/ccTypes.h" +#include "CCTextBitmap.h" +#include "napi/helper/NapiHelper.h" + +NS_CC_BEGIN + +int Device::getDPI() { + return JSFunction::getFunction("DeviceUtils.getDpi").invoke(); +} + +void Device::setAccelerometerEnabled(bool isEnabled) { + if (isEnabled) + { + Js_Cocos2dxHelper::enableAccelerometer(); + } + else + { + Js_Cocos2dxHelper::disableAccelerometer(); + } +} + +void Device::setAccelerometerInterval(float interval) { + Js_Cocos2dxHelper::setAccelerometerInterval(interval); +} + +class BitmapDC +{ +public: + + BitmapDC() + : m_nWidth(0) + , m_nHeight(0) + , m_pData(NULL) + { + } + + ~BitmapDC(void) + { + if (m_pData) + { + delete [] m_pData; + } + } + + bool getBitmapWithDrawing( const char *text, int nWidth, int nHeight, Device::TextAlign eAlignMask, const FontDefinition& textDefinition) { + CCTextBitmap *cCtextBitmap = new CCTextBitmap(); + CCTextBitmap::createCCTextBitmap(cCtextBitmap, text, textDefinition._fontName.data(), textDefinition._fontAlpha, textDefinition._fontFillColor.r, + textDefinition._fontFillColor.g, textDefinition._fontFillColor.b ,eAlignMask, nWidth, nHeight, textDefinition._fontSize); + void* pixels = cCtextBitmap->getPixelAddr(); + cocos2d::BitmapDC& bitmapDC = sharedBitmapDC(); + bitmapDC.m_nWidth = cCtextBitmap->GetWidth(); + bitmapDC.m_nHeight = cCtextBitmap->GetHeight(); + long size = bitmapDC.m_nWidth * bitmapDC.m_nHeight * 4; + bitmapDC.m_pData = new unsigned char[size]; + memcpy(bitmapDC.m_pData, pixels, size); + + delete cCtextBitmap; + return true; + } + +public: + int m_nWidth; + int m_nHeight; + unsigned char *m_pData; + + + + static BitmapDC& sharedBitmapDC() { + // TBD not safe for multi threads + static BitmapDC s_BmpDC; + return s_BmpDC; + } +}; + +Data Device::getTextureDataForText(const char * text, const FontDefinition& textDefinition, TextAlign align, int &width, int &height, bool& hasPremultipliedAlpha) { + Data ret; + do { + BitmapDC &dc = BitmapDC::sharedBitmapDC(); + if(! dc.getBitmapWithDrawing(text, (int)textDefinition._dimensions.width, (int)textDefinition._dimensions.height, align, textDefinition )) { + break; + }; + + width = dc.m_nWidth; + height = dc.m_nHeight; + ret.fastSet(dc.m_pData,width * height * 4); + hasPremultipliedAlpha = true; + } while (0); + return ret; +} + + +void Device::setKeepScreenOn(bool value) { + JSFunction::getFunction("DeviceUtils.setKeepScreenOn").invoke(value); +} + +void Device::vibrate(float duration) { + JSFunction::getFunction("DeviceUtils.startVibration").invoke(duration); +} + +NS_CC_END +#endif // CC_TARGET_PLATFORM == CC_PLATFORM_OHOS diff --git a/cocos/platform/ohos/CCFileUtils-ohos.cpp b/cocos/platform/ohos/CCFileUtils-ohos.cpp new file mode 100644 index 000000000000..8c5547a9d612 --- /dev/null +++ b/cocos/platform/ohos/CCFileUtils-ohos.cpp @@ -0,0 +1,294 @@ +#include "platform/CCPlatformConfig.h" +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + +#include "CCFileUtils-ohos.h" +#include "platform/CCCommon.h" +#include "CCLogOhos.h" +#include + + +using namespace std; + +NS_CC_BEGIN + +NativeResourceManager* FileUtilsOhos::nativeResourceManager_ = nullptr; +string FileUtilsOhos::ohWritablePath = ""; + +void FileUtilsOhos::setassetmanager(NativeResourceManager* a) { + if (nullptr == a) { + return; + } + + cocos2d::FileUtilsOhos::nativeResourceManager_ = a; +} + +FileUtils* FileUtils::getInstance() { + if (s_sharedFileUtils == nullptr) + { + s_sharedFileUtils = new FileUtilsOhos(); + if(!s_sharedFileUtils->init()) + { + delete s_sharedFileUtils; + s_sharedFileUtils = nullptr; + } + } + return s_sharedFileUtils; +} + +FileUtilsOhos::FileUtilsOhos() { +} + +FileUtilsOhos::~FileUtilsOhos() { +} + +bool FileUtilsOhos::init() { + _defaultResRootPath = ""; + return FileUtils::init(); +} + +bool FileUtilsOhos::isFileExist(const std::string& filename) const { + if (isAbsolutePath(filename)) { + return isFileExistInternal(filename); + } + std::string fullpath = const_cast(this)->fullPathForFilename(filename); + return !fullpath.empty(); +} + + + +bool FileUtilsOhos::isFileExistInternal(const std::string& strFilePath) const { + if (strFilePath.empty()) { + return false; + } + + bool bFound = false; + if (strFilePath[0] != '/') { + RawFile *fp = RawFileUtils::GetInstance().Open(strFilePath.c_str()); + if(fp) { + OHOS_LOGI("FileUtilsOhos::isFileExistInternal() - open %{public}s success", strFilePath.c_str()); + bFound = true; + RawFileUtils::GetInstance().Close(fp); + } + } else { + FILE *fp = fopen(strFilePath.c_str(), "r"); + if (fp) { + bFound = true; + fclose(fp); + } + } + return bFound; +} + +bool FileUtilsOhos::isAbsolutePath(const std::string& dirPath) const { + if (dirPath.empty()) return false; + std::string dirPathMf = dirPath[dirPath.length() - 1] == '/' ? dirPath.substr(0, dirPath.length() - 1) : dirPath; + + if (dirPathMf[0] == '/') { + struct stat st; + return stat(dirPathMf.c_str(), &st) == 0 && S_ISDIR(st.st_mode); + } + + if (dirPathMf.find(_defaultResRootPath) == 0) { + dirPathMf = dirPathMf.substr(_defaultResRootPath.length(), dirPathMf.length()); + } + + RawDir* rawDir = RawFileUtils::GetInstance().OpenDir(dirPathMf.c_str()); + if(rawDir) { + int file_count = RawFileUtils::GetInstance().GetDirSize(rawDir); + RawFileUtils::GetInstance().CloseDir(rawDir); + if (file_count) { + return true; + } + } + return false; +} + +std::vector FileUtilsOhos::listFiles(const std::string& dirPath) { + return RawFileUtils::GetInstance().searchFiles(dirPath.c_str(), false); +} + +Data FileUtilsOhos::getData(const std::string& filename) { + if (filename.empty()) { + return Data::Null; + } + + unsigned char* data = nullptr; + ssize_t size = 0; + + + std::string fullpath = isAbsolutePath(filename)? filename:fullPathForFilename(filename); + do { + + if(isAbsolutePath(fullpath)){ + FILE *fp = fopen(fullpath.c_str(), "r"); + if (!fp) break; +#if defined(_MSC_VER) + auto descriptor = _fileno(fp); +#else + auto descriptor = fileno(fp); +#endif + struct stat statBuf; + if (fstat(descriptor, &statBuf) == -1) { + fclose(fp); + break; + } + ssize_t fileSize = statBuf.st_size; + data = new unsigned char[fileSize]; + size = fread(data, 1, fileSize, fp); + fclose(fp); + } else { + RawFile *fp = RawFileUtils::GetInstance().Open(fullpath.c_str()); + CC_BREAK_IF(!fp); + ssize_t fileSize = RawFileUtils::GetInstance().GetSize(fp); + data = new unsigned char[fileSize]; + fileSize = RawFileUtils::GetInstance().Read(fp, data, fileSize); + RawFileUtils::GetInstance().Close(fp); + size = fileSize; + } + } while (0); + + Data ret; + if (data == nullptr || size == 0) { + std::string msg = "Get data from file("; + msg.append(filename).append(") failed!"); + OHOS_LOGD("%{public}s", msg.c_str()); + } else { + ret.fastSet(data, size); + } + + return ret; +} + + +bool FileUtilsOhos::getContents(const std::string& filename, ResizableBuffer* buffer) { + if (filename.empty()) { + //OHOS_LOGE("FileUtilsOhos::getContents() - filename is empty"); + return false; + } + + std::string fullpath = isAbsolutePath(filename)? filename:fullPathForFilename(filename); + + if (fullpath[0] == '/') { + FILE *fp = fopen(fullpath.c_str(), "rb"); + if (!fp) + return false; + +#if defined(_MSC_VER) + auto descriptor = _fileno(fp); +#else + auto descriptor = fileno(fp); +#endif + struct stat statBuf; + if (fstat(descriptor, &statBuf) == -1) { + fclose(fp); + return false; + } + size_t size = statBuf.st_size; + + buffer->resize(size); + size_t readsize = fread(buffer->buffer(), 1, size, fp); + fclose(fp); + + if (readsize < size) { + buffer->resize(readsize); + return false; + } + } else { + RawFile *fp = RawFileUtils::GetInstance().Open(fullpath.c_str()); + if (!fp) { + OHOS_LOGI("FileUtilsOhos::fp is nullptr"); + return false; + } + auto size = RawFileUtils::GetInstance().GetSize(fp); + buffer->resize(size); + + int readsize = RawFileUtils::GetInstance().Read(fp, buffer->buffer(), size); + RawFileUtils::GetInstance().Close(fp); + + if (readsize < size) { + if (readsize >= 0) + buffer->resize(readsize); + OHOS_LOGE("FileUtilsOhos::getContents() - readsize < size"); + return false; + } + + if (!buffer->buffer()) { + std::string msg = "Get data from file(" + filename + ") failed!"; + OHOS_LOGI("%{public}s", msg.c_str()); + } + } + + return true; +} + +std::string FileUtilsOhos::getStringFromFile(const std::string& filename) { + std::string s; + getContents(filename, &s); + return s; +} + +Data FileUtilsOhos::getDataFromFile(const std::string& filename) { + Data d; + getContents(filename, &d); + return d; +} + + +// unsigned char* CCFileUtilsOhos::getFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize) +unsigned char* FileUtilsOhos::getFileData(const std::string& filename, const char* mode, ssize_t * size) { + unsigned char * data = 0; + + if ( filename.empty() || (! mode) ) { + return 0; + } + + std::string fullpath = isAbsolutePath(filename)? filename:fullPathForFilename(filename); + + do { + RawFile *fp = RawFileUtils::GetInstance().Open(fullpath.c_str()); + //CCLOG("[Nnnnut] FileUtilsOhos::getFileData filename: %s", fullpath.c_str()); + CC_BREAK_IF(!fp); + ssize_t fileSize = RawFileUtils::GetInstance().GetSize(fp); + data = new unsigned char[fileSize]; + fileSize = RawFileUtils::GetInstance().Read(fp, data, fileSize); + RawFileUtils::GetInstance().Close(fp); + //CCLOG("[Nnnnut] FileUtilsOhos::getFileData fileSize: %d", fileSize); + + if (size) { + *size = fileSize; + } + + } while (0); + + if (! data) { + std::string msg = "Get data from file("; + msg.append(filename).append(") failed!"); + OHOS_LOGD("%{public}s", msg.c_str()); + } + return data; +} + +bool FileUtilsOhos::getRawFileDescriptor(const std::string &filename, RawFileDescriptor &descriptor) { + if (filename.empty()) { + return false; + } + std::string fullpath = isAbsolutePath(filename)? filename:fullPathForFilename(filename); + + RawFile *fp = RawFileUtils::GetInstance().Open(fullpath.c_str());//fopen(strFilePath.c_str(), "r"); + if (!fp) { + OHOS_LOGE("FileUtilsOhos::fp is nullptr"); + return false; + } + + bool result = RawFileUtils::GetInstance().GetRawFileDescriptor(fp, descriptor); + RawFileUtils::GetInstance().Close(fp); + return result; +} + +string FileUtilsOhos::getWritablePath() const { + return ohWritablePath; +} + +NS_CC_END + +#endif // CC_TARGET_PLATFORM == CC_PLATFORM_OHOS diff --git a/cocos/platform/ohos/CCFileUtils-ohos.h b/cocos/platform/ohos/CCFileUtils-ohos.h new file mode 100644 index 000000000000..45a9978395fe --- /dev/null +++ b/cocos/platform/ohos/CCFileUtils-ohos.h @@ -0,0 +1,158 @@ +#ifndef __CC_FILEUTILS_OHOS_H__ +#define __CC_FILEUTILS_OHOS_H__ + +#include "platform/CCPlatformConfig.h" +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + +#include "platform/CCFileUtils.h" +#include "platform/CCPlatformMacros.h" +#include "base/ccTypes.h" +#include +#include +#include "napi/modules/RawFileUtils.h" + +NS_CC_BEGIN + +class ZipFile; + +/** + * @addtogroup platform + * @{ + */ + + +class ResizableBuffer { +public: + virtual ~ResizableBuffer() {} + virtual void resize(size_t size) = 0; + virtual void* buffer() const = 0; +}; + +template +class ResizableBufferAdapter { }; + + +template +class ResizableBufferAdapter< std::basic_string > : public ResizableBuffer { + typedef std::basic_string BufferType; + BufferType* _buffer; +public: + explicit ResizableBufferAdapter(BufferType* buffer) : _buffer(buffer) {} + virtual void resize(size_t size) override { + _buffer->resize((size + sizeof(CharT) - 1) / sizeof(CharT)); + } + virtual void* buffer() const override { + // can not invoke string::front() if it is empty + + if (_buffer->empty()) + return nullptr; + else + return &_buffer->front(); + } +}; + +template +class ResizableBufferAdapter< std::vector > : public ResizableBuffer { + typedef std::vector BufferType; + BufferType* _buffer; +public: + explicit ResizableBufferAdapter(BufferType* buffer) : _buffer(buffer) {} + virtual void resize(size_t size) override { + _buffer->resize((size + sizeof(T) - 1) / sizeof(T)); + } + virtual void* buffer() const override { + // can not invoke vector::front() if it is empty + + if (_buffer->empty()) + return nullptr; + else + return &_buffer->front(); + } +}; + + +template<> +class ResizableBufferAdapter : public ResizableBuffer { + typedef Data BufferType; + BufferType* _buffer; +public: + explicit ResizableBufferAdapter(BufferType* buffer) : _buffer(buffer) {} + virtual void resize(size_t size) override { + if (static_cast(_buffer->getSize()) < size) { + auto old = _buffer->getBytes(); + void* buffer = realloc(old, size); + if (buffer) + _buffer->fastSet((unsigned char*)buffer, size); + } + } + virtual void* buffer() const override { + return _buffer->getBytes(); + } +}; + + +//! @brief Helper class to handle file operations +class CC_DLL FileUtilsOhos : public FileUtils +{ + friend class FileUtils; +public: + virtual ~FileUtilsOhos(); + + static std::string ohWritablePath; + + static void setassetmanager(NativeResourceManager* a); + static NativeResourceManager* getAssetManager() { return nativeResourceManager_; } + bool getRawFileDescriptor(const std::string &filename, RawFileDescriptor &descriptor); + /* override funtions */ + bool init(); + + /** @deprecated Please use FileUtils::getDataFromFile or FileUtils::getStringFromFile instead. */ + CC_DEPRECATED_ATTRIBUTE virtual unsigned char* getFileData(const std::string& filename, const char* mode, ssize_t * size) override; + + /** + * Gets string from a file. + */ + virtual std::string getStringFromFile(const std::string& filename) override; + + /** + * Creates binary data from a file. + * @return A data object. + */ + virtual Data getDataFromFile(const std::string& filename) override; + + virtual std::string getWritablePath() const; + virtual bool isAbsolutePath(const std::string& dirPath) const; + + virtual bool isFileExist(const std::string& filename) const; + + virtual std::vector listFiles(const std::string& dirPath); + +private: + FileUtilsOhos(); + virtual bool isFileExistInternal(const std::string& strFilePath) const; + Data getData(const std::string& filename); + + template < + typename T, + typename Enable = typename std::enable_if< + std::is_base_of< ResizableBuffer, ResizableBufferAdapter >::value + >::type + > + bool getContents(const std::string& filename, T* buffer) { + ResizableBufferAdapter buf(buffer); + return getContents(filename, &buf); + } + virtual bool getContents(const std::string& filename, ResizableBuffer* buffer); + + static NativeResourceManager* nativeResourceManager_; +}; + +// end of platform group +/// @} + +NS_CC_END + +#endif // CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + +#endif // __CC_FILEUTILS_OHOS_H__ + diff --git a/cocos/platform/ohos/CCGL-ohos.h b/cocos/platform/ohos/CCGL-ohos.h new file mode 100644 index 000000000000..9eb21c37f66b --- /dev/null +++ b/cocos/platform/ohos/CCGL-ohos.h @@ -0,0 +1,45 @@ +#ifndef __CCGL_H__ +#define __CCGL_H__ + +#include "platform/CCPlatformConfig.h" +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + +#define glClearDepth glClearDepthf +#define glDeleteVertexArrays glDeleteVertexArraysOES +#define glGenVertexArrays glGenVertexArraysOES +#define glBindVertexArray glBindVertexArrayOES +#define glMapBuffer glMapBufferOES +#define glUnmapBuffer glUnmapBufferOES + +#define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES +#define GL_WRITE_ONLY GL_WRITE_ONLY_OES + +// GL_GLEXT_PROTOTYPES isn't defined in glplatform.h on android ndk r7 +// we manually define it here +#include +#ifndef GL_GLEXT_PROTOTYPES +#define GL_GLEXT_PROTOTYPES 1 +#endif + +// normal process +#include +#include +// gl2.h doesn't define GLchar on Android +typedef char GLchar; +// android defines GL_BGRA_EXT but not GL_BRGA +#ifndef GL_BGRA +#define GL_BGRA 0x80E1 +#endif + +extern PFNGLGENVERTEXARRAYSOESPROC glGenVertexArraysOESEXT; +extern PFNGLBINDVERTEXARRAYOESPROC glBindVertexArrayOESEXT; +extern PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArraysOESEXT; + +#define glGenVertexArraysOES glGenVertexArraysOESEXT +#define glBindVertexArrayOES glBindVertexArrayOESEXT +#define glDeleteVertexArraysOES glDeleteVertexArraysOESEXT + + +#endif // CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + +#endif // __CCGL_H__ diff --git a/cocos/platform/ohos/CCGLViewImpl-ohos.cpp b/cocos/platform/ohos/CCGLViewImpl-ohos.cpp new file mode 100644 index 000000000000..803fd94058a3 --- /dev/null +++ b/cocos/platform/ohos/CCGLViewImpl-ohos.cpp @@ -0,0 +1,191 @@ +#include "platform/CCPlatformConfig.h" +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS +#include "CCGLViewImpl-ohos.h" +#include "base/CCDirector.h" +#include "base/ccMacros.h" +#include "base/CCIMEDispatcher.h" +#include "napi/helper/Js_Cocos2dxHelper.h" +#include "CCGL-ohos.h" + +#include +#include "CCLogOhos.h" +#include "napi/helper/NapiHelper.h" + + + +//#if CC_TEXTURE_ATLAS_USE_VAO + +// exists since android 2.3 +#include +PFNGLGENVERTEXARRAYSOESPROC glGenVertexArraysOESEXT = 0; +PFNGLBINDVERTEXARRAYOESPROC glBindVertexArrayOESEXT = 0; +PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArraysOESEXT = 0; + +//#endif + +#define DEFAULT_MARGIN_OHOS 30.0f +#define WIDE_SCREEN_ASPECT_RATIO_OHOS 2.0f + +void initExtensions() { +//#if CC_TEXTURE_ATLAS_USE_VAO + glGenVertexArraysOESEXT = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES"); + glBindVertexArrayOESEXT = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES"); + glDeleteVertexArraysOESEXT = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES"); +//#endif +} + +NS_CC_BEGIN + +GLViewImpl* GLViewImpl::createWithRect(const std::string& viewName, Rect rect, float frameZoomFactor) { + auto ret = new GLViewImpl; + if(ret && ret->initWithRect(viewName, rect, frameZoomFactor)) { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; +} + +GLViewImpl* GLViewImpl::create(const std::string& viewName) { + auto ret = new GLViewImpl; + if(ret && ret->initWithFullScreen(viewName)) { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; +} + +GLViewImpl* GLViewImpl::createWithFullScreen(const std::string& viewName) { + auto ret = new GLViewImpl(); + if(ret && ret->initWithFullScreen(viewName)) { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; +} + +GLViewImpl::GLViewImpl() { + initExtensions(); +} + +GLViewImpl::~GLViewImpl() { + +} + +bool GLViewImpl::initWithRect(const std::string& viewName, Rect rect, float frameZoomFactor) { + return true; +} + +bool GLViewImpl::initWithFullScreen(const std::string& viewName) { + return true; +} + +bool GLViewImpl::isOpenGLReady() { + return (_screenSize.width != 0 && _screenSize.height != 0); +} + +void GLViewImpl::end() { + OHOS_LOGD("GLViewImpl terminateProcess"); + Js_Cocos2dxHelper::terminateProcess(); +} + +void GLViewImpl::swapBuffers() { +} + +GLViewImpl* GLViewImpl::sharedOpenGLView() { + static GLViewImpl instance; + return &instance; +} + +void GLViewImpl::setIMEKeyboardState(bool bOpen) { + if (bOpen) { + std::string pszText = cocos2d::IMEDispatcher::sharedDispatcher()->getContentText(); + JSFunction::getFunction("DiaLog.showTextInputDialog").invoke(pszText); + } else { + JSFunction::getFunction("DiaLog.hideTextInputDialog").invoke(); + } +} + +Rect GLViewImpl::getSafeAreaRect() const { + Rect safeAreaRect = GLView::getSafeAreaRect(); + float deviceAspectRatio = 0; + if(safeAreaRect.size.height > safeAreaRect.size.width) { + deviceAspectRatio = safeAreaRect.size.height / safeAreaRect.size.width; + } else { + deviceAspectRatio = safeAreaRect.size.width / safeAreaRect.size.height; + } + + float marginX = DEFAULT_MARGIN_OHOS / _scaleX; + float marginY = DEFAULT_MARGIN_OHOS / _scaleY; + + bool isScreenRound = JSFunction::getFunction("DeviceUtils.isRoundScreen").invoke(); + bool hasSoftKeys = JSFunction::getFunction("DeviceUtils.hasSoftKeys").invoke(); + bool isCutoutEnabled = JSFunction::getFunction("DeviceUtils.isCutoutEnable").invoke(); + + if(isScreenRound) { + // edge screen + if(safeAreaRect.size.width < safeAreaRect.size.height) { + safeAreaRect.origin.y += marginY * 2.f; + safeAreaRect.size.height -= (marginY * 2.f); + + safeAreaRect.origin.x += marginX; + safeAreaRect.size.width -= (marginX * 2.f); + } else { + safeAreaRect.origin.y += marginY; + safeAreaRect.size.height -= (marginY * 2.f); + + // landscape: no changes with X-coords + } + } else if (deviceAspectRatio >= WIDE_SCREEN_ASPECT_RATIO_OHOS) { + // almost all devices on the market have round corners + float bottomMarginIfPortrait = 0; + if(hasSoftKeys) { + bottomMarginIfPortrait = marginY * 2.f; + } + + if(safeAreaRect.size.width < safeAreaRect.size.height) { + // portrait: double margin space if device has soft menu + safeAreaRect.origin.y += bottomMarginIfPortrait; + safeAreaRect.size.height -= (bottomMarginIfPortrait + marginY); + } else { + // landscape: ignore double margin at the bottom in any cases + // prepare signle margin for round corners + safeAreaRect.origin.y += marginY; + safeAreaRect.size.height -= (marginY * 2.f); + } + } else { + if(hasSoftKeys && (safeAreaRect.size.width < safeAreaRect.size.height)) { + // portrait: preserve only for soft system menu + safeAreaRect.origin.y += marginY * 2.f; + safeAreaRect.size.height -= (marginY * 2.f); + } + } + + if (isCutoutEnabled) { + // screen with enabled cutout area + int orientation = JSFunction::getFunction("DeviceUtils.getOrientation").invoke(); + + if(static_cast(GLViewImpl::Orientation::PORTRAIT) == orientation) { + double height = JSFunction::getFunction("DeviceUtils.getCutoutHeight").invoke() / _scaleY; + safeAreaRect.origin.y += height; + safeAreaRect.size.height -= height; + } else if(static_cast(GLViewImpl::Orientation::PORTRAIT_INVERTED) == orientation) { + double height =JSFunction::getFunction("DeviceUtils.getCutoutHeight").invoke() / _scaleY; + safeAreaRect.size.height -= height; + } else if(static_cast(GLViewImpl::Orientation::LANDSCAPE) == orientation) { + double width = JSFunction::getFunction("DeviceUtils.getCutoutWidth").invoke() / _scaleX; + safeAreaRect.size.width -= width; + } else if(static_cast(GLViewImpl::Orientation::LANDSCAPE_INVERTED) == orientation) { + double width = JSFunction::getFunction("DeviceUtils.getCutoutWidth").invoke() / _scaleX; + safeAreaRect.origin.x += width; + safeAreaRect.size.width -= width; + } + } + + return safeAreaRect; +} +NS_CC_END + +#endif // CC_TARGET_PLATFORM == CC_PLATFORM_OHOS diff --git a/cocos/platform/ohos/CCGLViewImpl-ohos.h b/cocos/platform/ohos/CCGLViewImpl-ohos.h new file mode 100644 index 000000000000..2259d0de1796 --- /dev/null +++ b/cocos/platform/ohos/CCGLViewImpl-ohos.h @@ -0,0 +1,51 @@ +#ifndef __CC_EGLVIEWIMPL_OHOS_H__ +#define __CC_EGLVIEWIMPL_OHOS_H__ + +#include "platform/CCPlatformConfig.h" +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS +#include "math/CCGeometry.h" +#include "platform/CCGLView.h" + +NS_CC_BEGIN + +class CC_DLL GLViewImpl : public GLView +{ +public: + enum class Orientation { + PORTRAIT = 0, + LANDSCAPE, + PORTRAIT_INVERTED, + LANDSCAPE_INVERTED, + UNKNOWN + }; + + // static function + static GLViewImpl* create(const std::string &viewname); + static GLViewImpl* createWithRect(const std::string& viewName, Rect rect, float frameZoomFactor = 1.0f); + static GLViewImpl* createWithFullScreen(const std::string& viewName); + + bool isOpenGLReady() override; + + // keep compatible + void end() override; + void swapBuffers() override; + void setIMEKeyboardState(bool bOpen) override; + virtual Rect getSafeAreaRect() const; + + // static function + /** + @brief get the shared main open gl window + */ + static GLViewImpl* sharedOpenGLView(); + +protected: + GLViewImpl(); + virtual ~GLViewImpl(); + + bool initWithRect(const std::string& viewName, Rect rect, float frameZoomFactor); + bool initWithFullScreen(const std::string& viewName); +}; + +NS_CC_END +#endif // end of CC_TARGET_PLATFORM == CC_PLATFORM_OHOS +#endif // end of __CC_EGLVIEW_ANDROID_H__ diff --git a/cocos/platform/ohos/CCImage.cpp b/cocos/platform/ohos/CCImage.cpp new file mode 100644 index 000000000000..f63bcc97ee48 --- /dev/null +++ b/cocos/platform/ohos/CCImage.cpp @@ -0,0 +1,274 @@ +/**************************************************************************** +Copyright (c) 2010 cocos2d-x.org + +http://www.cocos2d-x.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +****************************************************************************/ + +//#define COCOS2D_DEBUG 1 + +#include +#include +#include +#define __CC_PLATFORM_IMAGE_CPP__ +//#include "platform/CCImageCommon_cpp.h" +#include "platform/CCPlatformMacros.h" +#include "platform/CCImage.h" +#include "platform/CCFileUtils.h" +#include "CCTextBitmap.h" + +#include +// #include + +// prototype +void swapAlphaChannel(unsigned int *pImageMemory, unsigned int numPixels); + +NS_CC_BEGIN + +static CCTextBitmap* cCtextBitmap = nullptr; + +class BitmapDC +{ +public: + + BitmapDC() + : m_nWidth(0) + , m_nHeight(0) + , m_pData(NULL) + { + } + + ~BitmapDC(void) + { + if (m_pData) + { + delete [] m_pData; + } + } + + static BitmapDC& sharedBitmapDC() + { + static BitmapDC s_BmpDC; + return s_BmpDC; + } + + bool getBitmapFromJavaShadowStroke( const char *text, + int nWidth, + int nHeight, + Image::ETextAlign eAlignMask, + const char * pFontName, + float fontSize, + float textTintR = 1.0, + float textTintG = 1.0, + float textTintB = 1.0, + bool shadow = false, + float shadowDeltaX = 0.0, + float shadowDeltaY = 0.0, + float shadowBlur = 0.0, + float shadowIntensity = 0.0, + bool stroke = false, + float strokeColorR = 0.0, + float strokeColorG = 0.0, + float strokeColorB = 0.0, + float strokeSize = 0.0 ) + { + if (cCtextBitmap) { + delete(cCtextBitmap); + cCtextBitmap = nullptr; + } + cCtextBitmap = CCTextBitmap::createCCTextBitmap(text, pFontName, eAlignMask, nWidth, nHeight, fontSize); + void* pixels = cCtextBitmap->getPixelAddr(); + cocos2d::BitmapDC& bitmapDC = sharedBitmapDC(); + bitmapDC.m_nWidth = cCtextBitmap->GetWidth(); + bitmapDC.m_nHeight = cCtextBitmap->GetHeight(); + bitmapDC.m_pData = (unsigned char*)pixels; + + unsigned int *tempPtr = (unsigned int*)bitmapDC.m_pData; + unsigned int tempdata = 0; + for (int i = 0; i < cCtextBitmap->GetHeight(); ++i) + { + for (int j = 0; j < cCtextBitmap->GetWidth(); ++j) + { + tempdata = *tempPtr; + *tempPtr++ = bitmapDC.swapAlpha(tempdata); + } + } + + + return true; + } + + + bool getBitmapFromJava(const char *text, int nWidth, int nHeight, Image::ETextAlign eAlignMask, const char * pFontName, float fontSize) + { + return getBitmapFromJavaShadowStroke( text, nWidth, nHeight, eAlignMask, pFontName, fontSize ); + } + + // ARGB -> RGBA + inline unsigned int swapAlpha(unsigned int value) + { + return ((value << 8 & 0xffffff00) | (value >> 24 & 0x000000ff)); + } + +public: + int m_nWidth; + int m_nHeight; + unsigned char *m_pData; + // JNIEnv *env; +}; + + + +bool Image::initWithString( + const char * pText, + int nWidth/* = 0*/, + int nHeight/* = 0*/, + ETextAlign eAlignMask/* = kAlignCenter*/, + const char * pFontName/* = nil*/, + int nSize/* = 0*/) +{ + bool bRet = false; + + do + { +// CC_BREAK_IF(! pText); +// +// BitmapDC &dc = BitmapDC::sharedBitmapDC(); +// +// CC_BREAK_IF(! dc.getBitmapFromJava(pText, nWidth, nHeight, eAlignMask, pFontName, nSize)); +// +// // assign the dc.m_pData to m_pData in order to save time +// m_pData = dc.m_pData; +// CC_BREAK_IF(! m_pData); +// +// m_nWidth = (short)dc.m_nWidth; +// m_nHeight = (short)dc.m_nHeight; +// m_bHasAlpha = true; +// m_bPreMulti = true; +// m_nBitsPerComponent = 8; + + bRet = true; + } while (0); + + return bRet; +} + +bool Image::initWithStringShadowStroke( + const char * pText, + int nWidth , + int nHeight , +// ETextAlign eAlignMask , + const char * pFontName , + int nSize , + float textTintR, + float textTintG, + float textTintB, + bool shadow, + float shadowOffsetX, + float shadowOffsetY, + float shadowOpacity, + float shadowBlur, + bool stroke, + float strokeR, + float strokeG, + float strokeB, + float strokeSize) +{ + bool bRet = false; + do + { + CC_BREAK_IF(! pText); + + BitmapDC &dc = BitmapDC::sharedBitmapDC(); + + + CC_BREAK_IF(! dc.getBitmapFromJavaShadowStroke(pText, nWidth, nHeight, eAlignMask, pFontName, + nSize, textTintR, textTintG, textTintB, shadow, + shadowOffsetX, shadowOffsetY, shadowBlur, shadowOpacity, + stroke, strokeR, strokeG, strokeB, strokeSize )); + + + // assign the dc.m_pData to m_pData in order to save time +// m_pData = dc.m_pData; +// +// CC_BREAK_IF(! m_pData); +// +// m_nWidth = (short)dc.m_nWidth; +// m_nHeight = (short)dc.m_nHeight; +// m_bHasAlpha = true; +// m_bPreMulti = true; +// m_nBitsPerComponent = 8; +// +// // swap the alpha channel (ARGB to RGBA) +// swapAlphaChannel((unsigned int *)m_pData, (m_nWidth * m_nHeight) ); + + // ok + bRet = true; + + } while (0); + + return bRet; +} + +NS_CC_END + +// swap the alpha channel in an 32 bit image (from ARGB to RGBA) +void swapAlphaChannel(unsigned int *pImageMemory, unsigned int numPixels) +{ + for(int c = 0; c < numPixels; ++c, ++pImageMemory) + { + // copy the current pixel + unsigned int currenPixel = (*pImageMemory); + // swap channels and store back + unsigned char *pSource = (unsigned char *) ¤Pixel; + *pImageMemory = (pSource[0] << 24) | (pSource[3]<<16) | (pSource[2]<<8) | pSource[1]; + } +} + +// this method is called by Cocos2dxBitmap +extern "C" +{ + /** + * this method is called by java code to init width, height and pixels data + */ + /*JNIEXPORT*/ void /*JNICALL*/ Java_org_cocos2dx_lib_Cocos2dxBitmap_nativeInitBitmapDC(/*JNIEnv* env, jobject thiz, int width, int height, jbyteArray pixels */) + { + /* + int size = width * height * 4; + cocos2d::BitmapDC& bitmapDC = cocos2d::sharedBitmapDC(); + bitmapDC.m_nWidth = width; + bitmapDC.m_nHeight = height; + bitmapDC.m_pData = new unsigned char[size]; + env->GetByteArrayRegion(pixels, 0, size, (jbyte*)bitmapDC.m_pData); + + // swap data + unsigned int *tempPtr = (unsigned int*)bitmapDC.m_pData; + unsigned int tempdata = 0; + for (int i = 0; i < height; ++i) + { + for (int j = 0; j < width; ++j) + { + tempdata = *tempPtr; + *tempPtr++ = bitmapDC.swapAlpha(tempdata); + } + } + */ + } +}; diff --git a/cocos/platform/ohos/CCJsAudioEngine.cpp b/cocos/platform/ohos/CCJsAudioEngine.cpp new file mode 100644 index 000000000000..e0b5734a7c8d --- /dev/null +++ b/cocos/platform/ohos/CCJsAudioEngine.cpp @@ -0,0 +1,586 @@ +// +// Created on 2022/11/29. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". +#include "CCJsAudioEngine.h" +#include "CCLogOhos.h" + +#include +#include +#include +NS_CC_BEGIN +napi_env CCJsAudioEngine::_env = nullptr; + + +napi_value CCJsAudioEngine::initJsAudioEngine(napi_env env, napi_value exports) +{ + OHOS_LOGD("initJsAudioEngine start!"); + _env = env; + return 0; +} + +void CCJsAudioEngine::preLoadBackgroundMusic(std::string& audioUri) +{ + OHOS_LOGD("JsAudioEngine preLoadBackMusic start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::preLoadBackMusic get global failed! status:%d", status); + return; + } + napi_value preloadMusicFunc = getEngineFunc("preloadBackgroundMusic"); + if (preloadMusicFunc == nullptr) { + return; + } + napi_value argUri; + napi_create_string_utf8(_env, audioUri.data(), audioUri.length() ,&argUri); + napi_value args[] = {argUri}; + napi_value return_val; + status = napi_call_function(_env, global, preloadMusicFunc, 1, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::preLoadBackMusic napi_call_function failed! status:%d", status); + return; + } +} + + +void CCJsAudioEngine::playBackgroundMusic(std::string& audioUri, bool bLoop) +{ + playBackgroundMusic(audioUri, -1, bLoop); +} +void CCJsAudioEngine::playBackgroundMusic(std::string& audioUri, int seek, bool bLoop) +{ + OHOS_LOGD("JsAudioEngine playBackgroundMusic start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::playBackgroundMusic get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("playBackgroundMusic"); + if (playFunc == nullptr) { + return; + } + + std::vector args; + + napi_value argUri; + napi_create_string_utf8(_env, audioUri.data(), audioUri.length() ,&argUri); + args.push_back(argUri); + napi_value argSeek; + napi_create_int32(_env, seek, &argSeek); + args.push_back(argSeek); + napi_value argStartPlay; + napi_create_int32(_env, 1, &argStartPlay); + args.push_back(argStartPlay); + napi_value argLoop; + napi_get_boolean(_env, bLoop, &argLoop); + args.push_back(argLoop); + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::playBackgroundMusic napi_call_function failed! status:%d", status); + return; + } else { + OHOS_LOGE("CCJsAudioEngine::playBackgroundMusic napi_call_function success! status:%d", status); + } + +} + +void CCJsAudioEngine::stopBackgroundMusic(bool bReleaseData) +{ + OHOS_LOGD("JsAudioEngine stopBackgroundMusic start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::stopBackgroundMusic get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("stopBackgroundMusic"); + if (playFunc == nullptr) { + return; + } + napi_value argRelease; + napi_get_boolean(_env, bReleaseData, &argRelease); + + napi_value args[1] = {argRelease}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 1, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::stopBackgroundMusic napi_call_function failed! status:%d", status); + return; + } +} + +void CCJsAudioEngine::pauseBackgroundMusic() +{ + OHOS_LOGD("JsAudioEngine pauseBackgroundMusic start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::pauseBackgroundMusic get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("pauseBackgroundMusic"); + if (playFunc == nullptr) { + return; + } + + napi_value args[0] = {}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 0, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::pauseBackgroundMusic napi_call_function failed! status:%d", status); + return; + } +} + +void CCJsAudioEngine::resumeBackgroundMusic() +{ + OHOS_LOGD("JsAudioEngine resumeBackgroundMusic start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::resumeBackgroundMusic get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("resumeBackgroundMusic"); + if (playFunc == nullptr) { + return; + } + + napi_value args[0] = {}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 0, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::pauseBackgroundMusic napi_call_function failed! status:%d", status); + return; + } +} + +void CCJsAudioEngine::rewindBackgroundMusic() +{ + // seek 到 0 后重新播放。先暂停,然后重新播放 +// pauseBackgroundMusic(); + seekBackgroundMusic(0); + std::string uri("hello"); + playBackgroundMusic(uri, 0); +} + +void CCJsAudioEngine::seekBackgroundMusic(int seek) +{ + OHOS_LOGD("JsAudioEngine seekBackgroundMusic start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::seekBackgroundMusic get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("seekBackgroundMusic"); + if (playFunc == nullptr) { + return; + } + + napi_value argSeek; + napi_create_int32(_env, seek, &argSeek); + napi_value args[1] = {argSeek}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 1, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::seekBackgroundMusic napi_call_function failed! status:%d", status); + return; + } +} + + +float CCJsAudioEngine::getBackgroundMusicVolume() +{ + OHOS_LOGD("JsAudioEngine getBackgroundMusicVolume start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::getBackgroundMusicVolume get global failed! status:%d", status); + return DEFAULT_VOLUME; + } + napi_value playFunc = getEngineFunc("getBackgroundVolume"); + if (playFunc == nullptr) { + return DEFAULT_VOLUME; + } + + napi_value args[0] = {}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 0, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::getBackgroundMusicVolume napi_call_function failed! status:%d", status); + return DEFAULT_VOLUME; + } + double result; + napi_get_value_double(_env, return_val, &result); + return result; +} + +void CCJsAudioEngine::setBackgroundMusicVolume(const float volume) +{ + OHOS_LOGD("JsAudioEngine setBackgroundMusicVolume start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::setBackgroundMusicVolume get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("setBackgroundVolume"); + if (playFunc == nullptr) { + return; + } + + napi_value napiVolume; + napi_create_double(_env, volume, &napiVolume); + napi_value args[1] = {napiVolume}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 1, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::setBackgroundMusicVolume napi_call_function failed! status:%d", status); + return; + } +} + +float CCJsAudioEngine::getEffectsVolume() +{ + OHOS_LOGD("JsAudioEngine getEffectsVolume start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::getEffectsVolume get global failed! status:%d", status); + return DEFAULT_VOLUME; + } + napi_value playFunc = getEngineFunc("getEffectsVolume"); + if (playFunc == nullptr) { + return DEFAULT_VOLUME; + } + + napi_value args[0] = {}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 0, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::getEffectsVolume napi_call_function failed! status:%d", status); + return DEFAULT_VOLUME; + } + double result; + napi_get_value_double(_env, return_val, &result); + return result; +} + +void CCJsAudioEngine::setEffectsVolume(float volume) +{ + OHOS_LOGD("JsAudioEngine setEffectsVolume start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::setEffectsVolume get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("setEffectsVolume"); + if (playFunc == nullptr) { + return; + } + + napi_value napiVolume; + napi_create_double(_env, volume, &napiVolume); + napi_value args[1] = {napiVolume}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 1, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::setEffectsVolume napi_call_function failed! status:%d", status); + return; + } +} + +unsigned int CCJsAudioEngine::playEffect(const char* pszFilePath, bool bLoop) +{ + OHOS_LOGD("JsAudioEngine playEffect start!"); + std::string audioUri(pszFilePath); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::playEffect get global failed! status:%d", status); + return 0; + } + napi_value playFunc = getEngineFunc("playEffect"); + if (playFunc == nullptr) { + return 0; + } + + std::vector args; + napi_value argUri; + napi_create_string_utf8(_env, audioUri.data(), audioUri.length() ,&argUri); + napi_value argLoop; + napi_get_boolean(_env, bLoop, &argLoop); + args.push_back(argUri); + args.push_back(argLoop); + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::playEffect napi_call_function failed! status:%d", status); + return 0; + } else { + OHOS_LOGE("CCJsAudioEngine::playEffect napi_call_function success! status:%d", status); + } + unsigned int result; + napi_get_value_uint32(_env, return_val, &result); + return result; +} + +void CCJsAudioEngine::pauseEffect(unsigned int nSoundId) +{ + OHOS_LOGD("JsAudioEngine pauseEffect start! nSoundId:%d", nSoundId); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::pauseEffect get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("pauseEffect"); + if (playFunc == nullptr) { + return; + } + + std::vector args; + napi_value argSoundId; + napi_create_uint32(_env, nSoundId, &argSoundId); + args.push_back(argSoundId); + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::pauseEffect napi_call_function failed! status:%d", status); + return; + } +} + +void CCJsAudioEngine::pauseAllEffects() +{ + OHOS_LOGD("JsAudioEngine pauseAllEffects start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::pauseAllEffects get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("pauseAllEffects"); + if (playFunc == nullptr) { + return; + } + + std::vector args; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::pauseEffect napi_call_function failed! status:%d", status); + return; + } +} + +void CCJsAudioEngine::resumeEffect(unsigned int nSoundId) +{ + OHOS_LOGD("JsAudioEngine resumeEffect start! nSoundId:%d", nSoundId); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::resumeEffect get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("resumeEffect"); + if (playFunc == nullptr) { + return; + } + + std::vector args; + napi_value argSoundId; + napi_create_uint32(_env, nSoundId, &argSoundId); + args.push_back(argSoundId); + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::pauseBackgroundMusic napi_call_function failed! status:%d", status); + return; + } +} + +void CCJsAudioEngine::resumeAllEffects() +{ + OHOS_LOGD("JsAudioEngine resumeAllEffects start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::resumeAllEffects get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("resumeAllEffects"); + if (playFunc == nullptr) { + return; + } + + std::vector args; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::resumeAllEffects napi_call_function failed! status:%d", status); + return; + } +} + +void CCJsAudioEngine::stopEffect(unsigned int nSoundId) +{ + OHOS_LOGD("JsAudioEngine stopEffect start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::stopEffect get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("stopEffect"); + if (playFunc == nullptr) { + return; + } + + std::vector args; + napi_value argSoundId; + napi_create_uint32(_env, nSoundId, &argSoundId); + args.push_back(argSoundId); + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::stopEffect napi_call_function failed! status:%d", status); + return; + } +} + +void CCJsAudioEngine::stopAllEffects() +{ + OHOS_LOGD("JsAudioEngine stopAllEffects start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::stopAllEffects get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("stopAllEffects"); + if (playFunc == nullptr) { + return; + } + + std::vector args; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::stopAllEffects napi_call_function failed! status:%d", status); + return; + } +} + +void CCJsAudioEngine::preloadEffect(const char* pszFilePath) +{ + OHOS_LOGD("JsAudioEngine preLoadBackMusic start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::preLoadBackMusic get global failed! status:%d", status); + return; + } + napi_value preloadEffectFunc = getEngineFunc("preloadEffect"); + if (preloadEffectFunc == nullptr) { + return; + } + + std::vector args; + std::string uri(pszFilePath); + napi_value argUri; + napi_create_string_utf8(_env, uri.data(), uri.length() ,&argUri); + args.push_back(argUri); + + napi_value return_val; + status = napi_call_function(_env, global, preloadEffectFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::preLoadBackMusic napi_call_function failed! status:%d", status); + return; + } +} + +void CCJsAudioEngine::unloadEffect(const char* pszFilePath) +{ + OHOS_LOGD("JsAudioEngine unloadEffect start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::unloadEffect get global failed! status:%d", status); + return; + } + napi_value func = getEngineFunc("unloadEffect"); + if (func == nullptr) { + return; + } + + std::vector args; + std::string uri(pszFilePath); + napi_value argUri; + napi_create_string_utf8(_env, uri.data(), uri.length() ,&argUri); + args.push_back(argUri); + + napi_value return_val; + status = napi_call_function(_env, global, func, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("CCJsAudioEngine::unloadEffect napi_call_function failed! status:%d", status); + return; + } +} + +napi_value CCJsAudioEngine::getEngineFunc(const char* funcName) +{ + // Get the function named "AddTwo" on the global object + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("%s get global failed!", funcName); + return nullptr; + } + + napi_value cocosNs; + status = napi_get_named_property(_env, global, "ccCpp2Js", &cocosNs); + if (status != napi_ok) { + OHOS_LOGE("%s get cocosNs failed!", funcName); + return nullptr; + } + + napi_value func; + status = napi_get_named_property(_env, cocosNs, funcName, &func); + if (status != napi_ok) { + OHOS_LOGE("%s get func failed!", funcName); + return nullptr; + } + + napi_valuetype functype; + napi_typeof(_env, func, &functype); + if (functype != napi_function) { + OHOS_LOGE("%s get func but is not a function", funcName); + return nullptr; + } + return func; +} + +NS_CC_END \ No newline at end of file diff --git a/cocos/platform/ohos/CCJsAudioEngine.h b/cocos/platform/ohos/CCJsAudioEngine.h new file mode 100644 index 000000000000..5c96482cc146 --- /dev/null +++ b/cocos/platform/ohos/CCJsAudioEngine.h @@ -0,0 +1,48 @@ +// +// Created on 2022/11/29. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". + +#ifndef JsAudioEngine_H +#define JsAudioEngine_H + +#include +#include +#include "platform/CCPlatformMacros.h" + +NS_CC_BEGIN +class CCJsAudioEngine{ +public: + constexpr static const float DEFAULT_VOLUME = 0.5f; + static napi_value initJsAudioEngine(napi_env env, napi_value exports); + static void preLoadBackgroundMusic(std::string& audioUri); + static void playBackgroundMusic(std::string& audioUri, int seek, bool bLoop); + static void playBackgroundMusic(std::string& audioUri, bool bLoop); + static void stopBackgroundMusic(bool bReleaseData); + static void pauseBackgroundMusic(); + static void resumeBackgroundMusic(); + static void rewindBackgroundMusic(); + static void seekBackgroundMusic(int seek); + static float getBackgroundMusicVolume(); + static void setBackgroundMusicVolume(const float volume); + + static float getEffectsVolume(); + static void setEffectsVolume(float volume); + static unsigned int playEffect(const char* pszFilePath, bool bLoop); + static void pauseEffect(unsigned int nSoundId); + static void pauseAllEffects(); + static void resumeEffect(unsigned int nSoundId); + static void resumeAllEffects(); + static void stopEffect(unsigned int nSoundId); + static void stopAllEffects(); + static void preloadEffect(const char* pszFilePath); + static void unloadEffect(const char* pszFilePath); + + + static napi_value getEngineFunc(const char* funcName); +private: + static napi_env _env; +}; +#endif //XComponent_JsAudioEngine_H +NS_CC_END \ No newline at end of file diff --git a/cocos/platform/ohos/CCLogOhos.h b/cocos/platform/ohos/CCLogOhos.h new file mode 100644 index 000000000000..5df068fa3b76 --- /dev/null +++ b/cocos/platform/ohos/CCLogOhos.h @@ -0,0 +1,121 @@ +#ifndef DISP_COMMON_H +#define DISP_COMMON_H +#include +#include +#include +#ifdef HDF_LOG_TAG +#undef HDF_LOG_TAG +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#define APP_LOG_DOMAIN 0x0001 +#define APP_LOG_TAG "cocos" +#define OHOS_LOGI(...) ((void)OH_LOG_Print(LOG_APP, LOG_INFO, APP_LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) +#define OHOS_LOGD(...) ((void)OH_LOG_Print(LOG_APP, LOG_DEBUG, APP_LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) +#define OHOS_LOGW(...) ((void)OH_LOG_Print(LOG_APP, LOG_WARN, APP_LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) +#define OHOS_LOGE(...) ((void)OH_LOG_Print(LOG_APP, LOG_ERROR, APP_LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) + +#if 0 +#undef LOG_TAG +#undef LOG_DOMAIN +#define APP_LOG_DOMAIN 0x0001 +#define APP_LOG_TAG "cocos" + +#ifndef DISPLAY_UNUSED +#define DISPLAY_UNUSED(x) (void)x +#endif + +#define __FILENAME__ (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1) : __FILE__) + +#ifndef DISPLAY_DEBUG_ENABLE +#define DISPLAY_DEBUG_ENABLE 1 + +#endif + +#ifndef OHOS_LOGD +#define OHOS_LOGD(format, ...) \ + do { \ + if (DISPLAY_DEBUG_ENABLE) { \ + OH_LOG_Print(LOG_APP, LOG_INFO, APP_LOG_DOMAIN, "[%{public}s@%{public}s:%{public}d] " format "\n", \ + __FUNCTION__, __FILENAME__, __LINE__, \ + ##__VA_ARGS__); \ + } \ + } while (0) +#endif + +#ifndef OHOS_LOGI +#define OHOS_LOGI(format, ...) \ + do { \ + OH_LOG_Print(LOG_APP, LOG_INFO, APP_LOG_DOMAIN, "[%{public}s@%{public}s:%{public}d] " format "\n", __FUNCTION__, __FILENAME__, __LINE__, \ + ##__VA_ARGS__); \ + } while (0) +#endif + +#ifndef OHOS_LOGW +#define OHOS_LOGW(format, ...) \ + do { \ + OH_LOG_Print(LOG_APP, LOG_INFO, APP_LOG_DOMAIN, "[%{public}s@%{public}s:%{public}d] " format "\n", __FUNCTION__, __FILENAME__, __LINE__, \ + ##__VA_ARGS__); \ + } while (0) +#endif + +#ifndef OHOS_LOGE +#define OHOS_LOGE(format, ...) \ + do { \ + OH_LOG_Print(LOG_APP, LOG_INFO, APP_LOG_DOMAIN, \ + "\033[0;32;31m" \ + "[%{public}s@%{public}s:%{public}d] " format "\033[m" \ + "\n", \ + __FUNCTION__, __FILENAME__, __LINE__, ##__VA_ARGS__); \ + } while (0) +#endif + +#ifndef CHECK_NULLPOINTER_RETURN_VALUE +#define CHECK_NULLPOINTER_RETURN_VALUE(pointer, ret) \ + do { \ + if ((pointer) == NULL) { \ + OHOS_LOGE("pointer is null and return ret\n"); \ + return (ret); \ + } \ + } while (0) +#endif + +#ifndef CHECK_NULLPOINTER_RETURN +#define CHECK_NULLPOINTER_RETURN(pointer) \ + do { \ + if ((pointer) == NULL) { \ + OHOS_LOGE("pointer is null and return\n"); \ + return; \ + } \ + } while (0) +#endif + +#ifndef DISPLAY_CHK_RETURN +#define DISPLAY_CHK_RETURN(val, ret, ...) \ + do { \ + if (val) { \ + __VA_ARGS__; \ + return (ret); \ + } \ + } while (0) +#endif + +#ifndef DISPLAY_CHK_RETURN_NOT_VALUE +#define DISPLAY_CHK_RETURN_NOT_VALUE(val, ...) \ + do { \ + if (val) { \ + __VA_ARGS__; \ + return; \ + } \ + } while (0) +#endif + +#endif +#ifdef __cplusplus +} +#endif + +#endif /* DISP_COMMON_H */ diff --git a/cocos/platform/ohos/CCPlatformDefine-ohos.h b/cocos/platform/ohos/CCPlatformDefine-ohos.h new file mode 100644 index 000000000000..15c8c9382552 --- /dev/null +++ b/cocos/platform/ohos/CCPlatformDefine-ohos.h @@ -0,0 +1,49 @@ +#ifndef __CCPLATFORMDEFINE_H__ +#define __CCPLATFORMDEFINE_H__ + +#include "platform/CCPlatformConfig.h" +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS +#include "CCLogOhos.h" + +#define CC_DLL + +#define CC_NO_MESSAGE_PSEUDOASSERT(cond) \ + if (!(cond)) { \ + OHOS_LOGI("[cocos2d-x assert] %s function:%s line:%d", __FILE__, __FUNCTION__, __LINE__); \ + } + +#define CC_MESSAGE_PSEUDOASSERT(cond, msg) \ + if (!(cond)) { \ + OHOS_LOGI("[cocos2d-x assert] file:%s function:%s line:%d, %s", __FILE__, __FUNCTION__, __LINE__, msg); \ + } + + +#define CC_ASSERT(cond) CC_NO_MESSAGE_PSEUDOASSERT(cond) + +#define CC_UNUSED_PARAM(unusedparam) (void)unusedparam + +#define CC_AUDIO_SAFE_DELETE(p) do { if(p) { delete (p); (p) = 0; } } while(0) +/* Define NULL pointer value */ +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + +#if defined(_MSC_VER) +#define CC_FORCE_INLINE __forceinline +#elif defined(__GNUC__) || defined(__clang__) +#define CC_FORCE_INLINE inline __attribute__((always_inline)) +#else +#if defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +#define CC_FORCE_INLINE static inline +#elif +#define CC_FORCE_INLINE inline +#endif +#endif + +#endif // CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + +#endif /* __CCPLATFORMDEFINE_H__*/ diff --git a/cocos/platform/ohos/CCStdC-ohos.h b/cocos/platform/ohos/CCStdC-ohos.h new file mode 100644 index 000000000000..951ab8d92a5b --- /dev/null +++ b/cocos/platform/ohos/CCStdC-ohos.h @@ -0,0 +1,30 @@ +#ifndef __CC_STD_C_H__ +#define __CC_STD_C_H__ + +#include "platform/CCPlatformConfig.h" +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + +#include "platform/CCPlatformMacros.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MIN +#define MIN(x,y) (((x) > (y)) ? (y) : (x)) +#endif // MIN + +#ifndef MAX +#define MAX(x,y) (((x) < (y)) ? (y) : (x)) +#endif // MAX + +#endif // CC_TARGET_PLATFORM == CC_PLATFORM_OHOS + +#endif // __CC_STD_C_H__ diff --git a/cocos/platform/ohos/CCTextBitmap.cpp b/cocos/platform/ohos/CCTextBitmap.cpp new file mode 100644 index 000000000000..4aed9d6f3db2 --- /dev/null +++ b/cocos/platform/ohos/CCTextBitmap.cpp @@ -0,0 +1,179 @@ +#include +#include "CCTextBitmap.h" +#include "platform/CCPlatformMacros.h" +#include "platform/CCCommon.h" +#include "platform/ohos/napi/helper/NapiHelper.h" + +NS_CC_BEGIN + +void CCTextBitmap::createCCTextBitmap(CCTextBitmap* cCTextBitmap, const char *text, const char *pFontName, const float a, const float r, const float g, const float b, + const Device::TextAlign eAlignMask, int width_, int height_, double fontSize) { + createCCTextBitmap(cCTextBitmap, text, pFontName, fontSize, a, r, g, b, eAlignMask, width_, height_, false, 1, 1, 1, false, 1, 1, 1, 1); +} + +double CCTextBitmap::calxStartPosition(int pAlignment, int layoutWidth, int realWidth, int textWidth) { + if (pAlignment == TEXT_ALIGN_LEFT) { + return 0; + } + if (pAlignment == TEXT_ALIGN_CENTER) { + // Move to the leftmost part of the text, and then add the margin between the text and the actual rendering position. Note that the content drawn from the drawing moves to the left using the - position and to the right using the + position. + return (-(layoutWidth - realWidth) / 2) + ((textWidth - realWidth) / 2); + } + + if (pAlignment == TEXT_ALIGN_RIGHT) { + return -(layoutWidth - textWidth); + } + return 0; +} +double CCTextBitmap::calyStartPosition(int pAlignment, int realHeight, int textHeight) { + const int pVerticalAlignment = (pAlignment >> 4) & 0x0F; + int y = 0; + switch (pVerticalAlignment) { + case VERTICALALIGN_TOP: + y = 0; + break; + case VERTICALALIGN_CENTER: + y = (textHeight - realHeight) / 2; + break; + case VERTICALALIGN_BOTTOM: + y = textHeight - realHeight; + break; + default: + break; + } + + return y; +} +int CCTextBitmap::processTextAlign(int pAlignment) { + const int horizontalAlignment = pAlignment & 0x0F; + int align = TEXT_ALIGN_LEFT; + switch (horizontalAlignment) { + case HORIZONTALALIGN_CENTER: + align = TEXT_ALIGN_CENTER; + break; + case HORIZONTALALIGN_RIGHT: + align = TEXT_ALIGN_RIGHT; + break; + case HORIZONTALALIGN_LEFT: + default: + align = TEXT_ALIGN_LEFT; + break; + } + return align; +} + +void CCTextBitmap::createCCTextBitmap(CCTextBitmap* cCTextBitmap, const char *text, const char *pFontName, const int fontSize, + const float fontTintA, const float fontTintR, const float fontTintG, const float fontTintB, + const Device::TextAlign eAlignMask, const int pWidth, const int pHeight, const bool shadow, + const float shadowDX, const float shadowDY, const float shadowBlur, const bool stroke, + const float strokeR, const float strokeG, const float strokeB, const float strokeSize) { + // Manages typographical styles, such as text orientation. + cCTextBitmap->_typographyStyle = OH_Drawing_CreateTypographyStyle(); + // Set the text to be displayed from left to right. + OH_Drawing_SetTypographyTextDirection(cCTextBitmap->_typographyStyle, TEXT_DIRECTION_LTR); + int align = processTextAlign((int)eAlignMask); + // Set text alignment + OH_Drawing_SetTypographyTextAlign(cCTextBitmap->_typographyStyle, align); + // Used to load fonts + cCTextBitmap->_fontCollection = OH_Drawing_CreateFontCollection(); + // Creates a pointer to the OH_Drawing_TypographyCreate object + cCTextBitmap->_typographyCreate = OH_Drawing_CreateTypographyHandler(cCTextBitmap->_typographyStyle, + cCTextBitmap->_fontCollection); + // Used to manage font colors, decorations, etc. + cCTextBitmap->_textStyle = OH_Drawing_CreateTextStyle(); + + // Set Text Color + OH_Drawing_SetTextStyleColor(cCTextBitmap->_textStyle, OH_Drawing_ColorSetArgb(fontTintA, fontTintR, fontTintG, fontTintB)); + + // Set text size + OH_Drawing_SetTextStyleFontSize(cCTextBitmap->_textStyle, fontSize == 0 ? DEFAULT_FONTSIZE : fontSize) ; + // Set word weight + OH_Drawing_SetTextStyleFontWeight(cCTextBitmap->_textStyle, FONT_WEIGHT_400); + // Set the font baseline position. TEXT_BASELINE_ALPHABotic is used to display phonetic characters and the baseline position is lower in the middle. TEXT_BASELINE_IDEOGRAPHIC for ideographic text with baseline at bottom + OH_Drawing_SetTextStyleBaseLine(cCTextBitmap->_textStyle, TEXT_BASELINE_ALPHABETIC); + // Set font height + OH_Drawing_SetTextStyleFontHeight(cCTextBitmap->_textStyle, 1); + const char *fontFamilies[] = {pFontName}; + // Set the font type + OH_Drawing_SetTextStyleFontFamilies(cCTextBitmap->_textStyle, 1, fontFamilies); + // Set the font style. The font style is not italicized. FONT_EVEN_ITALIC Italic + OH_Drawing_SetTextStyleFontStyle(cCTextBitmap->_textStyle, FONT_STYLE_NORMAL); + // Setting the Language Area + OH_Drawing_SetTextStyleLocale(cCTextBitmap->_textStyle, "en"); + + // Set the typesetting style + OH_Drawing_TypographyHandlerPushTextStyle(cCTextBitmap->_typographyCreate, cCTextBitmap->_textStyle); + // Set text content + OH_Drawing_TypographyHandlerAddText(cCTextBitmap->_typographyCreate, text); + // Typesetting pop-up + OH_Drawing_TypographyHandlerPopTextStyle(cCTextBitmap->_typographyCreate); + + // Used to create OH_Drawing_Typography, which is used to manage the layout and display of typesetting. + cCTextBitmap->_typography = OH_Drawing_CreateTypography(cCTextBitmap->_typographyCreate); + + // The input width of the outer layer is preferentially used. If the input width is not used, the calculated width is used. + int layoutWidth = pWidth; + if (pWidth == 0) { + // If there is no width set, assume a maximum width and calculate the actual width as the layout width + OH_Drawing_TypographyLayout(cCTextBitmap->_typography, 100000); + layoutWidth = OH_Drawing_TypographyGetMaxIntrinsicWidth(cCTextBitmap->_typography) + 1; + } + // typographic layout, setting maximum text width + OH_Drawing_TypographyLayout(cCTextBitmap->_typography, layoutWidth); + + // Obtains the maximum inherent width. + int realWidth = OH_Drawing_TypographyGetMaxIntrinsicWidth(cCTextBitmap->_typography); + // Obtaining the height + int realHeight = OH_Drawing_TypographyGetHeight(cCTextBitmap->_typography); + int textWidth = pWidth != 0 ? pWidth : realWidth; + int textHeight = pHeight != 0 ? pHeight : realHeight; + + // Format used to describe the bit pixel, including color type and transparency type. + cCTextBitmap->_bitmap = OH_Drawing_BitmapCreate(); + // COLOR_FORMAT_RGBA_8888Each pixel is represented by a 32-bit quantity. 8 bits indicate transparency, 8 bits indicate red, 8 bits indicate green, and 8 bits indicate blue. + // ALPHA_FORMAT_OPAQUEBitmap has no transparency + OH_Drawing_BitmapFormat cFormat = {COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_OPAQUE}; + + // Initializes the width and height of the bitmap object, and sets the pixel format for the bitmap. + OH_Drawing_BitmapBuild(cCTextBitmap->_bitmap, textWidth, textHeight , &cFormat); + + // Create a canvas object + cCTextBitmap->_canvas = OH_Drawing_CanvasCreate(); + // Bind a bitmap object to the canvas so that the content drawn on the canvas is output to the bitmap (i.e., CPU rendering). + OH_Drawing_CanvasBind(cCTextBitmap->_canvas, cCTextBitmap->_bitmap); + + double xStart = calxStartPosition(align, layoutWidth, realWidth, textWidth); + double yStart = calyStartPosition((int)eAlignMask, realHeight, textHeight); + double position[2] = {xStart, yStart}; + // Uses the specified color to clear the canvas. OH_Drawing_ColorSetArgb: Converts four variables (respectively describing transparency, red, green, and blue) to a 32-bit (ARGB) variable that describes colors. + OH_Drawing_CanvasClear(cCTextBitmap->_canvas, OH_Drawing_ColorSetArgb(0x00, 0xFF, 0x00, 0x00)); + // Display Text + OH_Drawing_TypographyPaint(cCTextBitmap->_typography, cCTextBitmap->_canvas, position[0], position[1]); + + constexpr uint32_t stride = 4; + int32_t addrSize = pWidth * pHeight * stride; + // Obtains the pixel address of a specified bitmap. The pixel data of the bitmap can be obtained based on the pixel address. + cCTextBitmap->pixelAddr = OH_Drawing_BitmapGetPixels(cCTextBitmap->_bitmap); + cCTextBitmap->width = textWidth; + cCTextBitmap->height = textHeight; +} + +void* CCTextBitmap::getPixelAddr() { + return pixelAddr; +} + +CCTextBitmap::~CCTextBitmap() { + OH_Drawing_CanvasDestroy(_canvas); + OH_Drawing_BitmapDestroy(_bitmap); + OH_Drawing_DestroyTypographyStyle(_typographyStyle); + OH_Drawing_DestroyTextStyle(_textStyle); + OH_Drawing_DestroyTypographyHandler(_typographyCreate); + OH_Drawing_DestroyTypography(_typography); + OH_Drawing_DestroyFontCollection(_fontCollection); + + pixelAddr = nullptr; +} + + + +NS_CC_END diff --git a/cocos/platform/ohos/CCTextBitmap.h b/cocos/platform/ohos/CCTextBitmap.h new file mode 100644 index 000000000000..c1bba8a3ffe1 --- /dev/null +++ b/cocos/platform/ohos/CCTextBitmap.h @@ -0,0 +1,73 @@ +#ifndef XComponent_CCTextBitmap_H +#define XComponent_CCTextBitmap_H + +#include "platform/CCPlatformMacros.h" +#include "platform/CCImage.h" +#include "platform/CCDevice.h" + +#include +#include +#include +#include + +#define DEFAULT_FONTSIZE 20 + +NS_CC_BEGIN + class CCTextBitmap + { + public: + static const int HORIZONTALALIGN_LEFT = 1; + static const int HORIZONTALALIGN_RIGHT = 2; + static const int HORIZONTALALIGN_CENTER = 3; + + static const int VERTICALALIGN_TOP = 1; + static const int VERTICALALIGN_BOTTOM = 2; + static const int VERTICALALIGN_CENTER = 3; + static void createCCTextBitmap(CCTextBitmap* cCTextBitmap, const char *text, const char *pFontName, const float a, const float r, const float g, const float b, + const Device::TextAlign eAlignMask, int width_, int height_, double fontSize); + static void createCCTextBitmap(CCTextBitmap* cCTextBitmap, const char *text, const char *pFontName, const int pFontSize, + const float fontTintA, const float fontTintR, const float fontTintG, const float fontTintB, + const Device::TextAlign eAlignMask, const int pWidth, const int pHeight, const bool shadow, + const float shadowDX, const float shadowDY, const float shadowBlur, const bool stroke, + const float strokeR, const float strokeG, const float strokeB, const float strokeSize); + + ~CCTextBitmap(); + void* getPixelAddr(); + int GetWidth() { return width; } + int GetHeight() { return height; } + private: + + /** + * Calculate the start point of x-drawing + * @param pAlignment Edge settings for text + * @param layoutWidth Width when text is measured + * @param realWidth Actual text width + * @param textWidth Width of the last display of the text control, that is, the width of the bitmap. + * @return x The starting point of the drawing + */ + static double calxStartPosition(int pAlignment, int layoutWidth, int realWidth, int textWidth); + + /** + * Calculate the start point of y drawing + * @param pAlignment Edge settings for text + * @param realWidth Actual text width + * @param textWidth Width of the last display of the text control, that is, the width of the bitmap. + * @return x The starting point of the drawing + */ + static double calyStartPosition(int pAlignment, int realHeight, int textHeight); + + static int processTextAlign(int pAlignment); + void* pixelAddr = nullptr; + int width = 0; + int height = 0; + + OH_Drawing_Bitmap* _bitmap{nullptr}; + OH_Drawing_Canvas* _canvas{nullptr}; + OH_Drawing_TypographyStyle* _typographyStyle{nullptr}; + OH_Drawing_TypographyCreate* _typographyCreate{nullptr}; + OH_Drawing_FontCollection* _fontCollection{nullptr}; + OH_Drawing_TextStyle* _textStyle{nullptr}; + OH_Drawing_Typography *_typography{nullptr}; + }; +NS_CC_END +#endif diff --git a/cocos/platform/ohos/JsAudioEngine.cpp b/cocos/platform/ohos/JsAudioEngine.cpp new file mode 100644 index 000000000000..ee01049c848f --- /dev/null +++ b/cocos/platform/ohos/JsAudioEngine.cpp @@ -0,0 +1,579 @@ +// +// Created on 2022/11/29. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". +#include "JsAudioEngine.h" +#include "CCLogOhos.h" + +#include +#include +#include +NS_CC_BEGIN +napi_env JsAudioEngine::_env = nullptr; + + +napi_value JsAudioEngine::initJsAudioEngine(napi_env env, napi_value exports) +{ + OHOS_LOGD("initJsAudioEngine start!"); + _env = env; + return 0; +} + +void JsAudioEngine::preLoadBackgroundMusic(std::string& audioUri) +{ + OHOS_LOGD("JsAudioEngine preLoadBackMusic start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::preLoadBackMusic get global failed! status:%d", status); + return; + } + napi_value preloadMusicFunc = getEngineFunc("preloadBackgroundMusic"); + if (preloadMusicFunc == nullptr) { + return; + } + napi_value argUri; + napi_create_string_utf8(_env, audioUri.data(), audioUri.length() ,&argUri); + napi_value args[] = {argUri}; + napi_value return_val; + status = napi_call_function(_env, global, preloadMusicFunc, 1, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::preLoadBackMusic napi_call_function failed! status:%d", status); + return; + } +} + + +void JsAudioEngine::playBackgroundMusic(std::string& audioUri) +{ + playBackgroundMusic(audioUri, -1); +} +void JsAudioEngine::playBackgroundMusic(std::string& audioUri, int seek) +{ + OHOS_LOGD("JsAudioEngine playBackgroundMusic start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::playBackgroundMusic get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("playBackgroundMusic"); + if (playFunc == nullptr) { + return; + } + napi_value argUri; + napi_create_string_utf8(_env, audioUri.data(), audioUri.length() ,&argUri); + napi_value argSeek; + napi_create_int32(_env, seek, &argSeek); + napi_value argStartPlay; + napi_create_int32(_env, 1, &argStartPlay); + + napi_value args[3] = {argUri, argSeek, argStartPlay}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 3, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::playBackgroundMusic napi_call_function failed! status:%d", status); + return; + } else { + OHOS_LOGE("JsAudioEngine::playBackgroundMusic napi_call_function success! status:%d", status); + } + +} + +void JsAudioEngine::stopBackgroundMusic(std::string& audioUri) +{ + OHOS_LOGD("JsAudioEngine stopBackgroundMusic start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::stopBackgroundMusic get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("stopBackgroundMusic"); + if (playFunc == nullptr) { + return; + } + napi_value argUri; + napi_create_string_utf8(_env, audioUri.data(), audioUri.length() ,&argUri); + + napi_value args[1] = {argUri}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 1, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::stopBackgroundMusic napi_call_function failed! status:%d", status); + return; + } +} + +void JsAudioEngine::pauseBackgroundMusic() +{ + OHOS_LOGD("JsAudioEngine pauseBackgroundMusic start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::pauseBackgroundMusic get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("pauseBackgroundMusic"); + if (playFunc == nullptr) { + return; + } + + napi_value args[0] = {}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 0, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::pauseBackgroundMusic napi_call_function failed! status:%d", status); + return; + } +} + +void JsAudioEngine::resumeBackgroundMusic() +{ + OHOS_LOGD("JsAudioEngine resumeBackgroundMusic start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::resumeBackgroundMusic get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("resumeBackgroundMusic"); + if (playFunc == nullptr) { + return; + } + + napi_value args[0] = {}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 0, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::pauseBackgroundMusic napi_call_function failed! status:%d", status); + return; + } +} + +void JsAudioEngine::rewindBackgroundMusic() +{ + // seek 到 0 后重新播放。先暂停,然后重新播放 +// pauseBackgroundMusic(); + seekBackgroundMusic(0); + std::string uri("hello"); + playBackgroundMusic(uri, 0); +} + +void JsAudioEngine::seekBackgroundMusic(int seek) +{ + OHOS_LOGD("JsAudioEngine seekBackgroundMusic start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::seekBackgroundMusic get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("seekBackgroundMusic"); + if (playFunc == nullptr) { + return; + } + + napi_value argSeek; + napi_create_int32(_env, seek, &argSeek); + napi_value args[1] = {argSeek}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 1, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::seekBackgroundMusic napi_call_function failed! status:%d", status); + return; + } +} + + +float JsAudioEngine::getBackgroundMusicVolume() +{ + OHOS_LOGD("JsAudioEngine getBackgroundMusicVolume start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::getBackgroundMusicVolume get global failed! status:%d", status); + return DEFAULT_VOLUME; + } + napi_value playFunc = getEngineFunc("getBackgroundVolume"); + if (playFunc == nullptr) { + return DEFAULT_VOLUME; + } + + napi_value args[0] = {}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 0, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::getBackgroundMusicVolume napi_call_function failed! status:%d", status); + return DEFAULT_VOLUME; + } + double result; + napi_get_value_double(_env, return_val, &result); + return result; +} + +void JsAudioEngine::setBackgroundMusicVolume(const float volume) +{ + OHOS_LOGD("JsAudioEngine setBackgroundMusicVolume start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::setBackgroundMusicVolume get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("setBackgroundVolume"); + if (playFunc == nullptr) { + return; + } + + napi_value napiVolume; + napi_create_double(_env, volume, &napiVolume); + napi_value args[1] = {napiVolume}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 1, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::setBackgroundMusicVolume napi_call_function failed! status:%d", status); + return; + } +} + +float JsAudioEngine::getEffectsVolume() +{ + OHOS_LOGD("JsAudioEngine getEffectsVolume start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::getEffectsVolume get global failed! status:%d", status); + return DEFAULT_VOLUME; + } + napi_value playFunc = getEngineFunc("getEffectsVolume"); + if (playFunc == nullptr) { + return DEFAULT_VOLUME; + } + + napi_value args[0] = {}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 0, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::getEffectsVolume napi_call_function failed! status:%d", status); + return DEFAULT_VOLUME; + } + double result; + napi_get_value_double(_env, return_val, &result); + return result; +} + +void JsAudioEngine::setEffectsVolume(float volume) +{ + OHOS_LOGD("JsAudioEngine setEffectsVolume start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::setEffectsVolume get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("setEffectsVolume"); + if (playFunc == nullptr) { + return; + } + + napi_value napiVolume; + napi_create_double(_env, volume, &napiVolume); + napi_value args[1] = {napiVolume}; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, 1, args, &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::setEffectsVolume napi_call_function failed! status:%d", status); + return; + } +} + +unsigned int JsAudioEngine::playEffect(const char* pszFilePath, bool bLoop) +{ + OHOS_LOGD("JsAudioEngine playEffect start!"); + std::string audioUri(pszFilePath); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::playEffect get global failed! status:%d", status); + return 0; + } + napi_value playFunc = getEngineFunc("playEffect"); + if (playFunc == nullptr) { + return 0; + } + + std::vector args; + napi_value argUri; + napi_create_string_utf8(_env, audioUri.data(), audioUri.length() ,&argUri); + napi_value argLoop; + napi_get_boolean(_env, bLoop, &argLoop); + args.push_back(argUri); + args.push_back(argLoop); + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::playEffect napi_call_function failed! status:%d", status); + return 0; + } else { + OHOS_LOGE("JsAudioEngine::playEffect napi_call_function success! status:%d", status); + } + unsigned int result; + napi_get_value_uint32(_env, return_val, &result); + return result; +} + +void JsAudioEngine::pauseEffect(unsigned int nSoundId) +{ + OHOS_LOGD("JsAudioEngine pauseEffect start! nSoundId:%d", nSoundId); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::pauseEffect get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("pauseEffect"); + if (playFunc == nullptr) { + return; + } + + std::vector args; + napi_value argSoundId; + napi_create_uint32(_env, nSoundId, &argSoundId); + args.push_back(argSoundId); + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::pauseEffect napi_call_function failed! status:%d", status); + return; + } +} + +void JsAudioEngine::pauseAllEffects() +{ + OHOS_LOGD("JsAudioEngine pauseAllEffects start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::pauseAllEffects get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("pauseAllEffects"); + if (playFunc == nullptr) { + return; + } + + std::vector args; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::pauseEffect napi_call_function failed! status:%d", status); + return; + } +} + +void JsAudioEngine::resumeEffect(unsigned int nSoundId) +{ + OHOS_LOGD("JsAudioEngine resumeEffect start! nSoundId:%d", nSoundId); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::resumeEffect get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("resumeEffect"); + if (playFunc == nullptr) { + return; + } + + std::vector args; + napi_value argSoundId; + napi_create_uint32(_env, nSoundId, &argSoundId); + args.push_back(argSoundId); + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::pauseBackgroundMusic napi_call_function failed! status:%d", status); + return; + } +} + +void JsAudioEngine::resumeAllEffects() +{ + OHOS_LOGD("JsAudioEngine resumeAllEffects start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::resumeAllEffects get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("resumeAllEffects"); + if (playFunc == nullptr) { + return; + } + + std::vector args; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::resumeAllEffects napi_call_function failed! status:%d", status); + return; + } +} + +void JsAudioEngine::stopEffect(unsigned int nSoundId) +{ + OHOS_LOGD("JsAudioEngine stopEffect start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::stopEffect get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("stopEffect"); + if (playFunc == nullptr) { + return; + } + + std::vector args; + napi_value argSoundId; + napi_create_uint32(_env, nSoundId, &argSoundId); + args.push_back(argSoundId); + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::stopEffect napi_call_function failed! status:%d", status); + return; + } +} + +void JsAudioEngine::stopAllEffects() +{ + OHOS_LOGD("JsAudioEngine stopAllEffects start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::stopAllEffects get global failed! status:%d", status); + return; + } + napi_value playFunc = getEngineFunc("stopAllEffects"); + if (playFunc == nullptr) { + return; + } + + std::vector args; + + napi_value return_val; + status = napi_call_function(_env, global, playFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::stopAllEffects napi_call_function failed! status:%d", status); + return; + } +} + +void JsAudioEngine::preloadEffect(const char* pszFilePath) +{ + OHOS_LOGD("JsAudioEngine preLoadBackMusic start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::preLoadBackMusic get global failed! status:%d", status); + return; + } + napi_value preloadEffectFunc = getEngineFunc("preloadEffect"); + if (preloadEffectFunc == nullptr) { + return; + } + + std::vector args; + std::string uri(pszFilePath); + napi_value argUri; + napi_create_string_utf8(_env, uri.data(), uri.length() ,&argUri); + args.push_back(argUri); + + napi_value return_val; + status = napi_call_function(_env, global, preloadEffectFunc, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::preLoadBackMusic napi_call_function failed! status:%d", status); + return; + } +} + +void JsAudioEngine::unloadEffect(const char* pszFilePath) +{ + OHOS_LOGD("JsAudioEngine unloadEffect start!"); + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::unloadEffect get global failed! status:%d", status); + return; + } + napi_value func = getEngineFunc("unloadEffect"); + if (func == nullptr) { + return; + } + + std::vector args; + std::string uri(pszFilePath); + napi_value argUri; + napi_create_string_utf8(_env, uri.data(), uri.length() ,&argUri); + args.push_back(argUri); + + napi_value return_val; + status = napi_call_function(_env, global, func, args.size(), args.data(), &return_val); + if (status != napi_ok) { + OHOS_LOGE("JsAudioEngine::unloadEffect napi_call_function failed! status:%d", status); + return; + } +} + +napi_value JsAudioEngine::getEngineFunc(const char* funcName) +{ + // Get the function named "AddTwo" on the global object + napi_value global; + napi_status status = napi_get_global(_env, &global); + if (status != napi_ok) { + OHOS_LOGE("%s get global failed!", funcName); + return nullptr; + } + + napi_value cocosNs; + status = napi_get_named_property(_env, global, "ccCpp2Js", &cocosNs); + if (status != napi_ok) { + OHOS_LOGE("%s get cocosNs failed!", funcName); + return nullptr; + } + + napi_value func; + status = napi_get_named_property(_env, cocosNs, funcName, &func); + if (status != napi_ok) { + OHOS_LOGE("%s get func failed!", funcName); + return nullptr; + } + + napi_valuetype functype; + napi_typeof(_env, func, &functype); + if (functype != napi_function) { + OHOS_LOGE("%s get func but is not a function", funcName); + return nullptr; + } + return func; +} + +NS_CC_END \ No newline at end of file diff --git a/cocos/platform/ohos/JsAudioEngine.h b/cocos/platform/ohos/JsAudioEngine.h new file mode 100644 index 000000000000..31d67f7b0d43 --- /dev/null +++ b/cocos/platform/ohos/JsAudioEngine.h @@ -0,0 +1,48 @@ +// +// Created on 2022/11/29. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". + +#ifndef JsAudioEngine_H +#define JsAudioEngine_H + +#include +#include +#include "platform/CCPlatformMacros.h" + +NS_CC_BEGIN +class JsAudioEngine{ +public: + constexpr static const float DEFAULT_VOLUME = 0.5f; + static napi_value initJsAudioEngine(napi_env env, napi_value exports); + static void preLoadBackgroundMusic(std::string& audioUri); + static void playBackgroundMusic(std::string& audioUri, int seek); + static void playBackgroundMusic(std::string& audioUri); + static void stopBackgroundMusic(std::string& audioUri); + static void pauseBackgroundMusic(); + static void resumeBackgroundMusic(); + static void rewindBackgroundMusic(); + static void seekBackgroundMusic(int seek); + static float getBackgroundMusicVolume(); + static void setBackgroundMusicVolume(const float volume); + + static float getEffectsVolume(); + static void setEffectsVolume(float volume); + static unsigned int playEffect(const char* pszFilePath, bool bLoop); + static void pauseEffect(unsigned int nSoundId); + static void pauseAllEffects(); + static void resumeEffect(unsigned int nSoundId); + static void resumeAllEffects(); + static void stopEffect(unsigned int nSoundId); + static void stopAllEffects(); + static void preloadEffect(const char* pszFilePath); + static void unloadEffect(const char* pszFilePath); + + + static napi_value getEngineFunc(const char* funcName); +private: + static napi_env _env; +}; +#endif //XComponent_JsAudioEngine_H +NS_CC_END \ No newline at end of file diff --git a/cocos/platform/ohos/libSysCapabilities/Readme.txt b/cocos/platform/ohos/libSysCapabilities/Readme.txt new file mode 100644 index 000000000000..c830be7540cc --- /dev/null +++ b/cocos/platform/ohos/libSysCapabilities/Readme.txt @@ -0,0 +1,2 @@ +Currently, the DevEco does not support referencing Har projects outside the project directory. +Related capabilities are being planned. The libSysCapabilities folder of each project will be unified here. \ No newline at end of file diff --git a/cocos/platform/ohos/napi/WorkerMessageQueue.cpp b/cocos/platform/ohos/napi/WorkerMessageQueue.cpp new file mode 100644 index 000000000000..dfec23503bb4 --- /dev/null +++ b/cocos/platform/ohos/napi/WorkerMessageQueue.cpp @@ -0,0 +1,20 @@ +#include "WorkerMessageQueue.h" + +void WorkerMessageQueue::enqueue(const WorkerMessageData& data) { + std::lock_guard lck(_mutex); + _queue.push(data); +} + +bool WorkerMessageQueue::dequeue(WorkerMessageData *data) { + std::lock_guard lck(_mutex); + if (empty()) { + return false; + } + *data = _queue.front(); + _queue.pop(); + return true; +} + +bool WorkerMessageQueue::empty() const { + return _queue.empty(); +} diff --git a/cocos/platform/ohos/napi/WorkerMessageQueue.h b/cocos/platform/ohos/napi/WorkerMessageQueue.h new file mode 100644 index 000000000000..3af2ab04b7f8 --- /dev/null +++ b/cocos/platform/ohos/napi/WorkerMessageQueue.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include +#include +#include + +enum class MessageType { + WM_XCOMPONENT_SURFACE_CREATED = 0, + WM_XCOMPONENT_TOUCH_EVENT, + WM_XCOMPONENT_KEY_EVENT, + WM_XCOMPONENT_MOUSE_EVENT, + WM_XCOMPONENT_MOUSE_WHEEL_EVENT, + WM_XCOMPONENT_SURFACE_CHANGED, + WM_XCOMPONENT_SURFACE_HIDE, + WM_XCOMPONENT_SURFACE_SHOW, + WM_XCOMPONENT_SURFACE_DESTROY, + WM_APP_SHOW, + WM_APP_HIDE, + WM_APP_DESTROY, + WM_VSYNC, +}; + +struct WorkerMessageData { + MessageType type; + void* data; + void* window; + OH_NativeXComponent_TouchEvent* touchEvent; +}; + +class WorkerMessageQueue final { +public: + void enqueue(const WorkerMessageData& data); + bool dequeue(WorkerMessageData *data); + bool empty() const; + size_t size() const { + return _queue.size(); + } + +private: + std::mutex _mutex; + std::queue _queue; +}; diff --git a/cocos/platform/ohos/napi/common/native_common.h b/cocos/platform/ohos/napi/common/native_common.h new file mode 100644 index 000000000000..8875458426f2 --- /dev/null +++ b/cocos/platform/ohos/napi/common/native_common.h @@ -0,0 +1,83 @@ +#ifndef _NATIVE_COMMON_H_ +#define _NATIVE_COMMON_H_ + +#define NAPI_RETVAL_NOTHING + +#define GET_AND_THROW_LAST_ERROR(env) \ + do { \ + const napi_extended_error_info* errorInfo = nullptr; \ + napi_get_last_error_info((env), &errorInfo); \ + bool isPending = false; \ + napi_is_exception_pending((env), &isPending); \ + if (!isPending && errorInfo != nullptr) { \ + const char* errorMessage = \ + errorInfo->error_message != nullptr ? errorInfo->error_message : "empty error message"; \ + napi_throw_error((env), nullptr, errorMessage); \ + } \ + } while (0) + +#define NAPI_ASSERT_BASE(env, assertion, message, retVal) \ + do { \ + if (!(assertion)) { \ + napi_throw_error((env), nullptr, "assertion (" #assertion ") failed: " message); \ + return retVal; \ + } \ + } while (0) + +#define NAPI_ASSERT(env, assertion, message) NAPI_ASSERT_BASE(env, assertion, message, nullptr) + +#define NAPI_ASSERT_RETURN_VOID(env, assertion, message) NAPI_ASSERT_BASE(env, assertion, message, NAPI_RETVAL_NOTHING) + +#define NAPI_CALL_BASE(env, theCall, retVal) \ + do { \ + if ((theCall) != napi_ok) { \ + GET_AND_THROW_LAST_ERROR((env)); \ + return retVal; \ + } \ + } while (0) + +#define NAPI_CALL(env, theCall) NAPI_CALL_BASE(env, theCall, nullptr) + +#define NAPI_CALL_RETURN_VOID(env, theCall) NAPI_CALL_BASE(env, theCall, NAPI_RETVAL_NOTHING) + +#define DECLARE_NAPI_PROPERTY(name, val) \ + { \ + (name), nullptr, nullptr, nullptr, nullptr, val, napi_default, nullptr \ + } + +#define DECLARE_NAPI_STATIC_PROPERTY(name, val) \ + { \ + (name), nullptr, nullptr, nullptr, nullptr, val, napi_static, nullptr \ + } + +#define DECLARE_NAPI_FUNCTION(name, func) \ + { \ + (name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, nullptr \ + } + +#define DECLARE_NAPI_FUNCTION_WITH_DATA(name, func, data) \ + { \ + (name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, data \ + } + +#define DECLARE_NAPI_STATIC_FUNCTION(name, func) \ + { \ + (name), nullptr, (func), nullptr, nullptr, nullptr, napi_static, nullptr \ + } + +#define DECLARE_NAPI_GETTER(name, getter) \ + { \ + (name), nullptr, nullptr, (getter), nullptr, nullptr, napi_default, nullptr \ + } + +#define DECLARE_NAPI_SETTER(name, setter) \ + { \ + (name), nullptr, nullptr, nullptr, (setter), nullptr, napi_default, nullptr \ + } + +#define DECLARE_NAPI_GETTER_SETTER(name, getter, setter) \ + { \ + (name), nullptr, nullptr, (getter), (setter), nullptr, napi_default, nullptr \ + } + +#endif /* _NATIVE_COMMON_H_ */ diff --git a/cocos/platform/ohos/napi/helper/JSRegisterUtils.h b/cocos/platform/ohos/napi/helper/JSRegisterUtils.h new file mode 100644 index 000000000000..d7947f06c4b2 --- /dev/null +++ b/cocos/platform/ohos/napi/helper/JSRegisterUtils.h @@ -0,0 +1,73 @@ +#ifndef CC_OH_JsRegister_H +#define CC_OH_JsRegister_H + +#include +#include +#include "NapiHelper.h" + +#define APP_LOG_DOMAIN 0x0001 +#define APP_LOG_TAG "JSRegisterUtils" +#define LOGI(...) ((void)OH_LOG_Print(LOG_APP, LOG_INFO, APP_LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) + +napi_env _env = nullptr; +napi_value initRegisterFunction(napi_env env, napi_value exports) { + LOGI("initRegisterFunction start!"); + _env = env; + return 0; +} + +napi_value registerFunction(napi_env env, napi_callback_info info) { + LOGI("====begin to registerFunction!"); + napi_status status; + napi_value exports; + size_t argc = 2; + napi_value args[2]; + + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + + if (argc != 2) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return nullptr; + } + + napi_valuetype valuetype; + status = napi_typeof(env, args[0], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_string) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + auto jsArg = args[0]; + size_t len = 0; + status = napi_get_value_string_utf8(env, jsArg, nullptr, 0, &len); + std::string functionName = ""; + functionName.resize(len, '\0'); + status = napi_get_value_string_utf8(env, jsArg, (char*)functionName.data(), functionName.size() + 1, &len); + + napi_valuetype functionType; + status = napi_typeof(env, args[1], &functionType); + if (status != napi_ok) { + return nullptr; + } + if (functionType != napi_function) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + napi_ref fucRef; + NAPI_CALL(env, napi_create_reference(env, args[1], 1, &fucRef)); + + char* name = new char[functionName.length() + 1]; + strcpy(name, functionName.c_str()); + JSFunction* jsFunction = new JSFunction(name, env, fucRef); + + JSFunction::addFunction(name, jsFunction); + + LOGI("begin to return!"); + return nullptr; +} + +#endif //CC_OH_JsRegister_H \ No newline at end of file diff --git a/cocos/platform/ohos/napi/helper/Js_Cocos2dxHelper.cpp b/cocos/platform/ohos/napi/helper/Js_Cocos2dxHelper.cpp new file mode 100644 index 000000000000..a833d8b07969 --- /dev/null +++ b/cocos/platform/ohos/napi/helper/Js_Cocos2dxHelper.cpp @@ -0,0 +1,54 @@ +#include +#include +#include "NapiHelper.h" +#include "Js_Cocos2dxHelper.h" +#include "platform/ohos/CCLogOhos.h" + +napi_env Js_Cocos2dxHelper::_env = nullptr; +napi_value Js_Cocos2dxHelper::initJsCocos2dxHelper(napi_env env, napi_callback_info info) { + _env = env; + return 0; +} + +/** + * If you have more information that can be obtained asynchronously, add it here. + */ +napi_value Js_Cocos2dxHelper::initAsyncInfo(napi_env env, napi_callback_info info) { + JSFunction::getFunction("DeviceUtils.initScreenInfo").invoke(); + return nullptr; +} + +std::string Js_Cocos2dxHelper::_asyncInfoMap[AsyncInfo::LAST_INDEX]; + +void Js_Cocos2dxHelper::terminateProcess() { + JSFunction::getFunction("ApplicationManager.exit").invoke(); +} + +// The default accelerometer interval is 10000000 ns, that is, 10 ms. +float Js_Cocos2dxHelper::_accelerometerInterval = 10000000.0f; +bool Js_Cocos2dxHelper::_accelerometerFlag = false; +void Js_Cocos2dxHelper::enableAccelerometer() { + // Start accelerometer subscription when allowed use default interval + JSFunction::getFunction("Accelerometer.enable").invoke(_accelerometerInterval); + _accelerometerFlag = true; +} + +void Js_Cocos2dxHelper::disableAccelerometer() { + JSFunction::getFunction("Accelerometer.disable").invoke(); + _accelerometerFlag = false; +} + +void Js_Cocos2dxHelper::setAccelerometerInterval(float interval) { + OHOS_LOGD("accelerometer setAccelerometerInterval, change to %{public}f", interval); + // Same as the original one. No handling is required. + if(_accelerometerInterval == interval) { + return; + } + _accelerometerInterval = interval; + + // if accelerometer running, restart with new interval + if(_accelerometerFlag) { + JSFunction::getFunction("Accelerometer.enable").invoke(_accelerometerInterval); + } +} + diff --git a/cocos/platform/ohos/napi/helper/Js_Cocos2dxHelper.h b/cocos/platform/ohos/napi/helper/Js_Cocos2dxHelper.h new file mode 100644 index 000000000000..1d0001badff6 --- /dev/null +++ b/cocos/platform/ohos/napi/helper/Js_Cocos2dxHelper.h @@ -0,0 +1,41 @@ +#ifndef __Js_Cocos2dxHelper_H__ +#define __Js_Cocos2dxHelper_H__ + +#include +#include +#include +#include + +#define APP_LOG_DOMAIN 0x0001 +#define APP_LOG_TAG "Js_Cocos2dxHelper" +#define LOGI(...) ((void)OH_LOG_Print(LOG_APP, LOG_INFO, APP_LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) + +enum AsyncInfo { + // VERSION_NAME = 0, + LAST_INDEX // Only indicates the size of the array used for storing data. It is meaningless. If an enumeration is added later, keep LAST_INDEX at the end. +}; + +class Js_Cocos2dxHelper { +public: + static napi_value initJsCocos2dxHelper(napi_env env, napi_callback_info info); + static napi_value initAsyncInfo(napi_env env, napi_callback_info info); + static void setAsyncInfo(AsyncInfo key, const std::string& value) { + _asyncInfoMap[key] = value; + } + + static std::string getAsyncInfo(AsyncInfo key) { + return _asyncInfoMap[key]; + } + + static void terminateProcess(); + static void enableAccelerometer(); + static void disableAccelerometer(); + static void setAccelerometerInterval(float interval); + +private: + static std::string _asyncInfoMap[]; + static napi_env _env; + static float _accelerometerInterval; + static bool _accelerometerFlag; +}; +#endif /* __Js_Cocos2dxHelper_H__ */ diff --git a/cocos/platform/ohos/napi/helper/NapiHelper.cpp b/cocos/platform/ohos/napi/helper/NapiHelper.cpp new file mode 100644 index 000000000000..898362cd144c --- /dev/null +++ b/cocos/platform/ohos/napi/helper/NapiHelper.cpp @@ -0,0 +1,3 @@ +#include "NapiHelper.h" + +std::unordered_map JSFunction::FUNCTION_MAP; diff --git a/cocos/platform/ohos/napi/helper/NapiHelper.h b/cocos/platform/ohos/napi/helper/NapiHelper.h new file mode 100644 index 000000000000..ac662559af63 --- /dev/null +++ b/cocos/platform/ohos/napi/helper/NapiHelper.h @@ -0,0 +1,254 @@ +#ifndef CC_OH_NapiHelper_H +#define CC_OH_NapiHelper_H + +#include +#include +#include +#include +#include "../common/native_common.h" +#include +#include "NapiValueConverter.h" +#include +#include +#include "Js_Cocos2dxHelper.h" + +#define APP_LOG_DOMAIN 0x0001 +#define APP_LOG_TAG "NapiHelper" +#define LOGI(...) ((void)OH_LOG_Print(LOG_APP, LOG_INFO, APP_LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) + +const static int BUFFER_SIZE = 1024 * 10; + +// Defines locks and semaphores. +typedef struct ThreadLockInfo { + std::mutex mutex; + std::condition_variable condition; + bool ready = false; +} ThreadLockInfo; + +typedef struct WorkParam { + napi_env env; + napi_ref funcRef; + std::string inParam; + std::string replyParam = std::string(); + int replyInt = -1; + char replyString[64] = {0}; + // lock structure + std::shared_ptr < ThreadLockInfo > lockInfo; +} WorkParam; + +static auto successCallback = [](napi_env env, napi_callback_info info) -> napi_value { + size_t sLen = 0; + size_t argc = 1; + napi_value args[1] = {nullptr}; + void *param_in = nullptr; + napi_get_cb_info(env, info, &argc, args, nullptr, ¶m_in); + WorkParam *callbackParam = reinterpret_cast(param_in); + + // Use the napi_get_value method to read the return value based on the return value type (number or string). + napi_valuetype type; + napi_typeof(env, args[0], &type); + switch(type) { + case napi_number: + if (napi_get_value_int32(env, args[0], &callbackParam->replyInt) != napi_ok) { + LOGI("[%s] get number value error", __FUNCTION__); + } + break; + case napi_string: + if (napi_get_value_string_utf8(env, args[0], callbackParam->replyString, sizeof(callbackParam->replyString), &sLen) != napi_ok) { + LOGI("[%s] get string value error", __FUNCTION__); + } + callbackParam->replyParam =callbackParam->replyString; + LOGI("XXXXXX:retChar %{public}s", callbackParam->replyString); + callbackParam->replyInt = sLen; + break; + default: + LOGI("[%s] type is unknown", __FUNCTION__); + break; + } + + /* Child thread unlocking */ + std::unique_lock lock(callbackParam->lockInfo->mutex); + callbackParam->lockInfo->ready = true; + callbackParam->lockInfo->condition.notify_all(); + return nullptr; +}; + +class JSFunction { +public: + napi_ref funcRef; + napi_env env; + char* name = nullptr; + +public: + static std::unordered_map FUNCTION_MAP; + + explicit JSFunction(char* name, napi_env env, napi_ref funcRef) + : name(name), env(env), funcRef(funcRef){} + + explicit JSFunction(char* name, napi_env env) + : name(name), env(env){} + + explicit JSFunction(char* name) + : name(name){} + + static JSFunction getFunction(std::string functionName) + { + return FUNCTION_MAP.at(functionName); + } + + static void addFunction(std::string name, JSFunction* jsFunction) { + FUNCTION_MAP.emplace(name, *jsFunction); + } + + template + typename std::enable_if::value, ReturnType>::type + invoke(Args... args) { + LOGI("=========cocos-[NApiHelper]=========JSFunction::invoke ========="); + napi_value global; + napi_status status = napi_get_global(env, &global); + //if (status != napi_ok) return; + + napi_value func; + status = napi_get_reference_value(env, funcRef, &func); + + napi_value jsArgs[sizeof...(Args)] = {NapiValueConverter::ToNapiValue(env, args)...}; + napi_value return_val; + status = napi_call_function(env, global, func, sizeof...(Args), jsArgs, &return_val); + + ReturnType value; + if (!NapiValueConverter::ToCppValue(env, return_val, value)) { + // Handle error here + } + return value; + } + + template + typename std::enable_if::value, void>::type + invoke(Args... args) { + LOGI("=========cocos-[NApiHelper]=========JSFunction::invoke ========="); + napi_value global; + napi_status status = napi_get_global(env, &global); + if (status != napi_ok) return; + + napi_value func; + status = napi_get_reference_value(env, funcRef, &func); + + napi_value jsArgs[sizeof...(Args)] = {NapiValueConverter::ToNapiValue(env, args)...}; + napi_value return_val; + status = napi_call_function(env, global, func, sizeof...(Args), jsArgs, &return_val); + } + + static void callFunctionWithParams(WorkParam *param) { + napi_ref funcRef = param->funcRef; + std::string inParam = param->inParam; + napi_env env = param->env; + napi_value global; + napi_status status = napi_get_global(env, &global); + if (status != napi_ok) { + LOGI("XXXXXX:[getClassObject] napi_get_global != napi_ok"); + } + + napi_value func; + // Obtains the native interface and invokes the JS function transactFromNative. + status = napi_get_reference_value(env, funcRef, &func); + if (status != napi_ok) { + LOGI("XXXXXX: napi_get_named_property getClassObject != napi_ok %{public}d", status); + } + + napi_value promise; + // Obtains the promise returned by the transactFromNative function. + if (inParam.empty()){ + status = napi_call_function(env, global, func, 0, nullptr, &promise); + }else { + napi_value argsOne[1] = { nullptr }; + napi_create_string_utf8(env, inParam.c_str(), NAPI_AUTO_LENGTH, &argsOne[0]); + //napi_create_int32(env, 22, &argsOne[0]); + status = napi_call_function(env, global, func, 1, argsOne, &promise); + } + if (status != napi_ok) { + LOGI("XXXXXX:napi_call_function getClassObject != napi_ok %{public}d", status); + } + + napi_value thenFunc = nullptr; + // Obtains the then function of the promise. + status = napi_get_named_property(env, promise, "then", &thenFunc); + if (status != napi_ok) { + LOGI("XXXXXX:napi_get_named_property then failed, ret: %{public}d", status); + } + + napi_value successFunc = nullptr; + // Note that the fifth parameter is passed to the callback function to obtain the return value in the callback. + status = napi_create_function(env, "successFunc", NAPI_AUTO_LENGTH, successCallback, param, &successFunc); + if (status != napi_ok) { + LOGI("XXXXXX:napi_create_function successFunc failed, ret: %{public}d", status); + } + napi_value ret; + // Call the then function + status = napi_call_function(env, promise, thenFunc, 1, &successFunc, &ret); + if (status != napi_ok) { + LOGI("XXXXXX:napi_call_function thenFunc failed, ret: %{public}d", status); + } + } + // Callback Function Type + typedef std::function Callback; + + // Subthread function RunPromiseType: used to access the asynchronous function that returns promise in JS and implement synchronization. + static void RunPromiseType(napi_env env,napi_ref funcRef, Callback callback,const char *argstr) { + WorkParam *workParam = new (std::nothrow) WorkParam; + workParam->env = env; + workParam->funcRef = funcRef; + workParam->inParam = argstr; + workParam->lockInfo = std::make_shared(); + + uv_loop_s * loop = nullptr; + // Obtains the loop thread corresponding to the env in the JS. + napi_get_uv_event_loop(workParam->env, &loop); + // create uv_work + uv_work_t * work = new (std::nothrow) uv_work_t; + if (work == nullptr) { + LOGI("XXXXXX:failed to new uv_work_t"); + delete workParam; + } + // Thread input parameters are stored work->data + work->data = reinterpret_cast < void * > (workParam); + + // Execute work in the JS thread. + uv_queue_work( + loop, work, [](uv_work_t * work) {}, [](uv_work_t * work, int _status) { + WorkParam * param = reinterpret_cast < WorkParam * > (work->data); + callFunctionWithParams(param); + }); + LOGI("XXXXXX:childThread lock and wait"); + // The sub-thread is locked and waits for the callback result of the JS thread. + std::unique_lock lock(workParam->lockInfo->mutex); + // You can change the value to wait_for to prevent timeout. + workParam->lockInfo->condition.wait(lock,[&workParam] { return workParam->lockInfo->ready; }); + // workParam->lockInfo->condition.wait_for(lock,std::chrono::milliseconds(2000), [&workParam] { return workParam->lockInfo->ready; }); + LOGI("XXXXXX:wait work end, result %{public}s", workParam->replyParam.c_str()); + callback(workParam->replyParam); + delete workParam; + delete work; + } + + void invokeAsync(AsyncInfo key, const char *argstr) { + Callback callback = [key](std::string value) { + Js_Cocos2dxHelper::setAsyncInfo(key, value);//callback + }; + // Create a child thread + std::thread threadTestPromise(RunPromiseType,env,funcRef,callback,argstr); + // Thread separation + threadTestPromise.detach(); + } + + void invokeAsync(AsyncInfo key) { + Callback callback = [key](std::string value) { + Js_Cocos2dxHelper::setAsyncInfo(key, value);//callback + }; + // Create a child thread + std::thread threadTestPromise(RunPromiseType,env,funcRef,callback,""); + // Thread separation + threadTestPromise.detach(); + } + +}; +#endif //CC_OH_NapiHelper_H diff --git a/cocos/platform/ohos/napi/helper/NapiValueConverter.cpp b/cocos/platform/ohos/napi/helper/NapiValueConverter.cpp new file mode 100644 index 000000000000..9cfcec7cf54f --- /dev/null +++ b/cocos/platform/ohos/napi/helper/NapiValueConverter.cpp @@ -0,0 +1,89 @@ +#include "NapiValueConverter.h" +#include + +napi_valuetype GetNapiValueType(napi_env env, napi_value value) { + napi_valuetype type; + napi_typeof(env, value, &type); + return type; +} + +template<> +bool NapiValueConverter::ToCppValue(napi_env env, napi_value value, int& result) { + if (GetNapiValueType(env, value) != napi_number) { + napi_throw_type_error(env, nullptr, "Expected number"); + return false; + } + napi_get_value_int32(env, value, &result); + return true; +} + +template<> +bool NapiValueConverter::ToCppValue(napi_env env, napi_value value, bool& result) { + if (GetNapiValueType(env, value) != napi_boolean) { + napi_throw_type_error(env, nullptr, "Expected boolean"); + return false; + } + napi_get_value_bool(env, value, &result); + return true; +} + +template<> +bool NapiValueConverter::ToCppValue(napi_env env, napi_value value, double& result) { + if (GetNapiValueType(env, value) != napi_number) { + napi_throw_type_error(env, nullptr, "Expected number"); + return false; + } + napi_get_value_double(env, value, &result); + return true; +} + +template<> +bool NapiValueConverter::ToCppValue(napi_env env, napi_value value, std::string& result) { + if (GetNapiValueType(env, value) != napi_string) { + napi_throw_type_error(env, nullptr, "Expected string"); + return false; + } + + size_t str_size; + napi_get_value_string_utf8(env, value, nullptr, 0, &str_size); + char* buf = new char[str_size + 1]; + napi_get_value_string_utf8(env, value, buf, str_size + 1, &str_size); + result = std::string(buf); + delete[] buf; + + return true; +} + +napi_value NapiValueConverter::ToNapiValue(napi_env env, int32_t value) { + napi_value result; + napi_create_int32(env, value, &result); + return result; +} + +napi_value NapiValueConverter::ToNapiValue(napi_env env, int64_t value) { + napi_value result; + napi_create_int64(env, value, &result); + return result; +} + +napi_value NapiValueConverter::ToNapiValue(napi_env env, double value) { + napi_value result; + napi_create_double(env, value, &result); + return result; +} + +napi_value NapiValueConverter::ToNapiValue(napi_env env, bool value) { + napi_value result; + napi_get_boolean(env, value, &result); + return result; +} + +napi_value NapiValueConverter::ToNapiValue(napi_env env, const char* value) { + napi_value result; + napi_create_string_utf8(env, value, strlen(value), &result); + return result; +} + +napi_value NapiValueConverter::ToNapiValue(napi_env env, std::string value) { + return ToNapiValue(env, value.c_str()); +} \ No newline at end of file diff --git a/cocos/platform/ohos/napi/helper/NapiValueConverter.h b/cocos/platform/ohos/napi/helper/NapiValueConverter.h new file mode 100644 index 000000000000..a26e327ed637 --- /dev/null +++ b/cocos/platform/ohos/napi/helper/NapiValueConverter.h @@ -0,0 +1,16 @@ +#include +#include + +class NapiValueConverter { +public: + template + static bool ToCppValue(napi_env env, napi_value value, ReturnType& result); + + static napi_value ToNapiValue(napi_env env, int32_t value); + static napi_value ToNapiValue(napi_env env, int64_t value); + static napi_value ToNapiValue(napi_env env, double value); + static napi_value ToNapiValue(napi_env env, bool value); + static napi_value ToNapiValue(napi_env env, const char* value); + static napi_value ToNapiValue(napi_env env, std::string value); + +}; diff --git a/cocos/platform/ohos/napi/modules/InputNapi.cpp b/cocos/platform/ohos/napi/modules/InputNapi.cpp new file mode 100644 index 000000000000..2e9a30af0420 --- /dev/null +++ b/cocos/platform/ohos/napi/modules/InputNapi.cpp @@ -0,0 +1,185 @@ +#include +#include +#include "InputNapi.h" +#include "platform/ohos/napi/plugin_manager.h" +#include "../../CCLogOhos.h" +#include "ui/UIEditBox/UIEditBoxImpl-ohos.h" +#include "base/CCIMEDispatcher.h" + +napi_value InputNapi::editBoxOnFocusCB(napi_env env, napi_callback_info info) { + + napi_status status; + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + if (argc != 1) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return nullptr; + } + + napi_valuetype valuetype; + status = napi_typeof(env, args[0], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_number) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + int32_t index; + NAPI_CALL(env, napi_get_value_int32(env, args[0], &index)); + + cocos2d::ui::EditBoxImplOhos::onBeginCallBack(index); + return nullptr; +} + +napi_value InputNapi::editBoxOnChangeCB(napi_env env, napi_callback_info info) { + + napi_status status; + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + if (argc != 2) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return nullptr; + } + + napi_valuetype valuetype; + status = napi_typeof(env, args[0], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_number) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + status = napi_typeof(env, args[1], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_string) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + int32_t index; + NAPI_CALL(env, napi_get_value_int32(env, args[0], &index)); + size_t pInt; + char text[256]; + NAPI_CALL(env, napi_get_value_string_utf8(env, args[1], text, 256, &pInt)); + + cocos2d::ui::EditBoxImplOhos::onChangeCallBack(index, text); + return nullptr; +} + +napi_value InputNapi::editBoxOnEnterCB(napi_env env, napi_callback_info info) { + + napi_status status; + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + if (argc != 2) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return nullptr; + } + + napi_valuetype valuetype; + status = napi_typeof(env, args[0], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_number) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + status = napi_typeof(env, args[1], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_string) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + int32_t index; + NAPI_CALL(env, napi_get_value_int32(env, args[0], &index)); + size_t pInt; + char text[256]; + NAPI_CALL(env, napi_get_value_string_utf8(env, args[1], text, 256, &pInt)); + + cocos2d::ui::EditBoxImplOhos::onEnterCallBack(index, text); + return nullptr; +} + +napi_value InputNapi::textFieldTTFOnChangeCB(napi_env env, napi_callback_info info) { + + napi_status status; + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + if (argc != 1) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return nullptr; + } + + napi_valuetype valuetype; + status = napi_typeof(env, args[0], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_string) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + auto dispatcher = cocos2d::IMEDispatcher::sharedDispatcher(); + const std::string& oldContent = dispatcher->getContentText(); + + size_t textLen; + char text[2560] = {0}; + NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], text, 2560, &textLen)); + + // Optimization: Use string_view to avoid unnecessary string copying + std::string_view oldView(oldContent); + std::string_view newView(text, textLen); + + // Find the first different character position + size_t commonPrefixLen = 0; + const size_t minLen = std::min(oldView.length(), newView.length()); + while (commonPrefixLen < minLen && oldView[commonPrefixLen] == newView[commonPrefixLen]) { + commonPrefixLen++; + } + + // Delete the old content characters after the difference + const size_t charsToDelete = oldView.length() - commonPrefixLen; + const size_t deleteOperations = [&](){ + size_t count =0; + size_t pos = oldView.length() -1; + size_t remaining = charsToDelete; + while(remaining >0){ + //Check UTF-8 Chinese characters (3-byte characters starting with 0xE0-0xEF) + bool isChineseChar = (pos >= 2 && + (unsigned char)oldView[pos -2]>=0xE0 && + (unsigned char)oldView[pos -2] <= 0xEF); + remaining -= isChineseChar? 3:1; + pos -= isChineseChar?3:1; + count++; + } + return count; + }(); + //Delete characters in batches + for (size_t i = 0; i < deleteOperations; i++) { + dispatcher->dispatchDeleteBackward(); + } + + // Insert new characters after the difference + const size_t insertLen = newView.length() - commonPrefixLen; + if (insertLen > 0) { + const char* newText = text + commonPrefixLen; + dispatcher->dispatchInsertText(newText, insertLen); + } + return nullptr; +} \ No newline at end of file diff --git a/cocos/platform/ohos/napi/modules/InputNapi.h b/cocos/platform/ohos/napi/modules/InputNapi.h new file mode 100644 index 000000000000..94b6ab932e7a --- /dev/null +++ b/cocos/platform/ohos/napi/modules/InputNapi.h @@ -0,0 +1,15 @@ +#ifndef MyApplication_InputNapi_H +#define MyApplication_InputNapi_H + +#include +#include + +class InputNapi { +public: + static napi_value editBoxOnFocusCB(napi_env env, napi_callback_info info); + static napi_value editBoxOnChangeCB(napi_env env, napi_callback_info info); + static napi_value editBoxOnEnterCB(napi_env env, napi_callback_info info); + static napi_value textFieldTTFOnChangeCB(napi_env env, napi_callback_info info); +}; + +#endif //MyApplication_InputNapi_H \ No newline at end of file diff --git a/cocos/platform/ohos/napi/modules/MouseNapi.cpp b/cocos/platform/ohos/napi/modules/MouseNapi.cpp new file mode 100644 index 000000000000..b79d5eef4490 --- /dev/null +++ b/cocos/platform/ohos/napi/modules/MouseNapi.cpp @@ -0,0 +1,50 @@ +// +// Created on 2024/01/05. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". + +#include +#include +#include "MouseNapi.h" +#include "platform/ohos/napi/plugin_manager.h" +#include "../../CCLogOhos.h" +#include "base/CCIMEDispatcher.h" +#include "platform/ohos/napi/render/plugin_render.h" +#include + +napi_value MouseNapi::mouseWheelCB(napi_env env, napi_callback_info info) { + napi_status status; + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + if (argc != 2) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return nullptr; + } + napi_valuetype valuetype; + status = napi_typeof(env, args[0], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_string) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + status = napi_typeof(env, args[1], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_number) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + size_t pInt; + char eventType[256]; + NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], eventType, 256, &pInt)); + double scrollY; + NAPI_CALL(env, napi_get_value_double(env, args[1], &scrollY)); + PluginRender::MouseWheelCB(eventType, scrollY); + return nullptr; +} \ No newline at end of file diff --git a/cocos/platform/ohos/napi/modules/MouseNapi.h b/cocos/platform/ohos/napi/modules/MouseNapi.h new file mode 100644 index 000000000000..39971a4da045 --- /dev/null +++ b/cocos/platform/ohos/napi/modules/MouseNapi.h @@ -0,0 +1,18 @@ +// +// Created on 2024/01/05. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". + +#ifndef MyApplication_MouseNapi_H +#define MyApplication_MouseNapi_H + +#include +#include + +class MouseNapi { +public: + static napi_value mouseWheelCB(napi_env env, napi_callback_info info); +}; + +#endif //MyApplication_MouseNapi_H \ No newline at end of file diff --git a/cocos/platform/ohos/napi/modules/RawFileUtils.cpp b/cocos/platform/ohos/napi/modules/RawFileUtils.cpp new file mode 100644 index 000000000000..1033ac028c04 --- /dev/null +++ b/cocos/platform/ohos/napi/modules/RawFileUtils.cpp @@ -0,0 +1,44 @@ +#include +#include +#include + +#include + +#include "platform/ohos/napi/plugin_manager.h" +#include "../../CCLogOhos.h" + +#include +#include "RawFileUtils.h" +#include "../../CCFileUtils-ohos.h" + +NativeResourceManager* RawFileUtils::nativeResourceManager_ = nullptr; + +bool RawFileUtils::InitResourceManager(napi_env env, napi_value param) { + nativeResourceManager_ = OH_ResourceManager_InitNativeResourceManager(env, param); + OHOS_LOGD("cocos qgh initResourceManager %{public}p", nativeResourceManager_); + cocos2d::FileUtilsOhos::setassetmanager(nativeResourceManager_); + return true; +} + +std::vector RawFileUtils::searchFiles(const char *folder, bool recursive) { + std::vector results; + char *realFolder = const_cast (folder); + if (strcmp(folder, "/") == 0) { + realFolder = ""; + } + auto dir = OH_ResourceManager_OpenRawDir(nativeResourceManager_, realFolder); + if (dir) { + int file_count = GetDirSize(dir); + for (int index = 0; index < file_count; index++) { + std::string fileName = OH_ResourceManager_GetRawFileName(dir, index); + auto item = std::string(realFolder) + "/" + fileName; + results.push_back(item); + if (recursive) { + auto items = searchFiles(item.c_str(), recursive); + results.insert(results.end(), items.begin(), items.end()); + } + } + OH_ResourceManager_CloseRawDir(dir); + } + return results; +} \ No newline at end of file diff --git a/cocos/platform/ohos/napi/modules/RawFileUtils.h b/cocos/platform/ohos/napi/modules/RawFileUtils.h new file mode 100644 index 000000000000..86fec15b7fe1 --- /dev/null +++ b/cocos/platform/ohos/napi/modules/RawFileUtils.h @@ -0,0 +1,84 @@ +#ifndef __RawFileUtils_H__ +#define __RawFileUtils_H__ + + +#include +#include + +#include +#include +#include + +#include "../common/native_common.h" + +#include +#include +#include + +class RawFileUtils { +public: + static bool InitResourceManager(napi_env env, napi_value info); + + static napi_value nativeResourceManagerInit(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + InitResourceManager(env, args[0]); + return nullptr; + } + + static RawFileUtils& GetInstance() { + static RawFileUtils instance; + return instance; + } + + RawFile *Open(const char *fileName) { + return OH_ResourceManager_OpenRawFile(nativeResourceManager_, fileName); + } + + RawDir *OpenDir(const char *dirName) { + return OH_ResourceManager_OpenRawDir(nativeResourceManager_, dirName); + } + + void Close(RawFile *file) { + return OH_ResourceManager_CloseRawFile(file); + } + + void CloseDir(RawDir *rawDir) { + return OH_ResourceManager_CloseRawDir(rawDir); + } + + int Seek(const RawFile *file, long offset, int whence) { + return OH_ResourceManager_SeekRawFile(file, offset, whence); + } + + long GetSize(RawFile* file) { + return OH_ResourceManager_GetRawFileSize(file); + } + + long Read(RawFile *file, void* buf, size_t length) { + return OH_ResourceManager_ReadRawFile(file, buf, length); + } + + int GetDirSize(RawDir* rawDir) { + return OH_ResourceManager_GetRawFileCount(rawDir); + } + + bool GetRawFileDescriptor(RawFile *file, RawFileDescriptor &descriptor) { + return OH_ResourceManager_GetRawFileDescriptor(file, descriptor); + } + + bool ReleaseRawFileDescriptor(RawFileDescriptor &descriptor) { + return OH_ResourceManager_ReleaseRawFileDescriptor(descriptor); + } + + std::vector searchFiles(const char *folder, bool recursive = false); + + static NativeResourceManager* GetRM() { return nativeResourceManager_;} +private: + static NativeResourceManager* nativeResourceManager_; +}; + + + +#endif // __RawFileUtils_H__ diff --git a/cocos/platform/ohos/napi/modules/SensorNapi.cpp b/cocos/platform/ohos/napi/modules/SensorNapi.cpp new file mode 100644 index 000000000000..b8f3eeb56449 --- /dev/null +++ b/cocos/platform/ohos/napi/modules/SensorNapi.cpp @@ -0,0 +1,63 @@ +#include +#include +#include "cocos2d.h" +#include "base/CCEventAcceleration.h" +#include "platform/ohos/CCLogOhos.h" +#include "SensorNapi.h" +#include "../common/native_common.h" + +napi_value SensorNapi::onAccelerometerCallBack(napi_env env, napi_callback_info info) { + napi_status status; + size_t argc = 4; + napi_value args[4]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + if (argc != 4) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return nullptr; + } + + napi_valuetype valuetype; + status = napi_typeof(env, args[0], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_number) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + status = napi_typeof(env, args[3], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_number) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + double x; + NAPI_CALL(env, napi_get_value_double(env, args[0], &x)); + double y; + NAPI_CALL(env, napi_get_value_double(env, args[1], &y)); + double z; + NAPI_CALL(env, napi_get_value_double(env, args[2], &z)); + long timestamp; + NAPI_CALL(env, napi_get_value_int64(env, args[3], ×tamp)); + + dispatchAccelerometer(x, y, z, timestamp); + return nullptr; +} + +#define TG3_GRAVITY_EARTH (9.80665f) + +void SensorNapi::dispatchAccelerometer(double x, double y, double z, long timeStamp) { + OHOS_LOGD("accelerometer callback, interval is %{public}d", timeStamp); + cocos2d::Acceleration a; + a.x = -(x / TG3_GRAVITY_EARTH); + a.y = -(y / TG3_GRAVITY_EARTH); + a.z = -(z / TG3_GRAVITY_EARTH); + a.timestamp = (double)timeStamp; + + cocos2d::EventAcceleration event(a); + cocos2d::Director::getInstance()->getEventDispatcher()->dispatchEvent(&event); +} \ No newline at end of file diff --git a/cocos/platform/ohos/napi/modules/SensorNapi.h b/cocos/platform/ohos/napi/modules/SensorNapi.h new file mode 100644 index 000000000000..2325d5767c54 --- /dev/null +++ b/cocos/platform/ohos/napi/modules/SensorNapi.h @@ -0,0 +1,14 @@ +#ifndef _SENSOR_NAPI_H +#define _SENSOR_NAPI_H + +#include + +class SensorNapi { +public: + static napi_value onAccelerometerCallBack(napi_env env, napi_callback_info info); + +private: + static void dispatchAccelerometer(double x, double y, double z, long timeStamp); +}; + +#endif //_SENSOR_NAPI_H \ No newline at end of file diff --git a/cocos/platform/ohos/napi/modules/TouchesNapi.cpp b/cocos/platform/ohos/napi/modules/TouchesNapi.cpp new file mode 100644 index 000000000000..e4e2398e656d --- /dev/null +++ b/cocos/platform/ohos/napi/modules/TouchesNapi.cpp @@ -0,0 +1,46 @@ +#include "deprecated/CCSet.h" +#include "base/CCDirector.h" +#include "base/CCTouch.h" +#include "platform/CCGLView.h" +#include "platform/ohos/CCLogOhos.h" +#include + +using namespace cocos2d; + +extern "C" { + void Cocos2dxRenderer_nativeTouchesBegin(int num, intptr_t ids[] , float xs[], float ys[]) { + cocos2d::Director::sharedDirector()->getOpenGLView()->handleTouchesBegin(num, ids, xs, ys); + } + + void Cocos2dxRenderer_nativeTouchesEnd(intptr_t id, float x, float y) { + cocos2d::Director::sharedDirector()->getOpenGLView()->handleTouchesEnd(1, &id, &x, &y); + } + + void Cocos2dxRenderer_nativeTouchesMove(int num, intptr_t ids[] , float xs[], float ys[]) { + cocos2d::Director::sharedDirector()->getOpenGLView()->handleTouchesMove(num, ids, xs, ys); + } + + void Cocos2dxRenderer_nativeTouchesCancel(int num, intptr_t ids[] , float xs[], float ys[]) { + cocos2d::Director::sharedDirector()->getOpenGLView()->handleTouchesCancel(num, ids, xs, ys); + } + + #define KEYCODE_BACK 0x04 + #define KEYCODE_MENU 0x52 + bool Cocos2dxRenderer_nativeKeyDown(int keyCode) { + Director* pDirector = Director::sharedDirector(); + switch (keyCode) { + case KEYCODE_BACK: + // TBD need fixed +// if (pDirector->getKeypadDispatcher()->dispatchKeypadMSG(kTypeBackClicked)) + return true; + break; + case KEYCODE_MENU: +// if (pDirector->getKeypadDispatcher()->dispatchKeypadMSG(kTypeMenuClicked)) + return true; + break; + default: + return false; + } + return 0; + } +} diff --git a/cocos/platform/ohos/napi/modules/TouchesNapi.h b/cocos/platform/ohos/napi/modules/TouchesNapi.h new file mode 100644 index 000000000000..6dd5aefc42f8 --- /dev/null +++ b/cocos/platform/ohos/napi/modules/TouchesNapi.h @@ -0,0 +1,21 @@ +#ifndef __TouchesNapi_H__ +#define __TouchesNapi_H__ + +#include "deprecated/CCSet.h" +#include "base/CCDirector.h" +#include "base/CCTouch.h" +#include "platform/CCGLView.h" +#include "iostream" +#include "platform/ohos/CCLogOhos.h" + +using namespace cocos2d; + +extern "C" { + void Cocos2dxRenderer_nativeTouchesBegin(int num, intptr_t ids[], float xs[], float ys[]); + void Cocos2dxRenderer_nativeTouchesEnd(intptr_t id, float x, float y); + void Cocos2dxRenderer_nativeTouchesMove(int num, intptr_t ids[], float xs[], float ys[]); + void Cocos2dxRenderer_nativeTouchesCancel(int num, intptr_t ids[], float xs[], float ys[]); + bool Cocos2dxRenderer_nativeKeyDown(int keyCode); +} + +#endif diff --git a/cocos/platform/ohos/napi/modules/VideoPlayerNapi.cpp b/cocos/platform/ohos/napi/modules/VideoPlayerNapi.cpp new file mode 100644 index 000000000000..367410c2e161 --- /dev/null +++ b/cocos/platform/ohos/napi/modules/VideoPlayerNapi.cpp @@ -0,0 +1,45 @@ +#include "VideoPlayerNapi.h" +#include "platform/ohos/napi/plugin_manager.h" +#include "../../CCLogOhos.h" +#include "ui/UIVideoPlayer-ohos.h" +#include +#include + +napi_value VideoPlayerNapi::onVideoCallBack(napi_env env, napi_callback_info info) { + + napi_status status; + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + if (argc != 2) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return nullptr; + } + + napi_valuetype valuetype; + status = napi_typeof(env, args[0], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_number) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + status = napi_typeof(env, args[1], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_number) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + int32_t viewTag; + NAPI_CALL(env, napi_get_value_int32(env, args[0], &viewTag)); + int32_t event; + NAPI_CALL(env, napi_get_value_int32(env, args[1], &event)); + + executeVideoCallback(viewTag, event); + return nullptr; +} \ No newline at end of file diff --git a/cocos/platform/ohos/napi/modules/VideoPlayerNapi.h b/cocos/platform/ohos/napi/modules/VideoPlayerNapi.h new file mode 100644 index 000000000000..d8ad5380c0c6 --- /dev/null +++ b/cocos/platform/ohos/napi/modules/VideoPlayerNapi.h @@ -0,0 +1,6 @@ +#include + +class VideoPlayerNapi { +public: + static napi_value onVideoCallBack(napi_env env, napi_callback_info info); +}; \ No newline at end of file diff --git a/cocos/platform/ohos/napi/modules/WebViewNapi.cpp b/cocos/platform/ohos/napi/modules/WebViewNapi.cpp new file mode 100644 index 000000000000..f70a4c4605c5 --- /dev/null +++ b/cocos/platform/ohos/napi/modules/WebViewNapi.cpp @@ -0,0 +1,167 @@ +#include +#include + +#include "WebViewNapi.h" +#include "platform/ohos/napi/plugin_manager.h" +#include "../../CCLogOhos.h" +#include "ui/UIWebViewImpl-ohos.h" + +napi_value WebViewNapi::shouldStartLoading(napi_env env, napi_callback_info info) { + + napi_status status; + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + if (argc != 2) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return nullptr; + } + + napi_valuetype valuetype; + status = napi_typeof(env, args[0], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_number) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + status = napi_typeof(env, args[1], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_string) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + int32_t viewTag; + NAPI_CALL(env, napi_get_value_int32(env, args[0], &viewTag)); + size_t pInt; + char url[256]; + NAPI_CALL(env, napi_get_value_string_utf8(env, args[1], url, 256, &pInt)); + + cocos2d::experimental::ui::WebViewImpl::shouldStartLoading(viewTag, url); + return nullptr; +} + +napi_value WebViewNapi::finishLoading(napi_env env, napi_callback_info info) { + + napi_status status; + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + if (argc != 2) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return nullptr; + } + + napi_valuetype valuetype; + status = napi_typeof(env, args[0], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_number) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + status = napi_typeof(env, args[1], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_string) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + int32_t viewTag; + NAPI_CALL(env, napi_get_value_int32(env, args[0], &viewTag)); + size_t pInt; + char url[256]; + NAPI_CALL(env, napi_get_value_string_utf8(env, args[1], url, 256, &pInt)); + + cocos2d::experimental::ui::WebViewImpl::finishLoading(viewTag, url); + return nullptr; +} + +napi_value WebViewNapi::failLoading(napi_env env, napi_callback_info info) { + + napi_status status; + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + if (argc != 2) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return nullptr; + } + + napi_valuetype valuetype; + status = napi_typeof(env, args[0], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_number) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + status = napi_typeof(env, args[1], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_string) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + int32_t viewTag; + NAPI_CALL(env, napi_get_value_int32(env, args[0], &viewTag)); + size_t pInt; + char url[256]; + NAPI_CALL(env, napi_get_value_string_utf8(env, args[1], url, 256, &pInt)); + + cocos2d::experimental::ui::WebViewImpl::failLoading(viewTag, url); + return nullptr; +} + +napi_value WebViewNapi::jsCallback(napi_env env, napi_callback_info info) { + + napi_status status; + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + if (argc != 2) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return nullptr; + } + + napi_valuetype valuetype; + status = napi_typeof(env, args[0], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_number) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + status = napi_typeof(env, args[1], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_string) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + int32_t viewTag; + NAPI_CALL(env, napi_get_value_int32(env, args[0], &viewTag)); + size_t pInt; + char url[256]; + NAPI_CALL(env, napi_get_value_string_utf8(env, args[1], url, 256, &pInt)); + + cocos2d::experimental::ui::WebViewImpl::jsCallback(viewTag, url); + return nullptr; +} \ No newline at end of file diff --git a/cocos/platform/ohos/napi/modules/WebViewNapi.h b/cocos/platform/ohos/napi/modules/WebViewNapi.h new file mode 100644 index 000000000000..64dd953f5164 --- /dev/null +++ b/cocos/platform/ohos/napi/modules/WebViewNapi.h @@ -0,0 +1,15 @@ +#ifndef _WEB_VIEW_NAPI_H +#define _WEB_VIEW_NAPI_H + +#include +#include + +class WebViewNapi { +public: + static napi_value shouldStartLoading(napi_env env, napi_callback_info info); + static napi_value finishLoading(napi_env env, napi_callback_info info); + static napi_value failLoading(napi_env env, napi_callback_info info); + static napi_value jsCallback(napi_env env, napi_callback_info info); +}; + +#endif //_WEB_VIEW_NAPI_H \ No newline at end of file diff --git a/cocos/platform/ohos/napi/plugin_manager.cpp b/cocos/platform/ohos/napi/plugin_manager.cpp new file mode 100644 index 000000000000..c85f68e521ee --- /dev/null +++ b/cocos/platform/ohos/napi/plugin_manager.cpp @@ -0,0 +1,289 @@ +#include +#include +#include + +#include + +#include "modules/RawFileUtils.h" +#include "modules/InputNapi.h" +#include "modules/MouseNapi.h" +#include "modules/WebViewNapi.h" +#include "modules/SensorNapi.h" +#include "modules/VideoPlayerNapi.h" +#include "plugin_manager.h" +#include "../CCLogOhos.h" +#include "cocos2d.h" +#include "platform/CCApplication.h" +#include "platform/ohos/CCFileUtils-ohos.h" +#include "platform/ohos/napi/helper/JSRegisterUtils.h" +#include "platform/ohos/napi/helper/Js_Cocos2dxHelper.h" +#include "base/CCDirector.h" +#include "base/CCEventKeyboard.h" + +const int32_t kMaxStringLen = 512; +enum ContextType { + APP_LIFECYCLE = 0, + JS_PAGE_LIFECYCLE, + RAW_FILE_UTILS, + WORKER_INIT, + NATIVE_API, + INPUT_NAPI, + MOUSE_NAPI, + WEBVIEW_NAPI, + VIDEOPLAYER_NAPI, + SENSOR_API +}; + +NapiManager NapiManager::manager_; + +napi_value NapiManager::GetContext(napi_env env, napi_callback_info info) { + napi_status status; + napi_value exports; + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + + if (argc != 1) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return nullptr; + } + + napi_valuetype valuetype; + status = napi_typeof(env, args[0], &valuetype); + if (status != napi_ok) { + return nullptr; + } + if (valuetype != napi_number) { + napi_throw_type_error(env, NULL, "Wrong arguments"); + return nullptr; + } + + int64_t value; + NAPI_CALL(env, napi_get_value_int64(env, args[0], &value)); + + NAPI_CALL(env, napi_create_object(env, &exports)); + + switch (value) { + case APP_LIFECYCLE: { + /**** AppInit corresponds to the application life cycle in app.ets. onCreate, onShow, onHide, onDestroy ******/ + OHOS_LOGD("GetContext APP_LIFECYCLE"); + /**** Register App Lifecycle ******/ + napi_property_descriptor desc[] = { + DECLARE_NAPI_FUNCTION("onCreate", NapiManager::NapiOnCreate), + DECLARE_NAPI_FUNCTION("onShow", NapiManager::NapiOnShow), + DECLARE_NAPI_FUNCTION("onHide", NapiManager::NapiOnHide), + DECLARE_NAPI_FUNCTION("onBackPress", NapiManager::NapiOnBackPress), + DECLARE_NAPI_FUNCTION("onDestroy", NapiManager::NapiOnDestroy), + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + } + break; + case JS_PAGE_LIFECYCLE: { + /**************** Declarative Development Paradigm JS Page Lifecycle Registration ****************************/ + OHOS_LOGD("GetContext JS_PAGE_LIFECYCLE"); + napi_property_descriptor desc[] = { + DECLARE_NAPI_FUNCTION("onPageShow", NapiManager::NapiOnPageShow), + DECLARE_NAPI_FUNCTION("onPageHide", NapiManager::NapiOnPageHide), + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + } + break; + case RAW_FILE_UTILS: { + /**************** Declarative Development Paradigm JS Page Lifecycle Registration ****************************/ + OHOS_LOGD("GetContext RAW_FILE_UTILS"); + napi_property_descriptor desc[] = { + DECLARE_NAPI_FUNCTION("nativeResourceManagerInit", RawFileUtils::nativeResourceManagerInit), + DECLARE_NAPI_FUNCTION("writablePathInit", NapiManager::napiWritablePathInit), + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + + } + break; + case WORKER_INIT: { + OHOS_LOGD("NapiManager::GetContext WORKER_INIT"); + napi_property_descriptor desc[] = { + DECLARE_NAPI_FUNCTION("workerInit", NapiManager::napiWorkerInit), + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + } + break; + case NATIVE_API: { + OHOS_LOGD("NapiManager::GetContext NATIVE_RENDER_API"); + napi_property_descriptor desc[] = { + DECLARE_NAPI_FUNCTION("nativeEngineStart", NapiManager::napiNativeEngineStart), + DECLARE_NAPI_FUNCTION("registerFunction", registerFunction), + DECLARE_NAPI_FUNCTION("initAsyncInfo", Js_Cocos2dxHelper::initAsyncInfo), + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + } + break; + case INPUT_NAPI: { + OHOS_LOGD("NapiManager::GetContext INPUT_NAPI"); + napi_property_descriptor desc[] = { + DECLARE_NAPI_FUNCTION("editBoxOnFocusCB", InputNapi::editBoxOnFocusCB), + DECLARE_NAPI_FUNCTION("editBoxOnChangeCB", InputNapi::editBoxOnChangeCB), + DECLARE_NAPI_FUNCTION("editBoxOnEnterCB", InputNapi::editBoxOnEnterCB), + DECLARE_NAPI_FUNCTION("textFieldTTFOnChangeCB", InputNapi::textFieldTTFOnChangeCB), + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + } + break; + case MOUSE_NAPI: + { + OHOS_LOGD("NapiManager::GetContext INPUT_NAPI"); + napi_property_descriptor desc[] = { + DECLARE_NAPI_FUNCTION("mouseWheelCB", MouseNapi::mouseWheelCB), + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + } + break; + case WEBVIEW_NAPI: + { + OHOS_LOGD("NapiManager::GetContext WEBVIEW_NAPI"); + napi_property_descriptor desc[] = { + DECLARE_NAPI_FUNCTION("shouldStartLoading", WebViewNapi::shouldStartLoading), + DECLARE_NAPI_FUNCTION("finishLoading", WebViewNapi::finishLoading), + DECLARE_NAPI_FUNCTION("failLoading", WebViewNapi::failLoading), + DECLARE_NAPI_FUNCTION("jsCallback", WebViewNapi::jsCallback), + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + } + break; + case VIDEOPLAYER_NAPI: { + OHOS_LOGE("VideoPlayerNapi::Export"); + napi_property_descriptor desc[] = { + DECLARE_NAPI_FUNCTION("onVideoCallBack", VideoPlayerNapi::onVideoCallBack), + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + OHOS_LOGE("VideoPlayerNapi::Export finish"); + } + break; + case SENSOR_API: { + OHOS_LOGD("NapiManager::GetContext SENSOR_API"); + napi_property_descriptor desc[] = { + DECLARE_NAPI_FUNCTION("onAccelerometerCallBack", SensorNapi::onAccelerometerCallBack), + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + } + break; + default: + OHOS_LOGE("unknown type"); + } + return exports; +} + +bool NapiManager::Export(napi_env env, napi_value exports) { + OHOS_LOGD("NapiManager::Export"); + napi_status status; + napi_value exportInstance = nullptr; + OH_NativeXComponent *nativeXComponent = nullptr; + int32_t ret; + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { }; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + + status = napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance); + if (status != napi_ok) { + return false; + } + + status = napi_unwrap(env, exportInstance, reinterpret_cast(&nativeXComponent)); + if (status != napi_ok) { + return false; + } + + auto context = NapiManager::GetInstance(); + if (context) { + context->SetNativeXComponent(nativeXComponent); + PluginRender::GetInstance()->SetNativeXComponent(nativeXComponent); + PluginRender::GetInstance()->Export(env, exports); + return true; + } + return false; +} + +void NapiManager::SetNativeXComponent(OH_NativeXComponent* nativeXComponent) { + nativeXComponent_ = nativeXComponent; +} + +OH_NativeXComponent* NapiManager::GetNativeXComponent() { + return nativeXComponent_; +} + +void NapiManager::MainOnMessage(const uv_async_t* req) { + OHOS_LOGD("MainOnMessage Triggered"); +} + +napi_value NapiManager::NapiOnCreate(napi_env env, napi_callback_info info) { + return nullptr; +} + +napi_value NapiManager::NapiOnShow(napi_env env, napi_callback_info info) { + WorkerMessageData data{MessageType::WM_APP_SHOW, nullptr, nullptr}; + PluginRender::GetInstance()->enqueue(data); + return nullptr; +} + +napi_value NapiManager::NapiOnHide(napi_env env, napi_callback_info info) { + WorkerMessageData data{MessageType::WM_APP_HIDE, nullptr, nullptr}; + PluginRender::GetInstance()->enqueue(data); + return nullptr; +} + +napi_value NapiManager::NapiOnBackPress(napi_env env, napi_callback_info info) +{ + OHOS_LOGD("NapiManager::NapiOnBackPress"); + cocos2d::EventKeyboard event(cocos2d::EventKeyboard::KeyCode::KEY_BACK, false); + cocos2d::Director::getInstance()->getEventDispatcher()->dispatchEvent(&event); + return nullptr; +} + +napi_value NapiManager::NapiOnDestroy(napi_env env, napi_callback_info info) { + WorkerMessageData data{MessageType::WM_APP_DESTROY, nullptr, nullptr}; + PluginRender::GetInstance()->enqueue(data); + return nullptr; +} + +napi_value NapiManager::napiWorkerInit(napi_env env, napi_callback_info info) { + OHOS_LOGD("NapiManager::napiWorkerInit"); + uv_loop_t* loop = nullptr; + NAPI_CALL(env, napi_get_uv_event_loop(env, &loop)); + PluginRender::GetInstance()->workerInit(env, loop); + return nullptr; +} + +napi_value NapiManager::napiNativeEngineStart(napi_env env, napi_callback_info info) { + OHOS_LOGD("NapiManager::napiNativeEngineStart"); + PluginRender::GetInstance()->run(); + Js_Cocos2dxHelper::initJsCocos2dxHelper(env, nullptr); + return nullptr; +} + +napi_value NapiManager::napiWritablePathInit(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + + char buffer[kMaxStringLen]; + size_t result = 0; + NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], buffer, kMaxStringLen, &result)); + cocos2d::FileUtilsOhos::ohWritablePath = std::string(buffer) + '/'; + return nullptr; +} + +napi_value NapiManager::NapiOnPageShow(napi_env env, napi_callback_info info) { + OHOS_LOGD("NapiManager::NapiOnPageShow"); + return nullptr; +} + +napi_value NapiManager::NapiOnPageHide(napi_env env, napi_callback_info info) { + OHOS_LOGD("NapiManager::NapiOnPageHide"); + return nullptr; +} + +void NapiManager::OnPageShowNative() { + OHOS_LOGD("NapiManager::OnPageShowNative"); +} + +void NapiManager::OnPageHideNative() { + OHOS_LOGD("NapiManager::OnPageHideNative"); +} diff --git a/cocos/platform/ohos/napi/plugin_manager.h b/cocos/platform/ohos/napi/plugin_manager.h new file mode 100644 index 000000000000..73768cae0be5 --- /dev/null +++ b/cocos/platform/ohos/napi/plugin_manager.h @@ -0,0 +1,64 @@ +#ifndef _PLUGIN_MANAGER_H_ +#define _PLUGIN_MANAGER_H_ + +#include +#include + +#include +#include +#include + +#include "native_common.h" +#include "WorkerMessageQueue.h" +#include "plugin_render.h" + +class NapiManager { +public: + ~NapiManager() {} + + static NapiManager* GetInstance() + { + return &NapiManager::manager_; + } + + static napi_value GetContext(napi_env env, napi_callback_info info); + + /******************************APP Lifecycle******************************/ + static napi_value NapiOnCreate(napi_env env, napi_callback_info info); + static napi_value NapiOnShow(napi_env env, napi_callback_info info); + static napi_value NapiOnHide(napi_env env, napi_callback_info info); + static napi_value NapiOnBackPress(napi_env env, napi_callback_info info); + static napi_value NapiOnDestroy(napi_env env, napi_callback_info info); + /*********************************************************************/ + + /******************************JS Page : Lifecycle*****************************/ + static napi_value NapiOnPageShow(napi_env env, napi_callback_info info); + static napi_value NapiOnPageHide(napi_env env, napi_callback_info info); + void OnPageShowNative(); + void OnPageHideNative(); + /*************************************************************************/ + + // Worker Func + static napi_value napiWorkerInit(napi_env env, napi_callback_info info); + static napi_value napiNativeEngineStart(napi_env env, napi_callback_info info); + static napi_value napiWritablePathInit(napi_env env, napi_callback_info info); + + OH_NativeXComponent* GetNativeXComponent(); + void SetNativeXComponent(OH_NativeXComponent* nativeXComponent); + +public: + // Napi export + bool Export(napi_env env, napi_value exports); +private: + static void MainOnMessage(const uv_async_t* req); + static NapiManager manager_; + + OH_NativeXComponent* nativeXComponent_ = nullptr; + +public: + napi_env mainEnv_ = nullptr; + uv_loop_t* mainLoop_ = nullptr; + uv_async_t mainOnMessageSignal_ {}; +}; + +#endif // _PLUGIN_MANAGER_H_ \ No newline at end of file diff --git a/cocos/platform/ohos/napi/render/egl_core.cpp b/cocos/platform/ohos/napi/render/egl_core.cpp new file mode 100644 index 000000000000..65d6e5330576 --- /dev/null +++ b/cocos/platform/ohos/napi/render/egl_core.cpp @@ -0,0 +1,113 @@ +#include "egl_core.h" + +#include "../../CCLogOhos.h" +#include "plugin_render.h" +#include +#include + +EGLConfig getConfig(int version, EGLDisplay eglDisplay) { + int attribList[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_STENCIL_SIZE, 8, + EGL_DEPTH_SIZE, 24, + EGL_NONE + }; + EGLConfig configs = NULL; + int configsNum; + if (!eglChooseConfig(eglDisplay, attribList, &configs, 1, &configsNum)) { + OHOS_LOGE("eglChooseConfig ERROR"); + return NULL; + } + return configs; +} + +void EGLCore::GLContextInit(void* window, int w, int h) { + OHOS_LOGD("EGLCore::GLContextInit window = %{public}p, w = %{public}d, h = %{public}d.", window, w, h); + width_ = w; + height_ = h; + mEglWindow = (EGLNativeWindowType)(window); + + // 1. create sharedcontext + mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (mEGLDisplay == EGL_NO_DISPLAY) { + OHOS_LOGE("EGLCore::unable to get EGL display."); + return; + } + + EGLint eglMajVers, eglMinVers; + if (!eglInitialize(mEGLDisplay, &eglMajVers, &eglMinVers)) { + mEGLDisplay = EGL_NO_DISPLAY; + OHOS_LOGE("EGLCore::unable to initialize display"); + return; + } + + mEGLConfig = getConfig(3, mEGLDisplay); + if (mEGLConfig == nullptr) { + OHOS_LOGE("EGLCore::GLContextInit config ERROR"); + return; + } + + // 2. Create EGL Surface from Native Window + EGLint winAttribs[] = {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_LINEAR_KHR, EGL_NONE}; + if (mEglWindow) { + mEGLSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mEglWindow, nullptr); + if (mEGLSurface == nullptr) { + OHOS_LOGE("EGLCore::eglCreateContext eglSurface is null"); + return; + } + } + + // 3. Create EGLContext from + int attrib3_list[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + mEGLContext = eglCreateContext(mEGLDisplay, mEGLConfig, mSharedEGLContext, attrib3_list); + + if (!eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) { + OHOS_LOGE("EGLCore::eglMakeCurrent error = %{public}d", eglGetError()); + } +} + +void EGLCore::Update() { + eglSwapBuffers(mEGLDisplay, mEGLSurface); +} + +bool EGLCore::checkGlError(const char* op) { + OHOS_LOGE("EGL ERROR CODE = %{public}x", eglGetError()); + GLint error; + for (error = glGetError(); error; error = glGetError()) { + OHOS_LOGE("ERROR: %{public}s, ERROR CODE = %{public}x", op, error); + return true; + } + return false; +} + +void EGLCore::destroySurface() { + if(!eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { + OHOS_LOGE("eglMakeCurrent error = %{public}d", eglGetError()); + } + eglDestroySurface(mEGLDisplay, mEGLSurface); + mEGLSurface = nullptr; +} + +void EGLCore::createSurface(void* window) { + mEglWindow = (EGLNativeWindowType)(window); + if(mEglWindow) { + mEGLSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mEglWindow, NULL); + if(mEGLSurface == nullptr) { + OHOS_LOGE("EGL eglCreateWindowSurface eglSurface is null"); + return; + } + } + if(!(eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext))){ + OHOS_LOGE("eglMakeCurrent error = %{public}d", eglGetError()); + } + return; +} diff --git a/cocos/platform/ohos/napi/render/egl_core.h b/cocos/platform/ohos/napi/render/egl_core.h new file mode 100644 index 000000000000..6d9efee79e36 --- /dev/null +++ b/cocos/platform/ohos/napi/render/egl_core.h @@ -0,0 +1,32 @@ +#ifndef _GL_CORE_ +#define _GL_CORE_ + +#include +#include +#include + +#include + +class EGLCore { +public: + EGLCore() {}; + void GLContextInit(void* window, int w, int h); + void Update(); + void destroySurface(); + void createSurface(void* window); +public: + int width_; + int height_; + +private: + bool checkGlError(const char* op); + + EGLNativeWindowType mEglWindow; + EGLDisplay mEGLDisplay = EGL_NO_DISPLAY; + EGLConfig mEGLConfig = nullptr; + EGLContext mEGLContext = EGL_NO_CONTEXT; + EGLContext mSharedEGLContext = EGL_NO_CONTEXT; + EGLSurface mEGLSurface = nullptr; +}; + +#endif // _GL_CORE_ diff --git a/cocos/platform/ohos/napi/render/plugin_render.cpp b/cocos/platform/ohos/napi/render/plugin_render.cpp new file mode 100644 index 000000000000..9a2d62e25334 --- /dev/null +++ b/cocos/platform/ohos/napi/render/plugin_render.cpp @@ -0,0 +1,575 @@ +#include +#include + +#include "plugin_render.h" +#include "platform/ohos/napi/plugin_manager.h" +#include "../modules/TouchesNapi.h" +#include "../helper/NapiHelper.h" +#include "platform/ohos/CCLogOhos.h" +#include "cocos2d.h" +#include "native_window/external_window.h" +#include "native_buffer/native_buffer.h" + +using namespace cocos2d; + +#ifdef __cplusplus +extern "C" { +#endif + +PluginRender* PluginRender::instance_ = nullptr; +OH_NativeXComponent_Callback PluginRender::callback_; +OH_NativeXComponent_MouseEvent_Callback PluginRender::mouseCallback_; +std::queue PluginRender::keyEventQueue_; +std::queue PluginRender::mouseEventQueue_; +std::queue PluginRender::mouseWheelEventQueue_; +uint64_t PluginRender::animationInterval_ = 16; +uint64_t PluginRender::lastTime = 0; +const int keyCodeUnknownInOH = -1; +const int keyActionUnknownInOH = -1; +float mousePositionX = -1; +float mousePositionY = -1; +bool isMouseLeftActive = false; +double scrollDistance = 0; + +std::unordered_map ohKeyMap = { + {KEY_ESCAPE, cocos2d::EventKeyboard::KeyCode::KEY_ESCAPE}, + {KEY_GRAVE, cocos2d::EventKeyboard::KeyCode::KEY_GRAVE}, + {KEY_MINUS, cocos2d::EventKeyboard::KeyCode::KEY_MINUS}, + {KEY_EQUALS, cocos2d::EventKeyboard::KeyCode::KEY_EQUAL}, + {KEY_DEL, cocos2d::EventKeyboard::KeyCode::KEY_BACKSPACE}, + {KEY_TAB, cocos2d::EventKeyboard::KeyCode::KEY_TAB}, + {KEY_LEFT_BRACKET, cocos2d::EventKeyboard::KeyCode::KEY_LEFT_BRACKET}, + {KEY_RIGHT_BRACKET, cocos2d::EventKeyboard::KeyCode::KEY_RIGHT_BRACKET}, + {KEY_BACKSLASH, cocos2d::EventKeyboard::KeyCode::KEY_BACK_SLASH}, + {KEY_CAPS_LOCK, cocos2d::EventKeyboard::KeyCode::KEY_CAPS_LOCK}, + {KEY_SEMICOLON, cocos2d::EventKeyboard::KeyCode::KEY_SEMICOLON}, + {KEY_APOSTROPHE, cocos2d::EventKeyboard::KeyCode::KEY_QUOTE}, + {KEY_ENTER, cocos2d::EventKeyboard::KeyCode::KEY_ENTER}, + {KEY_SHIFT_LEFT, cocos2d::EventKeyboard::KeyCode::KEY_LEFT_SHIFT}, + {KEY_COMMA, cocos2d::EventKeyboard::KeyCode::KEY_COMMA}, + {KEY_PERIOD, cocos2d::EventKeyboard::KeyCode::KEY_PERIOD}, + {KEY_SLASH, cocos2d::EventKeyboard::KeyCode::KEY_SLASH}, + {KEY_SHIFT_RIGHT, cocos2d::EventKeyboard::KeyCode::KEY_RIGHT_SHIFT}, + {KEY_CTRL_LEFT, cocos2d::EventKeyboard::KeyCode::KEY_LEFT_CTRL}, + {KEY_ALT_LEFT, cocos2d::EventKeyboard::KeyCode::KEY_LEFT_ALT}, + {KEY_SPACE, cocos2d::EventKeyboard::KeyCode::KEY_SPACE}, + {KEY_ALT_RIGHT, cocos2d::EventKeyboard::KeyCode::KEY_RIGHT_ALT}, + {KEY_CTRL_RIGHT, cocos2d::EventKeyboard::KeyCode::KEY_RIGHT_CTRL}, + {KEY_DPAD_LEFT, cocos2d::EventKeyboard::KeyCode::KEY_DPAD_LEFT}, + {KEY_DPAD_RIGHT, cocos2d::EventKeyboard::KeyCode::KEY_DPAD_RIGHT}, + {KEY_DPAD_DOWN, cocos2d::EventKeyboard::KeyCode::KEY_DPAD_DOWN}, + {KEY_DPAD_UP, cocos2d::EventKeyboard::KeyCode::KEY_DPAD_UP}, + {KEY_SYSRQ, cocos2d::EventKeyboard::KeyCode::KEY_PRINT}, + {KEY_INSERT, cocos2d::EventKeyboard::KeyCode::KEY_INSERT}, + {KEY_FORWARD_DEL, cocos2d::EventKeyboard::KeyCode::KEY_DELETE} +}; + +cocos2d::EventKeyboard::KeyCode ohKeyCodeToCocosKeyCode(OH_NativeXComponent_KeyCode ohKeyCode) +{ + auto it = ohKeyMap.find(ohKeyCode); + if (it != ohKeyMap.end()) { + return static_cast(it->second); + } + if (ohKeyCode >= KEY_0 && ohKeyCode <= KEY_9) { + // 0 - 9 + return cocos2d::EventKeyboard::KeyCode(int(cocos2d::EventKeyboard::KeyCode::KEY_0) + (ohKeyCode - KEY_0)); + } + if (ohKeyCode >= KEY_F1 && ohKeyCode <= KEY_F12) { + // F1 - F12 + return cocos2d::EventKeyboard::KeyCode(int(cocos2d::EventKeyboard::KeyCode::KEY_F1) + (ohKeyCode - KEY_F1)); + } + if (ohKeyCode >= KEY_A && ohKeyCode <= KEY_Z) { + // A - Z + return cocos2d::EventKeyboard::KeyCode(int(cocos2d::EventKeyboard::KeyCode::KEY_A) + (ohKeyCode - KEY_A)); + } + return cocos2d::EventKeyboard::KeyCode(ohKeyCode); +} + +void OnSurfaceCreatedCB(OH_NativeXComponent* component, void* window) +{ + OHOS_LOGD("OnSurfaceCreatedCB"); + PluginRender::GetInstance()->sendMsgToWorker(MessageType::WM_XCOMPONENT_SURFACE_CREATED, component, window); +} + +void OnSurfaceChangedCB(OH_NativeXComponent* component, void* window) { + OHOS_LOGD("OnSurfaceChangedCB"); + PluginRender::GetInstance()->sendMsgToWorker(MessageType::WM_XCOMPONENT_SURFACE_CHANGED, component, window); +} + +void onSurfaceHideCB(OH_NativeXComponent* component, void* window) { + int32_t ret; + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); + if(ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + return; + } + PluginRender::GetInstance()->sendMsgToWorker(MessageType::WM_XCOMPONENT_SURFACE_HIDE, component, window); +} + +void onSurfaceShowCB(OH_NativeXComponent* component, void* window) { + int32_t ret; + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); + if(ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + return; + } + PluginRender::GetInstance()->sendMsgToWorker(MessageType::WM_XCOMPONENT_SURFACE_SHOW, component, window); +} + +void OnSurfaceDestroyedCB(OH_NativeXComponent* component, void* window) { + OHOS_LOGD("OnSurfaceDestroyedCB"); + PluginRender::GetInstance()->sendMsgToWorker(MessageType::WM_XCOMPONENT_SURFACE_DESTROY, component, window); +} + +void DispatchKeyEventCB(OH_NativeXComponent* component, void* window) +{ + OH_NativeXComponent_KeyEvent* keyEvent; + if (OH_NativeXComponent_GetKeyEvent(component, &keyEvent) >= 0) { + PluginRender::keyEventQueue_.push(keyEvent); + PluginRender::GetInstance()->sendMsgToWorker(MessageType::WM_XCOMPONENT_KEY_EVENT, component, window); + } else { + OHOS_LOGE("OpenHarmonyPlatform::getKeyEventError"); + } +} + +void DispatchMouseEventCB(OH_NativeXComponent* component, void* window) +{ + OH_NativeXComponent_MouseEvent mouseEvent; + int32_t ret = OH_NativeXComponent_GetMouseEvent(component, window, &mouseEvent); + if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + PluginRender::mouseEventQueue_.push(mouseEvent); + PluginRender::GetInstance()->sendMsgToWorker(MessageType::WM_XCOMPONENT_MOUSE_EVENT, component, window); + } else { + OHOS_LOGE("OpenHarmonyPlatform::getMouseEventError"); + } +} + +void DispatchHoverEventCB(OH_NativeXComponent* component, bool isHover) +{ + OHOS_LOGD("OpenHarmonyPlatform::DispatchHoverEventCB"); +} + +void DispatchTouchEventCB(OH_NativeXComponent* component, void* window) { + OH_NativeXComponent_TouchEvent* touchEvent = new(std::nothrow) OH_NativeXComponent_TouchEvent(); + if (!touchEvent) { + OHOS_LOGE("DispatchTouchEventCB::touchEvent alloc failed"); + return; + } + int32_t ret = OH_NativeXComponent_GetTouchEvent(component, window, touchEvent); + if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + PluginRender::GetInstance()->sendMsgToWorker(MessageType::WM_XCOMPONENT_TOUCH_EVENT, component, window, touchEvent); + } else { + delete touchEvent; + } +} + +PluginRender::PluginRender() : component_(nullptr) { + auto renderCallback = PluginRender::GetNXComponentCallback(); + renderCallback->OnSurfaceCreated = OnSurfaceCreatedCB; + renderCallback->OnSurfaceChanged = OnSurfaceChangedCB; + renderCallback->OnSurfaceDestroyed = OnSurfaceDestroyedCB; + renderCallback->DispatchTouchEvent = DispatchTouchEventCB; +} + +PluginRender* PluginRender::GetInstance() { + if (instance_ == nullptr) { + instance_ = new PluginRender(); + } + return instance_; +} + +OH_NativeXComponent_Callback* PluginRender::GetNXComponentCallback() { + return &PluginRender::callback_; +} + +// static +void PluginRender::onMessageCallback(const uv_async_t* /* req */) { + void* window = nullptr; + WorkerMessageData msgData; + PluginRender* render = PluginRender::GetInstance(); + + while (true) { + //loop until all msg dispatch + if (!render->dequeue(reinterpret_cast(&msgData))) { + // Queue has no data + break; + } + + if ((msgData.type >= MessageType::WM_XCOMPONENT_SURFACE_CREATED) && (msgData.type <= MessageType::WM_XCOMPONENT_SURFACE_DESTROY)) { + OH_NativeXComponent* nativexcomponet = reinterpret_cast(msgData.data); + CC_ASSERT(nativexcomponet != nullptr); + + if (msgData.type == MessageType::WM_XCOMPONENT_SURFACE_CREATED) { + render->OnSurfaceCreated(nativexcomponet, msgData.window); + } else if (msgData.type == MessageType::WM_XCOMPONENT_KEY_EVENT) { + render->DispatchKeyEvent(nativexcomponet, msgData.window); + } else if (msgData.type == MessageType::WM_XCOMPONENT_MOUSE_EVENT) { + render->DispatchMouseEvent(nativexcomponet, msgData.window); + } else if (msgData.type == MessageType::WM_XCOMPONENT_MOUSE_WHEEL_EVENT) { + render->DispatchMouseWheelEvent(); + } else if (msgData.type == MessageType::WM_XCOMPONENT_TOUCH_EVENT) { + render->DispatchTouchEvent(nativexcomponet, msgData.window, msgData.touchEvent); + } else if (msgData.type == MessageType::WM_XCOMPONENT_SURFACE_CHANGED) { + render->OnSurfaceChanged(nativexcomponet, msgData.window); + } else if (msgData.type == MessageType::WM_XCOMPONENT_SURFACE_SHOW) { + render->onSurfaceShow(msgData.window); + } else if (msgData.type == MessageType::WM_XCOMPONENT_SURFACE_HIDE) { + render->onSurfaceHide(); + } else if (msgData.type == MessageType::WM_XCOMPONENT_SURFACE_DESTROY) { + render->OnSurfaceDestroyed(nativexcomponet, msgData.window); + } else { + CC_ASSERT(false); + } + continue; + } + + if (msgData.type == MessageType::WM_APP_SHOW) { + render->OnShowNative(); + } else if (msgData.type == MessageType::WM_APP_HIDE) { + render->OnHideNative(); + } else if (msgData.type == MessageType::WM_APP_DESTROY) { + render->OnDestroyNative(); + } + if(msgData.type == MessageType::WM_VSYNC) { + // render->runTask(); + } + } +} + +static uint64_t getCurrentMillSecond() { + struct timeval stCurrentTime; + + gettimeofday(&stCurrentTime,NULL); + return stCurrentTime.tv_sec * 1000 + stCurrentTime.tv_usec / 1000; //millseconds +} + +// static +void PluginRender::timerCb(uv_timer_t* handle) +{ + // OHOS_LOGD("PluginRender::timerCb, animationInterval_ is %{public}lu", animationInterval_); + if (PluginRender::GetInstance()->eglCore_ != nullptr) { + cocos2d::CCDirector::sharedDirector()->mainLoop(); + PluginRender::GetInstance()->eglCore_->Update(); + } + uint64_t curTime = getCurrentMillSecond(); + if (curTime - lastTime < animationInterval_) + { + usleep((animationInterval_ - curTime + lastTime) * 1000); + } + lastTime = getCurrentMillSecond(); +} + +void PluginRender::SetNativeXComponent(OH_NativeXComponent* component) { + component_ = component; + OH_NativeXComponent_RegisterCallback(component_, &PluginRender::callback_); + // register keyEvent + OH_NativeXComponent_RegisterKeyEventCallback(component_, DispatchKeyEventCB); + // register mouseEvent + PluginRender::mouseCallback_.DispatchMouseEvent = DispatchMouseEventCB; + PluginRender::mouseCallback_.DispatchHoverEvent = DispatchHoverEventCB; + OH_NativeXComponent_RegisterMouseEventCallback(component_, &mouseCallback_); + OH_NativeXComponent_RegisterSurfaceHideCallback(component_, onSurfaceHideCB); + OH_NativeXComponent_RegisterSurfaceShowCallback(component_, onSurfaceShowCB); +} + +void PluginRender::workerInit(napi_env env, uv_loop_t* loop) { + OHOS_LOGD("PluginRender::workerInit"); + workerLoop_ = loop; + if (workerLoop_) { + uv_async_init(workerLoop_, &messageSignal_, reinterpret_cast(PluginRender::onMessageCallback)); + if (!messageQueue_.empty()) { + triggerMessageSignal(); // trigger the signal to handle the pending message + } + } +} + +void PluginRender::DispatchKeyEvent(OH_NativeXComponent* component, void* window) +{ + OH_NativeXComponent_KeyEvent* keyEvent; + while (!keyEventQueue_.empty()) { + keyEvent = keyEventQueue_.front(); + keyEventQueue_.pop(); + OH_NativeXComponent_KeyAction action; + OH_NativeXComponent_GetKeyEventAction(keyEvent, &action); + OH_NativeXComponent_KeyCode code; + OH_NativeXComponent_GetKeyEventCode(keyEvent, &code); + if (code == keyCodeUnknownInOH || action == keyActionUnknownInOH) { + // unknown code and action don't callback + return; + } + bool isPressed = action == 0; + EventKeyboard event(ohKeyCodeToCocosKeyCode(code), isPressed); + Director::getInstance()->getEventDispatcher()->dispatchEvent(&event); + } +} + +void PluginRender::DispatchMouseEvent(OH_NativeXComponent* component, void* window) +{ + OH_NativeXComponent_MouseEvent mouseEvent; + while (!mouseEventQueue_.empty()) { + mouseEvent = mouseEventQueue_.front(); + mouseEventQueue_.pop(); + EventMouse::MouseEventType mouseAction; + mousePositionX = mouseEvent.x; + mousePositionY = mouseEvent.y; + switch (mouseEvent.action) { + case 1: + mouseAction = EventMouse::MouseEventType::MOUSE_DOWN; + break; + case 2: + mouseAction = EventMouse::MouseEventType::MOUSE_UP; + break; + case 3: + mouseAction = EventMouse::MouseEventType::MOUSE_MOVE; + break; + default: + mouseAction = EventMouse::MouseEventType::MOUSE_NONE; + break; + } + int mouseButton; + switch (mouseEvent.button) { + case 1: + mouseButton = (int)EventMouse::MouseButton::BUTTON_LEFT; + break; + case 2: + mouseButton = (int)EventMouse::MouseButton::BUTTON_RIGHT; + break; + case 4: + mouseButton = (int)EventMouse::MouseButton::BUTTON_MIDDLE; + break; + default: + mouseButton = (int)EventMouse::MouseButton::BUTTON_UNSET; + break; + } + if (mouseEvent.action == 1 && mouseEvent.button == 1) { + isMouseLeftActive = true; + } + if (mouseEvent.action == 2 && mouseEvent.button == 1) { + isMouseLeftActive = false; + } + EventMouse event(mouseAction); + event.setCursorPosition(mouseEvent.x, mouseEvent.y); + event.setMouseButton(mouseButton); + Director::getInstance()->getEventDispatcher()->dispatchEvent(&event); + } + +} + +void PluginRender::sendMsgToWorker(const MessageType& type, OH_NativeXComponent* component, void* window) { + WorkerMessageData data{type, static_cast(component), window}; + enqueue(data); +} + +void PluginRender::sendMsgToWorker(const MessageType& type, OH_NativeXComponent* component, void* window, OH_NativeXComponent_TouchEvent* touchEvent) { + WorkerMessageData data{type, static_cast(component), window, touchEvent}; + enqueue(data); +} + +void PluginRender::enqueue(const WorkerMessageData& msg) { + messageQueue_.enqueue(msg); + triggerMessageSignal(); +} + +bool PluginRender::dequeue(WorkerMessageData* msg) { + return messageQueue_.dequeue(msg); +} + +void PluginRender::triggerMessageSignal() { + if(workerLoop_ != nullptr) { + // It is possible that when the message is sent, the worker thread has not yet started. + uv_async_send(&messageSignal_); + } +} + +void PluginRender::run() { + OHOS_LOGD("PluginRender::run"); + if (workerLoop_) { + uv_timer_init(workerLoop_, &timerHandle_); + timerInited_ = true; + } +} + +void PluginRender::changeFPS(uint64_t animationInterval) { + OHOS_LOGD("PluginRender::changeFPS, animationInterval from %{public}lu to %{public}lu", animationInterval_, animationInterval); + animationInterval_ = animationInterval; +} + +void Cocos2dxRenderer_nativeInit(int w, int h); +void PluginRender::OnSurfaceCreated(OH_NativeXComponent* component, void* window) { + OHOS_LOGD("PluginRender::OnSurfaceCreated"); + eglCore_ = new EGLCore(); + int32_t ret = OH_NativeXComponent_GetXComponentSize(component, window, &width_, &height_); + if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + int32_t code = SET_USAGE; + OHNativeWindow *nativeWindow = static_cast(window); + int32_t ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow, code, NATIVEBUFFER_USAGE_MEM_DMA); + eglCore_->GLContextInit(window, width_, height_); + Cocos2dxRenderer_nativeInit(width_, height_); + } +} + +void PluginRender::OnSurfaceChanged(OH_NativeXComponent* component, void* window) { + OHOS_LOGD("PluginRender::OnSurfaceChanged"); + int32_t ret = OH_NativeXComponent_GetXComponentSize(component, window, &width_, &height_); + if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + OHOS_LOGD("PluginRender::OnSurfaceChanged, width is %{public}d, height is %{public}d", width_, height_); + cocos2d::Application::getInstance()->applicationScreenSizeChanged(width_, height_); + } +} + +void PluginRender::onSurfaceHide() { + eglCore_->destroySurface(); +} + +void PluginRender::onSurfaceShow(void* window) { + eglCore_->createSurface(window); +} + +void PluginRender::OnSurfaceDestroyed(OH_NativeXComponent* component, void* window) { +} + +void PluginRender::DispatchMouseWheelEvent() +{ + EventMouseWheelData mouseWheelData; + while (!mouseWheelEventQueue_.empty()) { + mouseWheelData = mouseWheelEventQueue_.front(); + mouseWheelEventQueue_.pop(); + EventMouse mouseWheelEvent(cocos2d::EventMouse::MouseEventType::MOUSE_SCROLL); + mouseWheelEvent.setScrollData(0, -mouseWheelData.scrollY); + // mouseWheelEvent.setScrollData(0, -mouseWheelData.scrollY > 0 ? 1 : -1); + mouseWheelEvent.setCursorPosition(mouseWheelData.positonX, mouseWheelData.positonY); + Director::getInstance()->getEventDispatcher()->dispatchEvent(&mouseWheelEvent); + } +} + +void PluginRender::DispatchTouchEvent(OH_NativeXComponent* component, void* window, OH_NativeXComponent_TouchEvent* touchEvent) +{ + intptr_t ids[touchEvent->numPoints]; + float xs[touchEvent->numPoints]; + float ys[touchEvent->numPoints]; + for (int i = 0; i < touchEvent->numPoints; i++) { + ids[i] = touchEvent->touchPoints[i].id; + xs[i] = touchEvent->touchPoints[i].x; + ys[i] = touchEvent->touchPoints[i].y; + OHOS_LOGD("Touch Info : x = %{public}f, y = %{public}f", xs[i], ys[i]); + } + switch (touchEvent -> type) { + case OH_NATIVEXCOMPONENT_DOWN: + JSFunction::getFunction("CocosEditBox.hideAllEditBox").invoke(); // hide all editbox + Cocos2dxRenderer_nativeTouchesBegin(touchEvent->numPoints, ids, xs, ys); + OHOS_LOGD("Touch Info : OH_NATIVEXCOMPONENT_DOWN"); + break; + case OH_NATIVEXCOMPONENT_UP: + Cocos2dxRenderer_nativeTouchesEnd(touchEvent->id, touchEvent->x, touchEvent->y); + OHOS_LOGD("Touch Info : OH_NATIVEXCOMPONENT_UP"); + break; + case OH_NATIVEXCOMPONENT_MOVE: + Cocos2dxRenderer_nativeTouchesMove(touchEvent->numPoints, ids, xs, ys); + OHOS_LOGD("Touch Info : OH_NATIVEXCOMPONENT_MOVE"); + break; + case OH_NATIVEXCOMPONENT_CANCEL: + Cocos2dxRenderer_nativeTouchesCancel(touchEvent->numPoints, ids, xs, ys); + OHOS_LOGD("Touch Info : OH_NATIVEXCOMPONENT_CANCEL"); + break; + case OH_NATIVEXCOMPONENT_UNKNOWN: + OHOS_LOGD("Touch Info : OH_NATIVEXCOMPONENT_UNKNOWN"); + break; + default: + OHOS_LOGD("Touch Info : default"); + break; + } + delete touchEvent; +} + +void PluginRender::MouseWheelCB(std::string eventType, double scrollY) +{ + if (isMouseLeftActive) { + return; + } + if (eventType == "actionEnd") { + scrollDistance = 0; + } + if (eventType == "actionUpdate") { + double moveScrollY = scrollY - scrollDistance; + scrollDistance = scrollY; + PluginRender::EventMouseWheelData mouseWheelData{mousePositionX, mousePositionY, moveScrollY}; + PluginRender::mouseWheelEventQueue_.push(mouseWheelData); + PluginRender::GetInstance()->sendMsgToWorker(MessageType::WM_XCOMPONENT_MOUSE_WHEEL_EVENT, nullptr, nullptr); + } +} + +void PluginRender::OnCreateNative(napi_env env, uv_loop_t* loop) { + OHOS_LOGD("PluginRender::OnCreateNative"); +} + +void PluginRender::OnShowNative() { + OHOS_LOGD("PluginRender::OnShowNative"); + cocos2d::Application* app = cocos2d::Application::getInstance(); + if(app) { + app->applicationWillEnterForeground(); + } + cocos2d::EventCustom foregroundEvent(EVENT_COME_TO_FOREGROUND); + cocos2d::Director::getInstance()->getEventDispatcher()->dispatchEvent(&foregroundEvent); + if (timerInited_) { + uv_timer_start(&timerHandle_, &PluginRender::timerCb, 0, 1); + } +} + +void PluginRender::OnHideNative() { + OHOS_LOGD("PluginRender::OnHideNative"); + cocos2d::Application* app = cocos2d::Application::getInstance(); + if(app) { + app->applicationDidEnterBackground(); + } + cocos2d::EventCustom backgroundEvent(EVENT_COME_TO_BACKGROUND); + cocos2d::Director::getInstance()->getEventDispatcher()->dispatchEvent(&backgroundEvent); + if (timerInited_) { + uv_timer_stop(&timerHandle_); + } +} + +void PluginRender::OnDestroyNative() { + OHOS_LOGD("PluginRender::OnDestoryNative"); + if (timerInited_) { + uv_timer_stop(&timerHandle_); + } +} + +napi_value PluginRender::Export(napi_env env, napi_value exports) { + OHOS_LOGD("PluginRender::Export"); + // Register JS API + napi_property_descriptor desc[] = { + DECLARE_NAPI_FUNCTION("changeShape", PluginRender::NapiChangeShape), + DECLARE_NAPI_FUNCTION("drawTriangle", PluginRender::NapiDrawTriangle), + DECLARE_NAPI_FUNCTION("changeColor", PluginRender::NapiChangeColor), + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + return exports; +} + +napi_value PluginRender::NapiChangeShape(napi_env env, napi_callback_info info) +{ + OHOS_LOGD("PluginRender::NapiChangeShape"); + PluginRender* instance = PluginRender::GetInstance(); + if (instance) { + CCDirector::sharedDirector()->mainLoop(); + instance->eglCore_->Update(); + } + return nullptr; +} + +napi_value PluginRender::NapiDrawTriangle(napi_env env, napi_callback_info info) { + OHOS_LOGD("NapiDrawTriangle"); + return nullptr; +} + +napi_value PluginRender::NapiChangeColor(napi_env env, napi_callback_info info) { + OHOS_LOGD("NapiChangeColor"); + return nullptr; +} + +#ifdef __cplusplus +} +#endif diff --git a/cocos/platform/ohos/napi/render/plugin_render.h b/cocos/platform/ohos/napi/render/plugin_render.h new file mode 100644 index 000000000000..fbfb5419808f --- /dev/null +++ b/cocos/platform/ohos/napi/render/plugin_render.h @@ -0,0 +1,101 @@ +#ifndef _PLUGIN_RENDER_H_ +#define _PLUGIN_RENDER_H_ + +#include +#include +#include + +#include +#include + +#include "egl_core.h" +#include "../WorkerMessageQueue.h" + +class PluginRender { +public: + PluginRender(); + static PluginRender* GetInstance(); + static OH_NativeXComponent_Callback* GetNXComponentCallback(); + + static void onMessageCallback(const uv_async_t* req); + static void timerCb(uv_timer_t* handle); + + void SetNativeXComponent(OH_NativeXComponent* component); + + void workerInit(napi_env env, uv_loop_t* loop); + + void sendMsgToWorker(const MessageType& type, OH_NativeXComponent* component, void* window); + void sendMsgToWorker(const MessageType& type, OH_NativeXComponent* component, void* window, OH_NativeXComponent_TouchEvent* touchEvent); + void enqueue(const WorkerMessageData& data); + bool dequeue(WorkerMessageData* data); + void triggerMessageSignal(); + void run(); + void changeFPS(uint64_t animationInterval); + +public: + // NAPI interface + napi_value Export(napi_env env, napi_value exports); + + // Exposed to JS developers by NAPI + static napi_value NapiChangeShape(napi_env env, napi_callback_info info); + static napi_value NapiDrawTriangle(napi_env env, napi_callback_info info); + static napi_value NapiChangeColor(napi_env env, napi_callback_info info); + static napi_value NapiChangeColorWorker(napi_env env, napi_callback_info info); + + static void MouseWheelCB(std::string eventType, double scrollY); + // Callback, called by ACE XComponent + void OnSurfaceCreated(OH_NativeXComponent* component, void* window); + + void OnSurfaceChanged(OH_NativeXComponent* component, void* window); + + void OnSurfaceDestroyed(OH_NativeXComponent* component, void* window); + + void onSurfaceHide(); + + void onSurfaceShow(void* window); + + void DispatchKeyEvent(OH_NativeXComponent* component, void* window); + + void DispatchMouseEvent(OH_NativeXComponent* component, void* window); + + void DispatchTouchEvent(OH_NativeXComponent* component, void* window, OH_NativeXComponent_TouchEvent* touchEvent); + + void DispatchMouseWheelEvent(); + + void OnCreateNative(napi_env env, uv_loop_t* loop); + void OnShowNative(); + void OnHideNative(); + void OnDestroyNative(); + +public: + struct EventMouseWheelData { + float positonX; + float positonY; + double scrollY; + }; + static PluginRender* instance_; + static OH_NativeXComponent_Callback callback_; + static OH_NativeXComponent_MouseEvent_Callback mouseCallback_; + static std::queue keyEventQueue_; + static std::queue mouseEventQueue_; + static std::queue mouseWheelEventQueue_; + + OH_NativeXComponent* component_{nullptr}; + uv_timer_t timerHandle_; + bool timerInited_{false}; + uv_loop_t* workerLoop_{nullptr}; + uv_async_t messageSignal_{}; + WorkerMessageQueue messageQueue_; + EGLCore* eglCore_{nullptr}; + + uint64_t width_; + uint64_t height_; + + double x_; + double y_; + + static uint64_t animationInterval_; + static uint64_t lastTime; +}; + +#endif // _PLUGIN_RENDER_H_ diff --git a/cocos/renderer/CCRenderer.cpp b/cocos/renderer/CCRenderer.cpp index 86ab1045b828..4a45d1102c10 100644 --- a/cocos/renderer/CCRenderer.cpp +++ b/cocos/renderer/CCRenderer.cpp @@ -778,6 +778,7 @@ void Renderer::drawBatchedTriangles() if (Configuration::getInstance()->supportsShareableVAO()) { +#if (CC_TARGET_PLATFORM != CC_PLATFORM_OHOS) //Bind VAO GL::bindVAO(_buffersVAO); //Set VBO data @@ -799,6 +800,7 @@ void Renderer::drawBatchedTriangles() glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * _filledIndex, _indices, GL_STATIC_DRAW); +#endif } else { @@ -899,10 +901,11 @@ void Renderer::drawBatchedQuads() // option 3: orphaning + glMapBuffer glBufferData(GL_ARRAY_BUFFER, sizeof(_quadVerts[0]) * _numberQuads * 4, nullptr, GL_DYNAMIC_DRAW); +#if (CC_TARGET_PLATFORM != CC_PLATFORM_OHOS) void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); memcpy(buf, _quadVerts, sizeof(_quadVerts[0])* _numberQuads * 4); glUnmapBuffer(GL_ARRAY_BUFFER); - +#endif glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _quadbuffersVBO[1]); diff --git a/cocos/renderer/CCTexture2D.cpp b/cocos/renderer/CCTexture2D.cpp index 634c7c0f1cb6..9aaf91905646 100644 --- a/cocos/renderer/CCTexture2D.cpp +++ b/cocos/renderer/CCTexture2D.cpp @@ -1098,8 +1098,8 @@ bool Texture2D::initWithString(const char *text, const FontDefinition& textDefin return false; } -#if (CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID) && (CC_TARGET_PLATFORM != CC_PLATFORM_IOS) - CCASSERT(textDefinition._stroke._strokeEnabled == false, "Currently stroke only supported on iOS and Android!"); +#if (CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID) && (CC_TARGET_PLATFORM != CC_PLATFORM_IOS) && (CC_TARGET_PLATFORM != CC_PLATFORM_OHOS) + CCASSERT(textDefinition._stroke._strokeEnabled == false, "Currently stroke only supported on iOS, Android and OHOS!"); #endif PixelFormat pixelFormat = g_defaultAlphaPixelFormat; diff --git a/cocos/renderer/CCTextureAtlas.cpp b/cocos/renderer/CCTextureAtlas.cpp index 630236c571b9..9d58a4b496b9 100644 --- a/cocos/renderer/CCTextureAtlas.cpp +++ b/cocos/renderer/CCTextureAtlas.cpp @@ -626,10 +626,11 @@ void TextureAtlas::drawNumberOfQuads(ssize_t numberOfQuads, ssize_t start) // option 3: orphaning + glMapBuffer glBufferData(GL_ARRAY_BUFFER, sizeof(_quads[0]) * _capacity, nullptr, GL_DYNAMIC_DRAW); - void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); +#if (CC_TARGET_PLATFORM != CC_PLATFORM_OHOS) + void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); memcpy(buf, _quads, sizeof(_quads[0])* _totalQuads); glUnmapBuffer(GL_ARRAY_BUFFER); - +#endif glBindBuffer(GL_ARRAY_BUFFER, 0); _dirty = false; diff --git a/cocos/scripting/lua-bindings/CMakeLists.txt b/cocos/scripting/lua-bindings/CMakeLists.txt index 288e0485b3a6..3aa381996f49 100644 --- a/cocos/scripting/lua-bindings/CMakeLists.txt +++ b/cocos/scripting/lua-bindings/CMakeLists.txt @@ -1,39 +1,78 @@ set(cocos_root ${Cocos2d-X_SOURCE_DIR}) -include_directories( - ${cocos_root}/external/lua/tolua - ${cocos_root}/external/lua/lua - ${cocos_root}/external/lua - ${cocos_root}/external/xxtea - ${cocos_root}/external - ${cocos_root}/cocos - ${cocos_root}/cocos/ui - ${cocos_root}/cocos/2d - ${cocos_root}/cocos/3d - ${cocos_root}/cocos/base - ${cocos_root}/cocos/editor-support/spine - ${cocos_root}/cocos/editor-support/cocostudio - ${cocos_root}/cocos/editor-support/cocostudio/ActionTimeline - ${cocos_root}/cocos/editor-support/cocosbuilder - ${cocos_root}/cocos/editor-support - ${cocos_root}/cocos/platform - ${cocos_root}/cocos/audio/include - ${cocos_root}/cocos/physics3d - ${cocos_root}/cocos/navmesh - manual - manual/extension - manual/cocostudio - manual/ui - manual/cocos2d - manual/navmesh - auto -) +if(OHOS) + include_directories( + ${cocos_root}/external/lua/tolua + ${cocos_root}/external/lua/luajit/include + ${cocos_root}/external/lua + ${cocos_root}/external/xxtea + ${cocos_root}/external + ${cocos_root}/cocos + ${cocos_root}/cocos/ui + ${cocos_root}/cocos/2d + ${cocos_root}/cocos/3d + ${cocos_root}/cocos/base + ${cocos_root}/cocos/editor-support/spine + ${cocos_root}/cocos/editor-support/cocostudio + ${cocos_root}/cocos/editor-support/cocostudio/ActionTimeline + ${cocos_root}/cocos/editor-support/cocosbuilder + ${cocos_root}/cocos/editor-support + ${cocos_root}/cocos/platform + ${cocos_root}/cocos/audio/include + ${cocos_root}/cocos/physics3d + ${cocos_root}/cocos/navmesh + manual + manual/extension + manual/cocostudio + manual/ui + manual/cocos2d + manual/navmesh + auto + ) +else() + include_directories( + ${cocos_root}/external/lua/tolua + ${cocos_root}/external/lua/lua + ${cocos_root}/external/lua + ${cocos_root}/external/xxtea + ${cocos_root}/external + ${cocos_root}/cocos + ${cocos_root}/cocos/ui + ${cocos_root}/cocos/2d + ${cocos_root}/cocos/3d + ${cocos_root}/cocos/base + ${cocos_root}/cocos/editor-support/spine + ${cocos_root}/cocos/editor-support/cocostudio + ${cocos_root}/cocos/editor-support/cocostudio/ActionTimeline + ${cocos_root}/cocos/editor-support/cocosbuilder + ${cocos_root}/cocos/editor-support + ${cocos_root}/cocos/platform + ${cocos_root}/cocos/audio/include + ${cocos_root}/cocos/physics3d + ${cocos_root}/cocos/navmesh + manual + manual/extension + manual/cocostudio + manual/ui + manual/cocos2d + manual/navmesh + auto + ) +endif() +if(OHOS) + file(GLOB lua_cocos2d_source_files + "${cocos_root}/external/lua/luajit/include/*.c" + "${cocos_root}/external/lua/tolua/*.c" + "${cocos_root}/external/xxtea/xxtea.cpp" + ) +else() file(GLOB lua_cocos2d_source_files "${cocos_root}/external/lua/lua/*.c" "${cocos_root}/external/lua/tolua/*.c" "${cocos_root}/external/xxtea/xxtea.cpp" ) +endif() list(APPEND lua_cocos2d_source_files ${cocos_root}/external/lua/luasocket/luasocket.c @@ -68,6 +107,9 @@ elseif(UNIX) if(APPLE) add_definitions(-D_DARWIN_C_SOURCE) endif() + if(OHOS) + add_definitions(-D_GNU_SOURCE) + endif() list(APPEND lua_cocos2d_source_files ${cocos_root}/external/lua/luasocket/serial.c @@ -140,12 +182,25 @@ if(MACOSX) ${lua_bindings_manual_files} manual/platform/ios/CCLuaObjcBridge.mm ) +elseif(OHOS) + + set(lua_bindings_manual_files + ${lua_bindings_manual_files} + auto/lua_cocos2dx_experimental_webview_auto.cpp + manual/ui/lua_cocos2dx_experimental_webview_manual.cpp + auto/lua_cocos2dx_experimental_video_auto.cpp + manual/ui/lua_cocos2dx_experimental_video_manual.cpp + ) endif() set(lua_bindings_files ${lua_cocos2d_source_files} ${lua_bindings_manual_files} ${lua_bindings_auto_files}) add_library(luacocos2d ${lua_bindings_files}) -target_link_libraries(luacocos2d cocos2d) +if(OHOS) + target_link_libraries(luacocos2d cocos2d ext_luajit) +else() + target_link_libraries(luacocos2d cocos2d) +endif() set_target_properties(luacocos2d PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" diff --git a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_audioengine_auto.cpp b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_audioengine_auto.cpp index cd28c13787f6..533c3b33b3a4 100644 --- a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_audioengine_auto.cpp +++ b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_audioengine_auto.cpp @@ -1,5 +1,5 @@ #include "lua_cocos2dx_audioengine_auto.hpp" -#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 #include "AudioEngine.h" #include "tolua_fix.h" #include "LuaBasicConversions.h" diff --git a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_audioengine_auto.hpp b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_audioengine_auto.hpp index 8986dd181d8d..30ddb41dbd52 100644 --- a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_audioengine_auto.hpp +++ b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_audioengine_auto.hpp @@ -1,5 +1,5 @@ #include "base/ccConfig.h" -#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 #ifndef __cocos2dx_audioengine_h__ #define __cocos2dx_audioengine_h__ diff --git a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_controller_auto.cpp b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_controller_auto.cpp index d7c4d142af1e..0e8c4ee5aedd 100644 --- a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_controller_auto.cpp +++ b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_controller_auto.cpp @@ -1,10 +1,11 @@ #include "lua_cocos2dx_controller_auto.hpp" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) #include "CCGameController.h" #include "tolua_fix.h" #include "LuaBasicConversions.h" + int lua_cocos2dx_controller_Controller_receiveExternalKeyEvent(lua_State* tolua_S) { int argc = 0; diff --git a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_controller_auto.hpp b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_controller_auto.hpp index 4fd667b49f1a..f8c12c84b2b5 100644 --- a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_controller_auto.hpp +++ b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_controller_auto.hpp @@ -1,5 +1,5 @@ #include "base/ccConfig.h" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) #ifndef __cocos2dx_controller_h__ #define __cocos2dx_controller_h__ @@ -34,4 +34,4 @@ int register_all_cocos2dx_controller(lua_State* tolua_S); #endif // __cocos2dx_controller_h__ -#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) diff --git a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_experimental_video_auto.cpp b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_experimental_video_auto.cpp index 64d3c0f76dad..74bc5554e8b8 100644 --- a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_experimental_video_auto.cpp +++ b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_experimental_video_auto.cpp @@ -1,5 +1,5 @@ #include "lua_cocos2dx_experimental_video_auto.hpp" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #include "UIVideoPlayer.h" #include "tolua_fix.h" #include "LuaBasicConversions.h" diff --git a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_experimental_video_auto.hpp b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_experimental_video_auto.hpp index 44597483da4f..b62a7ceded80 100644 --- a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_experimental_video_auto.hpp +++ b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_experimental_video_auto.hpp @@ -1,5 +1,5 @@ #include "base/ccConfig.h" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #ifndef __cocos2dx_experimental_video_h__ #define __cocos2dx_experimental_video_h__ @@ -32,4 +32,4 @@ int register_all_cocos2dx_experimental_video(lua_State* tolua_S); #endif // __cocos2dx_experimental_video_h__ -#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) diff --git a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_experimental_webview_auto.cpp b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_experimental_webview_auto.cpp index 8cec27f723c2..9e6024572053 100644 --- a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_experimental_webview_auto.cpp +++ b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_experimental_webview_auto.cpp @@ -1,5 +1,5 @@ #include "lua_cocos2dx_experimental_webview_auto.hpp" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #include "UIWebView.h" #include "tolua_fix.h" #include "LuaBasicConversions.h" diff --git a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_experimental_webview_auto.hpp b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_experimental_webview_auto.hpp index 7a01ebc9c568..ed5512e8e328 100644 --- a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_experimental_webview_auto.hpp +++ b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_experimental_webview_auto.hpp @@ -1,5 +1,5 @@ #include "base/ccConfig.h" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #ifndef __cocos2dx_experimental_webview_h__ #define __cocos2dx_experimental_webview_h__ @@ -30,4 +30,4 @@ int register_all_cocos2dx_experimental_webview(lua_State* tolua_S); #endif // __cocos2dx_experimental_webview_h__ -#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) diff --git a/cocos/scripting/lua-bindings/manual/CCLuaEngine.cpp b/cocos/scripting/lua-bindings/manual/CCLuaEngine.cpp index 69ef046381d9..7efd35fe59d4 100644 --- a/cocos/scripting/lua-bindings/manual/CCLuaEngine.cpp +++ b/cocos/scripting/lua-bindings/manual/CCLuaEngine.cpp @@ -33,10 +33,12 @@ #include "lua_cocos2dx_coco_studio_manual.hpp" #include "lua_cocos2dx_ui_manual.hpp" -#if _MSC_VER > 1800 -#pragma comment(lib,"lua51-2015.lib") -#else -#pragma comment(lib,"lua51.lib") +#if (CC_TARGET_PLATFORM != CC_PLATFORM_OHOS || _WIN32) + #if _MSC_VER > 1800 + #pragma comment(lib,"lua51-2015.lib") + #else + #pragma comment(lib,"lua51.lib") + #endif #endif NS_CC_BEGIN diff --git a/cocos/scripting/lua-bindings/manual/CCLuaStack.cpp b/cocos/scripting/lua-bindings/manual/CCLuaStack.cpp index a3de8b324b42..11f1d3c9c3bb 100644 --- a/cocos/scripting/lua-bindings/manual/CCLuaStack.cpp +++ b/cocos/scripting/lua-bindings/manual/CCLuaStack.cpp @@ -94,6 +94,10 @@ int lua_print(lua_State * luastate) t += "\t"; } CCLOG("[LUA-print] %s", t.c_str()); +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + OHOS_LOGD("[LUA-print] %{public}s", t.c_str()); +#endif + return 0; } @@ -136,7 +140,9 @@ int lua_release_print(lua_State * L) t += "\t"; } log("[LUA-print] %s", t.c_str()); - +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + OHOS_LOGD("[LUA-print] %{public}s", t.c_str()); +#endif return 0; } } diff --git a/cocos/scripting/lua-bindings/manual/audioengine/lua_cocos2dx_audioengine_manual.cpp b/cocos/scripting/lua-bindings/manual/audioengine/lua_cocos2dx_audioengine_manual.cpp index 2d45e43ca8a5..380cc5cecd54 100644 --- a/cocos/scripting/lua-bindings/manual/audioengine/lua_cocos2dx_audioengine_manual.cpp +++ b/cocos/scripting/lua-bindings/manual/audioengine/lua_cocos2dx_audioengine_manual.cpp @@ -25,7 +25,7 @@ #include "lua_cocos2dx_audioengine_manual.h" -#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 #include "lua_cocos2dx_audioengine_auto.hpp" #include "tolua_fix.h" diff --git a/cocos/scripting/lua-bindings/manual/controller/lua_cocos2dx_controller_manual.cpp b/cocos/scripting/lua-bindings/manual/controller/lua_cocos2dx_controller_manual.cpp index 8ed24cfc6052..88214b4428a7 100644 --- a/cocos/scripting/lua-bindings/manual/controller/lua_cocos2dx_controller_manual.cpp +++ b/cocos/scripting/lua-bindings/manual/controller/lua_cocos2dx_controller_manual.cpp @@ -23,7 +23,7 @@ ****************************************************************************/ #include "lua_cocos2dx_controller_manual.hpp" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #include "cocos2d.h" #include "tolua_fix.h" @@ -370,4 +370,4 @@ int register_all_cocos2dx_controller_manual(lua_State* L) return 0; } -#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) diff --git a/cocos/scripting/lua-bindings/manual/controller/lua_cocos2dx_controller_manual.hpp b/cocos/scripting/lua-bindings/manual/controller/lua_cocos2dx_controller_manual.hpp index 7d44970ea0b0..3e7bb3b716c2 100644 --- a/cocos/scripting/lua-bindings/manual/controller/lua_cocos2dx_controller_manual.hpp +++ b/cocos/scripting/lua-bindings/manual/controller/lua_cocos2dx_controller_manual.hpp @@ -32,10 +32,10 @@ extern "C" { } #endif -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) TOLUA_API int register_all_cocos2dx_controller_manual(lua_State* L); -#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #endif // #ifndef COCOS_SCRIPTING_LUA_BINDINGS_LUA_COCOS2DX_CONTROLLER_MANUAL_H diff --git a/cocos/scripting/lua-bindings/manual/network/Lua_web_socket.cpp b/cocos/scripting/lua-bindings/manual/network/Lua_web_socket.cpp index 0b80e9a0e70f..e4f872c6452d 100644 --- a/cocos/scripting/lua-bindings/manual/network/Lua_web_socket.cpp +++ b/cocos/scripting/lua-bindings/manual/network/Lua_web_socket.cpp @@ -22,7 +22,7 @@ THE SOFTWARE. ****************************************************************************/ -#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_MAC) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_MAC) #include "Lua_web_socket.h" #include #include diff --git a/cocos/scripting/lua-bindings/manual/network/Lua_web_socket.h b/cocos/scripting/lua-bindings/manual/network/Lua_web_socket.h index 1a85155ae64c..be372fbb6a46 100644 --- a/cocos/scripting/lua-bindings/manual/network/Lua_web_socket.h +++ b/cocos/scripting/lua-bindings/manual/network/Lua_web_socket.h @@ -24,7 +24,7 @@ #ifndef __LUA_WEB_SOCKET_H__ #define __LUA_WEB_SOCKET_H__ -#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_MAC) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_MAC) #ifdef __cplusplus extern "C" { diff --git a/cocos/scripting/lua-bindings/manual/network/lua_cocos2dx_network_manual.cpp b/cocos/scripting/lua-bindings/manual/network/lua_cocos2dx_network_manual.cpp index bc6c25297676..8db1dc7a5104 100644 --- a/cocos/scripting/lua-bindings/manual/network/lua_cocos2dx_network_manual.cpp +++ b/cocos/scripting/lua-bindings/manual/network/lua_cocos2dx_network_manual.cpp @@ -23,11 +23,11 @@ ****************************************************************************/ #include "lua_cocos2dx_network_manual.h" extern "C" { -#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_MAC) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_MAC) #include "lua_extensions.h" #endif } -#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_MAC) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_MAC) #include "Lua_web_socket.h" #endif @@ -40,11 +40,11 @@ int register_network_module(lua_State* L) lua_getglobal(L, "_G"); if (lua_istable(L,-1))//stack:...,_G, { -#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_MAC) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) luaopen_lua_extensions(L); #endif -#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_MAC) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) tolua_web_socket_open(L); register_web_socket_manual(L); #endif diff --git a/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_experimental_video_manual.cpp b/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_experimental_video_manual.cpp index e0ade6d8d9df..7625f6b85fe6 100644 --- a/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_experimental_video_manual.cpp +++ b/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_experimental_video_manual.cpp @@ -1,6 +1,6 @@ #include "lua_cocos2dx_experimental_video_manual.hpp" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #include "UIVideoPlayer.h" #include "tolua_fix.h" diff --git a/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_experimental_video_manual.hpp b/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_experimental_video_manual.hpp index 6201540e5927..c62e40eb7ec4 100644 --- a/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_experimental_video_manual.hpp +++ b/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_experimental_video_manual.hpp @@ -22,7 +22,7 @@ THE SOFTWARE. ****************************************************************************/ #include "base/ccConfig.h" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #ifndef COCOS_SCRIPTING_LUA_BINDINGS_LUA_COCOS2DX_EXPERIMENTAL_VIDEO_MANUAL_H #define COCOS_SCRIPTING_LUA_BINDINGS_LUA_COCOS2DX_EXPERIMENTAL_VIDEO_MANUAL_H @@ -38,4 +38,4 @@ extern "C" { TOLUA_API int register_all_cocos2dx_experimental_video_manual(lua_State* L); #endif //#ifndef COCOS_SCRIPTING_LUA_BINDINGS_LUA_COCOS2DX_EXPERIMENTAL_VIDEO_MANUAL_H -#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) diff --git a/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_experimental_webview_manual.cpp b/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_experimental_webview_manual.cpp index f9892ae91ca8..dc72b3e90de0 100644 --- a/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_experimental_webview_manual.cpp +++ b/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_experimental_webview_manual.cpp @@ -1,6 +1,6 @@ #include "lua_cocos2dx_experimental_video_manual.hpp" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #include "UIWebView.h" #include "tolua_fix.h" diff --git a/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_experimental_webview_manual.hpp b/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_experimental_webview_manual.hpp index 909e64530c06..3f1012094f0c 100644 --- a/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_experimental_webview_manual.hpp +++ b/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_experimental_webview_manual.hpp @@ -22,7 +22,7 @@ THE SOFTWARE. ****************************************************************************/ #include "base/ccConfig.h" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #ifndef COCOS_SCRIPTING_LUA_BINDINGS_LUA_COCOS2DX_EXPERIMENTAL_WEBVIEW_MANUAL_H #define COCOS_SCRIPTING_LUA_BINDINGS_LUA_COCOS2DX_EXPERIMENTAL_WEBVIEW_MANUAL_H @@ -38,4 +38,4 @@ extern "C" { TOLUA_API int register_all_cocos2dx_experimental_webview_manual(lua_State* L); #endif //#ifndef COCOS_SCRIPTING_LUA_BINDINGS_LUA_COCOS2DX_EXPERIMENTAL_WEBVIEW_MANUAL_H -#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) +#endif //#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) diff --git a/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_ui_manual.cpp b/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_ui_manual.cpp index 68749b939ddd..ef118822bc81 100644 --- a/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_ui_manual.cpp +++ b/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_ui_manual.cpp @@ -23,7 +23,7 @@ ****************************************************************************/ #include "lua_cocos2dx_ui_manual.hpp" #include "lua_cocos2dx_ui_auto.hpp" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #include "lua_cocos2dx_experimental_video_auto.hpp" #include "lua_cocos2dx_experimental_video_manual.hpp" #include "lua_cocos2dx_experimental_webview_auto.hpp" diff --git a/cocos/scripting/lua-bindings/manual/video/lua_cocos2dx_experimental_video_manual.cpp b/cocos/scripting/lua-bindings/manual/video/lua_cocos2dx_experimental_video_manual.cpp index a937cada2cfd..b3b712555678 100644 --- a/cocos/scripting/lua-bindings/manual/video/lua_cocos2dx_experimental_video_manual.cpp +++ b/cocos/scripting/lua-bindings/manual/video/lua_cocos2dx_experimental_video_manual.cpp @@ -1,6 +1,6 @@ #include "lua_cocos2dx_experimental_video_manual.hpp" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #include "UIVideoPlayer.h" #include "tolua_fix.h" diff --git a/cocos/scripting/lua-bindings/manual/video/lua_cocos2dx_experimental_video_manual.hpp b/cocos/scripting/lua-bindings/manual/video/lua_cocos2dx_experimental_video_manual.hpp index 6201540e5927..8b8b9eca55dd 100644 --- a/cocos/scripting/lua-bindings/manual/video/lua_cocos2dx_experimental_video_manual.hpp +++ b/cocos/scripting/lua-bindings/manual/video/lua_cocos2dx_experimental_video_manual.hpp @@ -22,7 +22,7 @@ THE SOFTWARE. ****************************************************************************/ #include "base/ccConfig.h" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #ifndef COCOS_SCRIPTING_LUA_BINDINGS_LUA_COCOS2DX_EXPERIMENTAL_VIDEO_MANUAL_H #define COCOS_SCRIPTING_LUA_BINDINGS_LUA_COCOS2DX_EXPERIMENTAL_VIDEO_MANUAL_H diff --git a/cocos/scripting/lua-bindings/script/cocos2d/Cocos2dConstants.lua b/cocos/scripting/lua-bindings/script/cocos2d/Cocos2dConstants.lua index d4672d201e62..b73670c38078 100644 --- a/cocos/scripting/lua-bindings/script/cocos2d/Cocos2dConstants.lua +++ b/cocos/scripting/lua-bindings/script/cocos2d/Cocos2dConstants.lua @@ -188,6 +188,7 @@ cc.PLATFORM_OS_EMSCRIPTEN = 8 cc.PLATFORM_OS_TIZEN = 9 cc.PLATFORM_OS_WINRT = 10 cc.PLATFORM_OS_WP8 = 11 +cc.PLATFORM_OS_OHOS = 12 cc.LANGUAGE_ENGLISH = 0 cc.LANGUAGE_CHINESE = 1 diff --git a/cocos/ui/CMakeLists.txt b/cocos/ui/CMakeLists.txt index 42f2cde9a814..4b51fb5922ee 100644 --- a/cocos/ui/CMakeLists.txt +++ b/cocos/ui/CMakeLists.txt @@ -15,6 +15,15 @@ elseif(LINUX) set(COCOS_UI_SPECIFIC_SRC ui/UIEditBox/UIEditBoxImpl-stub.cpp ) +elseif(OHOS) + set(COCOS_UI_SPECIFIC_SRC + ui/UIWebViewImpl-ohos.h + ui/UIEditBox/UIEditBoxImpl-ohos.h + ui/UIEditBox/UIEditBoxImpl-common.cpp + ui/UIEditBox/UIEditBoxImpl-ohos.cpp + ui/UIWebViewImpl-ohos.cpp + ui/UIVideoPlayer-ohos.cpp + ) endif() #todo: android UIWebViewImpl and UIVideoPlayer diff --git a/cocos/ui/CocosGUI.h b/cocos/ui/CocosGUI.h index 126fb057dd48..11eeb1fa21b6 100644 --- a/cocos/ui/CocosGUI.h +++ b/cocos/ui/CocosGUI.h @@ -45,10 +45,10 @@ THE SOFTWARE. #include "ui/UIHBox.h" #include "ui/UIVBox.h" #include "ui/UIRelativeBox.h" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #include "ui/UIVideoPlayer.h" #endif -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #include "ui/UIWebView.h" #endif #include "ui/UIDeprecated.h" diff --git a/cocos/ui/UIEditBox/UIEditBoxImpl-common.cpp b/cocos/ui/UIEditBox/UIEditBoxImpl-common.cpp new file mode 100644 index 000000000000..9119c5ef1a07 --- /dev/null +++ b/cocos/ui/UIEditBox/UIEditBoxImpl-common.cpp @@ -0,0 +1,378 @@ +/**************************************************************************** + Copyright (c) 2010-2012 cocos2d-x.org + Copyright (c) 2012 James Chen + + 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 "UIEditBoxImpl-common.h" + +#define kLabelZOrder 9999 + +#include "UIEditBox.h" +#include "base/CCDirector.h" +#include "2d/CCLabel.h" +#include "ui/UIHelper.h" + +static const int CC_EDIT_BOX_PADDING = 5; + +NS_CC_BEGIN + +namespace ui { + +EditBoxImplCommon::EditBoxImplCommon(EditBox* pEditText) +: EditBoxImpl(pEditText) +, _label(nullptr) +, _labelPlaceHolder(nullptr) +, _editBoxInputMode(EditBox::InputMode::SINGLE_LINE) +, _editBoxInputFlag(EditBox::InputFlag::INTIAL_CAPS_ALL_CHARACTERS) +, _keyboardReturnType(EditBox::KeyboardReturnType::DEFAULT) +, _colText(Color3B::WHITE) +, _colPlaceHolder(Color3B::GRAY) +, _maxLength(-1) +{ +} + +EditBoxImplCommon::~EditBoxImplCommon() +{ +} + + +bool EditBoxImplCommon::initWithSize(const Size& size) +{ + do + { + + Rect rect = Rect(0, 0, size.width, size.height); + + this->createNativeControl(rect); + + initInactiveLabels(size); + setContentSize(size); + + return true; + }while (0); + + return false; +} + +void EditBoxImplCommon::initInactiveLabels(const Size& size) +{ + const char* pDefaultFontName = this->getNativeDefaultFontName(); + + _label = Label::create(); + _label->setAnchorPoint(Vec2(0, 0.5f)); + _label->setColor(Color3B::WHITE); + _label->setVisible(false); + _editBox->addChild(_label, kLabelZOrder); + + _labelPlaceHolder = Label::create(); + _labelPlaceHolder->setAnchorPoint(Vec2(0, 0.5f)); + _labelPlaceHolder->setColor(Color3B::GRAY); + _editBox->addChild(_labelPlaceHolder, kLabelZOrder); + + setFont(pDefaultFontName, size.height*2/3); + setPlaceholderFont(pDefaultFontName, size.height*2/3); +} + +void EditBoxImplCommon::placeInactiveLabels() +{ + _label->setPosition(CC_EDIT_BOX_PADDING, _contentSize.height / 2.0f); + _labelPlaceHolder->setPosition(CC_EDIT_BOX_PADDING, _contentSize.height / 2.0f); +} + +void EditBoxImplCommon::setInactiveText(const char* pText) +{ + if(EditBox::InputFlag::PASSWORD == _editBoxInputFlag) + { + std::string passwordString; + for(int i = 0; i < strlen(pText); ++i) + passwordString.append("\u25CF"); + _label->setString(passwordString.c_str()); + } + else + { + _label->setString(pText); + } + // Clip the text width to fit to the text box + float fMaxWidth = _editBox->getContentSize().width; + float fMaxHeight = _editBox->getContentSize().height; + Size labelSize = _label->getContentSize(); + if(labelSize.width > fMaxWidth || labelSize.height > fMaxHeight) + { + _label->setDimensions(fMaxWidth, fMaxHeight); + } +} + +void EditBoxImplCommon::setFont(const char* pFontName, int fontSize) +{ + this->setNativeFont(pFontName, fontSize * _label->getNodeToWorldAffineTransform().a); + + if(strlen(pFontName) > 0) + { + _label->setSystemFontName(pFontName); + } + if(fontSize > 0) + { + _label->setSystemFontSize(fontSize); + } +} + +void EditBoxImplCommon::setFontColor(const Color4B& color) +{ + this->setNativeFontColor(color); + + _label->setTextColor(color); +} + +void EditBoxImplCommon::setPlaceholderFont(const char* pFontName, int fontSize) +{ + this->setNativePlaceholderFont(pFontName, fontSize * _labelPlaceHolder->getNodeToWorldAffineTransform().a); + + if( strlen(pFontName) > 0) + { + _labelPlaceHolder->setSystemFontName(pFontName); + } + if(fontSize > 0) + { + _labelPlaceHolder->setSystemFontSize(fontSize); + } +} + +void EditBoxImplCommon::setPlaceholderFontColor(const Color4B &color) +{ + this->setNativePlaceholderFontColor(color); + + _labelPlaceHolder->setTextColor(color); +} + +void EditBoxImplCommon::setInputMode(EditBox::InputMode inputMode) +{ + _editBoxInputMode = inputMode; + this->setNativeInputMode(inputMode); +} + +void EditBoxImplCommon::setMaxLength(int maxLength) +{ + _maxLength = maxLength; + this->setNativeMaxLength(maxLength); +} + +int EditBoxImplCommon::getMaxLength() +{ + return _maxLength; +} + +void EditBoxImplCommon::setInputFlag(EditBox::InputFlag inputFlag) +{ + _editBoxInputFlag = inputFlag; + this->setNativeInputFlag(inputFlag); +} + +void EditBoxImplCommon::setReturnType(EditBox::KeyboardReturnType returnType) +{ + _keyboardReturnType = returnType; + this->setNativeReturnType(returnType); +} + +void EditBoxImplCommon::refreshInactiveText() +{ + setInactiveText(_text.c_str()); + if(_text.size() == 0) + { + _label->setVisible(false); + _labelPlaceHolder->setVisible(true); + } + else + { + _label->setVisible(true); + _labelPlaceHolder->setVisible(false); + } +} + +void EditBoxImplCommon::setText(const char* text) +{ + this->setNativeText(text); + _text = text; + refreshInactiveText(); +} + +const char* EditBoxImplCommon::getText(void) +{ + return _text.c_str(); +} + +void EditBoxImplCommon::setPlaceHolder(const char* pText) +{ + if (pText != NULL) + { + _placeHolder = pText; + if (_placeHolder.length() > 0 && _text.length() == 0) + { + _labelPlaceHolder->setVisible(true); + } + + _labelPlaceHolder->setString(_placeHolder.c_str()); + this->setNativePlaceHolder(pText); + } +} + + +void EditBoxImplCommon::setVisible(bool visible) +{ + this->setNativeVisible(visible); +} + +void EditBoxImplCommon::setContentSize(const Size& size) +{ + _contentSize = size; + CCLOG("[Edit text] content size = (%f, %f)", size.width, size.height); + placeInactiveLabels(); + + auto director = cocos2d::Director::getInstance(); + auto glview = director->getOpenGLView(); + Size controlSize = Size(size.width * glview->getScaleX() * _label->getNodeToWorldAffineTransform().a,size.height * glview->getScaleY() * _label->getNodeToWorldAffineTransform().a); + + this->setNativeContentSize(controlSize); + +} + +void EditBoxImplCommon::visit() +{ + auto rect = ui::Helper::convertBoundingBoxToScreen(_editBox); + this->updateNativeFrame(rect); +} + +void EditBoxImplCommon::onEnter(void) +{ + const char* pText = getText(); + if (pText) { + setInactiveText(pText); + } +} + +void EditBoxImplCommon::openKeyboard() +{ + _label->setVisible(false); + _labelPlaceHolder->setVisible(false); + + this->nativeOpenKeyboard(); +} + +void EditBoxImplCommon::closeKeyboard() +{ + this->nativeCloseKeyboard(); +} + +void EditBoxImplCommon::onEndEditing(const std::string& text) +{ + this->setNativeVisible(false); + + if(text.size() == 0) + { + _label->setVisible(false); + _labelPlaceHolder->setVisible(true); + } + else + { + _label->setVisible(true); + _labelPlaceHolder->setVisible(false); + setInactiveText(text.c_str()); + } +} + +void EditBoxImplCommon::editBoxEditingDidBegin() +{ + // LOGD("textFieldShouldBeginEditing..."); + cocos2d::ui::EditBoxDelegate *pDelegate = _editBox->getDelegate(); + + if (pDelegate != nullptr) + { + pDelegate->editBoxEditingDidBegin(_editBox); + } + +#if CC_ENABLE_SCRIPT_BINDING + if (NULL != _editBox && 0 != _editBox->getScriptEditBoxHandler()) + { + cocos2d::CommonScriptData data(_editBox->getScriptEditBoxHandler(), "began", _editBox); + cocos2d::ScriptEvent event(cocos2d::kCommonEvent, (void *)&data); + cocos2d::ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&event); + } +#endif +} + +void EditBoxImplCommon::editBoxEditingDidEnd(const std::string& text) +{ + // LOGD("textFieldShouldEndEditing..."); + _text = text; + this->refreshInactiveText(); + + cocos2d::ui::EditBoxDelegate *pDelegate = _editBox->getDelegate(); + if (pDelegate != nullptr) + { + pDelegate->editBoxEditingDidEnd(_editBox); + pDelegate->editBoxReturn(_editBox); + } + +#if CC_ENABLE_SCRIPT_BINDING + if (_editBox != nullptr && 0 != _editBox->getScriptEditBoxHandler()) + { + cocos2d::CommonScriptData data(_editBox->getScriptEditBoxHandler(), "ended", _editBox); + cocos2d::ScriptEvent event(cocos2d::kCommonEvent, (void *)&data); + cocos2d::ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&event); + memset(data.eventName, 0, sizeof(data.eventName)); + strncpy(data.eventName, "return", sizeof(data.eventName)); + event.data = (void *)&data; + cocos2d::ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&event); + } +#endif + + if (_editBox != nullptr) + { + this->onEndEditing(_text); + } +} + +void EditBoxImplCommon::editBoxEditingChanged(const std::string& text) +{ + // LOGD("editBoxTextChanged..."); + cocos2d::ui::EditBoxDelegate *pDelegate = _editBox->getDelegate(); + _text = text; + if (pDelegate != nullptr) + { + pDelegate->editBoxTextChanged(_editBox, text); + } + +#if CC_ENABLE_SCRIPT_BINDING + if (NULL != _editBox && 0 != _editBox->getScriptEditBoxHandler()) + { + cocos2d::CommonScriptData data(_editBox->getScriptEditBoxHandler(), "changed", _editBox); + cocos2d::ScriptEvent event(cocos2d::kCommonEvent, (void *)&data); + cocos2d::ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&event); + } +#endif +} + + +} + +NS_CC_END + + diff --git a/cocos/ui/UIEditBox/UIEditBoxImpl-common.h b/cocos/ui/UIEditBox/UIEditBoxImpl-common.h new file mode 100644 index 000000000000..8393b0942731 --- /dev/null +++ b/cocos/ui/UIEditBox/UIEditBoxImpl-common.h @@ -0,0 +1,144 @@ +/**************************************************************************** + Copyright (c) 2010-2012 cocos2d-x.org + Copyright (c) 2012 James Chen + + http://www.cocos2d-x.org + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ****************************************************************************/ + +#ifndef __UIEditBoxIMPLICOMMON_H__ +#define __UIEditBoxIMPLICOMMON_H__ + +#include "platform/CCPlatformConfig.h" + +#include "UIEditBoxImpl.h" + +NS_CC_BEGIN + +namespace ui { + +class EditBox; + +class EditBoxImplCommon : public EditBoxImpl +{ +public: + /** + * @js NA + */ + EditBoxImplCommon(EditBox* pEditText); + /** + * @js NA + * @lua NA + */ + virtual ~EditBoxImplCommon(); + + virtual bool initWithSize(const Size& size) override; + + virtual void setFont(const char* pFontName, int fontSize) override; + virtual void setFontColor(const Color4B& color) override; + virtual void setPlaceholderFont(const char* pFontName, int fontSize) override; + virtual void setPlaceholderFontColor(const Color4B& color) override; + virtual void setInputMode(EditBox::InputMode inputMode) override; + virtual void setInputFlag(EditBox::InputFlag inputFlag) override; + virtual void setReturnType(EditBox::KeyboardReturnType returnType) override; + virtual void setText(const char* pText) override; + virtual void setPlaceHolder(const char* pText) override; + virtual void setVisible(bool visible) override; + + + virtual void setMaxLength(int maxLength) override; + virtual int getMaxLength() override; + + virtual const char* getText(void) override; + virtual void refreshInactiveText(); + + virtual void setContentSize(const Size& size) override; + + virtual void setAnchorPoint(const Vec2& anchorPoint) override {} + virtual void setPosition(const Vec2& pos) override {} + + /** + * @js NA + * @lua NA + */ + virtual void visit(void) override; + /** + * @js NA + * @lua NA + */ + virtual void onEnter(void) override; + virtual void openKeyboard() override; + virtual void closeKeyboard() override; + + virtual void onEndEditing(const std::string& text); + + void editBoxEditingDidBegin(); + void editBoxEditingChanged(const std::string& text); + void editBoxEditingDidEnd(const std::string& text); + + virtual bool isEditing() override = 0; + virtual void createNativeControl(const Rect& frame) = 0; + virtual void setNativeFont(const char* pFontName, int fontSize) = 0; + virtual void setNativeFontColor(const Color4B& color) = 0; + virtual void setNativePlaceholderFont(const char* pFontName, int fontSize) = 0; + virtual void setNativePlaceholderFontColor(const Color4B& color) = 0; + virtual void setNativeInputMode(EditBox::InputMode inputMode) = 0; + virtual void setNativeInputFlag(EditBox::InputFlag inputFlag) = 0; + virtual void setNativeReturnType(EditBox::KeyboardReturnType returnType) = 0; + virtual void setNativeText(const char* pText) = 0; + virtual void setNativePlaceHolder(const char* pText) = 0; + virtual void setNativeVisible(bool visible) = 0; + virtual void updateNativeFrame(const Rect& rect) = 0; + virtual void setNativeContentSize(const Size& size) = 0; + virtual const char* getNativeDefaultFontName() = 0; + virtual void nativeOpenKeyboard() = 0; + virtual void nativeCloseKeyboard() = 0; + virtual void setNativeMaxLength(int maxLength) {}; + + +private: + void initInactiveLabels(const Size& size); + void setInactiveText(const char* pText); + void placeInactiveLabels(); + + Label* _label; + Label* _labelPlaceHolder; + EditBox::InputMode _editBoxInputMode; + EditBox::InputFlag _editBoxInputFlag; + EditBox::KeyboardReturnType _keyboardReturnType; + + std::string _text; + std::string _placeHolder; + + Color4B _colText; + Color4B _colPlaceHolder; + + int _maxLength; + Size _contentSize; +}; + + +} + +NS_CC_END + + +#endif /* __UIEditBoxIMPLICOMMON_H__ */ + diff --git a/cocos/ui/UIEditBox/UIEditBoxImpl-ohos.cpp b/cocos/ui/UIEditBox/UIEditBoxImpl-ohos.cpp new file mode 100644 index 000000000000..6562c0a1611c --- /dev/null +++ b/cocos/ui/UIEditBox/UIEditBoxImpl-ohos.cpp @@ -0,0 +1,212 @@ +#include "ui/UIEditBox/UIEditBoxImpl-ohos.h" +#include + +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + +#include "ui/UIEditBox/UIEditBox.h" +#include "2d/CCLabel.h" +#include "base/ccUTF8.h" +#include "math/Vec2.h" +#include "ui/UIHelper.h" +#include "base/CCDirector.h" +#include "platform/CCFileUtils.h" +#include "platform/ohos/napi/helper/NapiHelper.h" +#include "platform/ohos/CCLogOhos.h" + + +NS_CC_BEGIN + +namespace ui { + + static std::unordered_map s_allEditBoxes; + static int curIndex = 0; + + EditBoxImpl* __createSystemEditBox(EditBox* editBox) + { + return new EditBoxImplOhos(editBox); + } + + void EditBoxImplOhos::createNativeControl(const Rect& frame) + { + OHOS_LOGD("create textinput"); + + auto director = cocos2d::Director::getInstance(); + auto glView = director->getOpenGLView(); + auto frameSize = glView->getFrameSize(); + + auto winSize = director->getWinSize(); + auto leftBottom = _editBox->convertToWorldSpace(Point::ZERO); + + auto contentSize = frame.size; + auto rightTop = _editBox->convertToWorldSpace(Point(contentSize.width, contentSize.height)); + auto uiLeft = frameSize.width / 2 + (leftBottom.x - winSize.width / 2) * glView->getScaleX(); + auto uiTop = frameSize.height / 2 - (rightTop.y - winSize.height / 2) * glView->getScaleY(); + auto uiWidth = (rightTop.x - leftBottom.x) * glView->getScaleX(); + auto uiHeight = (rightTop.y - leftBottom.y) * glView->getScaleY(); + auto paddingW = (int)(5 * glView->getScaleX()); + auto paddingH = (int)(uiHeight * 0.33f / 2); + + s_allEditBoxes[curIndex] = this; + _editBoxIndex = curIndex; + JSFunction::getFunction("CocosEditBox.createCocosEditBox").invoke(_editBoxIndex, uiLeft, uiTop, uiWidth, uiHeight, paddingW, paddingH); + curIndex++; + } + + EditBoxImplOhos::EditBoxImplOhos(EditBox* pEditText) + : EditBoxImplCommon(pEditText) + , _editBoxIndex(-1) + { + + } + + EditBoxImplOhos::~EditBoxImplOhos() + { + s_allEditBoxes.erase(_editBoxIndex); + JSFunction::getFunction("CocosEditBox.removeCocosEditBox").invoke(_editBoxIndex); + } + + bool EditBoxImplOhos::isEditing() + { + return false; + } + + void EditBoxImplOhos::setNativeText(const char* pText) + { + JSFunction::getFunction("CocosEditBox.setCurrentText").invoke(_editBoxIndex, pText); + } + + void EditBoxImplOhos::setNativeFont(const char* pFontName, int fontSize) + { + auto director = cocos2d::Director::getInstance(); + auto glView = director->getOpenGLView(); + auto isFontFileExists = cocos2d::FileUtils::getInstance()->isFileExist(pFontName); + std::string realFontPath = pFontName; + if (isFontFileExists) { + realFontPath = cocos2d::FileUtils::getInstance()->fullPathForFilename(pFontName); + if (realFontPath.find("rawfile/") == 0) + { + realFontPath = realFontPath.substr(strlen("rawfile/")); // Chop out the 'assets/' portion of the path. + } + } + auto realFontsize = fontSize * glView->getScaleX(); + JSFunction::getFunction("CocosEditBox.setEditBoxFontSize").invoke(_editBoxIndex, realFontsize); + JSFunction::getFunction("CocosEditBox.setEditBoxFontPath").invoke(_editBoxIndex, realFontPath); + } + + void EditBoxImplOhos::setNativeFontColor(const Color4B& color) + { + JSFunction::getFunction("CocosEditBox.setEditBoxFontColor").invoke(_editBoxIndex, (int)color.r, (int)color.g, (int)color.b, (int)color.a); + } + + void EditBoxImplOhos::setNativePlaceHolder(const char* pText) + { + JSFunction::getFunction("CocosEditBox.setEditBoxPlaceHolder").invoke(_editBoxIndex, pText); + } + + void EditBoxImplOhos::setNativePlaceholderFont(const char* pFontName, int fontSize) + { + auto director = cocos2d::Director::getInstance(); + auto glView = director->getOpenGLView(); + auto isFontFileExists = cocos2d::FileUtils::getInstance()->isFileExist(pFontName); + std::string realFontPath = pFontName; + if (isFontFileExists) { + realFontPath = cocos2d::FileUtils::getInstance()->fullPathForFilename(pFontName); + if (realFontPath.find("rawfile/") == 0) + { + realFontPath = realFontPath.substr(strlen("rawfile/")); // Chop out the 'assets/' portion of the path. + } + } + auto realFontsize = fontSize * glView->getScaleX(); + JSFunction::getFunction("CocosEditBox.setEditBoxPlaceHolderFontSize").invoke(_editBoxIndex, realFontsize); + JSFunction::getFunction("CocosEditBox.setEditBoxPlaceHolderFontPath").invoke(_editBoxIndex, realFontPath); + } + + void EditBoxImplOhos::setNativePlaceholderFontColor(const Color4B& color) + { + JSFunction::getFunction("CocosEditBox.setEditBoxPlaceHolderFontColor").invoke(_editBoxIndex, (int)color.r, (int)color.g, (int)color.b, (int)color.a); + } + + void EditBoxImplOhos::setNativeMaxLength(int maxLength) + { + JSFunction::getFunction("CocosEditBox.setEditBoxMaxLength").invoke(_editBoxIndex, maxLength); + } + + void EditBoxImplOhos::setNativeInputMode(EditBox::InputMode inputMode) + { + JSFunction::getFunction("CocosEditBox.setNativeInputMode").invoke(_editBoxIndex, static_cast(inputMode)); + } + + void EditBoxImplOhos::setNativeInputFlag(EditBox::InputFlag inputFlag) + { + JSFunction::getFunction("CocosEditBox.setNativeInputFlag").invoke(_editBoxIndex, static_cast(inputFlag)); + } + + void EditBoxImplOhos::setNativeReturnType(EditBox::KeyboardReturnType returnType) + { + OHOS_LOGW("OHOS not support returnType %{public}d", returnType); + } + + void EditBoxImplOhos::setNativeVisible(bool visible) + { + JSFunction::getFunction("CocosEditBox.setEditBoxVisible").invoke(_editBoxIndex, visible); + } + + void EditBoxImplOhos::updateNativeFrame(const Rect& rect) + { + JSFunction::getFunction("CocosEditBox.setEditBoxViewRect").invoke(_editBoxIndex, (int)rect.origin.x, (int)rect.origin.y, (int)rect.size.width, (int)rect.size.height); + } + + void EditBoxImplOhos::nativeOpenKeyboard() + { + JSFunction::getFunction("CocosEditBox.setEditBoxVisible").invoke(_editBoxIndex, true); + } + + void EditBoxImplOhos::nativeCloseKeyboard() + { + JSFunction::getFunction("CocosEditBox.setEditBoxVisible").invoke(_editBoxIndex, false); + } + + void EditBoxImplOhos::hideAllEditBox() { + JSFunction::getFunction("CocosEditBox.hideAllEditBox").invoke(); + } + + void EditBoxImplOhos::onBeginCallBack(int index) + { + OHOS_LOGD("textinput editBoxEditingDidBegin"); + auto it = s_allEditBoxes.find(index); + if (it != s_allEditBoxes.end()) + { + s_allEditBoxes[index]->editBoxEditingDidBegin(); + } + } + + void EditBoxImplOhos::onChangeCallBack(int index, const std::string& text) + { + OHOS_LOGD("textinput onChangeCallBack"); + auto it = s_allEditBoxes.find(index); + if (it != s_allEditBoxes.end()) + { + s_allEditBoxes[index]->editBoxEditingChanged(text); + } + } + + void EditBoxImplOhos::onEnterCallBack(int index, const std::string& text) + { + OHOS_LOGD("textinput onEnterCallBack"); + JSFunction::getFunction("CocosEditBox.setEditBoxVisible").invoke(index, false); + auto it = s_allEditBoxes.find(index); + if (it != s_allEditBoxes.end()) + { + s_allEditBoxes[index]->editBoxEditingDidEnd(text); + } + } + + const char* EditBoxImplOhos::getNativeDefaultFontName() + { + return "sans-serif"; + } +} + +NS_CC_END + +#endif /* #if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) */ diff --git a/cocos/ui/UIEditBox/UIEditBoxImpl-ohos.h b/cocos/ui/UIEditBox/UIEditBoxImpl-ohos.h new file mode 100644 index 000000000000..f9215fe7e993 --- /dev/null +++ b/cocos/ui/UIEditBox/UIEditBoxImpl-ohos.h @@ -0,0 +1,68 @@ +#ifndef __UIEDITBOXIMPLOHOS_H__ +#define __UIEDITBOXIMPLOHOS_H__ + +#include "platform/CCPlatformConfig.h" + +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + +#include "ui/UIEditBox/UIEditBoxImpl-common.h" + +NS_CC_BEGIN + +class Label; + +namespace ui { + + class EditBox; + + class EditBoxImplOhos : public EditBoxImplCommon + { + public: + /** + * @js NA + */ + EditBoxImplOhos(EditBox* pEditText); + /** + * @js NA + * @lua NA + */ + virtual ~EditBoxImplOhos(); + + virtual bool isEditing() override; + virtual void createNativeControl(const Rect& frame) override; + virtual void setNativeFont(const char* pFontName, int fontSize) override ; + virtual void setNativeFontColor(const Color4B& color) override ; + virtual void setNativePlaceholderFont(const char* pFontName, int fontSize) override ; + virtual void setNativePlaceholderFontColor(const Color4B& color) override ; + virtual void setNativeInputMode(EditBox::InputMode inputMode) override ; + virtual void setNativeInputFlag(EditBox::InputFlag inputFlag) override ; + virtual void setNativeReturnType(EditBox::KeyboardReturnType returnType)override ; + virtual void setNativeText(const char* pText) override ; + virtual void setNativePlaceHolder(const char* pText) override ; + virtual void setNativeVisible(bool visible) override ; + virtual void updateNativeFrame(const Rect& rect) override ; + virtual void setNativeContentSize(const Size& size) override {}; + virtual const char* getNativeDefaultFontName() override ; + virtual void nativeOpenKeyboard() override ; + virtual void nativeCloseKeyboard() override ; + virtual void setNativeMaxLength(int maxLength) override; + + static void hideAllEditBox(); + static void onBeginCallBack(int index); + static void onChangeCallBack(int index, const std::string& text); + static void onEnterCallBack(int index, const std::string& text); + + private: + virtual void doAnimationWhenKeyboardMove(float duration, float distance)override {}; + int _editBoxIndex; + }; + + +} + +NS_CC_END + +#endif /* #if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) */ + +#endif /* __UIEDITBOXIMPLOHOS_H__ */ + diff --git a/cocos/ui/UIHelper.cpp b/cocos/ui/UIHelper.cpp index b523dcb44a40..e53823f364ea 100644 --- a/cocos/ui/UIHelper.cpp +++ b/cocos/ui/UIHelper.cpp @@ -25,6 +25,10 @@ THE SOFTWARE. #include "ui/UIHelper.h" #include "ui/UIWidget.h" #include "ui/UILayoutComponent.h" +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) +#include "base/CCDirector.h" +#include "base/ccUTF8.h" +#endif NS_CC_BEGIN @@ -191,6 +195,28 @@ Rect Helper::restrictCapInsetRect(const cocos2d::Rect &capInsets, const Size& te } return Rect(x, y, width, height); } + +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) +Rect Helper::convertBoundingBoxToScreen(Node* node) +{ + auto director = Director::getInstance(); + auto glView = director->getOpenGLView(); + auto frameSize = glView->getFrameSize(); + + auto winSize = director->getWinSize(); + auto leftBottom = node->convertToWorldSpace(Point::ZERO); + + auto contentSize = node->getContentSize(); + auto rightTop = node->convertToWorldSpace(Point(contentSize.width, contentSize.height)); + + auto uiLeft = frameSize.width / 2 + (leftBottom.x - winSize.width / 2 ) * glView->getScaleX(); + auto uiTop = frameSize.height /2 - (rightTop.y - winSize.height / 2) * glView->getScaleY(); + auto uiWidth = (rightTop.x - leftBottom.x) * glView->getScaleX(); + auto uiHeight = (rightTop.y - leftBottom.y) * glView->getScaleY(); + + return Rect(uiLeft, uiTop, uiWidth, uiHeight); +} +#endif } NS_CC_END diff --git a/cocos/ui/UIHelper.h b/cocos/ui/UIHelper.h index 51573e8b69e6..134455bf44fb 100644 --- a/cocos/ui/UIHelper.h +++ b/cocos/ui/UIHelper.h @@ -111,6 +111,16 @@ class CC_GUI_DLL Helper *@return a restricted capInset. */ static Rect restrictCapInsetRect(const Rect& capInsets, const Size& textureSize); +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + /**ohos + *@brief Convert a node's boundingBox rect into screen coordinates. + * + * @param node Any node pointer. + * + * @return A Rect in screen coordinates. + */ + static Rect convertBoundingBoxToScreen(Node* node); +#endif }; } diff --git a/cocos/ui/UIVideoPlayer-ohos.cpp b/cocos/ui/UIVideoPlayer-ohos.cpp new file mode 100644 index 000000000000..9c157abaca7d --- /dev/null +++ b/cocos/ui/UIVideoPlayer-ohos.cpp @@ -0,0 +1,305 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +#include "ui/UIVideoPlayer.h" +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + +#include +#include +#include +#include "base/CCDirector.h" +#include "base/CCEventListenerKeyboard.h" +#include "platform/ohos/CCFileUtils-ohos.h" +#include "ui/UIVideoPlayer-ohos.h" +#include "platform/ohos/napi/helper/NapiHelper.h" + +#include "ui/UIHelper.h" + +USING_NS_CC; +#define QUIT_FULLSCREEN 1000 + +//----------------------------------------------------------------------------------------------------------- + +using namespace cocos2d::experimental::ui; +static int32_t kVideoPlayerTag = 0; +static std::unordered_map s_allVideoPlayers; +static const std::string SANDBOX_PREFIX = "file://"; + +VideoPlayer::VideoPlayer() + : _fullScreenDirty(false), + _fullScreenEnabled(false), + _keepAspectRatioEnabled(false), + _videoPlayerIndex(-1), + _eventCallback(nullptr), + _isPlaying(false), + _isLooping(false), + _isUserInputEnabled(true), + _styleType(StyleType::DEFAULT) +{ + // 增加索引 + _videoPlayerIndex = kVideoPlayerTag++; + s_allVideoPlayers[_videoPlayerIndex] = this; + +#if CC_VIDEOPLAYER_DEBUG_DRAW + _debugDrawNode = DrawNode::create(); + addChild(_debugDrawNode); +#endif + JSFunction::getFunction("VideoPlayer.createVideoPlayer").invoke(_videoPlayerIndex); +} + +VideoPlayer::~VideoPlayer() +{ + if (_videoPlayerIndex != -1 && kVideoPlayerTag != -1) { + JSFunction::getFunction("VideoPlayer.removeVideoPlayer").invoke(_videoPlayerIndex); + auto iter = s_allVideoPlayers.find(_videoPlayerIndex); + if (iter != s_allVideoPlayers.end()) { + s_allVideoPlayers.erase(iter); + } + } +} + +void VideoPlayer::setFileName(const std::string &fileName) +{ + _videoURL = FileUtils::getInstance()->fullPathForFilename(fileName); + if (_videoURL[0] == '/') { + _videoSource = VideoPlayer::Source::URL; + JSFunction::getFunction("VideoPlayer.setURL").invoke(_videoPlayerIndex, SANDBOX_PREFIX + _videoURL, (int)_videoSource); + } else { + _videoSource = VideoPlayer::Source::FILENAME; + JSFunction::getFunction("VideoPlayer.setURL").invoke(_videoPlayerIndex, _videoURL, (int)_videoSource); + } +} + +void VideoPlayer::setURL(const std::string &videoUrl) +{ + _videoURL = videoUrl; + _videoSource = VideoPlayer::Source::URL; + JSFunction::getFunction("VideoPlayer.setURL").invoke(_videoPlayerIndex, _videoURL, (int)_videoSource); +} + +void VideoPlayer::setLooping(bool looping) +{ + _isLooping = looping; + JSFunction::getFunction("VideoPlayer.setLooping").invoke(_videoPlayerIndex, _isLooping); +} + +void VideoPlayer::setUserInputEnabled(bool enableInput) +{ + _isUserInputEnabled = enableInput; + // todo:鸿蒙暂时不支持 +} + +void VideoPlayer::setStyle(StyleType style) +{ + _styleType = style; +} + +void VideoPlayer::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) +{ + cocos2d::ui::Widget::draw(renderer, transform, flags); + + if (flags & FLAGS_TRANSFORM_DIRTY) { + auto uiRect = cocos2d::ui::Helper::convertBoundingBoxToScreen(this); + JSFunction::getFunction("VideoPlayer.setVideoPlayerRect").invoke(_videoPlayerIndex, (int)uiRect.origin.x, (int)uiRect.origin.y, + (int)uiRect.size.width, (int)uiRect.size.height); + } + +#if CC_VIDEOPLAYER_DEBUG_DRAW + _debugDrawNode->clear(); + auto size = getContentSize(); + Point vertices[4] = {Point::ZERO, Point(size.width, 0), Point(size.width, size.height), Point(0, size.height)}; + _debugdrawNode->drawPoly(vertices, 4, true, Color4F(1.0, 1.0, 1.0, 1.0)); +#endif +} + +void VideoPlayer::setFullScreenEnabled(bool enabled) +{ + if (_fullScreenEnabled != enabled) { + _fullScreenEnabled = enabled; + JSFunction::getFunction("VideoPlayer.requestFullscreen").invoke(_videoPlayerIndex, enabled); + } +} + +bool VideoPlayer::isFullScreenEnabled() const +{ + return _fullScreenEnabled; +} + +void VideoPlayer::setKeepAspectRatioEnabled(bool enable) +{ + if (_keepAspectRatioEnabled != enable) { + _keepAspectRatioEnabled = enable; + JSFunction::getFunction("VideoPlayer.setKeepAspectRatioEnabled").invoke(_videoPlayerIndex, enable); + } +} + +#if CC_VIDEOPLAYER_DEBUG_DRAW +void VideoPlayer::drawDebugData() +{ + Director *director = Director::getInstance(); + CCASSERT(nullptr != director, "Director is null when setting matrix stack"); + + director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); + director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform); + + auto size = getContentSize(); + + Point vertices[4] = {Point::ZERO, Point(size.width, 0), Point(size.width, size.height), Point(0, size.height)}; + + DrawPrimitives::drawPoly(vertices, 4, true); + + director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); +} +#endif + +void VideoPlayer::play() +{ + if (!_videoURL.empty()) { + JSFunction::getFunction("VideoPlayer.play").invoke(_videoPlayerIndex); + } +} + +void VideoPlayer::pause() +{ + if (!_videoURL.empty()) { + JSFunction::getFunction("VideoPlayer.pause").invoke(_videoPlayerIndex); + } +} + +void VideoPlayer::resume() +{ + if (!_videoURL.empty()) { + JSFunction::getFunction("VideoPlayer.play").invoke(_videoPlayerIndex); + } +} + +void VideoPlayer::stop() +{ + if (!_videoURL.empty()) { + JSFunction::getFunction("VideoPlayer.stop").invoke(_videoPlayerIndex); + } +} + +void VideoPlayer::seekTo(float sec) +{ + if (!_videoURL.empty()) { + JSFunction::getFunction("VideoPlayer.seekTo").invoke(_videoPlayerIndex, (int)sec); + } +} + +bool VideoPlayer::isPlaying() const +{ + return _isPlaying; +} + +bool VideoPlayer::isLooping() const +{ + return _isLooping; +} + +bool VideoPlayer::isUserInputEnabled() const +{ + return _isUserInputEnabled; +} + +void VideoPlayer::setVisible(bool visible) +{ + cocos2d::ui::Widget::setVisible(visible); + + if (!visible || isRunning()) { + JSFunction::getFunction("VideoPlayer.setVisible").invoke(_videoPlayerIndex, visible); + } +} + +void VideoPlayer::onEnter() +{ + Widget::onEnter(); + if (isVisible() && !_videoURL.empty()) { + JSFunction::getFunction("VideoPlayer.setVisible").invoke(_videoPlayerIndex, true); + } +} + +void VideoPlayer::onExit() +{ + Widget::onExit(); + JSFunction::getFunction("VideoPlayer.setVisible").invoke(_videoPlayerIndex, false); +} + +void VideoPlayer::addEventListener(const VideoPlayer::ccVideoPlayerCallback &callback) +{ + _eventCallback = callback; +} + +void VideoPlayer::onPlayEvent(int event) +{ + if (event == QUIT_FULLSCREEN) { + _fullScreenEnabled = false; + } else { + VideoPlayer::EventType videoEvent = (VideoPlayer::EventType)event; + if (videoEvent == VideoPlayer::EventType::PLAYING) { + _isPlaying = true; + } else { + _isPlaying = false; + } + + if (_eventCallback) { + _eventCallback(this, videoEvent); + } + } +} + +cocos2d::ui::Widget *VideoPlayer::createCloneInstance() +{ + return VideoPlayer::create(); +} + +void VideoPlayer::copySpecialProperties(Widget *widget) +{ + VideoPlayer *videoPlayer = dynamic_cast(widget); + if (videoPlayer) { + _isPlaying = videoPlayer->_isPlaying; + _isLooping = videoPlayer->_isLooping; + _isUserInputEnabled = videoPlayer->_isUserInputEnabled; + _styleType = videoPlayer->_styleType; + _fullScreenEnabled = videoPlayer->_fullScreenEnabled; + _fullScreenDirty = videoPlayer->_fullScreenDirty; + _videoURL = videoPlayer->_videoURL; + _keepAspectRatioEnabled = videoPlayer->_keepAspectRatioEnabled; + _videoSource = videoPlayer->_videoSource; + _videoPlayerIndex = videoPlayer->_videoPlayerIndex; + _eventCallback = videoPlayer->_eventCallback; + _videoView = videoPlayer->_videoView; + } +} + +void executeVideoCallback(int index, int event) +{ + auto it = s_allVideoPlayers.find(index); + if (it != s_allVideoPlayers.end()) { + s_allVideoPlayers[index]->onPlayEvent(event); + } +} + +#endif diff --git a/cocos/ui/UIVideoPlayer-ohos.h b/cocos/ui/UIVideoPlayer-ohos.h new file mode 100644 index 000000000000..46ea07db33ab --- /dev/null +++ b/cocos/ui/UIVideoPlayer-ohos.h @@ -0,0 +1,7 @@ +#pragma once + +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + +void executeVideoCallback(int index, int event); + +#endif \ No newline at end of file diff --git a/cocos/ui/UIVideoPlayer.h b/cocos/ui/UIVideoPlayer.h index 81b688a22ae5..d6dfab23a4bc 100644 --- a/cocos/ui/UIVideoPlayer.h +++ b/cocos/ui/UIVideoPlayer.h @@ -25,7 +25,7 @@ #ifndef __COCOS2D_UI_VIDEOWEIGTH_H_ #define __COCOS2D_UI_VIDEOWEIGTH_H_ -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #include "ui/UIWidget.h" @@ -57,7 +57,19 @@ namespace experimental{ PLAYING = 0, PAUSED, STOPPED, - COMPLETED + COMPLETED, + ERROR + }; + + /** + * Styles of how the the video player is presented + * For now only used on iOS to use either MPMovieControlStyleEmbedded (DEFAULT) or + * MPMovieControlStyleNone (NONE) + */ + enum class StyleType + { + DEFAULT = 0, + NONE }; /** @@ -172,6 +184,48 @@ namespace experimental{ virtual void onPlayEvent(int event); virtual void setVisible(bool visible) override; virtual void draw(Renderer *renderer, const Mat4& transform, uint32_t flags) override; + + #if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + /** + * @brief Set if playback is done in loop mode + * + * @param looping the video will or not automatically restart at the end + */ + virtual void setLooping(bool looping); + + /** + * Set if the player will enable user input for basic pause and resume of video + * + * @param enableInput If true, input will be handled for basic functionality (pause/resume) + */ + virtual void setUserInputEnabled(bool enableInput); + + /** + * Set the style of the player + * + * @param style The corresponding style + */ + virtual void setStyle(StyleType style); + + /** + * Checks whether the VideoPlayer is set with looping mode. + * + * @return true if the videoplayer is set to loop, false otherwise. + */ + virtual bool isLooping() const; + + + /** + * Checks whether the VideoPlayer is set to listen user input to resume and pause the video + * + * @return true if the videoplayer user input is set, false otherwise. + */ + virtual bool isUserInputEnabled() const; + + virtual void onEnter() ; + + virtual void onExit() ; +#endif protected: virtual cocos2d::ui::Widget* createCloneInstance() override; @@ -204,6 +258,11 @@ namespace experimental{ ccVideoPlayerCallback _eventCallback; void* _videoView; +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + bool _isLooping; + bool _isUserInputEnabled; + StyleType _styleType; +#endif }; } } diff --git a/cocos/ui/UIWebView.cpp b/cocos/ui/UIWebView.cpp index b7b2a4fc7db6..3ba037bdc460 100644 --- a/cocos/ui/UIWebView.cpp +++ b/cocos/ui/UIWebView.cpp @@ -29,3 +29,7 @@ #include "UIWebView-inl.h" #endif +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) +#include "ui/UIWebViewImpl-ohos.h" +#include "ui/UIWebView-inl.h" +#endif diff --git a/cocos/ui/UIWebView.h b/cocos/ui/UIWebView.h index 303dcd366b4c..14b9134b46ec 100644 --- a/cocos/ui/UIWebView.h +++ b/cocos/ui/UIWebView.h @@ -27,7 +27,7 @@ #include "platform/CCPlatformConfig.h" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS ) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_IOS ) #include "ui/UIWidget.h" diff --git a/cocos/ui/UIWebViewImpl-ohos.cpp b/cocos/ui/UIWebViewImpl-ohos.cpp new file mode 100644 index 000000000000..192d359fba50 --- /dev/null +++ b/cocos/ui/UIWebViewImpl-ohos.cpp @@ -0,0 +1,209 @@ +/**************************************************************************** + Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You shall + not use Cocos Creator software for developing other software or tools that's + used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. + + 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 "ui/UIWebViewImpl-ohos.h" + +#include +#include +#include + +#include "platform/CCFileUtils.h" +#include "platform/ohos/CCLogOhos.h" +#include "platform/ohos/napi/helper/NapiHelper.h" +#include "ui/UIHelper.h" +#include "ui/UIWebView.h" + +static const std::string SANDBOX_PREFIX = "file://"; +static const char S_MIME_TYPE_TEXT[] = "text/html"; +static const char S_ENCODING_UTF8[] = "UTF-8"; + +NS_CC_BEGIN +namespace experimental { + namespace ui { + static int32_t kWebViewTag = 0; + static std::unordered_map sWebViewImpls; + + WebViewImpl::WebViewImpl(WebView *webView) : _viewTag(-1), _webView(webView) { + _viewTag = kWebViewTag++; + JSFunction::getFunction("WebView.createWebView").invoke(_viewTag); + sWebViewImpls[_viewTag] = this; + } + + WebViewImpl::~WebViewImpl() { + if (_viewTag != -1) { + JSFunction::getFunction("WebView.removeWebView").invoke(_viewTag); + auto iter = sWebViewImpls.find(_viewTag); + if (iter != sWebViewImpls.end()) { + sWebViewImpls.erase(iter); + } + _viewTag = -1; + } + } + + void WebViewImpl::setJavascriptInterfaceScheme(const std::string &scheme) { + JSFunction::getFunction("WebView.setJavascriptInterfaceScheme").invoke(_viewTag, scheme); + } + + void WebViewImpl::loadData(const Data &data, const std::string &mimeType, + const std::string &encoding, const std::string &baseURL) { + std::string dataString(reinterpret_cast(data.getBytes()), + static_cast(data.getSize())); + JSFunction::getFunction("WebView.loadData").invoke(_viewTag, dataString, mimeType, encoding, baseURL); + } + + void WebViewImpl::loadHTMLString(const std::string &string, const std::string &baseURL) { + JSFunction::getFunction("WebView.loadData").invoke(_viewTag, string, S_MIME_TYPE_TEXT, S_ENCODING_UTF8, baseURL); + } + + void WebViewImpl::loadURL(const std::string &url) { + JSFunction::getFunction("WebView.loadURL").invoke(_viewTag, url); + } + + void WebViewImpl::loadURL(const std::string &url, bool cleanCachedData) { + // ӿδṩزһloadUrlʵһ + JSFunction::getFunction("WebView.loadURL").invoke(_viewTag, url); + } + + void WebViewImpl::loadFile(const std::string &fileName) { + std::string fullPath = FileUtils::getInstance()->fullPathForFilename(fileName); + if(fullPath[0] == '/') { + JSFunction::getFunction("WebView.loadURL").invoke(_viewTag, SANDBOX_PREFIX + fullPath); + } else { + JSFunction::getFunction("WebView.loadFile").invoke(_viewTag, fullPath); + } + } + + void WebViewImpl::stopLoading() { + JSFunction::getFunction("WebView.stopLoading").invoke(_viewTag); + } + + void WebViewImpl::reload() { + JSFunction::getFunction("WebView.reload").invoke(_viewTag); + } + + bool WebViewImpl::canGoBack() { + // return JSFunction::getFunction("WebView.canGoBack").invoke(_viewTag); + return true; + } + + bool WebViewImpl::canGoForward() { + // return JSFunction::getFunction("WebView.canGoForward").invoke(_viewTag); + return true; + } + + void WebViewImpl::goBack() { + JSFunction::getFunction("WebView.goBack").invoke(_viewTag); + } + + void WebViewImpl::goForward() { + JSFunction::getFunction("WebView.goForward").invoke(_viewTag); + } + + void WebViewImpl::evaluateJS(const std::string &js) { + JSFunction::getFunction("WebView.evaluateJS").invoke(_viewTag, js); + } + + void WebViewImpl::setScalesPageToFit(bool scalesPageToFit) { + JSFunction::getFunction("WebView.setScalesPageToFit").invoke(_viewTag, scalesPageToFit); + } + + void WebViewImpl::draw(cocos2d::Renderer *renderer, cocos2d::Mat4 const &transform, uint32_t flags) { + if (flags & cocos2d::Node::FLAGS_TRANSFORM_DIRTY) { + auto uiRect = cocos2d::ui::Helper::convertBoundingBoxToScreen(_webView); + JSFunction::getFunction("WebView.setWebViewRect") + .invoke(_viewTag, (int) uiRect.origin.x, (int) uiRect.origin.y, + (int) uiRect.size.width, (int) uiRect.size.height); + } + } + + void WebViewImpl::setVisible(bool visible) { + JSFunction::getFunction("WebView.setVisible").invoke(_viewTag, visible); + } + + void WebViewImpl::setOpacityWebView(const float opacity) { + _opacity = opacity; + JSFunction::getFunction("WebView.setOpacityWebView").invoke(_viewTag, (double)_opacity); + } + + float WebViewImpl::getOpacityWebView() const { + return _opacity; + } + + void WebViewImpl::setBackgroundTransparent() { + JSFunction::getFunction("WebView.setBackgroundTransparent").invoke(_viewTag); + } + + void WebViewImpl::setBounces(bool bounces) { + // empty function as this was mainly a fix for iOS + } + + bool WebViewImpl::shouldStartLoading(int viewTag, const std::string &url) { + bool allowLoad = true; + auto it = sWebViewImpls.find(viewTag); + if (it != sWebViewImpls.end()) { + auto webView = it->second->_webView; + if (webView->getOnShouldStartLoading()) { + std::function < bool(WebView * sender, + const std::string &url)> fun = webView->getOnShouldStartLoading(); + allowLoad = fun(webView, url); + } + } + return allowLoad; + } + + void WebViewImpl::finishLoading(int viewTag, const std::string &url) { + auto it = sWebViewImpls.find(viewTag); + if (it != sWebViewImpls.end()) { + auto webView = it->second->_webView; + if (webView->getOnDidFinishLoading()) { + WebView::ccWebViewCallback fun = webView->getOnDidFinishLoading(); + fun(webView, url); + } + } + } + + void WebViewImpl::failLoading(int viewTag, const std::string &url) { + auto it = sWebViewImpls.find(viewTag); + if (it != sWebViewImpls.end()) { + auto webView = it->second->_webView; + if (webView->getOnDidFailLoading()) { + WebView::ccWebViewCallback fun = webView->getOnDidFailLoading(); + fun(webView, url); + } + } + } + + void WebViewImpl::jsCallback(int viewTag, const std::string &message) { + auto it = sWebViewImpls.find(viewTag); + if (it != sWebViewImpls.end()) { + auto webView = it->second->_webView; + if (webView->getOnJSCallback()) { + WebView::ccWebViewCallback fun = webView->getOnJSCallback(); + fun(webView, message); + } + } + } + } // namespace ui +} // namespace experimental +NS_CC_END diff --git a/cocos/ui/UIWebViewImpl-ohos.h b/cocos/ui/UIWebViewImpl-ohos.h new file mode 100644 index 000000000000..8d2a84af6cd5 --- /dev/null +++ b/cocos/ui/UIWebViewImpl-ohos.h @@ -0,0 +1,111 @@ +/**************************************************************************** + 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. + ****************************************************************************/ +#ifndef __COCOS2D__UI__WEBVIEWIMPL_OPENHARMONY_H_ +#define __COCOS2D__UI__WEBVIEWIMPL_OPENHARMONY_H_ +#include +#include + +namespace cocos2d { + class Data; + class Renderer; + class Mat4; + + namespace experimental { + namespace ui{ + class WebView; + } + } +} + +namespace cocos2d { + namespace experimental { + namespace ui{ + + class WebViewImpl { + public: + WebViewImpl(cocos2d::experimental::ui::WebView *webView); + + virtual ~WebViewImpl(); + + void setJavascriptInterfaceScheme(const std::string &scheme); + + void loadData(const cocos2d::Data &data, const std::string &MIMEType, const std::string &encoding, const std::string &baseURL); + + void loadHTMLString(const std::string &string, const std::string &baseURL); + + void loadURL(const std::string &url); + + void loadURL(const std::string &url, bool cleanCachedData); + + void loadFile(const std::string &fileName); + + void stopLoading(); + + void reload(); + + bool canGoBack(); + + bool canGoForward(); + + void goBack(); + + void goForward(); + + void evaluateJS(const std::string &js); + + void setScalesPageToFit(const bool scalesPageToFit); + + virtual void draw(cocos2d::Renderer *renderer, cocos2d::Mat4 const &transform, uint32_t flags); + + virtual void setVisible(bool visible); + + void setOpacityWebView(float opacity); + + float getOpacityWebView()const; + + void setBackgroundTransparent(); + + void setBounces(bool bounces); + + static bool shouldStartLoading(int viewTag, const std::string& url); + + static void finishLoading(int viewTag, const std::string& url); + + static void failLoading(int viewTag, const std::string& url); + + static void jsCallback(int viewTag, const std::string& message); + public: + int _viewTag; + WebView *_webView; + float _opacity = 1.0f; + }; + + } // namespace ui + } // namespace experimental +} //cocos2d + + +/// @endcond +#endif /* __COCOS2D__UI__WEBVIEWIMPL_OPENHARMONY_H_ */ diff --git a/extensions/Particle3D/PU/CCPUMaterialManager.cpp b/extensions/Particle3D/PU/CCPUMaterialManager.cpp index 96dda1b2c73d..5df78b7455b1 100644 --- a/extensions/Particle3D/PU/CCPUMaterialManager.cpp +++ b/extensions/Particle3D/PU/CCPUMaterialManager.cpp @@ -34,6 +34,11 @@ #elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) #include "android/CCFileUtils-android.h" #include +#elif (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) +#include "platform/ohos/CCFileUtils-ohos.h" +#include +#include +#include #elif (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC) #include #elif (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX) @@ -159,6 +164,16 @@ bool PUMaterialCache::loadMaterialsFromSearchPaths( const std::string &fileFolde } } AAssetDir_close(dir); +#elif (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + std::vector files = static_cast(FileUtils::getInstance())-> listFiles(fileFolder); + for (auto fileName : files) { + if (FileUtils::getInstance()->getFileExtension(fileName) == ".material") + { + std::string fullpath = std::string(fileName); + loadMaterials(fullpath); + state = true; + } + } #elif (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC) ftw(fileFolder.c_str(), iterPath, 500); diff --git a/extensions/Particle3D/PU/CCPUParticleSystem3D.h b/extensions/Particle3D/PU/CCPUParticleSystem3D.h index c2d2ce0fc9b9..f53ec9b765a5 100644 --- a/extensions/Particle3D/PU/CCPUParticleSystem3D.h +++ b/extensions/Particle3D/PU/CCPUParticleSystem3D.h @@ -351,12 +351,15 @@ class CC_DLL PUParticleSystem3D : public ParticleSystem3D void calulateRotationOffset(void); virtual PUParticleSystem3D* clone(); - virtual void copyAttributesTo(PUParticleSystem3D* system); + virtual void copyAttributesTo (PUParticleSystem3D* system); CC_CONSTRUCTOR_ACCESS: PUParticleSystem3D(); virtual ~PUParticleSystem3D(); + #if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + bool initSystem(const std::string &filePath); + #endif bool initWithFilePath(const std::string &filePath); bool initWithFilePathAndMaterialPath (const std::string &filePath, const std::string &materialPath); @@ -379,7 +382,9 @@ class CC_DLL PUParticleSystem3D : public ParticleSystem3D inline bool isExpired(PUParticle3D* particle, float timeElapsed); - bool initSystem(const std::string &filePath); + #if (CC_TARGET_PLATFORM != CC_PLATFORM_OHOS) + bool initSystem(const std::string &filePath); + #endif static void convertToUnixStylePath(std::string &path); protected: diff --git a/tests/cpp-tests/CMakeLists.txt b/tests/cpp-tests/CMakeLists.txt index 0dd94940edeb..c89cab5c373a 100644 --- a/tests/cpp-tests/CMakeLists.txt +++ b/tests/cpp-tests/CMakeLists.txt @@ -1,7 +1,8 @@ set(APP_NAME cpp-tests) - -# Use same method as in cocos library -cocos_find_package(CURL CURL REQUIRED) +if(NOT OHOS) + # Use same method as in cocos library + cocos_find_package(CURL CURL REQUIRED) +endif() if(WIN32) set(PLATFORM_SRC proj.win32/main.cpp) @@ -16,6 +17,11 @@ elseif(MACOSX) elseif(LINUX) set(PLATFORM_SRC proj.linux/main.cpp) set(RES_PREFIX "/Resources") + +elseif(OHOS) + set(PLATFORM_SRC + proj.ohos/entry/src/main/cpp/main.cpp) + set(RES_PREFIX "/Resources") else() message( FATAL_ERROR "Unsupported platform, CMake will exit" ) endif() @@ -213,6 +219,15 @@ set(TESTS_SRC ${PLATFORM_SRC} ) +if(OHOS) + list(APPEND TESTS_SRC + Classes/UITest/CocoStudioGUITest/UIVideoPlayerTest/UIVideoPlayerTest.h + Classes/UITest/CocoStudioGUITest/UIWebViewTest/UIWebViewTest.h + Classes/UITest/CocoStudioGUITest/UIVideoPlayerTest/UIVideoPlayerTest.cpp + Classes/UITest/CocoStudioGUITest/UIWebViewTest/UIWebViewTest.cpp + ) +endif() + if(USE_CHIPMUNK) include_directories(${CHIPMUNK_INCLUDE_DIRS}) endif() @@ -233,15 +248,7 @@ if(USE_BULLET) ) endif() -if(LINUX) - set(EXTENDED_TESTS_SRC - ) -else() - set(EXTENDED_TESTS_SRC - Classes/ExtensionsTest/NetworkTest/HttpClientTest.cpp - Classes/ExtensionsTest/NetworkTest/SocketIOTest.cpp - Classes/ExtensionsTest/NetworkTest/WebSocketTest.cpp - +set(EXTENDED_TESTS_SRC_TMP Classes/UITest/CocoStudioGUITest/CocoStudioGUITest.cpp Classes/UITest/CocoStudioGUITest/CocosGUIScene.cpp Classes/UITest/CocoStudioGUITest/CocostudioParserTest.cpp @@ -254,6 +261,20 @@ else() Classes/UITest/CocoStudioGUITest/UISceneManager_Editor.cpp Classes/UITest/CocoStudioGUITest/UIScene_Editor.cpp ) +if(LINUX) + set(EXTENDED_TESTS_SRC + ) +elseif(OHOS) + set(EXTENDED_TESTS_SRC ${EXTENDED_TESTS_SRC_TMP}) + +else() + set(EXTENDED_TESTS_SRC + Classes/ExtensionsTest/NetworkTest/HttpClientTest.cpp + Classes/ExtensionsTest/NetworkTest/SocketIOTest.cpp + Classes/ExtensionsTest/NetworkTest/WebSocketTest.cpp + + ${EXTENDED_TESTS_SRC_TMP} + ) endif() @@ -263,16 +284,100 @@ include_directories( ) # add the executable -add_executable(${APP_NAME} - ${TESTS_SRC} - ${EXTENDED_TESTS_SRC} -) +if(OHOS) + add_definitions("-DOpenHarmony") + add_definitions(-DCC_ENABLE_CHIPMUNK_INTEGRATION=1) + add_definitions("-DCOCOS2D_DEBUG=3") + add_compile_options(-DUSE_FILE32API + -Wno-absolute-value + -Wno-extra + -Wno-implicit-int-float-conversion + -Wno-overloaded-virtual + -Wno-unused-function + -Wno-unused-private-field + -Wno-unused-parameter + -Wno-reorder-ctor + -Wno-unsequenced + -Wno-extra + -Wno-c++11-narrowing + -Wno-expansion-to-defined + -Wno-unused-command-line-argument + ) + include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + Classes + Classes/ExtensionsTest + ${COCOS2DX_ROOT_PATH} + ${COCOS2DX_ROOT_PATH}/external + ${COCOS2DX_ROOT_PATH}/external/chipmunk + ${COCOS2DX_ROOT_PATH}/external/chipmunk/include/chipmunk + ${COCOS2DX_ROOT_PATH}/external/curl/include/ohos + ${COCOS2DX_ROOT_PATH}/external/lib + ${COCOS2DX_ROOT_PATH}/external/Box2D + ${COCOS2DX_ROOT_PATH}/external/tiff + ${COCOS2DX_ROOT_PATH}/external/tiff/include/ohos + ${COCOS2DX_ROOT_PATH}/cocos + ${COCOS2DX_ROOT_PATH}/cocos/editor-support + ${COCOS2DX_ROOT_PATH}/extensions + ) -target_link_libraries(${APP_NAME} - cocos2d -) + list(APPEND TESTS_SRC + ${EXTENDED_TESTS_SRC} + ) + add_library(${APP_NAME} STATIC ${TESTS_SRC}) + + find_library( # Sets the name of the path variable. + EGL-lib + # Specifies the name of the NDK library that + # you want CMake to locate. + EGL ) + + find_library( # Sets the name of the path variable. + GLES-lib + # Specifies the name of the NDK library that + # you want CMake to locate. + GLESv3 ) -cocos_use_pkg(${APP_NAME} CURL) + find_library( # Sets the name of the path variable. + hilog-lib + # Specifies the name of the NDK library that + # you want CMake to locate. + hilog_ndk.z ) + + find_library( # Sets the name of the path variable. + libace-lib + # Specifies the name of the NDK library that + # you want CMake to locate. + ace_ndk.z ) + + find_library( # Sets the name of the path variable. + libnapi-lib + # Specifies the name of the NDK library that + # you want CMake to locate. + ace_napi.z ) + + find_library( # Sets the name of the path variable. + libuv-lib + # Specifies the name of the NDK library that + # you want CMake to locate. + uv ) + + target_link_libraries(${APP_NAME} PUBLIC ${EGL-lib} ${GLES-lib} ${hilog-lib} + ${libace-lib} ${libnapi-lib} ${libuv-lib} cocos2d) + +else() + add_executable(${APP_NAME} + ${TESTS_SRC} + ${EXTENDED_TESTS_SRC} + ) + target_link_libraries(${APP_NAME} + cocos2d + ) +endif() + +if(NOT OHOS) + cocos_use_pkg(${APP_NAME} CURL) +endif() if(MACOSX OR APPLE) set_target_properties(${APP_NAME} PROPERTIES diff --git a/tests/cpp-tests/Classes/AppDelegate.cpp b/tests/cpp-tests/Classes/AppDelegate.cpp index db9ef8c4a075..c1ef45bbb5ca 100644 --- a/tests/cpp-tests/Classes/AppDelegate.cpp +++ b/tests/cpp-tests/Classes/AppDelegate.cpp @@ -73,14 +73,14 @@ bool AppDelegate::applicationDidFinishLaunching() director->setAnimationInterval(1.0 / 60); auto screenSize = glview->getFrameSize(); - auto designSize = Size(480, 320); + auto designSize = Size(1024/2, 2112/2); auto fileUtils = FileUtils::getInstance(); std::vector searchPaths; if (screenSize.height > 320) { - auto resourceSize = Size(960, 640); + auto resourceSize = Size(1024, 2112); searchPaths.push_back("hd"); searchPaths.push_back("ccs-res/hd"); searchPaths.push_back("ccs-res/hd/scenetest"); @@ -165,7 +165,13 @@ bool AppDelegate::applicationDidFinishLaunching() fileUtils->setSearchPaths(searchPaths); + +#if (CC_TARGET_PLATFORM != CC_PLATFORM_OHOS) + // a bug in DirectX 11 level9-x on the device prevents ResolutionPolicy::NO_BORDER from working correctly glview->setDesignResolutionSize(designSize.width, designSize.height, ResolutionPolicy::SHOW_ALL); +#else + glview->setDesignResolutionSize(designSize.width, designSize.height, ResolutionPolicy::NO_BORDER); +#endif // Enable Remote Console auto console = director->getConsole(); @@ -197,3 +203,17 @@ void AppDelegate::applicationWillEnterForeground() Director::getInstance()->startAnimation(); } + +void AppDelegate::applicationScreenSizeChanged(int newWidth, int newHeight) +{ + auto director = cocos2d::Director::getInstance(); + auto glview = director->getOpenGLView(); + if (glview != NULL) { + // Set ResolutionPolicy to a proper value. here use the original value when the game is started. + ResolutionPolicy resolutionPolicy = glview->getResolutionPolicy(); + Size designSize = glview->getDesignResolutionSize(); + glview->setFrameSize(newWidth, newHeight); + // Set the design resolution to a proper value. here use the original value when the game is started. + glview->setDesignResolutionSize(designSize.width, designSize.height, resolutionPolicy); + } +} diff --git a/tests/cpp-tests/Classes/AppDelegate.h b/tests/cpp-tests/Classes/AppDelegate.h index ad41b84e01ad..a668b0eaca53 100644 --- a/tests/cpp-tests/Classes/AppDelegate.h +++ b/tests/cpp-tests/Classes/AppDelegate.h @@ -63,6 +63,13 @@ class AppDelegate : private cocos2d::Application private: TestController* _testController; + + /** + @brief This function will be called when the application screen size is changed. + @param new width + @param new height + */ + virtual void applicationScreenSizeChanged(int newWidth, int newHeight); }; #endif // _APP_DELEGATE_H_ diff --git a/tests/cpp-tests/Classes/CocosDenshionTest/CocosDenshionTest.cpp b/tests/cpp-tests/Classes/CocosDenshionTest/CocosDenshionTest.cpp index 7a6be1b28572..d9084f005d3b 100644 --- a/tests/cpp-tests/Classes/CocosDenshionTest/CocosDenshionTest.cpp +++ b/tests/cpp-tests/Classes/CocosDenshionTest/CocosDenshionTest.cpp @@ -4,7 +4,7 @@ #include "audio/include/SimpleAudioEngine.h" // android effect only support ogg -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) #define EFFECT_FILE "effect2.ogg" #elif( CC_TARGET_PLATFORM == CC_PLATFORM_MARMALADE) #define EFFECT_FILE "effect1.raw" diff --git a/tests/cpp-tests/Classes/NewAudioEngineTest/NewAudioEngineTest.cpp b/tests/cpp-tests/Classes/NewAudioEngineTest/NewAudioEngineTest.cpp index a506c286a58e..1e75d2bc7598 100644 --- a/tests/cpp-tests/Classes/NewAudioEngineTest/NewAudioEngineTest.cpp +++ b/tests/cpp-tests/Classes/NewAudioEngineTest/NewAudioEngineTest.cpp @@ -23,7 +23,7 @@ ****************************************************************************/ #include "platform/CCPlatformConfig.h" -#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 #include "NewAudioEngineTest.h" #include "ui/CocosGUI.h" diff --git a/tests/cpp-tests/Classes/NewAudioEngineTest/NewAudioEngineTest.h b/tests/cpp-tests/Classes/NewAudioEngineTest/NewAudioEngineTest.h index 033c901cc717..61f7e6e13250 100644 --- a/tests/cpp-tests/Classes/NewAudioEngineTest/NewAudioEngineTest.h +++ b/tests/cpp-tests/Classes/NewAudioEngineTest/NewAudioEngineTest.h @@ -23,7 +23,7 @@ ****************************************************************************/ #include "platform/CCPlatformConfig.h" -#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 +#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 #ifndef __NEWAUDIOENGINE_TEST_H_ #define __NEWAUDIOENGINE_TEST_H_ diff --git a/tests/cpp-tests/Classes/UITest/CocoStudioGUITest/CocosGUIScene.cpp b/tests/cpp-tests/Classes/UITest/CocoStudioGUITest/CocosGUIScene.cpp index 580dddba9a00..0c5a4a88cdbd 100644 --- a/tests/cpp-tests/Classes/UITest/CocoStudioGUITest/CocosGUIScene.cpp +++ b/tests/cpp-tests/Classes/UITest/CocoStudioGUITest/CocosGUIScene.cpp @@ -16,26 +16,26 @@ #include "UIWidgetAddNodeTest/UIWidgetAddNodeTest.h" #include "UIRichTextTest/UIRichTextTest.h" #include "UIFocusTest/UIFocusTest.h" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #include "UIVideoPlayerTest/UIVideoPlayerTest.h" #endif -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #include "UIWebViewTest/UIWebViewTest.h" #endif #include "UIScale9SpriteTest.h" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) || (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_TIZEN) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) || (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) || (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_TIZEN) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) #include "UIEditBoxTest.h" #endif GUIDynamicCreateTests::GUIDynamicCreateTests() { -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) addTest("VideoPlayer Test", [](){ return new (std::nothrow) VideoPlayerTests; }); #endif -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) addTest("WebView Test", [](){ return new (std::nothrow) WebViewTests; }); #endif -#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) || (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_TIZEN) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) || (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) || (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_TIZEN) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) addTest("EditBox Test", [](){ return new (std::nothrow) UIEditBoxTests; }); #endif addTest("Focus Test", [](){ return new (std::nothrow) UIFocusTests; }); diff --git a/tests/cpp-tests/Classes/controller.cpp b/tests/cpp-tests/Classes/controller.cpp index 2040c20a5ae0..16d9717bafa6 100644 --- a/tests/cpp-tests/Classes/controller.cpp +++ b/tests/cpp-tests/Classes/controller.cpp @@ -23,7 +23,7 @@ class RootTests : public TestList addTest("Actions - Progress", [](){return new (std::nothrow) ActionsProgressTests(); }); addTest("Allocator - Basic", [](){return new (std::nothrow) AllocatorTests(); }); addTest("Audio - CocosDenshion", []() { return new (std::nothrow) CocosDenshionTests(); }); -#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) addTest("Audio - NewAudioEngine", []() { return new (std::nothrow) AudioEngineTests(); }); #endif #if CC_ENABLE_BOX2D_INTEGRATION diff --git a/tests/cpp-tests/Classes/tests.h b/tests/cpp-tests/Classes/tests.h index 0e07c5da60f7..412f5270951f 100644 --- a/tests/cpp-tests/Classes/tests.h +++ b/tests/cpp-tests/Classes/tests.h @@ -41,7 +41,7 @@ #include "PerformanceTest/PerformanceTest.h" #include "ZwoptexTest/ZwoptexTest.h" #include "CocosDenshionTest/CocosDenshionTest.h" -#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) #include "NewAudioEngineTest/NewAudioEngineTest.h" #endif #if (CC_TARGET_PLATFORM != CC_PLATFORM_EMSCRIPEN) diff --git a/tests/cpp-tests/proj.ohos/.gitignore b/tests/cpp-tests/proj.ohos/.gitignore new file mode 100644 index 000000000000..43fc9cb4cc28 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/.gitignore @@ -0,0 +1,14 @@ +.hvigor +.idea +node_modules +entry/build +entry/.idea +entry/.cxx +local.properties +oh-package-lock.json5 +entry/src/main/resources/rawfile +oh_modules +/.clangd +/.clang-tidy +.clang-format +hvigor/hvigor-wrapper.js \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/AppScope/app.json5 b/tests/cpp-tests/proj.ohos/AppScope/app.json5 new file mode 100644 index 000000000000..4c4b2a0eacd1 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/AppScope/app.json5 @@ -0,0 +1,11 @@ +{ + "app": { + "bundleName": "ohos.cocos3_7.cpp.tests", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name", + "distributedNotificationEnabled": true + } +} diff --git a/tests/cpp-tests/proj.ohos/AppScope/resources/base/element/string.json b/tests/cpp-tests/proj.ohos/AppScope/resources/base/element/string.json new file mode 100644 index 000000000000..bfe3d589c03c --- /dev/null +++ b/tests/cpp-tests/proj.ohos/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "3.7_cpp_tests" + } + ] +} diff --git a/tests/cpp-tests/proj.ohos/AppScope/resources/base/media/app_icon.png b/tests/cpp-tests/proj.ohos/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c GIT binary patch literal 6790 zcmX|G1ymHk)?T_}Vd;>R?p|tHQo6fg38|$UVM!6BLrPFWk?s;$LOP{GmJpBl$qoSA!PUg~PA65-S00{{S`XKG6NkG0RgjEntPrmV+?0|00mu7;+5 zrdpa{2QLqPJ4Y{j7=Mrl{BaxrkdY69+c~(w{Fv-v&aR%aEI&JYSeRTLWm!zbv;?)_ ziZB;fwGbbeL5Q}YLx`J$lp~A09KK8t_z}PZ=4ZzgdeKtgoc+o5EvN9A1K1_<>M?MBqb#!ASf&# zEX?<)!RH(7>1P+j=jqG(58}TVN-$psA6K}atCuI!KTJD&FMmH-78ZejBm)0qc{ESp z|LuG1{QnBUJRg_E=h1#XMWt2%fcoN@l7eAS!Es?Q+;XsRNPhiiE=@AqlLkJzF`O18 zbsbSmKN=aaq8k3NFYZfDWpKmM!coBU0(XnL8R{4=i|wi{!uWYM2je{U{B*K2PVdu&=E zTq*-XsEsJ$u5H4g6DIm2Y!DN`>^v|AqlwuCD;w45K0@eqauiqWf7l&o)+YLHm~|L~ z7$0v5mkobriU!H<@mVJHLlmQqzQ3d6Rh_-|%Yy2li*tHO>_vcnuZ7OR_xkAIuIU&x z-|8Y0wj|6|a6_I(v91y%k_kNw6pnkNdxjqG8!%Vz_d%c_!X+6-;1`GC9_FpjoHev5fEV7RhJ>r=mh-jp$fqbqRJ=obwdgLDVP5+s zy1=_DWG0Y-Jb3t^WXmkr(d9~08k-|#Ly zaNOmT(^9tIb&eb4%CzIT zAm3CUtWSr1t4?h1kk#NBi{U|pJslvME{q|_eS^3En>SOqSxyuN1x;Is@8~m?*>}** znrRFArP!K_52RpX*&JHMR<^lVdm8ypJ}0R(SD(51j;6@ni$6bQ+2XL+R^|NnSp5}(kzvMZ^(@4fD_{QVu$(&K6H|C37TG1Am9Re{<<3gd zh@`>;BqkXMW&p0T6rt|iB$)~CvFe(XC)F9WgAZn*0@t$oZo;!*}r@_`h?KKH&6A@3= zISXoQB+~`op>NP-buiA*^0n{@i{_?MRG)&k)c)k_F+-2Lud!S9pc+i`s74NpBCaGF zXN+pHkubw*msGBTY27BKHv)RRh3;nMg4&$fD_6X9Vt~;_4D+5XPH~#Kn-yjcy!$}1 zigv#FNY>TqMhtIBb@UoF!cE~Q8~;!Pek>SQQwHnHuWKoVBosAiOr}q>!>aE*Krc)V zBUMEcJ5NU0g8}-h6i1zpMY9>m4ne?=U2~`w7K7Q0gB_=p@$5K7p6}thw z-~3dMj?YNX2X$lZ+7ngQ$=s}3mizNN@kE%OtB)?c&i~2L55z8^=yz;xMHLmlY>&Q# zJj?!)M#q_SyfkQh)k?j8IfLtB)ZCp|*vf4_B zos?73yd^h-Ac+;?E4*bpf=o*^3x3-`TVjbY4n6!EN10K6o@fxdyps05Vo3PU)otB} z`3kR+2w7_C#8Z!q`J)p{Vh!+m9-UP!$STp+Hb}}#@#_u^SsUQg<}59< zTvH3%XS4G+6FF^(m6bVF&nSUIXcl;nw{=H$%fgeJ>CgDYiLdpDXr{;-AnG z8dvcrHYVMI&`R6;GWekI@Ir3!uo)oz4^{6q0m^}@f2tM9&=YHNi6-?rh0-{+k@cQm zdp`g#YdQn%MDVg2GR>wZ`n2<0l4)9nx1Wfr&!Dvz=bPwU!h2S?ez6MVc5APE4-xLB zi&W9Q8k2@0w!C53g?iAIQ}~p*3O(@zja6KQ=M3zfW*_6o5SwR-)6VBh~m7{^-=MC-owYH5-u40a}a0liho3QZZ5L{bS_xM1)4}19)zTU$$MY zq3eZML1WC{K%YFd`Be0M-rkO^l?h{kM{$2oK1*A@HVJ57*yhDkUF!2WZ&oA4Y-sK( zCY69%#`mBCi6>6uw(x4gbFaP0+FD*JKJ-q!F1E?vLJ+d35!I5d7@^eU?(CS|C^tmI5?lv@s{{*|1F zFg|OzNpZ0hxljdjaW%45O0MOttRrd(Z?h{HYbB-KFUx&9GfFL3b8NwZ$zNu)WbBD` zYkj$^UB5%3Pj1MDr>S2Ejr9pUcgA!;ZG!@{uAy12)vG=*^9-|dNQBc8&`oxBlU~#y zs!anJX&T?57Jdr^sb>e+V`MVfY>Y0ESg7MG<7W0g&bR-ZYzzZ%2H&Etcp zcd6QeXO1D!5A#zM0lx*GH}`M)2~ZFLE;sP^RSB5wVMNfiZXPd(cmO>j=OSA3`o5r& zna(|^jGXbdN7PK)U8b7^zYtYkkeb%<%F~=OqB~kXMQkq}ii|skh@WSRt>5za;cjP0 zZ~nD%6)wzedqE}BMLt~qKwlvTr33))#uP~xyw#*Eaa|DbMQ_%mG0U8numf8)0DX`r zRoG2bM;#g|p-8gWnwRV5SCW0tLjLO&9Z?K>FImeIxlGUgo0Zk`9Qzhj1eco~7XZy+hXc@YF&ZQ=? zn*^1O56yK^x{y}q`j7}blGCx%dydV!c7)g~tJzmHhV=W~jbWRRR{1<^oDK+1clprm zz$eCy7y9+?{E|YgkW~}}iB#I4XoJ*xr8R?i_Hv$=Cof5bo-Nj~f`-DLebH}&0% zfQj9@WGd4;N~Y?mzQsHJTJq6!Qzl^-vwol(+fMt#Pl=Wh#lI5Vmu@QM0=_r+1wHt` z+8WZ~c2}KQQ+q)~2Ki77QvV&`xb|xVcTms99&cD$Zz4+-^R4kvUBxG8gDk7Y`K*)JZ^2rL(+ZWV~%W(@6 z)0bPArG#BROa_PHs~&WplQ_UIrpd)1N1QGPfv!J(Z9jNT#i%H?CE6|pPZb9hJ1JW4 z^q;ft#!HRNV0YgPojzIYT`8LuET2rUe-J|c!9l4`^*;4WtY@Ew@pL>wkjmMgGfN7 ze}}GtmU0@<_#08~I-Suk=^*9GLW=H4xhsml;vAV{%hy5Eegl@!6qKqbG024%n2HHw zCc@ivW_$@5ZoHP70(7D+(`PvgjW1Pd`wsiuv-aCukMrafwDm)B!xXVy*j2opohhoU zcJz%ADmj>i3`-3-$7nQKBQQuGY;2Qt&+(L~C>vSGFj5{Mlv?T_^dql;{zkpe4R1}R z%XfZyQ}wr*sr>jrKgm*PWLjuVc%6&&`Kbf1SuFpHPN&>W)$GmqC;pIoBC`=4-hPY8 zT*>%I2fP}vGW;R=^!1be?ta2UQd2>alOFFbVl;(SQJ4Jk#)4Z0^wpWEVvY4=vyDk@ zqlModi@iVPMC+{?rm=4(n+<;|lmUO@UKYA>EPTS~AndtK^Wy^%#3<;(dQdk3WaUkRtzSMC9}7x2||CNpF#(3T4C)@ z$~RWs`BNABKX|{cmBt>Q=&gkXl&x!!NK_%5hW0LS)Z4PB>%sV?F-{Wyj#s7W%$F{D zXdK^Fp3wvy+48+GP6F_|^PCRx=ddcTO3sG;B23A49~Qaw31SZ0Rc~`r4qqt%#OGW{ zCA_(LG5^N>yzUn&kAgVmxb=EA8s&tBXC}S1CZ(KoW)(%^JjLTPo^fs`Va;`=YlVPgmB$!yB}<(4ym6OeZ3xAJJ#;)2+B%p3P1Wt+d$eo`vz`T zXfUP2))kBDPoscH;Jc7I3NU<({|@wM$&GaDt`n7WLgIY3IA7A6-_R?z8N3mz|}*i z(zl5ot--Oq@f2-nv{X(ujT2T(k1vY_qh93pK@>H-qc%2Xta)IP0Q%zt%bqYgI`o!wv!0QerB`nCN^1n|@$sVOQ!V0teVG!I z_fD%JvfDeT1cK#-{o6Gv7}& zY0#NWin~kVaf$aufV&;63Hbs|`QVZWpDX6IMk1Hj2G}fiH9e-^6u2zf^FIr^BwD<6zjw63+{yUe8PUFvk8v{sJ=R{d#`O!sz`Q13~< zPT$JS(w=yQfU2`zPCNfSw=&zup@DXc(98afjhv@1w_f!m2Z>rMJ19AB&dB%P#Ls3b z=lK7OILM+SQ&VEd=1GN6o&>YVVtIzoZ%=Z_SdqJN2}E43{bE`>w+A;=y->@^k{oCC z$F*WTY&?34;kfyFV?b*Xb1Pq`Z=%OgwEg)Rz)tx=`f%5#w_INP=x&z5!jI;#;N$ma zhO)+MDm;SxOEVL15; zGq(v2pL3&P1Sl)8P*;G-fd{l1QJsv@e@d8)1PK4w2m*M%V3j-V~L^$i|&C@b?D?9tfwE{B^}Z$k8e5FmQ>v7Xz)sG32g9t}YBt zyR$+*_00RmPx+0mW+vVG4mxd(n$(eQf3-w>JPl2UJpafrPaL5@2j}%{VE-) zBI%6Qpj*dsdH<;g!S!avA~bv^0E+ zfyJbSjPb+j;J52U)<|cIcntQBI2T#>2;tOxu{%D?kML476AErF(qN9hPva5Nkc@BF zC-tLF@3ZFb%Kpj)M<{)x*l|*Ia@ECeXo2E4h2f!aV=cHAhi_E_mfUth(sM4^hJq7B zQsGWqdZUm9S%F`$nQ*_#NcuD`&)Ek%_s{&^78{9Hm ztri&rYLOxgFdG>O@+XHy z9#;|&vBCPXH5Mon^I`jSuR$&~ZWtyB67ujzFSj!51>#C}C17~TffQ{c-!QFQkTQ%! zIR^b1`zHx|*1GU?tbBx23weFLz5H?y_Q%N&t$}k?w+``2A=aotj0;2v$~AL z{scF-cL{wsdrmPvf#a9OHyYLcwQD4Kcm)`LLwMh4WT~p29f7M!iafJSU`IV}QY5Wa z(n44-9oA}?J{a+ah*@31WTs#&J#o1`H98#6IQf;Wv0N_!);f&9g7o-k(lW5rWnDUR zQBFIRG+X=6NnsI@mxnwm;tf5;_Uxg?jZ8m-m0}&6+DA!qam(p$mN5R})yA_7m$q@| zFEd|dpS595rxQr-n#GjI5i-AhnUE>Cr;jpCqSrD~EwK_DqI^7%3#p5)%T_od!t3SOmH9MyXeeGO2(UQL;ax|x?Ncixmeo1=$ z{-);Au{*tfzOG?KQ~K|ak8-HQ?`Pekhe2WM(8s{xv-p>Zmu_6{G!-oE$7$mY`MOJorI=+mMx?H;`pr!;fVYz?5~yXBACruWB`Ph zZM}90_<^OBxIhyZ9BW$`>6JvO;%VFpqVr8|7t3~AmxYak6?`Pp#c;**_SYmi`&z23 z`p6_~ePvH)C6x-G9$hgL=eVALq`-AiamN>!3~Lxw&{H(b{B(7xSRm6<3<{%{yXiH# zos5Rv1L+8fUKJLo%P>4I&$}ygetOpenGLView()) + { + OHOS_LOGD("Cocos2dxRenderer_nativeInit() - branch 1"); + GLView *view = GLViewImpl::sharedOpenGLView(); + view->setFrameSize(w, h); + CCDirector::sharedDirector()->setOpenGLView(view); + + AppDelegate *pAppDelegate = new AppDelegate(); + CCApplication::sharedApplication()->run(); + } + else + { + OHOS_LOGD("Cocos2dxRenderer_nativeInit() - branch 2"); + ccGLInvalidateStateCache(); + CCShaderCache::sharedShaderCache()->reloadDefaultShaders(); + ccDrawInit(); + CCTextureCache::reloadAllTextures(); + CCNotificationCenter::sharedNotificationCenter()->postNotification(EVENT_COME_TO_FOREGROUND, NULL); + CCDirector::sharedDirector()->setGLDefaultValues(); + } +} + +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/entry/src/main/cpp/napi_init.cpp b/tests/cpp-tests/proj.ohos/entry/src/main/cpp/napi_init.cpp new file mode 100644 index 000000000000..5b0457f4f87b --- /dev/null +++ b/tests/cpp-tests/proj.ohos/entry/src/main/cpp/napi_init.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 "platform/ohos/CCLogOhos.h" +#include "napi/plugin_manager.h" + +/* + * function for module exports + */ +static napi_value Init(napi_env env, napi_value exports) +{ + napi_property_descriptor desc[] ={ + DECLARE_NAPI_FUNCTION("getContext", NapiManager::GetContext), + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + + bool ret = NapiManager::GetInstance()->Export(env, exports); + if (!ret) { + OHOS_LOGE("Init failed"); + } + return exports; +} + +/* + * Napi Module define + */ +static napi_module nativerenderModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "nativerender", + .nm_priv = ((void*)0), + .reserved = { 0 }, +}; +/* + * Module register function + */ +extern "C" __attribute__((constructor)) void RegisterModule(void) +{ + napi_module_register(&nativerenderModule); +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/entry/src/main/cpp/types/libentry/index.d.ts b/tests/cpp-tests/proj.ohos/entry/src/main/cpp/types/libentry/index.d.ts new file mode 100644 index 000000000000..6a9293652bfe --- /dev/null +++ b/tests/cpp-tests/proj.ohos/entry/src/main/cpp/types/libentry/index.d.ts @@ -0,0 +1,30 @@ +import resmgr from '@ohos.resourceManager'; + +export interface CPPFunctions { + onCreate: () => void; + onShow: () => void; + onHide: () => void; + onBackPress: () => void; + onDestroy: () => void; + onPageShow: () => void; + onPageHide: () => void; + nativeResourceManagerInit: (resourceManager: resmgr.ResourceManager) => void; + writablePathInit: (writePath: string) => void; + workerInit: () => void; + nativeEngineStart: () => void; + registerFunction: () => void; + initAsyncInfo: () => void; + mouseWheelCB: (eventType: string, scrollY : number) => void; + editBoxOnFocusCB: (viewTag: number) => void; + editBoxOnChangeCB: (viewTag: number, text: string) => void; + editBoxOnEnterCB: (viewTag: number, text: string) => void; + textFieldTTFOnChangeCB: (text: string) => void; + shouldStartLoading: (viewTag: number, url: string) => void; + finishLoading: (viewTag: number, url: string) => void; + failLoading: (viewTag: number, url: string) => void; + jsCallback: () => void; + onVideoCallBack: (viewTag: number, event: number) => void; + onAccelerometerCallBack: (x: number, y: number, z: number, interval: number) => void; +} + +export const getContext: (a: number) => CPPFunctions; diff --git a/tests/cpp-tests/proj.ohos/entry/src/main/cpp/types/libentry/oh-package.json5 b/tests/cpp-tests/proj.ohos/entry/src/main/cpp/types/libentry/oh-package.json5 new file mode 100644 index 000000000000..fa7874d5940d --- /dev/null +++ b/tests/cpp-tests/proj.ohos/entry/src/main/cpp/types/libentry/oh-package.json5 @@ -0,0 +1,6 @@ +{ + "name": "libnativerender.so", + "types": "./index.d.ts", + "version": "", + "description": "Please describe the basic information." +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/entry/src/main/ets/MainAbility/MainAbility.ts b/tests/cpp-tests/proj.ohos/entry/src/main/ets/MainAbility/MainAbility.ts new file mode 100644 index 000000000000..a89ce067f484 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/entry/src/main/ets/MainAbility/MainAbility.ts @@ -0,0 +1,80 @@ +import window from '@ohos.window'; +import UIAbility from '@ohos.app.ability.UIAbility'; +import web_webview from '@ohos.web.webview'; +import nativeRender from "libnativerender.so"; +import { ContextType } from "@ohos/libSysCapabilities" +import { GlobalContext,GlobalContextConstants} from "@ohos/libSysCapabilities" + +const nativeAppLifecycle: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.APP_LIFECYCLE); +const rawFileUtils: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.RAW_FILE_UTILS); + +export default class MainAbility extends UIAbility { + onCreate(want, launchParam) { + nativeAppLifecycle.onCreate(); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT, this.context); + // Initializes the webView kernel of the system. This parameter is optional if it is not used. + web_webview.WebviewController.initializeWebEngine(); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_WANT, this.context); + console.info('[LIFECYCLE-App] onCreate') + } + + onDestroy() { + nativeAppLifecycle.onDestroy(); + console.info('[LIFECYCLE-App] onDestroy') + } + + onWindowStageCreate(windowStage) { + // Main window is created, set main page for this ability + windowStage.loadContent('pages/Index', (err, data) => { + if (err.code) { + return; + } + rawFileUtils.nativeResourceManagerInit(this.context.resourceManager); + rawFileUtils.writablePathInit(this.context.filesDir); + }); + + windowStage.getMainWindow().then((windowIns: window.Window) => { + // Set whether to display the status bar and navigation bar. If they are not displayed, [] is displayed. + let systemBarPromise = windowIns.setWindowSystemBarEnable([]); + // Whether the window layout is displayed in full screen mode + let fullScreenPromise = windowIns.setWindowLayoutFullScreen(true); + // Sets whether the screen is always on. + let keepScreenOnPromise = windowIns.setWindowKeepScreenOn(true); + Promise.all([systemBarPromise, fullScreenPromise, keepScreenOnPromise]).then(() => { + console.info('Succeeded in setting the window'); + }).catch((err) => { + console.error('Failed to set the window, cause ' + JSON.stringify(err)); + }); + }) + + windowStage.on("windowStageEvent", (data) => { + let stageEventType: window.WindowStageEventType = data; + switch (stageEventType) { + case window.WindowStageEventType.RESUMED: + nativeAppLifecycle.onShow(); + break; + case window.WindowStageEventType.PAUSED: + nativeAppLifecycle.onHide(); + break; + default: + break; + } + }); + } + + onWindowStageDestroy() { + // Main window is destroyed, release UI related resources + } + + onForeground() { + // Ability has brought to foreground + console.info('[LIFECYCLE-App] onShow') + nativeAppLifecycle.onShow(); + } + + onBackground() { + // Ability has back to background + console.info('[LIFECYCLE-App] onDestroy') + nativeAppLifecycle.onHide(); + } +}; diff --git a/tests/cpp-tests/proj.ohos/entry/src/main/ets/components/CocosEditBox.ets b/tests/cpp-tests/proj.ohos/entry/src/main/ets/components/CocosEditBox.ets new file mode 100644 index 000000000000..2587169f240f --- /dev/null +++ b/tests/cpp-tests/proj.ohos/entry/src/main/ets/components/CocosEditBox.ets @@ -0,0 +1,82 @@ +import { TextInputInfo } from '@ohos/libSysCapabilities/src/main/ets/components/editbox/EditBoxMsg' +import worker from '@ohos.worker'; +import { WorkerManager } from '../workers/WorkerManager'; + +@Component +export struct CocosEditBox { + @ObjectLink textInputInfo: TextInputInfo; + cocosWorker: worker.ThreadWorker = WorkerManager.getInstance().getWorker(); + + build(){ + if(this.textInputInfo.multiline){ + TextArea({text: this.textInputInfo.text, placeholder: this.textInputInfo.placeHolder}) + .id('TextArea'+this.textInputInfo.viewTag) + .position({x: px2vp(this.textInputInfo.x), y: px2vp(this.textInputInfo.y)}) + .size({width: px2vp(this.textInputInfo.w), height: px2vp(this.textInputInfo.h)}) + .padding({top: px2vp(this.textInputInfo.paddingH), right: px2vp(this.textInputInfo.paddingW), + bottom: px2vp(this.textInputInfo.paddingH), left: px2vp(this.textInputInfo.paddingW)}) + .borderRadius(0) + .maxLength(this.textInputInfo.maxLength) + .type(TextAreaType.NORMAL) + .fontFamily($rawfile(this.textInputInfo.fontPath)) + .fontSize(px2fp(this.textInputInfo.fontSize)) + .fontColor(`rgba(${this.textInputInfo.fontColor.r}, ${this.textInputInfo.fontColor.g}, ${this.textInputInfo.fontColor.b}, ${this.textInputInfo.fontColor.a})`) + .placeholderFont({size: px2fp(this.textInputInfo.placeholderFontSize), family: $rawfile(this.textInputInfo.placeHolderFontPath)}) + .placeholderColor(`rgba(${this.textInputInfo.placeholderFontColor.r}, ${this.textInputInfo.placeholderFontColor.g}, ${this.textInputInfo.placeholderFontColor.b}, ${this.textInputInfo.placeholderFontColor.a})`) + .backgroundColor(Color.Transparent) + .visibility(this.textInputInfo.visible ? Visibility.Visible : Visibility.None) + .onVisibleAreaChange([0.0, 1.0], () => { + focusControl.requestFocus('TextArea' + this.textInputInfo.viewTag); + }) + .onFocus(() => { + this.cocosWorker.postMessage({type: "editBoxOnFocus", viewTag: this.textInputInfo.viewTag}); + }) + .onChange((value: string)=>{ + this.textInputInfo.text = value; + this.cocosWorker.postMessage({type: "editBoxOnChange", viewTag: this.textInputInfo.viewTag, value: value}); + }) + .onBlur(()=> { + this.textInputInfo.visible = false + this.cocosWorker.postMessage({type: "editBoxOnEnter", viewTag: this.textInputInfo.viewTag, text: this.textInputInfo.text}); + }) + .onSubmit(()=>{ + this.cocosWorker.postMessage({type: "editBoxOnEnter", viewTag: this.textInputInfo.viewTag, text: this.textInputInfo.text}); + }) + }else{ + TextInput({text: this.textInputInfo.text, placeholder: this.textInputInfo.placeHolder}) + .id('TextInput'+this.textInputInfo.viewTag) + .position({x: px2vp(this.textInputInfo.x), y: px2vp(this.textInputInfo.y)}) + .size({width: px2vp(this.textInputInfo.w), height: px2vp(this.textInputInfo.h)}) + .padding({top: px2vp(this.textInputInfo.paddingH), right: px2vp(this.textInputInfo.paddingW), + bottom: px2vp(this.textInputInfo.paddingH), left: px2vp(this.textInputInfo.paddingW)}) + .borderRadius(0) + .maxLength(this.textInputInfo.maxLength) + .type(this.textInputInfo.inputMode) + .fontFamily($rawfile(this.textInputInfo.fontPath)) + .fontSize(px2fp(this.textInputInfo.fontSize)) + .fontColor(`rgba(${this.textInputInfo.fontColor.r}, ${this.textInputInfo.fontColor.g}, ${this.textInputInfo.fontColor.b}, ${this.textInputInfo.fontColor.a})`) + .placeholderFont({size: px2fp(this.textInputInfo.placeholderFontSize), family: $rawfile(this.textInputInfo.placeHolderFontPath)}) + .placeholderColor(`rgba(${this.textInputInfo.placeholderFontColor.r}, ${this.textInputInfo.placeholderFontColor.g}, ${this.textInputInfo.placeholderFontColor.b}, ${this.textInputInfo.placeholderFontColor.a})`) + .backgroundColor(Color.Transparent) + .visibility(this.textInputInfo.visible ? Visibility.Visible : Visibility.None) + .showPasswordIcon(false) + .onVisibleAreaChange([0.0, 1.0], () => { + focusControl.requestFocus('TextInput' + this.textInputInfo.viewTag); + }) + .onFocus(() => { + this.cocosWorker.postMessage({type: "editBoxOnFocus", viewTag: this.textInputInfo.viewTag}); + }) + .onChange((value: string)=>{ + this.textInputInfo.text = value; + this.cocosWorker.postMessage({type: "editBoxOnChange", viewTag: this.textInputInfo.viewTag, value: value}); + }) + .onBlur(()=> { + this.textInputInfo.visible = false + this.cocosWorker.postMessage({type: "editBoxOnEnter", viewTag: this.textInputInfo.viewTag, text: this.textInputInfo.text}); + }) + .onSubmit(()=>{ + this.cocosWorker.postMessage({type: "editBoxOnEnter", viewTag: this.textInputInfo.viewTag, text: this.textInputInfo.text}); + }) + } + } +} diff --git a/tests/cpp-tests/proj.ohos/entry/src/main/ets/components/CocosVideoPlayer.ets b/tests/cpp-tests/proj.ohos/entry/src/main/ets/components/CocosVideoPlayer.ets new file mode 100644 index 000000000000..6f67afe3b443 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/entry/src/main/ets/components/CocosVideoPlayer.ets @@ -0,0 +1,39 @@ +import { WorkerManager } from '../workers/WorkerManager' +import { VideoPlayerInfo, VideoEvent } from '@ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayerMsg'; +import worker from '@ohos.worker'; + +@Component +export struct CocosVideoPlayer { + @ObjectLink videoPlayerInfo: VideoPlayerInfo; + cocosWorker: worker.ThreadWorker = WorkerManager.getInstance().getWorker(); + + build() { + Column() { + Video({ + src: this.videoPlayerInfo.isUrl ? this.videoPlayerInfo.url : this.videoPlayerInfo.rawfile, + controller: this.videoPlayerInfo.controller + }) + .width(this.videoPlayerInfo.w) + .height(this.videoPlayerInfo.h) + .visibility(this.videoPlayerInfo.visible ? Visibility.Visible : Visibility.None) + .controls(false) + .loop(this.videoPlayerInfo.isLoop) + .objectFit(this.videoPlayerInfo.objectFit) + .onPrepared(()=>{ + if(this.videoPlayerInfo.isPlay) { + this.videoPlayerInfo.controller.start() + } + this.videoPlayerInfo.controller.requestFullscreen(this.videoPlayerInfo.isFullScreen) + }) + .onStart(()=>{ + this.cocosWorker.postMessage({type: "onVideoCallBack", viewTag: this.videoPlayerInfo.viewTag, event: VideoEvent.PLAYING}); + }) + .onPause(()=>{ + this.cocosWorker.postMessage({type: "onVideoCallBack", viewTag: this.videoPlayerInfo.viewTag, event: VideoEvent.PAUSED}); + }) + .onFinish(()=>{ + this.cocosWorker.postMessage({type: "onVideoCallBack", viewTag: this.videoPlayerInfo.viewTag, event: VideoEvent.COMPLETED}); + }) + } + } +} diff --git a/tests/cpp-tests/proj.ohos/entry/src/main/ets/components/CocosWebview.ets b/tests/cpp-tests/proj.ohos/entry/src/main/ets/components/CocosWebview.ets new file mode 100644 index 000000000000..341f2f74501b --- /dev/null +++ b/tests/cpp-tests/proj.ohos/entry/src/main/ets/components/CocosWebview.ets @@ -0,0 +1,43 @@ +import { WebViewInfo } from "@ohos/libSysCapabilities/src/main/ets/components/webview/WebViewMsg" +import worker from '@ohos.worker'; +import { WorkerManager } from '../workers/WorkerManager'; + +@Component +export struct CocosWebView { + viewInfo: WebViewInfo = new WebViewInfo(0, 0, 0, 0, 0); + cocosWorker: worker.ThreadWorker = WorkerManager.getInstance().getWorker(); + + build() { + Web({ src: this.viewInfo.isUrl ? this.viewInfo.url : $rawfile(this.viewInfo.url), controller: this.viewInfo.controller }) + .position({ x: this.viewInfo.x, y: this.viewInfo.y }) + .width(this.viewInfo.w) + .height(this.viewInfo.h) + .border({ width: 1 }) + .domStorageAccess(true) + .databaseAccess(true) + .imageAccess(true) + .onlineImageAccess(true) + .zoomAccess(this.viewInfo.zoomAccess) + .javaScriptAccess(true) + .visibility(this.viewInfo.visible ? Visibility.Visible : Visibility.None) + .opacity(this.viewInfo.opacity) + .backgroundColor(this.viewInfo.backgroundColor) + .onPageBegin((event) => { + this.cocosWorker.postMessage({ type: "onPageBegin", viewTag: this.viewInfo.viewTag, url: event == null ? '' : event.url }); + }) + .onPageEnd((event) => { + this.cocosWorker.postMessage({ type: "onPageEnd", viewTag: this.viewInfo.viewTag, url: event == null ? '' : event.url }) + if(this.viewInfo.jsInterfaceScheme != "" && event != null && event.url.startsWith(this.viewInfo.jsInterfaceScheme)) { + this.cocosWorker.postMessage({ type: "onJsCallBack", viewTag: this.viewInfo.viewTag, url: event == null ? '' : event.url }) + } + // if u want use the javascript on ur page, u can use registerJavaScriptProxy() to register js function here + // and confirm that just register once by a local variable + }) + .onErrorReceive((event) => { + this.cocosWorker.postMessage({ type: "onErrorReceive", viewTag: this.viewInfo.viewTag, url: this.viewInfo.url }) + }) + .onHttpErrorReceive((event) => { + this.cocosWorker.postMessage({ type: "onErrorReceive", viewTag: this.viewInfo.viewTag, url: this.viewInfo.url }) + }) + } +} diff --git a/tests/cpp-tests/proj.ohos/entry/src/main/ets/components/TextInputDialog.ets b/tests/cpp-tests/proj.ohos/entry/src/main/ets/components/TextInputDialog.ets new file mode 100644 index 000000000000..4a689341cc0a --- /dev/null +++ b/tests/cpp-tests/proj.ohos/entry/src/main/ets/components/TextInputDialog.ets @@ -0,0 +1,33 @@ +import { TextInputDialogEntity } from '@ohos/libSysCapabilities' +import worker from '@ohos.worker'; +import { WorkerManager } from '../workers/WorkerManager'; + +@CustomDialog +export struct TextInputDialog { + @State showMessage: TextInputDialogEntity = new TextInputDialogEntity(''); + cocosWorker: worker.ThreadWorker = WorkerManager.getInstance().getWorker(); + controller?: CustomDialogController + + build() { + Column() { + Row() { + TextInput({ text: this.showMessage.message }) + .backgroundColor('#ffffff') + .layoutWeight(1) + .defaultFocus(true) + .caretColor(Color.Transparent) + .onChange((value) => { + this.cocosWorker.postMessage({type: "textFieldTTFOnChange", data: value}) + }) + Blank(8).width(16) + Button($r('app.string.text_field_ttf_complete')).onClick(() => { + this.controller!.close(); + }) + }.padding({ left: 8, right: 8, top: 8, bottom: 8 }) + .backgroundColor(Color.Gray) + } + .width('100%') + + .justifyContent(FlexAlign.End) + } +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/entry/src/main/ets/pages/Index.ets b/tests/cpp-tests/proj.ohos/entry/src/main/ets/pages/Index.ets new file mode 100644 index 000000000000..dbb983025e14 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,158 @@ +import deviceInfo from '@ohos.deviceInfo'; + +import nativeRender from 'libnativerender.so' +import { ContextType } from '@ohos/libSysCapabilities' +import { TextInputInfo } from '@ohos/libSysCapabilities/src/main/ets/components/editbox/EditBoxMsg' +import { TextInputDialogEntity } from '@ohos/libSysCapabilities' +import { WebViewInfo } from '@ohos/libSysCapabilities/src/main/ets/components/webview/WebViewMsg' +import { VideoPlayerInfo } from '@ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayerMsg' +import { WorkerMsgUtils } from '@ohos/libSysCapabilities/src/main/ets/utils/WorkerMsgUtils' +import { WorkerManager } from '../workers/WorkerManager' +import { CocosEditBox } from '../components/CocosEditBox' +import { CocosWebView } from '../components/CocosWebview' +import { CocosVideoPlayer } from '../components/CocosVideoPlayer' +import { TextInputDialog } from '../components/TextInputDialog' +import { GlobalContext,GlobalContextConstants} from "@ohos/libSysCapabilities" +import promptAction from '@ohos.promptAction'; +import process from '@ohos.process'; +const nativePageLifecycle:nativeRender.CPPFunctions = nativeRender.getContext(ContextType.JSPAGE_LIFECYCLE); + +let cocosWorker = WorkerManager.getInstance().getWorker(); + +@Entry +@Component +struct Index { + xcomponentController: XComponentController = new XComponentController(); + + processMgr = new process.ProcessManager(); + + + // EditBox + @State editBoxArray: TextInputInfo[] = []; + private editBoxIndexMap: Map = new Map; + + // WebView + @State webViewArray: WebViewInfo[] = []; + private webViewIndexMap: Map = new Map; + + // videoPlayer + @State videoPlayerInfoArray: VideoPlayerInfo[] = []; + private videoPlayerIndexMap: Map = new Map; + + // textInputDialog + showMessage: TextInputDialogEntity = new TextInputDialogEntity(''); + dialogController: CustomDialogController = new CustomDialogController({ + builder: TextInputDialog({ + showMessage: this.showMessage + }), + autoCancel: true, + alignment: DialogAlignment.Bottom, + customStyle: true, + }) + // PanGesture + private panOption: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Up | PanDirection.Down }); + + onPageShow() { + console.log('[LIFECYCLE-Page] onPageShow'); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_ARRAY, this.editBoxArray); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP, this.editBoxIndexMap); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_COCOS_WORKER, cocosWorker); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY, this.webViewArray); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP, this.webViewIndexMap); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_ARRAY, this.videoPlayerInfoArray); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP, this.videoPlayerIndexMap); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_DIALOG_CONTROLLER, this.dialogController); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_SHOW_MESSAGE, this.showMessage); + nativePageLifecycle.onPageShow(); + } + + onPageHide() { + console.log('[LIFECYCLE-Page] onPageHide'); + nativePageLifecycle.onPageHide(); + } + + onBackPress() { + console.log('[LIFECYCLE-Page] onBackPress'); + try { + promptAction.showDialog({ + title: "提示", + message: "确认退出游戏吗", + buttons: [ + { + text: '取消', + color: '#000000' + }, + { + text: '确认', + color: '#000000' + } + ], + }).then(data => { + console.info('showDialog success, click button: ' + data.index); + if(data.index == 0) { + console.info('showDialog click button cancel'); + return; + } else { + console.info('showDialog click button ok'); + this.processMgr.exit(0); + } + }) + } catch (error) { + console.error(`showDialog args error code is ${error.code}, message is ${error.message}`); + }; + // If disable system exit needed, remove comment "return true" + return true; + } + + onMouseWheel(eventType: string, scrollY: number) { + cocosWorker.postMessage({ type: "onMouseWheel", eventType: eventType, scrollY: scrollY }); + } + + build() { + Stack() { + XComponent({ + id: 'xcomponentId', + type: 'surface', + libraryname: 'nativerender', + controller: this.xcomponentController + }) + .focusable(true) + .focusOnTouch(true) + .gesture( + PanGesture(this.panOption) + .onActionStart(() => { + this.onMouseWheel("actionStart", 0); + }) + .onActionUpdate((event: GestureEvent) => { + if (deviceInfo.deviceType === '2in1') { + this.onMouseWheel("actionUpdate", event.offsetY); + } + }) + .onActionEnd(() => { + this.onMouseWheel("actionEnd", 0); + }) + ) + .onLoad((context) => { + console.log('[cocos] XComponent.onLoad Callback'); + cocosWorker.postMessage({ type: "abilityContextInit", data: GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT)}); + cocosWorker.postMessage({ type: "onXCLoad", data: "XComponent" }); + cocosWorker.onmessage = WorkerMsgUtils.recvWorkerThreadMessage; + }) + .onDestroy(() => { + }) + ForEach(this.editBoxArray, (item:TextInputInfo) => { + CocosEditBox({ textInputInfo: item}); + }, (item:TextInputInfo) => item.viewTag.toString()) + + ForEach(this.webViewArray, (item:WebViewInfo) => { + CocosWebView({ viewInfo: item }) + }, (item:WebViewInfo) => item.uniqueId.toString()) + + ForEach(this.videoPlayerInfoArray, (item:VideoPlayerInfo) => { + CocosVideoPlayer({ videoPlayerInfo: item }) + }, (item:VideoPlayerInfo) => item.viewTag.toString()) + } + .width('100%') + .height('100%') + } +} diff --git a/tests/cpp-tests/proj.ohos/entry/src/main/ets/workers/CocosWorker.ts b/tests/cpp-tests/proj.ohos/entry/src/main/ets/workers/CocosWorker.ts new file mode 100644 index 000000000000..94ddb8d38920 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/entry/src/main/ets/workers/CocosWorker.ts @@ -0,0 +1,79 @@ +import worker, { ThreadWorkerGlobalScope } from '@ohos.worker'; +import nativeRender from "libnativerender.so"; +import { ContextType } from "@ohos/libSysCapabilities" +import { VideoPlayer } from "@ohos/libSysCapabilities" +import { CocosEditBox } from "@ohos/libSysCapabilities" +import { Dialog } from "@ohos/libSysCapabilities" +import { WebView } from "@ohos/libSysCapabilities" +import { JumpManager } from "@ohos/libSysCapabilities" +import { NapiHelper } from "@ohos/libSysCapabilities" +import { ApplicationManager } from "@ohos/libSysCapabilities" +import { GlobalContext,GlobalContextConstants} from "@ohos/libSysCapabilities" + +const appLifecycle: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.APP_LIFECYCLE); +const workerContext: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.WORKER_INIT); +const inputNapi: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.INPUT_NAPI); +const mouseNapi: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.MOUSE_NAPI); +const webViewNapi: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.WEBVIEW_NAPI); +const videoPlayNapi: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.VIDEOPLAYER_NAPI); +const napiContext: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.NATIVE_API); +workerContext.workerInit() + +napiContext.nativeEngineStart(); +NapiHelper.registerFunctions(napiContext.registerFunction) + +const workerPort: ThreadWorkerGlobalScope = worker.workerPort; + +workerPort.onmessage = function(e) : void { + let data = e.data; + switch(data.type) { + case "onXCLoad": + console.log("[cocos] onXCLoad Callback"); + Dialog.init(workerPort); + CocosEditBox.init(workerPort); + JumpManager.init(workerPort); + WebView.init(workerPort); + VideoPlayer.init(workerPort); + ApplicationManager.init(workerPort); + napiContext.initAsyncInfo(); + break; + case "abilityContextInit": + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT, data.data); + break; + case "editBoxOnFocus": + inputNapi.editBoxOnFocusCB(data.viewTag); + break; + case "editBoxOnChange": + inputNapi.editBoxOnChangeCB(data.viewTag, data.value); + break; + case "editBoxOnEnter": + inputNapi.editBoxOnEnterCB(data.viewTag, data.text); + break; + case "textFieldTTFOnChange": + inputNapi.textFieldTTFOnChangeCB(data.data); + break; + case "onMouseWheel": + mouseNapi.mouseWheelCB(data.eventType, data.scrollY); + break; + case "onPageBegin": + webViewNapi.shouldStartLoading(data.viewTag, data.url); + break; + case "onPageEnd": + webViewNapi.finishLoading(data.viewTag, data.url); + break; + case "onJsCallBack": + webViewNapi.jsCallback(); + break; + case "onErrorReceive": + webViewNapi.failLoading(data.viewTag, data.url); + break; + case "onVideoCallBack": + videoPlayNapi.onVideoCallBack(data.viewTag, data.event); + break; + case "exit": + appLifecycle.onBackPress(); + break; + default: + console.error("cocos worker: message type unknown") + } +} diff --git a/tests/cpp-tests/proj.ohos/entry/src/main/ets/workers/WorkerManager.ts b/tests/cpp-tests/proj.ohos/entry/src/main/ets/workers/WorkerManager.ts new file mode 100644 index 000000000000..e2ec70b1873e --- /dev/null +++ b/tests/cpp-tests/proj.ohos/entry/src/main/ets/workers/WorkerManager.ts @@ -0,0 +1,34 @@ +import worker from '@ohos.worker'; +import { Constants } from '@ohos/libSysCapabilities'; + +export class WorkerManager { + private cocosWorker: worker.ThreadWorker; + + private constructor() { + this.cocosWorker = new worker.ThreadWorker("entry/ets/workers/CocosWorker.ts", { + type: "classic", + name: "CocosWorker" + }); + this.cocosWorker.onerror = (e) => { + let msg = e.message; + let filename = e.filename; + let lineno = e.lineno; + let colno = e.colno; + console.error(`on Error ${msg} ${filename} ${lineno} ${colno}`); + } + } + + public static getInstance(): WorkerManager { + let workerManger: WorkerManager | undefined = AppStorage.get(Constants.APP_KEY_WORKER_MANAGER); + if (workerManger == undefined) { + workerManger = new WorkerManager; + AppStorage.setOrCreate(Constants.APP_KEY_WORKER_MANAGER, workerManger); + return workerManger; + } + return workerManger; + } + + public getWorker(): worker.ThreadWorker { + return this.cocosWorker; + } +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/entry/src/main/module.json5 b/tests/cpp-tests/proj.ohos/entry/src/main/module.json5 new file mode 100644 index 000000000000..7b0d63eef720 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/entry/src/main/module.json5 @@ -0,0 +1,70 @@ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:entry_desc", + "mainElement": "MainAbility", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "MainAbility", + "srcEntry": "./ets/MainAbility/MainAbility.ts", + "description": "$string:MainAbility_desc", + "icon": "$media:icon", + "label": "$string:MainAbility_label", + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:white", + "exported": true, + "orientation": "auto_rotation", + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ], + // https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/quick-start/module-configuration-file.md/ + "supportWindowMode": ["fullscreen"], + "maxWindowWidth": 1080, + "minWindowWidth": 1080, + "maxWindowHeight": 720, + "minWindowHeight": 720 + } + ], + "requestPermissions": [ + { + "name": "ohos.permission.INTERNET" + }, { + "name" : "ohos.permission.SET_NETWORK_INFO" + }, { + "name" : "ohos.permission.GET_NETWORK_INFO" + }, { + "name": "ohos.permission.GET_WIFI_INFO" + }, { + "name": "ohos.permission.ACCELEROMETER" + },{ + "name": "ohos.permission.VIBRATE" + } + ], + "metadata": [ + { + "name": "ArkTSPartialUpdate", + "value": "true" + }, + { + "name": "partialUpdateStrictCheck", + "value": "warn" + } + ] + } +} diff --git a/tests/cpp-tests/proj.ohos/entry/src/main/resources/base/element/color.json b/tests/cpp-tests/proj.ohos/entry/src/main/resources/base/element/color.json new file mode 100644 index 000000000000..1bbc9aa9617e --- /dev/null +++ b/tests/cpp-tests/proj.ohos/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "white", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/entry/src/main/resources/base/element/string.json b/tests/cpp-tests/proj.ohos/entry/src/main/resources/base/element/string.json new file mode 100644 index 000000000000..4a9fe9b48009 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/entry/src/main/resources/base/element/string.json @@ -0,0 +1,20 @@ +{ + "string": [ + { + "name": "entry_desc", + "value": "3.7_cpp_tests" + }, + { + "name": "MainAbility_label", + "value": "3.7_cpp_tests" + }, + { + "name": "MainAbility_desc", + "value": "description" + }, + { + "name": "text_field_ttf_complete", + "value": "完成" + } + ] +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/entry/src/main/resources/base/media/icon.png b/tests/cpp-tests/proj.ohos/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c GIT binary patch literal 6790 zcmX|G1ymHk)?T_}Vd;>R?p|tHQo6fg38|$UVM!6BLrPFWk?s;$LOP{GmJpBl$qoSA!PUg~PA65-S00{{S`XKG6NkG0RgjEntPrmV+?0|00mu7;+5 zrdpa{2QLqPJ4Y{j7=Mrl{BaxrkdY69+c~(w{Fv-v&aR%aEI&JYSeRTLWm!zbv;?)_ ziZB;fwGbbeL5Q}YLx`J$lp~A09KK8t_z}PZ=4ZzgdeKtgoc+o5EvN9A1K1_<>M?MBqb#!ASf&# zEX?<)!RH(7>1P+j=jqG(58}TVN-$psA6K}atCuI!KTJD&FMmH-78ZejBm)0qc{ESp z|LuG1{QnBUJRg_E=h1#XMWt2%fcoN@l7eAS!Es?Q+;XsRNPhiiE=@AqlLkJzF`O18 zbsbSmKN=aaq8k3NFYZfDWpKmM!coBU0(XnL8R{4=i|wi{!uWYM2je{U{B*K2PVdu&=E zTq*-XsEsJ$u5H4g6DIm2Y!DN`>^v|AqlwuCD;w45K0@eqauiqWf7l&o)+YLHm~|L~ z7$0v5mkobriU!H<@mVJHLlmQqzQ3d6Rh_-|%Yy2li*tHO>_vcnuZ7OR_xkAIuIU&x z-|8Y0wj|6|a6_I(v91y%k_kNw6pnkNdxjqG8!%Vz_d%c_!X+6-;1`GC9_FpjoHev5fEV7RhJ>r=mh-jp$fqbqRJ=obwdgLDVP5+s zy1=_DWG0Y-Jb3t^WXmkr(d9~08k-|#Ly zaNOmT(^9tIb&eb4%CzIT zAm3CUtWSr1t4?h1kk#NBi{U|pJslvME{q|_eS^3En>SOqSxyuN1x;Is@8~m?*>}** znrRFArP!K_52RpX*&JHMR<^lVdm8ypJ}0R(SD(51j;6@ni$6bQ+2XL+R^|NnSp5}(kzvMZ^(@4fD_{QVu$(&K6H|C37TG1Am9Re{<<3gd zh@`>;BqkXMW&p0T6rt|iB$)~CvFe(XC)F9WgAZn*0@t$oZo;!*}r@_`h?KKH&6A@3= zISXoQB+~`op>NP-buiA*^0n{@i{_?MRG)&k)c)k_F+-2Lud!S9pc+i`s74NpBCaGF zXN+pHkubw*msGBTY27BKHv)RRh3;nMg4&$fD_6X9Vt~;_4D+5XPH~#Kn-yjcy!$}1 zigv#FNY>TqMhtIBb@UoF!cE~Q8~;!Pek>SQQwHnHuWKoVBosAiOr}q>!>aE*Krc)V zBUMEcJ5NU0g8}-h6i1zpMY9>m4ne?=U2~`w7K7Q0gB_=p@$5K7p6}thw z-~3dMj?YNX2X$lZ+7ngQ$=s}3mizNN@kE%OtB)?c&i~2L55z8^=yz;xMHLmlY>&Q# zJj?!)M#q_SyfkQh)k?j8IfLtB)ZCp|*vf4_B zos?73yd^h-Ac+;?E4*bpf=o*^3x3-`TVjbY4n6!EN10K6o@fxdyps05Vo3PU)otB} z`3kR+2w7_C#8Z!q`J)p{Vh!+m9-UP!$STp+Hb}}#@#_u^SsUQg<}59< zTvH3%XS4G+6FF^(m6bVF&nSUIXcl;nw{=H$%fgeJ>CgDYiLdpDXr{;-AnG z8dvcrHYVMI&`R6;GWekI@Ir3!uo)oz4^{6q0m^}@f2tM9&=YHNi6-?rh0-{+k@cQm zdp`g#YdQn%MDVg2GR>wZ`n2<0l4)9nx1Wfr&!Dvz=bPwU!h2S?ez6MVc5APE4-xLB zi&W9Q8k2@0w!C53g?iAIQ}~p*3O(@zja6KQ=M3zfW*_6o5SwR-)6VBh~m7{^-=MC-owYH5-u40a}a0liho3QZZ5L{bS_xM1)4}19)zTU$$MY zq3eZML1WC{K%YFd`Be0M-rkO^l?h{kM{$2oK1*A@HVJ57*yhDkUF!2WZ&oA4Y-sK( zCY69%#`mBCi6>6uw(x4gbFaP0+FD*JKJ-q!F1E?vLJ+d35!I5d7@^eU?(CS|C^tmI5?lv@s{{*|1F zFg|OzNpZ0hxljdjaW%45O0MOttRrd(Z?h{HYbB-KFUx&9GfFL3b8NwZ$zNu)WbBD` zYkj$^UB5%3Pj1MDr>S2Ejr9pUcgA!;ZG!@{uAy12)vG=*^9-|dNQBc8&`oxBlU~#y zs!anJX&T?57Jdr^sb>e+V`MVfY>Y0ESg7MG<7W0g&bR-ZYzzZ%2H&Etcp zcd6QeXO1D!5A#zM0lx*GH}`M)2~ZFLE;sP^RSB5wVMNfiZXPd(cmO>j=OSA3`o5r& zna(|^jGXbdN7PK)U8b7^zYtYkkeb%<%F~=OqB~kXMQkq}ii|skh@WSRt>5za;cjP0 zZ~nD%6)wzedqE}BMLt~qKwlvTr33))#uP~xyw#*Eaa|DbMQ_%mG0U8numf8)0DX`r zRoG2bM;#g|p-8gWnwRV5SCW0tLjLO&9Z?K>FImeIxlGUgo0Zk`9Qzhj1eco~7XZy+hXc@YF&ZQ=? zn*^1O56yK^x{y}q`j7}blGCx%dydV!c7)g~tJzmHhV=W~jbWRRR{1<^oDK+1clprm zz$eCy7y9+?{E|YgkW~}}iB#I4XoJ*xr8R?i_Hv$=Cof5bo-Nj~f`-DLebH}&0% zfQj9@WGd4;N~Y?mzQsHJTJq6!Qzl^-vwol(+fMt#Pl=Wh#lI5Vmu@QM0=_r+1wHt` z+8WZ~c2}KQQ+q)~2Ki77QvV&`xb|xVcTms99&cD$Zz4+-^R4kvUBxG8gDk7Y`K*)JZ^2rL(+ZWV~%W(@6 z)0bPArG#BROa_PHs~&WplQ_UIrpd)1N1QGPfv!J(Z9jNT#i%H?CE6|pPZb9hJ1JW4 z^q;ft#!HRNV0YgPojzIYT`8LuET2rUe-J|c!9l4`^*;4WtY@Ew@pL>wkjmMgGfN7 ze}}GtmU0@<_#08~I-Suk=^*9GLW=H4xhsml;vAV{%hy5Eegl@!6qKqbG024%n2HHw zCc@ivW_$@5ZoHP70(7D+(`PvgjW1Pd`wsiuv-aCukMrafwDm)B!xXVy*j2opohhoU zcJz%ADmj>i3`-3-$7nQKBQQuGY;2Qt&+(L~C>vSGFj5{Mlv?T_^dql;{zkpe4R1}R z%XfZyQ}wr*sr>jrKgm*PWLjuVc%6&&`Kbf1SuFpHPN&>W)$GmqC;pIoBC`=4-hPY8 zT*>%I2fP}vGW;R=^!1be?ta2UQd2>alOFFbVl;(SQJ4Jk#)4Z0^wpWEVvY4=vyDk@ zqlModi@iVPMC+{?rm=4(n+<;|lmUO@UKYA>EPTS~AndtK^Wy^%#3<;(dQdk3WaUkRtzSMC9}7x2||CNpF#(3T4C)@ z$~RWs`BNABKX|{cmBt>Q=&gkXl&x!!NK_%5hW0LS)Z4PB>%sV?F-{Wyj#s7W%$F{D zXdK^Fp3wvy+48+GP6F_|^PCRx=ddcTO3sG;B23A49~Qaw31SZ0Rc~`r4qqt%#OGW{ zCA_(LG5^N>yzUn&kAgVmxb=EA8s&tBXC}S1CZ(KoW)(%^JjLTPo^fs`Va;`=YlVPgmB$!yB}<(4ym6OeZ3xAJJ#;)2+B%p3P1Wt+d$eo`vz`T zXfUP2))kBDPoscH;Jc7I3NU<({|@wM$&GaDt`n7WLgIY3IA7A6-_R?z8N3mz|}*i z(zl5ot--Oq@f2-nv{X(ujT2T(k1vY_qh93pK@>H-qc%2Xta)IP0Q%zt%bqYgI`o!wv!0QerB`nCN^1n|@$sVOQ!V0teVG!I z_fD%JvfDeT1cK#-{o6Gv7}& zY0#NWin~kVaf$aufV&;63Hbs|`QVZWpDX6IMk1Hj2G}fiH9e-^6u2zf^FIr^BwD<6zjw63+{yUe8PUFvk8v{sJ=R{d#`O!sz`Q13~< zPT$JS(w=yQfU2`zPCNfSw=&zup@DXc(98afjhv@1w_f!m2Z>rMJ19AB&dB%P#Ls3b z=lK7OILM+SQ&VEd=1GN6o&>YVVtIzoZ%=Z_SdqJN2}E43{bE`>w+A;=y->@^k{oCC z$F*WTY&?34;kfyFV?b*Xb1Pq`Z=%OgwEg)Rz)tx=`f%5#w_INP=x&z5!jI;#;N$ma zhO)+MDm;SxOEVL15; zGq(v2pL3&P1Sl)8P*;G-fd{l1QJsv@e@d8)1PK4w2m*M%V3j-V~L^$i|&C@b?D?9tfwE{B^}Z$k8e5FmQ>v7Xz)sG32g9t}YBt zyR$+*_00RmPx+0mW+vVG4mxd(n$(eQf3-w>JPl2UJpafrPaL5@2j}%{VE-) zBI%6Qpj*dsdH<;g!S!avA~bv^0E+ zfyJbSjPb+j;J52U)<|cIcntQBI2T#>2;tOxu{%D?kML476AErF(qN9hPva5Nkc@BF zC-tLF@3ZFb%Kpj)M<{)x*l|*Ia@ECeXo2E4h2f!aV=cHAhi_E_mfUth(sM4^hJq7B zQsGWqdZUm9S%F`$nQ*_#NcuD`&)Ek%_s{&^78{9Hm ztri&rYLOxgFdG>O@+XHy z9#;|&vBCPXH5Mon^I`jSuR$&~ZWtyB67ujzFSj!51>#C}C17~TffQ{c-!QFQkTQ%! zIR^b1`zHx|*1GU?tbBx23weFLz5H?y_Q%N&t$}k?w+``2A=aotj0;2v$~AL z{scF-cL{wsdrmPvf#a9OHyYLcwQD4Kcm)`LLwMh4WT~p29f7M!iafJSU`IV}QY5Wa z(n44-9oA}?J{a+ah*@31WTs#&J#o1`H98#6IQf;Wv0N_!);f&9g7o-k(lW5rWnDUR zQBFIRG+X=6NnsI@mxnwm;tf5;_Uxg?jZ8m-m0}&6+DA!qam(p$mN5R})yA_7m$q@| zFEd|dpS595rxQr-n#GjI5i-AhnUE>Cr;jpCqSrD~EwK_DqI^7%3#p5)%T_od!t3SOmH9MyXeeGO2(UQL;ax|x?Ncixmeo1=$ z{-);Au{*tfzOG?KQ~K|ak8-HQ?`Pekhe2WM(8s{xv-p>Zmu_6{G!-oE$7$mY`MOJorI=+mMx?H;`pr!;fVYz?5~yXBACruWB`Ph zZM}90_<^OBxIhyZ9BW$`>6JvO;%VFpqVr8|7t3~AmxYak6?`Pp#c;**_SYmi`&z23 z`p6_~ePvH)C6x-G9$hgL=eVALq`-AiamN>!3~Lxw&{H(b{B(7xSRm6<3<{%{yXiH# zos5Rv1L+8fUKJLo%P>4I&$}y { + if (item.viewTag == eventData.viewTag) { + removeIndex = index; + } + }); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_ARRAY).splice(removeIndex, 1); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP).delete(eventData.viewTag); + break; + } + case "setCurrentText": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.text = eventData.text; + break; + } + case "setEditBoxViewRect": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.x = eventData.viewRect.x; + textInputInfo.y = eventData.viewRect.y; + textInputInfo.w = eventData.viewRect.w; + textInputInfo.h = eventData.viewRect.h; + break; + } + case "setEditBoxVisible": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.visible = eventData.visible; + break; + } + case "setEditBoxFontSize": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.fontSize = eventData.fontSize; + break; + } + case "setEditBoxFontColor": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.fontColor = new FontColor(eventData.color.r, eventData.color.g, eventData.color.b, eventData.color.a / 255); + break; + } + case "setEditBoxPlaceHolderFontSize": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.placeholderFontSize = eventData.placeHolderSize; + break; + } + case "setEditBoxPlaceHolderFontColor": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.placeholderFontColor = new FontColor(eventData.placeHolderColor.r, eventData.placeHolderColor.g, eventData.placeHolderColor.b, eventData.placeHolderColor.a / 255); + break; + } + case "setEditBoxPlaceHolder": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.placeHolder = eventData.placeHolderText; + break; + } + case "setEditBoxMaxLength": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.maxLength = eventData.maxLength; + break; + } + case "setNativeInputMode": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.multiline = (eventData.inputMode == 0) ? true : false; + if (textInputInfo.inputMode != InputType.Password) { + switch (eventData.inputMode) { + case 0: + case 4: + case 6: + textInputInfo.inputMode = InputType.Normal; + break; + case 2: + case 5: + textInputInfo.inputMode = InputType.Number; + break; + case 3: + textInputInfo.inputMode = InputType.PhoneNumber; + break; + case 1: + textInputInfo.inputMode = InputType.Email; + break; + default: + break; + } + } + break; + } + case "setNativeInputFlag": { + // Any type can be changed to a password. The password can be changed to any type, + // but the password is mapped to the general type. Other changes are not allowed. + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + if (eventData.inputFlag == 0) { + textInputInfo.inputMode = InputType.Password; + } else if (textInputInfo.inputMode == InputType.Password) { + textInputInfo.inputMode = InputType.Normal; + } + break; + } + case "setEditBoxFontPath": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.fontPath = eventData.fontPath; + break; + } + case "setEditBoxPlaceHolderFontPath": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.placeHolderFontPath = eventData.placeHolderFontPath; + break; + } + case "hideAllEditBox": { + let editBoxArray: TextInputInfo[] = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_ARRAY); + editBoxArray.forEach(item => { + if (item.visible) { + item.visible = false; + let cocosWorker: worker.ThreadWorker = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_COCOS_WORKER); + cocosWorker.postMessage({ type: "editBoxOnEnter", viewTag: item.viewTag, text: item.text }) + } + }) + break; + } + } +} diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayer.ts b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayer.ts new file mode 100644 index 000000000000..50b7a7e69d00 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayer.ts @@ -0,0 +1,79 @@ +import { VideoPlayMsgEntity, ViewRect } from '../../entity/WorkerMsgEntity'; + +export class VideoPlayer { + static MODULE_NAME: string = 'VideoPlay'; + + private static workerPort; + + static init(workerPort) : void { + VideoPlayer.workerPort = workerPort; + } + + static setURL(viewTag: number, url: string, isUrl: number) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'setURL', viewTag); + videoPlayMsgEntity.url = url; + videoPlayMsgEntity.isUrl = isUrl; + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static setLooping(viewTag: number, isLoop: boolean) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'setLooping', viewTag); + videoPlayMsgEntity.isLoop = isLoop; + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static createVideoPlayer(viewTag: number) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'createVideoPlayer', viewTag); + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static removeVideoPlayer(viewTag: number) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'removeVideoPlayer', viewTag); + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static setVideoPlayerRect(viewTag: number, x: number, y: number, w: number, h: number) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'setVideoPlayerRect', viewTag); + let viewRect: ViewRect = new ViewRect(x, y, w, h); + videoPlayMsgEntity.viewRect = viewRect; + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static play(viewTag: number) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'play', viewTag); + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + static pause(viewTag: number) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'pause', viewTag); + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static stop(viewTag: number) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'stop', viewTag); + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static setVisible(viewTag: number, visible: boolean) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'setVisible', viewTag); + videoPlayMsgEntity.visible = visible + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static requestFullscreen(viewTag: number, isFullScreen: boolean) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'requestFullscreen', viewTag); + videoPlayMsgEntity.isFullScreen = isFullScreen; + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static seekTo(viewTag: number, seekTo: number) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'seekTo', viewTag); + videoPlayMsgEntity.seekTo = seekTo; + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static setKeepAspectRatioEnabled(viewTag: number, enable: boolean) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'setKeepAspectRatioEnabled', viewTag); + videoPlayMsgEntity.keepAspectRatioEnabled = enable; + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } +} diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayerMsg.ets b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayerMsg.ets new file mode 100644 index 000000000000..f11bdac82c9a --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayerMsg.ets @@ -0,0 +1,145 @@ +import Logger from '../../utils/Logger' +import { BusinessError } from '@ohos.base'; +import { GlobalContext,GlobalContextConstants } from "../../common/GlobalContext" +import { VideoPlayMsgEntity } from '../../entity/WorkerMsgEntity'; + +let log: Logger = new Logger(0x0001, "VideoPlayerMsg"); + +@Observed +export class VideoPlayerInfo { + // position + public x: number = 0; + public y: number = 0; + // size + public w: number = 0; + public h: number = 0; + // url + public url: string = '' + + public rawfile?: Resource; + public isUrl: boolean = false + // tag + public viewTag: number = 0 + + public isPlay: boolean = false + public isFullScreen: boolean = false + + // Whether to display + public visible: boolean = true + + public isLoop: boolean = false + + public objectFit: ImageFit = ImageFit.Auto + + public controller: VideoController = new VideoController() + + constructor(x: number, y: number, w: number, h: number, viewTag: number) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + this.viewTag = viewTag; + } +} + +export enum VideoEvent { + PLAYING = 0, + PAUSED, + STOPPED, + COMPLETED, +} + +export function handleVideoPlayMsg(eventData: VideoPlayMsgEntity) { + + switch (eventData.function) { + case "createVideoPlayer": { + let newVideoPlayerInfo = new VideoPlayerInfo(0, 0, 0, 0, eventData.viewTag); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_ARRAY).push(newVideoPlayerInfo); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).set(eventData.viewTag, newVideoPlayerInfo); + break; + } + case "removeVideoPlayer": { + let removeIndex = -1; + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_ARRAY).forEach((item:VideoPlayerInfo,index:number) => { + if (item.viewTag == eventData.viewTag) { + removeIndex = index; + } + }); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag).controller.requestFullscreen(false); //4.x已修复 + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_ARRAY).splice(removeIndex, 1); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).delete(eventData.viewTag); + break; + } + case "play": { + let videoPlayInfo :VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + videoPlayInfo.controller.start(); + videoPlayInfo.isPlay = true; + break; + } + case "pause": { + let videoPlayInfo:VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + videoPlayInfo.controller.pause(); + videoPlayInfo.isPlay = false; + break; + } + case "stop": { + let videoPlayInfo:VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + videoPlayInfo.controller.stop(); + videoPlayInfo.isPlay = false; + break; + } + case "setURL": { + let videoPlayInfo:VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + if(eventData.isUrl == 0) { + videoPlayInfo.isUrl = false; + videoPlayInfo.rawfile = $rawfile(eventData.url); + } else { + videoPlayInfo.isUrl = true; + videoPlayInfo.url = eventData.url; + } + break; + } + case "setLooping": { + let videoPlayInfo: VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + videoPlayInfo.isLoop = eventData.isLoop; + break; + } + case "setVideoPlayerRect": { + let videoPlayInfo:VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + try { + videoPlayInfo.x = px2vp(eventData.viewRect.x); + videoPlayInfo.y = px2vp(eventData.viewRect.y); + videoPlayInfo.w = px2vp(eventData.viewRect.w); + videoPlayInfo.h = px2vp(eventData.viewRect.h); + } catch (error) { + let e: BusinessError = error as BusinessError; + log.error('videoPlayerInfo ErrorCode: %{public}d, Message: %{public}s', e.code, e.message); + } + break; + } + case "setVisible": { + let videoPlayInfo:VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + if (videoPlayInfo.visible == eventData.visible) { + return; + } + videoPlayInfo.visible = eventData.visible; + break; + } + case "requestFullscreen": { + let videoPlayInfo: VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + videoPlayInfo.controller.requestFullscreen(eventData.isFullScreen); + videoPlayInfo.isFullScreen = eventData.isFullScreen; + break; + } + case "seekTo": { + let videoPlayInfo: VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + videoPlayInfo.controller.setCurrentTime(eventData.seekTo, SeekMode.Accurate); + break; + } + case "setKeepAspectRatioEnabled": { + let videoPlayInfo: VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + videoPlayInfo.objectFit = eventData.keepAspectRatioEnabled? ImageFit.Cover : ImageFit.Auto; + break; + } + } +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/webview/WebView.ts b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/webview/WebView.ts new file mode 100644 index 000000000000..872ec4ce70fe --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/webview/WebView.ts @@ -0,0 +1,114 @@ +import { ViewRect, WebViewMsgEntity } from '../../entity/WorkerMsgEntity'; + +export class WebView { + static MODULE_NAME: string = 'WebView'; + + private static workerPort; + + static init(workerPort) : void { + WebView.workerPort = workerPort; + } + + static createWebView(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'createWebView', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static removeWebView(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'removeWebView', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static setJavascriptInterfaceScheme(viewTag: number, jsInterfaceScheme: string) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'setJavascriptInterfaceScheme', viewTag); + webViewMsgEntity.jsInterfaceScheme = jsInterfaceScheme; + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static loadData(viewTag: number, data: string, mimeType: string, encoding: string, baseURL: string) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'loadData', viewTag); + webViewMsgEntity.data = data; + webViewMsgEntity.mimeType = mimeType; + webViewMsgEntity.encoding = encoding; + webViewMsgEntity.baseURL = baseURL; + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static loadURL(viewTag: number, url: string) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'loadURL', viewTag); + webViewMsgEntity.url = url; + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static loadFile(viewTag: number, filePath: string) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'loadFile', viewTag); + webViewMsgEntity.filePath = filePath; + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static stopLoading(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'stopLoading', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static reload(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'reload', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static canGoBack(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'canGoBack', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static canGoForward(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'canGoForward', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static goBack(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'goBack', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static goForward(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'goForward', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static setWebViewRect(viewTag: number, x: number, y: number, w: number, h: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'setWebViewRect', viewTag); + let viewRect: ViewRect = new ViewRect(x, y, w, h); + webViewMsgEntity.viewRect = viewRect; + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static setVisible(viewTag: number, visible: boolean) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'setVisible', viewTag); + webViewMsgEntity.visible = visible; + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static setOpacityWebView(viewTag: number, opacity: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'setOpacityWebView', viewTag); + webViewMsgEntity.opacity = opacity; + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static setBackgroundTransparent(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'setBackgroundTransparent', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static evaluateJS(viewTag: number, js: string) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'evaluateJS', viewTag); + webViewMsgEntity.js = js; + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static setScalesPageToFit(viewTag: number, scalesPageToFit: boolean) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'setScalesPageToFit', viewTag); + webViewMsgEntity.scalesPageToFit = scalesPageToFit; + WebView.workerPort.postMessage(webViewMsgEntity); + } +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/webview/WebViewMsg.ets b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/webview/WebViewMsg.ets new file mode 100644 index 000000000000..7dfab02a8d03 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/components/webview/WebViewMsg.ets @@ -0,0 +1,245 @@ +import web_webview from '@ohos.web.webview' +import Logger from '../../utils/Logger'; +import { GlobalContext, GlobalContextConstants } from '../../common/GlobalContext'; +import { WebViewMsgEntity } from '../../entity/WorkerMsgEntity'; +import { BusinessError } from '@ohos.base'; + +let log: Logger = new Logger(0x0001, "WebViewMsg"); + +export class WebViewInfo { + public uniqueId : number = 0; + // position + public x: number = 0; + public y: number = 0; + // size + public w: number = 0; + public h: number = 0; + // url + public url: string = ''; + public isUrl: boolean = true; + + public viewTag: number = 0 + public zoomAccess: boolean = true + public visible: boolean = true + public opacity: number = 1 + public backgroundColor: number = 0xffffff; + public jsInterfaceScheme: string = ""; + public controller: web_webview.WebviewController = new web_webview.WebviewController() + + constructor(x: number, y: number, w: number, h: number, viewTag: number) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + this.viewTag = viewTag + this.uniqueId = viewTag; + } +} + +function copyWebViewInfo(viewInfo: WebViewInfo) : WebViewInfo { + let newViewInfo: WebViewInfo = new WebViewInfo(viewInfo.x, viewInfo.y, viewInfo.w, viewInfo.h, viewInfo.viewTag); + newViewInfo.url = viewInfo.url; + newViewInfo.isUrl = viewInfo.isUrl; + newViewInfo.zoomAccess = viewInfo.zoomAccess; + newViewInfo.visible = viewInfo.visible; + newViewInfo.controller = viewInfo.controller; + newViewInfo.opacity = viewInfo.opacity; + newViewInfo.backgroundColor = viewInfo.backgroundColor; + newViewInfo.jsInterfaceScheme = viewInfo.jsInterfaceScheme; + newViewInfo.uniqueId = 100000 - viewInfo.uniqueId; // support 50000 webView exist at the same time + return newViewInfo; +} + +function waitUtilControllerAttached() : Promise { + return new Promise((resolve, reject) => { + resolve(1); + }) +} + +export function handleWebViewMsg(eventData: WebViewMsgEntity) { + let index: number; + switch (eventData.function) { + case "createWebView": { + let view = new WebViewInfo(0, 0, 0, 0, eventData.viewTag); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY).push(view); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP) + .set(eventData.viewTag, GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY) + .length - 1); + break; + } + case "removeWebView": { + if (GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY).length > 0) { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY).splice(index, 1); + // Do not assume the invoking time of removeWebView. After an element is deleted, the following elements need to be advanced by one bit. + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP) + .forEach((value: number, key: number, map: Map) => { + if (value > index) { + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).set(key, value - 1); + } + }) + + // Delete the subscript mapping of the removed webView. + let tempInfoMap: Map = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP); + tempInfoMap.delete(eventData.viewTag); + } + break; + } + case "setJavascriptInterfaceScheme": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + let webViewInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + webViewInfo.jsInterfaceScheme = eventData.jsInterfaceScheme; + break; + } + case "loadData": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + let webViewInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + waitUtilControllerAttached().then(() => { + webViewInfo.controller.loadData(eventData.data, eventData.mimeType, eventData.encoding, eventData.baseURL) + }); + break; + } + case "loadURL": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index].url = eventData.url; + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index].isUrl = true; + let webViewInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + waitUtilControllerAttached().then(() => { + webViewInfo.controller.loadUrl(eventData.url); + }) + break; + } + case "loadFile": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index].url = eventData.filePath; + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index].isUrl = false; + let webViewInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + waitUtilControllerAttached().then(() => { + webViewInfo.controller.loadUrl($rawfile(eventData.filePath)); + }) + break; + } + case "stopLoading": + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index].controller.stop(); + break; + case "reload": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + let webViewInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + waitUtilControllerAttached().then(() => { + webViewInfo.controller.refresh(); + }) + break; + } + case "canGoBack": + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + let result: number = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index] + .controller + .accessBackward(); + // todo The value needs to be sent back to the worker thread. + break; + case "canGoForward": + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + result = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index] + .controller + .accessForward(); + // todo The value needs to be sent back to the worker thread. + break; + case "goBack": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + let webViewInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + waitUtilControllerAttached().then(() => { + webViewInfo.controller.backward(); + }) + break; + } + case "goForward": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + let webViewInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + waitUtilControllerAttached().then(() => { + webViewInfo.controller.forward(); + }) + break; + } + case "setWebViewRect": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + let tmpWebInfo :WebViewInfo= GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + tmpWebInfo.x = px2vp(eventData.viewRect.x); + tmpWebInfo.y = px2vp(eventData.viewRect.y); + tmpWebInfo.w = px2vp(eventData.viewRect.w); + tmpWebInfo.h = px2vp(eventData.viewRect.h); + let newViewInfo:WebViewInfo = copyWebViewInfo(tmpWebInfo); + let tempInfoArray :WebViewInfo[]= GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY); + tempInfoArray[index] = newViewInfo; + break; + } + case "setVisible": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + if (GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index] + .visible == eventData.visible) { + return; + } + let tmpWebInfo:WebViewInfo= GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + tmpWebInfo.visible = eventData.visible; + let newViewInfo:WebViewInfo = copyWebViewInfo(tmpWebInfo); + let tempInfoArray :WebViewInfo[]= GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY); + tempInfoArray[index] = newViewInfo; + break; + } + case "setOpacityWebView": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + if (GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index] + .opacity == eventData.opacity) { + return; + } + let tmpWebInfo :WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + tmpWebInfo.opacity = eventData.opacity; + let newViewInfo :WebViewInfo= copyWebViewInfo(tmpWebInfo); + let tempInfoArray :WebViewInfo[]= GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY); + tempInfoArray[index] = newViewInfo; + break; + } + case "setBackgroundTransparent": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + if (GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index] + .backgroundColor == Color.Transparent) { + return; + } + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index] + .backgroundColor = Color.Transparent; + let newViewInfo :WebViewInfo= copyWebViewInfo(GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]); + let tempInfoArray :WebViewInfo[]= GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY); + tempInfoArray[index] = newViewInfo; + break; + } + case "evaluateJS": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index] + .controller + .runJavaScript(eventData.js, (error:BusinessError, result:string) => { + if (error) { + log.warn("webView run JavaScript error:%{public}s", JSON.stringify(error)); + return; + } + if (result) { + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_WEB_RESULT,result) + log.debug("webView run JavaScript result is %{public}s:", result); + } + }) + break; + } + case "setScalesPageToFit": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + if (GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index] + .zoomAccess == eventData.scalesPageToFit) { + return; + } + let tmpWebInfo:WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + tmpWebInfo.zoomAccess == eventData.scalesPageToFit + let newViewInfo:WebViewInfo = copyWebViewInfo(tmpWebInfo); + let tempInfoArray:WebViewInfo[] = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY); + tempInfoArray[index] = newViewInfo; + } + break; + } +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/entity/Result.ts b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/entity/Result.ts new file mode 100644 index 000000000000..dd9e38897da3 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/entity/Result.ts @@ -0,0 +1,16 @@ +export class Result { + public static success(data){ + return { + "errCode": 0, + "errMsg": "", + "data": data, + }; + } + + public static error(errCode, errMsg) { + return { + "errCode": errCode, + "errMsg": errMsg, + }; + } +}; \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/entity/TextInputDialogEntity.ts b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/entity/TextInputDialogEntity.ts new file mode 100644 index 000000000000..39d272a0b68c --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/entity/TextInputDialogEntity.ts @@ -0,0 +1,7 @@ +export class TextInputDialogEntity { + message : string; + + constructor(msg: string) { + this.message = msg; + } +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/entity/WorkerMsgEntity.ts b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/entity/WorkerMsgEntity.ts new file mode 100644 index 000000000000..608158d25be9 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/entity/WorkerMsgEntity.ts @@ -0,0 +1,141 @@ +export class ViewRect { + x: number + y: number + w: number + h: number + + constructor(x: number, y: number, w: number, h: number) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + } +} + +export class Color4B { + r: number + g: number + b: number + a: number + + constructor(r: number, g: number, b: number, a: number) { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } +} + +export class BaseWorkerMsgEntity { + module: string; + + function: string; + + constructor(module: string, func: string) { + this.module = module; + this.function = func; + } +} + +export class DialogMsgEntity extends BaseWorkerMsgEntity { + title: string; + + message: string; + + constructor(module: string, func: string) { + super(module, func); + } +} + +export class EditBoxMsgEntity extends BaseWorkerMsgEntity { + viewTag: number + + viewRect: ViewRect + + paddingW: number + paddingH: number + + visible: boolean + + text: string + fontSize: number + color: Color4B + fontPath: string + + placeHolderText: string + placeHolderSize: number + placeHolderColor: Color4B + placeHolderFontPath: string + + maxLength: number + + inputMode: number + + inputFlag: number + + constructor(module: string, func: string, viewTag?: number) { + super(module, func); + this.viewTag = viewTag; + } +} + +export class VideoPlayMsgEntity extends BaseWorkerMsgEntity { + viewTag: number + + url: string + isUrl: number + + isLoop: boolean + + viewRect: ViewRect + + visible: boolean + + isFullScreen: boolean + + seekTo: number + + keepAspectRatioEnabled: boolean + + constructor(module: string, func: string, viewTag: number) { + super(module, func); + this.viewTag = viewTag; + } +} + +export class WebViewMsgEntity extends BaseWorkerMsgEntity { + viewTag: number + + data: string + mimeType: string + encoding: string + baseURL: string + + url: string + + filePath: string + + viewRect: ViewRect + + visible: boolean + + opacity: number + + js: string + + scalesPageToFit: boolean + jsInterfaceScheme: string + + constructor(module: string, func: string, viewTag: number) { + super(module, func); + this.viewTag = viewTag; + } +} + +export class JumpMsgEntity extends BaseWorkerMsgEntity { + url: string; + + constructor(module: string, func: string) { + super(module, func); + } +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/napi/NapiHelper.ts b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/napi/NapiHelper.ts new file mode 100644 index 000000000000..d4df1a5f9806 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/napi/NapiHelper.ts @@ -0,0 +1,118 @@ +import { Dialog } from '../components/dialog/DialogWorker' +import StringUtils from '../utils/StringUtils' +import { JumpManager } from '../system/appJump/JumpManager' +import { DeviceUtils } from '../system/device/DeviceUtils' +import { ApplicationManager } from '../system/application/ApplicationManager' +import { CocosEditBox } from '../components/editbox/CocosEditBox' +import { WebView } from '../components/webview/WebView' +import { VideoPlayer } from '../components/videoplayer/VideoPlayer' +import Accelerometer from '../system/sensor/AccelerometerUtils' +import Preferences from '../preferences/Preferences' + +export class NapiHelper { + + static registerFunctions(registerFunc : Function) { + NapiHelper.registerOthers(registerFunc); + NapiHelper.registerDeviceUtils(registerFunc); + NapiHelper.registerEditBox(registerFunc); + NapiHelper.registerWebView(registerFunc); + NapiHelper.registerVideoPlay(registerFunc); + NapiHelper.registerSensor(registerFunc); + NapiHelper.registerPreferences(registerFunc); + } + + private static registerOthers(registerFunc : Function) { + registerFunc('DiaLog.showDialog', Dialog.showDialog); + registerFunc('DiaLog.showTextInputDialog', Dialog.showTextInputDialog); + registerFunc('DiaLog.hideTextInputDialog', Dialog.hideTextInputDialog); + registerFunc('StringUtils.getWidth', StringUtils.getWidth); + registerFunc('ApplicationManager.exit', ApplicationManager.exit); + registerFunc('ApplicationManager.getVersionName', ApplicationManager.getVersionName); + registerFunc('JumpManager.openUrl', JumpManager.openUrl); + } + + private static registerDeviceUtils(registerFunc : Function) { + registerFunc('DeviceUtils.getDpi', DeviceUtils.getDpi); + registerFunc('DeviceUtils.getSystemLanguage', DeviceUtils.getSystemLanguage); + registerFunc('DeviceUtils.startVibration', DeviceUtils.startVibration); + registerFunc('DeviceUtils.setKeepScreenOn', DeviceUtils.setKeepScreenOn); + registerFunc('DeviceUtils.isRoundScreen', DeviceUtils.isRoundScreen); + registerFunc('DeviceUtils.hasSoftKeys', DeviceUtils.hasSoftKeys); + registerFunc('DeviceUtils.isCutoutEnable', DeviceUtils.isCutoutEnable); + registerFunc('DeviceUtils.initScreenInfo', DeviceUtils.initScreenInfo); + registerFunc('DeviceUtils.getOrientation', DeviceUtils.getOrientation); + registerFunc('DeviceUtils.getCutoutHeight', DeviceUtils.getCutoutHeight); + registerFunc('DeviceUtils.getCutoutWidth', DeviceUtils.getCutoutWidth); + } + + private static registerEditBox(registerFunc : Function) { + registerFunc('CocosEditBox.createCocosEditBox', CocosEditBox.createCocosEditBox); + registerFunc('CocosEditBox.removeCocosEditBox', CocosEditBox.removeCocosEditBox); + registerFunc('CocosEditBox.setCurrentText', CocosEditBox.setCurrentText); + registerFunc('CocosEditBox.setEditBoxViewRect', CocosEditBox.setEditBoxViewRect); + registerFunc('CocosEditBox.setEditBoxVisible', CocosEditBox.setEditBoxVisible); + registerFunc('CocosEditBox.setEditBoxPlaceHolder', CocosEditBox.setEditBoxPlaceHolder); + registerFunc('CocosEditBox.setEditBoxFontSize', CocosEditBox.setEditBoxFontSize); + registerFunc('CocosEditBox.setEditBoxFontColor', CocosEditBox.setEditBoxFontColor); + registerFunc('CocosEditBox.setEditBoxFontPath', CocosEditBox.setEditBoxFontPath); + registerFunc('CocosEditBox.setEditBoxPlaceHolderFontSize', CocosEditBox.setEditBoxPlaceHolderFontSize); + registerFunc('CocosEditBox.setEditBoxPlaceHolderFontColor', CocosEditBox.setEditBoxPlaceHolderFontColor); + registerFunc('CocosEditBox.setEditBoxPlaceHolderFontPath', CocosEditBox.setEditBoxPlaceHolderFontPath); + registerFunc('CocosEditBox.setEditBoxMaxLength', CocosEditBox.setEditBoxMaxLength); + registerFunc('CocosEditBox.setNativeInputMode', CocosEditBox.setNativeInputMode); + registerFunc('CocosEditBox.setNativeInputFlag', CocosEditBox.setNativeInputFlag); + registerFunc('CocosEditBox.hideAllEditBox', CocosEditBox.hideAllEditBox); + } + + private static registerWebView(registerFunc : Function) { + registerFunc('WebView.createWebView', WebView.createWebView); + registerFunc('WebView.removeWebView', WebView.removeWebView); + registerFunc('WebView.setJavascriptInterfaceScheme', WebView.setJavascriptInterfaceScheme); + registerFunc('WebView.loadData', WebView.loadData); + registerFunc('WebView.loadURL', WebView.loadURL); + registerFunc('WebView.loadFile', WebView.loadFile); + registerFunc('WebView.stopLoading', WebView.stopLoading); + registerFunc('WebView.reload', WebView.reload); + registerFunc('WebView.canGoBack', WebView.canGoBack); + registerFunc('WebView.canGoForward', WebView.canGoForward); + registerFunc('WebView.goBack', WebView.goBack); + registerFunc('WebView.goForward', WebView.goForward); + registerFunc('WebView.setWebViewRect', WebView.setWebViewRect); + registerFunc('WebView.setVisible', WebView.setVisible); + registerFunc('WebView.setOpacityWebView', WebView.setOpacityWebView); + registerFunc('WebView.setBackgroundTransparent', WebView.setBackgroundTransparent); + registerFunc('WebView.evaluateJS', WebView.evaluateJS); + registerFunc('WebView.setScalesPageToFit', WebView.setScalesPageToFit); + } + + private static registerVideoPlay(registerFunc : Function) { + registerFunc('VideoPlayer.createVideoPlayer', VideoPlayer.createVideoPlayer); + registerFunc('VideoPlayer.removeVideoPlayer', VideoPlayer.removeVideoPlayer); + registerFunc('VideoPlayer.setURL', VideoPlayer.setURL); + registerFunc('VideoPlayer.setLooping', VideoPlayer.setLooping); + registerFunc('VideoPlayer.setVideoPlayerRect', VideoPlayer.setVideoPlayerRect); + registerFunc('VideoPlayer.play', VideoPlayer.play); + registerFunc('VideoPlayer.pause', VideoPlayer.pause); + registerFunc('VideoPlayer.stop', VideoPlayer.stop); + registerFunc('VideoPlayer.setVisible', VideoPlayer.setVisible); + registerFunc('VideoPlayer.requestFullscreen', VideoPlayer.requestFullscreen); + registerFunc('VideoPlayer.seekTo', VideoPlayer.seekTo); + registerFunc('VideoPlayer.setKeepAspectRatioEnabled', VideoPlayer.setKeepAspectRatioEnabled); + } + + private static registerSensor(registerFunc : Function) { + registerFunc('Accelerometer.enable', Accelerometer.enable); + registerFunc('Accelerometer.disable', Accelerometer.disable); + } + + private static registerPreferences(registerFunc : Function) { + registerFunc('Preferences.get', Preferences.get); + registerFunc('Preferences.getAll', Preferences.getAll); + registerFunc('Preferences.put', Preferences.put); + registerFunc('Preferences.has', Preferences.has); + registerFunc('Preferences.delete', Preferences.delete); + registerFunc('Preferences.flush', Preferences.flush); + registerFunc('Preferences.clear', Preferences.clear); + } + +} diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/preferences/Preferences.ts b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/preferences/Preferences.ts new file mode 100644 index 000000000000..e95e89b29d10 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/preferences/Preferences.ts @@ -0,0 +1,218 @@ +import Logger from '../utils/Logger'; +import data_preferences from '@ohos.data.preferences'; +import { BusinessError } from '@ohos.base'; +import common from '@ohos.app.ability.common'; +import { GlobalContext, GlobalContextConstants } from '../common/GlobalContext'; + +let log: Logger = new Logger(0x0001, "Preferences"); +let preferences: data_preferences.Preferences | null = null; +const PREFS_NAME: string = "Cocos2dxPreferences"; + +export default class Preferences { + + // 通过 preferencesName 获取Preferences实例 + static getPreferences(): data_preferences.Preferences { + let context: common.UIAbilityContext = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT); + try { + preferences = data_preferences.getPreferencesSync(context, {name: PREFS_NAME}); + log.info("Succeeded in getting preferences."); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to get preferences. code =" + code + ", message =" + message); + } + return preferences; + } + + /* + 通过 preferencesName 从缓存中移出指定的Preferences实例,若Preferences实例有对应的持久化文件,则同时删除其持久化文件。使用Promise异步回调。 + 调用该接口后,不建议再使用旧的Preferences实例进行数据操作,否则会出现数据一致性问题,应将Preferences实例置为null,系统将会统一回收。 + */ + static deletePreferences(): void { + let context: common.UIAbilityContext = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT); + try { + data_preferences.deletePreferences(context, PREFS_NAME).then(() => { + log.info("Succeeded in deleting preferences."); + }); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to delete preferences. code =" + code + ", message =" + message); + } + } + + /* + 通过 preferencesName 从缓存中移出指定的Preferences实例,使用Promise异步回调。 + 应用首次调用getPreferences接口获取某个Preferences实例后,该实例会被会被缓存起来,后续再次getPreferences时不会再次从持久化文件中读取, + 直接从缓存中获取Preferences实例。调用此接口移出缓存中的实例之后,再次getPreferences将会重新读取持久化文件,生成新的Preferences实例。 + 调用该接口后,不建议再使用旧的Preferences实例进行数据操作,否则会出现数据一致性问题,应将Preferences实例置为null,系统将会统一回收。 + */ + static removePreferencesFromCache(): void { + let context: common.UIAbilityContext = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT); + try { + data_preferences.removePreferencesFromCacheSync(context, PREFS_NAME); + log.info("Succeeded in removing preferences."); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to remove preferences. code =" + code + ", message =" + message); + } + } + + // 从缓存的Preferences实例中获取键对应的值,如果值为null或者非默认值类型,返回默认数据defValue + static get(key: string, defValue: data_preferences.ValueType): data_preferences.ValueType { + if (preferences === null) { + Preferences.getPreferences(); + } + try { + let data = preferences.getSync(key, defValue); + log.info("Succeeded in getting value of 'startup'. Data: " + data); + return data; + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to get value of 'startup'. code =" + code + ", message =" + message); + return defValue; + } + } + + // 将数据写入缓存的Preferences实例中,可通过flush将Preferences实例持久化 + static put(key: string, value: data_preferences.ValueType): void { + if (preferences === null) { + Preferences.getPreferences(); + } + try { + preferences.putSync(key, value); + log.info("Succeeded in put the key."); + Preferences.flush(); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to put. code =" + code + ", message =" + message); + } + } + + // 从缓存的Preferences实例中获取所有键值数据。 + static getAll(): string | undefined { + if (preferences === null) { + Preferences.getPreferences(); + } + try { + let object = preferences.getAllSync(); + let allKeys = getObjKeys(object); + log.info('getAll keys = ' + allKeys); + log.info("getAll object = " + JSON.stringify(object)); + return JSON.stringify(object); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to getAll. code =" + code + ", message =" + message); + return undefined; + } + } + + // 检查缓存的Preferences实例中是否包含名为给定Key的存储键值对 + static has(key: string): boolean { + if (preferences === null) { + Preferences.getPreferences(); + } + try { + let val = preferences.hasSync(key); + if (val) { + log.info("The key 'startup' is contained."); + return true; + } else { + log.info("The key 'startup' dose not contain."); + return false; + } + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to has. code =" + code + ", message =" + message); + return false; + } + } + + // 从缓存的Preferences实例中删除名为给定Key的存储键值对,可通过flush将Preferences实例持久化 + static delete(key: string): void { + if (preferences === null) { + Preferences.getPreferences(); + } + try { + preferences.deleteSync(key); + log.info("Succeeded in deleting the key."); + Preferences.flush(); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to delete. code =" + code + ", message =" + message); + } + } + + // 将缓存的Preferences实例中的数据异步存储到用户首选项的持久化文件中,使用Promise异步回调。 + static flush(): void { + if (preferences === null) { + Preferences.getPreferences(); + } + preferences.flush().then(()=>{ + log.info("Succeeded in flushing."); + }); + } + + // 清除缓存的Preferences实例中的所有数据,可通过flush将Preferences实例持久化,使用Promise异步回调。 + static clear(): void { + if (preferences === null) { + Preferences.getPreferences(); + } + try { + preferences.clearSync(); + log.info("Succeeded in clearing."); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to clear. code =" + code + ", message =" + message); + } + } + + + // 订阅数据变更,订阅的Key的值发生变更后,在执行flush方法后,触发callback回调。 + static onChange(cb: Function): void { + if (preferences === null) { + Preferences.getPreferences(); + } + try { + preferences.on('change', (key: string) => { + log.info("The key " + key + " changed."); + cb(key); + }); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to flush. code =" + code + ", message =" + message); + } + } + + // 取消订阅数据变更。 + static offChange(cb: Function): void { + if (preferences === null) { + Preferences.getPreferences(); + } + try { + preferences.off('change', (key: string) => { + log.info("The key " + key + " changed."); + cb(key); + }); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to flush. code =" + code + ", message =" + message); + } + } +} + +// 由于ArkTS中无Object.keys,且无法使用for..in... +// 若报ArkTS问题,请将此方法单独抽离至一个ts文件中并暴露,在需要用到的ets文件中引入使用 +function getObjKeys(obj: Object): string[] { + let keys = Object.keys(obj); + return keys; +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/appJump/JumpManager.ts b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/appJump/JumpManager.ts new file mode 100644 index 000000000000..dfa95dfaa5ab --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/appJump/JumpManager.ts @@ -0,0 +1,19 @@ +import type { ThreadWorkerGlobalScope } from '@ohos.worker'; +import { JumpMsgEntity } from '../../entity/WorkerMsgEntity'; + +export class JumpManager { + + static MODULE_NAME: string = 'JumpManager'; + + private static workerPort: ThreadWorkerGlobalScope; + + static init(workerPort: ThreadWorkerGlobalScope) : void { + JumpManager.workerPort = workerPort; + } + + static openUrl(url: string) : void { + let jumpMsgEntity: JumpMsgEntity = new JumpMsgEntity(JumpManager.MODULE_NAME, 'openUrl'); + jumpMsgEntity.url = url; + JumpManager.workerPort.postMessage(jumpMsgEntity); + } +} diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/appJump/JumpManagerMsg.ts b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/appJump/JumpManagerMsg.ts new file mode 100644 index 000000000000..ea373af90811 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/appJump/JumpManagerMsg.ts @@ -0,0 +1,31 @@ +import type common from '@ohos.app.ability.common'; +import { GlobalContext, GlobalContextConstants } from '../../common/GlobalContext'; +import {Result} from "../../entity/Result" +import type { JumpMsgEntity } from '../../entity/WorkerMsgEntity'; +import Logger from '../../utils/Logger' + +let log: Logger = new Logger(0x0001, "JumpManagerMsg"); + +export function handleJumpManagerMsg(eventData: JumpMsgEntity) : void { + switch (eventData.function) { + case "openUrl": + openUrl(eventData.url); + break; + default: + log.error('%{public}s has not implement yet', eventData.function); + } +} + +function openUrl(url: string): void { + let context: common.UIAbilityContext = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT); + let wantInfo = { + 'action': 'ohos.want.action.viewData', + 'entities': ['entity.system.browsable'], + 'uri': url + } + context.startAbility(wantInfo).then(() => { + log.info('%{public}s', JSON.stringify(Result.success({}))); + }).catch((err) => { + log.error('openUrl : err : %{public}s', JSON.stringify(Result.error(-1, JSON.stringify(err))) ?? ''); + }); +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/application/ApplicationManager.ts b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/application/ApplicationManager.ts new file mode 100644 index 000000000000..9a552f56e49f --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/application/ApplicationManager.ts @@ -0,0 +1,54 @@ +import bundleManager from '@ohos.bundle.bundleManager'; +import type { ThreadWorkerGlobalScope } from '@ohos.worker'; +import { BaseWorkerMsgEntity } from '../../entity/WorkerMsgEntity'; +import { common } from '@kit.AbilityKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { GlobalContext, GlobalContextConstants } from '../../common/GlobalContext'; + +export class ApplicationManager { + static MODULE_NAME: string = 'ApplicationManager'; + + private static workerPort: ThreadWorkerGlobalScope; + + static init(workerPort: ThreadWorkerGlobalScope): void { + ApplicationManager.workerPort = workerPort; + } + + static exit(): void { + let workerMsg: BaseWorkerMsgEntity = new BaseWorkerMsgEntity(ApplicationManager.MODULE_NAME, 'exit'); + ApplicationManager.workerPort.postMessage(workerMsg); + } + + static getVersionName(): string { + let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT; + return bundleManager.getBundleInfoForSelfSync(bundleFlags).versionName; + } +} + +export function handleApplicationMsg(eventData: BaseWorkerMsgEntity): void { + switch (eventData.function) { + case "exit": + terminateSelf(); + break; + default: + console.error('%{public}s has not implement yet', eventData.function); + } +} + +function terminateSelf(): void { + try { + let context: common.UIAbilityContext = + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT); + context.terminateSelf((err: BusinessError) => { + if (err.code) { + console.error(`terminateSelf failed, code is ${err.code}, message is ${err.message}`); + return; + } + console.info('terminateSelf succeed'); + }); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + console.error(`terminateSelf failed, code is ${code}, message is ${message}`); + } +} diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/device/DeviceUtils.ts b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/device/DeviceUtils.ts new file mode 100644 index 000000000000..b778f1584810 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/device/DeviceUtils.ts @@ -0,0 +1,164 @@ +import display from '@ohos.display' +import i18n from '@ohos.i18n'; +import vibrator from '@ohos.vibrator'; +import Logger from '../../utils/Logger'; +import window from '@ohos.window'; +import { GlobalContext, GlobalContextConstants } from '../../common/GlobalContext'; +import { Rect } from '@ohos.application.AccessibilityExtensionAbility'; + +let log = new Logger(0x0001, "DeviceUtils"); + +export class DeviceUtils { + static MODULE_NAME: string = 'DeviceUtils'; + static _roundScreen: boolean = false; + static _hasSoftKeys: boolean = false; + static _isCutoutEnable: boolean = false; + static _cutoutLeft: number; + static _cutoutWidth: number; + static _cutoutTop: number; + static _cutoutHeight: number; + + static getDpi(): number { + return display.getDefaultDisplaySync().densityDPI; + } + + static getSystemLanguage(): string { + return i18n.System.getSystemLanguage(); + } + + static startVibration(time: number) { + try { + vibrator.startVibration({ + type: 'time', + duration: time * 1000, // Seconds to milliseconds + }, { + id: 0, + usage: 'unknown' + }, (error) => { + if (error) { + log.error('vibrate fail, error.code: %{public}d, error.message: %{public}s', error.code, error.message); + return; + } + }); + } catch (err) { + log.error('error.code: %{public}d, error.message: %{public}s', err.code, err.message); + } + } + + static setKeepScreenOn(value: boolean) { + let windowClass = null; + try { + window.getLastWindow(GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT), (err, data) => { //获取窗口实例 + if (err.code) { + log.error('Failed to obtain last window when setKeepScreenOn. Cause:%{public}s', JSON.stringify(err)); + return; + } + windowClass = data; + // Sets whether the screen is always on. + let keepScreenOnPromise = windowClass.setWindowKeepScreenOn(value); + Promise.all([keepScreenOnPromise]).then(() => { + log.info('Succeeded in setKeepScreenOn, value:%{public}s', value); + }).catch((err) => { + log.error('Failed to setKeepScreenOn, cause:%{public}s', JSON.stringify(err)); + }); + }); + } catch (exception) { + log.error('Failed to get or set the window when setKeepScreenOn, cause:%{public}s', JSON.stringify(exception)); + } + } + + static isRoundScreen() : boolean { + return DeviceUtils._roundScreen; + } + + static hasSoftKeys() : boolean { + return DeviceUtils._hasSoftKeys; + } + + static isCutoutEnable() : boolean { + return DeviceUtils._isCutoutEnable; + } + + static initScreenInfo() : void { + let windowClass = null; + try { + window.getLastWindow(GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT), (err, data) => { + if (err.code) { + log.error('Failed to obtain last window when initScreenInfo. Cause:%{public}s', JSON.stringify(err)); + return; + } + + windowClass = data; + let windowProperties: window.WindowProperties = windowClass.getWindowProperties(); + DeviceUtils._roundScreen = windowProperties.isRoundCorner; + let rect: Rect = windowProperties.windowRect; + if(rect.top + rect.height < display.getDefaultDisplaySync().height) { + DeviceUtils._hasSoftKeys = true; + } else { + DeviceUtils._hasSoftKeys = false; + } + }); + } catch (exception) { + log.error('Failed to get or set the window when initScreenInfo, cause:%{public}s', JSON.stringify(exception)); + } + + display.getDefaultDisplaySync().getCutoutInfo().then((data) => { + if(data.boundingRects.length == 0) { + DeviceUtils._isCutoutEnable = false; + return; + } + + DeviceUtils._isCutoutEnable = true; + DeviceUtils._cutoutLeft = data.boundingRects[0].left; + DeviceUtils._cutoutTop = data.boundingRects[0].top; + DeviceUtils._cutoutWidth = data.boundingRects[0].width; + DeviceUtils._cutoutHeight = data.boundingRects[0].height; + }).catch((err) => { + log.error('Failed to obtain all the display objects. Code: ' + JSON.stringify(err)); + }); + } + + static getOrientation() : number { + let orientation: display.Orientation = display.getDefaultDisplaySync().orientation; + + // If the system enumeration value changes, the processing logic in the C++ code needs to be changed. Therefore, the mapping is performed again. + if(orientation == display.Orientation.PORTRAIT) { + return 0; + } + + if(orientation == display.Orientation.LANDSCAPE) { + return 1; + } + + if(orientation == display.Orientation.PORTRAIT_INVERTED) { + return 2; + } + + if(orientation == display.Orientation.LANDSCAPE_INVERTED) { + return 3; + } + + return 4; + } + + static getCutoutHeight() : number { + if(DeviceUtils._cutoutHeight) { + let height = DeviceUtils._cutoutTop + DeviceUtils._cutoutHeight; + return height; + } + return 0; + } + + static getCutoutWidth() : number { + if(!DeviceUtils._cutoutWidth) { + return 0; + } + + let disPlayWidth = display.getDefaultDisplaySync().width; + if(DeviceUtils._cutoutLeft + DeviceUtils._cutoutWidth > disPlayWidth - DeviceUtils._cutoutLeft) { + return disPlayWidth - DeviceUtils._cutoutLeft; + } + + return DeviceUtils._cutoutLeft + DeviceUtils._cutoutWidth; + } +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/sensor/AccelerometerUtils.ts b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/sensor/AccelerometerUtils.ts new file mode 100644 index 000000000000..56309439f011 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/system/sensor/AccelerometerUtils.ts @@ -0,0 +1,55 @@ +import { getContext } from "libnativerender.so"; +import { ContextType } from "../../common/Constants" +import sensor from '@ohos.sensor'; +import display from '@ohos.display'; +import {Result} from "../../entity/Result" +import Logger from '../../utils/Logger' + +let log: Logger = new Logger(0x0001, "AccelerometerUtils"); + +const accUtils = getContext(ContextType.SENSOR_API); + +export default class Accelerometer { + + private static instance = new Accelerometer(); + + static getInstance() : Accelerometer { + return Accelerometer.instance; + } + + static enable(intervalTime: number) : void { + try { + /* HarmonyOS allow multiple subscriptions, but the game only need one + so if the interval changed, cancel subscription and redo with the new interval */ + sensor.off(sensor.SensorId.ACCELEROMETER); + sensor.on(sensor.SensorId.ACCELEROMETER, function (data) { + let rotation = display.getDefaultDisplaySync().rotation; + if (rotation === 0) { + // Display device screen rotation 0° + accUtils.onAccelerometerCallBack(data.x, data.y, data.z, intervalTime); + } else if (rotation === 1) { + // Display device screen rotation 90° + accUtils.onAccelerometerCallBack(data.y, -data.x, data.z, intervalTime); + } else if (rotation === 2) { + // Display device screen rotation 180° + accUtils.onAccelerometerCallBack(-data.x, -data.y, data.z, intervalTime); + } else if (rotation === 3) { + // Display device screen rotation 270° + accUtils.onAccelerometerCallBack(-data.y, data.x, data.z, intervalTime); + } else { + log.error('Accelerometer init fail, err: %{public}s', JSON.stringify(Result.error(-1, 'unsupported rotation: ' + rotation))); + } + }, { interval: intervalTime }); + } catch (err) { + log.error('Accelerometer init fail, err: %{public}s', JSON.stringify(Result.error(-1, JSON.stringify(err))) ?? ''); + } + } + + static disable() : void { + try { + sensor.off(sensor.SensorId.ACCELEROMETER); + } catch (err) { + log.error('Accelerometer off fail, err: %{public}s', JSON.stringify(Result.error(-1, JSON.stringify(err))) ?? ''); + } + } +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/utils/Logger.ts b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/utils/Logger.ts new file mode 100644 index 000000000000..6277b0e1ab52 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/utils/Logger.ts @@ -0,0 +1,27 @@ +import hilog from '@ohos.hilog' + +export default class Logger { + private domain: number + private prefix: string + + constructor(domain : number, prefix: string) { + this.domain = domain + this.prefix = prefix + } + + debug(format: string, ...args: any[]) { + hilog.debug(this.domain, this.prefix, format, args) + } + + info(format: string, ...args: any[]) { + hilog.info(this.domain, this.prefix, format, args) + } + + warn(format: string, ...args: any[]) { + hilog.warn(this.domain, this.prefix, format, args) + } + + error(format: string, ...args: any[]) { + hilog.error(this.domain, this.prefix, format, args) + } +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/utils/StringUtils.ts b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/utils/StringUtils.ts new file mode 100644 index 000000000000..4e3d20ec55e1 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/utils/StringUtils.ts @@ -0,0 +1,8 @@ +import measure from '@ohos.measure'; + +export default class StringUtils { + + public static getWidth(text: string, fontSize: number, fontWeight: number): number { + return measure.measureText({ textContent: text, fontSize: fontSize + 'px', fontWeight: fontWeight }); + } +} \ No newline at end of file diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/utils/WorkerMsgUtils.ets b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/utils/WorkerMsgUtils.ets new file mode 100644 index 000000000000..b9cac7106b18 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/ets/utils/WorkerMsgUtils.ets @@ -0,0 +1,42 @@ +import worker, { MessageEvents } from '@ohos.worker'; +import Logger from './Logger'; +import { handleEditBoxMsg } from '../components/editbox/EditBoxMsg' +import { handleWebViewMsg } from '../components/webview/WebViewMsg' +import { handleVideoPlayMsg } from '../components/videoplayer/VideoPlayerMsg' +import { handleDialogMsg } from '../components/dialog/DialogMsg' +import { handleJumpManagerMsg } from '../system/appJump/JumpManagerMsg' +import { handleApplicationMsg } from '../system/application/ApplicationManager' +import { BaseWorkerMsgEntity, DialogMsgEntity, EditBoxMsgEntity, JumpMsgEntity, VideoPlayMsgEntity, WebViewMsgEntity } from '../entity/WorkerMsgEntity'; + +export class WorkerMsgUtils { + static workPort = worker.workerPort; + static log : Logger = new Logger(0x0001, 'WorkerMsgUtils') + + static async recvWorkerThreadMessage(event: MessageEvents) { + let eventData: BaseWorkerMsgEntity = event.data; + WorkerMsgUtils.log.debug('mainThread receiveMsg, module:%{public}s, function:%{public}s', eventData.module, eventData.function); + + switch (eventData.module) { + case 'EditBox': + handleEditBoxMsg(eventData as EditBoxMsgEntity); + break; + case "Dialog": + handleDialogMsg(eventData as DialogMsgEntity); + break; + case 'WebView': + handleWebViewMsg(eventData as WebViewMsgEntity); + break; + case 'VideoPlay': + handleVideoPlayMsg(eventData as VideoPlayMsgEntity); + break; + case 'JumpManager': + handleJumpManagerMsg(eventData as JumpMsgEntity); + break; + case 'ApplicationManager': + handleApplicationMsg(eventData as BaseWorkerMsgEntity); + break; + default: + WorkerMsgUtils.log.error('%{public}s has not implement yet', eventData.module); + } + } +} diff --git a/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/module.json5 b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/module.json5 new file mode 100644 index 000000000000..daa1891126f5 --- /dev/null +++ b/tests/cpp-tests/proj.ohos/libSysCapabilities/src/main/module.json5 @@ -0,0 +1,10 @@ +{ + "module": { + "name": "libSysCapabilities", + "type": "har", + "deviceTypes": [ + "phone", + "tablet" + ] + } +} diff --git a/tests/cpp-tests/proj.ohos/oh-package.json5 b/tests/cpp-tests/proj.ohos/oh-package.json5 new file mode 100644 index 000000000000..1ebf93ed2afb --- /dev/null +++ b/tests/cpp-tests/proj.ohos/oh-package.json5 @@ -0,0 +1,10 @@ +{ + "modelVersion": "5.0.0", + "license": "ISC", + "devDependencies": {}, + "name": "proj.ohos", + "description": "example description", + "repository": {}, + "version": "1.0.0", + "dependencies": {} +} \ No newline at end of file diff --git a/tests/lua-tests/project/CMakeLists.txt b/tests/lua-tests/project/CMakeLists.txt index c7b617335cb4..654af6906cde 100644 --- a/tests/lua-tests/project/CMakeLists.txt +++ b/tests/lua-tests/project/CMakeLists.txt @@ -25,18 +25,44 @@ elseif(MACOSX) cocos_mark_resources(FILES ${TEST_RESOURCES} BASEDIR ${CMAKE_SOURCE_DIR}/tests/cpp-tests/Resources RESOURCEBASE Resources/res) list(APPEND SAMPLE_SRC ${APP_RESOURCES} ${APP_SCRIPTS} ${COCOS_SCRIPTS} ${TEST_RESOURCES}) +elseif(OHOS) + list(APPEND GAME_SOURCE proj.ohos/entry/src/main/cpp/main.cpp) + file(GLOB resFiles "${CMAKE_CURRENT_LIST_DIR}/../res/*") + file(COPY ${resFiles} DESTINATION ${CMAKE_CURRENT_LIST_DIR}/proj.ohos/entry/src/main/resources/rawfile) + file(GLOB srcFiles "${CMAKE_CURRENT_LIST_DIR}/../src/*") + file(COPY ${srcFiles} DESTINATION ${CMAKE_CURRENT_LIST_DIR}/proj.ohos/entry/src/main/resources/rawfile) + file(GLOB testResource ${COCOS2DX_ROOT_PATH}/tests/cpp-tests/Resources/*) + file(COPY ${testResource} DESTINATION ${CMAKE_CURRENT_LIST_DIR}/proj.ohos/entry/src/main/resources/rawfile) + if(NOT EXISTS ${CMAKE_CURRENT_LIST_DIR}/proj.ohos/entry/src/main/resources/rawfile/cocos) + file(GLOB cocosFiles "${COCOS2DX_ROOT_PATH}/cocos/scripting/lua-bindings/script/*") + file(COPY ${cocosFiles} DESTINATION ${CMAKE_CURRENT_LIST_DIR}/proj.ohos/entry/src/main/resources/rawfile/cocos) + endif() endif() -include_directories( - Classes - ../../../cocos/scripting/lua-bindings/manual - ../../../cocos/scripting/lua-bindings/auto - ../../../external/lua/lua - ../../../external/lua/tolua -) +if (OHOS) + include_directories( + Classes + ../../../cocos/scripting/lua-bindings/manual + ../../../cocos/scripting/lua-bindings/auto + ../../../external/lua/luajit/include + ../../../external/lua/tolua + ) +else() + include_directories( + Classes + ../../../cocos/scripting/lua-bindings/manual + ../../../cocos/scripting/lua-bindings/auto + ../../../external/lua/lua + ../../../external/lua/tolua + ) +endif() -# add the executable -add_executable(${APP_NAME} ${SAMPLE_SRC}) +if (OHOS) + add_library(${APP_NAME} STATIC ${SAMPLE_SRC}) +else() + # add the executable + add_executable(${APP_NAME} ${SAMPLE_SRC}) +endif () target_link_libraries(${APP_NAME} luacocos2d cocos2d) @@ -52,12 +78,21 @@ else() set_target_properties(${APP_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${APP_BIN_DIR}") +if (OHOS) + pre_build(${APP_NAME} + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/../res ${APP_BIN_DIR}${RES_PREFIX}/res + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/../src ${APP_BIN_DIR}${RES_PREFIX}/src + COMMAND ${CMAKE_COMMAND} -E copy_directory ${COCOS2DX_ROOT_PATH}/cocos/scripting/lua-bindings/script/ ${APP_BIN_DIR}/Resources/src/cocos + COMMAND ${CMAKE_COMMAND} -E copy_directory ${COCOS2DX_ROOT_PATH}/tests/cpp-tests/Resources ${APP_BIN_DIR}${RES_PREFIX}/res + ) +else() pre_build(${APP_NAME} COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/../res ${APP_BIN_DIR}${RES_PREFIX}/res COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/../src ${APP_BIN_DIR}${RES_PREFIX}/src COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/cocos/scripting/lua-bindings/script/ ${APP_BIN_DIR}/Resources/src/cocos COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/tests/cpp-tests/Resources ${APP_BIN_DIR}${RES_PREFIX}/res ) +endif() endif() diff --git a/tests/lua-tests/project/Classes/AppDelegate.cpp b/tests/lua-tests/project/Classes/AppDelegate.cpp index 1b7c29ba64db..59a94a9ef77c 100644 --- a/tests/lua-tests/project/Classes/AppDelegate.cpp +++ b/tests/lua-tests/project/Classes/AppDelegate.cpp @@ -43,7 +43,7 @@ bool AppDelegate::applicationDidFinishLaunching() lua_getglobal(L, "_G"); if (lua_istable(L,-1))//stack:...,_G, { -#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID ||CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID ||CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) register_assetsmanager_test_sample(L); #endif register_test_binding(L); @@ -51,8 +51,8 @@ bool AppDelegate::applicationDidFinishLaunching() lua_pop(L, 1); - pEngine->executeScriptFile("src/controller.lua"); - + FileUtils::getInstance()->addSearchPath(""); + pEngine->executeScriptFile("controller.lua"); return true; } @@ -71,3 +71,17 @@ void AppDelegate::applicationWillEnterForeground() SimpleAudioEngine::getInstance()->resumeBackgroundMusic(); } + +void AppDelegate::applicationScreenSizeChanged(int newWidth, int newHeight) +{ + auto director = cocos2d::Director::getInstance(); + auto glview = director->getOpenGLView(); + if (glview != NULL) { + // Set ResolutionPolicy to a proper value. here use the original value when the game is started. + ResolutionPolicy resolutionPolicy = glview->getResolutionPolicy(); + Size designSize = glview->getDesignResolutionSize(); + glview->setFrameSize(newWidth, newHeight); + // Set the design resolution to a proper value. here use the original value when the game is started. + glview->setDesignResolutionSize(designSize.width, designSize.height, resolutionPolicy); + } +} \ No newline at end of file diff --git a/tests/lua-tests/project/Classes/AppDelegate.h b/tests/lua-tests/project/Classes/AppDelegate.h index f8fbbdbb3915..c19969cfb40c 100644 --- a/tests/lua-tests/project/Classes/AppDelegate.h +++ b/tests/lua-tests/project/Classes/AppDelegate.h @@ -33,6 +33,13 @@ class AppDelegate : private cocos2d::Application @param the pointer of the application */ virtual void applicationWillEnterForeground(); + + /** + @brief This function will be called when the application screen size is changed. + @param new width + @param new height + */ + virtual void applicationScreenSizeChanged(int newWidth, int newHeight); }; #endif // __APP_DELEGATE_H__ diff --git a/tests/lua-tests/project/proj.ohos/.gitignore b/tests/lua-tests/project/proj.ohos/.gitignore new file mode 100644 index 000000000000..43fc9cb4cc28 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/.gitignore @@ -0,0 +1,14 @@ +.hvigor +.idea +node_modules +entry/build +entry/.idea +entry/.cxx +local.properties +oh-package-lock.json5 +entry/src/main/resources/rawfile +oh_modules +/.clangd +/.clang-tidy +.clang-format +hvigor/hvigor-wrapper.js \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/AppScope/app.json5 b/tests/lua-tests/project/proj.ohos/AppScope/app.json5 new file mode 100644 index 000000000000..a3942f342f31 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/AppScope/app.json5 @@ -0,0 +1,11 @@ +{ + "app": { + "bundleName": "ohos.cocos3_7.lua.tests", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name", + "distributedNotificationEnabled": true + } +} diff --git a/tests/lua-tests/project/proj.ohos/AppScope/resources/base/element/string.json b/tests/lua-tests/project/proj.ohos/AppScope/resources/base/element/string.json new file mode 100644 index 000000000000..d6d16365f986 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "3.7_lua_tests" + } + ] +} diff --git a/tests/lua-tests/project/proj.ohos/AppScope/resources/base/media/app_icon.png b/tests/lua-tests/project/proj.ohos/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c GIT binary patch literal 6790 zcmX|G1ymHk)?T_}Vd;>R?p|tHQo6fg38|$UVM!6BLrPFWk?s;$LOP{GmJpBl$qoSA!PUg~PA65-S00{{S`XKG6NkG0RgjEntPrmV+?0|00mu7;+5 zrdpa{2QLqPJ4Y{j7=Mrl{BaxrkdY69+c~(w{Fv-v&aR%aEI&JYSeRTLWm!zbv;?)_ ziZB;fwGbbeL5Q}YLx`J$lp~A09KK8t_z}PZ=4ZzgdeKtgoc+o5EvN9A1K1_<>M?MBqb#!ASf&# zEX?<)!RH(7>1P+j=jqG(58}TVN-$psA6K}atCuI!KTJD&FMmH-78ZejBm)0qc{ESp z|LuG1{QnBUJRg_E=h1#XMWt2%fcoN@l7eAS!Es?Q+;XsRNPhiiE=@AqlLkJzF`O18 zbsbSmKN=aaq8k3NFYZfDWpKmM!coBU0(XnL8R{4=i|wi{!uWYM2je{U{B*K2PVdu&=E zTq*-XsEsJ$u5H4g6DIm2Y!DN`>^v|AqlwuCD;w45K0@eqauiqWf7l&o)+YLHm~|L~ z7$0v5mkobriU!H<@mVJHLlmQqzQ3d6Rh_-|%Yy2li*tHO>_vcnuZ7OR_xkAIuIU&x z-|8Y0wj|6|a6_I(v91y%k_kNw6pnkNdxjqG8!%Vz_d%c_!X+6-;1`GC9_FpjoHev5fEV7RhJ>r=mh-jp$fqbqRJ=obwdgLDVP5+s zy1=_DWG0Y-Jb3t^WXmkr(d9~08k-|#Ly zaNOmT(^9tIb&eb4%CzIT zAm3CUtWSr1t4?h1kk#NBi{U|pJslvME{q|_eS^3En>SOqSxyuN1x;Is@8~m?*>}** znrRFArP!K_52RpX*&JHMR<^lVdm8ypJ}0R(SD(51j;6@ni$6bQ+2XL+R^|NnSp5}(kzvMZ^(@4fD_{QVu$(&K6H|C37TG1Am9Re{<<3gd zh@`>;BqkXMW&p0T6rt|iB$)~CvFe(XC)F9WgAZn*0@t$oZo;!*}r@_`h?KKH&6A@3= zISXoQB+~`op>NP-buiA*^0n{@i{_?MRG)&k)c)k_F+-2Lud!S9pc+i`s74NpBCaGF zXN+pHkubw*msGBTY27BKHv)RRh3;nMg4&$fD_6X9Vt~;_4D+5XPH~#Kn-yjcy!$}1 zigv#FNY>TqMhtIBb@UoF!cE~Q8~;!Pek>SQQwHnHuWKoVBosAiOr}q>!>aE*Krc)V zBUMEcJ5NU0g8}-h6i1zpMY9>m4ne?=U2~`w7K7Q0gB_=p@$5K7p6}thw z-~3dMj?YNX2X$lZ+7ngQ$=s}3mizNN@kE%OtB)?c&i~2L55z8^=yz;xMHLmlY>&Q# zJj?!)M#q_SyfkQh)k?j8IfLtB)ZCp|*vf4_B zos?73yd^h-Ac+;?E4*bpf=o*^3x3-`TVjbY4n6!EN10K6o@fxdyps05Vo3PU)otB} z`3kR+2w7_C#8Z!q`J)p{Vh!+m9-UP!$STp+Hb}}#@#_u^SsUQg<}59< zTvH3%XS4G+6FF^(m6bVF&nSUIXcl;nw{=H$%fgeJ>CgDYiLdpDXr{;-AnG z8dvcrHYVMI&`R6;GWekI@Ir3!uo)oz4^{6q0m^}@f2tM9&=YHNi6-?rh0-{+k@cQm zdp`g#YdQn%MDVg2GR>wZ`n2<0l4)9nx1Wfr&!Dvz=bPwU!h2S?ez6MVc5APE4-xLB zi&W9Q8k2@0w!C53g?iAIQ}~p*3O(@zja6KQ=M3zfW*_6o5SwR-)6VBh~m7{^-=MC-owYH5-u40a}a0liho3QZZ5L{bS_xM1)4}19)zTU$$MY zq3eZML1WC{K%YFd`Be0M-rkO^l?h{kM{$2oK1*A@HVJ57*yhDkUF!2WZ&oA4Y-sK( zCY69%#`mBCi6>6uw(x4gbFaP0+FD*JKJ-q!F1E?vLJ+d35!I5d7@^eU?(CS|C^tmI5?lv@s{{*|1F zFg|OzNpZ0hxljdjaW%45O0MOttRrd(Z?h{HYbB-KFUx&9GfFL3b8NwZ$zNu)WbBD` zYkj$^UB5%3Pj1MDr>S2Ejr9pUcgA!;ZG!@{uAy12)vG=*^9-|dNQBc8&`oxBlU~#y zs!anJX&T?57Jdr^sb>e+V`MVfY>Y0ESg7MG<7W0g&bR-ZYzzZ%2H&Etcp zcd6QeXO1D!5A#zM0lx*GH}`M)2~ZFLE;sP^RSB5wVMNfiZXPd(cmO>j=OSA3`o5r& zna(|^jGXbdN7PK)U8b7^zYtYkkeb%<%F~=OqB~kXMQkq}ii|skh@WSRt>5za;cjP0 zZ~nD%6)wzedqE}BMLt~qKwlvTr33))#uP~xyw#*Eaa|DbMQ_%mG0U8numf8)0DX`r zRoG2bM;#g|p-8gWnwRV5SCW0tLjLO&9Z?K>FImeIxlGUgo0Zk`9Qzhj1eco~7XZy+hXc@YF&ZQ=? zn*^1O56yK^x{y}q`j7}blGCx%dydV!c7)g~tJzmHhV=W~jbWRRR{1<^oDK+1clprm zz$eCy7y9+?{E|YgkW~}}iB#I4XoJ*xr8R?i_Hv$=Cof5bo-Nj~f`-DLebH}&0% zfQj9@WGd4;N~Y?mzQsHJTJq6!Qzl^-vwol(+fMt#Pl=Wh#lI5Vmu@QM0=_r+1wHt` z+8WZ~c2}KQQ+q)~2Ki77QvV&`xb|xVcTms99&cD$Zz4+-^R4kvUBxG8gDk7Y`K*)JZ^2rL(+ZWV~%W(@6 z)0bPArG#BROa_PHs~&WplQ_UIrpd)1N1QGPfv!J(Z9jNT#i%H?CE6|pPZb9hJ1JW4 z^q;ft#!HRNV0YgPojzIYT`8LuET2rUe-J|c!9l4`^*;4WtY@Ew@pL>wkjmMgGfN7 ze}}GtmU0@<_#08~I-Suk=^*9GLW=H4xhsml;vAV{%hy5Eegl@!6qKqbG024%n2HHw zCc@ivW_$@5ZoHP70(7D+(`PvgjW1Pd`wsiuv-aCukMrafwDm)B!xXVy*j2opohhoU zcJz%ADmj>i3`-3-$7nQKBQQuGY;2Qt&+(L~C>vSGFj5{Mlv?T_^dql;{zkpe4R1}R z%XfZyQ}wr*sr>jrKgm*PWLjuVc%6&&`Kbf1SuFpHPN&>W)$GmqC;pIoBC`=4-hPY8 zT*>%I2fP}vGW;R=^!1be?ta2UQd2>alOFFbVl;(SQJ4Jk#)4Z0^wpWEVvY4=vyDk@ zqlModi@iVPMC+{?rm=4(n+<;|lmUO@UKYA>EPTS~AndtK^Wy^%#3<;(dQdk3WaUkRtzSMC9}7x2||CNpF#(3T4C)@ z$~RWs`BNABKX|{cmBt>Q=&gkXl&x!!NK_%5hW0LS)Z4PB>%sV?F-{Wyj#s7W%$F{D zXdK^Fp3wvy+48+GP6F_|^PCRx=ddcTO3sG;B23A49~Qaw31SZ0Rc~`r4qqt%#OGW{ zCA_(LG5^N>yzUn&kAgVmxb=EA8s&tBXC}S1CZ(KoW)(%^JjLTPo^fs`Va;`=YlVPgmB$!yB}<(4ym6OeZ3xAJJ#;)2+B%p3P1Wt+d$eo`vz`T zXfUP2))kBDPoscH;Jc7I3NU<({|@wM$&GaDt`n7WLgIY3IA7A6-_R?z8N3mz|}*i z(zl5ot--Oq@f2-nv{X(ujT2T(k1vY_qh93pK@>H-qc%2Xta)IP0Q%zt%bqYgI`o!wv!0QerB`nCN^1n|@$sVOQ!V0teVG!I z_fD%JvfDeT1cK#-{o6Gv7}& zY0#NWin~kVaf$aufV&;63Hbs|`QVZWpDX6IMk1Hj2G}fiH9e-^6u2zf^FIr^BwD<6zjw63+{yUe8PUFvk8v{sJ=R{d#`O!sz`Q13~< zPT$JS(w=yQfU2`zPCNfSw=&zup@DXc(98afjhv@1w_f!m2Z>rMJ19AB&dB%P#Ls3b z=lK7OILM+SQ&VEd=1GN6o&>YVVtIzoZ%=Z_SdqJN2}E43{bE`>w+A;=y->@^k{oCC z$F*WTY&?34;kfyFV?b*Xb1Pq`Z=%OgwEg)Rz)tx=`f%5#w_INP=x&z5!jI;#;N$ma zhO)+MDm;SxOEVL15; zGq(v2pL3&P1Sl)8P*;G-fd{l1QJsv@e@d8)1PK4w2m*M%V3j-V~L^$i|&C@b?D?9tfwE{B^}Z$k8e5FmQ>v7Xz)sG32g9t}YBt zyR$+*_00RmPx+0mW+vVG4mxd(n$(eQf3-w>JPl2UJpafrPaL5@2j}%{VE-) zBI%6Qpj*dsdH<;g!S!avA~bv^0E+ zfyJbSjPb+j;J52U)<|cIcntQBI2T#>2;tOxu{%D?kML476AErF(qN9hPva5Nkc@BF zC-tLF@3ZFb%Kpj)M<{)x*l|*Ia@ECeXo2E4h2f!aV=cHAhi_E_mfUth(sM4^hJq7B zQsGWqdZUm9S%F`$nQ*_#NcuD`&)Ek%_s{&^78{9Hm ztri&rYLOxgFdG>O@+XHy z9#;|&vBCPXH5Mon^I`jSuR$&~ZWtyB67ujzFSj!51>#C}C17~TffQ{c-!QFQkTQ%! zIR^b1`zHx|*1GU?tbBx23weFLz5H?y_Q%N&t$}k?w+``2A=aotj0;2v$~AL z{scF-cL{wsdrmPvf#a9OHyYLcwQD4Kcm)`LLwMh4WT~p29f7M!iafJSU`IV}QY5Wa z(n44-9oA}?J{a+ah*@31WTs#&J#o1`H98#6IQf;Wv0N_!);f&9g7o-k(lW5rWnDUR zQBFIRG+X=6NnsI@mxnwm;tf5;_Uxg?jZ8m-m0}&6+DA!qam(p$mN5R})yA_7m$q@| zFEd|dpS595rxQr-n#GjI5i-AhnUE>Cr;jpCqSrD~EwK_DqI^7%3#p5)%T_od!t3SOmH9MyXeeGO2(UQL;ax|x?Ncixmeo1=$ z{-);Au{*tfzOG?KQ~K|ak8-HQ?`Pekhe2WM(8s{xv-p>Zmu_6{G!-oE$7$mY`MOJorI=+mMx?H;`pr!;fVYz?5~yXBACruWB`Ph zZM}90_<^OBxIhyZ9BW$`>6JvO;%VFpqVr8|7t3~AmxYak6?`Pp#c;**_SYmi`&z23 z`p6_~ePvH)C6x-G9$hgL=eVALq`-AiamN>!3~Lxw&{H(b{B(7xSRm6<3<{%{yXiH# zos5Rv1L+8fUKJLo%P>4I&$}ygetOpenGLView()) + { + OHOS_LOGD("Cocos2dxRenderer_nativeInit() - branch 1"); + GLView *view = GLViewImpl::sharedOpenGLView(); + view->setFrameSize(w, h); + CCDirector::sharedDirector()->setOpenGLView(view); + + AppDelegate *pAppDelegate = new AppDelegate(); + CCApplication::sharedApplication()->run(); + } + else + { + OHOS_LOGD("Cocos2dxRenderer_nativeInit() - branch 2"); + ccGLInvalidateStateCache(); + CCShaderCache::sharedShaderCache()->reloadDefaultShaders(); + ccDrawInit(); + CCTextureCache::reloadAllTextures(); + CCNotificationCenter::sharedNotificationCenter()->postNotification(EVENT_COME_TO_FOREGROUND, NULL); + CCDirector::sharedDirector()->setGLDefaultValues(); + } +} + +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/entry/src/main/cpp/napi_init.cpp b/tests/lua-tests/project/proj.ohos/entry/src/main/cpp/napi_init.cpp new file mode 100644 index 000000000000..5b0457f4f87b --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/entry/src/main/cpp/napi_init.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 "platform/ohos/CCLogOhos.h" +#include "napi/plugin_manager.h" + +/* + * function for module exports + */ +static napi_value Init(napi_env env, napi_value exports) +{ + napi_property_descriptor desc[] ={ + DECLARE_NAPI_FUNCTION("getContext", NapiManager::GetContext), + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + + bool ret = NapiManager::GetInstance()->Export(env, exports); + if (!ret) { + OHOS_LOGE("Init failed"); + } + return exports; +} + +/* + * Napi Module define + */ +static napi_module nativerenderModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "nativerender", + .nm_priv = ((void*)0), + .reserved = { 0 }, +}; +/* + * Module register function + */ +extern "C" __attribute__((constructor)) void RegisterModule(void) +{ + napi_module_register(&nativerenderModule); +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/entry/src/main/cpp/types/libentry/index.d.ts b/tests/lua-tests/project/proj.ohos/entry/src/main/cpp/types/libentry/index.d.ts new file mode 100644 index 000000000000..6a9293652bfe --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/entry/src/main/cpp/types/libentry/index.d.ts @@ -0,0 +1,30 @@ +import resmgr from '@ohos.resourceManager'; + +export interface CPPFunctions { + onCreate: () => void; + onShow: () => void; + onHide: () => void; + onBackPress: () => void; + onDestroy: () => void; + onPageShow: () => void; + onPageHide: () => void; + nativeResourceManagerInit: (resourceManager: resmgr.ResourceManager) => void; + writablePathInit: (writePath: string) => void; + workerInit: () => void; + nativeEngineStart: () => void; + registerFunction: () => void; + initAsyncInfo: () => void; + mouseWheelCB: (eventType: string, scrollY : number) => void; + editBoxOnFocusCB: (viewTag: number) => void; + editBoxOnChangeCB: (viewTag: number, text: string) => void; + editBoxOnEnterCB: (viewTag: number, text: string) => void; + textFieldTTFOnChangeCB: (text: string) => void; + shouldStartLoading: (viewTag: number, url: string) => void; + finishLoading: (viewTag: number, url: string) => void; + failLoading: (viewTag: number, url: string) => void; + jsCallback: () => void; + onVideoCallBack: (viewTag: number, event: number) => void; + onAccelerometerCallBack: (x: number, y: number, z: number, interval: number) => void; +} + +export const getContext: (a: number) => CPPFunctions; diff --git a/tests/lua-tests/project/proj.ohos/entry/src/main/cpp/types/libentry/oh-package.json5 b/tests/lua-tests/project/proj.ohos/entry/src/main/cpp/types/libentry/oh-package.json5 new file mode 100644 index 000000000000..fa7874d5940d --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/entry/src/main/cpp/types/libentry/oh-package.json5 @@ -0,0 +1,6 @@ +{ + "name": "libnativerender.so", + "types": "./index.d.ts", + "version": "", + "description": "Please describe the basic information." +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/entry/src/main/ets/MainAbility/MainAbility.ts b/tests/lua-tests/project/proj.ohos/entry/src/main/ets/MainAbility/MainAbility.ts new file mode 100644 index 000000000000..a4378409d4f4 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/entry/src/main/ets/MainAbility/MainAbility.ts @@ -0,0 +1,80 @@ +import window from '@ohos.window'; +import UIAbility from '@ohos.app.ability.UIAbility'; +import web_webview from '@ohos.web.webview'; +import nativeRender from "libnativerender.so"; +import { ContextType } from "@ohos/libSysCapabilities" +import { GlobalContext,GlobalContextConstants} from "@ohos/libSysCapabilities" + +const nativeAppLifecycle: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.APP_LIFECYCLE); +const rawFileUtils: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.RAW_FILE_UTILS); + +export default class MainAbility extends UIAbility { + onCreate(want, launchParam) { + nativeAppLifecycle.onCreate(); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT, this.context); + // Initializes the webView kernel of the system. This parameter is optional if it is not used. + web_webview.WebviewController.initializeWebEngine(); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_WANT, this.context); + console.info('[LIFECYCLE-App] onCreate') + } + + onDestroy() { + nativeAppLifecycle.onDestroy(); + console.info('[LIFECYCLE-App] onDestroy') + } + + onWindowStageCreate(windowStage) { + // Main window is created, set main page for this ability + windowStage.loadContent('pages/Index', (err, data) => { + if (err.code) { + return; + } + rawFileUtils.nativeResourceManagerInit(this.context.resourceManager); + rawFileUtils.writablePathInit(this.context.filesDir); + }); + + windowStage.getMainWindow().then((windowIns: window.Window) => { + // Set whether to display the status bar and navigation bar. If they are not displayed, [] is displayed. + let systemBarPromise = windowIns.setWindowSystemBarEnable([]); + // Whether the window layout is displayed in full screen mode + let fullScreenPromise = windowIns.setWindowLayoutFullScreen(true); + // Sets whether the screen is always on. + let keepScreenOnPromise = windowIns.setWindowKeepScreenOn(true); + Promise.all([systemBarPromise, fullScreenPromise, keepScreenOnPromise]).then(() => { + console.info('Succeeded in setting the window'); + }).catch((err) => { + console.error('Failed to set the window, cause ' + JSON.stringify(err)); + }); + }) + + windowStage.on("windowStageEvent", (data) => { + let stageEventType: window.WindowStageEventType = data; + switch (stageEventType) { + case window.WindowStageEventType.RESUMED: + nativeAppLifecycle.onShow(); + break; + case window.WindowStageEventType.PAUSED: + nativeAppLifecycle.onHide(); + break; + default: + break; + } + }); + } + + onWindowStageDestroy() { + // Main window is destroyed, release UI related resources + } + + onForeground() { + // Ability has brought to foreground + console.info('[LIFECYCLE-App] onShow') + nativeAppLifecycle.onShow(); + } + + onBackground() { + // Ability has back to background + console.info('[LIFECYCLE-App] onDestroy') + nativeAppLifecycle.onHide(); + } +}; diff --git a/tests/lua-tests/project/proj.ohos/entry/src/main/ets/components/CocosEditBox.ets b/tests/lua-tests/project/proj.ohos/entry/src/main/ets/components/CocosEditBox.ets new file mode 100644 index 000000000000..2587169f240f --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/entry/src/main/ets/components/CocosEditBox.ets @@ -0,0 +1,82 @@ +import { TextInputInfo } from '@ohos/libSysCapabilities/src/main/ets/components/editbox/EditBoxMsg' +import worker from '@ohos.worker'; +import { WorkerManager } from '../workers/WorkerManager'; + +@Component +export struct CocosEditBox { + @ObjectLink textInputInfo: TextInputInfo; + cocosWorker: worker.ThreadWorker = WorkerManager.getInstance().getWorker(); + + build(){ + if(this.textInputInfo.multiline){ + TextArea({text: this.textInputInfo.text, placeholder: this.textInputInfo.placeHolder}) + .id('TextArea'+this.textInputInfo.viewTag) + .position({x: px2vp(this.textInputInfo.x), y: px2vp(this.textInputInfo.y)}) + .size({width: px2vp(this.textInputInfo.w), height: px2vp(this.textInputInfo.h)}) + .padding({top: px2vp(this.textInputInfo.paddingH), right: px2vp(this.textInputInfo.paddingW), + bottom: px2vp(this.textInputInfo.paddingH), left: px2vp(this.textInputInfo.paddingW)}) + .borderRadius(0) + .maxLength(this.textInputInfo.maxLength) + .type(TextAreaType.NORMAL) + .fontFamily($rawfile(this.textInputInfo.fontPath)) + .fontSize(px2fp(this.textInputInfo.fontSize)) + .fontColor(`rgba(${this.textInputInfo.fontColor.r}, ${this.textInputInfo.fontColor.g}, ${this.textInputInfo.fontColor.b}, ${this.textInputInfo.fontColor.a})`) + .placeholderFont({size: px2fp(this.textInputInfo.placeholderFontSize), family: $rawfile(this.textInputInfo.placeHolderFontPath)}) + .placeholderColor(`rgba(${this.textInputInfo.placeholderFontColor.r}, ${this.textInputInfo.placeholderFontColor.g}, ${this.textInputInfo.placeholderFontColor.b}, ${this.textInputInfo.placeholderFontColor.a})`) + .backgroundColor(Color.Transparent) + .visibility(this.textInputInfo.visible ? Visibility.Visible : Visibility.None) + .onVisibleAreaChange([0.0, 1.0], () => { + focusControl.requestFocus('TextArea' + this.textInputInfo.viewTag); + }) + .onFocus(() => { + this.cocosWorker.postMessage({type: "editBoxOnFocus", viewTag: this.textInputInfo.viewTag}); + }) + .onChange((value: string)=>{ + this.textInputInfo.text = value; + this.cocosWorker.postMessage({type: "editBoxOnChange", viewTag: this.textInputInfo.viewTag, value: value}); + }) + .onBlur(()=> { + this.textInputInfo.visible = false + this.cocosWorker.postMessage({type: "editBoxOnEnter", viewTag: this.textInputInfo.viewTag, text: this.textInputInfo.text}); + }) + .onSubmit(()=>{ + this.cocosWorker.postMessage({type: "editBoxOnEnter", viewTag: this.textInputInfo.viewTag, text: this.textInputInfo.text}); + }) + }else{ + TextInput({text: this.textInputInfo.text, placeholder: this.textInputInfo.placeHolder}) + .id('TextInput'+this.textInputInfo.viewTag) + .position({x: px2vp(this.textInputInfo.x), y: px2vp(this.textInputInfo.y)}) + .size({width: px2vp(this.textInputInfo.w), height: px2vp(this.textInputInfo.h)}) + .padding({top: px2vp(this.textInputInfo.paddingH), right: px2vp(this.textInputInfo.paddingW), + bottom: px2vp(this.textInputInfo.paddingH), left: px2vp(this.textInputInfo.paddingW)}) + .borderRadius(0) + .maxLength(this.textInputInfo.maxLength) + .type(this.textInputInfo.inputMode) + .fontFamily($rawfile(this.textInputInfo.fontPath)) + .fontSize(px2fp(this.textInputInfo.fontSize)) + .fontColor(`rgba(${this.textInputInfo.fontColor.r}, ${this.textInputInfo.fontColor.g}, ${this.textInputInfo.fontColor.b}, ${this.textInputInfo.fontColor.a})`) + .placeholderFont({size: px2fp(this.textInputInfo.placeholderFontSize), family: $rawfile(this.textInputInfo.placeHolderFontPath)}) + .placeholderColor(`rgba(${this.textInputInfo.placeholderFontColor.r}, ${this.textInputInfo.placeholderFontColor.g}, ${this.textInputInfo.placeholderFontColor.b}, ${this.textInputInfo.placeholderFontColor.a})`) + .backgroundColor(Color.Transparent) + .visibility(this.textInputInfo.visible ? Visibility.Visible : Visibility.None) + .showPasswordIcon(false) + .onVisibleAreaChange([0.0, 1.0], () => { + focusControl.requestFocus('TextInput' + this.textInputInfo.viewTag); + }) + .onFocus(() => { + this.cocosWorker.postMessage({type: "editBoxOnFocus", viewTag: this.textInputInfo.viewTag}); + }) + .onChange((value: string)=>{ + this.textInputInfo.text = value; + this.cocosWorker.postMessage({type: "editBoxOnChange", viewTag: this.textInputInfo.viewTag, value: value}); + }) + .onBlur(()=> { + this.textInputInfo.visible = false + this.cocosWorker.postMessage({type: "editBoxOnEnter", viewTag: this.textInputInfo.viewTag, text: this.textInputInfo.text}); + }) + .onSubmit(()=>{ + this.cocosWorker.postMessage({type: "editBoxOnEnter", viewTag: this.textInputInfo.viewTag, text: this.textInputInfo.text}); + }) + } + } +} diff --git a/tests/lua-tests/project/proj.ohos/entry/src/main/ets/components/CocosVideoPlayer.ets b/tests/lua-tests/project/proj.ohos/entry/src/main/ets/components/CocosVideoPlayer.ets new file mode 100644 index 000000000000..6f67afe3b443 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/entry/src/main/ets/components/CocosVideoPlayer.ets @@ -0,0 +1,39 @@ +import { WorkerManager } from '../workers/WorkerManager' +import { VideoPlayerInfo, VideoEvent } from '@ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayerMsg'; +import worker from '@ohos.worker'; + +@Component +export struct CocosVideoPlayer { + @ObjectLink videoPlayerInfo: VideoPlayerInfo; + cocosWorker: worker.ThreadWorker = WorkerManager.getInstance().getWorker(); + + build() { + Column() { + Video({ + src: this.videoPlayerInfo.isUrl ? this.videoPlayerInfo.url : this.videoPlayerInfo.rawfile, + controller: this.videoPlayerInfo.controller + }) + .width(this.videoPlayerInfo.w) + .height(this.videoPlayerInfo.h) + .visibility(this.videoPlayerInfo.visible ? Visibility.Visible : Visibility.None) + .controls(false) + .loop(this.videoPlayerInfo.isLoop) + .objectFit(this.videoPlayerInfo.objectFit) + .onPrepared(()=>{ + if(this.videoPlayerInfo.isPlay) { + this.videoPlayerInfo.controller.start() + } + this.videoPlayerInfo.controller.requestFullscreen(this.videoPlayerInfo.isFullScreen) + }) + .onStart(()=>{ + this.cocosWorker.postMessage({type: "onVideoCallBack", viewTag: this.videoPlayerInfo.viewTag, event: VideoEvent.PLAYING}); + }) + .onPause(()=>{ + this.cocosWorker.postMessage({type: "onVideoCallBack", viewTag: this.videoPlayerInfo.viewTag, event: VideoEvent.PAUSED}); + }) + .onFinish(()=>{ + this.cocosWorker.postMessage({type: "onVideoCallBack", viewTag: this.videoPlayerInfo.viewTag, event: VideoEvent.COMPLETED}); + }) + } + } +} diff --git a/tests/lua-tests/project/proj.ohos/entry/src/main/ets/components/CocosWebview.ets b/tests/lua-tests/project/proj.ohos/entry/src/main/ets/components/CocosWebview.ets new file mode 100644 index 000000000000..f26ba14cc6e7 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/entry/src/main/ets/components/CocosWebview.ets @@ -0,0 +1,43 @@ +import { WebViewInfo } from "@ohos/libSysCapabilities/src/main/ets/components/webview/WebViewMsg" +import worker from '@ohos.worker'; +import { WorkerManager } from '../workers/WorkerManager'; + +@Component +export struct CocosWebView { + viewInfo: WebViewInfo = new WebViewInfo(0, 0, 0, 0, 0); + cocosWorker: worker.ThreadWorker = WorkerManager.getInstance().getWorker(); + + build() { + Web({ src: this.viewInfo.isUrl ? this.viewInfo.url : $rawfile(this.viewInfo.url), controller: this.viewInfo.controller }) + .position({ x: this.viewInfo.x, y: this.viewInfo.y }) + .width(this.viewInfo.w) + .height(this.viewInfo.h) + .border({ width: 1 }) + .domStorageAccess(true) + .databaseAccess(true) + .imageAccess(true) + .onlineImageAccess(true) + .zoomAccess(this.viewInfo.zoomAccess) + .javaScriptAccess(true) + .visibility(this.viewInfo.visible ? Visibility.Visible : Visibility.None) + .opacity(this.viewInfo.opacity) + .backgroundColor(this.viewInfo.backgroundColor) + .onPageBegin((event) => { + this.cocosWorker.postMessage({ type: "onPageBegin", viewTag: this.viewInfo.viewTag, url: event == null ? '' : event.url }); + }) + .onPageEnd((event) => { + this.cocosWorker.postMessage({ type: "onPageEnd", viewTag: this.viewInfo.viewTag, url: event == null ? '' : event.url }) + if(this.viewInfo.jsInterfaceScheme != "" && event != null && event.url.startsWith(this.viewInfo.jsInterfaceScheme)) { + this.cocosWorker.postMessage({ type: "onJsCallBack", viewTag: this.viewInfo.viewTag, url: event == null ? '' : event.url }) + } + // if u want use the javascript on ur page, u can use registerJavaScriptProxy() to register js function here + // and confirm that just register once by a local variable + }) + .onErrorReceive((event) => { + this.cocosWorker.postMessage({ type: "onErrorReceive", viewTag: this.viewInfo.viewTag, url: this.viewInfo.url }) + }) + .onHttpErrorReceive((event) => { + this.cocosWorker.postMessage({ type: "onErrorReceive", viewTag: this.viewInfo.viewTag, url: this.viewInfo.url }) + }) + } +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/entry/src/main/ets/components/TextInputDialog.ets b/tests/lua-tests/project/proj.ohos/entry/src/main/ets/components/TextInputDialog.ets new file mode 100644 index 000000000000..4a689341cc0a --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/entry/src/main/ets/components/TextInputDialog.ets @@ -0,0 +1,33 @@ +import { TextInputDialogEntity } from '@ohos/libSysCapabilities' +import worker from '@ohos.worker'; +import { WorkerManager } from '../workers/WorkerManager'; + +@CustomDialog +export struct TextInputDialog { + @State showMessage: TextInputDialogEntity = new TextInputDialogEntity(''); + cocosWorker: worker.ThreadWorker = WorkerManager.getInstance().getWorker(); + controller?: CustomDialogController + + build() { + Column() { + Row() { + TextInput({ text: this.showMessage.message }) + .backgroundColor('#ffffff') + .layoutWeight(1) + .defaultFocus(true) + .caretColor(Color.Transparent) + .onChange((value) => { + this.cocosWorker.postMessage({type: "textFieldTTFOnChange", data: value}) + }) + Blank(8).width(16) + Button($r('app.string.text_field_ttf_complete')).onClick(() => { + this.controller!.close(); + }) + }.padding({ left: 8, right: 8, top: 8, bottom: 8 }) + .backgroundColor(Color.Gray) + } + .width('100%') + + .justifyContent(FlexAlign.End) + } +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/entry/src/main/ets/pages/Index.ets b/tests/lua-tests/project/proj.ohos/entry/src/main/ets/pages/Index.ets new file mode 100644 index 000000000000..0a46024aa591 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,157 @@ +import deviceInfo from '@ohos.deviceInfo'; + +import nativeRender from 'libnativerender.so' +import { ContextType } from '@ohos/libSysCapabilities' +import { TextInputInfo } from '@ohos/libSysCapabilities/src/main/ets/components/editbox/EditBoxMsg' +import { TextInputDialogEntity } from '@ohos/libSysCapabilities' +import { WebViewInfo } from '@ohos/libSysCapabilities/src/main/ets/components/webview/WebViewMsg' +import { VideoPlayerInfo } from '@ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayerMsg' +import { WorkerMsgUtils } from '@ohos/libSysCapabilities/src/main/ets/utils/WorkerMsgUtils' +import { WorkerManager } from '../workers/WorkerManager' +import { CocosEditBox } from '../components/CocosEditBox' +import { CocosWebView } from '../components/CocosWebview' +import { CocosVideoPlayer } from '../components/CocosVideoPlayer' +import { TextInputDialog } from '../components/TextInputDialog' +import { GlobalContext,GlobalContextConstants} from "@ohos/libSysCapabilities" +import promptAction from '@ohos.promptAction'; +import process from '@ohos.process'; +const nativePageLifecycle:nativeRender.CPPFunctions = nativeRender.getContext(ContextType.JSPAGE_LIFECYCLE); + +@Entry +@Component +struct Index { + + cocosWorker = WorkerManager.getInstance().getWorker(); + xcomponentController: XComponentController = new XComponentController(); + + processMgr = new process.ProcessManager(); + + + // EditBox + @State editBoxArray: TextInputInfo[] = []; + private editBoxIndexMap: Map = new Map; + + // WebView + @State webViewArray: WebViewInfo[] = []; + private webViewIndexMap: Map = new Map; + + // videoPlayer + @State videoPlayerInfoArray: VideoPlayerInfo[] = []; + private videoPlayerIndexMap: Map = new Map; + + // textInputDialog + showMessage: TextInputDialogEntity = new TextInputDialogEntity(''); + dialogController: CustomDialogController = new CustomDialogController({ + builder: TextInputDialog({ + showMessage: this.showMessage + }), + autoCancel: true, + alignment: DialogAlignment.Bottom, + customStyle: true, + }) + // PanGesture + private panOption: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Up | PanDirection.Down }); + + onPageShow() { + console.log('[LIFECYCLE-Page] onPageShow'); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_ARRAY, this.editBoxArray); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP, this.editBoxIndexMap); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_COCOS_WORKER, this.cocosWorker); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY, this.webViewArray); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP, this.webViewIndexMap); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_ARRAY, this.videoPlayerInfoArray); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP, this.videoPlayerIndexMap); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_DIALOG_CONTROLLER, this.dialogController); + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_SHOW_MESSAGE, this.showMessage); + nativePageLifecycle.onPageShow(); + } + + onPageHide() { + console.log('[LIFECYCLE-Page] onPageHide'); + nativePageLifecycle.onPageHide(); + } + + onBackPress() { + console.log('[LIFECYCLE-Page] onBackPress'); + try { + promptAction.showDialog({ + title: "提示", + message: "确认退出游戏吗", + buttons: [ + { + text: '取消', + color: '#000000' + }, + { + text: '确认', + color: '#000000' + } + ], + }).then(data => { + console.info('showDialog success, click button: ' + data.index); + if(data.index == 0) { + console.info('showDialog click button cancel'); + return; + } else { + console.info('showDialog click button ok'); + this.processMgr.exit(0); + } + }) + } catch (error) { + console.error(`showDialog args error code is ${error.code}, message is ${error.message}`); + }; + // If disable system exit needed, remove comment "return true" + return true; + } + onMouseWheel(eventType: string, scrollY: number) { + this.cocosWorker.postMessage({ type: "onMouseWheel", eventType: eventType, scrollY: scrollY }); + } + + build() { + Stack() { + XComponent({ + id: 'xcomponentId', + type: 'surface', + libraryname: 'nativerender', + controller: this.xcomponentController + }) + .focusable(true) + .focusOnTouch(true) + .gesture( + PanGesture(this.panOption) + .onActionStart(() => { + this.onMouseWheel("actionStart", 0); + }) + .onActionUpdate((event: GestureEvent) => { + if (deviceInfo.deviceType === '2in1') { + this.onMouseWheel("actionUpdate", event.offsetY); + } + }) + .onActionEnd(() => { + this.onMouseWheel("actionEnd", 0); + }) + ) + .onLoad((context) => { + console.log('[cocos] XComponent.onLoad Callback'); + this.cocosWorker.postMessage({ type: "abilityContextInit", data: GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT)}); + this.cocosWorker.postMessage({ type: "onXCLoad", data: "XComponent" }); + this.cocosWorker.onmessage = WorkerMsgUtils.recvWorkerThreadMessage; + }) + .onDestroy(() => { + }) + ForEach(this.editBoxArray, (item:TextInputInfo) => { + CocosEditBox({ textInputInfo: item}); + }, (item:TextInputInfo) => item.viewTag.toString()) + + ForEach(this.webViewArray, (item:WebViewInfo) => { + CocosWebView({ viewInfo: item }) + }, (item:WebViewInfo) => item.uniqueId.toString()) + + ForEach(this.videoPlayerInfoArray, (item:VideoPlayerInfo) => { + CocosVideoPlayer({ videoPlayerInfo: item }) + }, (item:VideoPlayerInfo) => item.viewTag.toString()) + } + .width('100%') + .height('100%') + } +} diff --git a/tests/lua-tests/project/proj.ohos/entry/src/main/ets/workers/CocosWorker.ts b/tests/lua-tests/project/proj.ohos/entry/src/main/ets/workers/CocosWorker.ts new file mode 100644 index 000000000000..94ddb8d38920 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/entry/src/main/ets/workers/CocosWorker.ts @@ -0,0 +1,79 @@ +import worker, { ThreadWorkerGlobalScope } from '@ohos.worker'; +import nativeRender from "libnativerender.so"; +import { ContextType } from "@ohos/libSysCapabilities" +import { VideoPlayer } from "@ohos/libSysCapabilities" +import { CocosEditBox } from "@ohos/libSysCapabilities" +import { Dialog } from "@ohos/libSysCapabilities" +import { WebView } from "@ohos/libSysCapabilities" +import { JumpManager } from "@ohos/libSysCapabilities" +import { NapiHelper } from "@ohos/libSysCapabilities" +import { ApplicationManager } from "@ohos/libSysCapabilities" +import { GlobalContext,GlobalContextConstants} from "@ohos/libSysCapabilities" + +const appLifecycle: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.APP_LIFECYCLE); +const workerContext: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.WORKER_INIT); +const inputNapi: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.INPUT_NAPI); +const mouseNapi: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.MOUSE_NAPI); +const webViewNapi: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.WEBVIEW_NAPI); +const videoPlayNapi: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.VIDEOPLAYER_NAPI); +const napiContext: nativeRender.CPPFunctions = nativeRender.getContext(ContextType.NATIVE_API); +workerContext.workerInit() + +napiContext.nativeEngineStart(); +NapiHelper.registerFunctions(napiContext.registerFunction) + +const workerPort: ThreadWorkerGlobalScope = worker.workerPort; + +workerPort.onmessage = function(e) : void { + let data = e.data; + switch(data.type) { + case "onXCLoad": + console.log("[cocos] onXCLoad Callback"); + Dialog.init(workerPort); + CocosEditBox.init(workerPort); + JumpManager.init(workerPort); + WebView.init(workerPort); + VideoPlayer.init(workerPort); + ApplicationManager.init(workerPort); + napiContext.initAsyncInfo(); + break; + case "abilityContextInit": + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT, data.data); + break; + case "editBoxOnFocus": + inputNapi.editBoxOnFocusCB(data.viewTag); + break; + case "editBoxOnChange": + inputNapi.editBoxOnChangeCB(data.viewTag, data.value); + break; + case "editBoxOnEnter": + inputNapi.editBoxOnEnterCB(data.viewTag, data.text); + break; + case "textFieldTTFOnChange": + inputNapi.textFieldTTFOnChangeCB(data.data); + break; + case "onMouseWheel": + mouseNapi.mouseWheelCB(data.eventType, data.scrollY); + break; + case "onPageBegin": + webViewNapi.shouldStartLoading(data.viewTag, data.url); + break; + case "onPageEnd": + webViewNapi.finishLoading(data.viewTag, data.url); + break; + case "onJsCallBack": + webViewNapi.jsCallback(); + break; + case "onErrorReceive": + webViewNapi.failLoading(data.viewTag, data.url); + break; + case "onVideoCallBack": + videoPlayNapi.onVideoCallBack(data.viewTag, data.event); + break; + case "exit": + appLifecycle.onBackPress(); + break; + default: + console.error("cocos worker: message type unknown") + } +} diff --git a/tests/lua-tests/project/proj.ohos/entry/src/main/ets/workers/WorkerManager.ts b/tests/lua-tests/project/proj.ohos/entry/src/main/ets/workers/WorkerManager.ts new file mode 100644 index 000000000000..e2ec70b1873e --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/entry/src/main/ets/workers/WorkerManager.ts @@ -0,0 +1,34 @@ +import worker from '@ohos.worker'; +import { Constants } from '@ohos/libSysCapabilities'; + +export class WorkerManager { + private cocosWorker: worker.ThreadWorker; + + private constructor() { + this.cocosWorker = new worker.ThreadWorker("entry/ets/workers/CocosWorker.ts", { + type: "classic", + name: "CocosWorker" + }); + this.cocosWorker.onerror = (e) => { + let msg = e.message; + let filename = e.filename; + let lineno = e.lineno; + let colno = e.colno; + console.error(`on Error ${msg} ${filename} ${lineno} ${colno}`); + } + } + + public static getInstance(): WorkerManager { + let workerManger: WorkerManager | undefined = AppStorage.get(Constants.APP_KEY_WORKER_MANAGER); + if (workerManger == undefined) { + workerManger = new WorkerManager; + AppStorage.setOrCreate(Constants.APP_KEY_WORKER_MANAGER, workerManger); + return workerManger; + } + return workerManger; + } + + public getWorker(): worker.ThreadWorker { + return this.cocosWorker; + } +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/entry/src/main/module.json5 b/tests/lua-tests/project/proj.ohos/entry/src/main/module.json5 new file mode 100644 index 000000000000..46cd3471c594 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/entry/src/main/module.json5 @@ -0,0 +1,70 @@ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:entry_desc", + "mainElement": "MainAbility", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "MainAbility", + "srcEntry": "./ets/MainAbility/MainAbility.ts", + "description": "$string:MainAbility_desc", + "icon": "$media:icon", + "label": "$string:MainAbility_label", + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:white", + "exported": true, + "orientation": "auto_rotation", + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ], + // https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/quick-start/module-configuration-file.md/ + "supportWindowMode": ["fullscreen"], + "maxWindowWidth": 1080, + "minWindowWidth": 1080, + "maxWindowHeight": 720, + "minWindowHeight": 720 + } + ], + "requestPermissions": [ + { + "name": "ohos.permission.INTERNET" + }, { + "name" : "ohos.permission.SET_NETWORK_INFO" + }, { + "name" : "ohos.permission.GET_NETWORK_INFO" + }, { + "name": "ohos.permission.GET_WIFI_INFO" + }, { + "name": "ohos.permission.ACCELEROMETER" + },{ + "name": "ohos.permission.VIBRATE" + } + ], + "metadata": [ + { + "name": "ArkTSPartialUpdate", + "value": "true" + }, + { + "name": "partialUpdateStrictCheck", + "value": "warn" + } + ] + } +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/entry/src/main/resources/base/element/color.json b/tests/lua-tests/project/proj.ohos/entry/src/main/resources/base/element/color.json new file mode 100644 index 000000000000..1bbc9aa9617e --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "white", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/entry/src/main/resources/base/element/string.json b/tests/lua-tests/project/proj.ohos/entry/src/main/resources/base/element/string.json new file mode 100644 index 000000000000..2a89ad8d40e7 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/entry/src/main/resources/base/element/string.json @@ -0,0 +1,20 @@ +{ + "string": [ + { + "name": "entry_desc", + "value": "3.7_lua_tests" + }, + { + "name": "MainAbility_label", + "value": "3.7_lua_tests" + }, + { + "name": "MainAbility_desc", + "value": "description" + }, + { + "name": "text_field_ttf_complete", + "value": "完成" + } + ] +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/entry/src/main/resources/base/media/icon.png b/tests/lua-tests/project/proj.ohos/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c GIT binary patch literal 6790 zcmX|G1ymHk)?T_}Vd;>R?p|tHQo6fg38|$UVM!6BLrPFWk?s;$LOP{GmJpBl$qoSA!PUg~PA65-S00{{S`XKG6NkG0RgjEntPrmV+?0|00mu7;+5 zrdpa{2QLqPJ4Y{j7=Mrl{BaxrkdY69+c~(w{Fv-v&aR%aEI&JYSeRTLWm!zbv;?)_ ziZB;fwGbbeL5Q}YLx`J$lp~A09KK8t_z}PZ=4ZzgdeKtgoc+o5EvN9A1K1_<>M?MBqb#!ASf&# zEX?<)!RH(7>1P+j=jqG(58}TVN-$psA6K}atCuI!KTJD&FMmH-78ZejBm)0qc{ESp z|LuG1{QnBUJRg_E=h1#XMWt2%fcoN@l7eAS!Es?Q+;XsRNPhiiE=@AqlLkJzF`O18 zbsbSmKN=aaq8k3NFYZfDWpKmM!coBU0(XnL8R{4=i|wi{!uWYM2je{U{B*K2PVdu&=E zTq*-XsEsJ$u5H4g6DIm2Y!DN`>^v|AqlwuCD;w45K0@eqauiqWf7l&o)+YLHm~|L~ z7$0v5mkobriU!H<@mVJHLlmQqzQ3d6Rh_-|%Yy2li*tHO>_vcnuZ7OR_xkAIuIU&x z-|8Y0wj|6|a6_I(v91y%k_kNw6pnkNdxjqG8!%Vz_d%c_!X+6-;1`GC9_FpjoHev5fEV7RhJ>r=mh-jp$fqbqRJ=obwdgLDVP5+s zy1=_DWG0Y-Jb3t^WXmkr(d9~08k-|#Ly zaNOmT(^9tIb&eb4%CzIT zAm3CUtWSr1t4?h1kk#NBi{U|pJslvME{q|_eS^3En>SOqSxyuN1x;Is@8~m?*>}** znrRFArP!K_52RpX*&JHMR<^lVdm8ypJ}0R(SD(51j;6@ni$6bQ+2XL+R^|NnSp5}(kzvMZ^(@4fD_{QVu$(&K6H|C37TG1Am9Re{<<3gd zh@`>;BqkXMW&p0T6rt|iB$)~CvFe(XC)F9WgAZn*0@t$oZo;!*}r@_`h?KKH&6A@3= zISXoQB+~`op>NP-buiA*^0n{@i{_?MRG)&k)c)k_F+-2Lud!S9pc+i`s74NpBCaGF zXN+pHkubw*msGBTY27BKHv)RRh3;nMg4&$fD_6X9Vt~;_4D+5XPH~#Kn-yjcy!$}1 zigv#FNY>TqMhtIBb@UoF!cE~Q8~;!Pek>SQQwHnHuWKoVBosAiOr}q>!>aE*Krc)V zBUMEcJ5NU0g8}-h6i1zpMY9>m4ne?=U2~`w7K7Q0gB_=p@$5K7p6}thw z-~3dMj?YNX2X$lZ+7ngQ$=s}3mizNN@kE%OtB)?c&i~2L55z8^=yz;xMHLmlY>&Q# zJj?!)M#q_SyfkQh)k?j8IfLtB)ZCp|*vf4_B zos?73yd^h-Ac+;?E4*bpf=o*^3x3-`TVjbY4n6!EN10K6o@fxdyps05Vo3PU)otB} z`3kR+2w7_C#8Z!q`J)p{Vh!+m9-UP!$STp+Hb}}#@#_u^SsUQg<}59< zTvH3%XS4G+6FF^(m6bVF&nSUIXcl;nw{=H$%fgeJ>CgDYiLdpDXr{;-AnG z8dvcrHYVMI&`R6;GWekI@Ir3!uo)oz4^{6q0m^}@f2tM9&=YHNi6-?rh0-{+k@cQm zdp`g#YdQn%MDVg2GR>wZ`n2<0l4)9nx1Wfr&!Dvz=bPwU!h2S?ez6MVc5APE4-xLB zi&W9Q8k2@0w!C53g?iAIQ}~p*3O(@zja6KQ=M3zfW*_6o5SwR-)6VBh~m7{^-=MC-owYH5-u40a}a0liho3QZZ5L{bS_xM1)4}19)zTU$$MY zq3eZML1WC{K%YFd`Be0M-rkO^l?h{kM{$2oK1*A@HVJ57*yhDkUF!2WZ&oA4Y-sK( zCY69%#`mBCi6>6uw(x4gbFaP0+FD*JKJ-q!F1E?vLJ+d35!I5d7@^eU?(CS|C^tmI5?lv@s{{*|1F zFg|OzNpZ0hxljdjaW%45O0MOttRrd(Z?h{HYbB-KFUx&9GfFL3b8NwZ$zNu)WbBD` zYkj$^UB5%3Pj1MDr>S2Ejr9pUcgA!;ZG!@{uAy12)vG=*^9-|dNQBc8&`oxBlU~#y zs!anJX&T?57Jdr^sb>e+V`MVfY>Y0ESg7MG<7W0g&bR-ZYzzZ%2H&Etcp zcd6QeXO1D!5A#zM0lx*GH}`M)2~ZFLE;sP^RSB5wVMNfiZXPd(cmO>j=OSA3`o5r& zna(|^jGXbdN7PK)U8b7^zYtYkkeb%<%F~=OqB~kXMQkq}ii|skh@WSRt>5za;cjP0 zZ~nD%6)wzedqE}BMLt~qKwlvTr33))#uP~xyw#*Eaa|DbMQ_%mG0U8numf8)0DX`r zRoG2bM;#g|p-8gWnwRV5SCW0tLjLO&9Z?K>FImeIxlGUgo0Zk`9Qzhj1eco~7XZy+hXc@YF&ZQ=? zn*^1O56yK^x{y}q`j7}blGCx%dydV!c7)g~tJzmHhV=W~jbWRRR{1<^oDK+1clprm zz$eCy7y9+?{E|YgkW~}}iB#I4XoJ*xr8R?i_Hv$=Cof5bo-Nj~f`-DLebH}&0% zfQj9@WGd4;N~Y?mzQsHJTJq6!Qzl^-vwol(+fMt#Pl=Wh#lI5Vmu@QM0=_r+1wHt` z+8WZ~c2}KQQ+q)~2Ki77QvV&`xb|xVcTms99&cD$Zz4+-^R4kvUBxG8gDk7Y`K*)JZ^2rL(+ZWV~%W(@6 z)0bPArG#BROa_PHs~&WplQ_UIrpd)1N1QGPfv!J(Z9jNT#i%H?CE6|pPZb9hJ1JW4 z^q;ft#!HRNV0YgPojzIYT`8LuET2rUe-J|c!9l4`^*;4WtY@Ew@pL>wkjmMgGfN7 ze}}GtmU0@<_#08~I-Suk=^*9GLW=H4xhsml;vAV{%hy5Eegl@!6qKqbG024%n2HHw zCc@ivW_$@5ZoHP70(7D+(`PvgjW1Pd`wsiuv-aCukMrafwDm)B!xXVy*j2opohhoU zcJz%ADmj>i3`-3-$7nQKBQQuGY;2Qt&+(L~C>vSGFj5{Mlv?T_^dql;{zkpe4R1}R z%XfZyQ}wr*sr>jrKgm*PWLjuVc%6&&`Kbf1SuFpHPN&>W)$GmqC;pIoBC`=4-hPY8 zT*>%I2fP}vGW;R=^!1be?ta2UQd2>alOFFbVl;(SQJ4Jk#)4Z0^wpWEVvY4=vyDk@ zqlModi@iVPMC+{?rm=4(n+<;|lmUO@UKYA>EPTS~AndtK^Wy^%#3<;(dQdk3WaUkRtzSMC9}7x2||CNpF#(3T4C)@ z$~RWs`BNABKX|{cmBt>Q=&gkXl&x!!NK_%5hW0LS)Z4PB>%sV?F-{Wyj#s7W%$F{D zXdK^Fp3wvy+48+GP6F_|^PCRx=ddcTO3sG;B23A49~Qaw31SZ0Rc~`r4qqt%#OGW{ zCA_(LG5^N>yzUn&kAgVmxb=EA8s&tBXC}S1CZ(KoW)(%^JjLTPo^fs`Va;`=YlVPgmB$!yB}<(4ym6OeZ3xAJJ#;)2+B%p3P1Wt+d$eo`vz`T zXfUP2))kBDPoscH;Jc7I3NU<({|@wM$&GaDt`n7WLgIY3IA7A6-_R?z8N3mz|}*i z(zl5ot--Oq@f2-nv{X(ujT2T(k1vY_qh93pK@>H-qc%2Xta)IP0Q%zt%bqYgI`o!wv!0QerB`nCN^1n|@$sVOQ!V0teVG!I z_fD%JvfDeT1cK#-{o6Gv7}& zY0#NWin~kVaf$aufV&;63Hbs|`QVZWpDX6IMk1Hj2G}fiH9e-^6u2zf^FIr^BwD<6zjw63+{yUe8PUFvk8v{sJ=R{d#`O!sz`Q13~< zPT$JS(w=yQfU2`zPCNfSw=&zup@DXc(98afjhv@1w_f!m2Z>rMJ19AB&dB%P#Ls3b z=lK7OILM+SQ&VEd=1GN6o&>YVVtIzoZ%=Z_SdqJN2}E43{bE`>w+A;=y->@^k{oCC z$F*WTY&?34;kfyFV?b*Xb1Pq`Z=%OgwEg)Rz)tx=`f%5#w_INP=x&z5!jI;#;N$ma zhO)+MDm;SxOEVL15; zGq(v2pL3&P1Sl)8P*;G-fd{l1QJsv@e@d8)1PK4w2m*M%V3j-V~L^$i|&C@b?D?9tfwE{B^}Z$k8e5FmQ>v7Xz)sG32g9t}YBt zyR$+*_00RmPx+0mW+vVG4mxd(n$(eQf3-w>JPl2UJpafrPaL5@2j}%{VE-) zBI%6Qpj*dsdH<;g!S!avA~bv^0E+ zfyJbSjPb+j;J52U)<|cIcntQBI2T#>2;tOxu{%D?kML476AErF(qN9hPva5Nkc@BF zC-tLF@3ZFb%Kpj)M<{)x*l|*Ia@ECeXo2E4h2f!aV=cHAhi_E_mfUth(sM4^hJq7B zQsGWqdZUm9S%F`$nQ*_#NcuD`&)Ek%_s{&^78{9Hm ztri&rYLOxgFdG>O@+XHy z9#;|&vBCPXH5Mon^I`jSuR$&~ZWtyB67ujzFSj!51>#C}C17~TffQ{c-!QFQkTQ%! zIR^b1`zHx|*1GU?tbBx23weFLz5H?y_Q%N&t$}k?w+``2A=aotj0;2v$~AL z{scF-cL{wsdrmPvf#a9OHyYLcwQD4Kcm)`LLwMh4WT~p29f7M!iafJSU`IV}QY5Wa z(n44-9oA}?J{a+ah*@31WTs#&J#o1`H98#6IQf;Wv0N_!);f&9g7o-k(lW5rWnDUR zQBFIRG+X=6NnsI@mxnwm;tf5;_Uxg?jZ8m-m0}&6+DA!qam(p$mN5R})yA_7m$q@| zFEd|dpS595rxQr-n#GjI5i-AhnUE>Cr;jpCqSrD~EwK_DqI^7%3#p5)%T_od!t3SOmH9MyXeeGO2(UQL;ax|x?Ncixmeo1=$ z{-);Au{*tfzOG?KQ~K|ak8-HQ?`Pekhe2WM(8s{xv-p>Zmu_6{G!-oE$7$mY`MOJorI=+mMx?H;`pr!;fVYz?5~yXBACruWB`Ph zZM}90_<^OBxIhyZ9BW$`>6JvO;%VFpqVr8|7t3~AmxYak6?`Pp#c;**_SYmi`&z23 z`p6_~ePvH)C6x-G9$hgL=eVALq`-AiamN>!3~Lxw&{H(b{B(7xSRm6<3<{%{yXiH# zos5Rv1L+8fUKJLo%P>4I&$}y { + if (item.viewTag == eventData.viewTag) { + removeIndex = index; + } + }); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_ARRAY).splice(removeIndex, 1); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP).delete(eventData.viewTag); + break; + } + case "setCurrentText": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.text = eventData.text; + break; + } + case "setEditBoxViewRect": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.x = eventData.viewRect.x; + textInputInfo.y = eventData.viewRect.y; + textInputInfo.w = eventData.viewRect.w; + textInputInfo.h = eventData.viewRect.h; + break; + } + case "setEditBoxVisible": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.visible = eventData.visible; + break; + } + case "setEditBoxFontSize": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.fontSize = eventData.fontSize; + break; + } + case "setEditBoxFontColor": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.fontColor = new FontColor(eventData.color.r, eventData.color.g, eventData.color.b, eventData.color.a / 255); + break; + } + case "setEditBoxPlaceHolderFontSize": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.placeholderFontSize = eventData.placeHolderSize; + break; + } + case "setEditBoxPlaceHolderFontColor": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.placeholderFontColor = new FontColor(eventData.placeHolderColor.r, eventData.placeHolderColor.g, eventData.placeHolderColor.b, eventData.placeHolderColor.a / 255); + break; + } + case "setEditBoxPlaceHolder": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.placeHolder = eventData.placeHolderText; + break; + } + case "setEditBoxMaxLength": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.maxLength = eventData.maxLength; + break; + } + case "setNativeInputMode": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.multiline = (eventData.inputMode == 0) ? true : false; + if (textInputInfo.inputMode != InputType.Password) { + switch (eventData.inputMode) { + case 0: + case 4: + case 6: + textInputInfo.inputMode = InputType.Normal; + break; + case 2: + case 5: + textInputInfo.inputMode = InputType.Number; + break; + case 3: + textInputInfo.inputMode = InputType.PhoneNumber; + break; + case 1: + textInputInfo.inputMode = InputType.Email; + break; + default: + break; + } + } + break; + } + case "setNativeInputFlag": { + // Any type can be changed to a password. The password can be changed to any type, + // but the password is mapped to the general type. Other changes are not allowed. + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + if (eventData.inputFlag == 0) { + textInputInfo.inputMode = InputType.Password; + } else if (textInputInfo.inputMode == InputType.Password) { + textInputInfo.inputMode = InputType.Normal; + } + break; + } + case "setEditBoxFontPath": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.fontPath = eventData.fontPath; + break; + } + case "setEditBoxPlaceHolderFontPath": { + let textInputInfo: TextInputInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_INDEX_MAP) + .get(eventData.viewTag); + textInputInfo.placeHolderFontPath = eventData.placeHolderFontPath; + break; + } + case "hideAllEditBox": { + let editBoxArray: TextInputInfo[] = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_EDIT_BOX_ARRAY); + editBoxArray.forEach(item => { + if (item.visible) { + item.visible = false; + let cocosWorker: worker.ThreadWorker = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_COCOS_WORKER); + cocosWorker.postMessage({ type: "editBoxOnEnter", viewTag: item.viewTag, text: item.text }) + } + }) + break; + } + } +} diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayer.ts b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayer.ts new file mode 100644 index 000000000000..50b7a7e69d00 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayer.ts @@ -0,0 +1,79 @@ +import { VideoPlayMsgEntity, ViewRect } from '../../entity/WorkerMsgEntity'; + +export class VideoPlayer { + static MODULE_NAME: string = 'VideoPlay'; + + private static workerPort; + + static init(workerPort) : void { + VideoPlayer.workerPort = workerPort; + } + + static setURL(viewTag: number, url: string, isUrl: number) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'setURL', viewTag); + videoPlayMsgEntity.url = url; + videoPlayMsgEntity.isUrl = isUrl; + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static setLooping(viewTag: number, isLoop: boolean) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'setLooping', viewTag); + videoPlayMsgEntity.isLoop = isLoop; + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static createVideoPlayer(viewTag: number) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'createVideoPlayer', viewTag); + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static removeVideoPlayer(viewTag: number) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'removeVideoPlayer', viewTag); + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static setVideoPlayerRect(viewTag: number, x: number, y: number, w: number, h: number) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'setVideoPlayerRect', viewTag); + let viewRect: ViewRect = new ViewRect(x, y, w, h); + videoPlayMsgEntity.viewRect = viewRect; + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static play(viewTag: number) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'play', viewTag); + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + static pause(viewTag: number) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'pause', viewTag); + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static stop(viewTag: number) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'stop', viewTag); + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static setVisible(viewTag: number, visible: boolean) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'setVisible', viewTag); + videoPlayMsgEntity.visible = visible + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static requestFullscreen(viewTag: number, isFullScreen: boolean) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'requestFullscreen', viewTag); + videoPlayMsgEntity.isFullScreen = isFullScreen; + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static seekTo(viewTag: number, seekTo: number) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'seekTo', viewTag); + videoPlayMsgEntity.seekTo = seekTo; + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } + + static setKeepAspectRatioEnabled(viewTag: number, enable: boolean) : void { + let videoPlayMsgEntity: VideoPlayMsgEntity = new VideoPlayMsgEntity(VideoPlayer.MODULE_NAME, 'setKeepAspectRatioEnabled', viewTag); + videoPlayMsgEntity.keepAspectRatioEnabled = enable; + VideoPlayer.workerPort.postMessage(videoPlayMsgEntity); + } +} diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayerMsg.ets b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayerMsg.ets new file mode 100644 index 000000000000..f11bdac82c9a --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/videoplayer/VideoPlayerMsg.ets @@ -0,0 +1,145 @@ +import Logger from '../../utils/Logger' +import { BusinessError } from '@ohos.base'; +import { GlobalContext,GlobalContextConstants } from "../../common/GlobalContext" +import { VideoPlayMsgEntity } from '../../entity/WorkerMsgEntity'; + +let log: Logger = new Logger(0x0001, "VideoPlayerMsg"); + +@Observed +export class VideoPlayerInfo { + // position + public x: number = 0; + public y: number = 0; + // size + public w: number = 0; + public h: number = 0; + // url + public url: string = '' + + public rawfile?: Resource; + public isUrl: boolean = false + // tag + public viewTag: number = 0 + + public isPlay: boolean = false + public isFullScreen: boolean = false + + // Whether to display + public visible: boolean = true + + public isLoop: boolean = false + + public objectFit: ImageFit = ImageFit.Auto + + public controller: VideoController = new VideoController() + + constructor(x: number, y: number, w: number, h: number, viewTag: number) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + this.viewTag = viewTag; + } +} + +export enum VideoEvent { + PLAYING = 0, + PAUSED, + STOPPED, + COMPLETED, +} + +export function handleVideoPlayMsg(eventData: VideoPlayMsgEntity) { + + switch (eventData.function) { + case "createVideoPlayer": { + let newVideoPlayerInfo = new VideoPlayerInfo(0, 0, 0, 0, eventData.viewTag); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_ARRAY).push(newVideoPlayerInfo); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).set(eventData.viewTag, newVideoPlayerInfo); + break; + } + case "removeVideoPlayer": { + let removeIndex = -1; + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_ARRAY).forEach((item:VideoPlayerInfo,index:number) => { + if (item.viewTag == eventData.viewTag) { + removeIndex = index; + } + }); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag).controller.requestFullscreen(false); //4.x已修复 + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_ARRAY).splice(removeIndex, 1); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).delete(eventData.viewTag); + break; + } + case "play": { + let videoPlayInfo :VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + videoPlayInfo.controller.start(); + videoPlayInfo.isPlay = true; + break; + } + case "pause": { + let videoPlayInfo:VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + videoPlayInfo.controller.pause(); + videoPlayInfo.isPlay = false; + break; + } + case "stop": { + let videoPlayInfo:VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + videoPlayInfo.controller.stop(); + videoPlayInfo.isPlay = false; + break; + } + case "setURL": { + let videoPlayInfo:VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + if(eventData.isUrl == 0) { + videoPlayInfo.isUrl = false; + videoPlayInfo.rawfile = $rawfile(eventData.url); + } else { + videoPlayInfo.isUrl = true; + videoPlayInfo.url = eventData.url; + } + break; + } + case "setLooping": { + let videoPlayInfo: VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + videoPlayInfo.isLoop = eventData.isLoop; + break; + } + case "setVideoPlayerRect": { + let videoPlayInfo:VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + try { + videoPlayInfo.x = px2vp(eventData.viewRect.x); + videoPlayInfo.y = px2vp(eventData.viewRect.y); + videoPlayInfo.w = px2vp(eventData.viewRect.w); + videoPlayInfo.h = px2vp(eventData.viewRect.h); + } catch (error) { + let e: BusinessError = error as BusinessError; + log.error('videoPlayerInfo ErrorCode: %{public}d, Message: %{public}s', e.code, e.message); + } + break; + } + case "setVisible": { + let videoPlayInfo:VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + if (videoPlayInfo.visible == eventData.visible) { + return; + } + videoPlayInfo.visible = eventData.visible; + break; + } + case "requestFullscreen": { + let videoPlayInfo: VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + videoPlayInfo.controller.requestFullscreen(eventData.isFullScreen); + videoPlayInfo.isFullScreen = eventData.isFullScreen; + break; + } + case "seekTo": { + let videoPlayInfo: VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + videoPlayInfo.controller.setCurrentTime(eventData.seekTo, SeekMode.Accurate); + break; + } + case "setKeepAspectRatioEnabled": { + let videoPlayInfo: VideoPlayerInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_VIDEO_PLAYER_INDEX_MAP).get(eventData.viewTag); + videoPlayInfo.objectFit = eventData.keepAspectRatioEnabled? ImageFit.Cover : ImageFit.Auto; + break; + } + } +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/webview/WebView.ts b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/webview/WebView.ts new file mode 100644 index 000000000000..872ec4ce70fe --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/webview/WebView.ts @@ -0,0 +1,114 @@ +import { ViewRect, WebViewMsgEntity } from '../../entity/WorkerMsgEntity'; + +export class WebView { + static MODULE_NAME: string = 'WebView'; + + private static workerPort; + + static init(workerPort) : void { + WebView.workerPort = workerPort; + } + + static createWebView(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'createWebView', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static removeWebView(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'removeWebView', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static setJavascriptInterfaceScheme(viewTag: number, jsInterfaceScheme: string) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'setJavascriptInterfaceScheme', viewTag); + webViewMsgEntity.jsInterfaceScheme = jsInterfaceScheme; + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static loadData(viewTag: number, data: string, mimeType: string, encoding: string, baseURL: string) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'loadData', viewTag); + webViewMsgEntity.data = data; + webViewMsgEntity.mimeType = mimeType; + webViewMsgEntity.encoding = encoding; + webViewMsgEntity.baseURL = baseURL; + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static loadURL(viewTag: number, url: string) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'loadURL', viewTag); + webViewMsgEntity.url = url; + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static loadFile(viewTag: number, filePath: string) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'loadFile', viewTag); + webViewMsgEntity.filePath = filePath; + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static stopLoading(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'stopLoading', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static reload(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'reload', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static canGoBack(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'canGoBack', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static canGoForward(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'canGoForward', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static goBack(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'goBack', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static goForward(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'goForward', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static setWebViewRect(viewTag: number, x: number, y: number, w: number, h: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'setWebViewRect', viewTag); + let viewRect: ViewRect = new ViewRect(x, y, w, h); + webViewMsgEntity.viewRect = viewRect; + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static setVisible(viewTag: number, visible: boolean) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'setVisible', viewTag); + webViewMsgEntity.visible = visible; + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static setOpacityWebView(viewTag: number, opacity: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'setOpacityWebView', viewTag); + webViewMsgEntity.opacity = opacity; + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static setBackgroundTransparent(viewTag: number) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'setBackgroundTransparent', viewTag); + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static evaluateJS(viewTag: number, js: string) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'evaluateJS', viewTag); + webViewMsgEntity.js = js; + WebView.workerPort.postMessage(webViewMsgEntity); + } + + static setScalesPageToFit(viewTag: number, scalesPageToFit: boolean) : void { + let webViewMsgEntity: WebViewMsgEntity = new WebViewMsgEntity(WebView.MODULE_NAME, 'setScalesPageToFit', viewTag); + webViewMsgEntity.scalesPageToFit = scalesPageToFit; + WebView.workerPort.postMessage(webViewMsgEntity); + } +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/webview/WebViewMsg.ets b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/webview/WebViewMsg.ets new file mode 100644 index 000000000000..de125a3331f5 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/components/webview/WebViewMsg.ets @@ -0,0 +1,246 @@ +import web_webview from '@ohos.web.webview' +import Logger from '../../utils/Logger'; +import { GlobalContext, GlobalContextConstants } from '../../common/GlobalContext'; +import { WebViewMsgEntity } from '../../entity/WorkerMsgEntity'; +import { BusinessError } from '@ohos.base'; + +let log: Logger = new Logger(0x0001, "WebViewMsg"); + +export class WebViewInfo { + public uniqueId: number = 0; + // position + public x: number = 0; + public y: number = 0; + // size + public w: number = 0; + public h: number = 0; + // url + public url: string = ''; + public isUrl: boolean = true; + public viewTag: number = 0 + public zoomAccess: boolean = true + public visible: boolean = true + public opacity: number = 1 + public backgroundColor: number = 0xffffff; + + public jsInterfaceScheme: string = ""; + + public controller: web_webview.WebviewController = new web_webview.WebviewController() + + constructor(x: number, y: number, w: number, h: number, viewTag: number) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + this.viewTag = viewTag + this.uniqueId = viewTag; + } +} + +function copyWebViewInfo(viewInfo: WebViewInfo): WebViewInfo { + let newViewInfo: WebViewInfo = new WebViewInfo(viewInfo.x, viewInfo.y, viewInfo.w, viewInfo.h, viewInfo.viewTag); + newViewInfo.url = viewInfo.url; + newViewInfo.isUrl = viewInfo.isUrl; + newViewInfo.zoomAccess = viewInfo.zoomAccess; + newViewInfo.visible = viewInfo.visible; + newViewInfo.controller = viewInfo.controller; + newViewInfo.opacity = viewInfo.opacity; + newViewInfo.backgroundColor = viewInfo.backgroundColor; + newViewInfo.jsInterfaceScheme = viewInfo.jsInterfaceScheme; + newViewInfo.uniqueId = 100000 - viewInfo.uniqueId; // support 50000 webView exist at the same time + return newViewInfo; +} + +function waitUtilControllerAttached(): Promise { + return new Promise((resolve, reject) => { + resolve(1); + }) +} + +export function handleWebViewMsg(eventData: WebViewMsgEntity) { + let index: number; + switch (eventData.function) { + case "createWebView": { + let view = new WebViewInfo(0, 0, 0, 0, eventData.viewTag); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY).push(view); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP) + .set(eventData.viewTag, GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY) + .length - 1); + break; + } + case "removeWebView": { + if (GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY).length > 0) { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY).splice(index, 1); + // Do not assume the invoking time of removeWebView. After an element is deleted, the following elements need to be advanced by one bit. + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP) + .forEach((value: number, key: number, map: Map) => { + if (value > index) { + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).set(key, value - 1); + } + }) + + // Delete the subscript mapping of the removed webView. + let tempInfoMap: Map = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP); + tempInfoMap.delete(eventData.viewTag); + } + break; + } + case "setJavascriptInterfaceScheme": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + let webViewInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + webViewInfo.jsInterfaceScheme = eventData.jsInterfaceScheme; + break; + } + case "loadData": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + let webViewInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + waitUtilControllerAttached().then(() => { + webViewInfo.controller.loadData(eventData.data, eventData.mimeType, eventData.encoding, eventData.baseURL) + }); + break; + } + case "loadURL": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index].url = eventData.url; + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index].isUrl = true; + let webViewInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + waitUtilControllerAttached().then(() => { + webViewInfo.controller.loadUrl(eventData.url); + }) + break; + } + case "loadFile": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index].url = eventData.filePath; + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index].isUrl = false; + let webViewInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + waitUtilControllerAttached().then(() => { + webViewInfo.controller.loadUrl($rawfile(eventData.filePath)); + }) + break; + } + case "stopLoading": + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index].controller.stop(); + break; + case "reload": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + let webViewInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + waitUtilControllerAttached().then(() => { + webViewInfo.controller.refresh(); + }) + break; + } + case "canGoBack": + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + let result: number = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index] + .controller + .accessBackward(); + // todo The value needs to be sent back to the worker thread. + break; + case "canGoForward": + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + result = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index] + .controller + .accessForward(); + // todo The value needs to be sent back to the worker thread. + break; + case "goBack": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + let webViewInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + waitUtilControllerAttached().then(() => { + webViewInfo.controller.backward(); + }) + break; + } + case "goForward": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + let webViewInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + waitUtilControllerAttached().then(() => { + webViewInfo.controller.forward(); + }) + break; + } + case "setWebViewRect": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + let tmpWebInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + tmpWebInfo.x = px2vp(eventData.viewRect.x); + tmpWebInfo.y = px2vp(eventData.viewRect.y); + tmpWebInfo.w = px2vp(eventData.viewRect.w); + tmpWebInfo.h = px2vp(eventData.viewRect.h); + let newViewInfo: WebViewInfo = copyWebViewInfo(tmpWebInfo); + let tempInfoArray: WebViewInfo[] = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY); + tempInfoArray[index] = newViewInfo; + break; + } + case "setVisible": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + if (GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index] + .visible == eventData.visible) { + return; + } + let tmpWebInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + tmpWebInfo.visible = eventData.visible; + let newViewInfo: WebViewInfo = copyWebViewInfo(tmpWebInfo); + let tempInfoArray: WebViewInfo[] = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY); + tempInfoArray[index] = newViewInfo; + break; + } + case "setOpacityWebView": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + if (GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index] + .opacity == eventData.opacity) { + return; + } + let tmpWebInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + tmpWebInfo.opacity = eventData.opacity; + let newViewInfo: WebViewInfo = copyWebViewInfo(tmpWebInfo); + let tempInfoArray: WebViewInfo[] = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY); + tempInfoArray[index] = newViewInfo; + break; + } + case "setBackgroundTransparent": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + if (GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index] + .backgroundColor == Color.Transparent) { + return; + } + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index] + .backgroundColor = Color.Transparent; + let newViewInfo: WebViewInfo = copyWebViewInfo(GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]); + let tempInfoArray: WebViewInfo[] = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY); + tempInfoArray[index] = newViewInfo; + break; + } + case "evaluateJS": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index] + .controller + .runJavaScript(eventData.js, (error: BusinessError, result: string) => { + if (error) { + log.warn("webView run JavaScript error:%{public}s", JSON.stringify(error)); + return; + } + if (result) { + GlobalContext.storeGlobalThis(GlobalContextConstants.COCOS2DX_WEB_RESULT, result) + log.debug("webView run JavaScript result is %{public}s:", result); + } + }) + break; + } + case "setScalesPageToFit": { + index = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_INDEX_MAP).get(eventData.viewTag); + if (GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index] + .zoomAccess == eventData.scalesPageToFit) { + return; + } + let tmpWebInfo: WebViewInfo = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY)[index]; + tmpWebInfo.zoomAccess == eventData.scalesPageToFit + let newViewInfo: WebViewInfo = copyWebViewInfo(tmpWebInfo); + let tempInfoArray: WebViewInfo[] = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_WEB_VIEW_ARRAY); + tempInfoArray[index] = newViewInfo; + } + break; + } +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/entity/Result.ts b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/entity/Result.ts new file mode 100644 index 000000000000..dd9e38897da3 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/entity/Result.ts @@ -0,0 +1,16 @@ +export class Result { + public static success(data){ + return { + "errCode": 0, + "errMsg": "", + "data": data, + }; + } + + public static error(errCode, errMsg) { + return { + "errCode": errCode, + "errMsg": errMsg, + }; + } +}; \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/entity/TextInputDialogEntity.ts b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/entity/TextInputDialogEntity.ts new file mode 100644 index 000000000000..39d272a0b68c --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/entity/TextInputDialogEntity.ts @@ -0,0 +1,7 @@ +export class TextInputDialogEntity { + message : string; + + constructor(msg: string) { + this.message = msg; + } +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/entity/WorkerMsgEntity.ts b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/entity/WorkerMsgEntity.ts new file mode 100644 index 000000000000..608158d25be9 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/entity/WorkerMsgEntity.ts @@ -0,0 +1,141 @@ +export class ViewRect { + x: number + y: number + w: number + h: number + + constructor(x: number, y: number, w: number, h: number) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + } +} + +export class Color4B { + r: number + g: number + b: number + a: number + + constructor(r: number, g: number, b: number, a: number) { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } +} + +export class BaseWorkerMsgEntity { + module: string; + + function: string; + + constructor(module: string, func: string) { + this.module = module; + this.function = func; + } +} + +export class DialogMsgEntity extends BaseWorkerMsgEntity { + title: string; + + message: string; + + constructor(module: string, func: string) { + super(module, func); + } +} + +export class EditBoxMsgEntity extends BaseWorkerMsgEntity { + viewTag: number + + viewRect: ViewRect + + paddingW: number + paddingH: number + + visible: boolean + + text: string + fontSize: number + color: Color4B + fontPath: string + + placeHolderText: string + placeHolderSize: number + placeHolderColor: Color4B + placeHolderFontPath: string + + maxLength: number + + inputMode: number + + inputFlag: number + + constructor(module: string, func: string, viewTag?: number) { + super(module, func); + this.viewTag = viewTag; + } +} + +export class VideoPlayMsgEntity extends BaseWorkerMsgEntity { + viewTag: number + + url: string + isUrl: number + + isLoop: boolean + + viewRect: ViewRect + + visible: boolean + + isFullScreen: boolean + + seekTo: number + + keepAspectRatioEnabled: boolean + + constructor(module: string, func: string, viewTag: number) { + super(module, func); + this.viewTag = viewTag; + } +} + +export class WebViewMsgEntity extends BaseWorkerMsgEntity { + viewTag: number + + data: string + mimeType: string + encoding: string + baseURL: string + + url: string + + filePath: string + + viewRect: ViewRect + + visible: boolean + + opacity: number + + js: string + + scalesPageToFit: boolean + jsInterfaceScheme: string + + constructor(module: string, func: string, viewTag: number) { + super(module, func); + this.viewTag = viewTag; + } +} + +export class JumpMsgEntity extends BaseWorkerMsgEntity { + url: string; + + constructor(module: string, func: string) { + super(module, func); + } +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/napi/NapiHelper.ts b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/napi/NapiHelper.ts new file mode 100644 index 000000000000..e70836332ec2 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/napi/NapiHelper.ts @@ -0,0 +1,119 @@ +import { Dialog } from '../components/dialog/DialogWorker' +import StringUtils from '../utils/StringUtils' +import { JumpManager } from '../system/appJump/JumpManager' +import { DeviceUtils } from '../system/device/DeviceUtils' +import { ApplicationManager } from '../system/application/ApplicationManager' +import { CocosEditBox } from '../components/editbox/CocosEditBox' +import { WebView } from '../components/webview/WebView' +import { VideoPlayer } from '../components/videoplayer/VideoPlayer' +import Accelerometer from '../system/sensor/AccelerometerUtils' +import Preferences from '../preferences/Preferences' + +export class NapiHelper { + + static registerFunctions(registerFunc : Function) { + NapiHelper.registerOthers(registerFunc); + NapiHelper.registerDeviceUtils(registerFunc); + NapiHelper.registerEditBox(registerFunc); + NapiHelper.registerWebView(registerFunc); + NapiHelper.registerVideoPlay(registerFunc); + NapiHelper.registerSensor(registerFunc); + NapiHelper.registerPreferences(registerFunc); + } + + private static registerOthers(registerFunc : Function) { + registerFunc('DiaLog.showDialog', Dialog.showDialog); + registerFunc('DiaLog.showTextInputDialog', Dialog.showTextInputDialog); + registerFunc('DiaLog.hideTextInputDialog', Dialog.hideTextInputDialog); + registerFunc('StringUtils.getWidth', StringUtils.getWidth); + registerFunc('ApplicationManager.exit', ApplicationManager.exit); + registerFunc('ApplicationManager.getVersionName', ApplicationManager.getVersionName); + registerFunc('JumpManager.openUrl', JumpManager.openUrl); + } + + + private static registerDeviceUtils(registerFunc : Function) { + registerFunc('DeviceUtils.getDpi', DeviceUtils.getDpi); + registerFunc('DeviceUtils.getSystemLanguage', DeviceUtils.getSystemLanguage); + registerFunc('DeviceUtils.startVibration', DeviceUtils.startVibration); + registerFunc('DeviceUtils.setKeepScreenOn', DeviceUtils.setKeepScreenOn); + registerFunc('DeviceUtils.isRoundScreen', DeviceUtils.isRoundScreen); + registerFunc('DeviceUtils.hasSoftKeys', DeviceUtils.hasSoftKeys); + registerFunc('DeviceUtils.isCutoutEnable', DeviceUtils.isCutoutEnable); + registerFunc('DeviceUtils.initScreenInfo', DeviceUtils.initScreenInfo); + registerFunc('DeviceUtils.getOrientation', DeviceUtils.getOrientation); + registerFunc('DeviceUtils.getCutoutHeight', DeviceUtils.getCutoutHeight); + registerFunc('DeviceUtils.getCutoutWidth', DeviceUtils.getCutoutWidth); + } + + private static registerEditBox(registerFunc : Function) { + registerFunc('CocosEditBox.createCocosEditBox', CocosEditBox.createCocosEditBox); + registerFunc('CocosEditBox.removeCocosEditBox', CocosEditBox.removeCocosEditBox); + registerFunc('CocosEditBox.setCurrentText', CocosEditBox.setCurrentText); + registerFunc('CocosEditBox.setEditBoxViewRect', CocosEditBox.setEditBoxViewRect); + registerFunc('CocosEditBox.setEditBoxVisible', CocosEditBox.setEditBoxVisible); + registerFunc('CocosEditBox.setEditBoxPlaceHolder', CocosEditBox.setEditBoxPlaceHolder); + registerFunc('CocosEditBox.setEditBoxFontSize', CocosEditBox.setEditBoxFontSize); + registerFunc('CocosEditBox.setEditBoxFontColor', CocosEditBox.setEditBoxFontColor); + registerFunc('CocosEditBox.setEditBoxFontPath', CocosEditBox.setEditBoxFontPath); + registerFunc('CocosEditBox.setEditBoxPlaceHolderFontSize', CocosEditBox.setEditBoxPlaceHolderFontSize); + registerFunc('CocosEditBox.setEditBoxPlaceHolderFontColor', CocosEditBox.setEditBoxPlaceHolderFontColor); + registerFunc('CocosEditBox.setEditBoxPlaceHolderFontPath', CocosEditBox.setEditBoxPlaceHolderFontPath); + registerFunc('CocosEditBox.setEditBoxMaxLength', CocosEditBox.setEditBoxMaxLength); + registerFunc('CocosEditBox.setNativeInputMode', CocosEditBox.setNativeInputMode); + registerFunc('CocosEditBox.setNativeInputFlag', CocosEditBox.setNativeInputFlag); + registerFunc('CocosEditBox.hideAllEditBox', CocosEditBox.hideAllEditBox); + } + + private static registerWebView(registerFunc : Function) { + registerFunc('WebView.createWebView', WebView.createWebView); + registerFunc('WebView.removeWebView', WebView.removeWebView); + registerFunc('WebView.setJavascriptInterfaceScheme', WebView.setJavascriptInterfaceScheme); + registerFunc('WebView.loadData', WebView.loadData); + registerFunc('WebView.loadURL', WebView.loadURL); + registerFunc('WebView.loadFile', WebView.loadFile); + registerFunc('WebView.stopLoading', WebView.stopLoading); + registerFunc('WebView.reload', WebView.reload); + registerFunc('WebView.canGoBack', WebView.canGoBack); + registerFunc('WebView.canGoForward', WebView.canGoForward); + registerFunc('WebView.goBack', WebView.goBack); + registerFunc('WebView.goForward', WebView.goForward); + registerFunc('WebView.setWebViewRect', WebView.setWebViewRect); + registerFunc('WebView.setVisible', WebView.setVisible); + registerFunc('WebView.setOpacityWebView', WebView.setOpacityWebView); + registerFunc('WebView.setBackgroundTransparent', WebView.setBackgroundTransparent); + registerFunc('WebView.evaluateJS', WebView.evaluateJS); + registerFunc('WebView.setScalesPageToFit', WebView.setScalesPageToFit); + } + + private static registerVideoPlay(registerFunc : Function) { + registerFunc('VideoPlayer.createVideoPlayer', VideoPlayer.createVideoPlayer); + registerFunc('VideoPlayer.removeVideoPlayer', VideoPlayer.removeVideoPlayer); + registerFunc('VideoPlayer.setURL', VideoPlayer.setURL); + registerFunc('VideoPlayer.setLooping', VideoPlayer.setLooping); + registerFunc('VideoPlayer.setVideoPlayerRect', VideoPlayer.setVideoPlayerRect); + registerFunc('VideoPlayer.play', VideoPlayer.play); + registerFunc('VideoPlayer.pause', VideoPlayer.pause); + registerFunc('VideoPlayer.stop', VideoPlayer.stop); + registerFunc('VideoPlayer.setVisible', VideoPlayer.setVisible); + registerFunc('VideoPlayer.requestFullscreen', VideoPlayer.requestFullscreen); + registerFunc('VideoPlayer.seekTo', VideoPlayer.seekTo); + registerFunc('VideoPlayer.setKeepAspectRatioEnabled', VideoPlayer.setKeepAspectRatioEnabled); + } + + private static registerSensor(registerFunc : Function) { + registerFunc('Accelerometer.enable', Accelerometer.enable); + registerFunc('Accelerometer.disable', Accelerometer.disable); + } + + private static registerPreferences(registerFunc : Function) { + registerFunc('Preferences.get', Preferences.get); + registerFunc('Preferences.getAll', Preferences.getAll); + registerFunc('Preferences.put', Preferences.put); + registerFunc('Preferences.has', Preferences.has); + registerFunc('Preferences.delete', Preferences.delete); + registerFunc('Preferences.flush', Preferences.flush); + registerFunc('Preferences.clear', Preferences.clear); + } + +} diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/preferences/Preferences.ts b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/preferences/Preferences.ts new file mode 100644 index 000000000000..e95e89b29d10 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/preferences/Preferences.ts @@ -0,0 +1,218 @@ +import Logger from '../utils/Logger'; +import data_preferences from '@ohos.data.preferences'; +import { BusinessError } from '@ohos.base'; +import common from '@ohos.app.ability.common'; +import { GlobalContext, GlobalContextConstants } from '../common/GlobalContext'; + +let log: Logger = new Logger(0x0001, "Preferences"); +let preferences: data_preferences.Preferences | null = null; +const PREFS_NAME: string = "Cocos2dxPreferences"; + +export default class Preferences { + + // 通过 preferencesName 获取Preferences实例 + static getPreferences(): data_preferences.Preferences { + let context: common.UIAbilityContext = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT); + try { + preferences = data_preferences.getPreferencesSync(context, {name: PREFS_NAME}); + log.info("Succeeded in getting preferences."); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to get preferences. code =" + code + ", message =" + message); + } + return preferences; + } + + /* + 通过 preferencesName 从缓存中移出指定的Preferences实例,若Preferences实例有对应的持久化文件,则同时删除其持久化文件。使用Promise异步回调。 + 调用该接口后,不建议再使用旧的Preferences实例进行数据操作,否则会出现数据一致性问题,应将Preferences实例置为null,系统将会统一回收。 + */ + static deletePreferences(): void { + let context: common.UIAbilityContext = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT); + try { + data_preferences.deletePreferences(context, PREFS_NAME).then(() => { + log.info("Succeeded in deleting preferences."); + }); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to delete preferences. code =" + code + ", message =" + message); + } + } + + /* + 通过 preferencesName 从缓存中移出指定的Preferences实例,使用Promise异步回调。 + 应用首次调用getPreferences接口获取某个Preferences实例后,该实例会被会被缓存起来,后续再次getPreferences时不会再次从持久化文件中读取, + 直接从缓存中获取Preferences实例。调用此接口移出缓存中的实例之后,再次getPreferences将会重新读取持久化文件,生成新的Preferences实例。 + 调用该接口后,不建议再使用旧的Preferences实例进行数据操作,否则会出现数据一致性问题,应将Preferences实例置为null,系统将会统一回收。 + */ + static removePreferencesFromCache(): void { + let context: common.UIAbilityContext = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT); + try { + data_preferences.removePreferencesFromCacheSync(context, PREFS_NAME); + log.info("Succeeded in removing preferences."); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to remove preferences. code =" + code + ", message =" + message); + } + } + + // 从缓存的Preferences实例中获取键对应的值,如果值为null或者非默认值类型,返回默认数据defValue + static get(key: string, defValue: data_preferences.ValueType): data_preferences.ValueType { + if (preferences === null) { + Preferences.getPreferences(); + } + try { + let data = preferences.getSync(key, defValue); + log.info("Succeeded in getting value of 'startup'. Data: " + data); + return data; + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to get value of 'startup'. code =" + code + ", message =" + message); + return defValue; + } + } + + // 将数据写入缓存的Preferences实例中,可通过flush将Preferences实例持久化 + static put(key: string, value: data_preferences.ValueType): void { + if (preferences === null) { + Preferences.getPreferences(); + } + try { + preferences.putSync(key, value); + log.info("Succeeded in put the key."); + Preferences.flush(); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to put. code =" + code + ", message =" + message); + } + } + + // 从缓存的Preferences实例中获取所有键值数据。 + static getAll(): string | undefined { + if (preferences === null) { + Preferences.getPreferences(); + } + try { + let object = preferences.getAllSync(); + let allKeys = getObjKeys(object); + log.info('getAll keys = ' + allKeys); + log.info("getAll object = " + JSON.stringify(object)); + return JSON.stringify(object); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to getAll. code =" + code + ", message =" + message); + return undefined; + } + } + + // 检查缓存的Preferences实例中是否包含名为给定Key的存储键值对 + static has(key: string): boolean { + if (preferences === null) { + Preferences.getPreferences(); + } + try { + let val = preferences.hasSync(key); + if (val) { + log.info("The key 'startup' is contained."); + return true; + } else { + log.info("The key 'startup' dose not contain."); + return false; + } + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to has. code =" + code + ", message =" + message); + return false; + } + } + + // 从缓存的Preferences实例中删除名为给定Key的存储键值对,可通过flush将Preferences实例持久化 + static delete(key: string): void { + if (preferences === null) { + Preferences.getPreferences(); + } + try { + preferences.deleteSync(key); + log.info("Succeeded in deleting the key."); + Preferences.flush(); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to delete. code =" + code + ", message =" + message); + } + } + + // 将缓存的Preferences实例中的数据异步存储到用户首选项的持久化文件中,使用Promise异步回调。 + static flush(): void { + if (preferences === null) { + Preferences.getPreferences(); + } + preferences.flush().then(()=>{ + log.info("Succeeded in flushing."); + }); + } + + // 清除缓存的Preferences实例中的所有数据,可通过flush将Preferences实例持久化,使用Promise异步回调。 + static clear(): void { + if (preferences === null) { + Preferences.getPreferences(); + } + try { + preferences.clearSync(); + log.info("Succeeded in clearing."); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to clear. code =" + code + ", message =" + message); + } + } + + + // 订阅数据变更,订阅的Key的值发生变更后,在执行flush方法后,触发callback回调。 + static onChange(cb: Function): void { + if (preferences === null) { + Preferences.getPreferences(); + } + try { + preferences.on('change', (key: string) => { + log.info("The key " + key + " changed."); + cb(key); + }); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to flush. code =" + code + ", message =" + message); + } + } + + // 取消订阅数据变更。 + static offChange(cb: Function): void { + if (preferences === null) { + Preferences.getPreferences(); + } + try { + preferences.off('change', (key: string) => { + log.info("The key " + key + " changed."); + cb(key); + }); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + log.error("Failed to flush. code =" + code + ", message =" + message); + } + } +} + +// 由于ArkTS中无Object.keys,且无法使用for..in... +// 若报ArkTS问题,请将此方法单独抽离至一个ts文件中并暴露,在需要用到的ets文件中引入使用 +function getObjKeys(obj: Object): string[] { + let keys = Object.keys(obj); + return keys; +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/appJump/JumpManager.ts b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/appJump/JumpManager.ts new file mode 100644 index 000000000000..dfa95dfaa5ab --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/appJump/JumpManager.ts @@ -0,0 +1,19 @@ +import type { ThreadWorkerGlobalScope } from '@ohos.worker'; +import { JumpMsgEntity } from '../../entity/WorkerMsgEntity'; + +export class JumpManager { + + static MODULE_NAME: string = 'JumpManager'; + + private static workerPort: ThreadWorkerGlobalScope; + + static init(workerPort: ThreadWorkerGlobalScope) : void { + JumpManager.workerPort = workerPort; + } + + static openUrl(url: string) : void { + let jumpMsgEntity: JumpMsgEntity = new JumpMsgEntity(JumpManager.MODULE_NAME, 'openUrl'); + jumpMsgEntity.url = url; + JumpManager.workerPort.postMessage(jumpMsgEntity); + } +} diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/appJump/JumpManagerMsg.ts b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/appJump/JumpManagerMsg.ts new file mode 100644 index 000000000000..ea373af90811 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/appJump/JumpManagerMsg.ts @@ -0,0 +1,31 @@ +import type common from '@ohos.app.ability.common'; +import { GlobalContext, GlobalContextConstants } from '../../common/GlobalContext'; +import {Result} from "../../entity/Result" +import type { JumpMsgEntity } from '../../entity/WorkerMsgEntity'; +import Logger from '../../utils/Logger' + +let log: Logger = new Logger(0x0001, "JumpManagerMsg"); + +export function handleJumpManagerMsg(eventData: JumpMsgEntity) : void { + switch (eventData.function) { + case "openUrl": + openUrl(eventData.url); + break; + default: + log.error('%{public}s has not implement yet', eventData.function); + } +} + +function openUrl(url: string): void { + let context: common.UIAbilityContext = GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT); + let wantInfo = { + 'action': 'ohos.want.action.viewData', + 'entities': ['entity.system.browsable'], + 'uri': url + } + context.startAbility(wantInfo).then(() => { + log.info('%{public}s', JSON.stringify(Result.success({}))); + }).catch((err) => { + log.error('openUrl : err : %{public}s', JSON.stringify(Result.error(-1, JSON.stringify(err))) ?? ''); + }); +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/application/ApplicationManager.ts b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/application/ApplicationManager.ts new file mode 100644 index 000000000000..ffe0826a5f7a --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/application/ApplicationManager.ts @@ -0,0 +1,55 @@ +import bundleManager from '@ohos.bundle.bundleManager'; +import type { ThreadWorkerGlobalScope } from '@ohos.worker'; +import { BaseWorkerMsgEntity } from '../../entity/WorkerMsgEntity'; +import { common } from '@kit.AbilityKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { GlobalContext, GlobalContextConstants } from '../../common/GlobalContext'; + +export class ApplicationManager { + static MODULE_NAME: string = 'ApplicationManager'; + + private static workerPort: ThreadWorkerGlobalScope; + + static init(workerPort: ThreadWorkerGlobalScope): void { + ApplicationManager.workerPort = workerPort; + } + + static exit(): void { + let workerMsg: BaseWorkerMsgEntity = new BaseWorkerMsgEntity(ApplicationManager.MODULE_NAME, 'exit'); + ApplicationManager.workerPort.postMessage(workerMsg); + } + + static getVersionName(): string { + let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT; + return bundleManager.getBundleInfoForSelfSync(bundleFlags).versionName; + } +} + +export function handleApplicationMsg(eventData: BaseWorkerMsgEntity): void { + switch (eventData.function) { + case "exit": + terminateSelf(); + break; + default: + console.error('%{public}s has not implement yet', eventData.function); + } +} + +function terminateSelf(): void { + try { + let context: common.UIAbilityContext = + GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT); + context.terminateSelf((err: BusinessError) => { + if (err.code) { + console.error(`terminateSelf failed, code is ${err.code}, message is ${err.message}`); + return; + } + console.info('terminateSelf succeed'); + }); + } catch (err) { + let code = (err as BusinessError).code; + let message = (err as BusinessError).message; + console.error(`terminateSelf failed, code is ${code}, message is ${message}`); + } +} + diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/device/DeviceUtils.ts b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/device/DeviceUtils.ts new file mode 100644 index 000000000000..b778f1584810 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/device/DeviceUtils.ts @@ -0,0 +1,164 @@ +import display from '@ohos.display' +import i18n from '@ohos.i18n'; +import vibrator from '@ohos.vibrator'; +import Logger from '../../utils/Logger'; +import window from '@ohos.window'; +import { GlobalContext, GlobalContextConstants } from '../../common/GlobalContext'; +import { Rect } from '@ohos.application.AccessibilityExtensionAbility'; + +let log = new Logger(0x0001, "DeviceUtils"); + +export class DeviceUtils { + static MODULE_NAME: string = 'DeviceUtils'; + static _roundScreen: boolean = false; + static _hasSoftKeys: boolean = false; + static _isCutoutEnable: boolean = false; + static _cutoutLeft: number; + static _cutoutWidth: number; + static _cutoutTop: number; + static _cutoutHeight: number; + + static getDpi(): number { + return display.getDefaultDisplaySync().densityDPI; + } + + static getSystemLanguage(): string { + return i18n.System.getSystemLanguage(); + } + + static startVibration(time: number) { + try { + vibrator.startVibration({ + type: 'time', + duration: time * 1000, // Seconds to milliseconds + }, { + id: 0, + usage: 'unknown' + }, (error) => { + if (error) { + log.error('vibrate fail, error.code: %{public}d, error.message: %{public}s', error.code, error.message); + return; + } + }); + } catch (err) { + log.error('error.code: %{public}d, error.message: %{public}s', err.code, err.message); + } + } + + static setKeepScreenOn(value: boolean) { + let windowClass = null; + try { + window.getLastWindow(GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT), (err, data) => { //获取窗口实例 + if (err.code) { + log.error('Failed to obtain last window when setKeepScreenOn. Cause:%{public}s', JSON.stringify(err)); + return; + } + windowClass = data; + // Sets whether the screen is always on. + let keepScreenOnPromise = windowClass.setWindowKeepScreenOn(value); + Promise.all([keepScreenOnPromise]).then(() => { + log.info('Succeeded in setKeepScreenOn, value:%{public}s', value); + }).catch((err) => { + log.error('Failed to setKeepScreenOn, cause:%{public}s', JSON.stringify(err)); + }); + }); + } catch (exception) { + log.error('Failed to get or set the window when setKeepScreenOn, cause:%{public}s', JSON.stringify(exception)); + } + } + + static isRoundScreen() : boolean { + return DeviceUtils._roundScreen; + } + + static hasSoftKeys() : boolean { + return DeviceUtils._hasSoftKeys; + } + + static isCutoutEnable() : boolean { + return DeviceUtils._isCutoutEnable; + } + + static initScreenInfo() : void { + let windowClass = null; + try { + window.getLastWindow(GlobalContext.loadGlobalThis(GlobalContextConstants.COCOS2DX_ABILITY_CONTEXT), (err, data) => { + if (err.code) { + log.error('Failed to obtain last window when initScreenInfo. Cause:%{public}s', JSON.stringify(err)); + return; + } + + windowClass = data; + let windowProperties: window.WindowProperties = windowClass.getWindowProperties(); + DeviceUtils._roundScreen = windowProperties.isRoundCorner; + let rect: Rect = windowProperties.windowRect; + if(rect.top + rect.height < display.getDefaultDisplaySync().height) { + DeviceUtils._hasSoftKeys = true; + } else { + DeviceUtils._hasSoftKeys = false; + } + }); + } catch (exception) { + log.error('Failed to get or set the window when initScreenInfo, cause:%{public}s', JSON.stringify(exception)); + } + + display.getDefaultDisplaySync().getCutoutInfo().then((data) => { + if(data.boundingRects.length == 0) { + DeviceUtils._isCutoutEnable = false; + return; + } + + DeviceUtils._isCutoutEnable = true; + DeviceUtils._cutoutLeft = data.boundingRects[0].left; + DeviceUtils._cutoutTop = data.boundingRects[0].top; + DeviceUtils._cutoutWidth = data.boundingRects[0].width; + DeviceUtils._cutoutHeight = data.boundingRects[0].height; + }).catch((err) => { + log.error('Failed to obtain all the display objects. Code: ' + JSON.stringify(err)); + }); + } + + static getOrientation() : number { + let orientation: display.Orientation = display.getDefaultDisplaySync().orientation; + + // If the system enumeration value changes, the processing logic in the C++ code needs to be changed. Therefore, the mapping is performed again. + if(orientation == display.Orientation.PORTRAIT) { + return 0; + } + + if(orientation == display.Orientation.LANDSCAPE) { + return 1; + } + + if(orientation == display.Orientation.PORTRAIT_INVERTED) { + return 2; + } + + if(orientation == display.Orientation.LANDSCAPE_INVERTED) { + return 3; + } + + return 4; + } + + static getCutoutHeight() : number { + if(DeviceUtils._cutoutHeight) { + let height = DeviceUtils._cutoutTop + DeviceUtils._cutoutHeight; + return height; + } + return 0; + } + + static getCutoutWidth() : number { + if(!DeviceUtils._cutoutWidth) { + return 0; + } + + let disPlayWidth = display.getDefaultDisplaySync().width; + if(DeviceUtils._cutoutLeft + DeviceUtils._cutoutWidth > disPlayWidth - DeviceUtils._cutoutLeft) { + return disPlayWidth - DeviceUtils._cutoutLeft; + } + + return DeviceUtils._cutoutLeft + DeviceUtils._cutoutWidth; + } +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/sensor/AccelerometerUtils.ts b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/sensor/AccelerometerUtils.ts new file mode 100644 index 000000000000..56309439f011 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/system/sensor/AccelerometerUtils.ts @@ -0,0 +1,55 @@ +import { getContext } from "libnativerender.so"; +import { ContextType } from "../../common/Constants" +import sensor from '@ohos.sensor'; +import display from '@ohos.display'; +import {Result} from "../../entity/Result" +import Logger from '../../utils/Logger' + +let log: Logger = new Logger(0x0001, "AccelerometerUtils"); + +const accUtils = getContext(ContextType.SENSOR_API); + +export default class Accelerometer { + + private static instance = new Accelerometer(); + + static getInstance() : Accelerometer { + return Accelerometer.instance; + } + + static enable(intervalTime: number) : void { + try { + /* HarmonyOS allow multiple subscriptions, but the game only need one + so if the interval changed, cancel subscription and redo with the new interval */ + sensor.off(sensor.SensorId.ACCELEROMETER); + sensor.on(sensor.SensorId.ACCELEROMETER, function (data) { + let rotation = display.getDefaultDisplaySync().rotation; + if (rotation === 0) { + // Display device screen rotation 0° + accUtils.onAccelerometerCallBack(data.x, data.y, data.z, intervalTime); + } else if (rotation === 1) { + // Display device screen rotation 90° + accUtils.onAccelerometerCallBack(data.y, -data.x, data.z, intervalTime); + } else if (rotation === 2) { + // Display device screen rotation 180° + accUtils.onAccelerometerCallBack(-data.x, -data.y, data.z, intervalTime); + } else if (rotation === 3) { + // Display device screen rotation 270° + accUtils.onAccelerometerCallBack(-data.y, data.x, data.z, intervalTime); + } else { + log.error('Accelerometer init fail, err: %{public}s', JSON.stringify(Result.error(-1, 'unsupported rotation: ' + rotation))); + } + }, { interval: intervalTime }); + } catch (err) { + log.error('Accelerometer init fail, err: %{public}s', JSON.stringify(Result.error(-1, JSON.stringify(err))) ?? ''); + } + } + + static disable() : void { + try { + sensor.off(sensor.SensorId.ACCELEROMETER); + } catch (err) { + log.error('Accelerometer off fail, err: %{public}s', JSON.stringify(Result.error(-1, JSON.stringify(err))) ?? ''); + } + } +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/utils/Logger.ts b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/utils/Logger.ts new file mode 100644 index 000000000000..6277b0e1ab52 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/utils/Logger.ts @@ -0,0 +1,27 @@ +import hilog from '@ohos.hilog' + +export default class Logger { + private domain: number + private prefix: string + + constructor(domain : number, prefix: string) { + this.domain = domain + this.prefix = prefix + } + + debug(format: string, ...args: any[]) { + hilog.debug(this.domain, this.prefix, format, args) + } + + info(format: string, ...args: any[]) { + hilog.info(this.domain, this.prefix, format, args) + } + + warn(format: string, ...args: any[]) { + hilog.warn(this.domain, this.prefix, format, args) + } + + error(format: string, ...args: any[]) { + hilog.error(this.domain, this.prefix, format, args) + } +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/utils/StringUtils.ts b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/utils/StringUtils.ts new file mode 100644 index 000000000000..4e3d20ec55e1 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/utils/StringUtils.ts @@ -0,0 +1,8 @@ +import measure from '@ohos.measure'; + +export default class StringUtils { + + public static getWidth(text: string, fontSize: number, fontWeight: number): number { + return measure.measureText({ textContent: text, fontSize: fontSize + 'px', fontWeight: fontWeight }); + } +} \ No newline at end of file diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/utils/WorkerMsgUtils.ets b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/utils/WorkerMsgUtils.ets new file mode 100644 index 000000000000..b9cac7106b18 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/ets/utils/WorkerMsgUtils.ets @@ -0,0 +1,42 @@ +import worker, { MessageEvents } from '@ohos.worker'; +import Logger from './Logger'; +import { handleEditBoxMsg } from '../components/editbox/EditBoxMsg' +import { handleWebViewMsg } from '../components/webview/WebViewMsg' +import { handleVideoPlayMsg } from '../components/videoplayer/VideoPlayerMsg' +import { handleDialogMsg } from '../components/dialog/DialogMsg' +import { handleJumpManagerMsg } from '../system/appJump/JumpManagerMsg' +import { handleApplicationMsg } from '../system/application/ApplicationManager' +import { BaseWorkerMsgEntity, DialogMsgEntity, EditBoxMsgEntity, JumpMsgEntity, VideoPlayMsgEntity, WebViewMsgEntity } from '../entity/WorkerMsgEntity'; + +export class WorkerMsgUtils { + static workPort = worker.workerPort; + static log : Logger = new Logger(0x0001, 'WorkerMsgUtils') + + static async recvWorkerThreadMessage(event: MessageEvents) { + let eventData: BaseWorkerMsgEntity = event.data; + WorkerMsgUtils.log.debug('mainThread receiveMsg, module:%{public}s, function:%{public}s', eventData.module, eventData.function); + + switch (eventData.module) { + case 'EditBox': + handleEditBoxMsg(eventData as EditBoxMsgEntity); + break; + case "Dialog": + handleDialogMsg(eventData as DialogMsgEntity); + break; + case 'WebView': + handleWebViewMsg(eventData as WebViewMsgEntity); + break; + case 'VideoPlay': + handleVideoPlayMsg(eventData as VideoPlayMsgEntity); + break; + case 'JumpManager': + handleJumpManagerMsg(eventData as JumpMsgEntity); + break; + case 'ApplicationManager': + handleApplicationMsg(eventData as BaseWorkerMsgEntity); + break; + default: + WorkerMsgUtils.log.error('%{public}s has not implement yet', eventData.module); + } + } +} diff --git a/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/module.json5 b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/module.json5 new file mode 100644 index 000000000000..daa1891126f5 --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/libSysCapabilities/src/main/module.json5 @@ -0,0 +1,10 @@ +{ + "module": { + "name": "libSysCapabilities", + "type": "har", + "deviceTypes": [ + "phone", + "tablet" + ] + } +} diff --git a/tests/lua-tests/project/proj.ohos/oh-package.json5 b/tests/lua-tests/project/proj.ohos/oh-package.json5 new file mode 100644 index 000000000000..1ebf93ed2afb --- /dev/null +++ b/tests/lua-tests/project/proj.ohos/oh-package.json5 @@ -0,0 +1,10 @@ +{ + "modelVersion": "5.0.0", + "license": "ISC", + "devDependencies": {}, + "name": "proj.ohos", + "description": "example description", + "repository": {}, + "version": "1.0.0", + "dependencies": {} +} \ No newline at end of file diff --git a/tests/lua-tests/src/AssetsManagerTest/AssetsManagerTest.lua b/tests/lua-tests/src/AssetsManagerTest/AssetsManagerTest.lua index 3ea12a07f408..15f618ddf5ea 100644 --- a/tests/lua-tests/src/AssetsManagerTest/AssetsManagerTest.lua +++ b/tests/lua-tests/src/AssetsManagerTest/AssetsManagerTest.lua @@ -17,7 +17,7 @@ local function updateLayer() local support = false if (cc.PLATFORM_OS_IPHONE == targetPlatform) or (cc.PLATFORM_OS_IPAD == targetPlatform) or (cc.PLATFORM_OS_WINDOWS == targetPlatform) or (cc.PLATFORM_OS_ANDROID == targetPlatform) - or (cc.PLATFORM_OS_MAC == targetPlatform) then + or (cc.PLATFORM_OS_MAC == targetPlatform) or (cc.PLATFORM_OS_OHOS == targetPlatform) then support = true end diff --git a/tests/lua-tests/src/CocosDenshionTest/CocosDenshionTest.lua b/tests/lua-tests/src/CocosDenshionTest/CocosDenshionTest.lua index 0d058f8be70c..70c3b04fde61 100644 --- a/tests/lua-tests/src/CocosDenshionTest/CocosDenshionTest.lua +++ b/tests/lua-tests/src/CocosDenshionTest/CocosDenshionTest.lua @@ -4,6 +4,8 @@ local MUSIC_FILE = nil local targetPlatform = cc.Application:getInstance():getTargetPlatform() if (cc.PLATFORM_OS_IPHONE == targetPlatform) or (cc.PLATFORM_OS_IPAD == targetPlatform) then MUSIC_FILE = "background.caf" +elseif (cc.PLATFORM_OS_OHOS == targetPlatform) then + MUSIC_FILE = "background.wav" else MUSIC_FILE = "background.mp3" end diff --git a/tests/lua-tests/src/NewAudioEngineTest/NewAudioEngineTest.lua b/tests/lua-tests/src/NewAudioEngineTest/NewAudioEngineTest.lua index b2d1fd0f71ab..ce02f807ff5c 100644 --- a/tests/lua-tests/src/NewAudioEngineTest/NewAudioEngineTest.lua +++ b/tests/lua-tests/src/NewAudioEngineTest/NewAudioEngineTest.lua @@ -455,6 +455,9 @@ function InvalidAudioFileTest.create() if (cc.PLATFORM_OS_ANDROID == targetPlatform) then ccexp.AudioEngine:play2d("background.caf") end + if (cc.PLATFORM_OS_OHOS == targetPlatform) then + ccexp.AudioEngine:play2d("background.wav") + end end local playItem1 = cc.MenuItemFont:create("play unsupported media type") diff --git a/tests/lua-tests/src/Particle3DTest/Particle3DTest.lua b/tests/lua-tests/src/Particle3DTest/Particle3DTest.lua index 8e6e3e89cdba..5dbf2a534cd1 100644 --- a/tests/lua-tests/src/Particle3DTest/Particle3DTest.lua +++ b/tests/lua-tests/src/Particle3DTest/Particle3DTest.lua @@ -8,7 +8,7 @@ local function baseInit(self) self._angle = 0 local targetPlatform = cc.Application:getInstance():getTargetPlatform() - if targetPlatform == cc.PLATFORM_OS_MAC or targetPlatform == cc.PLATFORM_OS_IPHONE or targetPlatform == cc.PLATFORM_OS_IPAD then + if targetPlatform == cc.PLATFORM_OS_MAC or targetPlatform == cc.PLATFORM_OS_IPHONE or targetPlatform == cc.PLATFORM_OS_IPAD or targetPlatform == cc.PLATFORM_OS_OHOS then cc.FileUtils:getInstance():addSearchPath("Particle3D/materials") cc.FileUtils:getInstance():addSearchPath("Particle3D/scripts") else diff --git a/tests/lua-tests/src/Sprite3DTest/Sprite3DTest.lua b/tests/lua-tests/src/Sprite3DTest/Sprite3DTest.lua index 082a0f3b2d30..a0574594826a 100644 --- a/tests/lua-tests/src/Sprite3DTest/Sprite3DTest.lua +++ b/tests/lua-tests/src/Sprite3DTest/Sprite3DTest.lua @@ -1110,7 +1110,7 @@ end function Sprite3DCubeMapTest:onExit() local targetPlatform = cc.Application:getInstance():getTargetPlatform() - if targetPlatform == cc.PLATFORM_OS_ANDROID or targetPlatform == cc.PLATFORM_OS_WINRT or targetPlatform == cc.PLATFORM_OS_WP8 then + if targetPlatform == cc.PLATFORM_OS_ANDROID or targetPlatform == cc.PLATFORM_OS_WINRT or targetPlatform == cc.PLATFORM_OS_WP8 or targetPlatform == cc.PLATFORM_OS_OHOS then cc.Director:getInstance():getEventDispatcher():removeEventListener(self._backToForegroundListener) end end @@ -1185,7 +1185,7 @@ function Sprite3DCubeMapTest:addNewSpriteWithCoords(pos) self:setCameraMask(2) local targetPlatform = cc.Application:getInstance():getTargetPlatform() - if targetPlatform == cc.PLATFORM_OS_ANDROID or targetPlatform == cc.PLATFORM_OS_WINRT or targetPlatform == cc.PLATFORM_OS_WP8 then + if targetPlatform == cc.PLATFORM_OS_ANDROID or targetPlatform == cc.PLATFORM_OS_WINRT or targetPlatform == cc.PLATFORM_OS_WP8 or targetPlatform == cc.PLATFORM_OS_OHOS then self._backToForegroundListener = cc.EventListenerCustom:create("event_renderer_recreated", function (eventCustom) local state = self._teapot:getGLProgramState() diff --git a/tests/lua-tests/src/controller.lua b/tests/lua-tests/src/controller.lua index 6c1f6bcb0ae2..8e0c62344400 100644 --- a/tests/lua-tests/src/controller.lua +++ b/tests/lua-tests/src/controller.lua @@ -1,18 +1,23 @@ +-- jit off +local jit = require("jit") +jit.off() -- avoid memory leak collectgarbage("setpause", 100) collectgarbage("setstepmul", 5000) ---------------- -- run -cc.FileUtils:getInstance():addSearchPath("src") +--cc.FileUtils:getInstance():addSearchPath("src") CC_USE_DEPRECATED_API = true require "cocos.init" local director = cc.Director:getInstance() local glView = director:getOpenGLView() +local widthx = 1024 +local heighty = 2112 if nil == glView then - glView = cc.GLViewImpl:createWithRect("Lua Tests", cc.rect(0,0,900,640)) + glView = cc.GLViewImpl:createWithRect("Lua Tests", cc.rect(0,0,widthx,heighty)) director:setOpenGLView(glView) end @@ -24,14 +29,14 @@ director:setAnimationInterval(1.0 / 60) local screenSize = glView:getFrameSize() -local designSize = {width = 480, height = 320} +local designSize = {width = widthx / 2, height = heighty / 2} if screenSize.height > 320 then - local resourceSize = {width = 960, height = 640} + local resourceSize = {width = widthx, height = heighty} cc.Director:getInstance():setContentScaleFactor(resourceSize.height/designSize.height) end -glView:setDesignResolutionSize(designSize.width, designSize.height, cc.ResolutionPolicy.FIXED_HEIGHT) +glView:setDesignResolutionSize(designSize.width, designSize.height, cc.ResolutionPolicy.SHOW_ALL) local fileUtils = cc.FileUtils:getInstance() local function addSearchPath(resPrefix, height) diff --git a/tests/lua-tests/src/mainMenu.lua b/tests/lua-tests/src/mainMenu.lua index fa3e41e09f15..6df2ab1aab43 100644 --- a/tests/lua-tests/src/mainMenu.lua +++ b/tests/lua-tests/src/mainMenu.lua @@ -11,7 +11,7 @@ require "AssetsManagerTest/AssetsManagerTest" require "AssetsManagerExTest/AssetsManagerExTest" require "BillBoardTest/BillBoardTest" require "BugsTest/BugsTest" -require "ByteCodeEncryptTest/ByteCodeEncryptTest" +---require "ByteCodeEncryptTest/ByteCodeEncryptTest" require "Camera3DTest/Camera3DTest" require "ClickAndMoveTest/ClickAndMoveTest" require "CocosDenshionTest/CocosDenshionTest" @@ -72,7 +72,7 @@ local BeginPos = {x = 0, y = 0} local audioEndineSupported = false local currPlatform = cc.Application:getInstance():getTargetPlatform() -if (cc.PLATFORM_OS_WINDOWS == currPlatform or cc.PLATFORM_OS_MAC == currPlatform or cc.PLATFORM_OS_IPHONE == currPlatform or cc.PLATFORM_OS_IPAD == currPlatform or cc.PLATFORM_OS_ANDROID == currPlatform) then +if (cc.PLATFORM_OS_WINDOWS == currPlatform or cc.PLATFORM_OS_MAC == currPlatform or cc.PLATFORM_OS_IPHONE == currPlatform or cc.PLATFORM_OS_IPAD == currPlatform or cc.PLATFORM_OS_ANDROID == currPlatform or cc.PLATFORM_OS_OHOS == currPlatform) then audioEndineSupported = true end @@ -89,7 +89,7 @@ local _allTests = { { isSupported = false, name = "Box2dTestBed" , create_func= Box2dTestBedMain }, { isSupported = true, name = "BillBoardTest" , create_func= BillBoardTestMain}, { isSupported = true, name = "BugsTest" , create_func= BugsTestMain }, - { isSupported = true, name = "ByteCodeEncryptTest" , create_func= ByteCodeEncryptTestMain }, + { isSupported = false, name = "ByteCodeEncryptTest" , create_func= ByteCodeEncryptTestMain }, { isSupported = true, name = "Camera3DTest" , create_func= Camera3DTestMain }, { isSupported = true, name = "CaptureScreenTest" , create_func = CaptureScreenTestMain }, { isSupported = false, name = "ChipmunkAccelTouchTest" , create_func= ChipmunkAccelTouchTestMain }, @@ -203,13 +203,13 @@ function CreateTestMenu() end if obj.name == "VideoPlayerTest" then - if cc.PLATFORM_OS_IPHONE ~= targetPlatform and cc.PLATFORM_OS_ANDROID ~= targetPlatform then + if cc.PLATFORM_OS_IPHONE ~= targetPlatform and cc.PLATFORM_OS_ANDROID ~= targetPlatform and cc.PLATFORM_OS_OHOS ~= targetPlatform then testMenuItem:setEnabled(false) end end if obj.name == "WebViewTest" then - if cc.PLATFORM_OS_IPHONE ~= targetPlatform and cc.PLATFORM_OS_ANDROID ~= targetPlatform then + if cc.PLATFORM_OS_IPHONE ~= targetPlatform and cc.PLATFORM_OS_ANDROID ~= targetPlatform and cc.PLATFORM_OS_OHOS ~= targetPlatform then testMenuItem:setEnabled(false) end end From 7cf6040ce6a1fcc5dfabcabcc212f4f52832408a Mon Sep 17 00:00:00 2001 From: xiqinggong <243795168@qq.com> Date: Thu, 19 Dec 2024 18:29:22 +0800 Subject: [PATCH 2/4] change "jit off" to mainMenu only ohos change "jit off" to mainMenu only ohos --- tests/lua-tests/src/controller.lua | 3 --- tests/lua-tests/src/mainMenu.lua | 9 ++++++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/lua-tests/src/controller.lua b/tests/lua-tests/src/controller.lua index 8e0c62344400..007de111fa83 100644 --- a/tests/lua-tests/src/controller.lua +++ b/tests/lua-tests/src/controller.lua @@ -1,7 +1,4 @@ --- jit off -local jit = require("jit") -jit.off() -- avoid memory leak collectgarbage("setpause", 100) collectgarbage("setstepmul", 5000) diff --git a/tests/lua-tests/src/mainMenu.lua b/tests/lua-tests/src/mainMenu.lua index 6df2ab1aab43..8f4eb81c676e 100644 --- a/tests/lua-tests/src/mainMenu.lua +++ b/tests/lua-tests/src/mainMenu.lua @@ -1,3 +1,11 @@ +local currPlatform = cc.Application:getInstance():getTargetPlatform() + +-- ohos jit off +if (cc.PLATFORM_OS_OHOS == currPlatform) then + local jit = require("jit") + jit.off() +end + require "helper" require "testResource" require "VisibleRect" @@ -71,7 +79,6 @@ local CurPos = {x = 0, y = 0} local BeginPos = {x = 0, y = 0} local audioEndineSupported = false -local currPlatform = cc.Application:getInstance():getTargetPlatform() if (cc.PLATFORM_OS_WINDOWS == currPlatform or cc.PLATFORM_OS_MAC == currPlatform or cc.PLATFORM_OS_IPHONE == currPlatform or cc.PLATFORM_OS_IPAD == currPlatform or cc.PLATFORM_OS_ANDROID == currPlatform or cc.PLATFORM_OS_OHOS == currPlatform) then audioEndineSupported = true end From eff2333ba24cdd4edbd2133f428063ad8ef449d5 Mon Sep 17 00:00:00 2001 From: "@wanghui187" <348582973@qq.com> Date: Sat, 28 Dec 2024 11:30:32 +0800 Subject: [PATCH 3/4] 3.7 version OHOS adaptation Signed-off-by: @wanghui187 <348582973@qq.com> --- .../auto/lua_cocos2dx_audioengine_auto.hpp | 2 +- .../manual/ui/lua_cocos2dx_ui_manual.cpp | 2 +- tests/lua-tests/project/Classes/AppDelegate.cpp | 8 +++++--- tests/lua-tests/src/controller.lua | 12 ++++++++++-- tests/lua-tests/src/mainMenu.lua | 13 ++++--------- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_audioengine_auto.hpp b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_audioengine_auto.hpp index 30ddb41dbd52..5e58895b7e17 100644 --- a/cocos/scripting/lua-bindings/auto/lua_cocos2dx_audioengine_auto.hpp +++ b/cocos/scripting/lua-bindings/auto/lua_cocos2dx_audioengine_auto.hpp @@ -40,4 +40,4 @@ int register_all_cocos2dx_audioengine(lua_State* tolua_S); #endif // __cocos2dx_audioengine_h__ -#endif //#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 +#endif //#if CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 diff --git a/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_ui_manual.cpp b/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_ui_manual.cpp index ef118822bc81..c5369f23b1cc 100644 --- a/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_ui_manual.cpp +++ b/cocos/scripting/lua-bindings/manual/ui/lua_cocos2dx_ui_manual.cpp @@ -1044,7 +1044,7 @@ int register_ui_moudle(lua_State* L) { register_all_cocos2dx_ui(L); register_all_cocos2dx_ui_manual(L); -#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) register_all_cocos2dx_experimental_video(L); register_all_cocos2dx_experimental_video_manual(L); register_all_cocos2dx_experimental_webview(L); diff --git a/tests/lua-tests/project/Classes/AppDelegate.cpp b/tests/lua-tests/project/Classes/AppDelegate.cpp index 59a94a9ef77c..0dc642c654f6 100644 --- a/tests/lua-tests/project/Classes/AppDelegate.cpp +++ b/tests/lua-tests/project/Classes/AppDelegate.cpp @@ -50,9 +50,11 @@ bool AppDelegate::applicationDidFinishLaunching() } lua_pop(L, 1); - - FileUtils::getInstance()->addSearchPath(""); - pEngine->executeScriptFile("controller.lua"); + #if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + pEngine->executeScriptFile("controller.lua"); + #else + pEngine->executeScriptFile("src/controller.lua"); + #endif return true; } diff --git a/tests/lua-tests/src/controller.lua b/tests/lua-tests/src/controller.lua index 007de111fa83..fd6bf035dcde 100644 --- a/tests/lua-tests/src/controller.lua +++ b/tests/lua-tests/src/controller.lua @@ -1,11 +1,19 @@ - +local currPlatform = cc.Application:getInstance():getTargetPlatform() +cc.PLATFORM_OS_OHOS=12 +if (cc.PLATFORM_OS_OHOS == currPlatform) then + local jit = require("jit") + jit.off() + print("jit.off()") +end -- avoid memory leak collectgarbage("setpause", 100) collectgarbage("setstepmul", 5000) ---------------- -- run ---cc.FileUtils:getInstance():addSearchPath("src") +if (cc.PLATFORM_OS_OHOS ~= currPlatform) then + cc.FileUtils:getInstance():addSearchPath("src") +end CC_USE_DEPRECATED_API = true require "cocos.init" diff --git a/tests/lua-tests/src/mainMenu.lua b/tests/lua-tests/src/mainMenu.lua index 8f4eb81c676e..afe942ea247f 100644 --- a/tests/lua-tests/src/mainMenu.lua +++ b/tests/lua-tests/src/mainMenu.lua @@ -1,11 +1,3 @@ -local currPlatform = cc.Application:getInstance():getTargetPlatform() - --- ohos jit off -if (cc.PLATFORM_OS_OHOS == currPlatform) then - local jit = require("jit") - jit.off() -end - require "helper" require "testResource" require "VisibleRect" @@ -19,7 +11,10 @@ require "AssetsManagerTest/AssetsManagerTest" require "AssetsManagerExTest/AssetsManagerExTest" require "BillBoardTest/BillBoardTest" require "BugsTest/BugsTest" ----require "ByteCodeEncryptTest/ByteCodeEncryptTest" +local currPlatform = cc.Application:getInstance():getTargetPlatform() +if (cc.PLATFORM_OS_OHOS ~= currPlatform) then +require "ByteCodeEncryptTest/ByteCodeEncryptTest" +end require "Camera3DTest/Camera3DTest" require "ClickAndMoveTest/ClickAndMoveTest" require "CocosDenshionTest/CocosDenshionTest" From f093b058ae05ef69c87366e8a1a6a01f14dc45cd Mon Sep 17 00:00:00 2001 From: "@wanghui187" <348582973@qq.com> Date: Mon, 30 Dec 2024 15:11:26 +0800 Subject: [PATCH 4/4] 3.7 version OHOS adaptation Signed-off-by: @wanghui187 <348582973@qq.com> --- tests/lua-tests/project/Classes/AppDelegate.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/lua-tests/project/Classes/AppDelegate.cpp b/tests/lua-tests/project/Classes/AppDelegate.cpp index 0dc642c654f6..cf2e92b9a48e 100644 --- a/tests/lua-tests/project/Classes/AppDelegate.cpp +++ b/tests/lua-tests/project/Classes/AppDelegate.cpp @@ -50,11 +50,11 @@ bool AppDelegate::applicationDidFinishLaunching() } lua_pop(L, 1); - #if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) - pEngine->executeScriptFile("controller.lua"); - #else - pEngine->executeScriptFile("src/controller.lua"); - #endif +#if (CC_TARGET_PLATFORM == CC_PLATFORM_OHOS) + pEngine->executeScriptFile("controller.lua"); +#else + pEngine->executeScriptFile("src/controller.lua"); +#endif return true; }