// Processing script for familiarisation experiment // Written by Jelle Zuidema on 22/3/2013. // Adapted by ... Last changes: 26/3/2013 // Import sound and URL libraries (don't change) import ddf.minim.signals.*; import ddf.minim.*; import ddf.minim.analysis.*; import ddf.minim.effects.*; import java.net.MalformedURLException; import java.net.URL; ExperimentLogger logger; // ******************************************************************* // Part 1: Settings that can be adapted for each particular experiment // ******************************************************************* // The identifier of this experiment (will be used in saving data etc.) String EXP_ID = "BF1.0"; // *Stimuli filenames; which stimuli to use in familiarisation and test phase* String[] stimuli = { "CCA.wav",//0 "DDF.wav",//1 "EEB.wav",//2 "CAC.wav",//3 "DFD.wav",//4 "EBE.wav",//5 "AAC.wav",//6 "ACA.wav",//7 "BBD.wav",//8 "BDB.wav",//9 "CCE.wav",//10 "CEC.wav",//11 "DCD.wav",//12 "DDC.wav",//13 "EAE.wav",//14 "EEA.wav",//15 "FAF.wav",//16 "FEF.wav",//17 "FFA.wav",//18 "FFE.wav",//19 "GHG.wav",//20 "HGH.wav",//21 "GGH.wav",//22 "HHG.wav"//23 }; int[] train1_indices = { 0,1,2 }; // condition A int[] train2_indices = { 3,4,5 }; // condition B int[] test1_indices = { 6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23 }; int Ntrain1=15, Ntest1=2; // number of cycles through the list of indices boolean randomizep=true; // *Other settings: pauses, keys, how/where data is saved, size of the screen* float pauseDuration=1000; // pause between stimuli in milliseconds char nextkey = ' '; boolean anyKey=false; // if true, any key is interpreted as nextkey in the instruction phases char skipkey = '+'; // for experimenters to force program to move to next phase char positivekey = 'z';//yes-answer char negativekey = '/';//no-answer boolean dataSavedOverWebp=true; // saved locally if false String url="http://www.illc.uva.nl/LaCo/webexperiment.nl/save-data.php?item="+EXP_ID; int schermBreedte = 800, schermHoogte = 600; //resolutie // *Instructions to the subjects (current version assumes they fit on one page)* String InstructionsBeforeFamiliarisation = "U krijgt zo fragmenten te horen van een niet bestaande taal. Luister aandachtig. In de testfase moet u aangeven of u denkt dat het gehoorde fragment onderdeel is van deze taal. Wanneer de luisterfase klaar is, drukt u op de spatiebalk om de luisterfase af te sluiten en de testfase te laten beginnen. Klik, afhankelijk van de ontvangen instructies, op A of B om te beginnen."; String InstructionsBeforeTest = "De luisterfase is nu afgelopen. Nu begint de testfase.\nGeef na elke fragment aan of het overeen komt met de gehoorde taal in de luisterfase.\n\nVoor ja toets 'z'\nVoor nee toets '/'\n\nReageer zo snel, maar foutloos mogelijk.\nDruk op de spatiebalk om de testfase te laten beginnen."; String InstructionsAfterStimulus = ""+positivekey+"='ja', "+negativekey+"='nee'"; String InstructionsAfterTest = "De testfase is nu afgelopen. Druk op de spatiebalk om het experiment af te sluiten en nog een paar vragen te beantwoorden."; // *************************************************************************** // Part 2: Global variables. Don't change (unless you know what you are doing) // *************************************************************************** int stimulus=0; boolean soundLoaded=false; Minim minim; AudioSnippet in; AudioMetaData meta; PFont font28; float stimulus_onset, stimulus_duration, key_time=millis(); String participant; PrintWriter output; int phase = 1, roundnr=0; int Ntrainingstimuli=Ntrain1*train1_indices.length; int Nteststimuli=Ntest1*test1_indices.length; String completed_url; // variable with link to exit questionair String event_url; // variable with link to data saving script String userSession; // user id char condition; // *************************************************************************** // Part 3: Definition of core Processing functions: setup, draw, keyReleased // *************************************************************************** void setup() { size(800,600);//screen.width, screen.height); fill(0); noStroke(); background(255); font28 = loadFont("Arial-Black-28.vlw"); textFont(font28); textAlign(CENTER); participant = nf(int(random(100000)),5); loadLogger(); if (dataSavedOverWebp) { // saving data the old fashion way url = url + participant + "_"; loadStrings(url+"Participant_nr:,"+participant); loadStrings(url+"Stimuli:,"+join(stimuli,",")); loadStrings(url+"participant,phase,roundnr,stimulus,stimulus_duration,responsetime,clocktime,response"); // and saving it using Sander's interface to a MySQL-database logger.data("Participant_nr:,"+participant); logger.data("Stimuli:,"+join(stimuli,",")); logger.data("participant,phase,roundnr,stimulus,stimulus_duration,responsetime,clocktime,response"); } /* //Commented out, because applets are not allowed to (try to) save data locally else { output = createWriter("data"+participant+".csv"); output.println("Participant_nr:,"+participant); output.println("Stimuli:,"+join(stimuli,",")); output.println("participant,phase,roundnr,stimulus,stimulus_duration,responsetime,clocktime,response"); } */ minim = new Minim(this); } void draw() { background(255); // text("condition:"+condition+"Phase:"+phase+", Round:"+roundnr,schermBreedte/2,schermHoogte-50); if (phase == 1) { text(InstructionsBeforeFamiliarisation, 10,10,width-10, height-10); } if (phase == 2) { if (millis()>stimulus_onset+stimulus_duration+pauseDuration) { if (roundnr>=Ntrainingstimuli) {phase++; roundnr=0;} else { if (randomizep && roundnr%train1_indices.length==0) randomize_array(train1_indices); playnextstimulus(); roundnr++; } } } if (phase == 3) { text(InstructionsBeforeTest, 10,10,width-10, height-10); } if (phase == 4) { if (millis()>stimulus_onset+stimulus_duration) { text(InstructionsAfterStimulus, 10,10,width-10, height-10); } } if (phase == 5) { text(InstructionsAfterTest, 10,10,width-10, height-10); } } public void keyReleased() { key_time = millis(); if (key_time < stimulus_onset+stimulus_duration) return; // key strokes that happen during stimulus presentation are ignored if (phase == 1) { // instruction screens // if (anyKey) { key=nextkey; } if (key=='a' || key=='A') { condition = 'A'; phase++; roundnr=0; } if (key=='b' || key=='B') { train1_indices = train2_indices; condition = 'B'; phase++; roundnr=0; } loadStrings(url+"Condition:,"+condition); } if (phase == 2) { // familiarisation phase if (key==skipkey) { phase++; roundnr=0; } } if (phase == 3) { // instruction screens if (anyKey) { key=nextkey; } if (key==nextkey) { phase++; roundnr=0; if (randomizep && roundnr%test1_indices.length==0) randomize_array(test1_indices); playnextstimulus(); } } if (phase == 4) { // test phase if (key==skipkey) { phase++; roundnr=0; } if (key==positivekey || key==negativekey) { save_data(""+(key==positivekey)); roundnr++; if (roundnr>=Nteststimuli) {phase++; roundnr=0;} else { if (randomizep && roundnr%test1_indices.length==0) randomize_array(test1_indices); playnextstimulus(); } } } if (phase == 5) { // instruction screens if (anyKey) { key=nextkey; } if (key==nextkey) stop(); } } // *************************************************************************** // Part 4: Definition of remaining functions // *************************************************************************** void loadExternalParameters(){ // Sander's code for handling parameters try{ setCompletedURL(param("completedURL")); }catch(Exception e){ setCompletedURL(""); } try{ setEventURL(param("eventURL")); }catch(Exception e){ setEventURL("http://www.illc.uva.nl/LaCo/webexperiment.nl/suite1_2/event.php"); } try{ setUserSession(param("userSession")); }catch(Exception e){ setUserSession("A"); } } void setCompletedURL(String completedURL){ if(completedURL != null){ completed_url = completedURL; } } void setEventURL(String eventURL){ if(eventURL != null){ event_url = eventURL; }else{ event_url = "http://www.illc.uva.nl/LaCo/webexperiment.nl/suite1_2/event.php"; } } void setUserSession(String session){ userSession = session; } void loadLogger(){ logger = new ExperimentLogger(EXP_ID, event_url); logger.setParticipantId(participant); if(userSession != null){ logger.setUserSession(userSession); } logger.start(""); } void playnextstimulus() { if (phase==2) { stimulus = train1_indices[roundnr%train1_indices.length]; } if (phase==4) { stimulus = test1_indices[roundnr%test1_indices.length]; } in = minim.loadSnippet(stimuli[stimulus]); meta = in.getMetaData(); stimulus_duration = meta.length(); stimulus_onset=millis(); in.play(); } void save_data(String thedata) { if (dataSavedOverWebp) { // saving the old fashion way loadStrings(url+","+participant+","+phase+","+roundnr+","+stimuli[stimulus]+","+stimulus_duration+","+(key_time-(stimulus_onset+stimulus_duration))+","+key_time+","+thedata); // saving to the MySQL database logger.data(""+participant+","+phase+","+roundnr+","+stimuli[stimulus]+","+stimulus_duration+","+(key_time-(stimulus_onset+stimulus_duration))+","+key_time+","+thedata); } /* Commented out because applets are not allowed to save data locally else { output.println(participant+","+phase+","+roundnr+","+stimuli[stimulus]+","+stimulus_duration+"," +(key_time-(stimulus_onset+stimulus_duration))+","+key_time+","+thedata); } */ } void stop() { // if (in != null) in.close(); if (minim != null) minim.stop(); if (!dataSavedOverWebp) { output.flush(); // Writes the remaining data to the file output.close(); // Finishes the file } super.stop(); // calling the original, overloaded, method. link("http://www.illc.uva.nl/LaCo/webexperiment.nl/bf1-post.php?results="+EXP_ID+","+participant); } void randomize_array(int[] old) { int candidate, oldval; for (int i=0; i