@jspsych/plugin-audio-keyboard-response 2.0.0 → 2.1.1
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.browser.js +45 -53
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.min.js +2 -2
- package/dist/index.browser.min.js.map +1 -1
- package/dist/index.cjs +43 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +43 -46
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +5 -3
package/dist/index.browser.js
CHANGED
|
@@ -49,109 +49,101 @@ var jsPsychAudioKeyboardResponse = (function (jspsych) {
|
|
|
49
49
|
|
|
50
50
|
var autoBind$1 = /*@__PURE__*/getDefaultExportFromCjs(autoBind);
|
|
51
51
|
|
|
52
|
-
var
|
|
53
|
-
name: "@jspsych/plugin-audio-keyboard-response",
|
|
54
|
-
version: "2.0.0",
|
|
55
|
-
description: "jsPsych plugin for playing an audio file and getting a keyboard response",
|
|
56
|
-
type: "module",
|
|
57
|
-
main: "dist/index.cjs",
|
|
58
|
-
exports: {
|
|
59
|
-
import: "./dist/index.js",
|
|
60
|
-
require: "./dist/index.cjs"
|
|
61
|
-
},
|
|
62
|
-
typings: "dist/index.d.ts",
|
|
63
|
-
unpkg: "dist/index.browser.min.js",
|
|
64
|
-
files: [
|
|
65
|
-
"src",
|
|
66
|
-
"dist"
|
|
67
|
-
],
|
|
68
|
-
source: "src/index.ts",
|
|
69
|
-
scripts: {
|
|
70
|
-
test: "jest",
|
|
71
|
-
"test:watch": "npm test -- --watch",
|
|
72
|
-
tsc: "tsc",
|
|
73
|
-
build: "rollup --config",
|
|
74
|
-
"build:watch": "npm run build -- --watch"
|
|
75
|
-
},
|
|
76
|
-
repository: {
|
|
77
|
-
type: "git",
|
|
78
|
-
url: "git+https://github.com/jspsych/jsPsych.git",
|
|
79
|
-
directory: "packages/plugin-audio-keyboard-response"
|
|
80
|
-
},
|
|
81
|
-
author: "Josh de Leeuw",
|
|
82
|
-
license: "MIT",
|
|
83
|
-
bugs: {
|
|
84
|
-
url: "https://github.com/jspsych/jsPsych/issues"
|
|
85
|
-
},
|
|
86
|
-
homepage: "https://www.jspsych.org/latest/plugins/audio-keyboard-response",
|
|
87
|
-
peerDependencies: {
|
|
88
|
-
jspsych: ">=7.1.0"
|
|
89
|
-
},
|
|
90
|
-
devDependencies: {
|
|
91
|
-
"@jspsych/config": "^3.0.0",
|
|
92
|
-
"@jspsych/test-utils": "^1.2.0"
|
|
93
|
-
}
|
|
94
|
-
};
|
|
52
|
+
var version = "2.1.1";
|
|
95
53
|
|
|
96
54
|
const info = {
|
|
97
55
|
name: "audio-keyboard-response",
|
|
98
|
-
version
|
|
56
|
+
version,
|
|
99
57
|
parameters: {
|
|
58
|
+
/** The audio file to be played. */
|
|
100
59
|
stimulus: {
|
|
101
60
|
type: jspsych.ParameterType.AUDIO,
|
|
102
61
|
default: void 0
|
|
103
62
|
},
|
|
63
|
+
/** This array contains the key(s) that the participant is allowed to press in order to respond to the stimulus.
|
|
64
|
+
* Keys should be specified as characters (e.g., `'a'`, `'q'`, `' '`, `'Enter'`, `'ArrowDown'`) -
|
|
65
|
+
* see [this page](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values)
|
|
66
|
+
* and [this page (event.key column)](https://www.freecodecamp.org/news/javascript-keycode-list-keypress-event-key-codes/)
|
|
67
|
+
* for more examples. Any key presses that are not listed in the array will be ignored. The default value of `"ALL_KEYS"`
|
|
68
|
+
* means that all keys will be accepted as valid responses. Specifying `"NO_KEYS"` will mean that no responses are allowed.
|
|
69
|
+
*/
|
|
104
70
|
choices: {
|
|
105
71
|
type: jspsych.ParameterType.KEYS,
|
|
106
72
|
default: "ALL_KEYS"
|
|
107
73
|
},
|
|
74
|
+
/** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that
|
|
75
|
+
* it can be used to provide a reminder about the action the participant is supposed to take (e.g., which key to press).
|
|
76
|
+
*/
|
|
108
77
|
prompt: {
|
|
109
78
|
type: jspsych.ParameterType.HTML_STRING,
|
|
110
79
|
pretty_name: "Prompt",
|
|
111
80
|
default: null
|
|
112
81
|
},
|
|
82
|
+
/** How long to wait for the participant to make a response before ending the trial in milliseconds. If the
|
|
83
|
+
* participant fails to make a response before this timer is reached, the participant's response will be
|
|
84
|
+
* recorded as null for the trial and the trial will end. If the value of this parameter is null, then the
|
|
85
|
+
* trial will wait for a response indefinitely.
|
|
86
|
+
*/
|
|
113
87
|
trial_duration: {
|
|
114
88
|
type: jspsych.ParameterType.INT,
|
|
115
89
|
default: null
|
|
116
90
|
},
|
|
91
|
+
/** If true, then the trial will end whenever the participant makes a response (assuming they make their
|
|
92
|
+
* response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will
|
|
93
|
+
* continue until the value for `trial_duration` is reached. You can use set this parameter to `false` to
|
|
94
|
+
* force the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete
|
|
95
|
+
*/
|
|
117
96
|
response_ends_trial: {
|
|
118
97
|
type: jspsych.ParameterType.BOOL,
|
|
119
98
|
default: true
|
|
120
99
|
},
|
|
100
|
+
/** If true, then the trial will end as soon as the audio file finishes playing. */
|
|
121
101
|
trial_ends_after_audio: {
|
|
122
102
|
type: jspsych.ParameterType.BOOL,
|
|
123
103
|
pretty_name: "Trial ends after audio",
|
|
124
104
|
default: false
|
|
125
105
|
},
|
|
106
|
+
/** If true, then responses are allowed while the audio is playing. If false, then the audio must finish
|
|
107
|
+
* playing before a keyboard response is accepted. Once the audio has played all the way through, a valid
|
|
108
|
+
* keyboard response is allowed (including while the audio is being re-played via on-screen playback controls).
|
|
109
|
+
*/
|
|
126
110
|
response_allowed_while_playing: {
|
|
127
111
|
type: jspsych.ParameterType.BOOL,
|
|
128
112
|
default: true
|
|
129
113
|
}
|
|
130
114
|
},
|
|
131
115
|
data: {
|
|
116
|
+
/** Indicates which key the participant pressed. If no key was pressed before the trial ended, then the value will be `null`. */
|
|
132
117
|
response: {
|
|
133
118
|
type: jspsych.ParameterType.STRING
|
|
134
119
|
},
|
|
120
|
+
/** The response time in milliseconds for the participant to make a response. The time is measured from when the stimulus
|
|
121
|
+
* first began playing until the participant made a key response. If no key was pressed before the trial ended, then the
|
|
122
|
+
* value will be `null`.
|
|
123
|
+
*/
|
|
135
124
|
rt: {
|
|
136
125
|
type: jspsych.ParameterType.INT
|
|
137
126
|
},
|
|
127
|
+
/** Path to the audio file that played during the trial. */
|
|
138
128
|
stimulus: {
|
|
139
129
|
type: jspsych.ParameterType.STRING
|
|
140
130
|
}
|
|
131
|
+
},
|
|
132
|
+
// prettier-ignore
|
|
133
|
+
citations: {
|
|
134
|
+
"apa": "de Leeuw, J. R., Gilbert, R. A., & Luchterhandt, B. (2023). jsPsych: Enabling an Open-Source Collaborative Ecosystem of Behavioral Experiments. Journal of Open Source Software, 8(85), 5351. https://doi.org/10.21105/joss.05351 ",
|
|
135
|
+
"bibtex": '@article{Leeuw2023jsPsych, author = {de Leeuw, Joshua R. and Gilbert, Rebecca A. and Luchterhandt, Bj{\\" o}rn}, journal = {Journal of Open Source Software}, doi = {10.21105/joss.05351}, issn = {2475-9066}, number = {85}, year = {2023}, month = {may 11}, pages = {5351}, publisher = {Open Journals}, title = {jsPsych: Enabling an {Open}-{Source} {Collaborative} {Ecosystem} of {Behavioral} {Experiments}}, url = {https://joss.theoj.org/papers/10.21105/joss.05351}, volume = {8}, } '
|
|
141
136
|
}
|
|
142
137
|
};
|
|
143
138
|
class AudioKeyboardResponsePlugin {
|
|
144
139
|
constructor(jsPsych) {
|
|
145
140
|
this.jsPsych = jsPsych;
|
|
141
|
+
this.response = { rt: null, key: null };
|
|
146
142
|
autoBind$1(this);
|
|
147
143
|
}
|
|
148
|
-
static
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
display;
|
|
152
|
-
response = { rt: null, key: null };
|
|
153
|
-
startTime;
|
|
154
|
-
finish;
|
|
144
|
+
static {
|
|
145
|
+
this.info = info;
|
|
146
|
+
}
|
|
155
147
|
trial(display_element, trial, on_load) {
|
|
156
148
|
return new Promise(async (resolve) => {
|
|
157
149
|
this.finish = resolve;
|
|
@@ -181,9 +173,9 @@ var jsPsychAudioKeyboardResponse = (function (jspsych) {
|
|
|
181
173
|
}
|
|
182
174
|
end_trial() {
|
|
183
175
|
this.jsPsych.pluginAPI.clearAllTimeouts();
|
|
184
|
-
this.audio.stop();
|
|
185
176
|
this.audio.removeEventListener("ended", this.end_trial);
|
|
186
177
|
this.audio.removeEventListener("ended", this.setup_keyboard_listener);
|
|
178
|
+
this.audio.stop();
|
|
187
179
|
this.jsPsych.pluginAPI.cancelAllKeyboardResponses();
|
|
188
180
|
var trial_data = {
|
|
189
181
|
rt: this.response.rt,
|
|
@@ -266,4 +258,4 @@ var jsPsychAudioKeyboardResponse = (function (jspsych) {
|
|
|
266
258
|
return AudioKeyboardResponsePlugin;
|
|
267
259
|
|
|
268
260
|
})(jsPsychModule);
|
|
269
|
-
//# sourceMappingURL=https://unpkg.com/@jspsych/plugin-audio-keyboard-response@2.
|
|
261
|
+
//# sourceMappingURL=https://unpkg.com/@jspsych/plugin-audio-keyboard-response@2.1.1/dist/index.browser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.browser.js","sources":["../../../node_modules/auto-bind/index.js","../src/index.ts"],"sourcesContent":["'use strict';\n\n// Gets all non-builtin properties up the prototype chain\nconst getAllProperties = object => {\n\tconst properties = new Set();\n\n\tdo {\n\t\tfor (const key of Reflect.ownKeys(object)) {\n\t\t\tproperties.add([object, key]);\n\t\t}\n\t} while ((object = Reflect.getPrototypeOf(object)) && object !== Object.prototype);\n\n\treturn properties;\n};\n\nmodule.exports = (self, {include, exclude} = {}) => {\n\tconst filter = key => {\n\t\tconst match = pattern => typeof pattern === 'string' ? key === pattern : pattern.test(key);\n\n\t\tif (include) {\n\t\t\treturn include.some(match);\n\t\t}\n\n\t\tif (exclude) {\n\t\t\treturn !exclude.some(match);\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tfor (const [object, key] of getAllProperties(self.constructor.prototype)) {\n\t\tif (key === 'constructor' || !filter(key)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst descriptor = Reflect.getOwnPropertyDescriptor(object, key);\n\t\tif (descriptor && typeof descriptor.value === 'function') {\n\t\t\tself[key] = self[key].bind(self);\n\t\t}\n\t}\n\n\treturn self;\n};\n","import autoBind from \"auto-bind\";\nimport { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { AudioPlayerInterface } from \"../../jspsych/src/modules/plugin-api/AudioPlayer\";\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"audio-keyboard-response\",\n version: version,\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n default: undefined,\n },\n /** This array contains the key(s) that the participant is allowed to press in order to respond to the stimulus.\n * Keys should be specified as characters (e.g., `'a'`, `'q'`, `' '`, `'Enter'`, `'ArrowDown'`) -\n * see [this page](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values)\n * and [this page (event.key column)](https://www.freecodecamp.org/news/javascript-keycode-list-keypress-event-key-codes/)\n * for more examples. Any key presses that are not listed in the array will be ignored. The default value of `\"ALL_KEYS\"`\n * means that all keys will be accepted as valid responses. Specifying `\"NO_KEYS\"` will mean that no responses are allowed.\n */\n choices: {\n type: ParameterType.KEYS,\n default: \"ALL_KEYS\",\n },\n /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that\n * it can be used to provide a reminder about the action the participant is supposed to take (e.g., which key to press).\n */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to wait for the participant to make a response before ending the trial in milliseconds. If the\n * participant fails to make a response before this timer is reached, the participant's response will be\n * recorded as null for the trial and the trial will end. If the value of this parameter is null, then the\n * trial will wait for a response indefinitely.\n */\n trial_duration: {\n type: ParameterType.INT,\n default: null,\n },\n /** If true, then the trial will end whenever the participant makes a response (assuming they make their\n * response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will\n * continue until the value for `trial_duration` is reached. You can use set this parameter to `false` to\n * force the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete\n */\n response_ends_trial: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n pretty_name: \"Trial ends after audio\",\n default: false,\n },\n /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish\n * playing before a keyboard response is accepted. Once the audio has played all the way through, a valid\n * keyboard response is allowed (including while the audio is being re-played via on-screen playback controls).\n */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n default: true,\n },\n },\n data: {\n /** Indicates which key the participant pressed. If no key was pressed before the trial ended, then the value will be `null`. */\n response: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from when the stimulus\n * first began playing until the participant made a key response. If no key was pressed before the trial ended, then the\n * value will be `null`.\n */\n rt: {\n type: ParameterType.INT,\n },\n /** Path to the audio file that played during the trial. */\n stimulus: {\n type: ParameterType.STRING,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * This plugin plays audio files and records responses generated with the keyboard.\n *\n * If the browser supports it, audio files are played using the WebAudio API. This allows for reasonably precise timing of the\n * playback. The timing of responses generated is measured against the WebAudio specific clock, improving the measurement of\n * response times. If the browser does not support the WebAudio API, then the audio file is played with HTML5 audio.\n *\n * Audio files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if you are using\n * timeline variables or another dynamic method to specify the audio stimulus, then you will need to [manually preload](../overview/media-preloading.md#manual-preloading) the audio.\n *\n * The trial can end when the participant responds, when the audio file has finished playing, or if the participant has\n * failed to respond within a fixed length of time. You can also prevent a keyboard response from being recorded before\n * the audio has finished playing.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}\n */\nclass AudioKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio: AudioPlayerInterface;\n private params: TrialType<Info>;\n private display: HTMLElement;\n private response: { rt: number; key: string } = { rt: null, key: null };\n private startTime: number;\n private finish: ({}: { rt: number; response: string; stimulus: string }) => void;\n\n constructor(private jsPsych: JsPsych) {\n autoBind(this);\n }\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n return new Promise(async (resolve) => {\n this.finish = resolve;\n this.params = trial;\n this.display = display_element;\n // load audio file\n this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);\n\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.end_trial);\n }\n\n // show prompt if there is one\n if (trial.prompt !== null) {\n display_element.innerHTML = trial.prompt;\n }\n\n // start playing audio here to record time\n // use this for offsetting RT measurement in\n // setup_keyboard_listener\n this.startTime = this.jsPsych.pluginAPI.audioContext()?.currentTime;\n\n // start keyboard listener when trial starts or sound ends\n if (trial.response_allowed_while_playing) {\n this.setup_keyboard_listener();\n } else if (!trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.setup_keyboard_listener);\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n this.end_trial();\n }, trial.trial_duration);\n }\n\n // call trial on_load method because we are done with all loading setup\n on_load();\n\n this.audio.play();\n });\n }\n\n private end_trial() {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // stop the audio file if it is playing\n this.audio.stop();\n\n // remove end event listeners if they exist\n this.audio.removeEventListener(\"ended\", this.end_trial);\n this.audio.removeEventListener(\"ended\", this.setup_keyboard_listener);\n\n // kill keyboard listeners\n this.jsPsych.pluginAPI.cancelAllKeyboardResponses();\n\n // gather the data to store for the trial\n var trial_data = {\n rt: this.response.rt,\n response: this.response.key,\n stimulus: this.params.stimulus,\n };\n\n // clear the display\n this.display.innerHTML = \"\";\n\n // move on to the next trial\n this.finish(trial_data);\n }\n\n private after_response(info: { key: string; rt: number }) {\n this.response = info;\n if (this.params.response_ends_trial) {\n this.end_trial();\n }\n }\n\n private setup_keyboard_listener() {\n // start the response listener\n if (this.jsPsych.pluginAPI.useWebaudio) {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: this.after_response,\n valid_responses: this.params.choices,\n rt_method: \"audio\",\n persist: false,\n allow_held_key: false,\n audio_context: this.jsPsych.pluginAPI.audioContext(),\n audio_context_start_time: this.startTime,\n });\n } else {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: this.after_response,\n valid_responses: this.params.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n }\n\n async simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n return this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n return this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n return data;\n }\n\n private async simulate_visual(\n trial: TrialType<Info>,\n simulation_options,\n load_callback: () => void\n ) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n const display_element = this.jsPsych.getDisplayElement();\n\n const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.pressKey(data.response, data.rt);\n }\n };\n\n const result = await this.trial(display_element, trial, () => {\n load_callback();\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n\n return result;\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\n response: this.jsPsych.pluginAPI.getValidKey(trial.choices),\n };\n\n const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n return data;\n }\n}\n\nexport default AudioKeyboardResponsePlugin;\n"],"names":["version","ParameterType","autoBind","info"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.browser.js","sources":["../../../node_modules/auto-bind/index.js","../package.json","../src/index.ts"],"sourcesContent":["'use strict';\n\n// Gets all non-builtin properties up the prototype chain\nconst getAllProperties = object => {\n\tconst properties = new Set();\n\n\tdo {\n\t\tfor (const key of Reflect.ownKeys(object)) {\n\t\t\tproperties.add([object, key]);\n\t\t}\n\t} while ((object = Reflect.getPrototypeOf(object)) && object !== Object.prototype);\n\n\treturn properties;\n};\n\nmodule.exports = (self, {include, exclude} = {}) => {\n\tconst filter = key => {\n\t\tconst match = pattern => typeof pattern === 'string' ? key === pattern : pattern.test(key);\n\n\t\tif (include) {\n\t\t\treturn include.some(match);\n\t\t}\n\n\t\tif (exclude) {\n\t\t\treturn !exclude.some(match);\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tfor (const [object, key] of getAllProperties(self.constructor.prototype)) {\n\t\tif (key === 'constructor' || !filter(key)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst descriptor = Reflect.getOwnPropertyDescriptor(object, key);\n\t\tif (descriptor && typeof descriptor.value === 'function') {\n\t\t\tself[key] = self[key].bind(self);\n\t\t}\n\t}\n\n\treturn self;\n};\n","{\n \"name\": \"@jspsych/plugin-audio-keyboard-response\",\n \"version\": \"2.1.1\",\n \"description\": \"jsPsych plugin for playing an audio file and getting a keyboard response\",\n \"type\": \"module\",\n \"main\": \"dist/index.cjs\",\n \"exports\": {\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n },\n \"typings\": \"dist/index.d.ts\",\n \"unpkg\": \"dist/index.browser.min.js\",\n \"files\": [\n \"src\",\n \"dist\"\n ],\n \"source\": \"src/index.ts\",\n \"scripts\": {\n \"test\": \"jest\",\n \"test:watch\": \"npm test -- --watch\",\n \"tsc\": \"tsc\",\n \"build\": \"rollup --config\",\n \"build:watch\": \"npm run build -- --watch\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/jspsych/jsPsych.git\",\n \"directory\": \"packages/plugin-audio-keyboard-response\"\n },\n \"author\": \"Josh de Leeuw\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/jspsych/jsPsych/issues\"\n },\n \"homepage\": \"https://www.jspsych.org/latest/plugins/audio-keyboard-response\",\n \"peerDependencies\": {\n \"jspsych\": \">=7.1.0\"\n },\n \"devDependencies\": {\n \"@jspsych/config\": \"^3.2.0\",\n \"@jspsych/test-utils\": \"^1.2.0\"\n }\n}\n","import autoBind from \"auto-bind\";\nimport { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { AudioPlayerInterface } from \"../../jspsych/src/modules/plugin-api/AudioPlayer\";\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"audio-keyboard-response\",\n version: version,\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n default: undefined,\n },\n /** This array contains the key(s) that the participant is allowed to press in order to respond to the stimulus.\n * Keys should be specified as characters (e.g., `'a'`, `'q'`, `' '`, `'Enter'`, `'ArrowDown'`) -\n * see [this page](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values)\n * and [this page (event.key column)](https://www.freecodecamp.org/news/javascript-keycode-list-keypress-event-key-codes/)\n * for more examples. Any key presses that are not listed in the array will be ignored. The default value of `\"ALL_KEYS\"`\n * means that all keys will be accepted as valid responses. Specifying `\"NO_KEYS\"` will mean that no responses are allowed.\n */\n choices: {\n type: ParameterType.KEYS,\n default: \"ALL_KEYS\",\n },\n /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that\n * it can be used to provide a reminder about the action the participant is supposed to take (e.g., which key to press).\n */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to wait for the participant to make a response before ending the trial in milliseconds. If the\n * participant fails to make a response before this timer is reached, the participant's response will be\n * recorded as null for the trial and the trial will end. If the value of this parameter is null, then the\n * trial will wait for a response indefinitely.\n */\n trial_duration: {\n type: ParameterType.INT,\n default: null,\n },\n /** If true, then the trial will end whenever the participant makes a response (assuming they make their\n * response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will\n * continue until the value for `trial_duration` is reached. You can use set this parameter to `false` to\n * force the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete\n */\n response_ends_trial: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n pretty_name: \"Trial ends after audio\",\n default: false,\n },\n /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish\n * playing before a keyboard response is accepted. Once the audio has played all the way through, a valid\n * keyboard response is allowed (including while the audio is being re-played via on-screen playback controls).\n */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n default: true,\n },\n },\n data: {\n /** Indicates which key the participant pressed. If no key was pressed before the trial ended, then the value will be `null`. */\n response: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from when the stimulus\n * first began playing until the participant made a key response. If no key was pressed before the trial ended, then the\n * value will be `null`.\n */\n rt: {\n type: ParameterType.INT,\n },\n /** Path to the audio file that played during the trial. */\n stimulus: {\n type: ParameterType.STRING,\n },\n },\n // prettier-ignore\n citations: '__CITATIONS__',\n};\n\ntype Info = typeof info;\n\n/**\n * This plugin plays audio files and records responses generated with the keyboard.\n *\n * If the browser supports it, audio files are played using the WebAudio API. This allows for reasonably precise timing of the\n * playback. The timing of responses generated is measured against the WebAudio specific clock, improving the measurement of\n * response times. If the browser does not support the WebAudio API, then the audio file is played with HTML5 audio.\n *\n * Audio files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if you are using\n * timeline variables or another dynamic method to specify the audio stimulus, then you will need to [manually preload](../overview/media-preloading.md#manual-preloading) the audio.\n *\n * The trial can end when the participant responds, when the audio file has finished playing, or if the participant has\n * failed to respond within a fixed length of time. You can also prevent a keyboard response from being recorded before\n * the audio has finished playing.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}\n */\nclass AudioKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio: AudioPlayerInterface;\n private params: TrialType<Info>;\n private display: HTMLElement;\n private response: { rt: number; key: string } = { rt: null, key: null };\n private startTime: number;\n private finish: ({}: { rt: number; response: string; stimulus: string }) => void;\n\n constructor(private jsPsych: JsPsych) {\n autoBind(this);\n }\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n return new Promise(async (resolve) => {\n this.finish = resolve;\n this.params = trial;\n this.display = display_element;\n // load audio file\n this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);\n\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.end_trial);\n }\n\n // show prompt if there is one\n if (trial.prompt !== null) {\n display_element.innerHTML = trial.prompt;\n }\n\n // start playing audio here to record time\n // use this for offsetting RT measurement in\n // setup_keyboard_listener\n this.startTime = this.jsPsych.pluginAPI.audioContext()?.currentTime;\n\n // start keyboard listener when trial starts or sound ends\n if (trial.response_allowed_while_playing) {\n this.setup_keyboard_listener();\n } else if (!trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.setup_keyboard_listener);\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n this.end_trial();\n }, trial.trial_duration);\n }\n\n // call trial on_load method because we are done with all loading setup\n on_load();\n\n this.audio.play();\n });\n }\n\n private end_trial() {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // remove end event listeners if they exist\n this.audio.removeEventListener(\"ended\", this.end_trial);\n this.audio.removeEventListener(\"ended\", this.setup_keyboard_listener);\n\n // stop the audio file if it is playing\n this.audio.stop();\n\n // kill keyboard listeners\n this.jsPsych.pluginAPI.cancelAllKeyboardResponses();\n\n // gather the data to store for the trial\n var trial_data = {\n rt: this.response.rt,\n response: this.response.key,\n stimulus: this.params.stimulus,\n };\n\n // clear the display\n this.display.innerHTML = \"\";\n\n // move on to the next trial\n this.finish(trial_data);\n }\n\n private after_response(info: { key: string; rt: number }) {\n this.response = info;\n if (this.params.response_ends_trial) {\n this.end_trial();\n }\n }\n\n private setup_keyboard_listener() {\n // start the response listener\n if (this.jsPsych.pluginAPI.useWebaudio) {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: this.after_response,\n valid_responses: this.params.choices,\n rt_method: \"audio\",\n persist: false,\n allow_held_key: false,\n audio_context: this.jsPsych.pluginAPI.audioContext(),\n audio_context_start_time: this.startTime,\n });\n } else {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: this.after_response,\n valid_responses: this.params.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n }\n\n async simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n return this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n return this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n return data;\n }\n\n private async simulate_visual(\n trial: TrialType<Info>,\n simulation_options,\n load_callback: () => void\n ) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n const display_element = this.jsPsych.getDisplayElement();\n\n const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.pressKey(data.response, data.rt);\n }\n };\n\n const result = await this.trial(display_element, trial, () => {\n load_callback();\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n\n return result;\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\n response: this.jsPsych.pluginAPI.getValidKey(trial.choices),\n };\n\n const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n return data;\n }\n}\n\nexport default AudioKeyboardResponsePlugin;\n"],"names":[],"mappings":";;;;;;;CAEA;CACA,MAAM,gBAAgB,GAAG,MAAM,IAAI;CACnC,CAAC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAE,CAAA;;CAE7B,CAAC,GAAG;CACJ,EAAE,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;CAC7C,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAA;CAChC,GAAA;CACA,EAAE,QAAQ,CAAC,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,MAAM,KAAK,MAAM,CAAC,SAAS,EAAA;;CAElF,CAAC,OAAO,UAAU,CAAA;CAClB,CAAC,CAAA;;KAED,QAAc,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK;CACpD,CAAC,MAAM,MAAM,GAAG,GAAG,IAAI;CACvB,EAAE,MAAM,KAAK,GAAG,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,GAAG,GAAG,KAAK,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;;CAE5F,EAAE,IAAI,OAAO,EAAE;CACf,GAAG,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;CAC7B,GAAA;;CAEA,EAAE,IAAI,OAAO,EAAE;CACf,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;CAC9B,GAAA;;CAEA,EAAE,OAAO,IAAI,CAAA;CACb,EAAE,CAAA;;CAEF,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE;CAC3E,EAAE,IAAI,GAAG,KAAK,aAAa,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;CAC7C,GAAG,SAAA;CACH,GAAA;;CAEA,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC,wBAAwB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAClE,EAAE,IAAI,UAAU,IAAI,OAAO,UAAU,CAAC,KAAK,KAAK,UAAU,EAAE;CAC5D,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;CACnC,GAAA;CACA,EAAA;;CAEA,CAAC,OAAO,IAAI,CAAA;CACZ,CAAC,CAAA;;;;CCxCC,IAAW,OAAA,GAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GCmFA,SAAA,EAAA;CAAA;;IAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","x_google_ignoreList":[0]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var jsPsychAudioKeyboardResponse=function(o){"use strict";function c(s){return s&&s.__esModule&&Object.prototype.hasOwnProperty.call(s,"default")?s.default:s}const y=s=>{const e=new Set;do for(const t of Reflect.ownKeys(s))e.add([s,t]);while((s=Reflect.getPrototypeOf(s))&&s!==Object.prototype);return e};var _=(s,{include:e,exclude:t}={})=>{const
|
|
2
|
-
//# sourceMappingURL=https://unpkg.com/@jspsych/plugin-audio-keyboard-response@2.
|
|
1
|
+
var jsPsychAudioKeyboardResponse=function(o){"use strict";function c(s){return s&&s.__esModule&&Object.prototype.hasOwnProperty.call(s,"default")?s.default:s}const y=s=>{const e=new Set;do for(const t of Reflect.ownKeys(s))e.add([s,t]);while((s=Reflect.getPrototypeOf(s))&&s!==Object.prototype);return e};var _=(s,{include:e,exclude:t}={})=>{const a=i=>{const r=n=>typeof n=="string"?i===n:n.test(i);return e?e.some(r):t?!t.some(r):!0};for(const[i,r]of y(s.constructor.prototype)){if(r==="constructor"||!a(r))continue;const n=Reflect.getOwnPropertyDescriptor(i,r);n&&typeof n.value=="function"&&(s[r]=s[r].bind(s))}return s},m=c(_),P="2.1.1",u=(s,e,t)=>new Promise((a,i)=>{var r=l=>{try{p(t.next(l))}catch(d){i(d)}},n=l=>{try{p(t.throw(l))}catch(d){i(d)}},p=l=>l.done?a(l.value):Promise.resolve(l.value).then(r,n);p((t=t.apply(s,e)).next())});const f={name:"audio-keyboard-response",version:P,parameters:{stimulus:{type:o.ParameterType.AUDIO,default:void 0},choices:{type:o.ParameterType.KEYS,default:"ALL_KEYS"},prompt:{type:o.ParameterType.HTML_STRING,pretty_name:"Prompt",default:null},trial_duration:{type:o.ParameterType.INT,default:null},response_ends_trial:{type:o.ParameterType.BOOL,default:!0},trial_ends_after_audio:{type:o.ParameterType.BOOL,pretty_name:"Trial ends after audio",default:!1},response_allowed_while_playing:{type:o.ParameterType.BOOL,default:!0}},data:{response:{type:o.ParameterType.STRING},rt:{type:o.ParameterType.INT},stimulus:{type:o.ParameterType.STRING}},citations:{apa:"de Leeuw, J. R., Gilbert, R. A., & Luchterhandt, B. (2023). jsPsych: Enabling an Open-Source Collaborative Ecosystem of Behavioral Experiments. Journal of Open Source Software, 8(85), 5351. https://doi.org/10.21105/joss.05351 ",bibtex:'@article{Leeuw2023jsPsych, author = {de Leeuw, Joshua R. and Gilbert, Rebecca A. and Luchterhandt, Bj{\\" o}rn}, journal = {Journal of Open Source Software}, doi = {10.21105/joss.05351}, issn = {2475-9066}, number = {85}, year = {2023}, month = {may 11}, pages = {5351}, publisher = {Open Journals}, title = {jsPsych: Enabling an {Open}-{Source} {Collaborative} {Ecosystem} of {Behavioral} {Experiments}}, url = {https://joss.theoj.org/papers/10.21105/joss.05351}, volume = {8}, } '}};class h{constructor(e){this.jsPsych=e,this.response={rt:null,key:null},m(this)}trial(e,t,a){return new Promise(i=>u(this,null,function*(){var r;this.finish=i,this.params=t,this.display=e,this.audio=yield this.jsPsych.pluginAPI.getAudioPlayer(t.stimulus),t.trial_ends_after_audio&&this.audio.addEventListener("ended",this.end_trial),t.prompt!==null&&(e.innerHTML=t.prompt),this.startTime=(r=this.jsPsych.pluginAPI.audioContext())==null?void 0:r.currentTime,t.response_allowed_while_playing?this.setup_keyboard_listener():t.trial_ends_after_audio||this.audio.addEventListener("ended",this.setup_keyboard_listener),t.trial_duration!==null&&this.jsPsych.pluginAPI.setTimeout(()=>{this.end_trial()},t.trial_duration),a(),this.audio.play()}))}end_trial(){this.jsPsych.pluginAPI.clearAllTimeouts(),this.audio.removeEventListener("ended",this.end_trial),this.audio.removeEventListener("ended",this.setup_keyboard_listener),this.audio.stop(),this.jsPsych.pluginAPI.cancelAllKeyboardResponses();var e={rt:this.response.rt,response:this.response.key,stimulus:this.params.stimulus};this.display.innerHTML="",this.finish(e)}after_response(e){this.response=e,this.params.response_ends_trial&&this.end_trial()}setup_keyboard_listener(){this.jsPsych.pluginAPI.useWebaudio?this.jsPsych.pluginAPI.getKeyboardResponse({callback_function:this.after_response,valid_responses:this.params.choices,rt_method:"audio",persist:!1,allow_held_key:!1,audio_context:this.jsPsych.pluginAPI.audioContext(),audio_context_start_time:this.startTime}):this.jsPsych.pluginAPI.getKeyboardResponse({callback_function:this.after_response,valid_responses:this.params.choices,rt_method:"performance",persist:!1,allow_held_key:!1})}simulate(e,t,a,i){return u(this,null,function*(){if(t=="data-only")return i(),this.simulate_data_only(e,a);if(t=="visual")return this.simulate_visual(e,a,i)})}simulate_data_only(e,t){return this.create_simulation_data(e,t)}simulate_visual(e,t,a){return u(this,null,function*(){const i=this.create_simulation_data(e,t),r=this.jsPsych.getDisplayElement(),n=()=>{i.rt!==null&&this.jsPsych.pluginAPI.pressKey(i.response,i.rt)};return yield this.trial(r,e,()=>{a(),e.response_allowed_while_playing?n():this.audio.addEventListener("ended",n)})})}create_simulation_data(e,t){const a={stimulus:e.stimulus,rt:this.jsPsych.randomization.sampleExGaussian(500,50,.006666666666666667,!0),response:this.jsPsych.pluginAPI.getValidKey(e.choices)},i=this.jsPsych.pluginAPI.mergeSimulationData(a,t);return this.jsPsych.pluginAPI.ensureSimulationDataConsistency(e,i),i}}return h.info=f,h}(jsPsychModule);
|
|
2
|
+
//# sourceMappingURL=https://unpkg.com/@jspsych/plugin-audio-keyboard-response@2.1.1/dist/index.browser.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.browser.min.js","sources":["../../../node_modules/auto-bind/index.js","../src/index.ts"],"sourcesContent":["'use strict';\n\n// Gets all non-builtin properties up the prototype chain\nconst getAllProperties = object => {\n\tconst properties = new Set();\n\n\tdo {\n\t\tfor (const key of Reflect.ownKeys(object)) {\n\t\t\tproperties.add([object, key]);\n\t\t}\n\t} while ((object = Reflect.getPrototypeOf(object)) && object !== Object.prototype);\n\n\treturn properties;\n};\n\nmodule.exports = (self, {include, exclude} = {}) => {\n\tconst filter = key => {\n\t\tconst match = pattern => typeof pattern === 'string' ? key === pattern : pattern.test(key);\n\n\t\tif (include) {\n\t\t\treturn include.some(match);\n\t\t}\n\n\t\tif (exclude) {\n\t\t\treturn !exclude.some(match);\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tfor (const [object, key] of getAllProperties(self.constructor.prototype)) {\n\t\tif (key === 'constructor' || !filter(key)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst descriptor = Reflect.getOwnPropertyDescriptor(object, key);\n\t\tif (descriptor && typeof descriptor.value === 'function') {\n\t\t\tself[key] = self[key].bind(self);\n\t\t}\n\t}\n\n\treturn self;\n};\n","import autoBind from \"auto-bind\";\nimport { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { AudioPlayerInterface } from \"../../jspsych/src/modules/plugin-api/AudioPlayer\";\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"audio-keyboard-response\",\n version: version,\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n default: undefined,\n },\n /** This array contains the key(s) that the participant is allowed to press in order to respond to the stimulus.\n * Keys should be specified as characters (e.g., `'a'`, `'q'`, `' '`, `'Enter'`, `'ArrowDown'`) -\n * see [this page](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values)\n * and [this page (event.key column)](https://www.freecodecamp.org/news/javascript-keycode-list-keypress-event-key-codes/)\n * for more examples. Any key presses that are not listed in the array will be ignored. The default value of `\"ALL_KEYS\"`\n * means that all keys will be accepted as valid responses. Specifying `\"NO_KEYS\"` will mean that no responses are allowed.\n */\n choices: {\n type: ParameterType.KEYS,\n default: \"ALL_KEYS\",\n },\n /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that\n * it can be used to provide a reminder about the action the participant is supposed to take (e.g., which key to press).\n */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to wait for the participant to make a response before ending the trial in milliseconds. If the\n * participant fails to make a response before this timer is reached, the participant's response will be\n * recorded as null for the trial and the trial will end. If the value of this parameter is null, then the\n * trial will wait for a response indefinitely.\n */\n trial_duration: {\n type: ParameterType.INT,\n default: null,\n },\n /** If true, then the trial will end whenever the participant makes a response (assuming they make their\n * response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will\n * continue until the value for `trial_duration` is reached. You can use set this parameter to `false` to\n * force the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete\n */\n response_ends_trial: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n pretty_name: \"Trial ends after audio\",\n default: false,\n },\n /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish\n * playing before a keyboard response is accepted. Once the audio has played all the way through, a valid\n * keyboard response is allowed (including while the audio is being re-played via on-screen playback controls).\n */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n default: true,\n },\n },\n data: {\n /** Indicates which key the participant pressed. If no key was pressed before the trial ended, then the value will be `null`. */\n response: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from when the stimulus\n * first began playing until the participant made a key response. If no key was pressed before the trial ended, then the\n * value will be `null`.\n */\n rt: {\n type: ParameterType.INT,\n },\n /** Path to the audio file that played during the trial. */\n stimulus: {\n type: ParameterType.STRING,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * This plugin plays audio files and records responses generated with the keyboard.\n *\n * If the browser supports it, audio files are played using the WebAudio API. This allows for reasonably precise timing of the\n * playback. The timing of responses generated is measured against the WebAudio specific clock, improving the measurement of\n * response times. If the browser does not support the WebAudio API, then the audio file is played with HTML5 audio.\n *\n * Audio files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if you are using\n * timeline variables or another dynamic method to specify the audio stimulus, then you will need to [manually preload](../overview/media-preloading.md#manual-preloading) the audio.\n *\n * The trial can end when the participant responds, when the audio file has finished playing, or if the participant has\n * failed to respond within a fixed length of time. You can also prevent a keyboard response from being recorded before\n * the audio has finished playing.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}\n */\nclass AudioKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio: AudioPlayerInterface;\n private params: TrialType<Info>;\n private display: HTMLElement;\n private response: { rt: number; key: string } = { rt: null, key: null };\n private startTime: number;\n private finish: ({}: { rt: number; response: string; stimulus: string }) => void;\n\n constructor(private jsPsych: JsPsych) {\n autoBind(this);\n }\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n return new Promise(async (resolve) => {\n this.finish = resolve;\n this.params = trial;\n this.display = display_element;\n // load audio file\n this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);\n\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.end_trial);\n }\n\n // show prompt if there is one\n if (trial.prompt !== null) {\n display_element.innerHTML = trial.prompt;\n }\n\n // start playing audio here to record time\n // use this for offsetting RT measurement in\n // setup_keyboard_listener\n this.startTime = this.jsPsych.pluginAPI.audioContext()?.currentTime;\n\n // start keyboard listener when trial starts or sound ends\n if (trial.response_allowed_while_playing) {\n this.setup_keyboard_listener();\n } else if (!trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.setup_keyboard_listener);\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n this.end_trial();\n }, trial.trial_duration);\n }\n\n // call trial on_load method because we are done with all loading setup\n on_load();\n\n this.audio.play();\n });\n }\n\n private end_trial() {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // stop the audio file if it is playing\n this.audio.stop();\n\n // remove end event listeners if they exist\n this.audio.removeEventListener(\"ended\", this.end_trial);\n this.audio.removeEventListener(\"ended\", this.setup_keyboard_listener);\n\n // kill keyboard listeners\n this.jsPsych.pluginAPI.cancelAllKeyboardResponses();\n\n // gather the data to store for the trial\n var trial_data = {\n rt: this.response.rt,\n response: this.response.key,\n stimulus: this.params.stimulus,\n };\n\n // clear the display\n this.display.innerHTML = \"\";\n\n // move on to the next trial\n this.finish(trial_data);\n }\n\n private after_response(info: { key: string; rt: number }) {\n this.response = info;\n if (this.params.response_ends_trial) {\n this.end_trial();\n }\n }\n\n private setup_keyboard_listener() {\n // start the response listener\n if (this.jsPsych.pluginAPI.useWebaudio) {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: this.after_response,\n valid_responses: this.params.choices,\n rt_method: \"audio\",\n persist: false,\n allow_held_key: false,\n audio_context: this.jsPsych.pluginAPI.audioContext(),\n audio_context_start_time: this.startTime,\n });\n } else {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: this.after_response,\n valid_responses: this.params.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n }\n\n async simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n return this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n return this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n return data;\n }\n\n private async simulate_visual(\n trial: TrialType<Info>,\n simulation_options,\n load_callback: () => void\n ) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n const display_element = this.jsPsych.getDisplayElement();\n\n const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.pressKey(data.response, data.rt);\n }\n };\n\n const result = await this.trial(display_element, trial, () => {\n load_callback();\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n\n return result;\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\n response: this.jsPsych.pluginAPI.getValidKey(trial.choices),\n };\n\n const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n return data;\n }\n}\n\nexport default AudioKeyboardResponsePlugin;\n"],"names":["getAllProperties","object","properties","key","autoBind","self","include","exclude","filter","match","pattern","descriptor","info","version","ParameterType","AudioKeyboardResponsePlugin","jsPsych","display_element","trial","on_load","resolve","__async","_a","trial_data","simulation_mode","simulation_options","load_callback","data","respond","default_data"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.browser.min.js","sources":["../../../node_modules/auto-bind/index.js","../package.json","../src/index.ts"],"sourcesContent":["'use strict';\n\n// Gets all non-builtin properties up the prototype chain\nconst getAllProperties = object => {\n\tconst properties = new Set();\n\n\tdo {\n\t\tfor (const key of Reflect.ownKeys(object)) {\n\t\t\tproperties.add([object, key]);\n\t\t}\n\t} while ((object = Reflect.getPrototypeOf(object)) && object !== Object.prototype);\n\n\treturn properties;\n};\n\nmodule.exports = (self, {include, exclude} = {}) => {\n\tconst filter = key => {\n\t\tconst match = pattern => typeof pattern === 'string' ? key === pattern : pattern.test(key);\n\n\t\tif (include) {\n\t\t\treturn include.some(match);\n\t\t}\n\n\t\tif (exclude) {\n\t\t\treturn !exclude.some(match);\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tfor (const [object, key] of getAllProperties(self.constructor.prototype)) {\n\t\tif (key === 'constructor' || !filter(key)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst descriptor = Reflect.getOwnPropertyDescriptor(object, key);\n\t\tif (descriptor && typeof descriptor.value === 'function') {\n\t\t\tself[key] = self[key].bind(self);\n\t\t}\n\t}\n\n\treturn self;\n};\n","{\n \"name\": \"@jspsych/plugin-audio-keyboard-response\",\n \"version\": \"2.1.1\",\n \"description\": \"jsPsych plugin for playing an audio file and getting a keyboard response\",\n \"type\": \"module\",\n \"main\": \"dist/index.cjs\",\n \"exports\": {\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n },\n \"typings\": \"dist/index.d.ts\",\n \"unpkg\": \"dist/index.browser.min.js\",\n \"files\": [\n \"src\",\n \"dist\"\n ],\n \"source\": \"src/index.ts\",\n \"scripts\": {\n \"test\": \"jest\",\n \"test:watch\": \"npm test -- --watch\",\n \"tsc\": \"tsc\",\n \"build\": \"rollup --config\",\n \"build:watch\": \"npm run build -- --watch\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/jspsych/jsPsych.git\",\n \"directory\": \"packages/plugin-audio-keyboard-response\"\n },\n \"author\": \"Josh de Leeuw\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/jspsych/jsPsych/issues\"\n },\n \"homepage\": \"https://www.jspsych.org/latest/plugins/audio-keyboard-response\",\n \"peerDependencies\": {\n \"jspsych\": \">=7.1.0\"\n },\n \"devDependencies\": {\n \"@jspsych/config\": \"^3.2.0\",\n \"@jspsych/test-utils\": \"^1.2.0\"\n }\n}\n","import autoBind from \"auto-bind\";\nimport { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { AudioPlayerInterface } from \"../../jspsych/src/modules/plugin-api/AudioPlayer\";\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"audio-keyboard-response\",\n version: version,\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n default: undefined,\n },\n /** This array contains the key(s) that the participant is allowed to press in order to respond to the stimulus.\n * Keys should be specified as characters (e.g., `'a'`, `'q'`, `' '`, `'Enter'`, `'ArrowDown'`) -\n * see [this page](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values)\n * and [this page (event.key column)](https://www.freecodecamp.org/news/javascript-keycode-list-keypress-event-key-codes/)\n * for more examples. Any key presses that are not listed in the array will be ignored. The default value of `\"ALL_KEYS\"`\n * means that all keys will be accepted as valid responses. Specifying `\"NO_KEYS\"` will mean that no responses are allowed.\n */\n choices: {\n type: ParameterType.KEYS,\n default: \"ALL_KEYS\",\n },\n /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that\n * it can be used to provide a reminder about the action the participant is supposed to take (e.g., which key to press).\n */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to wait for the participant to make a response before ending the trial in milliseconds. If the\n * participant fails to make a response before this timer is reached, the participant's response will be\n * recorded as null for the trial and the trial will end. If the value of this parameter is null, then the\n * trial will wait for a response indefinitely.\n */\n trial_duration: {\n type: ParameterType.INT,\n default: null,\n },\n /** If true, then the trial will end whenever the participant makes a response (assuming they make their\n * response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will\n * continue until the value for `trial_duration` is reached. You can use set this parameter to `false` to\n * force the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete\n */\n response_ends_trial: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n pretty_name: \"Trial ends after audio\",\n default: false,\n },\n /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish\n * playing before a keyboard response is accepted. Once the audio has played all the way through, a valid\n * keyboard response is allowed (including while the audio is being re-played via on-screen playback controls).\n */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n default: true,\n },\n },\n data: {\n /** Indicates which key the participant pressed. If no key was pressed before the trial ended, then the value will be `null`. */\n response: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from when the stimulus\n * first began playing until the participant made a key response. If no key was pressed before the trial ended, then the\n * value will be `null`.\n */\n rt: {\n type: ParameterType.INT,\n },\n /** Path to the audio file that played during the trial. */\n stimulus: {\n type: ParameterType.STRING,\n },\n },\n // prettier-ignore\n citations: '__CITATIONS__',\n};\n\ntype Info = typeof info;\n\n/**\n * This plugin plays audio files and records responses generated with the keyboard.\n *\n * If the browser supports it, audio files are played using the WebAudio API. This allows for reasonably precise timing of the\n * playback. The timing of responses generated is measured against the WebAudio specific clock, improving the measurement of\n * response times. If the browser does not support the WebAudio API, then the audio file is played with HTML5 audio.\n *\n * Audio files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if you are using\n * timeline variables or another dynamic method to specify the audio stimulus, then you will need to [manually preload](../overview/media-preloading.md#manual-preloading) the audio.\n *\n * The trial can end when the participant responds, when the audio file has finished playing, or if the participant has\n * failed to respond within a fixed length of time. You can also prevent a keyboard response from being recorded before\n * the audio has finished playing.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}\n */\nclass AudioKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio: AudioPlayerInterface;\n private params: TrialType<Info>;\n private display: HTMLElement;\n private response: { rt: number; key: string } = { rt: null, key: null };\n private startTime: number;\n private finish: ({}: { rt: number; response: string; stimulus: string }) => void;\n\n constructor(private jsPsych: JsPsych) {\n autoBind(this);\n }\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n return new Promise(async (resolve) => {\n this.finish = resolve;\n this.params = trial;\n this.display = display_element;\n // load audio file\n this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);\n\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.end_trial);\n }\n\n // show prompt if there is one\n if (trial.prompt !== null) {\n display_element.innerHTML = trial.prompt;\n }\n\n // start playing audio here to record time\n // use this for offsetting RT measurement in\n // setup_keyboard_listener\n this.startTime = this.jsPsych.pluginAPI.audioContext()?.currentTime;\n\n // start keyboard listener when trial starts or sound ends\n if (trial.response_allowed_while_playing) {\n this.setup_keyboard_listener();\n } else if (!trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.setup_keyboard_listener);\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n this.end_trial();\n }, trial.trial_duration);\n }\n\n // call trial on_load method because we are done with all loading setup\n on_load();\n\n this.audio.play();\n });\n }\n\n private end_trial() {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // remove end event listeners if they exist\n this.audio.removeEventListener(\"ended\", this.end_trial);\n this.audio.removeEventListener(\"ended\", this.setup_keyboard_listener);\n\n // stop the audio file if it is playing\n this.audio.stop();\n\n // kill keyboard listeners\n this.jsPsych.pluginAPI.cancelAllKeyboardResponses();\n\n // gather the data to store for the trial\n var trial_data = {\n rt: this.response.rt,\n response: this.response.key,\n stimulus: this.params.stimulus,\n };\n\n // clear the display\n this.display.innerHTML = \"\";\n\n // move on to the next trial\n this.finish(trial_data);\n }\n\n private after_response(info: { key: string; rt: number }) {\n this.response = info;\n if (this.params.response_ends_trial) {\n this.end_trial();\n }\n }\n\n private setup_keyboard_listener() {\n // start the response listener\n if (this.jsPsych.pluginAPI.useWebaudio) {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: this.after_response,\n valid_responses: this.params.choices,\n rt_method: \"audio\",\n persist: false,\n allow_held_key: false,\n audio_context: this.jsPsych.pluginAPI.audioContext(),\n audio_context_start_time: this.startTime,\n });\n } else {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: this.after_response,\n valid_responses: this.params.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n }\n\n async simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n return this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n return this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n return data;\n }\n\n private async simulate_visual(\n trial: TrialType<Info>,\n simulation_options,\n load_callback: () => void\n ) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n const display_element = this.jsPsych.getDisplayElement();\n\n const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.pressKey(data.response, data.rt);\n }\n };\n\n const result = await this.trial(display_element, trial, () => {\n load_callback();\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n\n return result;\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\n response: this.jsPsych.pluginAPI.getValidKey(trial.choices),\n };\n\n const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n return data;\n }\n}\n\nexport default AudioKeyboardResponsePlugin;\n"],"names":["getAllProperties","object","properties","key","autoBind","self","include","exclude","filter","match","pattern","descriptor","version","_a"],"mappings":"8JAGA,MAAMA,EAAmBC,GAAU,CAClC,MAAMC,EAAa,IAAI,IAEvB,EACC,WAAWC,KAAO,QAAQ,QAAQF,CAAM,EACvCC,EAAW,IAAI,CAACD,EAAQE,CAAG,CAAC,SAEpBF,EAAS,QAAQ,eAAeA,CAAM,IAAMA,IAAW,OAAO,WAExE,OAAOC,CACR,MAEAE,EAAiB,CAACC,EAAM,CAAC,QAAAC,EAAS,QAAAC,CAAO,EAAI,CAAA,IAAO,CACnD,MAAMC,EAASL,GAAO,CACrB,MAAMM,EAAQC,GAAW,OAAOA,GAAY,SAAWP,IAAQO,EAAUA,EAAQ,KAAKP,CAAG,EAEzF,OAAIG,EACIA,EAAQ,KAAKG,CAAK,EAGtBF,EACI,CAACA,EAAQ,KAAKE,CAAK,EAGpB,EACT,EAEC,SAAW,CAACR,EAAQE,CAAG,IAAKH,EAAiBK,EAAK,YAAY,SAAS,EAAG,CACzE,GAAIF,IAAQ,eAAiB,CAACK,EAAOL,CAAG,EACvC,SAGD,MAAMQ,EAAa,QAAQ,yBAAyBV,EAAQE,CAAG,EAC3DQ,GAAc,OAAOA,EAAW,OAAU,aAC7CN,EAAKF,CAAG,EAAIE,EAAKF,CAAG,EAAE,KAAKE,CAAI,EAElC,CAEC,OAAOA,CACR,SCxCEO,EAAW,s1BCmFA,UAAA,iuBAAe,4HArF5B,KAAA,WAAA,CAAA,IAAAC","x_google_ignoreList":[0]}
|
package/dist/index.cjs
CHANGED
|
@@ -3,95 +3,90 @@
|
|
|
3
3
|
var autoBind = require('auto-bind');
|
|
4
4
|
var jspsych = require('jspsych');
|
|
5
5
|
|
|
6
|
-
var
|
|
7
|
-
name: "@jspsych/plugin-audio-keyboard-response",
|
|
8
|
-
version: "2.0.0",
|
|
9
|
-
description: "jsPsych plugin for playing an audio file and getting a keyboard response",
|
|
10
|
-
type: "module",
|
|
11
|
-
main: "dist/index.cjs",
|
|
12
|
-
exports: {
|
|
13
|
-
import: "./dist/index.js",
|
|
14
|
-
require: "./dist/index.cjs"
|
|
15
|
-
},
|
|
16
|
-
typings: "dist/index.d.ts",
|
|
17
|
-
unpkg: "dist/index.browser.min.js",
|
|
18
|
-
files: [
|
|
19
|
-
"src",
|
|
20
|
-
"dist"
|
|
21
|
-
],
|
|
22
|
-
source: "src/index.ts",
|
|
23
|
-
scripts: {
|
|
24
|
-
test: "jest",
|
|
25
|
-
"test:watch": "npm test -- --watch",
|
|
26
|
-
tsc: "tsc",
|
|
27
|
-
build: "rollup --config",
|
|
28
|
-
"build:watch": "npm run build -- --watch"
|
|
29
|
-
},
|
|
30
|
-
repository: {
|
|
31
|
-
type: "git",
|
|
32
|
-
url: "git+https://github.com/jspsych/jsPsych.git",
|
|
33
|
-
directory: "packages/plugin-audio-keyboard-response"
|
|
34
|
-
},
|
|
35
|
-
author: "Josh de Leeuw",
|
|
36
|
-
license: "MIT",
|
|
37
|
-
bugs: {
|
|
38
|
-
url: "https://github.com/jspsych/jsPsych/issues"
|
|
39
|
-
},
|
|
40
|
-
homepage: "https://www.jspsych.org/latest/plugins/audio-keyboard-response",
|
|
41
|
-
peerDependencies: {
|
|
42
|
-
jspsych: ">=7.1.0"
|
|
43
|
-
},
|
|
44
|
-
devDependencies: {
|
|
45
|
-
"@jspsych/config": "^3.0.0",
|
|
46
|
-
"@jspsych/test-utils": "^1.2.0"
|
|
47
|
-
}
|
|
48
|
-
};
|
|
6
|
+
var version = "2.1.1";
|
|
49
7
|
|
|
50
8
|
const info = {
|
|
51
9
|
name: "audio-keyboard-response",
|
|
52
|
-
version
|
|
10
|
+
version,
|
|
53
11
|
parameters: {
|
|
12
|
+
/** The audio file to be played. */
|
|
54
13
|
stimulus: {
|
|
55
14
|
type: jspsych.ParameterType.AUDIO,
|
|
56
15
|
default: void 0
|
|
57
16
|
},
|
|
17
|
+
/** This array contains the key(s) that the participant is allowed to press in order to respond to the stimulus.
|
|
18
|
+
* Keys should be specified as characters (e.g., `'a'`, `'q'`, `' '`, `'Enter'`, `'ArrowDown'`) -
|
|
19
|
+
* see [this page](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values)
|
|
20
|
+
* and [this page (event.key column)](https://www.freecodecamp.org/news/javascript-keycode-list-keypress-event-key-codes/)
|
|
21
|
+
* for more examples. Any key presses that are not listed in the array will be ignored. The default value of `"ALL_KEYS"`
|
|
22
|
+
* means that all keys will be accepted as valid responses. Specifying `"NO_KEYS"` will mean that no responses are allowed.
|
|
23
|
+
*/
|
|
58
24
|
choices: {
|
|
59
25
|
type: jspsych.ParameterType.KEYS,
|
|
60
26
|
default: "ALL_KEYS"
|
|
61
27
|
},
|
|
28
|
+
/** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that
|
|
29
|
+
* it can be used to provide a reminder about the action the participant is supposed to take (e.g., which key to press).
|
|
30
|
+
*/
|
|
62
31
|
prompt: {
|
|
63
32
|
type: jspsych.ParameterType.HTML_STRING,
|
|
64
33
|
pretty_name: "Prompt",
|
|
65
34
|
default: null
|
|
66
35
|
},
|
|
36
|
+
/** How long to wait for the participant to make a response before ending the trial in milliseconds. If the
|
|
37
|
+
* participant fails to make a response before this timer is reached, the participant's response will be
|
|
38
|
+
* recorded as null for the trial and the trial will end. If the value of this parameter is null, then the
|
|
39
|
+
* trial will wait for a response indefinitely.
|
|
40
|
+
*/
|
|
67
41
|
trial_duration: {
|
|
68
42
|
type: jspsych.ParameterType.INT,
|
|
69
43
|
default: null
|
|
70
44
|
},
|
|
45
|
+
/** If true, then the trial will end whenever the participant makes a response (assuming they make their
|
|
46
|
+
* response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will
|
|
47
|
+
* continue until the value for `trial_duration` is reached. You can use set this parameter to `false` to
|
|
48
|
+
* force the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete
|
|
49
|
+
*/
|
|
71
50
|
response_ends_trial: {
|
|
72
51
|
type: jspsych.ParameterType.BOOL,
|
|
73
52
|
default: true
|
|
74
53
|
},
|
|
54
|
+
/** If true, then the trial will end as soon as the audio file finishes playing. */
|
|
75
55
|
trial_ends_after_audio: {
|
|
76
56
|
type: jspsych.ParameterType.BOOL,
|
|
77
57
|
pretty_name: "Trial ends after audio",
|
|
78
58
|
default: false
|
|
79
59
|
},
|
|
60
|
+
/** If true, then responses are allowed while the audio is playing. If false, then the audio must finish
|
|
61
|
+
* playing before a keyboard response is accepted. Once the audio has played all the way through, a valid
|
|
62
|
+
* keyboard response is allowed (including while the audio is being re-played via on-screen playback controls).
|
|
63
|
+
*/
|
|
80
64
|
response_allowed_while_playing: {
|
|
81
65
|
type: jspsych.ParameterType.BOOL,
|
|
82
66
|
default: true
|
|
83
67
|
}
|
|
84
68
|
},
|
|
85
69
|
data: {
|
|
70
|
+
/** Indicates which key the participant pressed. If no key was pressed before the trial ended, then the value will be `null`. */
|
|
86
71
|
response: {
|
|
87
72
|
type: jspsych.ParameterType.STRING
|
|
88
73
|
},
|
|
74
|
+
/** The response time in milliseconds for the participant to make a response. The time is measured from when the stimulus
|
|
75
|
+
* first began playing until the participant made a key response. If no key was pressed before the trial ended, then the
|
|
76
|
+
* value will be `null`.
|
|
77
|
+
*/
|
|
89
78
|
rt: {
|
|
90
79
|
type: jspsych.ParameterType.INT
|
|
91
80
|
},
|
|
81
|
+
/** Path to the audio file that played during the trial. */
|
|
92
82
|
stimulus: {
|
|
93
83
|
type: jspsych.ParameterType.STRING
|
|
94
84
|
}
|
|
85
|
+
},
|
|
86
|
+
// prettier-ignore
|
|
87
|
+
citations: {
|
|
88
|
+
"apa": "de Leeuw, J. R., Gilbert, R. A., & Luchterhandt, B. (2023). jsPsych: Enabling an Open-Source Collaborative Ecosystem of Behavioral Experiments. Journal of Open Source Software, 8(85), 5351. https://doi.org/10.21105/joss.05351 ",
|
|
89
|
+
"bibtex": '@article{Leeuw2023jsPsych, author = {de Leeuw, Joshua R. and Gilbert, Rebecca A. and Luchterhandt, Bj{\\" o}rn}, journal = {Journal of Open Source Software}, doi = {10.21105/joss.05351}, issn = {2475-9066}, number = {85}, year = {2023}, month = {may 11}, pages = {5351}, publisher = {Open Journals}, title = {jsPsych: Enabling an {Open}-{Source} {Collaborative} {Ecosystem} of {Behavioral} {Experiments}}, url = {https://joss.theoj.org/papers/10.21105/joss.05351}, volume = {8}, } '
|
|
95
90
|
}
|
|
96
91
|
};
|
|
97
92
|
class AudioKeyboardResponsePlugin {
|
|
@@ -100,6 +95,9 @@ class AudioKeyboardResponsePlugin {
|
|
|
100
95
|
this.response = { rt: null, key: null };
|
|
101
96
|
autoBind(this);
|
|
102
97
|
}
|
|
98
|
+
static {
|
|
99
|
+
this.info = info;
|
|
100
|
+
}
|
|
103
101
|
trial(display_element, trial, on_load) {
|
|
104
102
|
return new Promise(async (resolve) => {
|
|
105
103
|
this.finish = resolve;
|
|
@@ -129,9 +127,9 @@ class AudioKeyboardResponsePlugin {
|
|
|
129
127
|
}
|
|
130
128
|
end_trial() {
|
|
131
129
|
this.jsPsych.pluginAPI.clearAllTimeouts();
|
|
132
|
-
this.audio.stop();
|
|
133
130
|
this.audio.removeEventListener("ended", this.end_trial);
|
|
134
131
|
this.audio.removeEventListener("ended", this.setup_keyboard_listener);
|
|
132
|
+
this.audio.stop();
|
|
135
133
|
this.jsPsych.pluginAPI.cancelAllKeyboardResponses();
|
|
136
134
|
var trial_data = {
|
|
137
135
|
rt: this.response.rt,
|
|
@@ -210,7 +208,6 @@ class AudioKeyboardResponsePlugin {
|
|
|
210
208
|
return data;
|
|
211
209
|
}
|
|
212
210
|
}
|
|
213
|
-
AudioKeyboardResponsePlugin.info = info;
|
|
214
211
|
|
|
215
212
|
module.exports = AudioKeyboardResponsePlugin;
|
|
216
213
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":["import autoBind from \"auto-bind\";\nimport { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { AudioPlayerInterface } from \"../../jspsych/src/modules/plugin-api/AudioPlayer\";\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"audio-keyboard-response\",\n version: version,\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n default: undefined,\n },\n /** This array contains the key(s) that the participant is allowed to press in order to respond to the stimulus.\n * Keys should be specified as characters (e.g., `'a'`, `'q'`, `' '`, `'Enter'`, `'ArrowDown'`) -\n * see [this page](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values)\n * and [this page (event.key column)](https://www.freecodecamp.org/news/javascript-keycode-list-keypress-event-key-codes/)\n * for more examples. Any key presses that are not listed in the array will be ignored. The default value of `\"ALL_KEYS\"`\n * means that all keys will be accepted as valid responses. Specifying `\"NO_KEYS\"` will mean that no responses are allowed.\n */\n choices: {\n type: ParameterType.KEYS,\n default: \"ALL_KEYS\",\n },\n /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that\n * it can be used to provide a reminder about the action the participant is supposed to take (e.g., which key to press).\n */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to wait for the participant to make a response before ending the trial in milliseconds. If the\n * participant fails to make a response before this timer is reached, the participant's response will be\n * recorded as null for the trial and the trial will end. If the value of this parameter is null, then the\n * trial will wait for a response indefinitely.\n */\n trial_duration: {\n type: ParameterType.INT,\n default: null,\n },\n /** If true, then the trial will end whenever the participant makes a response (assuming they make their\n * response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will\n * continue until the value for `trial_duration` is reached. You can use set this parameter to `false` to\n * force the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete\n */\n response_ends_trial: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n pretty_name: \"Trial ends after audio\",\n default: false,\n },\n /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish\n * playing before a keyboard response is accepted. Once the audio has played all the way through, a valid\n * keyboard response is allowed (including while the audio is being re-played via on-screen playback controls).\n */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n default: true,\n },\n },\n data: {\n /** Indicates which key the participant pressed. If no key was pressed before the trial ended, then the value will be `null`. */\n response: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from when the stimulus\n * first began playing until the participant made a key response. If no key was pressed before the trial ended, then the\n * value will be `null`.\n */\n rt: {\n type: ParameterType.INT,\n },\n /** Path to the audio file that played during the trial. */\n stimulus: {\n type: ParameterType.STRING,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * This plugin plays audio files and records responses generated with the keyboard.\n *\n * If the browser supports it, audio files are played using the WebAudio API. This allows for reasonably precise timing of the\n * playback. The timing of responses generated is measured against the WebAudio specific clock, improving the measurement of\n * response times. If the browser does not support the WebAudio API, then the audio file is played with HTML5 audio.\n *\n * Audio files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if you are using\n * timeline variables or another dynamic method to specify the audio stimulus, then you will need to [manually preload](../overview/media-preloading.md#manual-preloading) the audio.\n *\n * The trial can end when the participant responds, when the audio file has finished playing, or if the participant has\n * failed to respond within a fixed length of time. You can also prevent a keyboard response from being recorded before\n * the audio has finished playing.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}\n */\nclass AudioKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio: AudioPlayerInterface;\n private params: TrialType<Info>;\n private display: HTMLElement;\n private response: { rt: number; key: string } = { rt: null, key: null };\n private startTime: number;\n private finish: ({}: { rt: number; response: string; stimulus: string }) => void;\n\n constructor(private jsPsych: JsPsych) {\n autoBind(this);\n }\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n return new Promise(async (resolve) => {\n this.finish = resolve;\n this.params = trial;\n this.display = display_element;\n // load audio file\n this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);\n\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.end_trial);\n }\n\n // show prompt if there is one\n if (trial.prompt !== null) {\n display_element.innerHTML = trial.prompt;\n }\n\n // start playing audio here to record time\n // use this for offsetting RT measurement in\n // setup_keyboard_listener\n this.startTime = this.jsPsych.pluginAPI.audioContext()?.currentTime;\n\n // start keyboard listener when trial starts or sound ends\n if (trial.response_allowed_while_playing) {\n this.setup_keyboard_listener();\n } else if (!trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.setup_keyboard_listener);\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n this.end_trial();\n }, trial.trial_duration);\n }\n\n // call trial on_load method because we are done with all loading setup\n on_load();\n\n this.audio.play();\n });\n }\n\n private end_trial() {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // stop the audio file if it is playing\n this.audio.stop();\n\n // remove end event listeners if they exist\n this.audio.removeEventListener(\"ended\", this.end_trial);\n this.audio.removeEventListener(\"ended\", this.setup_keyboard_listener);\n\n // kill keyboard listeners\n this.jsPsych.pluginAPI.cancelAllKeyboardResponses();\n\n // gather the data to store for the trial\n var trial_data = {\n rt: this.response.rt,\n response: this.response.key,\n stimulus: this.params.stimulus,\n };\n\n // clear the display\n this.display.innerHTML = \"\";\n\n // move on to the next trial\n this.finish(trial_data);\n }\n\n private after_response(info: { key: string; rt: number }) {\n this.response = info;\n if (this.params.response_ends_trial) {\n this.end_trial();\n }\n }\n\n private setup_keyboard_listener() {\n // start the response listener\n if (this.jsPsych.pluginAPI.useWebaudio) {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: this.after_response,\n valid_responses: this.params.choices,\n rt_method: \"audio\",\n persist: false,\n allow_held_key: false,\n audio_context: this.jsPsych.pluginAPI.audioContext(),\n audio_context_start_time: this.startTime,\n });\n } else {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: this.after_response,\n valid_responses: this.params.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n }\n\n async simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n return this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n return this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n return data;\n }\n\n private async simulate_visual(\n trial: TrialType<Info>,\n simulation_options,\n load_callback: () => void\n ) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n const display_element = this.jsPsych.getDisplayElement();\n\n const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.pressKey(data.response, data.rt);\n }\n };\n\n const result = await this.trial(display_element, trial, () => {\n load_callback();\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n\n return result;\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\n response: this.jsPsych.pluginAPI.getValidKey(trial.choices),\n };\n\n const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n return data;\n }\n}\n\nexport default AudioKeyboardResponsePlugin;\n"],"names":["version","ParameterType","info"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMA,MAAM,IAAc,GAAA;AAAA,EAClB,IAAM,EAAA,yBAAA;AAAA,WACNA,gBAAA;AAAA,EACA,UAAY,EAAA;AAAA,IAEV,QAAU,EAAA;AAAA,MACR,MAAMC,qBAAc,CAAA,KAAA;AAAA,MACpB,OAAS,EAAA,KAAA,CAAA;AAAA,KACX;AAAA,IAQA,OAAS,EAAA;AAAA,MACP,MAAMA,qBAAc,CAAA,IAAA;AAAA,MACpB,OAAS,EAAA,UAAA;AAAA,KACX;AAAA,IAIA,MAAQ,EAAA;AAAA,MACN,MAAMA,qBAAc,CAAA,WAAA;AAAA,MACpB,WAAa,EAAA,QAAA;AAAA,MACb,OAAS,EAAA,IAAA;AAAA,KACX;AAAA,IAMA,cAAgB,EAAA;AAAA,MACd,MAAMA,qBAAc,CAAA,GAAA;AAAA,MACpB,OAAS,EAAA,IAAA;AAAA,KACX;AAAA,IAMA,mBAAqB,EAAA;AAAA,MACnB,MAAMA,qBAAc,CAAA,IAAA;AAAA,MACpB,OAAS,EAAA,IAAA;AAAA,KACX;AAAA,IAEA,sBAAwB,EAAA;AAAA,MACtB,MAAMA,qBAAc,CAAA,IAAA;AAAA,MACpB,WAAa,EAAA,wBAAA;AAAA,MACb,OAAS,EAAA,KAAA;AAAA,KACX;AAAA,IAKA,8BAAgC,EAAA;AAAA,MAC9B,MAAMA,qBAAc,CAAA,IAAA;AAAA,MACpB,OAAS,EAAA,IAAA;AAAA,KACX;AAAA,GACF;AAAA,EACA,IAAM,EAAA;AAAA,IAEJ,QAAU,EAAA;AAAA,MACR,MAAMA,qBAAc,CAAA,MAAA;AAAA,KACtB;AAAA,IAKA,EAAI,EAAA;AAAA,MACF,MAAMA,qBAAc,CAAA,GAAA;AAAA,KACtB;AAAA,IAEA,QAAU,EAAA;AAAA,MACR,MAAMA,qBAAc,CAAA,MAAA;AAAA,KACtB;AAAA,GACF;AACF,CAAA,CAAA;AAqBA,MAAM,2BAA2D,CAAA;AAAA,EAS/D,YAAoB,OAAkB,EAAA;AAAlB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA,CAAA;AAJpB,IAAA,IAAA,CAAQ,QAAwC,GAAA,EAAE,EAAI,EAAA,IAAA,EAAM,KAAK,IAAK,EAAA,CAAA;AAKpE,IAAA,QAAA,CAAS,IAAI,CAAA,CAAA;AAAA,GACf;AAAA,EAEA,KAAA,CAAM,eAA8B,EAAA,KAAA,EAAwB,OAAqB,EAAA;AAC/E,IAAO,OAAA,IAAI,OAAQ,CAAA,OAAO,OAAY,KAAA;AACpC,MAAA,IAAA,CAAK,MAAS,GAAA,OAAA,CAAA;AACd,MAAA,IAAA,CAAK,MAAS,GAAA,KAAA,CAAA;AACd,MAAA,IAAA,CAAK,OAAU,GAAA,eAAA,CAAA;AAEf,MAAA,IAAA,CAAK,QAAQ,MAAM,IAAA,CAAK,QAAQ,SAAU,CAAA,cAAA,CAAe,MAAM,QAAQ,CAAA,CAAA;AAGvE,MAAA,IAAI,MAAM,sBAAwB,EAAA;AAChC,QAAA,IAAA,CAAK,KAAM,CAAA,gBAAA,CAAiB,OAAS,EAAA,IAAA,CAAK,SAAS,CAAA,CAAA;AAAA,OACrD;AAGA,MAAI,IAAA,KAAA,CAAM,WAAW,IAAM,EAAA;AACzB,QAAA,eAAA,CAAgB,YAAY,KAAM,CAAA,MAAA,CAAA;AAAA,OACpC;AAKA,MAAA,IAAA,CAAK,SAAY,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,CAAU,cAAgB,EAAA,WAAA,CAAA;AAGxD,MAAA,IAAI,MAAM,8BAAgC,EAAA;AACxC,QAAA,IAAA,CAAK,uBAAwB,EAAA,CAAA;AAAA,OAC/B,MAAA,IAAW,CAAC,KAAA,CAAM,sBAAwB,EAAA;AACxC,QAAA,IAAA,CAAK,KAAM,CAAA,gBAAA,CAAiB,OAAS,EAAA,IAAA,CAAK,uBAAuB,CAAA,CAAA;AAAA,OACnE;AAGA,MAAI,IAAA,KAAA,CAAM,mBAAmB,IAAM,EAAA;AACjC,QAAK,IAAA,CAAA,OAAA,CAAQ,SAAU,CAAA,UAAA,CAAW,MAAM;AACtC,UAAA,IAAA,CAAK,SAAU,EAAA,CAAA;AAAA,SACjB,EAAG,MAAM,cAAc,CAAA,CAAA;AAAA,OACzB;AAGA,MAAQ,OAAA,EAAA,CAAA;AAER,MAAA,IAAA,CAAK,MAAM,IAAK,EAAA,CAAA;AAAA,KACjB,CAAA,CAAA;AAAA,GACH;AAAA,EAEQ,SAAY,GAAA;AAElB,IAAK,IAAA,CAAA,OAAA,CAAQ,UAAU,gBAAiB,EAAA,CAAA;AAGxC,IAAA,IAAA,CAAK,MAAM,IAAK,EAAA,CAAA;AAGhB,IAAA,IAAA,CAAK,KAAM,CAAA,mBAAA,CAAoB,OAAS,EAAA,IAAA,CAAK,SAAS,CAAA,CAAA;AACtD,IAAA,IAAA,CAAK,KAAM,CAAA,mBAAA,CAAoB,OAAS,EAAA,IAAA,CAAK,uBAAuB,CAAA,CAAA;AAGpE,IAAK,IAAA,CAAA,OAAA,CAAQ,UAAU,0BAA2B,EAAA,CAAA;AAGlD,IAAA,IAAI,UAAa,GAAA;AAAA,MACf,EAAA,EAAI,KAAK,QAAS,CAAA,EAAA;AAAA,MAClB,QAAA,EAAU,KAAK,QAAS,CAAA,GAAA;AAAA,MACxB,QAAA,EAAU,KAAK,MAAO,CAAA,QAAA;AAAA,KACxB,CAAA;AAGA,IAAA,IAAA,CAAK,QAAQ,SAAY,GAAA,EAAA,CAAA;AAGzB,IAAA,IAAA,CAAK,OAAO,UAAU,CAAA,CAAA;AAAA,GACxB;AAAA,EAEQ,eAAeC,KAAmC,EAAA;AACxD,IAAA,IAAA,CAAK,QAAWA,GAAAA,KAAAA,CAAAA;AAChB,IAAI,IAAA,IAAA,CAAK,OAAO,mBAAqB,EAAA;AACnC,MAAA,IAAA,CAAK,SAAU,EAAA,CAAA;AAAA,KACjB;AAAA,GACF;AAAA,EAEQ,uBAA0B,GAAA;AAEhC,IAAI,IAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,CAAU,WAAa,EAAA;AACtC,MAAK,IAAA,CAAA,OAAA,CAAQ,UAAU,mBAAoB,CAAA;AAAA,QACzC,mBAAmB,IAAK,CAAA,cAAA;AAAA,QACxB,eAAA,EAAiB,KAAK,MAAO,CAAA,OAAA;AAAA,QAC7B,SAAW,EAAA,OAAA;AAAA,QACX,OAAS,EAAA,KAAA;AAAA,QACT,cAAgB,EAAA,KAAA;AAAA,QAChB,aAAe,EAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,CAAU,YAAa,EAAA;AAAA,QACnD,0BAA0B,IAAK,CAAA,SAAA;AAAA,OAChC,CAAA,CAAA;AAAA,KACI,MAAA;AACL,MAAK,IAAA,CAAA,OAAA,CAAQ,UAAU,mBAAoB,CAAA;AAAA,QACzC,mBAAmB,IAAK,CAAA,cAAA;AAAA,QACxB,eAAA,EAAiB,KAAK,MAAO,CAAA,OAAA;AAAA,QAC7B,SAAW,EAAA,aAAA;AAAA,QACX,OAAS,EAAA,KAAA;AAAA,QACT,cAAgB,EAAA,KAAA;AAAA,OACjB,CAAA,CAAA;AAAA,KACH;AAAA,GACF;AAAA,EAEA,MAAM,QAAA,CACJ,KACA,EAAA,eAAA,EACA,oBACA,aACA,EAAA;AACA,IAAA,IAAI,mBAAmB,WAAa,EAAA;AAClC,MAAc,aAAA,EAAA,CAAA;AACd,MAAO,OAAA,IAAA,CAAK,kBAAmB,CAAA,KAAA,EAAO,kBAAkB,CAAA,CAAA;AAAA,KAC1D;AACA,IAAA,IAAI,mBAAmB,QAAU,EAAA;AAC/B,MAAA,OAAO,IAAK,CAAA,eAAA,CAAgB,KAAO,EAAA,kBAAA,EAAoB,aAAa,CAAA,CAAA;AAAA,KACtE;AAAA,GACF;AAAA,EAEQ,kBAAA,CAAmB,OAAwB,kBAAoB,EAAA;AACrE,IAAA,MAAM,IAAO,GAAA,IAAA,CAAK,sBAAuB,CAAA,KAAA,EAAO,kBAAkB,CAAA,CAAA;AAElE,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAAA,EAEA,MAAc,eAAA,CACZ,KACA,EAAA,kBAAA,EACA,aACA,EAAA;AACA,IAAA,MAAM,IAAO,GAAA,IAAA,CAAK,sBAAuB,CAAA,KAAA,EAAO,kBAAkB,CAAA,CAAA;AAElE,IAAM,MAAA,eAAA,GAAkB,IAAK,CAAA,OAAA,CAAQ,iBAAkB,EAAA,CAAA;AAEvD,IAAA,MAAM,UAAU,MAAM;AACpB,MAAI,IAAA,IAAA,CAAK,OAAO,IAAM,EAAA;AACpB,QAAA,IAAA,CAAK,QAAQ,SAAU,CAAA,QAAA,CAAS,IAAK,CAAA,QAAA,EAAU,KAAK,EAAE,CAAA,CAAA;AAAA,OACxD;AAAA,KACF,CAAA;AAEA,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,KAAM,CAAA,eAAA,EAAiB,OAAO,MAAM;AAC5D,MAAc,aAAA,EAAA,CAAA;AACd,MAAI,IAAA,CAAC,MAAM,8BAAgC,EAAA;AACzC,QAAK,IAAA,CAAA,KAAA,CAAM,gBAAiB,CAAA,OAAA,EAAS,OAAO,CAAA,CAAA;AAAA,OACvC,MAAA;AACL,QAAQ,OAAA,EAAA,CAAA;AAAA,OACV;AAAA,KACD,CAAA,CAAA;AAED,IAAO,OAAA,MAAA,CAAA;AAAA,GACT;AAAA,EAEQ,sBAAA,CAAuB,OAAwB,kBAAoB,EAAA;AACzE,IAAA,MAAM,YAAe,GAAA;AAAA,MACnB,UAAU,KAAM,CAAA,QAAA;AAAA,MAChB,EAAA,EAAI,KAAK,OAAQ,CAAA,aAAA,CAAc,iBAAiB,GAAK,EAAA,EAAA,EAAI,CAAI,GAAA,GAAA,EAAK,IAAI,CAAA;AAAA,MACtE,UAAU,IAAK,CAAA,OAAA,CAAQ,SAAU,CAAA,WAAA,CAAY,MAAM,OAAO,CAAA;AAAA,KAC5D,CAAA;AAEA,IAAA,MAAM,OAAO,IAAK,CAAA,OAAA,CAAQ,SAAU,CAAA,mBAAA,CAAoB,cAAc,kBAAkB,CAAA,CAAA;AAExF,IAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,CAAU,+BAAgC,CAAA,KAAA,EAAO,IAAI,CAAA,CAAA;AAElE,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AACF,CAAA;AAhLM,2BAAA,CACG,IAAO,GAAA,IAAA;;;;"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../package.json","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@jspsych/plugin-audio-keyboard-response\",\n \"version\": \"2.1.1\",\n \"description\": \"jsPsych plugin for playing an audio file and getting a keyboard response\",\n \"type\": \"module\",\n \"main\": \"dist/index.cjs\",\n \"exports\": {\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n },\n \"typings\": \"dist/index.d.ts\",\n \"unpkg\": \"dist/index.browser.min.js\",\n \"files\": [\n \"src\",\n \"dist\"\n ],\n \"source\": \"src/index.ts\",\n \"scripts\": {\n \"test\": \"jest\",\n \"test:watch\": \"npm test -- --watch\",\n \"tsc\": \"tsc\",\n \"build\": \"rollup --config\",\n \"build:watch\": \"npm run build -- --watch\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/jspsych/jsPsych.git\",\n \"directory\": \"packages/plugin-audio-keyboard-response\"\n },\n \"author\": \"Josh de Leeuw\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/jspsych/jsPsych/issues\"\n },\n \"homepage\": \"https://www.jspsych.org/latest/plugins/audio-keyboard-response\",\n \"peerDependencies\": {\n \"jspsych\": \">=7.1.0\"\n },\n \"devDependencies\": {\n \"@jspsych/config\": \"^3.2.0\",\n \"@jspsych/test-utils\": \"^1.2.0\"\n }\n}\n","import autoBind from \"auto-bind\";\nimport { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { AudioPlayerInterface } from \"../../jspsych/src/modules/plugin-api/AudioPlayer\";\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"audio-keyboard-response\",\n version: version,\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n default: undefined,\n },\n /** This array contains the key(s) that the participant is allowed to press in order to respond to the stimulus.\n * Keys should be specified as characters (e.g., `'a'`, `'q'`, `' '`, `'Enter'`, `'ArrowDown'`) -\n * see [this page](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values)\n * and [this page (event.key column)](https://www.freecodecamp.org/news/javascript-keycode-list-keypress-event-key-codes/)\n * for more examples. Any key presses that are not listed in the array will be ignored. The default value of `\"ALL_KEYS\"`\n * means that all keys will be accepted as valid responses. Specifying `\"NO_KEYS\"` will mean that no responses are allowed.\n */\n choices: {\n type: ParameterType.KEYS,\n default: \"ALL_KEYS\",\n },\n /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that\n * it can be used to provide a reminder about the action the participant is supposed to take (e.g., which key to press).\n */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to wait for the participant to make a response before ending the trial in milliseconds. If the\n * participant fails to make a response before this timer is reached, the participant's response will be\n * recorded as null for the trial and the trial will end. If the value of this parameter is null, then the\n * trial will wait for a response indefinitely.\n */\n trial_duration: {\n type: ParameterType.INT,\n default: null,\n },\n /** If true, then the trial will end whenever the participant makes a response (assuming they make their\n * response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will\n * continue until the value for `trial_duration` is reached. You can use set this parameter to `false` to\n * force the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete\n */\n response_ends_trial: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n pretty_name: \"Trial ends after audio\",\n default: false,\n },\n /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish\n * playing before a keyboard response is accepted. Once the audio has played all the way through, a valid\n * keyboard response is allowed (including while the audio is being re-played via on-screen playback controls).\n */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n default: true,\n },\n },\n data: {\n /** Indicates which key the participant pressed. If no key was pressed before the trial ended, then the value will be `null`. */\n response: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from when the stimulus\n * first began playing until the participant made a key response. If no key was pressed before the trial ended, then the\n * value will be `null`.\n */\n rt: {\n type: ParameterType.INT,\n },\n /** Path to the audio file that played during the trial. */\n stimulus: {\n type: ParameterType.STRING,\n },\n },\n // prettier-ignore\n citations: '__CITATIONS__',\n};\n\ntype Info = typeof info;\n\n/**\n * This plugin plays audio files and records responses generated with the keyboard.\n *\n * If the browser supports it, audio files are played using the WebAudio API. This allows for reasonably precise timing of the\n * playback. The timing of responses generated is measured against the WebAudio specific clock, improving the measurement of\n * response times. If the browser does not support the WebAudio API, then the audio file is played with HTML5 audio.\n *\n * Audio files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if you are using\n * timeline variables or another dynamic method to specify the audio stimulus, then you will need to [manually preload](../overview/media-preloading.md#manual-preloading) the audio.\n *\n * The trial can end when the participant responds, when the audio file has finished playing, or if the participant has\n * failed to respond within a fixed length of time. You can also prevent a keyboard response from being recorded before\n * the audio has finished playing.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}\n */\nclass AudioKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio: AudioPlayerInterface;\n private params: TrialType<Info>;\n private display: HTMLElement;\n private response: { rt: number; key: string } = { rt: null, key: null };\n private startTime: number;\n private finish: ({}: { rt: number; response: string; stimulus: string }) => void;\n\n constructor(private jsPsych: JsPsych) {\n autoBind(this);\n }\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n return new Promise(async (resolve) => {\n this.finish = resolve;\n this.params = trial;\n this.display = display_element;\n // load audio file\n this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);\n\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.end_trial);\n }\n\n // show prompt if there is one\n if (trial.prompt !== null) {\n display_element.innerHTML = trial.prompt;\n }\n\n // start playing audio here to record time\n // use this for offsetting RT measurement in\n // setup_keyboard_listener\n this.startTime = this.jsPsych.pluginAPI.audioContext()?.currentTime;\n\n // start keyboard listener when trial starts or sound ends\n if (trial.response_allowed_while_playing) {\n this.setup_keyboard_listener();\n } else if (!trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.setup_keyboard_listener);\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n this.end_trial();\n }, trial.trial_duration);\n }\n\n // call trial on_load method because we are done with all loading setup\n on_load();\n\n this.audio.play();\n });\n }\n\n private end_trial() {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // remove end event listeners if they exist\n this.audio.removeEventListener(\"ended\", this.end_trial);\n this.audio.removeEventListener(\"ended\", this.setup_keyboard_listener);\n\n // stop the audio file if it is playing\n this.audio.stop();\n\n // kill keyboard listeners\n this.jsPsych.pluginAPI.cancelAllKeyboardResponses();\n\n // gather the data to store for the trial\n var trial_data = {\n rt: this.response.rt,\n response: this.response.key,\n stimulus: this.params.stimulus,\n };\n\n // clear the display\n this.display.innerHTML = \"\";\n\n // move on to the next trial\n this.finish(trial_data);\n }\n\n private after_response(info: { key: string; rt: number }) {\n this.response = info;\n if (this.params.response_ends_trial) {\n this.end_trial();\n }\n }\n\n private setup_keyboard_listener() {\n // start the response listener\n if (this.jsPsych.pluginAPI.useWebaudio) {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: this.after_response,\n valid_responses: this.params.choices,\n rt_method: \"audio\",\n persist: false,\n allow_held_key: false,\n audio_context: this.jsPsych.pluginAPI.audioContext(),\n audio_context_start_time: this.startTime,\n });\n } else {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: this.after_response,\n valid_responses: this.params.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n }\n\n async simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n return this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n return this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n return data;\n }\n\n private async simulate_visual(\n trial: TrialType<Info>,\n simulation_options,\n load_callback: () => void\n ) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n const display_element = this.jsPsych.getDisplayElement();\n\n const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.pressKey(data.response, data.rt);\n }\n };\n\n const result = await this.trial(display_element, trial, () => {\n load_callback();\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n\n return result;\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\n response: this.jsPsych.pluginAPI.getValidKey(trial.choices),\n };\n\n const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n return data;\n }\n}\n\nexport default AudioKeyboardResponsePlugin;\n"],"names":[],"mappings":";;;;;AAEE,IAAW,OAAA,GAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECmFA,SAAA,EAAA;AAAA;;GAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -78,6 +78,7 @@ declare const info: {
|
|
|
78
78
|
readonly type: ParameterType.STRING;
|
|
79
79
|
};
|
|
80
80
|
};
|
|
81
|
+
readonly citations: "__CITATIONS__";
|
|
81
82
|
};
|
|
82
83
|
type Info = typeof info;
|
|
83
84
|
/**
|
|
@@ -177,6 +178,7 @@ declare class AudioKeyboardResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
177
178
|
readonly type: ParameterType.STRING;
|
|
178
179
|
};
|
|
179
180
|
};
|
|
181
|
+
readonly citations: "__CITATIONS__";
|
|
180
182
|
};
|
|
181
183
|
private audio;
|
|
182
184
|
private params;
|
package/dist/index.js
CHANGED
|
@@ -1,95 +1,90 @@
|
|
|
1
1
|
import autoBind from 'auto-bind';
|
|
2
2
|
import { ParameterType } from 'jspsych';
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
name: "@jspsych/plugin-audio-keyboard-response",
|
|
6
|
-
version: "2.0.0",
|
|
7
|
-
description: "jsPsych plugin for playing an audio file and getting a keyboard response",
|
|
8
|
-
type: "module",
|
|
9
|
-
main: "dist/index.cjs",
|
|
10
|
-
exports: {
|
|
11
|
-
import: "./dist/index.js",
|
|
12
|
-
require: "./dist/index.cjs"
|
|
13
|
-
},
|
|
14
|
-
typings: "dist/index.d.ts",
|
|
15
|
-
unpkg: "dist/index.browser.min.js",
|
|
16
|
-
files: [
|
|
17
|
-
"src",
|
|
18
|
-
"dist"
|
|
19
|
-
],
|
|
20
|
-
source: "src/index.ts",
|
|
21
|
-
scripts: {
|
|
22
|
-
test: "jest",
|
|
23
|
-
"test:watch": "npm test -- --watch",
|
|
24
|
-
tsc: "tsc",
|
|
25
|
-
build: "rollup --config",
|
|
26
|
-
"build:watch": "npm run build -- --watch"
|
|
27
|
-
},
|
|
28
|
-
repository: {
|
|
29
|
-
type: "git",
|
|
30
|
-
url: "git+https://github.com/jspsych/jsPsych.git",
|
|
31
|
-
directory: "packages/plugin-audio-keyboard-response"
|
|
32
|
-
},
|
|
33
|
-
author: "Josh de Leeuw",
|
|
34
|
-
license: "MIT",
|
|
35
|
-
bugs: {
|
|
36
|
-
url: "https://github.com/jspsych/jsPsych/issues"
|
|
37
|
-
},
|
|
38
|
-
homepage: "https://www.jspsych.org/latest/plugins/audio-keyboard-response",
|
|
39
|
-
peerDependencies: {
|
|
40
|
-
jspsych: ">=7.1.0"
|
|
41
|
-
},
|
|
42
|
-
devDependencies: {
|
|
43
|
-
"@jspsych/config": "^3.0.0",
|
|
44
|
-
"@jspsych/test-utils": "^1.2.0"
|
|
45
|
-
}
|
|
46
|
-
};
|
|
4
|
+
var version = "2.1.1";
|
|
47
5
|
|
|
48
6
|
const info = {
|
|
49
7
|
name: "audio-keyboard-response",
|
|
50
|
-
version
|
|
8
|
+
version,
|
|
51
9
|
parameters: {
|
|
10
|
+
/** The audio file to be played. */
|
|
52
11
|
stimulus: {
|
|
53
12
|
type: ParameterType.AUDIO,
|
|
54
13
|
default: void 0
|
|
55
14
|
},
|
|
15
|
+
/** This array contains the key(s) that the participant is allowed to press in order to respond to the stimulus.
|
|
16
|
+
* Keys should be specified as characters (e.g., `'a'`, `'q'`, `' '`, `'Enter'`, `'ArrowDown'`) -
|
|
17
|
+
* see [this page](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values)
|
|
18
|
+
* and [this page (event.key column)](https://www.freecodecamp.org/news/javascript-keycode-list-keypress-event-key-codes/)
|
|
19
|
+
* for more examples. Any key presses that are not listed in the array will be ignored. The default value of `"ALL_KEYS"`
|
|
20
|
+
* means that all keys will be accepted as valid responses. Specifying `"NO_KEYS"` will mean that no responses are allowed.
|
|
21
|
+
*/
|
|
56
22
|
choices: {
|
|
57
23
|
type: ParameterType.KEYS,
|
|
58
24
|
default: "ALL_KEYS"
|
|
59
25
|
},
|
|
26
|
+
/** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that
|
|
27
|
+
* it can be used to provide a reminder about the action the participant is supposed to take (e.g., which key to press).
|
|
28
|
+
*/
|
|
60
29
|
prompt: {
|
|
61
30
|
type: ParameterType.HTML_STRING,
|
|
62
31
|
pretty_name: "Prompt",
|
|
63
32
|
default: null
|
|
64
33
|
},
|
|
34
|
+
/** How long to wait for the participant to make a response before ending the trial in milliseconds. If the
|
|
35
|
+
* participant fails to make a response before this timer is reached, the participant's response will be
|
|
36
|
+
* recorded as null for the trial and the trial will end. If the value of this parameter is null, then the
|
|
37
|
+
* trial will wait for a response indefinitely.
|
|
38
|
+
*/
|
|
65
39
|
trial_duration: {
|
|
66
40
|
type: ParameterType.INT,
|
|
67
41
|
default: null
|
|
68
42
|
},
|
|
43
|
+
/** If true, then the trial will end whenever the participant makes a response (assuming they make their
|
|
44
|
+
* response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will
|
|
45
|
+
* continue until the value for `trial_duration` is reached. You can use set this parameter to `false` to
|
|
46
|
+
* force the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete
|
|
47
|
+
*/
|
|
69
48
|
response_ends_trial: {
|
|
70
49
|
type: ParameterType.BOOL,
|
|
71
50
|
default: true
|
|
72
51
|
},
|
|
52
|
+
/** If true, then the trial will end as soon as the audio file finishes playing. */
|
|
73
53
|
trial_ends_after_audio: {
|
|
74
54
|
type: ParameterType.BOOL,
|
|
75
55
|
pretty_name: "Trial ends after audio",
|
|
76
56
|
default: false
|
|
77
57
|
},
|
|
58
|
+
/** If true, then responses are allowed while the audio is playing. If false, then the audio must finish
|
|
59
|
+
* playing before a keyboard response is accepted. Once the audio has played all the way through, a valid
|
|
60
|
+
* keyboard response is allowed (including while the audio is being re-played via on-screen playback controls).
|
|
61
|
+
*/
|
|
78
62
|
response_allowed_while_playing: {
|
|
79
63
|
type: ParameterType.BOOL,
|
|
80
64
|
default: true
|
|
81
65
|
}
|
|
82
66
|
},
|
|
83
67
|
data: {
|
|
68
|
+
/** Indicates which key the participant pressed. If no key was pressed before the trial ended, then the value will be `null`. */
|
|
84
69
|
response: {
|
|
85
70
|
type: ParameterType.STRING
|
|
86
71
|
},
|
|
72
|
+
/** The response time in milliseconds for the participant to make a response. The time is measured from when the stimulus
|
|
73
|
+
* first began playing until the participant made a key response. If no key was pressed before the trial ended, then the
|
|
74
|
+
* value will be `null`.
|
|
75
|
+
*/
|
|
87
76
|
rt: {
|
|
88
77
|
type: ParameterType.INT
|
|
89
78
|
},
|
|
79
|
+
/** Path to the audio file that played during the trial. */
|
|
90
80
|
stimulus: {
|
|
91
81
|
type: ParameterType.STRING
|
|
92
82
|
}
|
|
83
|
+
},
|
|
84
|
+
// prettier-ignore
|
|
85
|
+
citations: {
|
|
86
|
+
"apa": "de Leeuw, J. R., Gilbert, R. A., & Luchterhandt, B. (2023). jsPsych: Enabling an Open-Source Collaborative Ecosystem of Behavioral Experiments. Journal of Open Source Software, 8(85), 5351. https://doi.org/10.21105/joss.05351 ",
|
|
87
|
+
"bibtex": '@article{Leeuw2023jsPsych, author = {de Leeuw, Joshua R. and Gilbert, Rebecca A. and Luchterhandt, Bj{\\" o}rn}, journal = {Journal of Open Source Software}, doi = {10.21105/joss.05351}, issn = {2475-9066}, number = {85}, year = {2023}, month = {may 11}, pages = {5351}, publisher = {Open Journals}, title = {jsPsych: Enabling an {Open}-{Source} {Collaborative} {Ecosystem} of {Behavioral} {Experiments}}, url = {https://joss.theoj.org/papers/10.21105/joss.05351}, volume = {8}, } '
|
|
93
88
|
}
|
|
94
89
|
};
|
|
95
90
|
class AudioKeyboardResponsePlugin {
|
|
@@ -98,6 +93,9 @@ class AudioKeyboardResponsePlugin {
|
|
|
98
93
|
this.response = { rt: null, key: null };
|
|
99
94
|
autoBind(this);
|
|
100
95
|
}
|
|
96
|
+
static {
|
|
97
|
+
this.info = info;
|
|
98
|
+
}
|
|
101
99
|
trial(display_element, trial, on_load) {
|
|
102
100
|
return new Promise(async (resolve) => {
|
|
103
101
|
this.finish = resolve;
|
|
@@ -127,9 +125,9 @@ class AudioKeyboardResponsePlugin {
|
|
|
127
125
|
}
|
|
128
126
|
end_trial() {
|
|
129
127
|
this.jsPsych.pluginAPI.clearAllTimeouts();
|
|
130
|
-
this.audio.stop();
|
|
131
128
|
this.audio.removeEventListener("ended", this.end_trial);
|
|
132
129
|
this.audio.removeEventListener("ended", this.setup_keyboard_listener);
|
|
130
|
+
this.audio.stop();
|
|
133
131
|
this.jsPsych.pluginAPI.cancelAllKeyboardResponses();
|
|
134
132
|
var trial_data = {
|
|
135
133
|
rt: this.response.rt,
|
|
@@ -208,7 +206,6 @@ class AudioKeyboardResponsePlugin {
|
|
|
208
206
|
return data;
|
|
209
207
|
}
|
|
210
208
|
}
|
|
211
|
-
AudioKeyboardResponsePlugin.info = info;
|
|
212
209
|
|
|
213
210
|
export { AudioKeyboardResponsePlugin as default };
|
|
214
211
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import autoBind from \"auto-bind\";\nimport { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { AudioPlayerInterface } from \"../../jspsych/src/modules/plugin-api/AudioPlayer\";\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"audio-keyboard-response\",\n version: version,\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n default: undefined,\n },\n /** This array contains the key(s) that the participant is allowed to press in order to respond to the stimulus.\n * Keys should be specified as characters (e.g., `'a'`, `'q'`, `' '`, `'Enter'`, `'ArrowDown'`) -\n * see [this page](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values)\n * and [this page (event.key column)](https://www.freecodecamp.org/news/javascript-keycode-list-keypress-event-key-codes/)\n * for more examples. Any key presses that are not listed in the array will be ignored. The default value of `\"ALL_KEYS\"`\n * means that all keys will be accepted as valid responses. Specifying `\"NO_KEYS\"` will mean that no responses are allowed.\n */\n choices: {\n type: ParameterType.KEYS,\n default: \"ALL_KEYS\",\n },\n /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that\n * it can be used to provide a reminder about the action the participant is supposed to take (e.g., which key to press).\n */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to wait for the participant to make a response before ending the trial in milliseconds. If the\n * participant fails to make a response before this timer is reached, the participant's response will be\n * recorded as null for the trial and the trial will end. If the value of this parameter is null, then the\n * trial will wait for a response indefinitely.\n */\n trial_duration: {\n type: ParameterType.INT,\n default: null,\n },\n /** If true, then the trial will end whenever the participant makes a response (assuming they make their\n * response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will\n * continue until the value for `trial_duration` is reached. You can use set this parameter to `false` to\n * force the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete\n */\n response_ends_trial: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n pretty_name: \"Trial ends after audio\",\n default: false,\n },\n /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish\n * playing before a keyboard response is accepted. Once the audio has played all the way through, a valid\n * keyboard response is allowed (including while the audio is being re-played via on-screen playback controls).\n */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n default: true,\n },\n },\n data: {\n /** Indicates which key the participant pressed. If no key was pressed before the trial ended, then the value will be `null`. */\n response: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from when the stimulus\n * first began playing until the participant made a key response. If no key was pressed before the trial ended, then the\n * value will be `null`.\n */\n rt: {\n type: ParameterType.INT,\n },\n /** Path to the audio file that played during the trial. */\n stimulus: {\n type: ParameterType.STRING,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * This plugin plays audio files and records responses generated with the keyboard.\n *\n * If the browser supports it, audio files are played using the WebAudio API. This allows for reasonably precise timing of the\n * playback. The timing of responses generated is measured against the WebAudio specific clock, improving the measurement of\n * response times. If the browser does not support the WebAudio API, then the audio file is played with HTML5 audio.\n *\n * Audio files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if you are using\n * timeline variables or another dynamic method to specify the audio stimulus, then you will need to [manually preload](../overview/media-preloading.md#manual-preloading) the audio.\n *\n * The trial can end when the participant responds, when the audio file has finished playing, or if the participant has\n * failed to respond within a fixed length of time. You can also prevent a keyboard response from being recorded before\n * the audio has finished playing.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}\n */\nclass AudioKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio: AudioPlayerInterface;\n private params: TrialType<Info>;\n private display: HTMLElement;\n private response: { rt: number; key: string } = { rt: null, key: null };\n private startTime: number;\n private finish: ({}: { rt: number; response: string; stimulus: string }) => void;\n\n constructor(private jsPsych: JsPsych) {\n autoBind(this);\n }\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n return new Promise(async (resolve) => {\n this.finish = resolve;\n this.params = trial;\n this.display = display_element;\n // load audio file\n this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);\n\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.end_trial);\n }\n\n // show prompt if there is one\n if (trial.prompt !== null) {\n display_element.innerHTML = trial.prompt;\n }\n\n // start playing audio here to record time\n // use this for offsetting RT measurement in\n // setup_keyboard_listener\n this.startTime = this.jsPsych.pluginAPI.audioContext()?.currentTime;\n\n // start keyboard listener when trial starts or sound ends\n if (trial.response_allowed_while_playing) {\n this.setup_keyboard_listener();\n } else if (!trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.setup_keyboard_listener);\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n this.end_trial();\n }, trial.trial_duration);\n }\n\n // call trial on_load method because we are done with all loading setup\n on_load();\n\n this.audio.play();\n });\n }\n\n private end_trial() {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // stop the audio file if it is playing\n this.audio.stop();\n\n // remove end event listeners if they exist\n this.audio.removeEventListener(\"ended\", this.end_trial);\n this.audio.removeEventListener(\"ended\", this.setup_keyboard_listener);\n\n // kill keyboard listeners\n this.jsPsych.pluginAPI.cancelAllKeyboardResponses();\n\n // gather the data to store for the trial\n var trial_data = {\n rt: this.response.rt,\n response: this.response.key,\n stimulus: this.params.stimulus,\n };\n\n // clear the display\n this.display.innerHTML = \"\";\n\n // move on to the next trial\n this.finish(trial_data);\n }\n\n private after_response(info: { key: string; rt: number }) {\n this.response = info;\n if (this.params.response_ends_trial) {\n this.end_trial();\n }\n }\n\n private setup_keyboard_listener() {\n // start the response listener\n if (this.jsPsych.pluginAPI.useWebaudio) {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: this.after_response,\n valid_responses: this.params.choices,\n rt_method: \"audio\",\n persist: false,\n allow_held_key: false,\n audio_context: this.jsPsych.pluginAPI.audioContext(),\n audio_context_start_time: this.startTime,\n });\n } else {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: this.after_response,\n valid_responses: this.params.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n }\n\n async simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n return this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n return this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n return data;\n }\n\n private async simulate_visual(\n trial: TrialType<Info>,\n simulation_options,\n load_callback: () => void\n ) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n const display_element = this.jsPsych.getDisplayElement();\n\n const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.pressKey(data.response, data.rt);\n }\n };\n\n const result = await this.trial(display_element, trial, () => {\n load_callback();\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n\n return result;\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\n response: this.jsPsych.pluginAPI.getValidKey(trial.choices),\n };\n\n const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n return data;\n }\n}\n\nexport default AudioKeyboardResponsePlugin;\n"],"names":["version","info"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../package.json","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@jspsych/plugin-audio-keyboard-response\",\n \"version\": \"2.1.1\",\n \"description\": \"jsPsych plugin for playing an audio file and getting a keyboard response\",\n \"type\": \"module\",\n \"main\": \"dist/index.cjs\",\n \"exports\": {\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n },\n \"typings\": \"dist/index.d.ts\",\n \"unpkg\": \"dist/index.browser.min.js\",\n \"files\": [\n \"src\",\n \"dist\"\n ],\n \"source\": \"src/index.ts\",\n \"scripts\": {\n \"test\": \"jest\",\n \"test:watch\": \"npm test -- --watch\",\n \"tsc\": \"tsc\",\n \"build\": \"rollup --config\",\n \"build:watch\": \"npm run build -- --watch\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/jspsych/jsPsych.git\",\n \"directory\": \"packages/plugin-audio-keyboard-response\"\n },\n \"author\": \"Josh de Leeuw\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/jspsych/jsPsych/issues\"\n },\n \"homepage\": \"https://www.jspsych.org/latest/plugins/audio-keyboard-response\",\n \"peerDependencies\": {\n \"jspsych\": \">=7.1.0\"\n },\n \"devDependencies\": {\n \"@jspsych/config\": \"^3.2.0\",\n \"@jspsych/test-utils\": \"^1.2.0\"\n }\n}\n","import autoBind from \"auto-bind\";\nimport { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { AudioPlayerInterface } from \"../../jspsych/src/modules/plugin-api/AudioPlayer\";\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n name: \"audio-keyboard-response\",\n version: version,\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n default: undefined,\n },\n /** This array contains the key(s) that the participant is allowed to press in order to respond to the stimulus.\n * Keys should be specified as characters (e.g., `'a'`, `'q'`, `' '`, `'Enter'`, `'ArrowDown'`) -\n * see [this page](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values)\n * and [this page (event.key column)](https://www.freecodecamp.org/news/javascript-keycode-list-keypress-event-key-codes/)\n * for more examples. Any key presses that are not listed in the array will be ignored. The default value of `\"ALL_KEYS\"`\n * means that all keys will be accepted as valid responses. Specifying `\"NO_KEYS\"` will mean that no responses are allowed.\n */\n choices: {\n type: ParameterType.KEYS,\n default: \"ALL_KEYS\",\n },\n /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that\n * it can be used to provide a reminder about the action the participant is supposed to take (e.g., which key to press).\n */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to wait for the participant to make a response before ending the trial in milliseconds. If the\n * participant fails to make a response before this timer is reached, the participant's response will be\n * recorded as null for the trial and the trial will end. If the value of this parameter is null, then the\n * trial will wait for a response indefinitely.\n */\n trial_duration: {\n type: ParameterType.INT,\n default: null,\n },\n /** If true, then the trial will end whenever the participant makes a response (assuming they make their\n * response before the cutoff specified by the `trial_duration` parameter). If false, then the trial will\n * continue until the value for `trial_duration` is reached. You can use set this parameter to `false` to\n * force the participant to listen to the stimulus for a fixed amount of time, even if they respond before the time is complete\n */\n response_ends_trial: {\n type: ParameterType.BOOL,\n default: true,\n },\n /** If true, then the trial will end as soon as the audio file finishes playing. */\n trial_ends_after_audio: {\n type: ParameterType.BOOL,\n pretty_name: \"Trial ends after audio\",\n default: false,\n },\n /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish\n * playing before a keyboard response is accepted. Once the audio has played all the way through, a valid\n * keyboard response is allowed (including while the audio is being re-played via on-screen playback controls).\n */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n default: true,\n },\n },\n data: {\n /** Indicates which key the participant pressed. If no key was pressed before the trial ended, then the value will be `null`. */\n response: {\n type: ParameterType.STRING,\n },\n /** The response time in milliseconds for the participant to make a response. The time is measured from when the stimulus\n * first began playing until the participant made a key response. If no key was pressed before the trial ended, then the\n * value will be `null`.\n */\n rt: {\n type: ParameterType.INT,\n },\n /** Path to the audio file that played during the trial. */\n stimulus: {\n type: ParameterType.STRING,\n },\n },\n // prettier-ignore\n citations: '__CITATIONS__',\n};\n\ntype Info = typeof info;\n\n/**\n * This plugin plays audio files and records responses generated with the keyboard.\n *\n * If the browser supports it, audio files are played using the WebAudio API. This allows for reasonably precise timing of the\n * playback. The timing of responses generated is measured against the WebAudio specific clock, improving the measurement of\n * response times. If the browser does not support the WebAudio API, then the audio file is played with HTML5 audio.\n *\n * Audio files can be automatically preloaded by jsPsych using the [`preload` plugin](preload.md). However, if you are using\n * timeline variables or another dynamic method to specify the audio stimulus, then you will need to [manually preload](../overview/media-preloading.md#manual-preloading) the audio.\n *\n * The trial can end when the participant responds, when the audio file has finished playing, or if the participant has\n * failed to respond within a fixed length of time. You can also prevent a keyboard response from being recorded before\n * the audio has finished playing.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}\n */\nclass AudioKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio: AudioPlayerInterface;\n private params: TrialType<Info>;\n private display: HTMLElement;\n private response: { rt: number; key: string } = { rt: null, key: null };\n private startTime: number;\n private finish: ({}: { rt: number; response: string; stimulus: string }) => void;\n\n constructor(private jsPsych: JsPsych) {\n autoBind(this);\n }\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n return new Promise(async (resolve) => {\n this.finish = resolve;\n this.params = trial;\n this.display = display_element;\n // load audio file\n this.audio = await this.jsPsych.pluginAPI.getAudioPlayer(trial.stimulus);\n\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.end_trial);\n }\n\n // show prompt if there is one\n if (trial.prompt !== null) {\n display_element.innerHTML = trial.prompt;\n }\n\n // start playing audio here to record time\n // use this for offsetting RT measurement in\n // setup_keyboard_listener\n this.startTime = this.jsPsych.pluginAPI.audioContext()?.currentTime;\n\n // start keyboard listener when trial starts or sound ends\n if (trial.response_allowed_while_playing) {\n this.setup_keyboard_listener();\n } else if (!trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", this.setup_keyboard_listener);\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n this.end_trial();\n }, trial.trial_duration);\n }\n\n // call trial on_load method because we are done with all loading setup\n on_load();\n\n this.audio.play();\n });\n }\n\n private end_trial() {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // remove end event listeners if they exist\n this.audio.removeEventListener(\"ended\", this.end_trial);\n this.audio.removeEventListener(\"ended\", this.setup_keyboard_listener);\n\n // stop the audio file if it is playing\n this.audio.stop();\n\n // kill keyboard listeners\n this.jsPsych.pluginAPI.cancelAllKeyboardResponses();\n\n // gather the data to store for the trial\n var trial_data = {\n rt: this.response.rt,\n response: this.response.key,\n stimulus: this.params.stimulus,\n };\n\n // clear the display\n this.display.innerHTML = \"\";\n\n // move on to the next trial\n this.finish(trial_data);\n }\n\n private after_response(info: { key: string; rt: number }) {\n this.response = info;\n if (this.params.response_ends_trial) {\n this.end_trial();\n }\n }\n\n private setup_keyboard_listener() {\n // start the response listener\n if (this.jsPsych.pluginAPI.useWebaudio) {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: this.after_response,\n valid_responses: this.params.choices,\n rt_method: \"audio\",\n persist: false,\n allow_held_key: false,\n audio_context: this.jsPsych.pluginAPI.audioContext(),\n audio_context_start_time: this.startTime,\n });\n } else {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: this.after_response,\n valid_responses: this.params.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n }\n\n async simulate(\n trial: TrialType<Info>,\n simulation_mode,\n simulation_options: any,\n load_callback: () => void\n ) {\n if (simulation_mode == \"data-only\") {\n load_callback();\n return this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n return this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n return data;\n }\n\n private async simulate_visual(\n trial: TrialType<Info>,\n simulation_options,\n load_callback: () => void\n ) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n const display_element = this.jsPsych.getDisplayElement();\n\n const respond = () => {\n if (data.rt !== null) {\n this.jsPsych.pluginAPI.pressKey(data.response, data.rt);\n }\n };\n\n const result = await this.trial(display_element, trial, () => {\n load_callback();\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n\n return result;\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\n response: this.jsPsych.pluginAPI.getValidKey(trial.choices),\n };\n\n const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n return data;\n }\n}\n\nexport default AudioKeyboardResponsePlugin;\n"],"names":[],"mappings":";;;AAEE,IAAW,OAAA,GAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECmFA,SAAA,EAAA;AAAA;;GAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jspsych/plugin-audio-keyboard-response",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
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": "^3.
|
|
40
|
+
"@jspsych/config": "^3.2.0",
|
|
41
41
|
"@jspsych/test-utils": "^1.2.0"
|
|
42
42
|
}
|
|
43
43
|
}
|
package/src/index.ts
CHANGED
|
@@ -82,6 +82,8 @@ const info = <const>{
|
|
|
82
82
|
type: ParameterType.STRING,
|
|
83
83
|
},
|
|
84
84
|
},
|
|
85
|
+
// prettier-ignore
|
|
86
|
+
citations: '__CITATIONS__',
|
|
85
87
|
};
|
|
86
88
|
|
|
87
89
|
type Info = typeof info;
|
|
@@ -164,13 +166,13 @@ class AudioKeyboardResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
164
166
|
// kill any remaining setTimeout handlers
|
|
165
167
|
this.jsPsych.pluginAPI.clearAllTimeouts();
|
|
166
168
|
|
|
167
|
-
// stop the audio file if it is playing
|
|
168
|
-
this.audio.stop();
|
|
169
|
-
|
|
170
169
|
// remove end event listeners if they exist
|
|
171
170
|
this.audio.removeEventListener("ended", this.end_trial);
|
|
172
171
|
this.audio.removeEventListener("ended", this.setup_keyboard_listener);
|
|
173
172
|
|
|
173
|
+
// stop the audio file if it is playing
|
|
174
|
+
this.audio.stop();
|
|
175
|
+
|
|
174
176
|
// kill keyboard listeners
|
|
175
177
|
this.jsPsych.pluginAPI.cancelAllKeyboardResponses();
|
|
176
178
|
|