From the monthly archives:

May 2010

Image Caching With PHP

by josefchutka on May 24, 2010

While working on my next project, I am looking for the best possible techniques to make it fastest possible (both client and server). My app requests a lot of images here and there, it may count up to hundreds or thousands requests in a while. I can not hold all of those wthin flash player cache because some of them may change, and I also want shortest possible respond times and client-server traffic reduction as well as server side computing reduction. Thats where browser caching comes into the scene. I have experimented a bit with all possible http headers to understand each browser specifics and I came with a solution.

// $this->time() - returns filemtime() for the file
// $this->filename() - returns full path to the file

$etag=md5($this->time().$this->filename());
$time=gmdate('r', $this->time());

header('Last-Modified: '.$time);
header("Cache-Control: must-revalidate");
header('Expires: '.$time);
header("Etag: ".$etag); 

$test1 = isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
	&& $_SERVER['HTTP_IF_MODIFIED_SINCE'] == $time;
$test2 = isset($_SERVER['HTTP_IF_NONE_MATCH'])
	&& str_replace('"', '', stripslashes($_SERVER['HTTP_IF_NONE_MATCH'])) == $etag;
if($test1 || $test2){
	header('HTTP/1.1 304 Not Modified');
	exit();
}

header("Content-Length: ".filesize($this->filename()));
header("Content-type: image/png");

readfile($this->filename());

This is how it works:

  1. flash app requests a new image (thorough browser) from php server, minimum requests headers added with browser while it is a new image
  2. php server responds with important cache and Etag headers info and image bytes (content)
  3. browser caches response headers, flash player handles response

Repeated request:

  1. when flash player requests the same image again, browser adds Etag and date into request headers (while it caches the last response)
  2. php server catches request headers and see browser cached info in $_SERVER vars
  3. while Etag matches, it responds with 304 Not Modifed header and no content pushed (traffic is saved, server computing reduced)
  4. browser push its cached content into flash player (faster response)

Now someone changes the image on php server:

  1. flash player requests the image, browser adds headers
  2. php server generates new Etag for the image and compares it with browser added request headers
  3. those does not match so the new image content is pushed with response

Nice thing is, there are no random parameters required with your requests, while all requets are sent to the server and handled appropriately, so your content is always fresh. This script is tested with IE and Chrome (works as expected). I was not able to make my proxy watcher communicate with opera and my firefox settings are set to no-cache at all.

A different view on loops – listener-looping

by josefchutka on May 19, 2010

What is the usual practice when searching / setting / operating over some list of item? I think you usually think of iterating over some list (array) or linked list etc. Well, ActionScript has to offer one more technique you maybe never tought of, lets call it listener-looping. ActionScript is event based language, that means that every EventDispatcher based classes are able to listen and dispatch events. And this fits our needs for loop alternative. Lets suggest, you do not have any list to be iterated over but you still have a chance to accomplish it by making each item listening for custom event.

Here is a simple example code for counting all listening items and performing search over items.

package
{
    import flash.display.Sprite;

    public class TestAS extends Sprite
    {
        public function TestAS()
        {
            for(var i:uint = 0; i < 5; i++)
                new Item(i, this);

            var event:CustomEvent;

            event = new CustomEvent(CustomEvent.COUNT)
            dispatchEvent(event);
            trace("count:", event.count);

            event = new CustomEvent(CustomEvent.FIND)
            event.position = 2;
            dispatchEvent(event);
            trace("find position:", event.result.position);
        }
    }
}

import flash.events.EventDispatcher;
import flash.events.Event;

class Item extends Object
{
    public var position:uint;
    private var dispatcher:EventDispatcher;

    public function Item(position:uint, dispatcher:EventDispatcher):void
    {
        this.position = position;
        this.dispatcher = dispatcher;

        dispatcher.addEventListener(CustomEvent.COUNT, count);
        dispatcher.addEventListener(CustomEvent.FIND, find);
    }

    private function count(event:CustomEvent):void
    {
        event.count++;
    }

    private function find(event:CustomEvent):void
    {
        if(event.position != position)
            return;

        event.result = this;
        event.stopImmediatePropagation();
    }
}

class CustomEvent extends Event
{
    public static const COUNT:String = "CustomEventCOUNT";
    public static const FIND:String = "CustomEventFIND";

