How to code an audio envelope (attack time, fade in) for a Sound object? - actionscript-3

I made a simple sine wave tone generator. The problem is that when the tone is played a strong click can be heard, and I need to implement a fast fade in (attack time) to avoid this.
I tried using tweening (like tweenmax) but it induces distortion to the audio (maybe the steps in the tweening?). I found some vague tutorials on the subject but nothing regarding attack time specifically.
How can I do this?

For the fade to sound smooth, it has to be incremented on a per-sample basis, inside your synthesis loop. A tween engine may update many times a second, but your ear can still hear the changes as a click.
In your sampleData event handler, you will have to multiply the individual samples by a volume modifier, with the range of 0 to 1, incrementing for every sample.
To quickly fade in the sound, start by setting the volume to 0, and adding a small value to it for each sample, until it reaches 1.0. You can later expand this into a more complex envelope controller.
This is a rough example of what you might start with:
for( i = 0; i < length; i++ ) {
_count++;
factor = _frequency * Math.PI * 2 / 4400;
volume += 1.0 / 4400;
if( volume > 1.0 ) volume = 1.0; //don't actually do it like this, ok?
n = Math.sin( (_count) * factor ) * volume;
_buffer.writeFloat(n);
_buffer.writeFloat(n);
}
NOTE: I haven't tested this snippet, nor would I recommend using it for production. It's just to show you roughly what I mean.
Another technique that may work for you is to put an ease / delay on the volume. Use a volumeEase variable that always 'chases' the target volume at a certain speed. This will prevent clicks when changing volumes and can be used to make longer envelopes:
var volume:Number = 0; // the target volume
var volumeEase:Number = 1.0; // the value to use in the signal math
var volumeEaseSpeed:Number = 0.001; // tweak this to control responsiveness of ease
for( i = 0; i < length; i++ ) {
_count++;
// bring the volumeEase closer to the target:
volumeEase += ( volume - volumeEase ) * volumeEaseSpeed;
factor = _frequency * Math.PI * 2 / 4400;
//use volumeEase in the maths, rather than 'volume':
n = Math.sin( (_count) * factor ) * volumeEase;
_buffer.writeFloat(n);
_buffer.writeFloat(n);
}
If you wanted to, you could just use a linear interpolation, and just go 'toward' the target at a constant speed.
Once again, the snippet is not tested, so you may have to tweak volumeEaseSpeed.

Related

libgdx - fixed timestep with interpolation - without box2d

I am having some problems implementing fixed timestep with graphic interpolation in my game.
Here is part of the render method:
#Override
public void render(float delta)
{
double newTime = TimeUtils.millis() / 1000.0;
double frameTime = Math.min(newTime - currentTime, 0.25);
accumulator += frameTime;
currentTime = newTime;
while (accumulator >= step)
{
updateObjects(step);
accumulator -= step;
}
double alpha = accumulator / step;
interpolateObjects((float)alpha);
}
Here is updateObjects:
for (int i = 0; i < world.level.gameObjects.size(); i++)
{
GameObject go = world.level.gameObjects.get(i);
go.prevPosition.set(go.position);//save previous position
go.update(delta);
}
interpolateObjects:
for (int i = 0; i < world.level.gameObjects.size(); i++)
{
GameObject go = world.level.gameObjects.get(i);
go.position.lerp(go.prevPosition, alpha);
}
And then objects are rendered using position
As far as i can tell this should work, but it doesn't.
On high fps (200-400) everything is too slow, movement isn't even visible, i can just see that position is changing by 0.0001 or something like that
On low fps (10-20), movement is visible but again objects are very slow...
If i disable interpolation, than everything works as it should (on any fps), but then everything is jittery.
So the problem is somewhere in interpolation.
Your interpolation go.position.lerp(go.prevPosition, alpha) is set up to assume that prevPosition was last updated at an exact multiple of step, but then when you update prevPosition like this go.prevPosition.set(go.position) you are destroying that contract on the first update of the frame. It also looks like you are lerping backwards (from position to the previous position).
I think you need a third vector so the last interpolated value is guaranteed not to influence your fixed time updates. Here I'll call it interpPosition, and it will be used for drawing instead of position.
You actually seem to technically be extrapolating (not interpolating) the value, since you are not updating ahead of time and your alpha is calculated from time left in the accumulator. If you want to linearly extrapolate from the last two positions calculated, you can do it like this (note the 1+alpha to extrapolate):
for (int i = 0; i < world.level.gameObjects.size(); i++)
{
GameObject go = world.level.gameObjects.get(i);
interpPosition.set(go.prevPosition).lerp(go.position, 1 + alpha);
}
Depending on the speed of your simulation (and how fast objects can accelerate), this might still look jerky. I think a smoother, but computationally slower way to do this would be to do a fully calculated update using alpha instead of step time, and storing that in the interpPosition vector. But only do that if necessary.

