ActionScript : Sound from ByteArray has lag - actionscript-3

In my application, I use the microphone to record and get the result in ByteArray, then, I convert it in playable Sound by using the WavSound class from org.as3wavsound.
Record and play work correctly except a huge lag at the launching of the sound ( Easily 2 secondes ).
My code is something like this :
import org.as3wavsound.WavSound;
import org.bytearray.micrecorder.encoder.WaveEncoder;
import org.bytearray.micrecorder.MicRecorder;
/* ... */
var wavEncoder:WaveEncoder = new WaveEncoder( 0.5 );
var recorder:MicRecorder = new MicRecorder( wavEncoder );
recorder.record();
/* ... */
recorder.stop();
var sound:WavSound = new WavSound( recorder.output );
/* ... */
sound.play();
Thanks a lot to help me understand what's wrong in it.

Looks like this is a known issue with the as3wavsound library: Delay When Playing Sound using as3wavsound
The answer on that post tells you to decrease the MAX_BUFFERSIZE in WavSoundPlayer to 2048.

Related

AS3 Blitting is Slower than a Movieclip. Why?

I tried following a combination of Lee Brimlow's blitting tutorial series and and the technique in Rex Van der spuy's "advanced game design with flash"
I am a developer working on a web online virutal world made in flash. I made a phone application (works similar to the phone in grand theft auto games). Anyway, when a message is sent we want to play this crazy animation of an envelope flying around and transforming with sparkles around it. It was laggy (especially on older computers) so I thought it would be a great chance to use blitting. However, the blitting animation actually plays slower than a regular movieclip!! What the heck is going on here? Is blitting only better for mobile devices and actually slower on computers? Maybe I am doing something wrong. Here is my code:
// THIS PART HAPPENS WHEN PHONE IT INITIALIZED
//**
//---------------- Blitting stuff ----------------------------------
// add this bitmap stage to the display list so we can see it
_bitmapStage = new BitmapData(550, 400, true, 0xD6D6D6);
_phoneItself.addChild(new Bitmap(_bitmapStage));
var _spritesheetClass:Class = getDefinitionByName("ESpritesheet_1") as Class;
_spritesheet = new _spritesheetClass() as BitmapData;
_envelopeBlit = new BlitSprite(_spritesheet, BlitConfig.envelopeAnimAry , _bitmapStage);
_envelopeBlit.x = -100;
_envelopeBlit.y = 0;
_envelopePlayTimer = new Timer(5, 0);
_envelopePlayTimer.addEventListener(TimerEvent.TIMER, onEnterTimerFrame);
_envelopeBlit.addEventListener("ENV_ANIM_DONE", onEnvAnimFinished);
// a "BlitSprite" is a class that I made. It looks like this:
package com.fs.util_j.blit_utils
{
import flash.display.BitmapData;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.geom.Point;
import flash.geom.Rectangle;
public class BlitSprite extends EventDispatcher
{
private var _fullSpriteSheet:BitmapData;
private var _rects:Array;
private var _bitmapStage:BitmapData;
private var pos:Point = new Point ();
public var x:Number = 0;
public var y:Number = 0;
public var _animIndex:
int = 0;
private var _count:int = 0;
public var animate:Boolean = true;
private var _whiteTransparent:BitmapData;
private var _envelopeAnimAry:Array;
private var _model:Object;
public function BlitSprite(fullSpriteSheet:BitmapData, envelopeAnimAry:Array, bitmapStage:BitmapData, model:Object = null)
{
_fullSpriteSheet = fullSpriteSheet;
_envelopeAnimAry = envelopeAnimAry;
_bitmapStage = bitmapStage;
_model= model;
init();
}
private function init():void
{
// _whiteTransparent = new BitmapData(100, 100, true, 0x80FFffFF);
this.addEventListener("ENV_ANIM_DONE", onEvnAnimDone);
}
protected function onEvnAnimDone(event:Event):void
{
}
public function render():void
{
// pos.x = x - _rects[_animIndex].width*.5;
// pos.y = y - _rects[_animIndex].width*.5;
// if (_count % 1 == 0 && animate == true)
// {
// trace("rendering");
if (_animIndex == (_envelopeAnimAry.length - 1) )
{
// _animIndex = 0;
dispatchEvent(new Event("ENV_ANIM_DONE", true));
animate = false;
// trace("!!!!animate over " + _model.animOver);
// if (_model != null)
// {
// _model.animOver = true;
// }
// trace("!!!!animate over " + _model.animOver);
}
else
{
_animIndex++;
}
pos.x = x + _envelopeAnimAry[_animIndex][1];
pos.y = y + _envelopeAnimAry[_animIndex][2];
_bitmapStage.copyPixels(_fullSpriteSheet, _envelopeAnimAry[_animIndex][0], pos, null, null, true);
}
}
}
// THIS PART HAPPENS WHEN PHONE'S SEND BUTTON IS CLICKED
_envelopeBlit.animate = true;
_envelopeBlit._animIndex = 0;
_darkSquare.visible = true;
_envelopePlayTimer.addEventListener(TimerEvent.TIMER, onEnterTimerFrame);
_envelopePlayTimer.start();
it also uses BlitConfig which stores the info about the spritesheet spit out by TexturePacker
package com.fs.pack.phone.configuration
{
import flash.geom.Rectangle;
public final class BlitConfig
{
public static var _sending_message_real_20001:Rectangle = new Rectangle(300,1020,144,102);
public static var _sending_message_real_20002:Rectangle = new Rectangle(452,1012,144,102);
public static var _sending_message_real_20003:Rectangle = new Rectangle(852,852,146,102);
public static var _sending_message_real_20004:Rectangle = new Rectangle(2,1018,146,102);
public static var _sending_message_real_20005:Rectangle = new Rectangle(702,822,148,102);
.
.
.
public static var _sending_message_real_20139:Rectangle = new Rectangle(932,144,1,1);
public static var envelopeAnimAry:Array = [
// rectangle, x offset, y offset
[ _sending_message_real_20001, 184,155],
[ _sending_message_real_20002, 184,155],
[ _sending_message_real_20003, 183,155],
[ _sending_message_real_20004, 183,155],
.
.
.
[ _sending_message_real_20139, 0,0]
]
public function BlitConfig()
{
}
}
}
EDIT:
Knowing that this is not mobile, my answer below is irrelevant. I will leave it there, though, in case someone is having trouble with blitting on mobile in the future.
With regards to this specific question, you are running your timer every 5ms. First off, the lowest range that a Timer is accurate is >15ms so that will never be a viable solution. For any Timer relating to displaying soemthing on the stage, you should never do it less than a single frame. (1000/stage.framerate. ~40ms for a 30fps app)
For blitting, the goal is to reduce calculations and rendering. The way you have this set up right now, it looks like you are blitting every 5ms. That is actually more than 8 times as often as the MovieClip is rendering. You should reduce how often you blit. Only do it when a change has actually been made beyond translation. Doing it any more often than that is overkill and the reason it is so slow (again, creating bitmaps is slow)
In general, you do not want to blit in an AIR for Mobile application (which I assume you are doing since you mentioned the phone being initialized). I'm not sure if it is okay to do it using other/native SDKs, but avoid it in AIR.
Essentially, it comes down to how blitting works. Blitting takes a screen capture and displays that on the stage rather than the actual object. In general, this is great. It means that your display objects, particularly vectors which are slow to render, have to render far less often. It is especially good when animating because an object tends to re-render every time it is translated in any way, but not a bitmap.
On mobile platforms, however, creating that bitmap is incredibly slow. I've never looked into how the SDK creates the Bitmaps, but it doesn't do it efficiently (it often makes me wonder if it does it pixel-by-pixel). On desktops, this is generally fine. There is plenty of CPU and plenty of RAM to make this happen quickly. On mobile, however, that luxury is not there at the moment. So when you blit and create that bitmap, it takes a while to run that process.
The problem is exacerbated on high-resolution screens. An app I developed from January to May of this year selectively used blitting to use filters in a GPU accelerated environment. On an iPad 2, the blitting took my app from 30fps to ~24fps. Not a big deal, not anything the user would notice. On an iPad 3 with retina display, however, it dropped down to 10fps. It makes sense when you think about it, as retina iPads have 4x as many pixels as non-retina iPads do.
If you do want to use blitting on mobile, I recommend a few things:
Use GPU rendering mode. Without it, you stand no chance. Be aware that, at least with pre-AIR 3.7, filters were not supported in GPU mode. I am unsure if that is still the case. You should avoid using filters on mobile regardless, though, as they are very slow to render
Make sure to test a release-mode application. Depending on build settings, the difference between debug mode and a release mode app can be substantial, especially on iOS. An app I just developed went from taking 2-3 seconds to create a new Flex View in debug mode to less than a frame (~40ms) to do it in release mode on an iPhone 4
Use blitting sparingly. Only do it where absolutely necessary
Look for ways to simplify your display list. It is easy to have an object with 40 children to create a button. Instead, look for ways to simplify that into fewer objects and fewer filters (even if removing a filter requires you add another object). I don't believe this will help with the actual blitting process, but it should help with rendering the objects in the first place.
So in general, use blitting sparingly on mobile because bitmap creation is slow.