    public var count:uint = 0;
    public var position:uint;
    public var result:Item;

    public function CustomEvent(type:String, bubbless:Boolean = false,
        cancelable:Boolean = false):void
    {
        super(type, bubbless, cancelable);
    }
}

returns:

count: 5
find position: 2

What is happening here let us better understand event livecycle:

  1. Event instance is created
  2. Event is dispatched
    • every Item catches dispatched Event synchronously after dispatching (Event passed as reference)
    • Event reference is being modified, or may be even stopped (stopImmediatePropagation() method)
    • all listeners executed one by one (until stopped)
  3. Event reference is used as result holder

Now you may wonder how fast it is. Here is the app comparing speed of listener-looping vs. linked list:

Results may vary…

--- Starting Linked List test --- (500000 iterations)
testLinkedExe: 16 ms.

--- Starting Events test --- (500000 iterations)
testEventsExe: 109 ms.

… but listener-looping are slower than linked lists (+400% up to +1000%).

iDevcamp

by Robin on May 18, 2010

Rád bych vás pozval na akci která nemá v ČR konkurenci a věřím, že program který nabízíme stojí za investici. iDevcamp je dvoudenní konference jen a jen o iPadech a iPhonech. Hodiny praxe, zkušeností lidí z nejlepšího českého vývojářského týmu Tapmates. Zasvětíme Vás do programování, návrhu UI, grafiky a marketingu na App Store.

Nečekejte suché povídání na nudné konferenci. Zakládáme si na tom, že vše, co uslyšíte, bude podloženo praktickými ukázkami a vše si vyzkoušíte sami v praxi. Neuznáváme konference plné opakujících se příspěvků, ze kterých si účastníci další den nic nepamatují. Na to je náš čas příliš drahý.

Více informací naleznete na oficiálním webu iDevcamp a v článku O čem je iDevcamp. Sledovat aktuální stav můžete na sítí Facebook a Twitter.

Oficiální Event na Facebooku je také k dispozici.

Budeme se na vás těšit!

Myth Buster – Benchmarking Loops

by josefchutka on May 13, 2010

Lets have a look at some of the most common and fastest loops used in ActionScript 3. Is int really faster than uint and Number? Is while loop faster than for? Is pre-increment faster than post-increment? To test all of these I have created benchmark test application on wonderfl. App compares loops with 10.000.000 iterations and logs testing times. To compare different variable types, loop methods, increment / decrement modes, there are buttons grouping each test. Please be patient with your first click, the model Array is being generated (Link button also generates linked list). The first test compares Arrays, second one Vectors.

Loop Benchmark Test 1 uses Array:

Loop Benchmark Test 2 uses Vector.:

Loop Benchmar Test 4: Linked List vs. Final Linked List:

To sum it up fastest seems to be these methods:

Arrays
testForUintPreDecrement: 78 ms. *GLOBAL FASTEST*
testForUintPostDecrement: 78 ms. *GLOBAL FASTEST*
testForIntPostDecrement: 78 ms. *GLOBAL FASTEST*
testWhileUintPreDecrement: 78 ms. *GLOBAL FASTEST*
testWhileUintPostDecrement: 78 ms. *GLOBAL FASTEST*

Vectors
testForUintPreDecrement: 62 ms. *GLOBAL FASTEST*
testForIntPreDecrement: 62 ms. *GLOBAL FASTEST*
testForIntPostDecrement: 62 ms. *GLOBAL FASTEST*
testWhileIntPreIncrement: 62 ms. *GLOBAL FASTEST*
testWhileIntPostIncrement: 62 ms. *GLOBAL FASTEST*
testLinked: 62 ms. *GLOBAL FASTEST*

Those are my favorits:

public function testForIntPreDecrement():void
{
	for(var i:int = R - 1; i > -1; --i)
		modelArray[i];
}

public function testWhileUintPostDecrement():void
{
	var i:uint = modelArray.length;
	while(i--)
		modelArray[i];
}

public function testLinked():void
{
	var link:Linked = firstLink;
	while(link)
		link = link.next;
}

… that says it all:

  • “for” and “while” loops can be equally fast, while “for each” loop is much slower
  • “uint” and “int” is equally fast, and is much faster than “Number”
  • “pre” vs. “post” is more less the same
  • decrement mostly wins over increment, but in some cases is equally fast
  • Linked-lists iteration performance equals to Vector and is better than Arryas
  • when casting required, linked lists rules them all
  • using ByteArray as list is not as fast as Array nor Vector

