Pulling up to 12 RTMP streams - actionscript-3

I am VERY new to this site! I have been reading the codes for the past few nights! From Publishing a flash project with network access so it can contact the internet to RTMP applications and streaming. But I have hit a wall and I do not want to go off the wrong path as most of my work is bits and pieces of helpful information on this site. And please appologize I'm not the boss of a company, so I do not do much of the coding any more and i have surely forgotten most of my PHP coding background, Please do not smite me for dumb or"noob". But since I have made it this far with the flash and actionscripts3 programming I thought it would not hurt if I asked a few questions from much smarter people!
Currently I am working on a single windowed flash project that will allow me to pull up to 12 live RTMP streams. Currently with everything i have been able to do, I have a working code so far to JUST pull a single RTMP string. Took me 3 days of code that was pulled off the internet, to get it to work. i have been successful in getting one stream with action script
package com.endseven {
import flash.events.NetStatusEvent;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
import com.endseven.RTMPStream;
public class Downstream extends RTMPStream {
/* the constructor. */
public function Downstream():void {
trace("Downstream object has been created."); // debug trace..
this.oVideo = new Video(640, 480);
this.oConnection = new NetConnection();
this.oConnection.addEventListener(NetStatusEvent.NET_STATUS, eNetStatus, false, 0, true);
this.oConnection.connect(this.sMediaServerURL);
}
/* triggered when a net status event is received. */
private function eNetStatus(oEvent1:NetStatusEvent) {
trace("NetStatusEvent: " + oEvent1.info.code); // debug trace..
switch (oEvent1.info.code) {
case "NetConnection.Connect.Success":
// create a stream for the connection..
this.oNetStream = new NetStream(oConnection);
this.oNetStream.addEventListener(NetStatusEvent.NET_STATUS, eNetStatus, false, 0, true);
this.oNetStream.bufferTime = 5; // set this to whatever is comfortable..
// listen for meta data..
this.oMetaData.onMetaData = eMetaDataReceived;
this.oNetStream.client = this.oMetaData;
// attach the stream to the stage..
this.oVideo.attachNetStream(oNetStream);
this.oNetStream.play(sStreamName);
this.addChildAt(this.oVideo, 0);
trace("Connected to the RTMP server."); // debug trace..
break;
case "NetConnection.Connect.Closed":
trace("Disconnected from the RTMP server."); // debug trace..
break;
case "NetStream.Play.StreamNotFound":
trace("This stream is currently unavailable."); // debug trace..
break;
}
}
}
}
This is the actionscript3 code that is the only code i have gotten to work. But this only pulls in a single stream on a single display. I mean I could work with this and just have twelve of them. But that seems to be a waste. What is the best method to get this to work. I am very new so this next question seems even dumb to me! But cant i just create 12 flash files and import them and arrange them in a single project?

Related

AS3 using Workers

