Pavlovia only plays 1st audio file (But Psychopy locally plays all audio)

Hi all,

When I run an experiment on Pavlovia, only the first audio file in the loop plays; the rest of the audio files are silent. There is no problem when I run the experiment locally on PsychoPy. This experiment posted here is a simplified version (it’s simply 2 audio files in a loop and nothing) to replicate the problem in a barebones format.

I’ve tried changing browsers and changing file type (wav to mp3).

Any help is very much appreciated!!!

I’ll put the code in below.

/*********************************

  • Two_Audiofile_Experiment *
    *********************************/

import { core, data, sound, util, visual, hardware } from ‘./lib/psychojs-2023.2.0.js’;
const { PsychoJS } = core;
const { TrialHandler, MultiStairHandler } = data;
const { Scheduler } = util;
//some handy aliases as in the psychopy scripts;
const { abs, sin, cos, PI: pi, sqrt } = Math;
const { round } = util;

// store info about the experiment session:
let expName = ‘Two_AudioFile_Experiment’; // from the Builder filename that created this script
let expInfo = {
‘participant’: ${util.pad(Number.parseFloat(util.randint(0, 999999)).toFixed(0), 6)},
‘session’: ‘001’,
};

// Start code blocks for ‘Before Experiment’
// init psychoJS:
const psychoJS = new PsychoJS({
debug: true
});

// open window:
psychoJS.openWindow({
fullscr: true,
color: new util.Color([0,0,0]),
units: ‘height’,
waitBlanking: true
});
// schedule the experiment:
psychoJS.schedule(psychoJS.gui.DlgFromDict({
dictionary: expInfo,
title: expName
}));

const flowScheduler = new Scheduler(psychoJS);
const dialogCancelScheduler = new Scheduler(psychoJS);
psychoJS.scheduleCondition(function() { return (psychoJS.gui.dialogComponent.button === ‘OK’); }, flowScheduler, dialogCancelScheduler);

// flowScheduler gets run if the participants presses OK
flowScheduler.add(updateInfo); // add timeStamp
flowScheduler.add(experimentInit);
const trialsLoopScheduler = new Scheduler(psychoJS);
flowScheduler.add(trialsLoopBegin(trialsLoopScheduler));
flowScheduler.add(trialsLoopScheduler);
flowScheduler.add(trialsLoopEnd);

flowScheduler.add(quitPsychoJS, ‘’, true);

// quit if user presses Cancel in dialog box:
dialogCancelScheduler.add(quitPsychoJS, ‘’, false);

psychoJS.start({
expName: expName,
expInfo: expInfo,
resources: [
// resources:
{‘name’: ‘SimpleAudioExperiment.xlsx’, ‘path’: ‘SimpleAudioExperiment.xlsx’},
{‘name’: ‘Sounds/AudioFile_1.mp3’, ‘path’: ‘Sounds/AudioFile_1.mp3’},
{‘name’: ‘Sounds/AudioFile_2.mp3’, ‘path’: ‘Sounds/AudioFile_2.mp3’},
]
});

psychoJS.experimentLogger.setLevel(core.Logger.ServerLevel.EXP);

var currentLoop;
var frameDur;
async function updateInfo() {
currentLoop = psychoJS.experiment; // right now there are no loops
expInfo[‘date’] = util.MonotonicClock.getDateStr(); // add a simple timestamp
expInfo[‘expName’] = expName;
expInfo[‘psychopyVersion’] = ‘2023.2.0’;
expInfo[‘OS’] = window.navigator.platform;

// store frame rate of monitor if we can measure it successfully
expInfo[‘frameRate’] = psychoJS.window.getActualFrameRate();
if (typeof expInfo[‘frameRate’] !== ‘undefined’)
frameDur = 1.0 / Math.round(expInfo[‘frameRate’]);
else
frameDur = 1.0 / 60.0; // couldn’t get a reliable measure so guess

// add info from the URL:
util.addInfoFromUrl(expInfo);

psychoJS.experiment.dataFileName = ((“.” + “/”) + data/${expInfo["participant"]}_${expName}_${expInfo["date"]});
psychoJS.experiment.field_separator = ‘\t’;

return Scheduler.Event.NEXT;
}

var trialClock;
var sound_1;
var globalClock;
var routineTimer;
async function experimentInit() {
// Initialize components for Routine “trial”
trialClock = new util.Clock();
sound_1 = new sound.Sound({
win: psychoJS.window,
value: ‘A’,
secs: (- 1),
});
sound_1.setVolume(3.0);
// Create some handy timers
globalClock = new util.Clock(); // to track the time since experiment started
routineTimer = new util.CountdownTimer(); // to track time remaining of each (non-slip) routine

return Scheduler.Event.NEXT;
}

var trials;
function trialsLoopBegin(trialsLoopScheduler, snapshot) {
return async function() {
TrialHandler.fromSnapshot(snapshot); // update internal variables (.thisN etc) of the loop

// set up handler to look after randomisation of conditions etc
trials = new TrialHandler({
  psychoJS: psychoJS,
  nReps: 1, method: TrialHandler.Method.RANDOM,
  extraInfo: expInfo, originPath: undefined,
  trialList: 'SimpleAudioExperiment.xlsx',
  seed: undefined, name: 'trials'
});
psychoJS.experiment.addLoop(trials); // add the loop to the experiment
currentLoop = trials;  // we're now the current loop

// Schedule all the trials in the trialList:
for (const thisTrial of trials) {
  snapshot = trials.getSnapshot();
  trialsLoopScheduler.add(importConditions(snapshot));
  trialsLoopScheduler.add(trialRoutineBegin(snapshot));
  trialsLoopScheduler.add(trialRoutineEachFrame());
  trialsLoopScheduler.add(trialRoutineEnd(snapshot));
  trialsLoopScheduler.add(trialsLoopEndIteration(trialsLoopScheduler, snapshot));
}

return Scheduler.Event.NEXT;

}
}