Few days before I saw an amazing 3d effects on VidazPhoto. That made me want to master this effect. So, starting today morning I played around with flex and pixel bender and here are the results. I have created simple pixel bender kernel that uses monochromatic bitmapData to calculate amount of displacement for each pixel. All you have to do, in order to make your 2D images become 3D, is to create monochromatic map (black & white image) with the same dimensions as the original image (use lighter colors for closer objects, darker colors for distant ones). Than, based on rotation, push correct amounts of full displacement (dx, dy displacements for full white color) into shader. The displacement is linear starting on 0, 0 for black color, up to dx, dy for white color. All sources available to download.

First, here is the final application with displacement map used to fake 3D effect:

Click mouse to change images, move mouse to rotate image. Full ActionScript 3 code can be also downloaded from wonderfl. The core of this app are those few lines:

shader.data.map.input = Bitmap(mapLoader.content).bitmapData;
shader.data.dx.value = [imageContainer.rotationY];
shader.data.dy.value = [-imageContainer.rotationX];

filter = new ShaderFilter(shader);
imageLoader.filters = [filter];

simple displacement with pixel bender – displacementMap.pbk

<languageVersion : 1.0;>

kernel DisplacementMap
<
	namespace : "sk.yoz";
	vendor : "Yoz";
	version : 1;
	description : "Displacement by bitmap map";
>
{
	input image4 src;
	input image1 map;
	output pixel4 dst;

	parameter float dx
	<
		minValue: float(-1000.0);
		maxValue: float(1000.0);
		defaultValue: float(0.0);
		description: "The amount of displacement on x axis.";
	>;

	parameter float dy
	<
		minValue: float(-1000.0);
		maxValue: float(1000.0);
		defaultValue: float(0.0);
		description: "The amount of displacement on y axis.";
	>;

	void evaluatePixel()
	{
		float2 pos = outCoord();
		float amount = sampleLinear(map, pos);
		float2 displacedPos = float2(
			pos.x + dx * amount,
			pos.y + dy * amount);
		dst = sampleLinear(src, displacedPos);
	}
}

I have used image1 input type for monochromatic map (black and white bitmap), its enough and its superfast.

Due to great success, I have added fader parameter. Fader can be used for depth of field or, alpha transparency etc. You can download advanced pixel bender source here.

Abstract Drawing

by josefchutka on May 7, 2010

While some free time today, I have been playing on guiding script, than suddenly I got an idea, I can create some eye-catching thing with it and voilà, here is little thingy, can be used for screensaver etc… Have a nice Friday :-)

Full ActionScript 3 code can be also downloaded from wonderfl.

Quick tip: Embedded MovieClip not MovieClip?

by josefchutka on May 6, 2010

When embedding MovieClips (with only one frame) from Flash .swf file into your Flex projects, you can fall into runtime issues:

[Embed(source="assets/preloader.swf", symbol="preloader")]
public static const PRELOADER_CLASS:Class;
MovieClip(new Assets.PRELOADER_CLASS());
TypeError: Error #1034: Type Coercion failed: cannot convert *** to flash.display.MovieClip.

Why coercion failed when my symbol is for sure MovieClip? The answer is simple:

Flash will type the symbol based on the number of frames in it’s timeline. If the symbol has only 1 frame it will be typed as a Sprite, 2 or more frames it will be typed as a MovieClip.

Possible solutions:

  • add 1 more frame (so the frame count is more than 1)
  • or cast embedded asset as flash.display.Sprite (prefered while there is no need for MovieClip with one usable frame + Sprite container consumes less memory)

The day is saved thanks to Felix Turner.

Facebook Graph API & OAuth 2.0 & Flash (update)

by josefchutka on May 5, 2010

As previously mentioned, facebook released a new Graph API. It is based on OAuth 2.0 protocol (old authorization token also works). While it is fresh thing, there is no much ActionScript stuff around, so I came with FacebookOAuthGraph class. This class is meant to be used as an abstract class, while it contains just the basic authentication algorithm and call method to request data. It stores access token in SharedObject, so next time you came into app, you get connected on background without noticing (no popup etc.). Your token should expire in 24 hours.

