Play sounds in loop using JS code (no conditions file)

Description of the problem: I am trying to loop through an array of sound files and play them in sequence using only JS code. I know this can be easily done using a conditions file in a loop, but I would prefer a JS code solution because part of the experiment involves shuffling sounds and colours which I’ve found easier to do with code.

I would like to create a simple loop where each audio file in an array plays in sequence (without multiple pieces playing at once). However, a simple for loop using the .play() method of each audio file doesn’t work as all three audio files play at once. I am hoping to use a countdown timer to control when each piece plays.

The arrays would be defined such that song1, song2 and song3 are variables containing audio components.

Begin experiment tab:

// insert shuffle function
shuffle = util.shuffle 

// create sound components
song1 = new sound.Sound({
    win: psychoJS.window,
    value: 'A',
    secs: 10,
});
song1.setVolume(0.3);

song2 = new sound.Sound({
    win: psychoJS.window,
    value: 'B',
    secs: 10,
});
song2.setVolume(0.3);
  
song3 = new sound.Sound({
    win: psychoJS.window,
    value: 'C',
    secs: 10,
});
song3.setVolume(0.3);

// store sound components in array:
var songs = [song1, song2, song3]
// shuffle array
songs = shuffle(songs);

Begin routine tab:

// loop through songs
for (var i = 0; i < songs.length; i++) { 
// record duration of current song:
        var songDuration = songs[i].getDuration(); 
// initialize a countdown timer:
        var countdown = new util.CountdownTimer(songDuration);
// play current audio until countdown timer runs out and 
        while(countdown.getTime() > 0) {
                songs[i].play();
          }
}

It appears the sound files start in a staggered order, however I get this error message:
image

Any help would be greatly appreciated!

Hi @andersoc, I am not familiar with “manual” PsychoJS coding so this is just a wild guess: Could it be that you don’t really need the countdown and the while loop? From reading the documentation it seems to me that .play() simply starts the sound and will let it play for the specified duration. I suspect the while loop tries to start the sound repeatedly really fast and this might cause the issue.

I was hoping this would be the solution too, but unfortunately the audio files all play at once with only a for loop. The timer approach tries to wait to play a second audio file while the first audio file is playing by taking its duration into account. I am going to keep experimenting as it shouldn’t be too difficult in principle.

Thanks for the suggestion!

Yes, this is to be expected, because the for loop just goes through the stimuli as fast as it can, right? So you would need to pause the loop for the duration of the sound after starting it. Maybe there is a “sleep” function in js? Or some way to find out, if the current sound is still playing?

Maybe something like this

function sleep(milliseconds) {
  const date = Date.now();
  let currentDate = null;
  do {
    currentDate = Date.now();
  } while (currentDate - date < milliseconds);
}

taken from here.

Or alternatively, based on your code

for (var i = 0; i < songs.length; i++) { 
// record duration of current song:
        var songDuration = songs[i].getDuration(); 
// initialize a countdown timer:
        var countdown = new util.CountdownTimer(songDuration);
// play current audio
        songs[i].play();
// "sleep" for the duration of the sound
        timer = countdown.getTime()

        while(timer > 0) {
                timer = countdown.getTime()
          }
}
1 Like

Wonderful, thank you for the excellent suggestion! I was actually using the sleep function elsewhere in the experiment so this was very straightforward.

Here is the code that made it work:

for (var i = 0; i < songs.length; i++) { 
// record duration of current song:
        var songDuration = songs[i].getDuration(); 
// initialize a countdown timer:
        var countdown = new util.CountdownTimer(songDuration);
// play current audio
        songs[i].play();
// "sleep" for the duration of the sound
        while(countdown.getTime() > 0) {
            sleep(durs[i])
     }
}

where durs is an array of sound durations (defined above the previous code component):

var durs = []
//loop through songs
for (var i = 0; i < songs.length; i++) { 
// record duration of current song:
        var songDuration = songs[i].getDuration(); 
// initialize a countdown timer:
        durs.push(songDuration);
}
//console.log(songs);
//console.log(durs);

 // how much time should the delay between two iterations be (in milliseconds)?

for(var i=0; i<durs.length; i++) {
    durs[i] *= 1000;
}

I have yet to try this with actual audio files but I’m hoping it works.

Thanks for all your help!

1 Like