IDE: FLASH CS6 Professional
SDK: AIR 18 for Android
I'm trying to use Workers for my app.
The problem is, it doesn't work that it suppose to be.
public class Main extends Sprite {
private var worker:Worker;
public function Main() {
var ba:ByteArray = this.loaderInfo.bytes;
if(Worker.current.isPrimordial){
worker = WorkerDomain.current.createWorker(ba);
worker.start();
trace("Created Main");
}else{
trace("Created Worker");
}
}
}
It should output
Created Main
Created Worker
but i'm only getting
[SWF] Test.swf - 2831 bytes after decompression
Created Main
[UnloadSWF] Test.swf
Test Movie terminated.
EDIT:
Ok i tried to publish the app and ran it. It works like it should be. But why doesn't it work when i try to ran it with Adobe Debug Launcher ?
This is what it looks like on Android Debug Launcher (ADL)
ADL
This is what it looks like when Published.
Published
EDIT 2:
Tried it with Flash Professional CC 2015. Upgraded to AIR SDK 19.0.0.213.
Still the same result. What a pain Adobe.
It should work, but Flash Pro CS6 doesn't show the worker's trace statements. Check flashlog.txt or send messages from the worker to the main SWF over a MessageChannel.
I made a lib to support use of Workers, if you want use you can check it in GitHub, it is open source project.. hope help ASWorker Link
The implementation is like this
package
{
import com.tavernari.asworker.ASWorker;
import com.tavernari.asworker.notification.NotificationCenter;
import com.tavernari.asworker.notification.NotificationCenterEvent;
import flash.display.Sprite;
public class ASWorkerDemo extends Sprite
{
private var asWorker:ASWorker;
//BOTH AREA
public function ASWorkerDemo()
{
//important know, all class start here will be replicated in all works.
asWorker = new ASWorker(this.stage, uiWorkerStartedHandler, backWorkerStartedHandler);
}
//BOTH AREA END
//UI AREA START
private function uiWorkerStartedHandler():void{
//implement all class or calls for your UI
NotificationCenter.addEventListener("FROM_BACK_EVENT_MESSAGE", onFromBackEventMessageHandler );
}
private function onFromBackEventMessageHandler(e:NotificationCenterEvent):void
{
trace(e.data);
if(e.data == "completed job"){
NotificationCenter.dispatchEventBetweenWorkers( new NotificationCenterEvent("NEXT_MESSAGE") );
}
}
//UI AREA END
//BACK AREA START
private function backWorkerStartedHandler():void{
//implement all class or calls for your BACK operations
NotificationCenter.addEventListener("NEXT_MESSAGE", uiCallForNextMessageHandler );
}
private function uiCallForNextMessageHandler():void
{
for(var i:int = 0; i < 15; ++i){
NotificationCenter.dispatchEventBetweenWorkers( new NotificationCenterEvent("FROM_BACK_EVENT_MESSAGE", false, i) );
}
NotificationCenter.dispatchEventBetweenWorkers( new NotificationCenterEvent("FROM_BACK_EVENT_MESSAGE", false, "completed job") );
}
// BACK AREA END
}
}
Good luck with Workers
Air 20.0.0.204 ADL is also not enjoying anything worker related (from my limited experimentation), however the desktop Flash Player seems to work just fine. This makes debugging convoluted and problematic when you are using Air specific libraries in your project and prefer not to wildcard variable types.
At the moment, workers are only supported on desktop platform. This Adobe developer's guide page specifically mentions "for desktop platforms".
Meanwhile, there is an AS3-Worker-Compat library by Jeff Ward that enables you to write code with Workers that will still work correctly in an environment that doesn't support Workers (of course, in this case your code will run in single-threaded mode).
I think many people are waiting for Adobe to implement workers for mobile, hopefully it will come in the future.

Yet another "local file access in actionscript-3" q-u-e-stion