Here is the code for the following flex app, to make it work, get latest FacebookOAuthGraph and FacebookOAuthGraphEvent classes.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
    applicationComplete="init()">
<mx:Script>
<![CDATA[

    import sk.yoz.events.FacebookOAuthGraphEvent;
    import sk.yoz.net.FacebookOAuthGraph;

    // facebook Application ID
    private var clientId:String = "268718683475";

    // path to our callback
    private var redirectURI:String =
        "http://blog.yoz.sk/examples/FacebookOAuthGraph/callback.html";

    // required extended permissions
    private var scope:String = "publish_stream,user_photos,user_photo_video_tags";

    private var facebook:FacebookOAuthGraph = new FacebookOAuthGraph();

    [Bindable] private var connected:Boolean;

    private function init():void
    {
        facebook.clientId = clientId;
        facebook.redirectURI = redirectURI;
        facebook.scope = scope;
        facebook.useSecuredPath = true;
        facebook.addEventListener(FacebookOAuthGraphEvent.AUTHORIZED, authorized);
        facebook.autoConnect(parameters);

        log.text += "checkSavedToken()\n";
    }

    private function connect():void
    {
        facebook.connect();

        log.text += "connect()\n";
    }

    private function authorized(event:FacebookOAuthGraphEvent):void
    {
        connected = true;

        log.text += "authorized\n";
    }

    private function call(path:String, binary:Boolean):void
    {
        var loader:URLLoader = facebook.call(path);
        loader.dataFormat = binary
            ? URLLoaderDataFormat.BINARY
            : URLLoaderDataFormat.TEXT;
        loader.addEventListener(FacebookOAuthGraphEvent.DATA, callComplete);
        log.text += "call(" + path + ")\n";
    }

    private function changeStatus(message:String):void
    {
        var data:URLVariables = new URLVariables();
        data.message = message;
        var method:String = URLRequestMethod.POST;
        var loader:URLLoader = facebook.call("me/feed", data, method);
        loader.addEventListener(FacebookOAuthGraphEvent.DATA, callComplete);
        log.text += "changeStatus(" + message + ")\n";
    }

    private function callComplete(event:FacebookOAuthGraphEvent):void
    {
        log.text += "call completed -> see result\n";

        if(event.rawData is ByteArray)
        {
            var loader:Loader = new Loader();
            loader.loadBytes(event.rawData as ByteArray);
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE,
                function():void
                {
                    image.source = loader;
                });
        }
        else
        {
            result.text = event.rawData.toString();
        }
    }
]]>
</mx:Script>
<mx:HBox>
    <mx:Button click="connect()" label="connect" />
    <mx:Text text="{connected ? 'connected' : 'not connected'}" />
</mx:HBox>
<mx:HBox visible="{!connected}" includeInLayout="{!connected}">
    <mx:Text text="#access_token=121161974560905%7C2..." />
    <mx:TextInput id="hash" />
    <mx:Button label="add hash" click="facebook.confirmConnection(hash.text)"/>
</mx:HBox>
<mx:HBox>
    <mx:TextInput id="path" text="me" />
    <mx:Button label="call" click="call(path.text, false)" enabled="{connected}"/>
    <mx:Spacer width="20" />
    <mx:TextInput id="path2" text="me/picture" />
    <mx:Button label="call binary" click="call(path2.text, true)" enabled="{connected}"/>
</mx:HBox>
<mx:HBox>
    <mx:TextInput id="status" text="testing FacebookOAuthGraph" />
    <mx:Button label="change status" click="changeStatus(status.text)" enabled="{connected}"/>
</mx:HBox>
<mx:HDividedBox width="100%" height="100%">
    <mx:TextArea width="30%" height="100%" id="log"/>
    <mx:TextArea width="70%" height="100%" id="result"/>
    <mx:Image id="image" />
</mx:HDividedBox>
</mx:Application>

Make sure your html wrapper defines correct allowScriptAccess and both id and name for <object> tag. This enables ExternalInterface.objectID. With swfobject use:

var params = {
    allowScriptAccess: "sameDomain"
};

var attributes = {
    id: "FacebookOAuthGraphTest",
    name: "FacebookOAuthGraphTest"
};
swfobject.embedSWF("FacebookOAuthGraphTest.swf", "alternative", "100%", "100%", "10.0.0",
    "expressInstall.swf", flashvars, params, attributes);

