diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index b9bcd698a2..64f64053a1 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -4,10 +4,6 @@ on:
push:
workflow_dispatch:
-env:
- HAXE_VERSION: 4.3.7
- HXCPP_COMPILE_CACHE: ${{ github.workspace }}/.hxcpp_cache
-
jobs:
build:
name: Linux Build
@@ -15,22 +11,32 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Pulling the new commit
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
- haxe-version: ${{ env.HAXE_VERSION }}
- - name: Restore existing build cache for faster compilation
- uses: actions/cache@v4.2.3
- with:
- # not caching the bin folder to prevent asset duplication and stuff like that
- key: cache-build-linux
- path: |
- .haxelib/
- export/release/linux/haxe/
- export/release/linux/obj/
+ haxe-version: 4.3.7
+ # - name: Restore existing build cache for faster compilation
+ # uses: actions/cache@v4.2.3
+ # with:
+ # # not caching the bin folder to prevent asset duplication and stuff like that
+ # key: cache-build-linux
+ # path: |
+ # .haxelib/
+ # export/release/linux/obj/
+ - run: |
+ echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
+ - name: Installing Packages for Building Lime (TEMPORARY)
+ run: |
+ sudo apt-get update
+ sudo apt-get install -qq \
+ libegl1-mesa-dev libgl1-mesa-dev libibus-1.0-dev libdbus-1-dev libdecor-0-dev libudev-dev libgbm-dev libdrm-dev \
+ libpng-dev libturbojpeg-dev libvorbis-dev libopenal-dev libsdl2-dev libglu1-mesa-dev libmbedtls-dev libuv1-dev libsqlite3-dev \
+ libx11-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbcommon-dev libxcursor-dev libxrandr-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxxf86vm-dev \
+ libpulse-dev libasound2-dev libpipewire-0.3-dev \
+ g++-multilib gcc-multilib
- name: Installing LibVLC
run: |
sudo apt-get install libvlc-dev libvlccore-dev
@@ -39,47 +45,50 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -si
- name: Building the game
run: |
+ haxelib run lime setup -alias -y -nocffi
+ haxelib run lime rebuild hxcpp
+ haxelib run lime rebuild tools
+ haxelib run lime rebuild linux -nocolor -release
haxelib run lime build linux -DCOMPILE_EXPERIMENTAL
# - name: Tar files
# run: tar -zcvf CodenameEngine.tar.gz -C export/release/linux/bin .
- name: Uploading artifact (executable)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: Codename Engine (Executable Only)
path: export/release/linux/bin/CodenameEngine
- name: Uploading artifact (entire build)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: Codename Engine
path: export/release/linux/bin/
- - name: Clearing already existing cache
- uses: actions/github-script@v6
- with:
- script: |
- const caches = await github.rest.actions.getActionsCacheList({
- owner: context.repo.owner,
- repo: context.repo.repo,
- })
- for (const cache of caches.data.actions_caches) {
- if (cache.key == "cache-build-linux") {
- console.log('Clearing ' + cache.key + '...')
- await github.rest.actions.deleteActionsCacheById({
- owner: context.repo.owner,
- repo: context.repo.repo,
- cache_id: cache.id,
- })
- console.log("Cache cleared.")
- }
- }
- - name: Uploading new cache
- uses: actions/cache@v4.2.3
- with:
- # caching again since for some reason it doesnt work with the first post cache shit
- key: cache-build-linux
- path: |
- .haxelib/
- export/release/linux/haxe/
- export/release/linux/obj/
+ # - name: Clearing already existing cache
+ # uses: actions/github-script@v6
+ # with:
+ # script: |
+ # const caches = await github.rest.actions.getActionsCacheList({
+ # owner: context.repo.owner,
+ # repo: context.repo.repo,
+ # })
+ # for (const cache of caches.data.actions_caches) {
+ # if (cache.key == "cache-build-linux") {
+ # console.log('Clearing ' + cache.key + '...')
+ # await github.rest.actions.deleteActionsCacheById({
+ # owner: context.repo.owner,
+ # repo: context.repo.repo,
+ # cache_id: cache.id,
+ # })
+ # console.log("Cache cleared.")
+ # }
+ # }
+ # - name: Uploading new cache
+ # uses: actions/cache@v4.2.3
+ # with:
+ # # caching again since for some reason it doesnt work with the first post cache shit
+ # key: cache-build-linux
+ # path: |
+ # .haxelib/
+ # export/release/linux/obj/
# didnt compile debug in the same job or github would have said that job wasn't completed until debug was done too (debug uploads are not essential)
debug_build:
@@ -89,20 +98,30 @@ jobs:
needs: build # since its low priority, it'll run after, so actions will concentrate first on normal builds
steps:
- name: Pulling the new commit
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
- haxe-version: ${{ env.HAXE_VERSION }}
- - name: Restore existing build cache for faster compilation
- uses: actions/cache@v4.2.3
- with:
- # not caching the bin folder to prevent asset duplication and stuff like that
- key: cache-build-linux-debug
- path: |
- .haxelib/
- export/debug/linux/haxe/
- export/debug/linux/obj/
+ haxe-version: 4.3.7
+ # - name: Restore existing build cache for faster compilation
+ # uses: actions/cache@v4.2.3
+ # with:
+ # # not caching the bin folder to prevent asset duplication and stuff like that
+ # key: cache-build-linux-debug
+ # path: |
+ # .haxelib/
+ # export/debug/linux/obj/
+ - run: |
+ echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
+ - name: Installing Packages for Building Lime (TEMPORARY)
+ run: |
+ sudo apt-get update
+ sudo apt-get install -qq \
+ libegl1-mesa-dev libgl1-mesa-dev libibus-1.0-dev libdbus-1-dev libdecor-0-dev libudev-dev libgbm-dev libdrm-dev \
+ libpng-dev libturbojpeg-dev libvorbis-dev libopenal-dev libsdl2-dev libglu1-mesa-dev libmbedtls-dev libuv1-dev libsqlite3-dev \
+ libx11-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbcommon-dev libxcursor-dev libxrandr-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxxf86vm-dev \
+ libpulse-dev libasound2-dev libpipewire-0.3-dev \
+ g++-multilib gcc-multilib
- name: Installing LibVLC
run: |
sudo apt-get install libvlc-dev libvlccore-dev
@@ -111,39 +130,42 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -si
- name: Building the game
run: |
- haxelib run lime build linux -debug
+ haxelib run lime setup -alias -y -nocffi
+ haxelib run lime rebuild hxcpp
+ haxelib run lime rebuild tools
+ haxelib run lime rebuild linux -nocolor -debug
+ haxelib run lime build linux -debug -DCOMPILE_EXPERIMENTAL
# - name: Tar files
# run: tar -zcvf CodenameEngine.tar.gz -C export/debug/linux/bin .
- name: Uploading artifact (entire build)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: Codename Engine Debug
path: export/debug/linux/bin/
- - name: Clearing already existing cache
- uses: actions/github-script@v6
- with:
- script: |
- const caches = await github.rest.actions.getActionsCacheList({
- owner: context.repo.owner,
- repo: context.repo.repo,
- })
- for (const cache of caches.data.actions_caches) {
- if (cache.key == "cache-build-linux-debug") {
- console.log('Clearing ' + cache.key + '...')
- await github.rest.actions.deleteActionsCacheById({
- owner: context.repo.owner,
- repo: context.repo.repo,
- cache_id: cache.id,
- })
- console.log("Cache cleared.")
- }
- }
- - name: Uploading new cache
- uses: actions/cache@v4.2.3
- with:
- # caching again since for some reason it doesnt work with the first post cache shit
- key: cache-build-linux-debug
- path: |
- .haxelib/
- export/debug/linux/haxe/
- export/debug/linux/obj/
+ # - name: Clearing already existing cache
+ # uses: actions/github-script@v6
+ # with:
+ # script: |
+ # const caches = await github.rest.actions.getActionsCacheList({
+ # owner: context.repo.owner,
+ # repo: context.repo.repo,
+ # })
+ # for (const cache of caches.data.actions_caches) {
+ # if (cache.key == "cache-build-linux-debug") {
+ # console.log('Clearing ' + cache.key + '...')
+ # await github.rest.actions.deleteActionsCacheById({
+ # owner: context.repo.owner,
+ # repo: context.repo.repo,
+ # cache_id: cache.id,
+ # })
+ # console.log("Cache cleared.")
+ # }
+ # }
+ # - name: Uploading new cache
+ # uses: actions/cache@v4.2.3
+ # with:
+ # # caching again since for some reason it doesnt work with the first post cache shit
+ # key: cache-build-linux-debug
+ # path: |
+ # .haxelib/
+ # export/debug/linux/obj/
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index 72e1345e80..b0b76fa6db 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -4,140 +4,144 @@ on:
push:
workflow_dispatch:
-env:
- HAXE_VERSION: 4.3.7
- HXCPP_COMPILE_CACHE: ${{ github.workspace }}/.hxcpp_cache
-
jobs:
build:
name: Mac OS Build
permissions: write-all
- runs-on: macos-14
+ runs-on: macos-26
steps:
- name: Pulling the new commit
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
- haxe-version: ${{ env.HAXE_VERSION }}
- - name: Restore existing build cache for faster compilation
- uses: actions/cache@v4.2.3
- with:
- # not caching the bin folder to prevent asset duplication and stuff like that
- key: cache-build-mac
- path: |
- .haxelib/
- export/release/macos/haxe/
- export/release/macos/obj/
+ haxe-version: 4.3.7
+ # - name: Restore existing build cache for faster compilation
+ # uses: actions/cache@v4.2.3
+ # with:
+ # # not caching the bin folder to prevent asset duplication and stuff like that
+ # key: cache-build-mac
+ # path: |
+ # .haxelib/
+ # export/release/macos/obj/
+ - run: |
+ echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
- name: Installing/Updating libraries
run: |
haxe -cp commandline -D analyzer-optimize --run Main setup -si
- name: Building the game
run: |
- arch -x86_64 haxelib run lime build mac -DCOMPILE_EXPERIMENTAL
+ haxelib run lime setup -alias -y -nocffi
+ haxelib run lime rebuild hxcpp
+ haxelib run lime rebuild tools
+ haxelib run lime rebuild mac -nocolor -release
+ haxelib run lime build mac -DCOMPILE_EXPERIMENTAL
- name: Tar files
run: tar -zcvf CodenameEngine.tar.gz -C export/release/macos/bin .
- name: Uploading artifact (executable)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: Codename Engine (Executable Only)
path: export/release/macos/bin/CodenameEngine.app/Contents/MacOS/CodenameEngine
- name: Uploading artifact (entire build)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: Codename Engine
path: CodenameEngine.tar.gz
- - name: Clearing already existing cache
- uses: actions/github-script@v6
- with:
- script: |
- const caches = await github.rest.actions.getActionsCacheList({
- owner: context.repo.owner,
- repo: context.repo.repo,
- })
- for (const cache of caches.data.actions_caches) {
- if (cache.key == "cache-build-mac") {
- console.log('Clearing ' + cache.key + '...')
- await github.rest.actions.deleteActionsCacheById({
- owner: context.repo.owner,
- repo: context.repo.repo,
- cache_id: cache.id,
- })
- console.log("Cache cleared.")
- }
- }
- - name: Uploading new cache
- uses: actions/cache@v4.2.3
- with:
- # caching again since for some reason it doesnt work with the first post cache shit
- key: cache-build-mac
- path: |
- .haxelib/
- export/release/macos/haxe/
- export/release/macos/obj/
+ # - name: Clearing already existing cache
+ # uses: actions/github-script@v6
+ # with:
+ # script: |
+ # const caches = await github.rest.actions.getActionsCacheList({
+ # owner: context.repo.owner,
+ # repo: context.repo.repo,
+ # })
+ # for (const cache of caches.data.actions_caches) {
+ # if (cache.key == "cache-build-mac") {
+ # console.log('Clearing ' + cache.key + '...')
+ # await github.rest.actions.deleteActionsCacheById({
+ # owner: context.repo.owner,
+ # repo: context.repo.repo,
+ # cache_id: cache.id,
+ # })
+ # console.log("Cache cleared.")
+ # }
+ # }
+ # - name: Uploading new cache
+ # uses: actions/cache@v4.2.3
+ # with:
+ # # caching again since for some reason it doesnt work with the first post cache shit
+ # key: cache-build-mac
+ # path: |
+ # .haxelib/
+ # export/release/macos/obj/
# didnt compile debug in the same job or github would have said that job wasn't completed until debug was done too (debug uploads are not essential)
debug_build:
name: Mac OS Debug Build
permissions: write-all
- runs-on: macos-14
+ runs-on: macos-26
needs: build # since its low priority, it'll run after, so actions will concentrate first on normal builds
steps:
- name: Pulling the new commit
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
- haxe-version: ${{ env.HAXE_VERSION }}
- - name: Restore existing build cache for faster compilation
- uses: actions/cache@v4.2.3
- with:
- # not caching the bin folder to prevent asset duplication and stuff like that
- key: cache-build-mac-debug
- path: |
- .haxelib/
- export/debug/macos/haxe/
- export/debug/macos/obj/
+ haxe-version: 4.3.7
+ # - name: Restore existing build cache for faster compilation
+ # uses: actions/cache@v4.2.3
+ # with:
+ # # not caching the bin folder to prevent asset duplication and stuff like that
+ # key: cache-build-mac-debug
+ # path: |
+ # .haxelib/
+ # export/debug/macos/obj/
+ - run: |
+ echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
- name: Installing/Updating libraries
run: |
haxe -cp commandline -D analyzer-optimize --run Main setup -si
- name: Building the game
run: |
- arch -x86_64 haxelib run lime build mac -debug
+ haxelib run lime setup -alias -y -nocffi
+ haxelib run lime rebuild hxcpp
+ haxelib run lime rebuild tools
+ haxelib run lime rebuild mac -nocolor -debug
+ haxelib run lime build mac -debug -DCOMPILE_EXPERIMENTAL
- name: Tar files
run: tar -zcvf CodenameEngine.tar.gz -C export/debug/macos/bin .
- name: Uploading artifact (entire build)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: Codename Engine Debug
path: CodenameEngine.tar.gz
- - name: Clearing already existing cache
- uses: actions/github-script@v6
- with:
- script: |
- const caches = await github.rest.actions.getActionsCacheList({
- owner: context.repo.owner,
- repo: context.repo.repo,
- })
- for (const cache of caches.data.actions_caches) {
- if (cache.key == "cache-build-mac-debug") {
- console.log('Clearing ' + cache.key + '...')
- await github.rest.actions.deleteActionsCacheById({
- owner: context.repo.owner,
- repo: context.repo.repo,
- cache_id: cache.id,
- })
- console.log("Cache cleared.")
- }
- }
- - name: Uploading new cache
- uses: actions/cache@v4.2.3
- with:
- # caching again since for some reason it doesnt work with the first post cache shit
- key: cache-build-mac-debug
- path: |
- .haxelib/
- export/debug/macos/haxe/
- export/debug/macos/obj/
+ # - name: Clearing already existing cache
+ # uses: actions/github-script@v6
+ # with:
+ # script: |
+ # const caches = await github.rest.actions.getActionsCacheList({
+ # owner: context.repo.owner,
+ # repo: context.repo.repo,
+ # })
+ # for (const cache of caches.data.actions_caches) {
+ # if (cache.key == "cache-build-mac-debug") {
+ # console.log('Clearing ' + cache.key + '...')
+ # await github.rest.actions.deleteActionsCacheById({
+ # owner: context.repo.owner,
+ # repo: context.repo.repo,
+ # cache_id: cache.id,
+ # })
+ # console.log("Cache cleared.")
+ # }
+ # }
+ # - name: Uploading new cache
+ # uses: actions/cache@v4.2.3
+ # with:
+ # # caching again since for some reason it doesnt work with the first post cache shit
+ # key: cache-build-mac-debug
+ # path: |
+ # .haxelib/
+ # export/debug/macos/obj/
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 1d44b18914..3e78c83f20 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -21,12 +21,12 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Download Windows Full Build
- uses: dawidd6/action-download-artifact@v6
+ uses: dawidd6/action-download-artifact@v8
with:
workflow: windows.yml
name: Codename Engine
@@ -34,7 +34,7 @@ jobs:
allow_forks: false
- name: Download Windows Executable
- uses: dawidd6/action-download-artifact@v6
+ uses: dawidd6/action-download-artifact@v8
with:
workflow: windows.yml
name: Codename Engine (Executable Only)
@@ -42,7 +42,7 @@ jobs:
allow_forks: false
- name: Download Mac OS Full Build
- uses: dawidd6/action-download-artifact@v6
+ uses: dawidd6/action-download-artifact@v8
with:
workflow: macos.yml
name: Codename Engine
@@ -50,7 +50,7 @@ jobs:
allow_forks: false
- name: Download Mac OS Executable
- uses: dawidd6/action-download-artifact@v6
+ uses: dawidd6/action-download-artifact@v8
with:
workflow: macos.yml
name: Codename Engine (Executable Only)
@@ -58,7 +58,7 @@ jobs:
allow_forks: false
- name: Download Linux Full Build
- uses: dawidd6/action-download-artifact@v6
+ uses: dawidd6/action-download-artifact@v8
with:
workflow: linux.yml
name: Codename Engine
@@ -66,7 +66,7 @@ jobs:
allow_forks: false
- name: Download Linux Executable
- uses: dawidd6/action-download-artifact@v6
+ uses: dawidd6/action-download-artifact@v8
with:
workflow: linux.yml
name: Codename Engine (Executable Only)
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 2f1584327d..0d390f403f 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -4,10 +4,6 @@ on:
push:
workflow_dispatch:
-env:
- HAXE_VERSION: 4.3.7
- HXCPP_COMPILE_CACHE: ${{ github.workspace }}/.hxcpp_cache
-
jobs:
build:
name: Windows Build
@@ -15,66 +11,72 @@ jobs:
runs-on: windows-latest
steps:
- name: Pulling the new commit
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
- haxe-version: ${{ env.HAXE_VERSION }}
- - name: Restore existing build cache for faster compilation
- uses: actions/cache@v4.2.3
- with:
- # not caching the bin folder to prevent asset duplication and stuff like that
- key: cache-build-windows
- path: |
- .haxelib/
- export/release/windows/haxe/
- export/release/windows/obj/
+ haxe-version: 4.3.7
+ # - name: Restore existing build cache for faster compilation
+ # uses: actions/cache@v4.2.3
+ # with:
+ # # not caching the bin folder to prevent asset duplication and stuff like that
+ # key: cache-build-windows
+ # path: |
+ # .haxelib/
+ # export/release/windows/obj/
+ - run: |
+ echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
- name: Installing/Updating libraries
run: |
haxe -cp commandline -D analyzer-optimize --run Main setup -si --no-vscheck
- name: Building the game
run: |
+ haxelib run lime setup -alias -y -nocffi
+ haxelib run lime rebuild hxcpp
+ haxelib run lime rebuild tools
+ haxelib run lime rebuild windows -nocolor -release
haxelib run lime build windows -DCOMPILE_EXPERIMENTAL
- name: Uploading artifact (executable)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: Codename Engine (Executable Only)
- path: export/release/windows/bin/CodenameEngine.exe
+ path: |
+ export/release/windows/bin/CodenameEngine.exe
+ export/release/windows/bin/lime.ndll
- name: Uploading artifact (entire build)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: Codename Engine
path: export/release/windows/bin
- - name: Clearing already existing cache
- uses: actions/github-script@v6
- with:
- script: |
- const caches = await github.rest.actions.getActionsCacheList({
- owner: context.repo.owner,
- repo: context.repo.repo,
- })
- for (const cache of caches.data.actions_caches) {
- if (cache.key == "cache-build-windows") {
- console.log('Clearing ' + cache.key + '...')
- await github.rest.actions.deleteActionsCacheById({
- owner: context.repo.owner,
- repo: context.repo.repo,
- cache_id: cache.id,
- })
- console.log("Cache cleared.")
- }
- }
- - name: Uploading new cache
- uses: actions/cache@v4.2.3
- with:
- # caching again since for some reason it doesnt work with the first post cache shit
- key: cache-build-windows
- path: |
- .haxelib/
- export/release/windows/haxe/
- export/release/windows/obj/
+ # - name: Clearing already existing cache
+ # uses: actions/github-script@v6
+ # with:
+ # script: |
+ # const caches = await github.rest.actions.getActionsCacheList({
+ # owner: context.repo.owner,
+ # repo: context.repo.repo,
+ # })
+ # for (const cache of caches.data.actions_caches) {
+ # if (cache.key == "cache-build-windows") {
+ # console.log('Clearing ' + cache.key + '...')
+ # await github.rest.actions.deleteActionsCacheById({
+ # owner: context.repo.owner,
+ # repo: context.repo.repo,
+ # cache_id: cache.id,
+ # })
+ # console.log("Cache cleared.")
+ # }
+ # }
+ # - name: Uploading new cache
+ # uses: actions/cache@v4.2.3
+ # with:
+ # # caching again since for some reason it doesnt work with the first post cache shit
+ # key: cache-build-windows
+ # path: |
+ # .haxelib/
+ # export/release/windows/obj/
# didnt compile debug in the same job or github would have said that job wasn't completed until debug was done too (debug uploads are not essential)
debug_build:
@@ -84,56 +86,61 @@ jobs:
needs: build # since its low priority, it'll run after, so actions will concentrate first on normal builds
steps:
- name: Pulling the new commit
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Setting up Haxe
uses: krdlab/setup-haxe@v2
with:
- haxe-version: ${{ env.HAXE_VERSION }}
- - name: Restore existing build cache for faster compilation
- uses: actions/cache@v4.2.3
- with:
- # not caching the bin folder to prevent asset duplication and stuff like that
- key: cache-build-windows-debug
- path: |
- .haxelib/
- export/debug/windows/haxe/
- export/debug/windows/obj/
+ haxe-version: 4.3.7
+ # - name: Restore existing build cache for faster compilation
+ # uses: actions/cache@v4.2.3
+ # with:
+ # # not caching the bin folder to prevent asset duplication and stuff like that
+ # key: cache-build-windows-debug
+ # path: |
+ # .haxelib/
+ # export/debug/windows/haxe/
+ # export/debug/windows/obj/
+ - run: |
+ echo "HXCPP_COMPILE_CACHE=~/.hxcpp" >> $GITHUB_ENV
- name: Installing/Updating libraries
run: |
haxe -cp commandline -D analyzer-optimize --run Main setup -si --no-vscheck
- name: Building the game
run: |
+ haxelib run lime setup -alias -y -nocffi
+ haxelib run lime rebuild hxcpp
+ haxelib run lime rebuild tools
+ haxelib run lime rebuild windows -nocolor -debug
haxelib run lime build windows -debug
- name: Uploading artifact (entire build)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: Codename Engine Debug
path: export/debug/windows/bin
- - name: Clearing already existing cache
- uses: actions/github-script@v6
- with:
- script: |
- const caches = await github.rest.actions.getActionsCacheList({
- owner: context.repo.owner,
- repo: context.repo.repo,
- })
- for (const cache of caches.data.actions_caches) {
- if (cache.key == "cache-build-windows-debug") {
- console.log('Clearing ' + cache.key + '...')
- await github.rest.actions.deleteActionsCacheById({
- owner: context.repo.owner,
- repo: context.repo.repo,
- cache_id: cache.id,
- })
- console.log("Cache cleared.")
- }
- }
- - name: Uploading new cache
- uses: actions/cache@v4.2.3
- with:
- # caching again since for some reason it doesnt work with the first post cache shit
- key: cache-build-windows-debug
- path: |
- .haxelib/
- export/debug/windows/haxe/
- export/debug/windows/obj/
+ # - name: Clearing already existing cache
+ # uses: actions/github-script@v6
+ # with:
+ # script: |
+ # const caches = await github.rest.actions.getActionsCacheList({
+ # owner: context.repo.owner,
+ # repo: context.repo.repo,
+ # })
+ # for (const cache of caches.data.actions_caches) {
+ # if (cache.key == "cache-build-windows-debug") {
+ # console.log('Clearing ' + cache.key + '...')
+ # await github.rest.actions.deleteActionsCacheById({
+ # owner: context.repo.owner,
+ # repo: context.repo.repo,
+ # cache_id: cache.id,
+ # })
+ # console.log("Cache cleared.")
+ # }
+ # }
+ # - name: Uploading new cache
+ # uses: actions/cache@v4.2.3
+ # with:
+ # # caching again since for some reason it doesnt work with the first post cache shit
+ # key: cache-build-windows-debug
+ # path: |
+ # .haxelib/
+ # export/debug/windows/obj/
diff --git a/README.md b/README.md
index 02f9929bac..c9ffb7bfcd 100644
--- a/README.md
+++ b/README.md
@@ -47,7 +47,7 @@ The engine uses [HaxeFlixel](https://haxeflixel.com/) and it mainly features:
---
> [!NOTE]
-> Codename Engine as for now supports **Windows x64**, **Mac OS x64** and **Linux x64**.
+> Codename Engine as for now supports **Windows x64**, **Mac OS Universal** and **Linux x64**.
> More platforms will soon come, stay tuned!
> - [ ] **Web (HTML5) Support**
> - [ ] **Mobile Support**
diff --git a/assets/data/editors/layouts/stage/animEditScreen.xml b/assets/data/editors/layouts/stage/animEditScreen.xml
new file mode 100644
index 0000000000..39c3303ca5
--- /dev/null
+++ b/assets/data/editors/layouts/stage/animEditScreen.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+ function col(c:Int) {
+ return MARGIN + (200 + XOFFSET) * c;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+ var prevText:String = animID.label.text;
+ function onUpdate() {
+ if (animID.label.text != prevText)
+ self.winTitle = self.titleSpr.text = translate('win-title', null, [animID.label.text]);
+ prevText = animID.label.text;
+
+ labelText.y = (useFrameLabel.y + (useFrameLabel.height / 2)) - (labelText.height / 2);
+ }
+
+ function onSave() {
+ button.animData.name = animID.label.text;
+ button.animData.animType = animType.value;
+ button.animData.anim = animPrefix.label.text;
+ button.animData.fps = animFPS.value;
+ button.animData.x = offsetX.value; button.animData.y = offsetY.value;
+ button.animData.loop = loop.checked;
+ button.animData.forced = forcedPlay.checked;
+ button.animData.indices = CoolUtil.parseNumberRange(animIndices.label.text);
+ button.animData.label = useFrameLabel.checked;
+ button.updateDisplay();
+ }
+
+
\ No newline at end of file
diff --git a/assets/data/editors/layouts/stage/spriteEditScreen.xml b/assets/data/editors/layouts/stage/spriteEditScreen.xml
index de6e70bb27..80bb4d26f7 100644
--- a/assets/data/editors/layouts/stage/spriteEditScreen.xml
+++ b/assets/data/editors/layouts/stage/spriteEditScreen.xml
@@ -6,15 +6,21 @@
function col(c:Int) {
- return 20 + (200 + XOFFSET) * c;
+ return MARGIN + (200 + XOFFSET) * c;
}
if(sprite.animateAtlas == null) {
- previewSprite = new FunkinSprite(50, self.winHeight - 100);
+ previewSprite = new FunkinSprite(MARGIN, self.winHeight - (100 + MARGIN));
previewSprite.frame = sprite.frame;
self.add(previewSprite);
}
+
+ function animTypeToString(animType:AnimType) {
+ return ["none", "beat", "loop"][animType];
+ }
+ if (getEx('anims') == null)
+ setEx('anims', sprite.animDatas.copy());
@@ -25,7 +31,7 @@
-
+
@@ -77,30 +81,39 @@
- buttonlist.addButton.exists = false;
-
-
-
+ for (animData in getEx('anims')) {
+ if (animData.name == 'idle' && animData.anim == null) continue; // Skips dummy animation added by XMLUtil.loadSpriteFromXML
+ addAnimBtn(animData);
+ }
+
@@ -111,10 +124,37 @@
+
+ var prevText = spriteTextBox.label.text;
+ var prevID = nameTextBox.label.text;
+ var spriteExists = true;
+ var ratio = sprite.width / sprite.height;
+
+ spriteTextBox.onChange = function(text) {
+ var newSprite = stage.spritesParentFolder + text;
+ var animated = Paths.framesExists(newSprite, true);
+ spriteExists = ( animated || Assets.exists(Paths.image(newSprite)) );
+
+ if (text != prevText && spriteExists) {
+ if (animated) previewSprite.frames = Paths.getFrames(newSprite, previewSprite.animateSettings);
+ else previewSprite.loadGraphic(Paths.image(newSprite));
+ previewSprite.updateHitbox();
+
+ ratio = previewSprite.width / previewSprite.height;
+ previewSprite.colorTransform.__identity();
+ } else if (!spriteExists) {
+ previewSprite.colorTransform.color = 0xFFEF0202;
+ }
+ prevText = text;
+ }
+
function onUpdate() {
+ if (nameTextBox.label.text != prevID)
+ self.winTitle = self.titleSpr.text = translate('win-sprite-title', [nameTextBox.label.text]);
+ prevID = nameTextBox.label.text;
+
if(previewSprite == null) return;
previewSprite.color = 0xFFFFFFFF; // TODO: Color Wheel
previewSprite.skew.x = skewXStepper.value;
@@ -122,11 +162,10 @@
previewSprite.angle = angleStepper.value;
previewSprite.alpha = alphaStepper.value;
previewSprite.antialiasing = antialiasingCheckbox.checked;
- var ratio = sprite.width / sprite.height;
- previewSprite.setGraphicSize(50 * scaleXStepper.value * ratio, 50 * scaleYStepper.value);
+ previewSprite.setGraphicSize(85 * scaleXStepper.value * ratio, 85 * scaleYStepper.value);
previewSprite.updateHitbox();
}
-
+
function onSave() {
sprite.name = nameTextBox.label.text;
setEx("imageFile", spriteTextBox.label.text);
@@ -134,12 +173,12 @@
setEx("highMemory", highMemoryRadio.checked);
setEx("lowMemory", lowMemoryRadio.checked);
- //sprite.x = xStepper.value;
- //sprite.y = yStepper.value;
- //sprite.zoomFactor = zoomFactorStepper.value;
- //sprite.antialiasing = antialiasingCheckbox.checked;
+ sprite.x = xStepper.value;
+ sprite.y = yStepper.value;
+ sprite.zoomFactor = zoomFactorStepper.value;
+ sprite.antialiasing = antialiasingCheckbox.checked;
//sprite.color = colorWheel.color;
- //sprite.spriteAnimType = animType.value;
+ sprite.spriteAnimType = animTypeToString(animType.value);
sprite.angle = angleStepper.value;
sprite.alpha = alphaStepper.value;
@@ -171,15 +210,48 @@
button.xml.set("x", xStepper.value);
button.xml.set("y", yStepper.value);
button.xml.set("zoom", zoomFactorStepper.value);
- button.xml.set("type", animType.key);
+ button.xml.set("type", animTypeToString(animType.value));
button.xml.set("antialiasing", antialiasingCheckbox.checked);
button.xml.set("angle", angleStepper.value);
button.xml.set("alpha", alphaStepper.value);
+ getEx('anims').clear();
+ var allowedAnims:Array<String> = [for (b in animations.buttons.members) b.animData.name];
+ for (node in button.xml.elementsNamed('anim')) if (!allowedAnims.contains( node.get('name') ))
+ button.xml.removeChild(node);
+
+ for (i in 0...animations.buttons.length) {
+ var actualChildren:Array<Xml> = button.xml.children.filter((f) -> f.nodeType == 0);
+ if (i > actualChildren.length - 1) {
+ var newAnim = Xml.createElement('anim');
+ button.xml.addChild(newAnim);
+ actualChildren.push(newAnim);
+ //trace("added new at " + i + ": " + newAnim);
+ }
+
+ final animData:AnimData = animations.buttons.members[i].animData;
+ final animNode:Xml = actualChildren[i];
+ //trace(animNode + "(" + i + ") before");
+ animNode.set("name", animData.name);
+ animNode.set("type", animTypeToString(animData.animType)); // XMLAnimType is abstract of int, the code is treating it as just int
+ animNode.set("anim", animData.anim);
+ animNode.set("fps", animData.fps);
+ animNode.set("x", animData.x);
+ animNode.set("y", animData.y);
+ animNode.set("loop", animData.loop);
+ animNode.set("forced", CoolUtil.getDefault(animData.forced, false));
+ animNode.set("indices", CoolUtil.formatNumberRange(animData.indices));
+ animNode.set("label", animData.label);
+ //trace(animNode + "(" + i + ")");
+ getEx('anims').set(animData.name, animData);
+ }
+
saveXY(sprite.scrollFactor, "scroll", scrollXStepper, scrollYStepper);
saveXY(sprite.scale, "scale", scaleXStepper, scaleYStepper);
saveXY(sprite.skew, "skew", skewXStepper, skewYStepper);
XMLUtil.loadSpriteFromXML(sprite, button.xml, stage.spritesParentFolder);
+ button.updateInfo();
+ setEx('node', button.xml);
}
\ No newline at end of file
diff --git a/assets/languages/en/Editors.xml b/assets/languages/en/Editors.xml
index 3c98c70f01..ca440fe98d 100644
--- a/assets/languages/en/Editors.xml
+++ b/assets/languages/en/Editors.xml
@@ -594,8 +594,9 @@
Stage Info
Stage Name
Sprite Path\n(Needs Refresh)
- Start Camera Position (X, Y)
+ Start Camera Position\n(X, Y)
Zoom
+ Custom Attributes
@@ -609,7 +610,7 @@
Element Name
Name Identifier
- Image File (WIP: needs reload)\nIn (${stage.spritesParentFolder})
+ Image File\n(in "{0}")
Scroll {0}
Scale {0}
Camera {0}
@@ -625,6 +626,7 @@
Low Memory
Attributes
Transform Preview:
+ Animations
Animation Type
@@ -633,6 +635,8 @@
LOOP
Tip: To access custom properties,\nHold Shift when clicking edit
+
+ Name on Anim Data
@@ -647,6 +651,7 @@
Scale: {0}, {1}
Scroll: {0}, {1}
+ Unknown Sprite:\n{0}
@@ -656,6 +661,14 @@
Character
Creating a box isnt implemented yet!
+
+
+ Editing "{0}" Anim
+ Anim Data
+ Use Frame Labels?
+ Offset (X, Y)
+ Forced Playing?
+
diff --git a/assets/languages/es/Editors.xml b/assets/languages/es/Editors.xml
index f8f23dd52d..8e4da69a0f 100644
--- a/assets/languages/es/Editors.xml
+++ b/assets/languages/es/Editors.xml
@@ -551,7 +551,7 @@
Info. del Escenario
Nombre del Escenario
Ubicación de Imágenes\n(Necesita Refrescar)
- Posición inicial de la cámara (X, Y)
+ Posición inicial de la cámara\n(X, Y)
Zoom
@@ -566,7 +566,7 @@
Nombre del Elemento
Identificador de Nombre
- Imagen (WIP: necesita refrescar)\nEn (${stage.spritesParentFolder})
+ Imagen\n(en "{0}")
Desplazamiento {0}
Tamaño {0}
Cámara {0}
@@ -580,6 +580,7 @@
Baja Memoria
Atributos
Preview:
+ Animaciónes
Tipo de Animación
@@ -602,6 +603,7 @@
Tamaño: {0}, {1}
Desplazamiento: {0}, {1}
+ Sprite Desconocido:\n{0}
diff --git a/assets/languages/it/Editors.xml b/assets/languages/it/Editors.xml
index 90853b8587..d0968dc16a 100644
--- a/assets/languages/it/Editors.xml
+++ b/assets/languages/it/Editors.xml
@@ -561,7 +561,7 @@
Informazioni Palcoscenico
Nome Palcoscenico
Percorso file Sprite\n(E' necessario ricaricare la pagina)
- Posizioni iniziali della Telecamera (X, Y)
+ Posizioni iniziali della Telecamera\n(X, Y)
Zoom
@@ -576,7 +576,7 @@
Nome Elemento
Nome Identificatore
- File Immagine (WIP: reload necessario)\nIn (${stage.spritesParentFolder})
+ File Immagine\n(in "{0}")
Scorrimento {0}
Scala {0}
Teleamera {0}
diff --git a/assets/languages/pl/Editors.xml b/assets/languages/pl/Editors.xml
index 694e66c3d9..86ff120c14 100644
--- a/assets/languages/pl/Editors.xml
+++ b/assets/languages/pl/Editors.xml
@@ -568,7 +568,7 @@
Info Sceny
Nazwa Sceny
Ścieżka Plikowa Sprite'a\n(Wymaga Odświeżenia)
- Pozycja Startowa Kamery (X, Y)
+ Pozycja Startowa Kamery\n(X, Y)
Przybliżenie
@@ -583,7 +583,7 @@
Nazwa Elementu
Nazwa Identyfikatora
- Plik Obrazu (W TRACKIE PRACY: wymaga odświeżenia)\nIn (${stage.spritesParentFolder})
+ Plik Obrazu\n(in "{0}")
Przewijanie {0}
Skala {0}
Kamera {0}
diff --git a/assets/languages/pt/Editors.xml b/assets/languages/pt/Editors.xml
index aa718df294..2d0a05208d 100644
--- a/assets/languages/pt/Editors.xml
+++ b/assets/languages/pt/Editors.xml
@@ -588,7 +588,7 @@
Info. do Cenário
Nome do Cenário
Caminho do Sprite\n(Requer Recarregamento)
- Posição inicial da Câmara (X, Y)
+ Posição inicial da Câmara\n(X, Y)
Zoom
Atributos Especiais
@@ -604,7 +604,7 @@
Nome do Elemento
Nome Identificador
- Imagem (WIP: precisa recarregar)\nEm (${stage.spritesParentFolder})
+ Imagem\n(em "{0}")
Scroll {0}
Tamanho {0}
Câmara {0}
@@ -629,6 +629,8 @@
REPETIR
Dica: Para aceder a mais dados,\nSegure em Shift quando Editar
+
+ Nome em Dados de Animação
@@ -643,6 +645,7 @@
Tamanho: {0}, {1}
Deslocamento: {0}, {1}
+ Sprite Desconhecido:\n{0}
@@ -652,6 +655,14 @@
Personagem
Criar caixas ainda não foi implementado!
+
+
+ A Editar a Animação "{0}"
+ Dados da Animação
+ Usar Rótulos\nde Quadro?
+ Posições (X, Y)
+ Reprodução Forçada?
+
diff --git a/assets/shaders/engine/editorWaveforms.frag b/assets/shaders/engine/editorWaveforms.frag
index c65452dc7f..1f6d448bad 100644
--- a/assets/shaders/engine/editorWaveforms.frag
+++ b/assets/shaders/engine/editorWaveforms.frag
@@ -1,6 +1,5 @@
#pragma header
-
-// Used in charter by waveforms
+#version 120 // this shader doesnt require expensive calculations
const vec3 gradient1 = vec3(114.0/255.0, 81.0/255.0, 135.0/255.0);
const vec3 gradient2 = vec3(144.0/255.0, 80.0/255.0, 186.0/255.0);
@@ -18,6 +17,21 @@ uniform vec2 waveformSize;
uniform bool lowDetail;
+float catmullRom(
+ float p0, float p1, float p2, float p3,
+ float t
+) {
+ float t2 = t * t;
+ float t3 = t2 * t;
+
+ return 0.5 * (
+ (2.0 * p1) +
+ (-p0 + p2) * t +
+ (2.0*p0 - 5.0*p1 + 4.0*p2 - p3) * t2 +
+ (-p0 + 3.0*p1 - 3.0*p2 + p3) * t3
+ );
+}
+
float getAmplitude(vec2 pixel) {
float pixelID = floor((pixel.y+pixelOffset)/3.0);
diff --git a/building/alsoft.txt b/building/alsoft.txt
deleted file mode 100644
index 58d8c74cc1..0000000000
--- a/building/alsoft.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-[General]
-sample-type=float32
-stereo-mode=speakers
-hrtf=false
-cf_level=0
-output-limiter=false
-front-stabilizer=false
-volume-adjust=0
-period_size=441
-sources=512
-sends=64
-dither=false
-
-[decoder]
-hq-mode=true
-distance-comp=true
-nfc=false
\ No newline at end of file
diff --git a/building/libs.xml b/building/libs.xml
index 9eaf4de4e6..dd480bb843 100644
--- a/building/libs.xml
+++ b/building/libs.xml
@@ -3,35 +3,34 @@
Preparing installation...
-
-
-
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
@@ -40,15 +39,16 @@
+
-
+
+
+
+
diff --git a/project.xml b/project.xml
index b129dbb377..162ccccb98 100644
--- a/project.xml
+++ b/project.xml
@@ -1,84 +1,54 @@
-
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
+
-
-
+
+
-
-
-
+
+
-
+
-
+
-
+
@@ -87,36 +57,60 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -124,32 +118,47 @@
-
-
+
-
-
+
+
-
-
-
-
-
+
+
-
+
+
+
+
-
-
+
+
+
+
-
+
-
-
-
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -165,81 +174,69 @@
-
-
-
-
+
-
-
-
+
-
-
+
+
+
+
+
+
-
+
+
-
-
-
-
-
-
-
+
+
-
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/flixel/sound/FlxSound.hx b/source/flixel/sound/FlxSound.hx
deleted file mode 100644
index 9891bc934a..0000000000
--- a/source/flixel/sound/FlxSound.hx
+++ /dev/null
@@ -1,960 +0,0 @@
-package flixel.sound;
-
-import lime.media.AudioBuffer;
-import lime.media.AudioSource;
-import lime.media.AudioManager;
-
-import openfl.Assets;
-import openfl.events.IEventDispatcher;
-import openfl.events.Event;
-import openfl.media.Sound;
-import openfl.media.SoundChannel;
-import openfl.media.SoundTransform;
-import openfl.media.SoundMixer;
-import openfl.net.URLRequest;
-import openfl.utils.AssetType;
-#if flash11
-import openfl.utils.ByteArray;
-#end
-
-import flixel.math.FlxMath;
-import flixel.math.FlxPoint;
-import flixel.system.FlxAssets.FlxSoundAsset;
-import flixel.tweens.FlxTween;
-import flixel.util.FlxDestroyUtil;
-import flixel.util.FlxSignal;
-import flixel.util.FlxStringUtil;
-import flixel.FlxBasic;
-import flixel.FlxObject;
-import flixel.FlxG;
-
-@:access(flixel.FlxGame)
-class FlxSound extends FlxBasic {
- /**
- * The x position of this sound in world coordinates.
- * Only really matters if you are doing proximity/panning stuff.
- */
- public var x:Float;
-
- /**
- * The y position of this sound in world coordinates.
- * Only really matters if you are doing proximity/panning stuff.
- */
- public var y:Float;
-
- /**
- * Whether or not this sound should be automatically destroyed when you switch states.
- */
- public var persist:Bool;
-
- /**
- * Whether or not the sound is currently playing.
- */
- public var playing(get, never):Bool;
-
- /**
- * Set volume to a value between 0 and 1* to change how this sound is.
- */
- public var volume(get, set):Float;
-
- /**
- * Whether to make this sound muted or not.
- */
- public var muted(default, set):Bool;
-
- #if FLX_PITCH
- /**
- * Set pitch, which also alters the playback speed. Default is 1.
- * @since 5.0.0
- */
- public var pitch(get, set):Float;
-
- /**
- * Alters the pitch of the sound depends on the current FlxG.timeScale. Default is true.
- * @since raltyMod
- */
- public var timeScaleBased:Bool;
- #end
-
- /**
- * Pan amount. -1 = full left, 1 = full right. Proximity based panning overrides this.
- */
- public var pan(get, set):Float;
-
- /**
- * The position in runtime of the music playback in milliseconds.
- * If set while paused, changes only come into effect after a `resume()` call.
- */
- public var time(get, set):Float;
-
- /**
- * The offset for this FlxSound.
- * Useful for just generally offsetting this sound without affecting time.
- * @since raltyMod
- */
- public var offset(get, set):Float;
-
- /**
- * The length of the sound in milliseconds.
- * @since 4.2.0
- */
- public var length(get, never):Float;
-
- /**
- * The latency of the sound in milliseconds.
- * @since raltyMod
- */
- //public var latency(get, never):Float;
-
- /**
- * Whether or not this sound should loop.
- */
- public var looped(default, set):Bool;
-
- /**
- * In case of looping, the point (in milliseconds) from where to restart the sound when it loops back
- * @since 4.1.0
- */
- public var loopTime(default, set):Float;
-
- /**
- * At which point to stop playing the sound, in milliseconds.
- * If not set / `null`, the sound completes normally.
- * @since 4.2.0
- */
- public var endTime(default, set):Null;
-
- /**
- * The sound's "target" (for proximity and panning).
- * @since raltyMod
- */
- public var target:Null;
-
- /**
- * The maximum effective radius of this sound (for proximity and panning).
- * @since raltyMod
- */
- public var radius:Float;
-
- /**
- * Whether the proximity alters the pan or not.
- * @since raltyMod
- */
- public var proximityPan:Bool;
-
- /**
- * Controls how much this object is affected by camera scrolling. `0` = no movement (e.g. a static sound),
- * This is only useful if used with proximity (Initialized once proximity is used).
- * @since raltyMod
- */
- public var scrollFactor(default, null):FlxPoint;
-
- /**
- * Stores for how much channels are in the loaded sound.
- * @since raltyMod
- */
- public var channels(get, never):Int;
-
- /**
- * Whether or not this sound is stereo instead of mono.
- * @since raltyMod
- */
- public var stereo(get, never):Bool;
-
- /**
- * Whether or not this sound is loaded yet.
- * @since raltyMod
- */
- public var loaded(get, never):Bool;
-
- /**
- * Whether or not this sound is streamed, only vorbis though.
- * @since raltyMod
- */
- public var streamed(get, never):Bool;
-
- /**
- * Stores the average wave amplitude of both stereo channels.
- * @since raltyMod
- */
- public var amplitude(get, never):Float;
-
- /**
- * Just the amplitude of the left stereo channel
- * @since raltyMod
- */
- public var amplitudeLeft(get, never):Float;
-
- /**
- * Just the amplitude of the right stereo channel
- * @since raltyMod
- */
- public var amplitudeRight(get, never):Float;
-
- /**
- * The ID3 song name. Defaults to null. Currently only works for MP3 streamed sounds.
- */
- public var name(default, null):String;
-
- /**
- * The ID3 artist name. Defaults to null. Currently only works for MP3 streamed sounds.
- */
- public var artist(default, null):String;
-
- /**
- * Whether to call `destroy()` when the sound has finished playing.
- */
- public var autoDestroy:Bool;
-
- /**
- * Signal that is dispatched on sound complete.
- * Seperates the looping from onComplete.
- */
- public final onFinish:FlxSignal;
-
- /**
- * Tracker for sound complete callback. If assigned, will be called
- * each time when sound reaches its end.
- */
- //@:deprecated("`FlxSound.onComplete` is deprecated! Use `FlxSound.onFinish` instead.")
- public var onComplete:Void->Void;
-
- /**
- * The sound group this sound belongs to
- */
- public var group(default, set):FlxSoundGroup;
-
- /**
- * Stores the sound lime AudioBuffer if exists.
- * @since raltyMod
- */
- public var buffer(get, never):AudioBuffer;
-
- /**
- * The tween used to fade this sound's volume in and out (set via `fadeIn()` and `fadeOut()`)
- * @since 4.1.0
- */
- public var fadeTween:FlxTween;
-
- @:allow(flixel.system.frontEnds.SoundFrontEnd.load) var _sound:Sound;
- var _transform:SoundTransform;
- var _channel:SoundChannel;
- var _source:AudioSource;
- var _paused:Bool;
- var _volume:Float;
- var _volumeAdjust:Float;
- var _pan:Float;
- var _panAdjust:Float;
- var _time:Float;
- var _offset:Float;
- var _timeInterpolation:Float;
- var _lastTime:Null; // FlxG.game.getTicks(), in MS
- var _length:Float;
- #if FLX_PITCH
- var _pitch:Float;
- var _timeScaleAdjust:Float;
- var _realPitch:Float;
- #end
- var _amplitudeLeft:Float;
- var _amplitudeRight:Float;
- var _amplitudeUpdate:Bool;
- var _alreadyPaused:Null;
-
- public function new() {
- super();
- onFinish = new FlxSignal();
- revive();
- }
-
- /**
- * Resets this FlxSound properties.
- *
- * @param clean Whether if this FlxSound also needs to be cleaned up too.
- */
- public function reset():Void {
- if (_source != null) stop();
- onFinish.removeAll();
-
- x = y = 0;
- @:bypassAccessor muted = false;
- @:bypassAccessor looped = false;
- @:bypassAccessor loopTime = 0;
- @:bypassAccessor endTime = null;
- autoDestroy = false;
- visible = false;
- target = null;
- radius = 0;
- proximityPan = true;
- onComplete = null;
- timeScaleBased = true;
-
- _alreadyPaused = null;
- _cameras = null;
- _lastTime = null;
- _paused = false;
- _offset = _length = _time = 0;
- _volume = _volumeAdjust = 1;
- _amplitudeLeft = _amplitudeRight = 0;
- _amplitudeUpdate = true;
- #if FLX_PITCH _pitch = _realPitch = _timeScaleAdjust = 1; #end
-
- if (_transform == null) _transform = new SoundTransform();
- }
-
- /**
- * Internal cleanup function for cleaning up this FlxSound.
- * @since raltyMod
- */
- function cleanup(destroySound:Bool, resetPosition:Bool = true) @:privateAccess {
- active = false;
- _lastTime = null;
-
- if (destroySound) {
- scrollFactor = FlxDestroyUtil.put(scrollFactor);
-
- if (group != null) group.remove(this);
-
- if (_channel != null) {
- _channel.stop();
- _channel = null;
- }
- if (_source != null) {
- _source.onComplete.remove(stopped);
- _source.onLoop.remove(source_looped);
- _source.dispose();
- }
- _source = null;
- _sound = null;
-
- if (autoDestroy) persist = false;
- reset();
- }
- else if (_channel != null && _channel.__isValid) {
- if (resetPosition) {
- _source.stop();
-
- _time = 0;
- _paused = false;
- }
- else if (!_paused) {
- get_time();
- _source.pause();
- }
- }
- }
-
- /**
- * Handles fade out, fade in, panning, proximity, and amplitude operations each frame.
- */
- override function update(elapsed:Float):Void {
- if (!playing) return;
-
- var timeScaleTarget = timeScaleBased ? FlxG.timeScale : 1.0;
- if (_timeScaleAdjust != timeScaleTarget) {
- _timeScaleAdjust = timeScaleTarget;
- pitch = _pitch;
- if (_channel == null) return;
- }
-
- _amplitudeUpdate = true;
-
- // Distance-based volume control
- if (target != null) {
- var targetPosition = target.getPosition(), position = getPosition();
- var camera = camera;
- if (camera != null) {
- targetPosition.subtract(camera.scroll.x * target.scrollFactor.x, camera.scroll.y * target.scrollFactor.y);
- if (scrollFactor != null) position.subtract(camera.scroll.x * scrollFactor.x, camera.scroll.y * scrollFactor.y);
- else position.subtract(camera.scroll.x, camera.scroll.y);
- }
-
- var radialMultiplier = targetPosition.distanceTo(position) / radius;
-
- // Make it so it affects the 3d position of the source and not just the panning?
- _volumeAdjust = 1 - FlxMath.bound(radialMultiplier, 0, 1);
- if (proximityPan) _panAdjust = (position.x - targetPosition.x) / radius;
-
- targetPosition.put();
- position.put();
- }
- else
- _volumeAdjust = 1.0;
-
- updateTransform();
- }
-
- override function revive() {
- reset();
- super.revive();
- }
-
- override function kill() {
- super.kill();
- reset();
- }
-
- override function destroy() {
- super.destroy();
- cleanup(true);
- }
-
- /**
- * One of the main setup functions for sounds, this function loads a sound from an embedded MP3.
- *
- * @param embeddedSound An embedded Class object representing an MP3 file.
- * @param looped Whether or not this sound should loop endlessly.
- * @param autoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing.
- * Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default.
- * @param onComplete Called when the sound finished playing
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function loadEmbedded(embeddedSound:FlxSoundAsset, looped = false, autoDestroy = false, ?onComplete:Void->Void):FlxSound {
- if (!exists || embeddedSound == null) return this;
- cleanup(true);
-
- if ((embeddedSound is Sound)) _sound = embeddedSound;
- else if ((embeddedSound is Class)) _sound = Type.createInstance(embeddedSound, []);
- else if ((embeddedSound is String)) {
- if (Assets.exists(embeddedSound, AssetType.MUSIC) && this == FlxG.sound.music)
- _sound = Assets.getMusic(embeddedSound);
- else if (Assets.exists(embeddedSound, AssetType.SOUND))
- _sound = Assets.getSound(embeddedSound);
- else
- FlxG.log.error('Could not find a Sound asset with an ID of \'$embeddedSound\'.');
- }
-
- return init(looped, autoDestroy, onComplete);
- }
-
- /**
- * One of the main setup functions for sounds, this function loads a sound from a URL.
- *
- * @param soundURL A string representing the URL of the MP3 file you want to play.
- * @param looped Whether or not this sound should loop endlessly.
- * @param autoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing.
- * Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default.
- * @param onComplete Called when the sound finished playing
- * @param onLoad Called when the sound finished loading.
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function loadStream(soundURL:String, looped = false, autoDestroy = false, ?onComplete:Void->Void, ?onLoad:Void->Void):FlxSound {
- if (!exists) return this;
- cleanup(true);
-
- _sound = new Sound();
- _sound.addEventListener(Event.ID3, gotID3);
- var loadCallback:Event->Void = null;
- loadCallback = function(e:Event)
- {
- (e.target : IEventDispatcher).removeEventListener(e.type, loadCallback);
- // Check if the sound was destroyed before calling. Weak ref doesn't guarantee GC.
- if (_sound == e.target)
- {
- _length = _sound.length;
- if (onLoad != null)
- onLoad();
- }
- }
- // Use a weak reference so this can be garbage collected if destroyed before loading.
- _sound.addEventListener(Event.COMPLETE, loadCallback, false, 0, true);
- _sound.load(new URLRequest(soundURL));
-
- return init(looped, autoDestroy, onComplete);
- }
-
- #if flash11
- /**
- * One of the main setup functions for sounds, this function loads a sound from a ByteArray.
- *
- * @param bytes A ByteArray object.
- * @param looped Whether or not this sound should loop endlessly.
- * @param autoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing.
- * Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default.
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function loadByteArray(bytes:ByteArray, looped = false, autoDestroy = false, ?onComplete:Void->Void):FlxSound {
- if (!exists) return this;
- cleanup(true);
-
- _sound = new Sound();
- _sound.addEventListener(Event.ID3, gotID3);
- _sound.loadCompressedDataFromByteArray(bytes, bytes.length);
-
- return init(looped, autoDestroy, onComplete);
- }
- #end
-
- function init(?looped:Null, ?autoDestroy:Null, ?onComplete:NullVoid>):FlxSound {
- if (looped != null) this.looped = looped;
- if (autoDestroy != null) this.autoDestroy = autoDestroy;
- if (onComplete != null) this.onComplete = onComplete;
-
- if (_sound != null) makeChannel();
- else _length = 0;
-
- endTime = null;
-
- return this;
- }
-
- /**
- * Call inbetween init function if a sound asset does exists.
- * @since raltyMod
- */
- function makeChannel() @:privateAccess {
- if (_sound.__buffer == null) return;
- if (_source != null) _source.dispose();
-
- if (_channel == null) (_source = (_channel = new SoundChannel(null)).__source = new AudioSource(_sound.__buffer)).gain = 0;
- else {
- (_source = new AudioSource(_sound.__buffer)).gain = 0;
- _channel.__dispose();
- _channel.__source = _source;
- SoundMixer.__registerSoundChannel(_channel);
- }
- _length = _source.length;
-
- _source.onComplete.add(stopped);
- _source.onLoop.add(source_looped);
- _channel.__soundTransform = _transform;
- _channel.__isValid = true;
- }
-
- /**
- * Call after adjusting the volume to update the sound channel's settings.
- */
- @:allow(flixel.sound.FlxSoundGroup)
- function updateTransform() {
- _transform.volume = calcTransformVolume();
- _transform.pan = _pan + _panAdjust;
- if (_channel != null) _channel.soundTransform = _transform;
- }
-
- public function calcTransformVolume():Float {
- if (muted) return 0.0;
-
- #if FLX_SOUND_SYSTEM
- if (FlxG.sound.muted) return 0.0;
-
- // TODO: when flixel-cne is updated, enable this
- //return FlxG.sound.applySoundCurve(FlxG.sound.volume * volume);
- return FlxG.sound.volume * getActualVolume();
- #else
- return getActualVolume();
- #end
- }
-
- /**
- * Call this function if you want this sound's volume to change
- * based on distance from a particular FlxObject.
- *
- * @param X The X position of the sound.
- * @param Y The Y position of the sound.
- * @param TargetObject The object you want to track.
- * @param Radius The maximum distance this sound can travel.
- * @param Pan Whether panning should be used in addition to the volume changes.
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function proximity(x = 0.0, y = 0.0, ?targetObject:FlxObject, ?radius:Float, pan = true, ?scrollFactor:FlxPoint):FlxSound {
- setPosition(x, y);
- if (targetObject != null) this.target = targetObject;
- if (radius != null) this.radius = radius;
- proximityPan = pan;
-
- if (this.scrollFactor == null) this.scrollFactor = FlxPoint.get(1, 1);
- if (scrollFactor != null) this.scrollFactor.copyFrom(scrollFactor);
-
- return this;
- }
-
- /**
- * Helper function to set the coordinates of this object.
- * Sound positioning is used in conjunction with proximity/panning.
- *
- * @param x The new x position
- * @param y The new y position
- */
- public function setPosition(x = 0.0, y = 0.0):Void {
- this.x = x;
- this.y = y;
- }
-
- /**
- * Returns the world position of this object.
- *
- * @param result Optional arg for the returning point.
- * @return The world position of this object.
- * @since raltyMod
- */
- public function getPosition(?result:FlxPoint):FlxPoint {
- if (result == null)
- result = FlxPoint.get();
-
- return result.set(x, y);
- }
-
- /**
- * Call this function to play the sound - also works on paused sounds.
- *
- * @param forceRestart Whether to start the sound over or not.
- * Default value is false, meaning if the sound is already playing or was
- * paused when you call play(), it will continue playing from its current
- * position, NOT start again from the beginning.
- * @param startTime At which point to start playing the sound, in milliseconds.
- * @param endTime At which point to stop playing the sound, in milliseconds.
- * If not set / `null`, the sound completes normally.
- */
- public function play(forceRestart = false, startTime = 0.0, ?endTime:Null):FlxSound {
- if (!exists) return this;
-
- if (forceRestart) cleanup(false, true);
- else if (playing) return this;
-
- if (endTime != null) this.endTime = endTime;
- if (_paused) resume();
- else startSound(startTime);
-
- return this;
- }
-
- /**
- * Unpause a sound. Only works on sounds that have been paused.
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function resume():FlxSound {
- if (_paused) startSound(_time);
- return this;
- }
-
- /**
- * Call this function to pause this sound.
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function pause():FlxSound {
- if (!playing) return this;
-
- cleanup(false, false);
- _paused = true;
- return this;
- }
-
- /**
- * Call this function to stop this sound.
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function stop():FlxSound {
- cleanup(autoDestroy, true);
- return this;
- }
-
- /**
- * Helper function that tweens this sound's volume.
- *
- * @param duration The amount of time the fade-out operation should take.
- * @param to The volume to tween to, 0 by default.
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function fadeOut(duration = 1.0, to = 0.0, ?onComplete:FlxTween->Void):FlxSound {
- if (fadeTween != null) fadeTween.cancel();
- fadeTween = FlxTween.num(volume, to, duration, {onComplete: onComplete}, volumeTween);
-
- return this;
- }
-
- /**
- * Helper function that tweens this sound's volume.
- * If the sound wasn't playing at all, it'll play before the tween starts.
- *
- * @param duration The amount of time the fade-in operation should take.
- * @param from The volume to tween from, 0 by default.
- * @param to The volume to tween to, 1 by default.
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function fadeIn(duration = 1.0, from = 0.0, to = 1.0, ?onComplete:FlxTween->Void):FlxSound {
- if (!playing) play();
- if (fadeTween != null) fadeTween.cancel();
- fadeTween = FlxTween.num(from, to, duration, {onComplete: onComplete}, volumeTween);
-
- return this;
- }
-
- function volumeTween(f:Float) volume = f;
-
- /**
- * Returns the currently selected "real" volume of the sound (takes fades and proximity into account).
- *
- * @return The adjusted volume of the sound.
- */
- public inline function getActualVolume():Float
- return (group != null ? group.getVolume() : 1.0) * _volume * _volumeAdjust;
-
- #if FLX_PITCH
- /**
- * Returns the currently selected "real" pitch of the sound.
- *
- * @return The adjusted pitch of the sound.
- */
- public inline function getActualPitch():Float
- return _realPitch;
- #end
-
- /**
- * Returns the actual time coming from the internal, can be used for detecting sync error.
- *
- * @return The actual time of the sound.
- */
- public inline function getActualTime():Float {
- get_time();
- return _time;
- }
-
- /**
- * An internal helper function used to attempt to start playing
- * the sound and populate the _channel variable.
- */
- function startSound(startTime:Float) @:privateAccess {
- if (_sound == null) return;
-
- _paused = false;
- _time = startTime;
- _lastTime = FlxG.game.getTicks();
- if (_channel == null || !_channel.__isValid || _source == null #if lime_cffi || _source.__backend.disposed #end)
- makeChannel();
-
- if (_channel != null) {
- #if FLX_PITCH
- _timeScaleAdjust = timeScaleBased ? FlxG.timeScale : 1.0;
- pitch = _pitch;
- #end
-
- updateTransform();
- _channel.__lastPeakTime = -10;
- _channel.__leftPeak = 0;
- _channel.__rightPeak = 0;
-
- #if lime_cffi _source.__backend.playing = true; #end
- _source.offset = 0;
- _source.currentTime = startTime + _offset;
- #if !lime_cffi _source.play(); #end
-
- looped = looped;
- loopTime = loopTime;
- endTime = endTime;
-
- active = true;
- }
- else {
- exists = false;
- active = false;
- }
- }
-
- function stopped() {
- onFinish.dispatch();
-
- if (onComplete != null) onComplete();
-
- if (looped) {
- cleanup(false);
- play(false, loopTime, endTime);
- }
- else cleanup(autoDestroy);
- }
-
- function source_looped() {
- if (onComplete != null) onComplete();
-
- if (!looped) {
- cleanup(autoDestroy);
- _lastTime = FlxG.game.getTicks();
- _time = loopTime;
- }
- else if (_source != null)
- _source.loops = 999;
- }
-
- /**
- * Internal event handler for ID3 info (i.e. fetching the song name).
- */
- function gotID3(_) {
- name = _sound.id3.songName;
- artist = _sound.id3.artist;
- _sound.removeEventListener(Event.ID3, gotID3);
- }
-
- #if FLX_SOUND_SYSTEM
- @:allow(flixel.system.frontEnds.SoundFrontEnd)
- function onFocus() if (!_alreadyPaused) {
- resume();
- _alreadyPaused = null;
- }
-
- @:allow(flixel.system.frontEnds.SoundFrontEnd)
- function onFocusLost() if (_alreadyPaused == null && !(_alreadyPaused = _paused)) pause();
- #end
-
- function set_group(value:FlxSoundGroup):FlxSoundGroup {
- if (value != null) value.add(this);
- else group.remove(this);
- return group;
- }
-
- inline function get_playing():Bool @:privateAccess
- return _source != null && _source.playing;
-
- inline function get_volume():Float
- return _volume;
-
- inline function set_volume(v:Float):Float {
- _volume = FlxMath.bound(v, 0, 10);
- updateTransform();
- return _volume;
- }
-
- inline function set_muted(v:Bool):Bool {
- muted = v;
- updateTransform();
- return muted;
- }
-
- inline function get_pan():Float return _pan;
- inline function set_pan(v:Float):Float {
- _pan = FlxMath.bound(v, -1, 1);
- updateTransform();
- return _pan;
- }
-
- inline function get_loaded():Bool
- return buffer != null;
-
- inline function get_streamed():Bool
- @:privateAccess return #if lime_vorbis _sound != null && _sound.__buffer.__srcVorbisFile != null #else false #end;
-
- inline function get_buffer():AudioBuffer
- @:privateAccess return _sound != null ? _sound.__buffer : null;
-
- inline function update_amplitude():Void @:privateAccess {
- if (_channel != null && _channel.__updatePeaks(get_time()) && _amplitudeUpdate) {
- _amplitudeUpdate = false;
- _amplitudeLeft = _channel.__leftPeak;
- _amplitudeRight = _channel.__rightPeak;
- }
- }
-
- inline function get_amplitudeLeft():Float {
- update_amplitude();
- return _amplitudeLeft;
- }
-
- inline function get_amplitudeRight():Float {
- update_amplitude();
- return _amplitudeRight;
- }
-
- inline function get_amplitude():Float {
- update_amplitude();
- return Math.max(_amplitudeLeft, _amplitudeRight);//if (stereo) (_amplitudeLeft + _amplitudeRight) * 0.5; else _amplitudeLeft;
- }
-
- inline function get_channels():Int
- @:privateAccess return (buffer != null) ? buffer.channels : 0;
-
- inline function get_stereo():Bool
- return channels > 1;
-
- #if FLX_PITCH
- inline function get_pitch():Float
- return _pitch;
-
- inline function set_pitch(v:Float):Float {
- _realPitch = (_pitch = v) * _timeScaleAdjust;
- if (_source != null) _source.pitch = _realPitch;
- return _pitch;
- }
- #end
-
- function set_looped(v:Bool):Bool {
- if (playing) _source.loops = v ? 999 : 0;
- return looped = v;
- }
-
- function set_loopTime(v:Float):Float {
- if (playing) _source.loopTime = v;
- return loopTime = v;
- }
-
- function set_endTime(v:Null):Null {
- if (playing) {
- if (v != null && v > 0 && v < _length) _source.length = v;
- else _source.length = null;
- }
- return endTime = v;
- }
-
- inline function getFakeTime():Float {
- if (_source.playing && _realPitch > 0 && _lastTime != null)
- return _time + (FlxG.game.getTicks() - _lastTime) * _realPitch * _timeInterpolation;
- else
- return _time;
- }
- function get_time():Float {
- if (_source == null || /*AudioManager.context == null*/funkin.backend.system.Main.audioDisconnected) return _time;
-
- final sourceTime = _source.currentTime - _source.offset - _offset;
- if (!_source.playing || _realPitch <= 0) {
- _lastTime = null;
- return _time = sourceTime;
- }
-
- final fakeTime = getFakeTime();
- if (sourceTime != _time) {
- _lastTime = FlxG.game.getTicks();
- if ((_timeInterpolation = 1 - Math.min(fakeTime - sourceTime, 1000) * 0.001) < 1 && _timeInterpolation > .9)
- return _time = fakeTime;
- else {
- _timeInterpolation = 1;
- return _time = sourceTime;
- }
- }
- else
- return fakeTime;
- }
-
- function set_time(time:Float):Float @:privateAccess {
- time = FlxMath.bound(time, _offset, length - 1);
- if (_channel != null && _realPitch > 0) {
- if (!_channel.__isValid) {
- cleanup(false, true);
- startSound(time);
- }
- else {
- _source.offset = 0;
- _source.currentTime = time + _offset;
- }
- }
-
- _lastTime = null;
- return _time = time;
- }
-
- function get_offset():Float return _offset;
- function set_offset(offset:Float):Float {
- if (_offset == (_offset = offset)) return offset;
- //time = time + _offset;
- return offset;
- }
-
- function get_length():Float return _length - _offset;
-
- //function get_latency():Float return _source != null ? _source.latency : 0;
-
- override function toString():String {
- return FlxStringUtil.getDebugString([
- LabelValuePair.weak("playing", playing),
- LabelValuePair.weak("time", time),
- LabelValuePair.weak("length", length),
- LabelValuePair.weak("volume", volume),
- LabelValuePair.weak("pitch", pitch)
- ]);
- }
-}
\ No newline at end of file
diff --git a/source/flixel/sound/FlxSoundGroup.hx b/source/flixel/sound/FlxSoundGroup.hx
deleted file mode 100644
index 9c2fcc1cbd..0000000000
--- a/source/flixel/sound/FlxSoundGroup.hx
+++ /dev/null
@@ -1,120 +0,0 @@
-package flixel.sound;
-
-/**
- * A way of grouping sounds for things such as collective volume control
- */
-class FlxSoundGroup
-{
- /**
- * The sounds in this group
- */
- public var sounds:Array = [];
-
- /**
- * The volume of this group
- */
- public var volume(default, set):Float;
-
- /**
- * Whether or not this group is muted
- */
- public var muted(default, set):Bool;
-
- /**
- * Create a new sound group
- * @param volume The initial volume of this group
- */
- public function new(volume:Float = 1)
- {
- this.volume = volume;
- }
-
- /**
- * Add a sound to this group, will remove the sound from any group it is currently in
- * @param sound The sound to add to this group
- * @return True if sound was successfully added, false otherwise
- */
- public function add(sound:FlxSound):Bool
- {
- if (!sounds.contains(sound))
- {
- // remove from prev group
- if (sound.group != null)
- sound.group.sounds.remove(sound);
-
- sounds.push(sound);
- @:bypassAccessor
- sound.group = this;
- sound.updateTransform();
- return true;
- }
- return false;
- }
-
- /**
- * Remove a sound from this group
- * @param sound The sound to remove
- * @return True if sound was successfully removed, false otherwise
- */
- public function remove(sound:FlxSound):Bool
- {
- if (sounds.contains(sound))
- {
- @:bypassAccessor
- sound.group = null;
- sounds.remove(sound);
- sound.updateTransform();
- return true;
- }
- return false;
- }
-
- /**
- * Call this function to pause all sounds in this group.
- * @since 4.3.0
- */
- public function pause():Void
- {
- for (sound in sounds)
- sound.pause();
- }
-
- /**
- * Unpauses all sounds in this group. Only works on sounds that have been paused.
- * @since 4.3.0
- */
- public function resume():Void
- {
- for (sound in sounds)
- sound.resume();
- }
-
- /**
- * Returns the volume of this group, taking `muted` in account.
- * @return The volume of the group or 0 if the group is muted.
- */
- public function getVolume():Float
- {
- return muted ? 0.0 : volume;
- }
-
- function set_volume(volume:Float):Float
- {
- this.volume = volume;
- for (sound in sounds)
- {
- sound.updateTransform();
- }
- return volume;
- }
-
- function set_muted(value:Bool):Bool
- {
- muted = value;
- for (sound in sounds)
- {
- sound.updateTransform();
- }
- return muted;
- }
-}
diff --git a/source/flx3d/Flx3DCamera.hx b/source/flx3d/Flx3DCamera.hx
index d4f76229e8..48089857b7 100644
--- a/source/flx3d/Flx3DCamera.hx
+++ b/source/flx3d/Flx3DCamera.hx
@@ -25,19 +25,25 @@ import flx3d.Flx3DUtil;
import haxe.io.Path;
import openfl.Assets;
#end
-
import flixel.FlxCamera;
-class Flx3DCamera extends FlxCamera {
+class Flx3DCamera extends FlxCamera
+{
#if THREE_D_SUPPORT
private static var __3DIDS:Int = 0;
public var view:View3D;
var meshes:Array = [];
- public function new(X:Int = 0, Y:Int = 0, Width:Int = 0, Height:Int = 0, DefaultZoom:Float = 1) {
+
+ public function new(X:Int = 0, Y:Int = 0, Width:Int = 0, Height:Int = 0, DefaultZoom:Float = 1)
+ {
if (!Flx3DUtil.is3DAvailable())
- throw "[Flx3DCamera] 3D is not available on this platform. Stages in use: " + Flx3DUtil.getTotal3D() + ", Max stages allowed: " + FlxG.stage.stage3Ds.length + ".";
+ throw "[Flx3DCamera] 3D is not available on this platform. Stages in use: "
+ + Flx3DUtil.getTotal3D()
+ + ", Max stages allowed: "
+ + FlxG.stage.stage3Ds.length
+ + ".";
super(X, Y, Width, Height, DefaultZoom);
__cur3DStageID = __3DIDS++;
@@ -49,7 +55,8 @@ class Flx3DCamera extends FlxCamera {
FlxG.stage.addChild(view);
}
- public override function render() {
+ public override function render()
+ {
super.render();
view.x = FlxG.game.x + FlxG.game.scaleX * (flashSprite.x + flashSprite.scaleX * (_scrollRect.x + _scrollRect.scaleX * (_scrollRect.scrollRect.x)));
@@ -65,7 +72,8 @@ class Flx3DCamera extends FlxCamera {
view.render();
}
- public function addModel(assetPath:String, callback:Asset3DEvent->Void, ?texturePath:String, smoothTexture:Bool = true) {
+ public function addModel(assetPath:String, callback:Asset3DEvent->Void, ?texturePath:String, smoothTexture:Bool = true)
+ {
var model = Assets.getBytes(assetPath);
if (model == null)
throw 'Model at ${assetPath} was not found.';
@@ -79,36 +87,43 @@ class Flx3DCamera extends FlxCamera {
if (texturePath != null)
material = new TextureMaterial(Cast.bitmapTexture(Assets.getBitmapData(texturePath, true, false)), smoothTexture);
- return loadData(model, context, switch(Path.extension(assetPath).toLowerCase()) {
+ return loadData(model, context, switch (Path.extension(assetPath).toLowerCase())
+ {
case "dae": new DAEParser();
case "md2": new MD2Parser();
case "md5": new MD5MeshParser();
case "awd": new AWDParser();
- default: new OBJParser();
- }, (event:Asset3DEvent) -> {
- if (event.asset != null && event.asset.assetType == Asset3DType.MESH) {
- var mesh:Mesh = cast event.asset;
- if (material != null)
- mesh.material = material;
- meshes.push(mesh);
- }
- callback(event);
- });
+ default: new OBJParser();
+ }, (event:Asset3DEvent) ->
+ {
+ if (event.asset != null && event.asset.assetType == Asset3DType.MESH)
+ {
+ var mesh:Mesh = cast event.asset;
+ if (material != null)
+ mesh.material = material;
+ meshes.push(mesh);
+ }
+ callback(event);
+ });
}
private var __cur3DStageID:Int;
private var _loaders:Map = [];
- private function loadData(data:Dynamic, context:AssetLoaderContext, parser:ParserBase, onAssetCallback:Asset3DEvent->Void):AssetLoaderToken {
+ private function loadData(data:Dynamic, context:AssetLoaderContext, parser:ParserBase, onAssetCallback:Asset3DEvent->Void):AssetLoaderToken
+ {
var token:AssetLoaderToken;
var lib:Asset3DLibraryBundle = Asset3DLibraryBundle.getInstance('Flx3DView-${__cur3DStageID}');
token = lib.loadData(data, context, null, parser);
- token.addEventListener(Asset3DEvent.ASSET_COMPLETE, (event:Asset3DEvent) -> {
+ token.addEventListener(Asset3DEvent.ASSET_COMPLETE, (event:Asset3DEvent) ->
+ {
// ! Taken from Loader3D https://github.com/openfl/away3d/blob/master/away3d/loaders/Loader3D.hx#L207-L232
- if (event.type == Asset3DEvent.ASSET_COMPLETE) {
- var obj:ObjectContainer3D = switch (event.asset.assetType) {
+ if (event.type == Asset3DEvent.ASSET_COMPLETE)
+ {
+ var obj:ObjectContainer3D = switch (event.asset.assetType)
+ {
case Asset3DType.LIGHT: expect(event.asset, LightBase);
case Asset3DType.CONTAINER: expect(event.asset, ObjectContainer3D);
case Asset3DType.MESH: expect(event.asset, Mesh);
@@ -126,25 +141,29 @@ class Flx3DCamera extends FlxCamera {
onAssetCallback(event);
});
- token.addEventListener(LoaderEvent.RESOURCE_COMPLETE, (_) -> {
+ token.addEventListener(LoaderEvent.RESOURCE_COMPLETE, (_) ->
+ {
trace("Loader Finished...");
});
- _loaders.set(lib,token);
+ _loaders.set(lib, token);
return token;
}
- override function destroy() {
+ override function destroy()
+ {
if (meshes != null)
- for(mesh in meshes)
+ for (mesh in meshes)
mesh.dispose();
var bundle = Asset3DLibraryBundle.getInstance('Flx3DView-${__cur3DStageID}');
bundle.stopAllLoadingSessions();
@:privateAccess {
- if (bundle._loadingSessions != null) {
- for(load in bundle._loadingSessions) {
+ if (bundle._loadingSessions != null)
+ {
+ for (load in bundle._loadingSessions)
+ {
load.dispose();
}
}
@@ -152,10 +171,12 @@ class Flx3DCamera extends FlxCamera {
}
FlxG.stage.removeChild(view);
- try {
+ try
+ {
view.dispose();
- } catch(e) {
-
+ }
+ catch (e)
+ {
}
super.destroy();
@@ -164,4 +185,4 @@ class Flx3DCamera extends FlxCamera {
public function addChild(c)
view.scene.addChild(c);
#end
-}
\ No newline at end of file
+}
diff --git a/source/flx3d/Flx3DView.hx b/source/flx3d/Flx3DView.hx
index 59413e77db..9004d27af8 100644
--- a/source/flx3d/Flx3DView.hx
+++ b/source/flx3d/Flx3DView.hx
@@ -25,20 +25,27 @@ import away3d.utils.Utils.expect;
#end
// FlxView3D with helpers for easier updating
-class Flx3DView extends FlxView3D {
+class Flx3DView extends FlxView3D
+{
#if THREE_D_SUPPORT
private static var __3DIDS:Int = 0;
var meshes:Array = [];
- public function new(x:Float = 0, y:Float = 0, width:Int = -1, height:Int = -1) {
+
+ public function new(x:Float = 0, y:Float = 0, width:Int = -1, height:Int = -1)
+ {
if (!Flx3DUtil.is3DAvailable())
- throw "[Flx3DView] 3D is not available on this platform. Stages in use: " + Flx3DUtil.getUsed3D() + ", Max stages allowed: " + Flx3DUtil.getTotal3D() + ".";
+ throw "[Flx3DView] 3D is not available on this platform. Stages in use: "
+ + Flx3DUtil.getUsed3D()
+ + ", Max stages allowed: "
+ + Flx3DUtil.getTotal3D()
+ + ".";
super(x, y, width, height);
__cur3DStageID = __3DIDS++;
}
- public function addModel(assetPath:String, callback:Asset3DEvent->Void, ?texturePath:String, smoothTexture:Bool = true) {
-
+ public function addModel(assetPath:String, callback:Asset3DEvent->Void, ?texturePath:String, smoothTexture:Bool = true)
+ {
var model = Assets.getBytes(assetPath);
if (model == null)
throw 'Model at ${assetPath} was not found.';
@@ -52,37 +59,44 @@ class Flx3DView extends FlxView3D {
if (texturePath != null)
material = new TextureMaterial(Cast.bitmapTexture(Assets.getBitmapData(texturePath, true, false)), smoothTexture);
- return loadData(model, context, switch(Path.extension(assetPath).toLowerCase()) {
+ return loadData(model, context, switch (Path.extension(assetPath).toLowerCase())
+ {
case "dae": new DAEParser();
case "md2": new MD2Parser();
case "md5": new MD5MeshParser();
case "awd": new AWDParser();
- default: new OBJParser();
- }, (event:Asset3DEvent) -> {
- if (event.asset != null && event.asset.assetType == Asset3DType.MESH) {
- var mesh:Mesh = cast event.asset;
- if (material != null)
- mesh.material = material;
- meshes.push(mesh);
- }
- callback(event);
- });
+ default: new OBJParser();
+ }, (event:Asset3DEvent) ->
+ {
+ if (event.asset != null && event.asset.assetType == Asset3DType.MESH)
+ {
+ var mesh:Mesh = cast event.asset;
+ if (material != null)
+ mesh.material = material;
+ meshes.push(mesh);
+ }
+ callback(event);
+ });
}
private var __cur3DStageID:Int;
private var _loaders:Map = [];
- private function loadData(data:Dynamic, context:AssetLoaderContext, parser:ParserBase, onAssetCallback:Asset3DEvent->Void):AssetLoaderToken {
+ private function loadData(data:Dynamic, context:AssetLoaderContext, parser:ParserBase, onAssetCallback:Asset3DEvent->Void):AssetLoaderToken
+ {
var token:AssetLoaderToken;
var lib:Asset3DLibraryBundle;
lib = Asset3DLibraryBundle.getInstance('Flx3DView-${__cur3DStageID}');
token = lib.loadData(data, context, null, parser);
- token.addEventListener(Asset3DEvent.ASSET_COMPLETE, (event:Asset3DEvent) -> {
+ token.addEventListener(Asset3DEvent.ASSET_COMPLETE, (event:Asset3DEvent) ->
+ {
// ! Taken from Loader3D https://github.com/openfl/away3d/blob/master/away3d/loaders/Loader3D.hx#L207-L232
- if (event.type == Asset3DEvent.ASSET_COMPLETE) {
- var obj:ObjectContainer3D = switch (event.asset.assetType) {
+ if (event.type == Asset3DEvent.ASSET_COMPLETE)
+ {
+ var obj:ObjectContainer3D = switch (event.asset.assetType)
+ {
case Asset3DType.LIGHT: expect(event.asset, LightBase);
case Asset3DType.CONTAINER: expect(event.asset, ObjectContainer3D);
case Asset3DType.MESH: expect(event.asset, Mesh);
@@ -100,26 +114,29 @@ class Flx3DView extends FlxView3D {
onAssetCallback(event);
});
- token.addEventListener(LoaderEvent.RESOURCE_COMPLETE, (_) -> {
+ token.addEventListener(LoaderEvent.RESOURCE_COMPLETE, (_) ->
+ {
trace("Loader Finished...");
-
});
- _loaders.set(lib,token);
+ _loaders.set(lib, token);
return token;
}
- override function destroy() {
+ override function destroy()
+ {
if (meshes != null)
- for(mesh in meshes)
+ for (mesh in meshes)
mesh.dispose();
var bundle = Asset3DLibraryBundle.getInstance('Flx3DView-${__cur3DStageID}');
bundle.stopAllLoadingSessions();
@:privateAccess {
- if (bundle._loadingSessions != null) {
- for(load in bundle._loadingSessions) {
+ if (bundle._loadingSessions != null)
+ {
+ for (load in bundle._loadingSessions)
+ {
load.dispose();
}
}
@@ -132,4 +149,4 @@ class Flx3DView extends FlxView3D {
public function addChild(c)
view.scene.addChild(c);
#end
-}
\ No newline at end of file
+}
diff --git a/source/flx3d/FlxView3D.hx b/source/flx3d/FlxView3D.hx
index 11137eed3f..b07b78e213 100644
--- a/source/flx3d/FlxView3D.hx
+++ b/source/flx3d/FlxView3D.hx
@@ -1,6 +1,7 @@
package flx3d;
#if THREE_D_SUPPORT
+import flx3d._internal.TextureView3D;
import away3d.containers.View3D;
import away3d.library.assets.IAsset;
import flixel.FlxG;
@@ -18,7 +19,35 @@ import flixel.FlxSprite;
class FlxView3D extends FlxSprite
{
#if THREE_D_SUPPORT
- @:noCompletion private var bmp:BitmapData;
+ @:noCompletion private var bmp:BitmapData = null;
+ private var _textureView:TextureView3D;
+ private var legacyRender:Bool = false;
+
+ // With new rendering, it seems to only work if 2 or more views are being rendered.
+ // This workaround creates a second instance if there is only one view.
+ // "There is nothing more permanent than a temporary solution"
+ // -idk who said this i just wanted to quote it
+ private static var workaroundInstance:FlxView3D;
+ private static var createdWorkaround:Bool = false;
+
+ private inline function createWorkaround()
+ {
+ if (!createdWorkaround && !legacyRender)
+ {
+ createdWorkaround = true;
+ workaroundInstance = new FlxView3D(0, 0, 1, 1);
+ FlxG.state.add(workaroundInstance);
+ }
+ }
+
+ private inline function destroyWorkaround()
+ {
+ if (workaroundInstance == this)
+ {
+ workaroundInstance = null;
+ createdWorkaround = false;
+ }
+ }
/**
* The Away3D View
@@ -40,19 +69,39 @@ class FlxView3D extends FlxSprite
*/
public function new(x:Float = 0, y:Float = 0, width:Int = -1, height:Int = -1)
{
+ legacyRender = false;
+
super(x, y);
+ if (legacyRender)
+ {
+ view = new View3D();
+ }
+ else
+ {
+ _textureView = new TextureView3D();
+ _textureView.onUpdateBitmap = function(bitmap)
+ {
+ bmp = bitmap;
+ loadGraphic(bmp);
+ }
+
+ view = _textureView;
+ }
- view = new View3D();
view.visible = false;
- view.width = width == -1 ? FlxG.width : width;
- view.height = height == -1 ? FlxG.height : height;
+ this.width = width == -1 ? FlxG.width : width;
+ this.height = height == -1 ? FlxG.height : height;
+ if (legacyRender)
+ {
+ bmp = new BitmapData(Std.int(view.width), Std.int(view.height), true, 0x0);
+ loadGraphic(bmp);
+ }
view.backgroundAlpha = 0;
FlxG.stage.addChildAt(view, 0);
- bmp = new BitmapData(Std.int(view.width), Std.int(view.height), true, 0x0);
- loadGraphic(bmp);
+ createWorkaround();
}
/**
@@ -84,26 +133,34 @@ class FlxView3D extends FlxSprite
view.dispose();
view = null;
}
+
+ destroyWorkaround();
}
@:noCompletion override function draw()
{
- super.draw();
-
if (dirty3D)
{
- view.visible = false;
- FlxG.stage.addChildAt(view, 0);
-
- var old = FlxG.game.filters;
- FlxG.game.filters = null;
-
- view.renderer.queueSnapshot(bmp);
- view.render();
-
- FlxG.game.filters = old;
- FlxG.stage.removeChild(view);
+ if (legacyRender)
+ {
+ view.visible = false;
+ FlxG.stage.addChildAt(view, 0);
+
+ var old = FlxG.game.filters;
+ FlxG.game.filters = null;
+
+ view.renderer.queueSnapshot(bmp);
+ view.render();
+
+ FlxG.game.filters = old;
+ FlxG.stage.removeChild(view);
+ }
+ else
+ {
+ view.render();
+ }
}
+ super.draw();
}
@:noCompletion override function set_width(newWidth:Float):Float
diff --git a/source/flx3d/_internal/TextureView3D.hx b/source/flx3d/_internal/TextureView3D.hx
new file mode 100644
index 0000000000..efce9b9a4f
--- /dev/null
+++ b/source/flx3d/_internal/TextureView3D.hx
@@ -0,0 +1,177 @@
+package flx3d._internal;
+
+import flixel.FlxG;
+import haxe.Exception;
+import away3d.containers.View3D;
+import away3d.containers.Scene3D;
+import away3d.cameras.Camera3D;
+import away3d.core.render.RendererBase;
+import away3d.core.managers.Stage3DManager;
+import away3d.core.managers.Stage3DProxy;
+import openfl.display3D.textures.TextureBase;
+import openfl.display3D.textures.RectangleTexture;
+import openfl.display3D.Context3D;
+import openfl.display.BitmapData;
+import openfl.events.Event;
+import openfl.geom.Point;
+
+class TextureView3D extends View3D
+{
+ public var bitmap:BitmapData;
+
+ private var _framebuffer:TextureBase = null;
+
+ public var onUpdateBitmap:(BitmapData) -> Void;
+
+ /**
+ * Prevents the engine from disposing Flixel's Stage3D/Context3D instance
+ */
+ public override function dispose() @:privateAccess {
+ _stage3DProxy = null;
+ super.dispose();
+ }
+
+ private override function set_height(value:Float):Float
+ {
+ if (_height == value)
+ return value;
+ super.set_height(value);
+ _createFramebuffer();
+ return value;
+ }
+
+ private override function set_width(value:Float):Float
+ {
+ if (_width == value)
+ return value;
+ super.set_width(value);
+ _createFramebuffer();
+ return value;
+ }
+
+ public function new(scene:Scene3D = null, camera:Camera3D = null, renderer:RendererBase = null, forceSoftware:Bool = false, profile:String = "baseline",
+ contextIndex:Int = -1)
+ {
+ super(scene, camera, renderer, forceSoftware, profile, contextIndex);
+
+ _stage3DProxy = Stage3DManager.getInstance(FlxG.stage).getStage3DProxy(0);
+ _createFramebuffer();
+ }
+
+ private function _createFramebuffer()
+ {
+ if (width == 0 || height == 0)
+ return;
+ if (_framebuffer != null)
+ _framebuffer.dispose();
+ _framebuffer = FlxG.stage.context3D.createRectangleTexture(Std.int(_width), Std.int(_height), BGRA, true);
+ bitmap = BitmapDataCrashFix.fromTextureCrashFix(_framebuffer);
+ onUpdateBitmap(bitmap);
+ }
+
+ /**
+ * Renders the view.
+ */
+ public override function render():Void
+ {
+ Stage3DProxy.drawTriangleCount = 0;
+
+ // if context3D has Disposed by the OS,don't render at this frame
+ if (stage3DProxy.context3D == null || !stage3DProxy.recoverFromDisposal())
+ {
+ _backBufferInvalid = true;
+ return;
+ }
+
+ // reset or update render settings
+ if (_backBufferInvalid)
+ updateBackBuffer();
+
+ if (_shareContext && _layeredView)
+ stage3DProxy.clearDepthBuffer();
+
+ if (!_parentIsStage)
+ {
+ var globalPos:Point = parent.localToGlobal(_localTLPos);
+ if (_globalPos.x != globalPos.x || _globalPos.y != globalPos.y)
+ {
+ _globalPos = globalPos;
+ _globalPosDirty = true;
+ }
+ }
+
+ if (_globalPosDirty)
+ updateGlobalPos();
+
+ updateTime();
+
+ updateViewSizeData();
+
+ _entityCollector.clear();
+
+ // collect stuff to render
+ _scene.traversePartitions(_entityCollector);
+
+ // update picking
+ _mouse3DManager.updateCollider(this);
+ _touch3DManager.updateCollider();
+
+ if (_requireDepthRender)
+ renderSceneDepthToTexture(_entityCollector);
+
+ // todo: perform depth prepass after light update and before final render
+ if (_depthPrepass)
+ renderDepthPrepass(_entityCollector);
+
+ @:privateAccess _renderer.clearOnRender = !_depthPrepass;
+
+ if (_filter3DRenderer != null && _stage3DProxy.context3D != null)
+ {
+ _renderer.render(_entityCollector, _filter3DRenderer.getMainInputTexture(_stage3DProxy), _rttBufferManager.renderToTextureRect);
+ _filter3DRenderer.render(_stage3DProxy, camera, _depthRender);
+ }
+ else
+ {
+ @:privateAccess _renderer.shareContext = _shareContext;
+ if (_shareContext)
+ _renderer.render(_entityCollector, _framebuffer, _scissorRect);
+ else
+ _renderer.render(_entityCollector, _framebuffer);
+ }
+
+ if (!_shareContext)
+ {
+ stage3DProxy.present();
+
+ // fire collected mouse events
+ _mouse3DManager.fireMouseEvents();
+ _touch3DManager.fireTouchEvents();
+ }
+
+ // clean up data for this render
+ _entityCollector.cleanUp();
+
+ // register that a view has been rendered
+ stage3DProxy.bufferClear = false;
+ }
+}
+
+class BitmapDataCrashFix extends BitmapData
+{
+ public static function fromTextureCrashFix(texture:TextureBase):BitmapDataCrashFix @:privateAccess {
+ if (texture == null)
+ return null;
+
+ var bitmapData = new BitmapDataCrashFix(texture.__width, texture.__height, true, 0);
+ bitmapData.readable = false;
+ bitmapData.__texture = texture;
+ bitmapData.__textureContext = texture.__textureContext;
+ bitmapData.image = null;
+ return bitmapData;
+ }
+
+ @:dox(hide) public override function getTexture(context:Context3D):TextureBase
+ {
+ return __texture;
+ }
+}
diff --git a/source/funkin/backend/assets/MultiFramesCollection.hx b/source/funkin/backend/assets/MultiFramesCollection.hx
index 2a03c64cdd..d4bed77bf2 100644
--- a/source/funkin/backend/assets/MultiFramesCollection.hx
+++ b/source/funkin/backend/assets/MultiFramesCollection.hx
@@ -47,7 +47,7 @@ class MultiFramesCollection extends FlxFramesCollection
public function addFrames(collection:FlxFramesCollection) {
if (collection == null || collection.frames == null) return;
- collection.parent.useCount++;
+ collection.parent.incrementUseCount();
parentedFrames.push(collection);
for(f in collection.frames) {
@@ -63,7 +63,7 @@ class MultiFramesCollection extends FlxFramesCollection
if(parentedFrames != null) {
for(collection in parentedFrames) {
if(collection != null)
- collection.parent.useCount--;
+ collection.parent.decrementUseCount();
}
parentedFrames = null;
}
diff --git a/source/funkin/backend/scripting/Script.hx b/source/funkin/backend/scripting/Script.hx
index 4a03275f2a..9db6dcb5b5 100644
--- a/source/funkin/backend/scripting/Script.hx
+++ b/source/funkin/backend/scripting/Script.hx
@@ -89,7 +89,7 @@ class Script extends FlxBasic implements IFlxDestroyable {
"Paths" => funkin.backend.assets.Paths,
"Conductor" => funkin.backend.system.Conductor,
"FunkinShader" => funkin.backend.shaders.FunkinShader,
- "CustomShader" => funkin.backend.shaders.CustomShader,
+ "CustomShader" => funkin.backend.shaders.CustomShader, // deprecated
"FunkinText" => funkin.backend.FunkinText,
"FlxAnimate" => animate.FlxAnimate,
"FunkinSprite" => funkin.backend.FunkinSprite,
diff --git a/source/funkin/backend/shaders/BlendModeEffect.hx b/source/funkin/backend/shaders/BlendModeEffect.hx
deleted file mode 100644
index 2865829285..0000000000
--- a/source/funkin/backend/shaders/BlendModeEffect.hx
+++ /dev/null
@@ -1,36 +0,0 @@
-package funkin.backend.shaders;
-
-import flixel.util.FlxColor;
-import openfl.display.ShaderParameter;
-
-@:dox(hide)
-typedef BlendModeShader =
-{
- var uBlendColor:ShaderParameter;
-}
-
-@:dox(hide)
-class BlendModeEffect
-{
- public var shader(default, null):BlendModeShader;
-
- @:isVar
- public var color(default, set):FlxColor;
-
- public function new(shader:BlendModeShader, color:FlxColor):Void
- {
- shader.uBlendColor.value = [];
- this.shader = shader;
- this.color = color;
- }
-
- function set_color(color:FlxColor):FlxColor
- {
- shader.uBlendColor.value[0] = color.redFloat;
- shader.uBlendColor.value[1] = color.greenFloat;
- shader.uBlendColor.value[2] = color.blueFloat;
- shader.uBlendColor.value[3] = color.alphaFloat;
-
- return this.color = color;
- }
-}
diff --git a/source/funkin/backend/shaders/CustomShader.hx b/source/funkin/backend/shaders/CustomShader.hx
index 62308e69ee..61066575c2 100644
--- a/source/funkin/backend/shaders/CustomShader.hx
+++ b/source/funkin/backend/shaders/CustomShader.hx
@@ -2,39 +2,15 @@ package funkin.backend.shaders;
import openfl.Assets;
-/**
- * Class for custom shaders.
- *
- * To create one, create a `shaders` folder in your assets/mod folder, then add a file named `my-shader.frag` or/and `my-shader.vert`.
- *
- * Non-existent shaders will only load the default one, and throw a warning in the console.
- *
- * To access the shader's uniform variables, use `shader.variable`
- */
+@:deprecated("Use funkin.backend.shaders.FunkinShader.fromFile instead.")
class CustomShader extends FunkinShader {
- public var path:String = "";
+ @:isVar
+ public var path(get, set):String;
+ inline function get_path():String return path != null ? path : _fragmentFilePath + _vertexFilePath;
+ inline function set_path(v:Null):String return path = cast v;
- /**
- * Creates a new custom shader
- * @param name Name of the frag and vert files.
- * @param glslVersion GLSL version to use. Defaults to `120`.
- */
- public function new(name:String, glslVersion:String = null) {
- if (glslVersion == null) glslVersion = Flags.DEFAULT_GLSL_VERSION;
- var fragShaderPath = Paths.fragShader(name);
- var vertShaderPath = Paths.vertShader(name);
- var fragCode = Assets.exists(fragShaderPath) ? Assets.getText(fragShaderPath) : null;
- var vertCode = Assets.exists(vertShaderPath) ? Assets.getText(vertShaderPath) : null;
-
- fileName = name;
- fragFileName = fragShaderPath;
- vertFileName = vertShaderPath;
-
- path = fragShaderPath+vertShaderPath;
-
- if (fragCode == null && vertCode == null)
- Logs.error('Shader "$name" couldn\'t be found.');
-
- super(fragCode, vertCode, glslVersion);
+ public function new(name:String, ?glslVersion:String) {
+ super();
+ loadShaderFile(Paths.fragShader(name), Paths.vertShader(name), glslVersion);
}
}
\ No newline at end of file
diff --git a/source/funkin/backend/shaders/FunkinShader.hx b/source/funkin/backend/shaders/FunkinShader.hx
index 281616b03b..58fa9a4b8f 100644
--- a/source/funkin/backend/shaders/FunkinShader.hx
+++ b/source/funkin/backend/shaders/FunkinShader.hx
@@ -1,533 +1,65 @@
package funkin.backend.shaders;
-import flixel.graphics.FlxGraphic;
-import flixel.system.FlxAssets.FlxShader;
-import flixel.util.FlxSignal.FlxTypedSignal;
+import haxe.io.Path;
import haxe.Exception;
+
import hscript.IHScriptCustomBehaviour;
-import lime.utils.Float32Array;
+
import openfl.display.BitmapData;
-import openfl.display.ShaderInput;
+import openfl.display.Shader;
import openfl.display.ShaderParameter;
import openfl.display.ShaderParameterType;
+import openfl.display.ShaderPrecision;
+import openfl.display.ShaderInput;
import openfl.display3D._internal.GLProgram;
import openfl.display3D._internal.GLShader;
+import openfl.display3D.Program3D;
import openfl.utils._internal.Log;
+import openfl.utils.GLSLSourceAssembler;
+
+import flixel.addons.display.FlxRuntimeShader;
+import flixel.graphics.FlxGraphic;
+import flixel.util.FlxSignal.FlxTypedSignal;
+import flixel.util.FlxStringUtil;
-using StringTools;
@:access(openfl.display3D.Context3D)
@:access(openfl.display3D.Program3D)
@:access(openfl.display.ShaderInput)
@:access(openfl.display.ShaderParameter)
-class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
- private static var __instanceFields = Type.getInstanceFields(FunkinShader);
- private static var FRAGMENT_SHADER = 0;
- private static var VERTEX_SHADER = 1;
-
+class FunkinShader extends FlxRuntimeShader implements IHScriptCustomBehaviour {
public var onGLUpdate:FlxTypedSignalVoid> = new FlxTypedSignalVoid>();
- public var onProcessGLData:FlxTypedSignal<(String, String)->Void> = new FlxTypedSignal<(String, String)->Void>();
-
- public var glslVer:String = Flags.DEFAULT_GLSL_VERSION;
- public var fileName:String = "FunkinShader";
- public var fragFileName:String = "FunkinShader";
- public var vertFileName:String = "FunkinShader";
-
- public var shaderPrefix:String = "";
- public var fragmentPrefix:String = "";
- public var vertexPrefix:String = "";
-
- /**
- * Creates a new shader from the specified fragment and vertex source.
- * Accepts `#pragma header`.
- * @param frag Fragment source (pass `null` to use default)
- * @param vert Vertex source (pass `null` to use default)
- * @param glslVer Version of GLSL to use (defaults to 120)
- */
- public override function new(frag:String, vert:String, glslVer:String = null) {
- if (glslVer == null) glslVer = Flags.DEFAULT_GLSL_VERSION;
- if (frag == null) frag = ShaderTemplates.defaultFragmentSource;
- if (vert == null) vert = ShaderTemplates.defaultVertexSource;
- this.glFragmentSource = frag;
- this.glVertexSource = vert;
-
- this.glslVer = glslVer;
- super();
- }
-
- static var IMPORT_REGEX = ~/#import\s+<(.*)>/;
-
- private function processImports(value:String, type:Int):String
- {
- while(IMPORT_REGEX.match(value))
- {
- var importPath = IMPORT_REGEX.matched(1);
- var importSource = Assets.getText("assets/shaders/" + importPath);
- if(importSource == null) {
- var fileName = type == FRAGMENT_SHADER ? fragFileName : vertFileName;
- Logs.traceColored([
- Logs.logText('[Shader] ', RED),
- Logs.logText('Failed to import shader ${importPath} in ${fileName}', RED),
- ]);
- } else {
- value = value.replace(IMPORT_REGEX.matched(0), importSource);
- }
- }
- return value;
- }
-
- static var ERROR_POS_REGEX = ~/(\d+):(\d+): (.*)/g;
- static var ERROR_REGEX = ~/ERROR: (\d+):(\d+): (.*)/g;
- static var ERROR_REGEX_2 = ~/(\d+)\((\d+)\) : error ([^:]+): (.*)/g;
- @:noCompletion private override function __createGLShader(source:String, type:Int):GLShader
- {
- var gl = __context.gl;
-
- var shader = gl.createShader(type);
- gl.shaderSource(shader, source);
- gl.compileShader(shader);
- var shaderInfoLog = gl.getShaderInfoLog(shader);
- var hasInfoLog = shaderInfoLog != null && StringTools.trim(shaderInfoLog) != "";
- var compileStatus = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
-
- if (hasInfoLog || compileStatus == 0)
- {
- var isVertexShader = type == gl.VERTEX_SHADER;
- var messageBuf = new StringBuf();
- messageBuf.add((compileStatus == 0) ? "Error" : "Info");
- if(isVertexShader) {
- messageBuf.add(" compiling vertex shader");
- if(vertFileName != null && vertFileName.length > 0) {
- messageBuf.add(" (" + vertFileName + ")");
- }
- } else {
- messageBuf.add(" compiling fragment shader");
- if(fragFileName != null && fragFileName.length > 0) {
- messageBuf.add(" (" + fragFileName + ")");
- }
- }
- messageBuf.add("\n");
- var errorPositions = [];
- var regex = null;
- var tmp = shaderInfoLog;
- if(shaderInfoLog.contains(" : error ")) {
- regex = ERROR_REGEX_2;
-
- while(regex.match(tmp)) {
- errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(4)));
- tmp = regex.matchedRight();
- }
- } else if(shaderInfoLog.contains("ERROR: ")) {
- regex = ERROR_REGEX;
-
- while(regex.match(tmp)) {
- errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(3)));
- tmp = regex.matchedRight();
- }
- } else {
- regex = ERROR_POS_REGEX;
-
- while(regex.match(tmp)) {
- errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(3)));
- tmp = regex.matchedRight();
- }
- }
- var splitSource = source.split("\n");
- for(error in errorPositions) {
- messageBuf.add("ERROR: Line: " + error.line);
- if(error.column > 0) {
- messageBuf.add(", Column: " + error.column);
- }
- messageBuf.add(", " + error.message);
- if(error.line < splitSource.length) {
- messageBuf.add("\nLine: ");
- messageBuf.add(splitSource[error.line-1].trim());
- }
- messageBuf.add("\n\n");
- }
- var hasErrorPosition = errorPositions.length > 0;
- if(hasErrorPosition) {
- messageBuf.add("Raw shader info log:\n");
- }
- messageBuf.add(shaderInfoLog);
- messageBuf.add("\n");
- messageBuf.add(source);
-
- var message = messageBuf.toString();
- if (compileStatus == 0) Log.error(message);
- else if (hasInfoLog) Log.debug(message);
- }
-
- return shader;
- }
-
- @:noCompletion private override function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram
- {
- var program:GLProgram = null;
- try
- {
- var gl = __context.gl;
-
- var vertexShader = __createGLShader(vertexSource, gl.VERTEX_SHADER);
- var fragmentShader = __createGLShader(fragmentSource, gl.FRAGMENT_SHADER);
-
- program = gl.createProgram();
-
- // Fix support for drivers that don't draw if attribute 0 is disabled
- for (param in __paramFloat)
- {
- if (param.name.indexOf("Position") > -1 && StringTools.startsWith(param.name, "openfl_"))
- {
- gl.bindAttribLocation(program, 0, param.name);
- break;
- }
- }
-
- gl.attachShader(program, vertexShader);
- gl.attachShader(program, fragmentShader);
- gl.linkProgram(program);
-
- if (gl.getProgramParameter(program, gl.LINK_STATUS) == 0)
- {
- var messageBuf = new StringBuf();
- messageBuf.add("Unable to initialize the shader program");
- messageBuf.add("\n");
- messageBuf.add(gl.getProgramInfoLog(program));
- var message = messageBuf.toString();
- Log.error(message);
- }
- }
- catch (error:Dynamic)
- {
- Logs.traceColored([
- Logs.logText('[Shader] ', BLUE),
- Logs.logText('Failed to compile shader ${fileName}: ', RED),
- Logs.logText(Std.string(error))
- ], TRACE);
- }
- return program;
-
- return program;
- }
- var glRawFragmentSource:String;
- var glRawVertexSource:String;
-
- @:noCompletion override private function set_glFragmentSource(value:String):String
- {
- if(value == null)
- value = ShaderTemplates.defaultFragmentSource;
- glRawFragmentSource = value;
- value = processImports(value, FRAGMENT_SHADER);
- value = value.replace("#pragma header", ShaderTemplates.fragHeader).replace("#pragma body", ShaderTemplates.fragBody);
- if (value != __glFragmentSource)
- {
- __glSourceDirty = true;
- }
-
- return __glFragmentSource = value;
- }
-
- @:noCompletion override private function set_glVertexSource(value:String):String
- {
- if(value == null)
- value = ShaderTemplates.defaultVertexSource;
- glRawVertexSource = value;
- value = processImports(value, VERTEX_SHADER);
-
- var useBackCompat:Bool = true;
- for (regex in ShaderTemplates.vertBackCompatVarList) if (!regex.match(value)) {
- useBackCompat = false;
- break;
- }
-
- var header = useBackCompat ? ShaderTemplates.vertHeaderBackCompat : ShaderTemplates.vertHeader;
- var body = useBackCompat ? ShaderTemplates.vertBodyBackCompat : ShaderTemplates.vertBody;
-
- value = value.replace("#pragma header", header).replace("#pragma body", body);
-
- if (value != __glVertexSource)
- {
- __glSourceDirty = true;
- }
-
- return __glVertexSource = value;
- }
-
- override function __updateGL():Void {
- onGLUpdate.dispatch();
- super.__updateGL();
+ public function new(?fragmentSource:String, ?vertexSource:String, ?version:String) {
+ super(fragmentSource, vertexSource, version ?? (fragmentSource != null || vertexSource != null ? Flags.DEFAULT_GLSL_VERSION : null));
}
- @:noCompletion private override function __initGL():Void
- {
- if (__glSourceDirty || __paramBool == null)
- {
- __glSourceDirty = false;
- program = null;
-
- __inputBitmapData = new Array();
- __paramBool = new Array();
- __paramFloat = new Array();
- __paramInt = new Array();
-
- __processGLData(glVertexSource, "attribute");
- __processGLData(glVertexSource, "uniform");
- __processGLData(glFragmentSource, "uniform");
- }
-
- if (__context != null && program == null)
- {
- var prefixBuf = new StringBuf();
- prefixBuf.add('#version ${glslVer}\n');
- prefixBuf.add(shaderPrefix);
-
- var gl = __context.gl;
-
- prefixBuf.add("#ifdef GL_ES\n");
- if (precisionHint == FULL) {
- prefixBuf.add("#ifdef GL_FRAGMENT_PRECISION_HIGH\n");
- prefixBuf.add("precision highp float;\n");
- prefixBuf.add("#else\n");
- prefixBuf.add("precision mediump float;\n");
- prefixBuf.add("#endif\n");
- } else {
- prefixBuf.add("precision lowp float;\n");
- }
- prefixBuf.add("#endif\n");
-
- var prefix = prefixBuf.toString();
-
- var vertex = prefix + vertexPrefix + glVertexSource;
- var fragment = prefix + fragmentPrefix + glFragmentSource;
-
- var id = vertex + fragment;
-
- if (__context.__programs.exists(id))
- {
- program = __context.__programs.get(id);
- }
- else
- {
- program = __context.createProgram(GLSL);
- program.__glProgram = __createGLProgram(vertex, fragment);
- __context.__programs.set(id, program);
- }
-
- if (program != null)
- {
- glProgram = program.__glProgram;
-
- for (input in __inputBitmapData) {
-
- if (input.__isUniform) {
- input.index = gl.getUniformLocation(glProgram, input.name);
- } else {
- input.index = gl.getAttribLocation(glProgram, input.name);
- }
- }
-
- for (parameter in __paramBool) {
- if (parameter.__isUniform) {
- parameter.index = gl.getUniformLocation(glProgram, parameter.name);
- } else {
- parameter.index = gl.getAttribLocation(glProgram, parameter.name);
- }
- }
-
- for (parameter in __paramFloat) {
- if (parameter.__isUniform) {
- parameter.index = gl.getUniformLocation(glProgram, parameter.name);
- } else {
- parameter.index = gl.getAttribLocation(glProgram, parameter.name);
- }
- }
-
- for (parameter in __paramInt) {
- if (parameter.__isUniform) {
- parameter.index = gl.getUniformLocation(glProgram, parameter.name);
- } else {
- parameter.index = gl.getAttribLocation(glProgram, parameter.name);
- }
- }
- }
- // initInstance(vertex, fragment); // btw make sure to disable the prefixes for ._isInstance
- }
+ public static function fromFile(fragmentPath:String, ?vertexPath:String, ?version:String):FunkinShader {
+ return new FunkinShader().loadShaderFile(fragmentPath, vertexPath, version);
}
- var __cancelNextProcessGLData:Bool = false;
- @:noCompletion private override function __processGLData(source:String, storageType:String):Void
- {
- onProcessGLData.dispatch(source, storageType);
- if (__cancelNextProcessGLData != (__cancelNextProcessGLData = false))
- return;
- var lastMatch = 0, position, regex, name, type;
-
- if (storageType == "uniform")
- {
- regex = ~/uniform ([A-Za-z0-9]+) ([A-Za-z0-9_]+)/;
- }
- else
- {
- regex = ~/attribute ([A-Za-z0-9]+) ([A-Za-z0-9_]+)/;
+ public function loadShaderFile(fragmentPath:String, ?vertexPath:String, ?version:String):FunkinShader {
+ if (vertexPath == null) {
+ final idx = fragmentPath.lastIndexOf(".");
+ if (idx == -1) vertexPath = fragmentPath;
+ else vertexPath = fragmentPath.substr(0, idx);
}
- while (regex.matchSub(source, lastMatch))
- {
- type = regex.matched(1);
- name = regex.matched(2);
-
- if (StringTools.startsWith(name, "gl_"))
- {
- continue;
- }
+ fragmentPath = FlxRuntimeShader._getPath(fragmentPath, false);
+ vertexPath = FlxRuntimeShader._getPath(vertexPath, true);
+ _fromFile(fragmentPath, vertexPath, version ?? (fragmentPath != null || vertexPath != null ? Flags.DEFAULT_GLSL_VERSION : null));
- var isUniform = (storageType == "uniform");
- registerParameter(name, type, isUniform);
-
- position = regex.matchedPos();
- lastMatch = position.pos + position.len;
- }
+ return this;
}
- function registerParameter(name:String, type:String, isUniform:Bool):Void
- {
- if (StringTools.startsWith(type, "sampler"))
- {
- var input = new ShaderInput();
- input.name = name;
- input.__isUniform = isUniform;
- __inputBitmapData.push(input);
-
- switch (name)
- {
- case "openfl_Texture":
- __texture = input;
- case "bitmap":
- __bitmap = input;
- default:
- }
-
- Reflect.setField(__data, name, input);
- try{Reflect.setField(this, name, input);} catch(e) {}
- }
- else if (!Reflect.hasField(__data, name) || Reflect.field(__data, name) == null)
- {
- var parameterType:ShaderParameterType = switch (type)
- {
- case "bool": BOOL;
- case "double", "float": FLOAT;
- case "int", "uint": INT;
- case "bvec2": BOOL2;
- case "bvec3": BOOL3;
- case "bvec4": BOOL4;
- case "ivec2", "uvec2": INT2;
- case "ivec3", "uvec3": INT3;
- case "ivec4", "uvec4": INT4;
- case "vec2", "dvec2": FLOAT2;
- case "vec3", "dvec3": FLOAT3;
- case "vec4", "dvec4": FLOAT4;
- case "mat2", "mat2x2": MATRIX2X2;
- case "mat2x3": MATRIX2X3;
- case "mat2x4": MATRIX2X4;
- case "mat3x2": MATRIX3X2;
- case "mat3", "mat3x3": MATRIX3X3;
- case "mat3x4": MATRIX3X4;
- case "mat4x2": MATRIX4X2;
- case "mat4x3": MATRIX4X3;
- case "mat4", "mat4x4": MATRIX4X4;
- default: null;
- }
+ #if REGION /* IHScriptCustomBehaviour */
+ public function hget(name:String):Dynamic {
+ if (__glSourceDirty) __init();
- var length = switch (parameterType)
- {
- case BOOL2, INT2, FLOAT2: 2;
- case BOOL3, INT3, FLOAT3: 3;
- case BOOL4, INT4, FLOAT4, MATRIX2X2: 4;
- case MATRIX3X3: 9;
- case MATRIX4X4: 16;
- default: 1;
- }
+ if (__thisHasField(name) || __thisHasField('get_${name}')) return Reflect.getProperty(this, name);
+ else if (!Reflect.hasField(__data, name)) return null;
- var arrayLength = switch (parameterType)
- {
- case MATRIX2X2: 2;
- case MATRIX3X3: 3;
- case MATRIX4X4: 4;
- default: 1;
- }
+ final field:Dynamic = Reflect.field(__data, name);
- switch (parameterType)
- {
- case BOOL, BOOL2, BOOL3, BOOL4:
- var parameter = new ShaderParameter();
- parameter.name = name;
- parameter.type = parameterType;
- parameter.__arrayLength = arrayLength;
- parameter.__isBool = true;
- parameter.__isUniform = isUniform;
- parameter.__length = length;
- __paramBool.push(parameter);
-
- if (name == "openfl_HasColorTransform")
- {
- __hasColorTransform = parameter;
- }
-
- Reflect.setField(__data, name, parameter);
- try{Reflect.setField(this, name, parameter);} catch(e) {}
-
- case INT, INT2, INT3, INT4:
- var parameter = new ShaderParameter();
- parameter.name = name;
- parameter.type = parameterType;
- parameter.__arrayLength = arrayLength;
- parameter.__isInt = true;
- parameter.__isUniform = isUniform;
- parameter.__length = length;
- __paramInt.push(parameter);
- Reflect.setField(__data, name, parameter);
- try{Reflect.setField(this, name, parameter);} catch(e) {}
-
- default:
- var parameter = new ShaderParameter();
- parameter.name = name;
- parameter.type = parameterType;
- parameter.__arrayLength = arrayLength;
- #if lime
- if (arrayLength > 0) parameter.__uniformMatrix = new Float32Array(arrayLength * arrayLength);
- #end
- parameter.__isFloat = true;
- parameter.__isUniform = isUniform;
- parameter.__length = length;
- __paramFloat.push(parameter);
-
- if (StringTools.startsWith(name, "openfl_"))
- {
- switch (name)
- {
- case "openfl_Alpha": __alpha = parameter;
- case "openfl_ColorMultiplier": __colorMultiplier = parameter;
- case "openfl_ColorOffset": __colorOffset = parameter;
- case "openfl_Matrix": __matrix = parameter;
- case "openfl_Position": __position = parameter;
- case "openfl_TextureCoord": __textureCoord = parameter;
- case "openfl_TextureSize": __textureSize = parameter;
- default:
- }
- }
-
- Reflect.setField(__data, name, parameter);
- try{Reflect.setField(this, name, parameter);} catch(e) {}
- }
- }
- }
-
- public function hget(name:String):Dynamic {
- if (__instanceFields.contains(name) || __instanceFields.contains('get_${name}'))
- return Reflect.getProperty(this, name);
- if (!Reflect.hasField(data, name))
- return null;
- var field:Dynamic = Reflect.field(data, name);
var cl:String = Type.getClassName(Type.getClass(field));
// little problem we are facing boys...
@@ -546,17 +78,19 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
}
public function hset(name:String, val:Dynamic):Dynamic {
- if (__instanceFields.contains(name) || __instanceFields.contains('set_${name}')) {
+ if (__glSourceDirty) __init();
+
+ if (__thisHasField(name) || __thisHasField('set_${name}')) {
Reflect.setProperty(this, name, val);
return val;
}
-
- if (!Reflect.hasField(data, name)) {
- Reflect.setField(data, name, val);
+ else if (!Reflect.hasField(__data, name)) {
+ // ??? huh
+ Reflect.setField(__data, name, val);
return val;
}
- var field = Reflect.field(data, name);
+ var field = Reflect.field(__data, name);
var cl = Type.getClassName(Type.getClass(field));
var isNotNull = val != null;
// cant do "field is ShaderInput" for some reason
@@ -598,67 +132,142 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
return val;
}
-}
+ #end
-class ShaderTemplates {
- public static final fragHeader:String = "varying float openfl_Alphav;
-varying vec4 openfl_ColorMultiplierv;
-varying vec4 openfl_ColorOffsetv;
-varying vec2 openfl_TextureCoordv;
+ override function __updateGL():Void {
+ onGLUpdate.dispatch();
+ super.__updateGL();
+ }
-uniform bool openfl_HasColorTransform;
-uniform vec2 openfl_TextureSize;
-uniform sampler2D bitmap;
+ override function __createAssembler():Void {
+ __glSourceAssembler = new FunkinShaderSourceAssembler(this);
+ }
-uniform bool hasTransform;
-uniform bool hasColorTransform;
+ override function toString():String {
+ return __cacheProgramId != null ? 'FunkinShader(${__cacheProgramId})' : 'FunkinShader';
+ }
+
+ #if REGION /* Deprecated */
+ public var shaderPrefix:String = "";
+ public var fragmentPrefix:String = "";
+ public var vertexPrefix:String = "";
+ #end
+
+ #if REGION /* Backward Compatibility */
+ private static var __instanceFields = Type.getInstanceFields(FunkinShader);
+ private static var FRAGMENT_SHADER = 0;
+ private static var VERTEX_SHADER = 1;
+
+ public var fileName(get, set):String;
+ inline function get_fileName():String return _fragmentFilePath ?? _vertexFilePath ?? "FunkinShader";
+ inline function set_fileName(v:String):String return _fragmentFilePath = _vertexFilePath = v;
+
+ public var fragFileName(get, set):String;
+ inline function get_fragFileName():String return _fragmentFilePath ?? "FunkinShader";
+ inline function set_fragFileName(v:String):String return _fragmentFilePath = v;
-vec4 applyFlixelEffects(vec4 color) {
- if(!hasTransform) {
- return color;
+ public var vertFileName(get, set):String;
+ inline function get_vertFileName():String return _vertexFilePath ?? "FunkinShader";
+ inline function set_vertFileName(v:String):String return _vertexFilePath = v;
+
+ public var glslVer(get, set):String;
+ inline function get_glslVer():String return glVersion;
+ inline function set_glslVer(v:String):String return glVersion = v;
+
+ public var glRawFragmentSource(get, set):String;
+ inline function get_glRawFragmentSource():String return __glFragmentSourceRaw;
+ inline function set_glRawFragmentSource(v:String):String return __glFragmentSourceRaw = v;
+
+ public var glRawVertexSource(get, set):String;
+ inline function get_glRawVertexSource():String return __glVertexSourceRaw;
+ inline function set_glRawVertexSource(v:String):String return __glVertexSourceRaw = v;
+
+ function thisHasField(v:String):Bool return __thisHasField(v);
+
+ function registerParameter(name:String, type:String, isUniform:Bool) {
+ __registerParameter(name, Shader.getParameterTypeFromGLSL(type, false), StringTools.startsWith(type, "sampler"), 1, null, isUniform, null);
}
- if(color.a == 0.0) {
- return vec4(0.0, 0.0, 0.0, 0.0);
+ // Unused... cne-openfl uses a different system
+ var __cancelNextProcessGLData:Bool = false;
+ public var onProcessGLData:FlxTypedSignal<(String, String)->Void> = new FlxTypedSignal<(String, String)->Void>();
+ #end
+}
+
+class FunkinShaderSourceAssembler extends FlxRuntimeShader.FlxShaderSourceAssembler {
+ final funkinParent:FunkinShader;
+
+ public function new(parent:FunkinShader) {
+ super(funkinParent = parent);
}
- if(!hasColorTransform) {
- return color * openfl_Alphav;
+ override function __appendIncludes(source:String, isVertex:Bool, ?includedKeys:Map):String
+ {
+ if (includedKeys == null) includedKeys = [];
+
+ source = GLSLSourceAssembler.__getIncludeFinder().map(source, (regex:EReg) ->
+ {
+ var key = regex.matched(1);
+ if (includedKeys.get(key)) return '/*Recursive include $key*/\n';
+
+ var include = __getIncludeSource(key, isVertex);
+ if (include == null) return '/*Unknown include $key*/\n';
+
+ includedKeys.set(key, true);
+ return '/*include $key*/\n' + __appendIncludes(include, isVertex, includedKeys);
+ });
+
+ return __getImportCompatibilityFinder().map(source, (regex:EReg) ->
+ {
+ var key = regex.matched(1);
+ if (includedKeys.get(key)) return '/*Recursive import $key*/\n';
+
+ var include = __getIncludeSource(key, isVertex);
+ if (include == null) return '/*Unknown import $key*/\n';
+
+ includedKeys.set(key, true);
+ return '/*import $key*/\n' + __appendIncludes(include, isVertex, includedKeys);
+ });
}
- color.rgb = color.rgb / color.a;
- color = clamp(openfl_ColorOffsetv + (color * openfl_ColorMultiplierv), 0.0, 1.0);
+ override function __getIncludeSource(include:String, fromVertex:Bool):Null {
+ final path = Paths.getPath('shaders/' + include);
+ if (Assets.exists(path)) return Assets.getText(path);
- if(color.a > 0.0) {
- return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
+ final fallback = __getIncludeSource(include, fromVertex);
+ if (fallback != null) return fallback;
+
+ Logs.traceColored([
+ Logs.logText('[Shader] ', RED),
+ Logs.logText('Failed to import shader $include', RED),
+ ]);
+ return null;
}
- return vec4(0.0, 0.0, 0.0, 0.0);
-}
-vec4 flixel_texture2D(sampler2D bitmap, vec2 coord) {
- vec4 color = texture2D(bitmap, coord);
- return applyFlixelEffects(color);
-}
+ override function __appendPrefix(source:String, versionNumber:Int, versionProfile:String, extensions:Map, isVertex:Bool,
+ precisionHint:Null):String
+ {
+ var result = super.__appendPrefix(null, versionNumber, versionProfile, extensions, isVertex, precisionHint) + "\n";
-uniform vec4 _camSize;
+ result += funkinParent.shaderPrefix + "\n" + (isVertex ? funkinParent.vertexPrefix : funkinParent.fragmentPrefix) + "\n";
-float map(float value, float min1, float max1, float min2, float max2) {
- return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
-}
+ if (source != null) {
+ if (!isVertex && versionNumber >= 300 && versionProfile != "compatibility" && !StringTools.contains(source, "out vec4")) {
+ result += "out vec4 openfl_FragColor;\n";
+ }
+ result += source;
+ }
-vec2 getCamPos(vec2 pos) {
- vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
- return vec2(map(pos.x, size.x, size.x + size.z, 0.0, 1.0), map(pos.y, size.y, size.y + size.w, 0.0, 1.0));
-}
-vec2 camToOg(vec2 pos) {
- vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
- return vec2(map(pos.x, 0.0, 1.0, size.x, size.x + size.z), map(pos.y, 0.0, 1.0, size.y, size.y + size.w));
+ return result;
+ }
+
+ private static inline function __getImportCompatibilityFinder():EReg {
+ return ~/#import\s+(?|"([^"]+)"|'([^']+)'|<(.*)>|([^\s]+))/g;
+ }
}
-vec4 textureCam(sampler2D bitmap, vec2 pos) {
- return flixel_texture2D(bitmap, camToOg(pos));
-}";
- public static final fragBody:String = "gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv);";
+#if REGION /* Backward Compatibility */
+class ShaderTemplates {
public static final vertHeader:String = "attribute float openfl_Alpha;
attribute vec4 openfl_ColorMultiplier;
attribute vec4 openfl_ColorOffset;
@@ -679,32 +288,97 @@ attribute vec4 colorMultiplier;
attribute vec4 colorOffset;
uniform bool hasColorTransform;";
- public static final vertBody:String = "openfl_Alphav = openfl_Alpha;
-openfl_TextureCoordv = openfl_TextureCoord;
+ public static final vertBody:String = "openfl_TextureCoordv = openfl_TextureCoord;
-if(openfl_HasColorTransform) {
- openfl_ColorMultiplierv = openfl_ColorMultiplier;
- openfl_ColorOffsetv = openfl_ColorOffset / 255.0;
+if (hasColorTransform)
+{
+ openfl_Alphav = openfl_Alpha * colorMultiplier.a;
+ if (openfl_HasColorTransform)
+ {
+ openfl_ColorOffsetv = (openfl_ColorOffset / 255.0 * colorMultiplier) + (colorOffset / 255.0);
+ openfl_ColorMultiplierv = openfl_ColorMultiplier * vec4(colorMultiplier.rgb, 1.0);
+ }
+ else
+ {
+ openfl_ColorOffsetv = colorOffset / 255.0;
+ openfl_ColorMultiplierv = vec4(colorMultiplier.rgb, 1.0);
+ }
}
+else
+{
+ openfl_Alphav = openfl_Alpha * alpha;
+ if (openfl_HasColorTransform)
+ {
+ openfl_ColorOffsetv = (openfl_ColorOffset + colorOffset) / 255.0;
+ openfl_ColorMultiplierv = openfl_ColorMultiplier;
+ }
+ else
+ {
+ openfl_ColorOffsetv = colorOffset / 255.0;
+ openfl_ColorMultiplierv = vec4(1.0);
+ }
+}";
+
+ public static final fragHeader:String = "varying float openfl_Alphav;
+varying vec4 openfl_ColorMultiplierv;
+varying vec4 openfl_ColorOffsetv;
+varying vec2 openfl_TextureCoordv;
+
+uniform bool openfl_HasColorTransform;
+uniform vec2 openfl_TextureSize;
+uniform sampler2D bitmap;
+uniform bool hasTransform;
+uniform bool hasColorTransform;
+uniform bool premultiplyAlpha;
-openfl_Alphav = openfl_Alpha * alpha;
+vec4 apply_flixel_transform(vec4 color)
+{
+ if (!hasTransform) return color;
+ else if (color.a <= 0.0 || openfl_Alphav == 0.0) return vec4(0.0);
-if(hasColorTransform) {
- openfl_ColorOffsetv = colorOffset / 255.0;
- openfl_ColorMultiplierv = colorMultiplier;
+ // this is just solely for ASTC compressed textures.
+ // ...also in flixel_texture2D, it also converts to linear alpha anyway.
+ if (!premultiplyAlpha) color.rgb /= color.a;
+
+ color = clamp(openfl_ColorOffsetv + (color * openfl_ColorMultiplierv), 0.0, 1.0);
+ return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
}
+#define applyFlixelEffects(color) apply_flixel_transform(color)
-gl_Position = openfl_Matrix * openfl_Position;";
+vec4 flixel_texture2D(sampler2D bitmap, vec2 coord)
+{
+ return apply_flixel_transform(texture2D(bitmap, coord));
+}
-// TODO: make this ignore comments
-public static final vertBackCompatVarList:Array = [
- ~/attribute float alpha/,
- ~/attribute vec4 colorMultiplier/,
- ~/attribute vec4 colorOffset/,
- ~/uniform bool hasColorTransform/
-];
+uniform vec4 _camSize;
+
+float map(float value, float min1, float max1, float min2, float max2) {
+ return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
+}
-public static final vertHeaderBackCompat:String = "attribute float openfl_Alpha;
+vec2 getCamPos(vec2 pos) {
+ vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
+ return vec2(map(pos.x, size.x, size.x + size.z, 0.0, 1.0), map(pos.y, size.y, size.y + size.w, 0.0, 1.0));
+}
+vec2 camToOg(vec2 pos) {
+ vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
+ return vec2(map(pos.x, 0.0, 1.0, size.x, size.x + size.z), map(pos.y, 0.0, 1.0, size.y, size.y + size.w));
+}
+vec4 textureCam(sampler2D bitmap, vec2 pos) {
+ return flixel_texture2D(bitmap, camToOg(pos));
+}";
+
+ public static final fragBody:String = "gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv);
+if (gl_FragColor.a == 0.0) discard;";
+
+ public static final vertBackCompatVarList:Array = [
+ ~/attribute float alpha/,
+ ~/attribute vec4 colorMultiplier/,
+ ~/attribute vec4 colorOffset/,
+ ~/uniform bool hasColorTransform/
+ ];
+
+ public static final vertHeaderBackCompat:String = "attribute float openfl_Alpha;
attribute vec4 openfl_ColorMultiplier;
attribute vec4 openfl_ColorOffset;
attribute vec4 openfl_Position;
@@ -719,7 +393,7 @@ uniform mat4 openfl_Matrix;
uniform bool openfl_HasColorTransform;
uniform vec2 openfl_TextureSize;";
-public static final vertBodyBackCompat:String = "openfl_Alphav = openfl_Alpha;
+ public static final vertBodyBackCompat:String = "openfl_Alphav = openfl_Alpha;
openfl_TextureCoordv = openfl_TextureCoord;
if(openfl_HasColorTransform) {
@@ -728,19 +402,8 @@ if(openfl_HasColorTransform) {
}
gl_Position = openfl_Matrix * openfl_Position;";
-
- public static final defaultVertexSource:String = "#pragma header
-
-void main(void) {
- #pragma body
-}";
-
- public static final defaultFragmentSource:String = "#pragma header
-
-void main(void) {
- #pragma body
-}";
}
+#end
class ShaderTypeException extends Exception {
var has:Class;
@@ -753,16 +416,4 @@ class ShaderTypeException extends Exception {
this.name = name;
super('ShaderTypeException - Tried to set the shader uniform "${name}" as a ${Type.getClassName(has)}, but the shader uniform is a ${Std.string(want)}.');
}
-}
-
-class ShaderErrorPosition {
- public var column:Int;
- public var line:Int;
- public var message:String;
-
- public function new(line:String, column:String, message:String) {
- this.line = Std.parseInt(line);
- this.column = Std.parseInt(column);
- this.message = message;
- }
}
\ No newline at end of file
diff --git a/source/funkin/backend/shaders/FunkinShaderOLD._hx b/source/funkin/backend/shaders/FunkinShaderOLD._hx
new file mode 100644
index 0000000000..1853813dde
--- /dev/null
+++ b/source/funkin/backend/shaders/FunkinShaderOLD._hx
@@ -0,0 +1,471 @@
+package funkin.backend.shaders;
+
+import haxe.Exception;
+import haxe.io.Path;
+import flixel.graphics.FlxGraphic;
+import flixel.system.FlxAssets.FlxShader;
+import flixel.util.FlxSignal.FlxTypedSignal;
+import flixel.util.FlxStringUtil;
+import hscript.IHScriptCustomBehaviour;
+import openfl.display.BitmapData;
+import openfl.display.ShaderInput;
+import openfl.display.ShaderParameter;
+import openfl.display.ShaderParameterType;
+import openfl.display.Shader;
+import openfl.display3D._internal.GLProgram;
+import openfl.display3D._internal.GLShader;
+import openfl.display3D.Program3D;
+import openfl.utils._internal.Log;
+
+using StringTools;
+@:access(openfl.display3D.Context3D)
+@:access(openfl.display3D.Program3D)
+@:access(openfl.display.ShaderInput)
+@:access(openfl.display.ShaderParameter)
+class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
+ #if REGION /* Backward Compatibility */
+ private static var __instanceFields = Type.getInstanceFields(FunkinShader);
+ private static var FRAGMENT_SHADER = 0;
+ private static var VERTEX_SHADER = 1;
+
+ public var glslVer(get, set):String;
+ inline function get_glslVer():String return glVersion;
+ inline function set_glslVer(v:String):String return glVersion = v;
+
+ public var glRawFragmentSource(get, set):String;
+ inline function get_glRawFragmentSource():String return __glFragmentSourceRaw;
+ inline function set_glRawFragmentSource(v:String):String return __glFragmentSourceRaw = v;
+
+ public var glRawVertexSource(get, set):String;
+ inline function get_glRawVertexSource():String return __glVertexSourceRaw;
+ inline function set_glRawVertexSource(v:String):String return __glVertexSourceRaw = v;
+
+ // Unused... cne-openfl uses a different system
+ var __cancelNextProcessGLData:Bool = false;
+ public var onProcessGLData:FlxTypedSignal<(String, String)->Void> = new FlxTypedSignal<(String, String)->Void>();
+ #end
+
+ public static function getShaderCode(key:String, isFragment = true):Null {
+ var path = "shaders/" + key;
+ key = Path.withoutExtension(key);
+
+ final ext = Path.extension(path);
+ if (ext == "") path = path + (isFragment ? ".frag" : ".vert");
+ else isFragment = ext != "vert";
+
+ path = Paths.getPath(path);
+ return Assets.exists(path) ? Assets.getText(path) : null;
+ }
+
+ private static function processGLSLText(source:String, glVersion:String, isFragment:Bool, ?pragmas:Map):String
+ return Shader.processGLSLText(_processGLSLText(source, glVersion, isFragment, pragmas), glVersion, isFragment);
+
+ private static function _processGLSLText(source:String, glVersion:String, isFragment:Bool, ?pragmas:Map):String {
+ if (pragmas != null) {
+ final pragmaKeyword = ~/#pragma\s+(\w+)/g;
+ source = pragmaKeyword.map(source, (_) -> {
+ var name = pragmaKeyword.matched(1), pragma:String;
+ if (pragmas.exists(name)) pragma = pragmas.get(name);
+ else {
+ if (name != "header" && name != "body") return '#pragma $name';
+ pragma = "";
+ }
+ return _processGLSLText(pragma, glVersion, isFragment, pragmas);
+ });
+ }
+
+ inline function tryGetShaderCode(key:String) {
+ final s = getShaderCode(key, isFragment);
+ if (s == null) {
+ Logs.traceColored([
+ Logs.logText('[Shader] ', RED),
+ Logs.logText('Failed to import shader $key', RED),
+ ]);
+ return "";
+ }
+ return s;
+ }
+
+ final includeKeyword = ~/#include ['"](.+)['"]/g;
+ final importKeyword = ~/#import\s+<(.*)>/g;
+ source = importKeyword.map(source, (_) ->
+ return _processGLSLText(tryGetShaderCode(importKeyword.matched(1)), glVersion, isFragment, pragmas) ?? "");
+
+ return source = includeKeyword.map(source, (_) ->
+ return _processGLSLText(tryGetShaderCode(includeKeyword.matched(1)), glVersion, isFragment, pragmas) ?? "");
+ }
+
+ private static var __defaultsAvailable:Bool;
+ private static var __glFragmentSourceDefault:String;
+ private static var __glVertexSourceDefault:String;
+ private static var __glFragmentPragmasDefault:Map;
+ private static var __glVertexPragmasDefault:Map;
+ private static var __glFragmentExtensionsDefault:Array;
+ private static var __glVertexExtensionsDefault:Array;
+
+ public var onGLUpdate:FlxTypedSignalVoid> = new FlxTypedSignalVoid>();
+
+ public var fileName:String = "FunkinShader";
+ public var fragFileName:String = "FunkinShader";
+ public var vertFileName:String = "FunkinShader";
+
+ public var shaderPrefix:String = "";
+ public var fragmentPrefix:String = "";
+ public var vertexPrefix:String = "";
+
+ private var __immediate:Bool;
+
+ public function new(?fragmentSource:String, ?vertexSource:String, ?version:String,
+ ?fragmentExtensions:Array, ?vertexExtensions:Array, immediate = false
+ ) {
+ if (!__defaultsAvailable) {
+ __glFragmentSourceDefault = __glFragmentSourceRaw;
+ __glVertexSourceDefault = __glVertexSourceRaw;
+ __glFragmentPragmasDefault = __glFragmentPragmas.copy();
+ __glVertexPragmasDefault = __glVertexPragmas.copy();
+ __glFragmentExtensionsDefault = __glFragmentExtensions ?? [];
+ __glVertexExtensionsDefault = __glVertexExtensions ?? [];
+ __defaultsAvailable = true;
+ }
+
+ __immediate = immediate;
+
+ if (version != null) glVersion = version;
+ if (vertexExtensions != null) glVertexExtensions = vertexExtensions;
+ if (fragmentExtensions != null) glFragmentExtensions = fragmentExtensions;
+ if (vertexSource != null) glVertexSource = vertexSource;
+ if (fragmentSource != null) glFragmentSource = fragmentSource;
+
+ super();
+
+ if (!__isGenerated) {
+ __isGenerated = true;
+ __init();
+ }
+ }
+
+ public function loadShader(name:String, ?version:String, immediate = false):FunkinShader {
+ final fragment = getShaderCode(name, true), vertex = getShaderCode(name, false);
+
+ glVersion = version;
+ glVertexSource = vertex ?? __glVertexSourceDefault;
+ glFragmentSource = fragment ?? __glFragmentSourceDefault;
+
+ if (immediate) __init();
+ return this;
+ }
+
+ override function __initGL():Void {
+ if (__immediate) {
+ __context = FlxG.stage.context3D;
+ __enable();
+ }
+ super.__initGL();
+ }
+
+ override function __updateGL():Void {
+ onGLUpdate.dispatch();
+ super.__updateGL();
+ }
+
+ public function hget(name:String):Dynamic {
+ if (__glSourceDirty || __data == null) __init();
+
+ if (thisHasField(name) || thisHasField('get_${name}')) return Reflect.getProperty(this, name);
+ else if (!Reflect.hasField(__data, name)) return null;
+
+ final field:Dynamic = Reflect.field(__data, name);
+
+ var cl:String = Type.getClassName(Type.getClass(field));
+
+ // little problem we are facing boys...
+
+ // cant do "field is ShaderInput" because ShaderInput has the @:generic metadata
+ // aka instead of ShaderInput it gets built as ShaderInput_Float
+ // this should be fine tho because we check the class, and the fields don't vary based on the type
+
+ // thanks for looking in the code cne fans :D!! -lunar
+
+ if (cl.startsWith("openfl.display.ShaderParameter"))
+ return (field.__length > 1) ? field.value : field.value[0];
+ else if (cl.startsWith("openfl.display.ShaderInput"))
+ return field.input;
+ return field;
+ }
+
+ public function hset(name:String, val:Dynamic):Dynamic {
+ if (__glSourceDirty || __data == null) __init();
+
+ if (thisHasField(name) || thisHasField('set_${name}')) {
+ Reflect.setProperty(this, name, val);
+ return val;
+ }
+ else if (!Reflect.hasField(__data, name)) {
+ // ??? huh
+ Reflect.setField(__data, name, val);
+ return val;
+ }
+
+ var field = Reflect.field(__data, name);
+ var cl = Type.getClassName(Type.getClass(field));
+ var isNotNull = val != null;
+ // cant do "field is ShaderInput" for some reason
+ if (cl.startsWith("openfl.display.ShaderParameter")) {
+ if (field.__length <= 1) {
+ // that means we wait for a single number, instead of an array
+ if (field.__isInt && isNotNull && !(val is Int)) {
+ throw new ShaderTypeException(name, Type.getClass(val), 'Int');
+ return null;
+ } else
+ if (field.__isBool && isNotNull && !(val is Bool)) {
+ throw new ShaderTypeException(name, Type.getClass(val), 'Bool');
+ return null;
+ } else
+ if (field.__isFloat && isNotNull && !(val is Float)) {
+ throw new ShaderTypeException(name, Type.getClass(val), 'Float');
+ return null;
+ }
+ return field.value = isNotNull ? [val] : null;
+ } else {
+ if (isNotNull && !(val is Array)) {
+ throw new ShaderTypeException(name, Type.getClass(val), Array);
+ return null;
+ }
+ return field.value = val;
+ }
+ } else if (cl.startsWith("openfl.display.ShaderInput")) {
+ // shader input!!
+ var bitmap:BitmapData;
+ if (!isNotNull) bitmap = null;
+ else if (val is BitmapData) bitmap = val;
+ else if (val is FlxGraphic) bitmap = val.bitmap;
+ else {
+ throw new ShaderTypeException(name, Type.getClass(val), BitmapData);
+ return null;
+ }
+ field.input = bitmap;
+ }
+
+ return val;
+ }
+
+ override function __buildSourcePrefix(isFragment:Bool):String {
+ var result = super.__buildSourcePrefix(isFragment) + '\n$shaderPrefix';
+ return isFragment ? result + '\n$fragmentPrefix' : result + '\n$vertexPrefix';
+ }
+
+ override function set_glFragmentExtensions(value:Array):Array {
+ if (value == null) value = __glFragmentExtensionsDefault;
+ if (value != __glFragmentExtensions) __glSourceDirty = true;
+ return __glFragmentExtensions = value;
+ }
+
+ override function set_glVertexExtensions(value:Array):Array {
+ if (value == null) value = __glVertexExtensionsDefault;
+ if (value != __glVertexExtensions) __glSourceDirty = true;
+ return __glVertexExtensions = value;
+ }
+
+ override function set_glVersion(value:Null):String {
+ if (value == null || value == "") value = Flags.DEFAULT_GLSL_VERSION;
+ if ((__glVersionRaw = value) != __glVersion) {
+ __glSourceDirty = true;
+ if (__glVertexSourceRaw != null) __glVertexSource = processGLSLText(__glVertexSourceRaw, value, false, __glVertexPragmas);
+ if (__glFragmentSourceRaw != null) __glFragmentSource = processGLSLText(__glFragmentSourceRaw, value, true, __glFragmentPragmas);
+ }
+
+ return __glVersion = value;
+ }
+
+ override function set_glFragmentSource(value:String):String {
+ if (value == null || value == "") value = __glFragmentSourceDefault;
+ if ((__glFragmentSourceRaw = value) != null) {
+ if (__glVersion != (__glVersion = Shader.getGLSLTextVersion(value, __glVersionRaw)))
+ __glSourceDirty = true;
+
+ value = processGLSLText(value, __glVersion, true, __glFragmentPragmas);
+ }
+
+ if (value != __glFragmentSource) __glSourceDirty = true;
+ return __glFragmentSource = value;
+ }
+
+ override function set_glVertexSource(value:String):String {
+ if (value == null || value == "") value = __glVertexSourceDefault;
+ if ((__glVertexSourceRaw = value) != null) {
+ if (__glVersion != (__glVersion = Shader.getGLSLTextVersion(value, __glVersionRaw)))
+ __glSourceDirty = true;
+
+ value = processGLSLText(value, __glVersion, false, __glVertexPragmas);
+ }
+
+ if (value != __glVertexSource) __glSourceDirty = true;
+ return __glVertexSource = value;
+ }
+
+ override function set_glFragmentPragmas(value:Map):Map {
+ if (value == null) value = __glFragmentPragmasDefault;
+ if (value != __glFragmentPragmas)
+ __glSourceDirty = true;
+
+ return __glFragmentPragmas = value;
+ }
+
+ override function set_glVertexPragmas(value:Map):Map {
+ if (value == null) value = __glVertexPragmasDefault;
+ if (value != __glVertexPragmas)
+ __glSourceDirty = true;
+
+ return __glVertexPragmas = value;
+ }
+
+ function registerParameter(name:String, type:String, isUniform:Bool):Void {
+ __registerParameter(name, Program3D.getParameterTypeFromGLString(type, 1), 1, -1, isUniform, false, null);
+ }
+
+ public function toString():String
+ return FlxStringUtil.getDebugString([for (field in Reflect.fields(data)) LabelValuePair.weak(field, Reflect.field(data, field))]);
+}
+
+class ShaderTemplates {
+ #if REGION /* Backward Compatibility */
+ public static final vertHeader:String = "attribute float openfl_Alpha;
+attribute vec4 openfl_ColorMultiplier;
+attribute vec4 openfl_ColorOffset;
+attribute vec4 openfl_Position;
+attribute vec2 openfl_TextureCoord;
+
+varying float openfl_Alphav;
+varying vec4 openfl_ColorMultiplierv;
+varying vec4 openfl_ColorOffsetv;
+varying vec2 openfl_TextureCoordv;
+
+uniform mat4 openfl_Matrix;
+uniform bool openfl_HasColorTransform;
+uniform vec2 openfl_TextureSize;
+
+attribute float alpha;
+attribute vec4 colorMultiplier;
+attribute vec4 colorOffset;
+
+uniform bool hasColorTransform;";
+
+ public static final vertBody:String = "openfl_TextureCoordv = openfl_TextureCoord;
+
+if (hasColorTransform) {
+ openfl_Alphav = openfl_Alpha * colorMultiplier.a;
+ if (openfl_HasColorTransform) {
+ openfl_ColorOffsetv = (openfl_ColorOffset / 255.0 * colorMultiplier) + (colorOffset / 255.0);
+ openfl_ColorMultiplierv = openfl_ColorMultiplier * vec4(colorMultiplier.rgb, 1.0);
+ }
+ else {
+ openfl_ColorOffsetv = colorOffset / 255.0;
+ openfl_ColorMultiplierv = vec4(colorMultiplier.rgb, 1.0);
+ }
+}
+else {
+ openfl_Alphav = openfl_Alpha * alpha;
+ if (openfl_HasColorTransform) {
+ openfl_ColorOffsetv = (openfl_ColorOffset + colorOffset) / 255.0;
+ openfl_ColorMultiplierv = openfl_ColorMultiplier;
+ }
+ else {
+ openfl_ColorOffsetv = colorOffset / 255.0;
+ openfl_ColorMultiplierv = vec4(1.0);
+ }
+}";
+
+ public static final fragHeader:String = "varying float openfl_Alphav;
+varying vec4 openfl_ColorMultiplierv;
+varying vec4 openfl_ColorOffsetv;
+varying vec2 openfl_TextureCoordv;
+
+uniform bool openfl_HasColorTransform;
+uniform vec2 openfl_TextureSize;
+uniform sampler2D bitmap;
+
+uniform bool hasTransform;
+uniform bool hasColorTransform;
+
+vec4 apply_flixel_transform(vec4 color) {
+ if (!hasTransform) return color;
+ else if (color.a <= 0.0 || openfl_Alphav == 0.0) return vec4(0.0);
+
+ color.rgb /= color.a;
+ color = clamp(openfl_ColorOffsetv + (color * openfl_ColorMultiplierv), 0.0, 1.0);
+ return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
+}
+
+#define applyFlixelEffects(color) apply_flixel_transform(color)
+
+vec4 flixel_texture2D(sampler2D bitmap, vec2 coord) {
+ return apply_flixel_transform(texture2D(bitmap, coord));
+}
+
+uniform vec4 _camSize;
+
+float map(float value, float min1, float max1, float min2, float max2) {
+ return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
+}
+
+vec2 getCamPos(vec2 pos) {
+ vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
+ return vec2(map(pos.x, size.x, size.x + size.z, 0.0, 1.0), map(pos.y, size.y, size.y + size.w, 0.0, 1.0));
+}
+vec2 camToOg(vec2 pos) {
+ vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
+ return vec2(map(pos.x, 0.0, 1.0, size.x, size.x + size.z), map(pos.y, 0.0, 1.0, size.y, size.y + size.w));
+}
+vec4 textureCam(sampler2D bitmap, vec2 pos) {
+ return flixel_texture2D(bitmap, camToOg(pos));
+}";
+
+ public static final fragBody:String = "gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv);
+if (gl_FragColor.a == 0.0) discard;";
+
+ public static final vertBackCompatVarList:Array = [
+ ~/attribute float alpha/,
+ ~/attribute vec4 colorMultiplier/,
+ ~/attribute vec4 colorOffset/,
+ ~/uniform bool hasColorTransform/
+ ];
+
+ public static final vertHeaderBackCompat:String = "attribute float openfl_Alpha;
+attribute vec4 openfl_ColorMultiplier;
+attribute vec4 openfl_ColorOffset;
+attribute vec4 openfl_Position;
+attribute vec2 openfl_TextureCoord;
+
+varying float openfl_Alphav;
+varying vec4 openfl_ColorMultiplierv;
+varying vec4 openfl_ColorOffsetv;
+varying vec2 openfl_TextureCoordv;
+
+uniform mat4 openfl_Matrix;
+uniform bool openfl_HasColorTransform;
+uniform vec2 openfl_TextureSize;";
+
+ public static final vertBodyBackCompat:String = "openfl_Alphav = openfl_Alpha;
+openfl_TextureCoordv = openfl_TextureCoord;
+
+if(openfl_HasColorTransform) {
+ openfl_ColorMultiplierv = openfl_ColorMultiplier;
+ openfl_ColorOffsetv = openfl_ColorOffset / 255.0;
+}
+
+gl_Position = openfl_Matrix * openfl_Position;";
+ #end
+}
+
+class ShaderTypeException extends Exception {
+ var has:Class;
+ var want:Class;
+ var name:String;
+
+ public function new(name:String, has:Class, want:Dynamic) {
+ this.has = has;
+ this.want = want;
+ this.name = name;
+ super('ShaderTypeException - Tried to set the shader uniform "${name}" as a ${Type.getClassName(has)}, but the shader uniform is a ${Std.string(want)}.');
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/backend/system/FakeCamera.hx b/source/funkin/backend/system/FakeCamera.hx
index 99d0eb1b88..31caaac51a 100644
--- a/source/funkin/backend/system/FakeCamera.hx
+++ b/source/funkin/backend/system/FakeCamera.hx
@@ -11,6 +11,11 @@ import openfl.display.BlendMode;
import openfl.geom.ColorTransform;
import openfl.geom.Point;
import openfl.geom.Rectangle;
+import openfl.display.TriangleCulling;
+import openfl.display3D.Context3DWrapMode;
+import openfl.display3D.Context3DCompareMode;
+import openfl.filters.BitmapFilter;
+import openfl.filters.ShaderFilter;
class FakeCamera extends FlxCamera {
public static final instance = new FakeCamera();
@@ -21,14 +26,14 @@ class FakeCamera extends FlxCamera {
visible = false;
}
- override function startTrianglesBatch(graphic:FlxGraphic, smoothing:Bool = false, isColored:Bool = false, ?blend:BlendMode, ?hasColorOffsets:Bool, ?shader:FlxShader) { return null;}
- override function startQuadBatch(graphic:FlxGraphic, colored:Bool, hasColorOffsets:Bool = false, ?blend:BlendMode, smooth:Bool = false, ?shader:FlxShader) { return null;}
+ override function startTrianglesBatch(graphic:FlxGraphic, smoothing:Bool = false, isColored:Bool = false, ?blend:BlendMode, ?hasColorOffsets:Bool, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode, ?culling:TriangleCulling) { return null;}
+ override function startQuadBatch(graphic:FlxGraphic, colored:Bool, hasColorOffsets:Bool = false, ?blend:BlendMode, smooth:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) { return null;}
override function clearDrawStack() {}
override function render() {}
- public override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {}
- public override function copyPixels(?frame:FlxFrame, ?pixels:BitmapData, ?sourceRect:Rectangle, destPoint:Point, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {}
- public override function drawTriangles(graphic:FlxGraphic, vertices:DrawData, indices:DrawData, uvtData:DrawData, ?colors:DrawData, ?position:FlxPoint, ?blend:BlendMode, repeat:Bool = false, smoothing:Bool = false, ?transform:ColorTransform, ?shader:FlxShader):Void {}
+ public override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {}
+ public override function copyPixels(?frame:FlxFrame, ?pixels:BitmapData, ?sourceRect:Rectangle, destPoint:Point, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {}
+ public override function drawTriangles(graphic:FlxGraphic, vertices:DrawData, indices:DrawData, uvtData:DrawData, ?colors:DrawData, ?position:FlxPoint, ?blend:BlendMode, repeat:Bool = false, smoothing:Bool = false, ?transform:ColorTransform, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode, ?culling:TriangleCulling):Void {}
public override function update(elapsed:Float) {}
@@ -41,10 +46,10 @@ class FakeCallCamera extends FakeCamera {
public static final instance = new FakeCallCamera();
public var ignoreDraws:Bool = false;
- public dynamic function onDraw(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {
+ public dynamic function onDraw(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {
}
- override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {
- if (!ignoreDraws) onDraw(frame, pixels, matrix, transform, blend, smoothing, shader);
+ override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {
+ if (!ignoreDraws) onDraw(frame, pixels, matrix, transform, blend, smoothing, shader, wrapMode, depthCompareMode);
}
}
\ No newline at end of file
diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx
index 61c0e2e869..9764bc38c3 100644
--- a/source/funkin/backend/system/Flags.hx
+++ b/source/funkin/backend/system/Flags.hx
@@ -107,6 +107,10 @@ class Flags {
public static var DEFAULT_STEPS_PER_BEAT:Int = 4;
public static var DEFAULT_LOOP_TIME:Float = 0.0;
public static var ICONS_AUTOPOSITION:Bool = true;
+
+ @:lazy public static var DEFAULT_SOUND_TIME_SCALED_PITCH:Null = null;
+ @:lazy public static var USE_FLXTRAIL_FRAMES:Null = null;
+
public static var SUPPORTED_CHART_RUNTIME_FORMATS:Array = ["Legacy", "Psych Engine"];
public static var SUPPORTED_CHART_FORMATS:Array = ["BaseGame"];
@@ -282,7 +286,7 @@ class Flags {
public static var DEFAULT_CHARACTER_GHOSTDISABLE_SOUND:String = "editors/character/ghostDisable";
public static var DEFAULT_CHARACTER_GHOSTENABLE_SOUND:String = "editors/character/ghostEnable";
- public static var DEFAULT_GLSL_VERSION:String = "120";
+ @:lazy public static var DEFAULT_GLSL_VERSION:String = null;
@:also(funkin.backend.utils.HttpUtil.userAgent)
public static var USER_AGENT:String = 'request';
// -- End of Codename's Default Flags --
@@ -317,6 +321,20 @@ class Flags {
if (USE_LEGACY_TIMING == null) USE_LEGACY_TIMING = MOD_API_VERSION < 2;
if (USE_LEGACY_ZOOM_FACTOR == null) USE_LEGACY_ZOOM_FACTOR = MOD_API_VERSION < 2;
if (SUSTAINS_AS_ONE_NOTE == null) SUSTAINS_AS_ONE_NOTE = MOD_API_VERSION >= 2;
+ if (DEFAULT_GLSL_VERSION == null) {
+ if (MOD_API_VERSION < 2) {
+ DEFAULT_GLSL_VERSION = #if (android || mac || web) "100" #else "120" #end;
+ Logs.warn("Blend Mode Extensions won't work in MOD_API_VERSION below than 2");
+ }
+ else {
+ DEFAULT_GLSL_VERSION = openfl.utils.GLSLSourceAssembler.getDefaultVersion();
+ }
+ }
+ if (DEFAULT_SOUND_TIME_SCALED_PITCH == null) DEFAULT_SOUND_TIME_SCALED_PITCH = MOD_API_VERSION >= 2;
+ if (USE_FLXTRAIL_FRAMES == null) USE_FLXTRAIL_FRAMES = MOD_API_VERSION < 2;
+
+ flixel.sound.FlxSound.defaultTimeScaledPitch = cast DEFAULT_SOUND_TIME_SCALED_PITCH;
+ flixel.addons.effects.FlxTrail.defaultDelayBackwardCompatibility = cast USE_FLXTRAIL_FRAMES;
}
public static function loadFromDatas(datas:Array):Map {
diff --git a/source/funkin/backend/system/FunkinGame.hx b/source/funkin/backend/system/FunkinGame.hx
index b754c8d41d..08d2b5987d 100644
--- a/source/funkin/backend/system/FunkinGame.hx
+++ b/source/funkin/backend/system/FunkinGame.hx
@@ -28,12 +28,12 @@ class FunkinGame extends FlxGame {
super.switchState();
// draw once to put all images in gpu then put the last update time to now to prevent lag spikes or whatever
draw();
- _total = ticks = getTicks();
+ ticks = getTicks();
skipNextTickUpdate = true;
}
- public override function onEnterFrame(t) {
- if (skipNextTickUpdate != (skipNextTickUpdate = false)) _total = ticks = getTicks();
- super.onEnterFrame(t);
+ override function __enterFrame(deltaTime:Float) {
+ if (skipNextTickUpdate != (skipNextTickUpdate = false)) ticks = getTicks();
+ super.__enterFrame(deltaTime);
}
}
diff --git a/source/funkin/backend/system/GraphicCacheSprite.hx b/source/funkin/backend/system/GraphicCacheSprite.hx
index 6ff61905b3..cba41b61bd 100644
--- a/source/funkin/backend/system/GraphicCacheSprite.hx
+++ b/source/funkin/backend/system/GraphicCacheSprite.hx
@@ -36,7 +36,7 @@ class GraphicCacheSprite extends FlxSprite {
if (graphic == null) return;
// make their useCount one time higher to prevent them from auto being cleared from cache
- graphic.useCount++;
+ graphic.incrementUseCount();
graphic.destroyOnNoUse = false;
cachedGraphics.push(graphic);
nonRenderedCachedGraphics.push(graphic);
@@ -45,7 +45,7 @@ class GraphicCacheSprite extends FlxSprite {
public override function destroy() {
for(g in cachedGraphics) {
g.destroyOnNoUse = true;
- g.useCount--;
+ g.decrementUseCount();
}
graphic = null;
super.destroy();
diff --git a/source/funkin/backend/system/Logs.hx b/source/funkin/backend/system/Logs.hx
index 1913e4abc6..3903caf61b 100644
--- a/source/funkin/backend/system/Logs.hx
+++ b/source/funkin/backend/system/Logs.hx
@@ -115,7 +115,7 @@ final class Logs {
Sys.print(t.text);
}
NativeAPI.setConsoleColors();
- Sys.print("\r\n");
+ Sys.println("\r");
__showing = false;
#elseif mobile
while(__showing) {
diff --git a/source/funkin/backend/system/Main.hx b/source/funkin/backend/system/Main.hx
index 31d270d9d2..ed033baf13 100644
--- a/source/funkin/backend/system/Main.hx
+++ b/source/funkin/backend/system/Main.hx
@@ -24,8 +24,8 @@ import openfl.utils.AssetLibrary;
import sys.FileSystem;
import sys.io.File;
#if android
-import android.content.Context;
-import android.os.Build;
+import extension.androidtools.content.Context;
+import extension.androidtools.os.Build;
#end
class Main extends Sprite
@@ -33,7 +33,7 @@ class Main extends Sprite
public static var instance:Main;
public static var modToLoad:String = null;
- public static var forceGPUOnlyBitmapsOff:Bool = #if desktop false #else true #end;
+ public static var forceGPUOnlyBitmapsOff:Bool = false;
public static var noTerminalColor:Bool = false;
public static var verbose:Bool = false;
@@ -58,9 +58,11 @@ class Main extends Sprite
// You can pretty much ignore everything from here on - your code should go in your states.
public static function preInit() {
+ #if sys
funkin.backend.utils.NativeAPI.registerAsDPICompatible();
funkin.backend.system.CommandLineHandler.parseCommandLine(Sys.args());
funkin.backend.system.Main.fixWorkingDirectory();
+ #end
}
public function new()
@@ -95,16 +97,10 @@ class Main extends Sprite
// DEPRECATED
@:dox(hide) public static function execAsync(func:Void->Void) ThreadUtil.execAsync(func);
- private static function getTimer():Int {
- return time = Lib.getTimer();
- }
-
public static function loadGameSettings() {
WindowUtils.init();
SaveWarning.init();
MemoryUtil.init();
- @:privateAccess
- FlxG.game.getTimer = getTimer;
FunkinCache.init();
Paths.assetsTree = new AssetsLibraryList();
@@ -129,12 +125,11 @@ class Main extends Sprite
funkin.options.PlayerSettings.init();
Options.load();
+ game.focusLostFramerate = 30;
FlxG.fixedTimestep = false;
-
FlxG.scaleMode = scaleMode = new FunkinRatioScaleMode();
Conductor.init();
- AudioSwitchFix.init();
EventManager.init();
FlxG.signals.focusGained.add(onFocus);
FlxG.signals.preStateSwitch.add(onStateSwitch);
@@ -206,16 +201,6 @@ class Main extends Sprite
// manual asset clearing since base openfl one does'nt clear lime one
// does'nt clear bitmaps since flixel fork does it auto
- @:privateAccess {
- // clear uint8 pools
- for(length=>pool in openfl.display3D.utils.UInt8Buff._pools) {
- for(b in pool.clear())
- b.destroy();
- }
-
- openfl.display3D.utils.UInt8Buff._pools.clear();
- }
-
MemoryUtil.clearMajor();
}
diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx
index 14988dddc4..0b6da7234e 100644
--- a/source/funkin/backend/system/MainState.hx
+++ b/source/funkin/backend/system/MainState.hx
@@ -42,6 +42,7 @@ class MainState extends FlxState {
ControlsUtil.resetCustomControls();
FlxG.bitmap.reset();
FlxG.sound.destroy(true);
+ FlxG.sound.resetCache();
Paths.assetsTree.reset();
diff --git a/source/funkin/backend/system/OptimizedBitmapData.hx b/source/funkin/backend/system/OptimizedBitmapData.hx
index f6cf1117a9..510ebf14e2 100644
--- a/source/funkin/backend/system/OptimizedBitmapData.hx
+++ b/source/funkin/backend/system/OptimizedBitmapData.hx
@@ -1,62 +1,14 @@
package funkin.backend.system;
import lime.graphics.Image;
-import lime.graphics.cairo.CairoImageSurface;
import openfl.display.BitmapData;
-import openfl.geom.Rectangle;
+@:deprecated("Use openfl.display.BitmapData.toHardware instead.")
class OptimizedBitmapData extends BitmapData {
@SuppressWarnings("checkstyle:Dynamic")
@:noCompletion private override function __fromImage(image:#if lime Image #else Dynamic #end):Void
{
- #if lime
- if (image != null && image.buffer != null)
- {
- this.image = image;
-
- width = image.width;
- height = image.height;
- rect = new Rectangle(0, 0, image.width, image.height);
-
- __textureWidth = width;
- __textureHeight = height;
-
- #if sys
- image.format = BGRA32;
- image.premultiplied = true;
- #end
-
- __isValid = true;
- readable = true;
-
- if(FlxG.stage.context3D != null) {
- lock();
- getTexture(FlxG.stage.context3D);
- getSurface();
-
- readable = true;
- this.image = null;
-
- // @:privateAccess
- // if (FlxG.bitmap.__doNotDelete)
- // MemoryUtil.clearMinor();
- }
- }
- #end
- }
-
- @SuppressWarnings("checkstyle:Dynamic")
- @:dox(hide) public override function getSurface():#if lime CairoImageSurface #else Dynamic #end
- {
- #if lime
- if (__surface == null)
- {
- __surface = CairoImageSurface.fromImage(image);
- }
-
- return __surface;
- #else
- return null;
- #end
+ super.__fromImage(image);
+ toHardware();
}
}
\ No newline at end of file
diff --git a/source/funkin/backend/system/RotatingSpriteGroup.hx b/source/funkin/backend/system/RotatingSpriteGroup.hx
index ad3eea575e..520e77417f 100644
--- a/source/funkin/backend/system/RotatingSpriteGroup.hx
+++ b/source/funkin/backend/system/RotatingSpriteGroup.hx
@@ -8,7 +8,12 @@ class RotatingSpriteGroup extends FlxSpriteGroup {
if (maxSize <= 0)
return super.recycle(ObjectClass, ObjectFactory, Force, Revive);
if (group.members.length < maxSize)
- return group.recycleCreateObject(ObjectClass, ObjectFactory);
+ {
+ if (ObjectFactory != null) return add(ObjectFactory());
+ if (ObjectClass != null) return add(Type.createInstance(ObjectClass, []));
+
+ return null;
+ }
var spr = group.members.shift();
group.members.push(spr);
if (Revive)
diff --git a/source/funkin/backend/system/framerate/AssetTreeInfo.hx b/source/funkin/backend/system/framerate/AssetTreeInfo.hx
index f81218879e..b7647261c8 100644
--- a/source/funkin/backend/system/framerate/AssetTreeInfo.hx
+++ b/source/funkin/backend/system/framerate/AssetTreeInfo.hx
@@ -15,7 +15,7 @@ class AssetTreeInfo extends FramerateCategory {
super("Asset Libraries Tree Info");
}
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
if (alpha <= 0.05) return;
if ((lastUpdateTime += FlxG.rawElapsed) < 1)
diff --git a/source/funkin/backend/system/framerate/CodenameBuildField.hx b/source/funkin/backend/system/framerate/CodenameBuildField.hx
index 656c0fff9c..57bf752a2a 100644
--- a/source/funkin/backend/system/framerate/CodenameBuildField.hx
+++ b/source/funkin/backend/system/framerate/CodenameBuildField.hx
@@ -6,14 +6,17 @@ import openfl.text.TextField;
class CodenameBuildField extends TextField {
public function new() {
super();
- defaultTextFormat = Framerate.textFormat;
autoSize = LEFT;
multiline = wordWrap = false;
reload();
}
public function reload() {
- #if COMPILE_EXPERIMENTAL
+ defaultTextFormat = Framerate.textFormat;
+
+ #if TEST_BUILD
+ text = '${Flags.VERSION_MESSAGE} (Test Build)';
+ #elseif COMPILE_EXPERIMENTAL
text = '${Flags.VERSION_MESSAGE} (Experimental Build)';
#else
text = '${Flags.VERSION_MESSAGE}';
diff --git a/source/funkin/backend/system/framerate/ConductorInfo.hx b/source/funkin/backend/system/framerate/ConductorInfo.hx
index d5fc9a1737..3fb213d3dd 100644
--- a/source/funkin/backend/system/framerate/ConductorInfo.hx
+++ b/source/funkin/backend/system/framerate/ConductorInfo.hx
@@ -7,7 +7,7 @@ class ConductorInfo extends FramerateCategory {
super("Conductor Info");
}
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
if (alpha <= 0.05) return;
var buf = new StringBuf();
diff --git a/source/funkin/backend/system/framerate/FlixelInfo.hx b/source/funkin/backend/system/framerate/FlixelInfo.hx
index 0ce42c299a..675bd2facc 100644
--- a/source/funkin/backend/system/framerate/FlixelInfo.hx
+++ b/source/funkin/backend/system/framerate/FlixelInfo.hx
@@ -8,7 +8,7 @@ class FlixelInfo extends FramerateCategory {
super("Flixel Info");
}
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
if (alpha <= 0.05) return;
@:privateAccess {
diff --git a/source/funkin/backend/system/framerate/Framerate.hx b/source/funkin/backend/system/framerate/Framerate.hx
index c33b416c68..0d04c14bfb 100644
--- a/source/funkin/backend/system/framerate/Framerate.hx
+++ b/source/funkin/backend/system/framerate/Framerate.hx
@@ -76,6 +76,7 @@ class Framerate extends Sprite {
}
public function reload() {
+ textFormat = new TextFormat(fontName, 12, -1);
for(c in categories)
c.reload();
#if SHOW_BUILD_ON_FPS
@@ -100,7 +101,7 @@ class Framerate extends Sprite {
var debugAlpha:Float = 0;
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
alpha = CoolUtil.fpsLerp(alpha, debugMode > 0 ? 1 : 0, 0.5);
debugAlpha = CoolUtil.fpsLerp(debugAlpha, debugMode > 1 ? 1 : 0, 0.5);
diff --git a/source/funkin/backend/system/framerate/FramerateCategory.hx b/source/funkin/backend/system/framerate/FramerateCategory.hx
index 0bae85b2db..3bc28e3e56 100644
--- a/source/funkin/backend/system/framerate/FramerateCategory.hx
+++ b/source/funkin/backend/system/framerate/FramerateCategory.hx
@@ -40,9 +40,11 @@ class FramerateCategory extends Sprite {
this.text.y = this.title.y + this.title.height + 2;
}
- public function reload() {}
+ public function reload() {
+ for(label in [this.title, this.text]) label.defaultTextFormat = new TextFormat(Framerate.fontName, label == this.title ? 18 : 12, -1);
+ }
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
if (alpha <= 0.05) return;
super.__enterFrame(t);
diff --git a/source/funkin/backend/system/framerate/FramerateCounter.hx b/source/funkin/backend/system/framerate/FramerateCounter.hx
index 9f1406956e..3a00a3ecb6 100644
--- a/source/funkin/backend/system/framerate/FramerateCounter.hx
+++ b/source/funkin/backend/system/framerate/FramerateCounter.hx
@@ -35,10 +35,11 @@ class FramerateCounter extends Sprite {
}
public function reload() {
+ for (label in [fpsNum, fpsLabel]) label.defaultTextFormat = new TextFormat(Framerate.fontName, label == fpsNum ? 18 : 12, -1);
lastUpdateTime = 0;
}
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
if (alpha <= 0.05) return;
super.__enterFrame(t);
diff --git a/source/funkin/backend/system/framerate/MemoryCounter.hx b/source/funkin/backend/system/framerate/MemoryCounter.hx
index 068399b512..f95a2a93d1 100644
--- a/source/funkin/backend/system/framerate/MemoryCounter.hx
+++ b/source/funkin/backend/system/framerate/MemoryCounter.hx
@@ -23,16 +23,18 @@ class MemoryCounter extends Sprite {
label.y = 0;
label.text = "MEM";
label.multiline = label.wordWrap = false;
- label.defaultTextFormat = new TextFormat(Framerate.fontName, 12, -1);
+ label.defaultTextFormat = Framerate.textFormat;
label.selectable = false;
addChild(label);
}
memoryPeakText.alpha = 0.5;
}
- public function reload() {}
+ public function reload() {
+ for(label in [memoryText, memoryPeakText]) label.defaultTextFormat = Framerate.textFormat;
+ }
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
if (alpha <= 0.05) return;
super.__enterFrame(t);
diff --git a/source/funkin/backend/system/framerate/StatsInfo.hx b/source/funkin/backend/system/framerate/StatsInfo.hx
index bc98c39223..134c2649fc 100644
--- a/source/funkin/backend/system/framerate/StatsInfo.hx
+++ b/source/funkin/backend/system/framerate/StatsInfo.hx
@@ -10,7 +10,7 @@ class StatsInfo extends FramerateCategory {
super("Asset Libraries Tree Info");
}
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
if (alpha <= 0.05) return;
var buf = new StringBuf();
diff --git a/source/funkin/backend/system/framerate/SystemInfo.hx b/source/funkin/backend/system/framerate/SystemInfo.hx
index 4562b338a1..1cda22ff07 100644
--- a/source/funkin/backend/system/framerate/SystemInfo.hx
+++ b/source/funkin/backend/system/framerate/SystemInfo.hx
@@ -181,7 +181,7 @@ class SystemInfo extends FramerateCategory {
super("System Info");
}
- public override function __enterFrame(t:Int) {
+ public override function __enterFrame(t:Float) {
if (alpha <= 0.05) return;
var buf = new StringBuf();
diff --git a/source/funkin/backend/system/macros/Macros.hx b/source/funkin/backend/system/macros/Macros.hx
index 801eda0c17..a9a388a34e 100644
--- a/source/funkin/backend/system/macros/Macros.hx
+++ b/source/funkin/backend/system/macros/Macros.hx
@@ -58,6 +58,9 @@ class Macros {
final macroPath = 'funkin.backend.system.macros.Macros';
Compiler.addMetadata('@:build($macroPath.buildLimeAssetLibrary())', 'lime.utils.AssetLibrary');
+ Compiler.addMetadata('@:build($macroPath.buildLimeApplication())', 'lime.app.Application');
+ Compiler.addMetadata('@:build($macroPath.buildLimeWindow())', 'lime.ui.Window');
+ Compiler.addMetadata('@:build($macroPath.buildOpenflAssets())', 'openfl.utils.Assets');
//Adds Compat for #if hscript blocks when you have hscript improved
if (Context.defined("hscript_improved") && !Context.defined("hscript")) {
@@ -73,5 +76,58 @@ class Macros {
return fields;
}
+
+ public static function buildLimeApplication():Array {
+ final fields:Array = Context.getBuildFields(), pos:Position = Context.currentPos();
+ for (f in fields) switch (f.kind) {
+ case FFun(func): switch (f.name) {
+ case "exec": switch (func.expr.expr) {
+ case EBlock(exprs): exprs.insert(1, macro funkin.backend.system.Main.preInit());
+ default:
+ }
+ }
+ default:
+ }
+
+ return fields;
+ }
+
+ public static function buildLimeWindow():Array {
+ final fields:Array = Context.getBuildFields(), pos:Position = Context.currentPos();
+ if (!Context.defined("DARK_MODE_WINDOW")) return fields;
+
+ for (f in fields) switch (f.kind) {
+ case FFun(func): switch (f.name) {
+ case "new": switch (func.expr.expr) {
+ case EBlock(exprs): exprs.push(macro funkin.backend.utils.NativeAPI.setDarkMode(title, true));
+ default:
+ }
+ }
+ default:
+ }
+
+ return fields;
+ }
+
+ public static function buildOpenflAssets():Array {
+ final fields:Array = Context.getBuildFields(), pos:Position = Context.currentPos();
+ for (f in fields) switch (f.name) {
+ case "allowHardwareTextures": fields.remove(f);
+ default:
+ }
+
+ fields.push({name: 'allowHardwareTextures', access: [APublic, AStatic], pos: pos, kind: FProp("get", "set", macro :Bool)});
+ fields.push({name: '__allowHardwareTextures', access: [APrivate, AStatic], pos: pos, kind: FVar(macro :Null)});
+
+ fields.push({name: "get_allowHardwareTextures", access: [APublic, AStatic, AInline], pos: pos, kind: FFun({ret: macro :Bool, args: [], expr: macro {
+ return __allowHardwareTextures != null ? __allowHardwareTextures : !funkin.backend.system.Main.forceGPUOnlyBitmapsOff && funkin.options.Options.gpuOnlyBitmaps;
+ }})});
+ fields.push({name: "set_allowHardwareTextures", access: [APublic, AStatic, AInline], pos: pos, kind: FFun({ret: macro :Bool, args: [{name: "value", type: macro :Bool}], expr: macro {
+ __allowHardwareTextures = value;
+ return get_allowHardwareTextures();
+ }})});
+
+ return fields;
+ }
}
#end
\ No newline at end of file
diff --git a/source/funkin/backend/system/modules/ALSoftConfig.hx b/source/funkin/backend/system/modules/ALSoftConfig.hx
deleted file mode 100644
index 0b1b57a035..0000000000
--- a/source/funkin/backend/system/modules/ALSoftConfig.hx
+++ /dev/null
@@ -1,31 +0,0 @@
-package funkin.backend.system.modules;
-
-import haxe.io.Path;
-
-/*
- * A class that simply points OpenALSoft to a custom configuration file when
- * the game starts up.
- *
- * The config overrides a few global OpenALSoft settings with the aim of
- * improving audio quality on desktop targets.
- */
-@:keepInit class ALSoftConfig
-{
- #if desktop
- static function __init__():Void
- {
- var origin:String = #if hl Sys.getCwd() #else Sys.programPath() #end;
-
- var configPath:String = Path.directory(Path.withoutExtension(origin));
- #if windows
- configPath += "/plugins/alsoft.ini";
- #elseif mac
- configPath = Path.directory(configPath) + "/Resources/plugins/alsoft.conf";
- #else
- configPath += "/plugins/alsoft.conf";
- #end
-
- Sys.putEnv("ALSOFT_CONF", configPath);
- }
- #end
-}
\ No newline at end of file
diff --git a/source/funkin/backend/system/modules/AudioSwitchFix.hx b/source/funkin/backend/system/modules/AudioSwitchFix.hx
deleted file mode 100644
index 3e89e373b4..0000000000
--- a/source/funkin/backend/system/modules/AudioSwitchFix.hx
+++ /dev/null
@@ -1,69 +0,0 @@
-package funkin.backend.system.modules;
-
-import flixel.FlxState;
-import flixel.sound.FlxSound;
-import funkin.backend.utils.NativeAPI;
-import lime.media.AudioManager;
-import lime.media.AudioSource;
-import haxe.Timer;
-
-/**
- * if you are stealing this keep this comment at least please lol
- *
- * hi gray itsa me yoshicrafter29 i fixed it hehe
- */
-@:dox(hide)
-class AudioSwitchFix {
- public static function onAudioDisconnected() @:privateAccess {
- var sources:Array<{source:AudioSource, playing:Bool, time:Float, gain:Float, pitch:Float, position:lime.math.Vector4}> = [];
- for (source in AudioSource.activeSources) {
- var wasPlaying = source.playing;
- sources.push({
- source: source,
- playing: wasPlaying,
- time: source.currentTime,
- gain: source.gain,
- pitch: source.pitch,
- position: source.position
- });
-
- source.__backend.dispose();
- if (wasPlaying) source.__backend.playing = true;
- }
-
- AudioManager.shutdown();
- AudioManager.init();
- // #if !lime_doc_gen
- // if (AudioManager.context.type == OPENAL)
- // {
- // var alc = AudioManager.context.openal;
-
- // var device = alc.openDevice();
- // var ctx = alc.createContext(device);
- // alc.makeContextCurrent(ctx);
- // alc.processContext(ctx);
- // }
- // #end
-
- for (d in sources) {
- d.source.__backend.init();
- d.source.currentTime = d.time;
- d.source.gain = d.gain;
- d.source.pitch = d.pitch;
- d.source.position = d.position;
-
- if (d.playing) d.source.play();
- }
-
- Main.changeID++;
- Main.audioDisconnected = false;
- }
-
- private static var timer:Timer;
-
- private static function onRun() if (Main.audioDisconnected) onAudioDisconnected();
- public static function init() {
- NativeAPI.registerAudio();
- if (timer == null) (timer = new Timer(1000)).run = onRun;
- }
-}
\ No newline at end of file
diff --git a/source/funkin/backend/system/net/FunkinPacket.hx b/source/funkin/backend/system/net/FunkinPacket.hx
new file mode 100644
index 0000000000..8e15e0a302
--- /dev/null
+++ b/source/funkin/backend/system/net/FunkinPacket.hx
@@ -0,0 +1,70 @@
+package funkin.backend.system.net;
+
+import flixel.util.typeLimit.OneOfTwo;
+
+import haxe.io.Bytes;
+import haxe.io.BytesOutput;
+
+class FunkinPacket implements haxe.Constraints.IMap {
+ // Status of the packet. 200 is an OK response.
+ public var status:Int = -1;
+
+ // The JSON fields of the packet, as a Map.
+ private var fields:Map = [];
+
+ // If the recieved data is binary or contains binary, this will contain the raw bytes.
+ public var bytes:Bytes = null;
+
+ public function new() {}
+
+ public static function fromJson(json:OneOfTwo>):FunkinPacket { return (new FunkinPacket().appendJson(json)); }
+
+ public function appendJson(json:OneOfTwo>):FunkinPacket {
+ var parsedJson:haxe.DynamicAccess;
+ if (json is String) {
+ try {
+ parsedJson = haxe.Json.parse(json);
+ if (parsedJson == null) return this;
+
+ } catch(e) {
+ this.set("message", json);
+ return this;
+ }
+ } else parsedJson = json;
+ for (key => value in parsedJson) this.set(key, value);
+ return this;
+ }
+
+ public function toJson():haxe.DynamicAccess {
+ var json:haxe.DynamicAccess = {};
+ for (key => value in fields) json[key] = value;
+ return json;
+ }
+
+ inline public function stringify():String { return haxe.Json.stringify(toJson()); }
+
+ public function toBytes():Bytes {
+ var output = new BytesOutput();
+ output.writeString(haxe.Json.stringify(toJson()));
+ return output.getBytes();
+ }
+
+ inline public function get(key:String):Null { return fields.get(key); }
+ inline public function set(key:String, value:Dynamic):Void { fields.set(key, value); }
+ inline public function exists(key:String):Bool { return fields.exists(key); }
+ inline public function remove(key:String):Bool { return fields.remove(key); }
+
+ inline public function keys():Iterator { return fields.keys(); }
+ inline public function iterator():Iterator { return fields.iterator(); }
+ inline public function keyValueIterator():KeyValueIterator { return fields.keyValueIterator(); }
+
+ public function copy():FunkinPacket {
+ var copy = new FunkinPacket();
+ copy.fields = this.fields.copy();
+ copy.status = this.status;
+ return copy;
+ }
+
+ inline public function clear():Void { fields.clear(); }
+ inline public function toString():String { return 'FunkinPacket (Status: $status)'; }
+}
\ No newline at end of file
diff --git a/source/funkin/backend/system/net/FunkinSocket.hx b/source/funkin/backend/system/net/FunkinSocket.hx
new file mode 100644
index 0000000000..ef77fd3602
--- /dev/null
+++ b/source/funkin/backend/system/net/FunkinSocket.hx
@@ -0,0 +1,129 @@
+package funkin.backend.system.net;
+
+#if sys
+import sys.net.Host;
+import sys.net.Socket as SysSocket;
+import haxe.io.Bytes;
+
+@:keep
+class FunkinSocket implements IFlxDestroyable {
+ public var socket:SysSocket = new SysSocket();
+
+ public var metrics:Metrics = new Metrics();
+
+ public var FAST_SEND(default, set):Bool = true;
+ private function set_FAST_SEND(value:Bool):Bool {
+ FAST_SEND = value;
+ socket.setFastSend(value);
+ return value;
+ }
+ public var BLOCKING(default, set):Bool = false;
+ private function set_BLOCKING(value:Bool):Bool {
+ BLOCKING = value;
+ socket.setBlocking(value);
+ return value;
+ }
+
+ public var host:Host;
+ public var port:Int;
+
+ public function new(?_host:String = "127.0.0.1", ?_port:Int = 5000) {
+ FAST_SEND = true;
+ BLOCKING = false;
+ this.host = new Host(_host);
+ this.port = _port;
+ }
+
+ // Reading Area
+ public function readAll():Null {
+ try {
+ var bytes = this.socket.input.readAll();
+ if (bytes == null) return null;
+ metrics.updateBytesReceived(bytes.length);
+ return bytes;
+ } catch(e) { }
+ return null;
+ }
+ public function readLine():Null {
+ try {
+ var bytes = this.socket.input.readLine();
+ if (bytes == null) return null;
+ metrics.updateBytesReceived(bytes.length);
+ return bytes;
+ } catch(e) { }
+ return null;
+ }
+ public function read(nBytes:Int):Null {
+ try {
+ var bytes = this.socket.input.read(nBytes);
+ if (bytes == null) return null;
+ metrics.updateBytesReceived(bytes.length);
+ return bytes;
+ } catch(e) { }
+ return null;
+ }
+ public function readBytes(bytes:Bytes):Int {
+ try {
+ var length = this.socket.input.readBytes(bytes, 0, bytes.length);
+ metrics.updateBytesReceived(length);
+ return length;
+ } catch(e) { }
+ return 0;
+ }
+
+ // Writing Area
+ public function prepare(nbytes:Int):Void { socket.output.prepare(nbytes); }
+ public function write(bytes:Bytes):Bool {
+ try {
+ this.socket.output.write(bytes);
+ metrics.updateBytesSent(bytes.length);
+ return true;
+ } catch (e) { }
+ return false;
+ }
+ public function writeString(str:String):Bool {
+ try {
+ this.socket.output.writeString(str);
+ metrics.updateBytesSent(Bytes.ofString(str).length);
+ return true;
+ } catch(e) { }
+ return false;
+ }
+
+ public function bind(?expectingConnections:Int = 1):FunkinSocket {
+ Logs.traceColored([
+ Logs.logText('[FunkinSocket] ', BLUE),
+ Logs.logText('Binding to ', NONE), Logs.logText(host.toString(), YELLOW), Logs.logText(':', NONE), Logs.logText(Std.string(port), CYAN),
+ ]);
+ socket.bind(host, port);
+ socket.listen(expectingConnections);
+ return this;
+ }
+
+ public function connect():FunkinSocket {
+ Logs.traceColored([
+ Logs.logText('[FunkinSocket] ', BLUE),
+ Logs.logText('Connecting to ', NONE), Logs.logText(host.toString(), YELLOW), Logs.logText(':', NONE), Logs.logText(Std.string(port), CYAN),
+ ]);
+ socket.connect(host, port);
+ return this;
+ }
+
+ public function close() {
+ try {
+ if (socket != null) socket.close();
+ Logs.traceColored([
+ Logs.logText('[FunkinSocket] ', BLUE),
+ Logs.logText('Closing socket from ', NONE), Logs.logText(host.toString(), YELLOW), Logs.logText(':', NONE), Logs.logText(Std.string(port), CYAN),
+ ]);
+ } catch(e) {
+ Logs.traceColored([
+ Logs.logText('[FunkinSocket] ', BLUE),
+ Logs.logText('Failed to close socket: ${e}', NONE),
+ ]);
+ }
+ }
+
+ public function destroy() { close(); }
+}
+#end
\ No newline at end of file
diff --git a/source/funkin/backend/system/net/FunkinWebSocket.hx b/source/funkin/backend/system/net/FunkinWebSocket.hx
new file mode 100644
index 0000000000..a8f148042b
--- /dev/null
+++ b/source/funkin/backend/system/net/FunkinWebSocket.hx
@@ -0,0 +1,269 @@
+package funkin.backend.system.net;
+
+import funkin.backend.utils.ThreadUtil;
+
+import flixel.util.typeLimit.OneOfThree;
+
+import haxe.io.Bytes;
+import openfl.utils.ByteArray;
+
+import flixel.util.FlxSignal.FlxTypedSignal;
+import hx.ws.Log as LogWs;
+import hx.ws.WebSocket;
+import hx.ws.Types.MessageType;
+
+/**
+* This is a wrapper for hxWebSockets. Used in-tangem with `FunkinPacket` and `Metrics`.
+* You can override how `haxe.io.Bytes` is decoded by setting `AUTO_DECODE_PACKETS`. By default it will attempt to deserialize the packet into a `FunkinPacket`.
+* It also has `Metrics` which keeps track of the amount of bytes sent and received.
+**/
+class FunkinWebSocket implements IFlxDestroyable {
+ /**
+ * This interacts with the hxWebSockets logging system, probably the best way to get the debug info.
+ * Although, it's not in the format of CodenameEngine's logs so it might look weird.
+ **/
+ private static var LOG_INFO(default, set):Bool = false;
+ private static function set_LOG_INFO(value:Bool):Bool {
+ LOG_INFO = value;
+ if (LOG_INFO) LogWs.mask |= LogWs.INFO;
+ else LogWs.mask &= ~LogWs.INFO;
+ return value;
+ }
+ /**
+ * Ditto to LOG_INFO
+ **/
+ private static var LOG_DEBUG(default, set):Bool = false;
+ private static function set_LOG_DEBUG(value:Bool):Bool {
+ LOG_DEBUG = value;
+ if (LOG_DEBUG) LogWs.mask |= LogWs.DEBUG;
+ else LogWs.mask &= ~LogWs.DEBUG;
+ return value;
+ }
+ /**
+ * Ditto to LOG_INFO
+ **/
+ private static var LOG_DATA(default, set):Bool = false;
+ private static function set_LOG_DATA(value:Bool):Bool {
+ LOG_DATA = value;
+ if (LOG_DATA) LogWs.mask |= LogWs.DATA;
+ else LogWs.mask &= ~LogWs.DATA;
+ return value;
+ }
+
+ @:dox(hide) private var _ws:WebSocket;
+
+ /**
+ * This keeps track of the amount of bytes sent and received.
+ * You can set the logging state directly in the class.
+ **/
+ public var metrics:Metrics = new Metrics();
+
+ /**
+ * This signal is only called once when the connection is opened.
+ **/
+ public var onOpen:FlxTypedSignalVoid> = new FlxTypedSignalVoid>();
+ /**
+ * This signal is called every time a message is received.
+ * It can be one of three types: String, Bytes, or FunkinPacket.
+ * If you have AUTO_DECODE_PACKETS set to true, It will attempt to deserialize the packet into a FunkinPacket.
+ * If it fails to deserialize or AUTO_DECODE_PACKETS is false, it will just return the Bytes directly.
+ **/
+ public var onMessage:FlxTypedSignal->Void> = new FlxTypedSignal->Void>(); // cursed 😭😭
+ /**
+ * This signal is only called once when the connection is closed.
+ **/
+ public var onClose:FlxTypedSignalVoid> = new FlxTypedSignalVoid>();
+ /**
+ * This signal is only called when an error occurs. Useful for debugging and letting the user know something has gone wrong.
+ **/
+ public var onError:FlxTypedSignalVoid> = new FlxTypedSignalVoid>();
+
+ /**
+ * The URL to connect to, including the protocol (ws:// or wss://). Currently wss:// (Secure WebSocket) is not supported.
+ **/
+ public var url:String;
+
+ /**
+ * This just allows you to override or add custom headers when the handshake happens, as this is the first and last time we use proper HTTP Headers.
+ **/
+ public var handshakeHeaders(get, null):Map;
+ public function get_handshakeHeaders():Map { return this._ws.additionalHeaders; }
+
+ /**
+ * If true, the packets will be automatically deserialized into a `FunkinPacket` if possible.
+ * Otherwise you will handle everything manually.
+ **/
+ public var AUTO_DECODE_PACKETS:Bool = true;
+
+ /**
+ * @param _url The URL to connect to, including the protocol (ws:// or wss://). Currently wss:// (SSH) is not supported.
+ **/
+ public function new(_url:String) {
+ this.url = _url;
+
+ this._ws = new WebSocket(this.url, false);
+ this._ws.onopen = _onOpen;
+ this._ws.onmessage = _onMessage;
+ this._ws.onclose = _onClose;
+ this._ws.onerror = _onError;
+ }
+
+ /**
+ * Internal function for handling the onMessage event.
+ * @param message The message to handle from hxWebSockets.
+ **/
+ private function _onMessage(message:MessageType):Void {
+ var data:OneOfThree = "";
+
+ switch(message) {
+ case StrMessage(content):
+ data = content;
+ metrics.updateBytesReceived(Bytes.ofString(content).length);
+ // Can we just have a switch break in Haxe for the life of me bro 💔💔💔
+ if (AUTO_DECODE_PACKETS) {
+ var packet:FunkinPacket = FunkinPacket.fromJson(cast(data, String));
+ if (packet != null) {
+ packet.status = (packet.get("custom_status") ?? 200);
+
+ data = packet;
+ }
+ }
+ case BytesMessage(buffer):
+ metrics.updateBytesReceived(buffer.length);
+ if (AUTO_DECODE_PACKETS) {
+ var packet:FunkinPacket = new FunkinPacket();
+ packet.bytes = buffer.readAllAvailableBytes();
+ packet.status = 200;
+ data = packet;
+ } else
+ data = buffer.readAllAvailableBytes();
+ default: trace('Unknown message type: ${message}');
+ }
+
+ onMessage.dispatch(data);
+ }
+
+ /**
+ * Internal function for handling the onOpen event.
+ **/
+ private function _onOpen():Void {
+ onOpen.dispatch();
+ }
+
+ /**
+ * Internal function for handling the onClose event.
+ **/
+ private function _onClose():Void {
+ onClose.dispatch();
+ }
+
+ /**
+ * Internal function for handling the onError event.
+ **/
+ private function _onError(error:Dynamic):Void {
+ onError.dispatch(error);
+ }
+
+ /**
+ * Opens the WebSocket connection.
+ **/
+ public function open():FunkinWebSocket {
+ Logs.traceColored([
+ Logs.logText('[FunkinWebSocket] ', CYAN),
+ Logs.logText('Opening WebSocket to ', NONE), Logs.logText(url, YELLOW),
+ ]);
+ try {
+ this._ws.open();
+ } catch(e) {
+ Logs.traceColored([
+ Logs.logText('[FunkinWebSocket] ', CYAN),
+ Logs.logText('Failed to open WebSocket: ${e}', NONE),
+ ]);
+ onError.dispatch(e);
+ }
+ return this;
+ }
+
+ /**
+ * Sends data to the server.
+ * @param data The data to send.
+ * @return true if the data was sent successfully, false if it failed.
+ **/
+ public function send(data:Dynamic):Bool {
+ try {
+ if (data is FunkinPacket)
+ this._ws.send(data.stringify());
+ else
+ this._ws.send(data);
+
+ // Do an outside check to see if it wants to log before we try decoding stuff into raw bytes, to save on some unnecessary work.
+ if (metrics.IS_LOGGING) {
+ if (data is String) metrics.updateBytesSent(Bytes.ofString(data).length);
+ else if (data is Bytes || data is ByteArrayData) metrics.updateBytesSent(data.length);
+ else if (data is FunkinPacket) metrics.updateBytesSent(data.toBytes().length);
+ }
+
+ return true;
+ } catch(e) {
+ Logs.traceColored([
+ Logs.logText('[FunkinWebSocket] ', CYAN),
+ Logs.logText('Failed to send data: ${e}', NONE),
+ ]);
+ }
+ return false;
+ }
+
+
+ public static var MAX_BYTES_PER_CHUNK:Int = 16 * 1024; // 16KB
+
+ public function send_bytes(bytes:ByteArrayData, ?extra_start_data:Dynamic, ?extra_end_data:Dynamic):Void {
+ var bytes_start:FunkinPacket = new FunkinPacket();
+ bytes_start.set("sending_byte_chunks", true);
+ bytes_start.set("total_size", bytes.bytesAvailable);
+ bytes_start.appendJson(extra_start_data);
+
+ this.send(bytes_start);
+
+ // ensure we start at the beginning of the bytes
+ bytes.position = 0;
+ ThreadUtil.execAsync(() -> {
+ while (bytes.bytesAvailable > 0) {
+ try {
+ var size = Std.int(Math.min(MAX_BYTES_PER_CHUNK, bytes.bytesAvailable));
+
+ var chunk = new ByteArrayData();
+ bytes.readBytes(chunk, 0, size);
+ this._ws.send(chunk);
+ } catch(e) {
+ Logs.traceColored([
+ Logs.logText('[FunkinWebSocket] ', CYAN),
+ Logs.logText('Failed to compile a chunk: ${e}', NONE),
+ ]);
+ };
+ }
+ bytes.position = 0;
+
+ var bytes_end:FunkinPacket = new FunkinPacket();
+ bytes_end.set("chunks_complete", true);
+ bytes_end.appendJson(extra_end_data);
+ this.send(bytes_end);
+ });
+ }
+
+ /**
+ * Closes the WebSocket connection.
+ * Once you close the connection, you cannot reopen it, you must create a new instance.
+ **/
+ public function close():Void {
+ Logs.traceColored([
+ Logs.logText('[FunkinWebSocket] ', CYAN),
+ Logs.logText('Closing WebSocket from ', NONE), Logs.logText(url, YELLOW),
+ ]);
+ this._ws.close();
+ }
+
+ /**
+ * Basically the same as close(), but if a class is handling it and expects it to be IFlxDestroyable compatable, it will call this.
+ **/
+ public function destroy():Void { close(); }
+}
\ No newline at end of file
diff --git a/source/funkin/backend/system/net/Metrics.hx b/source/funkin/backend/system/net/Metrics.hx
new file mode 100644
index 0000000000..a417974120
--- /dev/null
+++ b/source/funkin/backend/system/net/Metrics.hx
@@ -0,0 +1,40 @@
+package funkin.backend.system.net;
+
+import flixel.util.FlxSignal.FlxTypedSignal;
+
+class Metrics {
+ public var bytesSent:Int = 0;
+ public var bytesReceived:Int = 0;
+
+ public var packetsSent:Int = 0;
+ public var packetsReceived:Int = 0;
+
+ public var IS_LOGGING:Bool = true;
+
+ public var onBytesSent:FlxTypedSignalVoid> = new FlxTypedSignalVoid>();
+ public var onBytesReceived:FlxTypedSignalVoid> = new FlxTypedSignalVoid>();
+
+ public function new() { }
+
+ public function updateBytesSent(amount:Int) {
+ if (!IS_LOGGING) return;
+
+ bytesSent += amount;
+ packetsSent++;
+
+ onBytesSent.dispatch(amount);
+ }
+
+ public function updateBytesReceived(amount:Int) {
+ if (!IS_LOGGING) return;
+
+ bytesReceived += amount;
+ packetsReceived++;
+
+ onBytesReceived.dispatch(amount);
+ }
+
+ public function toString():String {
+ return '(Metrics) $bytesSent bytes sent | $bytesReceived bytes received | $packetsSent packets sent | $packetsReceived packets received';
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/backend/system/net/Socket.hx b/source/funkin/backend/system/net/Socket.hx
deleted file mode 100644
index 19ebf9a116..0000000000
--- a/source/funkin/backend/system/net/Socket.hx
+++ /dev/null
@@ -1,65 +0,0 @@
-package funkin.backend.system.net;
-
-#if sys
-import sys.net.Host;
-import sys.net.Socket as SysSocket;
-
-@:keep
-class Socket implements IFlxDestroyable {
- public var socket:SysSocket;
-
- public function new(?socket:SysSocket) {
- this.socket = socket;
- if (this.socket == null)
- this.socket = new SysSocket();
- this.socket.setFastSend(true);
- this.socket.setBlocking(false);
- }
-
- public function read():String {
- try {
- return this.socket.input.readUntil('\n'.code).replace("\\n", "\n");
- } catch(e) {
-
- }
- return null;
- }
-
- public function write(str:String):Bool {
- try {
- this.socket.output.writeString(str.replace("\n", "\\n"));
- return true;
- } catch(e) {
-
- }
- return false;
- }
-
- public function host(host:Host, port:Int, nbConnections:Int = 1) {
- socket.bind(host, port);
- socket.listen(nbConnections);
- socket.setFastSend(true);
- }
-
- public function hostAndWait(h:Host, port:Int) {
- host(h, port);
- return acceptConnection();
- }
-
- public function acceptConnection():Socket {
- socket.setBlocking(true);
- var accept = new Socket(socket.accept());
- socket.setBlocking(false);
- return accept;
- }
-
- public function connect(host:Host, port:Int) {
- socket.connect(host, port);
- }
-
- public function destroy() {
- if (socket != null)
- socket.close();
- }
-}
-#end
\ No newline at end of file
diff --git a/source/funkin/backend/utils/AudioAnalyzer.hx b/source/funkin/backend/utils/AudioAnalyzer.hx
index c6f02cd6b9..3ad9d02dbb 100644
--- a/source/funkin/backend/utils/AudioAnalyzer.hx
+++ b/source/funkin/backend/utils/AudioAnalyzer.hx
@@ -1,67 +1,94 @@
package funkin.backend.utils;
-import flixel.sound.FlxSound;
-import lime.media.AudioBuffer;
+#if lime_openal
+import sys.thread.Mutex;
+
import lime.utils.ArrayBufferView.ArrayBufferIO;
import lime.utils.ArrayBuffer;
-#if (lime_cffi && lime_vorbis)
-import lime.media.vorbis.Vorbis;
-import lime.media.vorbis.VorbisFile;
-#end
+import flixel.sound.FlxSound;
+import flixel.sound.FlxSoundData;
-#if (target.threaded)
-import sys.thread.Mutex;
-#end
+typedef ReadCallback = Int->Int->Void;
+typedef WindowFunction = Float->Float;
+
+final class WindowFunctions {
+ static inline final TWO_PI:Float = 6.283185307179586;
+ static inline final FOUR_PI:Float = 12.566370614359172;
+ static inline final SIX_PI:Float = 18.84955592153876;
+ static inline final EIGHT_PI:Float = 25.132741228718345;
+
+ public static inline function triangular(x:Float):Float
+ return 1.0 - Math.abs(x - 0.5) * 2.0;
-typedef AudioAnalyzerCallback = Int->Int->Void;
+ public static inline function hann(x:Float):Float
+ return 0.5 - 0.5 * FlxMath.fastCos(TWO_PI * x);
+
+ public static inline function hamming(x:Float):Float
+ return 0.53836 - 0.46164 * FlxMath.fastCos(TWO_PI * x);
+
+ public static inline function blackmanNuttall(x:Float):Float
+ return 0.3635819 - 0.4891775 * FlxMath.fastCos(TWO_PI * x) + 0.1365995 * FlxMath.fastCos(FOUR_PI * x)
+ - 0.0106411 * FlxMath.fastCos(SIX_PI * x);
+
+ public static inline function blackmanHarris(x:Float):Float
+ return 0.4243801 - 0.4973406 * FlxMath.fastCos(TWO_PI * x) + 0.0782793 * FlxMath.fastCos(FOUR_PI * x);
+
+ public static inline function flatTop(x:Float):Float
+ return 0.21557895 - 0.41663158 * FlxMath.fastCos(TWO_PI * x) + 0.277263158 * FlxMath.fastCos(FOUR_PI * x)
+ + 0.083578947 * FlxMath.fastCos(SIX_PI * x) + 0.006947368 * FlxMath.fastCos(EIGHT_PI * x);
+}
+
+enum abstract TimeUnit(Int) from Int to Int {
+ var MILLISECOND = 0;
+ var SECOND = 1;
+ var SAMPLE = 2;
+}
/**
- * An utility that analyze FlxSounds,
+ * An utility that analyze FlxSound,
* can be used to make waveform or real-time audio visualizer.
- *
- * FlxSound.amplitude works so if any case if your only checking for peak of current time, use that instead.
*/
final class AudioAnalyzer {
/**
* Get bytes from an audio buffer with specified position and wordSize
- * @param buffer The audio buffer to get byte from.
- * @param position The specified position to get the byte from the audio buffer.
- * @param wordSize How many bytes to get with to one byte (Usually it's bitsPerSample / 8 or bitsPerSample >> 3).
+ * @param buffer The audio buffer to get byte from.
+ * @param position The specified position to get the byte from the audio buffer.
+ * @param wordSize How many bytes to get with to one byte (Usually it's bitsPerSample / 8 or bitsPerSample >> 3).
* @return Byte from the audio buffer with specified position.
*/
public static function getByte(buffer:ArrayBuffer, position:Int, wordSize:Int):Int {
- if (wordSize == 2) return inline ArrayBufferIO.getInt16(buffer, position);
+ if (wordSize == 2) return ArrayBufferIO.getInt16(buffer, position);
else if (wordSize == 3) {
- var b = inline ArrayBufferIO.getUint16(buffer, position) | (buffer.get(position + 2) << 16);
- if (b & 0x800000 != 0) return b - 0x1000000;
- else return b;
+ wordSize = ArrayBufferIO.getUint16(buffer, position) | (buffer.get(position + 2) << 16);
+ if (wordSize & 0x800000 != 0) return wordSize - 0x1000000;
+ else return wordSize;
}
- else if (wordSize == 4) return inline ArrayBufferIO.getInt32(buffer, position);
- else return inline ArrayBufferIO.getUint8(buffer, position) - 128;
+ else if (wordSize == 4) return ArrayBufferIO.getInt32(buffer, position);
+ else return ArrayBufferIO.getInt8(buffer, position);
}
/**
- * Gets levels from the frequencies with specified sample rate.
- * @param frequencies Frequencies input.
- * @param sampleRate Sample Rate input.
- * @param barCount How much bars to get.
- * @param levels The output for getting the values, to avoid memory leaks (Optional).
- * @param ratio How much ratio for smoothen the values from the previous levels values (Optional, use CoolUtil.getFPSRatio(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
- * @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
- * @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
- * @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
- * @param maxFreq The maximum frequency to cap (Optional, default 22000.0, Above 23000.0 is not recommended).
- * @return Output of levels/bars that ranges from 0 to 1.
+ * Gets spectrum from the frequencies with specified sample rate.
+ * @param frequencies Frequencies input.
+ * @param sampleRate Sample Rate input.
+ * @param barCount How much bars to get.
+ * @param spectrum The output for getting the values, to avoid memory leaks (Optional).
+ * @param ratio How much ratio for smoothen the values from the previous spectrum values (Optional, use FlxMath.getElapsedLerp(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
+ * @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
+ * @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
+ * @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
+ * @param maxFreq The maximum frequency to cap (Optional, default 20000.0, Above 23000.0 is not recommended).
+ * @return Output of spectrum/bars that ranges from 0 to 1.
*/
- public static function getLevelsFromFrequencies(frequencies:Array, sampleRate:Int, barCount:Int, ?levels:Array, ratio = 0.0, minDb = -63.0, maxDb = -10.0, minFreq = 20.0, maxFreq = 22000.0):Array {
- if (levels == null) levels = [];
- levels.resize(barCount);
+ public static function getSpectrumFromFrequencies(frequencies:Array, sampleRate:Int, barCount:Int, ?spectrum:Array, ratio = 0.0, minDb = -63.0, maxDb = -10.0, minFreq = 20.0, maxFreq = 20000.0):Array {
+ if (spectrum == null) spectrum = [];
+ if (spectrum.length != barCount) spectrum.resize(barCount);
- var logMin = Math.log(minFreq), logMax = Math.log(maxFreq);
- var logRange = logMax - logMin, dbRange = maxDb - minDb, n = frequencies.length;
+ var logMin = Math.log(minFreq), n = frequencies.length - 1;
+ var logRange = Math.log(maxFreq) - logMin, dbRangeRate = 1 / (maxDb - minDb), rate = frequencies.length * 2 / sampleRate;
inline function calculateScale(i:Int)
- return CoolUtil.bound(Math.exp(logMin + (logRange * i / (barCount + 1))) * n * 2 / sampleRate, 0, n - 1);
+ return FlxMath.bound(Math.exp(logMin + (logRange * i / (barCount + 1))) * rate, 0, n);
var s1 = calculateScale(0), s2;
var i1 = Math.floor(s1), i2;
@@ -81,138 +108,146 @@ final class AudioAnalyzer {
}
i1 = Math.floor(s1 = s2);
- v = CoolUtil.bound(((20 * Math.log(v) / 2.302585092994046) - minDb) / dbRange, 0, 1);
- if (ratio > 0 && ratio < 1 && v < levels[i]) levels[i] -= (levels[i] - v) * ratio;
- else levels[i] = v;
+ v = FlxMath.bound((Math.log(v) * 8.685889638065035 - minDb) * dbRangeRate, 0, 1);
+ if (ratio > 0 && ratio < 1 && v < spectrum[i]) spectrum[i] -= (spectrum[i] - v) * ratio;
+ else spectrum[i] = v;
}
- return levels;
+ return spectrum;
}
- static var __reverseIndices:Array> = [];
- static var __windows:Array> = [];
- static var __twiddleReals:Array> = [];
- static var __twiddleImags:Array> = [];
- static var __freqReals:Array> = [];
- static var __freqImags:Array> = [];
- static var __freqCalculating:Int = 0;
- #if (target.threaded)
- static var __mutex:Mutex = new Mutex();
- #end
+ /**
+ * Gets levels from the frequencies with specified sample rate.
+ * @param frequencies Frequencies input.
+ * @param sampleRate Sample Rate input.
+ * @param barCount How much bars to get.
+ * @param levels The output for getting the values, to avoid memory leaks (Optional).
+ * @param ratio How much ratio for smoothen the values from the previous levels values (Optional, use CoolUtil.getFPSRatio(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
+ * @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
+ * @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
+ * @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
+ * @param maxFreq The maximum frequency to cap (Optional, default 22000.0, Above 23000.0 is not recommended).
+ * @return Output of levels/bars that ranges from 0 to 1.
+ *
+ * deprecated, use getSpectrumFromFrequencies instead.
+ */
+ @:deprecated("Use getSpectrumFromFrequencies instead of getLevelsFromFrequencies.")
+ public static function getLevelsFromFrequencies(frequencies:Array, sampleRate:Int, barCount:Int, ?levels:Array, ratio = 0.0, minDb = -63.0, maxDb = -10.0, minFreq = 20.0, maxFreq = 22000.0):Array
+ return inline getSpectrumFromFrequencies(frequencies, sampleRate, barCount, levels, ratio, minDb, maxDb, minFreq, maxFreq);
+
+ static final _permutations:Map> = [];
+ static final _twiddleReals:Map> = [];
+ static final _twiddleImags:Map> = [];
+ static final _reals:Array> = [];
+ static final _imags:Array> = [];
+ static var _freqCalculating:Int = 0;
+ static final _mutex = new Mutex();
/**
* Gets frequencies from the samples.
- * @param samples The samples (can be from AudioAnalyzer.getSamples).
- * @param fftN How much samples for the fft to get, Has to be power of two, or it won't work.
- * @param useWindowing Should fft related stuff use blackman windowing? (Web AnalyzerNode windowing), Most of the time it's not worth it.
- * @param frequencies The output for getting the frequencies, to avoid memory leaks (Optional).
- * @return Output of frequencies.
+ * @param samples The samples (can be from FunkinAudioAnalyzer.getSamples).
+ * @param window The windowing function to use when passed.
+ * @param frequencies The output for getting the frequencies, to avoid memory leaks (Optional).
+ * @return Output of frequencies.
*/
- public static function getFrequenciesFromSamples(samples:Array, fftN = 2048, useWindowing = false, ?frequencies:Array):Array {
- var log = Math.floor(Math.log(fftN) / 0.6931471805599453);
- if (log == 0) throw "AudioAnalyzer.getFrequenciesFromSamples: Cannot insert a fftN of 1";
-
- var i = log - 1;
- fftN = 1 << log;
-
- #if (target.threaded) __mutex.acquire(); #end
- var reals:Array = __freqReals[__freqCalculating], imags:Array = __freqImags[__freqCalculating];
- if (reals == null) {
- __freqReals.push(reals = []);
- __freqImags.push(imags = []);
- }
- __freqCalculating++;
+ public static function getFrequenciesFromSamples(samples:Array, ?window:WindowFunction, ?frequencies:Array, ?fftN:Int):Array {
+ if (fftN == null) fftN = samples.length;
- var reverseIndices:Array = __reverseIndices[i];
- var windows:Array = __windows[i];
- var twiddleReals:Array = __twiddleReals[i];
- var twiddleImags:Array = __twiddleImags[i];
+ var bits = 0;
+ while ((fftN >>= 1) > 0) bits++;
+ if (bits == 0) throw "FunkinAudioAnalyzer.getFrequenciesFromSamples: Cannot insert a sample length or fftN of 1";
- if (reverseIndices == null) {
- __reverseIndices.resize(log);
- __windows.resize(log);
- __twiddleReals.resize(log);
- __twiddleImags.resize(log);
+ fftN = 1 << bits;
+ var fftN2 = fftN >> 1, n = fftN - 1;
- (reverseIndices = []).resize(fftN);
- (windows = []).resize(fftN);
- (twiddleReals = []).resize(fftN);
- (twiddleImags = []).resize(fftN);
+ var permutation:Array, twiddleReal:Array, twiddleImag:Array;
+ _mutex.acquire();
- var f;
+ var real:Array = _reals[_freqCalculating], imag:Array = _imags[_freqCalculating];
+ if (real == null) {
+ _reals.push(real = []);
+ _imags.push(imag = []);
+ }
+ _freqCalculating++;
+
+ if (_permutations.exists(bits)) {
+ permutation = _permutations.get(bits);
+ twiddleReal = _twiddleReals.get(bits);
+ twiddleImag = _twiddleImags.get(bits);
+ }
+ else {
+ (permutation = []).resize(fftN);
+ (twiddleReal = []).resize(fftN2);
+ (twiddleImag = []).resize(fftN2);
+
+ var ang:Float;
for (i in 0...fftN) {
- f = 2 * Math.PI * (i / fftN);
- windows[i] = 0.42 - 0.5 * Math.cos(f) + 0.08 * Math.cos(2 * f);
- reverseIndices[i] = __bitReverse(i, log);
- twiddleReals[i] = Math.cos(-f);
- twiddleImags[i] = Math.sin(-f);
+ permutation[i] = _bitReverse(i, bits);
+ if (i < fftN2) {
+ twiddleReal[i] = Math.cos((ang = -6.283185307179586 * i / n));
+ twiddleImag[i] = Math.sin(ang);
+ }
}
- __reverseIndices[i] = reverseIndices;
- __windows[i] = windows;
- __twiddleReals[i] = twiddleReals;
- __twiddleImags[i] = twiddleImags;
+ _permutations.set(bits, permutation);
+ _twiddleReals.set(bits, twiddleReal);
+ _twiddleImags.set(bits, twiddleImag);
}
- #if (target.threaded) __mutex.release(); #end
+ _mutex.release();
- if (fftN > reals.length) {
- reals.resize(fftN);
- imags.resize(fftN);
+ if (fftN > real.length) {
+ real.resize(fftN);
+ imag.resize(fftN);
}
if (frequencies == null) frequencies = [];
- frequencies.resize(1 << i);
+ if (frequencies.length != fftN2) frequencies.resize(fftN2);
- i = samples.length;
- while (i > 0) {
- i--;
- if (useWindowing) reals[reverseIndices[i]] = samples[i] * windows[i];
- else reals[reverseIndices[i]] = samples[i];
- imags[i] = 0;
+ var tr = 1 / n;
+ for (i in 0...fftN) {
+ real[permutation[i]] = samples[i];
+ if (window != null) real[permutation[i]] *= window(i * tr);
+ imag[i] = 0;
}
- var size = 1, n = fftN, half = 1, k, i0, i1, t, tr:Float, ti:Float;
- while ((size <<= 1) < fftN) {
- n >>= 1;
- i = 0;
- while (i < fftN) {
- k = 0;
- while (k < half) {
- i1 = (i0 = i + k) + half;
- t = (k * n) % fftN;
-
- tr = reals[i1] * twiddleReals[t] - imags[i1] * twiddleImags[t];
- ti = reals[i1] * twiddleImags[t] + imags[i1] * twiddleReals[t];
- reals[i1] = reals[i0] - tr;
- imags[i1] = imags[i0] - ti;
- reals[i0] += tr;
- imags[i0] += ti;
-
- k++;
+ var half = 1, g:Int, b:Int, r:Int, i0:Int, i1:Int, ti:Float;
+ while (fftN2 > 0) {
+ g = 0;
+ while (g < fftN) {
+ b = r = 0;
+ while (b < half) {
+ i1 = (i0 = g + b) + half;
+ tr = real[i1] * twiddleReal[r] - imag[i1] * twiddleImag[r];
+ ti = real[i1] * twiddleImag[r] + imag[i1] * twiddleReal[r];
+ real[i1] = real[i0] - tr;
+ imag[i1] = imag[i0] - ti;
+ real[i0] += tr;
+ imag[i0] += ti;
+ b++;
+ r += fftN2;
}
- i += size;
+ g += half << 1;
}
- half = size;
+ half <<= 1;
+ fftN2 >>= 1;
}
tr = 1.0 / fftN;
- i = 1 << (log - 1);
- while (i > 1) {
- i--;
- frequencies[i] = 2 * Math.sqrt(reals[i] * reals[i] + imags[i] * imags[i]) * tr;
- }
- frequencies[0] = Math.sqrt(reals[0] * reals[0] + imags[0] * imags[0]) * tr;
+ i0 = frequencies.length - 1;
+ for (i in 1...i0) frequencies[i] = 2 * Math.sqrt(real[i] * real[i] + imag[i] * imag[i]) * tr;
+ frequencies[0] = Math.sqrt(real[0] * real[0] + imag[0] * imag[0]) * tr;
+ frequencies[i0] = Math.sqrt(real[i0] * real[i0] + imag[i0] * imag[i0]) * tr;
- #if (target.threaded) __mutex.acquire(); #end
- __freqCalculating--;
- #if (target.threaded) __mutex.release(); #end
+ _mutex.acquire();
+ _freqCalculating--;
+ _mutex.release();
return frequencies;
}
- static function __bitReverse(x:Int, log:Int):Int {
- var y = 0, i = log;
+ static function _bitReverse(x:Int, bits:Int):Int {
+ var y = 0, i = bits;
while (i > 0) {
y = (y << 1) | (x & 1);
x >>= 1;
@@ -227,346 +262,357 @@ final class AudioAnalyzer {
public var sound:FlxSound;
/**
- * How much samples for the fft to get.
- * Usually for getting the levels or frequencies of the sound.
- *
- * Has to be power of two, or it won't work.
- */
- public var fftN:Int;
-
- /**
- * Should fft related stuff use blackman windowing? (Web AnalyzerNode windowing).
- * Most of the time looks bad with this.
+ * The current data from sound.
*/
- public var useWindowingFFT:Bool;
+ public var data(default, null):FlxSoundData;
/**
- * The current buffer from sound.
+ * How much samples for the fourier transform to get.
+ * Has to be power of two, or it won't work.
*/
- public var buffer(default, null):AudioBuffer;
+ public var fftN:Int;
/**
* The current byteSize from buffer.
- * Example the byteSize of 16 BitsPerSample is 32768 (1 << 16-1)
+ * Example the byteSize of 16 BitsPerSample is 32768 (1 << (16 - 1))
*/
public var byteSize(default, null):Int;
- var __toBits:Float;
- var __wordSize:Int;
- var __sampleSize:Int;
-
- #if (lime_cffi && lime_vorbis)
- var __vorbis:VorbisFile;
- var __buffer:ArrayBuffer;
- var __bufferSize:Int;
- var __bufferLastSize:Int;
- var __bufferTime:Float;
- var __bufferLastTime:Float;
- #end
-
- // analyze
- var __min:Array = [];
- var __max:Array = [];
- var __minByte:Int;
- var __maxByte:Int;
-
- // samples
- var __sampleIndex:Int;
- var __sampleChannel:Int;
- var __sampleToValue:Float;
- var __sampleOutputMerge:Bool;
- var __sampleOutputLength:Int;
- var __sampleOutput:Array;
-
- // frequencies
- var __freqSamples:Array;
- var __frequencies:Array;
-
- /**
- * Creates an analyzer for specified FlxSound
- * @param sound An FlxSound to analyze.
- * @param fftN How much samples for fft to get (Optional, default 2048, 4096 is recommended for highest quality).
- * @param useWindowingFFT Should fft related stuff use blackman windowing? (Web AnalyzerNode windowing).
- */
- public function new(sound:FlxSound, fftN = 2048, useWindowingFFT = false) {
+ var _sampleSize:Int;
+ var _mins:Array = [];
+ var _maxs:Array = [];
+ //var _decoder:FunkinAudioDecoder;
+ //var _buffer:ArrayBuffer;
+ //var _bufferLen:Int;
+ //var _bufferLastSize:Int;
+ //var _bufferLastSample:Int;
+ var _sampleIndex:Int;
+ var _sampleChannel:Int;
+ var _sampleValue:Int;
+ var _sampleValueGain:Float;
+ var _sampleOutputMerge:Bool;
+ var _sampleOutputLength:Int;
+ var _sampleOutput:Array;
+ var _freqSamples:Array;
+ var _frequencies:Array;
+
+ public function new(sound:FlxSound, fftN = 4096) {
this.sound = sound;
this.fftN = fftN;
- this.useWindowingFFT = useWindowingFFT;
- __check();
+ _check();
}
- function __check() if (sound != null && sound.buffer != buffer) {
- byteSize = 1 << ((buffer = sound.buffer).bitsPerSample - 1);
-
- #if (lime_cffi && lime_vorbis)
- __vorbis = null;
- __bufferLastSize = 0;
- __bufferTime = Math.NaN;
- __bufferLastTime = Math.NaN;
- #end
+ function _check() {
+ if (sound != null && !sound.data.isDestroyed) {
+ if (sound.data != data)
+ {
+ byteSize = 1 << ((data = sound.data).bitsPerSample - 1);
+ _sampleSize = data.channels * (data.bitsPerSample >> 3);
+ _mins.resize(data.channels);
+ _maxs.resize(data.channels);
+ //_decoder?.destroy();
+ }
+ }
+ else data = null;
+ }
- __toBits = buffer.sampleRate / 1000 * (__sampleSize = buffer.channels * (__wordSize = buffer.bitsPerSample >> 3));
- __min.resize(buffer.channels);
- __max.resize(buffer.channels);
+ /**
+ * Gets spectrum from an attached sound from position.
+ * @param pos Position to get (Optional).
+ * @param timeUnit TimeUnit to use for positions (Optional).
+ * @param gain How much gain multiplier will it affect the output. (Optional, default 1.0).
+ * @param barCount How much bars to get.
+ * @param spectrum The output for getting the values, to avoid memory leaks (Optional).
+ * @param ratio How much ratio for smoothen the values from the previous spectrum values (Optional, use FlxMath.getElapsedLerp(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
+ * @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
+ * @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
+ * @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
+ * @param maxFreq The maximum frequency to cap (Optional, default 20000.0, Above 23000.0 is not recommended).
+ * @return Output of spectrum/bars that ranges from 0 to 1.
+ */
+ public function getSpectrum(?pos:Float, ?timeUnit:TimeUnit, ?gain:Float, ?window:WindowFunction, barCount:Int, ?spectrum:Array, ?ratio:Float, ?minDb:Float, ?maxDb:Float, ?minFreq:Float, ?maxFreq:Float):Array {
+ return getSpectrumFromFrequencies(_frequencies = getFrequencies(pos, timeUnit, gain, window, _frequencies), data.sampleRate, barCount, spectrum, ratio, minDb, maxDb, minFreq, maxFreq);
}
/**
* Gets levels from an attached FlxSound from startPos, basically a minimized of frequencies.
- * @param startPos Start Position to get from sound in milliseconds.
- * @param volume How much volume multiplier will it affect the output. (Optional, default 1.0).
- * @param barCount How much bars to get.
- * @param levels The output for getting the values, to avoid memory leaks (Optional).
- * @param ratio How much ratio for smoothen the values from the previous levels values (Optional, use CoolUtil.getFPSRatio(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
- * @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
- * @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
- * @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
- * @param maxFreq The maximum frequency to cap (Optional, default 22000.0, Above 23000.0 is not recommended).
- * @return Output of levels/bars that ranges from 0 to 1.
+ * @param startPos Start Position to get from sound in milliseconds.
+ * @param volume How much volume multiplier will it affect the output. (Optional, default 1.0).
+ * @param barCount How much bars to get.
+ * @param levels The output for getting the values, to avoid memory leaks (Optional).
+ * @param ratio How much ratio for smoothen the values from the previous levels values (Optional, use CoolUtil.getFPSRatio(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
+ * @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
+ * @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
+ * @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
+ * @param maxFreq The maximum frequency to cap (Optional, default 22000.0, Above 23000.0 is not recommended).
+ * @return Output of levels/bars that ranges from 0 to 1.
+ *
+ * deprecated, use getLevels instead.
*/
+ @:deprecated("Use getSpectrum instead of getLevels.")
public function getLevels(?startPos:Float, ?volume:Float, barCount:Int, ?levels:Array, ?ratio:Float, ?minDb:Float, ?maxDb:Float, ?minFreq:Float, ?maxFreq:Float):Array
- return inline getLevelsFromFrequencies(__frequencies = getFrequencies(startPos, volume, __frequencies), buffer.sampleRate, barCount, levels, ratio, minDb, maxDb, minFreq, maxFreq);
+ return inline getSpectrum(startPos, MILLISECOND, volume, null, barCount, levels, ratio, minDb, maxDb, minFreq, maxFreq);
/**
- * Gets frequencies from an attached FlxSound from startPos.
- * @param startPos Start Position to get from sound in milliseconds.
- * @param volume How much volume multiplier will it affect the output. (Optional, default 1.0).
- * @param frequencies The output for getting the frequencies, to avoid memory leaks (Optional).
- * @return Output of frequencies.
+ * Gets frequencies from an attached sound from position.
+ * @param pos Position to get. (Optional).
+ * @param timeUnit TimeUnit to use for positions. (Optional).
+ * @param gain How much gain multiplier will it affect the output. (Optional, default 1.0).
+ * @param window The windowing function to use when passed.
+ * @param frequencies The output for getting the frequencies, to avoid memory leaks (Optional).
+ * @return Output of frequencies.
*/
- public function getFrequencies(?startPos:Float, ?volume:Float, ?frequencies:Array):Array
- return inline getFrequenciesFromSamples(__freqSamples = getSamples(startPos != null ? startPos : sound.time, fftN, true, -1, volume, __freqSamples), fftN, useWindowingFFT, frequencies);
+ public function getFrequencies(?pos:Float, ?timeUnit:TimeUnit, ?gain:Float, ?window:WindowFunction, ?frequencies:Array):Array {
+ if (pos == null) {
+ if (sound == null) return frequencies;
+ _check();
+ if ((pos = sound.time / 1000 * data.sampleRate - fftN) < 0) pos = 0;
+ timeUnit = SAMPLE;
+ }
+ return getFrequenciesFromSamples(_freqSamples = getSamples(pos, timeUnit, fftN, true, -1, gain, _freqSamples), window, frequencies);
+ }
/**
- * Analyzes an attached FlxSound from startPos to endPos in milliseconds to get the amplitudes.
- * @param startPos Start Position to get from sound in milliseconds.
- * @param endPos End Position to get from sound in milliseconds.
- * @param outOrOutMin The output minimum value from the analyzer, indices is in channels (0 to -0.5 -> 0 to 0.5) (Optional, if outMax doesn't get passed in, it will be [min, max] with all channels combined instead).
- * @param outMax The output maximum value from the analyzer, indices is in channels (Optional).
- * @return Output of amplitude from given position.
+ * Analyzes an attached sound from startPos to endPos in milliseconds to get the amplitudes.
+ * @param startPos Start Position to get.
+ * @param endPos End Position to get.
+ * @param timeUnit TimeUnit to use for positions.
+ * @param outOrOutMins The output minimum value from the analyzer, indices is in channels (0 to -0.5 -> 0 to 0.5) (Optional, if outMax doesn't get passed in, it will be [min, max] with all channels combined instead).
+ * @param outMaxs The output maximum value from the analyzer, indices is in channels (Optional).
+ * @return Output of amplitude from given position.
*/
- public function analyze(startPos:Float, endPos:Float, ?outOrOutMin:Array, ?outMax:Array):Float {
- var hasOut = outOrOutMin != null;
- var hasTwoOut = hasOut && outMax != null;
-
- if (hasTwoOut) for (i in 0...buffer.channels) __min[i] = __max[i] = 0;
- __minByte = __maxByte = 0;
-
- __check();
- __read(startPos, endPos, hasTwoOut ? __analyzeCallback : __analyzeCallbackSimple);
-
- if (hasOut) {
- var f:Float;
- if (hasTwoOut) for (i in 0...buffer.channels) {
- if (outOrOutMin[i] < (f = __min[i] / byteSize)) outOrOutMin[i] = f;
- if (outMax[i] < (f = __max[i] / byteSize)) outMax[i] = f;
- }
- else {
- outOrOutMin.resize(2);
- if (outOrOutMin[0] < (f = __minByte / byteSize)) outOrOutMin[0] = f;
- if (outOrOutMin[1] < (f = __maxByte / byteSize)) outOrOutMin[1] = f;
+ public function analyze(startPos:Float, endPos:Float, ?timeUnit:TimeUnit, ?outOrOutMins:Array, ?outMaxs:Array):Float {
+ var hasOut = outOrOutMins != null;
+ var hasTwoOut = hasOut && outMaxs != null;
+
+ _check();
+ var conversion:Float = switch (timeUnit) {
+ case SAMPLE: 1;
+ case SECOND: data.sampleRate;
+ default: data.sampleRate / 1000;
+ }
+ for (i in 0...data.channels) _mins[i] = _maxs[i] = -0x7FFFFFFF;
+ if (startPos > endPos) _read(Math.floor(startPos * conversion), Math.floor(endPos * conversion), _analyzeRead);
+
+ var min = -0x7FFFFFFF, max = -0x7FFFFFFF, v = 1 / byteSize, f:Float;
+ for (i in 0...data.channels) {
+ if (hasTwoOut) {
+ if ((f = _mins[i] * v) > outOrOutMins[i]) outOrOutMins[i] = f;
+ if ((f = _maxs[i] * v) > outMaxs[i]) outMaxs[i] = f;
}
+ if (_maxs[i] > max) max = _maxs[i];
+ if (_mins[i] > min) min = _mins[i];
}
- return (__maxByte + __minByte) / byteSize;
+ if (hasOut && outMaxs == null) {
+ if ((f = min * v) > outOrOutMins[0]) outOrOutMins[0] = f;
+ if ((f = max * v) > outOrOutMins[1]) outOrOutMins[1] = f;
+ }
+ return (max + min) * v;
}
- function __analyzeCallback(b:Int, c:Int):Void
- ((b > __max[c]) ? (if ((__max[c] = b) > __maxByte) (__maxByte = b)) : (if (-b > __min[c]) (if ((__min[c] = -b) > __minByte) (__minByte = __min[c]))));
-
- function __analyzeCallbackSimple(b:Int, c:Int):Void
- ((b > __maxByte) ? (__maxByte = b) : (if (-b > __minByte) (__minByte = -b)));
+ function _analyzeRead(b:Int, c:Int) ((b > _maxs[c]) ? (_maxs[c] = b) : (if (-b > _mins[c]) (_mins[c] = -b)));
/**
* Gets samples from startPos with given length of samples.
- * @param startPos Start Position to get from sound in milliseconds.
- * @param length Length of Samples.
- * @param mono Merge all of the byte channels of samples in one channel instead (Optional).
- * @param channel What channels to get from? (-1 == All Channels, Optional, this will be ignored if mono is enabled).
- * @param volume How much volume multiplier will it affect the output. (Optional, default 1.0).
- * @param output An Output that gets passed into this function, usually for to avoid memory leaks (Optional).
- * @param outputMerge Merge with previous values (Optional, default false).
- * @return Output of samples.
+ * @param startPos Start Position to get.
+ * @param timeUnit TimeUnit to use for positions.
+ * @param length Length of Samples.
+ * @param mono Merge all of the byte channels of samples in one channel instead (Optional).
+ * @param channel What channels to get from? (-1 == All Channels, Optional, this will be ignored if mono is enabled).
+ * @param gain How much gain multiplier will it affect the output. (Optional, default 1.0).
+ * @param output An Output that gets passed into this function, usually for to avoid memory leaks (Optional).
+ * @param outputMerge Merge with previous values (Optional, default false).
+ * @return Output of samples.
*/
- public function getSamples(startPos:Float, length:Int, mono = true, channel = -1, volume = 1.0, ?output:Array, ?outputMerge = false):Array {
- ((!mono && (__sampleChannel = channel) == -1) ? (__sampleOutputLength = length * buffer.channels) : (__sampleOutputLength = length));
- ((output == null) ? (__sampleOutput = output = []) : (__sampleOutput = output)).resize(__sampleOutputLength);
- ((mono) ? (__sampleToValue = volume / (byteSize * buffer.channels)) : (__sampleToValue = 1.0 / byteSize));
- __sampleOutputMerge = outputMerge;
- __sampleIndex = 0;
-
- __check();
- __read(startPos, startPos + (length / __toBits * buffer.channels), mono ? __getSamplesCallbackMono : (channel == -1 ? __getSamplesCallback : __getSamplesCallbackChannel));
-
- __sampleOutput = null;
+ public function getSamples(startPos:Float, ?timeUnit:TimeUnit, length:Int, mono = true, channel = -1, gain = 1.0, ?output:Array, ?outputMerge = false):Array {
+ _check();
+ ((!mono && channel == -1) ? (_sampleOutputLength = length * data.channels) : (_sampleOutputLength = length));
+ if (((output == null) ? (_sampleOutput = output = []) : (_sampleOutput = output)).length != _sampleOutputLength) output.resize(_sampleOutputLength);
+ _sampleValueGain = gain;
+ _sampleOutputMerge = outputMerge;
+ _sampleIndex = 0;
+ _sampleValue = 0;
+
+ final samplePos = Math.floor(switch (timeUnit) {
+ case SAMPLE: startPos;
+ case SECOND: startPos * data.sampleRate;
+ default: startPos * data.sampleRate / 1000;
+ });
+ _sampleChannel = mono ? data.channels - 1 : channel;
+ if (length > 0) _read(samplePos, samplePos + length, mono ? _getSamplesCallbackMono : (channel == -1 ? _getSamplesCallback : _getSamplesCallbackChannel));
+
+ _sampleOutput = null;
return output;
}
- function __getSamplesCallbackMono(b:Int, c:Int):Void if (__sampleIndex < __sampleOutputLength) {
- if (c == 0) {
- if (__sampleOutputMerge) __sampleOutput[__sampleIndex] += b * __sampleToValue;
- else __sampleOutput[__sampleIndex] = b * __sampleToValue;
- }
- else if (c == buffer.channels) {
- __sampleOutput[__sampleIndex] += b * __sampleToValue;
- __sampleIndex++;
+ function _getSamplesCallbackMono(b:Int, c:Int):Void if (_sampleIndex < _sampleOutputLength) {
+ if (c == 0) _sampleValue = idiv(b, data.channels);
+ else _sampleValue += idiv(b, data.channels);
+
+ if (c == _sampleChannel) {
+ if (_sampleOutputMerge) _sampleOutput[_sampleIndex] += _sampleValue / byteSize;
+ else _sampleOutput[_sampleIndex] = _sampleValue / byteSize;
+ _sampleIndex++;
}
- else
- __sampleOutput[__sampleIndex] += b * __sampleToValue;
}
- function __getSamplesCallbackChannel(b:Int, c:Int):Void if (__sampleIndex < __sampleOutputLength) {
- if (c == __sampleChannel) {
- if (__sampleOutputMerge) __sampleOutput[__sampleIndex] += b * __sampleToValue;
- else __sampleOutput[__sampleIndex] = b * __sampleToValue;
- __sampleIndex++;
+ function _getSamplesCallbackChannel(b:Int, c:Int):Void if (_sampleIndex < _sampleOutputLength) {
+ if (c == _sampleChannel) {
+ if (_sampleOutputMerge) _sampleOutput[_sampleIndex] += b / byteSize;
+ else _sampleOutput[_sampleIndex] = b / byteSize;
+ _sampleIndex++;
}
}
- function __getSamplesCallback(b:Int, c:Int):Void if (__sampleIndex < __sampleOutputLength) {
- if (__sampleOutputMerge) __sampleOutput[__sampleIndex] += b * __sampleToValue;
- else __sampleOutput[__sampleIndex] = b * __sampleToValue;
- __sampleIndex++;
+ function _getSamplesCallback(b:Int, c:Int):Void if (_sampleIndex < _sampleOutputLength) {
+ if (_sampleOutputMerge) _sampleOutput[_sampleIndex] += b / byteSize;
+ else _sampleOutput[_sampleIndex] = b / byteSize;
+ _sampleIndex++;
}
/**
- * Read an attached FlxSound from startPos to endPos in milliseconds with a callback.
- * @param startPos Start Position to get from sound in milliseconds.
- * @param endPos End Position to get from sound in milliseconds.
- * @param callback Int->Int->Void Byte->Channels->Void Callback to get the byte of a sample.
+ * Read an attached sound from startPos to endPos in milliseconds with a callback.
+ * @param startPos Start Position to get.
+ * @param endPos End Position to get.
+ * @param timeUnitTimeUnit to use for positions.
+ * @param callback Byte:Int->Channels:Int->Void Callback to get the byte of a sample.
*/
- public function read(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback) {
- __check();
- __read(startPos, endPos, callback);
+ public function read(startPos:Float, endPos:Float, ?timeUnit:TimeUnit, callback:ReadCallback) {
+ _check();
+ var conversion:Float = switch (timeUnit) {
+ case SAMPLE: 1;
+ case SECOND: data.sampleRate;
+ default: data.sampleRate / 1000;
+ }
+ if (startPos > endPos) _read(Math.floor(startPos * conversion), Math.floor(endPos * conversion), callback);
}
- inline function __read(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback) {
- if (buffer.data != null) __readData(startPos, endPos, callback);
- #if lime_cffi
- else if (__canReadStream() && (startPos += __readStream(startPos, endPos, callback)) >= endPos) {}
- #if lime_vorbis
- else if (__prepareDecoder()) __readDecoder(startPos, endPos, callback);
- #end
- #end
- }
+ function _read(startSample:Int, endSample:Int, callback:ReadCallback) {
+ // use data in ram if available
+ if (data.buffer.data != null) _readData(startSample * _sampleSize, endSample * _sampleSize, callback);
+ // use decoded datas that have been used in streaming sound to reduce jumping disk seeking
+ // if not use decoder and use seeking instead*
+ else if (sound.loaded) _readStream(startSample, endSample, callback);
- inline function __readData(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback) {
- var pos = Math.floor(startPos * __toBits), end = Math.min(Math.floor(endPos * __toBits), buffer.data.buffer.length), c = 0;
- pos -= pos % __sampleSize;
- end -= end % __sampleSize;
+ // TODO
+ //else if ((!sound.loaded || (startSample = _readStream(startSample, endSample, callback)) < endSample) && _prepareDecoder())
+ // _readDecoder(startSample, endSample, callback);
+ }
- while (pos < end) {
- callback(getByte(buffer.data.buffer, pos, __wordSize), c);
- if (++c > buffer.channels) c = 0;
- pos += __wordSize;
+ inline function _readData(startIndex:Int, endIndex:Int, callback:ReadCallback) {
+ if (endIndex > data.buffer.data.byteLength) endIndex = data.buffer.data.byteLength;
+ var buffer = data.buffer.data.buffer, byteRate = data.bitsPerSample >> 3, c = 0;
+ while (startIndex < endIndex) {
+ callback(getByte(buffer, startIndex, byteRate), c);
+ startIndex += byteRate;
+ if (++c == data.channels) c = 0;
}
}
- #if lime_cffi
- inline function __canReadStream():Bool
- @:privateAccess return sound._source != null && sound._source.__backend != null && sound._source.__backend.playing;
-
- inline function __readStream(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback):Float @:privateAccess {
- final backend = sound._source.__backend;
-
- // TODO: Wrap it with try until i figured it out an effective way to do this...
- // So... sometimes it just uses the decoder even if it looks good?? please help
- var n = Math.floor((endPos - startPos) * __toBits);
- var i = backend.bufferLengths.length - backend.requestBuffers - 1, time:Float;
- while (++i < backend.bufferLengths.length) if (startPos >= (time = backend.bufferTimes[i] * 1000)) {
- var pos = Math.floor((startPos - time) * __toBits), buf = backend.bufferDatas[i].buffer, size = backend.bufferLengths[i], c = 0;
- while (pos >= size) {
- if (++i >= backend.bufferLengths.length) break;
- pos -= size;
- buf = backend.bufferDatas[i].buffer;
- size = backend.bufferLengths[i];
- }
- if (i >= backend.bufferLengths.length) break;
- if ((pos -= pos % __sampleSize) < 0) pos = 0;
- n -= pos % __sampleSize;
-
- while (n > 0) {
- callback(getByte(buf, pos, __wordSize), c);
- if (++c > buffer.channels) c = 0;
- if ((pos += __wordSize) >= size) {
- if (++i >= backend.bufferLengths.length) break;
- pos = 0;
- buf = backend.bufferDatas[i].buffer;
- size = backend.bufferLengths[i];
+ function _readStream(startSample:Int, endSample:Int, callback:ReadCallback):Int @:privateAccess {
+ final backend = sound.source.__backend;
+ if (backend.filledBuffers == 0) return startSample;
+
+ backend.mutex.acquire();
+
+ final max = backend.bufferViews.length;
+ var byteRate = data.bitsPerSample >> 3, i = max - backend.queuedBuffers, buffer:ArrayBuffer, bufferLen:Int, bufferSample:Int, pos:Int, c:Int;
+
+ while (i < max && startSample < endSample) {
+ if (startSample >= (bufferSample = backend.bufferCurs[i])) {
+ if ((pos = (startSample - bufferSample) * _sampleSize) < (bufferLen = backend.bufferLens[i])) {
+ buffer = backend.bufferViews[i].buffer;
+ c = 0;
+ while (startSample < endSample) {
+ callback(getByte(buffer, pos, byteRate), c);
+ if ((pos += byteRate) >= bufferLen) {
+ startSample++;
+ break;
+ }
+ else if (++c == data.channels) {
+ c = 0;
+ startSample++;
+ }
+ }
}
- n -= __wordSize;
}
-
- break;
+ i++;
}
- return endPos - (n / __toBits);
+ backend.mutex.release();
+
+ return startSample;
}
- #if lime_vorbis
- inline function __prepareDecoder():Bool @:privateAccess {
- if (buffer.__srcVorbisFile == null) return __vorbis != null;
- if (__vorbis != null) return true;
- if ((__vorbis = buffer.__srcVorbisFile.clone()) != null) { // IM HOPING IT HAVE A GC CLOSURE.
- __buffer = new ArrayBuffer(__bufferSize = (buffer.sampleRate >> 1) * __sampleSize); // 0.5 seconds of buffers.
+ // TODO: Fix this and _readDecoder in the future.
+ inline function _prepareDecoder():Bool {
+ return false;
+ /*
+ if (_decoder != null) return true;
+ if (data.decoder != null && (_decoder = data.decoder.clone()) != null) {
+ _bufferLen = (data.sampleRate >> 2) * _sampleSize;
+ #if cpp
+ if (_buffer != null) {
+ if (_buffer.length < _bufferLen) {
+ _buffer.getData().resize(_bufferLen);
+ _buffer.fill(_buffer.length, _bufferLen - _buffer.length, 0);
+ @:privateAccess _buffer.length = _bufferLen;
+ }
+ }
+ else
+ #end
+ _buffer = new ArrayBuffer(_bufferLen);
return true;
}
return false;
+ */
}
- inline function __readDecoder(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback) {
- var n = Math.floor((endPos - startPos) * __toBits);
- if ((n -= n % __sampleSize) > 0) {
- var pos = Math.floor((startPos - __bufferTime * 1000) * __toBits);
- pos -= pos % __sampleSize;
-
- var doRead = __bufferLastSize == 0 || (pos < 0 && pos >= __bufferSize);
- if (doRead) {
- if (startPos < 1) {
- __vorbis.rawSeek(0);
- __bufferTime = 0;
- }
- else
- __vorbis.timeSeek(__bufferTime = startPos / 1000);
+ /*
+ function _readDecoder(startSample:Int, endSample:Int, callback:ReadCallback) {
+ var pos = (startSample - _bufferLastSample) * _sampleSize, n = endSample - startSample, c = 0;
- __bufferLastSize = pos = 0;
- }
+ var doDecode = _bufferLastSize == 0 || (pos < 0 && pos >= _bufferLastSize);
+ if (doDecode) {
+ _decoder.seek(startSample);
+ _bufferLastSize = pos = 0;
+ doDecode = true;
+ }
- var isBigEndian = lime.system.System.endianness == lime.system.Endian.BIG_ENDIAN, ranOut = false, c = 0, result;
- while (true) {
- if (doRead) {
- result = __vorbis.read(__buffer, pos, __bufferSize - pos, isBigEndian, __wordSize, true);
- if (result == Vorbis.HOLE) continue;
- else if (result < 0) break;
- else if (!(ranOut = result == 0)) {
- __bufferLastTime = __vorbis.timeTell();
- __bufferLastSize += result;
- while (pos < __bufferLastSize) {
- callback(getByte(__buffer, pos, __wordSize), c);
- if (++c > buffer.channels) c = 0;
- pos += __wordSize;
- if ((n -= __wordSize) <= 0) break;
- }
+ var result:Int;
+ while (n > 0) {
+ if (doDecode) {
+ _bufferLastSample = _decoder.tell();
+ result = _decoder.decode(_buffer, pos, _bufferLen - pos);
+ if (result == 0) break;
+
+ _bufferLastSize += result;
+ while (n > 0) {
+ callback(getByte(_buffer, pos, data.byteRate), c);
+ if (++c == data.channels) {
+ c = 0;
+ n--;
}
+ if ((pos += data.byteRate) >= _bufferLastSize) break;
}
- else {
- while (pos < __bufferLastSize) {
- callback(getByte(__buffer, pos, __wordSize), c);
- if (++c > buffer.channels) c = 0;
- pos += __wordSize;
- if ((n -= __wordSize) <= 0) break;
+ }
+ else {
+ while (n > 0) {
+ callback(getByte(_buffer, pos, data.byteRate), c);
+ if (++c == data.channels) {
+ c = 0;
+ n--;
}
- doRead = true;
- ranOut = pos >= __bufferSize;
- }
-
- if (n <= 0) break;
- else if (doRead && ranOut) {
- __bufferLastSize = pos = 0;
- __bufferTime = __bufferLastTime;
+ if ((pos += data.byteRate) >= _bufferLastSize) break;
}
+ doDecode = true;
+ _bufferLastSize = pos = 0;
}
}
}
- #end
- #end
-}
\ No newline at end of file
+ */
+
+ static inline function idiv(num:Int, denom:Int):Int return #if (cpp && !cppia) cpp.NativeMath.idiv(num, denom) #else Std.int(num / denom) #end;
+}
+#end
\ No newline at end of file
diff --git a/source/funkin/backend/utils/MathUtil.hx b/source/funkin/backend/utils/MathUtil.hx
index c71414edf3..5e6a3b4367 100644
--- a/source/funkin/backend/utils/MathUtil.hx
+++ b/source/funkin/backend/utils/MathUtil.hx
@@ -3,220 +3,232 @@ package funkin.backend.utils;
import haxe.macro.Expr;
final class MathUtil {
- public static inline var EULER:Float = 2.718281828459;
-
- /**
- * Returns the maximum value in the arguments.
- * @param args Array of values
- *
- * @return The maximum value
- **/
- public static function maxInt(...args:Int):Int {
- var max = args[0];
- for(i in 1...args.length) {
- var arg = args[i];
- if(arg > max)
- max = arg;
- }
- return max;
- }
-
- /**
- * Returns the minimum value in the arguments.
- * @param args Array of values
- *
- * @return The minimum value
- **/
- public static function minInt(...args:Int):Int {
- var min = args[0];
- for(i in 1...args.length) {
- var arg = args[i];
- if(arg < min)
- min = arg;
- }
- return min;
- }
-
- /**
- * Returns the maximum value in the arguments.
- *
- * NOTE: If you are using this in compile time, you should use `MathUtil.maxSmart` instead of this for better performance.
- *
- * @param args Array of values
- *
- * @return The maximum value
- **/
- public static function max(...args:Float):Float {
- var max = args[0];
- for(i in 1...args.length) {
- var arg = args[i];
- if(arg > max)
- max = arg;
- }
- return max;
- }
-
- /**
- * Returns the minimum value in the arguments.
- *
- * NOTE: If you are using this in compile time, you should use `MathUtil.minSmart` instead of this for better performance.
- *
- * @param args Array of values
- *
- * @return The minimum value
- **/
- public static function min(...args:Float):Float {
- var min = args[0];
- for(i in 1...args.length) {
- var arg = args[i];
- if(arg < min)
- min = arg;
- }
- return min;
- }
-
- /**
- * Checks if a is less than b with considering a margin of error.
- * * @param a Float
- * @param b Float
- * @param margin Float (Default: EPSILON)
- * * @return Bool
- **/
- public static function lessThan(a:Float, b:Float, margin:Float = 0.0000001):Bool {
- return a < b - margin;
- }
-
- /**
- * Checks if a is less than or equally b with considering a margin of error.
- * * @param a Float
- * @param b Float
- * @param margin Float (Default: EPSILON)
- * * @return Bool
- **/
- public static function lessThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
- return a <= b - margin;
- }
-
- /**
- * Checks if a is greater than b with considering a margin of error.
- * * @param a Float
- * @param b Float
- * @param margin Float (Default: EPSILON)
- * * @return Bool
- **/
- public static function greaterThan(a:Float, b:Float, margin:Float = 0.0000001):Bool {
- return a > b + margin;
- }
-
- /**
- * Checks if a is greater than or equally b with considering a margin of error.
- * * @param a Float
- * @param b Float
- * @param margin Float (Default: EPSILON)
- * * @return Bool
- **/
- public static function greaterThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
- return a >= b + margin;
- }
-
- /**
- * Checks if a is approximately equal to b.
- * * @param a Float
- * @param b Float
- * @param margin Float (Default: EPSILON)
- * * @return Bool
- **/
- public static function equal(a:Float, b:Float, margin:Float = 0.0000001):Bool {
- return Math.abs(a - b) <= margin;
- }
-
- /**
- * Checks if a are not approximately equal to b.
- * * @param a Float
- * @param b Float
- * @param margin Float (Default: EPSILON)
- * * @return Bool
- **/
- public static function notEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
- return Math.abs(a - b) > margin;
- }
-
- /**
- * @param edge0 Float
- * @param edge1 Float
- * @param x Float
- * @return Float
- **/
- public static function smoothStep(edge0:Float, edge1:Float, x:Float):Float {
- var t = (x - edge0) / (edge1 - edge0);
- var clamped = t < 0.0 ? 0.0 : (t > 1.0 ? 1.0 : t);
- return clamped * clamped * (3.0 - 2.0 * clamped);
- }
-
- /**
- * @param a Float
- * @param b Float
- * @param v Float
- * @return Float
- **/
- public static function inverseLerp(a:Float, b:Float, v:Float):Float {
- return (v - a) / (b - a);
- }
-
- /**
- * @param v Float
- * @return Float
- **/
- public static function fract(v:Float):Float {
- return v - Math.floor(v);
- }
-
- /**
- * Shortcut to `Math.max` but with infinite amount of arguments
- *
- * Might not preserve the order of arguments, please test this.
- *
- * Dont use this in hscript, it doesnt work, it only works on compile time
- **/
- @:dox(hide) public static macro function maxSmart(..._args:Expr):Expr {
- return genericMinMaxSmart(_args.toArray(), "Math.max");
- }
-
- /**
- * Shortcut to `Math.min` but with infinite amount of arguments
- *
- * Might not preserve the order of arguments, please test this.
- *
- * Dont use this in hscript, it doesnt work, it only works on compile time
- **/
- @:dox(hide) public static macro function minSmart(..._args:Expr):Expr {
- return genericMinMaxSmart(_args.toArray(), "Math.min");
- }
-
- #if macro
- @:dox(hide) private static function genericMinMaxSmart(_args:Array, funcPath:String):Expr {
- var args = _args.copy();
- if (args.length == 0) return macro 0;
-
- var func = funcPath.split(".");
-
- function nested(lst:Array):Expr {
- if (lst.length == 1) {
- return macro ${lst[0]};
- } else if (lst.length == 2) {
- return macro $p{func}(${lst[0]}, ${lst[1]});
- } else {
- var mid = Std.int(lst.length / 2);
- return macro $p{func}(${nested(lst.slice(0, mid))}, ${nested(lst.slice(mid, lst.length))});
- }
- }
-
- var expr = nested(args);
-
- //var printer = new haxe.macro.Printer();
- //trace(printer.printExpr(expr));
-
- return macro $expr;
- }
- #end
+ public static inline var EULER:Float = 2.718281828459;
+
+ /**
+ * Returns the maximum value in the arguments.
+ * @param args Array of values
+ *
+ * @return The maximum value
+ **/
+ public static function maxInt(...args:Int):Int {
+ var max = args[0];
+ for(i in 1...args.length) {
+ var arg = args[i];
+ if(arg > max)
+ max = arg;
+ }
+ return max;
+ }
+
+ /**
+ * Returns the minimum value in the arguments.
+ * @param args Array of values
+ *
+ * @return The minimum value
+ **/
+ public static function minInt(...args:Int):Int {
+ var min = args[0];
+ for(i in 1...args.length) {
+ var arg = args[i];
+ if(arg < min)
+ min = arg;
+ }
+ return min;
+ }
+
+ /**
+ * Returns the maximum value in the arguments.
+ *
+ * NOTE: If you are using this in compile time, you should use `MathUtil.maxSmart` instead of this for better performance.
+ *
+ * @param args Array of values
+ *
+ * @return The maximum value
+ **/
+ public static function max(...args:Float):Float {
+ var max = args[0];
+ for(i in 1...args.length) {
+ var arg = args[i];
+ if(arg > max)
+ max = arg;
+ }
+ return max;
+ }
+
+ /**
+ * Returns the minimum value in the arguments.
+ *
+ * NOTE: If you are using this in compile time, you should use `MathUtil.minSmart` instead of this for better performance.
+ *
+ * @param args Array of values
+ *
+ * @return The minimum value
+ **/
+ public static function min(...args:Float):Float {
+ var min = args[0];
+ for(i in 1...args.length) {
+ var arg = args[i];
+ if(arg < min)
+ min = arg;
+ }
+ return min;
+ }
+
+ /**
+ * Checks if a is less than b with considering a margin of error.
+ *
+ * @param a Float
+ * @param b Float
+ * @param margin Float (Default: EPSILON)
+ *
+ * @return Bool
+ **/
+ public static function lessThan(a:Float, b:Float, margin:Float = 0.0000001):Bool {
+ return a < b - margin;
+ }
+
+ /**
+ * Checks if a is less than or equally b with considering a margin of error.
+ *
+ * @param a Float
+ * @param b Float
+ * @param margin Float (Default: EPSILON)
+ *
+ * @return Bool
+ **/
+ public static function lessThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
+ return a <= b - margin;
+ }
+
+ /**
+ * Checks if a is greater than b with considering a margin of error.
+ *
+ * @param a Float
+ * @param b Float
+ * @param margin Float (Default: EPSILON)
+ *
+ * @return Bool
+ **/
+ public static function greaterThan(a:Float, b:Float, margin:Float = 0.0000001):Bool {
+ return a > b + margin;
+ }
+
+ /**
+ * Checks if a is greater than or equally b with considering a margin of error.
+ *
+ * @param a Float
+ * @param b Float
+ * @param margin Float (Default: EPSILON)
+ *
+ * @return Bool
+ **/
+ public static function greaterThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
+ return a >= b + margin;
+ }
+
+ /**
+ * Checks if a is approximately equal to b.
+ *
+ * @param a Float
+ * @param b Float
+ * @param margin Float (Default: EPSILON)
+ *
+ * @return Bool
+ **/
+ public static function equal(a:Float, b:Float, margin:Float = 0.0000001):Bool {
+ return Math.abs(a - b) <= margin;
+ }
+
+ /**
+ * Checks if a are not approximately equal to b.
+ *
+ * @param a Float
+ * @param b Float
+ * @param margin Float (Default: EPSILON)
+ *
+ * @return Bool
+ **/
+ public static function notEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
+ return Math.abs(a - b) > margin;
+ }
+
+ /**
+ * @param edge0 Float
+ * @param edge1 Float
+ * @param x Float
+ * @return Float
+ **/
+ public static function smoothStep(edge0:Float, edge1:Float, x:Float):Float {
+ var t = (x - edge0) / (edge1 - edge0);
+ var clamped = t < 0.0 ? 0.0 : (t > 1.0 ? 1.0 : t);
+ return clamped * clamped * (3.0 - 2.0 * clamped);
+ }
+
+ /**
+ * @param a Float
+ * @param b Float
+ * @param v Float
+ * @return Float
+ **/
+ public static function inverseLerp(a:Float, b:Float, v:Float):Float {
+ return (v - a) / (b - a);
+ }
+
+ /**
+ * @param v Float
+ * @return Float
+ **/
+ public static function fract(v:Float):Float {
+ return v - Math.floor(v);
+ }
+
+ /**
+ * Shortcut to `Math.max` but with infinite amount of arguments
+ *
+ * Might not preserve the order of arguments, please test this.
+ *
+ * Dont use this in hscript, it doesnt work, it only works on compile time
+ **/
+ @:dox(hide) public static macro function maxSmart(..._args:Expr):Expr {
+ return genericMinMaxSmart(_args.toArray(), "Math.max");
+ }
+
+ /**
+ * Shortcut to `Math.min` but with infinite amount of arguments
+ *
+ * Might not preserve the order of arguments, please test this.
+ *
+ * Dont use this in hscript, it doesnt work, it only works on compile time
+ **/
+ @:dox(hide) public static macro function minSmart(..._args:Expr):Expr {
+ return genericMinMaxSmart(_args.toArray(), "Math.min");
+ }
+
+ #if macro
+ @:dox(hide) private static function genericMinMaxSmart(_args:Array, funcPath:String):Expr {
+ var args = _args.copy();
+ if (args.length == 0) return macro 0;
+
+ var func = funcPath.split(".");
+
+ function nested(lst:Array):Expr {
+ if (lst.length == 1) {
+ return macro ${lst[0]};
+ } else if (lst.length == 2) {
+ return macro $p{func}(${lst[0]}, ${lst[1]});
+ } else {
+ var mid = Std.int(lst.length / 2);
+ return macro $p{func}(${nested(lst.slice(0, mid))}, ${nested(lst.slice(mid, lst.length))});
+ }
+ }
+
+ var expr = nested(args);
+
+ //var printer = new haxe.macro.Printer();
+ //trace(printer.printExpr(expr));
+
+ return macro $expr;
+ }
+ #end
}
diff --git a/source/funkin/backend/utils/NativeAPI.hx b/source/funkin/backend/utils/NativeAPI.hx
index d3b5e758e6..16e08c19b5 100644
--- a/source/funkin/backend/utils/NativeAPI.hx
+++ b/source/funkin/backend/utils/NativeAPI.hx
@@ -13,12 +13,6 @@ import flixel.util.FlxColor;
* Some functions might not have effect on some platforms.
*/
class NativeAPI {
- @:dox(hide) public static function registerAudio() {
- #if windows
- Windows.registerAudio();
- #end
- }
-
@:dox(hide) public static function registerAsDPICompatible() {
#if windows
Windows.registerAsDPICompatible();
@@ -185,16 +179,7 @@ class NativeAPI {
public static function setConsoleColors(foregroundColor:ConsoleColor = NONE, ?backgroundColor:ConsoleColor = NONE) {
if(Main.noTerminalColor) return;
- #if (windows && !hl)
- if(foregroundColor == NONE)
- foregroundColor = LIGHTGRAY;
- if(backgroundColor == NONE)
- backgroundColor = BLACK;
-
- var fg:Int = cast foregroundColor;
- var bg:Int = cast backgroundColor;
- Windows.setConsoleColors((bg * 16) + fg);
- #elseif sys
+ #if sys
Sys.print("\x1b[0m");
if(foregroundColor != NONE)
Sys.print("\x1b[" + Std.int(consoleColorToANSI(foregroundColor)) + "m");
diff --git a/source/funkin/backend/utils/native/Windows.hx b/source/funkin/backend/utils/native/Windows.hx
index 545e34b62a..1ac32c5b94 100644
--- a/source/funkin/backend/utils/native/Windows.hx
+++ b/source/funkin/backend/utils/native/Windows.hx
@@ -27,107 +27,10 @@ import funkin.backend.utils.NativeAPI.MessageBoxIcon;
#include
#include
#include
-
-#define SAFE_RELEASE(punk) \\
- if ((punk) != NULL) \\
- { (punk)->Release(); (punk) = NULL; }
-
-static long lastDefId = 0;
-
-class AudioFixClient : public IMMNotificationClient {
- LONG _cRef;
- IMMDeviceEnumerator *_pEnumerator;
-
- public:
- AudioFixClient() :
- _cRef(1),
- _pEnumerator(NULL)
- {
- HRESULT result = CoCreateInstance(__uuidof(MMDeviceEnumerator),
- NULL, CLSCTX_INPROC_SERVER,
- __uuidof(IMMDeviceEnumerator),
- (void**)&_pEnumerator);
- if (result == S_OK) {
- _pEnumerator->RegisterEndpointNotificationCallback(this);
- }
- }
-
- ~AudioFixClient()
- {
- SAFE_RELEASE(_pEnumerator);
- }
-
- ULONG STDMETHODCALLTYPE AddRef()
- {
- return InterlockedIncrement(&_cRef);
- }
-
- ULONG STDMETHODCALLTYPE Release()
- {
- ULONG ulRef = InterlockedDecrement(&_cRef);
- if (0 == ulRef)
- {
- delete this;
- }
- return ulRef;
- }
-
- HRESULT STDMETHODCALLTYPE QueryInterface(
- REFIID riid, VOID **ppvInterface)
- {
- return S_OK;
- }
-
- HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId)
- {
- return S_OK;
- };
-
- HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId)
- {
- return S_OK;
- }
-
- HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
- LPCWSTR pwstrDeviceId,
- DWORD dwNewState)
- {
- return S_OK;
- }
-
- HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
- LPCWSTR pwstrDeviceId,
- const PROPERTYKEY key)
- {
- return S_OK;
- }
-
- HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(
- EDataFlow flow, ERole role,
- LPCWSTR pwstrDeviceId)
- {
- ::funkin::backend::_hx_system::Main_obj::audioDisconnected = true;
- return S_OK;
- };
-};
-
-AudioFixClient *curAudioFix;
')
@:dox(hide)
final class Windows {
- public static var __audioChangeCallback:Void->Void = function() {
- trace("test");
- };
-
-
- @:functionCode('
- if (!curAudioFix) curAudioFix = new AudioFixClient();
- ')
- public static function registerAudio() {
- funkin.backend.system.Main.audioDisconnected = false;
- }
-
@:functionCode('
int darkMode = enable ? 1 : 0;
diff --git a/source/funkin/editors/SaveSubstate.hx b/source/funkin/editors/SaveSubstate.hx
index 7202cf3738..be964248d2 100644
--- a/source/funkin/editors/SaveSubstate.hx
+++ b/source/funkin/editors/SaveSubstate.hx
@@ -2,6 +2,9 @@ package funkin.editors;
import haxe.io.Path;
import lime.ui.FileDialog;
+#if lime_funkin
+import lime.ui.FileDialogFilter;
+#end
class SaveSubstate extends MusicBeatSubstate {
public var saveOptions:Map;
@@ -26,6 +29,13 @@ class SaveSubstate extends MusicBeatSubstate {
public override function create() {
super.create();
+ #if lime_funkin
+ FileDialog.saveFile(FlxG.stage.window, "Save File", (fileName:String, activeFilter:FileDialogFilter) -> {
+ CoolUtil.safeSaveFile(fileName, data);
+ close();
+ }, [new FileDialogFilter("Specified File Extension", options.saveExt.getDefault(Path.extension(options.defaultSaveFile)))],
+ options.defaultSaveFile);
+ #else
var fileDialog = new FileDialog();
fileDialog.onCancel.add(function() close());
fileDialog.onSelect.add(function(str) {
@@ -33,6 +43,7 @@ class SaveSubstate extends MusicBeatSubstate {
close();
});
fileDialog.browse(SAVE, options.saveExt.getDefault(Path.extension(options.defaultSaveFile)), options.defaultSaveFile);
+ #end
}
public override function update(elapsed:Float) {
diff --git a/source/funkin/editors/stage/StageEditor.hx b/source/funkin/editors/stage/StageEditor.hx
index 8b54a2c695..89c2a33473 100644
--- a/source/funkin/editors/stage/StageEditor.hx
+++ b/source/funkin/editors/stage/StageEditor.hx
@@ -260,6 +260,7 @@ class StageEditor extends UIState {
stageSpritesWindow.add(button);
} else if(sprite == null) {
var basic = new FlxBasic(); // prevent awkward layering
+ basic.exists = false;
insert(i, basic);
var button = new StageUnknownButton(0,0, basic, xml);
stageSpritesWindow.add(button);
@@ -308,8 +309,8 @@ class StageEditor extends UIState {
sprite.extra.set(exID("type"), node.name);
sprite.extra.set(exID("imageFile"), '${node.getAtt("sprite").getDefault(sprite.name)}');
sprite.extra.set(exID("parentNode"), parent);
- sprite.extra.set(exID("highMemory"), parent.name == "highMemory");
- sprite.extra.set(exID("lowMemory"), parent.name == "lowMemory");
+ sprite.extra.set(exID("highMemory"), parent.name == "high-memory");
+ sprite.extra.set(exID("lowMemory"), parent.name == "low-memory");
//sprite.active = false;
}
if(sprite is StageCharPos)
@@ -374,6 +375,7 @@ class StageEditor extends UIState {
}
char.name = charName;
char.debugMode = true;
+ char.useRenderTexture = true;
// Play first anim, and make it the last frame
var animToPlay = char.getAnimOrder()[0];
char.playAnim(animToPlay, true, NONE);
@@ -392,8 +394,8 @@ class StageEditor extends UIState {
char.extra.set(exID("camY"), charPos.camyoffset);
char.extra.set(exID("parentNode"), parent);
- char.extra.set(exID("highMemory"), parent.name == "highMemory");
- char.extra.set(exID("lowMemory"), parent.name == "lowMemory");
+ char.extra.set(exID("highMemory"), parent.name == "high-memory");
+ char.extra.set(exID("lowMemory"), parent.name == "low-memory");
chars.push(char);
stage.applyCharStuff(char, charPos.name, 0);
@@ -668,6 +670,8 @@ class StageEditor extends UIState {
saveToXml(spriteXML, "color", sprite.color.toWebString(), "#FFFFFF");
// TODO: save custom parameters
//saveToXml(spriteXML, "flipX", sprite.flipX, false);
+ if (node.hasNode.anim) for (animNode in node.nodes.anim)
+ spriteXML.addChild(animNode.x);
newNode = spriteXML;
} else if(button is StageCharacterButton) {
var button:StageCharacterButton = cast button;
diff --git a/source/funkin/editors/stage/elements/StageSolidButton.hx b/source/funkin/editors/stage/elements/StageSolidButton.hx
index 6ded6d26e8..49ffd1925b 100644
--- a/source/funkin/editors/stage/elements/StageSolidButton.hx
+++ b/source/funkin/editors/stage/elements/StageSolidButton.hx
@@ -8,6 +8,7 @@ class StageSolidButton extends StageSpriteButton {
public function new(x:Float,y:Float, sprite:FunkinSprite, xml:Access) {
super(x,y, sprite, xml);
color = 0xFFD9FF50;
+ hasAdvancedEdit = false;
}
public override function onEdit() {
diff --git a/source/funkin/editors/stage/elements/StageSpriteAnimButton.hx b/source/funkin/editors/stage/elements/StageSpriteAnimButton.hx
new file mode 100644
index 0000000000..651b736da5
--- /dev/null
+++ b/source/funkin/editors/stage/elements/StageSpriteAnimButton.hx
@@ -0,0 +1,99 @@
+package funkin.editors.stage.elements;
+
+import funkin.backend.utils.XMLUtil.AnimData;
+import haxe.xml.Access;
+import funkin.editors.extra.PropertyButton;
+
+class StageSpriteAnimButton extends PropertyButton
+{
+ public var fpsStepper:UINumericStepper;
+ public var editButton:UIButton;
+ public var editIcon:FlxSprite;
+ @:unreflective private var __initialized:Bool = false;
+
+ public var spriteXML:Access;
+ public var animData(default, null):AnimData;
+
+ public function new(anim:AnimData, parent, width:Int = 280, height:Int = 35, nameWidth:Int = 100, valueWidth:Int = 135, inputHeight:Int = 25)
+ {
+ super("", "", parent, width, height, nameWidth, valueWidth, inputHeight);
+ animData = anim;
+ propertyText.onChange = (text) -> animData.name = text;
+ valueText.onChange = (text) -> animData.anim = text;
+
+ var editSize = height - 5 * 2;
+ editButton = new UIButton(deleteButton.x - deleteButton.bWidth - 5, 5, null, openAnimEdit, editSize, editSize);
+ editButton.frames = Paths.getFrames("editors/ui/grayscale-button");
+ editButton.color = 0xFFFF5B0F;
+ editButton.autoAlpha = false;
+ members.insert(members.indexOf(deleteButton), editButton);
+
+ editIcon = new FlxSprite(editButton.x + 8, editButton.y + 10).loadGraphic(Paths.image('editors/stage/edit-button'), true, 16, 16);
+ editIcon.animation.add("advanced", [1]);
+ editIcon.animation.play("advanced");
+ editIcon.antialiasing = false;
+ members.insert(members.indexOf(editButton) + 1, editIcon);
+
+ fpsStepper = new UINumericStepper(valueText.x + valueText.bWidth + 15, 5, 0, 1, 2, 0, null, Math.round(width / 4), 25);
+ fpsStepper.onChange = (text) ->
+ {
+ @:privateAccess fpsStepper.__onChange(text);
+ animData.fps = fpsStepper.value;
+ };
+ members.insert(members.indexOf(deleteButton), fpsStepper);
+
+ __initialized = true;
+ updateDisplay();
+ }
+
+ public function openAnimEdit()
+ {
+ final parent = (FlxG.state.subState is UISoftcodedWindow) ? FlxG.state.subState : FlxG.state;
+ parent.openSubState(new SpriteAnimEditScreen(spriteXML, this, null));
+ }
+
+ public override function updatePos()
+ {
+ super.updatePos();
+ if (!__initialized) return;
+
+ fpsStepper.follow(this, valueText.x + valueText.bWidth, bHeight/2 - (fpsStepper.bHeight/2));
+ editButton.follow(this, deleteButton.x - deleteButton.bWidth - 5, bHeight/2 - (editButton.bHeight/2));
+ editIcon.follow(editButton, editButton.bWidth / 2 - editIcon.width / 2, editButton.bHeight / 2 - editIcon.height / 2);
+ }
+
+ public inline function updateDisplay()
+ {
+ propertyText.label.text = animData.name;
+ valueText.label.text = animData.anim;
+ fpsStepper.value = animData.fps;
+ }
+}
+
+class SpriteAnimEditScreen extends UISoftcodedWindow
+{
+ public var saveCallback:Void->Void;
+ public var parentButton:StageSpriteAnimButton;
+ public var xml:Access;
+
+ inline function translate(id:String, prefix:String = "stageSpriteAnimEditScreen.", ?args:Array)
+ return TU.translate(prefix + id, args);
+
+ public function new(xml:Access, parentButton:StageSpriteAnimButton, saveCallback:Void->Void) {
+ this.saveCallback = saveCallback;
+ this.parentButton = parentButton;
+ this.xml = xml;
+ super("layouts/stage/animEditScreen", [
+ "stage" => StageEditor.instance.stage,
+ "xml" => xml,
+ "exID" => StageEditor.exID,
+ "translate" => translate,
+ "button" => parentButton
+ ]);
+ }
+
+ override function saveData() {
+ super.saveData();
+ if(saveCallback != null) saveCallback();
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/editors/stage/elements/StageUnknownButton.hx b/source/funkin/editors/stage/elements/StageUnknownButton.hx
index a3fb09492a..6c0e5a623c 100644
--- a/source/funkin/editors/stage/elements/StageUnknownButton.hx
+++ b/source/funkin/editors/stage/elements/StageUnknownButton.hx
@@ -8,12 +8,13 @@ class StageUnknownButton extends StageElementButton {
public var lowMemory:Bool = false;
public var highMemory:Bool = false;
public var basic:FlxBasic;
+ var dummy:FunkinSprite; // to allow it to be saved
public function new(x:Float,y:Float, basic:FlxBasic, xml:Access) {
this.basic = basic;
- super(x,y, xml);
+ super(x, y, xml);
- if(xml.x.parent != null) {
+ if (xml.x.parent != null) {
lowMemory = xml.x.parent.nodeName == "low-memory";
highMemory = xml.x.parent.nodeName == "high-memory";
}
@@ -27,8 +28,12 @@ class StageUnknownButton extends StageElementButton {
members.remove(visibilityIcon);
setEditAdvanced();
-
updateInfo();
+
+ dummy = new FunkinSprite();
+ dummy.extra.set(StageEditor.exID("lowMemory"), lowMemory);
+ dummy.extra.set(StageEditor.exID("highMemory"), highMemory);
+ dummy.extra.set(StageEditor.exID("button"), this);
}
public override function update(elapsed:Float) {
@@ -40,11 +45,7 @@ class StageUnknownButton extends StageElementButton {
}
public override function getSprite():FunkinSprite {
- var sprite = new FunkinSprite(); // to allow it to be saved
- sprite.extra.set(StageEditor.exID("lowMemory"), lowMemory);
- sprite.extra.set(StageEditor.exID("highMemory"), highMemory);
- sprite.extra.set(StageEditor.exID("button"), this);
- return sprite;
+ return dummy;
}
public override function canRender() {
@@ -58,10 +59,12 @@ class StageUnknownButton extends StageElementButton {
FlxG.state.openSubState(new StageUnknownEditScreen(this));
}
- //public override function onEdit() {
- // UIState.state.displayNotification(new UIBaseNotification("Editing a character isnt implemented yet!", 2, BOTTOM_LEFT));
- // CoolUtil.playMenuSFX(WARNING, 0.45);
- //}
+ public override function onDelete() {
+ basic.destroy();
+ dummy.destroy();
+ xml.x.parent.removeChild(xml.x);
+ StageEditor.instance.stageSpritesWindow.remove(this);
+ }
public override function onVisiblityToggle() {
}
diff --git a/source/funkin/editors/ui/UIFileExplorer.hx b/source/funkin/editors/ui/UIFileExplorer.hx
index 4f75fd6efe..46ab81c4e7 100644
--- a/source/funkin/editors/ui/UIFileExplorer.hx
+++ b/source/funkin/editors/ui/UIFileExplorer.hx
@@ -2,6 +2,9 @@ package funkin.editors.ui;
import haxe.io.Bytes;
import lime.ui.FileDialog;
+#if lime_funkin
+import lime.ui.FileDialogFilter;
+#end
class UIFileExplorer extends UISliceSprite {
public var uploadButton:UIButton;
@@ -24,10 +27,16 @@ class UIFileExplorer extends UISliceSprite {
if (onFile != null) this.onFile = onFile;
- uploadButton = new UIButton(x + 8, y+ 8, null, function () {
+ uploadButton = new UIButton(x + 8, y + 8, null, function () {
+ #if lime_funkin
+ FileDialog.openFile(FlxG.stage.window, "Open File", (fileNames:Array, activeFilter:FileDialogFilter) -> {
+ loadFile(fileNames[0]);
+ }, this.fileType != null ? [new FileDialogFilter("Specified File Extension", this.fileType)] : null);
+ #else
var fileDialog = new FileDialog();
fileDialog.onSelect.add(loadFile);
fileDialog.browse(OPEN, this.fileType);
+ #end
}, bWidth - 16, bHeight - 16);
members.push(uploadButton);
diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx
index 07d7433893..e719262900 100644
--- a/source/funkin/game/PlayState.hx
+++ b/source/funkin/game/PlayState.hx
@@ -1110,7 +1110,7 @@ class PlayState extends MusicBeatState
if (notNull) PlayState.instance.gameAndCharsCall("onStageDestroy", [stage]);
scripts.call("destroy");
- for (g in __cachedGraphics) g.useCount--;
+ for (g in __cachedGraphics) g.decrementUseCount();
@:privateAccess {
for (strumLine in strumLines.members) FlxG.sound.destroySound(strumLine.vocals);
if (FlxG.sound.music != inst) FlxG.sound.destroySound(inst);
@@ -1261,11 +1261,6 @@ class PlayState extends MusicBeatState
@:dox(hide)
override public function onFocus():Void
{
- if (!paused && FlxG.autoPause) {
- for (strumLine in strumLines.members) strumLine.vocals.resume();
- inst.resume();
- vocals.resume();
- }
gameAndCharsCall("onFocus");
updateDiscordPresence();
super.onFocus();
@@ -1274,11 +1269,6 @@ class PlayState extends MusicBeatState
@:dox(hide)
override public function onFocusLost():Void
{
- if (!paused && FlxG.autoPause) {
- for (strumLine in strumLines.members) strumLine.vocals.pause();
- inst.pause();
- vocals.pause();
- }
gameAndCharsCall("onFocusLost");
updateDiscordPresence();
super.onFocusLost();
diff --git a/source/funkin/game/cutscenes/VideoCutscene.hx b/source/funkin/game/cutscenes/VideoCutscene.hx
index 0e8fbe7449..f979376f77 100644
--- a/source/funkin/game/cutscenes/VideoCutscene.hx
+++ b/source/funkin/game/cutscenes/VideoCutscene.hx
@@ -60,15 +60,12 @@ class VideoCutscene extends Cutscene {
add(video = new FlxVideoSprite());
video.antialiasing = true;
- #if (hxvlc < version("2.0.0"))
- video.autoPause = false; // Imma handle it better inside this class, mainly because of the pause menu - Nex
- #end
video.bitmap.onEndReached.add(close);
video.bitmap.onFormatSetup.add(function() if (video.bitmap != null && video.bitmap.bitmapData != null) {
final width = video.bitmap.bitmapData.width;
final height = video.bitmap.bitmapData.height;
final scale:Float = Math.min(FlxG.width / width, FlxG.height / height);
- video.setGraphicSize(Std.int(width * scale), Std.int(height * scale));
+ video.setGraphicSize(width * scale, height * scale);
video.updateHitbox();
video.screenCenter();
});
@@ -103,7 +100,7 @@ class VideoCutscene extends Cutscene {
FlxTween.tween(loadingBackdrop, {alpha: 1}, 0.5, {ease: FlxEase.sineInOut});
Main.execAsync(function() {
- if (video.load(localPath)) new FlxTimer().start(0.001, function(_) {
+ if (video.load(localPath)) FlxTimer.wait(0.001, function() {
mutex.acquire(); onReady(); mutex.release();
});
else { mutex.acquire(); close(); mutex.release(); }
@@ -203,18 +200,6 @@ class VideoCutscene extends Cutscene {
}
}
- #if (hxvlc < version("2.0.0"))
- @:dox(hide) override public function onFocus() {
- if(FlxG.autoPause && !paused) video.resume();
- super.onFocus();
- }
-
- @:dox(hide) override public function onFocusLost() {
- if(FlxG.autoPause && !paused) video.pause();
- super.onFocusLost();
- }
- #end
-
public override function pauseCutscene() {
video.pause();
super.pauseCutscene();
diff --git a/source/funkin/menus/FreeplayState.hx b/source/funkin/menus/FreeplayState.hx
index 27869494ec..7ee605baf3 100644
--- a/source/funkin/menus/FreeplayState.hx
+++ b/source/funkin/menus/FreeplayState.hx
@@ -189,37 +189,6 @@ class FreeplayState extends MusicBeatState
interpColor = new FlxInterpolateColor(bg.color);
}
- #if PRELOAD_ALL
- /**
- * How much time a song stays selected until it autoplays.
- */
- public var timeUntilAutoplay:Float = 1;
- /**
- * Whenever the song autoplays when hovered over.
- */
- public var disableAutoPlay:Bool = false;
- /**
- * Whenever the autoplayed song gets async loaded.
- */
- public var disableAsyncLoading:Bool = #if desktop false #else true #end;
- /**
- * Time elapsed since last autoplay. If this time exceeds `timeUntilAutoplay`, the currently selected song will play.
- */
- public var autoplayElapsed:Float = 0;
- /**
- * Whenever the currently selected song instrumental is playing.
- */
- public var songInstPlaying:Bool = true;
- /**
- * Path to the currently playing song instrumental.
- */
- public var curPlayingInst:String = null;
- /**
- * If it should play the song automatically.
- */
- public var autoplayShouldPlay:Bool = true;
- #end
-
private var TEXT_FREEPLAY_SCORE = TU.getRaw("freeplay.score");
override function update(elapsed:Float)
@@ -255,46 +224,6 @@ class FreeplayState extends MusicBeatState
interpColor.fpsLerpTo(curSong.color, 0.0625);
bg.color = interpColor.color;
- #if PRELOAD_ALL
- var dontPlaySongThisFrame = false;
- autoplayElapsed += elapsed;
- if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay)) {
- if (curPlayingInst != (curPlayingInst = Paths.inst(curSong.name, curDifficulties[curDifficulty], curSong.instSuffix))) {
- var streamed = false;
- /*if (Options.streamedMusic) {
- var sound = Assets.getMusic(curPlayingInst, true, false);
- streamed = sound != null;
-
- if (streamed && autoplayShouldPlay) {
- FlxG.sound.playMusic(sound, 0);
- Conductor.changeBPM(curSong.bpm, curSong.beatsPerMeasure, curSong.stepsPerBeat);
- }
- }*/
-
- if (!streamed) {
- var huh:Void->Void = function() {
- var soundPath = curPlayingInst;
- var sound = null;
- if (Assets.exists(soundPath, SOUND) || Assets.exists(soundPath, MUSIC))
- sound = Assets.getSound(soundPath);
- else
- FlxG.log.error('Could not find a Sound asset with an ID of \'$soundPath\'.');
-
- if (sound != null && autoplayShouldPlay) {
- FlxG.sound.playMusic(sound, 0);
- Conductor.changeBPM(curSong.bpm, curSong.beatsPerMeasure, curSong.stepsPerBeat);
- }
- }
- if (!disableAsyncLoading) Main.execAsync(huh);
- else huh();
- }
- }
- songInstPlaying = true;
- if (disableAsyncLoading/* && !Options.streamedMusic*/) dontPlaySongThisFrame = true;
- }
- #end
-
-
if (controls.BACK)
{
CoolUtil.playMenuSFX(CANCEL, 0.7);
@@ -306,7 +235,7 @@ class FreeplayState extends MusicBeatState
convertChart();
#end
- if (controls.ACCEPT #if PRELOAD_ALL && !dontPlaySongThisFrame #end)
+ if (controls.ACCEPT)
select();
}
@@ -338,10 +267,6 @@ class FreeplayState extends MusicBeatState
if (event.cancelled) return;
- #if PRELOAD_ALL
- autoplayShouldPlay = false;
- #end
-
Options.freeplayLastSong = curSong.name;
Options.freeplayLastDifficulty = curDifficulties[curDifficulty];
Options.freeplayLastVariation = curSong.variant;
@@ -377,13 +302,6 @@ class FreeplayState extends MusicBeatState
updateCurSong();
updateScore();
- #if PRELOAD_ALL
- if (curSong != prevSong) {
- autoplayElapsed = 0;
- songInstPlaying = false;
- }
- #end
-
var text = validDifficulties ? curDifficulties[curDifficulty].toUpperCase() + (curSong != songs[curSelected] ? ' (${curSong.variant.toUpperCase()})' : '') : '-';
diffText.text = curDifficulties.length > 1 ? '< $text >' : text;
}
@@ -466,11 +384,6 @@ class FreeplayState extends MusicBeatState
changeDiff(0, true);
- #if PRELOAD_ALL
- autoplayElapsed = 0;
- songInstPlaying = false;
- #end
-
coopText.visible = curSong.coopAllowed || curSong.opponentModeAllowed;
}
@@ -478,8 +391,8 @@ class FreeplayState extends MusicBeatState
var event = event("onUpdateOptionsAlpha", EventManager.get(FreeplayAlphaUpdateEvent).recycle(0.6, 0.45, 1, 1, 0.25));
if (event.cancelled) return;
- final idleAlpha = #if PRELOAD_ALL songInstPlaying ? event.idlePlayingAlpha : #end event.idleAlpha;
- final selectedAlpha = #if PRELOAD_ALL songInstPlaying ? event.selectedPlayingAlpha : #end event.selectedAlpha;
+ final idleAlpha = event.idleAlpha;
+ final selectedAlpha = event.selectedAlpha;
for (i in 0...iconArray.length)
iconArray[i].alpha = lerp(iconArray[i].alpha, idleAlpha, event.lerp);
diff --git a/source/funkin/options/Options.hx b/source/funkin/options/Options.hx
index 377dc1918a..9f4e8ab971 100644
--- a/source/funkin/options/Options.hx
+++ b/source/funkin/options/Options.hx
@@ -43,8 +43,8 @@ class Options
public static var framerate:Int = 120;
public static var gpuOnlyBitmaps:Bool = #if (mac || web) false #else true #end; // causes issues on mac and web
public static var language = "en"; // default to english, Flags.DEFAULT_LANGUAGE should not modify this
- public static var streamedMusic:Bool = false;
- public static var streamedVocals:Bool = false;
+ public static var streamedMusic:Bool = true;
+ public static var streamedVocals:Bool = true;
public static var quality:Int = 1;
public static var allowConfigWarning:Bool = true;
#if MODCHARTING_FEATURES
@@ -229,6 +229,7 @@ class Options
applyKeybinds();
applyQuality();
+ flixel.sound.FlxSoundData.allowStreaming = streamedMusic;
FlxG.sound.defaultMusicGroup.volume = volumeMusic;
FlxG.autoPause = autoPause;
if (FlxG.updateFramerate < framerate) FlxG.drawFramerate = FlxG.updateFramerate = framerate;
diff --git a/source/funkin/options/categories/GameplayOptions.hx b/source/funkin/options/categories/GameplayOptions.hx
index 4b69ccd3e7..868c3c4c95 100644
--- a/source/funkin/options/categories/GameplayOptions.hx
+++ b/source/funkin/options/categories/GameplayOptions.hx
@@ -67,14 +67,7 @@ class AdvancedGameplayOptions extends TreeMenuScreen {
public function new() {
super('optionsMenu.advanced', 'optionsTree.gameplay.advanced-desc', 'GameplayOptions.Advanced.');
- // Remove locked whenever this PR from FunkinCrew is merged.
- // https://github.com/FunkinCrew/lime/pull/57
- for (checkbox in [
- new Checkbox(getNameID('streamedMusic'), getDescID('streamedMusic'), 'streamedMusic'),
- new Checkbox(getNameID('streamedVocals'), getDescID('streamedVocals'), 'streamedVocals')
- ]) {
- checkbox.locked = true;
- add(checkbox);
- }
+ add(new Checkbox(getNameID('streamedMusic'), getDescID('streamedMusic'), 'streamedMusic'));
+ add(new Checkbox(getNameID('streamedVocals'), getDescID('streamedVocals'), 'streamedVocals'));
}
}
\ No newline at end of file
diff --git a/source/haxe/Timer.hx b/source/haxe/Timer.hx
deleted file mode 100644
index 5092af8cc9..0000000000
--- a/source/haxe/Timer.hx
+++ /dev/null
@@ -1,297 +0,0 @@
-package haxe;
-
-#if !lime_cffi
-// Original haxe.Timer class
-
-/*
- * Copyright (C)2005-2018 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-/**
- The Timer class allows you to create asynchronous timers on platforms that
- support events.
-
- The intended usage is to create an instance of the Timer class with a given
- interval, set its run() method to a custom function to be invoked and
- eventually call stop() to stop the Timer.
-
- Note that a running Timer may or may not prevent the program to exit
- automatically when main() returns.
-
- It is also possible to extend this class and override its run() method in
- the child class.
-**/
-class Timer
-{
- #if (flash || js)
- private var id:Null;
- #elseif java
- private var timer:java.util.Timer;
- private var task:java.util.TimerTask;
- #elseif (haxe_ver >= "3.4.0")
- private var event:MainLoop.MainEvent;
- #end
-
- /**
- Creates a new timer that will run every `time_ms` milliseconds.
-
- After creating the Timer instance, it calls `this.run` repeatedly,
- with delays of `time_ms` milliseconds, until `this.stop` is called.
-
- The first invocation occurs after `time_ms` milliseconds, not
- immediately.
-
- The accuracy of this may be platform-dependent.
- **/
- public function new(time_ms:Int)
- {
- #if flash
- var me = this;
- id = untyped __global__["flash.utils.setInterval"](function()
- {
- me.run();
- }, time_ms);
- #elseif js
- var me = this;
- id = untyped setInterval(function() me.run(), time_ms);
- #elseif java
- timer = new java.util.Timer();
- timer.scheduleAtFixedRate(task = new TimerTask(this), haxe.Int64.ofInt(time_ms), haxe.Int64.ofInt(time_ms));
- #elseif (haxe_ver >= "3.4.0")
- var dt = time_ms / 1000;
- event = MainLoop.add(function()
- {
- @:privateAccess event.nextRun += dt;
- run();
- });
- event.delay(dt);
- #end
- }
-
- /**
- Stops `this` Timer.
-
- After calling this method, no additional invocations of `this.run`
- will occur.
-
- It is not possible to restart `this` Timer once stopped.
- **/
- public function stop()
- {
- #if (flash || js)
- if (id == null) return;
- #if flash
- untyped __global__["flash.utils.clearInterval"](id);
- #elseif js
- untyped clearInterval(id);
- #end
- id = null;
- #elseif java
- if (timer != null)
- {
- timer.cancel();
- timer = null;
- }
- task = null;
- #elseif (haxe_ver >= "3.4.0")
- if (event != null)
- {
- event.stop();
- event = null;
- }
- #end
- }
-
- /**
- This method is invoked repeatedly on `this` Timer.
-
- It can be overridden in a subclass, or rebound directly to a custom
- function:
- var timer = new haxe.Timer(1000); // 1000ms delay
- timer.run = function() { ... }
-
- Once bound, it can still be rebound to different functions until `this`
- Timer is stopped through a call to `this.stop`.
- **/
- public dynamic function run() {}
-
- /**
- Invokes `f` after `time_ms` milliseconds.
-
- This is a convenience function for creating a new Timer instance with
- `time_ms` as argument, binding its run() method to `f` and then stopping
- `this` Timer upon the first invocation.
-
- If `f` is null, the result is unspecified.
- **/
- public static function delay(f:Void->Void, time_ms:Int)
- {
- var t = new haxe.Timer(time_ms);
- t.run = function()
- {
- t.stop();
- f();
- };
- return t;
- }
-
- /**
- Measures the time it takes to execute `f`, in seconds with fractions.
-
- This is a convenience function for calculating the difference between
- Timer.stamp() before and after the invocation of `f`.
-
- The difference is passed as argument to Log.trace(), with "s" appended
- to denote the unit. The optional `pos` argument is passed through.
-
- If `f` is null, the result is unspecified.
- **/
- public static function measure(f:Void->T, ?pos:PosInfos):T
- {
- var t0 = stamp();
- var r = f();
- Log.trace((stamp() - t0) + "s", pos);
- return r;
- }
-
- /**
- Returns a timestamp, in seconds with fractions.
-
- The value itself might differ depending on platforms, only differences
- between two values make sense.
- **/
- public static inline function stamp():Float
- {
- #if flash
- return flash.Lib.getTimer() / 1000;
- #elseif (neko || php)
- return Sys.time();
- #elseif js
- return Date.now().getTime() / 1000;
- #elseif cpp
- return untyped __global__.__time_stamp();
- #elseif python
- return Sys.cpuTime();
- #elseif sys
- return Sys.time();
- #else
- return 0;
- #end
- }
-}
-
-#if java
-@:nativeGen
-private class TimerTask extends java.util.TimerTask
-{
- var timer:Timer;
-
- public function new(timer:Timer):Void
- {
- super();
- this.timer = timer;
- }
-
- @:overload override public function run():Void
- {
- timer.run();
- }
-}
-#end
-#else
-import lime.system.System;
-
-class Timer
-{
- private static var sRunningTimers:Array = [];
-
- private var mTime:Float;
- private var mFireAt:Float;
- private var mRunning:Bool;
-
- public function new(time:Float)
- {
- mTime = time;
- sRunningTimers.push(this);
- mFireAt = getMS() + mTime;
- mRunning = true;
- }
-
- public static function delay(f:Void->Void, time:Int)
- {
- var t = new Timer(time);
-
- t.run = function()
- {
- t.stop();
- f();
- };
-
- return t;
- }
-
- private static function getMS():Float
- {
- return System.getTimer();
- }
-
- public static function measure(f:Void->T, ?pos:PosInfos):T
- {
- var t0 = stamp();
- var r = f();
- Log.trace((stamp() - t0) + "s", pos);
- return r;
- }
-
- dynamic public function run() {}
-
- public static inline function stamp():Float
- {
- var timer = System.getTimer();
- return (timer > 0 ? timer / 1000 : 0);
- }
-
- public function stop():Void
- {
- /*if (mRunning)
- {
-
- for (i in 0...sRunningTimers.length)
- {
- if (sRunningTimers[i] == this)
- {
- sRunningTimers[i] = null;
- break;
- }
- }
- }*/
- mRunning = false;
- }
-
- @:noCompletion private function __check(inTime:Float)
- {
- if (inTime >= mFireAt)
- {
- mFireAt += mTime;
- run();
- }
- }
-}
-#end
diff --git a/source/lime/_internal/backend/html5/HTML5AudioSource.hx b/source/lime/_internal/backend/html5/HTML5AudioSource.hx
deleted file mode 100644
index 5d66fc04a5..0000000000
--- a/source/lime/_internal/backend/html5/HTML5AudioSource.hx
+++ /dev/null
@@ -1,283 +0,0 @@
-package lime._internal.backend.html5;
-
-import lime.math.Vector4;
-import lime.media.AudioSource;
-import lime.media.AudioManager;
-
-@:access(lime.media.AudioBuffer)
-class HTML5AudioSource
-{
- private var completed:Bool;
- private var gain:Float;
- private var id:Int;
- private var length:Null;
- private var loops:Int;
- private var parent:AudioSource;
- private var playing:Bool;
- private var position:Vector4;
-
- public function new(parent:AudioSource)
- {
- this.parent = parent;
-
- id = -1;
- gain = 1;
- position = new Vector4();
- }
-
- public function dispose():Void {
- stop();
- }
-
- public function init():Void {}
-
- public function play():Void
- {
- #if lime_howlerjs
- if (playing || parent.buffer == null || parent.buffer.__srcHowl == null)
- {
- return;
- }
-
- playing = true;
-
- var time = getCurrentTime();
-
- completed = false;
-
- var cacheVolume = untyped parent.buffer.__srcHowl._volume;
- untyped parent.buffer.__srcHowl._volume = parent.gain;
-
- id = parent.buffer.__srcHowl.play();
-
- untyped parent.buffer.__srcHowl._volume = cacheVolume;
- // setGain (parent.gain);
-
- setPosition(parent.position);
-
- parent.buffer.__srcHowl.on("end", howl_onEnd, id);
-
- // Calling setCurrentTime causes html5 audio to replay from this position on next frame
- #if force_html5_audio
- if (time == 0) setCurrentTime(time);
- #else
- setCurrentTime(time);
- #end
- #end
- }
-
- public function pause():Void
- {
- #if lime_howlerjs
- playing = false;
-
- if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- parent.buffer.__srcHowl.pause(id);
- }
- #end
- }
-
- public function stop():Void
- {
- #if lime_howlerjs
- playing = false;
-
- if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- parent.buffer.__srcHowl.stop(id);
- parent.buffer.__srcHowl.off("end", howl_onEnd, id);
- }
- #end
- }
-
- // Event Handlers
- private function howl_onEnd()
- {
- #if lime_howlerjs
- playing = false;
-
- if (loops > 0)
- {
- loops--;
- stop();
- if (loopTime != null && loopTime > 0) setCurrentTime(loopTime);
- play();
- parent.onLoop.dispatch();
- return;
- }
- else if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- parent.buffer.__srcHowl.stop(id);
- parent.buffer.__srcHowl.off("end", howl_onEnd, id);
- }
-
- completed = true;
- parent.onComplete.dispatch();
- #end
- }
-
- // Get & Set Methods
- public function getCurrentTime():Float
- {
- if (id == -1)
- {
- return 0;
- }
-
- #if lime_howlerjs
- if (completed)
- {
- return getLength();
- }
- else if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- var time = parent.buffer.__srcHowl.seek(id) * 1000.0 - parent.offset;
- if (time < 0) return 0;
- return time;
- }
- #end
-
- return 0;
- }
-
- public function getLatency():Float
- {
- var ctx = AudioManager.context.web;
- if (ctx != null)
- {
- var baseLatency:Float = untyped ctx.baseLatency != null ? untyped ctx.baseLatency : 0;
- var outputLatency:Float = untyped ctx.outputLatency != null ? untyped ctx.outputLatency : 0;
-
- return (baseLatency + outputLatency) * 1000;
- }
-
- return 0;
- }
-
- public function setCurrentTime(value:Float):Float
- {
- #if lime_howlerjs
- if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- // if (playing) buffer.__srcHowl.play (id);
- var pos = (value + parent.offset) / 1000;
- if (pos < 0) pos = 0;
- parent.buffer.__srcHowl.seek(pos, id);
- }
- #end
-
- return value;
- }
-
- public function getGain():Float
- {
- return gain;
- }
-
- public function setGain(value:Float):Float
- {
- #if lime_howlerjs
- // set howler volume only if we have an active id.
- // Passing -1 might create issues in future play()'s.
-
- if (parent.buffer != null && parent.buffer.__srcHowl != null && id != -1)
- {
- parent.buffer.__srcHowl.volume(value, id);
- }
- #end
-
- return gain = value;
- }
-
- public function getLength():Null
- {
- if (length != 0)
- {
- return length;
- }
-
- #if lime_howlerjs
- if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- return parent.buffer.__srcHowl.duration() * 1000.0;
- }
- #end
-
- return 0;
- }
-
- public function setLength(value:Null):Null
- {
- return length = value;
- }
-
- public function getLoops():Int
- {
- return loops;
- }
-
- public function setLoops(value:Int):Int
- {
- return loops = value;
- }
-
- public function getLoopTime():Float {
- return loopTime;
- }
-
- public function setLoopTime(value:Float):Float {
- return loopTime = value;
- }
-
- public function getPitch():Float
- {
- #if lime_howlerjs
- return parent.buffer.__srcHowl.rate();
- #else
- return 1;
- #end
- }
-
- public function setPitch(value:Float):Float
- {
- #if lime_howlerjs
- parent.buffer.__srcHowl.rate(value);
- #end
-
- return getPitch();
- }
-
-
- public function getPosition():Vector4
- {
- return position;
- }
-
- public function setPosition(value:Vector4):Vector4
- {
- position.x = value.x;
- position.y = value.y;
- position.z = value.z;
- position.w = value.w;
-
- #if lime_howlerjs
- if (parent.buffer != null && parent.buffer.__srcHowl != null && parent.buffer.__srcHowl.pos != null) parent.buffer.__srcHowl.pos(position.x, position.y, position.z, id);
- // There are more settings to the position of the sound on the "pannerAttr()" function of howler. Maybe somebody who understands sound should look into it?
- #end
-
- return position;
- }
-
- public function getPan():Float
- {
- return position.x;
- }
-
- public function setPan(value:Float):Float
- {
- position.setTo(value, 0, -Math.sqrt(1 - value * value));
- if (parent.buffer != null && parent.buffer.__srcHowl != null && parent.buffer.__srcHowl.stereo != null) parent.buffer.__srcHowl.stereo(value, id);
- return value;
- }
-}
diff --git a/source/lime/_internal/backend/native/NativeApplication.hx b/source/lime/_internal/backend/native/NativeApplication.hx
deleted file mode 100644
index ac8ab467ed..0000000000
--- a/source/lime/_internal/backend/native/NativeApplication.hx
+++ /dev/null
@@ -1,981 +0,0 @@
-package lime._internal.backend.native;
-
-import haxe.Timer;
-import lime._internal.backend.native.NativeCFFI;
-import lime.app.Application;
-import lime.graphics.opengl.GL;
-import lime.graphics.OpenGLRenderContext;
-import lime.graphics.RenderContext;
-import lime.math.Rectangle;
-import lime.media.AudioManager;
-import lime.system.Clipboard;
-import lime.system.Display;
-import lime.system.DisplayMode;
-import lime.system.JNI;
-import lime.system.Sensor;
-import lime.system.SensorType;
-import lime.system.System;
-import lime.ui.Gamepad;
-import lime.ui.Joystick;
-import lime.ui.JoystickHatPosition;
-import lime.ui.KeyCode;
-import lime.ui.KeyModifier;
-import lime.ui.Touch;
-import lime.ui.Window;
-
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(haxe.Timer)
-@:access(lime._internal.backend.native.NativeCFFI)
-@:access(lime._internal.backend.native.NativeOpenGLRenderContext)
-@:access(lime._internal.backend.native.NativeWindow)
-@:access(lime.app.Application)
-@:access(lime.graphics.opengl.GL)
-@:access(lime.graphics.OpenGLRenderContext)
-@:access(lime.graphics.Renderer)
-@:access(lime.system.Clipboard)
-@:access(lime.system.Sensor)
-@:access(lime.ui.Gamepad)
-@:access(lime.ui.Joystick)
-@:access(lime.ui.Window)
-class NativeApplication
-{
- private var applicationEventInfo = new ApplicationEventInfo(UPDATE);
- private var clipboardEventInfo = new ClipboardEventInfo();
- private var currentTouches = new Map();
- private var dropEventInfo = new DropEventInfo();
- private var gamepadEventInfo = new GamepadEventInfo();
- private var joystickEventInfo = new JoystickEventInfo();
- private var keyEventInfo = new KeyEventInfo();
- private var mouseEventInfo = new MouseEventInfo();
- private var renderEventInfo = new RenderEventInfo(RENDER);
- private var sensorEventInfo = new SensorEventInfo();
- private var textEventInfo = new TextEventInfo();
- private var touchEventInfo = new TouchEventInfo();
- private var unusedTouchesPool = new List();
- private var windowEventInfo = new WindowEventInfo();
-
- public var handle:Dynamic;
-
- private var pauseTimer:Int;
- private var parent:Application;
- private var toggleFullscreen:Bool;
-
- private static function __init__()
- {
- #if (lime_cffi && !macro)
- var init = NativeCFFI;
- #end
- }
-
- public function new(parent:Application):Void
- {
- this.parent = parent;
- pauseTimer = -1;
- toggleFullscreen = true;
-
- AudioManager.init();
-
- #if (ios || android || tvos)
- Sensor.registerSensor(SensorType.ACCELEROMETER, 0);
- #end
-
- #if (!macro && lime_cffi)
- handle = NativeCFFI.lime_application_create();
- #end
- }
-
- private function advanceTimer():Void
- {
- #if lime_cffi
- if (pauseTimer > -1)
- {
- var offset = System.getTimer() - pauseTimer;
- for(timer in Timer.sRunningTimers) {
- if(timer != null && timer.mRunning) timer.mFireAt += offset;
- }
- pauseTimer = -1;
- }
- #end
- }
-
- public function exec():Int
- {
- #if !macro
- #if lime_cffi
- NativeCFFI.lime_application_event_manager_register(handleApplicationEvent, applicationEventInfo);
- NativeCFFI.lime_clipboard_event_manager_register(handleClipboardEvent, clipboardEventInfo);
- NativeCFFI.lime_drop_event_manager_register(handleDropEvent, dropEventInfo);
- NativeCFFI.lime_gamepad_event_manager_register(handleGamepadEvent, gamepadEventInfo);
- NativeCFFI.lime_joystick_event_manager_register(handleJoystickEvent, joystickEventInfo);
- NativeCFFI.lime_key_event_manager_register(handleKeyEvent, keyEventInfo);
- NativeCFFI.lime_mouse_event_manager_register(handleMouseEvent, mouseEventInfo);
- NativeCFFI.lime_render_event_manager_register(handleRenderEvent, renderEventInfo);
- NativeCFFI.lime_text_event_manager_register(handleTextEvent, textEventInfo);
- NativeCFFI.lime_touch_event_manager_register(handleTouchEvent, touchEventInfo);
- NativeCFFI.lime_window_event_manager_register(handleWindowEvent, windowEventInfo);
- #if (ios || android || tvos)
- NativeCFFI.lime_sensor_event_manager_register(handleSensorEvent, sensorEventInfo);
- #end
- #end
-
- #if (nodejs && lime_cffi)
- NativeCFFI.lime_application_init(handle);
-
- var eventLoop = function()
- {
- var active = NativeCFFI.lime_application_update(handle);
-
- if (!active)
- {
- untyped process.exitCode = NativeCFFI.lime_application_quit(handle);
- parent.onExit.dispatch(untyped process.exitCode);
- }
- else
- {
- untyped setImmediate(eventLoop);
- }
- }
-
- untyped setImmediate(eventLoop);
- return 0;
- #elseif lime_cffi
- var result = NativeCFFI.lime_application_exec(handle);
-
- #if (!webassembly && !ios && !nodejs)
- parent.onExit.dispatch(result);
- #end
-
- return result;
- #end
- #end
-
- return 0;
- }
-
- public function exit():Void
- {
- AudioManager.shutdown();
-
- #if (!macro && lime_cffi)
- NativeCFFI.lime_application_quit(handle);
- #end
- }
-
- private function handleApplicationEvent():Void
- {
- switch (applicationEventInfo.type)
- {
- case UPDATE:
- updateTimer();
-
- parent.onUpdate.dispatch(applicationEventInfo.deltaTime);
-
- default:
- }
- }
-
- private function handleClipboardEvent():Void
- {
- Clipboard.__update();
- }
-
- private function handleDropEvent():Void
- {
- for (window in parent.windows)
- {
- window.onDropFile.dispatch(#if hl @:privateAccess String.fromUTF8(dropEventInfo.file) #else dropEventInfo.file #end);
- }
- }
-
- private function handleGamepadEvent():Void
- {
- switch (gamepadEventInfo.type)
- {
- case AXIS_MOVE:
- var gamepad = Gamepad.devices.get(gamepadEventInfo.id);
- if (gamepad != null) gamepad.onAxisMove.dispatch(gamepadEventInfo.axis, gamepadEventInfo.axisValue);
-
- case BUTTON_DOWN:
- var gamepad = Gamepad.devices.get(gamepadEventInfo.id);
- if (gamepad != null) gamepad.onButtonDown.dispatch(gamepadEventInfo.button);
-
- case BUTTON_UP:
- var gamepad = Gamepad.devices.get(gamepadEventInfo.id);
- if (gamepad != null) gamepad.onButtonUp.dispatch(gamepadEventInfo.button);
-
- case CONNECT:
- Gamepad.__connect(gamepadEventInfo.id);
-
- case DISCONNECT:
- Gamepad.__disconnect(gamepadEventInfo.id);
- }
- }
-
- private function handleJoystickEvent():Void
- {
- switch (joystickEventInfo.type)
- {
- case AXIS_MOVE:
- var joystick = Joystick.devices.get(joystickEventInfo.id);
- if (joystick != null) joystick.onAxisMove.dispatch(joystickEventInfo.index, joystickEventInfo.x);
-
- case HAT_MOVE:
- var joystick = Joystick.devices.get(joystickEventInfo.id);
- if (joystick != null) joystick.onHatMove.dispatch(joystickEventInfo.index, joystickEventInfo.eventValue);
-
- case TRACKBALL_MOVE: // I guess this was just removed ??
-
- case BUTTON_DOWN:
- var joystick = Joystick.devices.get(joystickEventInfo.id);
- if (joystick != null) joystick.onButtonDown.dispatch(joystickEventInfo.index);
-
- case BUTTON_UP:
- var joystick = Joystick.devices.get(joystickEventInfo.id);
- if (joystick != null) joystick.onButtonUp.dispatch(joystickEventInfo.index);
-
- case CONNECT:
- Joystick.__connect(joystickEventInfo.id);
-
- case DISCONNECT:
- Joystick.__disconnect(joystickEventInfo.id);
- }
- }
-
- private function handleKeyEvent():Void
- {
- var window = parent.__windowByID.get(keyEventInfo.windowID);
-
- if (window != null)
- {
- var type:KeyEventType = keyEventInfo.type;
- var int32:Float = keyEventInfo.keyCode;
- var keyCode:KeyCode = Std.int(int32);
- var modifier:KeyModifier = keyEventInfo.modifier;
-
- switch (type)
- {
- case KEY_DOWN:
- window.onKeyDown.dispatch(keyCode, modifier);
-
- case KEY_UP:
- window.onKeyUp.dispatch(keyCode, modifier);
- }
-
- #if (windows || linux)
- if (keyCode == RETURN)
- {
- if (type == KEY_DOWN)
- {
- if (toggleFullscreen && modifier.altKey && (!modifier.ctrlKey && !modifier.shiftKey && !modifier.metaKey))
- {
- toggleFullscreen = false;
-
- if (!window.onKeyDown.canceled)
- {
- window.fullscreen = !window.fullscreen;
- }
- }
- }
- else
- {
- toggleFullscreen = true;
- }
- }
-
- #if rpi
- if (keyCode == ESCAPE && modifier == KeyModifier.NONE && type == KEY_UP && !window.onKeyUp.canceled)
- {
- System.exit(0);
- }
- #end
- #elseif mac
- if (keyCode == F)
- {
- if (type == KEY_DOWN)
- {
- if (toggleFullscreen && (modifier.ctrlKey && modifier.metaKey) && (!modifier.altKey && !modifier.shiftKey))
- {
- toggleFullscreen = false;
-
- if (!window.onKeyDown.canceled)
- {
- window.fullscreen = !window.fullscreen;
- }
- }
- }
- else
- {
- toggleFullscreen = true;
- }
- }
- #elseif android
- if (keyCode == APP_CONTROL_BACK && modifier == KeyModifier.NONE && type == KEY_UP && !window.onKeyUp.canceled)
- {
- var mainActivity = JNI.createStaticField("org/haxe/extension/Extension", "mainActivity", "Landroid/app/Activity;");
- var moveTaskToBack = JNI.createMemberMethod("android/app/Activity", "moveTaskToBack", "(Z)Z");
-
- moveTaskToBack(mainActivity.get(), true);
- }
- #end
- }
- }
-
- private function handleMouseEvent():Void
- {
- var window = parent.__windowByID.get(mouseEventInfo.windowID);
-
- if (window != null)
- {
- switch (mouseEventInfo.type)
- {
- case MOUSE_DOWN:
- window.clickCount = mouseEventInfo.clickCount;
- window.onMouseDown.dispatch(mouseEventInfo.x, mouseEventInfo.y, mouseEventInfo.button);
- window.clickCount = 0;
-
- case MOUSE_UP:
- window.clickCount = mouseEventInfo.clickCount;
- window.onMouseUp.dispatch(mouseEventInfo.x, mouseEventInfo.y, mouseEventInfo.button);
- window.clickCount = 0;
-
- case MOUSE_MOVE:
- window.onMouseMove.dispatch(mouseEventInfo.x, mouseEventInfo.y);
- window.onMouseMoveRelative.dispatch(mouseEventInfo.movementX, mouseEventInfo.movementY);
-
- case MOUSE_WHEEL:
- window.onMouseWheel.dispatch(mouseEventInfo.x, mouseEventInfo.y, UNKNOWN);
-
- default:
- }
- }
- }
-
- private function handleRenderEvent():Void
- {
- // TODO: Allow windows to render independently
-
- for (window in parent.__windows)
- {
- if (window == null) continue;
-
- // parent.renderer = renderer;
-
- switch (renderEventInfo.type)
- {
- case RENDER:
- if (window.context != null)
- {
- window.__backend.render();
- window.onRender.dispatch(window.context);
-
- if (!window.onRender.canceled)
- {
- window.__backend.contextFlip();
- }
- }
-
- case RENDER_CONTEXT_LOST:
- if (window.__backend.useHardware && window.context != null)
- {
- switch (window.context.type)
- {
- case OPENGL, OPENGLES, WEBGL:
- #if (lime_cffi && (lime_opengl || lime_opengles) && !display)
- var gl = window.context.gl;
- (gl : NativeOpenGLRenderContext).__contextLost();
- if (GL.context == gl) GL.context = null;
- #end
-
- default:
- }
-
- window.context = null;
- window.onRenderContextLost.dispatch();
- }
-
- case RENDER_CONTEXT_RESTORED:
- if (window.__backend.useHardware)
- {
- // GL.context = new OpenGLRenderContext ();
- // window.context.gl = GL.context;
-
- window.onRenderContextRestored.dispatch(window.context);
- }
- }
- }
- }
-
- private function handleSensorEvent():Void
- {
- var sensor = Sensor.sensorByID.get(sensorEventInfo.id);
-
- if (sensor != null)
- {
- sensor.onUpdate.dispatch(sensorEventInfo.x, sensorEventInfo.y, sensorEventInfo.z);
- }
- }
-
- private function handleTextEvent():Void
- {
- var window = parent.__windowByID.get(textEventInfo.windowID);
-
- if (window != null)
- {
- switch (textEventInfo.type)
- {
- case TEXT_INPUT:
- window.onTextInput.dispatch(#if hl @:privateAccess String.fromUTF8(textEventInfo.text) #else textEventInfo.text #end);
-
- case TEXT_EDIT:
- window.onTextEdit.dispatch(#if hl @:privateAccess String.fromUTF8(textEventInfo.text) #else textEventInfo.text #end, textEventInfo.start,
- textEventInfo.length);
-
- default:
- }
- }
- }
-
- private function handleTouchEvent():Void
- {
- switch (touchEventInfo.type)
- {
- case TOUCH_START:
- var touch = unusedTouchesPool.pop();
-
- if (touch == null)
- {
- touch = new Touch(touchEventInfo.x, touchEventInfo.y, touchEventInfo.id, touchEventInfo.dx, touchEventInfo.dy, touchEventInfo.pressure,
- touchEventInfo.device);
- }
- else
- {
- touch.x = touchEventInfo.x;
- touch.y = touchEventInfo.y;
- touch.id = touchEventInfo.id;
- touch.dx = touchEventInfo.dx;
- touch.dy = touchEventInfo.dy;
- touch.pressure = touchEventInfo.pressure;
- touch.device = touchEventInfo.device;
- }
-
- currentTouches.set(touch.id, touch);
-
- Touch.onStart.dispatch(touch);
-
- case TOUCH_END:
- var touch = currentTouches.get(touchEventInfo.id);
-
- if (touch != null)
- {
- touch.x = touchEventInfo.x;
- touch.y = touchEventInfo.y;
- touch.dx = touchEventInfo.dx;
- touch.dy = touchEventInfo.dy;
- touch.pressure = touchEventInfo.pressure;
-
- Touch.onEnd.dispatch(touch);
-
- currentTouches.remove(touchEventInfo.id);
- unusedTouchesPool.add(touch);
- }
-
- case TOUCH_MOVE:
- var touch = currentTouches.get(touchEventInfo.id);
-
- if (touch != null)
- {
- touch.x = touchEventInfo.x;
- touch.y = touchEventInfo.y;
- touch.dx = touchEventInfo.dx;
- touch.dy = touchEventInfo.dy;
- touch.pressure = touchEventInfo.pressure;
-
- Touch.onMove.dispatch(touch);
- }
-
- default:
- }
- }
-
- private function handleWindowEvent():Void
- {
- var window = parent.__windowByID.get(windowEventInfo.windowID);
-
- if (window != null)
- {
- switch (windowEventInfo.type)
- {
- case WINDOW_ACTIVATE:
- advanceTimer();
- window.onActivate.dispatch();
- AudioManager.resume();
-
- case WINDOW_CLOSE:
- window.close();
-
- case WINDOW_DEACTIVATE:
- window.onDeactivate.dispatch();
- AudioManager.suspend();
- pauseTimer = System.getTimer();
-
- case WINDOW_ENTER:
- window.onEnter.dispatch();
-
- case WINDOW_EXPOSE:
- window.onExpose.dispatch();
-
- case WINDOW_FOCUS_IN:
- window.onFocusIn.dispatch();
-
- case WINDOW_FOCUS_OUT:
- window.onFocusOut.dispatch();
-
- case WINDOW_LEAVE:
- window.onLeave.dispatch();
-
- case WINDOW_MAXIMIZE:
- window.__maximized = true;
- window.__fullscreen = false;
- window.__minimized = false;
- window.onMaximize.dispatch();
-
- case WINDOW_MINIMIZE:
- window.__minimized = true;
- window.__maximized = false;
- window.__fullscreen = false;
- window.onMinimize.dispatch();
-
- case WINDOW_MOVE:
- window.__x = windowEventInfo.x;
- window.__y = windowEventInfo.y;
- window.onMove.dispatch(windowEventInfo.x, windowEventInfo.y);
-
- case WINDOW_RESIZE:
- window.__width = windowEventInfo.width;
- window.__height = windowEventInfo.height;
- window.onResize.dispatch(windowEventInfo.width, windowEventInfo.height);
-
- case WINDOW_RESTORE:
- window.__fullscreen = false;
- window.__minimized = false;
- window.onRestore.dispatch();
-
- case WINDOW_SHOW:
- window.onShow.dispatch();
-
- case WINDOW_HIDE:
- window.onHide.dispatch();
- }
- }
- }
-
- private function updateTimer():Void
- {
- #if lime_cffi
- if (Timer.sRunningTimers.length > 0)
- {
- var currentTime = System.getTimer();
- var foundNull = false;
- var timer;
-
- for (i in 0...Timer.sRunningTimers.length)
- {
- timer = Timer.sRunningTimers[i];
-
- if (timer != null && timer.mRunning)
- {
- if (currentTime >= timer.mFireAt)
- {
- timer.mFireAt += timer.mTime;
- timer.run();
- }
- }
- else
- {
- foundNull = true;
- }
- }
-
- if (foundNull)
- {
- Timer.sRunningTimers = Timer.sRunningTimers.filter(function(val)
- {
- return val != null && val.mRunning;
- });
- }
- }
-
- #if (haxe_ver >= 4.2)
- #if target.threaded
- sys.thread.Thread.current().events.progress();
- #else
- // Duplicate code required because Haxe 3 can't handle
- // #if (haxe_ver >= 4.2 && target.threaded)
- @:privateAccess haxe.EntryPoint.processEvents();
- #end
- #else
- @:privateAccess haxe.EntryPoint.processEvents();
- #end
- #end
- }
-}
-
-@:keep /*private*/ class ApplicationEventInfo
-{
- public var deltaTime:Int;
- public var type:ApplicationEventType;
-
- public function new(type:ApplicationEventType = null, deltaTime:Int = 0)
- {
- this.type = type;
- this.deltaTime = deltaTime;
- }
-
- public function clone():ApplicationEventInfo
- {
- return new ApplicationEventInfo(type, deltaTime);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract ApplicationEventType(Int)
-{
- var UPDATE = 0;
- var EXIT = 1;
-}
-
-@:keep /*private*/ class ClipboardEventInfo
-{
- public var type:ClipboardEventType;
-
- public function new(type:ClipboardEventType = null)
- {
- this.type = type;
- }
-
- public function clone():ClipboardEventInfo
- {
- return new ClipboardEventInfo(type);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract ClipboardEventType(Int)
-{
- var UPDATE = 0;
-}
-
-@:keep /*private*/ class DropEventInfo
-{
- public var file:#if hl hl.Bytes #else String #end;
- public var type:DropEventType;
-
- public function new(type:DropEventType = null, file = null)
- {
- this.type = type;
- this.file = file;
- }
-
- public function clone():DropEventInfo
- {
- return new DropEventInfo(type, file);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract DropEventType(Int)
-{
- var DROP_FILE = 0;
-}
-
-@:keep /*private*/ class GamepadEventInfo
-{
- public var axis:Int;
- public var button:Int;
- public var id:Int;
- public var type:GamepadEventType;
- public var axisValue:Float;
-
- public function new(type:GamepadEventType = null, id:Int = 0, button:Int = 0, axis:Int = 0, value:Float = 0)
- {
- this.type = type;
- this.id = id;
- this.button = button;
- this.axis = axis;
- this.axisValue = value;
- }
-
- public function clone():GamepadEventInfo
- {
- return new GamepadEventInfo(type, id, button, axis, axisValue);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract GamepadEventType(Int)
-{
- var AXIS_MOVE = 0;
- var BUTTON_DOWN = 1;
- var BUTTON_UP = 2;
- var CONNECT = 3;
- var DISCONNECT = 4;
-}
-
-@:keep /*private*/ class JoystickEventInfo
-{
- public var id:Int;
- public var index:Int;
- public var type:JoystickEventType;
- public var eventValue:Int;
- public var x:Float;
- public var y:Float;
-
- public function new(type:JoystickEventType = null, id:Int = 0, index:Int = 0, value:Int = 0, x:Float = 0, y:Float = 0)
- {
- this.type = type;
- this.id = id;
- this.index = index;
- this.eventValue = value;
- this.x = x;
- this.y = y;
- }
-
- public function clone():JoystickEventInfo
- {
- return new JoystickEventInfo(type, id, index, eventValue, x, y);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract JoystickEventType(Int)
-{
- var AXIS_MOVE = 0;
- var HAT_MOVE = 1;
- var TRACKBALL_MOVE = 2;
- var BUTTON_DOWN = 3;
- var BUTTON_UP = 4;
- var CONNECT = 5;
- var DISCONNECT = 6;
-}
-
-@:keep /*private*/ class KeyEventInfo
-{
- public var keyCode: Float;
- public var modifier:Int;
- public var type:KeyEventType;
- public var windowID:Int;
-
- public function new(type:KeyEventType = null, windowID:Int = 0, keyCode: Float = 0, modifier:Int = 0)
- {
- this.type = type;
- this.windowID = windowID;
- this.keyCode = keyCode;
- this.modifier = modifier;
- }
-
- public function clone():KeyEventInfo
- {
- return new KeyEventInfo(type, windowID, keyCode, modifier);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract KeyEventType(Int)
-{
- var KEY_DOWN = 0;
- var KEY_UP = 1;
-}
-
-@:keep /*private*/ class MouseEventInfo
-{
- public var button:Int;
- public var movementX:Float;
- public var movementY:Float;
- public var type:MouseEventType;
- public var windowID:Int;
- public var x:Float;
- public var y:Float;
- public var clickCount:Int;
-
- public function new(type:MouseEventType = null, windowID:Int = 0, x:Float = 0, y:Float = 0, button:Int = 0, movementX:Float = 0, movementY:Float = 0, clickCount:Int = 0)
- {
- this.type = type;
- this.windowID = 0;
- this.x = x;
- this.y = y;
- this.button = button;
- this.movementX = movementX;
- this.movementY = movementY;
- this.clickCount = clickCount;
- }
-
- public function clone():MouseEventInfo
- {
- return new MouseEventInfo(type, windowID, x, y, button, movementX, movementY, clickCount);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract MouseEventType(Int)
-{
- var MOUSE_DOWN = 0;
- var MOUSE_UP = 1;
- var MOUSE_MOVE = 2;
- var MOUSE_WHEEL = 3;
-}
-
-@:keep /*private*/ class RenderEventInfo
-{
- public var type:RenderEventType;
-
- public function new(type:RenderEventType = null)
- {
- this.type = type;
- }
-
- public function clone():RenderEventInfo
- {
- return new RenderEventInfo(type);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract RenderEventType(Int)
-{
- var RENDER = 0;
- var RENDER_CONTEXT_LOST = 1;
- var RENDER_CONTEXT_RESTORED = 2;
-}
-
-@:keep /*private*/ class SensorEventInfo
-{
- public var id:Int;
- public var x:Float;
- public var y:Float;
- public var z:Float;
- public var type:SensorEventType;
-
- public function new(type:SensorEventType = null, id:Int = 0, x:Float = 0, y:Float = 0, z:Float = 0)
- {
- this.type = type;
- this.id = id;
- this.x = x;
- this.y = y;
- this.z = z;
- }
-
- public function clone():SensorEventInfo
- {
- return new SensorEventInfo(type, id, x, y, z);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract SensorEventType(Int)
-{
- var ACCELEROMETER = 0;
-}
-
-@:keep /*private*/ class TextEventInfo
-{
- public var id:Int;
- public var length:Int;
- public var start:Int;
- public var text:#if hl hl.Bytes #else String #end;
- public var type:TextEventType;
- public var windowID:Int;
-
- public function new(type:TextEventType = null, windowID:Int = 0, text = null, start:Int = 0, length:Int = 0)
- {
- this.type = type;
- this.windowID = windowID;
- this.text = text;
- this.start = start;
- this.length = length;
- }
-
- public function clone():TextEventInfo
- {
- return new TextEventInfo(type, windowID, text, start, length);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract TextEventType(Int)
-{
- var TEXT_INPUT = 0;
- var TEXT_EDIT = 1;
-}
-
-@:keep /*private*/ class TouchEventInfo
-{
- public var device:Int;
- public var dx:Float;
- public var dy:Float;
- public var id:Int;
- public var pressure:Float;
- public var type:TouchEventType;
- public var x:Float;
- public var y:Float;
-
- public function new(type:TouchEventType = null, x:Float = 0, y:Float = 0, id:Int = 0, dx:Float = 0, dy:Float = 0, pressure:Float = 0, device:Int = 0)
- {
- this.type = type;
- this.x = x;
- this.y = y;
- this.id = id;
- this.dx = dx;
- this.dy = dy;
- this.pressure = pressure;
- this.device = device;
- }
-
- public function clone():TouchEventInfo
- {
- return new TouchEventInfo(type, x, y, id, dx, dy, pressure, device);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract TouchEventType(Int)
-{
- var TOUCH_START = 0;
- var TOUCH_END = 1;
- var TOUCH_MOVE = 2;
-}
-
-@:keep /*private*/ class WindowEventInfo
-{
- public var height:Int;
- public var type:WindowEventType;
- public var width:Int;
- public var windowID:Int;
- public var x:Int;
- public var y:Int;
-
- public function new(type:WindowEventType = null, windowID:Int = 0, width:Int = 0, height:Int = 0, x:Int = 0, y:Int = 0)
- {
- this.type = type;
- this.windowID = windowID;
- this.width = width;
- this.height = height;
- this.x = x;
- this.y = y;
- }
-
- public function clone():WindowEventInfo
- {
- return new WindowEventInfo(type, windowID, width, height, x, y);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract WindowEventType(Int)
-{
- var WINDOW_ACTIVATE = 0;
- var WINDOW_CLOSE = 1;
- var WINDOW_DEACTIVATE = 2;
- var WINDOW_ENTER = 3;
- var WINDOW_EXPOSE = 4;
- var WINDOW_FOCUS_IN = 5;
- var WINDOW_FOCUS_OUT = 6;
- var WINDOW_LEAVE = 7;
- var WINDOW_MAXIMIZE = 8;
- var WINDOW_MINIMIZE = 9;
- var WINDOW_MOVE = 10;
- var WINDOW_RESIZE = 11;
- var WINDOW_RESTORE = 12;
- var WINDOW_SHOW = 13;
- var WINDOW_HIDE = 14;
-}
diff --git a/source/lime/_internal/backend/native/NativeAudioSource.hx b/source/lime/_internal/backend/native/NativeAudioSource.hx
deleted file mode 100644
index 9f09b24471..0000000000
--- a/source/lime/_internal/backend/native/NativeAudioSource.hx
+++ /dev/null
@@ -1,804 +0,0 @@
-package lime._internal.backend.native;
-
-import sys.thread.Thread;
-import sys.thread.Mutex;
-
-import haxe.Timer;
-import haxe.Int64;
-
-import lime.media.openal.AL;
-import lime.media.openal.ALBuffer;
-import lime.media.openal.ALSource;
-
-#if lime_vorbis
-import lime.media.vorbis.Vorbis;
-import lime.media.vorbis.VorbisFile;
-import lime.media.vorbis.VorbisInfo;
-#end
-
-import lime.math.Vector2;
-import lime.math.Vector4;
-import lime.media.AudioBuffer;
-import lime.media.AudioSource;
-import lime.system.Endian;
-import lime.system.System;
-import lime.utils.ArrayBuffer;
-import lime.utils.ArrayBufferView.TypedArrayType;
-import lime.utils.ArrayBufferView;
-
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(haxe.Timer)
-@:access(lime.media.AudioBuffer)
-@:access(lime.utils.ArrayBufferView)
-class NativeAudioSource {
- // Can hold up to 3 hours 44100 sampleRate audio, if you are into that, theorically.
-
- public static var STREAM_BUFFER_SAMPLES:Int = 0x2000; // how much buffers will be generating every frequency (doesnt have to be pow of 2?).
- public static var STREAM_MIN_BUFFERS:Int = 2; // how much buffers can a stream hold on minimum or starting.
- public static var STREAM_MAX_BUFFERS:Int = 8; // how much limit of a buffers can be used for streamed audios, must be higher than minimum.
- public static var STREAM_MAX_FLUSH_BUFFERS:Int = 3; // how much buffers can it play.
- public static var STREAM_PROCESS_BUFFERS:Int = 2; // how much buffers can be processed in a frequency tick.
- public static var POOL_MAX_BUFFERS:Int = 32; // how much buffers for the pool to hold.
-
- public static var moreFormatsSupported:Null;
- public static var loopPointsSupported:Null;
- public static var stereoAnglesExtensionSupported:Null;
- public static var latencyExtensionSupported:Null;
-
- private static var bufferDataPool:Array = [];
- private static var isBigEndian:Bool = System.endianness == Endian.BIG_ENDIAN;
-
- public static function getALFormat(bitsPerSample:Int, channels:Int):Int {
- if (moreFormatsSupported == null) moreFormatsSupported = AL.isExtensionPresent("AL_EXT_MCFORMATS");
-
- // There was a code to also supports for X-Fi Renderer but, that kind of device is
- // rare nowadays and none of sounds should have more than 24 bitsPerSample.
- // https://github.com/kcat/openal-soft/issues/934
-
- if (channels > 2 && moreFormatsSupported) {
- if (channels == 3) return bitsPerSample == 32 ? 0x1209 : (bitsPerSample == 16 ? 0x1208 : 0x1207);
- else if (channels == 4) return bitsPerSample == 32 ? 0x1206 : (bitsPerSample == 16 ? 0x1205 : 0x1204);
- else if (channels == 6) return bitsPerSample == 32 ? 0x120C : (bitsPerSample == 16 ? 0x120B : 0x120A);
- else if (channels == 7) return bitsPerSample == 32 ? 0x120F : (bitsPerSample == 16 ? 0x120E : 0x120D);
- else if (channels == 8) return bitsPerSample == 32 ? 0x1212 : (bitsPerSample == 16 ? 0x1211 : 0x1210);
- else return AL.FORMAT_MONO8;
- }
- else if (bitsPerSample == 32 && moreFormatsSupported) return channels == 2 ? 0x1203 : 0x1202;
- else if (channels == 2) return bitsPerSample == 16 ? AL.FORMAT_STEREO16 : AL.FORMAT_STEREO8;
- else return bitsPerSample == 16 ? AL.FORMAT_MONO16 : AL.FORMAT_MONO8;
- }
-
- private static function resetTimer(timer:Timer, time:Float, callback:Void->Void):Timer {
- if (timer == null) (timer = new Timer(time)).run = callback;
- else {
- timer.mTime = time;
- timer.mFireAt = Timer.getMS() + time;
- timer.mRunning = true;
- timer.run = callback;
-
- if (!Timer.sRunningTimers.contains(timer)) Timer.sRunningTimers.push(timer);
- }
- return timer;
- }
-
- inline private static function getFloat(x:Int64):Float return x.high * 4294967296. + (x.low >> 0);
-
- // Backward Compatibility Variables
- var handle(get, set):ALSource; inline function get_handle() return source; inline function set_handle(v) return source = v;
- var timer(get, set):Timer; inline function get_timer() return completeTimer; inline function set_timer(v) return completeTimer = v;
- var length(get, set):Null; inline function get_length() return endTime; inline function set_length(v) return endTime = v;
- var toLoop(get, set):Int; inline function get_toLoop() return streamLoops; inline function set_toLoop(v) return streamLoops = v;
- var bufferSizes(get, set):Array; inline function get_bufferSizes() return bufferLengths; inline function set_bufferSizes(v) return bufferLengths = v;
-
- var parent:AudioSource;
- var disposed:Bool;
- var streamed:Bool;
- var playing:Bool;
- var completed:Bool;
- var lastTime:Float;
-
- var position:Vector4;
- var angles:Vector2;
- var anglesArray:Array;
- var endTime:Null;
- var loopTime:Float;
- var loops:Int;
-
- var channels:Int;
- var sampleRate:Int;
- var wordSize:Int; // is bitsPerSample >> 3, ex: 8 bits is 1, 16 bits is 2, 32 bits is 4, etc.
- var samples:Int;
- var dataLength:Int;
- var duration:Float;
-
- var completeTimer:Timer;
- var source:ALSource;
- var buffer:ALBuffer;
- var standaloneBuffer:Bool;
- var format:Int; // AL.FORMAT_...
- var arrayType:TypedArrayType;
- var loopPoints:Array; // In Samples
-
- static var streamSources:Array = [];
- static var queuedStreamSources:Array = [];
-
- static var streamMutex:Mutex = new Mutex();
- static var streamTimer:Timer;
-
- #if !ALLOW_MULTITHREADING
- static var wasEmpty:Bool = false;
- static var threadRunning:Bool = false;
- static var streamThread:Thread;
- #end
-
- var streamRemove:Bool;
-
- var bufferLength:Int; // Size in bytes for current streamed audio buffers.
- var requestBuffers:Int;
- var queuedBuffers:Int;
- var streamLoops:Int;
- var streamEnded:Bool;
-
- // ORDERING IS CURRENT TO NEXT, STARTS FROM THE LENGTH OF THE ARRAYS
- var bufferDatas:Array;
- var bufferTimes:Array;
- var bufferLengths:Array;
-
- var buffers:Array;
- var nextBuffer:Int = 0;
-
- public function new(parent:AudioSource) {
- this.parent = parent;
-
- if (loopPointsSupported == null) loopPointsSupported = AL.isExtensionPresent("AL_SOFT_loop_points");
- if (stereoAnglesExtensionSupported == null) stereoAnglesExtensionSupported = AL.isExtensionPresent("AL_EXT_STEREO_ANGLES");
- if (latencyExtensionSupported == null) latencyExtensionSupported = AL.isExtensionPresent("AL_SOFT_source_latency");
- }
-
- public function dispose() {
- streamMutex.acquire();
- removeStream();
-
- stop();
- disposed = true;
-
- position = null;
- angles = null;
- anglesArray = null;
-
- if (source != null) {
- if (streamed) AL.sourceUnqueueBuffers(source, AL.getSourcei(source, AL.BUFFERS_QUEUED));
- else AL.sourcei(source, AL.BUFFER, AL.NONE);
-
- AL.deleteSource(source);
- source = null;
- }
-
- if (standaloneBuffer && buffer != null) {
- AL.bufferData(buffer, 0, null, 0, 0);
- AL.deleteBuffer(buffer);
- buffer = null;
- }
- loopPoints = null;
-
- if (buffers != null) {
- for (buffer in buffers) AL.bufferData(buffer, 0, null, 0, 0);
- AL.deleteBuffers(buffers);
- buffers = null;
- }
-
- if (bufferDatas != null) {
- for (data in bufferDatas) if (bufferDataPool.length < POOL_MAX_BUFFERS) bufferDataPool.push(data);
- bufferDatas = null;
- }
-
- completeTimer = null;
-
- bufferTimes = null;
- bufferLengths = null;
-
- streamMutex.release();
- }
-
- public function init() {
- if (source != null || (disposed = parent == null || (source = AL.createSource()) == null)) return;
- AL.sourcef(source, AL.MAX_GAIN, 32);
- AL.distanceModel(AL.NONE);
-
- if (position == null) position = new Vector4();
- if (angles == null) angles = new Vector2(Math.PI / 6, -Math.PI / 6); // https://github.com/kcat/openal-soft/issues/1032
- if (loopPoints == null) loopPoints = [0, 0];
- if (stereoAnglesExtensionSupported && anglesArray == null) anglesArray = [0, 0];
-
- resetBuffer();
- }
-
- public function resetBuffer() {
- if (parent.buffer == null) return;
-
- streamMutex.acquire();
- removeStream();
-
- stop();
-
- if (streamed) AL.sourceUnqueueBuffers(source, AL.getSourcei(source, AL.BUFFERS_QUEUED));
- else AL.sourcei(source, AL.BUFFER, AL.NONE);
-
- streamMutex.release();
-
- final audioBuffer = parent.buffer;
- channels = audioBuffer.channels;
- sampleRate = audioBuffer.sampleRate;
- wordSize = audioBuffer.bitsPerSample >> 3;
- format = getALFormat(audioBuffer.bitsPerSample, channels);
- arrayType = wordSize == 4 ? TypedArrayType.Uint32 : (wordSize == 2 ? TypedArrayType.Uint16 : TypedArrayType.Int8);
- standaloneBuffer = false;
- loopTime = 0;
- endTime = null;
-
- if (buffer != null) {
- if (standaloneBuffer) {
- AL.bufferData(buffer, 0, null, 0, 0);
- AL.deleteBuffer(buffer);
- }
- buffer = null;
- }
-
- if (audioBuffer.data != null) {
- streamed = false;
- samples = Std.int((dataLength = audioBuffer.data.byteLength) / wordSize / channels);
- }
- #if lime_vorbis
- else if (audioBuffer.__srcVorbisFile != null) {
- streamed = true;
- dataLength = Std.int(getFloat(samples = Int64.toInt(audioBuffer.__srcVorbisFile.pcmTotal())) * channels * wordSize);
- }
- #end
- else return;
-
- duration = getFloat(samples) / sampleRate * 1000;
-
- loopPoints[0] = 0;
- loopPoints[1] = samples - 1;
-
- if (streamed) {
- final length = STREAM_BUFFER_SAMPLES * channels;
- bufferLength = length * wordSize;
-
- if (buffers == null) buffers = AL.genBuffers(STREAM_MAX_FLUSH_BUFFERS);
- if (bufferDatas == null) {
- bufferDatas = [];
- bufferTimes = [];
- bufferLengths = [];
- }
-
- for (i in 0...STREAM_MAX_BUFFERS) {
- bufferTimes[i] = 0.0;
- bufferLengths[i] = 0;
-
- var data = bufferDataPool.pop();
- if (data == null) data = new ArrayBufferView(length, arrayType);
- else {
- data.type = arrayType;
- data.bytesPerElement = wordSize;
- data.length = length;
- if (data.byteLength != bufferLength) {
- #if cpp
- data.buffer.getData().resize(bufferLength);
- data.buffer.fill(data.byteLength, bufferLength - data.byteLength, 0);
- @:privateAccess data.buffer.length = bufferLength;
- #else
- data.buffer = new ArrayBuffer(bufferLength);
- #end
- }
- data.byteLength = bufferLength;
- }
- bufferDatas[i] = data;
- }
- }
- else {
- if (buffers != null) {
- for (buffer in buffers) AL.bufferData(buffer, 0, null, 0, 0);
- AL.deleteBuffers(buffers);
- buffers = null;
-
- for (data in bufferDatas) if (bufferDataPool.length < POOL_MAX_BUFFERS) bufferDataPool.push(data);
- bufferDatas.resize(0);
- }
-
- if (audioBuffer.__srcBuffer != null) {
- if (AL.getBufferi(audioBuffer.__srcBuffer, AL.SIZE) != dataLength) {
- AL.bufferData(audioBuffer.__srcBuffer, 0, null, 0, 0);
- AL.deleteBuffer(audioBuffer.__srcBuffer);
- if ((buffer = audioBuffer.__srcBuffer = AL.createBuffer()) != null)
- AL.bufferData(buffer, format, audioBuffer.data, dataLength, sampleRate);
- }
- else
- buffer = audioBuffer.__srcBuffer;
- }
- else if ((buffer = audioBuffer.__srcBuffer = AL.createBuffer()) != null)
- AL.bufferData(buffer, format, audioBuffer.data, dataLength, sampleRate);
-
- AL.sourcei(source, AL.BUFFER, buffer);
- }
-
- updateLoopPoints();
- }
-
- function updateLoopPoints() {
- if (loops <= 0) return AL.sourcei(source, AL.LOOPING, AL.FALSE);
-
- var time = getCurrentTime() + parent.offset, length = getRealLength();
- final fixed = time >= length;
- if (fixed) time = loopTime;
-
- if (!streamed) {
- var internalLoop = AL.TRUE;
- if (length < duration - 1 && loopTime > 1) {
- if (!loopPointsSupported) internalLoop = AL.FALSE;
- else {
- AL.sourceStop(source);
- AL.sourcei(source, AL.BUFFER, AL.NONE);
- if (!standaloneBuffer) {
- if (standaloneBuffer = (buffer = AL.createBuffer()) != null)
- AL.bufferData(buffer, format, parent.buffer.data, dataLength, sampleRate);
- else {
- buffer = parent.buffer.__srcBuffer;
- internalLoop = AL.FALSE;
- }
- }
- if (internalLoop == AL.TRUE) AL.bufferiv(buffer, 0x2015/*AL.LOOP_POINTS_SOFT*/, loopPoints);
- AL.sourcei(source, AL.BUFFER, buffer);
- }
- }
-
- AL.sourcei(source, AL.LOOPING, internalLoop);
- if (playing) setCurrentTime(time - parent.offset);
- else updateCompleteTimer();
- }
- else if (playing && (fixed || streamLoops > 0)) {
- AL.sourcei(source, AL.LOOPING, AL.FALSE);
- AL.sourceStop(source);
- snapBuffersToTime(time, streamLoops > 0);
- AL.sourcePlay(source);
- }
- }
-
- // https://github.com/xiph/vorbis/blob/master/CHANGES#L39 bug in libvorbis <= 1.3.4
- inline function streamSeek(samples:Int64)
- if (samples <= 1) parent.buffer.__srcVorbisFile.rawSeek(0); else parent.buffer.__srcVorbisFile.pcmSeek(samples);
-
- inline function streamTell():Int64
- return parent.buffer.__srcVorbisFile.pcmTell();
-
- inline function streamRead(buffer:ArrayBuffer, position:Int, length:Int, wordSize:Int):Int
- return parent.buffer.__srcVorbisFile.read(buffer, position, length, isBigEndian, wordSize, true);
-
- function readToBufferData(data:ArrayBufferView, currentPCM:Int64):Int {
- var length = (Int64.ofInt(loopPoints[1]) - currentPCM) * channels * wordSize;
- var n = length < bufferLength ? length.low : bufferLength, total = 0, result = 0, wasEOF = false;
-
- try while (total < bufferLength) {
- result = n > 0 ? streamRead(data.buffer, total, n, wordSize) : 0;
-
- if (result == Vorbis.HOLE) continue;
- else if (result <= Vorbis.EREAD) break;
- else if (result == 0) {
- if (streamEnded = wasEOF == (wasEOF = true) || loops <= streamLoops) break;
-
- streamSeek(Int64.ofInt(loopPoints[0]));
- streamLoops++;
- if ((length = Int64.ofInt(loopPoints[1] - loopPoints[0]) * channels * wordSize) < (n = bufferLength - total)) n = length.low;
- }
- else {
- total += result;
- n -= result;
- wasEOF = false;
- }
- }
- catch (e:haxe.Exception) {
- trace('NativeAudioSource readToBufferData Bug! error: ${e.details()}, streamEnded: $streamEnded, total: $total, n: $n');
- return result;
- }
-
- if (result < 0) {
- trace('NativeAudioSource readToBufferData Bug! reading result is $result, streamEnded: $streamEnded, total: $total, n: $n');
- return result;
- }
- return total;
- }
-
- function fillBuffers(n:Int) {
- final max = STREAM_MAX_BUFFERS - 1;
- var i:Int, j:Int, data:ArrayBufferView, pcm:Int64, decoded:Int;
- while (n-- > 0 && !streamEnded && (decoded = readToBufferData(data = bufferDatas[i = max - requestBuffers], pcm = streamTell())) > 0) {
- j = i;
- while (i < max) {
- bufferDatas[i] = bufferDatas[++j];
- bufferTimes[i] = bufferTimes[j];
- bufferLengths[i] = bufferLengths[j];
- i = j;
- }
- bufferDatas[max] = data;
- bufferTimes[max] = getFloat(pcm) / sampleRate;
- bufferLengths[max] = decoded;
- requestBuffers++;
- }
- }
-
- inline function flushBuffers() {
- var i = STREAM_MAX_BUFFERS - (requestBuffers - queuedBuffers);
- while (queuedBuffers < STREAM_MAX_FLUSH_BUFFERS && queuedBuffers < requestBuffers) {
- AL.bufferData(buffers[nextBuffer], format, bufferDatas[i], bufferLengths[i], sampleRate);
- AL.sourceQueueBuffer(source, buffers[nextBuffer]);
- if (++nextBuffer == STREAM_MAX_FLUSH_BUFFERS) nextBuffer = 0;
- queuedBuffers++;
- i++;
- }
- }
-
- inline function skipBuffers(n:Int) {
- queuedBuffers -= (n = AL.sourceUnqueueBuffers(source, n).length);
- requestBuffers -= n;
- }
-
- function snapBuffersToTime(time:Float, force:Bool) {
- if (source == null || parent.buffer == null || parent.buffer.__srcVorbisFile == null) return;
-
- streamMutex.acquire();
-
- final sec = time / 1000;
- if (!force) {
- var bufferTime:Float;
- for (i in (STREAM_MAX_BUFFERS - requestBuffers)...(STREAM_MAX_BUFFERS - STREAM_MIN_BUFFERS))
- if (sec >= (bufferTime = bufferTimes[i]) && sec < bufferTime + (bufferLengths[i] / wordSize / channels / sampleRate))
- {
- skipBuffers(i - STREAM_MAX_BUFFERS + requestBuffers);
- AL.sourcei(source, AL.SAMPLE_OFFSET, Math.floor((sec - bufferTime) * sampleRate));
- return streamMutex.release();
- }
- }
-
- AL.sourceUnqueueBuffers(source, AL.getSourcei(source, AL.BUFFERS_QUEUED));
-
- streamEnded = false;
- streamSeek(Int64.fromFloat(sec * sampleRate));
-
- requestBuffers = queuedBuffers = streamLoops = nextBuffer = 0;
- fillBuffers(STREAM_MIN_BUFFERS);
- flushBuffers();
- streamMutex.release();
- }
-
- static function streamBuffersUpdate() {
- streamMutex.acquire();
-
- var i:Int = streamSources.length, source:NativeAudioSource, process:Int, v:Int;
- while (i-- > 0) {
- if ((source = streamSources[i]).streamRemove) continue;
- else if (source.parent.buffer == null) {
- source.stopStream();
- continue;
- }
-
- process = source.requestBuffers < STREAM_MIN_BUFFERS ? STREAM_MIN_BUFFERS - source.requestBuffers : 0;
- process = STREAM_PROCESS_BUFFERS > process ? STREAM_PROCESS_BUFFERS : process;
- if ((process = (v = STREAM_MAX_BUFFERS - source.requestBuffers) > process ? process : v) > 0) source.fillBuffers(process);
- }
-
- streamMutex.release();
- }
-
- #if !ALLOW_MULTITHREADING
- static function streamThreadRun() {
- while (Thread.readMessage(true)) streamBuffersUpdate();
- threadRunning = false;
- }
- #end
-
- static function streamUpdate() {
- if (!streamMutex.tryAcquire()) return;
-
- var i = queuedStreamSources.length, source:NativeAudioSource;
- while (i-- > 0) streamSources.push(queuedStreamSources[i]);
- queuedStreamSources.resize(0);
-
- i = streamSources.length;
- while (i-- > 0) {
- if ((source = streamSources[i]).streamRemove || source.source == null) source.removeStream();
- else {
- source.skipBuffers(AL.getSourcei(source.source, AL.BUFFERS_PROCESSED));
- source.flushBuffers();
- if (AL.getSourcei(source.source, AL.SOURCE_STATE) == AL.STOPPED) {
- AL.sourcePlay(source.source);
- source.updateCompleteTimer();
- }
- if (source.streamEnded && source.requestBuffers == source.queuedBuffers) source.removeStream();
- }
- }
-
- #if ALLOW_MULTITHREADING
- if (streamSources.length != 0) funkin.backend.utils.ThreadUtil.execAsync(streamBuffersUpdate);
- #else
- if (streamSources.length == 0) {
- if (wasEmpty) {
- wasEmpty = false;
- streamTimer.stop();
- if (threadRunning) streamThread.sendMessage(1);
- }
- else {
- wasEmpty = true;
- streamTimer = resetTimer(streamTimer, 1000, streamUpdate);
- }
- }
- else {
- wasEmpty = false;
- if (threadRunning || (threadRunning = (streamThread = Thread.create(streamThreadRun)) != null))
- streamThread.sendMessage(1);
- }
- #end
-
- streamMutex.release();
- }
-
- function removeStream() {
- streamRemove = false;
- queuedStreamSources.remove(this);
- streamSources.remove(this);
- }
-
- function stopStream() {
- streamRemove = true;
- queuedStreamSources.remove(this);
- }
-
- function resetStream() {
- streamRemove = false;
- if (!queuedStreamSources.contains(this) && !streamSources.contains(this)) {
- queuedStreamSources.push(this);
- if (streamTimer == null || !streamTimer.mRunning) streamTimer = resetTimer(streamTimer, 0, streamUpdate);
- }
- }
-
- function timer_onRun() {
- final pitch = getPitch();
- var timeRemaining = (getLength() - getCurrentTime()) / pitch;
- if (timeRemaining > 50 && AL.getSourcei(source, AL.SOURCE_STATE) == AL.PLAYING && (!streamed || !streamEnded && streamLoops <= 0)) {
- completeTimer = resetTimer(completeTimer, timeRemaining, timer_onRun);
- return;
- }
-
- completeTimer.stop();
-
- if (loops == 0) return complete();
-
- if (streamLoops > 0) {
- loops -= streamLoops;
- streamLoops = 0;
- completeTimer = resetTimer(completeTimer, (getLength() + parent.offset - loopTime) / pitch, timer_onRun);
- }
- else if (!loopPointsSupported || AL.getSourcei(source, AL.LOOPING) == AL.FALSE) {
- loops--;
- playing = true;
- setCurrentTime(loopTime - parent.offset);
- }
-
- if (loops <= 0) {
- loops = 0;
- AL.sourcei(source, AL.LOOPING, AL.FALSE);
- }
-
- parent.onLoop.dispatch();
- }
-
- function updateCompleteTimer() {
- if (playing) {
- var timeRemaining = (getLength() - getCurrentTime()) / getPitch();
- if (timeRemaining > 50) completeTimer = resetTimer(completeTimer, timeRemaining, timer_onRun);
- else {
- if (completeTimer != null) completeTimer.stop();
- if (loops > 0) play();
- else complete();
- }
- }
- else if (completeTimer != null)
- completeTimer.stop();
- }
-
- public function play() {
- if (playing || disposed) return;
- final time = completed ? 0 : getCurrentTime();
- playing = true;
- setCurrentTime(time);
- }
-
- public function pause() {
- if (!disposed) AL.sourcePause(source);
- lastTime = getCurrentTime();
- playing = false;
- stopStream();
- if (completeTimer != null) completeTimer.stop();
- }
-
- public function stop() {
- if (!disposed) AL.sourceStop(source);
- lastTime = 0;
- streamLoops = 0;
- playing = false;
- stopStream();
- if (completeTimer != null) completeTimer.stop();
- }
-
- public function complete() {
- if (!completed) parent.onComplete.dispatch();
- stop();
- completed = true;
- }
-
- public function getCurrentTime():Float {
- if (disposed) return 0.0;
- else if (completed) return getLength();
- else if (!playing) return lastTime - parent.offset;
-
- var time = AL.getSourcef(source, AL.SEC_OFFSET);
- if (streamed) {
- if (playing && streamEnded && AL.getSourcei(source, AL.SOURCE_STATE) == AL.STOPPED) {
- complete();
- return getLength();
- }
- else if (bufferTimes != null)
- time += bufferTimes[STREAM_MAX_BUFFERS - requestBuffers];
- }
- time *= 1000;
-
- var length = getRealLength();
- return if (loops <= 0 || time <= length) time - parent.offset;
- else ((time - loopTime) % (length - loopTime)) + loopTime - parent.offset;
- }
-
- public function setCurrentTime(value:Float):Float {
- if (disposed) return 0.0;
-
- final length = getRealLength();
- value = Math.isFinite(value) ? Math.max(Math.min(value + parent.offset, length), parent.offset) : parent.offset;
-
- if (streamed) AL.sourceStop(source);
- else AL.sourcef(source, AL.SEC_OFFSET, value / 1000);
-
- final timeRemaining = (length - value) / getPitch();
- if (playing) {
- if (timeRemaining < 8 && value > 8) complete();
- else {
- completed = false;
- if (streamed) {
- snapBuffersToTime(value, false);
- if (!streamEnded) resetStream();
- }
- if (AL.getSourcei(source, AL.SOURCE_STATE) != AL.PLAYING) AL.sourcePlay(source);
- completeTimer = resetTimer(completeTimer, timeRemaining, timer_onRun);
- }
- }
- else {
- completed = timeRemaining < 8;
- lastTime = value;
- if (completeTimer != null) completeTimer.stop();
- }
-
- return value;
- }
-
- public function getPitch():Float {
- return if (disposed) 1;
- else AL.getSourcef(source, AL.PITCH);
- }
-
- public function setPitch(value:Float):Float {
- if (disposed || (value = Math.max(value, 0)) == AL.getSourcef(source, AL.PITCH)) return value;
- AL.sourcef(source, AL.PITCH, value);
- updateCompleteTimer();
- return value;
- }
-
- public function getGain():Float {
- return if (disposed) 1;
- else AL.getSourcef(source, AL.GAIN);
- }
-
- public function setGain(value:Float):Float {
- value = Math.max(value, 0);
- if (!disposed) AL.sourcef(source, AL.GAIN, value);
- return value;
- }
-
- public function getLoops():Int return loops;
-
- public function setLoops(value:Int):Int {
- if (loops == (loops = value < 0 ? 0 : value)) return loops;
- updateLoopPoints();
- return loops;
- }
-
- public function getLoopTime():Float return loopTime - parent.offset;
-
- public function setLoopTime(value:Float):Float {
- if (loopTime == (loopTime = Math.max(Math.min(value + parent.offset, duration), 0))) return loopTime - parent.offset;
- if ((loopPoints[0] = Std.int(value / 1000 * sampleRate)) >= samples) loopPoints[0] = samples - 1;
- updateLoopPoints();
- return loopTime - parent.offset;
- }
-
- public function getRealLength():Float return if (endTime == null) duration; else endTime;
- public function getLength():Float return if (disposed) 0; else (inline getRealLength()) - parent.offset;
-
- public function setLength(value:Null):Null {
- if (endTime == (endTime = (value == null ? value : Math.max(Math.min(value + parent.offset, duration), 0)))) return endTime - parent.offset;
- if ((loopPoints[1] = Std.int(getRealLength() / 1000 * sampleRate)) >= samples) loopPoints[1] = samples - 1;
- updateLoopPoints();
- return endTime - parent.offset;
- }
-
- public function getLatency():Float {
- //#if (lime >= "8.4.0")
- //if (latencyExtensionSupported) {
- // final offsets = AL.getSourcedvSOFT(source, AL.SEC_OFFSET_LATENCY_SOFT, 2);
- // if (offsets != null) return offsets[1] * 1000;
- //}
- //#end
- return 0;
- }
-
- public function getAngles():Vector2 {
- if (angles == null) angles = new Vector2(Math.PI / 6, -Math.PI / 6);
- return angles;
- }
-
- public function setAngles(left:Float, right:Float):Vector2 {
- if (angles == null) angles = new Vector2(left, right);
- else angles.setTo(left, right);
-
- if (!disposed && stereoAnglesExtensionSupported) {
- anglesArray[0] = angles.x;
- anglesArray[1] = angles.y;
- AL.sourcei(source, 0x1214/*AL.SOURCE_SPATIALIZE_SOFT*/, AL.FALSE);
- AL.sourcefv(source, 0x1030/*AL.STEREO_ANGLES*/, anglesArray);
- AL.source3f(source, AL.POSITION, 0, 0, 0);
- }
- return angles;
- }
-
- public function getPosition():Vector4 {
- if (position == null) position = new Vector4();
- return position;
- }
-
- public function setPosition(value:Vector4):Vector4 {
- position.x = value.x;
- position.y = value.y;
- position.z = value.z;
- position.w = value.w;
-
- // OpenAL Soft Positions doesn't seem to do anything but panning?
- if (!disposed) {
- if (stereoAnglesExtensionSupported) AL.sourcei(source, 0x1214/*AL.SOURCE_SPATIALIZE_SOFT*/, Math.abs(position.x) > 1e-04 ? AL.TRUE : AL.FALSE);
- AL.sourcei(source, AL.MAX_DISTANCE, 1);
- AL.source3f(source, AL.POSITION, position.x, position.y, position.z);
- }
- return position;
- }
-
- public function getPan():Float return getPosition().x;
-
- public function setPan(value:Float):Float {
- getPosition().setTo(value, 0, -Math.sqrt(1 - value * value));
- if (!disposed) {
- if (parent.buffer.channels > 1)
- setAngles(Math.PI * (Math.min(-value * 2 + 1, 1)) / 6, -Math.PI * Math.min(value * 2 + 1, 1) / 6);
- else
- setPosition(position);
- }
- return value;
- }
-}
\ No newline at end of file
diff --git a/source/lime/_internal/backend/native/NativeWindow.hx b/source/lime/_internal/backend/native/NativeWindow.hx
deleted file mode 100644
index 397d94ac90..0000000000
--- a/source/lime/_internal/backend/native/NativeWindow.hx
+++ /dev/null
@@ -1,766 +0,0 @@
-package lime._internal.backend.native;
-
-import haxe.io.Bytes;
-import lime._internal.backend.native.NativeCFFI;
-import lime.app.Application;
-import lime.graphics.cairo.Cairo;
-import lime.graphics.cairo.CairoFormat;
-import lime.graphics.cairo.CairoImageSurface;
-import lime.graphics.cairo.CairoSurface;
-import lime.graphics.opengl.GL;
-import lime.graphics.CairoRenderContext;
-import lime.graphics.Image;
-import lime.graphics.ImageBuffer;
-import lime.graphics.OpenGLRenderContext;
-import lime.graphics.RenderContext;
-import lime.math.Rectangle;
-import lime.math.Vector2;
-import lime.system.Display;
-import lime.system.DisplayMode;
-import lime.system.JNI;
-import lime.system.System;
-import lime.ui.MouseCursor;
-import lime.ui.Window;
-import lime.utils.UInt8Array;
-
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(lime._internal.backend.native.NativeCFFI)
-@:access(lime._internal.backend.native.NativeOpenGLRenderContext)
-@:access(lime.app.Application)
-@:access(lime.graphics.cairo.Cairo)
-@:access(lime.graphics.opengl.GL)
-@:access(lime.graphics.OpenGLRenderContext)
-@:access(lime.graphics.RenderContext)
-@:access(lime.system.DisplayMode)
-@:access(lime.ui.Window)
-class NativeWindow
-{
- public var handle:Dynamic;
-
- private var closing:Bool;
- private var cursor:MouseCursor;
- private var displayMode:DisplayMode;
- private var frameRate:Float;
- private var mouseLock:Bool;
- private var parent:Window;
- private var useHardware:Bool;
- #if lime_cairo
- private var cacheLock:Dynamic;
- private var cairo:Cairo;
- private var primarySurface:CairoSurface;
- #end
-
- public function new(parent:Window)
- {
- this.parent = parent;
-
- cursor = DEFAULT;
- displayMode = new DisplayMode(0, 0, 0, 0);
-
- var attributes = parent.__attributes;
- var contextAttributes = Reflect.hasField(attributes, "context") ? attributes.context : {};
- var title = Reflect.hasField(attributes, "title") ? attributes.title : "Lime Application";
- var flags = 0;
-
- if (!Reflect.hasField(contextAttributes, "antialiasing")) contextAttributes.antialiasing = 0;
- if (!Reflect.hasField(contextAttributes, "background")) contextAttributes.background = 0;
- if (!Reflect.hasField(contextAttributes, "colorDepth")) contextAttributes.colorDepth = 24;
- if (!Reflect.hasField(contextAttributes, "depth")) contextAttributes.depth = true;
- if (!Reflect.hasField(contextAttributes, "hardware")) contextAttributes.hardware = true;
- if (!Reflect.hasField(contextAttributes, "stencil")) contextAttributes.stencil = true;
- if (!Reflect.hasField(contextAttributes, "vsync")) contextAttributes.vsync = false;
-
- #if (cairo || (!lime_opengl && !lime_opengles))
- contextAttributes.type = CAIRO;
- #end
- if (Reflect.hasField(contextAttributes, "type") && contextAttributes.type == CAIRO) contextAttributes.hardware = false;
-
- if (Reflect.hasField(attributes, "allowHighDPI") && attributes.allowHighDPI) flags |= cast WindowFlags.WINDOW_FLAG_ALLOW_HIGHDPI;
- if (Reflect.hasField(attributes, "alwaysOnTop") && attributes.alwaysOnTop) flags |= cast WindowFlags.WINDOW_FLAG_ALWAYS_ON_TOP;
- if (Reflect.hasField(attributes, "borderless") && attributes.borderless) flags |= cast WindowFlags.WINDOW_FLAG_BORDERLESS;
- if (Reflect.hasField(attributes, "fullscreen") && attributes.fullscreen) flags |= cast WindowFlags.WINDOW_FLAG_FULLSCREEN;
- if (Reflect.hasField(attributes, "hidden") && attributes.hidden) flags |= cast WindowFlags.WINDOW_FLAG_HIDDEN;
- if (Reflect.hasField(attributes, "maximized") && attributes.maximized) flags |= cast WindowFlags.WINDOW_FLAG_MAXIMIZED;
- if (Reflect.hasField(attributes, "minimized") && attributes.minimized) flags |= cast WindowFlags.WINDOW_FLAG_MINIMIZED;
- if (Reflect.hasField(attributes, "resizable") && attributes.resizable) flags |= cast WindowFlags.WINDOW_FLAG_RESIZABLE;
-
- if (contextAttributes.antialiasing >= 4)
- {
- flags |= cast WindowFlags.WINDOW_FLAG_HW_AA_HIRES;
- }
- else if (contextAttributes.antialiasing >= 2)
- {
- flags |= cast WindowFlags.WINDOW_FLAG_HW_AA;
- }
-
- if (contextAttributes.colorDepth == 32) flags |= cast WindowFlags.WINDOW_FLAG_COLOR_DEPTH_32_BIT;
- if (contextAttributes.depth) flags |= cast WindowFlags.WINDOW_FLAG_DEPTH_BUFFER;
- if (contextAttributes.hardware) flags |= cast WindowFlags.WINDOW_FLAG_HARDWARE;
- if (contextAttributes.stencil) flags |= cast WindowFlags.WINDOW_FLAG_STENCIL_BUFFER;
- if (contextAttributes.vsync) flags |= cast WindowFlags.WINDOW_FLAG_VSYNC;
-
- var width = Reflect.hasField(attributes, "width") ? attributes.width : #if desktop 800 #else 0 #end;
- var height = Reflect.hasField(attributes, "height") ? attributes.height : #if desktop 600 #else 0 #end;
-
- #if (!macro && lime_cffi)
- handle = NativeCFFI.lime_window_create(parent.application.__backend.handle, width, height, flags, title);
-
- #if (DARK_MODE_WINDOW && !macro)
- funkin.backend.utils.NativeAPI.setDarkMode(title, true);
- #end
-
- if (handle != null)
- {
- parent.__width = NativeCFFI.lime_window_get_width(handle);
- parent.__height = NativeCFFI.lime_window_get_height(handle);
- parent.__x = NativeCFFI.lime_window_get_x(handle);
- parent.__y = NativeCFFI.lime_window_get_y(handle);
- parent.__hidden = (Reflect.hasField(attributes, "hidden") && attributes.hidden);
- parent.id = NativeCFFI.lime_window_get_id(handle);
- }
-
- parent.__scale = NativeCFFI.lime_window_get_scale(handle);
-
- var context = new RenderContext();
- context.window = parent;
-
- #if hl
- var contextType = @:privateAccess String.fromUTF8(NativeCFFI.lime_window_get_context_type(handle));
- #else
- var contextType:String = NativeCFFI.lime_window_get_context_type(handle);
- #end
-
- switch (contextType)
- {
- case "opengl":
- var gl = new NativeOpenGLRenderContext();
-
- useHardware = true;
-
- #if lime_opengl
- context.gl = gl;
- #end
-
- context.gles2 = gl;
- context.webgl = gl;
- context.type = gl.type;
- context.version = Std.string(gl.version);
-
- if (gl.type == OPENGLES && gl.version >= 3)
- {
- context.gles3 = gl;
- context.webgl2 = gl;
- }
-
- if (GL.context == null)
- {
- GL.context = gl;
- }
-
- default:
- useHardware = false;
-
- #if lime_cairo
- context.cairo = cairo;
- context.type = CAIRO;
- context.version = "";
-
- parent.context = context;
- render();
- #end
- context.type = CAIRO;
- }
-
- contextAttributes.type = context.type;
- context.attributes = contextAttributes;
- parent.context = context;
-
- setFrameRate(Reflect.hasField(attributes, "frameRate") ? attributes.frameRate : 60);
- #end
- }
-
- public function alert(message:String, title:String):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_alert(handle, message, title);
- #end
- }
- }
-
- public function close():Void
- {
- if (!closing)
- {
- closing = true;
- parent.onClose.dispatch();
-
- if (!parent.onClose.canceled)
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_close(handle);
- #end
- handle = null;
- }
- }
- else
- {
- closing = false;
- }
- }
- }
-
- public function contextFlip():Void
- {
- #if (!macro && lime_cffi)
- if (!useHardware)
- {
- #if lime_cairo
- if (cairo != null)
- {
- primarySurface.flush();
- }
- #end
- NativeCFFI.lime_window_context_unlock(handle);
- }
-
- NativeCFFI.lime_window_context_flip(handle);
- #end
- }
-
- public function focus():Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_focus(handle);
- #end
- }
- }
-
- public function getCursor():MouseCursor
- {
- return cursor;
- }
-
- public function getDisplay():Display
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- var index = NativeCFFI.lime_window_get_display(handle);
-
- if (index > -1)
- {
- return System.getDisplay(index);
- }
- #end
- }
-
- return null;
- }
-
- public function getDisplayMode():DisplayMode
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- #if hl
- NativeCFFI.lime_window_get_display_mode(handle, displayMode);
- #else
- var data:Dynamic = NativeCFFI.lime_window_get_display_mode(handle);
- displayMode.width = data.width;
- displayMode.height = data.height;
- displayMode.pixelFormat = data.pixelFormat;
- displayMode.refreshRate = data.refreshRate;
- #end
- #end
- }
-
- return displayMode;
- }
-
- public function getFrameRate():Float
- {
- return frameRate;
- }
-
- public function getMouseLock():Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- mouseLock = NativeCFFI.lime_window_get_mouse_lock(handle);
- #end
- }
-
- return mouseLock;
- }
-
- public function getTextInputEnabled():Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- return NativeCFFI.lime_window_get_text_input_enabled(handle);
- #end
- }
-
- return false;
- }
-
- public function move(x:Int, y:Int):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_move(handle, x, y);
- #end
- }
- }
-
- public function readPixels(rect:Rectangle):Image
- {
- var imageBuffer:ImageBuffer = null;
-
- switch (parent.context.type)
- {
- case OPENGL, OPENGLES, WEBGL:
- var gl = parent.context.webgl;
- var windowWidth = Std.int(parent.__width * parent.__scale);
- var windowHeight = Std.int(parent.__height * parent.__scale);
-
- var x, y, width, height;
-
- if (rect != null)
- {
- x = Std.int(rect.x);
- y = Std.int((windowHeight - rect.y) - rect.height);
- width = Std.int(rect.width);
- height = Std.int(rect.height);
- }
- else
- {
- x = 0;
- y = 0;
- width = windowWidth;
- height = windowHeight;
- }
-
- var data = new UInt8Array(width * height * 4);
-
- gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);
-
- #if !js // TODO
-
- var rowLength = width * 4;
- var srcPosition = (height - 1) * rowLength;
- var destPosition = 0;
-
- var temp = Bytes.alloc(rowLength);
- var buffer = data.buffer;
- var rows = Std.int(height / 2);
-
- while (rows-- > 0)
- {
- temp.blit(0, buffer, destPosition, rowLength);
- buffer.blit(destPosition, buffer, srcPosition, rowLength);
- buffer.blit(srcPosition, temp, 0, rowLength);
-
- destPosition += rowLength;
- srcPosition -= rowLength;
- }
- #end
-
- imageBuffer = new ImageBuffer(data, width, height, 32, RGBA32);
-
- default:
- #if (!macro && lime_cffi)
- #if !cs
- imageBuffer = NativeCFFI.lime_window_read_pixels(handle, rect, new ImageBuffer(new UInt8Array(Bytes.alloc(0))));
- #else
- var data:Dynamic = NativeCFFI.lime_window_read_pixels(handle, rect, null);
- if (data != null)
- {
- imageBuffer = new ImageBuffer(new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b)), data.width, data.height,
- data.bitsPerPixel);
- }
- #end
- #end
-
- if (imageBuffer != null)
- {
- imageBuffer.format = RGBA32;
- }
- }
-
- if (imageBuffer != null)
- {
- return new Image(imageBuffer);
- }
-
- return null;
- }
-
- public function render():Void
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_context_make_current(handle);
-
- if (!useHardware)
- {
- #if lime_cairo
- var lock:Dynamic = NativeCFFI.lime_window_context_lock(handle);
-
- if (lock != null
- && (cacheLock == null || cacheLock.pixels != lock.pixels || cacheLock.width != lock.width || cacheLock.height != lock.height))
- {
- primarySurface = CairoImageSurface.create(lock.pixels, CairoFormat.ARGB32, lock.width, lock.height, lock.pitch);
-
- if (cairo != null)
- {
- cairo.recreate(primarySurface);
- }
- else
- {
- cairo = new Cairo(primarySurface);
- }
-
- parent.context.cairo = cairo;
- }
-
- cacheLock = lock;
- #else
- parent.context = null;
- #end
- }
- #end
- }
-
- public function resize(width:Int, height:Int):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_resize(handle, width, height);
- #end
- }
- }
-
- #if (lime >= "8.1.0")
- public function setMinSize(width:Int, height:Int):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_minimum_size(handle, width, height);
- #end
- }
- }
-
- public function setMaxSize(width:Int, height:Int):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_maximum_size(handle, width, height);
- #end
- }
- }
- #end
-
- public function setBorderless(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_borderless(handle, value);
- #end
- }
-
- return value;
- }
-
- public function setCursor(value:MouseCursor):MouseCursor
- {
- if (cursor != value)
- {
- if (value == null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_cursor(handle, 0);
- #end
- }
- else
- {
- var type:MouseCursorType = switch (value)
- {
- case ARROW: ARROW;
- case CROSSHAIR: CROSSHAIR;
- case MOVE: MOVE;
- case POINTER: POINTER;
- case RESIZE_NESW: RESIZE_NESW;
- case RESIZE_NS: RESIZE_NS;
- case RESIZE_NWSE: RESIZE_NWSE;
- case RESIZE_WE: RESIZE_WE;
- case TEXT: TEXT;
- case WAIT: WAIT;
- case WAIT_ARROW: WAIT_ARROW;
- default: DEFAULT;
- }
-
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_cursor(handle, type);
- #end
- }
-
- cursor = value;
- }
-
- return cursor;
- }
-
- public function setDisplayMode(value:DisplayMode):DisplayMode
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- #if hl
- NativeCFFI.lime_window_set_display_mode(handle, value, displayMode);
- #else
- var data:Dynamic = NativeCFFI.lime_window_set_display_mode(handle, value);
- displayMode.width = data.width;
- displayMode.height = data.height;
- displayMode.pixelFormat = data.pixelFormat;
- displayMode.refreshRate = data.refreshRate;
- #end
- #end
- }
-
- return displayMode;
- }
-
- public function setMouseLock(value:Bool):Bool
- {
- if (mouseLock != value)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_mouse_lock(handle, value);
- #end
-
- mouseLock = value;
- }
-
- return mouseLock;
- }
-
- public function setTextInputEnabled(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_text_input_enabled(handle, value);
- #end
- }
-
- return value;
- }
-
- public function setTextInputRect(value:Rectangle):Rectangle
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_text_input_rect(handle, value);
- #end
- }
-
- return value;
- }
-
- public function setFrameRate(value:Float):Float
- {
- // TODO: Support multiple independent frame rates per window
-
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_application_set_frame_rate(parent.application.__backend.handle, value);
- #end
- }
-
- return frameRate = value;
- }
-
- public function setFullscreen(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- value = NativeCFFI.lime_window_set_fullscreen(handle, value);
-
- parent.__width = NativeCFFI.lime_window_get_width(handle);
- parent.__height = NativeCFFI.lime_window_get_height(handle);
- parent.__x = NativeCFFI.lime_window_get_x(handle);
- parent.__y = NativeCFFI.lime_window_get_y(handle);
- #end
-
- if (value)
- {
- parent.onFullscreen.dispatch();
- }
- }
-
- return value;
- }
-
- public function setIcon(image:Image):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_icon(handle, image.buffer);
- #end
- }
- }
-
- public function setMaximized(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- return NativeCFFI.lime_window_set_maximized(handle, value);
- #end
- }
-
- return value;
- }
-
- public function setMinimized(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- return NativeCFFI.lime_window_set_minimized(handle, value);
- #end
- }
-
- return value;
- }
-
- public function setResizable(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_resizable(handle, value);
-
- // TODO: remove need for workaround
-
- NativeCFFI.lime_window_set_borderless(handle, !parent.__borderless);
- NativeCFFI.lime_window_set_borderless(handle, parent.__borderless);
- #end
- }
-
- return value;
- }
-
- public function setTitle(value:String):String
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- return NativeCFFI.lime_window_set_title(handle, value);
- #end
- }
-
- return value;
- }
-
- #if (lime >= "8.1.0")
- public function setVisible(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_visible(handle, value);
- #end
- }
-
- return value;
- }
-
- public function getOpacity():Float
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- return NativeCFFI.lime_window_get_opacity(handle);
- #end
- }
-
- return 1.0;
- }
-
- public function setOpacity(value:Float):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_opacity(handle, value);
- #end
- }
- }
- #end
-
- public function warpMouse(x:Int, y:Int):Void
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_warp_mouse(handle, x, y);
- #end
- }
-}
-
-enum abstract MouseCursorType(Int) from Int to Int
-{
- var HIDDEN = 0;
- var ARROW = 1;
- var CROSSHAIR = 2;
- var DEFAULT = 3;
- var MOVE = 4;
- var POINTER = 5;
- var RESIZE_NESW = 6;
- var RESIZE_NS = 7;
- var RESIZE_NWSE = 8;
- var RESIZE_WE = 9;
- var TEXT = 10;
- var WAIT = 11;
- var WAIT_ARROW = 12;
-}
-
-enum abstract WindowFlags(Int)
-{
- var WINDOW_FLAG_FULLSCREEN = 0x00000001;
- var WINDOW_FLAG_BORDERLESS = 0x00000002;
- var WINDOW_FLAG_RESIZABLE = 0x00000004;
- var WINDOW_FLAG_HARDWARE = 0x00000008;
- var WINDOW_FLAG_VSYNC = 0x00000010;
- var WINDOW_FLAG_HW_AA = 0x00000020;
- var WINDOW_FLAG_HW_AA_HIRES = 0x00000060;
- var WINDOW_FLAG_ALLOW_SHADERS = 0x00000080;
- var WINDOW_FLAG_REQUIRE_SHADERS = 0x00000100;
- var WINDOW_FLAG_DEPTH_BUFFER = 0x00000200;
- var WINDOW_FLAG_STENCIL_BUFFER = 0x00000400;
- var WINDOW_FLAG_ALLOW_HIGHDPI = 0x00000800;
- var WINDOW_FLAG_HIDDEN = 0x00001000;
- var WINDOW_FLAG_MINIMIZED = 0x00002000;
- var WINDOW_FLAG_MAXIMIZED = 0x00004000;
- var WINDOW_FLAG_ALWAYS_ON_TOP = 0x00008000;
- var WINDOW_FLAG_COLOR_DEPTH_32_BIT = 0x00010000;
-}
\ No newline at end of file
diff --git a/source/lime/media/AudioBuffer.hx b/source/lime/media/AudioBuffer.hx
deleted file mode 100644
index 30c5448baa..0000000000
--- a/source/lime/media/AudioBuffer.hx
+++ /dev/null
@@ -1,518 +0,0 @@
-package lime.media;
-
-import haxe.io.Bytes;
-import haxe.io.Path;
-import lime._internal.backend.native.NativeCFFI;
-import lime._internal.format.Base64;
-import lime.app.Future;
-import lime.app.Promise;
-import lime.media.openal.AL;
-import lime.media.openal.ALBuffer;
-#if lime_vorbis
-import lime.media.vorbis.Vorbis;
-import lime.media.vorbis.VorbisFile;
-#end
-import lime.net.HTTPRequest;
-import lime.utils.Log;
-import lime.utils.UInt8Array;
-#if lime_howlerjs
-import lime.media.howlerjs.Howl;
-#end
-#if (js && html5)
-import js.html.Audio;
-#elseif flash
-import flash.media.Sound;
-import flash.net.URLRequest;
-#end
-
-@:access(lime._internal.backend.native.NativeCFFI)
-@:access(lime.utils.Assets)
-#if hl
-@:keep
-#end
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-
-/**
- The `AudioBuffer` class represents a buffer of audio data that can be played back using an `AudioSource`.
- It supports a variety of audio formats and platforms, providing a consistent API for loading and managing audio data.
-
- Depending on the platform, the audio backend may differ, but the class provides a unified interface for accessing
- audio data, whether it's stored in memory, loaded from a file, or streamed.
-
- @see lime.media.AudioSource
-**/
-class AudioBuffer
-{
- /**
- The number of bits per sample in the audio data.
- **/
- public var bitsPerSample:Int;
-
- /**
- The number of audio channels (e.g., 1 for mono, 2 for stereo).
- **/
- public var channels:Int;
-
- /**
- The raw audio data stored as a `UInt8Array`.
- **/
- public var data:UInt8Array;
-
- /**
- The sample rate of the audio data, in Hz.
- **/
- public var sampleRate:Int;
-
- /**
- The source of the audio data. This can be an `Audio`, `Sound`, `Howl`, or other platform-specific object.
- **/
- public var src(get, set):Dynamic;
-
- @:noCompletion private var __srcAudio:#if (js && html5) Audio #else Dynamic #end;
- @:noCompletion private var __srcBuffer:#if lime_cffi ALBuffer #else Dynamic #end;
- @:noCompletion private var __srcCustom:Dynamic;
- @:noCompletion private var __srcHowl:#if lime_howlerjs Howl #else Dynamic #end;
- @:noCompletion private var __srcSound:#if flash Sound #else Dynamic #end;
- @:noCompletion private var __srcVorbisFile:#if lime_vorbis VorbisFile #else Dynamic #end;
-
- #if commonjs
- private static function __init__()
- {
- var p = untyped AudioBuffer.prototype;
- untyped Object.defineProperties(p,
- {
- "src": {get: p.get_src, set: p.set_src}
- });
- }
- #end
-
- /**
- Creates a new, empty `AudioBuffer` instance.
- **/
- public function new() {}
-
- /**
- Disposes of the resources used by this `AudioBuffer`, such as unloading any associated audio data.
- **/
- public function dispose():Void
- {
- #if (js && html5 && lime_howlerjs)
- if (__srcHowl != null) __srcHowl.unload();
- __srcHowl = null;
- #end
- #if lime_cffi
- if (__srcBuffer != null) {
- AL.bufferData(__srcBuffer, 0, null, 0, 0);
- AL.deleteBuffer(__srcBuffer);
- }
- __srcBuffer = null;
- #end
- #if lime_vorbis
- if (__srcVorbisFile != null) __srcVorbisFile.clear();
- __srcVorbisFile = null;
- #end
- }
-
- /**
- Creates an `AudioBuffer` from a Base64-encoded string.
-
- @param base64String The Base64-encoded audio data.
- @return An `AudioBuffer` instance with the decoded audio data.
- **/
- public static function fromBase64(base64String:String):AudioBuffer
- {
- if (base64String == null) return null;
-
- #if (js && html5 && lime_howlerjs)
- // if base64String doesn't contain codec data, add it.
- if (base64String.indexOf(",") == -1)
- {
- base64String = "data:" + __getCodec(Base64.decode(base64String)) + ";base64," + base64String;
- }
-
- var audioBuffer = new AudioBuffer();
- audioBuffer.src = new Howl({src: [base64String], preload: false});
- return audioBuffer;
- #elseif (lime_cffi && !macro)
- #if !cs
- // if base64String contains codec data, strip it then decode it.
- var base64StringSplit = base64String.split(",");
- var base64StringNoEncoding = base64StringSplit[base64StringSplit.length - 1];
- var bytes:Bytes = Base64.decode(base64StringNoEncoding);
- var audioBuffer = new AudioBuffer();
- audioBuffer.data = new UInt8Array(Bytes.alloc(0));
-
- return NativeCFFI.lime_audio_load_bytes(bytes, audioBuffer);
- #else
- // if base64String contains codec data, strip it then decode it.
- var base64StringSplit = base64String.split(",");
- var base64StringNoEncoding = base64StringSplit[base64StringSplit.length - 1];
- var bytes:Bytes = Base64.decode(base64StringNoEncoding);
- var data:Dynamic = NativeCFFI.lime_audio_load_bytes(bytes, null);
-
- if (data != null)
- {
- var audioBuffer = new AudioBuffer();
- audioBuffer.bitsPerSample = data.bitsPerSample;
- audioBuffer.channels = data.channels;
- audioBuffer.data = new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b));
- audioBuffer.sampleRate = data.sampleRate;
- return audioBuffer;
- }
- #end
- #end
-
- return null;
- }
-
- /**
- Creates an `AudioBuffer` from a `Bytes` object.
-
- @param bytes The `Bytes` object containing the audio data.
- @return An `AudioBuffer` instance with the decoded audio data.
- **/
- public static function fromBytes(bytes:Bytes):AudioBuffer
- {
- if (bytes == null) return null;
-
- #if (js && html5 && lime_howlerjs)
- var audioBuffer = new AudioBuffer();
- audioBuffer.src = new Howl({src: ["data:" + __getCodec(bytes) + ";base64," + Base64.encode(bytes)], preload: false});
-
- return audioBuffer;
- #elseif (lime_cffi && !macro)
- #if lime_vorbis
- var vorbisFile = VorbisFile.fromBytes(bytes);
- if (vorbisFile != null) return fromVorbisFile(vorbisFile);
- #end
- #if !cs
- var audioBuffer = new AudioBuffer();
- audioBuffer.data = new UInt8Array(Bytes.alloc(0));
-
- return NativeCFFI.lime_audio_load_bytes(bytes, audioBuffer);
- #else
- var data:Dynamic = NativeCFFI.lime_audio_load_bytes(bytes, null);
-
- if (data != null)
- {
- var audioBuffer = new AudioBuffer();
- audioBuffer.bitsPerSample = data.bitsPerSample;
- audioBuffer.channels = data.channels;
- audioBuffer.data = new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b));
- audioBuffer.sampleRate = data.sampleRate;
- return audioBuffer;
- }
- #end
- #end
-
- return null;
- }
-
- /**
- Creates an `AudioBuffer` from a file.
-
- @param path The file path to the audio data.
- @return An `AudioBuffer` instance with the audio data loaded from the file.
- **/
- public static function fromFile(path:String #if (js && html5 && lime_howlerjs), ?howlHtml5 = false #end):AudioBuffer
- {
- if (path == null) return null;
-
- #if (js && html5 && lime_howlerjs)
- var audioBuffer = new AudioBuffer();
-
- #if force_html5_audio
- audioBuffer.__srcHowl = new Howl({src: [path], html5: true, preload: false});
- #else
- audioBuffer.__srcHowl = new Howl({src: [path], html5: howlHtml5, preload: false});
- #end
-
- return audioBuffer;
- #elseif flash
- switch (Path.extension(path))
- {
- case "ogg", "wav":
- return null;
- default:
- }
-
- var audioBuffer = new AudioBuffer();
- audioBuffer.__srcSound = new Sound(new URLRequest(path));
- return audioBuffer;
- #elseif (lime_cffi && !macro)
- #if !cs
- var audioBuffer = new AudioBuffer();
- audioBuffer.data = new UInt8Array(Bytes.alloc(0));
-
- //audioBuffer = NativeCFFI.lime_audio_load_file(path, audioBuffer);
- //if (audioBuffer != null) audioBuffer.initBuffer();
- //return audioBuffer;
- return NativeCFFI.lime_audio_load_file(path, audioBuffer);
- #else
- var data:Dynamic = NativeCFFI.lime_audio_load_file(path, null);
-
- if (data != null)
- {
- var audioBuffer = new AudioBuffer();
- audioBuffer.bitsPerSample = data.bitsPerSample;
- audioBuffer.channels = data.channels;
- audioBuffer.data = new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b));
- audioBuffer.sampleRate = data.sampleRate;
- //audioBuffer.initBuffer();
- return audioBuffer;
- }
-
- return null;
- #end
- #else
- return null;
- #end
- }
-
- /**
- Creates an `AudioBuffer` from an array of file paths.
-
- @param paths An array of file paths to search for audio data.
- @return An `AudioBuffer` instance with the audio data loaded from the first valid file found.
- **/
- public static function fromFiles(paths:Array #if (js && html5 && lime_howlerjs), ?howlHtml5 = false #end):AudioBuffer
- {
- #if (js && html5 && lime_howlerjs)
- var audioBuffer = new AudioBuffer();
-
- #if force_html5_audio
- audioBuffer.__srcHowl = new Howl({src: paths, html5: true, preload: false});
- #else
- audioBuffer.__srcHowl = new Howl({src: paths, html5: howlHtml5, preload: false});
- #end
-
- return audioBuffer;
- #else
- var buffer = null;
-
- for (path in paths)
- {
- buffer = AudioBuffer.fromFile(path);
- if (buffer != null) break;
- }
-
- return buffer;
- #end
- }
-
- /**
- Creates an `AudioBuffer` from a `VorbisFile`.
-
- @param vorbisFile The `VorbisFile` object containing the audio data.
- @return An `AudioBuffer` instance with the decoded audio data.
- **/
- #if lime_vorbis
-
- public static function fromVorbisFile(vorbisFile:VorbisFile):AudioBuffer
- {
- if (vorbisFile == null) return null;
-
- var info = vorbisFile.info();
-
- var audioBuffer = new AudioBuffer();
- audioBuffer.channels = info.channels;
- audioBuffer.sampleRate = info.rate;
- audioBuffer.bitsPerSample = 16;
-
- final pcmTotal = vorbisFile.pcmTotal(-1);
- if (!vorbisFile.seekable() || pcmTotal < (audioBuffer.sampleRate << 2)) {
- vorbisFile.rawSeek(0);
-
- final isBigEndian = lime.system.System.endianness == lime.system.Endian.BIG_ENDIAN;
- final bytes = Bytes.alloc(Std.int((pcmTotal.high * 4294967296. + (pcmTotal.low >> 0)) * info.channels * (audioBuffer.bitsPerSample >> 3)));
- var total = 0, result = 0;
- do {
- result = vorbisFile.read(bytes, total, 0x1000, isBigEndian, 2, true);
- total += result;
- } while (result > 0 || result == Vorbis.HOLE);
-
- audioBuffer.data = new UInt8Array(bytes);
- vorbisFile.clear();
- }
- else
- audioBuffer.__srcVorbisFile = vorbisFile;
-
- return audioBuffer;
- }
- #else
- public static function fromVorbisFile(vorbisFile:Dynamic):AudioBuffer
- {
- return null;
- }
- #end
-
- /**
- Asynchronously loads an `AudioBuffer` from a file.
-
- @param path The file path to the audio data.
- @return A `Future` that resolves to the loaded `AudioBuffer`.
- **/
- public static function loadFromFile(path:String):Future
- {
- #if (flash || (js && html5))
- var promise = new Promise();
-
- var audioBuffer = AudioBuffer.fromFile(path);
-
- if (audioBuffer != null)
- {
- #if flash
- audioBuffer.__srcSound.addEventListener(flash.events.Event.COMPLETE, function(event)
- {
- promise.complete(audioBuffer);
- });
-
- audioBuffer.__srcSound.addEventListener(flash.events.ProgressEvent.PROGRESS, function(event)
- {
- promise.progress(Std.int(event.bytesLoaded), Std.int(event.bytesTotal));
- });
-
- audioBuffer.__srcSound.addEventListener(flash.events.IOErrorEvent.IO_ERROR, promise.error);
- #elseif (js && html5 && lime_howlerjs)
- if (audioBuffer != null)
- {
- audioBuffer.__srcHowl.on("load", function()
- {
- promise.complete(audioBuffer);
- });
-
- audioBuffer.__srcHowl.on("loaderror", function(id, msg)
- {
- promise.error(msg);
- });
-
- audioBuffer.__srcHowl.load();
- }
- #else
- promise.complete(audioBuffer);
- #end
- }
- else
- {
- promise.error(null);
- }
-
- return promise.future;
- #else
- // TODO: Streaming
-
- var request = new HTTPRequest();
- return request.load(path).then(function(buffer)
- {
- if (buffer != null)
- {
- return Future.withValue(buffer);
- }
- else
- {
- return cast Future.withError("");
- }
- });
- #end
- }
-
- /**
- Asynchronously loads an `AudioBuffer` from multiple files.
-
- @param paths An array of file paths to search for audio data.
- @return A `Future` that resolves to the loaded `AudioBuffer`.
- **/
- public static function loadFromFiles(paths:Array):Future
- {
- #if (js && html5 && lime_howlerjs)
- var promise = new Promise();
-
- var audioBuffer = AudioBuffer.fromFiles(paths);
-
- if (audioBuffer != null)
- {
- audioBuffer.__srcHowl.on("load", function()
- {
- promise.complete(audioBuffer);
- });
-
- audioBuffer.__srcHowl.on("loaderror", function()
- {
- promise.error(null);
- });
-
- audioBuffer.__srcHowl.load();
- }
- else
- {
- promise.error(null);
- }
-
- return promise.future;
- #else
- return new Future(fromFiles.bind(paths), true);
- #end
- }
-
- private static function __getCodec(bytes:Bytes):String
- {
- var signature = bytes.getString(0, 4);
-
- switch (signature)
- {
- case "OggS":
- return "audio/ogg";
- case "fLaC":
- return "audio/flac";
- case "RIFF" if (bytes.getString(8, 4) == "WAVE"):
- return "audio/wav";
- default:
- switch ([bytes.get(0), bytes.get(1), bytes.get(2)])
- {
- case [73, 68, 51] | [255, 251, _] | [255, 250, _] | [255, 243, _]: return "audio/mp3";
- default:
- }
- }
-
- Log.error("Unsupported sound format");
- return null;
- }
-
- // Get & Set Methods
- @:noCompletion private function get_src():Dynamic
- {
- #if (js && html5)
- #if lime_howlerjs
- return __srcHowl;
- #else
- return __srcAudio;
- #end
- #elseif flash
- return __srcSound;
- #elseif lime_vorbis
- return __srcVorbisFile;
- #else
- return __srcCustom;
- #end
- }
-
- @:noCompletion private function set_src(value:Dynamic):Dynamic
- {
- #if (js && html5)
- #if lime_howlerjs
- return __srcHowl = value;
- #else
- return __srcAudio = value;
- #end
- #elseif flash
- return __srcSound = value;
- #elseif lime_vorbis
- return __srcVorbisFile = value;
- #else
- return __srcCustom = value;
- #end
- }
-}
diff --git a/source/lime/media/AudioSource.hx b/source/lime/media/AudioSource.hx
deleted file mode 100644
index 992bfdb5bf..0000000000
--- a/source/lime/media/AudioSource.hx
+++ /dev/null
@@ -1,257 +0,0 @@
-package lime.media;
-
-import lime.app.Event;
-import lime.math.Vector4;
-
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-/**
- The `AudioSource` class provides a way to control audio playback in a Lime application.
- It allows for playing, pausing, and stopping audio, as well as controlling various
- audio properties such as gain, pitch, and looping.
-
- Depending on the platform, the audio backend may vary, but the API remains consistent.
-
- @see lime.media.AudioBuffer
-**/
-class AudioSource
-{
- private static var activeSources:Array = [];
-
- /**
- An event that is dispatched when the audio playback is complete.
- **/
- public var onComplete = new EventVoid>();
-
- /**
- An event that is dispatched when the audio playback looped.
- **/
- public var onLoop = new EventVoid>();
-
- /**
- The `AudioBuffer` associated with this `AudioSource`.
- **/
- public var buffer:AudioBuffer;
-
- /**
- An property if this 'AudioSource' is playing.
- **/
- public var playing(get, null):Bool;
-
- /**
- The current playback position of the audio, in milliseconds.
- **/
- public var currentTime(get, set):Float;
-
- /**
- The gain (volume) of the audio. A value of `1.0` represents the default volume.
- **/
- public var gain(get, set):Float;
-
- /**
- The length of the audio, in milliseconds.
- **/
- public var length(get, set):Null;
-
- /**
- The number of times the audio will loop. A value of `0` means the audio will not loop.
- **/
- public var loops(get, set):Int;
-
- /**
- In which audio playback time the audio will loop.
- **/
- public var loopTime(get, set):Float;
-
- /**
- The pitch of the audio. A value of `1.0` represents the default pitch.
- **/
- public var pitch(get, set):Float;
-
- /**
- The offset within the audio buffer to start playback, in samples.
- **/
- public var offset:Float;
-
- /**
- The 3D position of the audio source, represented as a `Vector4`.
- **/
- public var position(get, set):Vector4;
-
- /**
- The stereo pan of the audio source.
- **/
- public var pan(get, set):Float;
-
- /**
- The latency of the audio source.
- **/
- public var latency(get, never):Float;
-
- @:noCompletion private var __backend:AudioSourceBackend;
-
- /**
- Creates a new `AudioSource` instance.
- @param buffer The `AudioBuffer` to associate with this `AudioSource`.
- @param offset The starting offset within the audio buffer, in samples.
- @param length The length of the audio to play, in milliseconds. If `null`, the full buffer is used.
- @param loops The number of times to loop the audio. `0` means no looping.
- **/
- public function new(buffer:AudioBuffer = null, offset:Float = 0, length:Null = null, loops:Int = 0)
- {
- this.buffer = buffer;
- this.offset = offset;
-
- __backend = new AudioSourceBackend(this);
-
- if (length != null && length != 0)
- {
- this.length = length;
- }
-
- if (buffer != null)
- {
- init();
- }
-
- this.loops = loops;
- }
-
- /**
- Releases any resources used by this `AudioSource`.
- **/
- inline public function dispose():Void
- {
- __backend.dispose();
- activeSources.remove(this);
- }
-
- @:noCompletion inline private function init():Void
- {
- __backend.init();
- if (!activeSources.contains(this)) activeSources.push(this);
- }
-
- /**
- Starts or resumes audio playback.
- **/
- inline public function play():Void
- {
- __backend.play();
- }
-
- /**
- Pauses audio playback.
- **/
- inline public function pause():Void
- {
- __backend.pause();
- }
-
- /**
- Stops audio playback and resets the playback position to the beginning.
- **/
- inline public function stop():Void
- {
- __backend.stop();
- }
-
- // Get & Set Methods
- @:noCompletion inline private function get_playing():Bool
- {
- @:privateAccess return __backend.playing;
- }
-
- @:noCompletion inline private function get_currentTime():Float
- {
- return __backend.getCurrentTime();
- }
-
- @:noCompletion inline private function set_currentTime(value:Float):Float
- {
- return __backend.setCurrentTime(value);
- }
-
- @:noCompletion inline private function get_gain():Float
- {
- return __backend.getGain();
- }
-
- @:noCompletion inline private function set_gain(value:Float):Float
- {
- return __backend.setGain(value);
- }
-
- @:noCompletion inline private function get_length():Null
- {
- return __backend.getLength();
- }
-
- @:noCompletion inline private function set_length(value:Null):Null
- {
- return __backend.setLength(value);
- }
-
- @:noCompletion inline private function get_loops():Int
- {
- return __backend.getLoops();
- }
-
- @:noCompletion inline private function set_loops(value:Int):Int
- {
- return __backend.setLoops(value);
- }
-
- @:noCompletion inline private function get_loopTime():Float
- {
- return __backend.getLoopTime();
- }
-
- @:noCompletion inline private function set_loopTime(value:Float):Float
- {
- return __backend.setLoopTime(value);
- }
-
- @:noCompletion inline private function get_pitch():Float
- {
- return __backend.getPitch();
- }
-
- @:noCompletion inline private function set_pitch(value:Float):Float
- {
- return __backend.setPitch(value);
- }
-
- @:noCompletion inline private function get_position():Vector4
- {
- return __backend.getPosition();
- }
-
- @:noCompletion inline private function set_position(value:Vector4):Vector4
- {
- return __backend.setPosition(value);
- }
-
- @:noCompletion inline private function get_pan():Float
- {
- return __backend.getPan();
- }
-
- @:noCompletion inline private function set_pan(value:Float):Float
- {
- return __backend.setPan(value);
- }
-
- @:noCompletion inline private function get_latency():Float
- {
- return __backend.getLatency();
- }
-}
-
-#if (js && html5)
-@:noCompletion private typedef AudioSourceBackend = lime._internal.backend.html5.HTML5AudioSource;
-#else
-@:noCompletion private typedef AudioSourceBackend = lime._internal.backend.native.NativeAudioSource;
-#end
diff --git a/source/lime/media/vorbis/VorbisFile.hx b/source/lime/media/vorbis/VorbisFile.hx
deleted file mode 100644
index bd29a7a7f5..0000000000
--- a/source/lime/media/vorbis/VorbisFile.hx
+++ /dev/null
@@ -1,356 +0,0 @@
-package lime.media.vorbis;
-
-#if (!lime_doc_gen || lime_vorbis)
-import haxe.Int64;
-import haxe.io.Bytes;
-import lime._internal.backend.native.NativeCFFI;
-
-#if hl
-@:keep
-#end
-@:access(lime._internal.backend.native.NativeCFFI)
-class VorbisFile
-{
- public var bitstream(default, null):Int;
-
- @:noCompletion private var handle:Dynamic;
-
- @:noCompletion var _filePath:String;
- @:noCompletion var _bytes:Bytes;
-
- @:noCompletion private function new(handle:Dynamic)
- {
- this.handle = handle;
- }
-
- public function bitrate(bitstream:Int = -1):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_bitrate(handle, bitstream);
- #else
- return 0;
- #end
- }
-
- public function bitrateInstant():Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_bitrate_instant(handle);
- #else
- return 0;
- #end
- }
-
- public function clear():Void
- {
- #if (lime_cffi && lime_vorbis && !macro)
- NativeCFFI.lime_vorbis_file_clear(handle);
- #end
- handle = null;
- _filePath = null;
- _bytes = null;
- }
-
- public function clone():VorbisFile
- {
- if (_filePath != null) return fromFile(_filePath);
- if (_bytes != null) return fromBytes(_bytes);
- return null;
- }
-
- public function comment(bitstream:Int = -1):VorbisComment
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_comment(handle, bitstream);
-
- if (data != null)
- {
- var comment = new VorbisComment();
- comment.userComments = data.userComments;
- comment.vendor = data.vendor;
- return comment;
- }
- #end
-
- return null;
- }
-
- public function crosslap(other:VorbisFile):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_crosslap(handle, other.handle);
- #else
- return 0;
- #end
- }
-
- public static function fromBytes(bytes:Bytes):VorbisFile
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var handle = NativeCFFI.lime_vorbis_file_from_bytes(bytes);
-
- if (handle != null)
- {
- var vorbisFile = new VorbisFile(handle);
- vorbisFile._bytes = bytes;
- return vorbisFile;
- }
- #end
-
- return null;
- }
-
- public static function fromFile(path:String):VorbisFile
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var handle = NativeCFFI.lime_vorbis_file_from_file(path);
-
- if (handle != null)
- {
- var vorbisFile = new VorbisFile(handle);
- vorbisFile._filePath = path;
- return vorbisFile;
- }
- #end
-
- return null;
- }
-
- public function info(bitstream:Int = -1):VorbisInfo
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_info(handle, bitstream);
-
- if (data != null)
- {
- var info = new VorbisInfo();
- info.bitrateLower = data.bitrateLower;
- info.bitrateNominal = data.bitrateNominal;
- info.bitrateUpper = data.bitrateUpper;
- info.channels = data.channels;
- info.rate = data.rate;
- info.version = data.version;
- return info;
- }
- #end
-
- return null;
- }
-
- public function pcmSeek(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_pcm_seek(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function pcmSeekLap(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_pcm_seek_lap(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function pcmSeekPage(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_pcm_seek_page(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function pcmSeekPageLap(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_pcm_seek_page_lap(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function pcmTell():Int64
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_pcm_tell(handle);
-
- if (data != null)
- {
- return Int64.make(data.high, data.low);
- }
- #end
-
- return Int64.ofInt(0);
- }
-
- public function pcmTotal(bitstream:Int = -1):Int64
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_pcm_total(handle, bitstream);
-
- if (data != null)
- {
- return Int64.make(data.high, data.low);
- }
- #end
-
- return Int64.ofInt(0);
- }
-
- public function rawSeek(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_raw_seek(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function rawSeekLap(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_raw_seek_lap(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function rawTell():Int64
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_raw_tell(handle);
-
- if (data != null)
- {
- return Int64.make(data.high, data.low);
- }
- #end
-
- return Int64.ofInt(0);
- }
-
- public function rawTotal(bitstream:Int = -1):Int64
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_raw_total(handle, bitstream);
-
- if (data != null)
- {
- return Int64.make(data.high, data.low);
- }
- #end
-
- return Int64.ofInt(0);
- }
-
- public function read(buffer:Bytes, position:Int, length:Int = 4096, bigEndianPacking:Bool = false, wordSize:Int = 2, signed:Bool = true):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_read(handle, buffer, position, length, bigEndianPacking, wordSize, signed);
- if (data == null) return 0;
- bitstream = data.bitstream;
- return data.returnValue;
- #else
- return 0;
- #end
- }
-
- // public function readFilter (buffer:Bytes, length:Int = 4096, endianness:Endian = LITTLE_ENDIAN, wordSize:Int = 2, signed:Bool = true, bitstream:Int = 0, filter, filter_param
- public function readFloat(pcmChannels:Bytes, samples:Int):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_read_float(handle, pcmChannels, samples);
- if (data == null) return 0;
- bitstream = data.bitstream;
- return data.returnValue;
- #else
- return 0;
- #end
- }
-
- public function seekable():Bool
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_seekable(handle);
- #else
- return false;
- #end
- }
-
- public function serialNumber(bitstream:Int = -1):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_serial_number(handle, bitstream);
- #else
- return 0;
- #end
- }
-
- public function streams():Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_streams(handle);
- #else
- return 0;
- #end
- }
-
- public function timeSeek(s:Float):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_seek(handle, s);
- #else
- return 0;
- #end
- }
-
- public function timeSeekLap(s:Float):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_seek_lap(handle, s);
- #else
- return 0;
- #end
- }
-
- public function timeSeekPage(s:Float):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_seek_page(handle, s);
- #else
- return 0;
- #end
- }
-
- public function timeSeekPageLap(s:Float):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_seek_page_lap(handle, s);
- #else
- return 0;
- #end
- }
-
- public function timeTell():Float
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_tell(handle);
- #else
- return 0;
- #end
- }
-
- public function timeTotal(bitstream:Int = -1):Float
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_total(handle, bitstream);
- #else
- return 0;
- #end
- }
-}
-#end
diff --git a/source/lime/system/System.hx b/source/lime/system/System.hx
deleted file mode 100644
index 2184f5fe62..0000000000
--- a/source/lime/system/System.hx
+++ /dev/null
@@ -1,905 +0,0 @@
-package lime.system;
-
-import haxe.Constraints;
-import lime._internal.backend.native.NativeCFFI;
-import lime.app.Application;
-import lime.graphics.RenderContextAttributes;
-import lime.math.Rectangle;
-import lime.ui.WindowAttributes;
-import lime.utils.ArrayBuffer;
-import lime.utils.UInt8Array;
-import lime.utils.UInt16Array;
-#if flash
-import openfl.net.URLRequest;
-import openfl.system.Capabilities;
-import openfl.Lib;
-#end
-#if air
-import openfl.desktop.NativeApplication;
-#end
-#if ((js && html5) || electron)
-import js.html.Element;
-import js.Browser;
-#end
-#if sys
-import sys.io.Process;
-#end
-
-/**
- Access operating system level settings and operations.
-**/
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(lime._internal.backend.native.NativeCFFI)
-@:access(lime.system.Display)
-@:access(lime.system.DisplayMode)
-#if (cpp && windows && !HXCPP_MINGW && !lime_disable_gpu_hint)
-@:cppFileCode('
-#if defined(HX_WINDOWS)
-extern "C" {
- _declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
- _declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
-}
-#endif
-')
-#end
-class System
-{
- /**
- Determines if the screen saver is allowed to start or not.
- **/
- public static var allowScreenTimeout(get, set):Bool;
-
- /**
- The path to the directory where the application is installed, along with
- its supporting files. In many cases, this directory is read-only, and
- attempts to write to files, create new files, or delete files in this
- directory are likely fail.
- **/
- public static var applicationDirectory(get, never):String;
-
- /**
- The application's dedicated storage directory, which unique to each
- application and user. Useful for storing settings on a user-specific
- and application-specific basis.
-
- This directory may or may not be removed when the application is
- uninstalled, and it depends on the platform and installer technology
- that is used.
- **/
- public static var applicationStorageDirectory(get, never):String;
-
- /**
- The path to the directory containing the user's desktop.
- **/
- public static var desktopDirectory(get, never):String;
-
- public static var deviceModel(get, never):String;
- public static var deviceVendor(get, never):String;
- public static var disableCFFI:Bool;
-
- /**
- The path to the directory containing the user's documents.
- **/
- public static var documentsDirectory(get, never):String;
-
- /**
- The platform's default endianness for bytes.
- **/
- public static var endianness(get, never):Endian;
-
- /**
- The path to the directory where fonts are installed.
- **/
- public static var fontsDirectory(get, never):String;
-
- /**
- The number of available video displays.
- **/
- public static var numDisplays(get, never):Int;
-
- public static var platformLabel(get, never):String;
- public static var platformName(get, never):String;
- public static var platformVersion(get, never):String;
-
- /**
- The path to the user's home directory.
- **/
- public static var userDirectory(get, never):String;
-
- @:noCompletion private static var __applicationDirectory:String;
- @:noCompletion private static var __applicationEntryPoint:Map;
- @:noCompletion private static var __applicationStorageDirectory:String;
- @:noCompletion private static var __desktopDirectory:String;
- @:noCompletion private static var __deviceModel:String;
- @:noCompletion private static var __deviceVendor:String;
- @:noCompletion private static var __directories = new Map();
- @:noCompletion private static var __documentsDirectory:String;
- @:noCompletion private static var __endianness:Endian;
- @:noCompletion private static var __fontsDirectory:String;
- @:noCompletion private static var __platformLabel:String;
- @:noCompletion private static var __platformName:String;
- @:noCompletion private static var __platformVersion:String;
- @:noCompletion private static var __userDirectory:String;
-
- #if (js && html5)
- @:keep @:expose("lime.embed")
- public static function embed(projectName:String, element:Dynamic, width:Null = null, height:Null = null, config:Dynamic = null):Void
- {
- if (__applicationEntryPoint == null) return;
-
- if (__applicationEntryPoint.exists(projectName))
- {
- var htmlElement:Element = null;
-
- if ((element is String))
- {
- htmlElement = cast Browser.document.getElementById(element);
- }
- else if (element == null)
- {
- htmlElement = cast Browser.document.createElement("div");
- }
- else
- {
- htmlElement = cast element;
- }
-
- if (htmlElement == null)
- {
- Browser.window.console.log("[lime.embed] ERROR: Cannot find target element: " + element);
- return;
- }
-
- if (width == null)
- {
- width = 0;
- }
-
- if (height == null)
- {
- height = 0;
- }
-
- if (config == null) config = {};
-
- if (Reflect.hasField(config, "background") && (config.background is String))
- {
- var background = StringTools.replace(Std.string(config.background), "#", "");
-
- if (background.indexOf("0x") > -1)
- {
- config.background = Std.parseInt(background);
- }
- else
- {
- config.background = Std.parseInt("0x" + background);
- }
- }
-
- config.element = htmlElement;
- config.width = width;
- config.height = height;
-
- __applicationEntryPoint[projectName](config);
- }
- }
- #end
-
- #if (!lime_doc_gen || sys)
- /**
- Attempts to exit the application. Dispatches `onExit`, and will not
- exit if the event is canceled.
- **/
- public static function exit(code:Int):Void
- {
- var currentApp = Application.current;
- #if ((sys || (js && html5) || air) && !macro)
- if (currentApp != null)
- {
- currentApp.onExit.dispatch(code);
-
- if (currentApp.onExit.canceled)
- {
- return;
- }
- }
- #end
-
- #if sys
- Sys.exit(code);
- #elseif (js && html5)
- if (currentApp != null && currentApp.window != null)
- {
- currentApp.window.close();
- }
- #elseif air
- NativeApplication.nativeApplication.exit(code);
- #end
- }
- #end
-
- /**
- Returns information about the video display with the specified ID.
- **/
- public static function getDisplay(id:Int):Display
- {
- #if (lime_cffi && !macro)
- var displayInfo:Dynamic = NativeCFFI.lime_system_get_display(id);
-
- if (displayInfo != null)
- {
- var display = new Display();
- display.id = id;
- #if hl
- display.name = @:privateAccess String.fromUTF8(displayInfo.name);
- #else
- display.name = displayInfo.name;
- #end
- display.bounds = new Rectangle(displayInfo.bounds.x, displayInfo.bounds.y, displayInfo.bounds.width, displayInfo.bounds.height);
-
- #if ios
- var tablet = NativeCFFI.lime_system_get_ios_tablet();
- var scale = Application.current.window.scale;
- if (!tablet && scale > 2.46)
- {
- display.dpi = 401; // workaround for iPhone Plus
- }
- else
- {
- display.dpi = (tablet ? 132 : 163) * scale;
- }
- #elseif android
- var getDisplayDPI = JNI.createStaticMethod("org/haxe/lime/GameActivity", "getDisplayXDPI", "()D");
- display.dpi = Math.round(getDisplayDPI());
- #else
- display.dpi = displayInfo.dpi;
- #end
-
- display.supportedModes = [];
-
- var displayMode;
-
- #if hl
- var supportedModes:hl.NativeArray = displayInfo.supportedModes;
- #else
- var supportedModes:Array = displayInfo.supportedModes;
- #end
- for (mode in supportedModes)
- {
- displayMode = new DisplayMode(mode.width, mode.height, mode.refreshRate, mode.pixelFormat);
- display.supportedModes.push(displayMode);
- }
-
- var mode = displayInfo.currentMode;
- var currentMode = new DisplayMode(mode.width, mode.height, mode.refreshRate, mode.pixelFormat);
-
- for (mode in display.supportedModes)
- {
- if (currentMode.pixelFormat == mode.pixelFormat
- && currentMode.width == mode.width
- && currentMode.height == mode.height
- && currentMode.refreshRate == mode.refreshRate)
- {
- currentMode = mode;
- break;
- }
- }
-
- display.currentMode = currentMode;
-
- return display;
- }
- #elseif (flash || html5)
- if (id == 0)
- {
- var display = new Display();
- display.id = 0;
- display.name = "Generic Display";
-
- #if flash
- display.dpi = Capabilities.screenDPI;
- display.currentMode = new DisplayMode(Std.int(Capabilities.screenResolutionX), Std.int(Capabilities.screenResolutionY), 60, ARGB32);
- #elseif (js && html5)
- // var div = Browser.document.createElement ("div");
- // div.style.width = "1in";
- // Browser.document.body.appendChild (div);
- // var ppi = Browser.document.defaultView.getComputedStyle (div, null).getPropertyValue ("width");
- // Browser.document.body.removeChild (div);
- // display.dpi = Std.parseFloat (ppi);
- display.dpi = 96 * Browser.window.devicePixelRatio;
- display.currentMode = new DisplayMode(Browser.window.screen.width, Browser.window.screen.height, 60, ARGB32);
- #end
-
- display.supportedModes = [display.currentMode];
- display.bounds = new Rectangle(0, 0, display.currentMode.width, display.currentMode.height);
- return display;
- }
- #end
-
- return null;
- }
-
- /**
- The number of milliseconds since the application was initialized.
- **/
- public static function getTimer():Int
- {
- #if flash
- return flash.Lib.getTimer();
- #elseif ((js && !nodejs) || electron)
- return Std.int(Browser.window.performance.now());
- #elseif (lime_cffi && !macro)
- return cast NativeCFFI.lime_system_get_timer();
- #elseif cpp
- return Std.int(untyped __global__.__time_stamp() * 1000);
- #elseif sys
- return Std.int(Sys.time() * 1000);
- #else
- return 0;
- #end
- }
-
- #if (!lime_doc_gen || lime_cffi)
- public static inline function load(library:String, method:String, args:Int = 0, lazy:Bool = false):Dynamic
- {
- #if !macro
- return CFFI.load(library, method, args, lazy);
- #else
- return null;
- #end
- }
- #end
-
- /**
- Opens a file with the suste, default application.
-
- In a web browser, opens a URL with target `_blank`.
- **/
- public static function openFile(path:String):Void
- {
- if (path != null)
- {
- #if (sys && windows)
- Sys.command("start", ["", path]);
- #elseif mac
- Sys.command("/usr/bin/open", [path]);
- #elseif linux
- // generally `xdg-open` should work in every distro
- var cmd = Sys.command("xdg-open", [path, "&"]);
- // run old command JUST IN CASE it fails, which it shouldn't
- if (cmd != 0) cmd = Sys.command("/usr/bin/xdg-open", [path, "&"]);
- #elseif (js && html5)
- Browser.window.open(path, "_blank");
- #elseif flash
- Lib.getURL(new URLRequest(path), "_blank");
- #elseif android
- var openFile = JNI.createStaticMethod("org/haxe/lime/GameActivity", "openFile", "(Ljava/lang/String;)V");
- openFile(path);
- #elseif (lime_cffi && !macro)
- NativeCFFI.lime_system_open_file(path);
- #end
- }
- }
-
- /**
- Opens a URL with the specified target web browser window.
- **/
- public static function openURL(url:String, target:String = "_blank"):Void
- {
- if (url != null)
- {
- #if desktop
- openFile(url);
- #elseif (js && html5)
- Browser.window.open(url, target);
- #elseif flash
- Lib.getURL(new URLRequest(url), target);
- #elseif android
- var openURL = JNI.createStaticMethod("org/haxe/lime/GameActivity", "openURL", "(Ljava/lang/String;Ljava/lang/String;)V");
- openURL(url, target);
- #elseif (lime_cffi && !macro)
- NativeCFFI.lime_system_open_url(url, target);
- #end
- }
- }
-
- @:noCompletion private static function __copyMissingFields(target:Dynamic, source:Dynamic):Void
- {
- if (source == null || target == null) return;
-
- for (field in Reflect.fields(source))
- {
- if (!Reflect.hasField(target, field))
- {
- Reflect.setField(target, field, Reflect.field(source, field));
- }
- }
- }
-
- @:noCompletion private static function __getDirectory(type:SystemDirectory):String
- {
- #if (lime_cffi && !macro)
- if (__directories.exists(type))
- {
- return __directories.get(type);
- }
- else
- {
- var path:String;
-
- if (type == APPLICATION_STORAGE)
- {
- var company = "MyCompany";
- var file = "MyApplication";
-
- if (Application.current != null)
- {
- if (Application.current.meta.exists("company"))
- {
- company = Application.current.meta.get("company");
- }
-
- if (Application.current.meta.exists("file"))
- {
- file = Application.current.meta.get("file");
- }
- }
-
- #if hl
- path = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_directory(type, company, file));
- #else
- path = NativeCFFI.lime_system_get_directory(type, company, file);
- #end
- }
- else
- {
- #if hl
- path = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_directory(type, null, null));
- #else
- path = NativeCFFI.lime_system_get_directory(type, null, null);
- #end
- }
-
- #if windows
- var seperator = "\\";
- #else
- var seperator = "/";
- #end
-
- if (path != null && path.length > 0 && !StringTools.endsWith(path, seperator))
- {
- path += seperator;
- }
-
- __directories.set(type, path);
- return path;
- }
- #elseif flash
- if (type != FONTS && Capabilities.playerType == "Desktop")
- {
- var propertyName = switch (type)
- {
- case APPLICATION: "applicationDirectory";
- case APPLICATION_STORAGE: "applicationStorageDirectory";
- case DESKTOP: "desktopDirectory";
- case DOCUMENTS: "documentsDirectory";
- default: "userDirectory";
- }
-
- return Reflect.getProperty(Type.resolveClass("flash.filesystem.File"), propertyName).nativePath;
- }
- #end
-
- return null;
- }
-
- #if sys
- private static function __parseArguments(attributes:WindowAttributes):Void
- {
- // TODO: Handle default arguments, like --window-fps=60
-
- var arguments = Sys.args();
- var stripQuotes = ~/^['"](.*)['"]$/;
- var equals, argValue, parameters = null;
- var windowParamPrefix = "--window-";
-
- if (arguments != null)
- {
- for (argument in arguments)
- {
- equals = argument.indexOf("=");
-
- if (equals > 0)
- {
- argValue = argument.substr(equals + 1);
-
- if (stripQuotes.match(argValue))
- {
- argValue = stripQuotes.matched(1);
- }
-
- if (parameters == null) parameters = new Map();
- parameters.set(argument.substr(0, equals), argValue);
- }
- }
- }
-
- if (parameters != null)
- {
- if (attributes.parameters == null) attributes.parameters = {};
- if (attributes.context == null) attributes.context = {};
-
- for (parameter in parameters.keys())
- {
- argValue = parameters.get(parameter);
-
- if (#if lime_disable_window_override false && #end StringTools.startsWith(parameter, windowParamPrefix))
- {
- switch (parameter.substr(windowParamPrefix.length))
- {
- case "allow-high-dpi":
- attributes.allowHighDPI = __parseBool(argValue);
- case "always-on-top":
- attributes.alwaysOnTop = __parseBool(argValue);
- case "antialiasing":
- attributes.context.antialiasing = Std.parseInt(argValue);
- case "background":
- attributes.context.background = (argValue == "" || argValue == "null") ? null : Std.parseInt(argValue);
- case "borderless":
- attributes.borderless = __parseBool(argValue);
- case "colorDepth":
- attributes.context.colorDepth = Std.parseInt(argValue);
- case "depth", "depth-buffer":
- attributes.context.depth = __parseBool(argValue);
- // case "display": windowConfig.display = Std.parseInt (argValue);
- case "fullscreen":
- attributes.fullscreen = __parseBool(argValue);
- case "hardware":
- attributes.context.hardware = __parseBool(argValue);
- case "height":
- attributes.height = Std.parseInt(argValue);
- case "hidden":
- attributes.hidden = __parseBool(argValue);
- case "maximized":
- attributes.maximized = __parseBool(argValue);
- case "minimized":
- attributes.minimized = __parseBool(argValue);
- case "render-type", "renderer":
- attributes.context.type = argValue;
- case "render-version", "renderer-version":
- attributes.context.version = argValue;
- case "resizable":
- attributes.resizable = __parseBool(argValue);
- case "stencil", "stencil-buffer":
- attributes.context.stencil = __parseBool(argValue);
- // case "title": windowConfig.title = argValue;
- case "vsync":
- attributes.context.vsync = __parseBool(argValue);
- case "width":
- attributes.width = Std.parseInt(argValue);
- case "x":
- attributes.x = Std.parseInt(argValue);
- case "y":
- attributes.y = Std.parseInt(argValue);
- default:
- }
- }
- else if (!Reflect.hasField(attributes.parameters, parameter))
- {
- Reflect.setField(attributes.parameters, parameter, argValue);
- }
- }
- }
- }
- #end
-
- @:noCompletion private static inline function __parseBool(value:String):Bool
- {
- return (value == "true");
- }
-
- @:noCompletion private static function __registerEntryPoint(projectName:String, entryPoint:Function):Void
- {
- // executes first!!
- #if (sys && !macro)
- funkin.backend.system.Main.preInit();
- #end
-
- if (__applicationEntryPoint == null)
- {
- __applicationEntryPoint = new Map();
- }
-
- __applicationEntryPoint[projectName] = entryPoint;
- }
-
- @:noCompletion private static function __runProcess(command:String, args:Array = null):String
- {
- #if sys
- try
- {
- if (args == null) args = [];
-
- var process = new Process(command, args);
- var value = StringTools.trim(process.stdout.readLine().toString());
- process.close();
- return value;
- }
- catch (e:Dynamic) {}
- #end
- return null;
- }
-
- // Get & Set Methods
- private static function get_allowScreenTimeout():Bool
- {
- #if (lime_cffi && !macro)
- return NativeCFFI.lime_system_get_allow_screen_timeout();
- #else
- return true;
- #end
- }
-
- private static function set_allowScreenTimeout(value:Bool):Bool
- {
- #if (lime_cffi && !macro)
- return NativeCFFI.lime_system_set_allow_screen_timeout(value);
- #else
- return true;
- #end
- }
-
- private static function get_applicationDirectory():String
- {
- if (__applicationDirectory == null)
- {
- __applicationDirectory = __getDirectory(APPLICATION);
- }
-
- return __applicationDirectory;
- }
-
- private static function get_applicationStorageDirectory():String
- {
- if (__applicationStorageDirectory == null)
- {
- __applicationStorageDirectory = __getDirectory(APPLICATION_STORAGE);
- }
-
- return __applicationStorageDirectory;
- }
-
- private static function get_deviceModel():String
- {
- if (__deviceModel == null)
- {
- #if (lime_cffi && !macro && (windows || ios || tvos))
- #if hl
- __deviceModel = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_device_model());
- #else
- __deviceModel = NativeCFFI.lime_system_get_device_model();
- #end
- #elseif android
- var manufacturer:String = JNI.createStaticField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").get();
- var model:String = JNI.createStaticField("android/os/Build", "MODEL", "Ljava/lang/String;").get();
- if (manufacturer != null && model != null)
- {
- if (StringTools.startsWith(model.toLowerCase(), manufacturer.toLowerCase()))
- {
- model = StringTools.trim(model.substr(manufacturer.length));
- while (StringTools.startsWith(model, "-"))
- {
- model = StringTools.trim(model.substr(1));
- }
- }
- __deviceModel = model;
- }
- #elseif mac
- __deviceModel = __runProcess("sysctl", ["-n", "hw.model"]);
- #elseif linux
- __deviceModel = __runProcess("cat", ["/sys/devices/virtual/dmi/id/sys_vendor"]);
- #end
- }
-
- return __deviceModel;
- }
-
- private static function get_deviceVendor():String
- {
- if (__deviceVendor == null)
- {
- #if (lime_cffi && !macro && windows && !html5)
- #if hl
- __deviceVendor = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_device_vendor());
- #else
- __deviceVendor = NativeCFFI.lime_system_get_device_vendor();
- #end
- #elseif android
- var vendor:String = JNI.createStaticField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").get();
- if (vendor != null)
- {
- __deviceVendor = vendor.charAt(0).toUpperCase() + vendor.substr(1);
- }
- #elseif (ios || mac || tvos)
- __deviceVendor = "Apple";
- #elseif linux
- __deviceVendor = __runProcess("cat", ["/sys/devices/virtual/dmi/id/product_name"]);
- #end
- }
-
- return __deviceVendor;
- }
-
- private static function get_desktopDirectory():String
- {
- if (__desktopDirectory == null)
- {
- __desktopDirectory = __getDirectory(DESKTOP);
- }
-
- return __desktopDirectory;
- }
-
- private static function get_documentsDirectory():String
- {
- if (__documentsDirectory == null)
- {
- __documentsDirectory = __getDirectory(DOCUMENTS);
- }
-
- return __documentsDirectory;
- }
-
- private static function get_endianness():Endian
- {
- if (__endianness == null)
- {
- #if (ps3 || wiiu || flash)
- __endianness = BIG_ENDIAN;
- #else
- var arrayBuffer = new ArrayBuffer(2);
- var uint8Array = new UInt8Array(arrayBuffer);
- var uint16array = new UInt16Array(arrayBuffer);
- uint8Array[0] = 0xAA;
- uint8Array[1] = 0xBB;
- if (uint16array[0] == 0xAABB) __endianness = BIG_ENDIAN;
- else
- __endianness = LITTLE_ENDIAN;
- #end
- }
-
- return __endianness;
- }
-
- private static function get_fontsDirectory():String
- {
- if (__fontsDirectory == null)
- {
- __fontsDirectory = __getDirectory(FONTS);
- }
-
- return __fontsDirectory;
- }
-
- private static function get_numDisplays():Int
- {
- #if (lime_cffi && !macro)
- return NativeCFFI.lime_system_get_num_displays();
- #else
- return 1;
- #end
- }
-
- private static function get_platformLabel():String
- {
- if (__platformLabel == null)
- {
- #if (lime_cffi && !macro && windows && !html5)
- #if hl
- var label:String = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_platform_label());
- #else
- var label:String = NativeCFFI.lime_system_get_platform_label();
- #end
- if (label != null) __platformLabel = StringTools.trim(label);
- #elseif linux
- __platformLabel = __runProcess("lsb_release", ["-ds"]);
- #else
- var name = System.platformName;
- var version = System.platformVersion;
- if (name != null && version != null) __platformLabel = name + " " + version;
- else if (name != null) __platformLabel = name;
- #end
- }
-
- return __platformLabel;
- }
-
- private static function get_platformName():String
- {
- if (__platformName == null)
- {
- #if windows
- __platformName = "Windows";
- #elseif mac
- __platformName = "macOS";
- #elseif linux
- __platformName = __runProcess("lsb_release", ["-is"]);
- #elseif ios
- __platformName = "iOS";
- #elseif android
- __platformName = "Android";
- #elseif air
- __platformName = "AIR";
- #elseif flash
- __platformName = "Flash Player";
- #elseif tvos
- __platformName = "tvOS";
- #elseif tizen
- __platformName = "Tizen";
- #elseif blackberry
- __platformName = "BlackBerry";
- #elseif firefox
- __platformName = "Firefox";
- #elseif webos
- __platformName = "webOS";
- #elseif nodejs
- __platformName = "Node.js";
- #elseif js
- __platformName = "HTML5";
- #end
- }
-
- return __platformName;
- }
-
- private static function get_platformVersion():String
- {
- if (__platformVersion == null)
- {
- #if (lime_cffi && !macro && windows && !html5)
- #if hl
- __platformVersion = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_platform_version());
- #else
- __platformVersion = NativeCFFI.lime_system_get_platform_version();
- #end
- #elseif android
- var release = JNI.createStaticField("android/os/Build$VERSION", "RELEASE", "Ljava/lang/String;").get();
- var api = JNI.createStaticField("android/os/Build$VERSION", "SDK_INT", "I").get();
- if (release != null && api != null) __platformVersion = release + " (API " + api + ")";
- #elseif (lime_cffi && !macro && (ios || tvos))
- __platformVersion = NativeCFFI.lime_system_get_platform_version();
- #elseif mac
- __platformVersion = __runProcess("sw_vers", ["-productVersion"]);
- #elseif linux
- __platformVersion = __runProcess("lsb_release", ["-rs"]);
- #elseif flash
- __platformVersion = Capabilities.version;
- #end
- }
-
- return __platformVersion;
- }
-
- private static function get_userDirectory():String
- {
- if (__userDirectory == null)
- {
- __userDirectory = __getDirectory(USER);
- }
-
- return __userDirectory;
- }
-}
-
-#if (haxe_ver >= 4.0) enum #else @:enum #end abstract SystemDirectory(Int) from Int to Int from UInt to UInt
-{
- var APPLICATION = 0;
- var APPLICATION_STORAGE = 1;
- var DESKTOP = 2;
- var DOCUMENTS = 3;
- var FONTS = 4;
- var USER = 5;
-}
diff --git a/source/lime/ui/FileDialog.hx b/source/lime/ui/FileDialog.hx
deleted file mode 100644
index 418d711936..0000000000
--- a/source/lime/ui/FileDialog.hx
+++ /dev/null
@@ -1,411 +0,0 @@
-package lime.ui;
-
-import haxe.io.Bytes;
-import haxe.io.Path;
-import lime._internal.backend.native.NativeCFFI;
-import lime.app.Event;
-import lime.graphics.Image;
-import lime.system.BackgroundWorker;
-import lime.utils.ArrayBuffer;
-import lime.utils.Resource;
-#if hl
-import hl.Bytes as HLBytes;
-import hl.NativeArray;
-#end
-#if sys
-import sys.io.File;
-#end
-#if (js && html5)
-import js.html.Blob;
-#end
-
-/**
- Simple file dialog used for asking user where to save a file, or select files to open.
-
- Example usage:
- ```haxe
- var fileDialog = new FileDialog();
-
- fileDialog.onCancel.add( () -> trace("Canceled.") );
-
- fileDialog.onSave.add( path -> trace("File saved in " + path) );
-
- fileDialog.onOpen.add( res -> trace("Size of the file = " + (res:haxe.io.Bytes).length) );
-
- if ( fileDialog.open("jpg", null, "Load file") )
- trace("File dialog opened, waiting for selection...");
- else
- trace("This dialog is unsupported.");
- ```
-
- Availability note: most file dialog operations are only available on desktop targets, though
- `save()` is also available in HTML5.
-**/
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(lime._internal.backend.native.NativeCFFI)
-@:access(lime.graphics.Image)
-class FileDialog
-{
- /**
- Triggers when the user clicks "Cancel" during any operation, or when a function is unsupported
- (such as `open()` on HTML5).
- **/
- public var onCancel = new EventVoid>();
-
- /**
- Triggers when `open()` is successful. The `lime.utils.Resource` contains the file's data, and can
- be implicitly cast to `haxe.io.Bytes`.
- **/
- public var onOpen = new EventVoid>();
-
- /**
- Triggers when `open()` is successful. The `lime.utils.Resource` contains the file's data, and can
- be implicitly cast to `haxe.io.Bytes`, the String is the path to the file.
- **/
- public var onOpenFile = new Event<(Resource, String)->Void>(); // Added by @NeeEoo
-
- /**
- Triggers when `save()` is successful. The `String` is the path to the saved file.
- **/
- public var onSave = new EventVoid>();
-
- /**
- Triggers when `browse()` is successful and `type` is anything other than
- `FileDialogType.OPEN_MULTIPLE`. The `String` is the path to the selected file.
- **/
- public var onSelect = new EventVoid>();
-
- /**
- Triggers when `browse()` is successful and `type` is `FileDialogType.OPEN_MULTIPLE`. The
- `Array` contains all selected file paths.
- **/
- public var onSelectMultiple = new Event->Void>();
-
- public function new() {}
-
- /**
- Opens a file selection dialog. If successful, either `onSelect` or `onSelectMultiple` will trigger
- with the result(s).
-
- This function only works on desktop targets, and will return `false` otherwise.
- @param type Type of the file dialog: `OPEN`, `SAVE`, `OPEN_DIRECTORY` or `OPEN_MULTIPLE`.
- @param filter A filter to use when browsing. Asterisks are treated as wildcards. For example,
- `"*.jpg"` will match any file ending in `.jpg`.
- @param defaultPath The directory in which to start browsing and/or the default filename to
- suggest. Defaults to `Sys.getCwd()`, with no default filename.
- @param title The title to give the dialog window.
- @return Whether `browse()` is supported on this target.
- **/
- public function browse(type:FileDialogType = null, filter:String = null, defaultPath:String = null, title:String = null):Bool
- {
- if (type == null) type = FileDialogType.OPEN;
-
- #if desktop
- var worker = new BackgroundWorker();
-
- worker.doWork.add(function(_)
- {
- switch (type)
- {
- case OPEN:
- #if linux
- if (title == null) title = "Open File";
- #end
-
- var path = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath);
- if (bytes != null)
- {
- path = @:privateAccess String.fromUTF8(cast bytes);
- }
- #else
- path = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(path);
-
- case OPEN_MULTIPLE:
- #if linux
- if (title == null) title = "Open Files";
- #end
-
- var paths = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes:NativeArray = cast NativeCFFI.lime_file_dialog_open_files(title, filter, defaultPath);
- if (bytes != null)
- {
- paths = [];
- for (i in 0...bytes.length)
- {
- paths[i] = @:privateAccess String.fromUTF8(bytes[i]);
- }
- }
- #else
- paths = NativeCFFI.lime_file_dialog_open_files(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(paths);
-
- case OPEN_DIRECTORY:
- #if linux
- if (title == null) title = "Open Directory";
- #end
-
- var path = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes = NativeCFFI.lime_file_dialog_open_directory(title, filter, defaultPath);
- if (bytes != null)
- {
- path = @:privateAccess String.fromUTF8(cast bytes);
- }
- #else
- path = NativeCFFI.lime_file_dialog_open_directory(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(path);
-
- case SAVE:
- #if linux
- if (title == null) title = "Save File";
- #end
-
- var path = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath);
- if (bytes != null)
- {
- path = @:privateAccess String.fromUTF8(cast bytes);
- }
- #else
- path = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(path);
- }
- });
-
- worker.onComplete.add(function(result)
- {
- switch (type)
- {
- case OPEN, OPEN_DIRECTORY, SAVE:
- var path:String = cast result;
-
- if (path != null)
- {
- // Makes sure the filename ends with extension
- if (type == SAVE && filter != null && path.indexOf(".") == -1)
- {
- path += "." + filter;
- }
-
- onSelect.dispatch(path);
- }
- else
- {
- onCancel.dispatch();
- }
-
- case OPEN_MULTIPLE:
- var paths:Array = cast result;
-
- if (paths != null && paths.length > 0)
- {
- onSelectMultiple.dispatch(paths);
- }
- else
- {
- onCancel.dispatch();
- }
- }
- });
-
- worker.run();
-
- return true;
- #else
- onCancel.dispatch();
- return false;
- #end
- }
-
- /**
- Shows an open file dialog. If successful, `onOpen` will trigger with the file contents.
-
- This function only works on desktop targets, and will return `false` otherwise.
- @param filter A filter to use when browsing. Asterisks are treated as wildcards. For example,
- `"*.jpg"` will match any file ending in `.jpg`.
- @param defaultPath The directory in which to start browsing and/or the default filename to
- suggest. Defaults to `Sys.getCwd()`, with no default filename.
- @param title The title to give the dialog window.
- @return Whether `open()` is supported on this target.
- **/
- public function open(filter:String = null, defaultPath:String = null, title:String = null):Bool
- {
- #if (desktop && sys)
- var worker = new BackgroundWorker();
-
- worker.doWork.add(function(_)
- {
- #if linux
- if (title == null) title = "Open File";
- #end
-
- var path = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath);
- if (bytes != null) path = @:privateAccess String.fromUTF8(cast bytes);
- #else
- path = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(path);
- });
-
- worker.onComplete.add(function(path:String)
- {
- if (path != null)
- {
- try
- {
- var data = File.getBytes(path);
- onOpen.dispatch(data);
- onOpenFile.dispatch(data, path); // Added by @NeeEoo
- return;
- }
- catch (e:Dynamic) {}
- }
-
- onCancel.dispatch();
- });
-
- worker.run();
-
- return true;
- #else
- onCancel.dispatch();
- return false;
- #end
- }
-
- /**
- Shows an open file dialog. If successful, `onSave` will trigger with the selected path.
-
- This function only works on desktop and HMTL5 targets, and will return `false` otherwise.
- @param data The file contents, in `haxe.io.Bytes` format. (Implicit casting possible.)
- @param filter A filter to use when browsing. Asterisks are treated as wildcards. For example,
- `"*.jpg"` will match any file ending in `.jpg`. Used only if targeting deskop.
- @param defaultPath The directory in which to start browsing and/or the default filename to
- suggest. When targeting destkop, this defaults to `Sys.getCwd()` with no default filename. When targeting
- HTML5, this defaults to the browser's download directory, with a default filename based on the MIME type.
- @param title The title to give the dialog window.
- @param type The default MIME type of the file, in case the type can't be determined from the
- file data. Used only if targeting HTML5.
- @return Whether `save()` is supported on this target.
- **/
- public function save(data:Resource, filter:String = null, defaultPath:String = null, title:String = null, type:String = "application/octet-stream"):Bool
- {
- if (data == null)
- {
- onCancel.dispatch();
- return false;
- }
-
- #if (desktop && sys)
- var worker = new BackgroundWorker();
-
- worker.doWork.add(function(_)
- {
- #if linux
- if (title == null) title = "Save File";
- #end
-
- var path = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath);
- path = @:privateAccess String.fromUTF8(cast bytes);
- #else
- path = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(path);
- });
-
- worker.onComplete.add(function(path:String)
- {
- if (path != null)
- {
- try
- {
- File.saveBytes(path, data);
- onSave.dispatch(path);
- return;
- }
- catch (e:Dynamic) {}
- }
-
- onCancel.dispatch();
- });
-
- worker.run();
-
- return true;
- #elseif (js && html5)
- // TODO: Cleaner API for mimeType detection
-
- var defaultExtension = "";
-
- if (Image.__isPNG(data))
- {
- type = "image/png";
- defaultExtension = ".png";
- }
- else if (Image.__isJPG(data))
- {
- type = "image/jpeg";
- defaultExtension = ".jpg";
- }
- else if (Image.__isGIF(data))
- {
- type = "image/gif";
- defaultExtension = ".gif";
- }
- else if (Image.__isWebP(data))
- {
- type = "image/webp";
- defaultExtension = ".webp";
- }
-
- var path = defaultPath != null ? Path.withoutDirectory(defaultPath) : "download" + defaultExtension;
- var buffer = (data : Bytes).getData();
- buffer = buffer.slice(0, (data : Bytes).length);
-
- #if commonjs
- untyped #if haxe4 js.Syntax.code #else __js__ #end ("require ('file-saver')")(new Blob([buffer], {type: type}), path, true);
- #else
- untyped window.saveAs(new Blob([buffer], {type: type}), path, true);
- #end
- onSave.dispatch(path);
- return true;
- #else
- onCancel.dispatch();
- return false;
- #end
- }
-}
diff --git a/source/lime/utils/Log.hx b/source/lime/utils/Log.hx
index de13c0403f..a9f174cee2 100644
--- a/source/lime/utils/Log.hx
+++ b/source/lime/utils/Log.hx
@@ -20,6 +20,8 @@ class Log
{
#if js
untyped #if haxe4 js.Syntax.code #else __js__ #end ("console").debug("[" + info.className + "] " + message);
+ #elseif !macro
+ FunkinLogs.trace('[${info.className}] $message', INFO, LIGHTGRAY);
#else
println("[" + info.className + "] " + Std.string(message));
#end
@@ -53,7 +55,7 @@ class Log
if (level >= LogLevel.INFO)
{
#if !macro
- FunkinLogs.trace('[${info.className}] $message', INFO, RED);
+ FunkinLogs.trace('[${info.className}] $message', INFO, CYAN);
#else
println("[" + info.className + "] " + Std.string(message));
#end
diff --git a/source/lime/utils/ObjectPool.hx b/source/lime/utils/ObjectPool.hx
deleted file mode 100644
index f05715c35e..0000000000
--- a/source/lime/utils/ObjectPool.hx
+++ /dev/null
@@ -1,347 +0,0 @@
-package lime.utils;
-
-import haxe.ds.ObjectMap;
-
-
-/**
- A generic object pool for reusing objects.
- **/
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-#if !js @:generic #end class ObjectPool
-{
- /**
- The number of active objects in the pool.
- **/
- public var activeObjects(default, null):Int;
-
- /**
- The number of inactive objects in the pool.
- **/
- public var inactiveObjects(default, null):Int;
-
- /**
- The total size of the object pool (both active and inactive objects).
- **/
- public var size(get, set):Null;
-
- @:noCompletion private var __inactiveObject0:T;
- @:noCompletion private var __inactiveObject1:T;
- @:noCompletion private var __inactiveObjectList:List;
- @:noCompletion private var __pool:Map;
- @:noCompletion private var __size:Null;
-
- /**
- Creates a new ObjectPool instance.
-
- @param create A function that creates a new instance of type T.
- @param clean A function that cleans up an instance of type T before it is reused.
- @param size The maximum size of the object pool.
- **/
- public function new(create:Void->T = null, clean:T->Void = null, size:Null = null)
- {
- __pool = cast new ObjectMap();
-
- activeObjects = 0;
- inactiveObjects = 0;
-
- __inactiveObject0 = null;
- __inactiveObject1 = null;
- __inactiveObjectList = new List();
-
- if (create != null)
- {
- this.create = create;
- }
- if (clean != null)
- {
- this.clean = clean;
- }
- if (size != null)
- {
- this.size = size;
- }
- }
- /**
- Adds an object to the object pool.
-
- @param object The object to add to the pool.
- **/
- public function add(object:T):Void
- {
- if (object != null && !__pool.exists(object))
- {
- __pool.set(object, false);
- clean(object);
- __addInactive(object);
- }
- }
-
- /**
- Dynamic function.
-
- Cleans up an object before returning it to the pool.
-
- @param object The object to clean up.
- **/
- public dynamic function clean(object:T):Void {}
-
- /**
- Clears the object pool, removing all objects.
- **/
- public function clear():Void
- {
- __pool = cast new ObjectMap();
-
- activeObjects = 0;
- inactiveObjects = 0;
-
- __inactiveObject0 = null;
- __inactiveObject1 = null;
- __inactiveObjectList.clear();
- }
-
- /**
- Dynamic function.
-
- Creates a new Object.
- **/
- public dynamic function create():T
- {
- return null;
- }
-
- /**
- Creates a new object and adds it to the pool, or returns an existing inactive object from the pool.
-
- @return The object retrieved from the pool, or null if the pool is full and no new objects can be created.
- **/
- public function get():T
- {
- var object = null;
-
- if (inactiveObjects > 0)
- {
- object = __getInactive();
- }
- else if (__size == null || activeObjects < __size)
- {
- object = create();
-
- if (object != null)
- {
- __pool.set(object, true);
- activeObjects++;
- }
- }
-
- return object;
- }
-
- /**
- Releases an active object back into the pool.
-
- @param object The object to release.
- **/
- public function release(object:T):Void
- {
- #if lime_pool_debug
- if (object == null || !__pool.exists(object))
- {
- Log.error("Object is not a member of the pool");
- }
- else if (!__pool.get(object))
- {
- Log.error("Object has already been released");
- }
- #end
-
- activeObjects--;
-
- if (__size == null || activeObjects + inactiveObjects < __size)
- {
- clean(object);
- __addInactive(object);
- }
- else
- {
- __pool.remove(object);
- }
- }
-
- /**
- Removes an object from the pool.
-
- @param object The object to remove from the pool.
- **/
- public function remove(object:T):Void
- {
- if (object != null && __pool.exists(object))
- {
- __pool.remove(object);
-
- if (__inactiveObject0 == object)
- {
- __inactiveObject0 = null;
- inactiveObjects--;
- }
- else if (__inactiveObject1 == object)
- {
- __inactiveObject1 = null;
- inactiveObjects--;
- }
- else if (__inactiveObjectList.remove(object))
- {
- inactiveObjects--;
- }
- else
- {
- activeObjects--;
- }
- }
- }
-
- @:noCompletion private inline function __addInactive(object:T):Void
- {
- #if lime_pool_debug
- __pool.set(object, false);
- #end
-
- if (__inactiveObject0 == null)
- {
- __inactiveObject0 = object;
- }
- else if (__inactiveObject1 == null)
- {
- __inactiveObject1 = object;
- }
- else
- {
- __inactiveObjectList.add(object);
- }
-
- inactiveObjects++;
- }
-
- @:noCompletion private inline function __getInactive():T
- {
- var object = null;
-
- if (__inactiveObject0 != null)
- {
- object = __inactiveObject0;
- __inactiveObject0 = null;
- }
- else if (__inactiveObject1 != null)
- {
- object = __inactiveObject1;
- __inactiveObject1 = null;
- }
- else
- {
- object = __inactiveObjectList.pop();
-
- if (__inactiveObjectList.length > 0)
- {
- __inactiveObject0 = __inactiveObjectList.pop();
- }
-
- if (__inactiveObjectList.length > 0)
- {
- __inactiveObject1 = __inactiveObjectList.pop();
- }
- }
-
- #if lime_pool_debug
- __pool.set(object, true);
- #end
-
- inactiveObjects--;
- activeObjects++;
-
- return object;
- }
-
- @:noCompletion private function __removeInactive(count:Int):Void
- {
- if (count <= 0 || inactiveObjects == 0) return;
-
- if (__inactiveObject0 != null)
- {
- __pool.remove(__inactiveObject0);
- __inactiveObject0 = null;
- inactiveObjects--;
- count--;
- }
-
- if (count == 0 || inactiveObjects == 0) return;
-
- if (__inactiveObject1 != null)
- {
- __pool.remove(__inactiveObject1);
- __inactiveObject1 = null;
- inactiveObjects--;
- count--;
- }
-
- if (count == 0 || inactiveObjects == 0) return;
-
- for (object in __inactiveObjectList)
- {
- __pool.remove(object);
- __inactiveObjectList.remove(object);
- inactiveObjects--;
- count--;
-
- if (count == 0 || inactiveObjects == 0) return;
- }
- }
-
- // Get & Set Methods
- @:noCompletion private function get_size():Null
- {
- return __size;
- }
-
- @:noCompletion private function set_size(value:Null):Null
- {
- if (value == null)
- {
- __size = null;
- }
- else
- {
- var current = inactiveObjects + activeObjects;
- __size = value;
-
- if (current > value)
- {
- __removeInactive(current - value);
- }
- else if (value > current)
- {
- var object;
-
- for (i in 0...(value - current))
- {
- object = create();
-
- if (object != null)
- {
- __pool.set(object, false);
- __inactiveObjectList.add(object);
- inactiveObjects++;
- }
- else
- {
- break;
- }
- }
- }
- }
-
- return value;
- }
-}
diff --git a/source/openfl/display/DisplayObjectRenderer.hx b/source/openfl/display/DisplayObjectRenderer.hx
deleted file mode 100644
index 0267742463..0000000000
--- a/source/openfl/display/DisplayObjectRenderer.hx
+++ /dev/null
@@ -1,864 +0,0 @@
-package openfl.display;
-
-#if !flash
-import openfl.display._internal.Context3DGraphics;
-import openfl.display.Bitmap;
-import openfl.display.DisplayObject;
-import openfl.display.Tilemap;
-import openfl.events.EventDispatcher;
-import openfl.events.RenderEvent;
-import openfl.geom.ColorTransform;
-import openfl.geom.Matrix;
-import openfl.geom.Point;
-import openfl.geom.Rectangle;
-import openfl.text.TextField;
-#if lime
-import lime._internal.graphics.ImageCanvasUtil; // TODO
-import lime.graphics.cairo.Cairo;
-import lime.graphics.RenderContext;
-import lime.graphics.RenderContextType;
-#end
-
-#if !openfl_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(openfl.display._internal.Context3DGraphics)
-@:access(lime.graphics.ImageBuffer)
-@:access(openfl.display.Bitmap)
-@:access(openfl.display.BitmapData)
-@:access(openfl.display.DisplayObject)
-@:access(openfl.display.Graphics)
-@:access(openfl.display.Tilemap)
-@:access(openfl.display3D.Context3D)
-@:access(openfl.events.RenderEvent)
-@:access(openfl.filters.BitmapFilter)
-@:access(openfl.geom.ColorTransform)
-@:access(openfl.geom.Rectangle)
-@:access(openfl.geom.Transform)
-@:access(openfl.text.TextField)
-@:allow(openfl.display._internal)
-@:allow(openfl.display)
-@:allow(openfl.text)
-class DisplayObjectRenderer extends EventDispatcher
-{
- @:noCompletion private var __allowSmoothing:Bool;
- @:noCompletion private var __blendMode:BlendMode;
- @:noCompletion private var __cleared:Bool;
- @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __context:#if lime RenderContext #else Dynamic #end;
- @:noCompletion private var __overrideBlendMode:BlendMode;
- @:noCompletion private var __pixelRatio:Float;
- @:noCompletion private var __roundPixels:Bool;
- @:noCompletion private var __stage:Stage;
- @:noCompletion private var __tempColorTransform:ColorTransform;
- @:noCompletion private var __transparent:Bool;
- @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __type:#if lime RenderContextType #else Dynamic #end;
- @:noCompletion private var __worldAlpha:Float;
- @:noCompletion private var __worldColorTransform:ColorTransform;
- @:noCompletion private var __worldTransform:Matrix;
-
- @:noCompletion private function new()
- {
- super();
-
- __allowSmoothing = true;
- __pixelRatio = 1;
- __tempColorTransform = new ColorTransform();
- __worldAlpha = 1;
- }
-
- @:noCompletion private function __clear():Void {}
-
- @:noCompletion private function __getAlpha(value:Float):Float
- {
- return value * __worldAlpha;
- }
-
- @:noCompletion private function __getColorTransform(value:ColorTransform):ColorTransform
- {
- if (__worldColorTransform != null)
- {
- __tempColorTransform.__copyFrom(__worldColorTransform);
- __tempColorTransform.__combine(value);
- return __tempColorTransform;
- }
- else
- {
- return value;
- }
- }
-
- @:noCompletion private function __popMask():Void {}
-
- @:noCompletion private function __popMaskObject(object:DisplayObject, handleScrollRect:Bool = true):Void {}
-
- @:noCompletion private function __popMaskRect():Void {}
-
- @:noCompletion private function __pushMask(mask:DisplayObject):Void {}
-
- @:noCompletion private function __pushMaskObject(object:DisplayObject, handleScrollRect:Bool = true):Void {}
-
- @:noCompletion private function __pushMaskRect(rect:Rectangle, transform:Matrix):Void {}
-
- @:noCompletion private function __render(object:IBitmapDrawable):Void {}
-
- @:noCompletion private function __renderEvent(displayObject:DisplayObject):Void
- {
- var renderer = this;
- #if lime
- if (displayObject.__customRenderEvent != null && displayObject.__renderable)
- {
- displayObject.__customRenderEvent.allowSmoothing = renderer.__allowSmoothing;
- displayObject.__customRenderEvent.objectMatrix.copyFrom(displayObject.__renderTransform);
- displayObject.__customRenderEvent.objectColorTransform.__copyFrom(displayObject.__worldColorTransform);
- displayObject.__customRenderEvent.renderer = renderer;
-
- switch (renderer.__type)
- {
- case OPENGL:
- if (!renderer.__cleared) renderer.__clear();
-
- var renderer:OpenGLRenderer = cast renderer;
- renderer.setShader(displayObject.__worldShader);
- renderer.__context3D.__flushGL();
-
- displayObject.__customRenderEvent.type = RenderEvent.RENDER_OPENGL;
-
- case CAIRO:
- displayObject.__customRenderEvent.type = RenderEvent.RENDER_CAIRO;
-
- case DOM:
- if (displayObject.stage != null && displayObject.__worldVisible)
- {
- displayObject.__customRenderEvent.type = RenderEvent.RENDER_DOM;
- }
- else
- {
- displayObject.__customRenderEvent.type = RenderEvent.CLEAR_DOM;
- }
-
- case CANVAS:
- displayObject.__customRenderEvent.type = RenderEvent.RENDER_CANVAS;
-
- default:
- return;
- }
-
- renderer.__setBlendMode(displayObject.__worldBlendMode);
- renderer.__pushMaskObject(displayObject);
-
- displayObject.dispatchEvent(displayObject.__customRenderEvent);
-
- renderer.__popMaskObject(displayObject);
-
- if (renderer.__type == OPENGL)
- {
- var renderer:OpenGLRenderer = cast renderer;
- renderer.setViewport();
- }
- }
- #end
- }
-
- @:noCompletion private function __resize(width:Int, height:Int):Void {}
-
- @:noCompletion private function __setBlendMode(value:BlendMode):Void {}
-
- @:noCompletion private function __shouldCacheHardware(displayObject:DisplayObject, value:Null):Null
- {
- if (displayObject == null) return null;
-
- switch (displayObject.__drawableType)
- {
- case SPRITE, STAGE:
- if (value == true) return true;
- value = __shouldCacheHardware_DisplayObject(displayObject, value);
- if (value == true) return true;
-
- if (displayObject.__children != null)
- {
- for (child in displayObject.__children)
- {
- value = __shouldCacheHardware_DisplayObject(child, value);
- if (value == true) return true;
- }
- }
-
- return value;
-
- case TEXT_FIELD:
- return value == true ? true : false;
-
- case TILEMAP:
- return true;
-
- default:
- return __shouldCacheHardware_DisplayObject(displayObject, value);
- }
- }
-
- @:noCompletion private function __shouldCacheHardware_DisplayObject(displayObject:DisplayObject, value:Null):Null
- {
- if (value == true || displayObject.__filters != null) return true;
-
- if (value == false || (displayObject.__graphics != null && !Context3DGraphics.isCompatible(displayObject.__graphics)))
- {
- return false;
- }
-
- return null;
- }
-
- @:noCompletion private function __updateCacheBitmap(displayObject:DisplayObject, force:Bool):Bool
- {
- if (displayObject == null) return false;
- var renderer = this;
-
- switch (displayObject.__drawableType)
- {
- case BITMAP:
- var bitmap:Bitmap = cast displayObject;
- // TODO: Handle filters without an intermediate draw
- if (bitmap.__bitmapData == null
- || (bitmap.__filters == null #if lime && renderer.__type == OPENGL #end && bitmap.__cacheBitmap == null)) return false;
- force = (bitmap.__bitmapData.image != null && bitmap.__bitmapData.image.version != bitmap.__imageVersion);
-
- case TEXT_FIELD:
- var textField:TextField = cast displayObject;
- if (textField.__filters == null #if lime && renderer.__type == OPENGL #end && textField.__cacheBitmap == null
- && !textField.__domRender) return false;
- if (force) textField.__renderDirty = true;
- force = force || textField.__dirty;
-
- case TILEMAP:
- var tilemap:Tilemap = cast displayObject;
- if (tilemap.__filters == null #if lime && renderer.__type == OPENGL #end && tilemap.__cacheBitmap == null) return false;
-
- default:
- }
-
- #if lime
- if (displayObject.__isCacheBitmapRender) return false;
- #if openfl_disable_cacheasbitmap
- return false;
- #end
-
- var colorTransform = ColorTransform.__pool.get();
- colorTransform.__copyFrom(displayObject.__worldColorTransform);
- if (renderer.__worldColorTransform != null) colorTransform.__combine(renderer.__worldColorTransform);
- var updated = false;
-
- // TODO: Do not force cacheAsBitmap on OpenGL once Scale-9 is properly supported in Context3DShape
- if (displayObject.cacheAsBitmap
- || (renderer.__type != OPENGL && !colorTransform.__isDefault(true))
- || (renderer.__type == OPENGL && displayObject.scale9Grid != null))
- {
- var rect = null;
-
- var needRender = (displayObject.__cacheBitmap == null
- || (displayObject.__renderDirty && (force || (displayObject.__children != null && displayObject.__children.length > 0)))
- || displayObject.opaqueBackground != displayObject.__cacheBitmapBackground);
- var softwareDirty = needRender
- || (displayObject.__graphics != null && displayObject.__graphics.__softwareDirty)
- || !displayObject.__cacheBitmapColorTransform.__equals(colorTransform, true);
- var hardwareDirty = needRender || (displayObject.__graphics != null && displayObject.__graphics.__hardwareDirty);
-
- var renderType = renderer.__type;
-
- if (softwareDirty || hardwareDirty)
- {
- #if !openfl_force_gl_cacheasbitmap
- if (renderType == OPENGL)
- {
- if (#if !openfl_disable_gl_cacheasbitmap __shouldCacheHardware(displayObject, null) == false #else true #end)
- {
- #if (js && html5)
- renderType = CANVAS;
- #else
- renderType = CAIRO;
- #end
- }
- }
- #end
-
- if (softwareDirty && (renderType == CANVAS || renderType == CAIRO)) needRender = true;
- if (hardwareDirty && renderType == OPENGL) needRender = true;
- }
-
- var updateTransform = (needRender || !displayObject.__cacheBitmap.__worldTransform.equals(displayObject.__worldTransform));
- var hasFilters = #if !openfl_disable_filters displayObject.__filters != null #else false #end;
-
- #if !openfl_enable_cacheasbitmap
- if (renderer.__type == DOM && !hasFilters)
- {
- return false;
- }
- #end
-
- if (hasFilters && !needRender)
- {
- for (filter in displayObject.__filters)
- {
- if (filter.__renderDirty)
- {
- needRender = true;
- break;
- }
- }
- }
-
- if (displayObject.__cacheBitmapMatrix == null)
- {
- displayObject.__cacheBitmapMatrix = new Matrix();
- }
-
- var bitmapMatrix = (displayObject.__cacheAsBitmapMatrix != null ? displayObject.__cacheAsBitmapMatrix : displayObject.__renderTransform);
-
- if (!needRender
- && (bitmapMatrix.a != displayObject.__cacheBitmapMatrix.a
- || bitmapMatrix.b != displayObject.__cacheBitmapMatrix.b
- || bitmapMatrix.c != displayObject.__cacheBitmapMatrix.c
- || bitmapMatrix.d != displayObject.__cacheBitmapMatrix.d))
- {
- needRender = true;
- }
-
- if (!needRender
- && renderer.__type != OPENGL
- && displayObject.__cacheBitmapData != null
- && displayObject.__cacheBitmapData.image != null
- && displayObject.__cacheBitmapData.image.version < displayObject.__cacheBitmapData.__textureVersion)
- {
- needRender = true;
- }
-
- // Ensure that cached bitmap is updated after changes to scrollRect
- if (!needRender)
- {
- var current = displayObject;
- while (current != null)
- {
- if (current.scrollRect != null)
- {
- // TODO: do we need to update transform if scroll rects haven't changed?
- updateTransform = true;
- break;
- }
- current = current.parent;
- }
- }
-
- displayObject.__cacheBitmapMatrix.copyFrom(bitmapMatrix);
- displayObject.__cacheBitmapMatrix.tx = 0;
- displayObject.__cacheBitmapMatrix.ty = 0;
-
- // TODO: Handle dimensions better if object has a scrollRect?
-
- var bitmapWidth = 0, bitmapHeight = 0;
- var filterWidth = 0, filterHeight = 0;
- var offsetX = 0., offsetY = 0.;
-
- #if (openfl_disable_hdpi || openfl_disable_hdpi_cacheasbitmap)
- var pixelRatio = 1;
- #else
- var pixelRatio = __pixelRatio;
- #end
-
- if (updateTransform || needRender)
- {
- rect = Rectangle.__pool.get();
-
- displayObject.__getFilterBounds(rect, displayObject.__cacheBitmapMatrix);
-
- filterWidth = rect.width > 0 ? Math.floor(rect.width * pixelRatio) : 0;
- filterHeight = rect.height > 0 ? Math.floor(rect.height * pixelRatio) : 0;
-
- offsetX = rect.x > 0 ? Math.ceil(rect.x) : Math.floor(rect.x);
- offsetY = rect.y > 0 ? Math.ceil(rect.y) : Math.floor(rect.y);
-
- if (displayObject.__cacheBitmapData != null)
- {
- if (filterWidth > displayObject.__cacheBitmapData.width || filterHeight > displayObject.__cacheBitmapData.height)
- {
- bitmapWidth = Math.ceil(Math.max(filterWidth * 1.25, displayObject.__cacheBitmapData.width));
- bitmapHeight = Math.ceil(Math.max(filterHeight * 1.25, displayObject.__cacheBitmapData.height));
- needRender = true;
- }
- else
- {
- bitmapWidth = displayObject.__cacheBitmapData.width;
- bitmapHeight = displayObject.__cacheBitmapData.height;
- }
- }
- else
- {
- bitmapWidth = filterWidth;
- bitmapHeight = filterHeight;
- }
- }
-
- if (needRender)
- {
- updateTransform = true;
- displayObject.__cacheBitmapBackground = displayObject.opaqueBackground;
-
- if (filterWidth >= 0.5 && filterHeight >= 0.5)
- {
- var needsFill = (displayObject.opaqueBackground != null
- && (bitmapWidth != filterWidth || bitmapHeight != filterHeight));
- var fillColor = displayObject.opaqueBackground != null ? (0xFF << 24) | displayObject.opaqueBackground : 0;
- var bitmapColor = needsFill ? 0 : fillColor;
- var allowFramebuffer = (renderer.__type == OPENGL);
-
- if (displayObject.__cacheBitmapData == null
- || bitmapWidth > displayObject.__cacheBitmapData.width
- || bitmapHeight > displayObject.__cacheBitmapData.height)
- {
- displayObject.__cacheBitmapData = new BitmapData(bitmapWidth, bitmapHeight, true, bitmapColor);
-
- if (displayObject.__cacheBitmap == null) displayObject.__cacheBitmap = new Bitmap();
- displayObject.__cacheBitmap.__bitmapData = displayObject.__cacheBitmapData;
- displayObject.__cacheBitmapRenderer = null;
- }
- else
- {
- displayObject.__cacheBitmapData.__fillRect(displayObject.__cacheBitmapData.rect, bitmapColor, allowFramebuffer);
- }
-
- if (needsFill)
- {
- rect.setTo(0, 0, filterWidth, filterHeight);
- displayObject.__cacheBitmapData.__fillRect(rect, fillColor, allowFramebuffer);
- }
- }
- else
- {
- ColorTransform.__pool.release(colorTransform);
-
- displayObject.__cacheBitmap = null;
- displayObject.__cacheBitmapData = null;
- displayObject.__cacheBitmapData2 = null;
- displayObject.__cacheBitmapData3 = null;
- displayObject.__cacheBitmapRenderer = null;
-
- if (displayObject.__drawableType == TEXT_FIELD)
- {
- var textField:TextField = cast displayObject;
- if (textField.__cacheBitmap != null)
- {
- textField.__cacheBitmap.__renderTransform.tx -= textField.__offsetX * pixelRatio;
- textField.__cacheBitmap.__renderTransform.ty -= textField.__offsetY * pixelRatio;
- }
- }
-
- return true;
- }
- }
- else
- {
- // Should we retain these longer?
-
- displayObject.__cacheBitmapData = displayObject.__cacheBitmap.bitmapData;
- displayObject.__cacheBitmapData2 = null;
- displayObject.__cacheBitmapData3 = null;
- }
-
- if (updateTransform || needRender)
- {
- displayObject.__cacheBitmap.__worldTransform.copyFrom(displayObject.__worldTransform);
-
- if (bitmapMatrix == displayObject.__renderTransform)
- {
- displayObject.__cacheBitmap.__renderTransform.identity();
- displayObject.__cacheBitmap.__renderTransform.scale(1 / pixelRatio, 1 / pixelRatio);
- displayObject.__cacheBitmap.__renderTransform.tx = displayObject.__renderTransform.tx + offsetX;
- displayObject.__cacheBitmap.__renderTransform.ty = displayObject.__renderTransform.ty + offsetY;
- }
- else
- {
- displayObject.__cacheBitmap.__renderTransform.copyFrom(displayObject.__cacheBitmapMatrix);
- displayObject.__cacheBitmap.__renderTransform.invert();
- displayObject.__cacheBitmap.__renderTransform.concat(displayObject.__renderTransform);
- displayObject.__cacheBitmap.__renderTransform.a *= 1 / pixelRatio;
- displayObject.__cacheBitmap.__renderTransform.d *= 1 / pixelRatio;
- displayObject.__cacheBitmap.__renderTransform.tx += offsetX;
- displayObject.__cacheBitmap.__renderTransform.ty += offsetY;
- }
- }
-
- displayObject.__cacheBitmap.smoothing = renderer.__allowSmoothing;
- displayObject.__cacheBitmap.__renderable = displayObject.__renderable;
- displayObject.__cacheBitmap.__worldAlpha = displayObject.__worldAlpha;
- displayObject.__cacheBitmap.__worldBlendMode = displayObject.__worldBlendMode;
- displayObject.__cacheBitmap.__worldShader = displayObject.__worldShader;
- // displayObject.__cacheBitmap.__scrollRect = displayObject.__scrollRect;
- // displayObject.__cacheBitmap.filters = displayObject.filters;
- displayObject.__cacheBitmap.mask = displayObject.__mask;
-
- if (needRender)
- {
- #if lime
- if (displayObject.__cacheBitmapRenderer == null || renderType != displayObject.__cacheBitmapRenderer.__type)
- {
- if (renderType == OPENGL)
- {
- displayObject.__cacheBitmapRenderer = new OpenGLRenderer(cast(renderer, OpenGLRenderer).__context3D, displayObject.__cacheBitmapData);
- }
- else
- {
- if (displayObject.__cacheBitmapData.image == null)
- {
- var color = displayObject.opaqueBackground != null ? (0xFF << 24) | displayObject.opaqueBackground : 0;
- displayObject.__cacheBitmapData = new BitmapData(bitmapWidth, bitmapHeight, true, color);
- displayObject.__cacheBitmap.__bitmapData = displayObject.__cacheBitmapData;
- }
-
- #if (js && html5)
- ImageCanvasUtil.convertToCanvas(displayObject.__cacheBitmapData.image);
- displayObject.__cacheBitmapRenderer = new CanvasRenderer(displayObject.__cacheBitmapData.image.buffer.__srcContext);
- #else
- displayObject.__cacheBitmapRenderer = new CairoRenderer(new Cairo(displayObject.__cacheBitmapData.getSurface()));
- #end
- }
-
- displayObject.__cacheBitmapRenderer.__worldTransform = new Matrix();
- displayObject.__cacheBitmapRenderer.__worldColorTransform = new ColorTransform();
- }
- #else
- return false;
- #end
-
- if (displayObject.__cacheBitmapColorTransform == null) displayObject.__cacheBitmapColorTransform = new ColorTransform();
-
- displayObject.__cacheBitmapRenderer.__stage = displayObject.stage;
-
- displayObject.__cacheBitmapRenderer.__allowSmoothing = renderer.__allowSmoothing;
- displayObject.__cacheBitmapRenderer.__setBlendMode(NORMAL);
- displayObject.__cacheBitmapRenderer.__worldAlpha = 1 / displayObject.__worldAlpha;
-
- displayObject.__cacheBitmapRenderer.__worldTransform.copyFrom(displayObject.__renderTransform);
- displayObject.__cacheBitmapRenderer.__worldTransform.invert();
- displayObject.__cacheBitmapRenderer.__worldTransform.concat(displayObject.__cacheBitmapMatrix);
- displayObject.__cacheBitmapRenderer.__worldTransform.tx -= offsetX;
- displayObject.__cacheBitmapRenderer.__worldTransform.ty -= offsetY;
- displayObject.__cacheBitmapRenderer.__worldTransform.scale(pixelRatio, pixelRatio);
-
- displayObject.__cacheBitmapRenderer.__pixelRatio = pixelRatio;
-
- displayObject.__cacheBitmapRenderer.__worldColorTransform.__copyFrom(colorTransform);
- displayObject.__cacheBitmapRenderer.__worldColorTransform.__invert();
-
- displayObject.__isCacheBitmapRender = true;
-
- if (displayObject.__cacheBitmapRenderer.__type == OPENGL)
- {
- var parentRenderer:OpenGLRenderer = cast renderer;
- var childRenderer:OpenGLRenderer = cast displayObject.__cacheBitmapRenderer;
-
- var context = childRenderer.__context3D;
-
- var cacheRTT = context.__state.renderToTexture;
- var cacheRTTDepthStencil = context.__state.renderToTextureDepthStencil;
- var cacheRTTAntiAlias = context.__state.renderToTextureAntiAlias;
- var cacheRTTSurfaceSelector = context.__state.renderToTextureSurfaceSelector;
-
- // var cacheFramebuffer = context.__contextState.__currentGLFramebuffer;
-
- var cacheBlendMode = parentRenderer.__blendMode;
- parentRenderer.__suspendClipAndMask();
- childRenderer.__copyShader(parentRenderer);
- // childRenderer.__copyState (parentRenderer);
-
- displayObject.__cacheBitmapData.__setUVRect(context, 0, 0, filterWidth, filterHeight);
- childRenderer.__setRenderTarget(displayObject.__cacheBitmapData);
- if (displayObject.__cacheBitmapData.image != null)
- displayObject.__cacheBitmapData.__textureVersion = displayObject.__cacheBitmapData.image.version
- + 1;
-
- displayObject.__cacheBitmapData.__drawGL(displayObject, childRenderer);
-
- if (hasFilters)
- {
- var needSecondBitmapData = true;
- var needCopyOfOriginal = false;
-
- for (filter in displayObject.__filters)
- {
- // if (filter.__needSecondBitmapData) {
- // needSecondBitmapData = true;
- // }
- if (filter.__preserveObject)
- {
- needCopyOfOriginal = true;
- }
- }
-
- var bitmap = displayObject.__cacheBitmapData;
- var bitmap2 = null;
- var bitmap3 = null;
-
- // if (needSecondBitmapData) {
- if (displayObject.__cacheBitmapData2 == null
- || bitmapWidth > displayObject.__cacheBitmapData2.width
- || bitmapHeight > displayObject.__cacheBitmapData2.height)
- {
- displayObject.__cacheBitmapData2 = new BitmapData(bitmapWidth, bitmapHeight, true, 0);
- }
- else
- {
- displayObject.__cacheBitmapData2.fillRect(displayObject.__cacheBitmapData2.rect, 0);
- if (displayObject.__cacheBitmapData2.image != null)
- {
- displayObject.__cacheBitmapData2.__textureVersion = displayObject.__cacheBitmapData2.image.version + 1;
- }
- }
- displayObject.__cacheBitmapData2.__setUVRect(context, 0, 0, filterWidth, filterHeight);
- bitmap2 = displayObject.__cacheBitmapData2;
- // } else {
- // bitmap2 = bitmapData;
- // }
-
- if (needCopyOfOriginal)
- {
- if (displayObject.__cacheBitmapData3 == null
- || bitmapWidth > displayObject.__cacheBitmapData3.width
- || bitmapHeight > displayObject.__cacheBitmapData3.height)
- {
- displayObject.__cacheBitmapData3 = new BitmapData(bitmapWidth, bitmapHeight, true, 0);
- }
- else
- {
- displayObject.__cacheBitmapData3.fillRect(displayObject.__cacheBitmapData3.rect, 0);
- if (displayObject.__cacheBitmapData3.image != null)
- {
- displayObject.__cacheBitmapData3.__textureVersion = displayObject.__cacheBitmapData3.image.version + 1;
- }
- }
- displayObject.__cacheBitmapData3.__setUVRect(context, 0, 0, filterWidth, filterHeight);
- bitmap3 = displayObject.__cacheBitmapData3;
- }
-
- childRenderer.__setBlendMode(NORMAL);
- childRenderer.__worldAlpha = 1;
- childRenderer.__worldTransform.identity();
- childRenderer.__worldColorTransform.__identity();
-
- // var sourceRect = bitmap.rect;
- // if (__tempPoint == null) __tempPoint = new Point ();
- // var destPoint = __tempPoint;
- var shader, cacheBitmap;
-
- for (filter in displayObject.__filters)
- {
- if (filter.__preserveObject)
- {
- childRenderer.__setRenderTarget(bitmap3);
- childRenderer.__renderFilterPass(bitmap, childRenderer.__defaultDisplayShader, filter.__smooth);
- }
-
- for (i in 0...filter.__numShaderPasses)
- {
- shader = filter.__initShader(childRenderer, i, filter.__preserveObject ? bitmap3 : null);
- childRenderer.__setBlendMode(filter.__shaderBlendMode);
- childRenderer.__setRenderTarget(bitmap2);
- childRenderer.__renderFilterPass(bitmap, shader, filter.__smooth);
-
- cacheBitmap = bitmap;
- bitmap = bitmap2;
- bitmap2 = cacheBitmap;
- }
-
- filter.__renderDirty = false;
- }
-
- displayObject.__cacheBitmap.__bitmapData = bitmap;
- }
-
- parentRenderer.__blendMode = NORMAL;
- parentRenderer.__setBlendMode(cacheBlendMode);
- parentRenderer.__copyShader(childRenderer);
-
- if (cacheRTT != null)
- {
- context.setRenderToTexture(cacheRTT, cacheRTTDepthStencil, cacheRTTAntiAlias, cacheRTTSurfaceSelector);
- }
- else
- {
- context.setRenderToBackBuffer();
- }
-
- // context.__bindGLFramebuffer (cacheFramebuffer);
-
- // parentRenderer.__restoreState (childRenderer);
- parentRenderer.__resumeClipAndMask(childRenderer);
- parentRenderer.setViewport();
-
- displayObject.__cacheBitmapColorTransform.__copyFrom(colorTransform);
- }
- else
- {
- #if (js && html5)
- displayObject.__cacheBitmapData.__drawCanvas(displayObject, cast displayObject.__cacheBitmapRenderer);
- #else
- displayObject.__cacheBitmapData.__drawCairo(displayObject, cast displayObject.__cacheBitmapRenderer);
- #end
-
- if (hasFilters)
- {
- var needSecondBitmapData = false;
- var needCopyOfOriginal = false;
-
- for (filter in displayObject.__filters)
- {
- if (filter.__needSecondBitmapData)
- {
- needSecondBitmapData = true;
- }
- if (filter.__preserveObject)
- {
- needCopyOfOriginal = true;
- }
- }
-
- var bitmap = displayObject.__cacheBitmapData;
- var bitmap2 = null;
- var bitmap3 = null;
-
- if (needSecondBitmapData)
- {
- if (displayObject.__cacheBitmapData2 == null
- || displayObject.__cacheBitmapData2.image == null
- || bitmapWidth > displayObject.__cacheBitmapData2.width
- || bitmapHeight > displayObject.__cacheBitmapData2.height)
- {
- displayObject.__cacheBitmapData2 = new BitmapData(bitmapWidth, bitmapHeight, true, 0);
- }
- else
- {
- displayObject.__cacheBitmapData2.fillRect(displayObject.__cacheBitmapData2.rect, 0);
- }
- bitmap2 = displayObject.__cacheBitmapData2;
- }
- else
- {
- bitmap2 = bitmap;
- }
-
- if (needCopyOfOriginal)
- {
- if (displayObject.__cacheBitmapData3 == null
- || displayObject.__cacheBitmapData3.image == null
- || bitmapWidth > displayObject.__cacheBitmapData3.width
- || bitmapHeight > displayObject.__cacheBitmapData3.height)
- {
- displayObject.__cacheBitmapData3 = new BitmapData(bitmapWidth, bitmapHeight, true, 0);
- }
- else
- {
- displayObject.__cacheBitmapData3.fillRect(displayObject.__cacheBitmapData3.rect, 0);
- }
- bitmap3 = displayObject.__cacheBitmapData3;
- }
-
- if (displayObject.__tempPoint == null) displayObject.__tempPoint = new Point();
- var destPoint = displayObject.__tempPoint;
- var cacheBitmap, lastBitmap;
-
- for (filter in displayObject.__filters)
- {
- if (filter.__preserveObject)
- {
- bitmap3.copyPixels(bitmap, bitmap.rect, destPoint);
- }
-
- lastBitmap = filter.__applyFilter(bitmap2, bitmap, bitmap.rect, destPoint);
-
- if (filter.__preserveObject)
- {
- lastBitmap.draw(bitmap3, null,
- displayObject.__objectTransform != null ? displayObject.__objectTransform.__colorTransform : null);
- }
- filter.__renderDirty = false;
-
- if (needSecondBitmapData && lastBitmap == bitmap2)
- {
- cacheBitmap = bitmap;
- bitmap = bitmap2;
- bitmap2 = cacheBitmap;
- }
- }
-
- if (displayObject.__cacheBitmapData != bitmap)
- {
- // TODO: Fix issue with swapping __cacheBitmap.__bitmapData
- // __cacheBitmapData.copyPixels (bitmap, bitmap.rect, destPoint);
-
- // Adding __cacheBitmapRenderer = null; makes this work
- cacheBitmap = displayObject.__cacheBitmapData;
- displayObject.__cacheBitmapData = bitmap;
- displayObject.__cacheBitmapData2 = cacheBitmap;
- displayObject.__cacheBitmap.__bitmapData = displayObject.__cacheBitmapData;
- displayObject.__cacheBitmapRenderer = null;
- }
-
- displayObject.__cacheBitmap.__imageVersion = displayObject.__cacheBitmapData.__textureVersion;
- }
-
- displayObject.__cacheBitmapColorTransform.__copyFrom(colorTransform);
-
- if (!displayObject.__cacheBitmapColorTransform.__isDefault(true))
- {
- displayObject.__cacheBitmapColorTransform.alphaMultiplier = 1;
- displayObject.__cacheBitmapData.colorTransform(displayObject.__cacheBitmapData.rect, displayObject.__cacheBitmapColorTransform);
- }
- }
-
- displayObject.__isCacheBitmapRender = false;
- }
-
- if (updateTransform || needRender)
- {
- Rectangle.__pool.release(rect);
- }
-
- updated = updateTransform;
- }
- else if (displayObject.__cacheBitmap != null)
- {
- if (renderer.__type == DOM)
- {
- var domRenderer:DOMRenderer = cast renderer;
- domRenderer.__renderDrawableClear(displayObject.__cacheBitmap);
- }
-
- displayObject.__cacheBitmap = null;
- displayObject.__cacheBitmapData = null;
- displayObject.__cacheBitmapData2 = null;
- displayObject.__cacheBitmapData3 = null;
- displayObject.__cacheBitmapColorTransform = null;
- displayObject.__cacheBitmapRenderer = null;
-
- updated = true;
- }
-
- ColorTransform.__pool.release(colorTransform);
-
- if (updated && displayObject.__drawableType == TEXT_FIELD)
- {
- var textField:TextField = cast displayObject;
- if (textField.__cacheBitmap != null)
- {
- textField.__cacheBitmap.__renderTransform.tx -= textField.__offsetX;
- textField.__cacheBitmap.__renderTransform.ty -= textField.__offsetY;
- }
- }
-
- return updated;
- #else
- return false;
- #end
- }
-}
-#else
-typedef DisplayObjectRenderer = Dynamic;
-#end
diff --git a/source/openfl/display/GraphicsShader.hx b/source/openfl/display/GraphicsShader.hx
deleted file mode 100644
index 72c0ed9776..0000000000
--- a/source/openfl/display/GraphicsShader.hx
+++ /dev/null
@@ -1,89 +0,0 @@
-package openfl.display;
-
-import openfl.utils.ByteArray;
-
-#if !openfl_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-class GraphicsShader extends Shader
-{
- @:glVertexHeader("attribute float openfl_Alpha;
-attribute vec4 openfl_ColorMultiplier;
-attribute vec4 openfl_ColorOffset;
-attribute vec4 openfl_Position;
-attribute vec2 openfl_TextureCoord;
-
-varying float openfl_Alphav;
-varying vec4 openfl_ColorMultiplierv;
-varying vec4 openfl_ColorOffsetv;
-varying vec2 openfl_TextureCoordv;
-
-uniform mat4 openfl_Matrix;
-uniform bool openfl_HasColorTransform;
-uniform vec2 openfl_TextureSize;")
- @:glVertexBody("openfl_Alphav = openfl_Alpha;
-openfl_TextureCoordv = openfl_TextureCoord;
-
-if (openfl_HasColorTransform) {
- openfl_ColorMultiplierv = openfl_ColorMultiplier;
- openfl_ColorOffsetv = openfl_ColorOffset / 255.0;
-}
-
-gl_Position = openfl_Matrix * openfl_Position;")
- @:glVertexSource("#pragma header
-
-void main(void) {
- #pragma body
-}")
- @:glFragmentHeader("varying float openfl_Alphav;
-varying vec4 openfl_ColorMultiplierv;
-varying vec4 openfl_ColorOffsetv;
-varying vec2 openfl_TextureCoordv;
-
-uniform bool openfl_HasColorTransform;
-uniform vec2 openfl_TextureSize;
-uniform sampler2D bitmap;")
- @:glFragmentBody("vec4 color = texture2D (bitmap, openfl_TextureCoordv);
-
-if (color.a == 0.0) {
- gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
-} else if (openfl_HasColorTransform) {
- color = vec4(color.rgb / color.a, color.a);
-
- mat4 colorMultiplier = mat4(0);
- colorMultiplier[0][0] = openfl_ColorMultiplierv.x;
- colorMultiplier[1][1] = openfl_ColorMultiplierv.y;
- colorMultiplier[2][2] = openfl_ColorMultiplierv.z;
- colorMultiplier[3][3] = 1.0; // openfl_ColorMultiplierv.w;
-
- color = clamp(openfl_ColorOffsetv + (color * colorMultiplier), 0.0, 1.0);
-
- if (color.a > 0.0) {
- gl_FragColor = vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
- } else {
- gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
- }
-} else {
- gl_FragColor = color * openfl_Alphav;
-}")
- #if emscripten
- @:glFragmentSource("#pragma header
-
-void main(void) {
- #pragma body
-
- gl_FragColor = gl_FragColor.bgra;
-}")
- #else
- @:glFragmentSource("#pragma header
-
-void main(void) {
- #pragma body
-}")
- #end
- public function new(code:ByteArray = null)
- {
- super(code);
- }
-}
diff --git a/source/openfl/display3D/Context3D.hx b/source/openfl/display3D/Context3D.hx
deleted file mode 100644
index 8443824dcb..0000000000
--- a/source/openfl/display3D/Context3D.hx
+++ /dev/null
@@ -1,2718 +0,0 @@
-package openfl.display3D;
-
-#if !flash
-import openfl.display3D.utils.UInt8Buff;
-import openfl.display3D._internal.Context3DState;
-import openfl.display3D._internal.GLBuffer;
-import openfl.display3D._internal.GLFramebuffer;
-import openfl.display3D._internal.GLTexture;
-import openfl.display._internal.SamplerState;
-import openfl.display3D.textures.CubeTexture;
-import openfl.display3D.textures.RectangleTexture;
-import openfl.display3D.textures.TextureBase;
-import openfl.display3D.textures.Texture;
-import openfl.display3D.textures.VideoTexture;
-import openfl.display.BitmapData;
-import openfl.display.Stage;
-import openfl.display.Stage3D;
-import openfl.errors.Error;
-import openfl.errors.IllegalOperationError;
-import openfl.events.EventDispatcher;
-import openfl.geom.Matrix3D;
-import openfl.geom.Point;
-import openfl.geom.Rectangle;
-import openfl.utils._internal.Float32Array;
-import openfl.utils._internal.UInt16Array;
-import openfl.utils._internal.UInt8Array;
-import openfl.utils.AGALMiniAssembler;
-import openfl.utils.ByteArray;
-#if lime
-import lime.graphics.opengl.GL;
-import lime.graphics.Image;
-import lime.graphics.ImageBuffer;
-import lime.graphics.RenderContext;
-import lime.graphics.WebGLRenderContext;
-import lime.math.Rectangle as LimeRectangle;
-import lime.math.Vector2;
-#end
-
-/**
- The Context3D class provides a context for rendering geometrically defined graphics.
- A rendering context includes a drawing surface and its associated resources and
- state. When possible, the rendering context uses the hardware graphics processing
- unit (GPU). Otherwise, the rendering context uses software. (If rendering through
- Context3D is not supported on a platform, the stage3Ds property of the Stage object
- contains an empty list.)
-
- The Context3D rendering context is a programmable pipeline that is very similar to
- OpenGL ES 2, but is abstracted so that it is compatible with a range of hardware and
- GPU interfaces. Although designed for 3D graphics, the rendering pipeline does not
- mandate that the rendering is three dimensional. Thus, you can create a 2D renderer
- by supplying the appropriate vertex and pixel fragment programs. In both the 3D and
- 2D cases, the only geometric primitive supported is the triangle.
-
- Get an instance of the Context3D class by calling the requestContext3D() method of a
- Stage3D object. A limited number of Context3D objects can exist per stage; one for
- each Stage3D in the Stage.stage3Ds list. When the context is created, the Stage3D
- object dispatches a context3DCreate event. A rendering context can be destroyed and
- recreated at any time, such as when another application that uses the GPU gains
- focus. Your code should anticipate receiving multiple context3DCreate events.
- Position the rendering area on the stage using the x and y properties of the
- associated Stage3D instance.
-
- To render and display a scene (after getting a Context3D object), the following steps
- are typical:
-
- 1. Configure the main display buffer attributes by calling `configureBackBuffer()`.
- 2. Create and initialize your rendering resources, including:
- * Vertex and index buffers defining the scene geometry
- * Vertex and pixel programs (shaders) for rendering the scene
- * Textures
- 3. Render a frame:
- * Set the render state as appropriate for an object or collection of objects in
- the scene.
- * Call the `drawTriangles()` method to render a set of triangles.
- * Change the rendering state for the next group of objects.
- * Call `drawTriangles()` to draw the triangles defining the objects.
- * Repeat until the scene is entirely rendered.
- * Call the `present()` method to display the rendered scene on the stage.
-
- The following limits apply to rendering:
-
- Resource limits:
-
- | Resource | Number allowed | Total memory |
- | --- | --- | --- |
- | Vertex buffers | 4096 | 256 MB |
- | Index buffers | 4096 | 128 MB |
- | Programs | 4096 | 16 MB |
- | Textures | 4096 | 128 MB |
- | Cube textures | 4096 | 256 MB |
-
- AGAL limits: 200 opcodes per program.
-
- Draw call limits: 32,768 `drawTriangles()` calls for each `present()` call.
-
- The following limits apply to textures:
-
- Texture limits for AIR 32 bit:
-
- | Texture | Maximum size | Total GPU memory |
- | --- | --- | --- |
- | Normal Texture (below Baseline extended) | 2048x2048 | 512 MB |
- | Normal Texture (Baseline extended and above) | 4096x4096 | 512 MB |
- | Rectangular Texture (below Baseline extended) | 2048x2048 | 512 MB |
- | Rectangular Texture (Baseline extended and above) | 4096x4096 | 512 MB |
- | Cube Texture | 1024x1024 | 256 MB |
-
- Texture limits for AIR 64 bit (Desktop):
-
- | Texture | Maximum size | Total GPU memory |
- | --- | --- | --- |
- | Normal Texture (below Baseline extended) | 2048x2048 | 512 MB |
- | Normal Texture (Baseline extended to Standard) | 4096x4096 | 512 MB |
- | Normal Texture (Standard extended and above) | 4096x4096 | 2048 MB |
- | Rectangular Texture (below Baseline extended) | 2048x2048 | 512 MB |
- | Rectangular Texture (Baseline extended to Standard) | 4096x4096 | 512 MB |
- | Rectangular Texture (Standard extended and above) | 4096x4096 | 2048 MB |
- | Cube Texture | 1024x1024 | 256 MB |
-
- 512 MB is the absolute limit for textures, including the texture memory required
- for mipmaps. However, for Cube Textures, the memory limit is 256 MB.
-
- You cannot create Context3D objects with the Context3D constructor. It is
- constructed and available as a property of a Stage3D instance. The Context3D class
- can be used on both desktop and mobile platforms, both when running in Flash Player
- and AIR.
-**/
-#if !openfl_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(openfl.display3D._internal.Context3DState)
-@:access(openfl.display3D.textures.CubeTexture)
-@:access(openfl.display3D.textures.RectangleTexture)
-@:access(openfl.display3D.textures.TextureBase)
-@:access(openfl.display3D.textures.Texture)
-@:access(openfl.display3D.textures.VideoTexture)
-@:access(openfl.display3D.IndexBuffer3D)
-@:access(openfl.display3D.Program3D)
-@:access(openfl.display3D.VertexBuffer3D)
-@:access(openfl.display.BitmapData)
-@:access(openfl.display.Bitmap)
-@:access(openfl.display.DisplayObjectRenderer)
-@:access(openfl.display.Shader)
-@:access(openfl.display.Stage)
-@:access(openfl.display.Stage3D)
-@:access(openfl.geom.Point)
-@:access(openfl.geom.Rectangle)
-@:final class Context3D extends EventDispatcher
-{
- /**
- Indicates if Context3D supports video texture.
- **/
- public static var supportsVideoTexture(default, null):Bool = #if (js && html5) true #else false #end;
-
- /**
- Specifies the height of the back buffer, which can be changed by a successful
- call to the `configureBackBuffer()` method. The height may be modified when the
- browser zoom factor changes if the `wantsBestResolutionOnBrowserZoom` parameter
- is set to `true` in the last successful call to the `configureBackBuffer()`
- method. The change in height can be detected by registering an event listener
- for the browser zoom change event.
- **/
- public var backBufferHeight(default, null):Int = 0;
-
- /**
- Specifies the width of the back buffer, which can be changed by a successful
- call to the `configureBackBuffer()` method. The width may be modified when the
- browser zoom factor changes if the `wantsBestResolutionOnBrowserZoom` parameter
- is set to `true` in the last successful call to the `configureBackBuffer()`
- method. The change in width can be detected by registering an event listener
- for the browser zoom change event.
- **/
- public var backBufferWidth(default, null):Int = 0;
-
- /**
- The type of graphics library driver used by this rendering context. Indicates
- whether the rendering is using software, a DirectX driver, or an OpenGL driver.
- Also indicates whether hardware rendering failed. If hardware rendering fails,
- Flash Player uses software rendering for Stage3D and `driverInfo` contains one
- of the following values:
-
- * "Software Hw_disabled=userDisabled" - The Enable hardware acceleration
- checkbox in the Adobe Flash Player Settings UI is not selected.
- * "Software Hw_disabled=oldDriver" - There are known problems with the
- hardware graphics driver. Updating the graphics driver may fix this problem.
- * "Software Hw_disabled=unavailable" - Known problems with the hardware
- graphics driver or hardware graphics initialization failure.
- * "Software Hw_disabled=explicit" - The content explicitly requested software
- rendering through requestContext3D.
- * "Software Hw_disabled=domainMemory" - The content uses domainMemory, which
- requires a license when used with Stage3D hardware rendering. Visit
- adobe.com/go/fpl.
- **/
- public var driverInfo(default, null):String = "OpenGL (Direct blitting)";
-
- /**
- Specifies whether errors encountered by the renderer are reported to the
- application.
-
- When `enableErrorChecking` is `true`, the `clear()`, and `drawTriangles()`
- methods are synchronous and can throw errors. When `enableErrorChecking`
- is `false`, the default, the `clear()`, and `drawTriangles()` methods are
- asynchronous and errors are not reported. Enabling error checking reduces
- rendering performance. You should only enable error checking when debugging.
- **/
- public var enableErrorChecking(get, set):Bool;
-
- /**
- Specifies the maximum height of the back buffer. The inital value is the system
- limit in the platform. The property can be set to a value smaller than or equal
- to, but not greater than, the system limit. The property can be set to a value
- greater than or equal to, but not smaller than, the minimum limit. The minimum
- limit is a constant value, 32, when the back buffer is not configured. The
- minimum limit will be the value of the `height` parameter in the last successful
- call to the `configureBackBuffer()` method after the back buffer is configured.
- **/
- public var maxBackBufferHeight(default, null):Int;
-
- /**
- Specifies the maximum width of the back buffer. The inital value is the system
- limit in the platform. The property can be set to a value smaller than or equal
- to, but not greater than, the system limit. The property can be set to a value
- greater than or equal to, but not smaller than, the minimum limit. The minimum
- limit is a constant value, 32, when the back buffer is not configured. The
- minimum limit will be the value of the width parameter in the last successful
- call to the `configureBackBuffer()` method after the back buffer is configured.
- **/
- public var maxBackBufferWidth(default, null):Int;
-
- /**
- The feature-support profile in use by this Context3D object.
- **/
- public var profile(default, null):Context3DProfile = STANDARD;
-
- /**
- Returns the total GPU memory allocated by Stage3D data structures of an
- application.
-
- Whenever a GPU resource object is created, memory utilized is stored in
- Context3D. This memory includes index buffers, vertex buffers,
- textures (excluding video texture), and programs that were created through this
- Context3D.
-
- API totalGPUMemory returns the total memory consumed by the above resources to
- the user. Default value returned is 0. The total GPU memory returned is in bytes.
- The information is only provided in Direct mode on mobile, and in Direct and
- GPU modes on desktop. (On desktop, using `