How to cycle through an array of Audio objects - javascript

I've recently started learning Javascript, and I'm working on my first web-embedded game, which is a musical puzzle game that uses the basic principals of twelve-tone serialist music. My game is mostly done, and you can find it here. But I'm having trouble with audio. I did manage to get it to play a sound when the user solves the puzzle, but I can't get it to actually play through the notes that appear on the game board.
Here's what I did: I created an array of 12 Audio objects, which contains every note from C to B. Then I created a method called "playToneRow()" which plays through them all, with the order determined by the numeric array ToneRow.notes[]. Here's the code:
this.playToneRow = function()
{
for (var i in this.notes)
{
noteSound[this.notes[i]].play();
}
};
But this method only plays the last note of the tone row. Now I should mention that my knowledge of Javascript has been cobbled together from various tutorials I've found online, and I'm fairly certain that there are significant gaps in my admittedly rudimentary coding skills. But I figured that the problem was that I wasn't putting any space in between the sounds, so it was trying to play them all at once, but it didn't have enough channels so it only played the last one. So then I tried this:
this.playToneRow = function()
{
var x = 0;
for (var i in this.notes)
{
x = this.notes[i];
setTimeout(function()
{
noteSound[x].play();
}, 700);
}
};
Now I'm really not sure if I'm using setTimeout() properly, but I'm guessing not, because once again, it only played the last note. I know that all 12 wav files are getting loaded, because if I change the tone row, it will play a different note. So it does have access to all the audio files; it's just a matter of getting it to play them all (and in the right order).
Thanks!

for won't really care about your setTimeout, try:
this.playToneRow = function()
{
var x = 0,
length = 0,
j = 0;
for (var i in this.notes)
{
length += 1;
}
function runIteration () {
x = this.notes[j];
setTimeout(function()
{
noteSound[x].play();
}, 700);
if (j === length) return;
j += 1;
setTimeout(runIteration, 700);
}
runIteration();
};
Hopefully that does it. I've taken a look at your code but its a bit complex so I wasn't able to determine if this.notes was an object or an array

http://jsfiddle.net/kgvgcsg3/14/
var notes = [{'0':'tone1'},{'1':'tone2'}]
var playToneRow = function(i)
{
setTimeout(function()
{
//notes[i].play(); goes here
console.log(notes[i])
if (i < notes.length-1){
i++;
playToneRow(i)
}
}, 700);
};
playToneRow(0)

Related

New to svelte and javascript, web app for learning solfege

