Audio, Text unfolding and Clicks

Audio

PennController makes playing audio simple: just create an audio element in the cache using newAudio and play it using the command .play.

the scripts on this page correspond to the content of the picture-selection PennController(...)

newAudio("2fishRoundTank.mp3")
    .play()
,
newText("The fish swim in a tank which is perfectly round")
    .print()
,
newImage("two", "2fishRoundTank.png")
    .settings.size(200,200)
,
newImage("one", "1fishSquareTank.png")
    .settings.size(200,200)
,
newCanvas(450,200)
    .settings.add(   0 , 0 , getImage("two") )
    .settings.add( 250 , 0 , getImage("one") )
    .print()
,
newKey("FJ")
    .settings.log()
    .wait()

You might still find it unsatisfying that when participants press a key before the audio has finished playing back, playback continues while execution proceeds to the next trial.
Let us consider two solutions: (a) you could decide to stop playback when a key is pressed, or (b) you could decide to wait until playback is over before moving on to the next trial.

Both solutions consist in doing something with the audio element after a key is pressed, in other words you will want to add commands after the key element’s .wait command, which will refer to the audio element you created at the beginning of the trial.

Referring back

In order to refer back to your audio element, you first have to name it. Then you can add a new last block of commands relating to it at the end of your trial by adding a comma and referring back to your audio element using getAudio.

Here is how you wait until playback is over to end the trial:

newAudio("description", "2fishRoundTank.mp3")
    .play()
,
newText("The fish swim in a tank which is perfectly round")
    .print()
,
newImage("two", "2fishRoundTank.png")
    .settings.size(200,200)
,
newImage("one", "1fishSquareTank.png")
    .settings.size(200,200)
,
newCanvas(450,200)
    .settings.add(   0 , 0 , getImage("two") )
    .settings.add( 250 , 0 , getImage("one") )
    .print()
,
newKey("FJ")
    .settings.log()
    .wait()
,
getAudio("description")
    .wait("first")

Now when execution resumes after a key press on F or J, instead of reaching the end of the trial, the script evaluates getAudio("description") and will interpret the next commands as relating to the audio element that you named description (see line 2). The last command of the trial is now .wait("first") which will pause the execution, and therefore prevent the trial from ending, until the audio is done playing.

Note that we inserted "first" in the parentheses of this .wait command: this is because, without it, execution would only resume when the script detects an end-of-playback event. But since execution was already paused once before and was only released with a key press, you can’t be sure exactly when line 24 will be executed: your participant may well have waited until playback was over before pressing any key. In such a case, there will be no further end-of-playback event to detect when line 24 is executed. The presence of "first" tells the .wait command to release execution whenever an end-of-playback event has occurred: this way, there will in fact be no pause if the end-of-playback event happened before execution of line 24 and the trial will therefore end immediately after the key press, but if instead the key was pressed before the audio was done playing, execution is paused until the end-of-playback event.

If you’d rather stop audio playback when a key is pressed, just replace .wait("first") with .stop() (since stop introduces no pause whatsoever, execution will proceed, reaching the end of the trial, regardless of whether playback was already over).

Text unfolding

You already know how to instantly print a text element onto the page: just use .print. If you want it to unfold over time, simply use .unfold instead. The duration of our audio file is approximately 2.6s, so we will use a duration of 2600ms for our .unfold command:

newAudio("description", "2fishRoundTank.mp3")
    .play()
,
newText("The fish swim in a tank which is perfectly round")
    .unfold(2600)
,
newImage("two", "2fishRoundTank.png")
    .settings.size(200,200)
,
newImage("one", "1fishSquareTank.png")
    .settings.size(200,200)
,
newCanvas(450,200)
    .settings.add(   0 , 0 , getImage("two") )
    .settings.add( 250 , 0 , getImage("one" )
    .print()
,
newKey("FJ")
    .settings.log()
    .wait()
,
getAudio("description")
    .wait("first")

Clicks

There is one last change we want to bring to our trial structure: in addition to pressing a key, we want to make it possible to click on the pictures to make a choice. This can seem trivial, but we currently pause the execution until selection happens using .wait on our key element. How can we instead pause the execution until (a) F or J is pressed, or (b) a picture is clicked?

Since all PennController commands are element-based, ideally we would want to use .wait on an element that represents a choice between two options: option (i) a press on the F key or a click on the picture on the left; option (ii) a press on the J key or a click on the picture on the right.

The selector element does just that: it groups elements together and makes them exclusively selectable through clicks or key presses:

newAudio("description", "2fishRoundTank.mp3")
    .play()
,
newText("The fish swim in a tank which is perfectly round")
    .unfold(2600)
,
newImage("two", "2fishRoundTank.png")
    .settings.size(200,200)
,
newImage("one", "1fishSquareTank.png")
    .settings.size(200,200)
,
newCanvas(450,200)
    .settings.add(   0 , 0 , getImage("two") )
    .settings.add( 250 , 0 , getImage("one") )
    .print()
,
// newKey("FJ")
newSelector()
    .settings.add( getImage("two") , getImage("one") )
    .settings.keys(          "F"    ,          "J"   )
    .settings.log()
    .wait()
,
getAudio("description")
   .wait("first")

We replaced the key element with a selector element, to which we added the two images using .settings.add( getImage("two") , getImage("one") ) which we respectively associate with the F and J keys using .settings.keys("F","J") (spaces only for clarity).

We kept the .settings.log command since we’re still interested in the selection event (which picture was selected, and when selection happened). We also kept the .wait command which now applies to the selector element and achieves our goal: it pauses execution until one of the elements added to the selector (that is, one of the two images) is selected, either through a click or through a key press.

You may want to insert a .settings.once() command just before .wait(), so that no further clicks or key presses affect the participant’s initial choice (in case selection happens before the end of playback).


Next: Table-generated trials