From 30177ac0aef002ab0622330f8d8ac2ab1f6043c6 Mon Sep 17 00:00:00 2001 From: grapefrukt Date: Tue, 8 May 2012 14:57:16 +0200 Subject: [PATCH] Initial commit --- com/grapefrukt/Timestep.as | 64 +++++ com/grapefrukt/debug/FPS.as | 75 ++++++ com/grapefrukt/debug/MEM.as | 41 ++++ com/grapefrukt/debug/TXT.as | 46 ++++ com/grapefrukt/display/FadedBackground.as | 93 ++++++++ .../display/utilities/ColorConverter.as | 139 +++++++++++ .../display/utilities/DisplayListTraverser.as | 37 +++ .../display/utilities/DrawGeometry.as | 221 ++++++++++++++++++ com/grapefrukt/input/LazyKeyboard.as | 67 ++++++ com/grapefrukt/input/OneButtonInput.as | 117 ++++++++++ com/grapefrukt/math/MathUtil.as | 38 +++ com/grapefrukt/sound/MP3LoopBase.as | 121 ++++++++++ com/grapefrukt/sound/MP3LoopController.as | 63 +++++ com/grapefrukt/sound/MP3LoopEmbedded.as | 25 ++ com/grapefrukt/sound/MP3LoopGapless.as | 96 ++++++++ com/grapefrukt/sound/SoundUtil.as | 181 ++++++++++++++ com/grapefrukt/sound/event/MP3LoopEvent.as | 34 +++ com/grapefrukt/string/StringUtil.as | 28 +++ 18 files changed, 1486 insertions(+) create mode 100644 com/grapefrukt/Timestep.as create mode 100644 com/grapefrukt/debug/FPS.as create mode 100644 com/grapefrukt/debug/MEM.as create mode 100644 com/grapefrukt/debug/TXT.as create mode 100644 com/grapefrukt/display/FadedBackground.as create mode 100644 com/grapefrukt/display/utilities/ColorConverter.as create mode 100644 com/grapefrukt/display/utilities/DisplayListTraverser.as create mode 100644 com/grapefrukt/display/utilities/DrawGeometry.as create mode 100644 com/grapefrukt/input/LazyKeyboard.as create mode 100644 com/grapefrukt/input/OneButtonInput.as create mode 100644 com/grapefrukt/math/MathUtil.as create mode 100644 com/grapefrukt/sound/MP3LoopBase.as create mode 100644 com/grapefrukt/sound/MP3LoopController.as create mode 100644 com/grapefrukt/sound/MP3LoopEmbedded.as create mode 100644 com/grapefrukt/sound/MP3LoopGapless.as create mode 100644 com/grapefrukt/sound/SoundUtil.as create mode 100644 com/grapefrukt/sound/event/MP3LoopEvent.as create mode 100644 com/grapefrukt/string/StringUtil.as diff --git a/com/grapefrukt/Timestep.as b/com/grapefrukt/Timestep.as new file mode 100644 index 0000000..270f4e8 --- /dev/null +++ b/com/grapefrukt/Timestep.as @@ -0,0 +1,64 @@ +package com.grapefrukt { + import flash.utils.getTimer; + /** + * ... + * @author Martin Jonasson (m@grapefrukt.com) + */ + public class Timestep { + + private var _game_speed :Number = 1; + private var _target_frametime :Number = 0.6; + private var _max_speed :Number = 3; + private var _smoothing :Number = .5; + + private var _real_speed :Number = 0.0; + private var _last_frame_time :Number = 0.0; + private var _delta :Number = 0.0; + + /** + * Initializes the timestepper + * @param fps The target framerate you wish to maintain + * @param gameSpeed The game's speed, useful for slowdown effects or general speed tweaking. 1 = 100% speed. + * @param maxSpeed The maximum size of a timeDelta, steps will not be bigger than this + * @param smoothing How much to smooth the step size across ticks, 1 gives old value full priority (value will never change), 0 means no smoothing, so new value will be used. + */ + public function Timestep(fps:int = 60, gameSpeed:Number = 1.0, maxSpeed:Number = 3.0, smoothing:Number = 0.5) { + _target_frametime = 1000 / fps; + _smoothing = smoothing; + this.gameSpeed = gameSpeed; + this.maxSpeed = maxSpeed; + } + + /** + * Call this function every frame to get a updated timeDelta + * @return timeDelta + */ + public function tick():Number { + _real_speed = (getTimer() - _last_frame_time) / _target_frametime; + _last_frame_time = getTimer(); + + if (_real_speed > _max_speed) _real_speed = _max_speed; + + _delta -= (_delta - _real_speed) * (1 - _smoothing); + + return _delta * _game_speed; + } + + public function get timeDelta():Number { return _delta * _game_speed; } + + public function get maxSpeed():Number { return _max_speed; } + public function set maxSpeed(value:Number):void { _max_speed = value; } + + public function get gameSpeed():Number { return _game_speed; } + public function set gameSpeed(value:Number):void { _game_speed = value; } + + public function get smoothing():Number { return _smoothing; } + public function set smoothing(value:Number):void { + if (value > 1) value = 1; + if (value < 0) value = 0; + _smoothing = value; + } + + } + +} \ No newline at end of file diff --git a/com/grapefrukt/debug/FPS.as b/com/grapefrukt/debug/FPS.as new file mode 100644 index 0000000..741c5c6 --- /dev/null +++ b/com/grapefrukt/debug/FPS.as @@ -0,0 +1,75 @@ +package com.grapefrukt.debug +{ + import flash.display.Sprite; + import flash.text.TextField; + import flash.text.TextFormat; + import flash.text.TextFieldAutoSize; + import flash.utils.getTimer; + import flash.events.Event; + + public class FPS extends Sprite + { + private var frametimes:Array; + private var last_tick:uint = 0; + private var fps_text:TextField; + private var target_fps:Number = 0; + private var manual_update:Boolean = false; + private var fps_label:String = "FPS"; + + private var _t:Number = 0; + private var _sum:Number = 0; + private var _average:Number = 0; + + private var insert_pos:int = 0; + + private var BUFFER_SIZE:uint = 0; + + public var speedFraction:Number = 1; + + public function FPS(_target_fps:Number, _label:String = "fps", _manual_update:Boolean = false, textColor:int = 0xffffff) { + target_fps = _target_fps; + BUFFER_SIZE = target_fps; + + fps_label = _label; + manual_update = _manual_update; + + frametimes = new Array(); + + for (var i:int = 0; i < BUFFER_SIZE; i++) frametimes.push(0); + + last_tick = getTimer(); + + var textformat:TextFormat = new TextFormat("Arial"); + + fps_text = new TextField(); + fps_text.textColor = textColor; + fps_text.selectable = false; + fps_text.autoSize = TextFieldAutoSize.LEFT; + fps_text.setTextFormat(textformat); + fps_text.defaultTextFormat = textformat; + addChild(fps_text); + + if(manual_update == false) addEventListener(Event.ENTER_FRAME, tick); + } + + public function tick(event:Event = null):void { + _t = getTimer() - last_tick; + last_tick = getTimer(); + + frametimes[insert_pos] = _t; + insert_pos++; + if (insert_pos > BUFFER_SIZE) insert_pos = 0; + + _sum = 0; + for each (var i:uint in frametimes) _sum += i; + + _average = _sum / BUFFER_SIZE; + + speedFraction = (1000/target_fps)/_average; + + fps_text.text = fps_label + ": " + Number(1000 / _average).toFixed(1) + " (" + Number(speedFraction*100).toFixed(0) + "%)"; + } + + public function get average():Number { return _average; } + } +} \ No newline at end of file diff --git a/com/grapefrukt/debug/MEM.as b/com/grapefrukt/debug/MEM.as new file mode 100644 index 0000000..32e7acc --- /dev/null +++ b/com/grapefrukt/debug/MEM.as @@ -0,0 +1,41 @@ +package com.grapefrukt.debug +{ + import flash.display.Sprite; + import flash.text.TextField; + import flash.text.TextFormat; + import flash.text.TextFieldAutoSize; + import flash.utils.getTimer; + import flash.events.Event; + import flash.system.System; + import flash.utils.Timer; + import flash.events.TimerEvent; + + public class MEM extends Sprite + { + + private var mem_text:TextField; + private var update_timer:Timer; + + public function MEM(color:uint = 0xffffff) { + + var textformat:TextFormat = new TextFormat("Arial"); + + mem_text = new TextField(); + mem_text.textColor = color; + mem_text.selectable = false; + mem_text.autoSize = TextFieldAutoSize.LEFT; + mem_text.setTextFormat(textformat); + mem_text.defaultTextFormat = textformat; + addChild(mem_text); + + update_timer = new Timer(250); + update_timer.start(); + + update_timer.addEventListener(TimerEvent.TIMER, onTimerCallback); + } + + private function onTimerCallback(event:Event):void { + mem_text.text = "mem: " + Number(System.totalMemory / (1024 * 1024)).toFixed(1) + " MB"; + } + } +} \ No newline at end of file diff --git a/com/grapefrukt/debug/TXT.as b/com/grapefrukt/debug/TXT.as new file mode 100644 index 0000000..dab47a1 --- /dev/null +++ b/com/grapefrukt/debug/TXT.as @@ -0,0 +1,46 @@ +package com.grapefrukt.debug { + + import flash.display.Sprite; + import flash.text.TextField; + import flash.text.TextFieldAutoSize; + import flash.text.TextFormat; + + public class TXT extends Sprite { + + private var txt_text:TextField; + + public function TXT(color:uint = 0xffffff, size:uint = 12){ + + var textformat:TextFormat = new TextFormat("Arial", size); + + txt_text = new TextField(); + txt_text.textColor = color; + txt_text.selectable = false; + //txt_text.autoSize = TextFieldAutoSize.LEFT; + txt_text.setTextFormat(textformat); + txt_text.defaultTextFormat = textformat; + addChild(txt_text); + + mouseChildren = false; + mouseEnabled = false; + + txt_text.mouseWheelEnabled = true; + + txt_text.height = 580; + txt_text.width = 400; + } + + public function setText(str:String):void { + txt_text.text = str + "\n"; + } + + public function appendText(str:String):void { + txt_text.appendText(str + "\n"); + } + + public function set selectable(value:Boolean):void { + txt_text.selectable = value; + } + + } +} \ No newline at end of file diff --git a/com/grapefrukt/display/FadedBackground.as b/com/grapefrukt/display/FadedBackground.as new file mode 100644 index 0000000..0c0082b --- /dev/null +++ b/com/grapefrukt/display/FadedBackground.as @@ -0,0 +1,93 @@ +package com.grapefrukt.display { + + import com.grapefrukt.display.utilities.ColorConverter; + import flash.display.GradientType; + import flash.display.Shape; + import flash.display.SpreadMethod; + import flash.display.Sprite; + import flash.events.Event; + import flash.geom.Matrix; + + /** + * ... + * @author Martin Jonasson + */ + public class FadedBackground extends Sprite { + + private var _base_color :uint = 0x619928; + private var _faded_color :uint; + private var _gfx :Shape; + private var _baseWidth :Number = 0; + private var _baseHeight :Number = 0; + + private var _stageNormalWidth :Number = 0; + private var _stageNormalHeight :Number = 0; + private var _ratios:Array; + + public function FadedBackground(newColor:uint, stageNormalWidth:Number, stageNormalHeight:Number, fadedColor:uint = 0x000000, ratios:Array = null):void { + _base_color = newColor; + _stageNormalWidth = stageNormalWidth; + _stageNormalHeight = stageNormalHeight; + + if(fadedColor == 0x000000){ + var fadedColorHSB:Array = ColorConverter.UINTtoHSB(_base_color); + fadedColorHSB[2] *= 0.3; + fadedColor = ColorConverter.HSBtoUINT(fadedColorHSB[0], fadedColorHSB[1], fadedColorHSB[2]); + } + _faded_color = fadedColor; + + if(!ratios) ratios = [0x00, 0xFF] + _ratios = ratios; + + _gfx = new Shape(); + addChild(_gfx); + + addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler); + } + + private function resizeHandler(event:Event):void { + var stageRatio:Number = stage.stageWidth / stage.stageHeight; + var bkgRatio:Number = _baseWidth / _baseWidth; + + if(stageRatio >= bkgRatio){ + width = stage.stageWidth; + height = width / _baseWidth * _baseWidth; + } else { + height = stage.stageHeight; + width = height / _baseWidth * _baseWidth; + } + + } + + private function addedToStageHandler(event:Event):void { + removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler); + redraw(); + resizeHandler(null); + stage.addEventListener(Event.RESIZE, resizeHandler); + } + + public function redraw(newColor:int = -1):void { + if(newColor >= 0) _base_color = newColor; + + graphics.clear(); + var fillType:String = GradientType.RADIAL; + var colors:Array = [_base_color, _faded_color]; + var alphas:Array = [1, 1]; + var ratios:Array = _ratios; + var matr:Matrix = new Matrix(); + matr.createGradientBox(300, 300, 0, -50, -50); + var spreadMethod:String = SpreadMethod.PAD; + _gfx.graphics.beginGradientFill(fillType, colors, alphas, ratios, matr, spreadMethod, null); + _gfx.graphics.drawRect(0, 0, 200, 200); + + _baseWidth = _gfx.width; + _baseWidth = _gfx.height; + + _gfx.x = -_baseWidth / 2; + _gfx.y = -_baseWidth / 2; + x = _stageNormalWidth / 2; + y = _stageNormalHeight / 2; + } + } + +} \ No newline at end of file diff --git a/com/grapefrukt/display/utilities/ColorConverter.as b/com/grapefrukt/display/utilities/ColorConverter.as new file mode 100644 index 0000000..8c33571 --- /dev/null +++ b/com/grapefrukt/display/utilities/ColorConverter.as @@ -0,0 +1,139 @@ +package com.grapefrukt.display.utilities { + // Stolen from http://d.hatena.ne.jp/flashrod/20060930#1159622027 + // Modded by grapefrukt + + public class ColorConverter { + + /** + * Converts a RGB uint value in to it's discrete r, g and b parts + * @param color The color you want to split + * @return RGB ([0] = red, [1] = green, [2] = blue) + */ + public static function UINTtoRGB(color:uint):Array { + var r:uint = (color >> 16) & 0xFF; + var g:uint = (color >> 8) & 0xFF; + var b:uint = color & 0xFF; + + return [r, g, b]; + } + + /** + * Converts a RGB uint value into hue, saturation and brightness + * @param color The color you want to split + * @return HSB ([0]=hue, [1]=saturation, [2]=brightness) + */ + public static function UINTtoHSB(color:uint):Array { + var rgb:Array = UINTtoRGB(color); + return RGBtoHSB(rgb[0], rgb[1], rgb[2]); + } + + /** + * Converts three distinct HSB values to three separate RGB values + * @param hue (0.0 - 1.0) + * @param saturation (0.0 - 1.0) + * @param brightness (0.0 - 1.0) + * @return RGB ([0] = red, [1] = green, [2] = blue) + */ + public static function HSBtoRGB(hue:Number, saturation:Number, brightness:Number):Array { + return UINTtoRGB(HSBtoUINT(hue, saturation, brightness)); + } + + /** + * Merges three distinct RGB values into a single uint + * @param r Red channel (0-255) + * @param g Green channel (0-255) + * @param b Blue channel (0-255) + * @return The color as an uint + */ + public static function RGBtoUINT(r:int, g:int, b:int):uint { + return (r << 16) | (g << 8) | (b << 0); + } + + /** + * Converts three distinct RGB values to three HSB values + * @param r Red channel (0-255) + * @param g Green channel (0-255) + * @param b Blue channel (0-255) + * @return HSB ([0]=hue, [1]=saturation, [2]=brightness) + */ + public static function RGBtoHSB(r:int, g:int, b:int):Array { + var cmax:Number = Math.max(r, g, b); + var cmin:Number = Math.min(r, g, b); + var brightness:Number = cmax / 255.0; + var hue:Number = 0; + var saturation:Number = (cmax != 0) ? (cmax - cmin) / cmax : 0; + if (saturation != 0) { + var redc:Number = (cmax - r) / (cmax - cmin); + var greenc:Number = (cmax - g) / (cmax - cmin); + var bluec:Number = (cmax - b) / (cmax - cmin); + if (r == cmax) { + hue = bluec - greenc; + } else if (g == cmax) { + hue = 2.0 + redc - bluec; + } else { + hue = 4.0 + greenc - redc; + } + hue = hue / 6.0; + if (hue < 0) { + hue = hue + 1.0; + } + } + return [hue, saturation, brightness]; + } + + /** + * Converts three distinct HSB values to an RGB-uint + * @param hue ?????? (0.0 - 1.0) + * @param saturation ???? (0.0 - 1.0) + * @param brightness ???? (0.0 - 1.0) + * @return RGB + */ + public static function HSBtoUINT(hue:Number, saturation:Number, brightness:Number):uint { + var r:int = 0; + var g:int = 0; + var b:int = 0; + if (saturation == 0) { + r = g = b = brightness * 255.0 + 0.5; + } else { + var h:Number = (hue - int(hue)) * 6.0; + var f:Number = h - int(h); + var p:Number = brightness * (1.0 - saturation); + var q:Number = brightness * (1.0 - saturation * f); + var t:Number = brightness * (1.0 - (saturation * (1.0 - f))); + switch (int(h)) { + case 0: + r = brightness * 255.0 + 0.5; + g = t * 255.0 + 0.5; + b = p * 255.0 + 0.5; + break; + case 1: + r = q * 255.0 + 0.5; + g = brightness * 255.0 + 0.5; + b = p * 255.0 + 0.5; + break; + case 2: + r = p * 255.0 + 0.5; + g = brightness * 255.0 + 0.5; + b = t * 255.0 + 0.5; + break; + case 3: + r = p * 255.0 + 0.5; + g = q * 255.0 + 0.5; + b = brightness * 255.0 + 0.5; + break; + case 4: + r = t * 255.0 + 0.5; + g = p * 255.0 + 0.5; + b = brightness * 255.0 + 0.5; + break; + case 5: + r = brightness * 255.0 + 0.5; + g = p * 255.0 + 0.5; + b = q * 255.0 + 0.5; + break; + } + } + return (r << 16) | (g << 8) | (b << 0); + } + } +} \ No newline at end of file diff --git a/com/grapefrukt/display/utilities/DisplayListTraverser.as b/com/grapefrukt/display/utilities/DisplayListTraverser.as new file mode 100644 index 0000000..1fbdfe0 --- /dev/null +++ b/com/grapefrukt/display/utilities/DisplayListTraverser.as @@ -0,0 +1,37 @@ +package com.grapefrukt.display.utilities { + import com.grapefrukt.string.StringUtil; + import flash.display.DisplayObject; + import flash.display.DisplayObjectContainer; + /** + * ... + * @author Martin Jonasson (m@webbfarbror.se) + */ + public class DisplayListTraverser { + + public static function explore(root:DisplayObjectContainer):void { + traverse(root); + } + + private static function traverse(node:DisplayObjectContainer, level:int = 0, childCount:int = 0):int { + var displayObject :DisplayObject; + var displayObjectContainer :DisplayObjectContainer; + + for (var i:int = 0; i < node.numChildren; i++) { + + displayObject = node.getChildAt(i) as DisplayObject; + displayObjectContainer = node.getChildAt(i) as DisplayObjectContainer; + + trace(StringUtil.padStart("", level, "\t") + node.getChildAt(i).name + "\t" + node.getChildAt(i) + "\t" + node.getChildAt(i).alpha + "\t" + node.getChildAt(i).visible); + + if (displayObjectContainer) { + childCount += traverse(displayObjectContainer, level+1); + } else { + childCount++; + } + } + + return childCount; + } + } + +} \ No newline at end of file diff --git a/com/grapefrukt/display/utilities/DrawGeometry.as b/com/grapefrukt/display/utilities/DrawGeometry.as new file mode 100644 index 0000000..a764739 --- /dev/null +++ b/com/grapefrukt/display/utilities/DrawGeometry.as @@ -0,0 +1,221 @@ +package com.grapefrukt.display.utilities { + import flash.display.Graphics; + + public class DrawGeometry + { + + + /* + * This function includes code from by Ric Ewing. + * @author Zack Jordan + */ + + static public function drawIrregularCircle(graphics:Graphics, x:Number, y:Number, radius:Number, irregularity:Number = .2, slices:int = -1):void { + if (slices < 0) { + slices = Math.round(Math.sqrt(radius * radius * Math.PI) / 10); + if (slices < 6) slices = 6; + } + + var angle:Number = Math.random() * 2 * Math.PI; // random offset over 360 degrees (in radians) + + var px:Number = 0; + var py:Number = 0; + var rndRadius:Number = 0; + for (var i:int = 0; i < slices; i++) { + rndRadius = radius * (1 + Math.random() * irregularity * 2 - irregularity / 2) + //angle *= (1 + Math.random() * irregularity - irregularity / 2); + px = x + Math.cos(angle) * rndRadius; + py = y + Math.sin(angle) * rndRadius; + + if (i == 0) graphics.moveTo(px, py); + graphics.lineTo(px, py); + + angle += 2 * Math.PI / slices; + } + graphics.endFill(); + } + + static public function drawDonut( + graphics:Graphics, + x:Number, + y:Number, + xRadius:Number, + yRadius:Number, + innerXRadius:Number, + innerYRadius:Number, + color:uint = 0xFF0000, + fillAlpha:Number = 1 + ): void + { + var segAngle : Number + var theta : Number + var angle : Number + var angleMid : Number + var segs : Number + var bx : Number + var by : Number + var cx : Number + var cy : Number; + + segs = 8; + segAngle = 45; + theta = 0; + angle = 0; + + //graphics.lineStyle( 0, 0x000000, 1 ); + graphics.beginFill( color, fillAlpha ); + graphics.moveTo( + x + Math.cos( 0 ) * innerXRadius, + y + Math.sin( 0 ) * innerYRadius + ); + + // line 1 + graphics.lineTo( + x + Math.cos( 0) * xRadius, + y + Math.sin( 0) * yRadius + ); + + // outer arc + for ( var i:int = 0; i < segs; i++ ) { + angle += theta; + angleMid = angle - ( theta / 2 ); + bx = x + Math.cos( angle ) * xRadius; + by = y + Math.sin( angle ) * yRadius; + cx = x + Math.cos( angleMid ) * ( xRadius / Math.cos( theta / 2 ) ); + cy = y + Math.sin( angleMid ) * ( yRadius / Math.cos( theta / 2 ) ); + graphics.curveTo( cx, cy, bx, by ); + } + + // line 2 + graphics.lineTo( + x + Math.cos( 2 * Math.PI ) * innerXRadius, + y + Math.sin( -2 * Math.PI ) * innerYRadius + ); + + theta = -( segAngle / 180 ) * Math.PI; + angle = -2 * Math.PI; + + // inner arc + for (var j:int = 0; j < segs; j++ ) { + angle -= theta; + angleMid = angle + ( theta / 2 ); + bx = x + Math.cos( angle ) * innerXRadius; + by = y + Math.sin( angle ) * innerYRadius; + cx = x + Math.cos( angleMid ) * ( innerXRadius / Math.cos( theta / 2 ) ); + cy = y + Math.sin( angleMid ) * ( innerYRadius / Math.cos( theta / 2 ) ); + graphics.curveTo( cx, cy, bx, by ); + } + graphics.endFill(); + } + + + /** + * Draws a wedge shape onto a Graphics3D instance. + * + * @param graphics a Graphics3D instance on which to draw + * @param x x position of the center of this wedge + * @param y y position of the center of this wedge + * @param startAngle the angle of one straight line of this wedge + * @param arc the angle (in degrees) of the total arc of this wedge + * @param xRadius the external radius along the x axis + * @param yRadius the external radius along the y axis + * @param innerXRadius the internal radius along the x axis + * @param innerYRadius the internal radius along the y axis + * @param color the color of the wedge fill + * @param fillAlpha the alpha value of the wedge fill + * + * @return nothing + */ + static public function drawWedge( + graphics:Graphics, + x:Number, + y:Number, + startAngle:Number, + arc:Number, + xRadius:Number, + yRadius:Number, + innerXRadius:Number, + innerYRadius:Number, + color:uint = 0xFF0000, + fillAlpha:Number = 1 + ): void + { + var segAngle : Number + var theta : Number + var angle : Number + var angleMid : Number + var segs : Number + var bx : Number + var by : Number + var cx : Number + var cy : Number; + + segs = Math.ceil( Math.abs( arc ) / 45 ); + segAngle = arc / segs; + theta = -( segAngle / 180 ) * Math.PI; + angle = -( startAngle / 180 ) * Math.PI; + + //graphics.lineStyle( 0, 0x000000, 1 ); + graphics.beginFill( color, fillAlpha ); + graphics.moveTo( + x + Math.cos( startAngle / 180 * Math.PI ) * innerXRadius, + y + Math.sin( -startAngle/180 * Math.PI ) * innerYRadius + ); + + // line 1 + graphics.lineTo( + x + Math.cos( startAngle / 180 * Math.PI ) * xRadius, + y + Math.sin( -startAngle / 180 * Math.PI ) * yRadius + ); + + // outer arc + for ( var i:int = 0; i < segs; i++ ) { + angle += theta; + angleMid = angle - ( theta / 2 ); + bx = x + Math.cos( angle ) * xRadius; + by = y + Math.sin( angle ) * yRadius; + cx = x + Math.cos( angleMid ) * ( xRadius / Math.cos( theta / 2 ) ); + cy = y + Math.sin( angleMid ) * ( yRadius / Math.cos( theta / 2 ) ); + graphics.curveTo( cx, cy, bx, by ); + } + + // line 2 + graphics.lineTo( + x + Math.cos( ( startAngle + arc ) / 180 * Math.PI ) * innerXRadius, + y + Math.sin( -( startAngle + arc ) / 180 * Math.PI ) * innerYRadius + ); + + theta = -( segAngle / 180 ) * Math.PI; + angle = -( ( startAngle + arc ) / 180 ) * Math.PI; + + // inner arc + for (var j:int = 0; j < segs; j++ ) { + angle -= theta; + angleMid = angle + ( theta / 2 ); + bx = x + Math.cos( angle ) * innerXRadius; + by = y + Math.sin( angle ) * innerYRadius; + cx = x + Math.cos( angleMid ) * ( innerXRadius / Math.cos( theta / 2 ) ); + cy = y + Math.sin( angleMid ) * ( innerYRadius / Math.cos( theta / 2 ) ); + graphics.curveTo( cx, cy, bx, by ); + } + graphics.endFill(); + } + + public static function drawTriangle(graphics:Graphics, x:Number, y:Number, size:Number, invert:Boolean = false):void { + if (invert) { + graphics.moveTo(x, y - size / 2); + graphics.lineTo(x + size / 2, y + size / 2); + graphics.lineTo(x - size / 2, y + size / 2); + } else { + graphics.moveTo(x - size / 2, y - size / 2); + graphics.lineTo(x + size / 2, y - size / 2); + graphics.lineTo(x, y + size / 2); + } + } + + public static function drawArrow(graphics:Graphics, size:Number):void { + //graphics.drawRect(-size / 4, 0, size/2, size / 2); + drawTriangle(graphics, 0, size, size); + } + } +} \ No newline at end of file diff --git a/com/grapefrukt/input/LazyKeyboard.as b/com/grapefrukt/input/LazyKeyboard.as new file mode 100644 index 0000000..53dfb78 --- /dev/null +++ b/com/grapefrukt/input/LazyKeyboard.as @@ -0,0 +1,67 @@ +package com.grapefrukt.input { + import flash.display.Stage; + import flash.events.KeyboardEvent; + import flash.ui.Keyboard; + + /** + * ... + * @author Martin Jonasson (grapefrukt@grapefrukt.com) + */ + + /* Key Ascii Key Ascii Key Ascii Key Ascii + L-Button 1  2 50  W 87  F12 123  + R-Button 2  3 51  X 88  F13 124  + Cancel 3  4 52  Y 89  F14 125 + M-Button 4  5 53  Z 90  F15 126 + Back 8  6 54  NP - 0 96  F16 127 + Tab 9  7 55  NP - 1 97  F17 128 + Clear 12  8 56  NP - 2 98  F18 129 + Return 13  9 57  NP - 3 99  F19 130 + Shift 16  A 65  NP - 4 100  F20 131 + Control 17  B 66  NP - 5 101  F21 132 + Menu 18  C 67  NP - 6 102  F22 133 + Pause 19  D 68  NP - 7 103  F23 134 + Cap 20  E 69  NP - 8 104  F24 135 + Escape 27  F 70  NP - 9 105  Numlock 144 + Space 32  G 71  * 106  Scroll 145 + Prior 33  H 72  + 107  Rshift 161 + Next 34  I 73  - 109  L-Ctrl 162 + End 35  J 74  . 110  R-Ctrl 163 + Home 36  K 75  / 111  L-Menu 164 + Left 37  L 76  F1 112  R-Menu 165 + Up 38  M 77  F2 113  = 187 + Right 39  N 78  F3 114  , 188 + Down 40  O 79  F4 115  [ 189 + Select 41  P 80  F5 116  . 190 + PrintScrn 44  Q 81  F6 117  / 191 + Insert 45  R 82  F7 118  ' 192 + Delete 46  S 83  F8 119 [ 219 + Help 47  T 84  F9 120 / 220 + 0 48  U 85  F10 121 ] 221 + 1 49  V 86  F11 122 ' 222 + */ + + public class LazyKeyboard { + + private var _keys:Object; + + public function LazyKeyboard(stage:Stage) { + _keys = { }; + stage.addEventListener(KeyboardEvent.KEY_DOWN, handleKey); + stage.addEventListener(KeyboardEvent.KEY_UP, handleKey); + } + + private function handleKey(e:KeyboardEvent):void { + var keyState:Boolean = false; + if (e.type == KeyboardEvent.KEY_DOWN) keyState = true; + + _keys[e.keyCode] = keyState; + } + + public function keyIsDown(keyCode:uint):Boolean { + return _keys[keyCode]; + } + + } + +} \ No newline at end of file diff --git a/com/grapefrukt/input/OneButtonInput.as b/com/grapefrukt/input/OneButtonInput.as new file mode 100644 index 0000000..bc7f3c6 --- /dev/null +++ b/com/grapefrukt/input/OneButtonInput.as @@ -0,0 +1,117 @@ +package com.grapefrukt.input { + import flash.display.Stage; + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.KeyboardEvent; + import flash.events.MouseEvent; + import flash.ui.Keyboard; + + /** + * ... + * @author Martin Jonasson (grapefrukt@grapefrukt.com) + */ + + /* Key Ascii Key Ascii Key Ascii Key Ascii + L-Button 1  2 50  W 87  F12 123  + R-Button 2  3 51  X 88  F13 124  + Cancel 3  4 52  Y 89  F14 125 + M-Button 4  5 53  Z 90  F15 126 + Back 8  6 54  NP - 0 96  F16 127 + Tab 9  7 55  NP - 1 97  F17 128 + Clear 12  8 56  NP - 2 98  F18 129 + Return 13  9 57  NP - 3 99  F19 130 + Shift 16  A 65  NP - 4 100  F20 131 + Control 17  B 66  NP - 5 101  F21 132 + Menu 18  C 67  NP - 6 102  F22 133 + Pause 19  D 68  NP - 7 103  F23 134 + Cap 20  E 69  NP - 8 104  F24 135 + Escape 27  F 70  NP - 9 105  Numlock 144 + Space 32  G 71  * 106  Scroll 145 + Prior 33  H 72  + 107  Rshift 161 + Next 34  I 73  - 109  L-Ctrl 162 + End 35  J 74  . 110  R-Ctrl 163 + Home 36  K 75  / 111  L-Menu 164 + Left 37  L 76  F1 112  R-Menu 165 + Up 38  M 77  F2 113  = 187 + Right 39  N 78  F3 114  , 188 + Down 40  O 79  F4 115  [ 189 + Select 41  P 80  F5 116  . 190 + PrintScrn 44  Q 81  F6 117  / 191 + Insert 45  R 82  F7 118  ' 192 + Delete 46  S 83  F8 119 [ 219 + Help 47  T 84  F9 120 / 220 + 0 48  U 85  F10 121 ] 221 + 1 49  V 86  F11 122 ' 222 + */ + + public class OneButtonInput extends EventDispatcher { + + private var _keyCode :int = 32; + private var _resetChangedOnReadout :Boolean = true; + private var _keyState :Boolean = false; + private var _changedSinceLastReadout:Boolean = false; + private var _last_was_keyboard :Boolean = true; + + public function OneButtonInput(stage:Stage, keyCode:int = 32, resetChangedOnReadout:Boolean = true) { + _keyCode = keyCode; + _resetChangedOnReadout = resetChangedOnReadout; + + stage.addEventListener(KeyboardEvent.KEY_DOWN, handleKey); + stage.addEventListener(KeyboardEvent.KEY_UP, handleKey); + stage.addEventListener(MouseEvent.MOUSE_DOWN, handleMouse); + stage.addEventListener(MouseEvent.MOUSE_UP, handleMouse); + + } + + private function handleMouse(e:MouseEvent):void { + var oldKeyState:Boolean = _keyState; + + _keyState = false; + if (e.type == MouseEvent.MOUSE_DOWN) _keyState = true; + + _last_was_keyboard = false; + + if (_keyState != oldKeyState) { + dispatchEvent(new Event(Event.CHANGE, false, true)); + _changedSinceLastReadout = true; + } + } + + private function handleKey(e:KeyboardEvent):void { + if (e.keyCode != _keyCode) return; + + var oldKeyState:Boolean = _keyState; + + _keyState = false; + if (e.type == KeyboardEvent.KEY_DOWN) _keyState = true; + + _last_was_keyboard = true; + + if (_keyState != oldKeyState) { + dispatchEvent(new Event(Event.CHANGE, false, true)); + _changedSinceLastReadout = true; + } + } + + public function get isChanged():Boolean { + var tmp:Boolean = _changedSinceLastReadout; + if (_resetChangedOnReadout) _changedSinceLastReadout = false; + return tmp; + } + + public function get isDown():Boolean { + return _keyState; + } + + /** + * Is set to true if the last input update came from the keyboard, false if it was the mouse + */ + public function get lastWasKeyboard():Boolean { return _last_was_keyboard; } + + public function resetChanged():void { + _changedSinceLastReadout = false; + } + + } + +} \ No newline at end of file diff --git a/com/grapefrukt/math/MathUtil.as b/com/grapefrukt/math/MathUtil.as new file mode 100644 index 0000000..2435d49 --- /dev/null +++ b/com/grapefrukt/math/MathUtil.as @@ -0,0 +1,38 @@ +package com.grapefrukt.math { + + /** + * ... + * @author Martin Jonasson + */ + public class MathUtil { + + public static function wrap(value:Number, max:Number = 1, min:Number = 0):Number { + while (value >= max) value -= (max - min); + while (value < min) value += (max - min); + return value; + } + + public static function clamp(value:Number, max:Number = 1, min:Number = 0):Number { + if (value > max) return max; + if (value < min) return min; + return value; + } + + public static function parseNumber(value:String, nanAsZero:Boolean = false):Number { + value = value.replace(",", "."); + var val:Number = parseFloat(value); + if (nanAsZero && isNaN(val)) val = 0; + return val; + } + + public static function parseBoolean(value:String):Boolean { + if (value == "true") return true; + if (value == "1") return true; + if (value == "yes") return true; + return false; + } + + + } + +} \ No newline at end of file diff --git a/com/grapefrukt/sound/MP3LoopBase.as b/com/grapefrukt/sound/MP3LoopBase.as new file mode 100644 index 0000000..b157969 --- /dev/null +++ b/com/grapefrukt/sound/MP3LoopBase.as @@ -0,0 +1,121 @@ +package com.grapefrukt.sound { + import com.grapefrukt.sound.event.MP3LoopEvent; + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IOErrorEvent; + import flash.events.ProgressEvent; + import flash.events.TimerEvent; + import flash.media.Sound; + import flash.media.SoundChannel; + import flash.net.URLRequest; + import flash.utils.Timer; + + /** + * ... + * @author Martin Jonasson (m@webbfarbror.se) + */ + public class MP3LoopBase extends EventDispatcher { + + public static var ASSET_CLASS :Class; + + protected var _out :Sound; // Used for output stream + + protected var _play_on_load :Boolean = false; + protected var _state :int = NOT_LOADED; + protected var _playing :Boolean = false; + protected var _loops :int = 0; + + protected var _url :String = ""; + protected var _out_channel :SoundChannel; // Used for output stream + + protected var _bytes_total :int = 0; + protected var _bytes_loaded :int = 0; + + protected static const NOT_LOADED :int = 0; + protected static const LOADING :int = 1; + protected static const LOADED :int = 2; + + public function MP3LoopBase(url:String, autoLoad:Boolean = false, playOnLoad:Boolean = false, loops:int = 0) { + _url = url; + _loops = loops; + this.playOnLoad = playOnLoad; + + _out = new Sound; + + if(autoLoad && _url) load(); + } + + public function load(): void { + trace("MP3LoopBase, load()", _state); + if (loaded || loading) return; + + if (ASSET_CLASS) { + _state = LOADING; + _out = new ASSET_CLASS[_url.substr(0, _url.length - 4)] as Sound; + + var t:Timer = new Timer(100, 1); + t.addEventListener(TimerEvent.TIMER_COMPLETE, function():void { + t.removeEventListener(TimerEvent.TIMER_COMPLETE, arguments.callee); + handleLoadComplete(null); + }); + t.start(); + + } else { + _state = LOADING; + _out.addEventListener( Event.COMPLETE, handleLoadComplete ); + _out.addEventListener( ProgressEvent.PROGRESS, handleProgress); + _out.addEventListener( IOErrorEvent.IO_ERROR, handleLoadError ); + _out.load( new URLRequest( _url ) ); + } + } + + private function handleProgress(e:ProgressEvent):void { + _bytes_total = _out.bytesTotal; + _bytes_loaded = _out.bytesLoaded; + + dispatchEvent(e); + } + + protected function handleLoadComplete( event:Event ):void { + trace("MP3LoopBase, handleLoadComplete()", _state); + _state = LOADED; + dispatchEvent(new MP3LoopEvent(MP3LoopEvent.COMPLETE, this)); + if (_play_on_load) play(); + } + + public function play():Boolean { + trace("playing loop, state:", _state); + if (_playing || !loaded) return false; + _out_channel = _out.play(0, _loops); + _playing = true; + dispatchEvent(new MP3LoopEvent(MP3LoopEvent.PLAY, this)); + return true; + } + + public function stop():Boolean { + if (!_playing) return false; + _out_channel.stop(); + _playing = false; + dispatchEvent(new MP3LoopEvent(MP3LoopEvent.STOP, this)); + return true; + } + + private function handleLoadError( event:IOErrorEvent ):void { + trace( event ); + } + + public function get loaded():Boolean { return _state == LOADED; } + public function get loading():Boolean { return _state == LOADING; } + + public function get soundChannel():SoundChannel { return _out_channel; } + + public function get playOnLoad():Boolean { return _play_on_load; } + public function set playOnLoad(value:Boolean):void { _play_on_load = value; } + public function get playing():Boolean { return _playing; } + + public function get bytesTotal():int { return _bytes_total; } + public function get bytesLoaded():int { return _bytes_loaded; } + + } + +} \ No newline at end of file diff --git a/com/grapefrukt/sound/MP3LoopController.as b/com/grapefrukt/sound/MP3LoopController.as new file mode 100644 index 0000000..d3fa1ad --- /dev/null +++ b/com/grapefrukt/sound/MP3LoopController.as @@ -0,0 +1,63 @@ +package com.grapefrukt.sound { + import com.gskinner.motion.GTween; + import flash.media.SoundTransform; + + /** + * ... + * @author Martin Jonasson (m@webbfarbror.se) + */ + public class MP3LoopController { + + private var _loop :MP3LoopBase; + private var _next_music :MP3LoopBase; + private var _gtween :GTween; + private var _volume :Number = 0; + + public function MP3LoopController(loop:MP3LoopBase) { + _loop = loop; + _gtween = new GTween(this, 1); + _gtween.onChange = handleTweenChange; + _gtween.onComplete = handleTweenComplete; + } + + private function handleTweenComplete(g:GTween):void{ + if (_volume == 0) _loop.stop(); + } + + private function handleTweenChange(g:GTween):void { + if (!_loop.soundChannel) return; + var soundTransform:SoundTransform = _loop.soundChannel.soundTransform; + soundTransform.volume = _volume; + _loop.soundChannel.soundTransform = soundTransform; + } + + public function tweenVolume(value:Number):void { + _gtween.proxy.volume = value; + + if (value > 0 && !_loop.playing) _loop.play(); + } + + public function play():Boolean { + if (!_loop.play()) return false; + + _loop.soundChannel.soundTransform = new SoundTransform(_volume); + if (_next_music && !_next_music.loaded) _next_music.load(); + + return true; + } + + public function setNextMusic(value:MP3LoopBase):void { + _next_music = value; + } + + public function get volume():Number { return _volume; } + + public function set volume(value:Number):void { + _volume = value; + } + + public function get loop():MP3LoopBase { return _loop; } + + } + +} \ No newline at end of file diff --git a/com/grapefrukt/sound/MP3LoopEmbedded.as b/com/grapefrukt/sound/MP3LoopEmbedded.as new file mode 100644 index 0000000..06c18c1 --- /dev/null +++ b/com/grapefrukt/sound/MP3LoopEmbedded.as @@ -0,0 +1,25 @@ +package com.grapefrukt.sound { + import flash.events.TimerEvent; + import flash.media.Sound; + import flash.utils.Timer; + /** + * ... + * @author Martin Jonasson (m@webbfarbror.se) + */ + public class MP3LoopEmbedded extends MP3LoopBase{ + + public function MP3LoopEmbedded(soundClass:Class, autoLoad:Boolean, loops:int = 0) { + super("", false, false, loops); + + _out = new soundClass() as Sound; + + if(autoLoad) load(); + } + + override public function load():void { + handleLoadComplete(null); + } + + } + +} \ No newline at end of file diff --git a/com/grapefrukt/sound/MP3LoopGapless.as b/com/grapefrukt/sound/MP3LoopGapless.as new file mode 100644 index 0000000..f1af9c6 --- /dev/null +++ b/com/grapefrukt/sound/MP3LoopGapless.as @@ -0,0 +1,96 @@ +package com.grapefrukt.sound { + + import com.grapefrukt.sound.event.MP3LoopEvent; + import flash.display.Sprite; + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IOErrorEvent; + import flash.events.ProgressEvent; + import flash.events.SampleDataEvent; + import flash.media.Sound; + import flash.media.SoundChannel; + import flash.net.URLRequest; + import flash.utils.ByteArray; + + /** + * Playback MP3-Loop (gapless) + * + * This source code enable sample exact looping of MP3. + * + * http://blog.andre-michelle.com/2010/playback-mp3-loop-gapless/ + * + * Tested with samplingrate 44.1 KHz + * + * MAGIC_DELAY does not change on different bitrates. + * Value was found by comparision of encoded and original audio file. + * + * @author andre.michelle@audiotool.com (04/2010) + */ + + public class MP3LoopGapless extends MP3LoopBase { + + private const MAGIC_DELAY :Number = 2257.0; // LAME 3.98.2 + flash.media.Sound Delay + private const BUFFER_SIZE :int = 2048; // Stable playback + + private var _samples_total :int = 0; // original amount of sample before encoding (change it to your loop) + private var _mp3 :Sound; // Used for decoding + private var _samples_position :int = 0; + + public function MP3LoopGapless(samplesTotal:int, url:String = "", autoLoad:Boolean = false, playOnLoad:Boolean = false) { + super(url, autoLoad, playOnLoad); + _samples_total = samplesTotal; + if (_samples_total == 0) throw new Error("sound must be longer than zero samples"); + _mp3 = new Sound; + } + + override protected function handleLoadComplete(event:Event):void { + _mp3 = _out; + _out = new Sound; + super.handleLoadComplete(event); + } + + override public function play():Boolean { + if (_playing || !loaded) return false; + _out.addEventListener( SampleDataEvent.SAMPLE_DATA, handleSampleDataRequest ); + super.play(); + return true; + } + + override public function stop():Boolean { + if (!super.stop()) return false; + _out.removeEventListener( SampleDataEvent.SAMPLE_DATA, handleSampleDataRequest ); + return true; + } + + private function handleSampleDataRequest( event:SampleDataEvent ):void { + extract( event.data, BUFFER_SIZE ); + } + + /** + * This methods extracts audio data from the mp3 and wraps it automatically with respect to encoder delay + * + * @param target The ByteArray where to write the audio data + * @param length The amount of samples to be read + */ + private function extract( target: ByteArray, length:int ):void { + while( 0 < length ) { + if( _samples_position + length > _samples_total ) { + var read: int = _samples_total - _samples_position; + _mp3.extract( target, read, _samples_position + MAGIC_DELAY ); + _samples_position += read; + length -= read; + } else { + _mp3.extract( target, length, _samples_position + MAGIC_DELAY ); + _samples_position += length; + length = 0; + } + + if( _samples_position == _samples_total ) { // END OF LOOP > WRAP + _samples_position = 0; + } + } + } + + + } +} \ No newline at end of file diff --git a/com/grapefrukt/sound/SoundUtil.as b/com/grapefrukt/sound/SoundUtil.as new file mode 100644 index 0000000..355f8c3 --- /dev/null +++ b/com/grapefrukt/sound/SoundUtil.as @@ -0,0 +1,181 @@ +/** + * SoundManager + * A bunch of convenience methods for playing sounds + * + * @author Martin Jonasson + * @version 1.0 + */ + +/* +Licensed under the MIT License + +Copyright (c) 2009 Martin Jonasson + +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. + +http://www.grapefrukt.com/blog/ + +*/ + +package com.grapefrukt.sound { + import flash.display.DisplayObject; + import flash.media.Sound; + import flash.media.SoundChannel; + import flash.media.SoundTransform; + + public class SoundUtil { + /** + * Sets the stage width used for sounds on DisplayObjects + */ + public static var stageWidth:uint = 800; + + /** + * Sets the stereo separation used for sounds on DisplayObjects + */ + public static var stereoSeparation:Number = 0.75; + + /** + * Sets the global volume for all future sounds played (if you already have sounds playing you will need to update them) + */ + public static var globalVolume:Number = 1; + + /** + * Plays a Sound at the specified DisplayObjects x coordinates + * @param sound The Sound you want to play + * @param target The target DisplayObject + * @param volume The relative volume of the sound (0 to 1) + * @param loops The number of loops you want the sound to play for + * @return The SoundChannel in which the sound is playing + */ + public static function playAtSprite(sound:Sound, target:DisplayObject = null, volume:Number = 1, loops:uint = 0):SoundChannel{ + return play(sound, volume, getPan(target), loops); + } + + /** + * Plays a sound from an embedded class at the specified DisplayObjects x coordinates + * @param soundClass The Class you want to play + * @param target The target DisplayObject + * @param volume The relative volume of the sound (0 to 1) + * @param loops The number of loops you want the sound to play for + * @return The SoundChannel in which the sound is playing + */ + public static function playClassAtSprite(soundClass:Class, target:DisplayObject = null, volume:Number = 1, loops:uint = 0):SoundChannel{ + return playAtSprite(getClassAsSound(soundClass), target, volume, loops); + } + + /** + * Plays a sound + * @param sound The sound you want to play + * @param target The target DisplayObject + * @param volume The relative volume of the sound (0 to 1) + * @param pan The pan of the sound (-1 to 1) + * @param loops The number of loops you want the sound to play for + * @return The SoundChannel in which the sound is playing + */ + public static function play(sound:Sound, volume:Number = 1, pan:Number = 0, loops:uint = 0):SoundChannel { + return sound.play(0, loops, generateTransform(volume, pan)); + } + + /** + * Plays a sound from an embedded class + * @param sound The sound you want to play + * @param target The target DisplayObject + * @param volume The relative volume of the sound (0 to 1) + * @param pan The pan of the sound (-1 to 1) + * @param loops The number of loops you want the sound to play for + * @return The SoundChannel in which the sound is playing + */ + public static function playClass(soundClass:Class, volume:Number = 1, pan:Number = 0, loops:uint = 0):SoundChannel { + return play(getClassAsSound(soundClass), volume, pan, loops); + } + + /** + * Casts a plain Class to Sound + */ + public static function getClassAsSound(soundClass:Class):Sound { + return new soundClass as Sound; + } + + /** + * Generates a sound transform from the values you specify, all values are wrapped + * @param volume The volume of the sound (0 to 1) + * @param pan The pan of the sound (-1 to 1) + * @return + */ + public static function generateTransform(volume:Number = 1, pan:Number = 0):SoundTransform { + var st:SoundTransform = new SoundTransform(); + st.volume = wrapVol(volume / 10) * 2 * globalVolume; + st.pan = wrapPan(pan); + return st; + } + + /** + * Updates the SoundTransform on an already created SoundChannel + * @param soundChannel The sound channel which transform you want to change + * @param volume The relative volume of the sound 0 to 1) + * @param pan The pan of the sound (-1 to 1) + */ + public static function setNewTransform(soundChannel:SoundChannel, volume:Number = 1, pan:Number = 0):void { + soundChannel.soundTransform = generateTransform(volume, pan); + } + + /** + * Updates the SoundTransform of a channel using the position of the DisplayObject + * @param soundChannel The sound channel which transform you want to change + * @param target The target DisplayObject + * @param volume The relative volume of the sound 0 to 1) + */ + public static function updateTransformAtSprite(soundChannel:SoundChannel, target:DisplayObject = null, volume:Number = 1):void { + setNewTransform(soundChannel, volume, getPan(target)); + } + + /** + * Calculates the position of the DisplayObject relative to the set stageWidth + * @param target The target DisplayObject + * @return The pan of the sound (-1 to 1) + * @see stageWidth + */ + private static function getPan(target:DisplayObject):Number { + var pan:Number = (target.x / stageWidth) * 2 - 1; + return pan * stereoSeparation; + } + + /** + * Wraps a value to within zero to one + * @param num A number of any value + * @return The number wrapped to a max of one and a min of zero + */ + private static function wrapVol(num:Number):Number{ + if(num > 1) num = 1; + if(num < 0) num = 0; + return num; + } + + /** + * Wraps a value between negative one and one + * @param num A number of any value + * @return The number wrapped to a max of one and a min of negative one + */ + private static function wrapPan(num:Number):Number{ + if(num > 1) num = 1; + if(num < -1) num = -1; + return num; + } + } +} \ No newline at end of file diff --git a/com/grapefrukt/sound/event/MP3LoopEvent.as b/com/grapefrukt/sound/event/MP3LoopEvent.as new file mode 100644 index 0000000..06e5605 --- /dev/null +++ b/com/grapefrukt/sound/event/MP3LoopEvent.as @@ -0,0 +1,34 @@ +package com.grapefrukt.sound.event { + import com.grapefrukt.sound.MP3LoopBase; + import flash.events.Event; + + /** + * ... + * @author Martin Jonasson (m@webbfarbror.se) + */ + public class MP3LoopEvent extends Event { + + public static const PLAY :String = "mp3loopevent_play"; + public static const STOP :String = "mp3loopevent_stop"; + public static const COMPLETE :String = "mp3loopevent_complete"; + + private var _loop:MP3LoopBase; + + public function MP3LoopEvent(type:String, loop:MP3LoopBase) { + super(type, bubbles, cancelable); + _loop = loop; + } + + public override function clone():Event { + return new MP3LoopEvent(type, _loop); + } + + public override function toString():String { + return formatToString("MP3LoopEvent", "type", "bubbles", "cancelable", "eventPhase"); + } + + public function get loop():MP3LoopBase { return _loop; } + + } + +} \ No newline at end of file diff --git a/com/grapefrukt/string/StringUtil.as b/com/grapefrukt/string/StringUtil.as new file mode 100644 index 0000000..a8ebed4 --- /dev/null +++ b/com/grapefrukt/string/StringUtil.as @@ -0,0 +1,28 @@ +package com.grapefrukt.string { + /** + * ... + * @author Martin Jonasson + */ + public class StringUtil { + + public static function padStart(string:String, len:int, padding:String):String { + while (string.length < len) string = padding + string; + return string; + } + + public static function zeroPad(string:String, len:int):String { + while (string.length < len) string = '0' + string; + return string; + } + + public static function secondsToTime(seconds:int, separator:String = ":"):String { + /*var hours:int = seconds / 60 / 60; + seconds -= hours * 60 * 60;*/ + var minutes:int = seconds / 60; + seconds -= minutes * 60; + return zeroPad(minutes.toString(), 2) + separator + zeroPad(seconds.toString(), 2); + } + + } + +} \ No newline at end of file