Difference between revisions of "Web-based perception experiments"

From Phonlab
Jump to navigationJump to search
Line 86: Line 86:
   
 
<?php
 
<?php
  +
 
function safe_post_param($p, $maxlen) {
 
function safe_post_param($p, $maxlen) {
 
if (isset($_POST[$p]) && preg_match("/^[a-zA-Z0-9 \.-_]{1,$maxlen}$/", $_POST[$p])) {
 
if (isset($_POST[$p]) && preg_match("/^[a-zA-Z0-9 \.-_]{1,$maxlen}$/", $_POST[$p])) {
Line 92: Line 93:
 
return $val;
 
return $val;
 
}
 
}
  +
  +
* this function checks that data coming in is safe to store
   
 
if (isset($_GET['n']) && preg_match("/^\w+$/", $_GET['n'])) {
 
if (isset($_GET['n']) && preg_match("/^\w+$/", $_GET['n'])) {
Line 104: Line 107:
 
exit();
 
exit();
 
}
 
}
  +
 
*This script constructs a filename from the parameter that you pass to it. The parameter is ?n=xxx and the script constructs a file name "ep_xxx.inc" and then "includes" that file so the script now knows the value of a hidden variable called $datafile that contains the name of the file where you will save the experiment data.
  +
 
** for example, if ep_xxx.inc contains $datafile="id_data1.csv" then your data will be stored, one line per button press response, into the .csv file.
  +
   
 
$form_params['subject'] = 20;
 
$form_params['subject'] = 20;
Line 115: Line 123:
 
$form_params['response'] = 10;
 
$form_params['response'] = 10;
 
$form_params['rt'] = 10;
 
$form_params['rt'] = 10;
  +
 
*The <input..> elements of "dataform" (# 4 above) are now saved as columns in the data file.
 
** process.php checks the names of the input elements in the form, and only accepts data from these form elements. The values are the maximum number of characters that can be taken from each input item.
   
 
if ($_SERVER["REQUEST_METHOD"] == "POST") {
 
if ($_SERVER["REQUEST_METHOD"] == "POST") {
Line 129: Line 140:
   
 
?>
 
?>
*This script constructs a filename from the parameter that you pass to it. The parameter is ?n=xxx and the script constructs a file name "ep_xxx.inc" and then "includes" that file so the script now knows the value of a hidden variable called $datafile that contains the name of the file where you will save the experiment data.
 
   
** for example, if ep_xxx.inc contains $datafile="id_data1.csv" then your data will be stored, one line per button press response, into the .csv file.
 
*The <input..> elements of "dataform" (# 4 above) are now saved as columns in the data file.
 
** process.php checks the names of the input elements in the form, and only accepts data from these form elements.
 
 
*The data file must already exist on the server
 
*The data file must already exist on the server
 
** This script adds lines, but does not create a new file
 
** This script adds lines, but does not create a new file

Revision as of 10:05, 14 February 2018

Experiments can be hosted on the Department of Linguistics server, and then distributed to listeners by email link, or via Amazon Mechanical Turk.

An example is here: example experiment

A Javascript library audexp.js makes it realatively easy to implement the following four typical kinds of experiments:

  • Identification (id) - a single audio file is played, and a two-alternative forced choice (2AFC) is given.
  • Discrimination (ax) - two audio files are played, and a 2AFC is given.
  • Rating (r) - a single audio file is played, and a rating number (from 1 to 7) is given.
  • Contrast Rating (cr) - two audio files are played, and a rating number is given.


Your html code must have four features:

1) after loading audexp.js, load a javascript file that defines:

  • an array (or two arrays) of filenames that will be presented.
  • a variable called 'block'
    • here's an example of such a file
    • this .js file was created with a small perl script from a .csv spreadsheet.
    • the html header contains the following two lines to get the audexp.js library and the experiment-specific list of sound files.
 <script src="js/audexp.js"></script>
 <script src="js/blocktest_list.js"></script>

In the experiment linked above, the link is to a php script which dynamically constructs the html code that is delivered the user's web browser. The word 'test' is passed to the php script in the URL: http://linguistics.berkeley.edu/~kjohnson/rating_exp.php?list=test

The list variable is read from the php $_GET variable:

<?php
  $block = $GET["list"];
?>


In the php script, the javascript lines above are generated with an embedded bit of php code (as below). This makes it possible for me to deploy different experiments (that just differ in the list of sound files that will be played), using the same php script. The script constructs different list file names on the fly from the "list" variable that is given in the URL.

<script src="js/audexp.js"></script>
<script src="js/block<?php echo $block; ?>_list.js"></script>


2) a call to load the experiment when the page is loaded

  • this call specifies the type of experiment (in the script below this is 'id')
  • whether to randomize the order of presentation of the list of sound files
  • the interstimulus interval for 'ax' and 'cr' types
  • and the intertrial interval for all experiments.


 <body onload="load('id',false,500,2000);">

3) Three or four span elements that will be used to give feedback to listeners.

  • Show warnings to the listener
<span id="wr"></span>
  • indicate that an audio file is playing
<span id="f1">sound 1</span>
  • indicate that a second audio file is playing (for ax, and cr type experiments)
<span id="f2">sound 2</span>
  • give feedback to the lister, showing which key they pressed.
<span id="key">#</span>

4) A <form ...> element named "dataform"

  • the order of the input items determines the column order in the output file.
<form method="POST" id="dataform" action="process.php?n=unique">
     <input type="hidden" name="subject" value=<?php echo $subj; ?> />
     <input type="hidden" name="trial" />
     <input type="hidden" name="list" />
     <input type="hidden" name="file1" />
     <input type="hidden" name="filedur" />
     <input type="hidden" name="mystatus" />
     <input type="hidden" name="loadtime" />
     <input type="hidden" name="response" />
     <input type="hidden" name="rt" />
</form>

The experiment php script linked at the top of this page generated a unique subject number for the user with a little php code:

<?php
   $block = $_GET["list"];
   $subj = uniqid($block);
?>

This subject number will start with the word or number that you passed to the script as 'list' in the url (http:\\linguistics.berkeley.edu\~kjohnson\rate_exp.php?list=test).


Saving data to a server file

Finally, in (4) above you may have noticed there was reference to a file: process.php. Here is that file.

<?php
function safe_post_param($p, $maxlen) {
 if (isset($_POST[$p]) && preg_match("/^[a-zA-Z0-9 \.-_]{1,$maxlen}$/", $_POST[$p])) {
   $val = $_POST[$p]; 
 } else { $val = '<invalid>'; }
 return $val;
}
  • this function checks that data coming in is safe to store
if (isset($_GET['n']) && preg_match("/^\w+$/", $_GET['n'])) {
 $n = $_GET['n'];
 $incfile = 'ep_' . $n . '.inc';
 $success = include_once($incfile);
 if (! $success) {
    throw new Exception("didn't open the include file.");
 }
} else {
 throw new Exception("didn't find GET parameter.");
 exit();
} 
  • This script constructs a filename from the parameter that you pass to it. The parameter is ?n=xxx and the script constructs a file name "ep_xxx.inc" and then "includes" that file so the script now knows the value of a hidden variable called $datafile that contains the name of the file where you will save the experiment data.
    • for example, if ep_xxx.inc contains $datafile="id_data1.csv" then your data will be stored, one line per button press response, into the .csv file.


$form_params['subject'] = 20;
$form_params['trial'] = 5;
$form_params['list'] = 5;
$form_params['file1'] = 20;
if ($type=='ax' || $type =='cr') {$form_params['file2'] = 20; }
$form_params['filedur'] = 10;
$form_params['loadtime'] = 10;
$form_params['mystatus'] = 20;
$form_params['response'] = 10;
$form_params['rt'] = 10;
  • The <input..> elements of "dataform" (# 4 above) are now saved as columns in the data file.
    • process.php checks the names of the input elements in the form, and only accepts data from these form elements. The values are the maximum number of characters that can be taken from each input item.
if ($_SERVER["REQUEST_METHOD"] == "POST") {
 $formdata = [];
 foreach($form_params as $p=>$maxlen) {
     array_push($formdata, safe_post_param($p, $maxlen));
 }
 $data = join(",", $formdata) . "\n";
 $ret = file_put_contents($datafile,$data,FILE_APPEND| LOCK_EX);
 if (! $ret) {
    throw new Exception("error on file_put_contents()");
 }
} 
?>
  • The data file must already exist on the server
    • This script adds lines, but does not create a new file
    • The data file must have access privileges that let the php script write to the file (see Ronald Sprouse about how to set these privileges).


  • You can read the data file directly into R from your server address:
data <- read.csv("http://linguistics.berkeley.edu/~kjohnson/talkers/id_data.csv",header=TRUE);