I hope I can get some help with my actionscript problem.
(inb4: 'What exactly are you trying to accomplish (other than opening an arbitrary file without user permission)?' link)
I am trying to assist with student exercises - so I thought I'd come up with an example of a random gallery. The idea with this would be that students would be able to generate a SWF file, and in the same directory, they would have a folder with images. Then the SWF movie would read the contents of the directory; choose a random image, and display it.
I would have hoped that this could be done both locally (while students develop) - and, should they choose so, they should be able to upload the entire directory online -- and hopefully, the example would work either way. That turns out not to be so easy - so these are my questions:
They say, that in order to get local directory listing, I should use AIR - what does that mean? Is there some executable - other than flashplayerdebugger - that I should call in order to reproduce the SWF if it uses AIR?
Is it possible to get a local directory listing without using this AIR (in the similar vein as the Flex code below?)
It seems I can load a local text file; but I cannot load a local bitmap file. Would it be possible to load a bitmap file as well?
How can you give local access permission to files/folders, if it so happens that you don't have access to the Internet, so you cannot access settings_manager04.html - but you'd like to use a Flash application both with local files and with network sockets on the local network?
Now, one thing is that most students work with Flash CSx versions - I on the other hand am on Linux, so I have tried to make a Flex example; so I've provided the example as Flex code, but I'd like to know possible answers for the Flash CS environment too. The directory structure, compile lines, and code is given at end of this post - but here is how the "application" directory looks like:
Ok, so here is the problem in a bit more detail. First, I thought I'd try to read the directory contents - so I tried the code here: AS3 Basics: List all files in a folder including subdirectories (AIR ... on students' Flash CS - but that seemed to fail miserably.
So I thought - OK, then let's put in a text file in the same directory as the .swf, which will contain a list of the images; e.g.
cat > fileslist.txt <<EOF
img/01.jpg
img/02.jpg
EOF
Then, the Flash app would read this file first - and then proceed to choose a random image from the contents. But at the very first instant, I get:
An ActionScript error has occurred:
SecurityError: Error #2148: SWF file file:///home/rgaltest/rgaltest.swf cannot access local resource file:///home/rgaltest/fileslist.txt. Only local-with-filesystem and trusted local SWF files may access local resources.
at flash.net::URLStream/load()
at flash.net::URLLoader/load()
at rgaltest()[/home/rgaltest/rgaltest.as:23]
So, the answer to this seems to be:
"If you are getting that error when using flash.net.URLLoader and the free flex compiler, add the following option when you compile to get your swf to work: -use-network=false" (juicy tidbits - Only local-with-filesystem and trusted local SWF files may access local resources.)
"Turns out the solution is to configure your Flash Player to trust the swfs in the project’s /bin-debug directory ... 1) Goto http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html ... " (Solution – Error #2010: Local-with-filesystem SWF files are not permitted to use sockets. » Mark Lapasa)
Now, I would not like to have -use-network=false - because students may need to integrate other network based stuff in that example. Then again, something similar to that "settings_manager04.html" ("Global Security Settings panel") page shows up if you open the SWF in a browser, then right-click, then choose "Global Settings" - but does it work for SWF files being shown as a part of Ctrl-Enter build in Flash CS (or correspondingly, those that are open with flashplayerdebugger?) So I try it - choose "Edit Locations", "Browse for folder", choose /home/rgaltest under "Always Allow":
... and now the fileslist.txt gets loaded alright; also when the .swf is ran with flashplayerdebugger.
Right, so now that the files list is loaded, I'd proceed on loading the bitmaps.. so I add the respective code (with onImgLoaded etc), and this is what I get in the log:
At frame #1
file onTextLoaded: img/01.jpg,img/02.jpg,
At frame #2
file onImgLoaded: [object URLLoader]
TypeError: Error #1034: Type Coercion failed: cannot convert "ÿØÿà
...
¿ÿÙ" to flash.display.Bitmap.
at rgaltest/onImgLoaded()[/home/rgaltest/rgaltest.as:64]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at flash.net::URLLoader/onComplete()
At frame #3
At frame #4
...
Interestingly - the image did get read - as all of its binary contents are dumped to the log! Yet, it cannot be interpreted as bitmap data???!
First of all, this is my directory layout:
mkdir /home/rgaltest
cd /home/rgaltest
touch rgaltest.as
touch fileslist.txt
mkdir img
cd img
convert -size 320x240 gradient:\#4b4-\#bfb 01.jpg
convert -size 320x240 gradient:\#b44-\#bff 02.jpg
chmod -R 777 /home/rgaltest # to ensure no problem with file permissions
The rgaltest.as is given below; I compile that with:
/path/to/flex_sdk_4.6.0.23201_mpl/bin/mxmlc -static-link-runtime-shared-libraries -use-network=true -verbose-stacktraces -debug=true rgaltest.as
... and I check that locally with (make sure mm.cfg is properly set up, see also Using Flash Player debugger):
# in one terminal:
tail -f ~/.macromedia/Flash_Player/Logs/flashlog.txt
# in another terminal:
/path/to/flashplayer_11/flashplayerdebugger rgaltest.swf
And finally, here is rgaltest.as:
package {
import flash.display.Sprite;
import flash.text.TextField;
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.IEventDispatcher;
import flash.net.*; //URLLoader;
import flash.display.Bitmap;
import flash.display.BitmapData;
[SWF(width="550", height="400")]
public class rgaltest extends MovieClip {
public var _pictureArray:Array;
public var frameCnt:Number = 0;
public var myTextLoader:URLLoader;
public var isFileslistLoaded:Boolean = false;
public var myImgLoader:URLLoader;
public var isImgLoading:Boolean = false;
// constructor - create/initialize objects here
public function rgaltest() {
this.stage.frameRate = 10; // frames per second
// load the text file with image list
myTextLoader = new URLLoader();
myTextLoader.addEventListener(Event.COMPLETE, onTextLoaded);
myTextLoader.load(new URLRequest("fileslist.txt"));
// add event listener for onEnterFrame
configureListeners(this);
}
private function configureListeners(dispatcher:IEventDispatcher):void {
dispatcher.addEventListener(Event.ENTER_FRAME, my_OnEnterFrame);
}
public function onTextLoaded(e:Event):void {
_pictureArray = e.target.data.split(/\n/);
trace("file onTextLoaded: " + _pictureArray);
isFileslistLoaded = true;
}
public function my_OnEnterFrame(event:Event):void {
frameCnt++;
trace("At frame #" + frameCnt);
if (isFileslistLoaded) {
if (!isImgLoading) {
//load a random image
var rndChoice:Number = Math.floor(Math.random()*2) + 1;
myImgLoader = new URLLoader();
myImgLoader.addEventListener(Event.COMPLETE, onImgLoaded);
myImgLoader.load(new URLRequest(_pictureArray[rndChoice]));
isImgLoading = true;
}
}
} // end onEnterFrame
public function onImgLoaded(e:Event):void {
trace("file onImgLoaded: " + e.target);
var _image:Bitmap = Bitmap(e.target.data);
var _bitmap:BitmapData = _image.bitmapData;
addChild(_image);
}
} //end class
} //end package
Some other relevant links:
flex - Adding Local Filenames/References To Array In Actionscript - Stack Overflow
flash - Is there a way to read local files in ActionScript 3 "silently"'? - Stack Overflow
adobe flash cs4 as3: get current directory of a running project? - Stack Overflow
flex - open local files in AS3 - Stack Overflow
Bump - it seems I solved the bitmap loading (putting it here in separate post, so that I don't clog the OP too much); I just used Loader class instead of URLLoader; see code below (also note the mistake in random calculation in OP, which is corrected below).
Anyways - this is the thing: when I tried the Loader class on the students' Flash CSx, it tended to fail miserably! But, maybe that was because in those cases, we didn't give any permissions to the folder from "settings_manager04.html"? Any special things that should be taken care of, when a code similar to this is generated in Flash?
Here is the reworked rgaltest.as:
package {
import flash.display.Sprite;
import flash.text.TextField;
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.IEventDispatcher;
import flash.net.*; //URLLoader;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader; //*
[SWF(width="550", height="400")]
public class rgaltest extends MovieClip {
public var _pictureArray:Array;
public var frameCnt:Number = 0;
public var myTextLoader:URLLoader;
public var isFileslistLoaded:Boolean = false;
public var myImgLoader:Loader;
public var isImgLoading:Boolean = false;
// constructor - create/initialize objects here
public function rgaltest() {
this.stage.frameRate = 10; // frames per second
// load the text file with image list
myTextLoader = new URLLoader();
myTextLoader.addEventListener(Event.COMPLETE, onTextLoaded);
myTextLoader.load(new URLRequest("fileslist.txt"));
// add event listener for onEnterFrame
configureListeners(this);
}
private function configureListeners(dispatcher:IEventDispatcher):void {
dispatcher.addEventListener(Event.ENTER_FRAME, my_OnEnterFrame);
}
public function onTextLoaded(e:Event):void {
_pictureArray = e.target.data.split(/\n/);
trace("file onTextLoaded: " + _pictureArray);
isFileslistLoaded = true;
}
public function my_OnEnterFrame(event:Event):void {
frameCnt++;
trace("At frame #" + frameCnt);
if (isFileslistLoaded) {
if (!isImgLoading) {
//load a random image
var rndChoice:Number = Math.floor(Math.random()*2) + 0;
trace("Loading: " + rndChoice + " / " + _pictureArray[rndChoice]);
myImgLoader = new Loader();
myImgLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImgLoaded);
myImgLoader.load(new URLRequest(_pictureArray[rndChoice]));
isImgLoading = true;
}
}
} // end onEnterFrame
public function onImgLoaded(e:Event):void {
trace("file onImgLoaded: " + e.target);
var _image:Bitmap = Bitmap(myImgLoader.content);
var _bitmap:BitmapData = _image.bitmapData;
addChild(_image);
}
} //end class
} //end package

Flash SecurityDomain, P2P settings and multiply swfs

I have two swfs:
application swf
p2p client swf that allows to load data using rtmfp replication
technology (through cirrus service)
The main idea is to have one p2p loader on the certain domain that will able to work in p2p network without asking many times for permission for each domain, so for instance:
application 1 (http://domain1.com/app.swf) |
application 2 (http://domain2.com/app.swf) | <--> p2p data loader (http://domainp2p.com/p2pcli.swf)
application N (http://domainN.com/app.swf) |
The p2p client loads binary data by request, content really does not matter I believe.
So, I load p2pclient swf using following class (app.swf)
public class ClientLoader {
// .. some code
public function load(cb:Function, err:Function):void
{
_cb = cb;
_err = err;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, _onLoaded);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, _onIoError);
loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, _onSecurityError);
// note that context has neither application domain nor security domain
loader.load(new URLRequest(_url), new LoaderContext());
}
private function _onLoaded(e:Event):void
{
trace("Loaded. Connecting to the p2p network...");
_client = e.target.content;
_client.addEventListener(Event.CONNECT, _onClientReady);
_client.connect();
}
private function _onClientReady(e:Event):void
{
_cb(_client);
}
}
}
The p2pclient itself (p2pcli.swf):
public class P2P extends Sprite
{
public function SqP2P() {
Security.allowDomain("*");
}
public function connect():void
{
_connection = new NetConnection();
_connection.addEventListener(NetStatusEvent.NET_STATUS, _netStatus);
_connection.connect(CIRRUS_ADDRESS, CIRRUS_KEY);
// after successful connect this method called
_loadGroup();
}
private method _loadGroup():void
{
var spec:GroupSpecifier = new GroupSpecifier(_name);
spec.serverChannelEnabled = true;
spec.objectReplicationEnabled = true;
_group = new NetGroup(connection, spec.groupspecWithAuthorizations());
_group.addEventListener(NetStatusEvent.NET_STATUS, _netStatus);
}
private function _netStatus(event:NetStatusEvent):void
{
trace("NetStatusEvent:", event.info.code);
}
}
But it looks like that Flash Player ignores the security session and is trying to save the pop-up settings for the domain that app.swf belongs to, but not for the p2pcli.swf domain. Why?!
I have absolutely same code, but p2pcli.swf replaced with swf that stores data in Local Shared Object and all the domain1-2-N.com has access to it.
Any ideas?
I know, my English is crap :(
I really am not totally sure, but I'll throw my answer out there in case it is helpful.
Based on the general purpose of such security messages, I'm not entirely sure you CAN keep that dialog from showing up. In some cases, I'm certain Peer Assisted Networking can be a security risk for some people (and anyway, it is using their bandwidth.) The settings for turning on and off that notification are user-side, in the Flash settings dialog (Control Panel in Windows 7...), thus that hints that it is inherently hardwired into the Flash platform.
Of course, since I'm more of an Adobe AIR specialist, I could be completely wrong...for your project's sake, I'm sincerely hoping I AM!
And, for the record, your English was almost perfect. I tweaked one paragraph for clarity, but otherwise, spot on. :D

AAC/MP4 not working in ActionScript 3's NetStream

I'm trying to play a remote AAC file in ActionScript 3 in Flash CS3 and am currently using this code:
var url:String = "http://a1.phobos.apple.com/us/r1000/020/Music/d4/50/94/mzm.kjjofihr.aac.p.m4a";
var connect_nc:NetConnection = new NetConnection();
connect_nc.connect(null);
var stream_ns:NetStream = new NetStream(connect_nc);
stream_ns.play(url);
(This is based on: http://www.adobe.com/devnet/flashplayer/articles/hd_video_flash_player_03.html)
No errors are thrown, but no sound is played. I get the same behavior with a local AAC file and with a local MP4 video.
If I use a URL or file path that isn't a streamable file, I get a NetStream.Play.StreamNotFound error, which I'm guessing means that the stream is found in the case of a valid URL. If I use a local FLV, its audio is played just fine.
If I add the following listener and trace(evt.info.code) in netStatusHandler, I only see any codes (e.g. NetStream.Play.Start) traced with the FLV. No codes are traced with the AAC or MP4.
stream_ns.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
The same is true of adding this listener (i.e. the onMetaData argument is only traced with the FLV, not with the other file types), with metaDataListener defined as an object with an onMetaData method that traces its argument.
stream_ns.client = metaDataListener;
Any ideas of what might be going wrong here, or how to diagnose it?
Thanks!
As stated here http://www.adobe.com/devnet/flashplayer/articles/hd_video_flash_player_03.html what you are doing is correct.
var connect_nc:NetConnection = new NetConnection();
connect_nc.connect(null);
var stream_ns:NetStream = new NetStream(connect_nc);
stream_ns.play("RE-Sample.m4a");
However, the Actionscript Language reference about nestream found here:
http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/flash/net/NetStream.html#play%28%29
states that:
play () method
...
...
When you use this method without Flash Media Server, there are security considerations. A file in the local-trusted or local-with-networking sandbox can load and play a video file from the remote sandbox, but cannot access the remote file's data without explicit permission in the form of a cross-domain policy file. Also, you can prevent a SWF file running in Flash Player from using this method by setting the allowNetworking parameter of the the object and embed tags in the HTML page that contains the SWF content.
...
...
Parameters
... arguments — The location of the video file to play, as a URLRequest object or a string. In Flash Player and in AIR content outside of the application security sandbox, you can play local video files that are stored in the same directory as the SWF file or in a subdirectory; however, you can't navigate to a higher-level directory.
So it's probably a security sandbox issue.
Everything in ActionScript 3.0 is event based (with few random exceptions where callbacks are used).
You need to listen for the NetStatusEvent with info.code "NetConnection.Connect.Success" in order to be allowed to call the NetStream.play() function.
Here's something that works (I just wrote it now, and tested it for you):
package
{
import flash.display.Sprite;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.events.NetStatusEvent;
import flash.events.AsyncErrorEvent;
import flash.events.Event;
public class MainDocument extends Sprite
{
private var _connection:NetConnection=new NetConnection();
private var _netStream:NetStream=null;
private var _strM4AURL:String="http://a1.phobos.apple.com/us/r1000/020/Music/d4/50/94/mzm.kjjofihr.aac.p.m4a";
//constructor
public function MainDocument():void
{
this._connect();
}
private function _connect():void
{
this._connection.close();
this._connection=new NetConnection();
this._connection.addEventListener(NetStatusEvent.NET_STATUS, this._netStatusHandler);
this._connection.addEventListener(AsyncErrorEvent.ASYNC_ERROR, this._asyncErrorHandler);
this._connection.connect(null);
}
private function _netStatusHandler(event:NetStatusEvent):void
{
trace(event.info.code);
switch (event.info.code)
{
case "NetConnection.Connect.Success":
this._requestAudio();
break;
}
}
private function _requestAudio():void
{
if(this._netStream!==null)
this._netStream.close();
this._netStream=new NetStream(this._connection);
this._netStream.addEventListener(NetStatusEvent.NET_STATUS, this._netStatusHandler);
this._netStream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, this._asyncErrorHandler);
this._netStream.checkPolicyFile=false;
this._netStream.play(this._strM4AURL);
}
private function _asyncErrorHandler(event:AsyncErrorEvent):void
{
trace(event);
}
}
}
Consult the ActionScript 3.0 Language Reference for more information.
There's a good chance what Oliver is saying is true as your not getting any feedback from any event listeners associated with the NetStream, and you are getting a StreamNotFound response.
StreamNotFound when not connecting to a FMS means that you either have the path wrong or its not seeing it, due to a security issue.

Accurate BPM event listener in AS3

I'm trying to sync animation to music at a specific BPM. I've tried using the Timer but it isn't accurate when dealing with small intervals in milliseconds. I did some reading and found an alternate method that uses a small silent audio file and the SOUND_COMPLETE event as a Timer.
I used 167ms long sound file with this code.
package
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.net.URLRequest;
public class BetterTimer extends EventDispatcher
{
private var silentSound:Sound;
public function BetterTimer(silentSoundUrl:String):void {
super();
silentSound = new Sound( new URLRequest(silentSoundUrl) );
silentSound.addEventListener(Event.COMPLETE, start);
}
public function start():void {
this.timerFired( new Event("start") );
}
private function timerFired(e:Event):void {
dispatchEvent( new Event("fire") );
var channel:SoundChannel = silentSound.play();
channel.addEventListener(Event.SOUND_COMPLETE, timerFired, false, 0, true);
}
}
}
This still doesn't stay on beat. Is the Flash Player capable of accuracy with sound?
You can also use the new Sound API with the SampleDataEvent and basically play your MP3 manually using Sound.extract(). In that case you know the latency up front and can even count up to the sample when your (delayed) event should happen.
This is what we do in the AudioTool and it works very well.
This is very tricky to get right! There's a small lib called BeatTimer that tries to do this. I haven't tried this code myself, but if it does what it claims it should be exactly what you need.
Setting the frame rate so that the event interval is a multiple of the frame rate might help (for example, 167ms equals 6 fps; 12, 18, 24 etc. are then also ok).
If I understood correctly, better solution would be to use the enterframe event. Instead of determining the position of the animation by counting the events, calculate it using elapsed time (getTimer or sound position). This would also make the animation work on slower computers that have lag.
I was looking through the popforge library's AudioBuffer and tried using one of the approach. That's the create a sync sound. The following is what i did.
var syncSamples:ByteArray = new ByteArray();
syncSamples.length = (2646000 / _bpm) << 1; /*44100*60=2646000*/
SoundFactory.fromByteArray(syncSamples, Audio.MONO, Audio.BIT16, Audio.RATE44100, soundInit);
The ms delay is pretty close, eg: at 120 BPM, it's between 509 - 512ms. The question is, am I going in the right direction?