0

I know this has been asked before, but I am new to JavaScript and after having read other answers I can't understand specifically why my method isn't working. The first track that plays is random, but then when the song ends, the same track repeats over and over again instead of choosing a different random track. If audio.play chooses a random track the first time, why doesn't it choose a random track again when the song ends, but instead loops the same track? Help appreciated:

var audio_files = [
"TRACKS/1.mp3",
"TRACKS/2.mp3",
"TRACKS/3.mp3"
]

var random_file = audio_files[Math.floor(Math.random() * audio_files.length)];

var audio = new Audio(random_file);

audio.play();

audio.addEventListener('ended', function(){
audio.play();
}
Tom
  • 15
  • 1
  • 4

4 Answers4

3

It would be easier to shuffle the array and simply iterate on it to get each file. With this solution, you will not get the same file twice because of the random solution.

Once you get to the end, do the same thing, shuffle the array and iterate again. Like this, the list will change giving the impression to selecting a different file in a random manner (but truly simply iterating).

Here is a pseudo code to it

function readFiles(){        
    array = shuffle(array);
    for(var i = 0; i < array.length; ++i){
         play(array[i]);
    }
    readFiles(); //to read undefinitly
}

Here and here, you will find a great threads to shuffle the array. I will not do it again, just follow the answer there.

In your case, you don't want a loop but you will use a counter the get the next file and shuffle the array again once you get to the end

function getNextFile(){
    if(currentFile >= array.length){ //reach the end
        shuffle(array);
        currentFile = 0;
    } 
    return array[currentFile+];
}
Community
  • 1
  • 1
AxelH
  • 14,325
  • 2
  • 25
  • 55
  • Thanks, I used this information combined with an addListenerEvent('ended') function to shuffle the array a second time; the new problem is that there are slight pauses between the tracks which I need to eliminate or cross-fade in order to make pleasing music... – Tom Jan 14 '17 at 18:14
  • @tom Where is it from? The iteration, the player, the file itself? Depending on the source, solution exists. – AxelH Jan 14 '17 at 18:35
  • I found a workable solution by having the next track start a half second sooner, though I think I will switch over to the Web Audio API once I understand JavaScript a bit better. The new issue: Every time I load the page, the shuffled array starts with whatever was #1 in the array. After that, the tracks are 'random,' but the first track when I load the page is always #1 in the array: so the array might be [1, 4, 9, 12] or [1, 9, 12, 4], etc, but 1 is always #1. Any ideas? I'll start a new thread to post the code if that's better. – Tom Jan 15 '17 at 18:29
  • @tom I would guess that your shuffle function is called after the first "reading" or that the loop used start at 1 instead of 0. But if this is not the case, you might need to open a new thread if you can not find any solution – AxelH Jan 15 '17 at 23:31
1

Your code need to be like this:

var audio_files = [
"TRACKS/1.mp3",
"TRACKS/2.mp3",
"TRACKS/3.mp3"
]

function random_file(){
  return audio_files[Math.floor(Math.random() * audio_files.length)];
}

var audio = new Audio( random_file() );

audio.play();

audio.addEventListener('ended', function(){
  audio.play( random_file() );
}

Your listner may be like this if player have another way to specify file for existing payer

audio.addEventListener('ended', function(){
  audio.src = random_file();
  audio.play();
}

or if your player have no such api method the only way is

function listner() {
  audio = new Audio( random_file() );
  audio.play();
  audio.addEventListener('ended', listner)
}

audio.addEventListener('ended', listner)
oklas
  • 7,935
  • 2
  • 26
  • 42
  • In the wild, the following way to generate a random file has been proved: `Math.floor(Math.random() * (max - min + 1)) + min;` . See also http://stackoverflow.com/documentation/javascript/203/arithmetic-math#t=201701131054542553563 . – Reporter Jan 13 '17 at 10:54
  • Where `min=0 max=(audio_files.length-1)` then: `Math.floor(Math.random() * ((audio_files.length-1)-0+1))+0` that equal: `Math.floor(Math.random() * audio_files.length)` – oklas Jan 13 '17 at 12:00
  • In your example it will be same. In case, the min value is different from zero then not anymore. – Reporter Jan 13 '17 at 12:26
  • Neither of these ways seem to work; the first (audio.set_file(random_file()) causes the original random selection to repeat on and on forever, without choosing a new random track; what I need is a way to select a new random track at the end of the first track The second, introducing var audio = new Audio again, plays only once, with no second track – Tom Jan 13 '17 at 13:03
  • Are you use `set_file()` function? I mean that you replace `set_file()` function with function from documentation of your `Audio` player. It is pseudocode you need to read your player manual about how to set new file. And try again solution with new Audio and set listners carefully. – oklas Jan 13 '17 at 14:25
  • My audio player is whatever Firefox uses by default to play mp3 files -- is that what you mean? (Sorry, I am new to all of this) – Tom Jan 13 '17 at 14:59
  • So read the its manual where you can find that property `src` is what you need. So set `src` and call `play()`. – oklas Jan 13 '17 at 15:26
  • If you satisfied the answer stackoverflow please do not forget make answer as solved and make vote. – oklas Jan 13 '17 at 18:48
0

Just install the famous underscoreJS module on client or server side with Node.js and invoke the sample function;

var random_file = _.sample(audio_files);
Med Lazhari
  • 608
  • 5
  • 9
  • How it help to solve problem of play in random order in infinite time? – oklas Jan 13 '17 at 10:45
  • Yes -- what I want is a playlist that will go on and on forever, choosing a new random track at the end of each track, so that I can create a kind of melody that goes on and on forever... Maybe too ambitious for my second day with JavaScript / code... – Tom Jan 13 '17 at 10:48
  • @Tom the fist thing is always to know exactly whose single steps are nassecary to create the whole picture. – Reporter Jan 13 '17 at 10:50
  • u can keep removing from list of audiofiles on every selection. Once it empties, start again with full list => do this infinit loop – Shishir Arora Jan 13 '17 at 11:04
0

Open demo in codepen

To play the files in the playlist infinitely with the help of the cool underscore module, you can do the following

If you are a nodejs developer you can install the module using the following command

npm i underscore

To use it in nodejs you can do the following

const _ = require("underscore")

To use it on the frontend you can put the following script just before the closing tag of the html body tag

<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.13.2/underscore-min.js"></script>

Now lets get started with the logic

Here is my html file

  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>SHUFFLED PLAYLIST</title>
  </head>
  <body>
     <button id="playsong">play playlist</button>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.13.2/underscore-min.js"></script>
  </body>
  </html>

Here is my javascript file

  let played_files = [];
  let playsong = document.querySelector("#playsong");

  let audio = new Audio();

  let audio_files = [
    "songs/song_num1.mp3",
    "songs/song_num2.mp3",
    "songs/song_num3.m4a",
    "songs/song_num4.mp3",
  ];

  function random_file() {
    let file = _.sample(audio_files);
    let allSongsPlayed = _.size(played_files) === _.size(audio_files);
    if (_.contains(played_files, file) || allSongsPlayed) {
      if (allSongsPlayed) {
        played_files = [];
      }
      return random_file();
    } else {
      played_files.push(file);
      return file;
    }
  }

  function playSong() {
    let file = random_file();
    audio.src = file;
    audio.play();
  }

  playsong.addEventListener("click", () => {
    playSong();
  });

  audio.addEventListener("ended", playSong);
prince david
  • 181
  • 1
  • 5