Fast Fourier Transform With Microphone Input Data in AS3

Is it possible to get the source of adobes flash.media.SoundMixer class ?
If it is, where can i find / get it ?
I waht to "clone" the .computeSpectrum() function to transform a raw sound wave ( byteArray ) from Microphone input into a frequency spectrum.
I've found a couple of examples like this one -> http://pierrickpluchon.fr/blog/as3-how-to-plug-your-microphone-with-a-soundspectrum-in-flash-player-10-1/
All other methos i've found are pretty much the same.
The problem is that there is always a Sound() playing, what i DON'T want. ( I don't want any loopback )
But if i'm not playing a sound, i can't use the SoundMixer.computeSpectrum() function to transform my ByteArray that comes from the Microphone to a frequency spectrum by turning the FFTMode to true ( computeSpectrum(myByteArray,true) )
Also if you know any other method to get the Frequency Spectrum from the Raw Sound Wave, please let me know.
UPDATE
my code:
var bytes:ByteArray = new ByteArray();
var mic:Microphone = Microphone.getMicrophone();
mic.rate = 44;
// mic.gain = 100; // gain
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
function onSampleData( event:SampleDataEvent ):void {
graphics.clear();
graphics.lineStyle(1, 0xFF0000);
for( var i:uint = 0; i < 256; i++ ) {
var num:Number = event.data.readFloat() * 100 + 100; // -Math.abs( )
if( i == 0 ) {
graphics.moveTo( i, num );
} else {
graphics.lineTo( i, num );
}
}
}
"..Also if you know any other method to get the Frequency Spectrum from the Raw Sound Wave, please let me know."
Well Joe Beuckman beat me to it and gave you the link to Gerry Beauregard's FFT code. That's the best AS3 one I've seen so far since I found it two years ago. From the comments I see you were wondering how to implement... Well to see implementation code you need to see another page on his blog:
http://gerrybeauregard.wordpress.com/2010/08/06/real-time-spectrum-analysis/
To test that code yourself you first have to save the classes shown in the link here: http://gerrybeauregard.wordpress.com/2010/08/03/an-even-faster-as3-fft/
Save each package's code respectively as FFT2.as and FFTElement.as
Now in your document class put the code from: http://gerrybeauregard.wordpress.com/2010/08/06/real-time-spectrum-analysis/
However in that code you must also add some lines importing the other saved .as classes
import __AS3__.vec.Vector;
import flash.display.Sprite;
import flash.events.*;
import flash.media.Microphone;
import flash.text.*;
import flash.utils.*;
import FFT2;
import FFTElement;
Now it should run without errors and show the same thing as screenshot on his blog. The online demo used to work for me but not today so I say screenshot just so you know what to expect when it works fine.
Hope it helps. VC:One
FFT means the Fast Fourier Transform. That is exactly the algorithm that transforms the raw sound wave values into the frequency space. You should be able to find (or port) an implementation of the FFT in AS3, and that does what you ask.

