43 Commits
Author SHA1 Message Date
Ralty dc61d682c0 Merge branch 'main' into internal-merge
# Conflicts:
#	source/funkin/backend/system/Flags.hx
2026-07-07 11:26:08 +07:00
Ralty bf3c45dbb7 Re-enable streamed music in this branch 2026-04-07 14:02:07 +07:00
Ralty 2591230927 Implement FlxTrail Backward Compatibility 2026-04-07 13:51:45 +07:00
Ralty d605eaea85 Use cne-hxvlc instead, rebuild lime in workflows 2026-04-07 13:37:58 +07:00
Ralty 4ad7b33bb2 Merge branch 'main' into internal-merge
# Conflicts:
#	.github/workflows/linux-selfhosted.yml
2026-04-07 10:44:48 +07:00
Ralty 91c84cd9d0 Merge branch 'main' into internal-merge
# Conflicts:
#	.github/workflows/linux.yml
#	.github/workflows/macos.yml
#	.github/workflows/release.yml
#	.github/workflows/windows.yml
#	source/funkin/menus/FreeplayState.hx
#	source/openfl/utils/Assets.hx
2026-04-07 10:42:35 +07:00
Ralty 621413682e Fix Backward Compatibility with Shader 2026-04-03 07:02:09 +07:00
Ralty 6502b8027f Don't use FAST_LINK with debug 2026-04-02 06:19:55 +07:00
Ralty b80c492afb FIx customizable fontName 2026-04-01 19:51:11 +07:00
Ralty ca0853615b Fix for long loading screen for a specific mod 2026-04-01 07:31:50 +07:00
Ralty 0530bd0732 Add back Modified StringTools.hx 2026-03-30 22:44:04 +07:00
Ralty 294b9f302a Merge branch 'main' into internal-merge 2026-03-30 06:58:21 +07:00
Ralty d7c9f2595a Fix Includes/Imports in Shader 2026-03-30 06:57:56 +07:00
FrakitsandGitHub 2ec0500cd5 oops 2026-03-29 14:17:19 +03:00
FrakitsandGitHub 959aed576c fix action 2026-03-29 14:02:48 +03:00
Frakits 6272c3e98f selfhosted linux workflow because ubuntu stinks 2026-03-29 13:56:44 +03:00
Ralty 5ec57c5c39 i hate everything 2026-03-29 16:04:52 +07:00
Ralty a342783de4 Fix for Log.debug 2026-03-29 14:44:43 +07:00
Ralty eac0dac9b2 Add Flag DEFAULT_SOUND_TIME_SCALED_PITCH 2026-03-29 14:44:35 +07:00
Ralty d6488d37ad Merge remote-tracking branch 'origin/main' into internal-merge 2026-03-29 13:45:10 +07:00
Ralty 995a28e88a Corrected console color in Log.info 2026-03-29 13:41:56 +07:00
Ralty 6dd4058ac6 Deprecate CustomShader 2026-03-29 13:41:56 +07:00
b447031e2d Enable Mac OS Universal support, update hxvlc & actions dependencies, fix VIDEO_CUTSCENES (#931)
* Update hxvlc to 2.2.6

* Remove `arch -x86_64` as no longer needed

Since `cne-lime` got rebased with FunkinCrew's, which includes Apple Silicon (ARM64) support.

* Wtf

* Rename **Mac OS x64** to **Mac OS Universal** in the ReadME

Reason: **x64** is only powered by Intel/AMD, not ARM, so it will make the macOS users to think macOS builds still uses Intel.

* Enable videos for iOS

Since hxvlc 2.0.0 or newer, it fixed the video playback for iOS.

* Fixed the extension-androidtools import, when using extension-androidtools 2.2.X.

* Replace `start` with `wait` in `VideoCutscene.hx`

* Revert

* Nvm I think I found out why I guess

* Will this fix some actions deprecations?

* Oops

* F#CK

* I'm gonna lose it

* I forgot again

I'm going to scream into the pillow, If I keep forgetting to update those.

* Update macOS version to 26 in the macOS workflow

* Remove old hxvlc apis from the `VideoCutscene.hx` class, since hxvlc got updated to the latest

* Fix the f#cking conflicts

* Now it works

---------

Co-authored-by: Ralty <78720179+Raltyro@users.noreply.github.com>
2026-03-29 13:41:27 +07:00
Ralty 17bea45cd6 Redo FunkinShader once again 2026-03-29 09:06:22 +07:00
Ralty 8fc068e718 Libraries update 2026-03-29 09:06:15 +07:00
Ralty cf2c3689c5 Merge branch 'main' into internal-merge
# Conflicts:
#	building/libs.xml
#	source/openfl/utils/Assets.hx
2026-03-28 17:58:25 +07:00
Ralty 491e5df430 Temporary commit 2026-03-28 17:39:06 +07:00
Ralty 0d9bf82b06 Fix Windows Compiling 2026-03-04 06:11:56 +07:00
Ralty 54eea79dec Disable Caching, several compiler flags 2026-03-04 05:00:38 +07:00
Ralty 4aaa3ba06b Modify openfl Assets in macro instead 2026-03-03 01:15:16 +07:00
Ralty 606e98c192 Merge branch 'main' into internal-merge
# Conflicts:
#	source/funkin/editors/ui/UITextBox.hx
2026-03-03 00:30:50 +07:00
Ralty d8964db5d9 Fix Shader Processing 2026-03-02 23:16:26 +07:00
Ralty 13e6b9a56f Fix UITextBox Text Input 2026-03-02 21:20:52 +07:00
Ralty 2e7a8dcd21 can you work now 2026-03-01 20:42:04 +07:00
Ralty df386d7e68 Merge remote-tracking branch 'origin/main' into internal-merge 2026-03-01 20:21:53 +07:00
Ralty 5671b87e05 fix compiler flags, shader, music playing, etc 2026-03-01 20:21:21 +07:00
Ralty d922e6112f Compatibility with old glsl version shader issues 2026-03-01 18:23:26 +07:00
HEIHUAaandRalty 6fb716be07 Optimize HXCPP configuration compile options (#883) 2026-03-01 16:47:52 +07:00
Ralty b6d3333dfb Merge remote-tracking branch 'origin/main' into internal-merge 2026-03-01 16:26:44 +07:00
Ralty c3b1900154 Remove freeplay preview music for now 2026-03-01 14:15:02 +07:00
Ralty c20c29250d Fix every internal_merge issues 2026-03-01 14:15:02 +07:00
Ralty 143cc447c8 Remove old library modification codes 2026-03-01 14:15:02 +07:00
Ralty 5a176e0ccc Clean libs.xml, project.xml 2026-03-01 14:15:01 +07:00
72 changed files with 2073 additions and 14952 deletions
+78 -80
View File
@@ -11,22 +11,21 @@ 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: 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/haxe/
export/release/linux/obj/
# - 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 LibVLC
@@ -37,47 +36,47 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -s
- name: Building the game
run: |
haxelib run lime rebuild linux -nocolor -nocffi -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:
@@ -87,20 +86,19 @@ 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: 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/haxe/
export/debug/linux/obj/
# - 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 LibVLC
@@ -111,39 +109,39 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -s
- name: Building the game
run: |
haxelib run lime build linux -debug
haxelib run lime rebuild linux -nocolor -nocffi -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/
+81 -83
View File
@@ -8,25 +8,24 @@ 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: 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/haxe/
export/release/macos/obj/
# - 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
@@ -34,70 +33,69 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -s
- name: Building the game
run: |
arch -x86_64 haxelib run lime build mac -DCOMPILE_EXPERIMENTAL
haxelib run lime rebuild mac -nocolor -nocffi -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: 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/haxe/
export/debug/macos/obj/
# - 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
@@ -105,39 +103,39 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -s
- name: Building the game
run: |
arch -x86_64 haxelib run lime build mac -debug
haxelib run lime rebuild mac -nocolor -nocffi -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/
+7 -7
View File
@@ -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)
+81 -80
View File
@@ -11,22 +11,21 @@ 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: 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/haxe/
export/release/windows/obj/
# - 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
@@ -34,45 +33,47 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -s --no-vscheck
- name: Building the game
run: |
haxelib run lime rebuild windows -nocolor -nocffi -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:
@@ -82,20 +83,20 @@ 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: 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/
# - 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
@@ -103,37 +104,37 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -s --no-vscheck
- name: Building the game
run: |
haxelib run lime rebuild windows -nocolor -nocffi -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/
+1 -1
View File
@@ -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**.<br>
> Codename Engine as for now supports **Windows x64**, **Mac OS Universal** and **Linux x64**.<br>
> More platforms will soon come, stay tuned!<br>
> - [ ] **Web (HTML5) Support**
> - [ ] **Mobile Support**
+16 -2
View File
@@ -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);
-17
View File
@@ -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
+26 -28
View File
@@ -3,35 +3,32 @@
<if cond="update || all">
<print pretty="true">Preparing installation...</print>
<!-- OpenFL & Lime (Required for Flixel) -->
<git name="openfl" url="https://github.com/CodenameCrew/cne-openfl" ref="old" />
<lib name="lime" version="8.1.2" />
<!-- Hxcpp & OpenFL & Lime (Required for Flixel) -->
<git name="hxcpp" url="https://github.com/CodenameCrew/cne-hxcpp"/>
<git name="openfl" url="https://github.com/CodenameCrew/cne-openfl" ref="cne" skipDeps="true"/>
<git name="lime" url="https://github.com/CodenameCrew/cne-lime" ref="cne" skipDeps="true"/>
<!-- Flixel -->
<git name="flixel" url="https://github.com/CodenameCrew/cne-flixel" ref="old" />
<git name="flixel-addons" url="https://github.com/CodenameCrew/cne-flixel-addons" ref="old" />
<git name="flixel" url="https://github.com/CodenameCrew/cne-flixel" ref="cne" skipDeps="true"/>
<git name="flixel-addons" url="https://github.com/CodenameCrew/cne-flixel-addons" ref="dev" skipDeps="true"/>
<!-- CNE Core Libraries -->
<git name="flixel-animate" url="https://github.com/CodenameCrew/cne-flixel-animate" skipDeps="true"/>
<git name="hscript-improved" url="https://github.com/CodenameCrew/hscript-improved" ref="codename-dev"/>
<git name="away3d" url="https://github.com/CodenameCrew/away3d" skipDeps="true"/>
<!-- Other Libraries -->
<git name="hscript-improved" url="https://github.com/CodenameCrew/hscript-improved" ref="codename-dev" />
<git name="hxdiscord_rpc" url="https://github.com/CodenameCrew/cne-hxdiscord_rpc" ref="old" skipDeps="true" />
<lib name="funkin-modchart" skipDeps="true" />
<lib name="hxvlc" version="1.9.3" skipDeps="true" />
<!-- Documentation and other features -->
<git name="away3d" url="https://github.com/CodenameCrew/away3d" />
<!-- <lib name="dox" /> -->
<lib name="markdown" />
<lib name="hxcpp-debug-server" />
<lib name="nape-haxe4" />
<git name="hxdiscord_rpc" url="https://github.com/CodenameCrew/cne-hxdiscord_rpc" skipDeps="true"/>
<git name="hxvlc" url="https://github.com/CodenameCrew/cne-hxvlc" skipDeps="true"/>
<lib name="funkin-modchart" skipDeps="true"/>
<lib name="hxcpp-debug-server"/>
<lib name="format"/>
<lib name="markdown"/>
<lib name="nape-haxe4"/>
<!-- For building lime -->
<lib global="true" name="hxp" /> <lib name="hxp" />
<lib global="true" name="format" /> <lib name="format" />
<git name="hxcpp" url="https://github.com/CodenameCrew/cne-hxcpp" ref="old" />
<git name="flixel-animate" url="https://github.com/CodenameCrew/cne-flixel-animate/" ref="old" skipDeps="true"/>
<!-- <lib global="true" name="hxp"/> <lib name="hxp"/> -->
<!-- <lib global="true" name="format"/> <lib name="format"/> -->
</if>
<!-- Hxcpp Building -->
@@ -40,15 +37,16 @@
<!-- Build hxcpp -->
<cmd inLib="hxcpp" dir="tools/hxcpp" cmd="haxe compile.hxml" />
<cmd inLib="lime" cmd="haxelib run lime rebuild hxcpp"/>
</if>
<!-- Lime Building -->
<!-- <if cond="build-lime || all">
<if cond="build-lime">
<print pretty="true">Rebuilding Lime...</print>
<!- - Build lime - ->
<cmd inLib="lime" cmd="haxelib run lime rebuild . $PLATFORM -release -clean" />
<cmd inLib="lime" dir="tools" cmd="haxe tools.hxml" />
</if> -->
<!-- Build lime -->
<cmd inLib="lime" dir="tools" cmd="haxe tools.hxml"/>
<cmd inLib="lime" cmd="haxelib run lime rebuild $PLATFORM -nocolor -nocffi -release"/>
</if>
</libraries>
+144 -148
View File
@@ -1,84 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<project>
<!-- _________________________ Application Settings _________________________ -->
<app
title="Friday Night Funkin' - Codename Engine" main="funkin.backend.system.Main"
file="CodenameEngine" packageName="com.yoshman29.codenameengine"
package="com.yoshman29.codenameengine"
version="1.0.1" company="Yoshman29" />
<project xmlns="http://lime.software/project/1.0.4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://lime.software/project/1.0.4 http://lime.software/xsd/project-1.0.4.xsd">
<!-- ____________________________ Window Settings ___________________________ -->
<!--
CHANGE THE SAVE PATH & NAME FOR YOUR MOD HERE!
-->
<!-- CHANGE THE SAVE PATH & NAME FOR YOUR MOD HERE! -->
<haxedef name="SAVE_PATH" value="CodenameEngine"/>
<haxedef name="SAVE_NAME" value="save-default"/>
<haxedef name="SAVE_OPTIONS_PATH" value="CodenameEngine"/>
<haxedef name="SAVE_OPTIONS_NAME" value="options"/>
<!--Switch Export with Unique ApplicationID and Icon-->
<set name="APP_ID" value="0x0100f6c013bbc000" />
<!-- Comment this out to disable commit number on FPS -->
<define name="SHOW_BUILD_ON_FPS"/>
<!-- DO NOT REMOVE!! ALLOWS ME TO DO REGIONS LIKE IN C# AND KEEP CODE ORGANIZED. REMOVING IT WOULD BREAK THE ENGINE -->
<haxedef name="REGION" />
<!-- Comment this out to disable dark mode windows -->
<define name="DARK_MODE_WINDOW"/>
<!--The flixel preloader is not accurate in Chrome. You can use it regularly if you embed the swf into a html file
or you can set the actual size of your file manually at "FlxPreloaderBase-onUpdate-bytesTotal"-->
<app preloader="flixel.system.FlxPreloader" />
<!-- Should it checks for updates? -->
<define name="UPDATE_CHECKING" if="desktop" unless="hl || debug"/>
<!--Minimum without FLX_NO_GAMEPAD: 11.8, without FLX_NO_NATIVE_CURSOR: 11.2-->
<set name="SWF_VERSION" value="11.8" />
<!-- ____________________________ Window Settings ___________________________ -->
<!--These window settings apply to all targets-->
<window width="1280" height="720" background="#000000" hardware="true" vsync="false" />
<!--HTML5-specific-->
<window if="html5" resizable="true" />
<!--Desktop-specific-->
<window if="desktop" orientation="landscape" fullscreen="false" resizable="true" vsync="false"/>
<window if="mac" allow-high-dpi="true" />
<!--Mobile-specific-->
<window if="mobile" orientation="landscape" fullscreen="true" width="0" height="0" resizable="false"/>
<!--Switch-specific-->
<window if="switch" orientation="landscape" fullscreen="true" width="0" height="0" resizable="true" />
<!-- _________________________________ Engine Settings _______________________________ -->
<!-- Comment this out to disable updates !-->
<define name="UPDATE_CHECKING" unless="web || hl || neko || debug"/>
<!-- Comment this out to disable Discord RPC !-->
<section if="cpp">
<define name="DISCORD_RPC" if="desktop"/>
<!-- Compile With Discord Rich Presence -->
<!-- TODO: Update Discord RPC Library & Rewrite DiscordUtil -->
<!-- <define name="DISCORD_RPC" if="desktop"/> -->
<!-- Compile with video support (hxvlc) -->
<define name="VIDEO_CUTSCENES" if="desktop || mobile"/>
</section>
<!-- Uncomment this to achive faster compile times, will strip extraneous and commonly unused features. -->
<!-- <define name="STRIPPED_COMPILE" unless="debug"/> -->
<!-- Comment this out to disable modcharting features -->
<define name="MODCHARTING_FEATURES" unless="STRIPPED_COMPILE" />
<!-- Compile with _theo0p's modcharting tools -->
<define name="MODCHARTING_FEATURES" unless="STRIPPED_COMPILE"/>
<!-- Comment this out to disable GitHub API integration !-->
<define name="GITHUB_API" unless="web || hl"/>
<!-- Comment this out to prevent unused classes from being compiled.
Reduces compilation time at the cost of limited HScript flexibility. !-->
<define name="COMPILE_ALL_CLASSES" unless="STRIPPED_COMPILE" />
<define name="COMPILE_ALL_CLASSES" unless="STRIPPED_COMPILE"/>
<!-- Comment this out to prevent extending classes in hscript.
Reduces compilation time at the cost of limited HScript flexibility. !-->
<define name="CUSTOM_CLASSES" unless="STRIPPED_COMPILE" />
<define name="CUSTOM_CLASSES" unless="STRIPPED_COMPILE"/>
<!-- Comment this out to disable abstract support in hscript. !-->
<haxedef name="HSCRIPT_ABSTRACT_SUPPORT" unless="STRIPPED_COMPILE" />
<!-- Comment this out to disable multithreading !-->
<haxedef name="ALLOW_MULTITHREADING" unless="web || flash" />
<haxedef name="ALLOW_MULTITHREADING" if="cpp"/>
<!-- Comment this out to disable mods folder !-->
<haxedef name="MOD_SUPPORT" unless="web"/>
@@ -87,36 +57,60 @@
<haxedef name="TRANSLATIONS_SUPPORT"/>
<!-- Comment this out to disable global scripts !-->
<haxedef name="GLOBAL_SCRIPT" />
<haxedef name="GLOBAL_SCRIPT"/>
<!-- Comment this out to disable softcoded states and state scripting !-->
<haxedef name="SOFTCODED_STATES" />
<haxedef name="SOFTCODED_STATES"/>
<!-- Comment this out to disable support for custom ndlls. !-->
<haxedef name="NDLLS_SUPPORTED" unless="web || iphonesim || hl" />
<haxedef name="NDLLS_SUPPORTED" unless="web || iphonesim || hl"/>
<!-- Comment this out to use the default OpenFL assets system for the assets/ folder. !-->
<define name="USE_ADAPTED_ASSETS" unless="web" />
<define name="USE_ADAPTED_ASSETS" unless="web"/>
<!-- Comment this out to disable support for Away3D Flixel Integration (reduces compile times) !-->
<define name="THREE_D_SUPPORT" unless="STRIPPED_COMPILE" />
<define name="THREE_D_SUPPORT" unless="STRIPPED_COMPILE"/>
<!-- Comment this out to disable support for Nape integration (flixel.addons.nape) (reduces compile times) !-->
<define name="NAPE_ENABLED" unless="STRIPPED_COMPILE" />
<!-- Comment this out to disable video cutscene support, in case of compilation errors. !-->
<section if="cpp">
<define name="VIDEO_CUTSCENES" if="desktop || android" />
</section>
<!-- Comment this out to disable commit number on FPS -->
<define name="SHOW_BUILD_ON_FPS" />
<!-- Comment this out to disable dark mode windows -->
<define name="DARK_MODE_WINDOW"/>
<define name="NAPE_ENABLED" unless="STRIPPED_COMPILE"/>
<!-- Disable a optimization, to allow reflection to use more functions -->
<define name="FLX_NO_GENERIC" />
<define name="FLX_NO_GENERIC"/>
<!-- _________________________ Application Settings _________________________ -->
<app
title="Friday Night Funkin' - Codename Engine"
file="CodenameEngine" packageName="com.yoshman29.codenameengine"
package="com.yoshman29.codenameengine"
version="1.0.2" company="Yoshman29"/>
<!--Switch Export with Unique ApplicationID and Icon-->
<set name="APP_ID" value="0x0100f6c013bbc000"/>
<!--<set name="SWF_VERSION" value="11.8"/>-->
<!--Place custom nodes like icons here (higher priority to override the HaxeFlixel icon)-->
<icon path="art/icon16.png" size='16'/>
<icon path="art/icon32.png" size='24'/>
<icon path="art/icon32.png" size='32'/>
<icon path="art/icon64.png" size='40'/>
<icon path="art/icon64.png" size='48'/>
<icon path="art/icon64.png" size='64'/>
<icon path="art/iconOG.png" size='96'/>
<icon path="art/iconOG.png" size='128'/>
<icon path="art/iconOG.png" size='196'/>
<icon path="art/iconOG.png" size='256'/>
<icon path="art/iconOG.png" size='512'/>
<icon path="art/iconOG.png" size='768'/>
<icon path="art/iconOG.png" size='1024'/>
<!-- ____________________________ Window Settings ___________________________ -->
<window hardware="true" colorDepth="32" background="" fps="0" orientation="landscape"/>
<window unless="desktop" width="0" height="0" vsync="true" fullscreen="true"/>
<window if="desktop" type="opengl" width="1280" height="720" fullscreen="false" resizable="true" vsync="false" allowHighDPI="true"/>
<window if="mobile" resizable="false"/>
<window if="html5 || switch" resizable="true"/>
<!-- _______________________________ Libraries ______________________________ -->
@@ -124,32 +118,46 @@
<define name="disable-version-check"/>
<haxedef name="disable-version-check"/>
<haxedef name="no-deprecation-warnings"/>
<haxedef name="DISCORD_DISABLE_IO_THREAD" if="DISCORD_RPC"/>
<haxelib name="flixel"/>
<haxelib name="flixel" />
<haxelib name="flixel-addons" />
<haxelib name="flixel-addons"/>
<haxelib name="flixel-animate"/>
<haxelib name="hxvlc" if="VIDEO_CUTSCENES" />
<haxelib name="away3d" if="THREE_D_SUPPORT" />
<haxelib name="format" />
<haxelib name="flixel-animate" />
<haxelib name="hscript-improved"/>
<haxelib name="format" unless="STRIPPED_COMPILE"/>
<haxelib name="markdown" unless="STRIPPED_COMPILE"/>
<haxelib name="hxvlc" if="VIDEO_CUTSCENES"/>
<haxelib name="hxdiscord_rpc" if="DISCORD_RPC"/>
<haxelib name="away3d" if="THREE_D_SUPPORT"/>
<haxelib name="nape-haxe4" if="NAPE_ENABLED"/>
<haxelib name="hscript-improved" />
<haxelib name="hxdiscord_rpc" if="DISCORD_RPC"/>
<haxelib name="markdown" />
<haxelib name="hxcpp-debug-server" if="TEST_BUILD debug" />
<haxelib name="hxcpp-debug-server" if="debug"/>
<!-- _____________________________ Path Settings ____________________________ -->
<!-- ____________________________ Source Settings ___________________________ -->
<set name="BUILD_DIR" value="export/debug" if="debug" />
<set name="BUILD_DIR" value="export/release" unless="debug" />
<set name="BUILD_DIR" value="export/debug-32bit" if="debug 32bit" />
<set name="BUILD_DIR" value="export/release-32bit" if="32bit" unless="debug" />
<set name="BUILD_DIR" value="latest/" if="beta" />
<!-- DO NO REMOVE THIS -->
<haxedef name="REGION"/>
<source name="source" />
<set name="BUILD_DIR" value="export/debug" if="debug"/>
<set name="BUILD_DIR" value="export/release" unless="debug"/>
<set name="BUILD_DIR" value="export/debug-32bit" if="debug 32bit"/>
<set name="BUILD_DIR" value="export/release-32bit" if="32bit" unless="debug"/>
<set name="BUILD_DIR" value="latest/" if="beta"/>
<app main="funkin.backend.system.Main" preloader="flixel.system.FlxPreloader"/>
<source name="source"/>
<section if="MODCHARTING_FEATURES">
<haxedef name="FM_ENGINE" value="CODENAME"/>
<haxedef name="FM_ENGINE_VERSION" value="1.0"/>
<haxelib name="funkin-modchart" />
<haxeflag name="--macro" value="modchart.backend.macros.Macro.includeFiles()"/>
</section>
<!-- ____________________________ Assets Settings ___________________________ -->
<define name="PRELOAD_ALL" unless="web" />
<define name="NO_PRELOAD_ALL" unless="PRELOAD_ALL"/>
@@ -165,81 +173,69 @@
<assets path='mods' rename='mods' embed='false' />
</section>
<assets path='art/readme.txt' rename='do NOT readme.txt' />
<!-- OpenAL config -->
<section if="desktop">
<assets path="building/alsoft.txt" rename="plugins/alsoft.ini" type="text" if="windows"/>
<assets path="building/alsoft.txt" rename="plugins/alsoft.conf" type="text" unless="windows"/>
</section>
<!-- <assets path='art/readme.txt' rename='do NOT readme.txt' /> -->
<!-- ______________________________ Haxedefines _____________________________ -->
<haxeflag name="--macro" value="funkin.backend.system.macros.BuildInfo.printBuildInfo()" />
<haxeflag name="--macro" value="funkin.backend.system.macros.Macros.initMacros()"/>
<haxedef name="FLX_NO_FOCUS_LOST_SCREEN" />
<haxedef name="VIDEO_CUTSCENES" if="VIDEO_CUTSCENES"/>
<haxedef name="UPDATE_CHECKING" if="UPDATE_CHECKING"/>
<haxedef name="DISCORD_RPC" if="DISCORD_RPC"/>
<haxedef name="SHOW_BUILD_ON_FPS" if="SHOW_BUILD_ON_FPS"/>
<haxedef name="SOFTCODED_CLASSES" if="SOFTCODED_CLASSES"/>
<haxedef name="USE_ADAPTED_ASSETS" if="USE_ADAPTED_ASSETS"/>
<!--Disable the Flixel core debugger. Automatically gets set whenever you compile in release mode!-->
<haxedef name="FLX_NO_DEBUG" unless="debug" />
<!-- Backward Compatibilities -->
<haxedef name="FLX_HEALTH" />
<haxedef name="analyzer-optimize" unless="debug" />
<haxedef name="FLX_NO_FOCUS_LOST_SCREEN"/>
<haxedef name="FLX_NO_DEBUG" unless="debug"/>
<haxedef name="VIDEO_CUTSCENES" if="VIDEO_CUTSCENES" />
<haxedef name="UPDATE_CHECKING" if="UPDATE_CHECKING" />
<haxedef name="DISCORD_RPC" if="DISCORD_RPC" />
<haxedef name="SHOW_BUILD_ON_FPS" if="SHOW_BUILD_ON_FPS" />
<haxedef name="SOFTCODED_CLASSES" if="SOFTCODED_CLASSES" />
<haxedef name="USE_ADAPTED_ASSETS" if="USE_ADAPTED_ASSETS" />
<haxedef name="openfl_dpi_aware" if="openfl_dpi_aware" />
<haxedef name="DISCORD_DISABLE_IO_THREAD" if="DISCORD_RPC"/>
<!-- Disable it for more concise compilation errors. -->
<haxedef name="message.reporting" value="pretty" />
<!-- _________________________________ Custom _______________________________ -->
<!--Place custom nodes like icons here (higher priority to override the HaxeFlixel icon)-->
<icon path="art/icon16.png" size='16'/>
<icon path="art/icon32.png" size='24'/>
<icon path="art/icon32.png" size='32'/>
<icon path="art/icon64.png" size='40'/>
<icon path="art/icon64.png" size='48'/>
<icon path="art/icon64.png" size='64'/>
<icon path="art/iconOG.png" size='96' />
<icon path="art/iconOG.png" size='128' />
<icon path="art/iconOG.png" size='196' />
<icon path="art/iconOG.png" size='256' />
<icon path="art/iconOG.png" size='512' />
<icon path="art/iconOG.png" size='768' />
<icon path="art/iconOG.png" size='1024' />
<!-- _________________________________ Additional stuff _______________________________ -->
<section if="MODCHARTING_FEATURES">
<haxedef name="FM_ENGINE" value="CODENAME"/>
<haxedef name="FM_ENGINE_VERSION" value="1.0"/>
<haxelib name="funkin-modchart" />
<haxeflag name="--macro" value="modchart.backend.macros.Macro.includeFiles()"/>
</section>
<section if="COMPILE_ALL_CLASSES">
<haxeflag name="-dce" value="no" />
<haxeflag name="--macro" value="funkin.backend.system.macros.Macros.addAdditionalClasses()" />
</section>
<haxeflag name="--macro" value="funkin.backend.system.macros.Macros.initMacros()" />
<!-- <haxedef name="no-deprecation-warnings"/> -->
<!-- Fix compiling issues -->
<setenv name="HAXEPATH" value="./"/>
<haxedef name="HXCPP_M32" if="32bit" />
<haxedef name="HXCPP_DEBUG_LINK" if="debug" />
<haxedef name="HXCPP_CHECK_POINTER" />
<haxedef name="HXCPP_STACK_LINE" />
<haxedef name="HXCPP_CATCH_SEGV" />
<!-- Haxe 4.3.0+: Enable pretty syntax errors and stuff. -->
<haxedef name="message.reporting" value="pretty"/>
<haxedef name="hscriptPos" /> <!-- for logging -->
<!-- for logging -->
<haxedef name="hscriptPos"/>
<section if="COMPILE_ALL_CLASSES">
<haxeflag name="-dce" value="no"/>
<haxeflag name="--macro" value="funkin.backend.system.macros.Macros.addAdditionalClasses()"/>
</section>
<haxeflag name="--macro" value="funkin.backend.system.macros.BuildInfo.printBuildInfo()"/>
<!-- EXPERMENTAL FEATURES -->
<haxedef name="EXPERIMENTAL_FLXGRAPHIC_DESTROY_FIX" />
<haxedef name="openfl_dpi_aware"/>
<haxedef name="openfl_pool_events"/>
<haxedef name="openfl-enable-handle-error"/>
<haxedef name="openfl_no_custom_render_event"/>
<haxedef name="HXCPP_M32" if="32bit"/>
<haxedef name="HXCPP_DEBUG_LINK" if="debug"/>
<!--C++20 seems doesn't seem to do anything but causes compile warnings-->
<!--<haxedef name="HXCPP_CPP20"/>-->
<!--<haxedef name="HXCPP_CLANG"/>-->
<!--<haxedef name="HXCPP_MSVC"/>-->
<!-- This seems to causes more harm, but to be fair, its mostly because of text rendering -->
<!-- This is really important to fix honestly, try to get cairo text rendering not memory leak in the future -->
<!--<haxedef name="HXCPP_GC_BIG_BLOCKS"/>-->
<!--<haxedef name="HXCPP_GC_GENERATIONAL"/>-->
<!--<haxedef name="HXCPP_GC_DYNAMIC_SIZE"/>-->
<haxedef name="HXCPP_CHECK_POINTER"/>
<haxedef name="HXCPP_STACK_LINE"/>
<haxedef name="HXCPP_STACK_TRACE"/>
<!--<haxedef name="HXCPP_CATCH_SEGV"/>-->
<haxedef name="HXCPP_FAST_LINK" unless="debug"/>
<!--<haxedef name="HXCPP_ALIGN_ALLOC"/>--> <!--hxcpp.h(71): warning C4005: 'HXCPP_ALIGN_ALLOC': macro redefinition-->
<haxedef name="analyzer-optimize" unless="debug"/>
</project>
-960
View File
@@ -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<Float>;
/**
* The sound's "target" (for proximity and panning).
* @since raltyMod
*/
public var target:Null<FlxObject>;
/**
* 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<Float>; // 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<Bool>;
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<Bool>, ?autoDestroy:Null<Bool>, ?onComplete:Null<Void->Void>):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<Float>):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<Float>):Null<Float> {
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)
]);
}
}
-120
View File
@@ -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<FlxSound> = [];
/**
* 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;
}
}
@@ -8,7 +8,6 @@ import lime.utils.AssetLibrary;
import haxe.ds.Map;
class AssetsLibraryList extends AssetLibrary {
public var libraries:Array<AssetLibrary> = [];
public var cleanLibraries(get, never):Array<AssetLibrary>;
function get_cleanLibraries():Array<AssetLibrary> {
@@ -31,6 +30,9 @@ class AssetsLibraryList extends AssetLibrary {
public var transLib:TranslatedAssetLibrary;
#end
private var cachePaths:Map<String, AssetLibrary> = [];
private var cacheNonExistentPaths:Map<String, Int> = [];
public function removeLibrary(lib:AssetLibrary) {
if (lib != null) {
libraries.remove(lib);
@@ -54,12 +56,29 @@ class AssetsLibraryList extends AssetLibrary {
public function existsSpecific(id:String, type:String, source:AssetSource = BOTH) {
if (!id.startsWith("assets/") && existsSpecific('assets/$id', type, source))
return true;
// There's a mod that heavily relies on addons and that can causes lags just to get a path.
final sec = Date.now().getSeconds();
if (cacheNonExistentPaths.exists(id) && sec - 10 < cacheNonExistentPaths.get(id)) {
return false;
}
else if (cachePaths.exists(id)) {
if (cachePaths.get(id).exists(id, type)) return true;
else cachePaths.remove(id);
}
for(k=>l in libraries) {
if (shouldSkipLib(l, source)) continue;
if (l.exists(id, type)) {
//trace("EXISTS", id, type, source);
cachePaths.set(id, l);
return true;
}
}
//trace("DOESNT EXISTS", id, type, source);
cacheNonExistentPaths.set(id, sec);
return false;
}
public override inline function exists(id:String, type:String):Bool
@@ -198,6 +217,7 @@ class AssetsLibraryList extends AssetLibrary {
public function reset() {
unloadLibraries();
cachePaths.clear();
libraries = [];
// adds default libraries in again
@@ -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;
}
+1 -1
View File
@@ -194,7 +194,7 @@ class Paths
return FlxAtlasFrames.fromAseprite('$key.${ext != null ? ext : Flags.IMAGE_EXT}', '$key.json');
inline static public function getAssetsRoot():String
return ModsFolder.currentModFolder != null ? '${ModsFolder.modsPath}${ModsFolder.currentModFolder}' : #if (sys && TEST_BUILD) './${Main.pathBack}assets/' #else './assets' #end;
return ModsFolder.currentModFolder != null ? '${ModsFolder.modsPath}${ModsFolder.currentModFolder}' : #if (sys && TEST_BUILD) './${Main.pathBack}assets/' #else './assets' #end;
/**
* Gets frames at specified path.
+1 -1
View File
@@ -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,
@@ -1,36 +0,0 @@
package funkin.backend.shaders;
import flixel.util.FlxColor;
import openfl.display.ShaderParameter;
@:dox(hide)
typedef BlendModeShader =
{
var uBlendColor:ShaderParameter<Float>;
}
@: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;
}
}
+8 -32
View File
@@ -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>):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);
}
}
+260 -609
View File
@@ -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:FlxTypedSignal<Void->Void> = new FlxTypedSignal<Void->Void>();
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();
public function new(?fragmentSource:String, ?vertexSource:String, ?version:String) {
super(fragmentSource, vertexSource, version ?? (fragmentSource != null || vertexSource != null ? Flags.DEFAULT_GLSL_VERSION : null));
}
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;
public static function fromFile(fragmentPath:String, ?vertexPath:String, ?version:String):FunkinShader {
return new FunkinShader().loadShaderFile(fragmentPath, vertexPath, version);
}
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);
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);
}
return shader;
}
fragmentPath = FlxRuntimeShader._getPath(fragmentPath, false);
vertexPath = FlxRuntimeShader._getPath(vertexPath, true);
_fromFile(fragmentPath, vertexPath, version ?? (fragmentPath != null || vertexPath != null ? Flags.DEFAULT_GLSL_VERSION : null));
@: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();
}
@: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
}
}
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_]+)/;
}
while (regex.matchSub(source, lastMatch))
{
type = regex.matched(1);
name = regex.matched(2);
if (StringTools.startsWith(name, "gl_"))
{
continue;
}
var isUniform = (storageType == "uniform");
registerParameter(name, type, isUniform);
position = regex.matchedPos();
lastMatch = position.pos + position.len;
}
}
function registerParameter(name:String, type:String, isUniform:Bool):Void
{
if (StringTools.startsWith(type, "sampler"))
{
var input = new ShaderInput<BitmapData>();
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;
}
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;
}
var arrayLength = switch (parameterType)
{
case MATRIX2X2: 2;
case MATRIX3X3: 3;
case MATRIX4X4: 4;
default: 1;
}
switch (parameterType)
{
case BOOL, BOOL2, BOOL3, BOOL4:
var parameter = new ShaderParameter<Bool>();
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<Int>();
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<Float>();
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) {}
}
}
return this;
}
#if REGION /* IHScriptCustomBehaviour */
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);
if (__glSourceDirty) __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...
@@ -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
override function __updateGL():Void {
onGLUpdate.dispatch();
super.__updateGL();
}
override function __createAssembler():Void {
__glSourceAssembler = new FunkinShaderSourceAssembler(this);
}
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;
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);
}
// 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);
}
override function __appendIncludes(source:String, isVertex:Bool, ?includedKeys:Map<String, Bool>):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);
});
}
override function __getIncludeSource(include:String, fromVertex:Bool):Null<String> {
final path = Paths.getPath('shaders/' + include);
if (Assets.exists(path)) return Assets.getText(path);
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;
}
override function __appendPrefix(source:String, versionNumber:Int, versionProfile:String, extensions:Map<String, String>, isVertex:Bool,
precisionHint:Null<ShaderPrecision>):String
{
var result = super.__appendPrefix(null, versionNumber, versionProfile, extensions, isVertex, precisionHint) + "\n";
result += funkinParent.shaderPrefix + "\n" + (isVertex ? funkinParent.vertexPrefix : funkinParent.fragmentPrefix) + "\n";
if (source != null) {
if (!isVertex && versionNumber >= 300 && versionProfile != "compatibility" && !StringTools.contains(source, "out vec4")) {
result += "out vec4 openfl_FragColor;\n";
}
result += source;
}
return result;
}
private static inline function __getImportCompatibilityFinder():EReg {
return ~/#import\s+(?|"([^"]+)"|'([^']+)'|<(.*)>|([^\s]+))/g;
}
}
#if REGION /* Backward Compatibility */
class ShaderTemplates {
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 applyFlixelEffects(vec4 color) {
if(!hasTransform) {
return color;
}
if(color.a == 0.0) {
return vec4(0.0, 0.0, 0.0, 0.0);
}
if(!hasColorTransform) {
return color * openfl_Alphav;
}
color.rgb = color.rgb / color.a;
color = clamp(openfl_ColorOffsetv + (color * openfl_ColorMultiplierv), 0.0, 1.0);
if(color.a > 0.0) {
return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
}
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);
}
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);";
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;
vec4 apply_flixel_transform(vec4 color)
{
if (!hasTransform) return color;
else if (color.a <= 0.0 || openfl_Alphav == 0.0) return vec4(0.0);
// 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)
vec4 flixel_texture2D(sampler2D bitmap, vec2 coord)
{
return apply_flixel_transform(texture2D(bitmap, coord));
}
openfl_Alphav = openfl_Alpha * alpha;
uniform vec4 _camSize;
if(hasColorTransform) {
openfl_ColorOffsetv = colorOffset / 255.0;
openfl_ColorMultiplierv = colorMultiplier;
float map(float value, float min1, float max1, float min2, float max2) {
return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
}
gl_Position = openfl_Matrix * openfl_Position;";
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));
}";
// TODO: make this ignore comments
public static final vertBackCompatVarList:Array<EReg> = [
~/attribute float alpha/,
~/attribute vec4 colorMultiplier/,
~/attribute vec4 colorOffset/,
~/uniform bool hasColorTransform/
];
public static final fragBody:String = "gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv);
if (gl_FragColor.a == 0.0) discard;";
public static final vertHeaderBackCompat:String = "attribute float openfl_Alpha;
public static final vertBackCompatVarList:Array<EReg> = [
~/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<Dynamic>;
@@ -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;
}
}
@@ -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<String> {
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, String>):String
return Shader.processGLSLText(_processGLSLText(source, glVersion, isFragment, pragmas), glVersion, isFragment);
private static function _processGLSLText(source:String, glVersion:String, isFragment:Bool, ?pragmas:Map<String, String>):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<String, String>;
private static var __glVertexPragmasDefault:Map<String, String>;
private static var __glFragmentExtensionsDefault:Array<ShaderExtension>;
private static var __glVertexExtensionsDefault:Array<ShaderExtension>;
public var onGLUpdate:FlxTypedSignal<Void->Void> = new FlxTypedSignal<Void->Void>();
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<ShaderExtension>, ?vertexExtensions:Array<ShaderExtension>, 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<Float> 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<ShaderExtension>):Array<ShaderExtension> {
if (value == null) value = __glFragmentExtensionsDefault;
if (value != __glFragmentExtensions) __glSourceDirty = true;
return __glFragmentExtensions = value;
}
override function set_glVertexExtensions(value:Array<ShaderExtension>):Array<ShaderExtension> {
if (value == null) value = __glVertexExtensionsDefault;
if (value != __glVertexExtensions) __glSourceDirty = true;
return __glVertexExtensions = value;
}
override function set_glVersion(value:Null<String>):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<String, String>):Map<String, String> {
if (value == null) value = __glFragmentPragmasDefault;
if (value != __glFragmentPragmas)
__glSourceDirty = true;
return __glFragmentPragmas = value;
}
override function set_glVertexPragmas(value:Map<String, String>):Map<String, String> {
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<EReg> = [
~/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<Dynamic>;
var want:Class<Dynamic>;
var name:String;
public function new(name:String, has:Class<Dynamic>, 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)}.');
}
}
+13 -8
View File
@@ -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<Float>, indices:DrawData<Int>, uvtData:DrawData<Float>, ?colors:DrawData<Int>, ?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<Float>, indices:DrawData<Int>, uvtData:DrawData<Float>, ?colors:DrawData<Int>, ?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);
}
}
+19 -1
View File
@@ -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<Bool> = null;
@:lazy public static var USE_FLXTRAIL_FRAMES:Null<Bool> = null;
public static var SUPPORTED_CHART_RUNTIME_FORMATS:Array<String> = ["Legacy", "Psych Engine"];
public static var SUPPORTED_CHART_FORMATS:Array<String> = ["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<String>):Map<String, String> {
+4 -5
View File
@@ -8,13 +8,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);
}
}
@@ -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();
+1 -1
View File
@@ -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) {
+6 -21
View File
@@ -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();
}
@@ -42,6 +42,7 @@ class MainState extends FlxState {
ControlsUtil.resetCustomControls();
FlxG.bitmap.reset();
FlxG.sound.destroy(true);
FlxG.sound.resetCache();
Paths.assetsTree.reset();
@@ -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();
}
}
@@ -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)
@@ -14,7 +14,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)
@@ -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}';
@@ -5,7 +5,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;
_text = 'Current Song Position: ${Math.floor(Conductor.songPosition * 1000) / 1000}';
_text += '\n - ${Conductor.curBeat} beats';
@@ -7,7 +7,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;
@@ -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);
@@ -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);
@@ -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);
@@ -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);
@@ -9,7 +9,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;
_text = "totalDC: " + Context3DStats.totalDrawCalls();
_text += "\nstageDC: " + Context3DStats.contextDrawCalls(DrawCallContext.STAGE);
@@ -169,7 +169,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;
_text = __formattedSysText;
@@ -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<Field> {
final fields:Array<Field> = 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<Field> {
final fields:Array<Field> = 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<Field> {
final fields:Array<Field> = 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<Bool>)});
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
@@ -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
}
@@ -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;
}
}
File diff suppressed because it is too large Load Diff
+207 -195
View File
@@ -3,220 +3,232 @@ package funkin.backend.utils;
import haxe.macro.Expr;
final class MathUtil {
public static inline var EULER:Float = 2.718281828459;
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 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 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 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;
}
/**
* 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 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 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 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 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 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;
}
/**
* 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 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 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);
}
/**
* @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.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");
}
/**
* 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<Expr>, funcPath:String):Expr {
var args = _args.copy();
if (args.length == 0) return macro 0;
#if macro
@:dox(hide) private static function genericMinMaxSmart(_args:Array<Expr>, funcPath:String):Expr {
var args = _args.copy();
if (args.length == 0) return macro 0;
var func = funcPath.split(".");
var func = funcPath.split(".");
function nested(lst:Array<Expr>):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))});
}
}
function nested(lst:Array<Expr>):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 expr = nested(args);
//var printer = new haxe.macro.Printer();
//trace(printer.printExpr(expr));
//var printer = new haxe.macro.Printer();
//trace(printer.printExpr(expr));
return macro $expr;
}
#end
return macro $expr;
}
#end
}
+1 -16
View File
@@ -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");
@@ -27,107 +27,10 @@ import funkin.backend.utils.NativeAPI.MessageBoxIcon;
#include <wingdi.h>
#include <shellapi.h>
#include <uxtheme.h>
#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;
+11
View File
@@ -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<String, Bool>;
@@ -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) {
+10 -1
View File
@@ -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<String>, 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);
+36 -30
View File
@@ -72,7 +72,10 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
framesOffset = (selected ? 18 : (hovered ? 9 : 0));
@:privateAccess {
if (selected) {
__wasFocused = true;
if (!__wasFocused) {
__wasFocused = true;
FlxG.stage.window.textInputEnabled = true;
}
caretSpr.alpha = (FlxG.game.ticks % 666) >= 333 ? 1 : 0;
var curPos = switch (position) {
@@ -92,6 +95,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
} else {
if (__wasFocused) {
__wasFocused = false;
FlxG.stage.window.textInputEnabled = false;
if (onChange != null)
onChange(label.text);
}
@@ -133,6 +137,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
focused = false;
if (onChange != null)
onChange(label.text);
return;
case LEFT:
if (modifier.ctrlKey) {
if (position == 0)
@@ -149,6 +154,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
}
changeSelection(-1);
return;
case RIGHT:
if (modifier.ctrlKey) {
if (position == label.text.length)
@@ -165,6 +171,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
}
changeSelection(1);
return;
case BACKSPACE:
UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTREMOVE_SOUND);
@@ -181,6 +188,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
label.text = label.text.substr(0, position - 1) + label.text.substr(position);
changeSelection(-1);
}
return;
case DELETE:
UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTREMOVE_SOUND);
@@ -196,48 +204,46 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
if (position < label.text.length) {
label.text = label.text.substr(0, position) + label.text.substr(position + 1);
}
return;
case HOME:
position = 0;
return;
case END:
position = label.text.length;
return;
case V:
UIState.playEditorSound(Flags.DEFAULT_EDITOR_PASTE_SOUND);
// Hey lj here, fixed copying because before we checked if the modifier was left or right ctrl
// but somehow it gave a int outside of the KeyModifier's range :sob:
// apparently there is a boolean that just checks for you. yw :D
if (modifier.ctrlKey) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_PASTE_SOUND);
// if we are not holding ctrl, ignore
if (!modifier.ctrlKey)
return;
// we pasting
var data:String = Clipboard.generalClipboard.getData(TEXT_FORMAT);
if (data != null)
onTextInput(data);
// Hey lj here, fixed copying because before we checked if the modifier was left or right ctrl
// but somehow it gave a int outside of the KeyModifier's range :sob:
// apparently there is a boolean that just checks for you. yw :D
// we pasting
var data:String = Clipboard.generalClipboard.getData(TEXT_FORMAT);
if (data != null)
onTextInput(data);
}
case C:
UIState.playEditorSound(Flags.DEFAULT_EDITOR_COPY_SOUND);
// if we are not holding ctrl, ignore
if (!modifier.ctrlKey)
return;
if (modifier.ctrlKey) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_COPY_SOUND);
// copying
Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
}
case X:
UIState.playEditorSound(Flags.DEFAULT_EDITOR_CUT_SOUND);
if (modifier.ctrlKey) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_CUT_SOUND);
// if we are not holding ctrl, ignore
if (!modifier.ctrlKey)
Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
position = 0;
label.text = "";
return;
// cutting
Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
position = 0;
label.text = "";
}
default:
if (modifier.ctrlKey || modifier.altKey || modifier.shiftKey)
return;
UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND);
}
if (modifier.ctrlKey || modifier.altKey || modifier.shiftKey) return;
UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND);
}
public function changeSelection(change:Int) {
+1 -11
View File
@@ -1102,7 +1102,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);
@@ -1251,11 +1251,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();
@@ -1264,11 +1259,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();
+2 -17
View File
@@ -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();
+3 -90
View File
@@ -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);
+3 -2
View File
@@ -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;
@@ -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'));
}
}
-297
View File
@@ -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<Int>;
#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<T>(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<Timer> = [];
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<T>(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
@@ -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<Float>;
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<Float>
{
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<Float>):Null<Float>
{
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;
}
}
@@ -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<Int, Touch>();
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<Touch>();
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;
}
@@ -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<Bool>;
public static var loopPointsSupported:Null<Bool>;
public static var stereoAnglesExtensionSupported:Null<Bool>;
public static var latencyExtensionSupported:Null<Bool>;
private static var bufferDataPool:Array<ArrayBufferView> = [];
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<Float>; 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<Int>; 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<Float>;
var endTime:Null<Float>;
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<Int>; // In Samples
static var streamSources:Array<NativeAudioSource> = [];
static var queuedStreamSources:Array<NativeAudioSource> = [];
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<ArrayBufferView>;
var bufferTimes:Array<Float>;
var bufferLengths:Array<Int>;
var buffers:Array<ALBuffer>;
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<Float>):Null<Float> {
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;
}
}
@@ -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;
}
-518
View File
@@ -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<String> #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<AudioBuffer>
{
#if (flash || (js && html5))
var promise = new Promise<AudioBuffer>();
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<AudioBuffer>();
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<String>):Future<AudioBuffer>
{
#if (js && html5 && lime_howlerjs)
var promise = new Promise<AudioBuffer>();
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
}
}
-257
View File
@@ -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<AudioSource> = [];
/**
An event that is dispatched when the audio playback is complete.
**/
public var onComplete = new Event<Void->Void>();
/**
An event that is dispatched when the audio playback looped.
**/
public var onLoop = new Event<Void->Void>();
/**
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<Float>;
/**
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<Float> = 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<Float>
{
return __backend.getLength();
}
@:noCompletion inline private function set_length(value:Null<Float>):Null<Float>
{
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
-356
View File
@@ -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
-905
View File
@@ -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<String, Function>;
@: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<SystemDirectory, String>();
@: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<Int> = null, height:Null<Int> = 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<Dynamic> = displayInfo.supportedModes;
#else
var supportedModes:Array<Dynamic> = 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<String, String>();
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<String> = 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;
}
-411
View File
@@ -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 Event<Void->Void>();
/**
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 Event<Resource->Void>();
/**
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 Event<String->Void>();
/**
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 Event<String->Void>();
/**
Triggers when `browse()` is successful and `type` is `FileDialogType.OPEN_MULTIPLE`. The
`Array<String>` contains all selected file paths.
**/
public var onSelectMultiple = new Event<Array<String>->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<HLBytes> = 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<String> = 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
}
}
+3 -1
View File
@@ -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
-347
View File
@@ -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<T>
{
/**
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<Int>;
@:noCompletion private var __inactiveObject0:T;
@:noCompletion private var __inactiveObject1:T;
@:noCompletion private var __inactiveObjectList:List<T>;
@:noCompletion private var __pool:Map<T, Bool>;
@:noCompletion private var __size:Null<Int>;
/**
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<Int> = null)
{
__pool = cast new ObjectMap();
activeObjects = 0;
inactiveObjects = 0;
__inactiveObject0 = null;
__inactiveObject1 = null;
__inactiveObjectList = new List<T>();
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<Int>
{
return __size;
}
@:noCompletion private function set_size(value:Null<Int>):Null<Int>
{
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;
}
}
@@ -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<Bool>):Null<Bool>
{
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<Bool>):Null<Bool>
{
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
-89
View File
@@ -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);
}
}
File diff suppressed because it is too large Load Diff
@@ -1,95 +0,0 @@
package openfl.display3D.utils;
import flixel.util.FlxPool.IFlxPooled;
import flixel.util.FlxPool;
import lime.utils.UInt8Array;
class UInt8Buff implements IFlxPooled
{
static var _pools:Map<Int, FlxPool<UInt8Buff>> = [];
public static function getPool(length:Int) {
if(!_pools.exists(length)) {
_pools.set(length, new FlxPool<UInt8Buff>(UInt8Buff));
}
return _pools.get(length);
}
/**
* Recycle or create new FlxRect.
* Be sure to put() them back into the pool after you're done with them!
*/
public static inline function get(length:Int):UInt8Buff
{
var rect = getPool(length).get().set(length);
rect._inPool = false;
if(rect.buffer == null)
rect.buffer = new UInt8Array(length);
return rect;
}
/**
* Recycle or create a new FlxRect which will automatically be released
* to the pool when passed into a flixel function.
*/
public static inline function weak(length:Int):UInt8Buff
{
var rect = get(length);
rect._weak = true;
return rect;
}
var _weak:Bool = false;
var _inPool:Bool = false;
public var length(get, never):Int;
inline function get_length() {
return buffer.length;
}
public var buffer:UInt8Array;
@:keep
private function new(length:Int)
{
set(length);
}
/**
* Add this FlxRect to the recycling pool.
*/
public inline function put():Void
{
if (!_inPool)
{
_inPool = true;
_weak = false;
getPool(length).putUnsafe(this);
}
}
/**
* Add this FlxPoint to the recycling pool if it's a weak reference (allocated via weak()).
*/
public inline function putWeak():Void
{
if (_weak)
{
put();
}
}
/**
* Does nothing
*/
private inline function set(_length:Int):UInt8Buff
{
return this;
}
/**
* Necessary for IFlxDestroyable.
*/
public function destroy() {}
}
-705
View File
@@ -1,705 +0,0 @@
package openfl.media;
#if !flash
import haxe.Int64;
import openfl.events.Event;
import openfl.events.EventDispatcher;
import openfl.events.IOErrorEvent;
import openfl.net.URLRequest;
import openfl.utils.ByteArray;
import openfl.utils.Future;
#if lime
import openfl.utils._internal.UInt8Array;
import lime.media.AudioBuffer;
import lime.media.AudioSource;
#end
/**
The Sound class lets you work with sound in an application. The Sound class
lets you create a Sound object, load and play an external MP3 file into
that object, close the sound stream, and access data about the sound, such
as information about the number of bytes in the stream and ID3 metadata.
More detailed control of the sound is performed through the sound source
- the SoundChannel or Microphone object for the sound - and through the
properties in the SoundTransform class that control the output of the sound
to the computer's speakers.
In Flash Player 10 and later and AIR 1.5 and later, you can also use
this class to work with sound that is generated dynamically. In this case,
the Sound object uses the function you assign to a `sampleData`
event handler to poll for sound data. The sound is played as it is
retrieved from a ByteArray object that you populate with sound data. You
can use `Sound.extract()` to extract sound data from a Sound
object, after which you can manipulate it before writing it back to the
stream for playback.
To control sounds that are embedded in a SWF file, use the properties in
the SoundMixer class.
**Note**: The ActionScript 3.0 Sound API differs from ActionScript
2.0. In ActionScript 3.0, you cannot take sound objects and arrange them in
a hierarchy to control their properties.
When you use this class, consider the following security model:
* Loading and playing a sound is not allowed if the calling file is in
a network sandbox and the sound file to be loaded is local.
* By default, loading and playing a sound is not allowed if the calling
file is local and tries to load and play a remote sound. A user must grant
explicit permission to allow this type of access.
* Certain operations dealing with sound are restricted. The data in a
loaded sound cannot be accessed by a file in a different domain unless you
implement a cross-domain policy file. Sound-related APIs that fall under
this restriction are `Sound.id3`,
`SoundMixer.computeSpectrum()`,
`SoundMixer.bufferTime`, and the `SoundTransform`
class.
However, in Adobe AIR, content in the `application` security
sandbox(content installed with the AIR application) are not restricted by
these security limitations.
For more information related to security, see the Flash Player Developer
Center Topic: [Security](http://www.adobe.com/go/devnet_security_en).
@event complete Dispatched when data has loaded successfully.
@event id3 Dispatched by a Sound object when ID3 data is available
for an MP3 sound.
@event ioError Dispatched when an input/output error occurs that causes
a load operation to fail.
@event open Dispatched when a load operation starts.
@event progress Dispatched when data is received as a load operation
progresses.
@event sampleData Dispatched when the runtime requests new audio data.
**/
#if !openfl_debug
@:fileXml('tags="haxe,release"')
@:noDebug
#end
@:access(lime.media.AudioBuffer)
@:access(lime.utils.AssetLibrary)
@:access(openfl.media.SoundMixer)
@:access(openfl.media.SoundChannel.new)
@:autoBuild(openfl.utils._internal.AssetsMacro.embedSound())
class Sound extends EventDispatcher
{
/**
Returns the currently available number of bytes in this sound object. This
property is usually useful only for externally loaded files.
**/
public var bytesLoaded(default, null):Int;
/**
Returns the total number of bytes in this sound object.
**/
public var bytesTotal(default, null):Int;
/**
Provides access to the metadata that is part of an MP3 file.
MP3 sound files can contain ID3 tags, which provide metadata about the
file. If an MP3 sound that you load using the `Sound.load()` method
contains ID3 tags, you can query these properties. Only ID3 tags that
use the UTF-8 character set are supported.
Flash Player 9 and later and AIR support ID3 2.0 tags, specifically
2.3 and 2.4. The following tables list the standard ID3 2.0 tags and
the type of content the tags represent. The `Sound.id3` property
provides access to these tags through the format `my_sound.id3.COMM`,
`my_sound.id3.TIME`, and so on. The first table describes tags that
can be accessed either through the ID3 2.0 property name or the
ActionScript property name. The second table describes ID3 tags that
are supported but do not have predefined properties in ActionScript.
| ID3 2.0 tag | Corresponding Sound class property |
| --- | --- |
| COMM | Sound.id3.comment |
| TALB | Sound.id3.album |
| TCON | Sound.id3.genre |
| TIT2 | Sound.id3.songName |
| TPE1 | Sound.id3.artist |
| TRCK | Sound.id3.track |
| TYER | Sound.id3.year |
The following table describes ID3 tags that are supported but do not
have predefined properties in the Sound class. You access them by
calling `mySound.id3.TFLT`, `mySound.id3.TIME`, and so on. **NOTE:**
None of these tags are supported in Flash Lite 4.
| Property | Description |
| --- | --- |
| TFLT | File type |
| TIME | Time |
| TIT1 | Content group description |
| TIT2 | Title/song name/content description |
| TIT3 | Subtitle/description refinement |
| TKEY | Initial key |
| TLAN | Languages |
| TLEN | Length |
| TMED | Media type |
| TOAL | Original album/movie/show title |
| TOFN | Original filename |
| TOLY | Original lyricists/text writers |
| TOPE | Original artists/performers |
| TORY | Original release year |
| TOWN | File owner/licensee |
| TPE1 | Lead performers/soloists |
| TPE2 | Band/orchestra/accompaniment |
| TPE3 | Conductor/performer refinement |
| TPE4 | Interpreted, remixed, or otherwise modified by |
| TPOS | Part of a set |
| TPUB | Publisher |
| TRCK | Track number/position in set |
| TRDA | Recording dates |
| TRSN | Internet radio station name |
| TRSO | Internet radio station owner |
| TSIZ | Size |
| TSRC | ISRC (international standard recording code) |
| TSSE | Software/hardware and settings used for encoding |
| TYER | Year |
| WXXX | URL link frame |
When using this property, consider the Flash Player security model:
* The `id3` property of a Sound object is always permitted for SWF
files that are in the same security sandbox as the sound file. For
files in other sandboxes, there are security checks.
* When you load the sound, using the `load()` method of the Sound
class, you can specify a `context` parameter, which is a
SoundLoaderContext object. If you set the `checkPolicyFile` property
of the SoundLoaderContext object to `true`, Flash Player checks for a
URL policy file on the server from which the sound is loaded. If a
policy file exists and permits access from the domain of the loading
SWF file, then the file is allowed to access the `id3` property of the
Sound object; otherwise it is not.
However, in Adobe AIR, content in the `application` security sandbox
(content installed with the AIR application) are not restricted by
these security limitations.
For more information related to security, see the Flash Player
Developer Center Topic: <a
href="http://www.adobe.com/go/devnet_security_en"
scope="external">Security</a>.
**/
public var id3(get, never):ID3Info;
/**
Returns the buffering state of external MP3 files. If the value is
`true`, any playback is currently suspended while the object
waits for more data.
**/
public var isBuffering(default, null):Bool;
// @:noCompletion @:dox(hide) @:require(flash10_1) public var isURLInaccessible (default, null):Bool;
/**
The length of the current sound in milliseconds.
**/
public var length(get, never):Float;
/**
The URL from which this sound was loaded. This property is applicable only
to Sound objects that were loaded using the `Sound.load()`
method. For Sound objects that are associated with a sound asset from a
SWF file's library, the value of the `url` property is
`null`.
When you first call `Sound.load()`, the `url`
property initially has a value of `null`, because the final URL
is not yet known. The `url` property will have a non-null value
as soon as an `open` event is dispatched from the Sound
object.
The `url` property contains the final, absolute URL from
which a sound was loaded. The value of `url` is usually the
same as the value passed to the `stream` parameter of
`Sound.load()`. However, if you passed a relative URL to
`Sound.load()` the value of the `url` property
represents the absolute URL. Additionally, if the original URL request is
redirected by an HTTP server, the value of the `url` property
reflects the final URL from which the sound file was actually downloaded.
This reporting of an absolute, final URL is equivalent to the behavior of
`LoaderInfo.url`.
In some cases, the value of the `url` property is truncated;
see the `isURLInaccessible` property for details.
**/
public var url(default, null):String;
#if lime
@:noCompletion private var __buffer:AudioBuffer;
#end
#if openfljs
@:noCompletion private static function __init__()
{
untyped Object.defineProperties(Sound.prototype, {
"id3": {get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_id3 (); }")},
"length": {get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_length (); }")},
});
}
#end
/**
Creates a new Sound object. If you pass a valid URLRequest object to the
Sound constructor, the constructor automatically calls the
`load()` function for the Sound object. If you do not pass a
valid URLRequest object to the Sound constructor, you must call the
`load()` function for the Sound object yourself, or the stream
will not load.
Once `load()` is called on a Sound object, you can't later
load a different sound file into that Sound object. To load a different
sound file, create a new Sound object.
In Flash Player 10 and later and AIR 1.5 and later, instead of using
`load()`, you can use the `sampleData` event handler
to load sound dynamically into the Sound object.
@param stream The URL that points to an external MP3 file.
@param context An optional SoundLoader context object, which can define
the buffer time(the minimum number of milliseconds of MP3
data to hold in the Sound object's buffer) and can specify
whether the application should check for a cross-domain
policy file prior to loading the sound.
**/
public function new(stream:URLRequest = null, context:SoundLoaderContext = null)
{
super(this);
bytesLoaded = 0;
bytesTotal = 0;
isBuffering = false;
url = null;
if (stream != null)
{
load(stream, context);
}
}
/**
Closes the stream, causing any download of data to cease. No data may
be read from the stream after the `close()` method is called.
@throws IOError The stream could not be closed, or the stream was not
open.
**/
public function close():Void
{
#if lime
if (__buffer != null)
{
__buffer.dispose();
__buffer = null;
}
#end
}
#if false
/**
Extracts raw sound data from a Sound object.
This method is designed to be used when you are working with
dynamically generated audio, using a function you assign to the
`sampleData` event for a different Sound object. That is, you can use
this method to extract sound data from a Sound object. Then you can
write the data to the byte array that another Sound object is using to
stream dynamic audio.
The audio data is placed in the target byte array starting from the
current position of the byte array. The audio data is always exposed
as 44100 Hz Stereo. The sample type is a 32-bit floating-point value,
which can be converted to a Number using `ByteArray.readFloat()`.
@param target A ByteArray object in which the extracted sound samples
are placed.
@param length The number of sound samples to extract. A sample
contains both the left and right channels נthat is,
two 32-bit floating-point values.
@return The number of samples written to the ByteArray specified in
the `target` parameter.
**/
// @:noCompletion @:dox(hide) @:require(flash10) public function extract (target:ByteArray, length:Float, startPosition:Float = -1):Float;
#end
#if lime
/**
Creates a new Sound from an AudioBuffer immediately.
@param buffer An AudioBuffer instance
@returns A new Sound
**/
public static function fromAudioBuffer(buffer:AudioBuffer):Sound
{
var sound = new Sound();
sound.__buffer = buffer;
return sound;
}
#end
/**
Creates a new Sound from a file path synchronously. This means that the
Sound will be returned immediately (if supported).
HTML5 and Flash do not support creating Sound synchronously, so these targets
always return `null`.
In order to load files from a remote web address, use the `loadFromFile` method,
which supports asynchronous loading.
@param path A local file path containing a sound
@returns A new Sound if successful, or `null` if unsuccessful
**/
public static function fromFile(path:String):Sound
{
#if lime
return fromAudioBuffer(AudioBuffer.fromFile(path));
#else
return null;
#end
}
/**
Initiates loading of an external MP3 file from the specified URL. If you
provide a valid URLRequest object to the Sound constructor, the
constructor calls `Sound.load()` for you. You only need to call
`Sound.load()` yourself if you don't pass a valid URLRequest
object to the Sound constructor or you pass a `null` value.
Once `load()` is called on a Sound object, you can't later
load a different sound file into that Sound object. To load a different
sound file, create a new Sound object.
When using this method, consider the following security model:
* Calling `Sound.load()` is not allowed if the calling file
is in the local-with-file-system sandbox and the sound is in a network
sandbox.
* Access from the local-trusted or local-with-networking sandbox
requires permission from a website through a URL policy file.
* You cannot connect to commonly reserved ports. For a complete list
of blocked ports, see "Restricting Networking APIs" in the _ActionScript
3.0 Developer's Guide_.
* You can prevent a SWF file from using this method by setting the
`allowNetworking` parameter of the `object` and
`embed` tags in the HTML page that contains the SWF
content.
In Flash Player 10 and later, if you use a multipart Content-Type(for
example "multipart/form-data") that contains an upload(indicated by a
"filename" parameter in a "content-disposition" header within the POST
body), the POST operation is subject to the security rules applied to
uploads:
* The POST operation must be performed in response to a user-initiated
action, such as a mouse click or key press.
* If the POST operation is cross-domain(the POST target is not on the
same server as the SWF file that is sending the POST request), the target
server must provide a URL policy file that permits cross-domain
access.
Also, for any multipart Content-Type, the syntax must be valid
(according to the RFC2046 standards). If the syntax appears to be invalid,
the POST operation is subject to the security rules applied to
uploads.
In Adobe AIR, content in the `application` security sandbox
(content installed with the AIR application) are not restricted by these
security limitations.
For more information related to security, see the Flash Player
Developer Center Topic: [Security](http://www.adobe.com/go/devnet_security_en).
@param stream A URL that points to an external MP3 file.
@param context An optional SoundLoader context object, which can define
the buffer time(the minimum number of milliseconds of MP3
data to hold in the Sound object's buffer) and can specify
whether the application should check for a cross-domain
policy file prior to loading the sound.
@throws IOError A network error caused the load to fail.
@throws IOError The `digest` property of the
`stream` object is not `null`.
You should only set the `digest` property
of a URLRequest object when calling the
`URLLoader.load()` method when loading a
SWZ file(an Adobe platform component).
@throws SecurityError Local untrusted files may not communicate with the
Internet. You can work around this by reclassifying
this file as local-with-networking or trusted.
@throws SecurityError You cannot connect to commonly reserved ports. For a
complete list of blocked ports, see "Restricting
Networking APIs" in the _ActionScript 3.0
Developer's Guide_.
**/
public function load(stream:URLRequest, context:SoundLoaderContext = null):Void
{
url = stream.url;
#if lime
#if (js && html5)
var defaultLibrary = lime.utils.Assets.getLibrary("default");
if (defaultLibrary != null && defaultLibrary.cachedAudioBuffers.exists(url))
{
AudioBuffer_onURLLoad(defaultLibrary.cachedAudioBuffers.get(url));
}
else
{
AudioBuffer.loadFromFile(url).onComplete(AudioBuffer_onURLLoad).onError(function(_)
{
AudioBuffer_onURLLoad(null);
});
}
#else
AudioBuffer.loadFromFile(url).onComplete(AudioBuffer_onURLLoad).onError(function(_)
{
AudioBuffer_onURLLoad(null);
});
#end
#end
}
/**
Load MP3 sound data from a ByteArray object into a Sound object. The data will be read from the current
ByteArray position and will leave the ByteArray position at the end of the specified bytes length once
finished. If the MP3 sound data contains ID3 data ID3 events will be dispatched during this function call.
This function will throw an exception if the ByteArray object does not contain enough data.
@param bytes
@param bytesLength
**/
public function loadCompressedDataFromByteArray(bytes:ByteArray, bytesLength:Int):Void
{
if (bytes == null || bytesLength <= 0)
{
dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
return;
}
if (bytes.position > 0 || bytes.length > bytesLength)
{
var copy = new ByteArray(bytesLength);
copy.writeBytes(bytes, bytes.position, bytesLength);
bytes = copy;
}
#if lime
__buffer = AudioBuffer.fromBytes(bytes);
if (__buffer == null)
{
dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
}
else
{
dispatchEvent(new Event(Event.COMPLETE));
}
#else
dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
#end
}
/**
Creates a new Sound from a file path or web address asynchronously. The file
load will occur in the background.
Progress, completion and error callbacks will be dispatched in the current
thread using callbacks attached to a returned Future object.
@param path A local file path or web address containing a sound
@returns A Future Sound
**/
public static function loadFromFile(path:String):Future<Sound>
{
#if lime
return AudioBuffer.loadFromFile(path).then(function(audioBuffer)
{
return Future.withValue(fromAudioBuffer(audioBuffer));
});
#else
return cast Future.withError("Cannot load audio file");
#end
}
/**
Creates a new Sound from a set of file paths or web addresses asynchronously.
The audio backend will choose the first compatible file format, and will load the file
it selects in the background.
Progress, completion and error callbacks will be dispatched in the current
thread using callbacks attached to a returned Future object.
@param paths A set of local file paths or web addresses containing sound
@returns A Future Sound
**/
public static function loadFromFiles(paths:Array<String>):Future<Sound>
{
#if lime
return AudioBuffer.loadFromFiles(paths).then(function(audioBuffer)
{
return Future.withValue(fromAudioBuffer(audioBuffer));
});
#else
return cast Future.withError("Cannot load audio files");
#end
}
/**
Load PCM 32-bit floating point sound data from a ByteArray object into a Sound object. The data will be read
from the current ByteArray position and will leave the ByteArray position at the end of the specified sample
length multiplied by either 1 channel or 2 channels if the stereo flag is set once finished.
Starting with Flash Player 11.8, the amount of audio data that can be passed to this function is limited. For
SWF versions >= 21, this function throws an exception if the amount of audio data passed into this function is
more than 1800 seconds. That is, samples / sampleRate should be less than or equal to 1800. For swf versions <
21, the runtime fails silently if the amount of audio data passed in is more than 12000 seconds. This is
provided only for backward compatibility.
This function throws an exception if the ByteArray object does not contain enough data.
@param bytes
@param samples
@param format
@param stereo
@param sampleRate
**/
public function loadPCMFromByteArray(bytes:ByteArray, samples:Int, format:String = "float", stereo:Bool = true, sampleRate:Float = 44100):Void
{
if (bytes == null)
{
dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
return;
}
var bitsPerSample = (format == "float" ? 32 : 16); // "short"
var channels = (stereo ? 2 : 1);
var bytesLength = Std.int(samples * channels * (bitsPerSample / 8));
if (bytes.position > 0 || bytes.length > bytesLength)
{
var copy = new ByteArray(bytesLength);
copy.writeBytes(bytes, bytes.position, bytesLength);
bytes = copy;
}
#if lime
var audioBuffer = new AudioBuffer();
audioBuffer.bitsPerSample = bitsPerSample;
audioBuffer.channels = channels;
audioBuffer.data = new UInt8Array(bytes);
audioBuffer.sampleRate = Std.int(sampleRate);
__buffer = audioBuffer;
dispatchEvent(new Event(Event.COMPLETE));
#else
dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
#end
}
/**
Generates a new SoundChannel object to play back the sound. This method
returns a SoundChannel object, which you access to stop the sound and to
monitor volume.(To control the volume, panning, and balance, access the
SoundTransform object assigned to the sound channel.)
@param startTime The initial position in milliseconds at which playback
should start.
@param loops Defines the number of times a sound loops back to the
`startTime` value before the sound channel
stops playback.
@param sndTransform The initial SoundTransform object assigned to the
sound channel.
@return A SoundChannel object, which you use to control the sound. This
method returns `null` if you have no sound card or if
you run out of available sound channels. The maximum number of
sound channels available at once is 32.
**/
public function play(startTime:Float = 0.0, loops:Int = 0, sndTransform:SoundTransform = null):SoundChannel
{
#if lime
if (__buffer == null || SoundMixer.__soundChannels.length >= SoundMixer.MAX_ACTIVE_CHANNELS)
{
return null;
}
if (sndTransform == null)
{
sndTransform = new SoundTransform();
}
else
{
sndTransform = sndTransform.clone();
}
var pan = SoundMixer.__soundTransform.pan + sndTransform.pan;
if (pan > 1) pan = 1;
else if (pan < -1) pan = -1;
var volume = SoundMixer.__soundTransform.volume * sndTransform.volume;
var source = new AudioSource(__buffer);
source.offset = Std.int(startTime);
if (loops > 1) source.loops = loops - 1;
source.gain = volume;
var position = source.position;
position.x = pan;
position.z = -1 * Math.sqrt(1 - Math.pow(pan, 2));
source.position = position;
return new SoundChannel(source, sndTransform);
#else
return null;
#end
}
// Get & Set Methods
@:noCompletion private function get_id3():ID3Info
{
return new ID3Info();
}
@:noCompletion private function get_length():Float
{
#if lime
if (__buffer != null)
{
#if (js && html5 && howlerjs)
return __buffer.src.duration() * 1000;
#else
if (__buffer.data != null) return (__buffer.data.length >> 0) / __buffer.channels / (__buffer.bitsPerSample >> 3) / __buffer.sampleRate * 1000;
else if (__buffer.__srcVorbisFile != null) {
var x = __buffer.__srcVorbisFile.pcmTotal();
return (x.high * 4294967296. + (x.low >> 0)) / __buffer.sampleRate * 1000;
}
#end
}
#end
return 0;
}
// Event Handlers
#if lime
@:noCompletion private function AudioBuffer_onURLLoad(buffer:AudioBuffer):Void
{
if (buffer == null)
{
dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
}
else
{
__buffer = buffer;
dispatchEvent(new Event(Event.COMPLETE));
}
}
#end
}
#else
typedef Sound = flash.media.Sound;
#end
-400
View File
@@ -1,400 +0,0 @@
package openfl.media;
#if !flash
import haxe.Int64;
import openfl.events.Event;
import openfl.events.EventDispatcher;
#if lime
import lime.media.AudioSource;
import lime.media.openal.AL;
#end
/**
The SoundChannel class controls a sound in an application. Every sound is
assigned to a sound channel, and the application can have multiple sound
channels that are mixed together. The SoundChannel class contains a
`stop()` method, properties for monitoring the amplitude
(volume) of the channel, and a property for assigning a SoundTransform
object to the channel.
@event soundComplete Dispatched when a sound has finished playing.
@see [Playing sounds](https://books.openfl.org/openfl-developers-guide/working-with-sound/playing-sounds.html)
@see `openfl.media.Sound`
**/
#if !openfl_debug
@:fileXml('tags="haxe,release"')
@:noDebug
#end
#if lime_cffi
@:access(lime._internal.backend.native.NativeAudioSource)
@:access(lime.media.AudioSource)
#end
@:access(openfl.media.Sound)
@:access(openfl.media.SoundMixer)
@:final @:keep class SoundChannel extends EventDispatcher
{
/**
The current amplitude(volume) of the left channel, from 0(silent) to 1
(full amplitude).
**/
public var leftPeak(get, null):Float;
/**
The current amplitude(volume) of the right channel, from 0(silent) to 1
(full amplitude).
**/
public var rightPeak(get, null):Float;
/**
When the sound is playing, the `position` property indicates in
milliseconds the current point that is being played in the sound file.
When the sound is stopped or paused, the `position` property
indicates the last point that was played in the sound file.
A common use case is to save the value of the `position`
property when the sound is stopped. You can resume the sound later by
restarting it from that saved position.
If the sound is looped, `position` is reset to 0 at the
beginning of each loop.
**/
public var position(get, set):Float;
/**
The SoundTransform object assigned to the sound channel. A SoundTransform
object includes properties for setting volume, panning, left speaker
assignment, and right speaker assignment.
**/
public var soundTransform(get, set):SoundTransform;
/**
self explanatory
*/
public var loopTime(get, set):Float;
public var endTime(get, set):Null<Float>;
public var pitch(get, set):Float;
public var loops(get, set):Int;
@:noCompletion private var __sound:Sound;
@:noCompletion private var __isValid:Bool;
@:noCompletion private var __soundTransform:SoundTransform;
@:noCompletion private var __lastPeakTime:Float;
@:noCompletion private var __leftPeak:Float;
@:noCompletion private var __rightPeak:Float;
#if lime
@:noCompletion private var __source:AudioSource;
@:noCompletion private var __audioSource(get, set):AudioSource; // forward??? compatibility??
#end
#if openfljs
@:noCompletion private static function __init__()
{
untyped Object.defineProperties(SoundChannel.prototype, {
"position": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_position (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_position (v); }")
},
"soundTransform": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_soundTransform (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_soundTransform (v); }")
},
});
}
#end
@:noCompletion private function new(source:#if lime AudioSource #else Dynamic #end = null, soundTransform:SoundTransform = null):Void
{
super(this);
if (soundTransform != null) __soundTransform = soundTransform;
else __soundTransform = new SoundTransform();
__initAudioSource(source);
SoundMixer.__registerSoundChannel(this);
}
/**
Stops the sound playing in the channel.
**/
public function stop():Void
{
SoundMixer.__unregisterSoundChannel(this);
if (!__isValid) return;
#if lime
__source.stop();
#end
__dispose();
}
@:noCompletion private function __dispose():Void
{
if (!__isValid) return;
#if lime
__source.onComplete.remove(source_onComplete);
__source.dispose();
__source = null;
#end
__isValid = false;
}
@:noCompletion private function __updateTransform():Void
{
this.soundTransform = soundTransform;
}
@:noCompletion private function __updatePeaks(time:Float):Bool
{
if (Math.abs(time - __lastPeakTime) < 8) return false;
__lastPeakTime = time;
#if !macro
if (!__isValid) return false;
var buffer = __source.buffer;
var wordSize = buffer.bitsPerSample >> 3, byteSize = 1 << (buffer.bitsPerSample - 1);
var pos = Math.floor(time * buffer.sampleRate / 1000 * buffer.channels * wordSize);
var leftMin = 0, leftMax = 0, rightMin = 0, rightMax = 0, size = 0, buf;
#if lime_cffi
var backend = __source.__backend, i = 0;
if (backend.streamed) {
size = backend.bufferLengths[i = backend.bufferLengths.length - backend.requestBuffers];
buf = backend.bufferDatas[i].buffer;
pos -= Math.floor(backend.bufferTimes[i] * buffer.sampleRate * buffer.channels * wordSize);
while (pos > size) {
if (++i >= backend.bufferLengths.length) return false;
pos -= size;
buf = backend.bufferDatas[i].buffer;
size = backend.bufferLengths[i];
}
}
else
#end {
buf = buffer.data #if !js .buffer #end;
size = #if js buf.byteLength #else buf.length #end;
}
var s = Math.floor(Math.min(buffer.sampleRate / 80, 512)), c = 0, b;
pos -= pos % (buffer.channels * wordSize);
while (s > 0) {
b = funkin.backend.utils.AudioAnalyzer.getByte(buf, pos, wordSize);
if (c % 2 == 0) ((b > leftMax) ? (leftMax = b) : (if ((b = -b) > leftMin) (leftMin = b)));
else ((b > rightMax) ? (rightMax = b) : (if ((b = -b) > rightMin) (rightMin = b)));
if ((pos += wordSize) >= size) #if lime_cffi {
if (!backend.streamed || ++i >= backend.bufferLengths.length) break;
pos = 0;
buf = backend.bufferDatas[i].buffer;
size = backend.bufferLengths[i];
}
#else break; #end
if (++c > buffer.channels) {
c = 0;
s--;
}
}
if (buffer.channels == 1) __rightPeak = (__leftPeak = (leftMax + leftMin) / byteSize);
else {
__leftPeak = (leftMax + leftMin) / byteSize;
__rightPeak = (rightMax + rightMin) / byteSize;
}
#end
return true;
}
@:noCompletion private function __initAudioSource(source:#if lime AudioSource #else Dynamic #end):Void
{
#if lime
__source = source;
if (__source == null)
{
return;
}
__source.onComplete.add(source_onComplete);
__isValid = true;
__source.play();
#end
}
// Get & Set Methods
@:noCompletion private function get_position():Float
{
if (!__isValid) return 0;
#if lime
return __source.currentTime + __source.offset;
#else
return 0;
#end
}
@:noCompletion private function set_position(value:Float):Float
{
if (!__isValid) return 0;
#if lime
__source.currentTime = value - __source.offset;
#end
return value;
}
@:noCompletion private function get_soundTransform():SoundTransform
{
return __soundTransform.clone();
}
@:noCompletion private function set_soundTransform(value:SoundTransform):SoundTransform
{
if (value != null)
{
__soundTransform.pan = value.pan;
__soundTransform.volume = value.volume;
var pan = SoundMixer.__soundTransform.pan + __soundTransform.pan;
if (pan < -1) pan = -1;
if (pan > 1) pan = 1;
var volume = SoundMixer.__soundTransform.volume * __soundTransform.volume;
if (__isValid)
{
#if lime
// TODO: implement SoundTransform.leftToRight, etc. with Native setAngles?
__source.gain = volume;
__source.pan = pan;
return value;
#end
}
}
return value;
}
@:noCompletion private function get_pitch():Float
{
if (!__isValid) return 1;
#if lime
return __source.pitch;
#else
return 0;
#end
}
@:noCompletion private function set_pitch(value:Float):Float
{
if (!__isValid) return 1;
#if lime
return __source.pitch = value;
#else
return 0;
#end
}
@:noCompletion private function get_loopTime():Float
{
if (!__isValid) return -1;
#if lime
return __source.loopTime;
#else
return -1;
#end
}
@:noCompletion private function set_loopTime(value:Float):Float
{
if (!__isValid) return -1;
#if lime
return __source.loopTime = value;
#else
return -1;
#end
}
@:noCompletion private function get_endTime():Null<Float>
{
if (!__isValid) return null;
#if lime
return __source.length;
#else
return null;
#end
}
@:noCompletion private function set_endTime(value:Null<Float>):Null<Float>
{
if (!__isValid) return null;
#if lime
return __source.length = value;
#else
return null;
#end
}
@:noCompletion private function get_loops():Int
{
if (!__isValid) return 0;
#if lime
return __source.loops;
#else
return 0;
#end
}
@:noCompletion private function set_loops(value:Int):Int
{
if (!__isValid) return 0;
#if lime
return __source.loops = value;
#else
return 0;
#end
}
@:noCompletion private function get_leftPeak():Float
{
__updatePeaks(get_position());
return __leftPeak * (soundTransform == null ? 1 : soundTransform.volume);
}
@:noCompletion private function get_rightPeak():Float
{
__updatePeaks(get_position());
return __rightPeak * (soundTransform == null ? 1 : soundTransform.volume);
}
// Event Handlers
@:noCompletion private function source_onComplete():Void
{
SoundMixer.__unregisterSoundChannel(this);
__dispose();
dispatchEvent(new Event(Event.SOUND_COMPLETE));
}
@:noCompletion private function get___audioSource():AudioSource return __source;
@:noCompletion private function set___audioSource(source:AudioSource):AudioSource return __source = source;
}
#else
typedef SoundChannel = flash.media.SoundChannel;
#end
-859
View File
@@ -1,859 +0,0 @@
package openfl.utils;
#if !macro
import funkin.backend.system.Main;
import funkin.options.Options;
import funkin.backend.system.OptimizedBitmapData;
#end
import openfl.utils._internal.Log;
import openfl.display.BitmapData;
import openfl.display.MovieClip;
import openfl.display.Sprite;
import openfl.events.Event;
import openfl.events.EventDispatcher;
import openfl.media.Sound;
import openfl.text.Font;
#if lime
import lime.app.Promise;
import lime.utils.AssetLibrary as LimeAssetLibrary;
import lime.utils.Assets as LimeAssets;
#end
#if lime_vorbis
import lime.media.AudioBuffer;
import lime.media.vorbis.VorbisFile;
#end
/**
The Assets class provides a cross-platform interface to access
embedded images, fonts, sounds and other resource files.
The contents are populated automatically when an application
is compiled using the OpenFL command-line tools, based on the
contents of the *.xml project file.
For most platforms, the assets are included in the same directory
or package as the application, and the paths are handled
automatically. For web content, the assets are preloaded before
the start of the rest of the application. You can customize the
preloader by extending the `NMEPreloader` class,
and specifying a custom preloader using <window preloader="" />
in the project file.
**/
#if !openfl_debug
@:fileXml('tags="haxe,release"')
@:noDebug
#end
@:access(openfl.display.BitmapData)
@:access(openfl.display.Sprite)
@:access(openfl.text.Font)
@:access(openfl.utils.AssetLibrary)
class Assets
{
public static var cache:IAssetCache = new AssetCache();
@:noCompletion private static var dispatcher:EventDispatcher #if !macro = new EventDispatcher() #end;
private static var libraryBindings:Map<String, AssetLibrary> = new Map();
public static function addEventListener(type:String, listener:Dynamic, useCapture:Bool = false, priority:Int = 0, useWeakReference:Bool = false):Void
{
#if lime
if (!LimeAssets.onChange.has(LimeAssets_onChange))
{
LimeAssets.onChange.add(LimeAssets_onChange);
}
#end
dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
}
public static function dispatchEvent(event:Event):Bool
{
return dispatcher.dispatchEvent(event);
}
/**
Returns whether a specific asset exists
@param id The ID or asset path for the asset
@param type The asset type to match, or null to match any type
@return Whether the requested asset ID and type exists
**/
public static function exists(id:String, type:AssetType = null):Bool
{
#if lime
return LimeAssets.exists(id, cast type);
#else
return false;
#end
}
/**
Gets an instance of an embedded bitmap
@usage var bitmap = new Bitmap (Assets.getBitmapData ("image.png"));
@param id The ID or asset path for the bitmap
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
@param pushToGPU Whenever the image should be immediately pushed to GPU.
@return A new BitmapData object
**/
public static function getBitmapData(id:String, useCache:Bool = true, pushToGPU:Bool = true):BitmapData
{
#if (lime && tools && !display)
if (useCache && cache.enabled && cache.hasBitmapData(id))
{
var bitmapData = cache.getBitmapData(id);
if (isValidBitmapData(bitmapData) && (pushToGPU || bitmapData.readable))
{
return bitmapData;
}
}
var image = LimeAssets.getImage(id, false);
if (image != null)
{
#if flash
var bitmapData = image.src;
#else
var bitmapData:BitmapData = null;
#if !macro if (pushToGPU && !Main.forceGPUOnlyBitmapsOff && Options.gpuOnlyBitmaps) {
bitmapData = new OptimizedBitmapData(0, 0, true, 0);
bitmapData.__fromImage(image);
} else #end {
bitmapData = BitmapData.fromImage(image);
}
#end
if (useCache && cache.enabled)
{
cache.setBitmapData(id, bitmapData);
}
return bitmapData;
}
#end
return null;
}
/**
Gets an instance of an embedded binary asset
@usage var bytes = Assets.getBytes ("file.zip");
@param id The ID or asset path for the asset
@return A new ByteArray object
**/
public static function getBytes(id:String):ByteArray
{
#if lime
return LimeAssets.getBytes(id);
#else
return null;
#end
}
/**
Gets an instance of an embedded font
@usage var fontName = Assets.getFont ("font.ttf").fontName;
@param id The ID or asset path for the font
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
@return A new Font object
**/
public static function getFont(id:String, useCache:Bool = true):Font
{
#if (lime && tools && !display && !macro)
if (useCache && cache.enabled && cache.hasFont(id))
{
return cache.getFont(id);
}
var limeFont = LimeAssets.getFont(id, false);
if (limeFont != null)
{
#if flash
var font = limeFont.src;
#else
var font = new Font();
font.__fromLimeFont(limeFont);
#end
if (useCache && cache.enabled)
{
cache.setFont(id, font);
}
return font;
}
#end
return new Font();
}
public static function getLibrary(name:String):#if lime LimeAssetLibrary #else AssetLibrary #end
{
#if lime
return LimeAssets.getLibrary(name);
#else
return null;
#end
}
/**
Gets an instance of an included MovieClip
@usage var movieClip = Assets.getMovieClip ("library:BouncingBall");
@param id The ID for the MovieClip
@return A new MovieClip object
**/
public static function getMovieClip(id:String):MovieClip
{
#if (lime && tools && !display)
var libraryName = id.substring(0, id.indexOf(":"));
var symbolName = id.substr(id.indexOf(":") + 1);
var limeLibrary = getLibrary(libraryName);
if (limeLibrary != null)
{
if ((limeLibrary is AssetLibrary))
{
var library:AssetLibrary = cast limeLibrary;
if (library.exists(symbolName, cast AssetType.MOVIE_CLIP))
{
if (library.isLocal(symbolName, cast AssetType.MOVIE_CLIP))
{
return library.getMovieClip(symbolName);
}
else
{
Log.error("MovieClip asset \"" + id + "\" exists, but only asynchronously");
return null;
}
}
}
Log.error("There is no MovieClip asset with an ID of \"" + id + "\"");
}
else
{
Log.error("There is no asset library named \"" + libraryName + "\"");
}
#end
return null;
}
public static function getMusic(id:String, useCache:Bool = true, staticFallback:Bool = true):Sound
{
if (useCache && staticFallback && cache.enabled && cache.hasSound(id)) {
var sound = cache.getSound(id);
if (isValidSound(sound)) return sound;
}
/*#if (lime_vorbis && lime > "7.9.0" && !macro)
if (Options.streamedMusic) {
var bytes = getBytes(id);
if (bytes == null) return null;
// TODO: What if it is a WAV or non-Vorbis file?
var vorbisFile = VorbisFile.fromBytes(bytes);
if (vorbisFile != null) return Sound.fromAudioBuffer(AudioBuffer.fromVorbisFile(vorbisFile));
}
#end*/
return if (staticFallback) getSound(id, useCache); else null;
}
/**
Gets the file path (if available) for an asset
@usage var path = Assets.getPath ("file.txt");
@param id The ID or asset path for the asset
@return The path to the asset, or null if it does not exist
**/
public static function getPath(id:String):String
{
#if lime
return LimeAssets.getPath(id);
#else
return null;
#end
}
/**
Gets an instance of an embedded sound
@usage var sound = Assets.getSound ("sound.wav");
@param id The ID or asset path for the sound
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
@return A new Sound object
**/
public static function getSound(id:String, useCache:Bool = true):Sound
{
#if (lime && tools && !display)
if (useCache && cache.enabled && cache.hasSound(id))
{
var sound = cache.getSound(id);
if (isValidSound(sound))
{
return sound;
}
}
var buffer = LimeAssets.getAudioBuffer(id, false);
if (buffer != null)
{
#if flash
var sound = buffer.src;
#else
var sound = Sound.fromAudioBuffer(buffer);
#end
if (useCache && cache.enabled)
{
cache.setSound(id, sound);
}
return sound;
}
#end
return null;
}
/**
Gets an instance of an embedded text asset
@usage var text = Assets.getText ("text.txt");
@param id The ID or asset path for the asset
@return A new String object
**/
public static function getText(id:String):String
{
#if lime
return LimeAssets.getText(id);
#else
return null;
#end
}
public static function hasEventListener(type:String):Bool
{
return dispatcher.hasEventListener(type);
}
public static function hasLibrary(name:String):Bool
{
#if lime
return LimeAssets.hasLibrary(name);
#else
return false;
#end
}
/**
Connects a user-defined class to a related asset class.
This method call is added to the beginning of user-defined class constructors when
the `@:bind` meta-data is used. This allows insertion of related asset resources in
compatible super classes, such as `openfl.display.MovieClip`.
@param className The registered class name of the asset constructor
@param instance The current class instance to be bound (default is null)
@return Whether asset binding was successful
**/
public static function initBinding(className:String, instance:Dynamic = null):Void
{
if (libraryBindings.exists(className))
{
var library = libraryBindings.get(className);
#if !flash
if (instance == null)
{
Sprite.__constructor = function(instance:Sprite)
{
instance.__bind(library, className);
}
}
else
{
Sprite.__constructor = null;
instance.__bind(library, className);
}
#else
// TODO: Consolidate behavior
library.bind(className);
#end
}
else
{
Log.warn("No asset is registered as \"" + className + "\"");
}
}
/**
Returns whether an asset is "local", and therefore can be loaded synchronously
@param id The ID or asset path for the asset
@param type The asset type to match, or null to match any type
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
@return Whether the asset is local
**/
public static function isLocal(id:String, type:AssetType = null, useCache:Bool = true):Bool
{
#if (lime && tools && !display)
if (useCache && cache.enabled)
{
if (type == AssetType.IMAGE || type == null)
{
if (cache.hasBitmapData(id)) return true;
}
if (type == AssetType.FONT || type == null)
{
if (cache.hasFont(id)) return true;
}
if (type == AssetType.SOUND || type == AssetType.MUSIC || type == null)
{
if (cache.hasSound(id)) return true;
}
}
var libraryName = id.substring(0, id.indexOf(":"));
var symbolName = id.substr(id.indexOf(":") + 1);
var library = getLibrary(libraryName);
if (library != null)
{
return library.isLocal(symbolName, cast type);
}
#end
return false;
}
@:analyzer(ignore) private static function isValidBitmapData(bitmapData:BitmapData):Bool
{
#if (lime && tools && !display)
#if flash
try
{
bitmapData.width;
return true;
}
catch (e:Dynamic)
{
return false;
}
#else
return (bitmapData != null && #if !lime_hybrid bitmapData.image != null #else bitmapData.__handle != null #end);
#end
#else
return true;
#end
}
@:noCompletion private static function isValidSound(sound:Sound):Bool
{
#if ((tools && !display) && (cpp || neko || nodejs))
return true;
// return (sound.__handle != null && sound.__handle != 0);
#else
return true;
#end
}
/**
Returns a list of all embedded assets (by type)
@param type The asset type to match, or null to match any type
@return An array of asset ID values
**/
public static function list(type:AssetType = null):Array<String>
{
#if lime
return LimeAssets.list(cast type);
#else
return [];
#end
}
/**
Loads an included bitmap asset asynchronously
@usage Assets.loadBitmapData ("image.png").onComplete (handleImage);
@param id The ID or asset path for the asset
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
@return Returns a Future<BitmapData>
**/
public static function loadBitmapData(id:String, useCache:Null<Bool> = true):Future<BitmapData>
{
if (useCache == null) useCache = true;
#if (lime && tools && !display)
var promise = new Promise<BitmapData>();
if (useCache && cache.enabled && cache.hasBitmapData(id))
{
var bitmapData = cache.getBitmapData(id);
if (isValidBitmapData(bitmapData))
{
promise.complete(bitmapData);
return promise.future;
}
}
LimeAssets.loadImage(id, false).onComplete(function(image)
{
if (image != null)
{
#if flash
var bitmapData = image.src;
#else
var bitmapData:BitmapData = null;
#if !macro if (!Main.forceGPUOnlyBitmapsOff && Options.gpuOnlyBitmaps) {
bitmapData = new OptimizedBitmapData(0, 0, true, 0);
bitmapData.__fromImage(image);
} else #end {
bitmapData = BitmapData.fromImage(image);
}
#end
if (useCache && cache.enabled)
{
cache.setBitmapData(id, bitmapData);
}
promise.complete(bitmapData);
}
else
{
promise.error("[Assets] Could not load Image \"" + id + "\"");
}
}).onError(promise.error).onProgress(promise.progress);
return promise.future;
#else
return Future.withValue(getBitmapData(id, useCache));
#end
}
/**
Loads an included byte asset asynchronously
@usage Assets.loadBytes ("file.zip").onComplete (handleBytes);
@param id The ID or asset path for the asset
@return Returns a Future<ByteArray>
**/
public static function loadBytes(id:String):Future<ByteArray>
{
#if lime
var promise = new Promise<ByteArray>();
var future = LimeAssets.loadBytes(id);
future.onComplete(function(bytes) promise.complete(bytes));
future.onProgress(function(progress, total) promise.progress(progress, total));
future.onError(function(msg) promise.error(msg));
return promise.future;
#else
return Future.withValue(getBytes(id));
#end
}
/**
Loads an included font asset asynchronously
@usage Assets.loadFont ("font.ttf").onComplete (handleFont);
@param id The ID or asset path for the asset
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
@return Returns a Future<Font>
**/
public static function loadFont(id:String, useCache:Null<Bool> = true):Future<Font>
{
if (useCache == null) useCache = true;
#if (lime && tools && !display && !macro)
var promise = new Promise<Font>();
if (useCache && cache.enabled && cache.hasFont(id))
{
promise.complete(cache.getFont(id));
return promise.future;
}
LimeAssets.loadFont(id)
.onComplete(function(limeFont)
{
#if flash
var font = limeFont.src;
#else
var font = new Font();
font.__fromLimeFont(limeFont);
#end
if (useCache && cache.enabled)
{
cache.setFont(id, font);
}
promise.complete(font);
})
.onError(promise.error)
.onProgress(promise.progress);
return promise.future;
#else
return Future.withValue(getFont(id, useCache));
#end
}
/**
Load an included AssetLibrary
@param name The name of the AssetLibrary to load
@return Returns a Future<AssetLibrary>
**/
public static function loadLibrary(name:String):#if java Future<LimeAssetLibrary> #else Future<AssetLibrary> #end
{
#if lime
return LimeAssets.loadLibrary(name).then(function(library)
{
var _library:AssetLibrary = null;
if (library != null)
{
if ((library is AssetLibrary))
{
_library = cast library;
}
else
{
_library = new AssetLibrary();
_library.__proxy = library;
LimeAssets.registerLibrary(name, _library);
}
}
return Future.withValue(_library);
});
#else
return cast Future.withError("Cannot load library");
#end
}
/**
Loads an included music asset asynchronously
@usage Assets.loadMusic ("music.ogg").onComplete (handleMusic);
@param id The ID or asset path for the asset
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
@return Returns a Future<Sound>
**/
public static function loadMusic(id:String, useCache:Null<Bool> = true):Future<Sound>
{
if (useCache == null) useCache = true;
#if lime
#if !html5
var promise = new Promise<Sound>();
LimeAssets.loadAudioBuffer(id, useCache)
.onComplete(function(buffer)
{
if (buffer != null)
{
#if flash
var sound = buffer.src;
#else
var sound = Sound.fromAudioBuffer(buffer);
#end
if (useCache && cache.enabled)
{
cache.setSound(id, sound);
}
promise.complete(sound);
}
else
{
promise.error("[Assets] Could not load Sound \"" + id + "\"");
}
})
.onError(promise.error)
.onProgress(promise.progress);
return promise.future;
#else
var future = new Future<Sound>(function() return getMusic(id, useCache));
return future;
#end
#else
return Future.withValue(getMusic(id, useCache));
#end
}
/**
Loads an included MovieClip asset asynchronously
@usage Assets.loadMovieClip ("library:BouncingBall").onComplete (handleMovieClip);
@param id The ID for the asset
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
@return Returns a Future<MovieClip>
**/
public static function loadMovieClip(id:String):Future<MovieClip>
{
#if (lime && tools && !display)
var promise = new Promise<MovieClip>();
var libraryName = id.substring(0, id.indexOf(":"));
var symbolName = id.substr(id.indexOf(":") + 1);
var limeLibrary = getLibrary(libraryName);
if (limeLibrary != null)
{
if ((limeLibrary is AssetLibrary))
{
var library:AssetLibrary = cast limeLibrary;
if (library.exists(symbolName, cast AssetType.MOVIE_CLIP))
{
promise.completeWith(library.loadMovieClip(symbolName));
return promise.future;
}
}
promise.error("[Assets] There is no MovieClip asset with an ID of \"" + id + "\"");
}
else
{
promise.error("[Assets] There is no asset library named \"" + libraryName + "\"");
}
return promise.future;
#else
return Future.withValue(getMovieClip(id));
#end
}
/**
Loads an included sound asset asynchronously
@usage Assets.loadSound ("sound.wav").onComplete (handleSound);
@param id The ID or asset path for the asset
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
@return Returns a Future<Sound>
**/
public static function loadSound(id:String, useCache:Null<Bool> = true):Future<Sound>
{
if (useCache == null) useCache = true;
#if lime
var promise = new Promise<Sound>();
LimeAssets.loadAudioBuffer(id, useCache)
.onComplete(function(buffer)
{
if (buffer != null)
{
#if flash
var sound = buffer.src;
#else
var sound = Sound.fromAudioBuffer(buffer);
#end
if (useCache && cache.enabled)
{
cache.setSound(id, sound);
}
promise.complete(sound);
}
else
{
promise.error("[Assets] Could not load Sound \"" + id + "\"");
}
})
.onError(promise.error)
.onProgress(promise.progress);
return promise.future;
#else
return Future.withValue(getSound(id, useCache));
#end
}
/**
Loads an included text asset asynchronously
@usage Assets.loadText ("text.txt").onComplete (handleString);
@param id The ID or asset path for the asset
@param useCache (Optional) Whether to allow use of the asset cache (Default: true)
@return Returns a Future<String>
**/
public static function loadText(id:String):Future<String>
{
#if lime
var future = LimeAssets.loadText(id);
return future;
#else
return Future.withValue(getText(id));
#end
}
/**
Registers an AssetLibrary binding for use with @:bind or Assets.bind
@param className The class name to use for the binding
@param method The AssetLibrary responsible for the binding
**/
public static function registerBinding(className:String, library:AssetLibrary):Void
{
libraryBindings.set(className, library);
}
/**
Registers a new AssetLibrary with the Assets class
@param name The name (prefix) to use for the library
@param library An AssetLibrary instance to register
**/
public static function registerLibrary(name:String, library:AssetLibrary):Void
{
#if lime
LimeAssets.registerLibrary(name, library);
#end
}
public static function removeEventListener(type:String, listener:Dynamic, capture:Bool = false):Void
{
dispatcher.removeEventListener(type, listener, capture);
}
@:noCompletion private static function resolveClass(name:String):Class<Dynamic>
{
return Type.resolveClass(name);
}
@:noCompletion private static function resolveEnum(name:String):Enum<Dynamic>
{
var value = Type.resolveEnum(name);
#if flash
if (value == null)
{
return cast Type.resolveClass(name);
}
#end
return value;
}
public static function unloadLibrary(name:String):Void
{
#if lime
LimeAssets.unloadLibrary(name);
#end
}
/**
Unregisters an AssetLibrary binding for use with @:bind or Assets.bind
@param className The class name to use for the binding
@param method The AssetLibrary responsible for the binding
**/
public static function unregisterBinding(className:String, library:AssetLibrary):Void
{
if (libraryBindings.exists(className) && libraryBindings.get(className) == library)
{
libraryBindings.remove(className);
}
}
// Event Handlers
@:noCompletion private static function LimeAssets_onChange():Void
{
dispatchEvent(new Event(Event.CHANGE));
}
}