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;
}