callback.html pushes url hash into flash app. When running this application from desktop (creating/debugging), your callback.html located on public domain has no access to its opener (different domain – XSS), so you need to pass access_token manualy into <TextInput id=”hash”>, but once your flash application is on the same domain with callback, it works automaticaly.

callback.html:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="sk" lang="sk" dir="ltr">
<head>
	<script type="text/javascript">
	<!--
		if(window.opener && window.opener.confirmFacebookConnection)
		{
			window.opener.confirmFacebookConnection(window.location.hash);
			self.close();
		}
	//-->
	</script>
</head>
<body>
<p>You may now close this window.</p>
</body>
</html>

Click connect and allow facebook application. Facebook redirects to callback.html that pastes hash into flash and closes popup. Now you are authenticated. Next time you visit this flash application (refresh this page) you will get authenticated in background (if your access token is still valid). Notice, some graph api calls returns JSON objects (me), other may return binary data (me/picture). For now it may take some time to finish calls (5 second or more), but I hope facebook will soon make it fast.

You get your JSON decoded data via event.data. Just make sure you do not try to decode ByteArray (eg. me/picture)

// call("me")
private function callComplete(event:FacebookOAuthGraphEvent):void
{
    trace(event.data.name);
}

Some calls to test:

Friends:             me/friends
News feed:           me/home
Profile feed (Wall): me/feed
Likes:               me/likes
Movies:              me/movies
Books:               me/books
Notes:               me/notes
Photos:              me/photos
Videos:              me/videos
Events:              me/events
Groups:              me/groups

Additional information about my facebook app:

Application ID:    268718683475
Connect URL:       http://blog.yoz.sk/examples/FacebookOAuthGraph/
Base Domain:       yoz.sk
FBML/iframe:       iframe
Developer Mode:    Off
Application Type:  Desktop
Private Install:   No

Based on facebook access token, it should be valid approximately for 24 hours. Notice the bold part, works as expiration time (unix time format). Use FacebookOAuthGraph.tokenToExpiration() to parse Date:

access_token=268718683475%7C2.w3fjqz80Xi1CdBXt7Ygh6A__.86400.1273158000-1215645368%7CDGv2l2HtTymd6cM6Fy_8k6P_8CQ.

Important update: May 19, 2010: Application is working again. Due to massive support in bug tracker (thanks all for your votes), facebook devs changed secured crossdomain.xml, so it allows unsecured requests. changeStatus() method added into example and some minor changes in FacebookOAuthGraph class (please update).

Here is what happend and why this app was not working for a while:

  • May 11, 2010: facebook changed rules, so requests on unsecured graph service (via http://graph.facebook.com…) were limited to just those without access_token parameter. Requets to secured service (https://graph.facebook.com) resulted in security violation due to missing secure=”false” parameter in crossdomain.xml
  • May 12, 2010: bug submitted
  • May 12, 2010 – May 14, 2010: massive bug voting
  • May 14, 2010: Bug confirmed by facebook dev team
  • May 19, 2010: Bug fixed, crossdomain file changed

I am glad that facebook devs listens and care. :-)

FAQ

How can I use this class in my iframe canvas page?

Please read article Authorizing Iframe Facebook Applications For Graph API

When I connect with this app in one browser (firefox), and I run another browser (chrome) I get automatically connected. Why?

Your authorization token is stored in SharedObject, that is OS-user persistent (eg. windows user). No matter what browser you run, all of those reads data from one SharedObject. If you need cookie or session persistent authorization, please extend my class and override methods that use SharedObject.

How to add request parameters into my call?

Pass URLVariables object as a second parameter to call() method:

// eg. in order to make this call "me/picture?type=large", do the following:
var data:URLVariables = new URLVariables();
data.type = "large"
call("me/picture", data);

Why I can not access me/photos?

Facebook has changed rules again. You also need “user_photo_video_tags” permission within your app. I have already added it into my app, so please remove your browser cache, refresh and click connect button again (even if you are connected already). Now it should work.

How to upload photo with graph api?

This is a piece of working code from Sean, thnx Sean:

