2 Commits

Author SHA1 Message Date
Etienne Gaudrain
23ec2b02b9 Update README.md 2026-04-22 11:20:53 +02:00
Etienne Gaudrain
2a130f79b8 Update to jspsych@6.3 2026-04-22 11:02:41 +02:00
8 changed files with 115 additions and 39 deletions

View File

@@ -11,8 +11,12 @@ We've implemented some generic methods for audio testing with jsPsych:
* A plugin to do alternative forced choice with sounds: [jspsych-audio-sequence-button-response](docs/jspsych-audio-sequence-button-response.md).
* A plugin to do extend `audio-keyboard-response`: [jspsych-audio-keyboard-response-wait](docs/jspsych-audio-keyboard-response-wait.md).
* A plugin to do extend `audio-keyboard-response`: [jspsych-audio-keyboard-response-clickable](plugins/jspsych-audio-keyboard-response-clickable.js).
* A plugin to display a Coordinate Response Measure interface: [jspsych-crm](docs/jspsych-crm.md).
* A plugin to do display a loading spinner while waiting for a (possible async) function to complete: [jspsych-waitfor-function](docs/jspsych-waitfor-function.md).
* A plugin where html can be clickable: [jspsych-html-keyboard-response-clickable](plugins/jspsych-html-keyboard-response-clickable.js).
* A plugin where images can be clickable: [jspsych-image-keyboard-response-clickable](plugins/jspsych-image-keyboard-response-clickable.js).
* A plugin to deal with Safari's quirky behaviour: [jspsych-audio-safari-init](plugins/jspsych-audio-safari-init.js).
## Tools

View File