Gravity trajectory with already known destination

I have been spending several days trying to solve this problem and I am about to go mad. Your help would be very much appreciated.
I am using a 2D stage in libgdx. I want to move actors (or sprite) to this stage with a "gravity" display effect: for example for actor1, his initial coordinates are (0, 0), destination coordinates would be (100, 50), and I want to move this actor to this destination with a gravity trajectory effect. Then I want to use the same gravity for actor2 moving from (0, 0) to (25, 75), then actor3 from (0, 0) to (200, 75) etc.
I managed to apply a gravity trajectory to any actor based on this well known loop:
setX(getX() + velocity.x);
setY(getY() + velocity.y);
velocity.y += getGravity().y * delta;
So tweaking the gravity value would modify the trajectory. It works fine.
Now, as I said earlier I want to give every actors a unique trajectory given their predetermined destination.
So I have tried to find a formula to determine the x and y for each actor at every frame of their trajectory
I am using the following static parameters:
gravity.y : same for all actors
delay : the amount of frames during which each actor moves between his initial coordinates and his destination coordinates. Same value for all actors too
First I calculate the velocity with this SUPER UGLY formula that I am absolutely not proud of:
velocity = new Vector2 ( (destinationx - b.getX() )/time, initdisty/time + ( Math.sqrt(delta*1000)*time / ( 500/Math.abs(gravity) ) ));
where delta = Gdx.graphics.getDeltaTime();
Then I apply this velocity each frame to calculate the x and y of each actor:
public void act(float delta){
for (int i=0; i<delay; i++) {
setX(getX() + velocity.x);
setY(getY() + velocity.y);
velocity.y += gravity.y * delta;
}
}
It KIND OF work, but of course, this can not be a long term solid solution. Calculating the x and y for each frame for each actor (there can be 5-6 actors moving at the same time) doesn't look good at all.
The main problem is that the trajectories are good on computer with consistent 60FPS, they are okay on a tablet, but on a phone with limited memory and 30 < fps < 60, the trajectories become terribly wrong.
After reading several blog posts, it seems like I could avoid the multi device memory fps problems by removing the delta parameter from my formulas, but I haven't found how. And it still doesn't give me a strong long term solution to calculate the trajectory with predetermined destination coordinates.
Thanks for reading and for your time, please let me know if this is unclear I'll do my best to explain better.
Cause of the problem
Maintaining both position and velocity leads to discretization of the system resulting in quantization error. So you will experience inconsistent behavior by your current method when fps fluctuates.
Solution
All you need is to reduce your number of state variables to only two i.e. don't store current velocity. It is causing the errors in final position.
In stead use parametric form of the trajectory.
v = u + at
and
s = ut + ½at²
Implementation
Suppose you want to go from (sourceX, sourceY) to (targetX, targetY) in time 'totalTime'.
Calculate initial velocity.
Vector2 initialVelocity = new Vector2((targetX - sourceX) / totalTime,
(targetY - sourceY) / totalTime - gravity * totalTime / 2);
float currentTime = 0;
In each iteration, calculate position directly and keep track of currentTime.
public void act(float delta){
if (currentTime < totalTime) {
currentTime += delta;
setX((initialVelocity.x + gravity.x * currentTime / 2) * currentTime);
setY((initialVelocity.y + gravity.y * currentTime / 2) * currentTime);
} else {
setX(targetX);
setY(targetY);
}
}

steering behavior and passed time

I'm trying to implement steering behaviors but I have a problem with "including" the passed time in the calculation and then allowing me to control the speed of the game. I have seen various sources of steering behaviors and I've come up with this (Arrive behavior):
var toTarget:Vector2D = new Vector2D( mEntity.targetPosition.x, mEntity.targetPosition.y );
toTarget.subtract( mEntity.position );
var dist:Number = toTarget.length;
toTarget.normalize().scale( mEntity.maxSpeed );
if( dist < slowDownDist ) {
toTarget.scale( dist / slowDownDist );
}
return toTarget.subtract( mEntity.velocity );
And here's the advanceTime method of MovingEntity:
var steeringForce:Vector2D = mSteering.calculate();
steeringForce.x /= mMass;
steeringForce.y /= mMass;
steeringForce.scale( time );
mVelocity.x += steeringForce.x;
mVelocity.y += steeringForce.y;
x += mVelocity.x * time;
y += mVelocity.y * time;
The steering force should be (at some point) directly opposite to the entity's velocity and thus making it stop. The problem I see and do not understand is that in order to simulate acceleration the force needs to be divided by mass and also to consider the passed time it needs to be multiplied by that time - but this scales the steering force down a lot and causes the entity to overshoot the target spot instead of stopping there so then it returns back which effectively causes oscillation. If I do not multiply the force with the time then the moving entity behaves slightly differently depending on the game speed.
By second (?) Newton's law F = m * a where F is force, m is mass and a is acceleration. Force is measured in newtons, mass in kilograms and acceleration in meters per second per second. So, you just need to apply bigger force, and lave the calculation as is.

