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