async function trialsLoopEnd() {
// terminate loop
psychoJS.experiment.removeLoop(trials);
// update the current loop from the ExperimentHandler
if (psychoJS.experiment._unfinishedLoops.length>0)
currentLoop = psychoJS.experiment._unfinishedLoops.at(-1);
else
currentLoop = psychoJS.experiment; // so we use addData from the experiment
return Scheduler.Event.NEXT;
}

function trialsLoopEndIteration(scheduler, snapshot) {
// ------Prepare for next entry------
return async function () {
if (typeof snapshot !== ‘undefined’) {
// ------Check if user ended loop early------
if (snapshot.finished) {
// Check for and save orphaned data
if (psychoJS.experiment.isEntryEmpty()) {
psychoJS.experiment.nextEntry(snapshot);
}
scheduler.stop();
} else {
psychoJS.experiment.nextEntry(snapshot);
}
return Scheduler.Event.NEXT;
}
};
}

var t;
var frameN;
var continueRoutine;
var trialComponents;
function trialRoutineBegin(snapshot) {
return async function () {
TrialHandler.fromSnapshot(snapshot); // ensure that .thisN vals are up to date

//--- Prepare to start Routine 'trial' ---
t = 0;
trialClock.reset(); // clock
frameN = -1;
continueRoutine = true; // until we're told otherwise
// update component parameters for each repeat
psychoJS.experiment.addData('trial.started', globalClock.getTime());
sound_1.setValue(SoundFile);
sound_1.setVolume(3.0);
// keep track of which components have finished
trialComponents = [];
trialComponents.push(sound_1);

for (const thisComponent of trialComponents)
  if ('status' in thisComponent)
    thisComponent.status = PsychoJS.Status.NOT_STARTED;
return Scheduler.Event.NEXT;

}
}

function trialRoutineEachFrame() {
return async function () {
//— Loop for each frame of Routine ‘trial’ —
// get current time
t = trialClock.getTime();
frameN = frameN + 1;// number of completed frames (so 0 is the first frame)
// update/draw components on each frame
// start/stop sound_1
if (t >= 0.0 && sound_1.status === PsychoJS.Status.NOT_STARTED) {
// keep track of start time/frame for later
sound_1.tStart = t; // (not accounting for frame time here)
sound_1.frameNStart = frameN; // exact frame index

  psychoJS.window.callOnFlip(function(){ sound_1.play(); });  // screen flip
  sound_1.status = PsychoJS.Status.STARTED;
}
if (t >= (sound_1.getDuration() + sound_1.tStart)     && sound_1.status === PsychoJS.Status.STARTED) {
  sound_1.stop();  // stop the sound (if longer than duration)
  sound_1.status = PsychoJS.Status.FINISHED;
}
// check for quit (typically the Esc key)
if (psychoJS.experiment.experimentEnded || psychoJS.eventManager.getKeys({keyList:['escape']}).length > 0) {
  return quitPsychoJS('The [Escape] key was pressed. Goodbye!', false);
}

// check if the Routine should terminate
if (!continueRoutine) {  // a component has requested a forced-end of Routine
  return Scheduler.Event.NEXT;
}

continueRoutine = false;  // reverts to True if at least one component still running
for (const thisComponent of trialComponents)
  if ('status' in thisComponent && thisComponent.status !== PsychoJS.Status.FINISHED) {
    continueRoutine = true;
    break;
  }

// refresh the screen if continuing
if (continueRoutine) {
  return Scheduler.Event.FLIP_REPEAT;
} else {
  return Scheduler.Event.NEXT;
}

};
}

function trialRoutineEnd(snapshot) {
return async function () {
//— Ending Routine ‘trial’ —
for (const thisComponent of trialComponents) {
if (typeof thisComponent.setAutoDraw === ‘function’) {
thisComponent.setAutoDraw(false);
}
}
psychoJS.experiment.addData(‘trial.stopped’, globalClock.getTime());
sound_1.stop(); // ensure sound has stopped at end of Routine
// the Routine “trial” was not non-slip safe, so reset the non-slip timer
routineTimer.reset();

// Routines running outside a loop should always advance the datafile row
if (currentLoop === psychoJS.experiment) {
  psychoJS.experiment.nextEntry(snapshot);
}
return Scheduler.Event.NEXT;

}
}

function importConditions(currentLoop) {
return async function () {
psychoJS.importAttributes(currentLoop.getCurrentTrial());
return Scheduler.Event.NEXT;
};
}

async function quitPsychoJS(message, isCompleted) {
// Check for and save orphaned data
if (psychoJS.experiment.isEntryEmpty()) {
psychoJS.experiment.nextEntry();
}
psychoJS.window.close();
psychoJS.quit({message: message, isCompleted: isCompleted});

return Scheduler.Event.QUIT;
}

Hi, I’m currently having this same issue, I was wondering if you ever found a solution?

My standard response to this issue is to give the audio a start time of between.2 and .5 seconds to allow it to load.

Thank you so much, that did the trick!