How to make smooth moving using as3?

I have loaded some images through XML and attached into dynamically created MovieClips named mc0,mc1,mc2...etc.
_loader.removeEventListener(ProgressEvent.PROGRESS, onLoadingAction);
count++;
var img:Bitmap = Bitmap(e.target.content);
img.cacheAsBitmap = true;
img.smoothing = true;
img.alpha = 0;
TweenLite.to(MovieClip(my_mc.getChildByName("mc"+count)).addChild(img),1, {alpha:1,ease:Quint.easeIn});
and within ENTER_FRAME handler
for (i=0; i < mc.numChildren; i++)
{
my_mc.getChildAt(i).x -= Math.round((mouseX-stage.stageWidth/2)*.006);
}
Everthing works fine. But it is shaking so that it was not looking good.
How do I achieve smooth movement?
One solution I've used is to round the (x,y) position to the closest integer. No matter that you've added smoothing to your bitmap and cached it, rounding could make it feel less choppy and way smoother.
Another thing you need to be careful is the dimensions of the images. Images that have an odd dimension won't be smoothed the same way as images with even dimensions. Check how to workaround this in my blog post Flash Smoothing Issue.
Since Flash has a variable frame rate (in the sense that it will drop frames), one shouldn't depend on the entering of a frame as a unit of action. Rather, it would be wiser to calculate the elapsed time explicitly.
For instance, in the enter frame handler:
var currentTime:Number = (new Date()).time;
for (i=0; i < mc.numChildren; i++)
{
my_mc.getChildAt(i).x -= speed * (currentTime - lastTime); // speed is in px/ms
}
lastTime = currentTime;
where you have the variable lastTime declared somewhere in a persistent scope:
var lastTime:Number = (new Date()).time;
I don't know if this addresses what you are calling "shaking", but it's at least something to consider.

How to get unrotated display object width/height of a rotated display object?

