1

I'm creating a Simon game for FreeCodeCamp and I'm trying to use a for loop to play an audio sound for each piece of the pattern.

The code I have now plays the audio all at once which won't work for the game. How can I change this code so that it will play each audio sound individually with a .5 second break in between?

Here's the function that plays the audio and adds visual effect

function playAudio() {
    if (colorColor === "red") {
      red.play();
      $('#red').effect("highlight", { color: colorColor }, 500);
    } else if (colorColor === "blue") {
      blue.play();
      $('#blue').effect("highlight", { color: colorColor }, 500);
    } else if (colorColor === "green") {
      green.play();
      $('#green').effect("highlight", { color: colorColor }, 500);
    } else if (colorColor === "yellow") {
      yellow.play();
      $('#yellow').effect("highlight", { color: colorColor }, 500);
    }
}

This is the function where I believe the issue is.

function playPattern() {
    for (var i = 0; i < pattern.length; i++) {
      colorColor = pattern[i];
      setTimeout(playAudio, 500);
    }
  setTimeout(random, 750);
}

And here's the random() function only because it is called within playPattern()

function random() {
    randomColor = colors[Math.floor(Math.random() * colors.length)];
    colorColor = randomColor;
    colorColor = colorColor.slice(1);
    pattern.push(colorColor);

    count++;
    if (count < 10) {
      document.getElementById("count").innerHTML = "0" + count;
    } else {
      document.getElementById("count").innerHTML = count;
    }

    playAudio();

    pickCount = 0;
    userPick = [];
}

Thanks!

Thomas Devries
  • 839
  • 4
  • 18
  • 3
    Use `setTimeout(playAudio, i * 500);` – 4castle Jul 21 '17 at 00:21
  • When the for-loop is executed, all timeouts are set to the same 500ms. So all audio is played at the same time. – lumio Jul 21 '17 at 00:22
  • Possible duplicate of [How do I add a delay in a JavaScript loop?](https://stackoverflow.com/questions/3583724/how-do-i-add-a-delay-in-a-javascript-loop) – 4castle Jul 21 '17 at 00:25
  • I changed it to that code but still doesn't seem to be working properly. – George Mortimer Jul 21 '17 at 00:25
  • I read that post and tried the function in the answer but it still wasn't working. – George Mortimer Jul 21 '17 at 00:26
  • How long are your audios? Are they all the same length? If yes somethimng like this: `setTimeout(playAudio, (i * (audioLength + 500));` could help you. – Axel Stone Jul 21 '17 at 00:31
  • There are many, many duplicates. See [*Javascript, setTimeout loops?*](https://stackoverflow.com/questions/22154129/javascript-settimeout-loops) and [*All timers created in loop (with setTimeout) fire at same time?*](https://stackoverflow.com/questions/9982293/all-timers-created-in-loop-with-settimeout-fire-at-same-time). – RobG Jul 21 '17 at 00:32
  • Do you want the audio files to play in the continuous loop (i.e. looping through colors and repeating)? Do the files have fixed duration? – Jarek Kulikowski Jul 21 '17 at 00:35

4 Answers4

0

Assuming your audios are all the same length:

var audioLength = 3000; // length of audio in ms

for (var i = 0; i < pattern.length; i++) {
  colorColor = pattern[i];
  setTimeout(playAudio, (i * (audioLength + 500));
}
Axel Stone
  • 1,521
  • 4
  • 23
  • 43
0

In your code where you are doing:

for (var i = 0; i < pattern.length; i++) {
  colorColor = pattern[i];
  setTimeout(playAudio, 500);
}

What happens is that you basically define to run playAudio in 500ms for all patterns. the for loop is processed immediately and 500ms later all audios are being played together.

What you want is to call the next timeout only when the playAudio function ends.

let i = 0;
function playAudio() {
    i++;
    colorColor = pattern[i];
    if (colorColor === "red") {
      red.play();
      $('#red').effect("highlight", { color: colorColor }, 500);
    } else if (colorColor === "blue") {
      blue.play();
      $('#blue').effect("highlight", { color: colorColor }, 500);
    } else if (colorColor === "green") {
      green.play();
      $('#green').effect("highlight", { color: colorColor }, 500);
    } else if (colorColor === "yellow") {
      yellow.play();
      $('#yellow').effect("highlight", { color: colorColor }, 500);
    }
    if (i < pattern.length) {
        setTimeout(playAudio, 500);
    }
}

function random() {
    randomColor = colors[Math.floor(Math.random() * colors.length)];
    colorColor = randomColor;
    colorColor = colorColor.slice(1);
    pattern.push(colorColor);

    count++;
    if (count < 10) {
      document.getElementById("count").innerHTML = "0" + count;
    } else {
      document.getElementById("count").innerHTML = count;
    }

    playAudio();

    pickCount = 0;
    userPick = [];
}

setTimeout(random, 750);
Ori Shalom
  • 470
  • 4
  • 11
0

I based my answer on this answer.

What I did was to preload audio files by creating an object with the titles as keys and with an object value which contains the files' source and if it's loaded already:

const notes = ['do', 're', 'mi', 'fa', 'sol', 'la', 'ti'];
const audioFiles = notes.reduce((obj, title) => {
  obj[title] = {
    src: `notes/${title}.wav`,
    loaded: false,
  };

  return obj;
}, {});

// Preload files.
Object.entries(audioFiles).forEach(([title, details]) => {
  const audio = new Audio();

  audio.addEventListener('canplaythrough', () => {
    details.loaded = true;
  });

  audio.src = details.src;
});

The playPlaylist function takes a playlist (array of titles) and an object containing the audio files:

const playAudio = function playAudio(player, src) {
  player.src = src;
  player.play();
};

const playPlaylist = function playPlaylist(playlist, files) {
  // TODO: Check if files in playlist are already loaded.
  // while (!filesLoaded) { ... }

  const player = new Audio();
  let audioIndex = 0;

  player.addEventListener('ended', () => {
    audioIndex++;

    if (audioIndex >= playlist.length) {
      return;
    }

    setTimeout(() => {
      playAudio(player, files[playlist[audioIndex]].src);
    }, 500);
  });

  playAudio(player, files[playlist[audioIndex]].src);
};

Then just provide the playlist and pass the audioFiles object.

// Play sample playlist.
const playlist = 'do,mi,fa,sol,do,re,la,fa,mi'.split(',');
playPlaylist(playlist, audioFiles);

Just change the files and file paths to test.

dork
  • 4,396
  • 2
  • 28
  • 56
-1

You need to consider how much the song lasts, and include that on your timeout, for example if red lasts 1 min, you need to set your next timeout to setTimeout(blue, 60*1000 + 500), something like this :

function playAudio(color) {
    if (colorColor === "red") {
      red.play();
      $('#red').effect("highlight", { color: colorColor }, 500);
      setTimeout(function () {
         playAudio("blue");
      },60*1000 + 500);
    } else if (colorColor === "blue") {
      blue.play();
      $('#blue').effect("highlight", { color: colorColor }, 500);
      setTimeout(function () {
         playAudio("green");
      },60*1000 + 500);
    } else if (colorColor === "green") {
    //.... ETC

You don't need a for, just some recursion

Hugo sama
  • 899
  • 1
  • 9
  • 19