real-time processing web audio api - javascript

I am working with web audio api and requestAnimationFrame to visualize the audio input from microphone. I can successfully visualize the time-domain frequency data, but the problem is that since web audio api calculates the time in seconds, every second my interface changes depending on what the input is.
So my question is, how can I visualize the sound and make the graph stay on screen, thus I can see all my frequency data for a certain limit of time (let's say I speak and meanwhile visualize on canvas for only 5 seconds).
I am using the following code (took from examples here):
MicrophoneSample.prototype.visualize = function() {
this.canvas.width = this.WIDTH;
this.canvas.height = this.HEIGHT;
var drawContext = this.canvas.getContext('2d');
var times = new Uint8Array(this.analyser.frequencyBinCount);
this.analyser.getByteTimeDomainData(times);
for (var i = 0; i < times.length; i++) {
var value = times[i];
var percent = value / 256;
var height = this.HEIGHT * percent;
var offset = this.HEIGHT - height - 1;
var barWidth = this.WIDTH/times.length;
drawContext.fillStyle = 'purple';
drawContext.fillRect(i * barWidth, offset, 1, 1);
}
requestAnimFrame(this.visualize.bind(this));
}

getByteTimeDomainData does not give you frequency information. These are time domain waveform values in real time also known as amplitude values. If you want to visualize them over time append the values it into an array and draw that. If you want real frequency values use getByteFrequencyData.

OP, here's some pseudo code. FYI, this really isn't a web audio question, more of an animation question.
Store a variable / field in your visualizer prototype function that keeps track of how many seconds you want to delay the redrawing of your canvas, keep a separate counter that will increment everytime requestAnimFrame(...) gets drawn. Once your counter reaches your delay amount, then redraw the canvas.
Edit
Now that I think of it...the solution should be very simple. Correct me if I'm wrong, but this rough solution is assuming that you are calling MicrophoneSample.visualize() from within your animation loop...and therefore, the code therein executes every second. I could be of more help if you post your MicrophoneSample object code as well, or at least your animation loop.
/* NOTE!
*
*/
// Find a way to put these into your PARENT MicrophoneSample object
var delay = 5;
// Note that I am setting delayCount initially to zero - during the loop
// the delayCount will actually get reset to 1 from thereafter (not 0)...
// this gives us a way to initially draw your visualization on the first frame.
var delayCount = 0;
// Pull var times out so it doesn't get calculated each time.
var times = new Uint8Array(MicrophoneSample.analyser.frequencyBinCount);
// Same goes for the canvas...
// I would set these values inside of the PARENT MicrophoneSample object
MicrophoneSample.canvas.width = this.WIDTH;
MicrophoneSample.canvas.height = this.HEIGHT;
// you only need to establish the drawing context once. Do it in the PARENT
// MicrophoneSample object
var drawContext = this.canvas.getContext('2d');
MicrophoneSample.prototype.visualize = function() {
/*
* NOTE!
*/
// Here's the juicy meat & potatoes:
// only if the delayCount reaches the delay amount, should you UPDATE THE
// TIME DOMAIN DATA ARRAY (times)
// if your loop runs every second, then delayCount increments each second
// and after 5 seconds will reach your designated delay amount and update your
// times array.
if(delayCount == 0 || delayCount == delay) {
this.analyser.getByteTimeDomainData(times);
// Now, it would be redundant (and totally noob-programmer of you) to
// redraw the same visualization onto the canvas 5 times in a row, so
// only draw the visualization after the first pass through the loop and then
// every 5th pass after that :]
for (var i = 0; i < times.length; i++) {
var value = times[i];
var percent = value / 256;
var height = this.HEIGHT * percent;
var offset = this.HEIGHT - height - 1;
var barWidth = this.WIDTH/times.length;
drawContext.fillStyle = 'purple';
drawContext.fillRect(i * barWidth, offset, 1, 1);
}
// Note: 1, not 0!
delayCount = 1;
}
else {
delayCount++;
}
requestAnimFrame(this.visualize.bind(this));
}
And just keep in mind that I haven't actually tested any of this. But it should at least point you in the right direction.

Related

How to run a requestAnimationFrame animation for a certain duration?

I have the following function that scrolls some elements "up" out of view by adjusting their style every "tick":
const incr = 1;
let moved = 0;
function changeHeight( children, duration, setTop) {
// duration = 1500
const height = children.clientHeight; // in this case, 166px
let moved = 0;
const slideUp = function (timestamp) {
// we're done if the amount moved is larger than height
if ( moved < height ) {
children.style.top = `${ setTop( moved, height ) }px`;
moved = moved + incr // move by some amount
requestAnimationFrame(slideUp)
} else {
// reset
moved = 0;
}
};
// start sliding
slideUp();
}
If requestAnimationFrame triggers roughly every 16ms or so, I would like to use duration to dictate how long the animation will be running for, so the formula seems to be height * 16.5 / duration
I'm confused by requestAnimationFrame - why is the time per tick not constant? I'd like to use timestamp that's generated by requestAnimationFrame but the first few cycles take much longer than the average of ~16.5
Is the 16.5 going to look different on a different machine or screen?
How do I make the height change take exactly the amount of time specified?
What you want is called delta-time.
The formula is Math.min((now - start) / duration, 1) * final_amount.
Using this delta-time, you don't need to care at which frequency your interval fires, every step is rendered "where it should be".
As for your questions,
why is the time per tick not constant
Certainly because the browser has a lot of things to do during the first frames and couldn't do everything in the 16.7ms frame. It will thus move your callback to be executed a bit later, and may even skip frames if under too much pressure.
Is the 16.5 going to look different on a different machine or screen?
Yes, requestAnimationFrame will basically try to follow the monitor's refresh rate. So on a 60Hz monitor you'll indeed have 16.7ms per frame, but on a 120Hz monitor you'd have only half of it.
How do I make the height change take exactly the amount of time specified?
Use a delta-time:
const elem = document.querySelector("div");
let moved = 0;
changeHeight(elem, 200, 5000);
function changeHeight(elem, height, duration) {
const start = performance.now();
const step = function () {
const now = performance.now();
const delta = Math.min((now - start) / duration, 1);
elem.style.height = (delta * height) + "px";
if (delta < 1) {
requestAnimationFrame(step);
}
};
step();
}
div { width: 50px; background: green; }
<div></div>

How do I generate a random X value for each "projectile" in my falling objects game using Javascript?

I am coding a game that is currently in its very early stages for a project to try to learn more about coding. In my game, objects generate randomly (green squares), and the player (red square), avoids them. I am having trouble trying to get the green squares to generate from a random position on the x-axis. I already have a formula to generate a random number for X, but after it selects a number randomly, all the "projectiles" generate there, rather than all generating from a different area. How would I get all the "projectiles" to generate from different positions on the x-axis randomly?
var randomX = Math.floor(Math.random() * 480) + 15;
function updateGameArea() {
var x, y;
for (i = 0; i < projectiles.length; i += 1) {
if (player.crashWith(projectiles[i])) {
gameArea.stop();
return;
}
}
gameArea.clear();
gameArea.frameNo += 1;
if (gameArea.frameNo == 1 || everyinterval(150)) {
x = randomX;
y = gameArea.canvas.height;
projectiles.push(new component(40, 40, "green", x, y));
}
for (i = 0; i < projectiles.length; i += 1) {
projectiles[i].y += -1; // the shape is using its coordinates to build downward from its y position
projectiles[i].update();
}
player.newPos();
player.update();
}
function everyinterval(n) {
if ((gameArea.frameNo / n) % 1 == 0) {return true;}
return false;
Expected: Green squares generate in random positions on the x- axis every 3 seconds and move upwards
Actual: Green squares all generate from the same random position on the X-axis.
You should reset X every time you're adding a new projectile:
if (gameArea.frameNo == 1 || everyinterval(150)) {
randomX = Math.floor(Math.random() * 480) + 15;
x = randomX;
y = gameArea.canvas.height;
projectiles.push(new component(40, 40, "green", x, y));
}
Otherwise, the randomX value stays constant as the value originally evaluated on line 1 when the interpreter reached it.
Here's your problem:
var randomX = Math.floor(Math.random() * 480) + 15;
// Generates a random number and stores it to randomX
// Called using 'randomX'
You need to turn it into a function if you want it to run each time:
var randomX = function() { Math.floor(Math.random() * 480) + 15 };
// Returns a new number each time
// Called using 'randomX()'
Both shivashriganesh mahato and natelouisdev have, essentially responded to how to fix the issue but since you are learning coding here is a tip. When you code, the code will run in a particular order. If you want something to be reassigned repeatedly, in this case a randomized number being used, and you want it to occur only after an event, you need to make sure that it gets trigger within each event.
natelouisdev has a good approach because, by using it as a function, you can call your randomizer more cleanly in your code and make it reassign the value of x each time.
Since you are building a game, it is also a good idea to compartmentalize your code. It'll make it easier to keep your ideas in order for each event trigger.
Example:
function gameLoss(){} - Define event return upon game loss. You can
then create editable rules to reason for loss without having to edit
the loss
function gameActive(){} - Defines what is normal gameplay. everything that occurs during normal gameplay should be managed here.
function gameArea(){} - Defines game canvas that function more for UI than for gameplay (scores, lifes, size of screen, etc)
Had you created individual functions you'd realize you only need a randomized 'x' value upon regular play thus you'd assign it within the gameActive() function and not as a global variable. Then you'd call the gameActive() function as many times as needed within a time interval to ensure a unique value is created each time.
-Side note: Don't litter unnecessary global variables. It'll make a mess off of your code when debugging. -

Javascript game Create new objects automatically to add into an array continuously [javascript]

I am pretty new to Javascript games, So please don't mind if this is an obvious question.
I have been trying to work on a frogger game. For this I have an object, And I just want to create new constructors consistently, so that it should look like as if a hoard of bugs are coming continuously.
This is my Enemy object.
// Enemies our player must avoid
var Enemy = function(x,y) {
// Variables applied to each of our instances go here,
// we've provided one for you to get started
// The image/sprite for our enemies, this uses
// a helper we've provided to easily load images
this.sprite = 'images/enemy-bug.png';
this.x = x;
this.y =y;
};
// Update the enemy's position, required method for game
// Parameter: dt, a time delta between ticks
Enemy.prototype.update = function(dt) {
// You should multiply any movement by the dt parameter
// which will ensure the game runs at the same speed for
// all computers.
this.x = this.x+((Math.random() * (15 - 1 + 1) + 1)*dt*35);
this.y = this.y;
};
// Draw the enemy on the screen, required method for game
Enemy.prototype.render = function() {
ctx.drawImage(Resources.get(this.sprite), this.x, this.y);
};
And then i push them manually into an Array
// Place all enemy objects in an array called allEnemies
var allEnemies=[];
allEnemies.push(new Enemy(0,135))
allEnemies.push(new Enemy(0,225))
allEnemies.push(new Enemy(0,50))
I can only see a single column of bugs. I want this above scenario to happen automatically, I figured I need to use call function here, but I still need to do that automatically at continuous interval I prefer.
You could use window.setInterval() like Joachim says, or use window.requestAnimationFrame() or even use window.setTimeout(). I personally recommend use requestAnimationFrame() with browsers because it is specifically for drawing animations and rendering, but if you're doing something in the node environment, you should just go with setInterval.
Other than that, I saw that you push all new instances of Enemy to an array, you could do this with one added statement.
You could also push to an array whenever you create an object like so:
var allEnemies = [];
function Enemy(x,y){
this.x = x || (Math.random() * WIDTH) | 0;
this.y = y || (Math.random() * height) | 0;
this.sprite = "bug-sprite-thing";
allEnemies.push(this); // this is called every time you do `new Enemy(n,n)
// the new object will automatically be pushed to the array.
}
If You want to spawn a new enemy at random intervals, you could use setTimeout
var MINIMUM = 100; // 0.1 seconds
var MILLISECONDS = 10000; // 10 seconds
function spawnAtRandom(){
// add your code here.
setTimeout(spawnAtRandom, minimum + (Math.random() * (MILLISECONDS-MINIMUM)));
}
spawnAtRandom();
What this function does is spawn one thing at the start, and then proceeds to spawn something in random intervals between MINUMUM and MILLISECONDS
Just call window.setInterval():
var allEnemies = [];
window.setInterval(function () {
allEnemies.push(new Enemy(0, 135));
}, 2000);
This will create a new Enemy object in your array every 2 seconds at the same position (which you could randomize as well).

Rendering too many points on Javascript-player

As part of a project, I have to render a video on a JS-player from a text file which has the points - all the changed coordinates along-with the color in each frame. Below is the code I'm using to draw these point on the screen.
But the issue is that the number of changed pixels in each frame are as high as ~20,000 and I need to display these in less than 30ms (inter-frame time difference). So, when I run this code the browser hangs for almost each frame. Could someone suggest an improvement for this?
Any help is really appreciated.
c.drawImage(img,0,0,800,800);
setInterval(
function(){
while(tArr[index]==time) {
var my_imageData = c.getImageData(0,0,width, height);
color(my_imageData,Math.round(yArr[index]),Math.round(xArr[index]),Math.round(iArr[index]),255);
c.putImageData(my_imageData,0,0);
index=index+1;
}
time = tArr[index];
}
,30);
xArr, yArr, iArr, tArr are arrays of x-coordinate, y-coordinate, intensity value and time of appearance respectively for the corresponding point to be rendered
function color(imageData,x,y,i,a){ //wrapper function to color the point
var index = (x + y * imageData.width) * 4;
imageData.data[index+0] = i;
imageData.data[index+1] = i;
imageData.data[index+2] = i;
imageData.data[index+3] = a;
}

HTML5 Canvas performance - calculating loops/frames per second

I know a few questions have been asked like this one before, such as this: Check FPS in JS? - which did work to some degree, I was able to find out how long each loop took to complete.
What I am looking for though is something more readable and controllable. I want to be able to set the refresh rate for the FPS counter to make it slow so it is human readable or as fast as the application can run, so I can use it on some kind of speedometer.
Anyway so here is the code I have right now:
var lastLoop = new Date().getTime();
function updateStage()
{
clearCanvas();
updateStageObjects();
drawStageObjects();
var thisLoop = new Date().getTime();
var fps = (thisLoop - lastLoop);
$('#details').html(fps);
lastLoop = thisLoop;
iteration = setTimeout(updateStage, 1);
}
Am I right to be setting the setTimeout function to a speed of 1 millisecond? I was thinking this will just make it loop as fast as it possibly can.
Should I count every 100 frames or so, find out how many milliseconds it took to run 100 frames then make a calculation to find out how many frames it would have done if the milliseconds were 1000? What would this calculation be?
To make the result more accurate I am guessing I need to display averages as one frame can vary a significant amount, how should I do this?
Any tips are greatly appreciated.
Thanks.
Note that the faster you update your output, the more you will affect your measurement. Although minimal, I try to update my fps output once per second or less unless it's necessary to go faster.
I like to have a low-pass filter on my results so that a temporary hiccup doesn't affect the values too strongly. This is easier to compute and write than a moving average, and doesn't have the problem of an overall average where your 'current' readings are affected by total performance over the entire run (e.g. anomalous readings during startup).
Put together, here's how I usually measure FPS:
var fps = 0, now, lastUpdate = (new Date)*1;
// The higher this value, the less the FPS will be affected by quick changes
// Setting this to 1 will show you the FPS of the last sampled frame only
var fpsFilter = 50;
function drawFrame(){
// ... draw the frame ...
var thisFrameFPS = 1000 / ((now=new Date) - lastUpdate);
if (now!=lastUpdate){
fps += (thisFrameFPS - fps) / fpsFilter;
lastUpdate = now;
}
setTimeout( drawFrame, 1 );
}
var fpsOut = document.getElementById('fps');
setInterval(function(){
fpsOut.innerHTML = fps.toFixed(1) + "fps";
}, 1000);
Ive tried something out,
If you change the
lastUpdate = now
to
lastUpdate = now * 1 - 1;
Your NaN problem is solved! This is also used where the lastUpdate is defined. Probably because it is not able to convert the date to unix timestamp.
The new result will be:
var fps = 0, now, lastUpdate = (new Date)*1 - 1;
// The higher this value, the less the FPS will be affected by quick changes
// Setting this to 1 will show you the FPS of the last sampled frame only
var fpsFilter = 50;
function drawFrame(){
// ... draw the frame ...
var thisFrameFPS = 1000 / ((now=new Date) - lastUpdate);
fps += (thisFrameFPS - fps) / fpsFilter;
lastUpdate = now * 1 - 1;
setTimeout( drawFrame, 1 );
}
var fpsOut = document.getElementById('fps');
setInterval(function(){
fpsOut.innerHTML = fps.toFixed(1) + "fps";
}, 1000);
I've taken the solution(s) posted and enhanced them a little. Have a look here - http://jsfiddle.net/ync3S/
I fixed that NaN error by using Date.now() instead of constructing a new date object each time and trying to reference it. This also prevents some garbage collection necessity.
I neatened up the variable and function names a bit and added some extra commenting - not necessary but nice to have.
I included some drawing code for testing.
I added fpsDesired as a test var for the engine loop.
I started fpsAverage at fpsDesired so with the fpsFilter it doesn't work up from 0 to the real FPS, rather starting at the desired FPS and adjusting from there.
Drawing now blocks incase it already was drawing, and this can be used for pausing and other control functions.
The main block is as follows:
var fpsFilter = 1; // the low pass filter to apply to the FPS average
var fpsDesired = 25; // your desired FPS, also works as a max
var fpsAverage = fpsDesired;
var timeCurrent, timeLast = Date.now();
var drawing = false;
function fpsUpdate() {
fpsOutput.innerHTML = fpsAverage.toFixed(2);
}
function frameDraw() {
if(drawing) { return; } else { drawing = true; }
timeCurrent = Date.now();
var fpsThisFrame = 1000 / (timeCurrent - timeLast);
if(timeCurrent > timeLast) {
fpsAverage += (fpsThisFrame - fpsAverage) / fpsFilter;
timeLast = timeCurrent;
}
drawing = false;
}
setInterval(fpsUpdate, 1000);
fpsUpdate();
setInterval(frameDraw, 1000 / fpsDesired);
frameDraw();
Going to have a tinker and see if I can come up with something smoother, as this thread is near the top in Google results.
Let's see what we can all come up with as a team, and I think it's always neat to not use 3rd party libraries, making the code portable for anyone :)
-Platima
Just set a interval that is resetting the fps counter every second.
var fpsOut, fpsCount;
var draw = function () {
fpsCount++;
..Draw To Canvas..
..Get the fps value: fpsOut
requestAnimationFrame(draw);
};
setInterval(function () {
fpsOut = fpsCount;
fpsCount = 0;
}, 1000);
draw();
If you want real-time updates, consider making it loop again and again in real time. To make it affect the performance less, only update the controlled variable, in this case, the FPS. You can have optional Frame Latency, which I will put here, just in case. Just copy, paste and tweak the code to your needs.
Take note that a single frame lasts for 16.66 miliseconds.
setInterval(function(){var latencybase1 = parseFloat(new Date().getTime());
var latencybase2 = parseFloat(new Date().getTime());
var latency = latencybase2-latencybase1;
var fps = Math.round(1000/latency);
if (latency<16.66)
{document.getElementById("FPS").innerHTML = fps+"
FPS";}
else {document.getElementById("FPS").innerHTML = ""+fps+" FPS";}
document.getElementById("Latency").innerHTML = latency+" ms";}, 0);

Categories

Resources