@jspsych/plugin-audio-keyboard-response 1.1.2 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2,235 +2,235 @@
2
2
 
3
3
  var jspsych = require('jspsych');
4
4
 
5
- const info = {
6
- name: "audio-keyboard-response",
7
- parameters: {
8
- /** The audio file to be played. */
9
- stimulus: {
10
- type: jspsych.ParameterType.AUDIO,
11
- pretty_name: "Stimulus",
12
- default: undefined,
13
- },
14
- /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */
15
- choices: {
16
- type: jspsych.ParameterType.KEYS,
17
- pretty_name: "Choices",
18
- default: "ALL_KEYS",
19
- },
20
- /** Any content here will be displayed below the stimulus. */
21
- prompt: {
22
- type: jspsych.ParameterType.HTML_STRING,
23
- pretty_name: "Prompt",
24
- default: null,
25
- },
26
- /** The maximum duration to wait for a response. */
27
- trial_duration: {
28
- type: jspsych.ParameterType.INT,
29
- pretty_name: "Trial duration",
30
- default: null,
31
- },
32
- /** If true, the trial will end when user makes a response. */
33
- response_ends_trial: {
34
- type: jspsych.ParameterType.BOOL,
35
- pretty_name: "Response ends trial",
36
- default: true,
37
- },
38
- /** If true, then the trial will end as soon as the audio file finishes playing. */
39
- trial_ends_after_audio: {
40
- type: jspsych.ParameterType.BOOL,
41
- pretty_name: "Trial ends after audio",
42
- default: false,
43
- },
44
- /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */
45
- response_allowed_while_playing: {
46
- type: jspsych.ParameterType.BOOL,
47
- pretty_name: "Response allowed while playing",
48
- default: true,
49
- },
50
- },
51
- };
52
- /**
53
- * **audio-keyboard-response**
54
- *
55
- * jsPsych plugin for playing an audio file and getting a keyboard response
56
- *
57
- * @author Josh de Leeuw
58
- * @see {@link https://www.jspsych.org/plugins/jspsych-audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}
59
- */
60
- class AudioKeyboardResponsePlugin {
61
- constructor(jsPsych) {
62
- this.jsPsych = jsPsych;
63
- }
64
- trial(display_element, trial, on_load) {
65
- // hold the .resolve() function from the Promise that ends the trial
66
- let trial_complete;
67
- // setup stimulus
68
- var context = this.jsPsych.pluginAPI.audioContext();
69
- // store response
70
- var response = {
71
- rt: null,
72
- key: null,
73
- };
74
- // record webaudio context start time
75
- var startTime;
76
- // load audio file
77
- this.jsPsych.pluginAPI
78
- .getAudioBuffer(trial.stimulus)
79
- .then((buffer) => {
80
- if (context !== null) {
81
- this.audio = context.createBufferSource();
82
- this.audio.buffer = buffer;
83
- this.audio.connect(context.destination);
84
- }
85
- else {
86
- this.audio = buffer;
87
- this.audio.currentTime = 0;
88
- }
89
- setupTrial();
90
- })
91
- .catch((err) => {
92
- console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`);
93
- console.error(err);
94
- });
95
- const setupTrial = () => {
96
- // set up end event if trial needs it
97
- if (trial.trial_ends_after_audio) {
98
- this.audio.addEventListener("ended", end_trial);
99
- }
100
- // show prompt if there is one
101
- if (trial.prompt !== null) {
102
- display_element.innerHTML = trial.prompt;
103
- }
104
- // start audio
105
- if (context !== null) {
106
- startTime = context.currentTime;
107
- this.audio.start(startTime);
108
- }
109
- else {
110
- this.audio.play();
111
- }
112
- // start keyboard listener when trial starts or sound ends
113
- if (trial.response_allowed_while_playing) {
114
- setup_keyboard_listener();
115
- }
116
- else if (!trial.trial_ends_after_audio) {
117
- this.audio.addEventListener("ended", setup_keyboard_listener);
118
- }
119
- // end trial if time limit is set
120
- if (trial.trial_duration !== null) {
121
- this.jsPsych.pluginAPI.setTimeout(() => {
122
- end_trial();
123
- }, trial.trial_duration);
124
- }
125
- on_load();
126
- };
127
- // function to end trial when it is time
128
- const end_trial = () => {
129
- // kill any remaining setTimeout handlers
130
- this.jsPsych.pluginAPI.clearAllTimeouts();
131
- // stop the audio file if it is playing
132
- // remove end event listeners if they exist
133
- if (context !== null) {
134
- this.audio.stop();
135
- }
136
- else {
137
- this.audio.pause();
138
- }
139
- this.audio.removeEventListener("ended", end_trial);
140
- this.audio.removeEventListener("ended", setup_keyboard_listener);
141
- // kill keyboard listeners
142
- this.jsPsych.pluginAPI.cancelAllKeyboardResponses();
143
- // gather the data to store for the trial
144
- var trial_data = {
145
- rt: response.rt,
146
- stimulus: trial.stimulus,
147
- response: response.key,
148
- };
149
- // clear the display
150
- display_element.innerHTML = "";
151
- // move on to the next trial
152
- this.jsPsych.finishTrial(trial_data);
153
- trial_complete();
154
- };
155
- // function to handle responses by the subject
156
- function after_response(info) {
157
- // only record the first response
158
- if (response.key == null) {
159
- response = info;
160
- }
161
- if (trial.response_ends_trial) {
162
- end_trial();
163
- }
164
- }
165
- const setup_keyboard_listener = () => {
166
- // start the response listener
167
- if (context !== null) {
168
- this.jsPsych.pluginAPI.getKeyboardResponse({
169
- callback_function: after_response,
170
- valid_responses: trial.choices,
171
- rt_method: "audio",
172
- persist: false,
173
- allow_held_key: false,
174
- audio_context: context,
175
- audio_context_start_time: startTime,
176
- });
177
- }
178
- else {
179
- this.jsPsych.pluginAPI.getKeyboardResponse({
180
- callback_function: after_response,
181
- valid_responses: trial.choices,
182
- rt_method: "performance",
183
- persist: false,
184
- allow_held_key: false,
185
- });
186
- }
187
- };
188
- return new Promise((resolve) => {
189
- trial_complete = resolve;
190
- });
191
- }
192
- simulate(trial, simulation_mode, simulation_options, load_callback) {
193
- if (simulation_mode == "data-only") {
194
- load_callback();
195
- this.simulate_data_only(trial, simulation_options);
196
- }
197
- if (simulation_mode == "visual") {
198
- this.simulate_visual(trial, simulation_options, load_callback);
199
- }
200
- }
201
- simulate_data_only(trial, simulation_options) {
202
- const data = this.create_simulation_data(trial, simulation_options);
203
- this.jsPsych.finishTrial(data);
204
- }
205
- simulate_visual(trial, simulation_options, load_callback) {
206
- const data = this.create_simulation_data(trial, simulation_options);
207
- const display_element = this.jsPsych.getDisplayElement();
208
- const respond = () => {
209
- if (data.rt !== null) {
210
- this.jsPsych.pluginAPI.pressKey(data.response, data.rt);
211
- }
212
- };
213
- this.trial(display_element, trial, () => {
214
- load_callback();
215
- if (!trial.response_allowed_while_playing) {
216
- this.audio.addEventListener("ended", respond);
217
- }
218
- else {
219
- respond();
220
- }
221
- });
222
- }
223
- create_simulation_data(trial, simulation_options) {
224
- const default_data = {
225
- stimulus: trial.stimulus,
226
- rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
227
- response: this.jsPsych.pluginAPI.getValidKey(trial.choices),
228
- };
229
- const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
230
- this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
231
- return data;
232
- }
233
- }
5
+ const info = {
6
+ name: "audio-keyboard-response",
7
+ parameters: {
8
+ /** The audio file to be played. */
9
+ stimulus: {
10
+ type: jspsych.ParameterType.AUDIO,
11
+ pretty_name: "Stimulus",
12
+ default: undefined,
13
+ },
14
+ /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */
15
+ choices: {
16
+ type: jspsych.ParameterType.KEYS,
17
+ pretty_name: "Choices",
18
+ default: "ALL_KEYS",
19
+ },
20
+ /** Any content here will be displayed below the stimulus. */
21
+ prompt: {
22
+ type: jspsych.ParameterType.HTML_STRING,
23
+ pretty_name: "Prompt",
24
+ default: null,
25
+ },
26
+ /** The maximum duration to wait for a response. */
27
+ trial_duration: {
28
+ type: jspsych.ParameterType.INT,
29
+ pretty_name: "Trial duration",
30
+ default: null,
31
+ },
32
+ /** If true, the trial will end when user makes a response. */
33
+ response_ends_trial: {
34
+ type: jspsych.ParameterType.BOOL,
35
+ pretty_name: "Response ends trial",
36
+ default: true,
37
+ },
38
+ /** If true, then the trial will end as soon as the audio file finishes playing. */
39
+ trial_ends_after_audio: {
40
+ type: jspsych.ParameterType.BOOL,
41
+ pretty_name: "Trial ends after audio",
42
+ default: false,
43
+ },
44
+ /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */
45
+ response_allowed_while_playing: {
46
+ type: jspsych.ParameterType.BOOL,
47
+ pretty_name: "Response allowed while playing",
48
+ default: true,
49
+ },
50
+ },
51
+ };
52
+ /**
53
+ * **audio-keyboard-response**
54
+ *
55
+ * jsPsych plugin for playing an audio file and getting a keyboard response
56
+ *
57
+ * @author Josh de Leeuw
58
+ * @see {@link https://www.jspsych.org/plugins/jspsych-audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}
59
+ */
60
+ class AudioKeyboardResponsePlugin {
61
+ constructor(jsPsych) {
62
+ this.jsPsych = jsPsych;
63
+ }
64
+ trial(display_element, trial, on_load) {
65
+ // hold the .resolve() function from the Promise that ends the trial
66
+ let trial_complete;
67
+ // setup stimulus
68
+ var context = this.jsPsych.pluginAPI.audioContext();
69
+ // store response
70
+ var response = {
71
+ rt: null,
72
+ key: null,
73
+ };
74
+ // record webaudio context start time
75
+ var startTime;
76
+ // load audio file
77
+ this.jsPsych.pluginAPI
78
+ .getAudioBuffer(trial.stimulus)
79
+ .then((buffer) => {
80
+ if (context !== null) {
81
+ this.audio = context.createBufferSource();
82
+ this.audio.buffer = buffer;
83
+ this.audio.connect(context.destination);
84
+ }
85
+ else {
86
+ this.audio = buffer;
87
+ this.audio.currentTime = 0;
88
+ }
89
+ setupTrial();
90
+ })
91
+ .catch((err) => {
92
+ console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`);
93
+ console.error(err);
94
+ });
95
+ const setupTrial = () => {
96
+ // set up end event if trial needs it
97
+ if (trial.trial_ends_after_audio) {
98
+ this.audio.addEventListener("ended", end_trial);
99
+ }
100
+ // show prompt if there is one
101
+ if (trial.prompt !== null) {
102
+ display_element.innerHTML = trial.prompt;
103
+ }
104
+ // start audio
105
+ if (context !== null) {
106
+ startTime = context.currentTime;
107
+ this.audio.start(startTime);
108
+ }
109
+ else {
110
+ this.audio.play();
111
+ }
112
+ // start keyboard listener when trial starts or sound ends
113
+ if (trial.response_allowed_while_playing) {
114
+ setup_keyboard_listener();
115
+ }
116
+ else if (!trial.trial_ends_after_audio) {
117
+ this.audio.addEventListener("ended", setup_keyboard_listener);
118
+ }
119
+ // end trial if time limit is set
120
+ if (trial.trial_duration !== null) {
121
+ this.jsPsych.pluginAPI.setTimeout(() => {
122
+ end_trial();
123
+ }, trial.trial_duration);
124
+ }
125
+ on_load();
126
+ };
127
+ // function to end trial when it is time
128
+ const end_trial = () => {
129
+ // kill any remaining setTimeout handlers
130
+ this.jsPsych.pluginAPI.clearAllTimeouts();
131
+ // stop the audio file if it is playing
132
+ // remove end event listeners if they exist
133
+ if (context !== null) {
134
+ this.audio.stop();
135
+ }
136
+ else {
137
+ this.audio.pause();
138
+ }
139
+ this.audio.removeEventListener("ended", end_trial);
140
+ this.audio.removeEventListener("ended", setup_keyboard_listener);
141
+ // kill keyboard listeners
142
+ this.jsPsych.pluginAPI.cancelAllKeyboardResponses();
143
+ // gather the data to store for the trial
144
+ var trial_data = {
145
+ rt: response.rt,
146
+ stimulus: trial.stimulus,
147
+ response: response.key,
148
+ };
149
+ // clear the display
150
+ display_element.innerHTML = "";
151
+ // move on to the next trial
152
+ this.jsPsych.finishTrial(trial_data);
153
+ trial_complete();
154
+ };
155
+ // function to handle responses by the subject
156
+ function after_response(info) {
157
+ // only record the first response
158
+ if (response.key == null) {
159
+ response = info;
160
+ }
161
+ if (trial.response_ends_trial) {
162
+ end_trial();
163
+ }
164
+ }
165
+ const setup_keyboard_listener = () => {
166
+ // start the response listener
167
+ if (context !== null) {
168
+ this.jsPsych.pluginAPI.getKeyboardResponse({
169
+ callback_function: after_response,
170
+ valid_responses: trial.choices,
171
+ rt_method: "audio",
172
+ persist: false,
173
+ allow_held_key: false,
174
+ audio_context: context,
175
+ audio_context_start_time: startTime,
176
+ });
177
+ }
178
+ else {
179
+ this.jsPsych.pluginAPI.getKeyboardResponse({
180
+ callback_function: after_response,
181
+ valid_responses: trial.choices,
182
+ rt_method: "performance",
183
+ persist: false,
184
+ allow_held_key: false,
185
+ });
186
+ }
187
+ };
188
+ return new Promise((resolve) => {
189
+ trial_complete = resolve;
190
+ });
191
+ }
192
+ simulate(trial, simulation_mode, simulation_options, load_callback) {
193
+ if (simulation_mode == "data-only") {
194
+ load_callback();
195
+ this.simulate_data_only(trial, simulation_options);
196
+ }
197
+ if (simulation_mode == "visual") {
198
+ this.simulate_visual(trial, simulation_options, load_callback);
199
+ }
200
+ }
201
+ simulate_data_only(trial, simulation_options) {
202
+ const data = this.create_simulation_data(trial, simulation_options);
203
+ this.jsPsych.finishTrial(data);
204
+ }
205
+ simulate_visual(trial, simulation_options, load_callback) {
206
+ const data = this.create_simulation_data(trial, simulation_options);
207
+ const display_element = this.jsPsych.getDisplayElement();
208
+ const respond = () => {
209
+ if (data.rt !== null) {
210
+ this.jsPsych.pluginAPI.pressKey(data.response, data.rt);
211
+ }
212
+ };
213
+ this.trial(display_element, trial, () => {
214
+ load_callback();
215
+ if (!trial.response_allowed_while_playing) {
216
+ this.audio.addEventListener("ended", respond);
217
+ }
218
+ else {
219
+ respond();
220
+ }
221
+ });
222
+ }
223
+ create_simulation_data(trial, simulation_options) {
224
+ const default_data = {
225
+ stimulus: trial.stimulus,
226
+ rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
227
+ response: this.jsPsych.pluginAPI.getValidKey(trial.choices),
228
+ };
229
+ const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
230
+ this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
231
+ return data;
232
+ }
233
+ }
234
234
  AudioKeyboardResponsePlugin.info = info;
235
235
 
236
236
  module.exports = AudioKeyboardResponsePlugin;