By Psych_Josh - 2/9/2016
Dear Inquisit,
I've run some successful scripts via Inquisit recently, mostly thanks to the extraordinary help offered by those on this forum.
I'm running a new, but similar, time estimation study that involves a rotating dot around a clock, and on pressing the correct button, produces the appropriate sound. The participant's task to judge the time interval between their button press and the sound appearing.
The clock-face(s) is essentially a set of images with a dot at a given position to indicate the time. Currently I'm using the /timeout expression = 60 to give the impression of a rotating dot at the desired rate (hence the requirement for the 'pretrial' so that the fixation cross only appears at the beginning - if there's an elegant way to not have a pretrial, that would be fantastic). However, this introduces a blinking effect - i.e., the continuous presentation of the appropriate .jpeg images (through continuous presentation of the trial) gives a strobe effect, instead of the continuous display of the images and the dot rotating around the clock. Is it possible to fix this? This also introduces the issue that, if the participant presses the button between the trials (i.e., the 'blink'), it naturally does not register a response.
Two other small queries are that I would like .jpegs to still continuously appear for a time (so that the dot rotates a random amount of time between 1000ms - 2500ms) after the participant presses the button. I've tried introducing a new trial, but this doesn't seem to work (and undesirably still requires a correctresponse, seeing as I am using the /timeout expression to rotate the dot around the clock). The other is that I would ideally like the /correctmessage sound to be delayed by 250ms. I've tried editing the physical sound, but either Inquisit plays the sound despite the 250ms gap, or doesn't play the sound at all.
I'm aware these issues are largely caused by the use of the /timeout expression, so I apologise in advance for the trouble. I have attached my script and some of the clock-faces to help my issue seem clearer - I also apologise for my confusing description.
Many thanks, Josh
|
By Dave - 2/9/2016
> However, this introduces a blinking effect - i.e., the continuous presentation of the appropriate .jpeg images (through continuous > presentation of the trial) gives a strobe effect, instead of the continuous display of the images and the dot rotating around the clock. Is it > possible to fix this?
You'll want to set the respective stimulus elements' /erase attributes to false. If you do need to remove stimuli from the screen at some point(s), you'll want to paint them over with a blank <shape>. See the "How to erase stimuli" topic in the documentation for information on Inquisit's default stimulus erasing behavior.
> I would like .jpegs to still continuously appear for a time (so that the dot rotates a random amount of time between 1000ms - 2500ms) > after the participant presses the button. I've tried introducing a new trial, but this doesn't seem to work (and undesirably still requires a > correctresponse, seeing as I am using the /timeout expression to rotate the dot around the clock).
Introducing a new trial is exactly what you should do. The <trial> should not accept any responses -- /validresponse = (0) -- and loop itself a randomly chosen number of times (by /branch-ing back to itself) and once it has been run X times /branch to trial.postlibet.
> The other is that I would ideally like the /correctmessage sound to be delayed by 250ms. I've tried editing the physical sound, but either > Inquisit plays the sound despite the 250ms gap, or doesn't play the sound at all.
Delaying the the beep conflicts with your other requirement above (keep rotating for a random amount of time), so I'm not sure what to answer here.
|
By Psych_Josh - 2/10/2016
Hi Dave,
Many thanks for your response - the /erase = false worked perfectly. On achieving this, appears to be a slight pause when the correct response is made, making the trial pause and thus the clockface/dot remain static for an instant, instead of continued fluid movement from <trial Libet> to <trial Rotate> - is this purely from Inquisit processing, or is there a way to achieve fluidity?
As for the repeated trials to continually rotate the dot, the best I can come up with (as I can't find a repeat trial X number of times expression anywhere) is:
<values> /currentlistnumber = 0 </values>
<list libetframes> /items = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24) /selectionmode = sequence </list>
<trial Rotate> /ontrialbegin = [{values.currentlistnumber = list.libetframes.nextvalue}] /stimulustimes = [1 = libetclock] /validresponse = (0) /timeout = 60 /branch = [if (values.currentlistnumber == 1) {trial.postLibet} else {trial.rotate}] /recorddata = false </trial>
This obviously means the clock will rotate afterwards until the given value, however I am unable to work out how to randomly generate this (or if there's a better solution).
>Delaying the the beep conflicts with your other requirement above (keep rotating for a random amount of time), so I'm not sure what to answer here
It is purely for the participants to estimate how long it took for the noise to appear after their action - the aim is to have a few different lengths of delay, with different types of noises, the time estimation bar afterwards is for them to measure how long it took for the sound to appear. If there is no way to achieve this, I will likely have to rethink how to present the trials in a way to produce the illusion of a dot rotating around the clock. Thank you anyway though!
Thanks, Josh
|
By Dave - 2/10/2016
> [...] appears to be a slight pause when the correct response is made, making the trial pause and thus the clockface/dot remain static for an > instant [...]
If your definition of <trial libet> is still the same as it was in your initial post, the pause is due to you displaying a correctmessage for 250ms.
<trial Libet> /ontrialbegin = [{values.currentlistnumber = list.libetframes.nextvalue}] /stimulustimes = [1 = libetclock] /correctresponse = (" ") /correctmessage = true(beep, 250) ... </trial>
Only after that will Inquisit move on to the next trial (e.g. <trial rotate>). It's what you instructed the script to do.
Regarding the rotation: At the start of each "round", i.e. in <trial preLibet>, generate a random number (using round(rand()) or by sampling from a <list> of random values) from the desired range and store it in a global variable (a <values> entry). The random number in that variable represents *the number of times* you want to run <trial rotate> after a response has been given in <trial libet>. You count the number of times <trial rotate> is run and /branch to <trial postLibet> once the count is equal to the random value.
<trial preLibet> / ontrialbegin = [values.keep_rotating_for_n_trials = round(rand(20,50)); values.rotationcount = 0; ] ... /branch = [trial.Libet] /recorddata = false </trial>
<trial Rotate> / ontrialbegin = [values.rotationcount += 1] ... /branch = [if (values.rotationcount >= values.keep_rotating_for_n_trials) {trial.postLibet} else {trial.rotate}] /recorddata = false </trial>
Regarding the delay: The display of the response message with (varying) delay should not be handled by <trial libet> then, but by some other, separate / dedicated <trial> element, e.g. <trial rotate>. You can 'inject' the <sound> stimulus into the <trial>'s stimulusframes by using the insertstimulusframe() function. As with the continued rotation detailed above, pick the instance of <trial rotate> in which the "beep" should occur. The do
<trial Rotate> / ontrialbegin = [values.rotationcount += 1] / ontrialbegin = [if (values.rotationcount == values.beep_in_instance) {trial.rotate.insertstimulusframe(sound.beep, 1);}; ] / ontrialend = [trial.rotate.resetstimulusframes(); ] ... /branch = [if (values.rotationcount >= values.keep_rotating_for_n_trials) {trial.postLibet} else {trial.rotate}] /recorddata = false </trial>
|
By Psych_Josh - 2/11/2016
Hi Dave,
That's great - It runs nice and smoothly now, except for one issue, which is that the sound doesn't play at all, now. I've tried recreating the physical sound file and replacing it to no avail. The study runs, and I receive no error message, but the sound still does not play.
Here's an updated script: <trial preLibet> /ontrialbegin = [{values.currentlistnumber = list.libetframes.nextvalue}] /ontrialbegin = [values.keep_rotating_for_n_trials = round(rand(20,50)); values.rotationcount = 0;] /stimulustimes = [1 = cross; 500 = libetclock] /correctresponse = (" ") /pretrialpause = 500 /timeout = 600 /branch = [trial.Libet] /recorddata = false </trial>
<trial Libet> /ontrialbegin = [{values.currentlistnumber = list.libetframes.nextvalue}] /stimulustimes = [1 = libetclock] /correctresponse = (57) /correctmessage = true(beep, 1) /timeout = 60 /branch = [if (trial.Libet.response == 57) {trial.rotate} else {trial.Libet}] /recorddata = false /ontrialend = [values.sound = 3] /ontrialend = [values.onset = list.libetframes.currentvalue] </trial> <trial Rotate> /ontrialbegin = [{values.currentlistnumber = list.libetframes.nextvalue}] /ontrialbegin = [values.rotationcount += 1] /ontrialbegin = [if (values.rotationcount == values.beep) {trial.rotate.insertstimulusframe(sound.beep, 250)}] /stimulustimes = [1 = libetclock] /validresponse = (0) /timeout = 60 /ontrialend = [trial.rotate.resetstimulusframes(100)] /branch = [if (values.rotationcount >= values.keep_rotating_for_n_trials) {trial.postLibet} else {trial.rotate}] /recorddata = false </trial>
<trial postLibet> /stimulustimes = [1 = cross] /timeout = 500 /branch = [surveypage.tep] /recorddata = false </trial>
Where I have put 250 is the amount of time I'd like to delay the sound for - I apologise if that's an inappropriate place. Thank you for your help, it is greatly appreciated. Josh
|
By Dave - 2/11/2016
<trial Rotate> /ontrialbegin = [{values.currentlistnumber = list.libetframes.nextvalue}] /ontrialbegin = [values.rotationcount += 1] /ontrialbegin = [if (values.rotationcount == values.beep) {trial.rotate.insertstimulusframe(sound.beep, 250)}] /stimulustimes = [1 = libetclock] /validresponse = (0) /timeout = 60 /ontrialend = [trial.rotate.resetstimulusframes(100)] /branch = [if (values.rotationcount >= values.keep_rotating_for_n_trials) {trial.postLibet} else {trial.rotate}] /recorddata = false </trial>
means you insert the sound *in the trial's 250th frame*, which does not make sense. The trial does not even last that long (it times out after just 60ms). You'll want to insert the stimulus in the 1st frame just like in the example code in my previous reply. And you need to make sure that values.beep makes sense. I.e., it should represent the *instance* of <trial rotate> that corresponds (most closely) to the desired delay. With a single instance of <trial rotate> lasting 60ms and the desired delay being ~250 ms, you'd want the beep inserted in the 5th instance of <trial rotate>, i.e., values.beep should be set to 5.
|
By Psych_Josh - 2/15/2016
Hi Dave,
Thanks for your help - I've worked through a lot of the issues, and everything has turned out nicely thanks to you.
However, there is one glaring problem - when the sound is played in the following trial, the rotating dot pauses in place whilst the sound is played. I assume this is because the .jpeg of whatever clock-face + dot is bound to the sound stimulus. What the task requires is that the dot still to continue rotating whilst the sound is being played, as the participant is going to be asked to judge the time the sound occurred (which will be quite easy if the .jpeg pauses and thus the dot is, to the participant's perspective, stationary). Is there a solution to this, or will it require a complete overhaul of the code to produce this effect? I've attached the updated script below for your reference.
Many thanks again, Josh
|
By Dave - 2/15/2016
> However, there is one glaring problem - when the sound is played in the following trial, the rotating dot pauses in place whilst the sound > is played. I assume this is because the .jpeg of whatever clock-face + dot is bound to the sound stimulus.
Yes and no. The clock-face is not bound to the sound stimulus; both are bound to the *trial*. You are essentially working in 60ms intervals (duration of a single instance of the relevant <trial>). Thus your options are:
- Make sure the sound you play is substantially shorter than 60ms, so that it "fits" within a single instance of the <trial>. - You've also forced the <trial> to play the sound to completion by setting /playthrough = true in the respective <sound> elements, i.e., the <trial> that plays the sound will not terminate and cut off the sound after 60ms before moving on to the next <trial>. - If making the sounds sufficiently short is not an option, you need to set up and run yet another <trial> element, one that lasts longer than 60ms (how long depends on the actual length of your sounds; I have no way of knowing what it is) and displays *more* than a single clock-face via its /stimulustimes.
|
By Dave - 2/15/2016
To illustrate, here's a quick example implementing the gist of the latter option:
<block myblock> / onblockbegin = [systembeep.frequency = 440; systembeep.duration = 300; ] / trials = [1-10=start] </block>
<values> / c1item = 1 / c2item = 1 / c3item = 1 / c4item = 1 / c5item = 1 / c6item = 1 / delay = 4 / keeprotatingfor = 0 / rotatecount = 0 </values>
<trial start> / ontrialbegin = [list.clocksteps.reset(); values.rotatecount = 0; values.keeprotatingfor = round(rand(20,50)); ] / stimulusframes = [1=fixation] / trialduration = 600 / branch = [trial.libet] </trial>
<trial libet> / ontrialbegin = [values.c1item = list.clocksteps.nextindex; ] / stimulusframes = [1=clock1] / trialduration = 60 / validresponse = (57, -57, 0) / branch = [if (trial.libet.response != 0) trial.rotate else trial.libet] </trial>
<trial rotate> / ontrialbegin = [values.c1item = list.clocksteps.nextindex; values.rotatecount += 1; ] / stimulusframes = [1=clock1] / validresponse = (0) / trialduration = 60 / branch = [if (values.rotatecount == values.delay) trial.audio] / branch = [if (values.rotatecount < values.keeprotatingfor) trial.rotate else trial.end] </trial>
<trial audio> / ontrialbegin = [values.c1item = list.clocksteps.nextindex; values.c2item = list.clocksteps.nextindex; values.c3item = list.clocksteps.nextindex; values.c4item = list.clocksteps.nextindex; values.c5item = list.clocksteps.nextindex; values.c5item = list.clocksteps.nextindex; ] / ontrialend = [values.rotatecount += 6] / stimulustimes = [0=systembeep, clock1; 60=clock2; 120=clock3; 180=clock4; 240=clock5; 300=clock6] / validresponse = (0) / trialduration = 360 / branch = [if (values.rotatecount < values.keeprotatingfor) trial.rotate else trial.end] </trial>
<trial end> / stimulusframes = [1=end] / validresponse = (57) </trial>
<list clocksteps> / poolsize = 24 / selectionmode = sequence / selectionrate = always </list>
<text clock1> / items = clockfaceitems / select = values.c1item / erase = false </text>
<text clock2> / items = clockfaceitems / select = values.c2item / erase = false </text>
<text clock3> / items = clockfaceitems / select = values.c3item / erase = false </text>
<text clock4> / items = clockfaceitems / select = values.c4item / erase = false </text>
<text clock5> / items = clockfaceitems / select = values.c5item / erase = false </text>
<text clock6> / items = clockfaceitems / select = values.c6item / erase = false </text>
<item clockfaceitems> / 1 = "01.jpg" / 2 = "02.jpg" / 3 = "03.jpg" / 4 = "04.jpg" / 5 = "05.jpg" / 6 = "06.jpg" / 7 = "07.jpg" / 8 = "08.jpg" / 9 = "09.jpg" / 10 = "10.jpg" / 11 = "11.jpg" / 12 = "12.jpg" / 13 = "13.jpg" / 14 = "14.jpg" / 15 = "15.jpg" / 16 = "16.jpg" / 17 = "17.jpg" / 18 = "18.jpg" / 19 = "19.jpg" / 20 = "20.jpg" / 21 = "21.jpg" / 22 = "22.jpg" / 23 = "23.jpg" / 24 = "24.jpg" </item>
<text fixation> / items = ("+") / erase = false </text>
<text end> / items = ("End of round.") </text>
|
By Psych_Josh - 2/15/2016
Hi Dave,
Thanks for your help! I had a bit of trouble implementing your example into my script (I need these specific sounds as the length that they are) - It runs without errors, but the clock face (i.e. trial) still pauses whilst the sound is played, and don't carry on afterwards, and the text name appears in front of the clock (although this is probably because they are text items in your example). I apologise if I made some glaring errors in my translation. I have acquired the correct stimuli, so I've added those in, which are positive and negative sounds. I've pasted the script below with just the positive sounds to share what I managed to do (or not do, as is the case):
<values> /completed = 0 /intervalestimation = 0 /currentlistnumber = 0 </values>
<values> /onset = 0 </values>
<values> /sound = 0 </values>
<values> /keep_rotating_for_n_trials = 0 /rotationcount = 0 /beep = 5 </values>
<list targetduration> /items = (100, 200, 300, 400, 500, 600, 700, 800, 900) /replace = false </list>
<list targetdurationh> /items = (100, 400, 700) /poolsize = 42 /replace = false </list>
<list targetdurations> /items = (100, 400, 700) /poolsize = 42 /replace = false </list>
<list libetframes> /items = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24) /selectionmode = sequence /poolsize = 24 </list>
******************************************************************************************************************* ******************************************************************************************************************* STIMULI ******************************************************************************************************************* *******************************************************************************************************************
********** Clock **********
<item libetclock> /1 = "Libet_clock1.jpg" /2 = "Libet_clock2.jpg" /3 = "Libet_clock3.jpg" /4 = "Libet_clock4.jpg" /5 = "Libet_clock5.jpg" /6 = "Libet_clock6.jpg" /7 = "Libet_clock7.jpg" /8 = "Libet_clock8.jpg" /9 = "Libet_clock9.jpg" /10 = "Libet_clock10.jpg" /11 = "Libet_clock11.jpg" /12 = "Libet_clock12.jpg" /13 = "Libet_clock13.jpg" /14 = "Libet_clock14.jpg" /15 = "Libet_clock15.jpg" /16 = "Libet_clock16.jpg" /17 = "Libet_clock17.jpg" /18 = "Libet_clock18.jpg" /19 = "Libet_clock19.jpg" /20 = "Libet_clock20.jpg" /21 = "Libet_clock21.jpg" /22 = "Libet_clock22.jpg" /23 = "Libet_clock23.jpg" /24 = "Libet_clock24.jpg" </item>
<picture libetclock> /items = libetclock /position = (50, 50) /select = list.libetframes.currentvalue /erase = false </picture>
********** Sounds **********
<item possounds> /1 = "FemCheer.wav" /2 = "FemLaugh.wav" /3 = "MaleCheer.wav" /4 = "MaleLaugh.wav" </item>
<sound possounds> /items = possounds /playthrough = true </sound>
********** Extra stimuli: **********
<text cross> /items = ("+") /color = (0,0,0) </text>
******************************************************************************************************************* ******************************************************************************************************************* TRIALS ******************************************************************************************************************* ******************************************************************************************************************* <values> /c1item = 1 /c2item = 1 /c3item = 1 /c4item = 1 /c5item = 1 /delay = 4 </values>
<text clock1> / items = libetclock / select = values.c1item / erase = false </text>
<text clock2> / items = libetclock / select = values.c2item / erase = false </text>
<text clock3> / items = libetclock / select = values.c3item / erase = false </text>
<text clock4> / items = libetclock / select = values.c4item / erase = false </text>
<text clock5> / items = libetclock / select = values.c5item / erase = false </text>
********* Libet trials - Positive sounds ********* <trial positive> /ontrialbegin = [{values.currentlistnumber = list.libetframes.nextvalue}] /ontrialbegin = [values.keep_rotating_for_n_trials = round(rand(20,50)); values.rotationcount = 0] /stimulustimes = [1 = cross; 500 = libetclock] /correctresponse = (" ") /pretrialpause = 500 /trialduration = 600 /branch = [trial.LibetP] /recorddata = false </trial>
<trial LibetP> /ontrialbegin = [{values.currentlistnumber = list.libetframes.nextvalue}] /ontrialbegin = [values.c1item = list.libetframes.nextindex] /stimulustimes = [1 = libetclock] /correctresponse = (57) /trialduration = 50 /branch = [if (trial.LibetP.response == 57) {trial.rotateP} else {trial.LibetP}] /recorddata = false /ontrialend = [values.sound = sound.possounds.currentitem] /ontrialend = [values.onset = list.libetframes.currentvalue] </trial> <trial RotateP> /ontrialbegin = [{values.currentlistnumber = list.libetframes.nextvalue}] /ontrialbegin = [values.rotationcount += 1; values.c1item = list.libetframes.nextindex] /stimulustimes = [1 = libetclock] /validresponse = (0) /trialduration = 50 /ontrialend = [trial.rotateP.resetstimulusframes(0)] /branch = [if (values.rotationcount >= values.keep_rotating_for_n_trials) {trial.AudioP} else {trial.rotateP}] /recorddata = false </trial>
<trial AudioP> / ontrialbegin = [values.c1item = list.libetframes.nextindex; values.c2item = list.libetframes.nextindex; values.c3item = list.libetframes.nextindex; values.c4item = list.libetframes.nextindex; values.c5item = list.libetframes.nextindex; values.c5item = list.libetframes.nextindex; ] / ontrialend = [values.rotationcount += 6] / stimulustimes = [0 = possounds, clock1; 50 = clock2; 100 = clock3; 150 = clock4; 200 = clock5] / validresponse = (0) / trialduration = 250 / branch = [if (values.rotationcount < values.keep_rotating_for_n_trials) trial.rotateP else trial.postlibetP] </trial>
<trial postLibetP> /stimulustimes = [1 = cross] /trialduration = 500 /branch = [surveypage.tep] /recorddata = false </trial>
Many thanks in advance, Josh
|
By Dave - 2/16/2016
WARNING: A proper implementation should look something along the following lines. BUT: Since I don't have your stimuli, etc. I cannot test this.
<values> /completed = 0 /intervalestimation = 0 /currentlistnumber = 0 </values>
<values> /onset = 0 </values>
<values> /sound = 0 </values>
<values> /keep_rotating_for_n_trials = 0 /rotationcount = 0 /beep = 5 </values>
<list targetduration> /items = (100, 200, 300, 400, 500, 600, 700, 800, 900) /replace = false </list>
<list targetdurationh> /items = (100, 400, 700) /poolsize = 42 /replace = false </list>
<list targetdurations> /items = (100, 400, 700) /poolsize = 42 /replace = false </list>
<list libetframes> /selectionmode = sequence / selectionrate = always /poolsize = 24 </list>
******************************************************************************************************************* ******************************************************************************************************************* STIMULI ******************************************************************************************************************* *******************************************************************************************************************
********** Clock **********
<item libetclock> /1 = "Libet_clock1.jpg" /2 = "Libet_clock2.jpg" /3 = "Libet_clock3.jpg" /4 = "Libet_clock4.jpg" /5 = "Libet_clock5.jpg" /6 = "Libet_clock6.jpg" /7 = "Libet_clock7.jpg" /8 = "Libet_clock8.jpg" /9 = "Libet_clock9.jpg" /10 = "Libet_clock10.jpg" /11 = "Libet_clock11.jpg" /12 = "Libet_clock12.jpg" /13 = "Libet_clock13.jpg" /14 = "Libet_clock14.jpg" /15 = "Libet_clock15.jpg" /16 = "Libet_clock16.jpg" /17 = "Libet_clock17.jpg" /18 = "Libet_clock18.jpg" /19 = "Libet_clock19.jpg" /20 = "Libet_clock20.jpg" /21 = "Libet_clock21.jpg" /22 = "Libet_clock22.jpg" /23 = "Libet_clock23.jpg" /24 = "Libet_clock24.jpg" </item>
<picture libetclock> /items = libetclock /position = (50, 50) /select = values.c1item /erase = false </picture>
********** Sounds **********
<item possounds> /1 = "FemCheer.wav" /2 = "FemLaugh.wav" /3 = "MaleCheer.wav" /4 = "MaleLaugh.wav" </item>
<sound possounds> /items = possounds /playthrough = true </sound>
********** Extra stimuli: **********
<text cross> /items = ("+") /color = (0,0,0) </text>
******************************************************************************************************************* ******************************************************************************************************************* TRIALS ******************************************************************************************************************* ******************************************************************************************************************* <values> /c1item = 1 /c2item = 1 /c3item = 1 /c4item = 1 /c5item = 1 </values>
<picture clock1> / items = libetclock / select = values.c1item / erase = false </picture>
<picture clock2> / items = libetclock / select = values.c2item / erase = false </picture>
<picture clock3> / items = libetclock / select = values.c3item / erase = false </picture>
<picture clock4> / items = libetclock / select = values.c4item / erase = false </picture>
<picture clock5> / items = libetclock / select = values.c5item / erase = false </picture>
********* Libet trials - Positive sounds ********* <trial positive> /ontrialbegin = [{values.c1item = list.libetframes.nextindex}] /ontrialbegin = [values.keep_rotating_for_n_trials = round(rand(20,50)); values.rotationcount = 0] /stimulustimes = [0 = cross; 500 = libetclock] /correctresponse = (" ") /pretrialpause = 500 /trialduration = 1050 /branch = [trial.LibetP] /recorddata = false </trial>
<trial LibetP> /ontrialbegin = [values.c1item = list.libetframes.nextindex] /stimulustimes = [1 = libetclock] /correctresponse = (57) /trialduration = 50 /branch = [if (trial.LibetP.response == 57) {trial.rotateP} else {trial.LibetP}] /recorddata = false /ontrialend = [values.sound = sound.possounds.currentitem] /ontrialend = [values.onset = values.c1item] </trial>
<trial RotateP> /ontrialbegin = [values.rotationcount += 1; values.c1item = list.libetframes.nextindex] /stimulustimes = [1 = libetclock] /validresponse = (0) /trialduration = 50 / branch = [if (values.rotatecount == values.beep) trial.AudioP] /branch = [if (values.rotationcount < values.keep_rotating_for_n_trials) trial.rotateP else trial.postlibetp] /recorddata = false </trial>
<trial AudioP> / ontrialbegin = [values.c1item = list.libetframes.nextindex; values.c2item = list.libetframes.nextindex; values.c3item = list.libetframes.nextindex; values.c4item = list.libetframes.nextindex; values.c5item = list.libetframes.nextindex; values.c5item = list.libetframes.nextindex; ] / ontrialend = [values.rotationcount += 6] / stimulustimes = [0 = possounds, clock1; 50 = clock2; 100 = clock3; 150 = clock4; 200 = clock5] / validresponse = (0) / trialduration = 250 / branch = [if (values.rotationcount < values.keep_rotating_for_n_trials) trial.rotateP else trial.postlibetP] </trial>
<trial postLibetP> /stimulustimes = [1 = cross] /trialduration = 500 /branch = [surveypage.tep] /recorddata = false </trial>
|
By Dave - 2/17/2016
NOTE: I accidentally deleted your last post. Sorry about that. My response to that now missing post is below:
> 1 - The trial stops around half-way through sound, instead.
Then your trial is not long enough. You've set it to 250ms. If your sounds are longer than that, you need to extend it.
> 2 - Multiple sounds (or trials) are now played in succession, randomly between 4-7, before the whole sequence transitions to > postLibetP.
The 2nd /branch is wrong
<trial RotateP> /ontrialbegin = [values.rotationcount += 1; values.c1item = list.libetframes.nextindex] /stimulustimes = [1 = libetclock] /validresponse = (0) /trialduration = 50 /branch = [if (values.rotationcount == values.beep) trial.AudioP] /branch = [if (values.rotationcount < values.keep_rotating_for_n_trials) {trial.AudioP} else {trial.RotateP}] /recorddata = false </trial>
and will invoke <trial AudioP> repeatedly. Note that in the example I gave you, the /branch reads differently:
<trial RotateP> /ontrialbegin = [values.rotationcount += 1; values.c1item = list.libetframes.nextindex] /stimulustimes = [1 = libetclock] /validresponse = (0) /trialduration = 50 / branch = [if (values.rotatecount == values.beep) trial.AudioP] /branch = [if (values.rotationcount < values.keep_rotating_for_n_trials) trial.rotateP else trial.postlibetp] /recorddata = false </trial>
|
By Psych_Josh - 2/17/2016
Hi Dave,
No worries. I apologise - I actually meant to say the dot, not the trial, stops half-way through the sound. The sound will play, the dot continues briefly, but stops before the sound ends.
On noticing the second error, I tried that exact same fix myself, but alas this didn't fix either of the errors I mentioned previously.
Sorry for taking up so much of your time on this! Josh
|
By Dave - 2/17/2016
> I actually meant to say the dot, not the trial, stops half-way through the sound.
Comes down to not quite the same, but a similar thing: - The trial is too short. Make it as long as your sound lasts plus a little bit. - Increase the number of clock faces you display during the trial. If your sound lasts, say, 400ms, you need to set up additional clock face stimuli and display them via /stimulustimes just like the ones that are already there.
> On noticing the second error, I tried that exact same fix myself, but alas this didn't fix either of the errors I mentioned previously.
I'm having trouble believing that (no offense intended).
<trial RotateP> /ontrialbegin = [values.rotationcount += 1; values.c1item = list.libetframes.nextindex] /stimulustimes = [1 = libetclock] /validresponse = (0) /trialduration = 50 / branch = [if (values.rotatecount == values.beep) trial.AudioP] /branch = [if (values.rotationcount < values.keep_rotating_for_n_trials) trial.rotateP else trial.postlibetp] /recorddata = false </trial>
The first /branch will invoke <trial AudioP> if -- and only if -- the rotationcount is equal to values.beep (i.e., the chosen delay).
<trial AudioP> / ontrialend = [values.rotationcount += 6] ... / branch = [if (values.rotationcount < values.keep_rotating_for_n_trials) trial.rotateP else trial.postlibetP] </trial>
<trial AudioP> displays the sound and then goes back to trial.rotateP if -- and only if -- the randomly chosen amount of post-response-rotations has not been completed yet. Critically, <trial AudioP> increases the rotation count by the number of clock faces displayed during <trial AudioP>. That means when <trial rotateP> is invoked again
<trial RotateP> /ontrialbegin = [values.rotationcount += 1; values.c1item = list.libetframes.nextindex] /stimulustimes = [1 = libetclock] /validresponse = (0) /trialduration = 50 / branch = [if (values.rotatecount == values.beep) trial.AudioP] /branch = [if (values.rotationcount < values.keep_rotating_for_n_trials) trial.rotateP else trial.postlibetp] /recorddata = false </trial>
the 1st /branch *cannot* fire again. The rotation count is not equal to values.beep anymore. Instead the 2nd /branch applies, i.e., <trial rotateP> is looped until the selected amount of post-response-rotations is reached. Once it has been reached, <trial postlibetP> is invoked.
|
By Dave - 2/18/2016
If you are still stuck, do the following: Put the current revision of your script as well as all the other files it requires (images, sound files) in a ZIP archive and attach it to this thread. I can then fix the remaining issues (there shouldn't be many) for you.
|
By Psych_Josh - 2/18/2016
Hi Dave,
Sorry - I was mistaken, I thought you had put: /branch = [if (values.rotationcount < values.keep_rotating_for_n_trials) {trial.AudioP} else {trial.RotateP}]
as the solution, not the issue causing the problem. The multiple sounds issue has been resolved, thank you!
I have doubled the amount of clockfaces as a test, their required values, and entered them as /stimulus times, but a strange effect occurs: The sound is still played at the same point it was anyway before the additional clockfaces, and the dot rapidly moves to, and stops at, the top-most clockface point (where 0/60 would be), before reversing back to where it was previously when the sound was played, and then carrying on forward for the remaining time.
I have attached the script - thanks a lot for your time - I confess this current task was, in hindsight, probably beyond my current Inquisit ability. Thanks again, Josh
|
By Dave - 2/19/2016
> I have doubled the amount of clockfaces as a test, their required values, and entered them as /stimulus times
Yes, but you are not actually setting those values:
<trial AudioP> / ontrialbegin = [values.c1item = list.libetframes.nextindex; values.c2item = list.libetframes.nextindex; values.c3item = list.libetframes.nextindex; values.c4item = list.libetframes.nextindex; values.c5item = list.libetframes.nextindex] / ontrialend = [values.rotationcount += 6] ... </trial>
You stop with values.c5item (i.e., the 5th clock face). You have forgotten to set values.c6item to values.c10item -- which are responsible for displaying the proper 6th to 10th clock face in the trial. You've also forgotten to adjust the rotationcount increase accordingly. To sum up:
<trial AudioP> / ontrialbegin = [values.c1item = list.libetframes.nextindex; values.c2item = list.libetframes.nextindex; values.c3item = list.libetframes.nextindex; values.c4item = list.libetframes.nextindex; values.c5item = list.libetframes.nextindex; values.c6item = list.libetframes.nextindex; values.c7item = list.libetframes.nextindex; values.c8item = list.libetframes.nextindex; values.c9item = list.libetframes.nextindex; values.c10item = list.libetframes.nextindex; ] / ontrialend = [values.rotationcount += 10] / stimulustimes = [0 = possounds, clock1; 50 = clock2; 100 = clock3; 150 = clock4; 200 = clock5; 250 = clock6; 300 = clock7; 350 = clock8; 400 = clock9; 450 = clock10] / validresponse = (0) / trialduration = 500 / branch = [if (values.rotationcount < values.keep_rotating_for_n_trials) {trial.rotateP} else {trial.postlibetP}] </trial>
is what the <trial> ought to look like.
Beyond that, the AudioP trial is *still* too short to accommodate some of your sounds: They are *longer* than 500ms (see e.g. "FemLaugh.wav" which lasts close to a second). You either need to extend the <trial> further or allow it to terminate the sound early by setting
<sound possounds> /items = possounds /playthrough = false </sound>
|
By Psych_Josh - 2/19/2016
Thanks Dave - I thought I'd doubled checked everything, but thanks for pointing that out.
Somehow, with the update, there is a still a minor pause when the sound and clockface of the first trial commence - when I say minor, it's very small, but I imagine would still be perceptible to participants. Also, despite checking for errors within the /stimulustimes, there is an additional minor pause within the longer sounds. I've pasted the updated script beneath:
<values> /c1item = 1 /c2item = 1 /c3item = 1 /c4item = 1 /c5item = 1 /c6item = 1 /c7item = 1 /c8item = 1 /c9item = 1 /c10item = 1 /c11item = 1 /c12item = 1 /c13item = 1 /c14item = 1 /c15item = 1 /c16item = 1 /c17item = 1 /c18item = 1 /c19item = 1 /c20item = 1 </values>
<picture clock1> /items = libetclock /select = values.c1item /erase = false </picture>
<picture clock2> /items = libetclock /select = values.c2item /erase = false </picture>
<picture clock3> /items = libetclock /select = values.c3item /erase = false </picture>
<picture clock4> /items = libetclock /select = values.c4item /erase = false </picture>
<picture clock5> /items = libetclock /select = values.c5item /erase = false </picture>
<picture clock6> /items = libetclock /select = values.c6item /erase = false </picture>
<picture clock7> /items = libetclock /select = values.c7item /erase = false </picture>
<picture clock8> /items = libetclock /select = values.c8item /erase = false </picture>
<picture clock9> /items = libetclock /select = values.c9item /erase = false </picture>
<picture clock10> /items = libetclock /select = values.c10item /erase = false </picture>
<picture clock11> /items = libetclock /select = values.c11item /erase = false </picture>
<picture clock12> /items = libetclock /select = values.c12item /erase = false </picture>
<picture clock13> /items = libetclock /select = values.c13item /erase = false </picture>
<picture clock14> /items = libetclock /select = values.c14item /erase = false </picture>
<picture clock15> /items = libetclock /select = values.c15item /erase = false </picture>
<picture clock16> /items = libetclock /select = values.c16item /erase = false </picture>
<picture clock17> /items = libetclock /select = values.c17item /erase = false </picture>
<picture clock18> /items = libetclock /select = values.c18item /erase = false </picture>
<picture clock19> /items = libetclock /select = values.c19item /erase = false </picture>
<picture clock20> /items = libetclock /select = values.c20item /erase = false </picture>
********* Libet trials - Positive sounds ********* <trial positive> /ontrialbegin = [{values.c1item = list.libetframes.nextvalue}] /ontrialbegin = [values.keep_rotating_for_n_trials = round(rand(35,70)); values.rotationcount = 0] /stimulustimes = [0 = cross; 500 = libetclock] /correctresponse = (" ") /pretrialpause = 500 /trialduration = 1050 /branch = [trial.LibetP] /recorddata = false </trial>
<trial LibetP> /ontrialbegin = [values.c1item = list.libetframes.nextindex] /stimulustimes = [1 = libetclock] /correctresponse = (57) /trialduration = 50 /branch = [if (trial.LibetP.response == 57) {trial.rotateP} else {trial.LibetP}] /recorddata = false /ontrialend = [values.sound = sound.possounds.currentitem] /ontrialend = [values.onset = values.c1item] </trial> <trial RotateP> /ontrialbegin = [values.rotationcount += 1; values.c1item = list.libetframes.nextindex] /stimulustimes = [1 = libetclock] /validresponse = (0) /trialduration = 50 /branch = [if (values.rotationcount == values.beep) trial.AudioP] /branch = [if (values.rotationcount < values.keep_rotating_for_n_trials) {trial.rotateP} else {trial.postLibetP}] /recorddata = false </trial>
<trial AudioP> /ontrialbegin = [values.c1item = list.libetframes.nextindex; values.c2item = list.libetframes.nextindex; values.c3item = list.libetframes.nextindex; values.c4item = list.libetframes.nextindex; values.c5item = list.libetframes.nextindex; values.c6item = list.libetframes.nextindex; values.c7item = list.libetframes.nextindex; values.c8item = list.libetframes.nextindex; values.c9item = list.libetframes.nextindex; values.c10item = list.libetframes.nextindex; values.c11item = list.libetframes.nextindex; values.c12item = list.libetframes.nextindex; values.c13item = list.libetframes.nextindex; values.c14item = list.libetframes.nextindex; values.c15item = list.libetframes.nextindex; values.c16item = list.libetframes.nextindex; values.c17item = list.libetframes.nextindex; values.c18item = list.libetframes.nextindex; values.c19item = list.libetframes.nextindex; values.c20item = list.libetframes.nextindex] /ontrialend = [values.rotationcount += 20] /stimulustimes = [0 = possounds, clock1; 50 = clock2; 100 = clock3; 150 = clock4; 200 = clock5; 250 = clock6; 300 = clock7; 350 = clock8; 400 = clock9; 450 = clock10; 500 = clock11; 550 = clock12; 600 = clock13; 650 = clock14; 700 = clock15; 750 = clock16; 800 = clock17; 850 = clock18; 900 = clock19; 950 = clock20] /validresponse = (0) /trialduration = 500 /branch = [if (values.rotationcount < values.keep_rotating_for_n_trials) {trial.rotateP} else {trial.postlibetP}] </trial>
<trial postLibetP> /stimulustimes = [1 = cross] /trialduration = 500 /branch = [surveypage.tep] /recorddata = false </trial>
Again, thanks for your assistance. Josh
|
By Dave - 2/19/2016
> Somehow, with the update, there is a still a minor pause when the sound and clockface of the first trial commence - when I say minor, it's > very small, but I imagine would still be perceptible to participants.
Your audio hardware has some level of startup latency; how much that is will vary from system to system. In addition, Inquisit must actually load the sound at the beginning of the trial before it can instruct your system to actually play it; that's a pretty quick process, but it's not instantaneous either. Both factors combined (hardware latency moreso than Inquisit) lead to the minor pause.
> Also, despite checking for errors within the /stimulustimes, there is an additional minor pause within the longer sounds.
Is perfectly in line with the above. Longer sounds take longer to load.
Usually one would define a sufficiently long /pretrialpause, which Inquisit would use to prepare stimuli *before* the actual stimulus presentation sequence starts. That, however, will not eliminate your audio hardware's startup latency.
/pretrialpause is also not an option within your script, because that would further interrupt the continuous display of clock faces -- Inquisit cannot display or change stimuli during a /pretrialpause (that would be antithetical to a /pretrialpause's purpose: prepare stimuli *before* displaying them).
Long story short: I unfortunately don't see a way to completely eliminate the pause. You can minimize it by keeping the sounds as short as possible and using fast audio hardware.
In addition, you should pay attention to your display's refresh rate. To be able to even reasonably achieve the timings you want, you'll probably want to run this using displays that support a 100Hz refresh rate (i.e., a 10ms display re-draw cycle).
|
By Psych_Josh - 2/19/2016
Hi Dave,
Thanks for the information and your help - I'll take your advice on board and try to use a solution that doesn't inhibit the desired mechanics of the study.
Thanks again, Josh
|
By Dave - 2/20/2016
FYI: I'll keep mulling this over for a little while longer. If I can come up with a way to make the transitions between trials smoother (i.e., further minimize or avoid delays introduced by stimulus preparation and/or hardware constraints), I will update this thread.
|
By Psych_Josh - 2/21/2016
Thanks Dave - Is there any way to preload the stimulus into the computer's RAM somehow? That's how matlab and eprime cope with such things, but they're unusable for online research.
Josh
|
By Dave - 2/21/2016
Inquisit loads stimuli into RAM (as much as possible) when it initially parses the script; that does not, however, eliminate the audio hardware's inherent startup latency.
As I said, I'm toying around with a couple of ideas to make transitions smoother, but I frankly don't know if any of them is workable yet.
|
By Dave - 2/23/2016
Okay, after thinking about this for a good long while as well as playing around with a couple of ideas, the best alternative implementation approach I can see is actually really unsophisticated: Use the <block> element's /bgstim attribute to have a video (20 frames per second) of the clock running constantly in the background. Then your <trial>s within that <block> are basically relieved of the burden to transition smoothly, i.e., without interrupting or briefly stalling the display of the moving clock. Example implementation (video included) is in the attached ZIP archive. Hope this helps.
|
By Psych_Josh - 2/23/2016
Hi Dave, thanks for your continued efforts - I tried running the study but encountered these errors:
Inquisit Media Error: Pins cannot connect because they don't support the same transport. line 74: file win\MediaPlayerControl.cpp Unable to the load media file 'C:/Users/josha/Downloads/Libet_Alternative/libet.avi'. Verify the file is of a supported video format.
- is this specific to my computer? As this would be run online, would this mean there'd be a requirement of the participant's computer to run the study?
Thanks again, Josh
|
By Dave - 2/23/2016
This would indicate a codec issue, i.e., it *might* be particular to your system, but others may run into it as well. You can take the video and re-encode it to a different format using your transcoder of choice (MPEG-2 should be a suitable codec choice for maximum compatibility across systems).
|
By Dave - 2/23/2016
By the way, if you want to toy around with this yourself: There are plenty tutorials available online (from basic to technically advanced) on how to generate videos from a set of still images. Search terms like "jpeg to mpeg" or "jpeg to avi" will bring up those articles and the tools you can use (see e.g. http://www.howtoanswer.com/create-avi-files-from-png-jpeg-and-bmp-images-format--155.html for a basic one and http://electron.mit.edu/~gsteele/ffmpeg/ for something on the technical side worth reading).
|
By Psych_Josh - 2/24/2016
Excellent, thanks Dave, you've been a great help. I'll try this method, and hopefully be able to figure this out myself.
Thanks again, Josh
|
By Psych_Josh - 2/24/2016
Hi Dave,
I've played around with script, converted the video, and I think I mostly understand all of the technical aspects - however, given that this is a video, I can't see a way to report the current value (position) of the dot around the clock when the participant makes their response, which is crucial for the data columns so that I can compare their estimated time vs. the actual time (which we previously did via values.onset). Is there a way to still record this? I was thinking that if the video frames/time are/is specifically linked to a .jpeg, we could code for 24 values for each .jpeg, and use a counting measure for how long the video has played for. Is this viable?
Many thanks, Josh
|
By Dave - 2/24/2016
There isn't a way to record the exact frame. What you'd do instead is capture the <block>'s elapsedtime property at the relevant points of interest. I.e., - At what time has the response-gathering trial started in the block (-> values.responsetrial_starttime)? This indicates when the video has been successfully started / is running. - At what time within the block have we observed the response (-> values.response_time)? - At what time within the block has the audio-trial started and the sound been initiated (-> values.audiotrial_starttime & values.sound_starttime)?
This should give you the information you need.
<values> / delay = 0 / keep_rotating_for = 0 / intervalestimation = 0 </values>
<values> / response_time = 0 / sound_starttime = 0 / audiotrial_starttime = 0 / responsetrial_starttime = 0 </values>
<data> / columns = [subject blockcode trialcode values.responsetrial_starttime values.response_time values.audiotrial_starttime values.sound_starttime values.delay values.intervalestimation] / separatefiles = true </data>
<defaults> / screencolor = white </defaults>
<expt> / blocks = [1-4 = fixate_p_block] </expt>
<block fixate_p_block> / bgstim = (cross) / trials = [1=fixate_p] / branch = [block.rotate_p_block] / recorddata = false </block>
<trial fixate_p> /ontrialbegin = [values.keep_rotating_for = round(rand(2000,3500)); values.delay = 1500; values.audiotrial_starttime = -1; values.intervalestimation = -1; values.responsetrial_starttime = -1; values.response_time = -1; values.audiotrial_starttime = -1; values.sound_starttime = -1; ] / validresponse = (0) / trialduration = 1000 / recorddata = false </trial>
<text cross> / items = ("+") / color = (0,0,0) / erase = false </text>
<block rotate_p_block> / bgstim = (clock) / trials = [1=response_p; 2=audio_p] / branch = [block.estimate_p_block] </block>
<trial response_p> / ontrialbegin = [values.responsetrial_starttime = block.rotate_p_block.elapsedtime; ] / correctresponse = (57) / ontrialend = [values.response_time = block.rotate_p_block.elapsedtime; ] </trial>
<trial audio_p> / ontrialbegin = [values.audiotrial_starttime = block.rotate_p_block.elapsedtime; ] / ontrialbegin = [trial.audio_p.insertstimulustime(sound.possounds, values.delay); ] / ontrialend = [values.sound_starttime = values.audiotrial_starttime + sound.possounds.stimulusonset; ] / ontrialend = [trial.audio_p.resetstimulusframes(); ] / validresponse = (0) / trialduration = values.delay + values.keep_rotating_for + 1000 </trial>
<sound possounds> / items = possounds / playthrough = false / select = noreplace / resetinterval = 0 </sound>
<item possounds> /1 = "FemCheer.wav" /2 = "FemLaugh.wav" /3 = "MaleCheer.wav" /4 = "MaleLaugh.wav" </item>
<video clock> / items = ("libet.avi") / playthrough = false / loop = true / erase = false / size = (100%, 100%) </video>
<block estimate_p_block> / trials = [1=tep] </block>
<surveypage TEp> /questions = [1 = TE_response] /showpagenumbers = false /ontrialend = [values.intervalestimation = slider.te_response.response] /recorddata = true </surveypage>
<slider TE_response> / caption="How much time before the image appeared (in ms)?" / labels=("0ms", "100ms", "200ms", "300ms", "400ms", "500ms", "600ms", "700ms", "800ms", "900ms", "1000ms") /responsefontstyle = ("Arial", 1.5%, false, false, false, false, 5, 1) / txcolor = (0, 0, 0) / range = (0, 1000) / increment = 1 /showticks = true /slidersize = (70%, 30%) /position = (10%, 50%) /defaultresponse = 0 </slider>
|
By Psych_Josh - 3/9/2016
Hi Dave,
Thanks, that script info was very useful. I'm still having a bit of trouble with trying to calculate the exact position of the dot as per the video from those values, given that each frame is 50ms, and the video lasts 1100ms (and therefore contain 20 frames).
The question participants are asked has been modified - not that they estimate how long it took for the sound to transpire, but where on the clock it occurred (via the dot, hence the needed to know the exact frame the sound occurred), given the 250ms delay.
Thus far I have: (values.sound_starttime - values.responsetrial_starttime) / 20
This yields the correct response (at least, when compared to my own estimations several times), and I have created a score for this via:
<values> /score = 0 </values>
/ontrialend = [values.score = (values.sound_starttime - values.responsetrial_starttime)/20]
Could you verify if this correct (sorry if that's a silly question)? The tricky thing is, though, that if the video obviously plays continuously, and therefore the dot rotates, the number of frames builds up over time, and so even if the clock rotates just once before the participant responds, this yields an inaccurate result (by (values.sound_starttime - values.responsetrial_starttime) being 1100 seconds out/per rotation). Is there a way to reset stimulus frames at the block level for background stimuli each time it loops (if that's a reasonable way of solving it)? I've attached the script in case that helps to give a clearer picture.
Many thanks, Josh
|
By Dave - 3/9/2016
> [...] Could you verify if this correct (sorry if that's a silly question)?
This seems fine to me, but I would defer to your own judgment and testing results rather than my gut instincts.
> [...] the number of frames builds up over time [...] > Is there a way to reset stimulus frames at the block level for background stimuli each time it loops (if that's a reasonable way of solving it)?
There is neither a way nor a need to reset stimulus frames at the block level (I'm not sure what that would mean, to be perfectly honest). This seems easily solvable by simple modulo arithmetic to me. What am I missing?
EDIT: On the 2nd question: Perhaps you could clarify by giving a concrete numerical example. Contrast the situation of response occurring during the initial rotation vs, say, it occurring after 2 or 3 rotations with actual (hypothetical) numbers.
|
By Psych_Josh - 3/9/2016
Hi Dave,
For example, the calculation works perfectly fine for:
(705 (values.sound_starttime) - 105 (values.responsetrial_starttime)) / 20 (fps)
= 30, the correct position of the dot on the clock-face.
However, if the clock has already rotated once, for example, then it becomes:
(1805 - 105) / 20
= 85
This would be okay if it was only one rotation, so therefore I could said conditional branches to conduct the arithmetic, but theoretically the participant could wait a several number of clock rotations before responding. When I meant reset the stimulus frames I speculated (hopefully) that each time the clock rotated that the frame count would reset back to 0 and avoid the accumulation. I apologise if the answer is quite simple and I'm missing it entirely,
Thanks again, Josh
|
By Dave - 3/9/2016
Okay, thanks. As indicated previously, modulo arithmetic to the rescue. Instead of calculating
(values.sound_starttime - values.responsetrial_starttime) / 20
you calculate values.sound_starttime *modulo* 1100 (the duration of one rotation):
(mod(values.sound_starttime,1100) - values.responsetrial_starttime) / 20
In numbers: - 0 rotations completed (mod(705,1100) - 105) / 20 = (705 - 105) / 20 = 30
- 1 rotation completed (mod(1805,1100) - 105) / 20 = (705 - 105) / 20 = 30
- 2 rotations completed (mod(2905,1100) - 105) / 20 = (705 - 105) / 20 = 30
Works for as many rotations as there may be.
|
By Psych_Josh - 3/10/2016
Hi Dave,
Thanks for the info, and the subsequent maths lesson too! I'm glad such a thing existed to make it an easy fix.
I would like to calculate the difference between the score and actual position of the dot - originally performed with:
[values.difference = values.score - values.actionq]
, however, obviously if the two scores are on different sides (e.g., the dot is at 58, but the participant answers 2) the results are skewed. Could this be resolved in a similar fashion? I apologise, again, if the answer is simpler than I am presuming.
The study is invariably ready, thanks to your help - however I have noticed in the video a small glitch - that is, when the clock rotates around successfully to its starting point, it'll make a minor jump from 60-5, as if it's skipping those frames of the video. This occurs in formats other than .avi, which isn't exactly the most accessible format if I am to run this study online. Do you know if this is purely do with the specific codec parameters of the video, given that the other options I have tried tend to be compressed .avi formats (e.g. mpg), or if there is a method around that so that more accessible formats can be used without this small glitch?
Many thanks (again, and again), Josh
|
By Dave - 3/10/2016
I'm not 100% clear what you want values.difference to reflect in concrete terms. Can you elaborate, please? As every so often, specific numerical examples illustrating a "normal" vs a "problematic" case may be helpful. Thanks.
Re. the video "jumping": Yes, it's most likely a codec issue. Not every format can be forced to arbitrary frame rates, e.g. MPEG does (to the best of my knowledge) *not* support 20fps, but only 25fps. You'll have to settle on some format / codec that supports the frame rate you need. I don't really see a perfect solution here with respect to running this online, since you just cannot know with 100% what kind of systems you will encounter in the wild and if they happen to have a proper codec available.
EDIT: FWIW, I haven't tried or tested it in this particular case, but creating an animated GIF from your clock still images may be a suitable alternative to a using an "actual" video / codec. Animated GIFs are handled by Inquisit's <video> element just like regular videos (AVI, MPG, WMV, etc.).
Note, though, that the initial (non-video) approach wasn't necessarily better in this regard; here, too, the varying performance characteristics of systems in the wild (different display refresh rates, keyboards with varying latency, etc.) would play a major role. On balance, I still believe the video-approach is preferable and will work better across a wider range of systems than the non-video approach.
|
By Psych_Josh - 3/11/2016
Hi Dave,
Sure - so, normal estimates of dot position contain estimates [values.actionq] vs the actual position [values.score].
For example, values.actionq = 30, values.score = 33, thus values.difference (values.score - values.actionq) = 3, an estimation reflecting some form of anticipatory judgement of the dot's position (a negative score would mean delayed judgement, in this instance). However, if values.actionq = 58, and values.score = 2, then values.difference = 56, which obviously would skew the results of the average of values.difference significantly. I've tried modulo arithmetic similar to the previous solution ([values.difference = (mod(values.score,60) - values.actionq)]), but, as you can guess, this did not work.
Thanks for the info re: videos, I think I'll just have to restrict participant pools to those able to run .avi's through Inquisit via some form of instruction for the time being.
Many thanks, Josh
|
By Dave - 3/11/2016
> [...] if values.actionq = 58, and values.score = 2, then values.difference = 56
And what you would be the "correct" result here? 4?
|
By Psych_Josh - 3/11/2016
Hi Dave,
Yes, 4 would be the correct answer - sorry for not being clear.
Many thanks, Josh
|
By Dave - 3/11/2016
Okay, thanks for the clarification. I'll have to think about this for a while when I have some quiet time.
Another minor point of confusion from your previous post for me: You define
values.difference = (values.score - values.actionq)
as per
> [...] For example, values.actionq = 30, values.score = 33, thus values.difference (values.score - values.actionq) = 3, > an estimation reflecting some form of anticipatory judgement of the dot's position (a negative score would mean > delayed judgement, in this instance) [...]
Yet you seem to reverse the terms directly afterwards in
> [...] However, if values.actionq = 58, and values.score = 2, then values.difference = 56 [...]
Just plugging in the numbers in the equation
values.difference = (values.score - values.actionq)
would give a result of -56 (i.e., 2 - 58 = -56), not +56 as stated in the example. So, I'm wondering whether it's a mistake in the description of the "problematic" case or a mistake in the description of the equation.
|
By Dave - 3/11/2016
After thinking about this for a while, here's what I would do (not sure if correct, please use your own judgement).
#1: Compute the difference (s - a). #2: If the *absolute value* of the difference abs(s - a) < 30 -- i.e. less than half a rotation -- you are done. #3: If the absolute value is > 30 AND the difference is negative, compute 60 + (s - a). Done. #4: If the absolute value is > 30 AND the difference is positive, compute (s - a) - 60. Done.
To illustrate:
<values> / s = 0 / a = 0 </values>
<expressions> / diff = (values.s - values.a) / diff_abs = abs(values.s - values.a) / score = if (expressions.diff_abs < 30) {expressions.diff} else if (expressions.diff < 0) (60 + expressions.diff) else if (expressions.diff > 0) (expressions.diff - 60) </expressions>
<surveypage mypage> / ontrialend = [values.s = textbox.s_input.response; values.a = textbox.a_input.response] / questions = [1=s_input; 2=a_input] / branch = [trial.result] / showquestionnumbers = false / showpagenumbers = false </surveypage>
<textbox s_input> / caption = "Enter value for S (must be between 0 and 59):" / mask = positiveintegerorzero </textbox>
<textbox a_input> / caption = "Enter value for A (must be between 0 and 59):" / mask = positiveintegerorzero </textbox>
<trial result> / stimulusframes = [1=resulttxt] / validresponse = (57) / branch = [surveypage.mypage] </trial>
<text resulttxt> / items = ("Inputs: S=<%values.s%> | A=<%values.a%> Difference=<%expressions.diff%> | Absolute Difference=<%expressions.diff_abs%> Score=<%expressions.score%>") / size = (75%, 50%) </text>
<block myblock> / trials = [1=mypage] </block>
|
By Psych_Josh - 3/15/2016
Hi Dave,
Many thanks - the <expressions> addition resolved the issue perfectly.
That appears to be the last of the issues I am unable to resolve myself - except one that I've just noticed, which is that occasionally there is a delay at the beginning of <block rotate_p_block> in:
<block rotate_p_block> /bgstim = (clock1) /trials = [1 = response_p; 2 = audio_p] /branch = [block.estimate_block] </block>
<trial response_p> /ontrialbegin = [values.responsetrial_starttime = block.rotate_p_block.elapsedtime] /correctresponse = (57) /ontrialend = [values.response_time = block.rotate_p_block.elapsedtime] /ontrialend = [values.clock_number = 1] /recorddata = false </trial>
<trial audio_p> /ontrialbegin = [values.audiotrial_starttime = block.rotate_p_block.elapsedtime] /ontrialbegin = [trial.audio_p.insertstimulustime(sound.possounds, values.delay = 258)] /ontrialend = [values.sound_starttime = values.audiotrial_starttime + sound.possounds.stimulusonset] /ontrialend = [trial.audio_p.resetstimulusframes(0)] /ontrialend = [values.onset = values.sound_starttime - values.response_time] /ontrialend = [values.sound = sound.possounds.currentitem] /ontrialend = [values.score = (mod(values.sound_starttime,2580) - values.responsetrial_starttime)/43] /ontrialend = [values.valence = "Positive"] /validresponse = (0) /trialduration = values.delay + values.keep_rotating_for + 1000 /recorddata = false </trial>
, for whatever reason, where it only displays a blank screen (I am assuming it is loading stimuli during this), this counts towards the values.responsetrial_starttime. Subsequently, this results in a large number (e.g., in excess of 2000), and can skew values.score.
To provide a concrete example, a delay at the beginning results in: values.responsetrial_starttime = 2302, values.sound_starttime = 4087. Using the previous stated method of calculating values.score (via (mod(values.sound_starttime,2580) - values.responsetrial_starttime)/43), this results in a values.score of -18.488, which obviously does not pertain to a position on the clock. However, using your previous solution to calculate values.difference (i.e., expressions.score), this still provides relatively accurately scores (i.e. from my estimates of the sound's onset, thus I don't know if this is something I should actually be concerned over). Equally, this appears to only affect the first couple of trials (presumably because the stimuli has been fully loaded as a background stimulus).
Your thoughts would be greatly appreciated. Many thanks, Josh
|
By Dave - 3/15/2016
Such delays as you describe are not something I'm seeing on my system. It may be that there is something fishy about your video's encoding, I really don't know.
> Equally, this appears to only affect the first couple of trials (presumably because the stimuli has been fully loaded as a background > stimulus).
Interesting observation, but -- at least to me -- the theory doesn't quite make sense. It should be testable, though: Add a <block> to the very start that lasts for a couple of clock rotations (i.e., fully "loads" and runs the video) and does nothing else. If your theory is correct that should affect the delay (i.e. noticeably reduce it) in any subsequent "real" trials.
I am also wondering why the issue wasn't there before -- or was it and you just didn't notice? If it wasn't, it's likely caused by something else entirely.
|
By Psych_Josh - 3/15/2016
Hi Dave,
This has only been present since today, for some unknown reason - I have shut down other programs in use which has reduced it (albeit not entirely), so perhaps it was simply CPU processing being affected. Your method resolved the issue, however the minor glitch where it takes a small moment to reload the stimuli between <block begin_p> and <block rotate_p_block>:
<block begin_p> /bgstim = (clock1) /trials = [1 = begin_p] /branch = [block.rotate_p_block] </block>
<trial begin_p> /timeout = 2580 /recorddata = false </trial>
<block rotate_p_block> /bgstim = (clock1) /trials = [1 = response_p; 2 = audio_p] /branch = [block.estimate_block] </block>
However, I feel we'll be circulating back towards our previous discussions, so I feel I'll limit the strain on CPU usage and eliminate results where values.responsetrial_starttime is in excess of, for example, 1000, so that they do not potential obscure results, given that they only occur rarely.
Many thanks again, Josh
|
By Psych_Josh - 3/15/2016
Hi Dave,
I have one final question - is there a way to check the speed of the clock? I ask this because in some instances the .avi file seems to run more smoothly than the clock within Inquisit, and the speed of the clock is vital in this particular study (i.e., it must reflect one rotation per 2580ms).
Many thanks, Josh
|
By Dave - 3/15/2016
> I ask this because in some instances the .avi file seems to run more smoothly than the clock within Inquisit
Sorry, I don't understand this sentence. What does "run more smoothly than the clock within Inquisit" mean? Thanks.
|
By Psych_Josh - 3/15/2016
Sorry for any ambiguity - I meant the .avi file played using a video player (i.e., VLC) runs more smoothly than when Inquisit runs the .avi file as a video stimulus.
Thanks, Josh
|
By Dave - 3/15/2016
Thanks. That's probably a codec problem then. VLC comes with its own set of binary codecs -- it does not use the codecs that are available system-wide, which are the ones other applications -- including Inquisit -- rely on.
|
By Psych_Josh - 3/16/2016
Hi Dave,
Thanks for the info. You have been an enormous help through this study-creation - pardon the many questions due to my novice programming skills.
Many thanks again, Josh
|
By Psych_Josh - 3/21/2016
Hi Dave,
The study has run smoothly, however one extension I'd like to add is the use of multiple clocks to achieve multiple starting points of the clock-hand. Essentially, I have created 7 extra videos where the clock starts elsewhere on the clock, and rotates as usual. These are all fine, except I was wondering how to implement these into the script, and obviously ensure that the data recorded is accurate, given that originally it was simply a method of measuring the elapsed time of the trial and corresponding that to a position on the clock.
For example, I have:
<video clock1> /items = ("libet.avi") /playthrough = false /loop = true /erase = false /size = (20%, 20%) </video>
<video clock2> /items = ("libet7.avi") /playthrough = false /loop = true /erase = false /size = (20%, 20%) </video>
<video clock3> /items = ("libet14.avi") /playthrough = false /loop = true /erase = false /size = (20%, 20%) </video>
<video clock4> /items = ("libet21.avi") /playthrough = false /loop = true /erase = false /size = (20%, 20%) </video>
<video clock5> /items = ("libet28.avi") /playthrough = false /loop = true /erase = false /size = (20%, 20%) </video>
<video clock6> /items = ("libet35.avi") /playthrough = false /loop = true /erase = false /size = (20%, 20%) </video>
<video clock7> /items = ("libet48.avi") /playthrough = false /loop = true /erase = false /size = (20%, 20%) </video>
<video clock8> /items = ("libet54.avi") /playthrough = false /loop = true /erase = false /size = (20%, 20%) </video>
The number within the file name represents the time/starting position of the clock-hand (bar the first clock).
The block thus looks presently like this:
<block rotate_p_block_aa> /bgstim = (clock1) /trials = [1 = response_p_aa; 2 = audio_p_aa] /branch = [block.estimate_block_action_a] </block>
<trial response_p_aa> /ontrialbegin = [values.responsetrial_starttime = block.rotate_p_block_aa.elapsedtime] /correctresponse = (57) /ontrialend = [values.response_time = block.rotate_p_block_aa.elapsedtime] /recorddata = false </trial>
<trial audio_p_aa> /ontrialbegin = [values.audiotrial_starttime = block.rotate_p_block_aa.elapsedtime] /ontrialbegin = [trial.audio_p_aa.insertstimulustime(sound.possounds, values.delay = 258)] /ontrialend = [values.sound_starttime = values.audiotrial_starttime + sound.possounds.stimulusonset] /ontrialend = [trial.audio_p_aa.resetstimulusframes(0)] /ontrialend = [values.onset = values.sound_starttime - values.response_time] /ontrialend = [values.sound = sound.possounds.currentitem] /ontrialend = [values.score = (mod(values.response_time,2500) - values.responsetrial_starttime)/41.6667] /ontrialend = [values.valence = "Positive"] /ontrialend = [values.elapsed_time = block.rotate_p_block_aa.elapsedtime; values.actual_time = values.sound_starttime + values.keep_rotating_for] /validresponse = (0) /trialduration = values.delay + values.keep_rotating_for + 1000 /recorddata = false </trial>
Please let me know if you need anymore information.
Many thanks, Josh
|
By Dave - 3/21/2016
You incorporate them into the script just like any other stimulus with multiple items. Set up a single <video> element, with the eight AVIs as its items. Select one of the itemnumbers 1-8 /onblockbegin via a <list>.
<values> ... / clockitem = 1 ... </values>
<clock clock> / items = ("libet.avi", "libet7.avi", ...) ... / select = values.clockitem ... </clock>
<list clockitemnumbers> / items = (1,2,3,4,5,6,7,8) </list>
<block rotate_p_block_aa> / onblockbegin = [values.clockitem = list.clockitemnumbers.nextvalue] /bgstim = (clock) /trials = [1 = response_p_aa; 2 = audio_p_aa] /branch = [block.estimate_block_action_a] </block>
Beyond that, I'm not sure what you mean with "obviously ensure that the data recorded is accurate, given that originally it was simply a method of measuring the elapsed time of the trial and corresponding that to a position on the clock."
We've discussed the math involved at length in this thread and I would assume that all of that still applies.
|
By Psych_Josh - 3/21/2016
Hi Dave,
Thanks - pardon if the solution was simpler than I was anticipating.
I've tried the <clock clock> solution, but I'm receiving an error saying that the setting has invalid text (i.e., / items...; thus presenting the standard digital clock Inquisit uses).
As for the math, I was only concerned that if the video starts from a different position (i.e., 7), then the recorded time of the sound occurrence will be askew by 7. Trying this with some of the videos has confirmed this; i.e., values.difference (i.e., values.score (the computed position of the clock-hand) - values.response (the reported time of the participant)) is consistently out by 28 (give or take +1 or -1 for human error), when using the video "libet28.avi".
Many thanks, Josh
|
By Dave - 3/21/2016
Typo on my part -- I meant
<video clock> ... </video>
as should be clear from the context.
As for the math: The math doesn't change. You know which video item was selected / shown (look at and log values.clockitem) and thus where the respective clock started. Adjust your various calculations accordingly.
|
By Psych_Josh - 3/22/2016
Hi Dave,
Thanks for the info. In an attempt to adjust the calculations, I think I might be committing an error, as I acquire variously wrong results.
I have tried both I have tried both:
/ontrialend = [if (list.clock == 2) {values.score = values.score - 7} else if (list.clock == 3) {values.score = values.score - 14} else if (list.clock == 4) {values.score = values.score - 21} else if (list.clock == 5) {values.score = values.score - 28} else if (list.clock == 6) {values.score = values.score - 35} else if (list.clock == 7) {values.score = values.score - 48} else if (list.clock == 8) {values.score = values.score - 54} else if (list.clock) = 1 {true}]
within:
<trial audio_p_aa> /ontrialbegin = [values.audiotrial_starttime = block.rotate_p_block_aa.elapsedtime] /ontrialbegin = [trial.audio_p_aa.insertstimulustime(sound.possounds, values.delay = 258)] /ontrialend = [values.sound_starttime = values.audiotrial_starttime + sound.possounds.stimulusonset] /ontrialend = [trial.audio_p_aa.resetstimulusframes(0)] /ontrialend = [values.onset = values.sound_starttime - values.response_time] /ontrialend = [values.sound = sound.possounds.currentitem] /ontrialend = [values.score = (mod(values.sound_starttime,2500) - values.responsetrial_starttime)/41.6667] /ontrialend = [values.valence = "Positive"] /ontrialend = [values.elapsed_time = block.rotate_p_block_aa.elapsedtime; values.actual_time = values.sound_starttime + values.keep_rotating_for] /validresponse = (0) /trialduration = values.delay + values.keep_rotating_for + 1000 /recorddata = false </trial>
, as well as:
/ontrialbegin = [if (list.clock == 2) {values.difference = values.difference + 7} else if (list.clock == 3) {values.difference = values.difference + 14} else if (list.clock == 4) {values.difference = values.difference + 21} else if (list.clock == 5) {values.difference = values.difference + 28} else if (list.clock == 6) {values.difference = values.difference + 35} else if (list.clock == 7) {values.difference = values.difference + 48} else if (list.clock == 8) {values.difference = values.difference + 54} else if (list.clock) = 1 {true}]
within:
<openended action> /stimulusframes = [1 = action] /position = (50, 50) /buttonlabel = "Click here to advance" /linelength = 50 /charlimit = 30 /numlines = 1 /size = (500, 50) /recorddata = true /required = true /ontrialend = [values.response = openended.action.response] /ontrialend = [values.question = "Action"] /ontrialend = [values.difference = expressions.score] </openended>
These take their information from:
<video clock> /items = ("libet0.avi", "libet7.avi", "libet14.avi", "libet21.avi", "libet28.avi", "libet35.avi", "libet48.avi", "libet54.avi") /select = values.clockitem /playthrough = false /loop = true /erase = false /size = (20%, 20%) </video>
<list clock> /items = (1,2,3,4,5,6,7,8) /replace = false /selectionmode = random </list>
I don't profess to be anywhere near proficient at conditional logic, but I don't receive an error message when coding either into the script. Essentially, they are not modifying value.score nor values.difference in any way that I can observe, and certainly not towards acquiring the correct results. If you have the time (and the energy left for this script, as I know mine is significantly waning), please tell me where I've gone wrong.
Many thanks, Josh
|
By Dave - 3/22/2016
/ontrialend = [if (list.clock == 2) ...
does not access any list-property and will thus never evaluate to true and the logic will never be executed.
It would have to read something along the lines of
/ontrialend = [if (list.clock.currentvalue == 2) ...
Why don't you simply store the "correction factor" associated with each itemnumber in a <list> tied to the itemnumber <list>. Pull that correction from the list after you've selected the itemnumber /onblockbegin and store it in a value. Subtract that value from the score, etc. at the appropriate point in time.
|
By Dave - 3/22/2016
I'd also add the following, perhaps it's helpful:
(1) If you are unsure about the math you want to / need to do, work it out with paper and pencil first. (You've probably already done this, at least that's how I read your previous comments on the topic "[...] Trying this with some of the videos has confirmed this; i.e., values.difference (i.e., values.score (the computed position of the clock-hand) - values.response (the reported time of the participant)) is consistently out by 28 (give or take +1 or -1 for human error), when using the video "libet28.avi"".)
(2) Before implementing that math into the "full" script, make a small demo that does *just* the math. Take https://www.millisecond.com/forums/FindPost18581.aspx and extend it to deal with the correction factors associated with the various clock item start positions.
(3) Once you have (2) working as you want / need, implement the changes in the full script.
|
By Psych_Josh - 4/3/2016
Hi Dave,
Thanks for the info - I ended up using the simple calculation of:
/ontrialend = [values.scorecorrected = values.score + ((values.clock_number-1) * 7) + 6], though I did take your suggestion and find it to be very helpful.
Many thanks again, Josh
|
|