I am new to JavaScript and Svelte. For a school project, I want to create a web application to learn the basics of solfeggio, but I have found a way to do it, but I feel that it's not the right way at all
Right now I have an array that represents my sheet music staff, of another array of int that represents my lines, and i update like this code snippet:
onMount(()
const interval = setInterval(() => {
rand();
portee[wch_line].main[pos] = 1;
portee[wch_line].main[pos + 1] = 0;
if (pos === 0) {
reset();
}
pos -= 1;
}, 10);
return () => {
clearInterval(interval);
};
});```
I shift my 1 on my array from 0, and then I display the note at the location of the 1, otherwise I display a "-" which visually represents my line. Here is the link to my git if you want to take a look: https://github.com/BaptisteKELAGOPIAN/SolfegeLearning. But as I said, it's slow at times, but most of all I don't want to continue like this when I feel there is a better way to do all of this. If you have any leads, it would be great, thank you.

How to rotate from different titles

No, I am not talking about <h1> or something like that. I am talking about the <title> element. I created a function, that delays every .5 seconds or so, and changes titles.
The script I have developed reads the array, which includes the titles, and rotates from it. The first time I have ran it, I did not use setTimeout, and it ran instantly, I couldnt read anything.
But when I tried using the setTimeout function, it started to return undefined. What is the problem with this code?
for(var i = 0; i < titles.length ; i++){
setTimeout(function () {document.title = titles[i]}, 500)
}
You're going about this the wrong way (IMO). Using a loop you'll fire off all your timeouts at once. You'd be better off using setInterval
let i = 0, titles = ['title1', 'title2', 'title3'];
let int = setInterval(() => {
document.title = titles[i];
console.log('doc.title: ', document.title);
if (i++ >= titles.length - 1) clearInterval(int);
}, 500)

'CSS display: none' will work just fine, but 'document.body.removeChild(child);' will break my code

people that love programming.
Before I explain my issue, I need you to understand what I am doing here. It is a very simple game.
Do you remember that console video game "Guitar Hero"? Or if you are a little younger, "Piano Tiles"?
If you don't know what I'm talking about, click on this link to get an idea of what it is = https://www.youtube.com/watch?v=nWLKlEg0VMs&ab_channel=MattSterner
Well, that is exactly what I am doing with this website (on a more basic level of course).
In other words, there are musical notes falling from the top and the player has to press a button when that note hits the bottom of the screen.
Finally, what is my issue?
Well, when the note gets to the button, I am able to recognize it, which was actually hard and took me quite a while. And when I recognize it (inside the for loop below), and when I press the key E or keycode 69, the note disappears, which is exactly what I want.
So what's wrong?
Well, as you can see, I am setting the div's display property to none but the div is not actually being eliminated, this results in every div/musical note in the game to accumulate at the bottom with a display property of none, making the game slow.
So what do I want?
I want the div/musical note to be eliminated from the website entirely, gone, finito... not just setting its display property to none.
So what have I tried myself?
I tried document.body.removeChild(child) but this actually broke the game, notes stopped coming from the top, so it was not a fix (I have no clue of why this happened by the way).
You can see the part of the code I am having trouble with here:
document.body.addEventListener("keydown", event => {
if (event.keyCode === 69 /* E */) {
for (let i = 0; i < leftSquares.length; i++){
if (leftSquares[i].style.top > "740.5px" && leftSquares[i].style.top < "790.5px"){
console.log('correctin');
leftSquares[i].style.display = "none";
// document.body.removeChild(leftSquares[i]);
}
}
}
})
Or you can play around with the full game here: https://codepen.io/xavi-font/pen/MWeRdwr
Sticks and stones won't break my code, but document.body.removeChild(child) does (for a reason I don't know).
When you are doing document.body.removeChild(leftSquares[i]);
You remove the element from the page (DOM), but you do not remove the id from idArray = [];
So on the next loop of your code you get to:
for (let i = 0; i < idArray.length; i++) {
let element = document.getElementById(i.toString());
animateFunction(element);
}
and now the element does not exist, so you can't animate it, and you get an error that breaks the game.
A simple solution (not necessarily the best) could be:
for (let i = 0; i < idArray.length; i++) {
let element = document.getElementById(i.toString());
if (element) {
animateFunction(element);
}
}
Another option would be to keep specific id's in the array idArray, then when you delete an element, remove it's ID from the array, and where you previously have for (let i = 0; i < idArray.length; i++) { you can now use
idArray.forEach((id) => {
let element = document.getElementById(id.toString());
...
});
Your game also slows down, and if you console.log the element.style.top from within animateFunction you will see that if you don't press E at the correct time, then the element just keeps animating down the page forever.
So I would also suggest a garbage collect function within the animateFunction
maybe something like this?
function animateFunction(element) {
if (element.style.top == "-100px") {
const interval = setInterval(function() {
element.style.top = eval(parseInt(element.style.top) + 1).toString() + "px";
let currentNotePosition = element.getBoundingClientRect();
// Garbage Collect
if (currentNotePosition.top > window.innerHeight) {
document.body.removeChild(element);
}
}, speed);
}
}

Adding delay to function during drawing objects

i placed setInterval into the code but this obviously delaying all the lasers by 1 second.
I want it to be working in the following sequence:
- at start laser1 and laser2 are fired.
- 1 second break and fire another set of lasers etc.
Also if someone could teach me how to move all block of code by four spaces on forum, that would be amazing, as none of the ways i found in google solve this ridiculous problem.
Code shortcut:
let laser1;
let lasers1 = [];
let laser2;
let lasers2 = [];
function createLaser() {
laser1 = new Laser(bossOne.x, bossOne.y + bossOne.sizeY, 10, 50, 5);
lasers1.push(laser1);
laser2 = new Laser(bossOne.x + bossOne.sizeX - 10, bossOne.y +
bossOne.sizeY, 10, 50, 5);
lasers2.push(laser2);
}
function draw() {
requestAnimationFrame(draw);
setInterval(createLaser, 1000);
for (i = 0; i < lasers1.length; i++) {
lasers1[i].show();
lasers1[i].move();
}
for (i = 0; i < lasers2.length; i++) {
lasers2[i].show();
lasers2[i].move();
}
}
requestAnimationFrame(draw);
Remote host for full code if needed:
https://stacho163.000webhostapp.com/
lasers are red
I think i should work with booleans, but can't handle to set it there.
Got a similar topic with key activation, but i lost contact with the one who proposed a solution on the basis of booleans (as it didn't work well) so i took the easiest part first without involving the keys.
Any tips are appreciated :)
Try changing setInterval to setTimeout, as it's already looping recursively - just add a delay to it. Also move the requestAnimationFrame call to the bottom of draw, not the top:
function draw()
setTimeout(createLasers, 1000);
//Loops
requestAnimationFrame(draw);
}

How to speed up this moving algorithm? In Javascript

I have an Array of 16 billiard balls in JS and want to move each ball smoothly with its direction and speed.
For that I set up a timer, calling UpdateThis() every 42ms (for 24 fps).
The problem is that UpdateThis() takes 53ms as firebug states.
Now UpdateThis iterates over every ball and calls UpdateBall(ball).
I assume that the problem lies there.UpdateBall looks like this:
function UpdateBall(ball)
{
if(ball.direction.x != 0 && ball.direction.y != 0) {
//ball moving!
for(var i = 0; i < balls.length; i++) {
//CheckCollision(ball, balls[i]); //even without this it takes 53 ms!
}
var ps = VAdd(ball.position, VMul(ball.direction, ball.speed)); //Multiply Direction with speed and add to position!
if(ps.x < Bx || ps.y < By || ps.x > Bw || ps.y > Bh) { //Bounce off the wall!
ball.direction = VMul(ball.direction, -1); //Invert direction
ball.speed *= 1;
ps = VAdd(ball.position, VMul(ball.direction, ball.speed)); //Calc new position!
}
ball.position = ps;
ball.MoveTo(); //See explanation at the bottom.
ball.speed *= GRK; //Gravity
if(ball.speed < 0.05) {
ball.speed = 0;
}
}
}
it seems that the most time is spent in ball.MoveTo() which looks like this:
function()
{
this.image.style.left = this.position.x + "px";
this.image.style.top = this.position.y + "px";
}
-- UPDATE --
function UpdateThis() {
for(var i = 0; i < balls.length; i++) {
var cur = balls[i];
UpdateBall(cur);
balls[i] = cur;
}
}
and onload looks like
nx = setInterval(function() { UpdateThis(); }, 42);
Does somebody have any ideas on how to speed this up?
-- UPDATE 2 --
You can download the folder with the HTML file here (the password is password)
What about separating the position updates from the drawing? So have something like this (untested code):
function DrawBall(ball)
{
ball.MoveTo(); //Take this line out of UpdateBall
}
-
function UpdateThis() {
for(var i = 0; i < balls.length; i++) {
var cur = balls[i];
UpdateBall(cur);
balls[i] = cur;
}
}
-
function DrawThis() {
for(var i = 0; i < balls.length; i++) {
DrawBall(balls[i]);
}
setTimeout(function() { DrawThis(); }, 42);
}
-
nx = setInterval(function() { UpdateThis(); }, 42);
setTimeout(function() { DrawThis(); }, 42);
If indeed it's the moving of the position that's slow, this way the logic update still happens at 42ms, and the framerate is no faster than 42ms but it can skip frames. (I haven't actually tried this, so this is all theoretical and you may need to tweak some stuff)
Why moving may be (and most probably is) slow?
Move functionality could be slow, because it has more things to do than simple variable assignment. It has to actually render some element to some other place. You could test this if you run this on IE9. I anticipate it should run faster since it uses hardware video acceleration.
As for the other routine I hope others will dissect it. :)
Questions for you
Can you please describe how do balls move? Sporadically? How do you call UpdateBall() for each ball? Do you queue those calls?
Provide VMul and VAdd functionality
Have you played with styling? Maybe relative positioning of balls' immediate parent may speed up rendering. And setting overflow:hidden on it as well. I don't know. Depends on how you've done it. Hence a JSFiddle would be very helpful.
A suggestion
Instead of using setInterval to call your function you should maybe just queue them and let them execute as fast as it gets. And just for the sake of it, provide a central setInterval with some watcher that they don't run too fast.
But I guess that it still utilizes your processor to 100% which isn't good anyway.
Very important note: Don't run you app while Firebug's enabled because it's a well known fact that Javascript executes much slower when Firebug is running.
That's tough, if MoveTo() is in fact your bottleneck, since there is not a whole lot going on there. About the only things I can think of, right off hand, are
1) Cache the style property of the image and position for faster lookups. Everytime you see a dot in the object chain it's requires stepping through the scope chain. Ideally you can cache this property at the time the parent of MoveTo() is constructed.
2) Are the 'px' strings required? It may result in an invalid CSS specification, but it may still work. I have a hard time believing 2 string concats would really change all that much though.
The main problem here is likely the fact that anytime you change the DOM, the browsers re-flows the entire page. Your only other option may be to refactor such that instead of changing the styles, you actually remove the previous contents, and replace it with the a document fragment describing the new state. This would result in only 2 re-flows for the entire step (1 for removal, 1 for addition), instead of 2 for each ball.
EDIT: Regarding #1 above, when I say cache, I don't mean just locally in the function call. But perhaps as a closure in the parent object. For example:
var Ball = function(img){
var style = img.style;
var posX;
var posY;
function MoveTo(){
style.left = posX + "px";
style.right = posY + "px";
}
};

Categories