pax_global_header00006660000000000000000000000064141774643250014527gustar00rootroot0000000000000052 comment=c64ec84fae7a5aef063ea6565b167525f89b93ba obs-move-transition-2.5.7/000077500000000000000000000000001417746432500154615ustar00rootroot00000000000000obs-move-transition-2.5.7/.github/000077500000000000000000000000001417746432500170215ustar00rootroot00000000000000obs-move-transition-2.5.7/.github/FUNDING.yml000066400000000000000000000001111417746432500206270ustar00rootroot00000000000000github: exeldro custom: "https://www.paypal.me/exeldro" patreon: Exeldro obs-move-transition-2.5.7/.github/workflows/000077500000000000000000000000001417746432500210565ustar00rootroot00000000000000obs-move-transition-2.5.7/.github/workflows/build.yml000066400000000000000000000322421417746432500227030ustar00rootroot00000000000000name: build obs plugin on: push: branches: [ master ] pull_request: branches: [ master ] env: PLUGIN_NAME: move-transition OBS_VERSION: 27.0.0 jobs: macos64: name: "macOS 64-bit" runs-on: [macos-latest] env: QT_VERSION: '5.15.2' MACOS_DEPS_VERSION: '2021-02-28' steps: - name: Checkout uses: actions/checkout@v2.3.3 with: repository: obsproject/obs-studio ref: ${{ env.OBS_VERSION }} submodules: 'recursive' - name: "Checkout plugin" uses: actions/checkout@v2.3.3 with: path: plugins/${{ env.PLUGIN_NAME }} - name: Fetch Git Tags run: | cd plugins/${{ env.PLUGIN_NAME }} git fetch --prune --tags --unshallow - name: 'Install prerequisites (Homebrew)' shell: bash run: | if [ -d /usr/local/opt/openssl@1.0.2t ]; then brew uninstall openssl@1.0.2t brew untap local/openssl fi if [ -d /usr/local/opt/python@2.7.17 ]; then brew uninstall python@2.7.17 brew untap local/python2 fi brew bundle --file ./CI/scripts/macos/Brewfile - name: 'Install prerequisite: Pre-built dependencies' if: steps.deps-cache.outputs.cache-hit != 'true' shell: bash run: | curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/macos-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz tar -xf ./macos-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz -C "/tmp" - name: 'Install prerequisite: Pre-built dependency Qt' if: steps.deps-qt-cache.outputs.cache-hit != 'true' shell: bash run: | curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/macos-qt-${{ env.QT_VERSION }}-${{ env.MACOS_DEPS_VERSION }}.tar.gz tar -xf ./macos-qt-${{ env.QT_VERSION }}-${{ env.MACOS_DEPS_VERSION }}.tar.gz -C "/tmp" xattr -r -d com.apple.quarantine /tmp/obsdeps - name: Configure shell: bash run: | echo "add_subdirectory(${{ env.PLUGIN_NAME }})" >> plugins/CMakeLists.txt mkdir ./build cd ./build cmake -DDISABLE_PYTHON=ON -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 -DQTDIR="/tmp/obsdeps" -DSWIGDIR="/tmp/obsdeps" -DDepsPath="/tmp/obsdeps" -DBUILD_BROWSER=OFF -DENABLE_PIPEWIRE=OFF -DBUILD_VST=OFF -DBUILD_VIRTUALCAM=OFF -DENABLE_SCRIPTING=OFF .. cd - - name: Build shell: bash run: | set -e cd ./build make -j4 cd - - name: 'Install prerequisite: Packages app' if: success() shell: bash run: | curl -L -O http://s.sudre.free.fr/Software/files/Packages.dmg sudo hdiutil attach ./Packages.dmg sudo installer -pkg /Volumes/Packages\ 1.2.9/Install\ Packages.pkg -target / - name: Package if: success() shell: bash run: | cd plugins/${{ env.PLUGIN_NAME }} FILE_DATE=$(date +%Y-%m-%d) FILE_NAME=${{ env.PLUGIN_NAME }}-$FILE_DATE-${{ github.sha }}-macos.pkg echo "FILE_NAME=${FILE_NAME}" >> $GITHUB_ENV packagesbuild ./CI/macos/${{ env.PLUGIN_NAME }}.pkgproj cd - mkdir ./nightly mv plugins/${{ env.PLUGIN_NAME }}/${{ env.PLUGIN_NAME }}.pkg ./nightly/${FILE_NAME} - name: Publish if: success() uses: actions/upload-artifact@v2.2.0 with: name: '${{ env.FILE_NAME }}' path: ./nightly/*.pkg ubuntu64: name: 'Linux/Ubuntu 64-bit' runs-on: [ubuntu-latest] steps: - name: Checkout uses: actions/checkout@v2.3.3 with: repository: obsproject/obs-studio ref: ${{ env.OBS_VERSION }} submodules: 'recursive' - name: "Checkout plugin" uses: actions/checkout@v2.3.3 with: path: plugins/${{ env.PLUGIN_NAME }} - name: Add plugin to obs cmake shell: bash run: echo "add_subdirectory(${{ env.PLUGIN_NAME }})" >> plugins/CMakeLists.txt - name: Fetch Git Tags run: git fetch --prune --tags --unshallow - name: Install prerequisites (Apt) shell: bash run: | sudo dpkg --add-architecture amd64 sudo apt-get -qq update sudo apt-get install -y \ build-essential \ checkinstall \ cmake \ libasound2-dev \ libavcodec-dev \ libavdevice-dev \ libavfilter-dev \ libavformat-dev \ libavutil-dev \ libcurl4-openssl-dev \ libfdk-aac-dev \ libfontconfig-dev \ libfreetype6-dev \ libgl1-mesa-dev \ libjack-jackd2-dev \ libjansson-dev \ libluajit-5.1-dev \ libpulse-dev \ libqt5x11extras5-dev \ libsndio-dev \ libspeexdsp-dev \ libswresample-dev \ libswscale-dev \ libudev-dev \ libv4l-dev \ libva-dev \ libvlc-dev \ libx11-dev \ libx11-xcb-dev \ libx264-dev \ libxcb-randr0-dev \ libxcb-shm0-dev \ libxcb-xfixes0-dev \ libxcb-xinerama0-dev \ libxcomposite-dev \ libxinerama-dev \ libmbedtls-dev \ pkg-config \ python3-dev \ qtbase5-dev \ qtbase5-private-dev \ libqt5svg5-dev \ swig \ linux-generic - name: 'Configure' shell: bash run: | mkdir ./build cd ./build cmake -DUNIX_STRUCTURE=0 -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/obs-studio-portable" -DBUILD_CAPTIONS=OFF -DWITH_RTMPS=OFF -DBUILD_BROWSER=OFF -DBUILD_VIRTUALCAM=OFF -DBUILD_VST=OFF -DENABLE_PIPEWIRE=OFF -DENABLE_SCRIPTING=OFF .. - name: 'Build' shell: bash working-directory: ${{ github.workspace }}/build run: make -j4 - name: 'Package' shell: bash run: | FILE_DATE=$(date +%Y-%m-%d) FILE_NAME=${{ env.PLUGIN_NAME }}-$FILE_DATE-${{ github.sha }}-linux64.tar.gz echo "FILE_NAME=${FILE_NAME}" >> $GITHUB_ENV mkdir -p ./${{ env.PLUGIN_NAME }}/bin/64bit/ mv ./build/plugins/${{ env.PLUGIN_NAME }}/${{ env.PLUGIN_NAME }}.so ./${{ env.PLUGIN_NAME }}/bin/64bit/${{ env.PLUGIN_NAME }}.so mv ./plugins/${{ env.PLUGIN_NAME }}/data ./${{ env.PLUGIN_NAME }}/data tar -cvzf "${FILE_NAME}" ${{ env.PLUGIN_NAME }} - name: 'Publish' uses: actions/upload-artifact@v2.2.0 with: name: '${{ env.FILE_NAME }}' path: '*.tar.gz' windows: name: Windows runs-on: [windows-latest] env: QT_VERSION: '5.15.2' CMAKE_GENERATOR: "Visual Studio 17 2022" CMAKE_SYSTEM_VERSION: "10.0.18363.657" WINDOWS_DEPS_VERSION: '2019' steps: - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v1.0.2 - name: Checkout obs uses: actions/checkout@v2.3.3 with: repository: obsproject/obs-studio ref: ${{ env.OBS_VERSION }} submodules: 'recursive' - name: Checkout plugin uses: actions/checkout@v2.3.3 with: path: plugins/${{ env.PLUGIN_NAME}} - name: Add plugin to obs cmake shell: cmd run: echo add_subdirectory(${{ env.PLUGIN_NAME }}) >> plugins/CMakeLists.txt - name: Fetch Git Tags run: git fetch --prune --tags --unshallow - name: 'Restore QT dependency from cache' id: qt-cache uses: actions/cache@v2.1.2 env: CACHE_NAME: 'qt-cache' with: path: ${{ github.workspace }}/cmbuild/QT key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.QT_VERSION }} - name: 'Restore pre-built dependencies from cache' id: deps-cache uses: actions/cache@v2.1.2 env: CACHE_NAME: 'deps-cache' with: path: ${{ github.workspace }}/cmbuild/deps key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.WINDOWS_DEPS_VERSION }} - name: 'Install prerequisite: QT' if: steps.qt-cache.outputs.cache-hit != 'true' run: | curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_${{ env.QT_VERSION }}.7z -f --retry 5 -C - 7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}/cmbuild/QT" - name: 'Install prerequisite: Pre-built dependencies' if: steps.deps-cache.outputs.cache-hit != 'true' run: | curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -f --retry 5 -C - 7z x dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}/cmbuild/deps" - name: Configure run: | mkdir ./package mkdir ./build32 cd ./build32 cmake -G"${{ env.CMAKE_GENERATOR }}" -A"Win32" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DBUILD_BROWSER=false -DBUILD_VST=false -DBUILD_VIRTUALCAM=false -DBUILD_CAPTIONS=false -DCOMPILE_D3D12_HOOK=false -DENABLE_SCRIPTING=false -DDepsPath="${{ github.workspace }}/cmbuild/deps/win32" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2019" -DCOPIED_DEPENDENCIES=FALSE -DCOPY_DEPENDENCIES=TRUE .. cd .. mkdir ./build64 cd ./build64 cmake -G"${{ env.CMAKE_GENERATOR }}" -A"x64" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DBUILD_BROWSER=false -DBUILD_VST=false -DBUILD_VIRTUALCAM=false -DBUILD_CAPTIONS=false -DCOMPILE_D3D12_HOOK=false -DENABLE_SCRIPTING=false -DDepsPath="${{ github.workspace }}/cmbuild/deps/win64" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2019_64" -DCOPIED_DEPENDENCIES=FALSE -DCOPY_DEPENDENCIES=TRUE .. - name: 'Build 32' run: msbuild /m /p:Configuration=RelWithDebInfo .\build32\obs-studio.sln - name: 'Build 64' run: msbuild /m /p:Configuration=RelWithDebInfo .\build64\obs-studio.sln - name: Package if: success() run: | $env:FILE_DATE=(Get-Date -UFormat "%F") $env:FILE_NAME="${{ env.PLUGIN_NAME }}-${env:FILE_DATE}-${{ github.sha }}-windows" echo "FILE_NAME=${env:FILE_NAME}" >> ${env:GITHUB_ENV} robocopy .\build32\rundir\RelWithDebInfo\obs-plugins\32bit\ .\package\obs-plugins\32bit ${{ env.PLUGIN_NAME }}.* /E /XF .gitignore robocopy .\build64\rundir\RelWithDebInfo\obs-plugins\64bit\ .\package\obs-plugins\64bit ${{ env.PLUGIN_NAME }}.* /E /XF .gitignore robocopy .\build64\rundir\RelWithDebInfo\data\obs-plugins\${{ env.PLUGIN_NAME }}\ .\package\data\obs-plugins\${{ env.PLUGIN_NAME }}\ /E /XF .gitignore exit 0 - name: Create Code Signing Certificate if: success() && github.event_name != 'pull_request' run: | New-Item -ItemType directory -Path certificate Set-Content -Path certificate\certificate.txt -Value '${{ secrets.CERTIFICATE }}' certutil -decode certificate\certificate.txt certificate\certificate.pfx - name: Code Sign 32 if: success() && github.event_name != 'pull_request' run: | & 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.20348.0/x86/signtool.exe' sign /fd SHA256 /f certificate\certificate.pfx /p '${{ secrets.CERTIFICATE_PASS }}' /t http://timestamp.comodoca.com/authenticode .\package\obs-plugins\32bit\${{ env.PLUGIN_NAME }}.dll - name: Code Sign 64 if: success() && github.event_name != 'pull_request' run: | & 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.20348.0/x64/signtool.exe' sign /fd SHA256 /f certificate\certificate.pfx /p '${{ secrets.CERTIFICATE_PASS }}' /t http://timestamp.comodoca.com/authenticode .\package\obs-plugins\64bit\${{ env.PLUGIN_NAME }}.dll - name: Publish zip if: success() uses: actions/upload-artifact@v2.2.0 with: name: '${{ env.FILE_NAME }}' path: package/* - name: "Package Installer (Prereqs)" run: | curl "-kL" "https://github.com/Xaymar/msvc-redist-helper/releases/download/0.1/msvc-redist-helper-64.exe" "-f" "--retry" "5" "-o" "msvc-redist-helper.exe" curl "-kL" "https://files.jrsoftware.org/is/6/innosetup-6.0.3.exe" "-f" "--retry" "5" "-o" "inno.exe" .\inno.exe /VERYSILENT /SP- /SUPPRESSMSGBOXES /NORESTART - name: "Package Installer (Compile)" run: | & 'C:\Program Files (x86)\Inno Setup 6\ISCC.exe' /Qp ".\build64\plugins\${{ env.PLUGIN_NAME }}\installer.iss" - name: Code Sign Installer if: success() && github.event_name != 'pull_request' run: | & 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.20348.0/x64/signtool.exe' sign /fd SHA256 /f certificate\certificate.pfx /p '${{ secrets.CERTIFICATE_PASS }}' /t http://timestamp.comodoca.com/authenticode .\package\${{ env.PLUGIN_NAME }}-installer.exe - name: Publish installer if: success() uses: actions/upload-artifact@v2.2.0 with: name: '${{ env.FILE_NAME }}-installer' path: package/*.exe obs-move-transition-2.5.7/CI/000077500000000000000000000000001417746432500157545ustar00rootroot00000000000000obs-move-transition-2.5.7/CI/macos/000077500000000000000000000000001417746432500170565ustar00rootroot00000000000000obs-move-transition-2.5.7/CI/macos/move-transition.pkgproj000066400000000000000000000472701417746432500236240ustar00rootroot00000000000000 PROJECT PACKAGE_FILES DEFAULT_INSTALL_LOCATION / HIERARCHY CHILDREN CHILDREN CHILDREN CHILDREN CHILDREN CHILDREN CHILDREN CHILDREN GID 80 PATH ../../../../build/plugins/move-transition/move-transition.so PATH_TYPE 3 PERMISSIONS 493 TYPE 3 UID 0 GID 80 PATH bin PATH_TYPE 0 PERMISSIONS 493 TYPE 2 UID 0 CHILDREN GID 80 PATH ../../data PATH_TYPE 1 PERMISSIONS 493 TYPE 3 UID 0 GID 80 PATH move-transition PATH_TYPE 0 PERMISSIONS 493 TYPE 2 UID 0 GID 80 PATH plugins PATH_TYPE 0 PERMISSIONS 493 TYPE 2 UID 0 GID 80 PATH obs-studio PATH_TYPE 0 PERMISSIONS 493 TYPE 2 UID 0 GID 80 PATH Application Support PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Automator PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Documentation PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Extensions PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Filesystems PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Frameworks PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Input Methods PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Internet Plug-Ins PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH LaunchAgents PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH LaunchDaemons PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH PreferencePanes PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Preferences PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 80 PATH Printers PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH PrivilegedHelperTools PATH_TYPE 0 PERMISSIONS 1005 TYPE 1 UID 0 CHILDREN GID 0 PATH QuickLook PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH QuickTime PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Screen Savers PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Scripts PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Services PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Widgets PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 GID 0 PATH Library PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN CHILDREN GID 0 PATH Shared PATH_TYPE 0 PERMISSIONS 1023 TYPE 1 UID 0 GID 80 PATH Users PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 80 PATH Applications PATH_TYPE 0 PERMISSIONS 509 TYPE 1 UID 0 GID 0 PATH / PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 PAYLOAD_TYPE 0 PRESERVE_EXTENDED_ATTRIBUTES SHOW_INVISIBLE SPLIT_FORKS TREAT_MISSING_FILES_AS_WARNING VERSION 5 PACKAGE_SCRIPTS POSTINSTALL_PATH PATH_TYPE 0 PREINSTALL_PATH PATH_TYPE 0 RESOURCES PACKAGE_SETTINGS AUTHENTICATION 1 CONCLUSION_ACTION 0 FOLLOW_SYMBOLIC_LINKS IDENTIFIER com.exeldro.move-transition LOCATION 0 NAME OVERWRITE_PERMISSIONS PAYLOAD_SIZE -1 REFERENCE_PATH RELOCATABLE USE_HFS+_COMPRESSION VERSION 0.0.2 PROJECT_COMMENTS NOTES PCFET0NUWVBFIGh0bWwgUFVCTElDICItLy9XM0MvL0RURCBIVE1M IDQuMDEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvVFIvaHRtbDQv c3RyaWN0LmR0ZCI+CjxodG1sPgo8aGVhZD4KPG1ldGEgaHR0cC1l cXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7 IGNoYXJzZXQ9VVRGLTgiPgo8bWV0YSBodHRwLWVxdWl2PSJDb250 ZW50LVN0eWxlLVR5cGUiIGNvbnRlbnQ9InRleHQvY3NzIj4KPHRp dGxlPjwvdGl0bGU+CjxtZXRhIG5hbWU9IkdlbmVyYXRvciIgY29u dGVudD0iQ29jb2EgSFRNTCBXcml0ZXIiPgo8bWV0YSBuYW1lPSJD b2NvYVZlcnNpb24iIGNvbnRlbnQ9IjE0MDQuMTMiPgo8c3R5bGUg dHlwZT0idGV4dC9jc3MiPgo8L3N0eWxlPgo8L2hlYWQ+Cjxib2R5 Pgo8L2JvZHk+CjwvaHRtbD4K PROJECT_SETTINGS BUILD_PATH PATH ../.. PATH_TYPE 1 EXCLUDED_FILES PATTERNS_ARRAY REGULAR_EXPRESSION STRING .DS_Store TYPE 0 PROTECTED PROXY_NAME Remove .DS_Store files PROXY_TOOLTIP Remove ".DS_Store" files created by the Finder. STATE PATTERNS_ARRAY REGULAR_EXPRESSION STRING .pbdevelopment TYPE 0 PROTECTED PROXY_NAME Remove .pbdevelopment files PROXY_TOOLTIP Remove ".pbdevelopment" files created by ProjectBuilder or Xcode. STATE PATTERNS_ARRAY REGULAR_EXPRESSION STRING CVS TYPE 1 REGULAR_EXPRESSION STRING .cvsignore TYPE 0 REGULAR_EXPRESSION STRING .cvspass TYPE 0 REGULAR_EXPRESSION STRING .svn TYPE 1 REGULAR_EXPRESSION STRING .git TYPE 1 REGULAR_EXPRESSION STRING .gitignore TYPE 0 PROTECTED PROXY_NAME Remove SCM metadata PROXY_TOOLTIP Remove helper files and folders used by the CVS, SVN or Git Source Code Management systems. STATE PATTERNS_ARRAY REGULAR_EXPRESSION STRING classes.nib TYPE 0 REGULAR_EXPRESSION STRING designable.db TYPE 0 REGULAR_EXPRESSION STRING info.nib TYPE 0 PROTECTED PROXY_NAME Optimize nib files PROXY_TOOLTIP Remove "classes.nib", "info.nib" and "designable.nib" files within .nib bundles. STATE PATTERNS_ARRAY REGULAR_EXPRESSION STRING Resources Disabled TYPE 1 PROTECTED PROXY_NAME Remove Resources Disabled folders PROXY_TOOLTIP Remove "Resources Disabled" folders. STATE SEPARATOR NAME move-transition PAYLOAD_ONLY TYPE 1 VERSION 2 obs-move-transition-2.5.7/CMakeLists.txt000066400000000000000000000037231417746432500202260ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.12..4.0) project(move-transition VERSION 2.5.7) set(PROJECT_FULL_NAME "Move Transition") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/version.h) set(move-transition_HEADERS move-transition.h easing.h version.h) set(move-transition_SOURCES move-transition.c move-transition-override-filter.c move-source-filter.c move-value-filter.c audio-move.c easing.c) if(WIN32) get_filename_component(ISS_FILES_DIR "${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE) file(TO_NATIVE_PATH "${ISS_FILES_DIR}" ISS_FILES_DIR) get_filename_component(ISS_PACKAGE_DIR "${CMAKE_PACKAGE_PREFIX}\\.." ABSOLUTE) file(TO_NATIVE_PATH "${ISS_PACKAGE_DIR}" ISS_PACKAGE_DIR) get_filename_component(ISS_SOURCE_DIR "${PROJECT_SOURCE_DIR}" ABSOLUTE) file(TO_NATIVE_PATH "${ISS_SOURCE_DIR}" ISS_SOURCE_DIR) configure_file("installer.iss.in" "${PROJECT_BINARY_DIR}/installer.iss" ) configure_file(resource.rc.in move-transition.rc) list(APPEND move-transition_SOURCES move-transition.rc) endif() if(BUILD_OUT_OF_TREE) find_package(LibObs REQUIRED) endif() add_library(move-transition MODULE ${move-transition_HEADERS} ${move-transition_SOURCES}) target_link_libraries(move-transition obs-frontend-api libobs) if(BUILD_OUT_OF_TREE) if(NOT LIB_OUT_DIR) set(LIB_OUT_DIR "/lib/obs-plugins") endif() if(NOT DATA_OUT_DIR) set(DATA_OUT_DIR "/share/obs/obs-plugins/move-transition") endif() set_target_properties(move-transition PROPERTIES PREFIX "") install(TARGETS move-transition LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${LIB_OUT_DIR}) install(DIRECTORY data/locale DESTINATION ${CMAKE_INSTALL_PREFIX}/${DATA_OUT_DIR}) else() target_include_directories(move-transition PRIVATE "${CMAKE_SOURCE_DIR}/UI/obs-frontend-api") set_target_properties(move-transition PROPERTIES FOLDER "plugins/exeldro") install_obs_plugin_with_data(move-transition data) endif() obs-move-transition-2.5.7/LICENSE000066400000000000000000000432541417746432500164760ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. obs-move-transition-2.5.7/README.md000066400000000000000000000012641417746432500167430ustar00rootroot00000000000000# Move transition for OBS Studio Plugin for OBS Studio to move source to a new position during scene transition # Download https://obsproject.com/forum/resources/move-transition.913/ # Build 1. In-tree build - Build OBS Studio: https://obsproject.com/wiki/Install-Instructions - Check out this repository to plugins/move-transition - Add `add_subdirectory(move-transition)` to plugins/CMakeLists.txt - Rebuild OBS Studio 1. Stand-alone build (Linux only) - Verify that you have package with development files for OBS - Check out this repository and run `cmake -S . -B build -DBUILD_OUT_OF_TREE=On && cmake --build build` # Donations https://www.paypal.me/exeldro obs-move-transition-2.5.7/audio-move.c000066400000000000000000000776611417746432500177130ustar00rootroot00000000000000#include "move-transition.h" #include #include #include #define METER_TYPE_MAGNITUDE 0 #define METER_TYPE_PEAK_SAMPLE 1 #define METER_TYPE_PEAK_TRUE 2 #define METER_TYPE_INPUT_PEAK_SAMPLE 3 #define METER_TYPE_INPUT_PEAK_TRUE 4 #define VALUE_ACTION_TRANSFORM 0 #define VALUE_ACTION_SETTING 1 #define VALUE_ACTION_SOURCE_VISIBILITY 2 #define VALUE_ACTION_FILTER_ENABLE 3 #define THRESHOLD_NONE 0 #define THRESHOLD_ENABLE_OVER 1 #define THRESHOLD_ENABLE_UNDER 2 #define THRESHOLD_DISABLE_OVER 3 #define THRESHOLD_DISABLE_UNDER 4 #define THRESHOLD_ENABLE_OVER_DISABLE_UNDER 5 #define THRESHOLD_ENABLE_UNDER_DISABLE_OVER 6 #define TRANSFORM_NONE 0 #define TRANSFORM_POS_X 1 #define TRANSFORM_POS_Y 2 #define TRANSFORM_ROT 3 #define TRANSFORM_SCALE 4 #define TRANSFORM_SCALE_X 4 #define TRANSFORM_SCALE_Y 5 #define TRANSFORM_BOUNDS_X 6 #define TRANSFORM_BOUNDS_Y 7 #define TRANSFORM_CROP_LEFT 8 #define TRANSFORM_CROP_TOP 9 #define TRANSFORM_CROP_RIGHT 10 #define TRANSFORM_CROP_BOTTOM 11 #define TRANSFORM_CROP_HORIZONTAL 12 #define TRANSFORM_CROP_VERTICAL 13 struct audio_move_info { obs_source_t *source; double easing; double audio_value; double base_value; double factor; long long action; long long threshold_action; double threshold; obs_sceneitem_t *sceneitem; obs_weak_source_t *target_source; char *setting_name; obs_volmeter_t *volmeter; long long meter_type; long long transform; }; static const char *audio_move_get_name(void *type_data) { UNUSED_PARAMETER(type_data); return obs_module_text("AudioMoveFilter"); } void audio_move_volmeter_updated(void *data, const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS], const float input_peak[MAX_AUDIO_CHANNELS]) { struct audio_move_info *audio_move = data; float v = 0.0f; if (audio_move->meter_type == METER_TYPE_MAGNITUDE) { v = magnitude[0]; } else if (audio_move->meter_type == METER_TYPE_INPUT_PEAK_SAMPLE || audio_move->meter_type == METER_TYPE_INPUT_PEAK_TRUE) { v = input_peak[0]; } else if (audio_move->meter_type == METER_TYPE_PEAK_SAMPLE || audio_move->meter_type == METER_TYPE_PEAK_TRUE) { v = peak[0]; } v = obs_db_to_mul(v); audio_move->audio_value = audio_move->easing * audio_move->audio_value + (1.0 - audio_move->easing) * v; } void audio_move_source_destroy(void *data, calldata_t *call_data) { struct audio_move_info *audio_move = data; audio_move->target_source = NULL; audio_move->sceneitem = NULL; } void audio_move_item_remove(void *data, calldata_t *call_data); void audio_move_source_remove(void *data, calldata_t *call_data) { struct audio_move_info *audio_move = data; if (audio_move->target_source) { obs_source_t *source = obs_weak_source_get_source(audio_move->target_source); signal_handler_t *sh = obs_source_get_signal_handler(source); signal_handler_disconnect(sh, "remove", audio_move_source_remove, audio_move); signal_handler_disconnect( sh, "destroy", audio_move_source_destroy, audio_move); obs_source_release(source); obs_weak_source_release(audio_move->target_source); } audio_move->target_source = NULL; if (audio_move->sceneitem) { obs_scene_t *scene = obs_sceneitem_get_scene(audio_move->sceneitem); signal_handler_t *sh = obs_source_get_signal_handler( obs_scene_get_source(scene)); if (sh) { signal_handler_disconnect(sh, "item_remove", audio_move_item_remove, audio_move); signal_handler_disconnect(sh, "remove", audio_move_source_remove, audio_move); signal_handler_disconnect(sh, "destroy", audio_move_source_destroy, audio_move); } obs_source_t *item_source = obs_sceneitem_get_source(audio_move->sceneitem); if (item_source) { sh = obs_source_get_signal_handler(item_source); signal_handler_disconnect(sh, "remove", audio_move_source_remove, audio_move); signal_handler_disconnect(sh, "destroy", audio_move_source_destroy, audio_move); } } audio_move->sceneitem = NULL; } void audio_move_item_remove(void *data, calldata_t *call_data) { struct audio_move_info *audio_move = data; obs_scene_t *scene = NULL; calldata_get_ptr(call_data, "scene", &scene); obs_sceneitem_t *item = NULL; calldata_get_ptr(call_data, "item", &item); if (item == audio_move->sceneitem) { audio_move->sceneitem = NULL; obs_source_t *parent = obs_scene_get_source(scene); if (parent) { signal_handler_t *sh = obs_source_get_signal_handler(parent); if (sh) { signal_handler_disconnect( sh, "item_remove", audio_move_item_remove, audio_move); signal_handler_disconnect( sh, "remove", audio_move_source_remove, audio_move); signal_handler_disconnect( sh, "destroy", audio_move_source_destroy, audio_move); } } } } void audio_move_update(void *data, obs_data_t *settings) { struct audio_move_info *audio_move = data; obs_source_t *parent = obs_filter_get_parent(audio_move->source); if (parent) obs_volmeter_attach_source(audio_move->volmeter, parent); const long long meter_type = obs_data_get_int(settings, "meter_type"); if (meter_type != audio_move->meter_type) { audio_move->meter_type = meter_type; if (meter_type == METER_TYPE_INPUT_PEAK_SAMPLE || meter_type == METER_TYPE_PEAK_SAMPLE) { obs_volmeter_set_peak_meter_type(audio_move->volmeter, SAMPLE_PEAK_METER); } else if (meter_type == METER_TYPE_INPUT_PEAK_TRUE || meter_type == METER_TYPE_PEAK_TRUE) { obs_volmeter_set_peak_meter_type(audio_move->volmeter, TRUE_PEAK_METER); } } audio_move->easing = obs_data_get_double(settings, "easing") / 100.0; audio_move->action = obs_data_get_int(settings, "value_action"); audio_move->transform = obs_data_get_int(settings, "transform"); audio_move->base_value = obs_data_get_double(settings, "base_value"); audio_move->factor = obs_data_get_double(settings, "factor"); const char *scene_name = obs_data_get_string(settings, "scene"); const char *sceneitem_name = obs_data_get_string(settings, "sceneitem"); obs_source_t *source = obs_get_source_by_name(scene_name); obs_source_release(source); if (source && obs_source_removed(source)) source = NULL; obs_scene_t *scene = obs_scene_from_source(source); if (audio_move->sceneitem) { signal_handler_t *sh = obs_source_get_signal_handler(source); if (sh) { signal_handler_disconnect(sh, "item_remove", audio_move_item_remove, audio_move); signal_handler_disconnect(sh, "remove", audio_move_source_remove, audio_move); signal_handler_disconnect(sh, "destroy", audio_move_source_destroy, audio_move); } obs_source_t *item_source = obs_sceneitem_get_source(audio_move->sceneitem); if (item_source) { sh = obs_source_get_signal_handler(item_source); signal_handler_disconnect(sh, "remove", audio_move_source_remove, audio_move); signal_handler_disconnect(sh, "destroy", audio_move_source_destroy, audio_move); } } audio_move->sceneitem = scene ? obs_scene_find_source(scene, sceneitem_name) : NULL; if (audio_move->sceneitem && obs_source_removed( obs_sceneitem_get_source(audio_move->sceneitem))) { audio_move->sceneitem = NULL; } if (audio_move->sceneitem && source) { signal_handler_t *sh = obs_source_get_signal_handler(source); if (sh) { signal_handler_connect(sh, "item_remove", audio_move_item_remove, audio_move); signal_handler_connect(sh, "remove", audio_move_source_remove, audio_move); signal_handler_connect(sh, "destroy", audio_move_source_destroy, audio_move); } obs_source_t *item_source = obs_sceneitem_get_source(audio_move->sceneitem); if (item_source) { sh = obs_source_get_signal_handler(item_source); signal_handler_connect(sh, "remove", audio_move_source_remove, audio_move); signal_handler_connect(sh, "destroy", audio_move_source_destroy, audio_move); } } if (audio_move->target_source) { source = obs_weak_source_get_source(audio_move->target_source); if (source) { signal_handler_t *sh = obs_source_get_signal_handler(source); signal_handler_disconnect(sh, "remove", audio_move_source_remove, audio_move); signal_handler_disconnect(sh, "destroy", audio_move_source_destroy, audio_move); obs_source_release(source); } obs_weak_source_release(audio_move->target_source); } audio_move->target_source = NULL; obs_source_t *target_source = NULL; if (audio_move->action == VALUE_ACTION_FILTER_ENABLE) { source = obs_get_source_by_name( obs_data_get_string(settings, "source")); if (source) { obs_source_t *filter = obs_source_get_filter_by_name( source, obs_data_get_string(settings, "filter")); if (filter) { target_source = filter; } obs_source_release(source); } } else if (audio_move->action == VALUE_ACTION_SETTING) { source = obs_get_source_by_name( obs_data_get_string(settings, "source")); if (source) { const char *filter_name = obs_data_get_string(settings, "filter"); obs_source_t *filter = filter_name && strlen(filter_name) ? obs_source_get_filter_by_name( source, filter_name) : NULL; if (filter) { target_source = filter; obs_source_release(source); } else { target_source = source; } } } if (target_source && obs_source_removed(target_source)) { target_source = NULL; } if (target_source) { audio_move->target_source = obs_source_get_weak_source(target_source); signal_handler_t *sh = obs_source_get_signal_handler(target_source); signal_handler_connect(sh, "remove", audio_move_source_remove, audio_move); signal_handler_connect(sh, "destroy", audio_move_source_destroy, audio_move); obs_source_release(target_source); } audio_move->threshold_action = obs_data_get_int(settings, "threshold_action"); audio_move->threshold = obs_data_get_double(settings, "threshold") / 100.0; const char *setting_name = obs_data_get_string(settings, "setting"); if (!audio_move->setting_name || strcmp(audio_move->setting_name, setting_name) != 0) { bfree(audio_move->setting_name); audio_move->setting_name = bstrdup(setting_name); } } static void *audio_move_create(obs_data_t *settings, obs_source_t *source) { struct audio_move_info *audio_move = bzalloc(sizeof(struct audio_move_info)); audio_move->source = source; audio_move->volmeter = obs_volmeter_create(OBS_FADER_LOG); obs_volmeter_add_callback(audio_move->volmeter, audio_move_volmeter_updated, audio_move); audio_move_update(audio_move, settings); return audio_move; } static void audio_move_destroy(void *data) { struct audio_move_info *audio_move = data; obs_volmeter_detach_source(audio_move->volmeter); obs_volmeter_remove_callback(audio_move->volmeter, audio_move_volmeter_updated, audio_move); obs_volmeter_destroy(audio_move->volmeter); audio_move->volmeter = NULL; if (audio_move->target_source) { obs_source_t *source = obs_weak_source_get_source(audio_move->target_source); if (source) { signal_handler_t *sh = obs_source_get_signal_handler(source); signal_handler_disconnect(sh, "remove", audio_move_source_remove, audio_move); signal_handler_disconnect(sh, "destroy", audio_move_source_destroy, audio_move); obs_source_release(source); } obs_weak_source_release(audio_move->target_source); } audio_move->target_source = NULL; if (audio_move->sceneitem) { obs_scene_t *scene = obs_sceneitem_get_scene(audio_move->sceneitem); signal_handler_t *sh = obs_source_get_signal_handler( obs_scene_get_source(scene)); if (sh) { signal_handler_disconnect(sh, "item_remove", audio_move_item_remove, audio_move); signal_handler_disconnect(sh, "remove", audio_move_source_remove, audio_move); signal_handler_disconnect(sh, "destroy", audio_move_source_destroy, audio_move); } obs_source_t *item_source = obs_sceneitem_get_source(audio_move->sceneitem); if (item_source) { sh = obs_source_get_signal_handler(item_source); signal_handler_disconnect(sh, "remove", audio_move_source_remove, audio_move); signal_handler_disconnect(sh, "destroy", audio_move_source_destroy, audio_move); } } audio_move->sceneitem = NULL; bfree(audio_move->setting_name); bfree(audio_move); } static bool add_source_to_prop_list(void *data, obs_source_t *source) { obs_property_t *p = (obs_property_t *)data; const char *name = obs_source_get_name(source); if (name && strlen(name)) obs_property_list_add_string(p, name, name); return true; } static bool audio_move_action_changed(obs_properties_t *props, obs_property_t *property, obs_data_t *settings) { UNUSED_PARAMETER(property); long long action = obs_data_get_int(settings, "value_action"); obs_property_t *scene = obs_properties_get(props, "scene"); obs_property_t *sceneitem = obs_properties_get(props, "sceneitem"); if (action == VALUE_ACTION_TRANSFORM || action == VALUE_ACTION_SOURCE_VISIBILITY) { obs_property_list_clear(scene); obs_enum_scenes(add_source_to_prop_list, scene); obs_property_set_visible(scene, true); obs_property_set_visible(sceneitem, true); } else { obs_property_set_visible(scene, false); obs_property_set_visible(sceneitem, false); } obs_property_t *source = obs_properties_get(props, "source"); obs_property_t *filter = obs_properties_get(props, "filter"); if (action == VALUE_ACTION_SETTING || action == VALUE_ACTION_FILTER_ENABLE) { obs_property_list_clear(source); obs_enum_sources(add_source_to_prop_list, source); obs_enum_scenes(add_source_to_prop_list, source); obs_property_set_visible(source, true); obs_property_set_visible(filter, true); } else { obs_property_set_visible(source, false); obs_property_set_visible(filter, false); } obs_property_t *base_value = obs_properties_get(props, "base_value"); obs_property_t *factor = obs_properties_get(props, "factor"); if (action == VALUE_ACTION_SETTING || action == VALUE_ACTION_TRANSFORM) { obs_property_set_visible(base_value, true); obs_property_set_visible(factor, true); } else { obs_property_set_visible(base_value, false); obs_property_set_visible(factor, false); } obs_property_t *threshold_action = obs_properties_get(props, "threshold_action"); obs_property_t *threshold = obs_properties_get(props, "threshold"); if (action == VALUE_ACTION_SOURCE_VISIBILITY || action == VALUE_ACTION_FILTER_ENABLE) { obs_property_set_visible(threshold_action, true); obs_property_set_visible(threshold, true); } else { obs_property_set_visible(threshold_action, false); obs_property_set_visible(threshold, false); } obs_property_t *transform = obs_properties_get(props, "transform"); if (action == VALUE_ACTION_TRANSFORM) { obs_property_set_visible(transform, true); } else { obs_property_set_visible(transform, false); } obs_property_t *setting = obs_properties_get(props, "setting"); if (action == VALUE_ACTION_SETTING) { obs_property_set_visible(setting, true); } else { obs_property_set_visible(setting, false); } return true; } static bool add_sceneitem_to_prop_list(obs_scene_t *scene, obs_sceneitem_t *item, void *data) { UNUSED_PARAMETER(scene); obs_property_t *p = (obs_property_t *)data; const obs_source_t *source = obs_sceneitem_get_source(item); const char *name = obs_source_get_name(source); if (name && strlen(name)) obs_property_list_add_string(p, name, name); return true; } static bool audio_move_scene_changed(void *data, obs_properties_t *props, obs_property_t *property, obs_data_t *settings) { UNUSED_PARAMETER(data); UNUSED_PARAMETER(property); const char *scene_name = obs_data_get_string(settings, "scene"); obs_property_t *sceneitem = obs_properties_get(props, "sceneitem"); obs_property_list_clear(sceneitem); obs_source_t *source = obs_get_source_by_name(scene_name); obs_source_release(source); obs_scene_t *scene = obs_scene_from_source(source); if (!scene) return true; obs_scene_enum_items(scene, add_sceneitem_to_prop_list, sceneitem); return true; } static void add_filter_to_prop_list(obs_source_t *parent, obs_source_t *child, void *data) { UNUSED_PARAMETER(parent); obs_property_t *p = (obs_property_t *)data; const char *name = obs_source_get_name(child); const char *src_id = obs_source_get_id(child); if (name && strlen(name) && strcmp(src_id, AUDIO_MOVE_FILTER_ID) != 0) obs_property_list_add_string(p, name, name); } static void load_properties(obs_properties_t *props_from, obs_property_t *setting_list) { obs_property_t *prop_from = obs_properties_first(props_from); for (; prop_from != NULL; obs_property_next(&prop_from)) { const char *name = obs_property_name(prop_from); const char *description = obs_property_description(prop_from); if (!obs_property_visible(prop_from)) continue; const enum obs_property_type prop_type = obs_property_get_type(prop_from); if (prop_type == OBS_PROPERTY_GROUP) { load_properties(obs_property_group_content(prop_from), setting_list); } else if (prop_type == OBS_PROPERTY_FLOAT || prop_type == OBS_PROPERTY_INT) { obs_property_list_add_string(setting_list, description, name); } } } static bool audio_move_source_changed(void *data, obs_properties_t *props, obs_property_t *property, obs_data_t *settings) { UNUSED_PARAMETER(property); UNUSED_PARAMETER(data); const char *source_name = obs_data_get_string(settings, "source"); const char *filter_name = obs_data_get_string(settings, "filter"); obs_property_t *filter = obs_properties_get(props, "filter"); obs_property_list_clear(filter); obs_source_t *source = obs_get_source_by_name(source_name); obs_source_release(source); obs_source_enum_filters(source, add_filter_to_prop_list, filter); obs_property_t *setting = obs_properties_get(props, "setting"); obs_property_list_clear(setting); obs_properties_t *properties = NULL; if (filter_name && strlen(filter_name)) { obs_source_t *f = obs_source_get_filter_by_name(source, filter_name); if (f) { properties = obs_source_properties(f); } } else { properties = obs_source_properties(source); } if (properties) { load_properties(properties, setting); obs_properties_destroy(properties); } return true; } static obs_properties_t *audio_move_properties(void *data) { UNUSED_PARAMETER(data); obs_properties_t *ppts = obs_properties_create(); obs_property_t *p = obs_properties_add_list( ppts, "meter_type", obs_module_text("MeterType"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("MeterType.Magnitude"), METER_TYPE_MAGNITUDE); obs_property_list_add_int(p, obs_module_text("MeterType.PeakSample"), METER_TYPE_PEAK_SAMPLE); obs_property_list_add_int(p, obs_module_text("MeterType.PeakTrue"), METER_TYPE_PEAK_TRUE); obs_property_list_add_int(p, obs_module_text("MeterType.InputPeakSample"), METER_TYPE_INPUT_PEAK_SAMPLE); obs_property_list_add_int(p, obs_module_text("MeterType.InputPeakTrue"), METER_TYPE_INPUT_PEAK_TRUE); p = obs_properties_add_float_slider( ppts, "easing", obs_module_text("Easing"), 0.0, 99.99, 0.01); p = obs_properties_add_list(ppts, "value_action", obs_module_text("ValueAction"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("ValueAction.Transform"), VALUE_ACTION_TRANSFORM); obs_property_list_add_int(p, obs_module_text("ValueAction.Setting"), VALUE_ACTION_SETTING); obs_property_list_add_int( p, obs_module_text("ValueAction.SourceVisibility"), VALUE_ACTION_SOURCE_VISIBILITY); obs_property_list_add_int(p, obs_module_text("ValueAction.FilterEnable"), VALUE_ACTION_FILTER_ENABLE); obs_property_set_modified_callback(p, audio_move_action_changed); p = obs_properties_add_list(ppts, "scene", obs_module_text("Scene"), OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); obs_property_set_modified_callback2(p, audio_move_scene_changed, data); p = obs_properties_add_list(ppts, "sceneitem", obs_module_text("Source"), OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); p = obs_properties_add_list(ppts, "source", obs_module_text("Source"), OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); obs_property_set_modified_callback2(p, audio_move_source_changed, data); p = obs_properties_add_list(ppts, "filter", obs_module_text("Filter"), OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); obs_property_set_modified_callback2(p, audio_move_source_changed, data); p = obs_properties_add_list(ppts, "transform", obs_module_text("Transform"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("Transform.PosX"), TRANSFORM_POS_X); obs_property_list_add_int(p, obs_module_text("Transform.PosY"), TRANSFORM_POS_Y); obs_property_list_add_int(p, obs_module_text("Transform.Rotation"), TRANSFORM_ROT); obs_property_list_add_int(p, obs_module_text("Transform.Scale"), TRANSFORM_SCALE); obs_property_list_add_int(p, obs_module_text("Transform.ScaleX"), TRANSFORM_SCALE_X); obs_property_list_add_int(p, obs_module_text("Transform.ScaleY"), TRANSFORM_SCALE_Y); obs_property_list_add_int(p, obs_module_text("Transform.BoundsX"), TRANSFORM_BOUNDS_X); obs_property_list_add_int(p, obs_module_text("Transform.BoundsY"), TRANSFORM_BOUNDS_Y); obs_property_list_add_int(p, obs_module_text("Transform.CropLeft"), TRANSFORM_CROP_LEFT); obs_property_list_add_int(p, obs_module_text("Transform.CropTop"), TRANSFORM_CROP_TOP); obs_property_list_add_int(p, obs_module_text("Transform.CropRight"), TRANSFORM_CROP_RIGHT); obs_property_list_add_int(p, obs_module_text("Transform.CropBottom"), TRANSFORM_CROP_BOTTOM); obs_property_list_add_int(p, obs_module_text("Transform.CropHorizontal"), TRANSFORM_CROP_HORIZONTAL); obs_property_list_add_int(p, obs_module_text("Transform.CropVertical"), TRANSFORM_CROP_VERTICAL); p = obs_properties_add_list(ppts, "setting", obs_module_text("Setting"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); p = obs_properties_add_float(ppts, "base_value", obs_module_text("BaseValue"), -DBL_MAX, DBL_MAX, 0.01); p = obs_properties_add_float(ppts, "factor", obs_module_text("Factor"), -DBL_MAX, DBL_MAX, 0.01); p = obs_properties_add_list(ppts, "threshold_action", obs_module_text("ThresholdAction"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("ThresholdAction.None"), THRESHOLD_NONE); obs_property_list_add_int(p, obs_module_text("ThresholdAction.EnableOver"), THRESHOLD_ENABLE_OVER); obs_property_list_add_int( p, obs_module_text("ThresholdAction.EnableUnder"), THRESHOLD_ENABLE_UNDER); obs_property_list_add_int( p, obs_module_text("ThresholdAction.DisableOver"), THRESHOLD_DISABLE_OVER); obs_property_list_add_int( p, obs_module_text("ThresholdAction.DisableUnder"), THRESHOLD_DISABLE_UNDER); obs_property_list_add_int( p, obs_module_text("ThresholdAction.EnableOverDisableUnder"), THRESHOLD_ENABLE_OVER_DISABLE_UNDER); obs_property_list_add_int( p, obs_module_text("ThresholdAction.EnableUnderDisableOver"), THRESHOLD_ENABLE_UNDER_DISABLE_OVER); p = obs_properties_add_float_slider(ppts, "threshold", obs_module_text("Threshold"), 0.0, 100.0, 0.01); return ppts; } void audio_move_defaults(obs_data_t *settings) { obs_data_set_default_double(settings, "factor", 1000.0); } void audio_move_tick(void *data, float seconds) { UNUSED_PARAMETER(seconds); struct audio_move_info *filter = data; if (!obs_source_enabled(filter->source)) return; if (filter->action == VALUE_ACTION_TRANSFORM) { if (!filter->sceneitem) { obs_data_t *settings = obs_source_get_settings(filter->source); audio_move_update(filter, settings); obs_data_release(settings); } if (!filter->sceneitem) return; if (filter->transform == TRANSFORM_POS_X) { struct vec2 pos; obs_sceneitem_get_pos(filter->sceneitem, &pos); pos.x = filter->factor * filter->audio_value + filter->base_value; obs_sceneitem_set_pos(filter->sceneitem, &pos); } else if (filter->transform == TRANSFORM_POS_Y) { struct vec2 pos; obs_sceneitem_get_pos(filter->sceneitem, &pos); pos.y = filter->factor * filter->audio_value + filter->base_value; obs_sceneitem_set_pos(filter->sceneitem, &pos); } else if (filter->transform == TRANSFORM_ROT) { obs_sceneitem_set_rot( filter->sceneitem, filter->factor * filter->audio_value + filter->base_value); } else if (filter->transform == TRANSFORM_SCALE) { struct vec2 scale; scale.x = filter->factor * filter->audio_value + filter->base_value; scale.y = scale.x; obs_sceneitem_set_scale(filter->sceneitem, &scale); } else if (filter->transform == TRANSFORM_SCALE_X) { struct vec2 scale; obs_sceneitem_get_scale(filter->sceneitem, &scale); scale.x = filter->factor * filter->audio_value + filter->base_value; obs_sceneitem_set_scale(filter->sceneitem, &scale); } else if (filter->transform == TRANSFORM_SCALE_Y) { struct vec2 scale; obs_sceneitem_get_scale(filter->sceneitem, &scale); scale.y = filter->factor * filter->audio_value + filter->base_value; obs_sceneitem_set_scale(filter->sceneitem, &scale); } else if (filter->transform == TRANSFORM_BOUNDS_X) { struct vec2 bounds; obs_sceneitem_get_bounds(filter->sceneitem, &bounds); bounds.x = filter->factor * filter->audio_value + filter->base_value; obs_sceneitem_set_scale(filter->sceneitem, &bounds); } else if (filter->transform == TRANSFORM_BOUNDS_Y) { struct vec2 bounds; obs_sceneitem_get_bounds(filter->sceneitem, &bounds); bounds.y = filter->factor * filter->audio_value + filter->base_value; obs_sceneitem_set_scale(filter->sceneitem, &bounds); } else if (filter->transform == TRANSFORM_CROP_LEFT) { struct obs_sceneitem_crop crop; obs_sceneitem_get_crop(filter->sceneitem, &crop); crop.left = filter->factor * filter->audio_value + filter->base_value; obs_sceneitem_set_crop(filter->sceneitem, &crop); } else if (filter->transform == TRANSFORM_CROP_TOP) { struct obs_sceneitem_crop crop; obs_sceneitem_get_crop(filter->sceneitem, &crop); crop.top = filter->factor * filter->audio_value + filter->base_value; obs_sceneitem_set_crop(filter->sceneitem, &crop); } else if (filter->transform == TRANSFORM_CROP_RIGHT) { struct obs_sceneitem_crop crop; obs_sceneitem_get_crop(filter->sceneitem, &crop); crop.right = filter->factor * filter->audio_value + filter->base_value; obs_sceneitem_set_crop(filter->sceneitem, &crop); } else if (filter->transform == TRANSFORM_CROP_BOTTOM) { struct obs_sceneitem_crop crop; obs_sceneitem_get_crop(filter->sceneitem, &crop); crop.bottom = filter->factor * filter->audio_value + filter->base_value; obs_sceneitem_set_crop(filter->sceneitem, &crop); } else if (filter->transform == TRANSFORM_CROP_HORIZONTAL) { struct obs_sceneitem_crop crop; obs_sceneitem_get_crop(filter->sceneitem, &crop); crop.left = filter->factor * filter->audio_value + filter->base_value; crop.right = crop.left; obs_sceneitem_set_crop(filter->sceneitem, &crop); } else if (filter->transform == TRANSFORM_CROP_VERTICAL) { struct obs_sceneitem_crop crop; obs_sceneitem_get_crop(filter->sceneitem, &crop); crop.top = filter->factor * filter->audio_value + filter->base_value; crop.bottom = crop.top; obs_sceneitem_set_crop(filter->sceneitem, &crop); } } else if (filter->action == VALUE_ACTION_SOURCE_VISIBILITY) { if (!filter->sceneitem) { obs_data_t *settings = obs_source_get_settings(filter->source); audio_move_update(filter, settings); obs_data_release(settings); } if (!filter->sceneitem) return; if ((filter->threshold_action == THRESHOLD_ENABLE_OVER || filter->threshold_action == THRESHOLD_ENABLE_OVER_DISABLE_UNDER) && filter->audio_value >= filter->threshold) { obs_sceneitem_set_visible(filter->sceneitem, true); } else if ((filter->threshold_action == THRESHOLD_ENABLE_UNDER || filter->threshold_action == THRESHOLD_ENABLE_UNDER_DISABLE_OVER) && filter->audio_value < filter->threshold) { obs_sceneitem_set_visible(filter->sceneitem, true); } else if ((filter->threshold_action == THRESHOLD_DISABLE_OVER || filter->threshold_action == THRESHOLD_ENABLE_UNDER_DISABLE_OVER) && filter->audio_value >= filter->threshold) { obs_sceneitem_set_visible(filter->sceneitem, false); } else if ((filter->threshold_action == THRESHOLD_DISABLE_UNDER || filter->threshold_action == THRESHOLD_ENABLE_OVER_DISABLE_UNDER) && filter->audio_value < filter->threshold) { obs_sceneitem_set_visible(filter->sceneitem, false); } } else if (filter->action == VALUE_ACTION_FILTER_ENABLE) { if (!filter->target_source) { obs_data_t *settings = obs_source_get_settings(filter->source); audio_move_update(filter, settings); obs_data_release(settings); } if (!filter->target_source) return; obs_source_t *source = obs_weak_source_get_source(filter->target_source); if (!source) return; if ((filter->threshold_action == THRESHOLD_ENABLE_OVER || filter->threshold_action == THRESHOLD_ENABLE_OVER_DISABLE_UNDER) && filter->audio_value >= filter->threshold && !obs_source_enabled(source)) { obs_source_set_enabled(source, true); } else if ((filter->threshold_action == THRESHOLD_ENABLE_UNDER || filter->threshold_action == THRESHOLD_ENABLE_UNDER_DISABLE_OVER) && filter->audio_value < filter->threshold && !obs_source_enabled(source)) { obs_source_set_enabled(source, true); } else if ((filter->threshold_action == THRESHOLD_DISABLE_OVER || filter->threshold_action == THRESHOLD_ENABLE_UNDER_DISABLE_OVER) && filter->audio_value >= filter->threshold && obs_source_enabled(source)) { obs_source_set_enabled(source, false); } else if ((filter->threshold_action == THRESHOLD_DISABLE_UNDER || filter->threshold_action == THRESHOLD_ENABLE_OVER_DISABLE_UNDER) && filter->audio_value < filter->threshold && obs_source_enabled(source)) { obs_source_set_enabled(source, false); } obs_source_release(source); } else if (filter->action == VALUE_ACTION_SETTING && filter->setting_name && strlen(filter->setting_name)) { if (!filter->target_source) { obs_data_t *settings = obs_source_get_settings(filter->source); audio_move_update(filter, settings); obs_data_release(settings); } if (!filter->target_source) return; obs_source_t *source = obs_weak_source_get_source(filter->target_source); if (!source) return; obs_data_t *settings = obs_source_get_settings(source); const double val = filter->factor * filter->audio_value + filter->base_value; obs_data_item_t *setting = obs_data_item_byname(settings, filter->setting_name); if (setting) { const enum obs_data_number_type num_type = obs_data_item_numtype(setting); if (num_type == OBS_DATA_NUM_INT) { obs_data_item_set_int(&setting, val); } else if (num_type == OBS_DATA_NUM_DOUBLE) { obs_data_item_set_double(&setting, val); } obs_data_item_release(&setting); } else { obs_data_set_double(settings, filter->setting_name, val); } obs_data_release(settings); obs_source_update(source, NULL); obs_source_release(source); } } struct obs_source_info audio_move_filter = { .id = AUDIO_MOVE_FILTER_ID, .type = OBS_SOURCE_TYPE_FILTER, .output_flags = OBS_SOURCE_AUDIO, .get_name = audio_move_get_name, .create = audio_move_create, .destroy = audio_move_destroy, .get_properties = audio_move_properties, .get_defaults = audio_move_defaults, .update = audio_move_update, .load = audio_move_update, .video_tick = audio_move_tick, }; obs-move-transition-2.5.7/data/000077500000000000000000000000001417746432500163725ustar00rootroot00000000000000obs-move-transition-2.5.7/data/locale/000077500000000000000000000000001417746432500176315ustar00rootroot00000000000000obs-move-transition-2.5.7/data/locale/de-DE.ini000066400000000000000000000020041417746432500212040ustar00rootroot00000000000000Move="Verschieben" Description="Szenenübergang, der alle Quellen an ihre neue Position verschiebt." Easing="Easing" Easing.None="Kein easing" Easing.In="Ease in" Easing.Out="Ease out" Easing.InOut="Ease in and out" EasingFunction="Easing Function" EasingFunction.Quadratic="Quadratisch" EasingFunction.Cubic="Kubisch" EasingFunction.Quartic="Quartisch" EasingFunction.Quintic="Quintisch" EasingFunction.Sine="Sinus" EasingFunction.Circular="Kreisfunktion" EasingFunction.Exponential="Exponentiell" EasingFunction.Elastic="Elastisch" EasingFunction.Bounce="Aufspringen" EasingFunction.Back="Zurückfedern" Position="Position" Zoom="Zoomen" Curve="Kurvenkrümmung" Position.None="Keine" Position.Center="Mittig" Position.CenterInverse="Weg von der Mitte" Position.TopLeft="Oben links" Position.TopCenter="Oben mittig" Position.TopRight="Oben rechts" Position.CenterRight="Mittig rechts" Position.BottomRight="Unten rechts" Position.BottomCenter="Unten mittig" Position.BottomLeft="Unten links" Position.CenterLeft="Mittig links" obs-move-transition-2.5.7/data/locale/en-US.ini000066400000000000000000000171131417746432500212640ustar00rootroot00000000000000Move="Move" MoveTransition="Move Transition" Description="Transition that moves all sources to a new position" MoveMatch="Matched items" MoveIn="Appearing items" MoveOut="Disappearing items" Easing="Easing" Easing.None="No easing" Easing.In="Ease in" Easing.Out="Ease out" Easing.InOut="Ease in and out" EasingFunction="Easing Function" EasingFunction.Quadratic="Quadratic" EasingFunction.Cubic="Cubic" EasingFunction.Quartic="Quartic" EasingFunction.Quintic="Quintic" EasingFunction.Sine="Sine" EasingFunction.Circular="Circular" EasingFunction.Exponential="Exponential" EasingFunction.Elastic="Elastic" EasingFunction.Bounce="Bounce" EasingFunction.Back="Back" Position="Position" Zoom="Zoom" Curve="Curve" CurveOverride="Override Curve" Position.None="None" Position.Center="Center" Position.CenterInverse="Away from center" Position.TopLeft="Top left" Position.TopCenter="Top center" Position.TopRight="Top right" Position.CenterRight="Center right" Position.BottomRight="Bottom right" Position.BottomCenter="Bottom center" Position.BottomLeft="Bottom left" Position.CenterLeft="Center left" Position.Left="Left" Position.Top="Top" Position.Right="Right" Position.Bottom="Bottom" Transition="Transition" Transition.None="None" MatchName="Match if the source name" NamePartMatch="contains the other source name" NameNumberMatch="with numbers removed from end matches the other source name" NameLastWordMatch="with the last word removed matches the other source name" TransitionScaleType="Transition scale type" TransitionScale.MaxOnly="Max only: Scale to aspect ratio, but only to the maximum size of each source" TransitionScale.Aspect="Aspect: Always scale the sources, but keep aspect ratio" TransitionScale.Stretch="Stretch: Scale and stretch the sources to the size of the transition" MoveTransitionOverrideFilter="Move transition override" NoOverride="No override" Yes="Yes" No="No" StartMove="Start Move Filter" StartMoveMatchFrom="Start Move Filter From" StartMoveMatchTo="Start Move Filter To" MoveSourceFilter="Move Source" ScenesOnlyFilter="This filter only works on scenes and groups" General="General" TransformRelative="Transform Relative" Transform="Transform" GetTransform="Get transform" CustomDuration="Custom Duration" Duration="Duration" Start="Start" SwitchPoint="Switch Point" Source="Source" MatchSource="Match Source" MoveAll="General" CacheTransitions="Cache Transitions" StartTrigger="Start Trigger" StartTrigger.None="None: not started automatic, use a hotkey or next move to start this move" StartTrigger.Activate="Activate: When this filter becomes actively shown in the final mix" StartTrigger.Deactivate="Deactivate: When this filter becomes not active, so not showing the final mix." StartTrigger.Show="Show: This filter is being displayed anywhere at all, whether on a display context or on the final output" StartTrigger.Hide="Hide: When this filter is not showing anywhere" StartTrigger.Enable="Enable: When the eye icon in front of this filter is enabled" StartTrigger.SourceActivate="Source Activate: When the source becomes actively shown in the final mix" StartTrigger.SourceDeactivate="Source Deactivate: When the source becomes not active, so not showing the final mix." StartTrigger.SourceShow="Source Show: The source is being displayed anywhere at all, whether on a display context or on the final output" StartTrigger.SourceHide="Source Hide: When the source is not showing anywhere" StartTrigger.MediaStarted="Media Started: When the media of the source starts playing" StartTrigger.MediaEnded="Media Ended: When playing of the media source has ended" StartTrigger.Load="Load: When this filter is loaded or updated" StopTrigger="Stop Trigger" StopTrigger.None="None: stop only when the move is done or this filter is disabled" Actions="Actions" SimultaneousMove="Simultaneous Move" SimultaneousMove.None="None" NextMove="Next Move" NextMove.None="None" NextMove.Reverse="Reverse" NextMoveOn="Next Move On" NextMoveOn.End="Move End" NextMoveOn.Hotkey="Hotkey" StartDelay="Start Delay" EndDelay="End Delay" StartDelayTo="Start Delay To" EndDelayTo="End Delay To" StartDelayFrom="Start Delay From" EndDelayFrom="End Delay From" MoveValueFilter="Move Value" Filter="Filter" Filter.None="None" MoveValueType="Move Value Type" MoveValueType.SingleSetting="Single Setting" MoveValueType.Settings="Settings" MoveValueType.Random="Random" MoveValueType.SettingAdd="Add" MoveValueType.Type="Typing" FormatType="Format Type" FormatType.Decimals="Decimals number" FormatType.Float="Float format using printf" FormatType.Time="Time format using strftime" Format="Format" Settings="Settings" Setting="Setting" Setting.None="None" Setting.Volume="Source volume" Decimals="Decimals" Value="Value" MinValue="Min Value" MaxValue="Max Value" Text="Text" GetValue="Get Value" GetValues="Get Values" VisibilityOrder="Visibility and Order" ChangeVisibility="Visibility" ChangeVisibility.No="No Change" ChangeVisibility.ShowStart="Show at the start of movement" ChangeVisibility.ShowEnd="Show at the end of movement" ChangeVisibility.ShowStartEnd="Show during movement" ChangeVisibility.HideStart="Hide at the start of movement" ChangeVisibility.HideEnd="Hide at the end of movement" ChangeVisibility.HideStartEnd="Hide during movement" ChangeVisibility.Toggle="Toggle" ChangeVisibility.ToggleStart="Toggle at the start of movement" ChangeVisibility.ToggleEnd="Toggle at the end of movement" ChangeOrder="Order" ChangeOrder.No="No Change" ChangeOrder.StartRelative="Start Relative" ChangeOrder.EndRelative="End Relative" ChangeOrder.StartAbsolute="Start Absolute" ChangeOrder.EndAbsolute="End Absolute" OrderPosition="Difference / Position" MediaAction="Media" MediaAction.Start="Start Action" MediaAction.End="End Action" MediaAction.None="None" MediaAction.Play="Play" MediaAction.Pause="Pause" MediaAction.Stop="Stop" MediaAction.Restart="Restart" MediaAction.Next="Next" MediaAction.Previous="Previous" MediaAction.PlayFrom="Play From" MediaAction.PauseAt="Pause At" MediaAction.Time="Time" AudioAction="Audio" MuteAction="Mute" MuteAction.None="None" MuteAction.MuteStart="Mute at Start" MuteAction.MuteEnd="Mute at End" MuteAction.UnmuteStart="Unmute at Start" MuteAction.UnmuteEnd="Unmute at End" MuteAction.MuteDuring="Mute During: Mute at Start and Unmute at End" MuteAction.UnmuteDuring="Unmute During: Unmute at Start and Mute at End" AudioFade="Fade" EnabledMatchMoving="Filter only enabled when moving" AudioMoveFilter="Audio Move" MeterType="Meter Type" MeterType.Magnitude="Magnitude" MeterType.PeakSample="Peak Sample" MeterType.PeakTrue="Peak True" MeterType.InputPeakSample="Input Peak Sample" MeterType.InputPeakTrue="Input Peak True" ValueAction="Action" ValueAction.Transform="Transform" ValueAction.Setting="Setting" ValueAction.SourceVisibility="Source Visibility" ValueAction.FilterEnable="Filter Enable" Scene="Scene" Filter="Filter" Transform.PosX="Position X" Transform.PosY="Position Y" Transform.Rotation="Rotation" Transform.Scale="Scale" Transform.ScaleX="Scale X" Transform.ScaleY="Scale Y" Transform.BoundsX="Bounds X" Transform.BoundsY="Bounds Y" Transform.CropLeft="Crop Left" Transform.CropTop="Crop Top" Transform.CropRight="Crop Right" Transform.CropBottom="Crop Bottom" Transform.CropHorizontal="Crop Horizontal" Transform.CropVertical="Crop Vertical" BaseValue="Base Value" Factor="Factor" ThresholdAction="Threshold Action" ThresholdAction.None="None" ThresholdAction.EnableOver="Enable Over" ThresholdAction.EnableUnder="Enable Under" ThresholdAction.DisableOver="Disable Over" ThresholdAction.DisableUnder="Disable Under" ThresholdAction.EnableOverDisableUnder="Enable Over and Disable Under" ThresholdAction.EnableUnderDisableOver="Enable Under and Disable Over" Threshold="Threshold" obs-move-transition-2.5.7/data/locale/es-ES.ini000066400000000000000000000211631417746432500212510ustar00rootroot00000000000000Move="Mover" MoveTransition="Transición Mover" Description="Transición que mueve todas las fuentes a una nueva posición" MoveMatch="Elementos coincidentes" MoveIn="Elementos que aparecen" MoveOut="Elementos que desaparecen" Easing="Suavizado" Easing.None="Sin suavizado" Easing.In="Suavizado de entrada" Easing.Out="Suavizado de salida" Easing.InOut="Suavizado de entrada y salida" EasingFunction="Función de suavizado" EasingFunction.Quadratic="Cuadrática" EasingFunction.Cubic="Cúbica" EasingFunction.Quartic="Cuártica" EasingFunction.Quintic="Quíntica" EasingFunction.Sine="Seno" EasingFunction.Circular="Circular" EasingFunction.Exponential="Exponencial" EasingFunction.Elastic="Elástica" EasingFunction.Bounce="Rebote" EasingFunction.Back="Regreso" Position="Posición" Zoom="Zoom" Curve="Curva" CurveOverride="Anular curva" Position.None="Ninguno" Position.Center="Centro" Position.CenterInverse="Alejado del centro" Position.TopLeft="Superior izquierda" Position.TopCenter="Superior centro" Position.TopRight="Superior derecha" Position.CenterRight="Centro derecha" Position.BottomRight="Inferior derecha" Position.BottomCenter="Inferior centro" Position.BottomLeft="Inferior izquierda" Position.CenterLeft="Centro izquierda" Position.Left="Izquierda" Position.Top="Superior" Position.Right="Derecha" Position.Bottom="Inferior" Transition="Transición" Transition.None="Ninguna" MatchName="Compara si el nombre de la fuente" NamePartMatch="contiene el nombre de la otra fuente" NameNumberMatch="con los números eliminados del final coincide con el nombre de otra fuente" NameLastWordMatch="con la última palabra eliminada coincide con el nombre de otra fuente" TransitionScaleType="Tipo de escala" TransitionScale.MaxOnly="Máx solo: Escala con relación de aspecto, sólo hasta el tamaño máximo de cada fuente" TransitionScale.Aspect="Aspecto: Escala siempre las fuentes, pero mantiene la relación de aspecto" TransitionScale.Stretch="Estirar: Escala y estira las fuentes al tamaño de la transición" MoveTransitionOverrideFilter="Mover - Anular transición" NoOverride="Sin anulación" Yes="Si" No="No" StartMove="Iniciar Mover Filtro" StartMoveMatchFrom="Iniciar Mover Filtro desde" StartMoveMatchTo="Iniciar Mover Filtro hasta" MoveSourceFilter="Mover Fuente" ScenesOnlyFilter="Este filtro sólo funciona en escenas y grupos" General="General" TransformRelative="Transformación relativa" Transform="Transformación" GetTransform="Obtener transformación" CustomDuration="Duración personalizada" Duration="Duración" Start="Iniciar" SwitchPoint="Punto de cambio" Source="Fuente" MatchSource="Coincidir con fuente" MoveAll="General" CacheTransitions="Transiciones en caché" StartTrigger="Activador de inicio" StartTrigger.None="Ninguno: No se inicia automáticamente, use una tecla de acceso rápido o el siguiente movimiento para iniciar" StartTrigger.Activate="Activar: Cuando este filtro se vuelve activo, mostrándose en la mezcla final" StartTrigger.Deactivate="Desactivar: Cuando este filtro deja de estar activo, no mostrándose en la mezcla final" StartTrigger.Show="Mostrar: Cuando este filtro se muestra en algún lugar, en cualquier previsualización o en la salida final" StartTrigger.Hide="Ocultar: Cuando este filtro no se está mostrando en ninguna parte" StartTrigger.Enable="Habilitar: Cuando el icono del ojo delante de este filtro está habilitado" StartTrigger.SourceActivate="Activar Fuente: Cuando la fuente se vuelve activa, mostrándose en la mezcla final" StartTrigger.SourceDeactivate="Desactivar Fuente: Cuando la fuente deja de estar activa, no mostrándose en la mezcla final" StartTrigger.SourceShow="Mostrar Fuente: Cuando la fuente se muestra en algún lugar, en cualquier previsualización o en la salida final" StartTrigger.SourceHide="Ocultar Fuente: Cuando la fuente no se está mostrando en ninguna parte" StartTrigger.MediaStarted="Medio Iniciado: Cuando la fuente multimedia comienza a reproducirse" StartTrigger.MediaEnded="Medio Finalizado: Cuando la reproducción de la fuente multimedia ha finalizado" StartTrigger.Load="Carga: Cuando este filtro se carga o actualiza" StopTrigger="Activador de detención" StopTrigger.None="Ninguno: Detener sólo cuando el movimiento ha terminado o este filtro está deshabilitado" Actions="Acciones" SimultaneousMove="Mover simultáneamente" SimultaneousMove.None="Ninguno" NextMove="Siguiente movimiento" NextMove.None="Ninguno" NextMove.Reverse="Revertir" NextMoveOn="Activar siguiente movimiento" NextMoveOn.End="Al finalizar el movimiento" NextMoveOn.Hotkey="Con tecla de acceso rápido" StartDelay="Retardar inicio" EndDelay="Retardar final" StartDelayTo="Retardar inicio hasta" EndDelayTo="Retardar final hasta" StartDelayFrom="Retardar inicio desde" EndDelayFrom="Retardar final desde" MoveValueFilter="Mover Valor" Filter="Filtro" Filter.None="Ninguno" MoveValueType="Tipo de valor" MoveValueType.SingleSetting="Ajuste individual" MoveValueType.Settings="Ajustes" MoveValueType.Random="Aleatorio" MoveValueType.SettingAdd="Añadir" MoveValueType.Type="Escribiendo" FormatType="Tipo de formato" FormatType.Decimals="Número de decimales" FormatType.Float="Formato flotante usando printf" FormatType.Time="Formato de tiempo usando strftime" Format="Formato" Settings="Ajustes" Setting="Ajuste" Setting.None="Ninguno" Setting.Volume="Volumen de la fuente" Decimals="Decimales" Value="Valor" MinValue="Valor Min" MaxValue="Valor Max" Text="Texto" GetValue="Obtener Valor" GetValues="Obtener Valores" VisibilityOrder="Visibilidad y Orden" ChangeVisibility="Visibilidad" ChangeVisibility.No="Sin cambios" ChangeVisibility.ShowStart="Mostrar al iniciar el movimiento" ChangeVisibility.ShowEnd="Mostrar al finalizar el movimiento" ChangeVisibility.ShowStartEnd="Mostrar mientras se realiza el movimiento" ChangeVisibility.HideStart="Ocultar al iniciar el movimiento" ChangeVisibility.HideEnd="Ocultar al finalizar el movimiento" ChangeVisibility.HideStartEnd="Ocultar mientras se realiza el movimiento" ChangeVisibility.Toggle="Alternar" ChangeVisibility.ToggleStart="Alternar al iniciar el movimiento" ChangeVisibility.ToggleEnd="Alternar al finalizar el movimiento" ChangeOrder="Orden" ChangeOrder.No="Sin cambios" ChangeOrder.StartRelative="Inicio Relativo" ChangeOrder.EndRelative="Final Relativo" ChangeOrder.StartAbsolute="Inicio Absoluto" ChangeOrder.EndAbsolute="Final Absoluto" OrderPosition="Diferencia de Orden / Posición" MediaAction="Multimedia" MediaAction.Start="Acción inicial" MediaAction.End="Acción final" MediaAction.None="Ninguna" MediaAction.Play="Reproducir" MediaAction.Pause="Pausa" MediaAction.Stop="Detener" MediaAction.Restart="Reiniciar" MediaAction.Next="Siguiente" MediaAction.Previous="Anterior" MediaAction.PlayFrom="Reproducir desde" MediaAction.PauseAt="Pausar en" MediaAction.Time="Tiempo" AudioAction="Audio" MuteAction="Silenciar" MuteAction.None="Ninguna" MuteAction.MuteStart="Silenciar al inicio" MuteAction.MuteEnd="Silenciar al finalizar" MuteAction.UnmuteStart="No silenciar al inicio" MuteAction.UnmuteEnd="No silenciar al finalizar" MuteAction.MuteDuring="Silenciar durante: Silenciar al inicio y dejar de silenciar al final" MuteAction.UnmuteDuring="No silenciar durante: No silenciar al inicio y silenciar al final" AudioFade="Desvanecimiento" EnabledMatchMoving="El filtro sólo se habilita cuando se mueve" AudioMoveFilter="Mover Audio" MeterType="Tipo de medidor" MeterType.Magnitude="Magnitud" MeterType.PeakSample="Pico de muestra" MeterType.PeakTrue="True Peak" MeterType.InputPeakSample="Entrada Pico de muestra" MeterType.InputPeakTrue="Entrada True Peak" ValueAction="Acción" ValueAction.Transform="Transformar" ValueAction.Setting="Ajuste" ValueAction.SourceVisibility="Visibilidad de la fuente" ValueAction.FilterEnable="Habilitar filtro" Scene="Escena" Filter="Filtro" Transform.PosX="Posición X" Transform.PosY="Posición Y" Transform.Rotation="Rotación" Transform.Scale="Escala" Transform.ScaleX="Escala X" Transform.ScaleY="Escala Y" Transform.BoundsX="Límites X" Transform.BoundsY="Límites Y" Transform.CropLeft="Recorte Izquierda" Transform.CropTop="Recorte Arriba" Transform.CropRight="Recorte Derecha" Transform.CropBottom="Recorte Abajo" Transform.CropHorizontal="Recorte Horizontal" Transform.CropVertical="Recorte Vertical" BaseValue="Valor Base" Factor="Factor" ThresholdAction="Acción de umbral" ThresholdAction.None="Ninguna" ThresholdAction.EnableOver="Habilitar por encima" ThresholdAction.EnableUnder="Habilitar por debajo" ThresholdAction.DisableOver="Deshabilitar por encima" ThresholdAction.DisableUnder="Deshabilitar por debajo" ThresholdAction.EnableOverDisableUnder="Habilitar por encima y deshabilitar por debajo" ThresholdAction.EnableUnderDisableOver="Habilitar por debajo y deshabilitar por encima" Threshold="Umbral" obs-move-transition-2.5.7/data/locale/nl-NL.ini000066400000000000000000000052561417746432500212620ustar00rootroot00000000000000Move="Move" MoveTransition="Move transitie" Description="Transitie die alle bronnen naar een nieuwe plek verplaats" MoveMatch="Gekoppelde items" MoveIn="Verschijnende items" MoveOut="Verdwijnende items" Easing="Soepel" Easing.None="Niet soepel" Easing.In="Soepel in" Easing.Out="Soepel uit" Easing.InOut="Soepel in en uit" EasingFunction="Soepel functie" EasingFunction.Quadratic="Tweedegraads" EasingFunction.Cubic="Derdegraads" EasingFunction.Quartic="Vierdegraads" EasingFunction.Quintic="Vijfdegraads" EasingFunction.Sine="Sinus" EasingFunction.Circular="Cirkelvormig" EasingFunction.Exponential="Exponentieel" EasingFunction.Elastic="Elastiek" EasingFunction.Bounce="Stuiter" EasingFunction.Back="Terug" Position="Positie" Zoom="Zoem" Curve="Kromming" CurveOverride="Overschrijf kromming" Position.None="Geen" Position.Center="Midden" Position.CenterInverse="Weg van het midden" Position.TopLeft="Linksboven" Position.TopCenter="Middenboven" Position.TopRight="Rechtsboven" Position.CenterRight="Rechtsmidden" Position.BottomRight="Rechtsonder" Position.BottomCenter="Middenonder" Position.BottomLeft="Linksonder" Position.CenterLeft="Linksmidden" Position.Left="Links" Position.Top="Boven" Position.Right="Rechts" Position.Bottom="Beneden" Transition="Transitie" Transition.None="Geen" MatchName="Match als de bron naam" NamePartMatch="de andere bron naam bevat" NameNumberMatch="met cijfers verwijderd van het einde overeen komt met de andere bron naam" NameLastWordMatch="met het laatste woord verwijderd overeen komt met de andere bron naam" TransitionScaleType="Transitie move schaal type" TransitionScale.MaxOnly="Alleen max: Schaal naar aspectverhouding tot maximum de grootte van de bron" TransitionScale.Aspect="Aspect: Altijd de bronnen schalen, maar behoud de aspectverhouding" TransitionScale.Stretch="Uitrekken: Schaalt en rekt de bronnen naar de grootte van transistie" MoveTransitionOverrideFilter="Move transitie overschrijven" NoOverride="Niet overschrijven" Yes="Ja" No="Nee" MoveSourceFilter="Move Bron" ScenesOnlyFilter="Dit filter werkt alleen op scenes" Transform="Transformatie" GetTransform="Transformatie ophalen" Duration="Duur" Start="Start" SwitchPoint="Schakel Punt" Source="Bron" MatchSource="Match bron" MoveAll="Algemeen" CacheTransitions="Cache transities" StartTrigger="Start trigger" StartTrigger.None="Geen" StartTrigger.Activate="Activeren" StartTrigger.Deactivate="Deactiveren" StartTrigger.Show="Tonen" StartTrigger.Hide="Verbergen" StartTrigger.Enable="Inschakelen" NextMove="Volgende Move" NextMove.None="Geen" StartDelay="Start vertraging" MoveValueFilter="Move waarde" Filter="Filter" Filter.None="Geen" Setting="Instelling" Setting.None="Geen" Value="Waarde" GetValue="Waarde ophalen" obs-move-transition-2.5.7/data/locale/pt-BR.ini000066400000000000000000000207211417746432500212600ustar00rootroot00000000000000Move="Mover" MoveTransition="Mover Transição" Description="Transição que move todas as fontes para uma nova posição" MoveMatch="Itens Correspondidos" MoveIn="Itens Aparecendo" MoveOut="Itens Desaparecendo" Easing="Suavização" Easing.None="Não suavizar" Easing.In="Suavizar entrada" Easing.Out="Suavizar saída" Easing.InOut="Suavizar entrada e saída" EasingFunction="Função de Suavização" EasingFunction.Quadratic="Quadrática" EasingFunction.Cubic="Cúbica" EasingFunction.Quartic="Quártica" EasingFunction.Quintic="Quíntica" EasingFunction.Sine="Senoidal" EasingFunction.Circular="Circular" EasingFunction.Exponential="Exponencial" EasingFunction.Elastic="Elástica" EasingFunction.Bounce="Salto" EasingFunction.Back="Retorno" Position="Posição" Zoom="Ampliar" Curve="Curva" CurveOverride="Sobrepor Curva" Position.None="Nenhuma" Position.Center="Centralizada" Position.CenterInverse="Longe do centro" Position.TopLeft="Superior esquerda" Position.TopCenter="Superior centralizada" Position.TopRight="Superior direita" Position.CenterRight="Centro direita" Position.BottomRight="Inferior direita" Position.BottomCenter="Inferior centralizada" Position.BottomLeft="Inferior esquerda" Position.CenterLeft="Centro esquerda" Position.Left="Esquerda" Position.Top="Superior" Position.Right="Direita" Position.Bottom="Inferior" Transition="Transição" Transition.None="Nenhuma" MatchName="Corresponder se o nome da fonte" NamePartMatch="contém o nome da outra fonte" NameNumberMatch="com números removidos do final, coincide com o nome da outra fonte" NameLastWordMatch="com a última palavra removida, coincide com o nome da outra fonte" TransitionScaleType="Escala de Transição" TransitionScale.MaxOnly="Máxima: escalar para a proporção, mas apenas para o tamanho máximo de cada fonte" TransitionScale.Aspect="Aspecto: sempre escalar as fontes, mas manter a proporção" TransitionScale.Stretch="Esticada: escalar e esticar as fontes para o tamanho da transição" MoveTransitionOverrideFilter="Mover/Sobrepor Transição" NoOverride="Não sobrepor" Yes="Sim" No="Não" StartMove="Iniciar Filtro Mover" StartMoveMatchFrom="Iniciar Filtro Mover de" StartMoveMatchTo="Iniciar Filtro Mover para" MoveSourceFilter="Mover/Fonte" ScenesOnlyFilter="Este filtro funciona apenas em cenas e grupos" General="Configurações gerais" TransformRelative="Transformação Relativa" Transform="Transformação" GetTransform="Obter Transformação" CustomDuration="Duração Customizada" Duration="Duração" Start="Iniciar" SwitchPoint="Ponto de Mudança" Source="Fonte" MatchSource="Corresponder com a fonte" MoveAll="Configurações gerais" CacheTransitions="Cache de Transições" StartTrigger="Gatilho de Início" StartTrigger.None="Nenhum: não é iniciado automaticamente, use uma tecla de atalho ou mover próximo para iniciar esta transição" StartTrigger.Activate="Ativado: quando este filtro se torna ativo mostrando na saída final" StartTrigger.Deactivate="Desativado: quando este filtro fica inativo, não mostrando na saída final" StartTrigger.Show="Mostrado: este filtro está sendo exibido em qualquer lugar, seja em um contexto de exibição ou na saída final" StartTrigger.Hide="Ocultado: quando este filtro não está sendo exibido em nenhum lugar" StartTrigger.Enable="Habilitado: quando o ícone de olho na frente deste filtro está habilitado" StartTrigger.SourceActivate="Fonte Ativada: quando a fonte se torna ativa mostrando na saída final" StartTrigger.SourceDeactivate="Fonte Desativada: quando a fonte fica inativa, não mostrando na saída final" StartTrigger.SourceShow="Fonte Visível: quando a fonte está sendo exibida em qualquer lugar, seja em um contexto de exibição ou na saída final" StartTrigger.SourceHide="Fonte Ocultada: quando a fonte não está sendo exibida em nenhum lugar" StartTrigger.MediaStarted="Mídia Iniciada: quando a fonte de mídia começar a tocar" StartTrigger.MediaEnded="Mídia Finalizada: quando a reprodução da fonte de mídia terminar" StartTrigger.Load="Carregado: quando este filtro for carregado ou atualizado" StopTrigger="Gatilho de Parada" StopTrigger.None="Nenhum: parar apenas quando a movimentação for concluída ou este filtro for desativado" Actions="Ações" SimultaneousMove="Mover Simultaneamente" SimultaneousMove.None="Não mover" NextMove="Próxima Ação" NextMove.None="Nenhuma" NextMove.Reverse="Reverter" NextMoveOn="Próxima Ação ao" NextMoveOn.End="Finalizar o movimento" NextMoveOn.Hotkey="Usar uma tecla de atalho" StartDelay="Atraso Inicial" EndDelay="Atraso Final" StartDelayTo="Atraso Inicial para" EndDelayTo="Atraso Final para" StartDelayFrom="Atraso Inicial de" EndDelayFrom="Atraso Final de" MoveValueFilter="Mover/Valor" Filter="Filtro" Filter.None="Nenhum" MoveValueType="Mover/Valor Tipo" MoveValueType.SingleSetting="Propriedade Individual" MoveValueType.Settings="Propriedades" MoveValueType.Random="Aleatório" MoveValueType.SettingAdd="Adicionar" MoveValueType.Type="Digitando" FormatType="Tipo de formato" FormatType.Decimals="Número de decimais" FormatType.Float="Formato flutuante usando printf" FormatType.Time="Formato de tempo usando strftime" Format="Formato" Settings="Propriedades" Setting="Propriedade" Setting.None="Nenhuma" Decimals="Decimais" Value="Valor" MinValue="Mínimo" MaxValue="Máximo" Text="Texto" GetValue="Obter Valor" GetValues="Obter Valores" VisibilityOrder="Visibilidade e Ordenação" ChangeVisibility="Visibilidade" ChangeVisibility.No="Não alterar" ChangeVisibility.ShowStart="Mostrar no início do movimento" ChangeVisibility.ShowEnd="Mostrar no final do movimento" ChangeVisibility.ShowStartEnd="Mostrar durante o movimento" ChangeVisibility.HideStart="Ocultar no início do movimento" ChangeVisibility.HideEnd="Ocultar no final do movimento" ChangeVisibility.HideStartEnd="Ocultar durante o movimento" ChangeVisibility.Toggle="Alternar durante o movimento" ChangeVisibility.ToggleStart="Alternar no início do movimento" ChangeVisibility.ToggleEnd="Alternar no final do movimento" ChangeOrder="Ordenação" ChangeOrder.No="Não alterar" ChangeOrder.StartRelative="Início relativo" ChangeOrder.EndRelative="Final relativo" ChangeOrder.StartAbsolute="Início absoluto" ChangeOrder.EndAbsolute="Final absoluto" OrderPosition="Diferença / Posição" MediaAction="Mídia" MediaAction.Start="Iniciar ação" MediaAction.End="Finalizar ação" MediaAction.None="Nenhuma" MediaAction.Play="Reproduzir" MediaAction.Pause="Pausar" MediaAction.Stop="Parar" MediaAction.Restart="Reiniciar" MediaAction.Next="Próximo" MediaAction.Previous="Anterior" MediaAction.PlayFrom="Reproduzir de" MediaAction.PauseAt="Pausar em" MediaAction.Time="Tempo" AudioAction="Ãudio" MuteAction="Silenciar" MuteAction.None="Nenhum" MuteAction.MuteStart="Silenciar no início" MuteAction.MuteEnd="Silenciar no final" MuteAction.UnmuteStart="Ativar no início" MuteAction.UnmuteEnd="Reativar no final" MuteAction.MuteDuring="Silenciar Durante: silenciar no início e reativar no final" MuteAction.UnmuteDuring="Ativar Durante: ativar no início e silenciar no final" AudioFade="Desvanecimento" EnabledMatchMoving="Habilitar filtro apenas ao mover" AudioMoveFilter="Mover/Ãudio" MeterType="Tipo de Medidor" MeterType.Magnitude="Magnitude" MeterType.PeakSample="Amostra de pico" MeterType.PeakTrue="Pico verdadeiro" MeterType.InputPeakSample="Amostra de pico de entrada" MeterType.InputPeakTrue="Pico de entrada verdadeiro" ValueAction="Ação" ValueAction.Transform="Transformar" ValueAction.Setting="Configurar" ValueAction.SourceVisibility="Alterar visibilidade da fonte" ValueAction.FilterEnable="Habilitar filtro" Scene="Cena" Filter="Filtro" Transform.PosX="Posicionar em X" Transform.PosY="Posicionar em Y" Transform.Rotation="Rotacionar" Transform.Scale="Escalar" Transform.ScaleX="Escalar em X" Transform.ScaleY="Escalar em Y" Transform.BoundsX="Limitar em X" Transform.BoundsY="Limitar em Y" Transform.CropLeft="Cortar à esquerda" Transform.CropTop="Cortar parte superior" Transform.CropRight="Cortar à direita" Transform.CropBottom="Cortar parte inferior" Transform.CropHorizontal="Cortar na horizontal" Transform.CropVertical="Cortar na vertical" BaseValue="Valor Base" Factor="Fator" ThresholdAction="Ação Limiar" ThresholdAction.None="Nenhuma" ThresholdAction.EnableOver="Habilitar acima" ThresholdAction.EnableUnder="Habilitar abaixo" ThresholdAction.DisableOver="Desabilitar acima" ThresholdAction.DisableUnder="Desabilitar abaixo" ThresholdAction.EnableOverDisableUnder="Habilitar acima e desabilitar abaixo" ThresholdAction.EnableUnderDisableOver="Habilitar abaixo e desabilitar acima" Threshold="Limiar" obs-move-transition-2.5.7/data/locale/ru-RU.ini000066400000000000000000000224701417746432500213110ustar00rootroot00000000000000Move="Движение" MoveTransition="ДвижущийÑÑ ÐŸÐµÑ€ÐµÑ…Ð¾Ð´" Description="Переход который перемещает вÑе иÑточники в новую позицию" MoveMatch="Одинаковые Ñлементы" MoveIn="ПоÑвлÑющиеÑÑ Ñлементы" MoveOut="ИÑчезающие Ñлементы" Easing="ÐнимациÑ" Easing.None="Ðет" Easing.In="Плавный вход" Easing.Out="Плавный выход" Easing.InOut="Плавный вход и выход" EasingFunction="Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¿Ð»Ð°Ð²Ð½Ð¾Ñти" EasingFunction.Quadratic="КвадратичнаÑ" EasingFunction.Cubic="КубичеÑкаÑ" EasingFunction.Quartic="ЧетвертичнаÑ" EasingFunction.Quintic="КвинтичеÑкаÑ" EasingFunction.Sine="СинуÑоваÑ" EasingFunction.Circular="КруговаÑ" EasingFunction.Exponential="ЭкÑпоненциальнаÑ" EasingFunction.Elastic="УпругаÑ" EasingFunction.Bounce="ОтÑкакивающаÑ" EasingFunction.Back="ОбратнаÑ" Position="ПозициÑ" Zoom="Увеличение" Curve="КриваÑ" CurveOverride="Указать другую кривую" Position.None="Ðет" Position.Center="Центр" Position.CenterInverse="Вдали от центра" Position.TopLeft="Сверху Ñлева" Position.TopCenter="Сверху по центру" Position.TopRight="Сверху Ñправа" Position.CenterRight="Справа по центру" Position.BottomRight="Снизу Ñправа" Position.BottomCenter="Снизу по центру" Position.BottomLeft="Слева Ñнизу" Position.CenterLeft="Слева по центру" Position.Left="Слева" Position.Top="Сверху" Position.Right="Справа" Position.Bottom="Снизу" Transition="Переход" Transition.None="Ðет" MatchName="СоответÑтвовать еÑли Ð¸Ð¼Ñ Ð¸Ñточника" NamePartMatch="Ñодержит другое Ð¸Ð¼Ñ Ð¸Ñточника" NameNumberMatch="Ñодержит другое Ð¸Ð¼Ñ Ð¸Ñточника без цифр в конце" NameLastWordMatch="Ñодержит другое Ð¸Ð¼Ñ Ð¸Ñточника без Ñлова в конце" TransitionScaleType="Тип маÑштабированного перехода" TransitionScale.MaxOnly="МакÑимальный: маÑштабировать до ÑÐ¾Ð¾Ñ‚Ð½Ð¾ÑˆÐµÐ½Ð¸Ñ Ñторон,но только до макÑимального размера каждого иÑточника" TransitionScale.Aspect="ÐÑпект: вÑегда маÑштабировать иÑточники,но ÑохранÑть Ñоотношение Ñторон " TransitionScale.Stretch="РаÑÑ‚Ñгивание: маÑштабировать и раÑÑ‚Ñгивать иÑточники до размера перехода" MoveTransitionOverrideFilter="Указать другой движущийÑÑ Ð¿ÐµÑ€ÐµÑ…Ð¾Ð´" NoOverride="Ðе указывать" Yes="Да" No="Ðет" MoveSourceFilter="ПеремеÑтить иÑточник" ScenesOnlyFilter="Этот фильтр работает только на Ñценах и группах" General="Общие" TransformRelative="ТранÑформировать ОтноÑительно" Transform="ТранÑформировать" GetTransform="Получить транÑформацию" CustomDuration="ÐаÑÑ‚Ñ€Ð°Ð¸Ð²Ð°ÐµÐ¼Ð°Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñть" Duration="ПродолжительноÑть" Start="Ðачать" SwitchPoint="Точка переключениÑ" Source="ИÑточник" MoveAll="Общие" CacheTransitions="КÑшировать Переходы" StartTrigger="ЗапуÑк Триггера" StartTrigger.None="Ðет: не запуÑкать автоматичеÑки,иÑпользовать горÑчую клавишу или Ñледующие движение чтобы запуÑтить Ñто движение" StartTrigger.Activate="Ðктивировать: Когда Ñтот фильтр ÑтановитÑÑ Ð²Ð¸Ð´ÐµÐ½ в финальном микÑе" StartTrigger.Deactivate="Деактивировать: Когда Ñтот фильтр больше не виден, он не отображаетÑÑ Ð² финальном микÑе" StartTrigger.Show="Показать: Этот фильтр виден хоть где-то, либо в контекÑте диÑплеÑ, либо в финальном выводе" StartTrigger.Hide="Скрыть: Когда Ñтот фильтр нигде не виден" StartTrigger.Enable="Включить: Когда значок напротив Ñтого фильтра включен" StartTrigger.EnableDisable="Включить & Выключить: Триггер на включение и выключение когда движение закончено" StartTrigger.SourceActivate="Ðктивированный ИÑточник: Когда иÑточник ÑтановитÑÑ Ð²Ð¸Ð´ÐµÐ½ в финальном микÑе" StartTrigger.SourceDeactivate="Деактивированный ИÑточник: Когда иÑточник больше не виден, он не отображаетÑÑ Ð² финальном микÑе" StartTrigger.SourceShow="ИÑточник Виден: ИÑточник виден хоть где-то, либо в контекÑте диÑплеÑ, либо в финальном выводе" StartTrigger.SourceHide="ИÑточник Скрыт: Когда иÑточник нигде не виден" StartTrigger.MediaStarted="Медиа Запущено: Когда медиа иÑточника начинает воÑпроизводитÑÑ" StartTrigger.MediaEnded="Медиа Завершено: Когда медиа иÑточника завершено" StopTrigger="ОÑтановка Триггера" StopTrigger.None="Ðет: оÑтанавливатÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ еÑли движение закончено либо Ñтот фильтр отключен" Actions="ДейÑтвиÑ" SimultaneousMove="Одновременное Движение" SimultaneousMove.None="Ðет" NextMove="Следующее Движение" NextMove.None="Ðет" NextMove.Reverse="Обратное" NextMoveOn="Следующее Движение Ðа" NextMoveOn.End="Конец ДвижениÑ" NextMoveOn.Hotkey="ГорÑчую клавишу" StartDelay="Задержка ЗапуÑка" EndDelay="Задержка Конца" StartDelayTo="Задержка ЗапуÑка До" EndDelayTo="Задержка Конца До" StartDelayFrom="Задержка ЗапуÑка От" EndDelayFrom="Задержка Конца От" MoveValueFilter="Движение ЗначениÑ" Filter="Фильтр" Filter.None="Ðет" SingleSetting="ÐžÐ´Ð¸Ð½Ð¾Ñ‡Ð½Ð°Ñ ÐаÑтройка" Settings="ÐаÑтройки" Setting="Параметр" Setting.None="Ðет" Value="Значение" GetValue="Получить Значение" GetValues="Получить ЗначениÑ" VisibilityOrder="ВидимоÑть И ПорÑдок" ChangeVisibility="ВидимоÑть" ChangeVisibility.No="Ðе менÑть" ChangeVisibility.ShowStart="Показать в начале движениÑ" ChangeVisibility.ShowEnd="Показать в конце движениÑ" ChangeVisibility.ShowStartEnd="Показать во Ð²Ñ€ÐµÐ¼Ñ Ð´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ" ChangeVisibility.HideStart="Скрыть в начале движениÑ" ChangeVisibility.HideEnd="Скрыть в конце движениÑ" ChangeVisibility.HideStartEnd="Скрыть во Ð²Ñ€ÐµÐ¼Ñ Ð´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ" ChangeVisibility.Toggle="Переключить" ChangeVisibility.ToggleStart="Переключить в начале движениÑ" ChangeVisibility.ToggleEnd="Переключить в конце движениÑ" ChangeOrder="ПорÑдок" ChangeOrder.No="Ðе менÑть" ChangeOrder.StartRelative="ЗапуÑк ОтноÑительно" ChangeOrder.EndRelative="Конец ОтноÑительно" ChangeOrder.StartAbsolute="ЗапуÑк ÐбÑолютно" ChangeOrder.EndAbsolute="Конец ÐбÑолютно" OrderPosition="Разница / ПозициÑ" MediaAction="МедиÑ" MediaAction.Start="ЗапуÑк ДейÑтвиÑ" MediaAction.End="Конец ДейÑтвиÑ" MediaAction.None="Ðет" MediaAction.Play="ВоÑпроизвеÑти" MediaAction.Pause="ПриоÑтановить" MediaAction.Stop="ОÑтановить" MediaAction.Restart="ПерезапуÑтить" MediaAction.Next="Следующее" MediaAction.Previous="Предыдущее" MediaAction.PlayFrom="ВоÑпроизвеÑти Ñ" MediaAction.PauseAt="ПриоÑтановить в" MediaAction.Time="ВремÑ" AudioAction="Ðудио" MuteAction="Отключить звук" MuteAction.None="Ðет" MuteAction.MuteStart="Заглушить при ЗапуÑке" MuteAction.MuteEnd="Заглушить в Конце" MuteAction.UnmuteStart="Включить звук при ЗапуÑке" MuteAction.UnmuteEnd="Включить звук в Конце" MuteAction.MuteDuring="Выключить звук во времÑ: Выключить при ЗапуÑке Включить в Конце" MuteAction.UnmuteDuring="Включить звук во времÑ: Включить при ЗапуÑке Выключить в Конце" AudioFade="Затухание" obs-move-transition-2.5.7/easing.c000066400000000000000000000140151417746432500170740ustar00rootroot00000000000000// // easing.c // // Copyright (c) 2011, Auerhaus Development, LLC // // This program is free software. It comes without any warranty, to // the extent permitted by applicable law. You can redistribute it // and/or modify it under the terms of the Do What The Fuck You Want // To Public License, Version 2, as published by Sam Hocevar. See // http://sam.zoy.org/wtfpl/COPYING for more details. // #include #include "easing.h" // Modeled after the line y = x AHFloat LinearInterpolation(AHFloat p) { return p; } // Modeled after the parabola y = x^2 AHFloat QuadraticEaseIn(AHFloat p) { return p * p; } // Modeled after the parabola y = -x^2 + 2x AHFloat QuadraticEaseOut(AHFloat p) { return -(p * (p - 2)); } // Modeled after the piecewise quadratic // y = (1/2)((2x)^2) ; [0, 0.5) // y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1] AHFloat QuadraticEaseInOut(AHFloat p) { if (p < 0.5) { return 2 * p * p; } else { return (-2 * p * p) + (4 * p) - 1; } } // Modeled after the cubic y = x^3 AHFloat CubicEaseIn(AHFloat p) { return p * p * p; } // Modeled after the cubic y = (x - 1)^3 + 1 AHFloat CubicEaseOut(AHFloat p) { AHFloat f = (p - 1); return f * f * f + 1; } // Modeled after the piecewise cubic // y = (1/2)((2x)^3) ; [0, 0.5) // y = (1/2)((2x-2)^3 + 2) ; [0.5, 1] AHFloat CubicEaseInOut(AHFloat p) { if (p < 0.5) { return 4 * p * p * p; } else { AHFloat f = ((2 * p) - 2); return 0.5 * f * f * f + 1; } } // Modeled after the quartic x^4 AHFloat QuarticEaseIn(AHFloat p) { return p * p * p * p; } // Modeled after the quartic y = 1 - (x - 1)^4 AHFloat QuarticEaseOut(AHFloat p) { AHFloat f = (p - 1); return f * f * f * (1 - p) + 1; } // Modeled after the piecewise quartic // y = (1/2)((2x)^4) ; [0, 0.5) // y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1] AHFloat QuarticEaseInOut(AHFloat p) { if (p < 0.5) { return 8 * p * p * p * p; } else { AHFloat f = (p - 1); return -8 * f * f * f * f + 1; } } // Modeled after the quintic y = x^5 AHFloat QuinticEaseIn(AHFloat p) { return p * p * p * p * p; } // Modeled after the quintic y = (x - 1)^5 + 1 AHFloat QuinticEaseOut(AHFloat p) { AHFloat f = (p - 1); return f * f * f * f * f + 1; } // Modeled after the piecewise quintic // y = (1/2)((2x)^5) ; [0, 0.5) // y = (1/2)((2x-2)^5 + 2) ; [0.5, 1] AHFloat QuinticEaseInOut(AHFloat p) { if (p < 0.5) { return 16 * p * p * p * p * p; } else { AHFloat f = ((2 * p) - 2); return 0.5 * f * f * f * f * f + 1; } } // Modeled after quarter-cycle of sine wave AHFloat SineEaseIn(AHFloat p) { return sin((p - 1) * M_PI_2) + 1; } // Modeled after quarter-cycle of sine wave (different phase) AHFloat SineEaseOut(AHFloat p) { return sin(p * M_PI_2); } // Modeled after half sine wave AHFloat SineEaseInOut(AHFloat p) { return 0.5 * (1 - cos(p * M_PI)); } // Modeled after shifted quadrant IV of unit circle AHFloat CircularEaseIn(AHFloat p) { return 1 - sqrt(1 - (p * p)); } // Modeled after shifted quadrant II of unit circle AHFloat CircularEaseOut(AHFloat p) { return sqrt((2 - p) * p); } // Modeled after the piecewise circular function // y = (1/2)(1 - sqrt(1 - 4x^2)) ; [0, 0.5) // y = (1/2)(sqrt(-(2x - 3)*(2x - 1)) + 1) ; [0.5, 1] AHFloat CircularEaseInOut(AHFloat p) { if (p < 0.5) { return 0.5 * (1 - sqrt(1 - 4 * (p * p))); } else { return 0.5 * (sqrt(-((2 * p) - 3) * ((2 * p) - 1)) + 1); } } // Modeled after the exponential function y = 2^(10(x - 1)) AHFloat ExponentialEaseIn(AHFloat p) { return (p == 0.0) ? p : pow(2, 10 * (p - 1)); } // Modeled after the exponential function y = -2^(-10x) + 1 AHFloat ExponentialEaseOut(AHFloat p) { return (p == 1.0) ? p : 1 - pow(2, -10 * p); } // Modeled after the piecewise exponential // y = (1/2)2^(10(2x - 1)) ; [0,0.5) // y = -(1/2)*2^(-10(2x - 1))) + 1 ; [0.5,1] AHFloat ExponentialEaseInOut(AHFloat p) { if (p == 0.0 || p == 1.0) return p; if (p < 0.5) { return 0.5 * pow(2, (20 * p) - 10); } else { return -0.5 * pow(2, (-20 * p) + 10) + 1; } } // Modeled after the damped sine wave y = sin(13pi/2*x)*pow(2, 10 * (x - 1)) AHFloat ElasticEaseIn(AHFloat p) { return sin(13 * M_PI_2 * p) * pow(2, 10 * (p - 1)); } // Modeled after the damped sine wave y = sin(-13pi/2*(x + 1))*pow(2, -10x) + 1 AHFloat ElasticEaseOut(AHFloat p) { return sin(-13 * M_PI_2 * (p + 1)) * pow(2, -10 * p) + 1; } // Modeled after the piecewise exponentially-damped sine wave: // y = (1/2)*sin(13pi/2*(2*x))*pow(2, 10 * ((2*x) - 1)) ; [0,0.5) // y = (1/2)*(sin(-13pi/2*((2x-1)+1))*pow(2,-10(2*x-1)) + 2) ; [0.5, 1] AHFloat ElasticEaseInOut(AHFloat p) { if (p < 0.5) { return 0.5 * sin(13 * M_PI_2 * (2 * p)) * pow(2, 10 * ((2 * p) - 1)); } else { return 0.5 * (sin(-13 * M_PI_2 * ((2 * p - 1) + 1)) * pow(2, -10 * (2 * p - 1)) + 2); } } // Modeled after the overshooting cubic y = x^3-x*sin(x*pi) AHFloat BackEaseIn(AHFloat p) { return p * p * p - p * sin(p * M_PI); } // Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi)) AHFloat BackEaseOut(AHFloat p) { AHFloat f = (1 - p); return 1 - (f * f * f - f * sin(f * M_PI)); } // Modeled after the piecewise overshooting cubic function: // y = (1/2)*((2x)^3-(2x)*sin(2*x*pi)) ; [0, 0.5) // y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1] AHFloat BackEaseInOut(AHFloat p) { if (p < 0.5) { AHFloat f = 2 * p; return 0.5 * (f * f * f - f * sin(f * M_PI)); } else { AHFloat f = (1 - (2 * p - 1)); return 0.5 * (1 - (f * f * f - f * sin(f * M_PI))) + 0.5; } } AHFloat BounceEaseIn(AHFloat p) { return 1 - BounceEaseOut(1 - p); } AHFloat BounceEaseOut(AHFloat p) { if (p < 4 / 11.0) { return (121 * p * p) / 16.0; } else if (p < 8 / 11.0) { return (363 / 40.0 * p * p) - (99 / 10.0 * p) + 17 / 5.0; } else if (p < 9 / 10.0) { return (4356 / 361.0 * p * p) - (35442 / 1805.0 * p) + 16061 / 1805.0; } else { return (54 / 5.0 * p * p) - (513 / 25.0 * p) + 268 / 25.0; } } AHFloat BounceEaseInOut(AHFloat p) { if (p < 0.5) { return 0.5 * BounceEaseIn(p * 2); } else { return 0.5 * BounceEaseOut(p * 2 - 1) + 0.5; } } obs-move-transition-2.5.7/easing.h000066400000000000000000000045241417746432500171050ustar00rootroot00000000000000// // easing.h // // Copyright (c) 2011, Auerhaus Development, LLC // // This program is free software. It comes without any warranty, to // the extent permitted by applicable law. You can redistribute it // and/or modify it under the terms of the Do What The Fuck You Want // To Public License, Version 2, as published by Sam Hocevar. See // http://sam.zoy.org/wtfpl/COPYING for more details. // #ifndef AH_EASING_H #define AH_EASING_H #if defined(__LP64__) && !defined(AH_EASING_USE_DBL_PRECIS) #define AH_EASING_USE_DBL_PRECIS #endif #ifdef AH_EASING_USE_DBL_PRECIS #define AH_FLOAT_TYPE float #else #define AH_FLOAT_TYPE float #endif typedef AH_FLOAT_TYPE AHFloat; #if defined __cplusplus extern "C" { #endif #ifndef M_PI #define M_PI 3.14159265358979323846 /* pi */ #endif #ifndef M_PI_2 #define M_PI_2 1.57079632679489661923 /* pi/2 */ #endif typedef AHFloat (*AHEasingFunction)(AHFloat); // Linear interpolation (no easing) AHFloat LinearInterpolation(AHFloat p); // Quadratic easing; p^2 AHFloat QuadraticEaseIn(AHFloat p); AHFloat QuadraticEaseOut(AHFloat p); AHFloat QuadraticEaseInOut(AHFloat p); // Cubic easing; p^3 AHFloat CubicEaseIn(AHFloat p); AHFloat CubicEaseOut(AHFloat p); AHFloat CubicEaseInOut(AHFloat p); // Quartic easing; p^4 AHFloat QuarticEaseIn(AHFloat p); AHFloat QuarticEaseOut(AHFloat p); AHFloat QuarticEaseInOut(AHFloat p); // Quintic easing; p^5 AHFloat QuinticEaseIn(AHFloat p); AHFloat QuinticEaseOut(AHFloat p); AHFloat QuinticEaseInOut(AHFloat p); // Sine wave easing; sin(p * PI/2) AHFloat SineEaseIn(AHFloat p); AHFloat SineEaseOut(AHFloat p); AHFloat SineEaseInOut(AHFloat p); // Circular easing; sqrt(1 - p^2) AHFloat CircularEaseIn(AHFloat p); AHFloat CircularEaseOut(AHFloat p); AHFloat CircularEaseInOut(AHFloat p); // Exponential easing, base 2 AHFloat ExponentialEaseIn(AHFloat p); AHFloat ExponentialEaseOut(AHFloat p); AHFloat ExponentialEaseInOut(AHFloat p); // Exponentially-damped sine wave easing AHFloat ElasticEaseIn(AHFloat p); AHFloat ElasticEaseOut(AHFloat p); AHFloat ElasticEaseInOut(AHFloat p); // Overshooting cubic easing; AHFloat BackEaseIn(AHFloat p); AHFloat BackEaseOut(AHFloat p); AHFloat BackEaseInOut(AHFloat p); // Exponentially-decaying bounce easing AHFloat BounceEaseIn(AHFloat p); AHFloat BounceEaseOut(AHFloat p); AHFloat BounceEaseInOut(AHFloat p); #ifdef __cplusplus } #endif #endif obs-move-transition-2.5.7/installer.iss.in000066400000000000000000000077231417746432500206140ustar00rootroot00000000000000; Script generated by the Inno Setup Script Wizard. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "@PROJECT_FULL_NAME@" #define MyAppVersion "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@" #define MyAppPublisher "Exeldro" [Setup] ; NOTE: The value of AppId uniquely identifies this application. ; Do not use the same AppId value in installers for other applications. ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) ; app Information AppId={{83443BC3-6FCC-4A35-922E-1FF66F294AA4}} AppName={#MyAppName} AppVersion={#MyAppVersion} AppPublisher={#MyAppPublisher} AppMutex={#MyAppName} VersionInfoVersion={#MyAppVersion} VersionInfoCompany={#MyAppPublisher} VersionInfoDescription={#MyAppName} Setup ; Compression Compression=lzma2/ultra64 SolidCompression=yes LZMAAlgorithm=1 ; Other Information DefaultDirName={code:GetDirName} DefaultGroupName={#MyAppName} AllowNoIcons=yes OutputDir="@ISS_FILES_DIR@" OutputBaseFilename=@PROJECT_NAME@-installer DirExistsWarning=no ; Wizard Information WizardStyle=modern WizardResizable=yes SetupIconFile="@PROJECT_SOURCE_DIR@/media/icon.ico" [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" [Files] Source: "@ISS_FILES_DIR@/*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs Source: "@ISS_PACKAGE_DIR@/msvc-redist-helper.exe"; DestDir: "{app}"; DestName: "msvc-redist-helper.exe"; Flags: ignoreversion dontcopy noencryption ; NOTE: Don't use "Flags: ignoreversion" on any shared system files [Icons] Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}" [Code] function GetDirName(Value: string): string; var InstallPath: string; begin // initialize default path, which will be returned when the following registry // key queries fail due to missing keys or for some different reason Result := ExpandConstant('{pf}\obs-studio'); // query the first registry value; if this succeeds, return the obtained value if RegQueryStringValue(HKLM32, 'SOFTWARE\OBS Studio', '', InstallPath) then Result := InstallPath end; ///////////////////////////////////////////////////////////////////// function GetUninstallString(): String; var sUnInstPath: String; sUnInstallString: String; begin sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1'); sUnInstallString := ''; if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString); Result := sUnInstallString; end; ///////////////////////////////////////////////////////////////////// function IsUpgrade(): Boolean; begin Result := (GetUninstallString() <> ''); end; ///////////////////////////////////////////////////////////////////// function UnInstallOldVersion(): Integer; var sUnInstallString: String; iResultCode: Integer; begin // Return Values: // 1 - uninstall string is empty // 2 - error executing the UnInstallString // 3 - successfully executed the UnInstallString // default return value Result := 0; // get the uninstall string of the old app sUnInstallString := GetUninstallString(); if sUnInstallString <> '' then begin sUnInstallString := RemoveQuotes(sUnInstallString); if Exec(sUnInstallString, '/VERYSILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResultCode) then Result := 3 else Result := 2; end else Result := 1; end; ///////////////////////////////////////////////////////////////////// procedure CurStepChanged(CurStep: TSetupStep); var ResultCode: Integer; begin if (CurStep=ssInstall) then begin if (IsUpgrade()) then begin UnInstallOldVersion(); end; end; if (CurStep=ssPostInstall) then begin ExtractTemporaryFile('msvc-redist-helper.exe'); Exec(ExpandConstant('{tmp}\msvc-redist-helper.exe'), '2019', '', SW_HIDE, ewWaitUntilTerminated, ResultCode); end; end; obs-move-transition-2.5.7/media/000077500000000000000000000000001417746432500165405ustar00rootroot00000000000000obs-move-transition-2.5.7/media/icon.ico000066400000000000000000000734261417746432500202000ustar00rootroot00000000000000 hV ˆ ¾  ¨F00 ¨%îßæ€2–D(  ÃÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿ¸¸¸ÿ¸¸¸ÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõÿáááÿùùùÿÿÿÿÿøøøÿ„„„ÿ„„„ÿøøøÿÿÿÿÿõõõÿâââÿúúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼¼¼ÿQQQÿãããÿÿÿÿÿÿÿÿÿÐÐÐÿÐÐÐÿÿÿÿÿÿÿÿÿÍÍÍÿMMMÿÜÜÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéééÿÂÂÂÿ±±±ÿóóóÿÿÿÿÿÒÒÒÿÒÒÒÿÿÿÿÿæææÿ¬¬¬ÿÂÂÂÿðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÚÚÿ½½½ÿ»»»ÿ§§§ÿ§§§ÿ¼¼¼ÿ½½½ÿèèèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûûÿüüüÿÿÿÿÿÿÿÿÿ™™™ÿÿÿÿÿÅÅÅÿÿÿÿÿÿÿÿÿöööÿüüüÿÿÿÿÿúúúÿŠŠŠÿŸŸŸÿÌÌÌÿÏÏÏÿˆˆˆÿÿÿÿ ÿ©©©ÿÆÆÆÿÆÆÆÿÿ¡¡¡ÿÿÿÿÿûûûÿ”””ÿ¨¨¨ÿÓÓÓÿÕÕÕÿŠŠŠÿÿÿÿ ÿ²²²ÿÚÚÚÿÚÚÚÿ›››ÿ¸¸¸ÿÿÿÿÿÿÿÿÿýýýÿýýýÿÿÿÿÿÿÿÿÿ™™™ÿÿÿÿÿÂÂÂÿÿÿÿÿÿÿÿÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÕÕÿÂÂÂÿ»»»ÿ   ÿ§§§ÿ¼¼¼ÿ···ÿÞÞÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáááÿ»»»ÿ´´´ÿöööÿÿÿÿÿËËËÿÙÙÙÿÿÿÿÿîîîÿ­­­ÿ²²²ÿîîîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­­­ÿZZZÿïïïÿÿÿÿÿÿÿÿÿÈÈÈÿÖÖÖÿÿÿÿÿÿÿÿÿËËËÿUUUÿßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóóóÿãããÿüüüÿÿÿÿÿôôôÿtttÿ‰‰‰ÿúúúÿÿÿÿÿùùùÿñññÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿ¼¼¼ÿÈÈÈÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(0 ÃÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñññÿzzzÿzzzÿñññÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÄÄÿ888ÿ888ÿÄÄÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöööÿ”””ÿ„„„ÿäääÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿ²²²ÿ²²²ÿüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÏÏÏÿ~~~ÿ¶¶¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëëëÿ///ÿ>>>ÿçççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ···ÿ···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÃÃÿÿhhhÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõÿ½½½ÿ°°°ÿ”””ÿòòòÿÿÿÿÿÿÿÿÿÿÿÿÿ¶¶¶ÿ¶¶¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÙÙÙÿˆˆˆÿ¶¶¶ÿ»»»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ···ÿ‘‘‘ÿôôôÿÿÿÿÿÿÿÿÿÀÀÀÿÀÀÀÿÿÿÿÿÿÿÿÿÚÚÚÿ………ÿÚÚÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËËËÿ³³³ÿ£££ÿ£££ÿ–––ÿ–––ÿ£££ÿ¥¥¥ÿ···ÿßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿiiiÿÿÿÿÿÿÿ±±±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÒÒÿÛÛÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿhhhÿÿÿÿÿÿÿ°°°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ···ÿáááÿÿÿÿÿÿÿÿÿÿÿÿÿÔÔÔÿ...ÿpppÿ±±±ÿ±±±ÿ±±±ÿÄÄÄÿeeeÿÿÿÿÿÿÿÿ«««ÿ¤¤¤ÿ¤¤¤ÿ¤¤¤ÿ999ÿPPPÿñññÿÿÿÿÿÿÿÿÿÛÛÛÿ<<<ÿ{{{ÿÀÀÀÿÀÀÀÿÀÀÀÿÏÏÏÿeeeÿÿÿÿÿÿÿ¥¥¥ÿÒÒÒÿÏÏÏÿÏÏÏÿÎÎÎÿVVVÿ|||ÿ÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿßßßÿãããÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿhhhÿÿÿÿÿÿÿ°°°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞÞÞÿ÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿiiiÿÿÿÿÿÿÿ¯¯¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿÇÇÇÿºººÿ£££ÿ£££ÿÿ•••ÿ£££ÿ¥¥¥ÿªªªÿÉÉÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿ«««ÿ›››ÿùùùÿÿÿÿÿÿÿÿÿ¯¯¯ÿÍÍÍÿÿÿÿÿÿÿÿÿéééÿŠŠŠÿÄÄÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìììÿ­­­ÿ¥¥¥ÿÿ÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿ§§§ÿÆÆÆÿÿÿÿÿÿÿÿÿÿÿÿÿëëëÿÿšššÿ®®®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÜÜÿÿXXXÿôôôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¨¨¨ÿÇÇÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ···ÿÿlllÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïïÿ‹‹‹ÿ“““ÿóóóÿÿÿÿÿÿÿÿÿÿÿÿÿöööÿžžžÿºººÿùùùÿÿÿÿÿÿÿÿÿÿÿÿÿÔÔÔÿªªªÿÐÐÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿ'''ÿ999ÿÑÑÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòòòÿ‚‚‚ÿÿúúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ( @ ÃÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæææÿæææÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØØØÿAAAÿAAAÿØØØÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõÿfffÿÿÿfffÿõõõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïïÿÞÞÞÿÞÞÞÿôôôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿäääÿ‹‹‹ÿ‹‹‹ÿäääÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìììÿÞÞÞÿáááÿúúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡‡‡ÿÿ555ÿÐÐÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   ÿ   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢¢¢ÿ###ÿ,,,ÿÖÖÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqqqÿÿVVVÿñññÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸÿŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÅÅÿ&&&ÿÿÍÍÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³³³ÿ»»»ÿÿ€€€ÿóóóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸÿŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌÌÿdddÿ···ÿ„„„ÿÜÜÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿÿ|||ÿðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžžžÿžžžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈÈÈÿcccÿËËËÿÿÿÿÿþþþÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúúúÿ”””ÿ„„„ÿûûûÿÿÿÿÿÿÿÿÿÿÿÿÿ»»»ÿ»»»ÿÿÿÿÿÿÿÿÿÿÿÿÿÖÖÖÿeeeÿÎÎÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúúúÿÎÎÎÿ¡¡¡ÿŠŠŠÿŠŠŠÿŠŠŠÿ‡‡‡ÿ‡‡‡ÿŠŠŠÿŠŠŠÿŠŠŠÿ¸¸¸ÿÞÞÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøøÿ<<<ÿÿÿÿÿÿÿÿÿœœœÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûûÿ÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõÿ>>>ÿÿÿÿÿÿÿÿÿ›››ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿéééÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõÿ~~~ÿ¢¢¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ÿ>>>ÿÿÿÿÿÿÿÿÿ›››ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòòòÿSSSÿ©©©ÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿûûûÿÿÿPPPÿ–––ÿ–––ÿ–––ÿ–––ÿ–––ÿÈÈÈÿ@@@ÿÿÿÿÿÿÿÿÿ”””ÿžžžÿÿÿÿÿyyyÿÿÿÂÂÂÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿ’’’ÿ ÿ\\\ÿ®®®ÿ®®®ÿ®®®ÿ®®®ÿ®®®ÿÓÓÓÿ???ÿÿÿÿÿÿÿÿÿ˜˜˜ÿÔÔÔÿÇÇÇÿÇÇÇÿÇÇÇÿÇÇÇÿºººÿ###ÿFFFÿßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûûÿ———ÿ®®®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ÿ>>>ÿÿÿÿÿÿÿÿÿ›››ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøøÿ”””ÿàààÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõÿ>>>ÿÿÿÿÿÿÿÿÿ›››ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøøÿ<<<ÿÿÿÿÿÿÿÿÿ›››ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõÿÏÏÏÿ¥¥¥ÿŠŠŠÿŠŠŠÿŠŠŠÿÿ„„„ÿŠŠŠÿŠŠŠÿŠŠŠÿ¨¨¨ÿ¹¹¹ÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôôôÿÿ———ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžžžÿÈÈÈÿÿÿÿÿÿÿÿÿÿÿÿÿçççÿlllÿ¨¨¨ÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿÿÿÿÿòòòÿ~~~ÿŽŽŽÿ÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ†††ÿ¸¸¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæææÿnnnÿ¤¤¤ÿþþþÿùùùÿüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿ’’’ÿ²²²ÿ~~~ÿ’’’ÿùùùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡‡‡ÿ¹¹¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéééÿtttÿÿjjjÿ×××ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿQQQÿ ÿzzzÿúúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡‡‡ÿ¹¹¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºººÿÿ ÿÍÍÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿkkkÿÿXXXÿïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‰‰‰ÿ¼¼¼ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿ‘‘‘ÿKKKÿYYYÿßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëëëÿÞÞÞÿáááÿûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ÿÍÍÍÿjjjÿ’’’ÿ×××ÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûûÿúúúÿûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíííÿQQQÿÿ ÿƒƒƒÿüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙÙÙÿGGGÿnnnÿðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîîÿöööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(0` $ÃÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôôôÿ|||ÿ|||ÿôôôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòòòÿqqqÿÿÿqqqÿòòòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøøÿpppÿÿÿÿÿpppÿøøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿ™™™ÿ|||ÿ888ÿ888ÿ|||ÿ™™™ÿùùùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéééÿÝÝÝÿÝÝÝÿÝÝÝÿòòòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtttÿtttÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿæææÿÝÝÝÿÝÝÝÿáááÿúúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ÿVVVÿÿÿÿ¦¦¦ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsssÿsssÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôôôÿVVVÿÿÿ(((ÿÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôôôÿ:::ÿÿÿ```ÿìììÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsssÿsssÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿ¿¿¿ÿÿÿ ÿÄÄÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôôôÿ888ÿ ÿ"""ÿpppÿöööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsssÿsssÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³³³ÿ'''ÿÿÿÄÄÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøøÿcccÿ’’’ÿÏÏÏÿ>>>ÿpppÿóóóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsssÿsssÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬¬¬ÿ---ÿ   ÿ¨¨¨ÿ+++ÿÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóóóÿýýýÿÿÿÿÿ×××ÿ@@@ÿkkkÿñññÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsssÿsssÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿ§§§ÿ---ÿ¥¥¥ÿþþþÿÿÿÿÿÝÝÝÿöööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÛÛÿDDDÿfffÿðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿrrrÿrrrÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿ¢¢¢ÿ---ÿªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞÞÞÿHHHÿ```ÿëëëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxxxÿxxxÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûûÿ›››ÿ---ÿ¯¯¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáááÿ[[[ÿ½½½ÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿÛÛÛÿÛÛÛÿþþþÿþþþÿþþþÿþþþÿþþþÿìììÿ]]]ÿ³³³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ÿÏÏÏÿ]]]ÿXXXÿXXXÿXXXÿXXXÿXXXÿZZZÿZZZÿXXXÿXXXÿXXXÿXXXÿXXXÿ   ÿôôôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½½½ÿÿÿÿÿÿÿÿÿÿÿÿÿÿnnnÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾¾¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿqqqÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûûÿöööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾¾¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿqqqÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòòòÿâââÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöööÿ|||ÿ{{{ÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾¾¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿqqqÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°°°ÿ111ÿÁÁÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôôôÿwwwÿÿVVVÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾¾¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿqqqÿüüüÿóóóÿóóóÿóóóÿóóóÿóóóÿóóóÿóóóÿóóóÿŸŸŸÿÿ%%%ÿ½½½ÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿ~~~ÿÿÿ"""ÿ```ÿaaaÿaaaÿaaaÿaaaÿaaaÿaaaÿaaaÿ   ÿ¾¾¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿtttÿÄÄÄÿDDDÿAAAÿAAAÿAAAÿAAAÿAAAÿAAAÿAAAÿ+++ÿÿÿ***ÿáááÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿšššÿÿÿ111ÿÿŽŽŽÿŽŽŽÿŽŽŽÿŽŽŽÿŽŽŽÿŽŽŽÿŽŽŽÿ¼¼¼ÿ¾¾¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿrrrÿìììÿ½½½ÿ»»»ÿ»»»ÿ»»»ÿ»»»ÿ»»»ÿ»»»ÿ»»»ÿ{{{ÿÿÿ~~~ÿõõõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿœœœÿÿWWWÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾¾¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿqqqÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªªÿ ÿ„„„ÿøøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿ£££ÿ–––ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾¾¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿqqqÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙÙÙÿ§§§ÿùùùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾¾¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿqqqÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾¾¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿqqqÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½½½ÿÿÿÿÿÿÿÿÿÿÿÿÿÿoooÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõÿÒÒÒÿ]]]ÿXXXÿXXXÿXXXÿXXXÿXXXÿ[[[ÿYYYÿXXXÿXXXÿXXXÿXXXÿXXXÿ™™™ÿÀÀÀÿóóóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇÿVVVÿÝÝÝÿþþþÿþþþÿþþþÿþþþÿþþþÿüüüÿ°°°ÿØØØÿþþþÿþþþÿþþþÿþþþÿþþþÿîîîÿNNNÿkkkÿòòòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÄÄÿ333ÿ„„„ÿ÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúúúÿIIIÿ£££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙÙÙÿCCCÿgggÿðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿¿¿ÿ111ÿŠŠŠÿúúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿIIIÿ£££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝÝÝÿGGGÿbbbÿîîîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúúúÿÜÜÜÿüüüÿÿÿÿÿ»»»ÿ000ÿÿûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿIIIÿ£££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàààÿKKKÿ]]]ÿìììÿûûûÿ½½½ÿîîîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßßßÿ222ÿ’’’ÿ³³³ÿ///ÿ•••ÿüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿIIIÿ£££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäääÿQQQÿ\\\ÿ|||ÿÿÆÆÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÛÛÿÿ ÿ ÿœœœÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿIIIÿ£££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÒÒÿÿÿ ÿÅÅÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÛÛÿÿÿÿ©©©ÿüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿIIIÿ£££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíííÿWWWÿÿÿÿÄÄÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãããÿ666ÿÿÿAAAÿèèèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúúúÿIIIÿ£££ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçççÿtttÿgggÿgggÿrrrÿâââÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿäääÿÝÝÝÿÝÝÝÿâââÿüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿöööÿHHHÿ¡¡¡ÿûûûÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåååÿ___ÿMMMÿÿ222ÿMMMÿ”””ÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòòòÿiiiÿÿÿÿÿ³³³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóóóÿqqqÿÿ ÿ¸¸¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôôôÿÿ¾¾¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(ßÌ(ÃÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿø?ÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿàÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿø?ÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿàÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿàÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿø?ÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿø?ÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿø?ÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿø?ÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿø?ÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿø?ÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿø?ÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿþÿÿÿÿÿø?ÿÿÿÿÿðÿÿÿÿþÿÿÿÿü?ÿÿÿÿÿø?ÿÿÿÿÿàÿÿÿÿþÿÿÿÿü?ÿÿÿÿÿø?ÿÿÿÿÿàÿÿÿÿþÿÿÿÿü?ÿÿÿÿÿø?ÿÿÿÿÿàÿÿÿÿþÿÿÿÿüÿÿÿÿÿø?ÿÿÿÿÿðÿÿÿÿþÿÿÿÿüÿÿÿÿÿÿø?ÿÿÿÿÿøÿÿÿÿþÿÿÿÿüÿÿÿÿÿÿø?ÿÿÿÿÿüÿÿÿÿþÿÿÿÿüÿÿÿÿÿÿø?ÿÿÿÿÿþÿÿÿÿþÿÿÿÿüÿÿÿÿÿÿø?ÿÿÿÿÿÿÿÿÿÿþÿÿÿÿüÿÿÿÿÿÿø?ÿÿÿÿÿÿ€ÿÿÿÿþÿÿÿÿüÿÿÿÿÿÿø?ÿÿÿÿÿÿÿÿÿÿþÿÿÿÿüÿÿÿÿÿÿø?ÿÿÿÿÿþÿÿÿÿþÿÿÿÿüÿÿÿÿÿÿø?ÿÿÿÿÿüÿÿÿÿþÿÿÿÿüÿÿÿÿÿÿø?ÿÿÿÿÿøÿÿÿÿþÿÿÿÿü€ÿÿÿÿÿÿø?ÿÿÿÿÿðÿÿÿÿþÿÿÿÿüÀÿÿÿÿÿø?ÿÿÿÿÿà<ÿÿÿÿþÿÿÿÿüà?ÿÿÿÿÿø?ÿÿÿÿÿÀ~ÿÿÿÿþÿÿÿÿüðÿÿÿÿÿø?ÿÿÿÿÿ€ÿÿÿÿÿþÿÿÿÿþ?øÿÿÿÿÿø?ÿÿÿÿÿÿƒÿÿÿÿþÿÿÿÿÿÿüÿÿÿÿÿø?ÿÿÿÿþÿÇÿÿÿÿþÿÿÿÿÿÿþÿÿÿÿÿø?ÿÿÿÿüÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿø?ÿÿÿÿøÿÿÿÿÿÿþÿÿÿÿÿÿÿ€ÿÿÿÿÿø?ÿÿÿÿðÿÿÿÿÿÿþÿÿÿÿÿÿÿÀÿÿÿÿø?ÿÿÿÿà?ÿÿÿÿÿÿþÿÿÿÿÿÿÿà?ÿÿÿÿø?ÿÿÿÿÀÿÿÿÿÿÿþÿÿÿÿÿÿÿðÿÿÿÿø?ÿÿÿÿ€ÿÿÿÿÿÿÿþÿÿÿÿÿÿÿøÿÿÿÿø?ÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿüÿÿÿÿø?ÿÿÿþÿÿÿÿÿÿÿþÿÿÿÿÿÿÿþÿÿÿÿø?ÿÿÿüÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿø?ÿÿÿøÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿ€ÿÿÿÿø?ÿÿÿðÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÀÿÿÿø?ÿÿÿà?ÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿà?ÿÿÿø?ÿÿÿÀÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿðÿÿÿø?ÿÿÿ€ÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿøÿÿÿüÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿüÿÿþÿÿÿãÿÿÿÿÿüÿÿÿÿÿø?ÿÿþÿÿÿÁÿÿÿÿÿüÿÿÿÿÿøÿÿþÿÿÿÿÿÿÿÿüÿÿÿÿÿøÿÿþÿÿÿÿÿÿÿÿüÿÿÿÿÿøÿÿþÿÿþÿÿÿÿÿüÿÿÿÿÿøÿÿþÿÿüÿÿÿÿÿüÿÿÿÿÿøÿÿþÿÿøÿÿÿÿÿüÿÿÿÿÿøÿÿþÿÿðÿÿÿÿÿüÿÿÿÿÿøÿþÿÿàÿÿÿÿÿüÿÿÿÿÿø?ÿþÿÿÀÿÿÿÿÿüÿÿÿÿÿøÿþÿÿ€ÿÿÿÿÿüðÿþÿÿüàÿþÿþ|àÿþÿþ|àÿþÿþ|ðÿþÿÿüÿÿÿÿÿøÿþÿÿ€ÿÿÿÿÿüÿÿÿÿÿø?ÿþÿÿÀÿÿÿÿÿüÿÿÿÿÿøÿþÿÿàÿÿÿÿÿüÿÿÿÿÿøÿÿþÿÿðÿÿÿÿÿüÿÿÿÿÿøÿÿþÿÿøÿÿÿÿÿüÿÿÿÿÿøÿÿþÿÿüÿÿÿÿÿüÿÿÿÿÿøÿÿþÿÿþÿÿÿÿÿüÿÿÿÿÿøÿÿþÿÿÿÿÿÿÿÿüÿÿÿÿÿøÿÿþÿÿÿÿÿÿÿÿüÿÿÿÿÿø?ÿÿþÿÿÿÁÿÿÿÿÿüÿÿÿÿÿüÿÿþÿÿÿãÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿþ?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿøÿÿÿøÿÿÿÿ€ÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿðÿÿÿðÿÿÿÀÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿà?ÿÿÿðÿÿÿà?ÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÀÿÿÿðÿÿÿðÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿ€ÿÿÿÿðÿÿÿøÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿüÿÿÿÿÿÿÿþÿÿÿÿÿÿÿþÿÿÿÿðÿÿÿþÿÿÿÿÿÿÿþÿÿÿÿÿÿÿüÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿøÿÿÿÿðÿÿÿÿ€ÿÿÿÿÿÿÿþÿÿÿÿÿÿÿðÿÿÿÿðÿÿÿÿÀÿÿÿÿÿÿþÿÿÿÿÿÿÿà?ÿÿÿÿðÿÿÿÿà?ÿÿÿÿÿÿþÿÿÿÿÿÿÿÀÿÿÿÿðÿÿÿÿðÿÿÿÿÿÿþÿÿÿÿÿÿÿ€ÿÿÿÿÿðÿÿÿÿøÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿüÿÿÿÿÿÿþÿÿÿÿÿÿþÿÿÿÿÿðÿÿÿÿþÿÿÿÿÿÿþÿÿÿÿÿÿüÿÿÿÿÿðÿÿÿÿÿÿÇÿÿÿÿþÿÿÿÿüøÿÿÿÿÿðÿÿÿÿÿ€ÿƒÿÿÿÿþÿÿÿÿø?ðÿÿÿÿÿðÿÿÿÿÿÀÿÿÿÿþÿÿÿÿøà?ÿÿÿÿÿðÿÿÿÿÿà>ÿÿÿÿþÿÿÿÿøÀÿÿÿÿÿðÿÿÿÿÿðÿÿÿÿþÿÿÿÿø€ÿÿÿÿÿÿðÿÿÿÿÿøÿÿÿÿþÿÿÿÿøÿÿÿÿÿÿðÿÿÿÿÿüÿÿÿÿþÿÿÿÿøÿÿÿÿÿÿðÿÿÿÿÿþÿÿÿÿþÿÿÿÿøÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿþÿÿÿÿøÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿþÿÿÿÿøÿÿÿÿÿÿðÿÿÿÿÿþÿÿÿÿþÿÿÿÿø?ÿÿÿÿÿÿðÿÿÿÿÿüÿÿÿÿþÿÿÿÿøÿÿÿÿÿÿðÿÿÿÿÿøÿÿÿÿþÿÿÿÿøÿÿÿÿÿÿðÿÿÿÿÿðÿÿÿÿþÿÿÿÿøÿÿÿÿÿÿðÿÿÿÿÿàÿÿÿÿþÿÿÿÿøÿÿÿÿÿÿðÿÿÿÿÿÀÿÿÿÿþÿÿÿÿøÿÿÿÿÿÿðÿÿÿÿÿÀÿÿÿÿþÿÿÿÿøÿÿÿÿÿÿðÿÿÿÿÿÀÿÿÿÿþÿÿÿÿøÿÿÿÿÿÿðÿÿÿÿÿàÿÿÿÿþÿÿÿÿøÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿüÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿà?ÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿøÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿà?ÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿøÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþobs-move-transition-2.5.7/media/logo.png000066400000000000000000000051421417746432500202100ustar00rootroot00000000000000‰PNG  IHDRßæÚîÇsRGB®ÎégAMA± üa pHYsÃÃÇo¨d ÷IDATx^íÕÑ’ë¶DÑüÿOçº2·Œ²l“"-6Ä^oD©Acrþù×L•·Óty;M—·Óty;M—·Óty;M—·Óty;M—·Óty;M—·Óty;M—·Óty;M—·Óty;M—·Óty;M—·Óty;M—·sŽÿm3ø6'`1Uæ«ÅJfôlŒïqËx„'l€/ñ<Öð=ž³³|ƒ'±€ßð´âë;ƒÕkÃëç»ëÆÒõà¤uòÅõaÝúqÞzøÖ:°hgñkæ+kÅŠá]ÖÆ÷Õ„åš7Z_Öw¬Õ<¼×¾ñM}ÇNÍÃ{íßÔwìÔ<¼×¾ñM5a­fàÖÀ—5{¨Ú0_åle jÃ|•°•ª óUNÀVª6ÌW9[¨Ú0_åle jÃ\%¿aNª"e jî¾J~À~œ—DÄ@Õ†y;' b *†p8¼ˆ·s"ªJHv ¯X¡ÆvrX)U%$;…W¬P`;9)Œ ª’Â+VPßNŽi#k *†pý8¿‚ôvrFqU=äëÄát·“8P•DÄœ\At;yºBªªHÙŒc+ˆnç* q *Œ m8³‚îv>pFqUmdmÀ¤·ócÚȨj#k¬ ¾œFÐ@UAÛpf¡íüðÀ_K)UU¤lƱT¶“ö(eô$1P•DÄì]ýÏßÁ%®þ6gôžÐÈèé!_ ª‡|½7ÝÚ+\ým&~Bã팞ªb—Ñ T3z+\ým&Tßࡌž’ªJH–ÑËè=¡±Â‚o3tÛØ<šÑ“A¬@U±2zGx"P]Aî*_qI= d T5)£÷Ï­ž¥Àv>pU= T(£WA™¬\mFo5Òª«‘&£WD©¿¤#ô–"J ºQ2zuTûc:Borªë#£WJÁ¿§#ô!D º!2zÕÔü“:Boª+ £WPÙ¿ª#ô.ÇçÕËñùŒ^M•ÿ°ŽÐ»ßT¯Å·3zeÿÛ:BïB|8P½ÎèUVÿÏ뽫ðÕ@õ*|5£WÜ-þŽлŸ T/Á'3zõÝåì½ßã{êïñ½ŒÞ-Üèïì½ãcêñ±ŒÞ]ÜëOí½_âKê/ñ¥ŒÞÜî¯íãc^Í©îø—Q½ ¦ÊèÝÎMÿæÿ}# ö„ÆÝy¶[b%Õ›òvÖÃbÞ}56ÚN~Ò²c'ÞÎ2c'ÞÎ2c'ÞÎ2c'ÞÎ2c'ÞÎ2c'ÞÎ2c'ÞÎ2c'ÞÎ2c'ÞÎ2c'ÞÎ2c'ÞÎ2c'ÞÎ2c'ÞÎ2c'ÞÎ2c'ÞÎ2c'ÞÎ2c'ÞÎ2c'ÞÎ2c'ógæ.õn“Xe1ÆN&ÏÌEªÈTcÈ Ö/ƒÍ|5aŸÐÐ@¦²C™ÕÙ¦½—˜= d*‹14é ©æ¼”€/hk SYŒ¡L½y&¼‘hGxB™Êb dzA{’Ñ×ê Ò@¦²C™ŽðÄ Cï"Î{<§Le1†2½ÁCÃοˆ ñ¨2•ÅÈôÏ9ù"|ÃÓÈTch ÓG<:àÌ+øxh SYŒ¡LßðôYÝçù¬<â>¡Qch SœÒw˜Aè@µ,ÆxBCqûy;Ë`Œ@µBwòv–Áj„îÔ}Œ¯É#îe1F Z¡;9Æp@™ÊbŒ'4ä·ßÙ¥nÃÓÈTch Sœ2°× xT™Êb dú†§Ï[íoxN™Êb dúˆG o÷G<¤Le1†2½Çscf,ø{<¡Le1†2½ÁCÃ&íø´5©,ÆÐ@¦#<1ü5?BO™Êb dzA{’©›þ‚†2•ÅȔћgö²gT5©,ÆÐ@¦'4¦úÁ¾þ[±Êb d Tg“Û¡ßá"Ëb Äúe0ogŒ±ogŒ±ogŒ±ogŒ±ogŒ±ogŒ±ogŒ±ogŒ±ogŒ±ogŒ±ogŒ±ogŒ±ogŒ±ogŒ±ogŒ±ogŒ±ogŒ±“g¶*¼¦ë¶ÛÉ?‡wü‘Á6ø·þžò몷ÀHêMÝù-ÿGãé ;ºÛlüb½ãc?þßÈèÝέã·Êèý Tƒodôîå>Sñ+eô~ïª?Ãg2z7r“‘ø}2z—à“ê/ñ¥ŒÞ]Üa~™ŒÞUøj úc|,£w å‡á7Éè]ˆª¿Ç÷2zõÕž„_#£w-¾¨^‚OfôŠ+<¿CFïr|>P½ _ÍèUVu~ŒÞ $T/ć3ze•€»Ïè-Bˆ@õZ|;£WS½ôÜzForª—ã󽂊Eç¾3zK%P]½j*忦3z«‘&P]„½RÊ„æŽ3z¨®CŽŒ^5s»= d T—"JF¯ˆq¹×Œž bª«‘&£÷®dÍç½axžËè)!Y *€@½7x(P]aÁ·:P=Â=1„ T5)£÷‚ö+,ø6C?¡‘ÑËèé!_ *ƒX½'42z+,ø6CgôÕŒž$"ªJH–Ñû¥´WXðm†~A»á‘2PC¸ìCëÏßK,ø6Cy×ý;¨Œ ªòõàä ZÛyˆcÚȨJ"b3Ž­ ¾œ‘GÜ@U)Ûpféíä@$T…´VðvÎAâ@UY¿á餷ó3òˆ¨ #h¬ ¾ÓFÖ@U)Ûpf…ÛùÀIa T%±ÇV¨±VEÊ@Uùzpr…2ÛùÀyID TÅ®‡W¨´¼BùU%$ëÇù¼s/PUB²~œ_ÁÛ9ùU%$ëÇùÖ|›¹;qXU1„ëÁÉE¤òBø1Uã{œƒ­ TmŒïq¶2Pµ1¾Ç9ØÊ@ÕÆøç`+Uã{œƒ­ TmŒïq¶2Pµ1¾ÇVìÝ ¼Ñ¾ñM5a­æá½ö‘¯© ;5ïµ|MMØ©yx¯}äkjÅZÍÀíßT–k ï²¾¬>¬ØY¼ÅÚø¾º±hý8oÍ|eg°n=8i=|k'±tm8c|qç±zßð´õóÝ aßã9;Å×7Š5<Âv–op–1£g|‰s°’ªñ=NÃbz5çñUš.o§éòvš.o§éòvš.o§éòvš.o§éòvš.o§éòvš.o§éòvš.o§éòvš.o§éòvš.o§éòvš.o§©ú÷ßÿ¤…_ÌJ¨IEND®B`‚obs-move-transition-2.5.7/move-source-filter.c000066400000000000000000002075751417746432500213740ustar00rootroot00000000000000#include "move-transition.h" #include #include #include #include void move_source_item_remove(void *data, calldata_t *call_data) { struct move_source_info *move_source = data; if (!move_source) return; if (!call_data) return; obs_sceneitem_t *item = calldata_ptr(call_data, "item"); if (!item || item != move_source->scene_item) return; move_source->scene_item = NULL; obs_scene_t *scene = calldata_ptr(call_data, "scene"); if (!scene) return; obs_source_t *parent = obs_scene_get_source(scene); if (!parent) return; signal_handler_t *sh = obs_source_get_signal_handler(parent); if (!sh) return; signal_handler_disconnect(sh, "item_remove", move_source_item_remove, move_source); } bool find_sceneitem(obs_scene_t *scene, obs_sceneitem_t *scene_item, void *data) { UNUSED_PARAMETER(scene); struct move_source_info *move_source = data; const char *name = obs_source_get_name(obs_sceneitem_get_source(scene_item)); if (!name || strcmp(name, move_source->source_name) != 0) return true; move_source->scene_item = scene_item; obs_source_t *parent = obs_scene_get_source(scene); if (!parent) return false; signal_handler_t *sh = obs_source_get_signal_handler(parent); if (sh) signal_handler_connect(sh, "item_remove", move_source_item_remove, move_source); return false; } char obs_data_get_char(obs_data_t *data, const char *name) { const char *s = obs_data_get_string(data, name); return (s && strlen(s)) ? s[0] : ' '; } void obs_data_set_char(obs_data_t *data, const char *name, char val) { char s[2]; s[0] = val; s[1] = 0; obs_data_set_string(data, name, s); } float calc_sign(char sign, float from, float to) { if (sign == '+') { return from + to; } else if (sign == '-') { return from - to; } else if (sign == '*') { return from * to; } else if (sign == '/') { return to == 0.0f ? from : from / to; } else { return to; } } void calc_relative_to(struct move_source_info *move_source) { obs_data_t *settings = obs_source_get_settings(move_source->source); move_source->rot_to = calc_sign( obs_data_get_char(settings, "rot_sign"), move_source->rot_from, (float)obs_data_get_double(settings, S_ROT)); obs_data_t *pos = obs_data_get_obj(settings, S_POS); move_source->pos_to.x = calc_sign(obs_data_get_char(pos, "x_sign"), move_source->pos_from.x, (float)obs_data_get_double(pos, "x")); move_source->pos_to.y = calc_sign(obs_data_get_char(pos, "y_sign"), move_source->pos_from.y, (float)obs_data_get_double(pos, "y")); obs_data_release(pos); obs_data_t *scale = obs_data_get_obj(settings, S_SCALE); move_source->scale_to.x = calc_sign( obs_data_get_char(scale, "x_sign"), move_source->scale_from.x, (float)obs_data_get_double(scale, "x")); move_source->scale_to.y = calc_sign( obs_data_get_char(scale, "y_sign"), move_source->scale_from.y, (float)obs_data_get_double(scale, "y")); obs_data_release(scale); obs_data_t *bounds = obs_data_get_obj(settings, S_BOUNDS); move_source->bounds_to.x = calc_sign( obs_data_get_char(bounds, "x_sign"), move_source->bounds_from.x, (float)obs_data_get_double(bounds, "x")); move_source->bounds_to.y = calc_sign( obs_data_get_char(bounds, "y_sign"), move_source->bounds_from.y, (float)obs_data_get_double(bounds, "y")); obs_data_release(bounds); obs_data_t *crop = obs_data_get_obj(settings, S_CROP); move_source->crop_to.left = calc_sign( obs_data_get_char(crop, "left_sign"), move_source->crop_from.left, obs_data_get_int(crop, "left")); move_source->crop_to.top = calc_sign( obs_data_get_char(crop, "top_sign"), move_source->crop_from.top, obs_data_get_int(crop, "top")); move_source->crop_to.right = calc_sign( obs_data_get_char(crop, "right_sign"), move_source->crop_from.right, obs_data_get_int(crop, "right")); move_source->crop_to.bottom = calc_sign(obs_data_get_char(crop, "bottom_sign"), move_source->crop_from.bottom, obs_data_get_int(crop, "bottom")); obs_data_release(crop); obs_data_release(settings); } void move_source_media_action(struct move_source_info *move_source, long long media_action, int64_t media_time) { if (media_action == MEDIA_ACTION_PLAY) { const enum obs_media_state state = obs_source_media_get_state( obs_sceneitem_get_source(move_source->scene_item)); if (state == OBS_MEDIA_STATE_PAUSED) { obs_source_media_play_pause( obs_sceneitem_get_source( move_source->scene_item), false); } else if (state != OBS_MEDIA_STATE_PLAYING) { obs_source_media_restart(obs_sceneitem_get_source( move_source->scene_item)); } } else if (media_action == MEDIA_ACTION_PAUSE) { obs_source_media_play_pause( obs_sceneitem_get_source(move_source->scene_item), true); } else if (media_action == MEDIA_ACTION_STOP) { obs_source_media_stop( obs_sceneitem_get_source(move_source->scene_item)); } else if (media_action == MEDIA_ACTION_RESTART) { obs_source_media_restart( obs_sceneitem_get_source(move_source->scene_item)); } else if (media_action == MEDIA_ACTION_NEXT) { obs_source_media_next( obs_sceneitem_get_source(move_source->scene_item)); } else if (media_action == MEDIA_ACTION_PREVIOUS) { obs_source_media_previous( obs_sceneitem_get_source(move_source->scene_item)); } else if (media_action == MEDIA_ACTION_PLAY_FROM) { const int64_t duration = obs_source_media_get_duration( obs_sceneitem_get_source(move_source->scene_item)); if (media_time < 0 && duration + media_time > 0) { const enum obs_media_state state = obs_source_media_get_state( obs_sceneitem_get_source( move_source->scene_item)); if (state == OBS_MEDIA_STATE_PAUSED) { obs_source_media_play_pause( obs_sceneitem_get_source( move_source->scene_item), false); } else if (state != OBS_MEDIA_STATE_PLAYING) { obs_source_media_restart( obs_sceneitem_get_source( move_source->scene_item)); } obs_source_media_set_time( obs_sceneitem_get_source( move_source->scene_item), duration + media_time); } else if (media_time >= 0 && media_time <= duration) { const enum obs_media_state state = obs_source_media_get_state( obs_sceneitem_get_source( move_source->scene_item)); if (state == OBS_MEDIA_STATE_PAUSED) { obs_source_media_play_pause( obs_sceneitem_get_source( move_source->scene_item), false); } else if (state != OBS_MEDIA_STATE_PLAYING) { obs_source_media_restart( obs_sceneitem_get_source( move_source->scene_item)); } obs_source_media_set_time( obs_sceneitem_get_source( move_source->scene_item), media_time); } } else if (media_action == MEDIA_ACTION_PAUSE_AT) { const int64_t duration = obs_source_media_get_duration( obs_sceneitem_get_source(move_source->scene_item)); if (media_time < 0 && duration + media_time > 0) { obs_source_media_set_time( obs_sceneitem_get_source( move_source->scene_item), duration + media_time); obs_source_media_play_pause( obs_sceneitem_get_source( move_source->scene_item), true); } else if (media_time >= 0 && media_time <= duration) { obs_source_media_set_time( obs_sceneitem_get_source( move_source->scene_item), media_time); obs_source_media_play_pause( obs_sceneitem_get_source( move_source->scene_item), true); } } } void move_source_ended(struct move_source_info *move_source); void move_source_start(struct move_source_info *move_source) { if (!move_source->scene_item && move_source->source_name && strlen(move_source->source_name)) { obs_source_t *parent = obs_filter_get_parent(move_source->source); if (parent) { obs_scene_t *scene = obs_scene_from_source(parent); if (!scene) scene = obs_group_from_source(parent); if (scene) obs_scene_enum_items(scene, find_sceneitem, move_source); } } if (!move_source->scene_item) return; if (!move_source->custom_duration) move_source->duration = obs_frontend_get_transition_duration(); if (move_source->moving && obs_source_enabled(move_source->source)) { if (move_source->next_move_on == NEXT_MOVE_ON_HOTKEY && move_source->next_move_name && strcmp(move_source->next_move_name, NEXT_MOVE_REVERSE) == 0) { move_source->reverse = !move_source->reverse; move_source->running_duration = (float)(move_source->duration + move_source->start_delay + move_source->end_delay) / 1000.0f - move_source->running_duration; } return; } if ((move_source->change_order & CHANGE_ORDER_START) != 0) { if ((move_source->change_order & CHANGE_ORDER_RELATIVE) != 0 && move_source->order_position) { if (move_source->order_position > 0) { for (int i = 0; i < move_source->order_position; i++) { obs_sceneitem_set_order( move_source->scene_item, OBS_ORDER_MOVE_UP); } } else if (move_source->order_position < 0) { for (int i = 0; i > move_source->order_position; i--) { obs_sceneitem_set_order( move_source->scene_item, OBS_ORDER_MOVE_DOWN); } } } else if ((move_source->change_order & CHANGE_ORDER_ABSOLUTE) != 0) { obs_sceneitem_set_order_position( move_source->scene_item, move_source->order_position); } } if ((move_source->change_visibility == CHANGE_VISIBILITY_SHOW_START || move_source->change_visibility == CHANGE_VISIBILITY_SHOW_START_END || move_source->change_visibility == CHANGE_VISIBILITY_TOGGLE) && !obs_sceneitem_visible(move_source->scene_item)) { obs_sceneitem_set_visible(move_source->scene_item, true); move_source->visibility_toggled = true; } else { move_source->visibility_toggled = false; } if (move_source->change_visibility == CHANGE_VISIBILITY_TOGGLE_START) { obs_sceneitem_set_visible( move_source->scene_item, !obs_sceneitem_visible(move_source->scene_item)); } else if (move_source->change_visibility == CHANGE_VISIBILITY_HIDE_START || move_source->change_visibility == CHANGE_VISIBILITY_HIDE_START_END) { obs_sceneitem_set_visible(move_source->scene_item, false); } move_source_media_action(move_source, move_source->media_action_start, move_source->media_time_start); if ((move_source->mute_action == MUTE_ACTION_MUTE_START || move_source->mute_action == MUTE_ACTION_MUTE_DURING) && !obs_source_muted( obs_sceneitem_get_source(move_source->scene_item))) { obs_source_set_muted( obs_sceneitem_get_source(move_source->scene_item), true); } else if ((move_source->mute_action == MUTE_ACTION_UNMUTE_START || move_source->mute_action == MUTE_ACTION_UNMUTE_DURING) && obs_source_muted( obs_sceneitem_get_source(move_source->scene_item))) { obs_source_set_muted( obs_sceneitem_get_source(move_source->scene_item), false); } move_source->running_duration = 0.0f; if (!move_source->reverse) { move_source->rot_from = obs_sceneitem_get_rot(move_source->scene_item); obs_sceneitem_get_pos(move_source->scene_item, &move_source->pos_from); obs_sceneitem_get_scale(move_source->scene_item, &move_source->scale_from); obs_sceneitem_get_bounds(move_source->scene_item, &move_source->bounds_from); obs_sceneitem_get_crop(move_source->scene_item, &move_source->crop_from); obs_source_t *scene_source = obs_scene_get_source( obs_sceneitem_get_scene(move_source->scene_item)); move_source->canvas_width = obs_source_get_width(scene_source); move_source->canvas_height = obs_source_get_height(scene_source); calc_relative_to(move_source); move_source->audio_fade_from = obs_source_get_volume( obs_sceneitem_get_source(move_source->scene_item)); } move_source->moving = true; if (move_source->enabled_match_moving && !obs_source_enabled(move_source->source)) { move_source->enabled = true; obs_source_set_enabled(move_source->source, true); } if (move_source->simultaneous_move_name && strlen(move_source->simultaneous_move_name) && (!move_source->filter_name || strcmp(move_source->filter_name, move_source->simultaneous_move_name) != 0)) { obs_source_t *parent = obs_filter_get_parent(move_source->source); if (parent) { obs_source_t *filter = obs_source_get_filter_by_name( parent, move_source->simultaneous_move_name); if (!filter) { filter = obs_source_get_filter_by_name( obs_sceneitem_get_source( move_source->scene_item), move_source->simultaneous_move_name); } if (filter) { if (strcmp(obs_source_get_unversioned_id(filter), MOVE_SOURCE_FILTER_ID) == 0) { struct move_source_info *filter_data = obs_obj_get_data(filter); move_source_start(filter_data); } else if (strcmp(obs_source_get_unversioned_id( filter), MOVE_VALUE_FILTER_ID) == 0 || strcmp(obs_source_get_unversioned_id( filter), MOVE_AUDIO_VALUE_FILTER_ID) == 0) { struct move_value_info *filter_data = obs_obj_get_data(filter); move_value_start(filter_data); } obs_source_release(filter); } } } } bool move_source_start_button(obs_properties_t *props, obs_property_t *property, void *data) { struct move_source_info *move_source = data; move_source_start(move_source); UNUSED_PARAMETER(props); UNUSED_PARAMETER(property); return false; } void move_source_start_hotkey(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey, bool pressed) { if (!pressed) return; struct move_source_info *move_source = data; if (move_source->next_move_on != NEXT_MOVE_ON_HOTKEY || !move_source->next_move_name || !strlen(move_source->next_move_name)) { move_source_start(move_source); return; } if (!move_source->filters_done.num) { move_source_start(move_source); da_push_back(move_source->filters_done, &move_source->source); return; } if (move_source->moving && obs_source_enabled(move_source->source) && move_source->next_move_name && strcmp(move_source->next_move_name, NEXT_MOVE_REVERSE) != 0) { move_source->moving = false; if (move_source->enabled_match_moving) obs_source_set_enabled(move_source->source, false); } char *next_move_name = move_source->next_move_name; obs_source_t *filter = move_source->source; obs_source_t *parent = obs_filter_get_parent(filter); obs_source_t *source = obs_sceneitem_get_source(move_source->scene_item); long long next_move_on = move_source->next_move_on; size_t i = 0; while (i < move_source->filters_done.num) { if (!next_move_name || !strlen(next_move_name)) { move_source_start(move_source); move_source->filters_done.num = 0; da_push_back(move_source->filters_done, &move_source->source); return; } if (next_move_on != NEXT_MOVE_ON_HOTKEY) { da_push_back(move_source->filters_done, &filter); } filter = obs_source_get_filter_by_name(parent, next_move_name); if (!filter && source) { filter = obs_source_get_filter_by_name(source, next_move_name); } if (filter && strcmp(obs_source_get_unversioned_id(filter), MOVE_SOURCE_FILTER_ID) == 0) { struct move_source_info *filter_data = obs_obj_get_data(filter); if (filter_data->moving && obs_source_enabled(filter_data->source) && (filter_data->reverse || !filter_data->next_move_name || strcmp(filter_data->next_move_name, NEXT_MOVE_REVERSE) != 0)) { filter_data->moving = false; if (filter_data->enabled_match_moving) obs_source_set_enabled( filter_data->source, false); } parent = obs_filter_get_parent(filter); source = obs_sceneitem_get_source( filter_data->scene_item); next_move_name = filter_data->next_move_name; next_move_on = filter_data->next_move_on; } else if (filter && (strcmp(obs_source_get_unversioned_id(filter), MOVE_VALUE_FILTER_ID) == 0 || strcmp(obs_source_get_unversioned_id(filter), MOVE_AUDIO_VALUE_FILTER_ID) == 0)) { struct move_value_info *filter_data = obs_obj_get_data(filter); if (filter_data->moving && obs_source_enabled(filter_data->source) && (filter_data->reverse || !filter_data->next_move_name || strcmp(filter_data->next_move_name, NEXT_MOVE_REVERSE) != 0)) { filter_data->moving = false; if (filter_data->enabled_match_moving) obs_source_set_enabled( filter_data->source, false); } parent = obs_filter_get_parent(filter); source = NULL; next_move_name = filter_data->next_move_name; next_move_on = filter_data->next_move_on; } else { obs_source_release(filter); move_source_start(move_source); move_source->filters_done.num = 0; da_push_back(move_source->filters_done, &move_source->source); return; } obs_source_release(filter); i++; } for (i = 0; i < move_source->filters_done.num; i++) { if (move_source->filters_done.array[i] == filter) { move_source_start(move_source); move_source->filters_done.num = 0; da_push_back(move_source->filters_done, &move_source->source); return; } } if (strcmp(obs_source_get_unversioned_id(filter), MOVE_SOURCE_FILTER_ID) == 0) { move_source_start(obs_obj_get_data(filter)); } else if (strcmp(obs_source_get_unversioned_id(filter), MOVE_VALUE_FILTER_ID) == 0 || strcmp(obs_source_get_unversioned_id(filter), MOVE_AUDIO_VALUE_FILTER_ID) == 0) { move_value_start(obs_obj_get_data(filter)); } da_push_back(move_source->filters_done, &filter); UNUSED_PARAMETER(id); UNUSED_PARAMETER(hotkey); } void move_source_stop(struct move_source_info *move_source) { move_source->moving = false; if (move_source->enabled_match_moving && obs_source_enabled(move_source->source)) { obs_source_set_enabled(move_source->source, false); } } void move_source_source_activate(void *data, calldata_t *call_data) { struct move_source_info *move_source = data; if (move_source->start_trigger == START_TRIGGER_SOURCE_ACTIVATE) move_source_start(move_source); if (move_source->stop_trigger == START_TRIGGER_SOURCE_ACTIVATE) move_source_stop(move_source); UNUSED_PARAMETER(call_data); } void move_source_source_deactivate(void *data, calldata_t *call_data) { struct move_source_info *move_source = data; if (move_source->start_trigger == START_TRIGGER_SOURCE_DEACTIVATE) move_source_start(move_source); if (move_source->stop_trigger == START_TRIGGER_SOURCE_DEACTIVATE) move_source_stop(move_source); UNUSED_PARAMETER(call_data); } void move_source_source_show(void *data, calldata_t *call_data) { struct move_source_info *move_source = data; if (move_source->start_trigger == START_TRIGGER_SOURCE_SHOW) move_source_start(move_source); if (move_source->stop_trigger == START_TRIGGER_SOURCE_SHOW) move_source_stop(move_source); UNUSED_PARAMETER(call_data); } void move_source_source_hide(void *data, calldata_t *call_data) { struct move_source_info *move_source = data; if (move_source->start_trigger == START_TRIGGER_SOURCE_HIDE) move_source_start(move_source); if (move_source->stop_trigger == START_TRIGGER_SOURCE_HIDE) move_source_stop(move_source); UNUSED_PARAMETER(call_data); } void move_source_source_media_started(void *data, calldata_t *call_data) { struct move_source_info *move_source = data; if (move_source->start_trigger == START_TRIGGER_MEDIA_STARTED) move_source_start(move_source); if (move_source->stop_trigger == START_TRIGGER_MEDIA_STARTED) move_source_stop(move_source); UNUSED_PARAMETER(call_data); } void move_source_source_media_ended(void *data, calldata_t *call_data) { struct move_source_info *move_source = data; if (move_source->start_trigger == START_TRIGGER_MEDIA_ENDED) move_source_start(move_source); if (move_source->stop_trigger == START_TRIGGER_MEDIA_ENDED) move_source_stop(move_source); UNUSED_PARAMETER(call_data); } void move_source_source_remove(void *data, calldata_t *call_data) { struct move_source_info *move_source = data; move_source->scene_item = NULL; UNUSED_PARAMETER(call_data); } void move_source_source_changed(struct move_source_info *move_source, const char *source_name) { obs_source_t *source = move_source->source_name && strlen(move_source->source_name) ? obs_get_source_by_name(move_source->source_name) : NULL; if (source) { signal_handler_t *sh = obs_source_get_signal_handler(source); if (sh) { signal_handler_disconnect(sh, "activate", move_source_source_activate, move_source); signal_handler_disconnect(sh, "deactivate", move_source_source_deactivate, move_source); signal_handler_disconnect(sh, "show", move_source_source_show, move_source); signal_handler_disconnect(sh, "hide", move_source_source_hide, move_source); signal_handler_disconnect( sh, "media_started", move_source_source_media_started, move_source); signal_handler_disconnect( sh, "media_ended", move_source_source_media_ended, move_source); signal_handler_disconnect(sh, "remove", move_source_source_remove, move_source); } obs_source_release(source); } bfree(move_source->source_name); move_source->source_name = NULL; source = obs_get_source_by_name(source_name); if (source) { signal_handler_t *sh = obs_source_get_signal_handler(source); if (sh) { signal_handler_connect(sh, "activate", move_source_source_activate, move_source); signal_handler_connect(sh, "deactivate", move_source_source_deactivate, move_source); signal_handler_connect(sh, "show", move_source_source_show, move_source); signal_handler_connect(sh, "hide", move_source_source_hide, move_source); signal_handler_connect(sh, "media_started", move_source_source_media_started, move_source); signal_handler_connect(sh, "media_ended", move_source_source_media_ended, move_source); signal_handler_connect(sh, "remove", move_source_source_remove, move_source); move_source->source_name = bstrdup(source_name); } obs_source_release(source); } move_source->scene_item = NULL; obs_source_t *parent = obs_filter_get_parent(move_source->source); if (parent) { signal_handler_t *sh = obs_source_get_signal_handler(parent); if (sh) signal_handler_disconnect(sh, "item_remove", move_source_item_remove, move_source); } obs_scene_t *scene = obs_scene_from_source(parent); if (!scene) scene = obs_group_from_source(parent); if (move_source->source_name && scene) obs_scene_enum_items(scene, find_sceneitem, move_source); } static void obs_data_set_sign(obs_data_t *settings, const char *name, const char *val) { obs_data_t *obj = obs_data_get_obj(settings, name); if (obj) { obs_data_set_string(obj, "x_sign", val); obs_data_set_string(obj, "y_sign", val); obs_data_release(obj); } } void move_source_update(void *data, obs_data_t *settings) { struct move_source_info *move_source = data; obs_source_t *parent = obs_filter_get_parent(move_source->source); const char *source_name = obs_data_get_string(settings, S_SOURCE); if (!move_source->source_name || strcmp(move_source->source_name, source_name) != 0) { move_source_source_changed(move_source, source_name); } const char *filter_name = obs_source_get_name(move_source->source); if (!move_source->filter_name || strcmp(move_source->filter_name, filter_name) != 0) { bfree(move_source->filter_name); move_source->filter_name = NULL; if (move_source->move_start_hotkey != OBS_INVALID_HOTKEY_ID) { obs_hotkey_unregister(move_source->move_start_hotkey); move_source->move_start_hotkey = OBS_INVALID_HOTKEY_ID; } if (parent) { move_source->filter_name = bstrdup(filter_name); move_source->move_start_hotkey = obs_hotkey_register_source( parent, move_source->filter_name, move_source->filter_name, move_source_start_hotkey, data); } } move_source->enabled_match_moving = obs_data_get_bool(settings, S_ENABLED_MATCH_MOVING); if (move_source->enabled_match_moving && !move_source->moving && obs_source_enabled(move_source->source)) move_source_start(move_source); move_source->change_visibility = obs_data_get_int(settings, S_CHANGE_VISIBILITY); move_source->custom_duration = obs_data_get_bool(settings, S_CUSTOM_DURATION); if (move_source->custom_duration) move_source->duration = obs_data_get_int(settings, S_DURATION); move_source->start_delay = obs_data_get_int(settings, S_START_DELAY); move_source->end_delay = obs_data_get_int(settings, S_END_DELAY); move_source->curve = (float)obs_data_get_double(settings, S_CURVE_MATCH); move_source->easing = obs_data_get_int(settings, S_EASING_MATCH); move_source->easing_function = obs_data_get_int(settings, S_EASING_FUNCTION_MATCH); move_source->transform = obs_data_get_bool(settings, S_TRANSFORM); if (obs_data_has_user_value(settings, "crop_left") || obs_data_has_user_value(settings, "crop_top") || obs_data_has_user_value(settings, "crop_right") || obs_data_has_user_value(settings, "crop_bottom")) { obs_data_t *obj = obs_data_get_obj(settings, S_CROP); if (!obj) { obj = obs_data_create(); obs_data_set_obj(settings, S_CROP, obj); } obs_data_set_int(obj, "left", obs_data_get_int(settings, "crop_left")); obs_data_set_int(obj, "top", obs_data_get_int(settings, "crop_top")); obs_data_set_int(obj, "right", obs_data_get_int(settings, "crop_right")); obs_data_set_int(obj, "bottom", obs_data_get_int(settings, "crop_bottom")); obs_data_release(obj); obs_data_unset_user_value(settings, "crop_left"); obs_data_unset_user_value(settings, "crop_top"); obs_data_unset_user_value(settings, "crop_right"); obs_data_unset_user_value(settings, "crop_bottom"); } if (obs_data_has_user_value(settings, S_TRANSFORM_RELATIVE)) { if (obs_data_get_bool(settings, S_TRANSFORM_RELATIVE)) { obs_data_set_sign(settings, S_POS, "+"); obs_data_set_sign(settings, S_SCALE, "+"); obs_data_set_sign(settings, S_BOUNDS, "+"); obs_data_set_string(settings, "rot_sign", "+"); obs_data_t *obj = obs_data_get_obj(settings, S_CROP); if (obj) { obs_data_set_string(obj, "left_sign", "+"); obs_data_set_string(obj, "top_sign", "+"); obs_data_set_string(obj, "right_sign", "+"); obs_data_set_string(obj, "bottom_sign", "+"); obs_data_release(obj); } } obs_data_unset_user_value(settings, S_TRANSFORM_RELATIVE); } calc_relative_to(move_source); move_source->start_trigger = (uint32_t)obs_data_get_int(settings, S_START_TRIGGER); move_source->stop_trigger = (uint32_t)obs_data_get_int(settings, S_STOP_TRIGGER); const char *simultaneous_move_name = obs_data_get_string(settings, S_SIMULTANEOUS_MOVE); if (!move_source->simultaneous_move_name || strcmp(move_source->simultaneous_move_name, simultaneous_move_name) != 0) { bfree(move_source->simultaneous_move_name); move_source->simultaneous_move_name = bstrdup(simultaneous_move_name); } const char *next_move_name = obs_data_get_string(settings, S_NEXT_MOVE); if (!move_source->next_move_name || strcmp(move_source->next_move_name, next_move_name) != 0) { bfree(move_source->next_move_name); move_source->next_move_name = bstrdup(next_move_name); move_source->reverse = false; } move_source->next_move_on = obs_data_get_int(settings, S_NEXT_MOVE_ON); move_source->change_order = obs_data_get_int(settings, S_CHANGE_ORDER); move_source->order_position = obs_data_get_int(settings, S_ORDER_POSITION); move_source->media_action_start = obs_data_get_int(settings, S_MEDIA_ACTION_START); move_source->media_time_start = obs_data_get_int(settings, S_MEDIA_ACTION_START_TIME); move_source->media_action_end = obs_data_get_int(settings, S_MEDIA_ACTION_END); move_source->media_time_end = obs_data_get_int(settings, S_MEDIA_ACTION_END_TIME); move_source->mute_action = obs_data_get_int(settings, S_MUTE_ACTION); move_source->audio_fade = obs_data_get_bool(settings, S_AUDIO_FADE); move_source->audio_fade_to = (float)obs_data_get_double(settings, S_AUDIO_FADE_PERCENT) / 100.0f; if (move_source->start_trigger == START_TRIGGER_LOAD) { move_source_start(move_source); } } void update_transform_text(struct move_source_info *move_source, obs_data_t *settings) { obs_data_t *pos = obs_data_get_obj(settings, S_POS); obs_data_t *scale = obs_data_get_obj(settings, S_SCALE); obs_data_t *bounds = obs_data_get_obj(settings, S_BOUNDS); obs_data_t *crop = obs_data_get_obj(settings, S_CROP); char transform_text[500]; if (move_source->scene_item) { if (obs_sceneitem_get_bounds_type(move_source->scene_item) == OBS_BOUNDS_NONE) { snprintf( transform_text, 500, "pos: x%c%.1f y%c%.1f rot:%c%.1f scale: x%c%.3f y%c%.3f crop: l%c%d t%c%d r%c%d b%c%d", obs_data_get_char(pos, "x_sign"), obs_data_get_double(pos, "x"), obs_data_get_char(pos, "y_sign"), obs_data_get_double(pos, "y"), obs_data_get_char(settings, "rot_sign"), obs_data_get_double(settings, S_ROT), obs_data_get_char(scale, "x_sign"), obs_data_get_double(scale, "x"), obs_data_get_char(scale, "y_sign"), obs_data_get_double(scale, "y"), obs_data_get_char(crop, "left_sign"), (int)obs_data_get_int(crop, "left"), obs_data_get_char(crop, "top_sign"), (int)obs_data_get_int(crop, "top"), obs_data_get_char(crop, "right_sign"), (int)obs_data_get_int(crop, "right"), obs_data_get_char(crop, "bottom_sign"), (int)obs_data_get_int(crop, "bottom")); } else { snprintf( transform_text, 500, "pos: x%c%.1f y%c%.1f rot:%c%.1f bounds: x%c%.3f y%c%.3f crop: l%c%d t%c%d r%c%d b%c%d", obs_data_get_char(pos, "x_sign"), obs_data_get_double(pos, "x"), obs_data_get_char(pos, "y_sign"), obs_data_get_double(pos, "y"), obs_data_get_char(settings, "rot_sign"), obs_data_get_double(settings, S_ROT), obs_data_get_char(bounds, "x_sign"), obs_data_get_double(bounds, "x"), obs_data_get_char(bounds, "y_sign"), obs_data_get_double(bounds, "y"), obs_data_get_char(crop, "left_sign"), (int)obs_data_get_int(crop, "left"), obs_data_get_char(crop, "top_sign"), (int)obs_data_get_int(crop, "top"), obs_data_get_char(crop, "right_sign"), (int)obs_data_get_int(crop, "right"), obs_data_get_char(crop, "bottom_sign"), (int)obs_data_get_int(crop, "bottom")); } } else { snprintf( transform_text, 500, "pos: x%c%.1f y%c%.1f rot:%c%.1f scale: x%c%.3f y%c%.3f bounds: x%c%.3f y%c%.3f crop: l%c%d t%c%d r%c%d b%c%d", obs_data_get_char(pos, "x_sign"), obs_data_get_double(pos, "x"), obs_data_get_char(pos, "y_sign"), obs_data_get_double(pos, "y"), obs_data_get_char(settings, "rot_sign"), obs_data_get_double(settings, S_ROT), obs_data_get_char(scale, "x_sign"), obs_data_get_double(scale, "x"), obs_data_get_char(scale, "y_sign"), obs_data_get_double(scale, "y"), obs_data_get_char(bounds, "x_sign"), obs_data_get_double(bounds, "x"), obs_data_get_char(bounds, "y_sign"), obs_data_get_double(bounds, "y"), obs_data_get_char(crop, "left_sign"), (int)obs_data_get_int(crop, "left"), obs_data_get_char(crop, "top_sign"), (int)obs_data_get_int(crop, "top"), obs_data_get_char(crop, "right_sign"), (int)obs_data_get_int(crop, "right"), obs_data_get_char(crop, "bottom_sign"), (int)obs_data_get_int(crop, "bottom")); } obs_data_set_string(settings, S_TRANSFORM_TEXT, transform_text); obs_data_release(pos); obs_data_release(scale); obs_data_release(bounds); obs_data_release(crop); } void move_source_load(void *data, obs_data_t *settings) { struct move_source_info *move_source = data; move_source_update(move_source, settings); update_transform_text(move_source, settings); } void move_source_source_rename(void *data, calldata_t *call_data) { struct move_source_info *move_source = data; const char *new_name = calldata_string(call_data, "new_name"); const char *prev_name = calldata_string(call_data, "prev_name"); obs_data_t *settings = obs_source_get_settings(move_source->source); if (!settings || !new_name || !prev_name) return; const char *source_name = obs_data_get_string(settings, S_SOURCE); if (source_name && strlen(source_name) && strcmp(source_name, prev_name) == 0) { obs_data_set_string(settings, S_SOURCE, new_name); } obs_data_release(settings); } static void *move_source_create(obs_data_t *settings, obs_source_t *source) { struct move_source_info *move_source = bzalloc(sizeof(struct move_source_info)); move_source->source = source; move_source->move_start_hotkey = OBS_INVALID_HOTKEY_ID; move_source_update(move_source, settings); signal_handler_connect(obs_get_signal_handler(), "source_rename", move_source_source_rename, move_source); return move_source; } static void move_source_destroy(void *data) { struct move_source_info *move_source = data; signal_handler_disconnect(obs_get_signal_handler(), "source_rename", move_source_source_rename, move_source); obs_source_t *parent = obs_filter_get_parent(move_source->source); if (parent) { signal_handler_t *sh = obs_source_get_signal_handler(parent); signal_handler_disconnect(sh, "item_remove", move_source_item_remove, move_source); } obs_source_t *source = NULL; if (move_source->scene_item) { source = obs_sceneitem_get_source(move_source->scene_item); } if (!source && move_source->source_name && strlen(move_source->source_name)) { source = obs_get_source_by_name(move_source->source_name); obs_source_release(source); } if (source) { signal_handler_t *sh = obs_source_get_signal_handler(source); if (sh) { signal_handler_disconnect(sh, "activate", move_source_source_activate, data); signal_handler_disconnect(sh, "deactivate", move_source_source_deactivate, data); signal_handler_disconnect( sh, "show", move_source_source_show, data); signal_handler_disconnect( sh, "hide", move_source_source_hide, data); signal_handler_disconnect( sh, "media_started", move_source_source_media_started, data); signal_handler_disconnect( sh, "media_ended", move_source_source_media_ended, data); signal_handler_disconnect( sh, "remove", move_source_source_remove, data); } } move_source->scene_item = NULL; if (move_source->move_start_hotkey != OBS_INVALID_HOTKEY_ID) obs_hotkey_unregister(move_source->move_start_hotkey); bfree(move_source->source_name); bfree(move_source->filter_name); bfree(move_source->simultaneous_move_name); bfree(move_source->next_move_name); da_free(move_source->filters_done); bfree(move_source); } static void obs_data_set_vec2_sign(obs_data_t *data, const char *name, const struct vec2 *val, char x_sign, char y_sign) { obs_data_t *obj = obs_data_create(); obs_data_set_double(obj, "x", val->x); obs_data_set_char(obj, "x_sign", x_sign); obs_data_set_double(obj, "y", val->y); obs_data_set_char(obj, "y_sign", y_sign); obs_data_set_obj(data, name, obj); obs_data_release(obj); } static void obs_data_set_crop_sign(obs_data_t *settings, const char *name, struct obs_sceneitem_crop *crop, char crop_left_sign, char crop_top_sign, char crop_right_sign, char crop_bottom_sign) { obs_data_t *obj = obs_data_create(); obs_data_set_double(obj, "left", crop->left); obs_data_set_char(obj, "left_sign", crop_left_sign); obs_data_set_double(obj, "top", crop->top); obs_data_set_char(obj, "top_sign", crop_top_sign); obs_data_set_double(obj, "right", crop->right); obs_data_set_char(obj, "right_sign", crop_right_sign); obs_data_set_double(obj, "bottom", crop->bottom); obs_data_set_char(obj, "bottom_sign", crop_bottom_sign); obs_data_set_obj(settings, name, obj); obs_data_release(obj); } bool move_source_get_transform(obs_properties_t *props, obs_property_t *property, void *data) { UNUSED_PARAMETER(props); UNUSED_PARAMETER(property); struct move_source_info *move_source = data; bool settings_changed = false; if (!move_source->scene_item && move_source->source_name && strlen(move_source->source_name)) { obs_source_t *parent = obs_filter_get_parent(move_source->source); if (parent) { obs_scene_t *scene = obs_scene_from_source(parent); if (!scene) scene = obs_group_from_source(parent); if (scene) obs_scene_enum_items(scene, find_sceneitem, data); } } if (!move_source->scene_item) return settings_changed; settings_changed = true; obs_data_t *settings = obs_source_get_settings(move_source->source); struct vec2 pos; obs_sceneitem_get_pos(move_source->scene_item, &pos); struct vec2 scale; obs_sceneitem_get_scale(move_source->scene_item, &scale); struct vec2 bounds; obs_sceneitem_get_bounds(move_source->scene_item, &bounds); struct obs_sceneitem_crop crop; obs_sceneitem_get_crop(move_source->scene_item, &crop); obs_data_set_double(settings, S_ROT, obs_sceneitem_get_rot(move_source->scene_item)); obs_data_set_char(settings, "rot_sign", ' '); obs_data_set_vec2_sign(settings, S_POS, &pos, ' ', ' '); obs_data_set_vec2_sign(settings, S_SCALE, &scale, ' ', ' '); obs_data_set_vec2_sign(settings, S_BOUNDS, &bounds, ' ', ' '); obs_data_set_crop_sign(settings, S_CROP, &crop, ' ', ' ', ' ', ' '); move_source_update(data, settings); update_transform_text(move_source, settings); obs_data_release(settings); return settings_changed; } bool move_source_relative(obs_properties_t *props, obs_property_t *property, void *data) { UNUSED_PARAMETER(props); UNUSED_PARAMETER(property); struct move_source_info *move_source = data; bool settings_changed = true; obs_data_t *settings = obs_source_get_settings(move_source->source); struct vec2 pos; pos.x = 0.0f; pos.y = 0.0f; struct vec2 scale; scale.x = 1.0f; scale.y = 1.0f; struct vec2 bounds; bounds.x = 1.0f; bounds.y = 1.0f; struct obs_sceneitem_crop crop = {0, 0, 0, 0}; obs_data_set_double(settings, S_ROT, 0.0); obs_data_set_char(settings, "rot_sign", '+'); obs_data_set_vec2_sign(settings, S_POS, &pos, '+', '+'); obs_data_set_vec2_sign(settings, S_SCALE, &scale, '*', '*'); obs_data_set_vec2_sign(settings, S_BOUNDS, &bounds, '*', '*'); obs_data_set_crop_sign(settings, S_CROP, &crop, '+', '+', '+', '+'); update_transform_text(move_source, settings); move_source_update(data, settings); obs_data_release(settings); return settings_changed; } void prop_list_add_move_source_filter(obs_source_t *parent, obs_source_t *child, void *data) { UNUSED_PARAMETER(parent); if (strcmp(obs_source_get_unversioned_id(child), MOVE_SOURCE_FILTER_ID) != 0 && strcmp(obs_source_get_unversioned_id(child), MOVE_VALUE_FILTER_ID) != 0 && strcmp(obs_source_get_unversioned_id(child), MOVE_AUDIO_VALUE_FILTER_ID) != 0) return; obs_property_t *p = data; const char *name = obs_source_get_name(child); obs_property_list_add_string(p, name, name); } bool move_source_changed(void *data, obs_properties_t *props, obs_property_t *property, obs_data_t *settings) { struct move_source_info *move_source = data; bool refresh = false; const char *source_name = obs_data_get_string(settings, S_SOURCE); if (move_source->source_name && strcmp(move_source->source_name, source_name) == 0) return refresh; move_source_source_changed(move_source, source_name); obs_source_t *parent = obs_filter_get_parent(move_source->source); obs_property_t *p = obs_properties_get(props, S_SIMULTANEOUS_MOVE); if (p) { obs_property_list_clear(p); obs_property_list_add_string( p, obs_module_text("SimultaneousMove.None"), ""); obs_source_enum_filters(parent, prop_list_add_move_source_filter, p); obs_source_t *source = obs_sceneitem_get_source(move_source->scene_item); if (source) obs_source_enum_filters( source, prop_list_add_move_source_filter, p); } p = obs_properties_get(props, S_NEXT_MOVE); if (p) { obs_property_list_clear(p); obs_property_list_add_string( p, obs_module_text("NextMove.None"), ""); obs_property_list_add_string( p, obs_module_text("NextMove.Reverse"), NEXT_MOVE_REVERSE); obs_source_enum_filters(parent, prop_list_add_move_source_filter, p); obs_source_t *source = obs_sceneitem_get_source(move_source->scene_item); if (source) obs_source_enum_filters( source, prop_list_add_move_source_filter, p); } obs_source_t *source = obs_get_source_by_name(source_name); if (source) { uint32_t flags = obs_source_get_output_flags(source); const bool media = flags & OBS_SOURCE_CONTROLLABLE_MEDIA; p = obs_properties_get(props, S_MEDIA_ACTION); obs_property_set_visible(p, media); p = obs_properties_get(props, S_AUDIO_ACTION); const bool audio = flags & OBS_SOURCE_AUDIO; obs_property_set_visible(p, audio); obs_source_release(source); } else { p = obs_properties_get(props, S_MEDIA_ACTION); obs_property_set_visible(p, false); p = obs_properties_get(props, S_AUDIO_ACTION); obs_property_set_visible(p, false); } refresh = move_source_get_transform(props, property, data); return refresh; } bool prop_list_add_sceneitem(obs_scene_t *scene, obs_sceneitem_t *item, void *data); void prop_list_add_easings(obs_property_t *p); void prop_list_add_easing_functions(obs_property_t *p); bool move_source_transform_text_changed(void *data, obs_properties_t *props, obs_property_t *property, obs_data_t *settings) { UNUSED_PARAMETER(props); UNUSED_PARAMETER(property); struct move_source_info *move_source = data; const char *transform_text = obs_data_get_string(settings, S_TRANSFORM_TEXT); struct vec2 pos; float rot; struct vec2 scale; struct vec2 bounds; struct obs_sceneitem_crop crop; char pos_x_sign, pos_y_sign, rot_sign, scale_x_sign, scale_y_sign, bounds_x_sign, bounds_y_sign, crop_left_sign, crop_top_sign, crop_right_sign, crop_bottom_sign; if (move_source->scene_item) { if (obs_sceneitem_get_bounds_type(move_source->scene_item) == OBS_BOUNDS_NONE) { if (sscanf(transform_text, "pos: x%c%f y%c%f rot:%c%f scale: x%c%f y%c%f crop: l%c%d t%c%d r%c%d b%c%d", &pos_x_sign, &pos.x, &pos_y_sign, &pos.y, &rot_sign, &rot, &scale_x_sign, &scale.x, &scale_y_sign, &scale.y, &crop_left_sign, &crop.left, &crop_top_sign, &crop.top, &crop_right_sign, &crop.right, &crop_bottom_sign, &crop.bottom) != 18) { update_transform_text(move_source, settings); return true; } obs_data_set_vec2_sign(settings, S_SCALE, &scale, scale_x_sign, scale_y_sign); } else { if (sscanf(transform_text, "pos: x%c%f y%c%f rot:%c%f bounds: x%c%f y%c%f crop: l%c%d t%c%d r%c%d b%c%d", &pos_x_sign, &pos.x, &pos_y_sign, &pos.y, &rot_sign, &rot, &bounds_x_sign, &bounds.x, &bounds_y_sign, &bounds.y, &crop_left_sign, &crop.left, &crop_top_sign, &crop.top, &crop_right_sign, &crop.right, &crop_bottom_sign, &crop.bottom) != 18) { update_transform_text(move_source, settings); return true; } obs_data_set_vec2_sign(settings, S_BOUNDS, &bounds, bounds_x_sign, bounds_y_sign); } } else { if (sscanf(transform_text, "pos: x%c%f y%c%f rot:%c%f scale: x%c%f y%c%f bounds: x%c%f y%c%f crop: l%c%d t%c%d r%c%d b%c%d", &pos_x_sign, &pos.x, &pos_y_sign, &pos.y, &rot_sign, &rot, &scale_x_sign, &scale.x, &scale_y_sign, &scale.y, &bounds_x_sign, &bounds.x, &bounds_y_sign, &bounds.y, &crop_left_sign, &crop.left, &crop_top_sign, &crop.top, &crop_right_sign, &crop.right, &crop_bottom_sign, &crop.bottom) != 22) { update_transform_text(move_source, settings); return true; } obs_data_set_vec2_sign(settings, S_SCALE, &scale, scale_x_sign, scale_y_sign); obs_data_set_vec2_sign(settings, S_BOUNDS, &bounds, bounds_x_sign, bounds_y_sign); } obs_data_set_vec2_sign(settings, S_POS, &pos, pos_x_sign, pos_y_sign); obs_data_set_double(settings, S_ROT, rot); obs_data_set_char(settings, "rot_sign", rot_sign); obs_data_set_crop_sign(settings, S_CROP, &crop, crop_left_sign, crop_top_sign, crop_right_sign, crop_bottom_sign); return false; } static void prop_list_add_media_actions(obs_property_t *p) { obs_property_list_add_int(p, obs_module_text("MediaAction.None"), MEDIA_ACTION_NONE); obs_property_list_add_int(p, obs_module_text("MediaAction.Play"), MEDIA_ACTION_PLAY); obs_property_list_add_int(p, obs_module_text("MediaAction.Pause"), MEDIA_ACTION_PAUSE); obs_property_list_add_int(p, obs_module_text("MediaAction.Stop"), MEDIA_ACTION_STOP); obs_property_list_add_int(p, obs_module_text("MediaAction.Restart"), MEDIA_ACTION_RESTART); obs_property_list_add_int(p, obs_module_text("MediaAction.Next"), MEDIA_ACTION_NEXT); obs_property_list_add_int(p, obs_module_text("MediaAction.Previous"), MEDIA_ACTION_PREVIOUS); obs_property_list_add_int(p, obs_module_text("MediaAction.PlayFrom"), MEDIA_ACTION_PLAY_FROM); obs_property_list_add_int(p, obs_module_text("MediaAction.PauseAt"), MEDIA_ACTION_PAUSE_AT); } static obs_properties_t *move_source_properties(void *data) { obs_properties_t *ppts = obs_properties_create(); struct move_source_info *move_source = data; obs_source_t *parent = obs_filter_get_parent(move_source->source); obs_scene_t *scene = obs_scene_from_source(parent); if (!scene) scene = obs_group_from_source(parent); if (!scene) { obs_properties_add_button(ppts, "warning", obs_module_text("ScenesOnlyFilter"), NULL); return ppts; } if (!move_source->scene_item && move_source->source_name && strlen(move_source->source_name)) { obs_scene_enum_items(scene, find_sceneitem, move_source); } obs_properties_t *group = obs_properties_create(); obs_property_t *p = obs_properties_add_list(group, S_SOURCE, obs_module_text("Source"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_scene_enum_items(scene, prop_list_add_sceneitem, p); obs_property_set_modified_callback2(p, move_source_changed, data); p = obs_properties_add_int(group, S_START_DELAY, obs_module_text("StartDelay"), 0, 10000000, 100); obs_property_int_set_suffix(p, "ms"); obs_properties_t *duration = obs_properties_create(); p = obs_properties_add_int(duration, S_DURATION, "", 10, 10000000, 100); obs_property_int_set_suffix(p, "ms"); p = obs_properties_add_group(group, S_CUSTOM_DURATION, obs_module_text("CustomDuration"), OBS_GROUP_CHECKABLE, duration); p = obs_properties_add_int(group, S_END_DELAY, obs_module_text("EndDelay"), 0, 10000000, 100); obs_property_int_set_suffix(p, "ms"); p = obs_properties_add_list(group, S_EASING_MATCH, obs_module_text("Easing"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); prop_list_add_easings(p); p = obs_properties_add_list(group, S_EASING_FUNCTION_MATCH, obs_module_text("EasingFunction"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); prop_list_add_easing_functions(p); p = obs_properties_add_group(ppts, S_GENERAL, obs_module_text("General"), OBS_GROUP_NORMAL, group); group = obs_properties_create(); p = obs_properties_add_text(group, S_TRANSFORM_TEXT, obs_module_text("Transform"), OBS_TEXT_DEFAULT); obs_property_set_modified_callback2( p, move_source_transform_text_changed, data); obs_properties_add_button(group, "transform_get", obs_module_text("GetTransform"), move_source_get_transform); obs_properties_add_button(group, "switch_to_relative", obs_module_text("TransformRelative"), move_source_relative); obs_properties_add_float_slider(group, S_CURVE_MATCH, obs_module_text("Curve"), -2.0, 2.0, 0.01); p = obs_properties_add_group(ppts, S_TRANSFORM, obs_module_text("Transform"), OBS_GROUP_CHECKABLE, group); group = obs_properties_create(); p = obs_properties_add_list(group, S_CHANGE_VISIBILITY, obs_module_text("ChangeVisibility"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("ChangeVisibility.No"), CHANGE_VISIBILITY_NONE); obs_property_list_add_int(p, obs_module_text("ChangeVisibility.ShowStart"), CHANGE_VISIBILITY_SHOW_START); obs_property_list_add_int(p, obs_module_text("ChangeVisibility.ShowEnd"), CHANGE_VISIBILITY_SHOW_END); obs_property_list_add_int( p, obs_module_text("ChangeVisibility.ShowStartEnd"), CHANGE_VISIBILITY_SHOW_START_END); obs_property_list_add_int(p, obs_module_text("ChangeVisibility.HideStart"), CHANGE_VISIBILITY_HIDE_START); obs_property_list_add_int(p, obs_module_text("ChangeVisibility.HideEnd"), CHANGE_VISIBILITY_HIDE_END); obs_property_list_add_int( p, obs_module_text("ChangeVisibility.HideStartEnd"), CHANGE_VISIBILITY_HIDE_START_END); obs_property_list_add_int(p, obs_module_text("ChangeVisibility.Toggle"), CHANGE_VISIBILITY_TOGGLE); obs_property_list_add_int( p, obs_module_text("ChangeVisibility.ToggleStart"), CHANGE_VISIBILITY_TOGGLE_START); obs_property_list_add_int(p, obs_module_text("ChangeVisibility.ToggleEnd"), CHANGE_VISIBILITY_TOGGLE_END); p = obs_properties_add_list(group, S_CHANGE_ORDER, obs_module_text("ChangeOrder"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("ChangeOrder.No"), CHANGE_ORDER_NONE); obs_property_list_add_int(p, obs_module_text("ChangeOrder.StartAbsolute"), CHANGE_ORDER_START | CHANGE_ORDER_ABSOLUTE); obs_property_list_add_int(p, obs_module_text("ChangeOrder.EndAbsolute"), CHANGE_ORDER_END | CHANGE_ORDER_ABSOLUTE); obs_property_list_add_int(p, obs_module_text("ChangeOrder.StartRelative"), CHANGE_ORDER_START | CHANGE_ORDER_RELATIVE); obs_property_list_add_int(p, obs_module_text("ChangeOrder.EndRelative"), CHANGE_ORDER_END | CHANGE_ORDER_RELATIVE); p = obs_properties_add_int(group, S_ORDER_POSITION, obs_module_text("OrderPosition"), -1000, 1000, 1); p = obs_properties_add_group(ppts, S_VISIBILITY_ORDER, obs_module_text("VisibilityOrder"), OBS_GROUP_NORMAL, group); obs_source_t *source = obs_sceneitem_get_source(move_source->scene_item); group = obs_properties_create(); const uint32_t flags = source ? obs_source_get_output_flags(source) : 0; const bool media = flags & OBS_SOURCE_CONTROLLABLE_MEDIA; p = obs_properties_add_list(group, S_MEDIA_ACTION_START, obs_module_text("MediaAction.Start"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); prop_list_add_media_actions(p); p = obs_properties_add_int(group, S_MEDIA_ACTION_START_TIME, obs_module_text("MediaAction.Time"), -1000000, 1000000, 100); obs_property_int_set_suffix(p, "ms"); p = obs_properties_add_list(group, S_MEDIA_ACTION_END, obs_module_text("MediaAction.End"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); prop_list_add_media_actions(p); p = obs_properties_add_int(group, S_MEDIA_ACTION_END_TIME, obs_module_text("MediaAction.Time"), -1000000, 1000000, 100); obs_property_int_set_suffix(p, "ms"); p = obs_properties_add_group(ppts, S_MEDIA_ACTION, obs_module_text("MediaAction"), OBS_GROUP_NORMAL, group); obs_property_set_visible(p, media); const bool audio = flags & OBS_SOURCE_AUDIO; group = obs_properties_create(); p = obs_properties_add_list(group, S_MUTE_ACTION, obs_module_text("MuteAction"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("MuteAction.None"), MUTE_ACTION_NONE); obs_property_list_add_int(p, obs_module_text("MuteAction.MuteStart"), MUTE_ACTION_MUTE_START); obs_property_list_add_int(p, obs_module_text("MuteAction.MuteEnd"), MUTE_ACTION_MUTE_END); obs_property_list_add_int(p, obs_module_text("MuteAction.UnmuteStart"), MUTE_ACTION_UNMUTE_START); obs_property_list_add_int(p, obs_module_text("MuteAction.UnmuteEnd"), MUTE_ACTION_UNMUTE_END); obs_property_list_add_int(p, obs_module_text("MuteAction.MuteDuring"), MUTE_ACTION_MUTE_DURING); obs_property_list_add_int(p, obs_module_text("MuteAction.UnmuteDuring"), MUTE_ACTION_UNMUTE_DURING); obs_properties_t *fade = obs_properties_create(); p = obs_properties_add_float_slider(fade, S_AUDIO_FADE_PERCENT, "", 0.0, 100.0, 1.0); obs_property_float_set_suffix(p, "%"); p = obs_properties_add_group(group, S_AUDIO_FADE, obs_module_text("AudioFade"), OBS_GROUP_CHECKABLE, fade); p = obs_properties_add_group(ppts, S_AUDIO_ACTION, obs_module_text("AudioAction"), OBS_GROUP_NORMAL, group); obs_property_set_visible(p, audio); group = obs_properties_create(); p = obs_properties_add_bool(group, S_ENABLED_MATCH_MOVING, obs_module_text("EnabledMatchMoving")); p = obs_properties_add_list(group, S_START_TRIGGER, obs_module_text("StartTrigger"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("StartTrigger.None"), START_TRIGGER_NONE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Activate"), START_TRIGGER_ACTIVATE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Deactivate"), START_TRIGGER_DEACTIVATE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Show"), START_TRIGGER_SHOW); obs_property_list_add_int(p, obs_module_text("StartTrigger.Hide"), START_TRIGGER_HIDE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Enable"), START_TRIGGER_ENABLE); obs_property_list_add_int( p, obs_module_text("StartTrigger.SourceActivate"), START_TRIGGER_SOURCE_ACTIVATE); obs_property_list_add_int( p, obs_module_text("StartTrigger.SourceDeactivate"), START_TRIGGER_SOURCE_DEACTIVATE); obs_property_list_add_int(p, obs_module_text("StartTrigger.SourceShow"), START_TRIGGER_SOURCE_SHOW); obs_property_list_add_int(p, obs_module_text("StartTrigger.SourceHide"), START_TRIGGER_SOURCE_HIDE); obs_property_list_add_int(p, obs_module_text("StartTrigger.MediaStarted"), START_TRIGGER_MEDIA_STARTED); obs_property_list_add_int(p, obs_module_text("StartTrigger.MediaEnded"), START_TRIGGER_MEDIA_ENDED); obs_property_list_add_int(p, obs_module_text("StartTrigger.Load"), START_TRIGGER_LOAD); p = obs_properties_add_list(group, S_STOP_TRIGGER, obs_module_text("StopTrigger"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("StopTrigger.None"), START_TRIGGER_NONE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Activate"), START_TRIGGER_ACTIVATE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Deactivate"), START_TRIGGER_DEACTIVATE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Show"), START_TRIGGER_SHOW); obs_property_list_add_int(p, obs_module_text("StartTrigger.Hide"), START_TRIGGER_HIDE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Enable"), START_TRIGGER_ENABLE); obs_property_list_add_int( p, obs_module_text("StartTrigger.SourceActivate"), START_TRIGGER_SOURCE_ACTIVATE); obs_property_list_add_int( p, obs_module_text("StartTrigger.SourceDeactivate"), START_TRIGGER_SOURCE_DEACTIVATE); obs_property_list_add_int(p, obs_module_text("StartTrigger.SourceShow"), START_TRIGGER_SOURCE_SHOW); obs_property_list_add_int(p, obs_module_text("StartTrigger.SourceHide"), START_TRIGGER_SOURCE_HIDE); obs_property_list_add_int(p, obs_module_text("StartTrigger.MediaStarted"), START_TRIGGER_MEDIA_STARTED); obs_property_list_add_int(p, obs_module_text("StartTrigger.MediaEnded"), START_TRIGGER_MEDIA_ENDED); p = obs_properties_add_list(group, S_SIMULTANEOUS_MOVE, obs_module_text("SimultaneousMove"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string( p, obs_module_text("SimultaneousMove.None"), ""); obs_source_enum_filters(parent, prop_list_add_move_source_filter, p); if (source) obs_source_enum_filters(source, prop_list_add_move_source_filter, p); p = obs_properties_add_list(group, S_NEXT_MOVE, obs_module_text("NextMove"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, obs_module_text("NextMove.None"), ""); obs_property_list_add_string(p, obs_module_text("NextMove.Reverse"), NEXT_MOVE_REVERSE); obs_source_enum_filters(parent, prop_list_add_move_source_filter, p); if (source) obs_source_enum_filters(source, prop_list_add_move_source_filter, p); p = obs_properties_add_list(group, S_NEXT_MOVE_ON, obs_module_text("NextMoveOn"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("NextMoveOn.End"), NEXT_MOVE_ON_END); obs_property_list_add_int(p, obs_module_text("NextMoveOn.Hotkey"), NEXT_MOVE_ON_HOTKEY); obs_properties_add_button(group, "move_source_start", obs_module_text("Start"), move_source_start_button); p = obs_properties_add_group(ppts, S_ACTIONS, obs_module_text("Actions"), OBS_GROUP_NORMAL, group); return ppts; } void move_source_defaults(obs_data_t *settings) { obs_data_set_default_bool(settings, S_CUSTOM_DURATION, true); obs_data_set_default_bool(settings, S_TRANSFORM, true); obs_data_set_default_int(settings, S_DURATION, 300); obs_data_set_default_int(settings, S_EASING_MATCH, EASE_IN_OUT); obs_data_set_default_int(settings, S_EASING_FUNCTION_MATCH, EASING_CUBIC); obs_data_set_default_double(settings, S_CURVE_MATCH, 0.0); obs_data_set_default_bool(settings, S_ENABLED_MATCH_MOVING, true); } void move_source_video_render(void *data, gs_effect_t *effect) { UNUSED_PARAMETER(effect); struct move_source_info *filter = data; obs_source_skip_video_filter(filter->source); } static const char *move_source_get_name(void *type_data) { UNUSED_PARAMETER(type_data); return obs_module_text("MoveSourceFilter"); } float get_eased(float f, long long easing, long long easing_function); void vec2_bezier(struct vec2 *dst, struct vec2 *begin, struct vec2 *control, struct vec2 *end, const float t); void move_source_ended(struct move_source_info *move_source) { if (move_source->enabled_match_moving && (move_source->reverse || move_source->next_move_on == NEXT_MOVE_ON_HOTKEY || !move_source->next_move_name || strcmp(move_source->next_move_name, NEXT_MOVE_REVERSE) != 0) && obs_source_enabled(move_source->source)) { obs_source_set_enabled(move_source->source, false); } if (move_source->change_visibility == CHANGE_VISIBILITY_HIDE_END || move_source->change_visibility == CHANGE_VISIBILITY_SHOW_START_END) { obs_sceneitem_set_visible(move_source->scene_item, false); } else if (move_source->change_visibility == CHANGE_VISIBILITY_SHOW_END || move_source->change_visibility == CHANGE_VISIBILITY_HIDE_START_END) { obs_sceneitem_set_visible(move_source->scene_item, true); } else if (move_source->change_visibility == CHANGE_VISIBILITY_TOGGLE_END) { obs_sceneitem_set_visible( move_source->scene_item, !obs_sceneitem_visible(move_source->scene_item)); } else if (move_source->change_visibility == CHANGE_VISIBILITY_TOGGLE && !move_source->visibility_toggled) { obs_sceneitem_set_visible(move_source->scene_item, false); } move_source_media_action(move_source, move_source->media_action_end, move_source->media_time_end); if ((move_source->mute_action == MUTE_ACTION_MUTE_END || move_source->mute_action == MUTE_ACTION_UNMUTE_DURING) && !obs_source_muted( obs_sceneitem_get_source(move_source->scene_item))) { obs_source_set_muted( obs_sceneitem_get_source(move_source->scene_item), true); } else if ((move_source->mute_action == MUTE_ACTION_UNMUTE_END || move_source->mute_action == MUTE_ACTION_MUTE_DURING) && obs_source_muted( obs_sceneitem_get_source(move_source->scene_item))) { obs_source_set_muted( obs_sceneitem_get_source(move_source->scene_item), false); } if ((move_source->change_order & CHANGE_ORDER_END) != 0) { if ((move_source->change_order & CHANGE_ORDER_RELATIVE) != 0 && move_source->order_position) { if (move_source->order_position > 0) { for (int i = 0; i < move_source->order_position; i++) { obs_sceneitem_set_order( move_source->scene_item, OBS_ORDER_MOVE_UP); } } else if (move_source->order_position < 0) { for (int i = 0; i > move_source->order_position; i--) { obs_sceneitem_set_order( move_source->scene_item, OBS_ORDER_MOVE_DOWN); } } } else if ((move_source->change_order & CHANGE_ORDER_ABSOLUTE) != 0) { obs_sceneitem_set_order_position( move_source->scene_item, move_source->order_position); } } if (move_source->next_move_on == NEXT_MOVE_ON_END && move_source->next_move_name && strlen(move_source->next_move_name) && (!move_source->filter_name || strcmp(move_source->filter_name, move_source->next_move_name) != 0)) { if (strcmp(move_source->next_move_name, NEXT_MOVE_REVERSE) == 0) { move_source->reverse = !move_source->reverse; if (move_source->reverse) move_source_start(move_source); } else { obs_source_t *parent = obs_filter_get_parent(move_source->source); if (parent) { obs_source_t *filter = obs_source_get_filter_by_name( parent, move_source->next_move_name); if (!filter) { filter = obs_source_get_filter_by_name( obs_sceneitem_get_source( move_source->scene_item), move_source->next_move_name); } if (filter) { if (strcmp(obs_source_get_unversioned_id( filter), MOVE_SOURCE_FILTER_ID) == 0) { struct move_source_info *filter_data = obs_obj_get_data( filter); move_source_start(filter_data); } else if ( strcmp(obs_source_get_unversioned_id( filter), MOVE_VALUE_FILTER_ID) == 0 || strcmp(obs_source_get_unversioned_id( filter), MOVE_AUDIO_VALUE_FILTER_ID) == 0) { struct move_value_info *filter_data = obs_obj_get_data( filter); move_value_start(filter_data); } obs_source_release(filter); } } } } else if (move_source->next_move_on == NEXT_MOVE_ON_HOTKEY && move_source->next_move_name && strcmp(move_source->next_move_name, NEXT_MOVE_REVERSE) == 0) { move_source->reverse = !move_source->reverse; } } void move_source_tick(void *data, float seconds) { struct move_source_info *move_source = data; const bool enabled = obs_source_enabled(move_source->source); if (move_source->enabled != enabled) { if (enabled && (move_source->start_trigger == START_TRIGGER_ENABLE || (move_source->enabled_match_moving && !move_source->moving))) move_source_start(move_source); if (enabled && move_source->stop_trigger == START_TRIGGER_ENABLE) move_source_stop(move_source); move_source->enabled = enabled; } if (move_source->enabled_match_moving && enabled != move_source->moving) { if (enabled) { move_source_start(move_source); } else { move_source_stop(move_source); } } if (!move_source->moving || !enabled) return; if (!move_source->scene_item || !move_source->duration) { move_source->moving = false; return; } move_source->running_duration += seconds; if (move_source->running_duration * 1000.0f < (move_source->reverse ? move_source->end_delay : move_source->start_delay)) { if (!move_source->reverse) { calc_relative_to(move_source); } return; } if (move_source->running_duration * 1000.0f >= (float)(move_source->start_delay + move_source->duration + move_source->end_delay)) { move_source->moving = false; } float t = (move_source->running_duration * 1000.0f - (float)(move_source->reverse ? move_source->end_delay : move_source->start_delay)) / (float)move_source->duration; if (t >= 1.0f) { t = 1.0f; } if (move_source->reverse) { t = 1.0f - t; } t = get_eased(t, move_source->easing, move_source->easing_function); float ot = t; if (t > 1.0f) ot = 1.0f; else if (t < 0.0f) ot = 0.0f; if (move_source->audio_fade) { obs_source_set_volume( obs_sceneitem_get_source(move_source->scene_item), (1.0f - ot) * move_source->audio_fade_from + ot * move_source->audio_fade_to); } if (move_source->transform) { struct vec2 pos; if (move_source->curve != 0.0f) { const float diff_x = fabsf(move_source->pos_from.x - move_source->pos_to.x); const float diff_y = fabsf(move_source->pos_from.y - move_source->pos_to.y); struct vec2 control_pos; vec2_set(&control_pos, 0.5f * move_source->pos_from.x + 0.5f * move_source->pos_to.x, 0.5f * move_source->pos_from.y + 0.5f * move_source->pos_to.y); if (control_pos.x >= (move_source->canvas_width >> 1)) { control_pos.x += diff_y * move_source->curve; } else { control_pos.x -= diff_y * move_source->curve; } if (control_pos.y >= (move_source->canvas_height >> 1)) { control_pos.y += diff_x * move_source->curve; } else { control_pos.y -= diff_x * move_source->curve; } vec2_bezier(&pos, &move_source->pos_from, &control_pos, &move_source->pos_to, t); } else { vec2_set(&pos, (1.0f - t) * move_source->pos_from.x + t * move_source->pos_to.x, (1.0f - t) * move_source->pos_from.y + t * move_source->pos_to.y); } obs_sceneitem_defer_update_begin(move_source->scene_item); obs_sceneitem_set_pos(move_source->scene_item, &pos); const float rot = (1.0f - t) * move_source->rot_from + t * move_source->rot_to; obs_sceneitem_set_rot(move_source->scene_item, rot); struct vec2 scale; vec2_set(&scale, (1.0f - t) * move_source->scale_from.x + t * move_source->scale_to.x, (1.0f - t) * move_source->scale_from.y + t * move_source->scale_to.y); obs_sceneitem_set_scale(move_source->scene_item, &scale); struct vec2 bounds; vec2_set(&bounds, (1.0f - t) * move_source->bounds_from.x + t * move_source->bounds_to.x, (1.0f - t) * move_source->bounds_from.y + t * move_source->bounds_to.y); obs_sceneitem_set_bounds(move_source->scene_item, &bounds); struct obs_sceneitem_crop crop; crop.left = (int)((float)(1.0f - ot) * (float)move_source->crop_from.left + ot * (float)move_source->crop_to.left); crop.top = (int)((float)(1.0f - ot) * (float)move_source->crop_from.top + ot * (float)move_source->crop_to.top); crop.right = (int)((float)(1.0f - ot) * (float)move_source->crop_from.right + ot * (float)move_source->crop_to.right); crop.bottom = (int)((float)(1.0f - ot) * (float)move_source->crop_from.bottom + ot * (float)move_source->crop_to.bottom); obs_sceneitem_set_crop(move_source->scene_item, &crop); obs_sceneitem_defer_update_end(move_source->scene_item); } if (!move_source->moving) { move_source_ended(move_source); } } void move_source_activate(void *data) { struct move_source_info *move_source = data; if (move_source->start_trigger == START_TRIGGER_ACTIVATE) move_source_start(move_source); if (move_source->stop_trigger == START_TRIGGER_ACTIVATE) move_source_stop(move_source); } void move_source_deactivate(void *data) { struct move_source_info *move_source = data; if (move_source->start_trigger == START_TRIGGER_DEACTIVATE) move_source_start(move_source); if (move_source->stop_trigger == START_TRIGGER_DEACTIVATE) move_source_stop(move_source); } void move_source_show(void *data) { struct move_source_info *move_source = data; if (move_source->start_trigger == START_TRIGGER_SHOW) move_source_start(move_source); if (move_source->stop_trigger == START_TRIGGER_SHOW) move_source_stop(move_source); } void move_source_hide(void *data) { struct move_source_info *move_source = data; if (move_source->start_trigger == START_TRIGGER_HIDE) move_source_start(move_source); if (move_source->stop_trigger == START_TRIGGER_HIDE) move_source_stop(move_source); } struct obs_source_info move_source_filter = { .id = MOVE_SOURCE_FILTER_ID, .type = OBS_SOURCE_TYPE_FILTER, .output_flags = OBS_SOURCE_VIDEO, .get_name = move_source_get_name, .create = move_source_create, .destroy = move_source_destroy, .get_properties = move_source_properties, .get_defaults = move_source_defaults, .video_render = move_source_video_render, .video_tick = move_source_tick, .update = move_source_update, .load = move_source_load, .activate = move_source_activate, .deactivate = move_source_deactivate, .show = move_source_show, .hide = move_source_hide, }; obs-move-transition-2.5.7/move-transition-override-filter.c000066400000000000000000000315361417746432500240730ustar00rootroot00000000000000#include "move-transition.h" #include struct move_filter_info { obs_source_t *source; }; void move_filter_source_rename(void *data, calldata_t *call_data) { struct move_filter_info *move_filter = data; const char *new_name = calldata_string(call_data, "new_name"); const char *prev_name = calldata_string(call_data, "prev_name"); obs_data_t *settings = obs_source_get_settings(move_filter->source); if (!settings || !new_name || !prev_name) return; const char *source_name = obs_data_get_string(settings, S_SOURCE); if (source_name && strlen(source_name) && strcmp(source_name, prev_name) == 0) { obs_data_set_string(settings, S_SOURCE, new_name); } obs_data_release(settings); } static void *move_filter_create(obs_data_t *settings, obs_source_t *source) { struct move_filter_info *move_filter = bzalloc(sizeof(struct move_filter_info)); move_filter->source = source; signal_handler_connect(obs_get_signal_handler(), "source_rename", move_filter_source_rename, move_filter); UNUSED_PARAMETER(settings); return move_filter; } static void move_filter_destroy(void *data) { struct move_filter_info *move_filter = data; signal_handler_disconnect(obs_get_signal_handler(), "source_rename", move_filter_source_rename, move_filter); bfree(move_filter); } void prop_list_add_easings(obs_property_t *p); void prop_list_add_easing_functions(obs_property_t *p); void prop_list_add_positions(obs_property_t *p); void prop_list_add_transitions(obs_property_t *p); void prop_list_add_scales(obs_property_t *p); bool prop_list_add_sceneitem(obs_scene_t *scene, obs_sceneitem_t *item, void *data) { UNUSED_PARAMETER(scene); obs_property_t *p = data; const char *name = obs_source_get_name(obs_sceneitem_get_source(item)); obs_property_list_add_string(p, name, name); return true; } bool prop_list_add_source(void *data, obs_source_t *source) { obs_property_t *p = data; const char *name = obs_source_get_name(source); if (name && strlen(name)) obs_property_list_add_string(p, name, name); return true; } static obs_properties_t *move_filter_properties(void *data) { struct move_filter_info *move_filter = data; obs_properties_t *ppts = obs_properties_create(); obs_property_t *p; obs_source_t *parent = obs_filter_get_parent(move_filter->source); obs_scene_t *scene = obs_scene_from_source(parent); obs_source_t *source = NULL; if (scene) { p = obs_properties_add_list(ppts, S_SOURCE, obs_module_text("Source"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_scene_enum_items(scene, prop_list_add_sceneitem, p); obs_data_t *settings = obs_source_get_settings(move_filter->source); if (settings) { source = obs_sceneitem_get_source(obs_scene_find_source( scene, obs_data_get_string(settings, S_SOURCE))); obs_data_release(settings); } } //Matched items obs_properties_t *group = obs_properties_create(); p = obs_properties_add_list(group, S_MATCH_SOURCE, obs_module_text("MatchSource"), OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, "", ""); obs_enum_sources(prop_list_add_source, p); obs_enum_scenes(prop_list_add_source, p); p = obs_properties_add_int_slider(group, S_START_DELAY_MATCH_TO, obs_module_text("StartDelayTo"), -1, 100, 1); obs_property_int_set_suffix(p, "%"); p = obs_properties_add_int_slider(group, S_END_DELAY_MATCH_TO, obs_module_text("EndDelayTo"), -1, 100, 1); obs_property_int_set_suffix(p, "%"); p = obs_properties_add_int_slider(group, S_START_DELAY_MATCH_FROM, obs_module_text("StartDelayFrom"), -1, 100, 1); obs_property_int_set_suffix(p, "%"); p = obs_properties_add_int_slider(group, S_END_DELAY_MATCH_FROM, obs_module_text("EndDelayFrom"), -1, 100, 1); obs_property_int_set_suffix(p, "%"); p = obs_properties_add_list(group, S_EASING_MATCH, obs_module_text("Easing"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("NoOverride"), NO_OVERRIDE); prop_list_add_easings(p); p = obs_properties_add_list(group, S_EASING_FUNCTION_MATCH, obs_module_text("EasingFunction"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("NoOverride"), NO_OVERRIDE); prop_list_add_easing_functions(p); p = obs_properties_add_list(group, S_TRANSITION_MATCH, obs_module_text("Transition"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, obs_module_text("NoOverride"), NULL); prop_list_add_transitions(p); p = obs_properties_add_list(group, S_TRANSITION_SCALE, obs_module_text("TransitionScaleType"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("NoOverride"), NO_OVERRIDE); prop_list_add_scales(p); obs_properties_t *curve_group = obs_properties_create(); obs_properties_add_float_slider(curve_group, S_CURVE_MATCH, obs_module_text("Curve"), -2.0, 2.0, 0.01); obs_properties_add_group(group, S_CURVE_OVERRIDE_MATCH, obs_module_text("CurveOverride"), OBS_GROUP_CHECKABLE, curve_group); p = obs_properties_add_list(group, S_START_MOVE_MATCH_FROM, obs_module_text("StartMoveMatchFrom"), OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, "", ""); obs_source_enum_filters(parent, prop_list_add_move_source_filter, p); if (source) obs_source_enum_filters(source, prop_list_add_move_source_filter, p); p = obs_properties_add_list(group, S_START_MOVE_MATCH_TO, obs_module_text("StartMoveMatchTo"), OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, "", ""); obs_source_enum_filters(parent, prop_list_add_move_source_filter, p); if (source) obs_source_enum_filters(source, prop_list_add_move_source_filter, p); obs_properties_add_group(ppts, S_MOVE_MATCH, obs_module_text("MoveMatch"), OBS_GROUP_NORMAL, group); //Move in group = obs_properties_create(); p = obs_properties_add_int_slider(group, S_START_DELAY_IN, obs_module_text("StartDelay"), -1, 100, 1); obs_property_int_set_suffix(p, "%"); p = obs_properties_add_int_slider( group, S_END_DELAY_IN, obs_module_text("EndDelay"), -1, 100, 1); obs_property_int_set_suffix(p, "%"); p = obs_properties_add_list(group, S_EASING_IN, obs_module_text("Easing"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("NoOverride"), NO_OVERRIDE); prop_list_add_easings(p); p = obs_properties_add_list(group, S_EASING_FUNCTION_IN, obs_module_text("EasingFunction"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("NoOverride"), NO_OVERRIDE); prop_list_add_easing_functions(p); p = obs_properties_add_list(group, S_ZOOM_IN, obs_module_text("Zoom"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("NoOverride"), NO_OVERRIDE); obs_property_list_add_int(p, obs_module_text("No"), ZOOM_NO); obs_property_list_add_int(p, obs_module_text("Yes"), ZOOM_YES); p = obs_properties_add_list(group, S_POSITION_IN, obs_module_text("Position"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("NoOverride"), NO_OVERRIDE); prop_list_add_positions(p); p = obs_properties_add_list(group, S_TRANSITION_IN, obs_module_text("Transition"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, obs_module_text("NoOverride"), NULL); prop_list_add_transitions(p); curve_group = obs_properties_create(); obs_properties_add_float_slider(curve_group, S_CURVE_IN, obs_module_text("Curve"), -2.0, 2.0, 0.01); obs_properties_add_group(group, S_CURVE_OVERRIDE_IN, obs_module_text("CurveOverride"), OBS_GROUP_CHECKABLE, curve_group); p = obs_properties_add_list(group, S_START_MOVE_IN, obs_module_text("StartMove"), OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, "", ""); obs_source_enum_filters(parent, prop_list_add_move_source_filter, p); if (source) obs_source_enum_filters(source, prop_list_add_move_source_filter, p); obs_properties_add_group(ppts, S_MOVE_IN, obs_module_text("MoveIn"), OBS_GROUP_NORMAL, group); //Move out group = obs_properties_create(); p = obs_properties_add_int_slider(group, S_START_DELAY_OUT, obs_module_text("StartDelay"), -1, 100, 1); obs_property_int_set_suffix(p, "%"); p = obs_properties_add_int_slider(group, S_END_DELAY_OUT, obs_module_text("EndDelay"), -1, 100, 1); obs_property_int_set_suffix(p, "%"); p = obs_properties_add_list(group, S_EASING_OUT, obs_module_text("Easing"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("NoOverride"), NO_OVERRIDE); prop_list_add_easings(p); p = obs_properties_add_list(group, S_EASING_FUNCTION_OUT, obs_module_text("EasingFunction"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("NoOverride"), NO_OVERRIDE); prop_list_add_easing_functions(p); p = obs_properties_add_list(group, S_ZOOM_OUT, obs_module_text("Zoom"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("NoOverride"), NO_OVERRIDE); obs_property_list_add_int(p, obs_module_text("No"), ZOOM_NO); obs_property_list_add_int(p, obs_module_text("Yes"), ZOOM_YES); p = obs_properties_add_list(group, S_POSITION_OUT, obs_module_text("Position"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("NoOverride"), NO_OVERRIDE); prop_list_add_positions(p); p = obs_properties_add_list(group, S_TRANSITION_OUT, obs_module_text("Transition"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, obs_module_text("NoOverride"), NULL); prop_list_add_transitions(p); curve_group = obs_properties_create(); obs_properties_add_float_slider(curve_group, S_CURVE_OUT, obs_module_text("Curve"), -2.0, 2.0, 0.01); obs_properties_add_group(group, S_CURVE_OVERRIDE_OUT, obs_module_text("CurveOverride"), OBS_GROUP_CHECKABLE, curve_group); p = obs_properties_add_list(group, S_START_MOVE_OUT, obs_module_text("StartMove"), OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, "", ""); obs_source_enum_filters(parent, prop_list_add_move_source_filter, p); if (source) obs_source_enum_filters(source, prop_list_add_move_source_filter, p); obs_properties_add_group(ppts, S_MOVE_OUT, obs_module_text("MoveOut"), OBS_GROUP_NORMAL, group); return ppts; } void move_filter_defaults(obs_data_t *settings) { obs_data_set_default_int(settings, S_START_DELAY_MATCH_TO, NO_OVERRIDE); obs_data_set_default_int(settings, S_START_DELAY_MATCH_FROM, NO_OVERRIDE); obs_data_set_default_int(settings, S_START_DELAY_IN, NO_OVERRIDE); obs_data_set_default_int(settings, S_START_DELAY_OUT, NO_OVERRIDE); obs_data_set_default_int(settings, S_END_DELAY_MATCH_TO, NO_OVERRIDE); obs_data_set_default_int(settings, S_END_DELAY_MATCH_FROM, NO_OVERRIDE); obs_data_set_default_int(settings, S_END_DELAY_IN, NO_OVERRIDE); obs_data_set_default_int(settings, S_END_DELAY_OUT, NO_OVERRIDE); obs_data_set_default_int(settings, S_EASING_MATCH, NO_OVERRIDE); obs_data_set_default_int(settings, S_EASING_IN, NO_OVERRIDE); obs_data_set_default_int(settings, S_EASING_OUT, NO_OVERRIDE); obs_data_set_default_int(settings, S_EASING_FUNCTION_MATCH, NO_OVERRIDE); obs_data_set_default_int(settings, S_EASING_FUNCTION_IN, NO_OVERRIDE); obs_data_set_default_int(settings, S_EASING_FUNCTION_OUT, NO_OVERRIDE); obs_data_set_default_int(settings, S_POSITION_IN, NO_OVERRIDE); obs_data_set_default_int(settings, S_ZOOM_IN, NO_OVERRIDE); obs_data_set_default_int(settings, S_POSITION_OUT, NO_OVERRIDE); obs_data_set_default_int(settings, S_ZOOM_OUT, NO_OVERRIDE); obs_data_set_default_int(settings, S_TRANSITION_SCALE, NO_OVERRIDE); } void move_filter_video_render(void *data, gs_effect_t *effect) { UNUSED_PARAMETER(effect); struct move_filter_info *filter = data; obs_source_skip_video_filter(filter->source); } static const char *move_filter_get_name(void *type_data) { UNUSED_PARAMETER(type_data); return obs_module_text("MoveTransitionOverrideFilter"); } struct obs_source_info move_transition_override_filter = { .id = "move_transition_override_filter", .type = OBS_SOURCE_TYPE_FILTER, .output_flags = OBS_SOURCE_VIDEO, .get_name = move_filter_get_name, .create = move_filter_create, .destroy = move_filter_destroy, .get_properties = move_filter_properties, .get_defaults = move_filter_defaults, .video_render = move_filter_video_render}; obs-move-transition-2.5.7/move-transition.c000066400000000000000000002201611417746432500207650ustar00rootroot00000000000000#include "move-transition.h" #include #include #include "graphics/math-defs.h" #include "graphics/matrix4.h" #include "easing.h" #include "version.h" struct move_info { obs_source_t *source; bool start_init; DARRAY(struct move_item *) items_a; DARRAY(struct move_item *) items_b; float t; float curve_move; float curve_in; float curve_out; obs_source_t *scene_source_a; obs_source_t *scene_source_b; gs_samplerstate_t *point_sampler; long long easing_move; long long easing_in; long long easing_out; long long easing_function_move; long long easing_function_in; long long easing_function_out; bool zoom_in; bool zoom_out; long long position_in; long long position_out; char *transition_move; char *transition_in; char *transition_out; bool part_match; bool number_match; bool last_word_match; enum obs_transition_scale_type transition_move_scale; size_t item_pos; uint32_t matched_items; bool matched_scene_a; bool matched_scene_b; uint32_t item_order_switch_percentage; bool cache_transitions; DARRAY(obs_source_t *) transition_pool_move; size_t transition_pool_move_index; DARRAY(obs_source_t *) transition_pool_in; size_t transition_pool_in_index; DARRAY(obs_source_t *) transition_pool_out; size_t transition_pool_out_index; }; struct move_item { obs_sceneitem_t *item_a; obs_sceneitem_t *item_b; gs_texrender_t *item_render; obs_source_t *transition; long long easing; long long easing_function; bool zoom; long long position; char *transition_name; enum obs_transition_scale_type transition_scale; float curve; bool move_scene; int start_percentage; int end_percentage; obs_scene_t *release_scene_a; obs_scene_t *release_scene_b; }; static const char *move_get_name(void *type_data) { UNUSED_PARAMETER(type_data); return obs_module_text("Move"); } static void *move_create(obs_data_t *settings, obs_source_t *source) { struct move_info *move = bzalloc(sizeof(struct move_info)); move->source = source; da_init(move->items_a); da_init(move->items_b); da_init(move->transition_pool_out); da_init(move->transition_pool_in); da_init(move->transition_pool_out); obs_source_update(source, settings); return move; } static void clear_items(struct move_info *move, bool in_graphics) { bool graphics = false; for (size_t i = 0; i < move->items_a.num; i++) { struct move_item *item = move->items_a.array[i]; if (item->item_render) { if (!graphics && !in_graphics) { obs_enter_graphics(); graphics = true; } gs_texrender_destroy(item->item_render); item->item_render = NULL; } } if (graphics) obs_leave_graphics(); for (size_t i = 0; i < move->items_a.num; i++) { struct move_item *item = move->items_a.array[i]; obs_scene_release(item->release_scene_a); item->release_scene_a = NULL; obs_sceneitem_release(item->item_a); item->item_a = NULL; obs_scene_release(item->release_scene_b); item->release_scene_b = NULL; obs_sceneitem_release(item->item_b); item->item_b = NULL; if (item->transition) { obs_transition_force_stop(item->transition); obs_transition_clear(item->transition); obs_source_release(item->transition); item->transition = NULL; } bfree(item->transition_name); bfree(item); } move->items_a.num = 0; move->items_b.num = 0; } void clear_transition_pool(void *data) { DARRAY(obs_source_t *) *transition_pool = data; for (size_t i = 0; i < transition_pool->num; i++) { obs_source_release(transition_pool->array[i]); } transition_pool->num = 0; } static void move_destroy(void *data) { struct move_info *move = data; clear_items(move, false); da_free(move->items_a); da_free(move->items_b); clear_transition_pool(&move->transition_pool_move); da_free(move->transition_pool_move); clear_transition_pool(&move->transition_pool_in); da_free(move->transition_pool_in); clear_transition_pool(&move->transition_pool_out); da_free(move->transition_pool_out); obs_source_release(move->scene_source_a); obs_source_release(move->scene_source_b); bfree(move->transition_in); bfree(move->transition_out); bfree(move->transition_move); if (move->point_sampler) { obs_enter_graphics(); gs_samplerstate_destroy(move->point_sampler); obs_leave_graphics(); } bfree(move); } static void move_update(void *data, obs_data_t *settings) { struct move_info *move = data; move->easing_move = obs_data_get_int(settings, S_EASING_MATCH); move->easing_in = obs_data_get_int(settings, S_EASING_IN); move->easing_out = obs_data_get_int(settings, S_EASING_OUT); move->easing_function_move = obs_data_get_int(settings, S_EASING_FUNCTION_MATCH); move->easing_function_in = obs_data_get_int(settings, S_EASING_FUNCTION_IN); move->easing_function_out = obs_data_get_int(settings, S_EASING_FUNCTION_OUT); move->position_in = obs_data_get_int(settings, S_POSITION_IN); move->zoom_in = obs_data_get_bool(settings, S_ZOOM_IN); move->position_out = obs_data_get_int(settings, S_POSITION_OUT); move->zoom_out = obs_data_get_bool(settings, S_ZOOM_OUT); move->curve_move = (float)obs_data_get_double(settings, S_CURVE_MATCH); move->curve_in = (float)obs_data_get_double(settings, S_CURVE_IN); move->curve_out = (float)obs_data_get_double(settings, S_CURVE_OUT); bfree(move->transition_in); move->transition_in = bstrdup(obs_data_get_string(settings, S_TRANSITION_IN)); if (move->transition_in && strlen(move->transition_in) && move->transition_pool_in.num && strcmp(obs_source_get_name(move->transition_pool_in.array[0]), move->transition_in) != 0) { clear_transition_pool(&move->transition_pool_in); } bfree(move->transition_out); move->transition_out = bstrdup(obs_data_get_string(settings, S_TRANSITION_OUT)); if (move->transition_out && strlen(move->transition_out) && move->transition_pool_out.num && strcmp(obs_source_get_name(move->transition_pool_out.array[0]), move->transition_out) != 0) { clear_transition_pool(&move->transition_pool_out); } move->part_match = obs_data_get_bool(settings, S_NAME_PART_MATCH); move->number_match = obs_data_get_bool(settings, S_NAME_NUMBER_MATCH); move->last_word_match = obs_data_get_bool(settings, S_NAME_LAST_WORD_MATCH); bfree(move->transition_move); move->transition_move = bstrdup(obs_data_get_string(settings, S_TRANSITION_MATCH)); if (move->transition_move && strlen(move->transition_move) && move->transition_pool_move.num && strcmp(obs_source_get_name(move->transition_pool_move.array[0]), move->transition_move) != 0) { clear_transition_pool(&move->transition_pool_move); } move->transition_move_scale = obs_data_get_int(settings, S_TRANSITION_SCALE); move->item_order_switch_percentage = (uint32_t)obs_data_get_int(settings, S_SWITCH_PERCENTAGE); move->cache_transitions = obs_data_get_bool(settings, S_CACHE_TRANSITIONS); } void add_alignment(struct vec2 *v, uint32_t align, int32_t cx, int32_t cy) { if (align & OBS_ALIGN_RIGHT) v->x += (float)cx; else if ((align & OBS_ALIGN_LEFT) == 0) v->x += (float)(cx >> 1); if (align & OBS_ALIGN_BOTTOM) v->y += (float)cy; else if ((align & OBS_ALIGN_TOP) == 0) v->y += (float)(cy >> 1); } void add_move_alignment(struct vec2 *v, uint32_t align_a, uint32_t align_b, float t, int32_t cx, int32_t cy) { if (align_a & OBS_ALIGN_RIGHT) v->x += (float)cx * (1.0f - t); else if ((align_a & OBS_ALIGN_LEFT) == 0) v->x += (float)(cx >> 1) * (1.0f - t); if (align_a & OBS_ALIGN_BOTTOM) v->y += (float)cy * (1.0f - t); else if ((align_a & OBS_ALIGN_TOP) == 0) v->y += (float)(cy >> 1) * (1.0f - t); if (align_b & OBS_ALIGN_RIGHT) v->x += (float)cx * t; else if ((align_b & OBS_ALIGN_LEFT) == 0) v->x += (float)(cx >> 1) * t; if (align_b & OBS_ALIGN_BOTTOM) v->y += (float)cy * t; else if ((align_b & OBS_ALIGN_TOP) == 0) v->y += (float)(cy >> 1) * t; } static void calculate_bounds_data(struct obs_scene_item *item, struct vec2 *origin, struct vec2 *scale, int32_t *cx, int32_t *cy, struct vec2 *bounds) { float width = (float)(*cx) * fabsf(scale->x); float height = (float)(*cy) * fabsf(scale->y); const float item_aspect = width / height; const float bounds_aspect = bounds->x / bounds->y; uint32_t bounds_type = obs_sceneitem_get_bounds_type(item); if (bounds_type == OBS_BOUNDS_MAX_ONLY) if (width > bounds->x || height > bounds->y) bounds_type = OBS_BOUNDS_SCALE_INNER; if (bounds_type == OBS_BOUNDS_SCALE_INNER || bounds_type == OBS_BOUNDS_SCALE_OUTER) { bool use_width = (bounds_aspect < item_aspect); if (obs_sceneitem_get_bounds_type(item) == OBS_BOUNDS_SCALE_OUTER) use_width = !use_width; const float mul = use_width ? bounds->x / width : bounds->y / height; vec2_mulf(scale, scale, mul); } else if (bounds_type == OBS_BOUNDS_SCALE_TO_WIDTH) { vec2_mulf(scale, scale, bounds->x / width); } else if (bounds_type == OBS_BOUNDS_SCALE_TO_HEIGHT) { vec2_mulf(scale, scale, bounds->y / height); } else if (bounds_type == OBS_BOUNDS_STRETCH) { scale->x = bounds->x / (float)(*cx); scale->y = bounds->y / (float)(*cy); } width = (float)(*cx) * scale->x; height = (float)(*cy) * scale->y; const float width_diff = bounds->x - width; const float height_diff = bounds->y - height; *cx = (int32_t)roundf(bounds->x); *cy = (int32_t)roundf(bounds->y); add_alignment(origin, obs_sceneitem_get_bounds_alignment(item), (int32_t)-roundf(width_diff), (int32_t)-roundf(height_diff)); } static void calculate_move_bounds_data(struct obs_scene_item *item_a, struct obs_scene_item *item_b, float t, struct vec2 *origin, struct vec2 *scale, int32_t *cx, int32_t *cy, struct vec2 *bounds) { struct vec2 origin_a; vec2_set(&origin_a, origin->x, origin->y); struct vec2 origin_b; vec2_set(&origin_b, origin->x, origin->y); struct vec2 scale_a; vec2_set(&scale_a, scale->x, scale->y); struct vec2 scale_b; vec2_set(&scale_b, scale->x, scale->y); int32_t cxa = *cx; int32_t cxb = *cx; int32_t cya = *cy; int32_t cyb = *cy; calculate_bounds_data(item_a, &origin_a, &scale_a, &cxa, &cya, bounds); calculate_bounds_data(item_b, &origin_b, &scale_b, &cxb, &cyb, bounds); vec2_set(origin, origin_a.x * (1.0f - t) + origin_b.x * t, origin_a.y * (1.0f - t) + origin_b.y * t); vec2_set(scale, scale_a.x * (1.0f - t) + scale_b.x * t, scale_a.y * (1.0f - t) + scale_b.y * t); *cx = (int32_t)roundf((float)cxa * (1.0f - t) + (float)cxb * t); *cy = (int32_t)roundf((float)cya * (1.0f - t) + (float)cyb * t); } static inline bool item_is_scene(struct obs_scene_item *item) { obs_source_t *source = obs_sceneitem_get_source(item); return source && obs_source_get_type(source) == OBS_SOURCE_TYPE_SCENE; } static inline bool scale_filter_enabled(struct obs_scene_item *item) { return obs_sceneitem_get_scale_filter(item) != OBS_SCALE_DISABLE; } static inline bool crop_enabled(const struct obs_sceneitem_crop *crop) { return crop->left || crop->right || crop->top || crop->bottom; } static inline bool item_texture_enabled(struct obs_scene_item *item) { if (!item) false; struct obs_sceneitem_crop crop; obs_sceneitem_get_crop(item, &crop); return crop_enabled(&crop) || scale_filter_enabled(item) || (item_is_scene(item) && !obs_sceneitem_is_group(item)); } void pos_add_center(struct vec2 *pos, uint32_t alignment, uint32_t cx, uint32_t cy) { if (alignment & OBS_ALIGN_LEFT) { pos->x -= cx >> 1; } else if (alignment & OBS_ALIGN_RIGHT) { pos->x += cx >> 1; } if (alignment & OBS_ALIGN_TOP) { pos->y -= cy >> 1; } else if (alignment & OBS_ALIGN_BOTTOM) { pos->y += cy >> 1; } } void pos_subtract_center(struct vec2 *pos, uint32_t alignment, int32_t cx, int32_t cy) { if (alignment & OBS_ALIGN_LEFT) { pos->x += cx >> 1; } else if (alignment & OBS_ALIGN_RIGHT) { pos->x -= cx >> 1; } if (alignment & OBS_ALIGN_TOP) { pos->y += cy >> 1; } else if (alignment & OBS_ALIGN_BOTTOM) { pos->y -= cy >> 1; } } void calc_edge_position(struct vec2 *pos, long long position, uint32_t canvas_width, uint32_t canvas_height, uint32_t alignment, int32_t cx, int32_t cy, bool zoom) { int32_t cx2 = abs(cx >> 1); int32_t cy2 = abs(cy >> 1); if (zoom) { cx2 = 0; cy2 = 0; } if (position - POS_EDGE == 0) { if (alignment & OBS_ALIGN_LEFT) { pos->x += cx >> 1; } else if (alignment & OBS_ALIGN_RIGHT) { pos->x -= cx >> 1; } if (alignment & OBS_ALIGN_TOP) { pos->y += cy >> 1; } else if (alignment & OBS_ALIGN_BOTTOM) { pos->y -= cy >> 1; } // pos is center of object float diff_x = pos->x - (canvas_width >> 1); float diff_y = pos->y - (canvas_height >> 1); float factor_x = fabsf(diff_x) / (canvas_width >> 1); float factor_y = fabsf(diff_y) / (canvas_height >> 1); if (diff_x == 0.0f && diff_y == 0.0f) { diff_y = 1.0f; } if (factor_x > factor_y) { if (diff_x < 0.0f) { //left edge const float move_x = -(pos->x + cx2); const float move_y = diff_y * (diff_x + move_x) / diff_x; vec2_set(pos, -(float)cx2, (float)(canvas_height >> 1) + move_y); } else { //right edge const float move_x = (canvas_width - pos->x) + cx2; const float move_y = diff_y * (diff_x + move_x) / diff_x; vec2_set(pos, (float)canvas_width + cx2, (float)(canvas_height >> 1) + move_y); } } else { if (diff_y < 0.0f) { //top edge const float move_y = -(pos->y + cy2); const float move_x = diff_x * (diff_y + move_y) / diff_y; vec2_set(pos, (float)(canvas_width >> 1) + move_x, -(float)cy2); } else { //bottom edge const float move_y = (canvas_height - pos->y) + cy2; const float move_x = diff_x * (diff_y + move_y) / diff_y; vec2_set(pos, (float)(canvas_width >> 1) + move_x, (float)canvas_height + cy2); } } if (alignment & OBS_ALIGN_LEFT) { if (cx < 0) { pos->x += cx2; } else { pos->x -= cx2; } } else if (alignment & OBS_ALIGN_RIGHT) { if (cx < 0) { pos->x -= cx2; } else { pos->x += cx2; } } if (alignment & OBS_ALIGN_TOP) { if (cy < 0) { pos->y += cy2; } else { pos->y -= cy2; } } else if (alignment & OBS_ALIGN_BOTTOM) { if (cy < 0) { pos->y -= cy2; } else { pos->y += cy2; } } return; } if (zoom) { cx = 0; cy = 0; } if (position & POS_EDGE) vec2_set(pos, 0, 0); if (position & POS_RIGHT) { pos->x = (float)canvas_width; if (alignment & OBS_ALIGN_RIGHT) { pos->x += cx; } else if (alignment & OBS_ALIGN_LEFT) { } else { pos->x += cx2; } } else if (position & POS_LEFT) { pos->x = 0; if (alignment & OBS_ALIGN_RIGHT) { } else if (alignment & OBS_ALIGN_LEFT) { pos->x -= cx; } else { pos->x -= cx2; } } else if (position & POS_EDGE) { pos->x = (float)(canvas_width >> 1); if (alignment & OBS_ALIGN_RIGHT) { pos->x += cx2; } else if (alignment & OBS_ALIGN_LEFT) { pos->x -= cx2; } } if (position & POS_BOTTOM) { pos->y = (float)canvas_height; if (alignment & OBS_ALIGN_TOP) { } else if (alignment & OBS_ALIGN_BOTTOM) { pos->y += cy; } else { pos->y += cy2; } } else if (position & POS_TOP) { pos->y = 0; if (alignment & OBS_ALIGN_BOTTOM) { } else if (alignment & OBS_ALIGN_TOP) { pos->y -= cy; } else { pos->y -= cy2; } } else if (position & POS_EDGE) { pos->y = (float)(canvas_height >> 1); if (alignment & OBS_ALIGN_TOP) { pos->y -= cy2; } else if (alignment & OBS_ALIGN_BOTTOM) { pos->y += cy2; } } } float bezier(float point[], float t, int order) { const float p = 1.0f - t; if (order < 1) return point[0]; if (order == 1) return p * point[0] + t * point[1]; return p * bezier(point, t, order - 1) + t * bezier(&point[1], t, order - 1); } void vec2_bezier(struct vec2 *dst, struct vec2 *begin, struct vec2 *control, struct vec2 *end, const float t) { float x[3] = {begin->x, control->x, end->x}; float y[3] = {begin->y, control->y, end->y}; dst->x = bezier(x, t, 2); dst->y = bezier(y, t, 2); } static obs_source_t *obs_frontend_get_transition(const char *name) { if (!name) return NULL; struct obs_frontend_source_list transitions = {0}; obs_frontend_get_transitions(&transitions); for (size_t i = 0; i < transitions.sources.num; i++) { const char *n = obs_source_get_name(transitions.sources.array[i]); if (n && strcmp(n, name) == 0) { obs_source_t *transition = obs_source_get_ref(transitions.sources.array[i]); obs_frontend_source_list_free(&transitions); return transition; } } obs_frontend_source_list_free(&transitions); return NULL; } float get_eased(float f, long long easing, long long easing_function) { float t = f; if (EASE_NONE == easing) { } else if (EASE_IN == easing) { switch (easing_function) { case EASING_QUADRATIC: t = QuadraticEaseIn(f); break; case EASING_CUBIC: t = CubicEaseIn(f); break; case EASING_QUARTIC: t = QuarticEaseIn(f); break; case EASING_QUINTIC: t = QuinticEaseIn(f); break; case EASING_SINE: t = SineEaseIn(f); break; case EASING_CIRCULAR: t = CircularEaseIn(f); break; case EASING_EXPONENTIAL: t = ExponentialEaseIn(f); break; case EASING_ELASTIC: t = ElasticEaseIn(f); break; case EASING_BOUNCE: t = BounceEaseIn(f); break; case EASING_BACK: t = BackEaseIn(f); break; default:; } } else if (EASE_OUT == easing) { switch (easing_function) { case EASING_QUADRATIC: t = QuadraticEaseOut(f); break; case EASING_CUBIC: t = CubicEaseOut(f); break; case EASING_QUARTIC: t = QuarticEaseOut(f); break; case EASING_QUINTIC: t = QuinticEaseOut(f); break; case EASING_SINE: t = SineEaseOut(f); break; case EASING_CIRCULAR: t = CircularEaseOut(f); break; case EASING_EXPONENTIAL: t = ExponentialEaseOut(f); break; case EASING_ELASTIC: t = ElasticEaseOut(f); break; case EASING_BOUNCE: t = BounceEaseOut(f); break; case EASING_BACK: t = BackEaseOut(f); break; default:; } } else if (EASE_IN_OUT == easing) { switch (easing_function) { case EASING_QUADRATIC: t = QuadraticEaseInOut(f); break; case EASING_CUBIC: t = CubicEaseInOut(f); break; case EASING_QUARTIC: t = QuarticEaseInOut(f); break; case EASING_QUINTIC: t = QuinticEaseInOut(f); break; case EASING_SINE: t = SineEaseInOut(f); break; case EASING_CIRCULAR: t = CircularEaseInOut(f); break; case EASING_EXPONENTIAL: t = ExponentialEaseInOut(f); break; case EASING_ELASTIC: t = ElasticEaseInOut(f); break; case EASING_BOUNCE: t = BounceEaseInOut(f); break; case EASING_BACK: t = BackEaseInOut(f); break; default:; } } return t; } obs_source_t *get_transition(const char *transition_name, void *pool_data, size_t *index, bool cache) { if (!transition_name || strlen(transition_name) == 0 || strcmp(transition_name, "None") == 0) return NULL; DARRAY(obs_source_t *) *transition_pool = pool_data; const size_t i = *index; if (cache && transition_pool->num && *index < transition_pool->num) { obs_source_t *transition = obs_source_get_ref(transition_pool->array[i]); *index = i + 1; return transition; } obs_source_t *frontend_transition = obs_frontend_get_transition(transition_name); if (!frontend_transition) return NULL; obs_source_t *transition = obs_source_duplicate(frontend_transition, transition_name, true); obs_source_release(frontend_transition); if (cache) { transition = obs_source_get_ref(transition); darray_push_back(sizeof(obs_source_t *), &transition_pool->da, &transition); *index = i + 1; } return transition; } bool render2_item(struct move_info *move, struct move_item *item) { obs_sceneitem_t *scene_item = NULL; if (item->item_a && item->item_b) { if (move->t <= 0.5) { scene_item = item->item_a; } else { scene_item = item->item_b; } } else if (item->item_a) { scene_item = item->item_a; } else if (item->item_b) { scene_item = item->item_b; } obs_source_t *source = obs_sceneitem_get_source(scene_item); uint32_t width = obs_source_get_width(source); uint32_t height = obs_source_get_height(source); bool move_out = item->item_a == scene_item; if (item->move_scene) { if (item->transition_name && !item->transition) { item->transition = get_transition( item->transition_name, &move->transition_pool_move, &move->transition_pool_move_index, move->cache_transitions); if (item->transition) { obs_transition_set_size(item->transition, width, height); obs_transition_set_alignment(item->transition, OBS_ALIGN_CENTER); obs_transition_set_scale_type( item->transition, item->transition_scale); obs_transition_set( item->transition, obs_sceneitem_get_source(scene_item)); obs_transition_start( item->transition, obs_transition_fixed(item->transition) ? OBS_TRANSITION_MODE_AUTO : OBS_TRANSITION_MODE_MANUAL, obs_frontend_get_transition_duration(), obs_sceneitem_get_source(scene_item)); } } } else if (item->item_a && item->item_b) { if (item->transition_name && !item->transition) { item->transition = get_transition( item->transition_name, &move->transition_pool_move, &move->transition_pool_move_index, move->cache_transitions); if (item->transition) { obs_transition_set_size(item->transition, width, height); obs_transition_set_alignment(item->transition, OBS_ALIGN_CENTER); obs_transition_set_scale_type( item->transition, item->transition_scale); obs_transition_set( item->transition, obs_sceneitem_get_source(item->item_a)); obs_transition_start( item->transition, obs_transition_fixed(item->transition) ? OBS_TRANSITION_MODE_AUTO : OBS_TRANSITION_MODE_MANUAL, obs_frontend_get_transition_duration(), obs_sceneitem_get_source(item->item_b)); } } } else if (move_out && item->transition_name && !item->transition) { item->transition = get_transition( item->transition_name, &move->transition_pool_out, &move->transition_pool_out_index, move->cache_transitions); if (item->transition) { obs_transition_set_size(item->transition, width, height); obs_transition_set_alignment(item->transition, OBS_ALIGN_CENTER); obs_transition_set_scale_type( item->transition, OBS_TRANSITION_SCALE_ASPECT); obs_transition_set(item->transition, source); obs_transition_start( item->transition, obs_transition_fixed(item->transition) ? OBS_TRANSITION_MODE_AUTO : OBS_TRANSITION_MODE_MANUAL, obs_frontend_get_transition_duration(), NULL); } } else if (!move_out && item->transition_name && !item->transition) { item->transition = get_transition( item->transition_name, &move->transition_pool_in, &move->transition_pool_in_index, move->cache_transitions); if (item->transition) { obs_transition_set_size(item->transition, width, height); obs_transition_set_alignment(item->transition, OBS_ALIGN_CENTER); obs_transition_set_scale_type( item->transition, OBS_TRANSITION_SCALE_ASPECT); obs_transition_set(item->transition, NULL); obs_transition_start( item->transition, obs_transition_fixed(item->transition) ? OBS_TRANSITION_MODE_AUTO : OBS_TRANSITION_MODE_MANUAL, obs_frontend_get_transition_duration(), source); } } float t = 0.0f; if (item->start_percentage > 0 || item->end_percentage < 100) { if (item->start_percentage > item->end_percentage) { float avg_switch_point = (float)(item->start_percentage + item->end_percentage) / 200.0f; if (move->t > avg_switch_point) { t = 1.0f; } } else if (move->t * 100.0 < item->start_percentage) { t = 0.0f; } else if (move->t * 100.0 > item->end_percentage) { t = 1.0f; } else { int duration_percentage = item->end_percentage - item->start_percentage; t = move->t - (float)item->start_percentage / 100.0f; t = t / (float)duration_percentage * 100.0f; t = get_eased(t, item->easing, item->easing_function); } } else { t = get_eased(move->t, item->easing, item->easing_function); } float ot = t; if (t > 1.0f) ot = 1.0f; else if (t < 0.0f) ot = 0.0f; if (item->item_a && item->item_b && item->transition && !move->start_init) { uint32_t width_a = obs_source_get_width( obs_sceneitem_get_source(item->item_a)); uint32_t width_b = obs_source_get_width( obs_sceneitem_get_source(item->item_b)); uint32_t height_a = obs_source_get_height( obs_sceneitem_get_source(item->item_a)); uint32_t height_b = obs_source_get_height( obs_sceneitem_get_source(item->item_b)); if (width_a != width_b) width = (uint32_t)roundf((1.0f - t) * width_a + t * width_b); if (height_a != height_b) height = (uint32_t)roundf((1.0f - t) * height_a + t * height_b); if (height_a != height_b || width_a != width_b) obs_transition_set_size(item->transition, width, height); } uint32_t original_width = width; uint32_t original_height = height; struct obs_sceneitem_crop crop; if (item->move_scene) { obs_sceneitem_get_crop(scene_item, &crop); if (item->item_a) { crop.left = (int)roundf((float)(1.0f - ot) * (float)crop.left); crop.top = (int)roundf((float)(1.0f - ot) * (float)crop.top); crop.right = (int)roundf((float)(1.0f - ot) * (float)crop.right); crop.bottom = (int)roundf((float)(1.0f - ot) * (float)crop.bottom); } else if (item->item_b) { crop.left = (int)roundf((float)ot * (float)crop.left); crop.top = (int)roundf((float)ot * (float)crop.top); crop.right = (int)roundf((float)ot * (float)crop.right); crop.bottom = (int)roundf((float)ot * (float)crop.bottom); } } else if (item->item_a && item->item_b) { struct obs_sceneitem_crop crop_a; obs_sceneitem_get_crop(item->item_a, &crop_a); struct obs_sceneitem_crop crop_b; obs_sceneitem_get_crop(item->item_b, &crop_b); crop.left = (int)roundf((float)(1.0f - ot) * (float)crop_a.left + ot * (float)crop_b.left); crop.top = (int)roundf((float)(1.0f - ot) * (float)crop_a.top + ot * (float)crop_b.top); crop.right = (int)roundf((float)(1.0f - ot) * (float)crop_a.right + ot * (float)crop_b.right); crop.bottom = (int)roundf((float)(1.0f - ot) * (float)crop_a.bottom + ot * (float)crop_b.bottom); } else { obs_sceneitem_get_crop(scene_item, &crop); } uint32_t crop_cx = crop.left + crop.right; int32_t cx = (crop_cx > width) ? 2 : (width - crop_cx); uint32_t crop_cy = crop.top + crop.bottom; int32_t cy = (crop_cy > height) ? 2 : (height - crop_cy); struct vec2 scale; struct vec2 original_scale; obs_sceneitem_get_scale(scene_item, &original_scale); if (item->item_a && item->item_b) { struct vec2 scale_a; obs_sceneitem_get_scale(item->item_a, &scale_a); struct vec2 scale_b; obs_sceneitem_get_scale(item->item_b, &scale_b); vec2_set(&scale, (1.0f - t) * scale_a.x + t * scale_b.x, (1.0f - t) * scale_a.y + t * scale_b.y); } else { if (obs_sceneitem_get_bounds_type(scene_item) != OBS_BOUNDS_NONE) { obs_sceneitem_get_scale(scene_item, &scale); } else { obs_sceneitem_get_scale(scene_item, &scale); if (item->move_scene) { if (item->item_a) { vec2_set(&scale, (1.0f - t) * scale.x + t, (1.0f - t) * scale.y + t); } else if (item->item_b) { vec2_set(&scale, (1.0f - t) + t * scale.x, (1.0f - t) + t * scale.y); } } else if (!move_out && item->zoom) { vec2_set(&scale, t * scale.x, t * scale.y); } else if (move_out && item->zoom) { vec2_set(&scale, (1.0f - t) * scale.x, (1.0f - t) * scale.y); } } } width = cx; height = cy; int32_t original_cx = cx; int32_t original_cy = cy; struct vec2 base_origin; struct vec2 origin; struct vec2 origin2; vec2_zero(&base_origin); vec2_zero(&origin); vec2_zero(&origin2); uint32_t canvas_width = obs_source_get_width(move->source); uint32_t canvas_height = obs_source_get_height(move->source); if (obs_sceneitem_get_bounds_type(scene_item) != OBS_BOUNDS_NONE) { struct vec2 bounds; if (item->move_scene) { obs_sceneitem_get_bounds(scene_item, &bounds); if (item->item_a) { vec2_set(&bounds, (1.0f - t) * bounds.x + t * canvas_width, (1.0f - t) * bounds.y + t * canvas_height); } else if (item->item_b) { vec2_set(&bounds, (1.0f - t) * canvas_width + t * bounds.x, (1.0f - t) * canvas_height + t * bounds.y); } } else if (item->item_a && item->item_b) { struct vec2 bounds_a; obs_sceneitem_get_bounds(item->item_a, &bounds_a); struct vec2 bounds_b; obs_sceneitem_get_bounds(item->item_b, &bounds_b); vec2_set(&bounds, (1.0f - t) * bounds_a.x + t * bounds_b.x, (1.0f - t) * bounds_a.y + t * bounds_b.y); } else { obs_sceneitem_get_bounds(scene_item, &bounds); if (!move_out && item->zoom) { vec2_set(&bounds, t * bounds.x, t * bounds.y); } else if (move_out && item->zoom) { vec2_set(&bounds, (1.0f - t) * bounds.x, (1.0f - t) * bounds.y); } } if (item->item_a && item->item_b && (obs_sceneitem_get_bounds_alignment(item->item_a) != obs_sceneitem_get_bounds_alignment(item->item_b) || obs_sceneitem_get_bounds_type(item->item_a) != obs_sceneitem_get_bounds_type(item->item_b))) { calculate_move_bounds_data(item->item_a, item->item_b, t, &origin, &scale, &cx, &cy, &bounds); } else { calculate_bounds_data(scene_item, &origin, &scale, &cx, &cy, &bounds); } struct vec2 original_bounds; obs_sceneitem_get_bounds(scene_item, &original_bounds); calculate_bounds_data(scene_item, &origin2, &original_scale, &original_cx, &original_cy, &original_bounds); } else { original_cx = (int32_t)roundf((float)cx * original_scale.x); original_cy = (int32_t)roundf((float)cy * original_scale.y); cx = (int32_t)roundf((float)cx * scale.x); cy = (int32_t)roundf((float)cy * scale.y); } if (item->item_a && item->item_b && obs_sceneitem_get_alignment(item->item_a) != obs_sceneitem_get_alignment(item->item_b)) { add_move_alignment(&origin, obs_sceneitem_get_alignment(item->item_a), obs_sceneitem_get_alignment(item->item_b), t, cx, cy); } else { add_alignment(&origin, obs_sceneitem_get_alignment(scene_item), cx, cy); } struct matrix4 draw_transform; matrix4_identity(&draw_transform); matrix4_scale3f(&draw_transform, &draw_transform, scale.x, scale.y, 1.0f); matrix4_translate3f(&draw_transform, &draw_transform, -origin.x, -origin.y, 0.0f); float rot; if (item->move_scene) { rot = obs_sceneitem_get_rot(scene_item); if (item->item_a) { rot *= (1.0f - t); } else if (item->item_b) { rot *= t; } } else if (item->item_a && item->item_b) { float rot_a = obs_sceneitem_get_rot(item->item_a); float rot_b = obs_sceneitem_get_rot(item->item_b); rot = (1.0f - t) * rot_a + t * rot_b; } else { rot = obs_sceneitem_get_rot(scene_item); } matrix4_rotate_aa4f(&draw_transform, &draw_transform, 0.0f, 0.0f, 1.0f, RAD(rot)); struct vec2 pos_a; if (item->item_a) { obs_sceneitem_get_pos(item->item_a, &pos_a); } else if (item->move_scene) { uint32_t alignment = obs_sceneitem_get_alignment(scene_item); vec2_set(&pos_a, 0, 0); if (alignment & OBS_ALIGN_RIGHT) { pos_a.x += canvas_width; } else if (alignment & OBS_ALIGN_LEFT) { } else { pos_a.x += canvas_width >> 1; } if (alignment & OBS_ALIGN_BOTTOM) { pos_a.y += canvas_height; } else if (alignment & OBS_ALIGN_TOP) { } else { pos_a.x += canvas_height >> 1; } } else { uint32_t alignment = obs_sceneitem_get_alignment(scene_item); if (item->position & POS_CENTER) { vec2_set(&pos_a, (float)(canvas_width >> 1), (float)(canvas_height >> 1)); if (!item->zoom) pos_add_center(&pos_a, alignment, cx, cy); } else if (item->position & POS_EDGE || item->position & POS_SWIPE) { obs_sceneitem_get_pos(item->item_b, &pos_a); calc_edge_position(&pos_a, item->position, canvas_width, canvas_height, alignment, original_cx, original_cy, item->zoom); } else { obs_sceneitem_get_pos(item->item_b, &pos_a); if (item->zoom) pos_subtract_center(&pos_a, alignment, original_cx, original_cy); } } struct vec2 pos_b; if (item->item_b) { obs_sceneitem_get_pos(item->item_b, &pos_b); } else if (item->move_scene) { uint32_t alignment = obs_sceneitem_get_alignment(scene_item); vec2_set(&pos_b, 0, 0); if (alignment & OBS_ALIGN_RIGHT) { pos_b.x += canvas_width; } else if (alignment & OBS_ALIGN_LEFT) { } else { pos_b.x += canvas_width >> 1; } if (alignment & OBS_ALIGN_BOTTOM) { pos_b.y += canvas_height; } else if (alignment & OBS_ALIGN_TOP) { } else { pos_b.x += canvas_height >> 1; } } else { uint32_t alignment = obs_sceneitem_get_alignment(scene_item); if (item->position & POS_CENTER) { vec2_set(&pos_b, (float)(canvas_width >> 1), (float)(canvas_height >> 1)); if (!item->zoom) pos_add_center(&pos_b, alignment, cx, cy); } else if (item->position & POS_EDGE || item->position & POS_SWIPE) { obs_sceneitem_get_pos(item->item_a, &pos_b); calc_edge_position(&pos_b, item->position, canvas_width, canvas_height, alignment, original_cx, original_cy, item->zoom); } else { obs_sceneitem_get_pos(item->item_a, &pos_b); if (item->zoom) pos_subtract_center(&pos_b, alignment, original_cx, original_cy); } } struct vec2 pos; if (item->curve != 0.0f) { float diff_x = fabsf(pos_a.x - pos_b.x); float diff_y = fabsf(pos_a.y - pos_b.y); struct vec2 control_pos; vec2_set(&control_pos, 0.5f * pos_a.x + 0.5f * pos_b.x, 0.5f * pos_a.y + 0.5f * pos_b.y); if (control_pos.x >= (canvas_width >> 1)) { control_pos.x += diff_y * item->curve; } else { control_pos.x -= diff_y * item->curve; } if (control_pos.y >= (canvas_height >> 1)) { control_pos.y += diff_x * item->curve; } else { control_pos.y -= diff_x * item->curve; } vec2_bezier(&pos, &pos_a, &control_pos, &pos_b, t); } else { vec2_set(&pos, (1.0f - t) * pos_a.x + t * pos_b.x, (1.0f - t) * pos_a.y + t * pos_b.y); } matrix4_translate3f(&draw_transform, &draw_transform, pos.x, pos.y, 0.0f); struct vec2 output_scale = scale; if (item->item_render && !item_texture_enabled(item->item_a) && !item_texture_enabled(item->item_b)) { gs_texrender_destroy(item->item_render); item->item_render = NULL; } else if (!item->item_render && (item->item_a && item_texture_enabled(item->item_a) || item->item_b && item_texture_enabled(item->item_b))) { item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE); } else if (item->item_render) { gs_texrender_reset(item->item_render); } if (!move->point_sampler) { struct gs_sampler_info point_sampler_info = {0}; point_sampler_info.max_anisotropy = 1; move->point_sampler = gs_samplerstate_create(&point_sampler_info); } if (item->item_render) { if (width && height && gs_texrender_begin(item->item_render, width, height)) { float cx_scale = (float)original_width / (float)width; float cy_scale = (float)original_height / (float)height; struct vec4 clear_color; vec4_zero(&clear_color); gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0); gs_ortho(0.0f, (float)original_width, 0.0f, (float)original_height, -100.0f, 100.0f); gs_matrix_scale3f(cx_scale, cy_scale, 1.0f); gs_matrix_translate3f(-(float)crop.left, -(float)crop.top, 0.0f); if (item->transition) { obs_transition_set_manual_time(item->transition, ot); if (!move->start_init) { obs_source_video_render( item->transition); } else if (item->item_a) { obs_source_video_render(source); } } else { obs_source_video_render(source); } gs_texrender_end(item->item_render); } } gs_matrix_push(); gs_matrix_mul(&draw_transform); const bool previous = gs_set_linear_srgb(true); if (item->item_render) { //render_item_texture(item); gs_texture_t *tex = gs_texrender_get_texture(item->item_render); if (!tex) { gs_set_linear_srgb(previous); gs_matrix_pop(); return true; } gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); enum obs_scale_type type = obs_sceneitem_get_scale_filter(scene_item); cx = gs_texture_get_width(tex); cy = gs_texture_get_height(tex); const char *tech = "Draw"; if (type != OBS_SCALE_DISABLE) { if (type == OBS_SCALE_POINT) { gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_next_sampler(image, move->point_sampler); } else if (!close_float(output_scale.x, 1.0f, EPSILON) || !close_float(output_scale.y, 1.0f, EPSILON)) { gs_eparam_t *scale_param; gs_eparam_t *scale_i_param; if (output_scale.x < 0.5f || output_scale.y < 0.5f) { effect = obs_get_base_effect( OBS_EFFECT_BILINEAR_LOWRES); } else if (type == OBS_SCALE_BICUBIC) { effect = obs_get_base_effect( OBS_EFFECT_BICUBIC); } else if (type == OBS_SCALE_LANCZOS) { effect = obs_get_base_effect( OBS_EFFECT_LANCZOS); } else if (type == OBS_SCALE_AREA) { effect = obs_get_base_effect( OBS_EFFECT_AREA); if ((output_scale.x >= 1.0f) && (output_scale.y >= 1.0f)) tech = "DrawUpscale"; } scale_param = gs_effect_get_param_by_name( effect, "base_dimension"); if (scale_param) { struct vec2 base_res = {(float)cx, (float)cy}; gs_effect_set_vec2(scale_param, &base_res); } scale_i_param = gs_effect_get_param_by_name( effect, "base_dimension_i"); if (scale_i_param) { struct vec2 base_res_i = { 1.0f / (float)cx, 1.0f / (float)cy}; gs_effect_set_vec2(scale_i_param, &base_res_i); } } } gs_blend_state_push(); gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA); while (gs_effect_loop(effect, tech)) obs_source_draw(tex, 0, 0, 0, 0, 0); gs_blend_state_pop(); } else { if (item->transition) { obs_transition_set_manual_time(item->transition, ot); if (!move->start_init) { obs_source_video_render(item->transition); } else if (item->item_a) { obs_source_video_render(source); } } else { obs_source_video_render(source); } } gs_set_linear_srgb(previous); gs_matrix_pop(); return true; } void get_override_filter(obs_source_t *source, obs_source_t *filter, void *param) { UNUSED_PARAMETER(source); if (!obs_source_enabled(filter)) return; if (strcmp(obs_source_get_unversioned_id(filter), "move_transition_override_filter") != 0) return; obs_source_t *target = *(obs_source_t **)param; if (!target) { *(obs_source_t **)param = filter; return; } if (obs_source_get_type(target) == OBS_SOURCE_TYPE_FILTER) return; obs_data_t *settings = obs_source_get_settings(filter); if (!settings) return; const char *sn = obs_data_get_string(settings, S_SOURCE); if (sn && strlen(sn)) { if (strcmp(obs_source_get_name(target), sn) == 0) { *(obs_source_t **)param = filter; } } obs_data_release(settings); } obs_data_t *get_override_filter_settings(obs_sceneitem_t *item) { if (!item) return NULL; obs_source_t *filter = obs_sceneitem_get_source(item); obs_scene_t *scene = obs_sceneitem_get_scene(item); if (scene) { obs_source_t *scene_source = obs_scene_get_source(scene); obs_source_enum_filters(scene_source, get_override_filter, &filter); } obs_source_t *source = obs_sceneitem_get_source(item); if (!source) return NULL; if (filter && filter != source) return obs_source_get_settings(filter); filter = NULL; obs_source_enum_filters(source, get_override_filter, &filter); if (filter && filter != source) return obs_source_get_settings(filter); return NULL; } bool same_transform_type(struct obs_transform_info *info_a, struct obs_transform_info *info_b) { if (!info_a || !info_b) return false; return info_a->alignment == info_b->alignment && info_a->bounds_type == info_b->bounds_type && info_a->bounds_alignment == info_b->bounds_alignment; } bool is_number_match(const char c) { if (c >= '0' && c <= '9') return true; if (c == '(' || c == ')' || c == ' ' || c == '.' || c == ',') return true; return false; } struct move_item *match_item2(struct move_info *move, obs_sceneitem_t *scene_item, bool part_match, size_t *found_pos) { struct move_item *item = NULL; obs_source_t *source = obs_sceneitem_get_source(scene_item); const char *name_b = obs_source_get_name(source); obs_data_t *override_filter = get_override_filter_settings(scene_item); const char *name_b2 = override_filter ? obs_data_get_string(override_filter, S_MATCH_SOURCE) : NULL; if (override_filter) obs_data_release(override_filter); for (size_t i = 0; i < move->items_a.num; i++) { struct move_item *check_item = move->items_a.array[i]; if (check_item->item_b) continue; if (obs_sceneitem_get_bounds_type(check_item->item_a) == OBS_BOUNDS_NONE && obs_sceneitem_get_bounds_type(scene_item) != OBS_BOUNDS_NONE) continue; if (obs_sceneitem_get_bounds_type(check_item->item_a) != OBS_BOUNDS_NONE && obs_sceneitem_get_bounds_type(scene_item) == OBS_BOUNDS_NONE) continue; obs_source_t *check_source = obs_sceneitem_get_source(check_item->item_a); if (!check_source) continue; if (check_source == source) { item = check_item; *found_pos = i; break; } const char *name_a = obs_source_get_name(check_source); if (name_a && name_b) { if (strcmp(name_a, name_b) == 0) { item = check_item; *found_pos = i; break; } if (name_b2 && strcmp(name_a, name_b2) == 0) { item = check_item; *found_pos = i; break; } override_filter = get_override_filter_settings( check_item->item_a); if (override_filter) { const char *name_a2 = obs_data_get_string( override_filter, S_MATCH_SOURCE); obs_data_release(override_filter); if (strcmp(name_a2, name_b) == 0) { item = check_item; *found_pos = i; break; } } if (part_match) { size_t len_a = strlen(name_a); size_t len_b = strlen(name_b); if (!len_a || !len_b) continue; if (len_a > len_b) { if (move->last_word_match) { char *last_space = strrchr(name_b, ' '); if (last_space && last_space > name_b) { len_b = last_space - name_b; } } while (len_b > 0 && move->number_match && is_number_match( name_b[len_b - 1])) len_b--; if (len_b > 0 && move->part_match) { for (size_t pos = 0; pos <= len_a - len_b; pos++) { if (memcmp(name_a + pos, name_b, len_b) == 0) { item = check_item; *found_pos = i; break; } } if (item) break; } else if (len_b > 0 && memcmp(name_a, name_b, len_b) == 0) { item = check_item; *found_pos = i; break; } } else { if (move->last_word_match) { char *last_space = strrchr(name_a, ' '); if (last_space && last_space > name_a) { len_a = last_space - name_a; } } while (len_a > 0 && move->number_match && is_number_match( name_a[len_a - 1])) len_a--; if (len_a > 0 && move->part_match) { for (size_t pos = 0; pos <= len_b - len_a; pos++) { if (memcmp(name_a, name_b + pos, len_a) == 0) { item = check_item; *found_pos = i; break; } } if (item) break; } else if (len_a > 0 && memcmp(name_a, name_b, len_a) == 0) { item = check_item; *found_pos = i; break; } } } } else if (!part_match) { if (obs_source_get_type(check_source) == obs_source_get_type(source)) { obs_data_t *settings = obs_source_get_settings(source); obs_data_t *check_settings = obs_source_get_settings(check_source); if (settings && check_settings && strcmp(obs_data_get_json(settings), obs_data_get_json(check_settings)) == 0) { item = check_item; *found_pos = i; obs_data_release(check_settings); obs_data_release(settings); break; } obs_data_release(check_settings); obs_data_release(settings); } } } return item; } struct move_item *create_move_item() { struct move_item *item = bzalloc(sizeof(struct move_item)); item->end_percentage = 100; return item; } bool add_item(obs_scene_t *scene, obs_sceneitem_t *scene_item, void *data) { UNUSED_PARAMETER(scene); if (!obs_sceneitem_visible(scene_item)) { return true; } struct move_info *move = data; struct move_item *item = create_move_item(); da_push_back(move->items_a, &item); obs_sceneitem_addref(scene_item); item->item_a = scene_item; item->move_scene = obs_sceneitem_get_source(scene_item) == move->scene_source_b; if (item->move_scene) move->matched_scene_b = true; return true; } bool match_item(obs_scene_t *scene, obs_sceneitem_t *scene_item, void *data) { UNUSED_PARAMETER(scene); if (!obs_sceneitem_visible(scene_item)) { return true; } struct move_info *move = data; size_t old_pos; struct move_item *item = match_item2(move, scene_item, false, &old_pos); if (!item && (move->part_match || move->number_match || move->last_word_match)) { item = match_item2(move, scene_item, true, &old_pos); } if (item) { move->matched_items++; if (old_pos >= move->item_pos) move->item_pos = old_pos + 1; } else { item = create_move_item(); da_insert(move->items_a, move->item_pos, &item); move->item_pos++; } obs_sceneitem_addref(scene_item); item->item_b = scene_item; item->move_scene = obs_sceneitem_get_source(scene_item) == move->scene_source_a; if (item->move_scene) move->matched_scene_a = true; da_push_back(move->items_b, &item); return true; } void sceneitem_start_move(obs_sceneitem_t *item, const char *start_move) { obs_scene_t *scene = obs_sceneitem_get_scene(item); obs_source_t *scene_source = obs_scene_get_source(scene); obs_source_t *filter = obs_source_get_filter_by_name(scene_source, start_move); if (!filter) { obs_source_t *source = obs_sceneitem_get_source(item); filter = obs_source_get_filter_by_name(source, start_move); } if (!filter) return; const char *filter_id = obs_source_get_unversioned_id(filter); if (strcmp(filter_id, MOVE_SOURCE_FILTER_ID) == 0) { move_source_start(obs_obj_get_data(filter)); } else if (strcmp(filter_id, MOVE_VALUE_FILTER_ID) == 0 || strcmp(filter_id, MOVE_AUDIO_VALUE_FILTER_ID) == 0) { move_value_start(obs_obj_get_data(filter)); } } static void move_video_render(void *data, gs_effect_t *effect) { struct move_info *move = data; move->t = obs_transition_get_time(move->source); if (move->start_init) { obs_source_t *old_scene_a = move->scene_source_a; move->scene_source_a = obs_transition_get_source( move->source, OBS_TRANSITION_SOURCE_A); obs_source_t *old_scene_b = move->scene_source_b; move->scene_source_b = obs_transition_get_source( move->source, OBS_TRANSITION_SOURCE_B); obs_source_release(old_scene_a); obs_source_release(old_scene_b); clear_items(move, true); move->matched_items = 0; move->transition_pool_move_index = 0; move->transition_pool_in_index = 0; move->transition_pool_out_index = 0; move->matched_scene_a = false; move->matched_scene_b = false; move->item_pos = 0; obs_scene_t *scene_a = obs_scene_from_source(move->scene_source_a); if (scene_a) { obs_scene_enum_items(scene_a, add_item, data); } else if (move->scene_source_a) { scene_a = obs_scene_create_private( obs_source_get_name(move->scene_source_a)); obs_sceneitem_t *scene_item = obs_scene_add(scene_a, move->scene_source_a); struct move_item *item = create_move_item(); da_push_back(move->items_a, &item); item->item_a = scene_item; item->release_scene_a = scene_a; } move->item_pos = 0; obs_scene_t *scene_b = obs_scene_from_source(move->scene_source_b); if (scene_b) { obs_scene_enum_items(scene_b, match_item, data); } else if (move->scene_source_b) { scene_b = obs_scene_create_private( obs_source_get_name(move->scene_source_b)); obs_sceneitem_t *scene_item = obs_scene_add(scene_b, move->scene_source_b); size_t old_pos; struct move_item *item = match_item2(move, scene_item, false, &old_pos); if (!item && (move->part_match || move->number_match || move->last_word_match)) { item = match_item2(move, scene_item, true, &old_pos); } if (item) { move->matched_items++; if (old_pos >= move->item_pos) move->item_pos = old_pos + 1; } else { item = create_move_item(); da_insert(move->items_a, move->item_pos, &item); move->item_pos++; } item->item_b = scene_item; item->release_scene_b = scene_b; da_push_back(move->items_b, &item); } if (!move->matched_items && (move->matched_scene_a || move->matched_scene_b)) { size_t i = 0; while (i < move->items_a.num) { struct move_item *item = move->items_a.array[i]; if (move->matched_scene_a && item->item_a) { obs_sceneitem_release(item->item_a); da_erase(move->items_a, i); } else if (move->matched_scene_b && item->item_b) { obs_sceneitem_release(item->item_b); da_erase(move->items_a, i); } else { i++; } } if (move->matched_scene_b) { move->items_b.num = 0; } } // insert missing items from items_a into items_b move->item_pos = 0; for (size_t i = 0; i < move->items_a.num; i++) { struct move_item *item = move->items_a.array[i]; if (item->item_a && !item->item_b) { da_insert(move->items_b, move->item_pos, &item); move->item_pos++; } else { for (size_t j = move->item_pos; j < move->items_b.num; j++) { if (item == move->items_b.array[j]) { move->item_pos = j + 1; break; } } } } for (size_t i = 0; i < move->items_a.num; i++) { struct move_item *item = move->items_a.array[i]; if ((item->item_a && item->item_b) || item->move_scene) { item->easing = move->easing_move; item->easing_function = move->easing_function_move; item->transition_scale = move->transition_move_scale; item->curve = move->curve_move; } else if (item->item_b) { item->easing = move->easing_in; item->easing_function = move->easing_function_in; item->position = move->position_in; item->zoom = move->zoom_in; item->curve = move->curve_in; } else if (item->item_a) { item->easing = move->easing_out; item->easing_function = move->easing_function_out; item->position = move->position_out; item->zoom = move->zoom_out; item->curve = move->curve_out; } obs_data_t *settings_a = get_override_filter_settings(item->item_a); obs_data_t *settings_b = get_override_filter_settings(item->item_b); if (settings_a && settings_b) { long long val_a = obs_data_get_int( settings_a, S_EASING_MATCH); long long val_b = obs_data_get_int( settings_b, S_EASING_MATCH); if (val_a != NO_OVERRIDE && val_b != NO_OVERRIDE) { item->easing = (val_a & EASE_IN) | (val_b & EASE_OUT); } else if (val_a != NO_OVERRIDE) { item->easing = val_a; } else if (val_b != NO_OVERRIDE) { item->easing = val_b; } val_a = obs_data_get_int( settings_a, S_EASING_FUNCTION_MATCH); val_b = obs_data_get_int( settings_b, S_EASING_FUNCTION_MATCH); if (val_a != NO_OVERRIDE) { item->easing_function = val_a; } else if (val_b != NO_OVERRIDE) { item->easing_function = val_b; } const char *cv_a = obs_data_get_string( settings_a, S_TRANSITION_MATCH); const char *cv_b = obs_data_get_string( settings_b, S_TRANSITION_MATCH); if (cv_a && strlen(cv_a)) { bfree(item->transition_name); item->transition_name = bstrdup(cv_a); } else if (cv_b && strlen(cv_b)) { bfree(item->transition_name); item->transition_name = bstrdup(cv_b); } val_a = obs_data_get_int(settings_a, S_TRANSITION_SCALE); val_b = obs_data_get_int(settings_b, S_TRANSITION_SCALE); if (val_a != NO_OVERRIDE) { item->transition_scale = val_a; } else if (val_b != NO_OVERRIDE) { item->transition_scale = val_b; } if (obs_data_get_bool(settings_a, S_CURVE_OVERRIDE_MATCH)) { item->curve = (float)obs_data_get_double( settings_a, S_CURVE_MATCH); } else if (obs_data_get_bool( settings_b, S_CURVE_OVERRIDE_MATCH)) { item->curve = (float)obs_data_get_double( settings_b, S_CURVE_MATCH); } val_a = obs_data_get_int( settings_a, S_START_DELAY_MATCH_FROM); val_b = obs_data_get_int( settings_b, S_START_DELAY_MATCH_TO); if (val_a != NO_OVERRIDE && val_b != NO_OVERRIDE) { item->start_percentage = (int)(val_a + val_b) >> 1; } else if (val_a != NO_OVERRIDE) { item->start_percentage = (int)val_a; } else if (val_b != NO_OVERRIDE) { item->start_percentage = (int)val_b; } val_a = obs_data_get_int( settings_a, S_END_DELAY_MATCH_FROM); val_b = obs_data_get_int(settings_b, S_END_DELAY_MATCH_TO); if (val_a != NO_OVERRIDE && val_b != NO_OVERRIDE) { item->end_percentage = 100 - ((int)(val_a + val_b) >> 1); } else if (val_a != NO_OVERRIDE) { item->end_percentage = 100 - (int)val_a; } else if (val_b != NO_OVERRIDE) { item->end_percentage = 100 - (int)val_b; } const char *start_move_a = obs_data_get_string( settings_a, S_START_MOVE_MATCH_FROM); if (start_move_a && strlen(start_move_a)) { sceneitem_start_move(item->item_a, start_move_a); } const char *start_move_b = obs_data_get_string( settings_b, S_START_MOVE_MATCH_TO); if (start_move_b && strlen(start_move_b)) { sceneitem_start_move(item->item_b, start_move_b); } } else if (settings_a) { long long val; if ((item->item_a && item->item_b) || item->move_scene) val = obs_data_get_int(settings_a, S_EASING_MATCH); else val = obs_data_get_int(settings_a, S_EASING_OUT); if (val != NO_OVERRIDE) { item->easing = val; } if ((item->item_a && item->item_b) || item->move_scene) val = obs_data_get_int( settings_a, S_EASING_FUNCTION_MATCH); else val = obs_data_get_int( settings_a, S_EASING_FUNCTION_OUT); if (val != NO_OVERRIDE) { item->easing_function = val; } val = obs_data_get_int(settings_a, S_ZOOM_OUT); if (val != NO_OVERRIDE) { item->zoom = !!val; } val = obs_data_get_int(settings_a, S_POSITION_OUT); if (val != NO_OVERRIDE) { item->position = val; } val = obs_data_get_int(settings_a, S_TRANSITION_SCALE); if (val != NO_OVERRIDE) { item->transition_scale = val; } const char *ti = obs_data_get_string( settings_a, S_TRANSITION_OUT); if (!item->move_scene && ti && strlen(ti) && item->item_a && !item->item_b) { bfree(item->transition_name); item->transition_name = bstrdup(ti); } const char *tm = obs_data_get_string( settings_a, S_TRANSITION_MATCH); if (tm && strlen(tm) && ((item->item_a && item->item_b) || item->move_scene)) { bfree(item->transition_name); item->transition_name = bstrdup(tm); } if (((item->item_a && item->item_b) || item->move_scene) && obs_data_get_bool(settings_a, S_CURVE_OVERRIDE_MATCH)) { item->curve = (float)obs_data_get_double( settings_a, S_CURVE_MATCH); } else if (!item->move_scene && item->item_a && !item->item_b && obs_data_get_bool( settings_a, S_CURVE_OVERRIDE_OUT)) { item->curve = (float)obs_data_get_double( settings_a, S_CURVE_OUT); } if ((item->item_a && item->item_b) || item->move_scene) val = obs_data_get_int( settings_a, S_START_DELAY_MATCH_FROM); else val = obs_data_get_int( settings_a, S_START_DELAY_OUT); if (val != NO_OVERRIDE) { item->start_percentage = (int)val; } if ((item->item_a && item->item_b) || item->move_scene) val = obs_data_get_int( settings_a, S_END_DELAY_MATCH_FROM); else val = obs_data_get_int(settings_a, S_END_DELAY_OUT); if (val != NO_OVERRIDE) { item->end_percentage = 100 - (int)val; } const char *move_start = ((item->item_a && item->item_b) || item->move_scene) ? obs_data_get_string( settings_a, S_START_MOVE_MATCH_FROM) : obs_data_get_string( settings_a, S_START_MOVE_OUT); if (move_start && strlen(move_start)) { sceneitem_start_move(item->item_a, move_start); } } else if (settings_b) { long long val; if ((item->item_a && item->item_b) || item->move_scene) val = obs_data_get_int(settings_b, S_EASING_MATCH); else val = obs_data_get_int(settings_b, S_EASING_IN); if (val != NO_OVERRIDE) { item->easing = val; } if ((item->item_a && item->item_b) || item->move_scene) val = obs_data_get_int( settings_b, S_EASING_FUNCTION_MATCH); else val = obs_data_get_int( settings_b, S_EASING_FUNCTION_IN); if (val != NO_OVERRIDE) { item->easing_function = val; } val = obs_data_get_int(settings_b, S_ZOOM_IN); if (val != NO_OVERRIDE) { item->zoom = !!val; } val = obs_data_get_int(settings_b, S_POSITION_IN); if (val != NO_OVERRIDE) { item->position = val; } val = obs_data_get_int(settings_b, S_TRANSITION_SCALE); if (val != NO_OVERRIDE) { item->transition_scale = val; } const char *to = obs_data_get_string( settings_b, S_TRANSITION_IN); if (!item->move_scene && to && strlen(to) && !item->item_a && item->item_b) { bfree(item->transition_name); item->transition_name = bstrdup(to); } const char *tm = obs_data_get_string( settings_b, S_TRANSITION_MATCH); if (tm && strlen(tm) && ((item->item_a && item->item_b) || item->move_scene)) { bfree(item->transition_name); item->transition_name = bstrdup(tm); } if (((item->item_a && item->item_b) || item->move_scene) && obs_data_get_bool(settings_b, S_CURVE_OVERRIDE_MATCH)) { item->curve = (float)obs_data_get_double( settings_b, S_CURVE_MATCH); } else if (!item->move_scene && !item->item_a && item->item_b && obs_data_get_bool( settings_b, S_CURVE_OVERRIDE_IN)) { item->curve = (float)obs_data_get_double( settings_b, S_CURVE_IN); } if ((item->item_a && item->item_b) || item->move_scene) val = obs_data_get_int( settings_b, S_START_DELAY_MATCH_TO); else val = obs_data_get_int( settings_b, S_START_DELAY_IN); if (val != NO_OVERRIDE) { item->start_percentage = (int)val; } if ((item->item_a && item->item_b) || item->move_scene) val = obs_data_get_int( settings_b, S_END_DELAY_MATCH_TO); else val = obs_data_get_int(settings_b, S_END_DELAY_IN); if (val != NO_OVERRIDE) { item->end_percentage = 100 - (int)val; } const char *move_start = ((item->item_a && item->item_b) || item->move_scene) ? obs_data_get_string( settings_b, S_START_MOVE_MATCH_TO) : obs_data_get_string( settings_a, S_START_MOVE_IN); if (move_start && strlen(move_start)) { sceneitem_start_move(item->item_b, move_start); } } obs_data_release(settings_a); obs_data_release(settings_b); if (!item->transition_name && !item->move_scene && !item->item_a && item->item_b && move->transition_in && strlen(move->transition_in)) { bfree(item->transition_name); item->transition_name = bstrdup(move->transition_in); } if (!item->transition_name && !item->move_scene && item->item_a && !item->item_b && move->transition_out && strlen(move->transition_out)) { bfree(item->transition_name); item->transition_name = bstrdup(move->transition_out); } if (!item->transition_name && ((item->item_a && item->item_b) || item->move_scene) && move->transition_move && strlen(move->transition_move)) { bfree(item->transition_name); item->transition_name = bstrdup(move->transition_move); } } } if (move->t > 0.0f && move->t < 1.0f) { if (!move->scene_source_a) move->scene_source_a = obs_transition_get_source( move->source, OBS_TRANSITION_SOURCE_A); if (!move->scene_source_b) move->scene_source_b = obs_transition_get_source( move->source, OBS_TRANSITION_SOURCE_B); gs_matrix_push(); gs_blend_state_push(); gs_reset_blend_state(); if (move->t * 100.0 < move->item_order_switch_percentage) { for (size_t i = 0; i < move->items_a.num; i++) { struct move_item *item = move->items_a.array[i]; render2_item(move, item); } } else { for (size_t i = 0; i < move->items_b.num; i++) { struct move_item *item = move->items_b.array[i]; render2_item(move, item); } } gs_blend_state_pop(); gs_matrix_pop(); } else if (move->t <= 0.5f) { obs_transition_video_render_direct(move->source, OBS_TRANSITION_SOURCE_A); } else { obs_transition_video_render_direct(move->source, OBS_TRANSITION_SOURCE_B); } move->start_init = false; UNUSED_PARAMETER(effect); } static float mix_a(void *data, float t) { UNUSED_PARAMETER(data); return 1.0f - CubicEaseInOut(t); } static float mix_b(void *data, float t) { UNUSED_PARAMETER(data); return CubicEaseInOut(t); } static bool move_audio_render(void *data, uint64_t *ts_out, struct obs_source_audio_mix *audio, uint32_t mixers, size_t channels, size_t sample_rate) { struct move_info *move = data; return obs_transition_audio_render(move->source, ts_out, audio, mixers, channels, sample_rate, mix_a, mix_b); } void prop_list_add_positions(obs_property_t *p) { obs_property_list_add_int(p, obs_module_text("Position.None"), POS_NONE); obs_property_list_add_int(p, obs_module_text("Position.Center"), POS_CENTER); obs_property_list_add_int(p, obs_module_text("Position.CenterInverse"), POS_EDGE); obs_property_list_add_int(p, obs_module_text("Position.TopLeft"), POS_EDGE | POS_TOP | POS_LEFT); obs_property_list_add_int(p, obs_module_text("Position.TopCenter"), POS_EDGE | POS_TOP); obs_property_list_add_int(p, obs_module_text("Position.TopRight"), POS_EDGE | POS_TOP | POS_RIGHT); obs_property_list_add_int(p, obs_module_text("Position.CenterRight"), POS_EDGE | POS_RIGHT); obs_property_list_add_int(p, obs_module_text("Position.BottomRight"), POS_EDGE | POS_BOTTOM | POS_RIGHT); obs_property_list_add_int(p, obs_module_text("Position.BottomCenter"), POS_EDGE | POS_BOTTOM); obs_property_list_add_int(p, obs_module_text("Position.BottomLeft"), POS_EDGE | POS_BOTTOM | POS_LEFT); obs_property_list_add_int(p, obs_module_text("Position.CenterLeft"), POS_EDGE | POS_LEFT); obs_property_list_add_int(p, obs_module_text("Position.Left"), POS_SWIPE | POS_LEFT); obs_property_list_add_int(p, obs_module_text("Position.Top"), POS_SWIPE | POS_TOP); obs_property_list_add_int(p, obs_module_text("Position.Right"), POS_SWIPE | POS_RIGHT); obs_property_list_add_int(p, obs_module_text("Position.Bottom"), POS_SWIPE | POS_BOTTOM); } void prop_list_add_easings(obs_property_t *p) { obs_property_list_add_int(p, obs_module_text("Easing.None"), EASE_NONE); obs_property_list_add_int(p, obs_module_text("Easing.In"), EASE_IN); obs_property_list_add_int(p, obs_module_text("Easing.Out"), EASE_OUT); obs_property_list_add_int(p, obs_module_text("Easing.InOut"), EASE_IN_OUT); } void prop_list_add_easing_functions(obs_property_t *p) { obs_property_list_add_int(p, obs_module_text("EasingFunction.Quadratic"), EASING_QUADRATIC); obs_property_list_add_int(p, obs_module_text("EasingFunction.Cubic"), EASING_CUBIC); obs_property_list_add_int(p, obs_module_text("EasingFunction.Quartic"), EASING_QUARTIC); obs_property_list_add_int(p, obs_module_text("EasingFunction.Quintic"), EASING_QUINTIC); obs_property_list_add_int(p, obs_module_text("EasingFunction.Sine"), EASING_SINE); obs_property_list_add_int(p, obs_module_text("EasingFunction.Circular"), EASING_CIRCULAR); obs_property_list_add_int(p, obs_module_text("EasingFunction.Exponential"), EASING_EXPONENTIAL); obs_property_list_add_int(p, obs_module_text("EasingFunction.Elastic"), EASING_ELASTIC); obs_property_list_add_int(p, obs_module_text("EasingFunction.Bounce"), EASING_BOUNCE); obs_property_list_add_int(p, obs_module_text("EasingFunction.Back"), EASING_BACK); } void prop_list_add_transitions(obs_property_t *p) { struct obs_frontend_source_list transitions = {0}; obs_property_list_add_string(p, obs_module_text("Transition.None"), "None"); obs_frontend_get_transitions(&transitions); for (size_t i = 0; i < transitions.sources.num; i++) { const char *id = obs_source_get_unversioned_id( transitions.sources.array[i]); if (!id || strcmp(id, "move_transition") == 0) continue; const char *name = obs_source_get_name(transitions.sources.array[i]); obs_property_list_add_string(p, name, name); } obs_frontend_source_list_free(&transitions); } void prop_list_add_scales(obs_property_t *p) { obs_property_list_add_int(p, obs_module_text("TransitionScale.MaxOnly"), OBS_TRANSITION_SCALE_MAX_ONLY); obs_property_list_add_int(p, obs_module_text("TransitionScale.Aspect"), OBS_TRANSITION_SCALE_ASPECT); obs_property_list_add_int(p, obs_module_text("TransitionScale.Stretch"), OBS_TRANSITION_SCALE_STRETCH); } static obs_properties_t *move_properties(void *data) { obs_property_t *p; obs_properties_t *ppts = obs_properties_create(); obs_properties_t *group = obs_properties_create(); obs_properties_add_bool(group, S_NAME_PART_MATCH, obs_module_text("NamePartMatch")); obs_properties_add_bool(group, S_NAME_NUMBER_MATCH, obs_module_text("NameNumberMatch")); obs_properties_add_bool(group, S_NAME_LAST_WORD_MATCH, obs_module_text("NameLastWordMatch")); obs_properties_add_group(ppts, S_MATCH, obs_module_text("MatchName"), OBS_GROUP_NORMAL, group); group = obs_properties_create(); obs_properties_add_bool(group, S_CACHE_TRANSITIONS, obs_module_text("CacheTransitions")); p = obs_properties_add_int_slider(group, S_SWITCH_PERCENTAGE, obs_module_text("SwitchPoint"), 0, 100, 1); obs_property_int_set_suffix(p, "%"); obs_properties_add_group(ppts, S_MOVE_ALL, obs_module_text("MoveAll"), OBS_GROUP_NORMAL, group); //Matched items group = obs_properties_create(); p = obs_properties_add_list(group, S_EASING_MATCH, obs_module_text("Easing"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); prop_list_add_easings(p); p = obs_properties_add_list(group, S_EASING_FUNCTION_MATCH, obs_module_text("EasingFunction"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); prop_list_add_easing_functions(p); p = obs_properties_add_list(group, S_TRANSITION_MATCH, obs_module_text("Transition"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); prop_list_add_transitions(p); p = obs_properties_add_list(group, S_TRANSITION_SCALE, obs_module_text("TransitionScaleType"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); prop_list_add_scales(p); obs_properties_add_float_slider(group, S_CURVE_MATCH, obs_module_text("Curve"), -2.0, 2.0, 0.01); obs_properties_add_group(ppts, S_MOVE_MATCH, obs_module_text("MoveMatch"), OBS_GROUP_NORMAL, group); //Move in group = obs_properties_create(); p = obs_properties_add_list(group, S_EASING_IN, obs_module_text("Easing"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); prop_list_add_easings(p); p = obs_properties_add_list(group, S_EASING_FUNCTION_IN, obs_module_text("EasingFunction"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); prop_list_add_easing_functions(p); obs_properties_add_bool(group, S_ZOOM_IN, obs_module_text("Zoom")); p = obs_properties_add_list(group, S_POSITION_IN, obs_module_text("Position"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); prop_list_add_positions(p); p = obs_properties_add_list(group, S_TRANSITION_IN, obs_module_text("Transition"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); prop_list_add_transitions(p); obs_properties_add_float_slider( group, S_CURVE_IN, obs_module_text("Curve"), -2.0, 2.0, 0.01); obs_properties_add_group(ppts, S_MOVE_IN, obs_module_text("MoveIn"), OBS_GROUP_NORMAL, group); //Move out group = obs_properties_create(); p = obs_properties_add_list(group, S_EASING_OUT, obs_module_text("Easing"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); prop_list_add_easings(p); p = obs_properties_add_list(group, S_EASING_FUNCTION_OUT, obs_module_text("EasingFunction"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); prop_list_add_easing_functions(p); obs_properties_add_bool(group, S_ZOOM_OUT, obs_module_text("Zoom")); p = obs_properties_add_list(group, S_POSITION_OUT, obs_module_text("Position"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); prop_list_add_positions(p); p = obs_properties_add_list(group, S_TRANSITION_OUT, obs_module_text("Transition"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); prop_list_add_transitions(p); obs_properties_add_float_slider( group, S_CURVE_OUT, obs_module_text("Curve"), -2.0, 2.0, 0.01); obs_properties_add_group(ppts, S_MOVE_OUT, obs_module_text("MoveOut"), OBS_GROUP_NORMAL, group); UNUSED_PARAMETER(data); return ppts; } void move_defaults(obs_data_t *settings) { obs_data_set_default_int(settings, S_EASING_MATCH, EASE_IN_OUT); obs_data_set_default_int(settings, S_EASING_IN, EASE_IN_OUT); obs_data_set_default_int(settings, S_EASING_OUT, EASE_IN_OUT); obs_data_set_default_int(settings, S_EASING_FUNCTION_MATCH, EASING_CUBIC); obs_data_set_default_int(settings, S_EASING_FUNCTION_IN, EASING_CUBIC); obs_data_set_default_int(settings, S_EASING_FUNCTION_OUT, EASING_CUBIC); obs_data_set_default_int(settings, S_POSITION_IN, POS_EDGE | POS_LEFT); obs_data_set_default_bool(settings, S_ZOOM_IN, true); obs_data_set_default_int(settings, S_POSITION_OUT, POS_EDGE | POS_RIGHT); obs_data_set_default_bool(settings, S_ZOOM_OUT, true); obs_data_set_default_double(settings, S_CURVE_MATCH, 0.0); obs_data_set_default_double(settings, S_CURVE_IN, 0.0); obs_data_set_default_double(settings, S_CURVE_OUT, 0.0); obs_data_set_default_int(settings, S_SWITCH_PERCENTAGE, 50); } static void move_start(void *data) { struct move_info *move = data; move->start_init = true; } static void move_stop(void *data) { struct move_info *move = data; clear_items(move, false); } struct obs_source_info move_transition = {.id = "move_transition", .type = OBS_SOURCE_TYPE_TRANSITION, .get_name = move_get_name, .create = move_create, .destroy = move_destroy, .update = move_update, .video_render = move_video_render, .audio_render = move_audio_render, .get_properties = move_properties, .get_defaults = move_defaults, .transition_start = move_start, .transition_stop = move_stop}; OBS_DECLARE_MODULE() OBS_MODULE_AUTHOR("Exeldro"); OBS_MODULE_USE_DEFAULT_LOCALE("move-transition", "en-US") MODULE_EXPORT const char *obs_module_description(void) { return obs_module_text("Description"); } MODULE_EXPORT const char *obs_module_name(void) { return obs_module_text("MoveTransition"); } extern struct obs_source_info move_transition_override_filter; extern struct obs_source_info move_source_filter; extern struct obs_source_info move_value_filter; extern struct obs_source_info move_audio_value_filter; extern struct obs_source_info audio_move_filter; bool obs_module_load(void) { blog(LOG_INFO, "[Move Transition] loaded version %s", PROJECT_VERSION); obs_register_source(&move_transition); obs_register_source(&move_transition_override_filter); obs_register_source(&move_source_filter); obs_register_source(&move_value_filter); obs_register_source(&move_audio_value_filter); obs_register_source(&audio_move_filter); return true; } obs-move-transition-2.5.7/move-transition.h000066400000000000000000000226221417746432500207740ustar00rootroot00000000000000#pragma once #include #include #define MOVE_SOURCE_FILTER_ID "move_source_filter" #define MOVE_VALUE_FILTER_ID "move_value_filter" #define MOVE_AUDIO_VALUE_FILTER_ID "move_audio_value_filter" #define AUDIO_MOVE_FILTER_ID "audio_move_filter" #define S_MATCH "match" #define S_MOVE_ALL "move_all" #define S_MOVE_MATCH "move_match" #define S_MOVE_IN "move_in" #define S_MOVE_OUT "move_out" #define S_NAME_PART_MATCH "name_part_match" #define S_NAME_NUMBER_MATCH "name_number_match" #define S_NAME_LAST_WORD_MATCH "name_last_word_match" #define S_GENERAL "general" #define S_SOURCE "source" #define S_POSITION_IN "position_in" #define S_POSITION_OUT "position_out" #define S_ZOOM_IN "zoom_in" #define S_ZOOM_OUT "zoom_out" #define S_EASING_MATCH "easing_match" #define S_EASING_IN "easing_in" #define S_EASING_OUT "easing_out" #define S_EASING_FUNCTION_MATCH "easing_function_match" #define S_EASING_FUNCTION_IN "easing_function_in" #define S_EASING_FUNCTION_OUT "easing_function_out" #define S_CURVE_MATCH "curve_match" #define S_CURVE_IN "curve_in" #define S_CURVE_OUT "curve_out" #define S_CURVE_OVERRIDE_MATCH "curve_override_match" #define S_CURVE_OVERRIDE_IN "curve_override_in" #define S_CURVE_OVERRIDE_OUT "curve_override_out" #define S_TRANSITION_MATCH "transition_match" #define S_TRANSITION_SCALE "transition_scale_match" #define S_TRANSITION_IN "transition_in" #define S_TRANSITION_OUT "transition_out" #define S_START_DELAY_MATCH_FROM "start_delay_match_from" #define S_START_DELAY_MATCH_TO "start_delay_match_to" #define S_START_DELAY_IN "start_delay_in" #define S_START_DELAY_OUT "start_delay_out" #define S_END_DELAY_MATCH_FROM "end_delay_match_from" #define S_END_DELAY_MATCH_TO "end_delay_match_to" #define S_END_DELAY_IN "end_delay_in" #define S_END_DELAY_OUT "end_delay_out" #define S_START_MOVE_MATCH_FROM "start_move_match_from" #define S_START_MOVE_MATCH_TO "start_move_match_to" #define S_START_MOVE_IN "start_move_match_in" #define S_START_MOVE_OUT "start_move_match_out" #define S_CUSTOM_DURATION "custom_duration" #define S_DURATION "duration" #define S_ROT "rot" #define S_POS "pos" #define S_SCALE "scale" #define S_BOUNDS "bounds" #define S_CROP "crop" #define S_TRANSFORM_TEXT "transform_text" #define S_SWITCH_PERCENTAGE "switch_percentage" #define S_CACHE_TRANSITIONS "cache_transitions" #define S_START_TRIGGER "start_trigger" #define S_STOP_TRIGGER "stop_trigger" #define S_START_DELAY "start_delay" #define S_END_DELAY "end_delay" #define S_ACTIONS "actions" #define S_SIMULTANEOUS_MOVE "simultaneous_move" #define S_NEXT_MOVE "next_move" #define S_NEXT_MOVE_ON "next_move_on" #define S_FILTER "filter" #define S_SINGLE_SETTING "single_setting" #define S_MOVE_VALUE_TYPE "move_value_type" #define S_SETTING_VALUE "setting_value" #define S_SETTING_RANDOM "setting_random" #define S_SETTING_NAME "setting_name" #define S_SETTING_INT "setting_int" #define S_SETTING_INT_MIN "setting_int_min" #define S_SETTING_INT_MAX "setting_int_max" #define S_SETTING_FLOAT "setting_float" #define S_SETTING_FLOAT_MIN "setting_float_min" #define S_SETTING_FLOAT_MAX "setting_float_max" #define S_SETTING_DECIMALS "setting_decimals" #define S_SETTING_FORMAT_TYPE "setting_format_type" #define S_SETTING_FORMAT "setting_format" #define S_SETTING_COLOR "setting_color" #define S_SETTING_COLOR_MIN "setting_color_min" #define S_SETTING_COLOR_MAX "setting_color_max" #define S_SETTING_TEXT "setting_text" #define S_SETTINGS "settings" #define S_SETTING_FROM "setting_from" #define S_SETTING_TO "setting_to" #define S_VALUE_TYPE "value_type" #define S_TRANSFORM "transform" #define S_TRANSFORM_RELATIVE "transform_relative" #define S_VISIBILITY_ORDER "visibility_order" #define S_CHANGE_VISIBILITY "change_visibility" #define S_CHANGE_ORDER "change_order" #define S_ORDER_POSITION "order_position" #define S_MEDIA_ACTION "media_action" #define S_MEDIA_ACTION_START "media_action_start" #define S_MEDIA_ACTION_START_TIME "media_action_start_time" #define S_MEDIA_ACTION_END "media_action_end" #define S_MEDIA_ACTION_END_TIME "media_action_end_time" #define S_AUDIO_ACTION "audio_action" #define S_MUTE_ACTION "mute_action" #define S_AUDIO_FADE "audio_fade" #define S_AUDIO_FADE_PERCENT "audio_fade_percent" #define S_ENABLED_MATCH_MOVING "enabled_match_moving" #define S_MATCH_SOURCE "match_source" #define NO_OVERRIDE (-1) #define ZOOM_NO 0 #define ZOOM_YES 1 #define EASE_NONE 0 #define EASE_IN 1 #define EASE_OUT 2 #define EASE_IN_OUT 3 #define EASING_QUADRATIC 1 #define EASING_CUBIC 2 #define EASING_QUARTIC 3 #define EASING_QUINTIC 4 #define EASING_SINE 5 #define EASING_CIRCULAR 6 #define EASING_EXPONENTIAL 7 #define EASING_ELASTIC 8 #define EASING_BOUNCE 9 #define EASING_BACK 10 #define POS_NONE 0 #define POS_CENTER (1 << 0) #define POS_EDGE (1 << 1) #define POS_LEFT (1 << 2) #define POS_RIGHT (1 << 3) #define POS_TOP (1 << 4) #define POS_BOTTOM (1 << 5) #define POS_SWIPE (1 << 6) #define START_TRIGGER_NONE 0 #define START_TRIGGER_ACTIVATE 1 #define START_TRIGGER_DEACTIVATE 2 #define START_TRIGGER_SHOW 3 #define START_TRIGGER_HIDE 4 #define START_TRIGGER_ENABLE 5 #define START_TRIGGER_SOURCE_ACTIVATE 6 #define START_TRIGGER_SOURCE_DEACTIVATE 7 #define START_TRIGGER_SOURCE_SHOW 8 #define START_TRIGGER_SOURCE_HIDE 9 #define START_TRIGGER_ENABLE_DISABLE_OLD 10 #define START_TRIGGER_MEDIA_STARTED 11 #define START_TRIGGER_MEDIA_ENDED 12 #define START_TRIGGER_LOAD 13 #define MOVE_VALUE_UNKNOWN 0 #define MOVE_VALUE_INT 1 #define MOVE_VALUE_FLOAT 2 #define MOVE_VALUE_COLOR 3 #define MOVE_VALUE_TEXT 4 #define MOVE_VALUE_FORMAT_DECIMALS 0 #define MOVE_VALUE_FORMAT_FLOAT 1 #define MOVE_VALUE_FORMAT_TIME 2 #define NEXT_MOVE_ON_END 0 #define NEXT_MOVE_ON_HOTKEY 1 #define NEXT_MOVE_REVERSE "Reverse" #define CHANGE_VISIBILITY_NONE 0 #define CHANGE_VISIBILITY_SHOW_START 1 #define CHANGE_VISIBILITY_HIDE_END 2 #define CHANGE_VISIBILITY_TOGGLE 3 #define CHANGE_VISIBILITY_SHOW_END 4 #define CHANGE_VISIBILITY_HIDE_START 5 #define CHANGE_VISIBILITY_TOGGLE_START 6 #define CHANGE_VISIBILITY_TOGGLE_END 7 #define CHANGE_VISIBILITY_SHOW_START_END 8 #define CHANGE_VISIBILITY_HIDE_START_END 9 #define CHANGE_ORDER_NONE 0 #define CHANGE_ORDER_RELATIVE (1 << 0) #define CHANGE_ORDER_ABSOLUTE (1 << 1) #define CHANGE_ORDER_START (1 << 2) #define CHANGE_ORDER_END (1 << 3) #define MEDIA_ACTION_NONE 0 #define MEDIA_ACTION_PLAY 1 #define MEDIA_ACTION_PAUSE 2 #define MEDIA_ACTION_STOP 3 #define MEDIA_ACTION_RESTART 4 #define MEDIA_ACTION_NEXT 5 #define MEDIA_ACTION_PREVIOUS 6 #define MEDIA_ACTION_PLAY_FROM 7 #define MEDIA_ACTION_PAUSE_AT 8 #define MUTE_ACTION_NONE 0 #define MUTE_ACTION_MUTE_START 1 #define MUTE_ACTION_UNMUTE_START 2 #define MUTE_ACTION_MUTE_END 3 #define MUTE_ACTION_UNMUTE_END 4 #define MUTE_ACTION_MUTE_DURING 5 #define MUTE_ACTION_UNMUTE_DURING 6 #define MOVE_VALUE_TYPE_SINGLE_SETTING 0 #define MOVE_VALUE_TYPE_SETTINGS 1 #define MOVE_VALUE_TYPE_RANDOM 2 #define MOVE_VALUE_TYPE_SETTING_ADD 3 #define MOVE_VALUE_TYPE_TYPING 4 struct move_value_info { obs_source_t *source; char *filter_name; obs_source_t *filter; char *setting_filter_name; char *setting_name; obs_hotkey_id move_start_hotkey; bool custom_duration; uint64_t duration; uint64_t start_delay; uint64_t end_delay; uint32_t start_trigger; uint32_t stop_trigger; bool moving; float running_duration; char *simultaneous_move_name; char *next_move_name; bool enabled; long long easing; long long easing_function; long long int_to; long long int_value; long long int_from; long long int_min; long long int_max; int decimals; double double_to; double double_value; double double_from; double double_min; double double_max; struct vec4 color_to; struct vec4 color_value; struct vec4 color_from; struct vec4 color_min; struct vec4 color_max; char *text_from; size_t text_from_len; char *text_to; size_t text_to_len; size_t text_same; size_t text_step; size_t text_steps; obs_data_array_t *settings; long long move_value_type; long long value_type; long long format_type; char *format; DARRAY(obs_source_t *) filters_done; long long next_move_on; bool reverse; bool enabled_match_moving; }; struct move_source_info { obs_source_t *source; char *source_name; char *filter_name; obs_sceneitem_t *scene_item; obs_hotkey_id move_start_hotkey; long long easing; long long easing_function; float curve; bool transform; struct vec2 pos_from; struct vec2 pos_to; float rot_from; float rot_to; struct vec2 scale_from; struct vec2 scale_to; struct vec2 bounds_from; struct vec2 bounds_to; struct obs_sceneitem_crop crop_from; struct obs_sceneitem_crop crop_to; bool custom_duration; uint64_t duration; uint64_t start_delay; uint64_t end_delay; bool moving; float running_duration; uint32_t canvas_width; uint32_t canvas_height; uint32_t start_trigger; uint32_t stop_trigger; bool enabled; char *simultaneous_move_name; char *next_move_name; DARRAY(obs_source_t *) filters_done; long long next_move_on; long long change_visibility; bool visibility_toggled; bool reverse; long long change_order; long long order_position; long long media_action_start; int64_t media_time_start; long long media_action_end; int64_t media_time_end; bool audio_fade; float audio_fade_from; float audio_fade_to; long long mute_action; bool enabled_match_moving; }; void move_value_start(struct move_value_info *move_value); void move_source_start(struct move_source_info *move_source); void prop_list_add_move_source_filter(obs_source_t *parent, obs_source_t *child, void *data); obs-move-transition-2.5.7/move-value-filter.c000066400000000000000000002056211417746432500211760ustar00rootroot00000000000000#include "move-transition.h" #include #include #include #include #include #include #define TEXT_BUFFER_SIZE 256 #define VOLUME_SETTING "source_volume" #define VOLUME_MIN 0.0 #define VOLUME_MAX 100.0 #define VOLUME_STEP 1.0 static void load_properties(obs_properties_t *props_from, obs_data_array_t *array, obs_data_t *settings_to, obs_data_t *settings_from) { obs_property_t *prop_from = obs_properties_first(props_from); for (; prop_from != NULL; obs_property_next(&prop_from)) { const char *name = obs_property_name(prop_from); if (!obs_property_visible(prop_from)) continue; obs_data_t *setting = NULL; const size_t count = obs_data_array_count(array); for (size_t i = 0; i < count; i++) { obs_data_t *item2 = obs_data_array_item(array, i); const char *setting_name2 = obs_data_get_string(item2, S_SETTING_NAME); if (strcmp(setting_name2, name) == 0) { setting = item2; } } const enum obs_property_type prop_type = obs_property_get_type(prop_from); if (prop_type == OBS_PROPERTY_GROUP) { load_properties(obs_property_group_content(prop_from), array, settings_to, settings_from); } else if (prop_type == OBS_PROPERTY_INT) { if (!setting) { setting = obs_data_create(); obs_data_set_string(setting, S_SETTING_NAME, name); obs_data_array_push_back(array, setting); } obs_data_set_int(setting, S_VALUE_TYPE, MOVE_VALUE_INT); if (obs_data_has_default_value(settings_from, name)) obs_data_set_default_int( settings_to, name, obs_data_get_default_int(settings_from, name)); const long long to = obs_data_get_int(settings_to, name); obs_data_set_int(setting, S_SETTING_TO, to); const long long from = obs_data_get_int(settings_from, name); obs_data_set_int(setting, S_SETTING_FROM, from); } else if (prop_type == OBS_PROPERTY_FLOAT) { if (!setting) { setting = obs_data_create(); obs_data_set_string(setting, S_SETTING_NAME, name); obs_data_array_push_back(array, setting); } obs_data_set_int(setting, S_VALUE_TYPE, MOVE_VALUE_FLOAT); if (obs_data_has_default_value(settings_from, name)) obs_data_set_default_double( settings_to, name, obs_data_get_default_double( settings_from, name)); const double to = obs_data_get_double(settings_to, name); obs_data_set_double(setting, S_SETTING_TO, to); const double from = obs_data_get_double(settings_from, name); obs_data_set_double(setting, S_SETTING_FROM, from); } else if (prop_type == OBS_PROPERTY_COLOR || prop_type == OBS_PROPERTY_COLOR_ALPHA) { if (!setting) { setting = obs_data_create(); obs_data_set_string(setting, S_SETTING_NAME, name); obs_data_array_push_back(array, setting); } obs_data_set_int(setting, S_VALUE_TYPE, MOVE_VALUE_COLOR); if (obs_data_has_default_value(settings_from, name)) obs_data_set_default_int( settings_to, name, obs_data_get_default_int(settings_from, name)); obs_data_set_int(setting, S_SETTING_TO, obs_data_get_int(settings_to, name)); const long long from = obs_data_get_int(settings_from, name); obs_data_set_int(setting, S_SETTING_FROM, from); } } } void move_values_load_properties(struct move_value_info *move_value, obs_source_t *source, obs_data_t *settings) { if (source && source != move_value->source) { obs_properties_t *sps = obs_source_properties(source); size_t index = 0; while (index < obs_data_array_count(move_value->settings)) { obs_data_t *item = obs_data_array_item( move_value->settings, index); const char *setting_name = obs_data_get_string(item, S_SETTING_NAME); if (obs_properties_get(sps, setting_name) == NULL) { obs_data_array_erase(move_value->settings, index); } else { index++; } } obs_data_t *data_from = obs_source_get_settings(source); load_properties(sps, move_value->settings, settings, data_from); obs_data_release(data_from); obs_properties_destroy(sps); } else { while (obs_data_array_count(move_value->settings)) { obs_data_array_erase(move_value->settings, 0); } } } long long rand_between(long long a, long long b) { return b > a ? a + rand() % (b - a) : b + rand() % (a - b); } float rand_between_float(float a, float b) { return b > a ? a + (b - a) * (float)rand() / (float)RAND_MAX : b + (a - b) * (float)rand() / (float)RAND_MAX; } double rand_between_double(double a, double b) { return b > a ? a + (b - a) * (double)rand() / (double)RAND_MAX : b + (a - b) * (double)rand() / (double)RAND_MAX; } double parse_text(long long format_type, const char *format, const char *text) { double value = 0.0; if (format_type == MOVE_VALUE_FORMAT_FLOAT) { sscanf(text, format, &value); } else if (format_type == MOVE_VALUE_FORMAT_TIME) { char *pos; unsigned int sec = 0; unsigned int min = 0; unsigned int hour = 0; if ((pos = strstr(format, "%X")) || (pos = strstr(format, "%H:%M:%S"))) { if ((pos - format) < strlen(text)) sscanf(text + (pos - format), "%u:%u:%u", &hour, &min, &sec); } else if ((pos = strstr(format, "%R")) || (pos = strstr(format, "%H:%M"))) { if ((pos - format) < strlen(text)) sscanf(text + (pos - format), "%u:%u", &hour, &min); } else if (pos = strstr(format, "%M:%S")) { if ((pos - format) < strlen(text)) sscanf(text + (pos - format), "%u:%u", &min, &sec); } else { if (pos = strstr(format, "%S")) { sscanf(text + (pos - format), "%u", &sec); } if (pos = strstr(format, "%M")) { sscanf(text + (pos - format), "%u", &min); } if (pos = strstr(format, "%H")) { sscanf(text + (pos - format), "%u", &hour); } } value = hour * 3600 + min * 60 + sec; } else { value = strtod(text, NULL); } return value; } void move_value_start(struct move_value_info *move_value) { if (!move_value->filter && move_value->setting_filter_name && strlen(move_value->setting_filter_name)) { obs_source_t *parent = obs_filter_get_parent(move_value->source); if (parent) { move_value->filter = obs_source_get_filter_by_name( parent, move_value->setting_filter_name); } else { return; } } if (move_value->reverse) { move_value->running_duration = 0.0f; move_value->moving = true; return; } if (!move_value->custom_duration) move_value->duration = obs_frontend_get_transition_duration(); if (move_value->moving && move_value->enabled) { if (move_value->next_move_on == NEXT_MOVE_ON_HOTKEY && move_value->next_move_name && strcmp(move_value->next_move_name, NEXT_MOVE_REVERSE) == 0) { move_value->reverse = !move_value->reverse; move_value->running_duration = (float)(move_value->duration + move_value->start_delay + move_value->end_delay) / 1000.0f - move_value->running_duration; } return; } if (!move_value->setting_filter_name) { obs_source_update(move_value->source, NULL); } obs_source_t *source = move_value->setting_filter_name && strlen(move_value->setting_filter_name) ? move_value->filter : obs_filter_get_parent(move_value->source); obs_data_t *ss = obs_source_get_settings(source); if (move_value->settings) { obs_data_t *settings = obs_source_get_settings(move_value->source); move_values_load_properties(move_value, source, settings); obs_data_release(settings); move_value->running_duration = 0.0f; move_value->moving = true; } else if (move_value->value_type == MOVE_VALUE_INT) { if (strcmp(move_value->setting_name, VOLUME_SETTING) == 0) { move_value->int_from = obs_source_get_volume(source) * 100.0f; } else { move_value->int_from = obs_data_get_int(ss, move_value->setting_name); } if (move_value->move_value_type == MOVE_VALUE_TYPE_RANDOM) { move_value->int_to = rand_between(move_value->int_min, move_value->int_max); } else if (move_value->move_value_type == MOVE_VALUE_TYPE_SETTING_ADD) { move_value->int_to = move_value->int_from + move_value->int_value; } else { move_value->int_to = move_value->int_value; } move_value->running_duration = 0.0f; move_value->moving = true; } else if (move_value->value_type == MOVE_VALUE_FLOAT) { if (strcmp(move_value->setting_name, VOLUME_SETTING) == 0) { move_value->double_from = obs_source_get_volume(source) * 100.0f; } else { move_value->double_from = obs_data_get_double( ss, move_value->setting_name); } if (move_value->move_value_type == MOVE_VALUE_TYPE_RANDOM) { move_value->double_to = rand_between_double( move_value->double_min, move_value->double_max); } else if (move_value->move_value_type == MOVE_VALUE_TYPE_SETTING_ADD) { move_value->double_to = move_value->double_from + move_value->double_value; } else { move_value->double_to = move_value->double_value; } move_value->running_duration = 0.0f; move_value->moving = true; } else if (move_value->value_type == MOVE_VALUE_COLOR) { vec4_from_rgba(&move_value->color_from, (uint32_t)obs_data_get_int( ss, move_value->setting_name)); gs_float3_srgb_nonlinear_to_linear(move_value->color_from.ptr); if (move_value->move_value_type == MOVE_VALUE_TYPE_RANDOM) { move_value->color_to.w = rand_between_float(move_value->color_min.w, move_value->color_max.w); move_value->color_to.x = rand_between_float(move_value->color_min.x, move_value->color_max.x); move_value->color_to.y = rand_between_float(move_value->color_min.y, move_value->color_max.y); move_value->color_to.z = rand_between_float(move_value->color_min.z, move_value->color_max.z); } else if (move_value->move_value_type == MOVE_VALUE_TYPE_SETTING_ADD) { move_value->color_to.w = move_value->color_from.w + move_value->color_value.w; move_value->color_to.x = move_value->color_from.x + move_value->color_value.x; move_value->color_to.y = move_value->color_from.y + move_value->color_value.y; move_value->color_to.z = move_value->color_from.z + move_value->color_value.z; } else { vec4_copy(&move_value->color_to, &move_value->color_value); } gs_float3_srgb_nonlinear_to_linear(move_value->color_to.ptr); move_value->running_duration = 0.0f; move_value->moving = true; } else if (move_value->value_type == MOVE_VALUE_TEXT) { const char *text_from = obs_data_get_string(ss, move_value->setting_name); move_value->double_from = parse_text( move_value->format_type, move_value->format, text_from); if (move_value->move_value_type == MOVE_VALUE_TYPE_RANDOM) { move_value->double_to = rand_between_double( move_value->double_min, move_value->double_max); } else if (move_value->move_value_type == MOVE_VALUE_TYPE_SETTING_ADD) { move_value->double_to = move_value->double_from + move_value->double_value; } else if (move_value->move_value_type == MOVE_VALUE_TYPE_TYPING) { bfree(move_value->text_from); move_value->text_from = bstrdup(text_from); move_value->text_from_len = strlen(text_from); move_value->text_step = 0; move_value->text_same = 0; while (move_value->text_same < move_value->text_from_len && move_value->text_same < move_value->text_to_len && move_value->text_from[move_value->text_same] == move_value ->text_to[move_value->text_same]) move_value->text_same++; move_value->text_steps = (move_value->text_from_len - move_value->text_same) + (move_value->text_to_len - move_value->text_same); } else { move_value->double_to = move_value->double_value; } move_value->running_duration = 0.0f; move_value->moving = true; } else { if (strcmp(move_value->setting_name, VOLUME_SETTING) == 0) { move_value->int_from = obs_source_get_volume(source) * 100.0f; move_value->double_from = obs_source_get_volume(source) * 100.0; } else { move_value->int_from = obs_data_get_int(ss, move_value->setting_name); move_value->double_from = obs_data_get_double( ss, move_value->setting_name); } move_value->int_to = move_value->int_value; move_value->double_to = move_value->double_value; move_value->running_duration = 0.0f; move_value->moving = true; } if (move_value->enabled_match_moving && obs_source_enabled(move_value->source) != move_value->moving) { move_value->enabled = move_value->moving; obs_source_set_enabled(move_value->source, move_value->moving); } obs_data_release(ss); if (move_value->moving && move_value->simultaneous_move_name && strlen(move_value->simultaneous_move_name) && (!move_value->filter_name || strcmp(move_value->filter_name, move_value->simultaneous_move_name) != 0)) { obs_source_t *parent = obs_filter_get_parent(move_value->source); if (parent) { obs_source_t *filter = obs_source_get_filter_by_name( parent, move_value->simultaneous_move_name); if (filter && (strcmp(obs_source_get_unversioned_id(filter), MOVE_VALUE_FILTER_ID) == 0 || strcmp(obs_source_get_unversioned_id(filter), MOVE_AUDIO_VALUE_FILTER_ID) == 0)) { struct move_value_info *filter_data = obs_obj_get_data(filter); if (!filter_data->moving) { move_value_start(filter_data); } } obs_source_release(filter); } } } bool move_value_start_button(obs_properties_t *props, obs_property_t *property, void *data) { struct move_value_info *move_value = data; move_value_start(move_value); UNUSED_PARAMETER(props); UNUSED_PARAMETER(property); return false; } void move_value_start_hotkey(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey, bool pressed) { if (!pressed) return; struct move_value_info *move_value = data; if (move_value->next_move_on != NEXT_MOVE_ON_HOTKEY || !move_value->next_move_name || !strlen(move_value->next_move_name)) { move_value_start(move_value); return; } if (!move_value->filters_done.num) { move_value_start(move_value); da_push_back(move_value->filters_done, &move_value->source); return; } obs_source_t *parent = obs_filter_get_parent(move_value->source); if (!parent) return; struct move_value_info *filter_data = move_value; size_t i = 0; while (i < move_value->filters_done.num) { if (!filter_data->next_move_name || !strlen(filter_data->next_move_name)) { move_value_start(move_value); move_value->filters_done.num = 0; da_push_back(move_value->filters_done, &move_value->source); return; } obs_source_t *filter = obs_source_get_filter_by_name( parent, filter_data->next_move_name); if (!filter || (strcmp(obs_source_get_unversioned_id(filter), MOVE_VALUE_FILTER_ID) != 0 && strcmp(obs_source_get_unversioned_id(filter), MOVE_AUDIO_VALUE_FILTER_ID) != 0)) { obs_source_release(filter); move_value_start(move_value); move_value->filters_done.num = 0; da_push_back(move_value->filters_done, &move_value->source); return; } if (filter_data->moving && obs_source_enabled(filter_data->source) && (filter_data->reverse || !filter_data->next_move_name || strcmp(filter_data->next_move_name, NEXT_MOVE_REVERSE) != 0)) { filter_data->moving = false; if (filter_data->enabled_match_moving) obs_source_set_enabled(filter_data->source, false); } if (filter_data->next_move_on != NEXT_MOVE_ON_HOTKEY) { filter_data = obs_obj_get_data(filter); da_push_back(move_value->filters_done, &filter_data->source); } else { filter_data = obs_obj_get_data(filter); } obs_source_release(filter); i++; } for (i = 0; i < move_value->filters_done.num; i++) { if (move_value->filters_done.array[i] == filter_data->source) { move_value_start(move_value); move_value->filters_done.num = 0; da_push_back(move_value->filters_done, &move_value->source); return; } } move_value_start(filter_data); da_push_back(move_value->filters_done, &filter_data->source); UNUSED_PARAMETER(id); UNUSED_PARAMETER(hotkey); } void move_value_update(void *data, obs_data_t *settings) { struct move_value_info *move_value = data; obs_source_t *parent = obs_filter_get_parent(move_value->source); const char *filter_name = obs_source_get_name(move_value->source); if (!move_value->filter_name || strcmp(move_value->filter_name, filter_name) != 0) { bfree(move_value->filter_name); move_value->filter_name = NULL; if (move_value->move_start_hotkey != OBS_INVALID_HOTKEY_ID) { obs_hotkey_unregister(move_value->move_start_hotkey); move_value->move_start_hotkey = OBS_INVALID_HOTKEY_ID; } if (parent) { move_value->filter_name = bstrdup(filter_name); move_value->move_start_hotkey = obs_hotkey_register_source( parent, filter_name, filter_name, move_value_start_hotkey, data); } } const char *setting_filter_name = obs_data_get_string(settings, S_FILTER); if (!move_value->setting_filter_name || strcmp(move_value->setting_filter_name, setting_filter_name) != 0) { obs_source_release(move_value->filter); move_value->filter = NULL; if (parent) { bfree(move_value->setting_filter_name); move_value->setting_filter_name = bstrdup(setting_filter_name); move_value->filter = obs_source_get_filter_by_name( parent, setting_filter_name); } } const char *setting_name = obs_data_get_string(settings, S_SETTING_NAME); if (!move_value->setting_name || strcmp(move_value->setting_name, setting_name) != 0) { bfree(move_value->setting_name); move_value->setting_name = bstrdup(setting_name); } if (obs_data_has_user_value(settings, S_SINGLE_SETTING)) { obs_data_set_int(settings, S_MOVE_VALUE_TYPE, obs_data_get_bool(settings, S_SINGLE_SETTING) ? MOVE_VALUE_TYPE_SINGLE_SETTING : MOVE_VALUE_TYPE_SETTINGS); obs_data_unset_user_value(settings, S_SINGLE_SETTING); } if (obs_data_get_int(settings, S_MOVE_VALUE_TYPE) != MOVE_VALUE_TYPE_SETTINGS) { obs_data_array_release(move_value->settings); move_value->settings = NULL; } else if (parent) { if (!move_value->settings) move_value->settings = obs_data_array_create(); obs_source_t *source = move_value->setting_filter_name && strlen(move_value->setting_filter_name) ? move_value->filter : parent; move_values_load_properties(move_value, source, settings); } move_value->move_value_type = obs_data_get_int(settings, S_MOVE_VALUE_TYPE); move_value->value_type = obs_data_get_int(settings, S_VALUE_TYPE); move_value->format_type = obs_data_get_int(settings, S_SETTING_FORMAT_TYPE); char *format = obs_data_get_string(settings, S_SETTING_FORMAT); if (move_value->format_type == MOVE_VALUE_FORMAT_FLOAT && strlen(format) == 0) { format = "%f"; obs_data_set_string(settings, S_SETTING_FORMAT, format); } if (move_value->format_type == MOVE_VALUE_FORMAT_TIME && strlen(format) == 0) { format = "%X"; obs_data_set_string(settings, S_SETTING_FORMAT, format); } if (!move_value->format || strcmp(move_value->format, format) != 0) { bfree(move_value->format); move_value->format = bstrdup(format); } move_value->decimals = obs_data_get_int(settings, S_SETTING_DECIMALS); move_value->int_value = obs_data_get_int(settings, S_SETTING_INT); move_value->int_min = obs_data_get_int(settings, S_SETTING_INT_MIN); move_value->int_max = obs_data_get_int(settings, S_SETTING_INT_MAX); move_value->double_value = obs_data_get_double(settings, S_SETTING_FLOAT); move_value->double_min = obs_data_get_double(settings, S_SETTING_FLOAT_MIN); move_value->double_max = obs_data_get_double(settings, S_SETTING_FLOAT_MAX); vec4_from_rgba(&move_value->color_value, (uint32_t)obs_data_get_int(settings, S_SETTING_COLOR)); vec4_from_rgba(&move_value->color_min, (uint32_t)obs_data_get_int(settings, S_SETTING_COLOR_MIN)); vec4_from_rgba(&move_value->color_max, (uint32_t)obs_data_get_int(settings, S_SETTING_COLOR_MAX)); const char *text_to = obs_data_get_string(settings, S_SETTING_TEXT); if (!move_value->text_to || strcmp(move_value->text_to, text_to) != 0) { bfree(move_value->text_to); move_value->text_to = bstrdup(text_to); move_value->text_to_len = strlen(text_to); } move_value->custom_duration = obs_data_get_bool(settings, S_CUSTOM_DURATION); if (move_value->custom_duration) move_value->duration = obs_data_get_int(settings, S_DURATION); move_value->start_delay = obs_data_get_int(settings, S_START_DELAY); move_value->end_delay = obs_data_get_int(settings, S_END_DELAY); move_value->easing = obs_data_get_int(settings, S_EASING_MATCH); move_value->easing_function = obs_data_get_int(settings, S_EASING_FUNCTION_MATCH); move_value->enabled_match_moving = obs_data_get_bool(settings, S_ENABLED_MATCH_MOVING); if (move_value->enabled_match_moving && !move_value->moving && obs_source_enabled(move_value->source)) move_value_start(move_value); move_value->start_trigger = (uint32_t)obs_data_get_int(settings, S_START_TRIGGER); move_value->stop_trigger = (uint32_t)obs_data_get_int(settings, S_STOP_TRIGGER); const char *simultaneous_move_name = obs_data_get_string(settings, S_SIMULTANEOUS_MOVE); if (!move_value->simultaneous_move_name || strcmp(move_value->simultaneous_move_name, simultaneous_move_name) != 0) { bfree(move_value->simultaneous_move_name); move_value->simultaneous_move_name = bstrdup(simultaneous_move_name); } const char *next_move_name = obs_data_get_string(settings, S_NEXT_MOVE); if (!move_value->next_move_name || strcmp(move_value->next_move_name, next_move_name) != 0) { bfree(move_value->next_move_name); move_value->next_move_name = bstrdup(next_move_name); move_value->reverse = false; } move_value->next_move_on = obs_data_get_int(settings, S_NEXT_MOVE_ON); if (move_value->start_trigger == START_TRIGGER_LOAD) { move_value_start(move_value); } } static void *move_value_create(obs_data_t *settings, obs_source_t *source) { struct move_value_info *move_value = bzalloc(sizeof(struct move_value_info)); move_value->source = source; move_value->move_start_hotkey = OBS_INVALID_HOTKEY_ID; move_value_update(move_value, settings); return move_value; } static void move_value_destroy(void *data) { struct move_value_info *move_value = data; obs_source_release(move_value->filter); move_value->filter = NULL; if (move_value->move_start_hotkey != OBS_INVALID_HOTKEY_ID) obs_hotkey_unregister(move_value->move_start_hotkey); bfree(move_value->format); bfree(move_value->text_from); bfree(move_value->text_to); bfree(move_value->filter_name); bfree(move_value->setting_filter_name); bfree(move_value->setting_name); bfree(move_value->simultaneous_move_name); bfree(move_value->next_move_name); obs_data_array_release(move_value->settings); da_free(move_value->filters_done); bfree(move_value); } void prop_list_add_easings(obs_property_t *p); void prop_list_add_easing_functions(obs_property_t *p); void prop_list_add_filter(obs_source_t *parent, obs_source_t *child, void *data) { UNUSED_PARAMETER(parent); obs_property_t *p = data; const char *name = obs_source_get_name(child); obs_property_list_add_string(p, name, name); } void prop_list_add_move_value_filter(obs_source_t *parent, obs_source_t *child, void *data) { UNUSED_PARAMETER(parent); if (strcmp(obs_source_get_unversioned_id(child), MOVE_VALUE_FILTER_ID) != 0 && strcmp(obs_source_get_unversioned_id(child), MOVE_AUDIO_VALUE_FILTER_ID) != 0) return; obs_property_t *p = data; const char *name = obs_source_get_name(child); obs_property_list_add_string(p, name, name); } bool move_value_get_value(obs_properties_t *props, obs_property_t *property, void *data) { UNUSED_PARAMETER(props); UNUSED_PARAMETER(property); struct move_value_info *move_value = data; bool settings_changed = false; obs_source_t *source = move_value->filter ? move_value->filter : obs_filter_get_parent(move_value->source); if (source == NULL || source == move_value->source) return settings_changed; obs_data_t *settings = obs_source_get_settings(move_value->source); if (strcmp(move_value->setting_name, VOLUME_SETTING) == 0) { const double value = (double)obs_source_get_volume(source) * 100.0; obs_data_set_double(settings, S_SETTING_FLOAT, value); obs_data_set_double(settings, S_SETTING_FLOAT_MIN, value); obs_data_set_double(settings, S_SETTING_FLOAT_MAX, value); obs_data_release(settings); return true; } obs_properties_t *sps = obs_source_properties(source); obs_property_t *sp = obs_properties_get(sps, move_value->setting_name); obs_data_t *ss = obs_source_get_settings(source); const enum obs_property_type prop_type = obs_property_get_type(sp); if (prop_type == OBS_PROPERTY_INT) { const long long value = obs_data_get_int(ss, move_value->setting_name); obs_data_set_int(settings, S_SETTING_INT, value); obs_data_set_int(settings, S_SETTING_INT_MIN, value); obs_data_set_int(settings, S_SETTING_INT_MAX, value); settings_changed = true; } else if (prop_type == OBS_PROPERTY_FLOAT) { const double value = obs_data_get_double(ss, move_value->setting_name); obs_data_set_double(settings, S_SETTING_FLOAT, value); obs_data_set_double(settings, S_SETTING_FLOAT_MIN, value); obs_data_set_double(settings, S_SETTING_FLOAT_MAX, value); settings_changed = true; } else if (prop_type == OBS_PROPERTY_COLOR || prop_type == OBS_PROPERTY_COLOR_ALPHA) { const long long color = obs_data_get_int(ss, move_value->setting_name); obs_data_set_int(settings, S_SETTING_COLOR, color); obs_data_set_int(settings, S_SETTING_COLOR_MIN, color); obs_data_set_int(settings, S_SETTING_COLOR_MAX, color); settings_changed = true; } else if (prop_type == OBS_PROPERTY_TEXT) { const char *text = obs_data_get_string(ss, move_value->setting_name); if (move_value->move_value_type == MOVE_VALUE_TYPE_TYPING) { obs_data_set_string(settings, S_SETTING_TEXT, text); } else { const double value = parse_text(move_value->format_type, move_value->format, text); obs_data_set_double(settings, S_SETTING_FLOAT, value); obs_data_set_double(settings, S_SETTING_FLOAT_MIN, value); obs_data_set_double(settings, S_SETTING_FLOAT_MAX, value); } settings_changed = true; } obs_data_release(settings); obs_properties_destroy(sps); return settings_changed; } bool move_value_get_values(obs_properties_t *props, obs_property_t *property, void *data) { struct move_value_info *move_value = data; obs_source_t *source = move_value->filter ? move_value->filter : obs_filter_get_parent(move_value->source); if (source == NULL || source == move_value->source) return false; obs_data_t *settings = obs_source_get_settings(move_value->source); obs_data_t *ss = obs_source_get_settings(source); const size_t count = obs_data_array_count(move_value->settings); for (size_t i = 0; i < count; i++) { obs_data_t *item = obs_data_array_item(move_value->settings, i); const char *name = obs_data_get_string(item, S_SETTING_NAME); const long long value_type = obs_data_get_int(item, S_VALUE_TYPE); if (value_type == MOVE_VALUE_INT) { const long long value = obs_data_get_int(ss, name); obs_data_set_int(settings, name, value); } else if (value_type == MOVE_VALUE_FLOAT) { const double value = obs_data_get_double(ss, name); obs_data_set_double(settings, name, value); } else if (value_type == MOVE_VALUE_COLOR) { const long long color = obs_data_get_int(ss, name); obs_data_set_int(settings, name, color); } else if (value_type == MOVE_VALUE_TEXT) { const char *text = obs_data_get_string(ss, name); const double value = parse_text(move_value->format_type, move_value->format, text); obs_data_set_double(settings, name, value); } } if (count > 0) { obs_properties_t *sps = obs_source_properties(source); load_properties(sps, move_value->settings, settings, ss); obs_properties_destroy(sps); } obs_data_release(ss); obs_data_release(settings); return count > 0; } void copy_properties(obs_properties_t *props_from, obs_properties_t *props_to, obs_data_t *data_from, obs_data_t *data_to, obs_property_t *setting_list) { obs_property_t *prop_from = obs_properties_first(props_from); for (; prop_from != NULL; obs_property_next(&prop_from)) { const char *name = obs_property_name(prop_from); const char *description = obs_property_description(prop_from); if (!obs_property_visible(prop_from)) continue; obs_property_t *prop_to = NULL; const enum obs_property_type prop_type = obs_property_get_type(prop_from); if (prop_type == OBS_PROPERTY_GROUP) { obs_properties_t *group_to = obs_properties_create(); copy_properties(obs_property_group_content(prop_from), group_to, data_from, data_to, setting_list); if (obs_properties_first(group_to) == NULL) { obs_properties_destroy(group_to); } else { prop_to = obs_properties_add_group( props_to, name, description, obs_property_group_type(prop_from), group_to); } } else if (prop_type == OBS_PROPERTY_INT) { obs_property_list_add_string(setting_list, description, name); if (obs_property_int_type(prop_from) == OBS_NUMBER_SLIDER) { prop_to = obs_properties_add_int_slider( props_to, name, description, obs_property_int_min(prop_from), obs_property_int_max(prop_from), obs_property_int_step(prop_from)); } else { prop_to = obs_properties_add_int( props_to, name, description, obs_property_int_min(prop_from), obs_property_int_max(prop_from), obs_property_int_step(prop_from)); } if (obs_data_has_default_value(data_from, name)) obs_data_set_default_int( data_to, name, obs_data_get_default_int(data_from, name)); obs_property_int_set_suffix( prop_to, obs_property_int_suffix(prop_from)); } else if (prop_type == OBS_PROPERTY_FLOAT) { obs_property_list_add_string(setting_list, description, name); if (obs_property_float_type(prop_from) == OBS_NUMBER_SLIDER) { prop_to = obs_properties_add_float_slider( props_to, name, description, obs_property_float_min(prop_from), obs_property_float_max(prop_from), obs_property_float_step(prop_from)); } else { prop_to = obs_properties_add_float( props_to, name, description, obs_property_float_min(prop_from), obs_property_float_max(prop_from), obs_property_float_step(prop_from)); } if (obs_data_has_default_value(data_from, name)) obs_data_set_default_double( data_to, name, obs_data_get_default_double(data_from, name)); obs_property_float_set_suffix( prop_to, obs_property_float_suffix(prop_from)); } else if (prop_type == OBS_PROPERTY_COLOR || prop_type == OBS_PROPERTY_COLOR_ALPHA) { obs_property_list_add_string(setting_list, description, name); prop_to = obs_properties_add_color(props_to, name, description); if (obs_data_has_default_value(data_from, name)) obs_data_set_default_int( data_to, name, obs_data_get_default_int(data_from, name)); } else if (prop_type == OBS_PROPERTY_TEXT) { obs_property_list_add_string(setting_list, description, name); } } } bool move_value_filter_changed(void *data, obs_properties_t *props, obs_property_t *property, obs_data_t *settings) { UNUSED_PARAMETER(property); struct move_value_info *move_value = data; bool refresh = false; obs_source_t *parent = obs_filter_get_parent(move_value->source); const char *filter_name = obs_data_get_string(settings, S_FILTER); if (!move_value->setting_filter_name || strcmp(move_value->setting_filter_name, filter_name) != 0 || (!move_value->filter && strlen(filter_name))) { bfree(move_value->setting_filter_name); move_value->setting_filter_name = bstrdup(filter_name); obs_source_release(move_value->filter); move_value->filter = obs_source_get_filter_by_name(parent, filter_name); } refresh = true; obs_property_t *p = obs_properties_get(props, S_SETTING_NAME); obs_property_list_clear(p); obs_property_list_add_string(p, obs_module_text("Setting.None"), ""); obs_property_t *ps = obs_properties_get(props, S_SETTINGS); obs_properties_t *g = obs_property_group_content(ps); obs_property_t *i = obs_properties_first(g); while (i != NULL) { const char *name = obs_property_name(i); obs_property_next(&i); if (strcmp(name, "values_get") == 0) continue; obs_properties_remove_by_name(g, name); } obs_source_t *source = move_value->filter ? move_value->filter : parent; obs_data_t *s = obs_source_get_settings(source); if (!s || source == move_value->source) return refresh; if (obs_source_get_type(source) == OBS_SOURCE_TYPE_INPUT && (obs_source_get_output_flags(source) & OBS_SOURCE_AUDIO)) obs_property_list_add_string( p, obs_module_text("Setting.Volume"), VOLUME_SETTING); obs_properties_t *sps = obs_source_properties(source); copy_properties(sps, g, s, settings, p); obs_properties_destroy(sps); obs_data_release(s); return refresh; } bool move_value_format_type_changed(void *data, obs_properties_t *props, obs_property_t *property, obs_data_t *settings) { obs_property_t *prop_format = obs_properties_get(props, S_SETTING_FORMAT); obs_property_t *prop_decimals = obs_properties_get(props, S_SETTING_DECIMALS); obs_property_set_visible(prop_format, false); obs_property_set_visible(prop_decimals, false); if (obs_data_get_int(settings, S_VALUE_TYPE) == MOVE_VALUE_TEXT && obs_data_get_int(settings, S_MOVE_VALUE_TYPE) != MOVE_VALUE_TYPE_TYPING) { const long long format_type = obs_data_get_int(settings, S_SETTING_FORMAT_TYPE); if (format_type == MOVE_VALUE_FORMAT_DECIMALS) { obs_property_set_visible(prop_decimals, true); } else { obs_property_set_visible(prop_format, true); } } return true; } bool move_value_setting_changed(void *data, obs_properties_t *props, obs_property_t *property, obs_data_t *settings) { UNUSED_PARAMETER(property); struct move_value_info *move_value = data; bool refresh = false; const char *setting_name = obs_data_get_string(settings, S_SETTING_NAME); if (!move_value->setting_name || strcmp(move_value->setting_name, setting_name) != 0) { refresh = true; bfree(move_value->setting_name); move_value->setting_name = bstrdup(setting_name); } obs_source_t *source = move_value->filter ? move_value->filter : obs_filter_get_parent(move_value->source); if (source == move_value->source) return refresh; obs_property_t *prop_int = obs_properties_get(props, S_SETTING_INT); obs_property_t *prop_int_min = obs_properties_get(props, S_SETTING_INT_MIN); obs_property_t *prop_int_max = obs_properties_get(props, S_SETTING_INT_MAX); obs_property_t *prop_float = obs_properties_get(props, S_SETTING_FLOAT); obs_property_t *prop_float_min = obs_properties_get(props, S_SETTING_FLOAT_MIN); obs_property_t *prop_float_max = obs_properties_get(props, S_SETTING_FLOAT_MAX); obs_property_t *prop_format_type = obs_properties_get(props, S_SETTING_FORMAT_TYPE); obs_property_t *prop_color = obs_properties_get(props, S_SETTING_COLOR); obs_property_t *prop_color_min = obs_properties_get(props, S_SETTING_COLOR_MIN); obs_property_t *prop_color_max = obs_properties_get(props, S_SETTING_COLOR_MAX); obs_property_t *prop_text = obs_properties_get(props, S_SETTING_TEXT); obs_property_set_visible(prop_int, false); obs_property_set_visible(prop_int_min, false); obs_property_set_visible(prop_int_max, false); obs_property_set_visible(prop_float, false); obs_property_set_visible(prop_float_min, false); obs_property_set_visible(prop_float_max, false); obs_property_set_visible(prop_format_type, false); obs_property_set_visible(prop_color, false); obs_property_set_visible(prop_color_min, false); obs_property_set_visible(prop_color_max, false); obs_property_set_visible(prop_text, false); const long long move_value_type = obs_data_get_int(settings, S_MOVE_VALUE_TYPE); if (strcmp(move_value->setting_name, VOLUME_SETTING) == 0) { if (move_value_type == MOVE_VALUE_TYPE_SINGLE_SETTING) { obs_property_set_visible(prop_float, true); obs_property_float_set_limits(prop_float, VOLUME_MIN, VOLUME_MAX, VOLUME_STEP); obs_property_float_set_suffix(prop_float, "%"); if (refresh) obs_data_set_double( settings, S_SETTING_FLOAT, obs_source_get_volume(source) * 100.0); } else if (move_value_type == MOVE_VALUE_TYPE_SETTING_ADD) { obs_property_set_visible(prop_float, true); obs_property_float_set_limits(prop_float, -VOLUME_MAX, VOLUME_MAX, VOLUME_STEP); obs_property_float_set_suffix(prop_float, "%"); } else if (move_value_type == MOVE_VALUE_TYPE_RANDOM) { obs_property_set_visible(prop_float_min, true); obs_property_set_visible(prop_float_max, true); obs_property_float_set_limits(prop_float_min, VOLUME_MIN, VOLUME_MAX, VOLUME_STEP); obs_property_float_set_limits(prop_float_max, VOLUME_MIN, VOLUME_MAX, VOLUME_STEP); obs_property_float_set_suffix(prop_float_min, "%"); obs_property_float_set_suffix(prop_float_max, "%"); if (refresh) { obs_data_set_double( settings, S_SETTING_FLOAT_MIN, obs_source_get_volume(source) * 100.0); obs_data_set_double( settings, S_SETTING_FLOAT_MAX, obs_source_get_volume(source) * 100.0); } } obs_data_set_int(settings, S_VALUE_TYPE, MOVE_VALUE_FLOAT); return true; } obs_data_t *ss = obs_source_get_settings(source); obs_properties_t *sps = obs_source_properties(source); obs_property_t *sp = obs_properties_get(sps, setting_name); const enum obs_property_type prop_type = obs_property_get_type(sp); if (prop_type == OBS_PROPERTY_INT) { if (move_value_type == MOVE_VALUE_TYPE_SINGLE_SETTING) { obs_property_set_visible(prop_int, true); obs_property_int_set_limits(prop_int, obs_property_int_min(sp), obs_property_int_max(sp), obs_property_int_step(sp)); obs_property_int_set_suffix( prop_int, obs_property_int_suffix(sp)); if (refresh) obs_data_set_int( settings, S_SETTING_INT, obs_data_get_int(ss, setting_name)); } else if (move_value_type == MOVE_VALUE_TYPE_SETTING_ADD) { obs_property_set_visible(prop_int, true); obs_property_int_set_limits(prop_int, -1000, 1000, obs_property_int_step(sp)); obs_property_int_set_suffix( prop_int, obs_property_int_suffix(sp)); } else if (move_value_type == MOVE_VALUE_TYPE_RANDOM) { obs_property_set_visible(prop_int_min, true); obs_property_set_visible(prop_int_max, true); obs_property_int_set_limits(prop_int_min, obs_property_int_min(sp), obs_property_int_max(sp), obs_property_int_step(sp)); obs_property_int_set_limits(prop_int_max, obs_property_int_min(sp), obs_property_int_max(sp), obs_property_int_step(sp)); obs_property_int_set_suffix( prop_int_min, obs_property_int_suffix(sp)); obs_property_int_set_suffix( prop_int_max, obs_property_int_suffix(sp)); if (refresh) { obs_data_set_int( settings, S_SETTING_INT_MIN, obs_data_get_int(ss, setting_name)); obs_data_set_int( settings, S_SETTING_INT_MAX, obs_data_get_int(ss, setting_name)); } } obs_data_set_int(settings, S_VALUE_TYPE, MOVE_VALUE_INT); } else if (prop_type == OBS_PROPERTY_FLOAT) { if (move_value_type == MOVE_VALUE_TYPE_SINGLE_SETTING) { obs_property_set_visible(prop_float, true); obs_property_float_set_limits( prop_float, obs_property_float_min(sp), obs_property_float_max(sp), obs_property_float_step(sp)); obs_property_float_set_suffix( prop_float, obs_property_float_suffix(sp)); if (refresh) obs_data_set_double( settings, S_SETTING_FLOAT, obs_data_get_double(ss, setting_name)); } else if (move_value_type == MOVE_VALUE_TYPE_SETTING_ADD) { obs_property_set_visible(prop_float, true); obs_property_float_set_limits( prop_float, -1000.0, 1000.0, obs_property_float_step(sp)); obs_property_float_set_suffix( prop_float, obs_property_float_suffix(sp)); } else if (move_value_type == MOVE_VALUE_TYPE_RANDOM) { obs_property_set_visible(prop_float_min, true); obs_property_set_visible(prop_float_max, true); obs_property_float_set_limits( prop_float_min, obs_property_float_min(sp), obs_property_float_max(sp), obs_property_float_step(sp)); obs_property_float_set_limits( prop_float_max, obs_property_float_min(sp), obs_property_float_max(sp), obs_property_float_step(sp)); obs_property_float_set_suffix( prop_float_min, obs_property_float_suffix(sp)); obs_property_float_set_suffix( prop_float_max, obs_property_float_suffix(sp)); if (refresh) { obs_data_set_double( settings, S_SETTING_FLOAT_MIN, obs_data_get_double(ss, setting_name)); obs_data_set_double( settings, S_SETTING_FLOAT_MAX, obs_data_get_double(ss, setting_name)); } } obs_data_set_int(settings, S_VALUE_TYPE, MOVE_VALUE_FLOAT); } else if (prop_type == OBS_PROPERTY_COLOR || prop_type == OBS_PROPERTY_COLOR_ALPHA) { if (move_value_type == MOVE_VALUE_TYPE_SINGLE_SETTING) { obs_property_set_visible(prop_color, true); if (refresh) obs_data_set_int( settings, S_SETTING_COLOR, obs_data_get_int(ss, setting_name)); } else if (move_value_type == MOVE_VALUE_TYPE_SETTING_ADD) { obs_property_set_visible(prop_color, true); } else if (move_value_type == MOVE_VALUE_TYPE_RANDOM) { obs_property_set_visible(prop_color_min, true); obs_property_set_visible(prop_color_max, true); if (refresh) { obs_data_set_int( settings, S_SETTING_COLOR_MIN, obs_data_get_int(ss, setting_name)); obs_data_set_int( settings, S_SETTING_COLOR_MAX, obs_data_get_int(ss, setting_name)); } } obs_data_set_int(settings, S_VALUE_TYPE, MOVE_VALUE_COLOR); } else if (prop_type == OBS_PROPERTY_TEXT) { if (move_value_type == MOVE_VALUE_TYPE_SINGLE_SETTING) { obs_property_set_visible(prop_format_type, true); obs_property_set_visible(prop_float, true); obs_property_float_set_limits(prop_float, -DBL_MAX, DBL_MAX, 1.0); obs_property_float_set_suffix(prop_float, NULL); if (refresh) { const char *text_val = obs_data_get_string(ss, setting_name); const double val = strtod(text_val, NULL); obs_data_set_double(settings, S_SETTING_FLOAT, val); } } else if (move_value_type == MOVE_VALUE_TYPE_SETTING_ADD) { obs_property_set_visible(prop_format_type, true); obs_property_set_visible(prop_float, true); obs_property_float_set_limits(prop_float, -DBL_MAX, DBL_MAX, 1.0); obs_property_float_set_suffix(prop_float, NULL); } else if (move_value_type == MOVE_VALUE_TYPE_RANDOM) { obs_property_set_visible(prop_format_type, true); obs_property_set_visible(prop_float_min, true); obs_property_set_visible(prop_float_max, true); obs_property_float_set_limits(prop_float_min, -DBL_MAX, DBL_MAX, 1.0); obs_property_float_set_limits(prop_float_max, -DBL_MAX, DBL_MAX, 1.0); obs_property_float_set_suffix(prop_float_min, NULL); obs_property_float_set_suffix(prop_float_max, NULL); if (refresh) { const char *text_val = obs_data_get_string(ss, setting_name); const double val = strtod(text_val, NULL); obs_data_set_double(settings, S_SETTING_FLOAT_MIN, val); obs_data_set_double(settings, S_SETTING_FLOAT_MAX, val); } } else if (move_value_type == MOVE_VALUE_TYPE_TYPING) { obs_property_set_visible(prop_text, true); } obs_data_set_int(settings, S_VALUE_TYPE, MOVE_VALUE_TEXT); } else { obs_data_set_int(settings, S_VALUE_TYPE, MOVE_VALUE_UNKNOWN); } obs_data_release(ss); obs_properties_destroy(sps); move_value_format_type_changed(data, props, property, settings); return true; } bool move_value_type_changed(void *data, obs_properties_t *props, obs_property_t *property, obs_data_t *settings) { bool refresh = false; const long long move_value_type = obs_data_get_int(settings, S_MOVE_VALUE_TYPE); obs_property_t *p = obs_properties_get(props, S_SETTING_VALUE); if (obs_property_visible(p) != (move_value_type != MOVE_VALUE_TYPE_SETTINGS)) { obs_property_set_visible(p, move_value_type != MOVE_VALUE_TYPE_SETTINGS); refresh = true; } p = obs_properties_get(props, S_SETTINGS); if (obs_property_visible(p) != (move_value_type == MOVE_VALUE_TYPE_SETTINGS)) { obs_property_set_visible(p, move_value_type == MOVE_VALUE_TYPE_SETTINGS); refresh = true; } return move_value_setting_changed(data, props, property, settings) || refresh; } bool move_value_decimals_changed(void *data, obs_properties_t *props, obs_property_t *property, obs_data_t *settings) { const int decimals = (int)obs_data_get_int(settings, S_SETTING_DECIMALS); const double step = pow(10.0, -1.0 * (double)decimals); obs_property_t *prop_float = obs_properties_get(props, S_SETTING_FLOAT); obs_property_t *prop_float_min = obs_properties_get(props, S_SETTING_FLOAT_MIN); obs_property_t *prop_float_max = obs_properties_get(props, S_SETTING_FLOAT_MAX); obs_property_float_set_limits(prop_float, -DBL_MAX, DBL_MAX, step); obs_property_float_set_limits(prop_float_min, -DBL_MAX, DBL_MAX, step); obs_property_float_set_limits(prop_float_max, -DBL_MAX, DBL_MAX, step); return true; } static obs_properties_t *move_value_properties(void *data) { obs_properties_t *ppts = obs_properties_create(); const struct move_value_info *move_value = data; obs_source_t *parent = obs_filter_get_parent(move_value->source); obs_property_t *p = obs_properties_add_list(ppts, S_FILTER, obs_module_text("Filter"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, obs_module_text("Filter.None"), ""); obs_source_enum_filters(parent, prop_list_add_filter, p); obs_property_set_modified_callback2(p, move_value_filter_changed, data); p = obs_properties_add_list(ppts, S_MOVE_VALUE_TYPE, obs_module_text("MoveValueType"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int( p, obs_module_text("MoveValueType.SingleSetting"), MOVE_VALUE_TYPE_SINGLE_SETTING); obs_property_list_add_int(p, obs_module_text("MoveValueType.Settings"), MOVE_VALUE_TYPE_SETTINGS); obs_property_list_add_int(p, obs_module_text("MoveValueType.Random"), MOVE_VALUE_TYPE_RANDOM); obs_property_list_add_int(p, obs_module_text("MoveValueType.SettingAdd"), MOVE_VALUE_TYPE_SETTING_ADD); obs_property_list_add_int(p, obs_module_text("MoveValueType.Type"), MOVE_VALUE_TYPE_TYPING); obs_property_set_modified_callback2(p, move_value_type_changed, data); obs_properties_t *setting_value = obs_properties_create(); p = obs_properties_add_list(setting_value, S_SETTING_NAME, obs_module_text("Setting"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, obs_module_text("Setting.None"), ""); obs_property_set_modified_callback2(p, move_value_setting_changed, data); p = obs_properties_add_list(setting_value, S_SETTING_FORMAT_TYPE, obs_module_text("FormatType"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("FormatType.Decimals"), MOVE_VALUE_FORMAT_DECIMALS); obs_property_list_add_int(p, obs_module_text("FormatType.Float"), MOVE_VALUE_FORMAT_FLOAT); obs_property_list_add_int(p, obs_module_text("FormatType.Time"), MOVE_VALUE_FORMAT_TIME); obs_property_set_visible(p, false); obs_property_set_modified_callback2(p, move_value_format_type_changed, data); p = obs_properties_add_text(setting_value, S_SETTING_FORMAT, obs_module_text("Format"), OBS_TEXT_DEFAULT); obs_property_set_visible(p, false); p = obs_properties_add_int(setting_value, S_SETTING_DECIMALS, obs_module_text("Decimals"), -10, 10, 1); obs_property_set_visible(p, false); obs_property_set_modified_callback2(p, move_value_decimals_changed, data); p = obs_properties_add_int(setting_value, S_SETTING_INT, obs_module_text("Value"), 0, 0, 0); obs_property_set_visible(p, false); p = obs_properties_add_int(setting_value, S_SETTING_INT_MIN, obs_module_text("MinValue"), 0, 0, 0); obs_property_set_visible(p, false); p = obs_properties_add_int(setting_value, S_SETTING_INT_MAX, obs_module_text("MaxValue"), 0, 0, 0); obs_property_set_visible(p, false); p = obs_properties_add_float(setting_value, S_SETTING_FLOAT, obs_module_text("Value"), 0, 0, 0); obs_property_set_visible(p, false); p = obs_properties_add_float(setting_value, S_SETTING_FLOAT_MIN, obs_module_text("MinValue"), 0, 0, 0); obs_property_set_visible(p, false); p = obs_properties_add_float(setting_value, S_SETTING_FLOAT_MAX, obs_module_text("MaxValue"), 0, 0, 0); obs_property_set_visible(p, false); p = obs_properties_add_color(setting_value, S_SETTING_COLOR, obs_module_text("Value")); obs_property_set_visible(p, false); p = obs_properties_add_color(setting_value, S_SETTING_COLOR_MIN, obs_module_text("MinValue")); obs_property_set_visible(p, false); p = obs_properties_add_color(setting_value, S_SETTING_COLOR_MAX, obs_module_text("MaxValue")); obs_property_set_visible(p, false); p = obs_properties_add_text(setting_value, S_SETTING_TEXT, obs_module_text("Text"), OBS_TEXT_MULTILINE); obs_property_set_visible(p, false); obs_properties_add_button(setting_value, "value_get", obs_module_text("GetValue"), move_value_get_value); obs_properties_add_group(ppts, S_SETTING_VALUE, obs_module_text("Setting"), OBS_GROUP_NORMAL, setting_value); obs_properties_t *settings = obs_properties_create(); obs_properties_add_button(settings, "values_get", obs_module_text("GetValues"), move_value_get_values); obs_properties_add_group(ppts, S_SETTINGS, obs_module_text("Settings"), OBS_GROUP_NORMAL, settings); p = obs_properties_add_int(ppts, S_START_DELAY, obs_module_text("StartDelay"), 0, 10000000, 100); obs_property_int_set_suffix(p, "ms"); obs_properties_t *duration = obs_properties_create(); p = obs_properties_add_int(duration, S_DURATION, "", 10, 10000000, 100); obs_property_int_set_suffix(p, "ms"); p = obs_properties_add_group(ppts, S_CUSTOM_DURATION, obs_module_text("CustomDuration"), OBS_GROUP_CHECKABLE, duration); p = obs_properties_add_int(ppts, S_END_DELAY, obs_module_text("EndDelay"), 0, 10000000, 100); obs_property_int_set_suffix(p, "ms"); p = obs_properties_add_list(ppts, S_EASING_MATCH, obs_module_text("Easing"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); prop_list_add_easings(p); p = obs_properties_add_list(ppts, S_EASING_FUNCTION_MATCH, obs_module_text("EasingFunction"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); prop_list_add_easing_functions(p); p = obs_properties_add_bool(ppts, S_ENABLED_MATCH_MOVING, obs_module_text("EnabledMatchMoving")); p = obs_properties_add_list(ppts, S_START_TRIGGER, obs_module_text("StartTrigger"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("StartTrigger.None"), START_TRIGGER_NONE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Activate"), START_TRIGGER_ACTIVATE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Deactivate"), START_TRIGGER_DEACTIVATE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Show"), START_TRIGGER_SHOW); obs_property_list_add_int(p, obs_module_text("StartTrigger.Hide"), START_TRIGGER_HIDE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Enable"), START_TRIGGER_ENABLE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Load"), START_TRIGGER_LOAD); p = obs_properties_add_list(ppts, S_STOP_TRIGGER, obs_module_text("StopTrigger"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("StopTrigger.None"), START_TRIGGER_NONE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Activate"), START_TRIGGER_ACTIVATE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Deactivate"), START_TRIGGER_DEACTIVATE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Show"), START_TRIGGER_SHOW); obs_property_list_add_int(p, obs_module_text("StartTrigger.Hide"), START_TRIGGER_HIDE); obs_property_list_add_int(p, obs_module_text("StartTrigger.Enable"), START_TRIGGER_ENABLE); p = obs_properties_add_list(ppts, S_SIMULTANEOUS_MOVE, obs_module_text("SimultaneousMove"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string( p, obs_module_text("SimultaneousMove.None"), ""); obs_source_enum_filters(parent, prop_list_add_move_value_filter, p); p = obs_properties_add_list(ppts, S_NEXT_MOVE, obs_module_text("NextMove"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_list_add_string(p, obs_module_text("NextMove.None"), ""); obs_property_list_add_string(p, obs_module_text("NextMove.Reverse"), NEXT_MOVE_REVERSE); obs_source_enum_filters(parent, prop_list_add_move_value_filter, p); p = obs_properties_add_list(ppts, S_NEXT_MOVE_ON, obs_module_text("NextMoveOn"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(p, obs_module_text("NextMoveOn.End"), NEXT_MOVE_ON_END); obs_property_list_add_int(p, obs_module_text("NextMoveOn.Hotkey"), NEXT_MOVE_ON_HOTKEY); obs_properties_add_button(ppts, "move_value_start", obs_module_text("Start"), move_value_start_button); return ppts; } void move_value_defaults(obs_data_t *settings) { obs_data_set_default_bool(settings, S_SINGLE_SETTING, true); obs_data_set_default_bool(settings, S_CUSTOM_DURATION, true); obs_data_set_default_int(settings, S_DURATION, 300); obs_data_set_default_int(settings, S_EASING_MATCH, EASE_IN_OUT); obs_data_set_default_int(settings, S_EASING_FUNCTION_MATCH, EASING_CUBIC); obs_data_set_default_bool(settings, S_ENABLED_MATCH_MOVING, true); } void move_value_video_render(void *data, gs_effect_t *effect) { UNUSED_PARAMETER(effect); struct move_value_info *filter = data; obs_source_skip_video_filter(filter->source); } static const char *move_value_get_name(void *type_data) { UNUSED_PARAMETER(type_data); return obs_module_text("MoveValueFilter"); } float get_eased(float f, long long easing, long long easing_function); void vec2_bezier(struct vec2 *dst, struct vec2 *begin, struct vec2 *control, struct vec2 *end, const float t); void move_value_stop(struct move_value_info *move_value) { move_value->moving = false; if (move_value->enabled_match_moving && obs_source_enabled(move_value->source)) { obs_source_set_enabled(move_value->source, false); } } void move_value_tick(void *data, float seconds) { struct move_value_info *move_value = data; if (move_value->move_start_hotkey == OBS_INVALID_HOTKEY_ID && move_value->filter_name && strlen(move_value->filter_name)) { obs_source_t *parent = obs_filter_get_parent(move_value->source); if (parent) move_value->move_start_hotkey = obs_hotkey_register_source( parent, move_value->filter_name, move_value->filter_name, move_value_start_hotkey, data); } const bool enabled = obs_source_enabled(move_value->source); if (move_value->enabled != enabled) { if (enabled && (move_value->start_trigger == START_TRIGGER_ENABLE || (move_value->enabled_match_moving && !move_value->moving))) move_value_start(move_value); if (enabled && move_value->stop_trigger == START_TRIGGER_ENABLE) move_value_stop(move_value); move_value->enabled = enabled; } if (!move_value->moving || !enabled) return; if (!move_value->duration) { move_value->moving = false; return; } move_value->running_duration += seconds; if (move_value->running_duration * 1000.0f < (move_value->reverse ? move_value->end_delay : move_value->start_delay)) { if (move_value->reverse) return; obs_source_t *source = move_value->filter ? move_value->filter : obs_filter_get_parent(move_value->source); if (strcmp(move_value->setting_name, VOLUME_SETTING) == 0) { move_value->int_from = obs_source_get_volume(source) * 100.0; move_value->double_from = obs_source_get_volume(source) * 100.0; return; } obs_data_t *ss = obs_source_get_settings(source); move_value->int_from = obs_data_get_int(ss, move_value->setting_name); move_value->double_from = obs_data_get_double(ss, move_value->setting_name); obs_data_release(ss); return; } if (move_value->running_duration * 1000.0f >= (float)(move_value->start_delay + move_value->duration + move_value->end_delay)) { move_value->moving = false; } float t = (move_value->running_duration * 1000.0f - (float)(move_value->reverse ? move_value->end_delay : move_value->start_delay)) / (float)move_value->duration; if (t >= 1.0f) { t = 1.0f; } if (move_value->reverse) { t = 1.0f - t; } t = get_eased(t, move_value->easing, move_value->easing_function); obs_source_t *source = move_value->filter ? move_value->filter : obs_filter_get_parent(move_value->source); if (!source) return; obs_data_t *ss = obs_source_get_settings(source); bool update = true; if (move_value->settings) { const size_t count = obs_data_array_count(move_value->settings); for (size_t i = 0; i < count; i++) { obs_data_t *item = obs_data_array_item(move_value->settings, i); const char *setting_name = obs_data_get_string(item, S_SETTING_NAME); const long long value_type = obs_data_get_int(item, S_VALUE_TYPE); if (value_type == MOVE_VALUE_INT) { const long long int_from = obs_data_get_int(item, S_SETTING_FROM); const long long int_to = obs_data_get_int(item, S_SETTING_TO); const long long value_int = (long long)((1.0 - t) * (double)int_from + t * (double)int_to); obs_data_set_int(ss, setting_name, value_int); } else if (value_type == MOVE_VALUE_FLOAT) { const double double_from = obs_data_get_double( item, S_SETTING_FROM); const double double_to = obs_data_get_double(item, S_SETTING_TO); const double value_double = ((1.0 - t) * double_from + t * double_to); obs_data_set_double(ss, setting_name, value_double); } else if (value_type == MOVE_VALUE_COLOR) { struct vec4 color_from; vec4_from_rgba(&color_from, (uint32_t)obs_data_get_int( item, S_SETTING_FROM)); gs_float3_srgb_nonlinear_to_linear( color_from.ptr); struct vec4 color_to; vec4_from_rgba(&color_to, (uint32_t)obs_data_get_int( item, S_SETTING_TO)); gs_float3_srgb_nonlinear_to_linear( color_to.ptr); struct vec4 color; color.w = (1.0f - t) * color_from.w + t * color_to.w; color.x = (1.0f - t) * color_from.x + t * color_to.x; color.y = (1.0f - t) * color_from.y + t * color_to.y; color.z = (1.0f - t) * color_from.z + t * color_to.z; gs_float3_srgb_linear_to_nonlinear(color.ptr); const long long value_int = vec4_to_rgba(&color); obs_data_set_int(ss, setting_name, value_int); } } } else if (move_value->value_type == MOVE_VALUE_INT) { const long long value_int = (long long)((1.0 - t) * (double)move_value->int_from + t * (double)move_value->int_to); if (strcmp(move_value->setting_name, VOLUME_SETTING) == 0) { obs_source_set_volume(source, (float)value_int / 100.0f); update = false; } else { obs_data_set_int(ss, move_value->setting_name, value_int); } } else if (move_value->value_type == MOVE_VALUE_FLOAT) { const double value_double = (1.0 - t) * move_value->double_from + t * move_value->double_to; if (strcmp(move_value->setting_name, VOLUME_SETTING) == 0) { obs_source_set_volume(source, value_double / 100.0); update = false; } else { obs_data_set_double(ss, move_value->setting_name, value_double); } } else if (move_value->value_type == MOVE_VALUE_COLOR) { struct vec4 color; color.w = (1.0f - t) * move_value->color_from.w + t * move_value->color_to.w; color.x = (1.0f - t) * move_value->color_from.x + t * move_value->color_to.x; color.y = (1.0f - t) * move_value->color_from.y + t * move_value->color_to.y; color.z = (1.0f - t) * move_value->color_from.z + t * move_value->color_to.z; gs_float3_srgb_linear_to_nonlinear(color.ptr); const long long value_int = vec4_to_rgba(&color); obs_data_set_int(ss, move_value->setting_name, value_int); } else if (move_value->value_type == MOVE_VALUE_TEXT && move_value->move_value_type == MOVE_VALUE_TYPE_TYPING) { if (t * move_value->text_steps <= move_value->text_step && move_value->moving) { obs_data_release(ss); return; } move_value->text_step = t * move_value->text_steps; char *text = NULL; if (move_value->text_step < move_value->text_from_len - move_value->text_same) { text = bstrdup_n(move_value->text_from, move_value->text_from_len - move_value->text_step); } else { text = bstrdup_n(move_value->text_to, move_value->text_same + move_value->text_step - (move_value->text_from_len - move_value->text_same)); } obs_data_set_string(ss, move_value->setting_name, text); bfree(text); } else if (move_value->value_type == MOVE_VALUE_TEXT) { double value_double = (1.0 - t) * move_value->double_from + t * move_value->double_to; char text[TEXT_BUFFER_SIZE]; if (move_value->format_type == MOVE_VALUE_FORMAT_FLOAT) { if (snprintf(text, TEXT_BUFFER_SIZE, move_value->format, value_double) == 0) text[0] = '\0'; } else if (move_value->format_type == MOVE_VALUE_FORMAT_TIME) { long long t = value_double; struct tm *tm_info = gmtime(&t); if (strftime(text, TEXT_BUFFER_SIZE, move_value->format, tm_info) == 0) text[0] = '\0'; } else { if (move_value->decimals >= 0) { char format[10]; snprintf(format, 10, "%%.%df", move_value->decimals); snprintf(text, TEXT_BUFFER_SIZE, format, value_double); } else { double factor = pow( 10, -1.0 * (double)move_value->decimals); value_double = floor(value_double / factor) * factor; snprintf(text, TEXT_BUFFER_SIZE, "%.0f", value_double); } } obs_data_set_string(ss, move_value->setting_name, text); } else { obs_data_item_t *item = obs_data_item_byname(ss, move_value->setting_name); const enum obs_data_number_type item_type = obs_data_item_numtype(item); if (item_type == OBS_DATA_NUM_INT) { const long long value_int = (long long)((1.0 - t) * (double)move_value->int_from + t * (double)move_value->int_to); if (strcmp(move_value->setting_name, VOLUME_SETTING) == 0) { obs_source_set_volume(source, (float)value_int / 100.0f); update = false; } else { obs_data_set_int(ss, move_value->setting_name, value_int); } } else if (item_type == OBS_DATA_NUM_DOUBLE) { const double value_double = (1.0 - t) * move_value->double_from + t * move_value->double_to; if (strcmp(move_value->setting_name, VOLUME_SETTING) == 0) { obs_source_set_volume(source, value_double / 100.0); update = false; } else { obs_data_set_double(ss, move_value->setting_name, value_double); } } obs_data_item_release(&item); } obs_data_release(ss); if (update) obs_source_update(source, NULL); if (!move_value->moving) { if (move_value->enabled_match_moving && (move_value->reverse || !move_value->next_move_name || strcmp(move_value->next_move_name, NEXT_MOVE_REVERSE) != 0)) { obs_source_set_enabled(move_value->source, false); } if (move_value->next_move_on == NEXT_MOVE_ON_END && move_value->next_move_name && strlen(move_value->next_move_name) && (!move_value->filter_name || strcmp(move_value->filter_name, move_value->next_move_name) != 0)) { if (strcmp(move_value->next_move_name, NEXT_MOVE_REVERSE) == 0) { move_value->reverse = !move_value->reverse; if (move_value->reverse) move_value_start(move_value); } else { obs_source_t *parent = obs_filter_get_parent( move_value->source); if (parent) { obs_source_t *filter = obs_source_get_filter_by_name( parent, move_value ->next_move_name); if (filter && (strcmp(obs_source_get_unversioned_id( filter), MOVE_VALUE_FILTER_ID) == 0 || strcmp(obs_source_get_unversioned_id( filter), MOVE_AUDIO_VALUE_FILTER_ID) == 0)) { struct move_value_info *filter_data = obs_obj_get_data( filter); move_value_start(filter_data); } obs_source_release(filter); } } } else if (move_value->next_move_on == NEXT_MOVE_ON_HOTKEY && move_value->next_move_name && strcmp(move_value->next_move_name, NEXT_MOVE_REVERSE) == 0) { move_value->reverse = !move_value->reverse; } } } void move_value_activate(void *data) { struct move_value_info *move_value = data; if (move_value->start_trigger == START_TRIGGER_ACTIVATE) move_value_start(move_value); if (move_value->stop_trigger == START_TRIGGER_ACTIVATE) move_value_stop(move_value); } void move_value_deactivate(void *data) { struct move_value_info *move_value = data; if (move_value->start_trigger == START_TRIGGER_DEACTIVATE) move_value_start(move_value); if (move_value->stop_trigger == START_TRIGGER_DEACTIVATE) move_value_stop(move_value); } void move_value_show(void *data) { struct move_value_info *move_value = data; if (move_value->start_trigger == START_TRIGGER_SHOW) move_value_start(move_value); if (move_value->stop_trigger == START_TRIGGER_SHOW) move_value_stop(move_value); } void move_value_hide(void *data) { struct move_value_info *move_value = data; if (move_value->start_trigger == START_TRIGGER_HIDE) move_value_start(move_value); if (move_value->stop_trigger == START_TRIGGER_HIDE) move_value_stop(move_value); } struct obs_source_info move_value_filter = { .id = MOVE_VALUE_FILTER_ID, .type = OBS_SOURCE_TYPE_FILTER, .output_flags = OBS_SOURCE_VIDEO, .get_name = move_value_get_name, .create = move_value_create, .destroy = move_value_destroy, .get_properties = move_value_properties, .get_defaults = move_value_defaults, .video_render = move_value_video_render, .video_tick = move_value_tick, .update = move_value_update, .load = move_value_update, .activate = move_value_activate, .deactivate = move_value_deactivate, .show = move_value_show, .hide = move_value_hide, }; struct obs_source_info move_audio_value_filter = { .id = MOVE_AUDIO_VALUE_FILTER_ID, .type = OBS_SOURCE_TYPE_FILTER, .output_flags = OBS_SOURCE_AUDIO, .get_name = move_value_get_name, .create = move_value_create, .destroy = move_value_destroy, .get_properties = move_value_properties, .get_defaults = move_value_defaults, .video_render = move_value_video_render, .video_tick = move_value_tick, .update = move_value_update, .load = move_value_update, .activate = move_value_activate, .deactivate = move_value_deactivate, .show = move_value_show, .hide = move_value_hide, }; obs-move-transition-2.5.7/resource.rc.in000066400000000000000000000016531417746432500202500ustar00rootroot000000000000001 VERSIONINFO FILEVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},0 PRODUCTVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},0 FILEFLAGSMASK 0x0L #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x0L FILETYPE 0x2L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Exeldro" VALUE "FileDescription", "${PROJECT_FULL_NAME}" VALUE "FileVersion", "${PROJECT_VERSION}" VALUE "InternalName", "${PROJECT_NAME}" VALUE "LegalCopyright", "(C) Exeldro" VALUE "OriginalFilename", "${PROJECT_NAME}" VALUE "ProductName", "${PROJECT_FULL_NAME}" VALUE "ProductVersion", "${PROJECT_VERSION}" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END obs-move-transition-2.5.7/version.h.in000066400000000000000000000003401417746432500177210ustar00rootroot00000000000000#pragma once #define PROJECT_VERSION "${PROJECT_VERSION}" #define PROJECT_VERSION_MAJOR ${PROJECT_VERSION_MAJOR} #define PROJECT_VERSION_MINOR ${PROJECT_VERSION_MINOR} #define PROJECT_VERSION_PATCH ${PROJECT_VERSION_PATCH}