How do you loop a sound in flash AS3 when it ends?

What AS3 code is used to loop a sound using AS3?
This won't give you perfect, gapless playback but it will cause the sound to loop.
var sound:Sound = new Sound();
var soundChannel:SoundChannel;
sound.addEventListener(Event.COMPLETE, onSoundLoadComplete);
sound.load("yourmp3.mp3");
// we wait until the sound finishes loading and then play it, storing the
// soundchannel so that we can hear when it "completes".
function onSoundLoadComplete(e:Event):void{
sound.removeEventListener(Event.COMPLETE, onSoundLoadComplete);
soundChannel = sound.play();
soundChannel.addEventListener(Event.SOUND_COMPLETE, onSoundChannelSoundComplete);
}
// this is called when the sound channel completes.
function onSoundChannelSoundComplete(e:Event):void{
e.currentTarget.removeEventListener(Event.SOUND_COMPLETE, onSoundChannelSoundComplete);
soundChannel = sound.play();
}
If you want the sound to loop many times with a flawless, gapless playback, you can call
sound.play(0, 9999); // 9999 means to loop 9999 times
But you still would need to set up a soundcomplete listener if you want infinite playback after the 9999th play. The problem with this way of doing things is if you have to pause/restart the sound. This will create a soundChannel whose duration is 9999 times longer than the actual sound file's duration, and calling play(duration) when duration is longer than the sound's length causes a horrible crash.
var sound:Sound = whateverSoundYouNeedToPlay;
function playSound():void
{
var channel:SoundChannel = sound.play();
channel.addEventListener(Event.SOUND_COMPLETE, onComplete);
}
function onComplete(event:Event):void
{
SoundChannel(event.target).removeEventListener(event.type, onComplete);
playSound();
}
import flash.media.Sound;
import flash.media.SoundChannel;
var mySound:Sound = new Bgm(); //Bgm() is the class of the internal sound which can be done in the library panel.
playSound();
function playSound():void
{
var channel:SoundChannel = mySound.play();
channel.addEventListener(Event.SOUND_COMPLETE, onComplete);
}
function onComplete(event:Event):void
{
SoundChannel(event.target).removeEventListener(event.type, onComplete);
playSound();
}
This works perfectly.
To expand on #scriptocalypse's gapless playback a bit:
The problem of not having proper gapless playback comes from mp3 including information about the file in either the head or the tail of the file (id3 tags etc), hence the small pause when you try to loop it. There are a few things you can do depending on your situation.
Ignore it, just play as normal, with a small pause at the end of every file. You can also try and mask it with another sound (a beat drop yo), or fade out and fade in.
If your sounds are embedded, and not streaming, then create a fla file, drag your mp3 in there, and set them to export (the same way you'd add a linkage name for a MovieClip etc). It seems that when you export sounds like this, Flash takes the delay into account, or strips it out when it creates the Sound object. Either way, you can just do a simple play() passing the loops that you want for a gapless playback (I've found using a loops parameter is better than waiting on the SOUND_COMPLETE event and playing it again).
You can try some of the ogg libraries to use .ogg files instead of .mp3. A simple google search for "as3 ogg lib" will turn up what you need. Personally, I found them a bit awkward to use, and I couldn't afford the overhead added (as opposed to mp3 decoding, which is done in the player).
If your mp3 files are streaming, then the only way to get gapless playback is to layer them. Determine the gap (depending on what you used to encode them, it'll be different - my files has a gap of about 330ms), and when you reach it, start playing the overlay. It's a proper pain if you're doing fading, but when it works, it works quite nicely. Worst case scenario, you end up with situation (1)
I guess this what you looking for in case the voice/music file is in the library:
var mysound:my_sound = new my_sound();
mysound.play(0,2); // this will repeat the sound 2 times.
This appears to have worked for me:
var nowTime:Number = (new Date()).time;
var timeElapsed:Number = nowTime - _lastTime;
_lastTime = nowTime;
_musicTimeElapsed+=timeElapsed;
if(_musicTimeElapsed >= _musicA.length - GAP_LENGTH)
{
_musicTimeElapsed = 0;
_musicA.play(0);
}
The other answers are great, however if you do not want to use code (for whatever reason), you can put the sound in a movieclip, set the sound property to "Stream", and then add as many frames as you like to the movie clip to ensure it plays fully.
This, of course, is a less preferred way, but for animators I'm sure it may be preferable in some situations (for example when synced with mouth animations that the animator wants looped).
this work for me :
import flash.media.Sound;
import flash.media.SoundChannel;
var soundUrl:String ="music.mp3";
var soundChannel:SoundChannel = new SoundChannel();
var sound:Sound = new Sound();
sound.load(new URLRequest(soundUrl));
soundChannel = sound.play();
soundChannel.addEventListener(Event.SOUND_COMPLETE,onComplete);
function onComplete(e:Event):void{
sound = new Sound();
sound.load(new URLRequest(soundUrl));
soundChannel = sound.play();
soundChannel.addEventListener(Event.SOUND_COMPLETE,onComplete);
}

How to set Video Frame Capture Using Bitmap Data

I'm implementing an Augmented Reality application for android using Flash. In order to get the application working on my Android Phone (nexus One) the Phone Camera must be activated as well. So I need 2 layers one for the background which is the feed of my phone camera and an other one on top of it which is the view from away3d in this case.
So setting up a BitmapData object to hold the information of the most recent webcam still-frame I can make this work.
If I use papervision3D library and FLARToolkit we setting up the BitmapData using the following part of the code found from this video tutorial:
//import libraries
import org.libspark.flartoolkit.core.raster.rgb.FLARRgbRaster_BitmapData;
import org.libspark.flartoolkit.detector.FLARSingleMarkerDetector;
private function setupCamera():void
{
vid = new Video(640, 480);
cam = Camera.getCamera();
cam.setMode(320, 240, 10);
vid.attachCamera(cam);
addChild(vid);
}
private function setupBitmap():void
{
bmd = new BitmapData(640, 480);
bmd.draw(vid);
raster = new FLARRgbRaster_BitmapData(bmd);
detector = new FLARSingleMarkerDetector(fparams, mpattern, 80);
}
private function loop(e:Event):void
{
if(detector.detectMarkerLite(raster, 80) && detector.getConfidence() > 0.5)
{
vp.visible = true;
detector.getTransformMatrix(trans);
container.setTransformMatrix(trans);
bre.renderScene(scene, camera, vp);
}
else{
vp.visible = false}
}
catch(e:Error){}}}}
However, to implement my application Im using Away3D engine and FLARManager and the way of doing that is very different as I can understand. I have implement the following code but the only think it does is just show the Flash Camera in the front of the 3D view and I can't check if my application is work or not since it doesn't show me any 3D Object when I place the marker in front of the screen.
My code is:
//Setting Up Away3DLite Camera3D
import com.transmote.flar.camera.FLARCamera_Away3DLite;
private var camera3D:FLARCamera_Away3DLite;
this.camera3D = new FLARCamera_Away3DLite(this.flarManager, new Rectangle(0, 0, this.stage.stageWidth, this.stage.stageHeight));
//Setting Up the bitmapData
private function bitmap():void
{
c = Camera.getCamera();
c.setMode(320,240,10)
this.v.attachCamera(c);
addChild(this.v);
bmd = new BitmapData(640,480);
bmd.draw(this.v);
}
Can you please help me to find out how can I combine those two?
I will really appreciate any advice i can get from you.
Thank you
To isolate your problem, I'd try to break these two things up and make sure each part works first. It's sounds like you've got the camera part working, try doing just some 3D (no AR) draw a cube or something. Then try implementing the AR but have it do something simple like trace something out or making an object visible or invisible. Then start combining them.

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?