If I create a rectangle with 100px width and 100px height and then rotate it, the size of the element's "box" will have increased.
With 45 rotation, the size becomes about 143x143 (from 100x100).
Doing sometimes like cos(angleRad) * currentWidth seems to work for 45 rotation, but for other bigger angles it doesn't.
At the moment I am doing this:
var currentRotation = object.rotation;
object.rotation = 0;
var normalizedWidth = object.width;
var normalizedHeight = object.height;
object.rotation = currentRotation;
Surely, there must be a better and more efficient way. How should I get the "normalized" width and height of a displayobject, aka the size when it has not been rotated?
The best approach would probably be to use the code posted in the question - i.e. to unrotate the object, check its width, and then re-rotate it. Here's why.
First, simplicity. It's obvious what's being done, and why it works. Anyone coming along later should have no trouble understanding it.
Second, accuracy. Out of curiosity I coded up all three suggestions currently in this thread, and I was not really surprised to find that for an arbitrarily scaled object, they give three slightly different answers. The reason for this, in a nutshell, is that Flash's rendering internals are heavily optimized, and among other things, width and height are not stored internally as floats. They're stored as "twips" (twentieths of a pixel) on the ground that further accuracy is visually irrelevant.
Anyway, if the three methods give different answers, which is the most accurate? For my money, the most correct answer is what Flash thinks the width of the object is when it's unrotated, which is what the simple method gives us. Also, this method is the only one that always give answers rounded to the nearest 1/20, which I surmise (though I'm guessing) to mean it's probably equal to the value being stored internally, as opposed to being a calculated value.
Finally, speed. I assume this will surprise you, but when I coded the three methods up, the simple approach was the fastest by a small margin. (Don't read too much into that - they were all very close, and if you tweak my code, a different method might edge into the lead. The point is they're very comparable.)
You probably expected the simple method to be slower on the grounds that changing an object's rotation would cause lots of other things to be recalculated, incurring overhead. But all that really happens immediately when you change the rotation is that the object's transform matrix gets some new values. Flash doesn't really do much with that matrix until it's next time to draw the object on the screen. As for what math occurs when you then read the object's width/height, it's difficult to say. But it's worth noting that whatever math takes place in the simple method is done by the Player's heavily optimized internals, rather than being done in AS3 like the algebraic method.
Anyway I invite you to try out the sample code, and I think you'll find that the simple straightforward method is, at the least, no slower than any other. That plus simplicity makes it the one I'd go with.
Here's the code I used:
// init
var clip:MovieClip = new MovieClip();
clip.graphics.lineStyle( 10 );
clip.graphics.moveTo( 12.345, 37.123 ); // arbitrary
clip.graphics.lineTo( 45.678, 29.456 ); // arbitrary
clip.scaleX = .87; // arbitrary
clip.scaleY = 1.12; // arbitrary
clip.rotation = 47.123; // arbitrary
// run the test
var iterations:int = 1000000;
test( method1, iterations );
test( method2, iterations );
test( method3, iterations );
function test( fcn:Function, iter:int ) {
var t0:uint = getTimer();
for (var i:int=0; i<iter; i++) {
fcn( clip, i==0 );
}
trace(["Elapsed time", getTimer()-t0]);
}
// the "simple" method
function method1( m:MovieClip, traceSize:Boolean ) {
var rot:Number = m.rotation;
m.rotation = 0;
var w:Number = m.width;
var h:Number = m.height;
m.rotation = rot;
if (traceSize) { trace([ "method 1", w, h ]); }
}
// the "algebraic" method
function method2( m:MovieClip, traceSize:Boolean ) {
var r:Number = m.rotation * Math.PI/180;
var c:Number = Math.abs( Math.cos( r ) );
var s:Number = Math.abs( Math.sin( r ) );
var denominator:Number = (c*c - s*s); // an optimization
var w:Number = (m.width * c - m.height * s) / denominator;
var h:Number = (m.height * c - m.width * s) / denominator;
if (traceSize) { trace([ "method 2", w, h ]); }
}
// the "getBounds" method
function method3( m:MovieClip, traceSize:Boolean ) {
var r:Rectangle = m.getBounds(m);
var w:Number = r.width*m.scaleX;
var h:Number = r.height*m.scaleY;
if (traceSize) { trace([ "method 3", w, h ]); }
}
And my output:
method 1,37.7,19.75
Elapsed time,1416
method 2,37.74191378925391,19.608455916982187
Elapsed time,1703
method 3,37.7145,19.768000000000004
Elapsed time,1589
Surprising, eh? But there's an important lesson here about Flash development. I hereby christen Fen's Law of Flash Laziness:
Whenever possible, avoid tricky math by getting the renderer to do it for you.
It not only gets you done quicker, in my experience it usually results in a performance win anyway. Happy optimizing!
Here's the algorithmic approach, and its derivation.
First, let's do the opposite problem: Given a rectangle of unrotated width w, unrotated height h, and rotation r, what is the rotated width and height?
wr = abs(sin(r)) * h + abs(cos(r)) * w
hr = abs(sin(r)) * w + abs(cos(r)) * h
Now, try the problem as given: Given a rectangle of rotated width wr, rotated height hr, and rotation r, what is the unrotated width and height?
We need to solve the above equations for h and w. Let c represent abs(cos(r)) and s represent abs(sin(r)). If my rusty algebra skills still work, then the above equations can be solved with:
w = (wr * c - hr * s) / (c2 - s2)
h = (hr * c - wr * s) / (c2 - s2)
You should get the bounds of your square in your object's coordinate space (which means no rotations).
e.g.
var b:Sprite = new Sprite();
b.graphics.lineStyle(0.1);
b.graphics.drawRect(0,0,100,100);
b.rotation = 10;
trace('global coordinate bounds: ' + b.getBounds(this));//prints global coordinate bounds: (x=-17.35, y=0, w=115.85, h=115.85);
trace('local coordinate bounds: ' + b.getBounds(b));//prints local coordinate bounds: (x=0, y=0, w=100, h=100)
HTH,
George
Chip's answer in code:
// convert degrees to radians
var r:Number = this.rotation * Math.PI/180;
// cos, c in the equation
var c:Number = Math.abs(Math.cos(r));
// sin, s in the equation
var s:Number = Math.abs(Math.sin(r));
// get the unrotated width
var w:Number = (this.width * c - this.height * s) / (Math.pow(c, 2) - Math.pow(s, 2));