@@ -10,6 +10,8 @@ The trial can end when the subject responds, when the audio file has finished pl
Note that the buttons are disabled during playing so the subject cannot press any button during that time.
Make sure to define a CSS style for `.jspsych-audio-sequence-button-response button.highlighted` to see the button light up, and you may have to use `!important` for it to show up.
## Parameters
Parameters with a default value of *undefined* must be specified. Other parameters can be left unspecified if the default value is acceptable.
@@ -72,6 +74,18 @@ The animation of the visual feedback can make use of [Semantic UI's transitions]
#### Three alternative forced choice (3AFC)
CSS:
```css
.jspsych-audio-sequence-button-response button {
min-width: 5em;
min-height: 4em;
}
.jspsych-audio-sequence-button-response button.highlighted {
background-color: #ffff00 !important;
}
```
Javascript:
```javascript
var trial = {
type: 'audio-button-response',

View File

@@ -1,10 +1,12 @@
/**
* jspsych-audio-sequence-button-response
* Etienne Gaudrain <etienne.gaudrain@cnrs.fr>
* jspsych-audio-sequence-button-response for jsPsych v6.3
* Etienne Gaudrain <etienne.gaudrain@cnrs.fr> 2021-10-15
*
* Plugin for playing a sequence of audio files and getting an HTML button response
*
* Based on jspsych-audio-button-response.
*
* 2022-03-19: Fixed bug that ISI was applied also to last item.
**/
jsPsych.plugins["audio-sequence-button-response"] = (function() {
@@ -103,11 +105,7 @@ jsPsych.plugins["audio-sequence-button-response"] = (function() {
plugin.trial = function(display_element, trial) {
var context = jsPsych.pluginAPI.audioContext();
if(context !== null) {
var source;
} else {
var audio;
}
var audio;
if(trial.visual_feedback===true && trial.i_correct===null)
throw "'i_correct' has to be defined if visual feedback is requested.";
@@ -125,12 +123,6 @@ jsPsych.plugins["audio-sequence-button-response"] = (function() {
play_next_audio.i = 0;
}
/*
// We un-highlight the previous button
if(play_next_audio.i>0)
$(display_element).find('#jspsych-audio-sequence-button-response-' + (play_next_audio.i-1) +' button').toggleClass('highlighted');
*/
// Is it the last stimulus, do we need to end trial?
if(play_next_audio.i >= trial.stimuli.length) {
$(display_element).find(".jspsych-audio-sequence-button-response button").removeClass("disabled").prop('disabled', false);
@@ -141,34 +133,37 @@ jsPsych.plugins["audio-sequence-button-response"] = (function() {
}
// Prepare the next sound to play
if(context !== null) {
source = context.createBufferSource();
source.buffer = jsPsych.pluginAPI.getAudioBuffer(trial.stimuli[play_next_audio.i]);
source.connect(context.destination);
source.onended = function(){
jsPsych.pluginAPI.getAudioBuffer(trial.stimuli[play_next_audio.i]).then(function(buffer){
if(context !== null) {
audio = context.createBufferSource();
audio.buffer = buffer;
audio.connect(context.destination);
} else {
audio = buffer;
audio.currentTime = 0;
}
audio.addEventListener('ended', function _audio_ended(){
$(display_element).find('.jspsych-audio-sequence-button-response button.highlighted').removeClass('highlighted');
setTimeout(play_next_audio, trial.isi);
};
} else {
audio = jsPsych.pluginAPI.getAudioBuffer(trial.stimuli[play_next_audio.i]);
audio.currentTime = 0;
audio.addEventListener('ended', function(){
$(display_element).find('.jspsych-audio-sequence-button-response button.highlighted').removeClass('highlighted');
setTimeout(play_next_audio, trial.isi);
if(play_next_audio.i<trial.stimuli.length){
setTimeout(play_next_audio, trial.isi);
} else {
setTimeout(play_next_audio, 0);
}
audio.removeEventListener('ended', _audio_ended);
});
}
// Highlight the current button
$(display_element).find('#jspsych-audio-sequence-button-response-' + play_next_audio.i +' button').addClass('highlighted');
// Highlight the current button
$(display_element).find('#jspsych-audio-sequence-button-response-' + play_next_audio.i +' button').addClass('highlighted');
if(context !== null) {
startTime = context.currentTime;
source.start(startTime);
} else {
audio.play();
}
if(context !== null) {
startTime = context.currentTime;
audio.start(startTime);
} else {
audio.play();
}
play_next_audio.i++;
play_next_audio.i++;
});
}
//display buttons
@@ -288,12 +283,11 @@ jsPsych.plugins["audio-sequence-button-response"] = (function() {
// stop the audio file if it is playing
// remove end event listeners if they exist
if(context !== null) {
source.stop();
source.onended = function() {}
audio.stop();
} else {
audio.pause();
audio.removeEventListener('ended', end_trial);
}
audio.removeEventListener('ended', end_trial);
// kill any remaining setTimeout handlers
jsPsych.pluginAPI.clearAllTimeouts();

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,64 @@
<!DOCTYPE html>
<html>
<head>
<title>Test for jspsych-html-keyboard-response-clickable</title>
<script src="https://cdn.jsdelivr.net/gh/jquery/jquery/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/jspsych/jsPsych@6.3.1/jspsych.js"></script>
<script src="https://cdn.jsdelivr.net/gh/jspsych/jsPsych@6.3.1/plugins/jspsych-html-button-response.js"></script>
<link href="https://cdn.jsdelivr.net/gh/jspsych/jsPsych@6.3.1/css/jspsych.css" rel="stylesheet" type="text/css">
<script src="../plugins/jspsych-audio-sequence-button-response.js"></script>
<style>
.jspsych-audio-sequence-button-response button {
min-width: 5em;
min-height: 4em;
}
.jspsych-audio-sequence-button-response button.highlighted {
background-color: #ffff00 !important;
}
</style>
</head>
<body>
<script>
document.addEventListener('DOMContentLoaded', function(event) {
var timeline = [];
timeline.push({
type: 'html-button-response',
choices: ['start'],
stimulus: ""
});
timeline.push({
type: 'audio-sequence-button-response',
choices: ['&nbsp;', '&nbsp;', '&nbsp;'],
prompt: '<p>Prompt</p>',
stimuli: ['res/eg-syllables-bi01.wav', 'res/eg-syllables-bu01.wav', 'res/eg-syllables-di01.wav'],
trial_ends_after_audio: true,
on_finish: function(){
console.log("Trial is finished.");
}
});
timeline.push({
type: 'audio-sequence-button-response',
choices: ['&nbsp;', '&nbsp;', '&nbsp;'],
prompt: '<p>Prompt</p>',
stimuli: ['res/eg-syllables-bi01.wav', 'res/eg-syllables-bu01.wav', 'res/eg-syllables-di01.wav'],
trial_ends_after_audio: true,
on_finish: function(){
console.log("Trial is finished.");
}
});
jsPsych.init({
timeline: timeline
});
});
</script>
</body>
</html>