Remove old library modification codes

This commit is contained in:
Ralty
2026-03-01 14:15:02 +07:00
parent 5a176e0ccc
commit 143cc447c8
23 changed files with 0 additions and 12629 deletions
-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
-636
View File
@@ -1,636 +0,0 @@
/*
* Copyright (C)2005-2019 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.
*/
import haxe.iterators.StringIterator;
import haxe.iterators.StringKeyValueIterator;
#if cpp
using cpp.NativeString;
#end
/**
This class provides advanced methods on Strings. It is ideally used with
`using StringTools` and then acts as an [extension](https://haxe.org/manual/lf-static-extension.html)
to the `String` class.
If the first argument to any of the methods is null, the result is
unspecified.
**/
class StringTools {
/**
Encode an URL by using the standard format.
**/
#if (!java && !cpp && !lua && !eval) inline #end public static function urlEncode(s:String):String {
#if flash
return untyped __global__["encodeURIComponent"](s);
#elseif neko
return untyped new String(_urlEncode(s.__s));
#elseif js
return untyped encodeURIComponent(s);
#elseif cpp
return untyped s.__URLEncode();
#elseif java
return postProcessUrlEncode(java.net.URLEncoder.encode(s, "UTF-8"));
#elseif python
return python.lib.urllib.Parse.quote(s, "");
#elseif hl
var len = 0;
var b = @:privateAccess s.bytes.urlEncode(len);
return @:privateAccess String.__alloc__(b, len);
#elseif lua
s = lua.NativeStringTools.gsub(s, "\n", "\r\n");
s = lua.NativeStringTools.gsub(s, "([^%w %-%_%.%~])", function(c) {
return lua.NativeStringTools.format("%%%02X", lua.NativeStringTools.byte(c) + '');
});
s = lua.NativeStringTools.gsub(s, " ", "+");
return s;
#else
return null;
#end
}
#if java
private static function postProcessUrlEncode(s:String):String {
var ret = new StringBuf();
var i = 0, len = s.length;
while (i < len) {
switch (_charAt(s, i++)) {
case '+'.code:
ret.add('%20');
case '%'.code if (i <= len - 2):
var c1 = _charAt(s, i++), c2 = _charAt(s, i++);
switch [c1, c2] {
case ['2'.code, '1'.code]:
ret.addChar('!'.code);
case ['2'.code, '7'.code]:
ret.addChar('\''.code);
case ['2'.code, '8'.code]:
ret.addChar('('.code);
case ['2'.code, '9'.code]:
ret.addChar(')'.code);
case ['7'.code, 'E'.code] | ['7'.code, 'e'.code]:
ret.addChar('~'.code);
case _:
ret.addChar('%'.code);
ret.addChar(cast c1);
ret.addChar(cast c2);
}
case var chr:
ret.addChar(cast chr);
}
}
return ret.toString();
}
#end
/**
Decode an URL using the standard format.
**/
#if (!java && !cpp && !lua && !eval) inline #end public static function urlDecode(s:String):String {
#if flash
return untyped __global__["decodeURIComponent"](s.split("+").join(" "));
#elseif neko
return untyped new String(_urlDecode(s.__s));
#elseif js
return untyped decodeURIComponent(s.split("+").join(" "));
#elseif cpp
return untyped s.__URLDecode();
#elseif java
try
return java.net.URLDecoder.decode(s, "UTF-8")
catch (e:Dynamic)
throw e;
#elseif python
return python.lib.urllib.Parse.unquote(s);
#elseif hl
var len = 0;
var b = @:privateAccess s.bytes.urlDecode(len);
return @:privateAccess String.__alloc__(b, len);
#elseif lua
s = lua.NativeStringTools.gsub(s, "+", " ");
s = lua.NativeStringTools.gsub(s, "%%(%x%x)", function(h) {
return lua.NativeStringTools.char(lua.Lua.tonumber(h, 16));
});
s = lua.NativeStringTools.gsub(s, "\r\n", "\n");
return s;
#else
return null;
#end
}
/**
Escapes HTML special characters of the string `s`.
The following replacements are made:
- `&` becomes `&amp`;
- `<` becomes `&lt`;
- `>` becomes `&gt`;
If `quotes` is true, the following characters are also replaced:
- `"` becomes `&quot`;
- `'` becomes `&#039`;
**/
public static function htmlEscape(s:String, ?quotes:Bool):String {
var buf = new StringBuf();
for (code in #if neko iterator(s) #else new haxe.iterators.StringIteratorUnicode(s) #end) {
switch (code) {
case '&'.code:
buf.add("&amp;");
case '<'.code:
buf.add("&lt;");
case '>'.code:
buf.add("&gt;");
case '"'.code if (quotes):
buf.add("&quot;");
case '\''.code if (quotes):
buf.add("&#039;");
case _:
buf.addChar(code);
}
}
return buf.toString();
}
/**
Unescapes HTML special characters of the string `s`.
This is the inverse operation to htmlEscape, i.e. the following always
holds: `htmlUnescape(htmlEscape(s)) == s`
The replacements follow:
- `&amp;` becomes `&`
- `&lt;` becomes `<`
- `&gt;` becomes `>`
- `&quot;` becomes `"`
- `&#039;` becomes `'`
**/
public static function htmlUnescape(s:String):String {
return s.split("&gt;")
.join(">")
.split("&lt;")
.join("<")
.split("&quot;")
.join('"')
.split("&#039;")
.join("'")
.split("&amp;")
.join("&");
}
/**
Returns `true` if `s` contains `value` and `false` otherwise.
When `value` is `null`, the result is unspecified.
**/
public static inline function contains(s:String, value:String):Bool {
#if (js && js_es >= 6)
return (cast s).includes(value);
#else
return s.indexOf(value) != -1;
#end
}
/**
Tells if the string `s` starts with the string `start`.
If `start` is `null`, the result is unspecified.
If `start` is the empty String `""`, the result is true.
**/
public static #if (java || python || (js && js_es >= 6)) inline #end function startsWith(s:String, start:String):Bool {
#if java
return (cast s : java.NativeString).startsWith(start);
#elseif hl
return @:privateAccess (s.length >= start.length && s.bytes.compare(0, start.bytes, 0, start.length << 1) == 0);
#elseif python
return python.NativeStringTools.startswith(s, start);
#elseif (js && js_es >= 6)
return (cast s).startsWith(start);
#elseif lua
return untyped __lua__("{0}:sub(1, #{1}) == {1}", s, start);
#elseif cpp
return untyped s.__StartsWith(start);
#else
return (s.length >= start.length && s.indexOf(start, 0) == 0);
#end
}
/**
Tells if the string `s` ends with the string `end`.
If `end` is `null`, the result is unspecified.
If `end` is the empty String `""`, the result is true.
**/
public static #if (java || python || (js && js_es >= 6)) inline #end function endsWith(s:String, end:String):Bool {
#if java
return (cast s : java.NativeString).endsWith(end);
#elseif hl
var elen = end.length;
var slen = s.length;
return @:privateAccess (slen >= elen && s.bytes.compare((slen - elen) << 1, end.bytes, 0, elen << 1) == 0);
#elseif python
return python.NativeStringTools.endswith(s, end);
#elseif (js && js_es >= 6)
return (cast s).endsWith(end);
#elseif lua
return end == "" || untyped __lua__("{0}:sub(-#{1}) == {1}", s, end);
#elseif cpp
return untyped s.__EndsWith(end);
#else
var elen = end.length;
var slen = s.length;
return (slen >= elen && s.lastIndexOf(end, (slen - elen)) == (slen - elen));
#end
}
/**
Tells if the character in the string `s` at position `pos` is a space.
A character is considered to be a space character if its character code
is 9,10,11,12,13 or 32.
If `s` is the empty String `""`, or if pos is not a valid position within
`s`, the result is false.
**/
public static function isSpace(s:String, pos:Int):Bool {
#if (python || lua)
if (s.length == 0 || pos < 0 || pos >= s.length)
return false;
#end
var c = StringTools.fastCodeAt(s, pos);
return (c > 8 && c < 14) || c == 32;
}
/**
Removes leading space characters of `s`.
This function internally calls `isSpace()` to decide which characters to
remove.
If `s` is the empty String `""` or consists only of space characters, the
result is the empty String `""`.
**/
public inline static function ltrim(s:String):String {
var l = s.length;
var r = 0;
while (r < l && isSpace(s, r)) {
r++;
}
if (r > 0)
return s.substr(r, l - r);
else
return s;
}
/**
Removes trailing space characters of `s`.
This function internally calls `isSpace()` to decide which characters to
remove.
If `s` is the empty String `""` or consists only of space characters, the
result is the empty String `""`.
**/
public inline static function rtrim(s:String):String {
var l = s.length;
var r = 0;
while (r < l && isSpace(s, l - r - 1)) {
r++;
}
if (r > 0) {
return s.substr(0, l - r);
} else {
return s;
}
}
/**
Removes leading and trailing space characters of `s`.
This is a convenience function for `ltrim(rtrim(s))`.
**/
public #if java inline #end static function trim(s:String):String {
#if java
return (cast s : java.NativeString).trim();
#else
//return ltrim(rtrim(s));
var l = s.length;
var start = 0;
var end = l;
// Trim leading spaces
while (start < l && isSpace(s, start)) {
start++;
}
// Trim trailing spaces
while (end > start && isSpace(s, end - 1)) {
end--;
}
return (start > 0 || end < l) ? s.substr(start, end - start) : s;
#end
}
/**
Concatenates `c` to `s` until `s.length` is at least `l`.
If `c` is the empty String `""` or if `l` does not exceed `s.length`,
`s` is returned unchanged.
If `c.length` is 1, the resulting String length is exactly `l`.
Otherwise the length may exceed `l`.
If `c` is null, the result is unspecified.
**/
public static function lpad(s:String, c:String, l:Int):String {
if (c.length <= 0)
return s;
var buf = new StringBuf();
l -= s.length;
while (buf.length < l) {
buf.add(c);
}
buf.add(s);
return buf.toString();
}
/**
Appends `c` to `s` until `s.length` is at least `l`.
If `c` is the empty String `""` or if `l` does not exceed `s.length`,
`s` is returned unchanged.
If `c.length` is 1, the resulting String length is exactly `l`.
Otherwise the length may exceed `l`.
If `c` is null, the result is unspecified.
**/
public static function rpad(s:String, c:String, l:Int):String {
if (c.length <= 0)
return s;
var buf = new StringBuf();
buf.add(s);
while (buf.length < l) {
buf.add(c);
}
return buf.toString();
}
/**
Replace all occurrences of the String `sub` in the String `s` by the
String `by`.
If `sub` is the empty String `""`, `by` is inserted after each character
of `s` except the last one. If `by` is also the empty String `""`, `s`
remains unchanged.
If `sub` or `by` are null, the result is unspecified.
**/
public static function replace(s:String, sub:String, by:String):String {
#if java
if (sub.length == 0)
return s.split(sub).join(by);
else
return (cast s : java.NativeString).replace(sub, by);
#else
return s.split(sub).join(by);
#end
}
/**
Encodes `n` into a hexadecimal representation.
If `digits` is specified, the resulting String is padded with "0" until
its `length` equals `digits`.
**/
public static function hex(n:Int, ?digits:Int) {
#if flash
var n:UInt = n;
var s:String = untyped n.toString(16);
s = s.toUpperCase();
#else
var s = "";
var hexChars = "0123456789ABCDEF";
do {
s = hexChars.charAt(n & 15) + s;
n >>>= 4;
} while (n > 0);
#end
#if python
if (digits != null && s.length < digits) {
var diff = digits - s.length;
for (_ in 0...diff) {
s = "0" + s;
}
}
#else
if (digits != null)
while (s.length < digits)
s = "0" + s;
#end
return s;
}
/**
Returns the character code at position `index` of String `s`, or an
end-of-file indicator at if `position` equals `s.length`.
This method is faster than `String.charCodeAt()` on some platforms, but
the result is unspecified if `index` is negative or greater than
`s.length`.
End of file status can be checked by calling `StringTools.isEof()` with
the returned value as argument.
This operation is not guaranteed to work if `s` contains the `\0`
character.
**/
public static #if !eval inline #end function fastCodeAt(s:String, index:Int):Int {
#if neko
return untyped __dollar__sget(s.__s, index);
#elseif cpp
return untyped s.cca(index);
#elseif flash
return untyped s.cca(index);
#elseif java
return (index < s.length) ? cast(_charAt(s, index), Int) : -1;
#elseif js
return (cast s).charCodeAt(index);
#elseif python
return if (index >= s.length) -1 else python.internal.UBuiltins.ord(python.Syntax.arrayAccess(s, index));
#elseif hl
return @:privateAccess s.bytes.getUI16(index << 1);
#elseif lua
#if lua_vanilla
return lua.NativeStringTools.byte(s, index + 1);
#else
return lua.lib.luautf8.Utf8.byte(s, index + 1);
#end
#else
return untyped s.cca(index);
#end
}
/**
Returns the character code at position `index` of String `s`, or an
end-of-file indicator at if `position` equals `s.length`.
This method is faster than `String.charCodeAt()` on some platforms, but
the result is unspecified if `index` is negative or greater than
`s.length`.
This operation is not guaranteed to work if `s` contains the `\0`
character.
**/
public static #if !eval inline #end function unsafeCodeAt(s:String, index:Int):Int {
#if neko
return untyped __dollar__sget(s.__s, index);
#elseif cpp
return untyped s.cca(index);
#elseif flash
return untyped s.cca(index);
#elseif java
return cast(_charAt(s, index), Int);
#elseif js
return (cast s).charCodeAt(index);
#elseif python
return python.internal.UBuiltins.ord(python.Syntax.arrayAccess(s, index));
#elseif hl
return @:privateAccess s.bytes.getUI16(index << 1);
#elseif lua
#if lua_vanilla
return lua.NativeStringTools.byte(s, index + 1);
#else
return lua.lib.luautf8.Utf8.byte(s, index + 1);
#end
#else
return untyped s.cca(index);
#end
}
/**
Returns an iterator of the char codes.
Note that char codes may differ across platforms because of different
internal encoding of strings in different runtimes.
For the consistent cross-platform UTF8 char codes see `haxe.iterators.StringIteratorUnicode`.
**/
public static inline function iterator(s:String):StringIterator {
return new StringIterator(s);
}
/**
Returns an iterator of the char indexes and codes.
Note that char codes may differ across platforms because of different
internal encoding of strings in different of runtimes.
For the consistent cross-platform UTF8 char codes see `haxe.iterators.StringKeyValueIteratorUnicode`.
**/
public static inline function keyValueIterator(s:String):StringKeyValueIterator {
return new StringKeyValueIterator(s);
}
/**
Tells if `c` represents the end-of-file (EOF) character.
**/
@:noUsing public static inline function isEof(c:Int):Bool {
#if (flash || cpp || hl)
return c == 0;
#elseif js
return c != c; // fast NaN
#elseif (neko || lua || eval)
return c == null;
#elseif (java || python)
return c == -1;
#else
return false;
#end
}
/**
Returns a String that can be used as a single command line argument
on Unix.
The input will be quoted, or escaped if necessary.
**/
@:noCompletion
@:deprecated('StringTools.quoteUnixArg() is deprecated. Use haxe.SysTools.quoteUnixArg() instead.')
public static function quoteUnixArg(argument:String):String {
return inline haxe.SysTools.quoteUnixArg(argument);
}
/**
Character codes of the characters that will be escaped by `quoteWinArg(_, true)`.
**/
@:noCompletion
@:deprecated('StringTools.winMetaCharacters is deprecated. Use haxe.SysTools.winMetaCharacters instead.')
public static var winMetaCharacters:Array<Int> = cast haxe.SysTools.winMetaCharacters;
/**
Returns a String that can be used as a single command line argument
on Windows.
The input will be quoted, or escaped if necessary, such that the output
will be parsed as a single argument using the rule specified in
http://msdn.microsoft.com/en-us/library/ms880421
Examples:
```haxe
quoteWinArg("abc") == "abc";
quoteWinArg("ab c") == '"ab c"';
```
**/
@:noCompletion
@:deprecated('StringTools.quoteWinArg() is deprecated. Use haxe.SysTools.quoteWinArg() instead.')
public static function quoteWinArg(argument:String, escapeMetaCharacters:Bool):String {
return inline haxe.SysTools.quoteWinArg(argument, escapeMetaCharacters);
}
#if java
private static inline function _charAt(str:String, idx:Int):java.StdTypes.Char16
return (cast str : java.NativeString).charAt(idx);
#end
#if neko
private static var _urlEncode = neko.Lib.load("std", "url_encode", 1);
private static var _urlDecode = neko.Lib.load("std", "url_decode", 1);
#end
#if utf16
static inline var MIN_SURROGATE_CODE_POINT = 65536;
static inline function utf16CodePointAt(s:String, index:Int):Int {
var c = StringTools.fastCodeAt(s, index);
if (c >= 0xD800 && c <= 0xDBFF) {
c = ((c - 0xD7C0) << 10) | (StringTools.fastCodeAt(s, index + 1) & 0x3FF);
}
return c;
}
#end
}
-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;
}
}
@@ -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;
}
}
-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
}
}
-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