// MultipartURLLoader by Eugene Zatepyakin can be found here: http://bit.ly/9wx4q7
public function uploadImageCall(path:String,ba:ByteArray,message:String,token:String=null):MultipartURLLoader
{
    var mpLoader:MultipartURLLoader = new MultipartURLLoader();
    mpLoader.addVariable("message", message);
    mpLoader.addFile(ba, "image.jpg", "image");
    loaderAddListeners(mpLoader.loader);
    mpLoader.load(apiSecuredPath + "/"+ path + "&access_token="+ _token);
    return mpLoader;
}

Can I make fql calls with this class?

However graph api does not implement fql calls, you can make fql calls using this class. The resulted data are not JSON but XML:

var data:URLVariables = new URLVariables();
data.query = "SELECT uid, name FROM user WHERE uid = XXX"; // insert your uid
var loader:URLLoader = facebook.call("method/fql.query", data,
    URLRequestMethod.POST, null, "https://api.facebook.com");
loader.addEventListener(FacebookOAuthGraphEvent.DATA, fqlComplete);

private function fqlComplete(event:FacebookOAuthGraphEvent):void{
        var xml:XML = new XML(event.rawData);

For fqlComplete method, please read Parsing FQL result article.

Can I post feeds with attachments?

Graph API has not documented attachment functionality for feeds, anyway, you can publish streams with attachments with this class as simple as:

var media:Object = {};
media.type = "flash";
media.swfsrc = "http://zombo.com/inrozxa.swf";
media.imgsrc = "http://blog.yoz.sk/wp-content/uploads/3d-150x150.jpg";
media.width = "80";
media.height = "80";
media.expanded_width = "120";
media.expanded_height = "120";

var attachment:Object = {};
attachment.name = "test name";
attachment.link = "http://blog.yoz.sk"
attachment.description = "test description";
attachment.caption = "test caption";
attachment.media = [media];

var data:URLVariables = new URLVariables();
data.message = "test message";
data.attachment = JSON.encode(attachment);

facebook.call("method/stream.publish", data, URLRequestMethod.POST, null, "https://api.facebook.com");

Quick tip: Alpha On TextFields

by josefchutka on May 4, 2010

When targeting your movie into flash player 9 (or lower), you can fall into issues with text transparency. If TextField contains not embedded font, by default it will not render transparency (alpha) correctly. Luckily, there is a solution for that, as simple as setting blendMode to “layer” parameter to your TextField. This solution was mentioned on Glenn Gervais blog plus there are some other techniques mentioned in comments, for example applying BlurFilter(0, 0) or cacheAsBitmap=true. Thankfully, once you start targeting Flash Player 10, you won’t need any sort of hacks.

var myTextField:TextField() = new TextField();
myTextField.blendMode = BlendMode.LAYER;
myTextField.alpha = 0.5;

8bit Players & Synthesizers In Flash

by josefchutka on May 4, 2010

FlashModPlug is a flash module player created by Ralph Hauwert (UnitZeroOne). It is intended to be a future reference implementation for FlashModPlugLib, intended to be released in the near future. It is an Adobe Alchemy port of the well know LibModPlug, originally created by Olivier Lapicqu and currently maintained by Konstanty Bialkowski. Player includes well known formats such as MOD, XM, IT, S3M, and lesser know formats like Okt and 669.

8Bitboy – A Flash based Amiga Modplayer, is open source, pure ActionScript, mod player created by Joa Ebert and Andre Michelle. Karsten Obarski invented 1987 – twenty years ago – the MOD format for his Ultimate Tracker running on Amiga computers. Since then, countless songs are created, especially for the demoscene. MODs have special attributes. All samples are stored in 8bits and the number of voices is limited to four. To have something similarly to chords, the three notes of it are repeated very fast. This makes MODs sound so freaky.

Flash MOD Player is a MOD player written for Flash 9 using the haXe programming language. Although written in haXe, however, FMP can be used with ActionScript 3 by converting the haXe code in ActionScript 3 code (the haXe compiler does this). However i do not have access to any version of Flash, so i cannot make sure that this will always be the case. The player is written with reusability in mind, in other words that you can take the player class, put it in your game code and use it with ease.

SiON SoundObject Quartet is a ActionScript based synthesizer created by Kei Mesuda. SiON project location can be found here. The “SiON” is a sound library featuring PSG, FM, WaveTable?, PCM and pyhisical modeling sound module emulations, MML (Music Macro Language) compiler and sequencer, Sound effectors.

More fun with audio