Remove old library modification codes
This commit is contained in:
@@ -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
|
||||
@@ -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() {}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user