How can I clear buffer in XAudioJS - javascript

I am using XAudioJS (https://github.com/taisel/XAudioJS) for a current project and I need to do the following:
On mousedown, a sine wave should be played. I did this by generating a sine wave in the background and when the event is fired, the volume is set to 1 or 0 accordingly
When a user clicks on a div, a sequence of samples is being played. Other userinput is paused during this time
I could manage to do both independently but I can't bring them both together. When the user clicks on the div, the audio sequence is played but first a bit of the sine wave which was muted was also played
I suspect that it has to do with the buffer of XAudioJS. It is not cleared and therefore plays parts of the sine wave which is still in the buffer. I can't figure out a way though, how to clear it before playing the sequence.
Also I think I don't really understand what buffer-low and buffer-high for XAudioJS exactly means or which values are better for performance and which are not.
Here is my underRunCallback function I gave to XAudioJS:
var sample = []; // samples for the sample sequence
var samplePos = 0; // counter for samples
var sampleRateUsed = 8000;
var frequencyUsed = 550; //frequency for sine wave
var frequencyCounter = 0;
var counterIncrementAmount = Math.PI * 2 * frequencyUsed / sampleRateUsed;
function underRunCallback(samplesToGenerate) {
//Generate audio on the fly=>
if (samplesToGenerate == 0) {
return [];
}
var tempBuffer = []; // buffer that is returned
// create sine wave on the fly:
if(!isPlaying){
while (samplesToGenerate--) {
tempBuffer.push(Math.sin(frequencyCounter));
frequencyCounter += counterIncrementAmount;
}
return tempBuffer;
}
// NEED TO CLEAR BUFFER HERE
// play sequence of samples:
samplesToGenerate = Math.min(samplesToGenerate, sample.length - samplePos);
if(samplePos == sample.length){ // if all samples of sequence played
input_paused = false; // allow new userinput
isPlaying = false;
xaudioHandle.changeVolume(0); // mute audio
}
if (samplesToGenerate > 0) { // if new slice of samples is needed
tempBuffer = sample.slice(samplePos, samplePos + samplesToGenerate);
samplePos += samplesToGenerate;
return tempBuffer;
} else {
isPlayingB = false;
return [];
}
}

Related

How do you force a canvas refresh in Javascript

In the code that follows the function board repaints the canvas. However the canvas is not repainted until control reverts to the user. What code sequence is required to force a refresh whenever the function is invoked?
function doRound (ctx, game, loc, move){ // move is selected human move
let go = move;
let translate = {0:-1,1:-8,2:1,3:8}
for (var i = 0; i < 2; i++) {
loc = loc+translate[go]; // calculate move
if (illegal(loc)){
let text = (i === 0) ? "You lose!" : "You win!";
document.getElementById("result").innerHTML = text;
break; // game over
}
game[loc] = true; // place move on board
board(ctx); // refresh canvas
if (i === 0){go = compMove()}; // calc computer move
}
}
You would need to wipe off your canvas before redrawing all its previous content.
you can do so by using clearRect() and pass it the size of the canvas :
ctx.clearRect(0, 0, canvas.width, canvas.height);
here is a basic example on how this can be useful
//note in javascript if an html element has an id a variable with the same name exists you can ommit to get it with document.getElementById("mycan");
let ctx=mycan.getContext("2d");
//initial rect position in x
let move = 20;
//does some heavy processing for no reason
const heavyProcessing = (arr)=>{
for(let i = 0;i<1000000;i++)
{arr.push(Math.floor(Math.random()*100)+1)}
};
//adding an eventListener who listens to user input
window.addEventListener("keydown",(e)=>{
//if(e.key=="ArrowRight){move++} increments x position of red square if the user presses the right arrow on his keyboard
move+=(e.key=="ArrowRight");
});
//call to heavyProcessing before animation begins
let ar=[];
heavyProcessing(ar);
//simple animation timer
setInterval(()=>{
//wipe off canvas before each redraw
ctx.clearRect(0,0,mycan.width,mycan.height);
//choose next rectangle color before drawing
ctx.fillStyle = "red";
//drawing a rectangle
ctx.fillRect(move,20,50,50);
//recalls the code above every frame at a rate of 60 per second
},1000/60)
<canvas id = "mycan" width=300 height=200>
</canvas>

Phaser JS walk up tile based stairs

I'm creating a small 2d-minecraft clone, in Phaser js, on my own as a learning experience. So far I have gotten player movement and random level seeds to work ok.
I am using Phasers P2JS engine and have sprites that are box based. What I'm struggling with now Is I want the player to be able to walk unhindered up small elevations, (1-tile high) but I don't have any good idea of how I should Implement this.
I have tried changing the bounding box of the player so that it had a slope at the bottom but this gets me in to a bunch of trouble with wall climbing. I want a way to do this where it gets as seamless as possible. Preferably the player speed is not altered much by climbing the steps.
I am concidering writing some kind of collision detection function to handle this but I am uncertain if this is the best way to do it.
Thanks for your help.
Below is my code and an image that shows the kind of step I want to beable to walk up. Its the first elevation to the left in the image.
var pablo = require('../generators/pablo.js');
var destiny = {};
var socket;
var player;
var jumpButton;
var levelCollisionGroup;
var playerCollisionGroup;
destiny.create = function () {
console.info("game loaded");
// World
this.game.world.setBounds(0, 0, 4000, 1000);
this.game.physics.startSystem(Phaser.Physics.P2JS);
this.game.physics.p2.gravity.y = 600;
this.game.physics.p2.applySpringForces= false;
this.game.physics.p2.applyDamping= false;
this.game.physics.p2.restitution = 0;
this.game.physics.p2.friction = 0.01;
// Player
playerCollisionGroup = this.game.physics.p2.createCollisionGroup();
player = this.game.add.sprite(this.game.world.centerX, 800, 'player');
this.game.physics.p2.enable(player,true);
player.body.fixedRotation = true;
player.body.setCollisionGroup(playerCollisionGroup);
player.body.mass = 2;
// Camera
this.game.camera.follow(player);
this.game.camera.deadzone = new Phaser.Rectangle(200, 0, 400, 100);
// Controls
jumpButton = this.game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
leftButton = this.game.input.keyboard.addKey(Phaser.Keyboard.A);
rightButton = this.game.input.keyboard.addKey(Phaser.Keyboard.D);
// Level
levelCollisionGroup = this.game.physics.p2.createCollisionGroup();
this.game.physics.p2.updateBoundsCollisionGroup();
for (i = 0; i < 280; i = i + 1) {
var block;
var height = pablo.getHeight(i);
for(j = 0; j < height; j = j + 1){
if(j === height-1){
block = this.game.add.sprite(15*i, 993-15*j, 'grass');
} else {
block = this.game.add.sprite(15*i, 993-15*j, 'dirt');
}
block.width = 15;
block.height = 15;
this.game.physics.p2.enable(block);
block.body.static=true;
block.body.immovable = true;
block.body.collides([levelCollisionGroup, playerCollisionGroup]);
block.body.setCollisionGroup(levelCollisionGroup);
if(j == height){
}
}
}
player.body.collides(levelCollisionGroup);
this.game.stage.backgroundColor = "#5599CC";
};
destiny.update = function() {
player.body.velocity.x=0;
if (leftButton.isDown) {
player.body.velocity.x = -200;
} else if (rightButton.isDown) {
player.body.velocity.x = 200;
}
if (jumpButton.isDown && this.checkIfCanJump()) {
player.body.velocity.y = -400;
}
};
destiny.render = function() {
this.game.debug.cameraInfo(this.game.camera, 32, 32);
this.game.debug.spriteCoords(player, 32, 550);
};
destiny.checkIfCanJump = function() {
var result = false;
for (var i=0; i < this.game.physics.p2.world.narrowphase.contactEquations.length; i++) {
var c = this.game.physics.p2.world.narrowphase.contactEquations[i];
if (c.bodyA === player.body.data || c.bodyB === player.body.data) {
var d = p2.vec2.dot(c.normalA, p2.vec2.fromValues(0, 1));
if (c.bodyA === player.body.data) {
d *= -1;
}
if (d > 0.5) {
result = true;
}
}
}
return result;
};
module.exports = destiny;
===================== Edit =====================
I have now tried creating slopes of the edge pieces when generating the world. But I realized that this makes me have to regenerate the world when I later add the feature for hacking away blocks. Thus this is not the solution. I think I will need to do some collision detection and move the player up when I hit an edge. But I'm not quite sure how to do this in phaser. Any help is still appreciated.
!!! Here is an image of what not to do !!!
Emanuele Feronato has a post on replicating the game Magick in Phaser.
There he covers the case of a block colliding with a barrier/wall, with the ability of the block to climb one level up.
You can check the tutorial, but what he appears to be doing is checking to see if the diagonal tile is empty (in other words, is it just a 'step' up), and if it is, running a 'jump' function, which looks more like a climb.
Depending upon how you want your character to step, you could potentially look at both the next tile (on the x-axis) as well as the one after it to check for the height.
So for example, if moving right and the next tile is flat, but the second tile has a step, you might start moving your character up on the y-axis.

Creating a scratch card in EaselJS

I've been trying to develop a scratch card in EaselJS.
So far, I've managed to get a Shape instance above a Bitmap one and enabled erasing it with click and drag events, so the image below becomes visible.
I've used the updateCache() with the compositeOperation approach and it was easy enough, but here is my issue:
How can I find out how much the user has already erased from the Shape instance, so I can setup a callback function when, say, 90% of the image below is visible?
Here is a functioning example of what I'm pursuing: http://codecanyon.net/item/html5-scratch-card/full_screen_preview/8721110?ref=jqueryrain&ref=jqueryrain&clickthrough_id=471288428&redirect_back=true
This is my code so far:
function Lottery(stageId) {
this.Stage_constructor(stageId);
var self = this;
var isDrawing = false;
var x, y;
this.autoClear = true;
this.enableMouseOver();
self.on("stagemousedown", startDrawing);
self.on("stagemouseup", stopDrawing);
self.on("stagemousemove", draw);
var rectWidth = self.canvas.width;
var rectHeight = self.canvas.height;
// Image
var background = new createjs.Bitmap("http://www.taxjusticeblog.org/lottery.jpg");
self.addChild(background);
// Layer above image
var overlay = new createjs.Shape();
overlay.graphics
.f("#55BB55")
.r(0, 0, rectWidth, rectHeight);
self.addChild(overlay);
overlay.cache(0, 0, self.canvas.width, self.canvas.height);
// Cursor
self.brush = new createjs.Shape();
self.brush.graphics
.f("#DD1111")
.dc(0, 0, 5);
self.brush.cache(-10, -10, 25, 25);
self.cursor = "none";
self.addChild(self.brush);
function startDrawing(evt) {
x = evt.stageX-0.001;
y = evt.stageY-0.001;
isDrawing = true;
draw(evt);
};
function stopDrawing() {
isDrawing = false;
};
function draw(evt) {
self.brush.x = self.mouseX;
self.brush.y = self.mouseY;
if (!isDrawing) {
self.update();
return;
}
overlay.graphics.clear();
// Eraser line
overlay.graphics
.ss(15, 1)
.s("rgba(30,30,30,1)")
.mt(x, y)
.lt(evt.stageX, evt.stageY);
overlay.updateCache("destination-out");
x = evt.stageX;
y = evt.stageY;
self.update();
$rootScope.$broadcast("LotteryChangeEvent");
};
}
Any ideas?
That's a tricky one, regardless of the language. The naive solution would simply be to track the length of the paths the user "draws" within the active area, and then reveal when they scratch long enough. That's obviously not very accurate, but is fairly simple and might be good enough.
The more accurate approach would be to get the pixel data of the cacheCanvas, then check the alpha value of each pixel to get an idea of how many pixels are transparent (have low alpha). You could optimize this significantly by only checking every N pixel (ex. every 5th pixel in every 5th row would run 25X faster).

How can make random images come out from the holes in Whack-a-Mole game instead of one image

I was wondering would someone be able to help me to show me a way to be able to bring out random images from the holes instead of one image in a Whack a mole game on Javascript.
The following is the function used for an image to come up in random holes. Currently its only one image but I'd like it to be about 10 different images coming out.
var wormImg = new Image();
var worm;
wormImg.src = 'hobby.png';
wormImg.name = 'worm';
wormImg.onload = loadGfx;
function showWorm()
{
if(currentWorms == totalWorms)
{
showAlert();
}
else
{
if(lastWorm != null)
{
lastWorm.onPress = null;
stage.removeChild(lastWorm);
stage.update();
lastWorm = null;
}
var randomPos = Math.floor(Math.random() * 8);
var worm = new Bitmap (wormImg);
worm.x = wormsX[randomPos];
worm.y = wormsY[randomPos];
stage.addChild(worm);
worm.onPress = wormHit;
lastWorm = worm;
lastWorm.scaleY = 0.3;
lastWorm.y += 42;
stage.update();
Tween.get(lastWorm).to({scaleY: 1, y: wormsY[randomPos]}, 200).wait(1000).call(function(){currentWorms++; showWorm()});
}
}
I would appreciate any help, thank you for your time.
You could create an array of possible values for worm.src, and randomly select one whenever showing a new worm

Consistent FPS in frame by frame video with <canvas>

I'm trying to display precisely enough a video that I can stop on or jump to a specific frame. For now my approach is to display a video frame by frame on a canvas (I do have the list of images to display, I don't have to extract them from the video). The speed doesn't really matter as long as it's consistent and around 30fps. Compatibility somewhat matters (we can ignore IE≤8).
So first off, I'm pre-loading all the images:
var all_images_loaded = {};
var all_images_src = ["Continuity_0001.png","Continuity_0002.png", ..., "Continuity_0161.png"];
function init() {
for (var i = all_images_src.length - 1; i >= 0; i--) {
var objImage = new Image();
objImage.onload = imagesLoaded;
objImage.src = 'Continuity/'+all_images_src[i];
all_images_loaded[all_images_src[i]] = objImage;
}
}
var loaded_count = 0;
function imagesLoaded () {
console.log(loaded_count + " / " + all_images_src.length);
if(++loaded_count === all_images_src.length) startvid();
}
init();
and once that's done, the function startvid() is called.
Then the first solution I came up with was to draw on requestAnimationFrame() after a setTimeout (to tame the fps):
var canvas = document.getElementsByTagName('canvas')[0];
var ctx = canvas.getContext("2d");
var video_pointer = 0;
function startvid () {
video_pointer++;
if(all_images_src[video_pointer]){
window.requestAnimationFrame((function (video_pointer) {
ctx.drawImage(all_images_loaded[all_images_src[video_pointer]], 0, 0);
}).bind(undefined, video_pointer))
setTimeout(startvid, 33);
}
}
but that felt somewhat slow and irregular...
So second solution is to use 2 canvases and draw on the one being hidden and then switch it to visible with the proper timing:
var canvas = document.getElementsByTagName('canvas');
var ctx = [canvas[0].getContext("2d"), canvas[1].getContext("2d")];
var curr_can_is_0 = true;
var video_pointer = 0;
function startvid () {
video_pointer++;
curr_can_is_0 = !curr_can_is_0;
if(all_images_src[video_pointer]){
ctx[curr_can_is_0?1:0].drawImage(all_images_loaded[all_images_src[video_pointer]], 0, 0);
window.requestAnimationFrame((function (curr_can_is_0, video_pointer) {
ctx[curr_can_is_0?0:1].canvas.style.visibility = "visible";
ctx[curr_can_is_0?1:0].canvas.style.visibility = "hidden";
}).bind(undefined, curr_can_is_0, video_pointer));
setTimeout(startvid, 33);
}
}
but that too feels slow and irregular...
Yet, Google Chrome (which I'm developing on) seems to have plenty of idle time:
So what can I do?
The Problem:
Your main issue is setTimeout and setInterval are not guaranteed to fire at exactly the delay specified, but at some point after the delay.
From the MDN article on setTimeout (emphasis added by me).
delay is the number of milliseconds (thousandths of a second) that the function call should be delayed by. If omitted, it defaults to 0. The actual delay may be longer; see Notes below.
Here are the relevant notes from MDN mentioned above.
Historically browsers implement setTimeout() "clamping": successive setTimeout() calls with delay smaller than the "minimum delay" limit are forced to use at least the minimum delay. The minimum delay, DOM_MIN_TIMEOUT_VALUE, is 4 ms (stored in a preference in Firefox: dom.min_timeout_value), with a DOM_CLAMP_TIMEOUT_NESTING_LEVEL of 5.
In fact, 4ms is specified by the HTML5 spec and is consistent across browsers released in 2010 and onward. Prior to (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2), the minimum timeout value for nested timeouts was 10 ms.
In addition to "clamping", the timeout can also fire later when the page (or the OS/browser itself) is busy with other tasks.
The Solution:
You would be better off using just requestAnimationFrame, and inside the callback using the timestamp arguments passed to the callback to compute the delta time into the video, and drawing the necessary frame from the list. See working example below. As a bonus, I've even included code to prevent re-drawing the same frame twice.
Working Example:
var start_time = null;
var frame_rate = 30;
var canvas = document.getElementById('video');
var ctx = canvas.getContext('2d');
var all_images_loaded = {};
var all_images_src = (function(frames, fps){//Generate some placeholder images.
var a = [];
var zfill = function(s, l) {
s = '' + s;
while (s.length < l) {
s = '0' + s;
}
return s;
}
for(var i = 0; i < frames; i++) {
a[i] = 'http://placehold.it/480x270&text=' + zfill(Math.floor(i / fps), 2) + '+:+' + zfill(i % fps, 2)
}
return a;
})(161, frame_rate);
var video_duration = (all_images_src.length / frame_rate) * 1000;
function init() {
for (var i = all_images_src.length - 1; i >= 0; i--) {
var objImage = new Image();
objImage.onload = imagesLoaded;
//objImage.src = 'Continuity/'+all_images_src[i];
objImage.src = all_images_src[i];
all_images_loaded[all_images_src[i]] = objImage;
}
}
var loaded_count = 0;
function imagesLoaded () {
//console.log(loaded_count + " / " + all_images_src.length);
if (++loaded_count === all_images_src.length) {
startvid();
}
}
function startvid() {
requestAnimationFrame(draw);
}
var last_frame = null;
function draw(timestamp) {
//Set the start time on the first call.
if (!start_time) {
start_time = timestamp;
}
//Find the current time in the video.
var current_time = (timestamp - start_time);
//Check that it is less than the end of the video.
if (current_time < video_duration) {
//Find the delta of the video completed.
var delta = current_time / video_duration;
//Find the frame for that delta.
var current_frame = Math.floor(all_images_src.length * delta);
//Only draw this frame if it is different from the last one.
if (current_frame !== last_frame) {
ctx.drawImage(all_images_loaded[all_images_src[current_frame]], 0, 0);
last_frame = current_frame;
}
//Continue the animation loop.
requestAnimationFrame(draw);
}
}
init();
<canvas id="video" width="480" height="270"></canvas>

Categories