@jspsych/plugin-audio-keyboard-response 1.0.0

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.
@@ -0,0 +1,198 @@
1
+ var jsPsychAudioKeyboardResponse = (function (jspsych) {
2
+ 'use strict';
3
+
4
+ const info = {
5
+ name: "audio-keyboard-response",
6
+ parameters: {
7
+ /** The audio file to be played. */
8
+ stimulus: {
9
+ type: jspsych.ParameterType.AUDIO,
10
+ pretty_name: "Stimulus",
11
+ default: undefined,
12
+ },
13
+ /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */
14
+ choices: {
15
+ type: jspsych.ParameterType.KEYS,
16
+ pretty_name: "Choices",
17
+ default: "ALL_KEYS",
18
+ },
19
+ /** Any content here will be displayed below the stimulus. */
20
+ prompt: {
21
+ type: jspsych.ParameterType.HTML_STRING,
22
+ pretty_name: "Prompt",
23
+ default: null,
24
+ },
25
+ /** The maximum duration to wait for a response. */
26
+ trial_duration: {
27
+ type: jspsych.ParameterType.INT,
28
+ pretty_name: "Trial duration",
29
+ default: null,
30
+ },
31
+ /** If true, the trial will end when user makes a response. */
32
+ response_ends_trial: {
33
+ type: jspsych.ParameterType.BOOL,
34
+ pretty_name: "Response ends trial",
35
+ default: true,
36
+ },
37
+ /** If true, then the trial will end as soon as the audio file finishes playing. */
38
+ trial_ends_after_audio: {
39
+ type: jspsych.ParameterType.BOOL,
40
+ pretty_name: "Trial ends after audio",
41
+ default: false,
42
+ },
43
+ /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */
44
+ response_allowed_while_playing: {
45
+ type: jspsych.ParameterType.BOOL,
46
+ pretty_name: "Response allowed while playing",
47
+ default: true,
48
+ },
49
+ },
50
+ };
51
+ /**
52
+ * **audio-keyboard-response**
53
+ *
54
+ * jsPsych plugin for playing an audio file and getting a keyboard response
55
+ *
56
+ * @author Josh de Leeuw
57
+ * @see {@link https://www.jspsych.org/plugins/jspsych-audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}
58
+ */
59
+ class AudioKeyboardResponsePlugin {
60
+ constructor(jsPsych) {
61
+ this.jsPsych = jsPsych;
62
+ }
63
+ trial(display_element, trial, on_load) {
64
+ // hold the .resolve() function from the Promise that ends the trial
65
+ let trial_complete;
66
+ // setup stimulus
67
+ var context = this.jsPsych.pluginAPI.audioContext();
68
+ var audio;
69
+ // store response
70
+ var response = {
71
+ rt: null,
72
+ key: null,
73
+ };
74
+ // record webaudio context start time
75
+ var startTime;
76
+ // load audio file
77
+ this.jsPsych.pluginAPI
78
+ .getAudioBuffer(trial.stimulus)
79
+ .then(function (buffer) {
80
+ if (context !== null) {
81
+ audio = context.createBufferSource();
82
+ audio.buffer = buffer;
83
+ audio.connect(context.destination);
84
+ }
85
+ else {
86
+ audio = buffer;
87
+ audio.currentTime = 0;
88
+ }
89
+ setupTrial();
90
+ })
91
+ .catch(function (err) {
92
+ console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`);
93
+ console.error(err);
94
+ });
95
+ const setupTrial = () => {
96
+ // set up end event if trial needs it
97
+ if (trial.trial_ends_after_audio) {
98
+ audio.addEventListener("ended", end_trial);
99
+ }
100
+ // show prompt if there is one
101
+ if (trial.prompt !== null) {
102
+ display_element.innerHTML = trial.prompt;
103
+ }
104
+ // start audio
105
+ if (context !== null) {
106
+ startTime = context.currentTime;
107
+ audio.start(startTime);
108
+ }
109
+ else {
110
+ audio.play();
111
+ }
112
+ // start keyboard listener when trial starts or sound ends
113
+ if (trial.response_allowed_while_playing) {
114
+ setup_keyboard_listener();
115
+ }
116
+ else if (!trial.trial_ends_after_audio) {
117
+ audio.addEventListener("ended", setup_keyboard_listener);
118
+ }
119
+ // end trial if time limit is set
120
+ if (trial.trial_duration !== null) {
121
+ this.jsPsych.pluginAPI.setTimeout(function () {
122
+ end_trial();
123
+ }, trial.trial_duration);
124
+ }
125
+ on_load();
126
+ };
127
+ // function to end trial when it is time
128
+ const end_trial = () => {
129
+ // kill any remaining setTimeout handlers
130
+ this.jsPsych.pluginAPI.clearAllTimeouts();
131
+ // stop the audio file if it is playing
132
+ // remove end event listeners if they exist
133
+ if (context !== null) {
134
+ audio.stop();
135
+ }
136
+ else {
137
+ audio.pause();
138
+ }
139
+ audio.removeEventListener("ended", end_trial);
140
+ audio.removeEventListener("ended", setup_keyboard_listener);
141
+ // kill keyboard listeners
142
+ this.jsPsych.pluginAPI.cancelAllKeyboardResponses();
143
+ // gather the data to store for the trial
144
+ var trial_data = {
145
+ rt: response.rt,
146
+ stimulus: trial.stimulus,
147
+ response: response.key,
148
+ };
149
+ // clear the display
150
+ display_element.innerHTML = "";
151
+ // move on to the next trial
152
+ this.jsPsych.finishTrial(trial_data);
153
+ trial_complete();
154
+ };
155
+ // function to handle responses by the subject
156
+ function after_response(info) {
157
+ // only record the first response
158
+ if (response.key == null) {
159
+ response = info;
160
+ }
161
+ if (trial.response_ends_trial) {
162
+ end_trial();
163
+ }
164
+ }
165
+ const setup_keyboard_listener = () => {
166
+ // start the response listener
167
+ if (context !== null) {
168
+ this.jsPsych.pluginAPI.getKeyboardResponse({
169
+ callback_function: after_response,
170
+ valid_responses: trial.choices,
171
+ rt_method: "audio",
172
+ persist: false,
173
+ allow_held_key: false,
174
+ audio_context: context,
175
+ audio_context_start_time: startTime,
176
+ });
177
+ }
178
+ else {
179
+ this.jsPsych.pluginAPI.getKeyboardResponse({
180
+ callback_function: after_response,
181
+ valid_responses: trial.choices,
182
+ rt_method: "performance",
183
+ persist: false,
184
+ allow_held_key: false,
185
+ });
186
+ }
187
+ };
188
+ return new Promise((resolve) => {
189
+ trial_complete = resolve;
190
+ });
191
+ }
192
+ }
193
+ AudioKeyboardResponsePlugin.info = info;
194
+
195
+ return AudioKeyboardResponsePlugin;
196
+
197
+ })(jsPsychModule);
198
+ //# sourceMappingURL=index.browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.browser.js","sources":["../src/index.ts"],"sourcesContent":["import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nconst info = <const>{\n name: \"audio-keyboard-response\",\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n pretty_name: \"Stimulus\",\n default: undefined,\n },\n /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */\n choices: {\n type: ParameterType.KEYS,\n pretty_name: \"Choices\",\n default: \"ALL_KEYS\",\n },\n /** Any content here will be displayed below the stimulus. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** The maximum duration to wait for a response. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, the trial will end when user makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\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 playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * **audio-keyboard-response**\n *\n * jsPsych plugin for playing an audio file and getting a keyboard response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}\n */\nclass AudioKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n // hold the .resolve() function from the Promise that ends the trial\n let trial_complete;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n var audio;\n\n // store response\n var response = {\n rt: null,\n key: null,\n };\n\n // record webaudio context start time\n var startTime;\n\n // load audio file\n this.jsPsych.pluginAPI\n .getAudioBuffer(trial.stimulus)\n .then(function (buffer) {\n if (context !== null) {\n audio = context.createBufferSource();\n audio.buffer = buffer;\n audio.connect(context.destination);\n } else {\n audio = buffer;\n audio.currentTime = 0;\n }\n setupTrial();\n })\n .catch(function (err) {\n console.error(\n `Failed to load audio file \"${trial.stimulus}\". Try checking the file path. We recommend using the preload plugin to load audio files.`\n );\n console.error(err);\n });\n\n const setupTrial = () => {\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", 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 audio\n if (context !== null) {\n startTime = context.currentTime;\n audio.start(startTime);\n } else {\n audio.play();\n }\n\n // start keyboard listener when trial starts or sound ends\n if (trial.response_allowed_while_playing) {\n setup_keyboard_listener();\n } else if (!trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", setup_keyboard_listener);\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(function () {\n end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n };\n\n // function to end trial when it is time\n const end_trial = () => {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // stop the audio file if it is playing\n // remove end event listeners if they exist\n if (context !== null) {\n audio.stop();\n } else {\n audio.pause();\n }\n\n audio.removeEventListener(\"ended\", end_trial);\n audio.removeEventListener(\"ended\", 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: response.rt,\n stimulus: trial.stimulus,\n response: response.key,\n };\n\n // clear the display\n display_element.innerHTML = \"\";\n\n // move on to the next trial\n this.jsPsych.finishTrial(trial_data);\n\n trial_complete();\n };\n\n // function to handle responses by the subject\n function after_response(info) {\n // only record the first response\n if (response.key == null) {\n response = info;\n }\n\n if (trial.response_ends_trial) {\n end_trial();\n }\n }\n\n const setup_keyboard_listener = () => {\n // start the response listener\n if (context !== null) {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"audio\",\n persist: false,\n allow_held_key: false,\n audio_context: context,\n audio_context_start_time: startTime,\n });\n } else {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n}\n\nexport default AudioKeyboardResponsePlugin;\n"],"names":["ParameterType"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ var jsPsychAudioKeyboardResponse=function(e){"use strict";const t={name:"audio-keyboard-response",parameters:{stimulus:{type:e.ParameterType.AUDIO,pretty_name:"Stimulus",default:void 0},choices:{type:e.ParameterType.KEYS,pretty_name:"Choices",default:"ALL_KEYS"},prompt:{type:e.ParameterType.HTML_STRING,pretty_name:"Prompt",default:null},trial_duration:{type:e.ParameterType.INT,pretty_name:"Trial duration",default:null},response_ends_trial:{type:e.ParameterType.BOOL,pretty_name:"Response ends trial",default:!0},trial_ends_after_audio:{type:e.ParameterType.BOOL,pretty_name:"Trial ends after audio",default:!1},response_allowed_while_playing:{type:e.ParameterType.BOOL,pretty_name:"Response allowed while playing",default:!0}}};class s{constructor(e){this.jsPsych=e}trial(e,t,s){let n;var r,a,i=this.jsPsych.pluginAPI.audioContext(),l={rt:null,key:null};this.jsPsych.pluginAPI.getAudioBuffer(t.stimulus).then((function(e){null!==i?((r=i.createBufferSource()).buffer=e,r.connect(i.destination)):(r=e).currentTime=0,o()})).catch((function(e){console.error(`Failed to load audio file "${t.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`),console.error(e)}));const o=()=>{t.trial_ends_after_audio&&r.addEventListener("ended",u),null!==t.prompt&&(e.innerHTML=t.prompt),null!==i?(a=i.currentTime,r.start(a)):r.play(),t.response_allowed_while_playing?p():t.trial_ends_after_audio||r.addEventListener("ended",p),null!==t.trial_duration&&this.jsPsych.pluginAPI.setTimeout((function(){u()}),t.trial_duration),s()},u=()=>{this.jsPsych.pluginAPI.clearAllTimeouts(),null!==i?r.stop():r.pause(),r.removeEventListener("ended",u),r.removeEventListener("ended",p),this.jsPsych.pluginAPI.cancelAllKeyboardResponses();var s={rt:l.rt,stimulus:t.stimulus,response:l.key};e.innerHTML="",this.jsPsych.finishTrial(s),n()};function d(e){null==l.key&&(l=e),t.response_ends_trial&&u()}const p=()=>{null!==i?this.jsPsych.pluginAPI.getKeyboardResponse({callback_function:d,valid_responses:t.choices,rt_method:"audio",persist:!1,allow_held_key:!1,audio_context:i,audio_context_start_time:a}):this.jsPsych.pluginAPI.getKeyboardResponse({callback_function:d,valid_responses:t.choices,rt_method:"performance",persist:!1,allow_held_key:!1})};return new Promise((e=>{n=e}))}}return s.info=t,s}(jsPsychModule);
2
+ //# sourceMappingURL=index.browser.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.browser.min.js","sources":["../src/index.ts"],"sourcesContent":["import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nconst info = <const>{\n name: \"audio-keyboard-response\",\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n pretty_name: \"Stimulus\",\n default: undefined,\n },\n /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */\n choices: {\n type: ParameterType.KEYS,\n pretty_name: \"Choices\",\n default: \"ALL_KEYS\",\n },\n /** Any content here will be displayed below the stimulus. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** The maximum duration to wait for a response. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, the trial will end when user makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\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 playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * **audio-keyboard-response**\n *\n * jsPsych plugin for playing an audio file and getting a keyboard response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}\n */\nclass AudioKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n // hold the .resolve() function from the Promise that ends the trial\n let trial_complete;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n var audio;\n\n // store response\n var response = {\n rt: null,\n key: null,\n };\n\n // record webaudio context start time\n var startTime;\n\n // load audio file\n this.jsPsych.pluginAPI\n .getAudioBuffer(trial.stimulus)\n .then(function (buffer) {\n if (context !== null) {\n audio = context.createBufferSource();\n audio.buffer = buffer;\n audio.connect(context.destination);\n } else {\n audio = buffer;\n audio.currentTime = 0;\n }\n setupTrial();\n })\n .catch(function (err) {\n console.error(\n `Failed to load audio file \"${trial.stimulus}\". Try checking the file path. We recommend using the preload plugin to load audio files.`\n );\n console.error(err);\n });\n\n const setupTrial = () => {\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", 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 audio\n if (context !== null) {\n startTime = context.currentTime;\n audio.start(startTime);\n } else {\n audio.play();\n }\n\n // start keyboard listener when trial starts or sound ends\n if (trial.response_allowed_while_playing) {\n setup_keyboard_listener();\n } else if (!trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", setup_keyboard_listener);\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(function () {\n end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n };\n\n // function to end trial when it is time\n const end_trial = () => {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // stop the audio file if it is playing\n // remove end event listeners if they exist\n if (context !== null) {\n audio.stop();\n } else {\n audio.pause();\n }\n\n audio.removeEventListener(\"ended\", end_trial);\n audio.removeEventListener(\"ended\", 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: response.rt,\n stimulus: trial.stimulus,\n response: response.key,\n };\n\n // clear the display\n display_element.innerHTML = \"\";\n\n // move on to the next trial\n this.jsPsych.finishTrial(trial_data);\n\n trial_complete();\n };\n\n // function to handle responses by the subject\n function after_response(info) {\n // only record the first response\n if (response.key == null) {\n response = info;\n }\n\n if (trial.response_ends_trial) {\n end_trial();\n }\n }\n\n const setup_keyboard_listener = () => {\n // start the response listener\n if (context !== null) {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"audio\",\n persist: false,\n allow_held_key: false,\n audio_context: context,\n audio_context_start_time: startTime,\n });\n } else {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n}\n\nexport default AudioKeyboardResponsePlugin;\n"],"names":["info","name","parameters","stimulus","type","ParameterType","AUDIO","pretty_name","default","undefined","choices","KEYS","prompt","HTML_STRING","trial_duration","INT","response_ends_trial","BOOL","trial_ends_after_audio","response_allowed_while_playing","AudioKeyboardResponsePlugin","constructor","jsPsych","this","trial","display_element","on_load","trial_complete","audio","startTime","context","pluginAPI","audioContext","response","rt","key","getAudioBuffer","then","buffer","createBufferSource","connect","destination","currentTime","setupTrial","catch","err","console","error","addEventListener","end_trial","innerHTML","start","play","setup_keyboard_listener","setTimeout","clearAllTimeouts","stop","pause","removeEventListener","cancelAllKeyboardResponses","trial_data","finishTrial","after_response","getKeyboardResponse","callback_function","valid_responses","rt_method","persist","allow_held_key","audio_context","audio_context_start_time","Promise","resolve"],"mappings":"0DAEA,MAAMA,EAAc,CAClBC,KAAM,0BACNC,WAAY,CAEVC,SAAU,CACRC,KAAMC,gBAAcC,MACpBC,YAAa,WACbC,aAASC,GAGXC,QAAS,CACPN,KAAMC,gBAAcM,KACpBJ,YAAa,UACbC,QAAS,YAGXI,OAAQ,CACNR,KAAMC,gBAAcQ,YACpBN,YAAa,SACbC,QAAS,MAGXM,eAAgB,CACdV,KAAMC,gBAAcU,IACpBR,YAAa,iBACbC,QAAS,MAGXQ,oBAAqB,CACnBZ,KAAMC,gBAAcY,KACpBV,YAAa,sBACbC,SAAS,GAGXU,uBAAwB,CACtBd,KAAMC,gBAAcY,KACpBV,YAAa,yBACbC,SAAS,GAGXW,+BAAgC,CAC9Bf,KAAMC,gBAAcY,KACpBV,YAAa,iCACbC,SAAS,KAef,MAAMY,EAGJC,YAAoBC,GAAAC,aAAAD,EAEpBE,MAAMC,EAA8BD,EAAwBE,GAE1D,IAAIC,EAGJ,IACIC,EASAC,EAVAC,EAAUP,KAAKD,QAAQS,UAAUC,eAIjCC,EAAW,CACbC,GAAI,KACJC,IAAK,MAOPZ,KAAKD,QAAQS,UACVK,eAAeZ,EAAMrB,UACrBkC,MAAK,SAAUC,GACE,OAAZR,IACFF,EAAQE,EAAQS,sBACVD,OAASA,EACfV,EAAMY,QAAQV,EAAQW,eAEtBb,EAAQU,GACFI,YAAc,EAEtBC,OAEDC,OAAM,SAAUC,GACfC,QAAQC,MACN,8BAA8BvB,EAAMrB,qGAEtC2C,QAAQC,MAAMF,MAGlB,MAAMF,EAAa,KAEbnB,EAAMN,wBACRU,EAAMoB,iBAAiB,QAASC,GAIb,OAAjBzB,EAAMZ,SACRa,EAAgByB,UAAY1B,EAAMZ,QAIpB,OAAZkB,GACFD,EAAYC,EAAQY,YACpBd,EAAMuB,MAAMtB,IAEZD,EAAMwB,OAIJ5B,EAAML,+BACRkC,IACU7B,EAAMN,wBAChBU,EAAMoB,iBAAiB,QAASK,GAIL,OAAzB7B,EAAMV,gBACRS,KAAKD,QAAQS,UAAUuB,YAAW,WAChCL,MACCzB,EAAMV,gBAGXY,KAIIuB,EAAY,KAEhB1B,KAAKD,QAAQS,UAAUwB,mBAIP,OAAZzB,EACFF,EAAM4B,OAEN5B,EAAM6B,QAGR7B,EAAM8B,oBAAoB,QAAST,GACnCrB,EAAM8B,oBAAoB,QAASL,GAGnC9B,KAAKD,QAAQS,UAAU4B,6BAGvB,IAAIC,EAAa,CACf1B,GAAID,EAASC,GACb/B,SAAUqB,EAAMrB,SAChB8B,SAAUA,EAASE,KAIrBV,EAAgByB,UAAY,GAG5B3B,KAAKD,QAAQuC,YAAYD,GAEzBjC,KAIF,SAASmC,EAAe9D,GAEF,MAAhBiC,EAASE,MACXF,EAAWjC,GAGTwB,EAAMR,qBACRiC,IAIJ,MAAMI,EAA0B,KAEd,OAAZvB,EACFP,KAAKD,QAAQS,UAAUgC,oBAAoB,CACzCC,kBAAmBF,EACnBG,gBAAiBzC,EAAMd,QACvBwD,UAAW,QACXC,SAAS,EACTC,gBAAgB,EAChBC,cAAevC,EACfwC,yBAA0BzC,IAG5BN,KAAKD,QAAQS,UAAUgC,oBAAoB,CACzCC,kBAAmBF,EACnBG,gBAAiBzC,EAAMd,QACvBwD,UAAW,cACXC,SAAS,EACTC,gBAAgB,KAKtB,OAAO,IAAIG,SAASC,IAClB7C,EAAiB6C,aArJdpD,OAAOpB"}
package/dist/index.cjs ADDED
@@ -0,0 +1,197 @@
1
+ 'use strict';
2
+
3
+ var jspsych = require('jspsych');
4
+
5
+ const info = {
6
+ name: "audio-keyboard-response",
7
+ parameters: {
8
+ /** The audio file to be played. */
9
+ stimulus: {
10
+ type: jspsych.ParameterType.AUDIO,
11
+ pretty_name: "Stimulus",
12
+ default: undefined,
13
+ },
14
+ /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */
15
+ choices: {
16
+ type: jspsych.ParameterType.KEYS,
17
+ pretty_name: "Choices",
18
+ default: "ALL_KEYS",
19
+ },
20
+ /** Any content here will be displayed below the stimulus. */
21
+ prompt: {
22
+ type: jspsych.ParameterType.HTML_STRING,
23
+ pretty_name: "Prompt",
24
+ default: null,
25
+ },
26
+ /** The maximum duration to wait for a response. */
27
+ trial_duration: {
28
+ type: jspsych.ParameterType.INT,
29
+ pretty_name: "Trial duration",
30
+ default: null,
31
+ },
32
+ /** If true, the trial will end when user makes a response. */
33
+ response_ends_trial: {
34
+ type: jspsych.ParameterType.BOOL,
35
+ pretty_name: "Response ends trial",
36
+ default: true,
37
+ },
38
+ /** If true, then the trial will end as soon as the audio file finishes playing. */
39
+ trial_ends_after_audio: {
40
+ type: jspsych.ParameterType.BOOL,
41
+ pretty_name: "Trial ends after audio",
42
+ default: false,
43
+ },
44
+ /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */
45
+ response_allowed_while_playing: {
46
+ type: jspsych.ParameterType.BOOL,
47
+ pretty_name: "Response allowed while playing",
48
+ default: true,
49
+ },
50
+ },
51
+ };
52
+ /**
53
+ * **audio-keyboard-response**
54
+ *
55
+ * jsPsych plugin for playing an audio file and getting a keyboard response
56
+ *
57
+ * @author Josh de Leeuw
58
+ * @see {@link https://www.jspsych.org/plugins/jspsych-audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}
59
+ */
60
+ class AudioKeyboardResponsePlugin {
61
+ constructor(jsPsych) {
62
+ this.jsPsych = jsPsych;
63
+ }
64
+ trial(display_element, trial, on_load) {
65
+ // hold the .resolve() function from the Promise that ends the trial
66
+ let trial_complete;
67
+ // setup stimulus
68
+ var context = this.jsPsych.pluginAPI.audioContext();
69
+ var audio;
70
+ // store response
71
+ var response = {
72
+ rt: null,
73
+ key: null,
74
+ };
75
+ // record webaudio context start time
76
+ var startTime;
77
+ // load audio file
78
+ this.jsPsych.pluginAPI
79
+ .getAudioBuffer(trial.stimulus)
80
+ .then(function (buffer) {
81
+ if (context !== null) {
82
+ audio = context.createBufferSource();
83
+ audio.buffer = buffer;
84
+ audio.connect(context.destination);
85
+ }
86
+ else {
87
+ audio = buffer;
88
+ audio.currentTime = 0;
89
+ }
90
+ setupTrial();
91
+ })
92
+ .catch(function (err) {
93
+ console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`);
94
+ console.error(err);
95
+ });
96
+ const setupTrial = () => {
97
+ // set up end event if trial needs it
98
+ if (trial.trial_ends_after_audio) {
99
+ audio.addEventListener("ended", end_trial);
100
+ }
101
+ // show prompt if there is one
102
+ if (trial.prompt !== null) {
103
+ display_element.innerHTML = trial.prompt;
104
+ }
105
+ // start audio
106
+ if (context !== null) {
107
+ startTime = context.currentTime;
108
+ audio.start(startTime);
109
+ }
110
+ else {
111
+ audio.play();
112
+ }
113
+ // start keyboard listener when trial starts or sound ends
114
+ if (trial.response_allowed_while_playing) {
115
+ setup_keyboard_listener();
116
+ }
117
+ else if (!trial.trial_ends_after_audio) {
118
+ audio.addEventListener("ended", setup_keyboard_listener);
119
+ }
120
+ // end trial if time limit is set
121
+ if (trial.trial_duration !== null) {
122
+ this.jsPsych.pluginAPI.setTimeout(function () {
123
+ end_trial();
124
+ }, trial.trial_duration);
125
+ }
126
+ on_load();
127
+ };
128
+ // function to end trial when it is time
129
+ const end_trial = () => {
130
+ // kill any remaining setTimeout handlers
131
+ this.jsPsych.pluginAPI.clearAllTimeouts();
132
+ // stop the audio file if it is playing
133
+ // remove end event listeners if they exist
134
+ if (context !== null) {
135
+ audio.stop();
136
+ }
137
+ else {
138
+ audio.pause();
139
+ }
140
+ audio.removeEventListener("ended", end_trial);
141
+ audio.removeEventListener("ended", setup_keyboard_listener);
142
+ // kill keyboard listeners
143
+ this.jsPsych.pluginAPI.cancelAllKeyboardResponses();
144
+ // gather the data to store for the trial
145
+ var trial_data = {
146
+ rt: response.rt,
147
+ stimulus: trial.stimulus,
148
+ response: response.key,
149
+ };
150
+ // clear the display
151
+ display_element.innerHTML = "";
152
+ // move on to the next trial
153
+ this.jsPsych.finishTrial(trial_data);
154
+ trial_complete();
155
+ };
156
+ // function to handle responses by the subject
157
+ function after_response(info) {
158
+ // only record the first response
159
+ if (response.key == null) {
160
+ response = info;
161
+ }
162
+ if (trial.response_ends_trial) {
163
+ end_trial();
164
+ }
165
+ }
166
+ const setup_keyboard_listener = () => {
167
+ // start the response listener
168
+ if (context !== null) {
169
+ this.jsPsych.pluginAPI.getKeyboardResponse({
170
+ callback_function: after_response,
171
+ valid_responses: trial.choices,
172
+ rt_method: "audio",
173
+ persist: false,
174
+ allow_held_key: false,
175
+ audio_context: context,
176
+ audio_context_start_time: startTime,
177
+ });
178
+ }
179
+ else {
180
+ this.jsPsych.pluginAPI.getKeyboardResponse({
181
+ callback_function: after_response,
182
+ valid_responses: trial.choices,
183
+ rt_method: "performance",
184
+ persist: false,
185
+ allow_held_key: false,
186
+ });
187
+ }
188
+ };
189
+ return new Promise((resolve) => {
190
+ trial_complete = resolve;
191
+ });
192
+ }
193
+ }
194
+ AudioKeyboardResponsePlugin.info = info;
195
+
196
+ module.exports = AudioKeyboardResponsePlugin;
197
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":["import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nconst info = <const>{\n name: \"audio-keyboard-response\",\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n pretty_name: \"Stimulus\",\n default: undefined,\n },\n /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */\n choices: {\n type: ParameterType.KEYS,\n pretty_name: \"Choices\",\n default: \"ALL_KEYS\",\n },\n /** Any content here will be displayed below the stimulus. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** The maximum duration to wait for a response. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, the trial will end when user makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\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 playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * **audio-keyboard-response**\n *\n * jsPsych plugin for playing an audio file and getting a keyboard response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}\n */\nclass AudioKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n // hold the .resolve() function from the Promise that ends the trial\n let trial_complete;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n var audio;\n\n // store response\n var response = {\n rt: null,\n key: null,\n };\n\n // record webaudio context start time\n var startTime;\n\n // load audio file\n this.jsPsych.pluginAPI\n .getAudioBuffer(trial.stimulus)\n .then(function (buffer) {\n if (context !== null) {\n audio = context.createBufferSource();\n audio.buffer = buffer;\n audio.connect(context.destination);\n } else {\n audio = buffer;\n audio.currentTime = 0;\n }\n setupTrial();\n })\n .catch(function (err) {\n console.error(\n `Failed to load audio file \"${trial.stimulus}\". Try checking the file path. We recommend using the preload plugin to load audio files.`\n );\n console.error(err);\n });\n\n const setupTrial = () => {\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", 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 audio\n if (context !== null) {\n startTime = context.currentTime;\n audio.start(startTime);\n } else {\n audio.play();\n }\n\n // start keyboard listener when trial starts or sound ends\n if (trial.response_allowed_while_playing) {\n setup_keyboard_listener();\n } else if (!trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", setup_keyboard_listener);\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(function () {\n end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n };\n\n // function to end trial when it is time\n const end_trial = () => {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // stop the audio file if it is playing\n // remove end event listeners if they exist\n if (context !== null) {\n audio.stop();\n } else {\n audio.pause();\n }\n\n audio.removeEventListener(\"ended\", end_trial);\n audio.removeEventListener(\"ended\", 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: response.rt,\n stimulus: trial.stimulus,\n response: response.key,\n };\n\n // clear the display\n display_element.innerHTML = \"\";\n\n // move on to the next trial\n this.jsPsych.finishTrial(trial_data);\n\n trial_complete();\n };\n\n // function to handle responses by the subject\n function after_response(info) {\n // only record the first response\n if (response.key == null) {\n response = info;\n }\n\n if (trial.response_ends_trial) {\n end_trial();\n }\n }\n\n const setup_keyboard_listener = () => {\n // start the response listener\n if (context !== null) {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"audio\",\n persist: false,\n allow_held_key: false,\n audio_context: context,\n audio_context_start_time: startTime,\n });\n } else {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n}\n\nexport default AudioKeyboardResponsePlugin;\n"],"names":["ParameterType"],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from "jspsych";
2
+ declare const info: {
3
+ readonly name: "audio-keyboard-response";
4
+ readonly parameters: {
5
+ /** The audio file to be played. */
6
+ readonly stimulus: {
7
+ readonly type: ParameterType.AUDIO;
8
+ readonly pretty_name: "Stimulus";
9
+ readonly default: any;
10
+ };
11
+ /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */
12
+ readonly choices: {
13
+ readonly type: ParameterType.KEYS;
14
+ readonly pretty_name: "Choices";
15
+ readonly default: "ALL_KEYS";
16
+ };
17
+ /** Any content here will be displayed below the stimulus. */
18
+ readonly prompt: {
19
+ readonly type: ParameterType.HTML_STRING;
20
+ readonly pretty_name: "Prompt";
21
+ readonly default: any;
22
+ };
23
+ /** The maximum duration to wait for a response. */
24
+ readonly trial_duration: {
25
+ readonly type: ParameterType.INT;
26
+ readonly pretty_name: "Trial duration";
27
+ readonly default: any;
28
+ };
29
+ /** If true, the trial will end when user makes a response. */
30
+ readonly response_ends_trial: {
31
+ readonly type: ParameterType.BOOL;
32
+ readonly pretty_name: "Response ends trial";
33
+ readonly default: true;
34
+ };
35
+ /** If true, then the trial will end as soon as the audio file finishes playing. */
36
+ readonly trial_ends_after_audio: {
37
+ readonly type: ParameterType.BOOL;
38
+ readonly pretty_name: "Trial ends after audio";
39
+ readonly default: false;
40
+ };
41
+ /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */
42
+ readonly response_allowed_while_playing: {
43
+ readonly type: ParameterType.BOOL;
44
+ readonly pretty_name: "Response allowed while playing";
45
+ readonly default: true;
46
+ };
47
+ };
48
+ };
49
+ declare type Info = typeof info;
50
+ /**
51
+ * **audio-keyboard-response**
52
+ *
53
+ * jsPsych plugin for playing an audio file and getting a keyboard response
54
+ *
55
+ * @author Josh de Leeuw
56
+ * @see {@link https://www.jspsych.org/plugins/jspsych-audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}
57
+ */
58
+ declare class AudioKeyboardResponsePlugin implements JsPsychPlugin<Info> {
59
+ private jsPsych;
60
+ static info: {
61
+ readonly name: "audio-keyboard-response";
62
+ readonly parameters: {
63
+ /** The audio file to be played. */
64
+ readonly stimulus: {
65
+ readonly type: ParameterType.AUDIO;
66
+ readonly pretty_name: "Stimulus";
67
+ readonly default: any;
68
+ };
69
+ /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */
70
+ readonly choices: {
71
+ readonly type: ParameterType.KEYS;
72
+ readonly pretty_name: "Choices";
73
+ readonly default: "ALL_KEYS";
74
+ };
75
+ /** Any content here will be displayed below the stimulus. */
76
+ readonly prompt: {
77
+ readonly type: ParameterType.HTML_STRING;
78
+ readonly pretty_name: "Prompt";
79
+ readonly default: any;
80
+ };
81
+ /** The maximum duration to wait for a response. */
82
+ readonly trial_duration: {
83
+ readonly type: ParameterType.INT;
84
+ readonly pretty_name: "Trial duration";
85
+ readonly default: any;
86
+ };
87
+ /** If true, the trial will end when user makes a response. */
88
+ readonly response_ends_trial: {
89
+ readonly type: ParameterType.BOOL;
90
+ readonly pretty_name: "Response ends trial";
91
+ readonly default: true;
92
+ };
93
+ /** If true, then the trial will end as soon as the audio file finishes playing. */
94
+ readonly trial_ends_after_audio: {
95
+ readonly type: ParameterType.BOOL;
96
+ readonly pretty_name: "Trial ends after audio";
97
+ readonly default: false;
98
+ };
99
+ /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */
100
+ readonly response_allowed_while_playing: {
101
+ readonly type: ParameterType.BOOL;
102
+ readonly pretty_name: "Response allowed while playing";
103
+ readonly default: true;
104
+ };
105
+ };
106
+ };
107
+ constructor(jsPsych: JsPsych);
108
+ trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void): Promise<unknown>;
109
+ }
110
+ export default AudioKeyboardResponsePlugin;
package/dist/index.js ADDED
@@ -0,0 +1,195 @@
1
+ import { ParameterType } from 'jspsych';
2
+
3
+ const info = {
4
+ name: "audio-keyboard-response",
5
+ parameters: {
6
+ /** The audio file to be played. */
7
+ stimulus: {
8
+ type: ParameterType.AUDIO,
9
+ pretty_name: "Stimulus",
10
+ default: undefined,
11
+ },
12
+ /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */
13
+ choices: {
14
+ type: ParameterType.KEYS,
15
+ pretty_name: "Choices",
16
+ default: "ALL_KEYS",
17
+ },
18
+ /** Any content here will be displayed below the stimulus. */
19
+ prompt: {
20
+ type: ParameterType.HTML_STRING,
21
+ pretty_name: "Prompt",
22
+ default: null,
23
+ },
24
+ /** The maximum duration to wait for a response. */
25
+ trial_duration: {
26
+ type: ParameterType.INT,
27
+ pretty_name: "Trial duration",
28
+ default: null,
29
+ },
30
+ /** If true, the trial will end when user makes a response. */
31
+ response_ends_trial: {
32
+ type: ParameterType.BOOL,
33
+ pretty_name: "Response ends trial",
34
+ default: true,
35
+ },
36
+ /** If true, then the trial will end as soon as the audio file finishes playing. */
37
+ trial_ends_after_audio: {
38
+ type: ParameterType.BOOL,
39
+ pretty_name: "Trial ends after audio",
40
+ default: false,
41
+ },
42
+ /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */
43
+ response_allowed_while_playing: {
44
+ type: ParameterType.BOOL,
45
+ pretty_name: "Response allowed while playing",
46
+ default: true,
47
+ },
48
+ },
49
+ };
50
+ /**
51
+ * **audio-keyboard-response**
52
+ *
53
+ * jsPsych plugin for playing an audio file and getting a keyboard response
54
+ *
55
+ * @author Josh de Leeuw
56
+ * @see {@link https://www.jspsych.org/plugins/jspsych-audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}
57
+ */
58
+ class AudioKeyboardResponsePlugin {
59
+ constructor(jsPsych) {
60
+ this.jsPsych = jsPsych;
61
+ }
62
+ trial(display_element, trial, on_load) {
63
+ // hold the .resolve() function from the Promise that ends the trial
64
+ let trial_complete;
65
+ // setup stimulus
66
+ var context = this.jsPsych.pluginAPI.audioContext();
67
+ var audio;
68
+ // store response
69
+ var response = {
70
+ rt: null,
71
+ key: null,
72
+ };
73
+ // record webaudio context start time
74
+ var startTime;
75
+ // load audio file
76
+ this.jsPsych.pluginAPI
77
+ .getAudioBuffer(trial.stimulus)
78
+ .then(function (buffer) {
79
+ if (context !== null) {
80
+ audio = context.createBufferSource();
81
+ audio.buffer = buffer;
82
+ audio.connect(context.destination);
83
+ }
84
+ else {
85
+ audio = buffer;
86
+ audio.currentTime = 0;
87
+ }
88
+ setupTrial();
89
+ })
90
+ .catch(function (err) {
91
+ console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`);
92
+ console.error(err);
93
+ });
94
+ const setupTrial = () => {
95
+ // set up end event if trial needs it
96
+ if (trial.trial_ends_after_audio) {
97
+ audio.addEventListener("ended", end_trial);
98
+ }
99
+ // show prompt if there is one
100
+ if (trial.prompt !== null) {
101
+ display_element.innerHTML = trial.prompt;
102
+ }
103
+ // start audio
104
+ if (context !== null) {
105
+ startTime = context.currentTime;
106
+ audio.start(startTime);
107
+ }
108
+ else {
109
+ audio.play();
110
+ }
111
+ // start keyboard listener when trial starts or sound ends
112
+ if (trial.response_allowed_while_playing) {
113
+ setup_keyboard_listener();
114
+ }
115
+ else if (!trial.trial_ends_after_audio) {
116
+ audio.addEventListener("ended", setup_keyboard_listener);
117
+ }
118
+ // end trial if time limit is set
119
+ if (trial.trial_duration !== null) {
120
+ this.jsPsych.pluginAPI.setTimeout(function () {
121
+ end_trial();
122
+ }, trial.trial_duration);
123
+ }
124
+ on_load();
125
+ };
126
+ // function to end trial when it is time
127
+ const end_trial = () => {
128
+ // kill any remaining setTimeout handlers
129
+ this.jsPsych.pluginAPI.clearAllTimeouts();
130
+ // stop the audio file if it is playing
131
+ // remove end event listeners if they exist
132
+ if (context !== null) {
133
+ audio.stop();
134
+ }
135
+ else {
136
+ audio.pause();
137
+ }
138
+ audio.removeEventListener("ended", end_trial);
139
+ audio.removeEventListener("ended", setup_keyboard_listener);
140
+ // kill keyboard listeners
141
+ this.jsPsych.pluginAPI.cancelAllKeyboardResponses();
142
+ // gather the data to store for the trial
143
+ var trial_data = {
144
+ rt: response.rt,
145
+ stimulus: trial.stimulus,
146
+ response: response.key,
147
+ };
148
+ // clear the display
149
+ display_element.innerHTML = "";
150
+ // move on to the next trial
151
+ this.jsPsych.finishTrial(trial_data);
152
+ trial_complete();
153
+ };
154
+ // function to handle responses by the subject
155
+ function after_response(info) {
156
+ // only record the first response
157
+ if (response.key == null) {
158
+ response = info;
159
+ }
160
+ if (trial.response_ends_trial) {
161
+ end_trial();
162
+ }
163
+ }
164
+ const setup_keyboard_listener = () => {
165
+ // start the response listener
166
+ if (context !== null) {
167
+ this.jsPsych.pluginAPI.getKeyboardResponse({
168
+ callback_function: after_response,
169
+ valid_responses: trial.choices,
170
+ rt_method: "audio",
171
+ persist: false,
172
+ allow_held_key: false,
173
+ audio_context: context,
174
+ audio_context_start_time: startTime,
175
+ });
176
+ }
177
+ else {
178
+ this.jsPsych.pluginAPI.getKeyboardResponse({
179
+ callback_function: after_response,
180
+ valid_responses: trial.choices,
181
+ rt_method: "performance",
182
+ persist: false,
183
+ allow_held_key: false,
184
+ });
185
+ }
186
+ };
187
+ return new Promise((resolve) => {
188
+ trial_complete = resolve;
189
+ });
190
+ }
191
+ }
192
+ AudioKeyboardResponsePlugin.info = info;
193
+
194
+ export { AudioKeyboardResponsePlugin as default };
195
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nconst info = <const>{\n name: \"audio-keyboard-response\",\n parameters: {\n /** The audio file to be played. */\n stimulus: {\n type: ParameterType.AUDIO,\n pretty_name: \"Stimulus\",\n default: undefined,\n },\n /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */\n choices: {\n type: ParameterType.KEYS,\n pretty_name: \"Choices\",\n default: \"ALL_KEYS\",\n },\n /** Any content here will be displayed below the stimulus. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** The maximum duration to wait for a response. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, the trial will end when user makes a response. */\n response_ends_trial: {\n type: ParameterType.BOOL,\n pretty_name: \"Response ends trial\",\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 playing before a response is accepted. */\n response_allowed_while_playing: {\n type: ParameterType.BOOL,\n pretty_name: \"Response allowed while playing\",\n default: true,\n },\n },\n};\n\ntype Info = typeof info;\n\n/**\n * **audio-keyboard-response**\n *\n * jsPsych plugin for playing an audio file and getting a keyboard response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}\n */\nclass AudioKeyboardResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n // hold the .resolve() function from the Promise that ends the trial\n let trial_complete;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n var audio;\n\n // store response\n var response = {\n rt: null,\n key: null,\n };\n\n // record webaudio context start time\n var startTime;\n\n // load audio file\n this.jsPsych.pluginAPI\n .getAudioBuffer(trial.stimulus)\n .then(function (buffer) {\n if (context !== null) {\n audio = context.createBufferSource();\n audio.buffer = buffer;\n audio.connect(context.destination);\n } else {\n audio = buffer;\n audio.currentTime = 0;\n }\n setupTrial();\n })\n .catch(function (err) {\n console.error(\n `Failed to load audio file \"${trial.stimulus}\". Try checking the file path. We recommend using the preload plugin to load audio files.`\n );\n console.error(err);\n });\n\n const setupTrial = () => {\n // set up end event if trial needs it\n if (trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", 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 audio\n if (context !== null) {\n startTime = context.currentTime;\n audio.start(startTime);\n } else {\n audio.play();\n }\n\n // start keyboard listener when trial starts or sound ends\n if (trial.response_allowed_while_playing) {\n setup_keyboard_listener();\n } else if (!trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", setup_keyboard_listener);\n }\n\n // end trial if time limit is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(function () {\n end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n };\n\n // function to end trial when it is time\n const end_trial = () => {\n // kill any remaining setTimeout handlers\n this.jsPsych.pluginAPI.clearAllTimeouts();\n\n // stop the audio file if it is playing\n // remove end event listeners if they exist\n if (context !== null) {\n audio.stop();\n } else {\n audio.pause();\n }\n\n audio.removeEventListener(\"ended\", end_trial);\n audio.removeEventListener(\"ended\", 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: response.rt,\n stimulus: trial.stimulus,\n response: response.key,\n };\n\n // clear the display\n display_element.innerHTML = \"\";\n\n // move on to the next trial\n this.jsPsych.finishTrial(trial_data);\n\n trial_complete();\n };\n\n // function to handle responses by the subject\n function after_response(info) {\n // only record the first response\n if (response.key == null) {\n response = info;\n }\n\n if (trial.response_ends_trial) {\n end_trial();\n }\n }\n\n const setup_keyboard_listener = () => {\n // start the response listener\n if (context !== null) {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"audio\",\n persist: false,\n allow_held_key: false,\n audio_context: context,\n audio_context_start_time: startTime,\n });\n } else {\n this.jsPsych.pluginAPI.getKeyboardResponse({\n callback_function: after_response,\n valid_responses: trial.choices,\n rt_method: \"performance\",\n persist: false,\n allow_held_key: false,\n });\n }\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n}\n\nexport default AudioKeyboardResponsePlugin;\n"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@jspsych/plugin-audio-keyboard-response",
3
+ "version": "1.0.0",
4
+ "description": "jsPsych plugin for playing an audio file and getting a keyboard response",
5
+ "type": "module",
6
+ "main": "dist/index.cjs",
7
+ "exports": {
8
+ "import": "./dist/index.js",
9
+ "require": "./dist/index.cjs"
10
+ },
11
+ "typings": "dist/index.d.ts",
12
+ "unpkg": "dist/index.browser.min.js",
13
+ "files": [
14
+ "src",
15
+ "dist"
16
+ ],
17
+ "source": "src/index.ts",
18
+ "scripts": {
19
+ "test": "jest --passWithNoTests",
20
+ "test:watch": "npm test -- --watch",
21
+ "tsc": "tsc",
22
+ "build": "rollup --config",
23
+ "build:watch": "npm run build -- --watch"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/jspsych/jsPsych.git",
28
+ "directory": "packages/plugin-audio-keyboard-response"
29
+ },
30
+ "author": "Josh de Leeuw",
31
+ "license": "MIT",
32
+ "bugs": {
33
+ "url": "https://github.com/jspsych/jsPsych/issues"
34
+ },
35
+ "homepage": "https://www.jspsych.org/latest/plugins/audio-keyboard-response",
36
+ "peerDependencies": {
37
+ "jspsych": ">=7.0.0"
38
+ },
39
+ "devDependencies": {
40
+ "@jspsych/config": "^1.0.0",
41
+ "@jspsych/test-utils": "^1.0.0"
42
+ }
43
+ }
package/src/index.ts ADDED
@@ -0,0 +1,216 @@
1
+ import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from "jspsych";
2
+
3
+ const info = <const>{
4
+ name: "audio-keyboard-response",
5
+ parameters: {
6
+ /** The audio file to be played. */
7
+ stimulus: {
8
+ type: ParameterType.AUDIO,
9
+ pretty_name: "Stimulus",
10
+ default: undefined,
11
+ },
12
+ /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */
13
+ choices: {
14
+ type: ParameterType.KEYS,
15
+ pretty_name: "Choices",
16
+ default: "ALL_KEYS",
17
+ },
18
+ /** Any content here will be displayed below the stimulus. */
19
+ prompt: {
20
+ type: ParameterType.HTML_STRING,
21
+ pretty_name: "Prompt",
22
+ default: null,
23
+ },
24
+ /** The maximum duration to wait for a response. */
25
+ trial_duration: {
26
+ type: ParameterType.INT,
27
+ pretty_name: "Trial duration",
28
+ default: null,
29
+ },
30
+ /** If true, the trial will end when user makes a response. */
31
+ response_ends_trial: {
32
+ type: ParameterType.BOOL,
33
+ pretty_name: "Response ends trial",
34
+ default: true,
35
+ },
36
+ /** If true, then the trial will end as soon as the audio file finishes playing. */
37
+ trial_ends_after_audio: {
38
+ type: ParameterType.BOOL,
39
+ pretty_name: "Trial ends after audio",
40
+ default: false,
41
+ },
42
+ /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */
43
+ response_allowed_while_playing: {
44
+ type: ParameterType.BOOL,
45
+ pretty_name: "Response allowed while playing",
46
+ default: true,
47
+ },
48
+ },
49
+ };
50
+
51
+ type Info = typeof info;
52
+
53
+ /**
54
+ * **audio-keyboard-response**
55
+ *
56
+ * jsPsych plugin for playing an audio file and getting a keyboard response
57
+ *
58
+ * @author Josh de Leeuw
59
+ * @see {@link https://www.jspsych.org/plugins/jspsych-audio-keyboard-response/ audio-keyboard-response plugin documentation on jspsych.org}
60
+ */
61
+ class AudioKeyboardResponsePlugin implements JsPsychPlugin<Info> {
62
+ static info = info;
63
+
64
+ constructor(private jsPsych: JsPsych) {}
65
+
66
+ trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {
67
+ // hold the .resolve() function from the Promise that ends the trial
68
+ let trial_complete;
69
+
70
+ // setup stimulus
71
+ var context = this.jsPsych.pluginAPI.audioContext();
72
+ var audio;
73
+
74
+ // store response
75
+ var response = {
76
+ rt: null,
77
+ key: null,
78
+ };
79
+
80
+ // record webaudio context start time
81
+ var startTime;
82
+
83
+ // load audio file
84
+ this.jsPsych.pluginAPI
85
+ .getAudioBuffer(trial.stimulus)
86
+ .then(function (buffer) {
87
+ if (context !== null) {
88
+ audio = context.createBufferSource();
89
+ audio.buffer = buffer;
90
+ audio.connect(context.destination);
91
+ } else {
92
+ audio = buffer;
93
+ audio.currentTime = 0;
94
+ }
95
+ setupTrial();
96
+ })
97
+ .catch(function (err) {
98
+ console.error(
99
+ `Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`
100
+ );
101
+ console.error(err);
102
+ });
103
+
104
+ const setupTrial = () => {
105
+ // set up end event if trial needs it
106
+ if (trial.trial_ends_after_audio) {
107
+ audio.addEventListener("ended", end_trial);
108
+ }
109
+
110
+ // show prompt if there is one
111
+ if (trial.prompt !== null) {
112
+ display_element.innerHTML = trial.prompt;
113
+ }
114
+
115
+ // start audio
116
+ if (context !== null) {
117
+ startTime = context.currentTime;
118
+ audio.start(startTime);
119
+ } else {
120
+ audio.play();
121
+ }
122
+
123
+ // start keyboard listener when trial starts or sound ends
124
+ if (trial.response_allowed_while_playing) {
125
+ setup_keyboard_listener();
126
+ } else if (!trial.trial_ends_after_audio) {
127
+ audio.addEventListener("ended", setup_keyboard_listener);
128
+ }
129
+
130
+ // end trial if time limit is set
131
+ if (trial.trial_duration !== null) {
132
+ this.jsPsych.pluginAPI.setTimeout(function () {
133
+ end_trial();
134
+ }, trial.trial_duration);
135
+ }
136
+
137
+ on_load();
138
+ };
139
+
140
+ // function to end trial when it is time
141
+ const end_trial = () => {
142
+ // kill any remaining setTimeout handlers
143
+ this.jsPsych.pluginAPI.clearAllTimeouts();
144
+
145
+ // stop the audio file if it is playing
146
+ // remove end event listeners if they exist
147
+ if (context !== null) {
148
+ audio.stop();
149
+ } else {
150
+ audio.pause();
151
+ }
152
+
153
+ audio.removeEventListener("ended", end_trial);
154
+ audio.removeEventListener("ended", setup_keyboard_listener);
155
+
156
+ // kill keyboard listeners
157
+ this.jsPsych.pluginAPI.cancelAllKeyboardResponses();
158
+
159
+ // gather the data to store for the trial
160
+ var trial_data = {
161
+ rt: response.rt,
162
+ stimulus: trial.stimulus,
163
+ response: response.key,
164
+ };
165
+
166
+ // clear the display
167
+ display_element.innerHTML = "";
168
+
169
+ // move on to the next trial
170
+ this.jsPsych.finishTrial(trial_data);
171
+
172
+ trial_complete();
173
+ };
174
+
175
+ // function to handle responses by the subject
176
+ function after_response(info) {
177
+ // only record the first response
178
+ if (response.key == null) {
179
+ response = info;
180
+ }
181
+
182
+ if (trial.response_ends_trial) {
183
+ end_trial();
184
+ }
185
+ }
186
+
187
+ const setup_keyboard_listener = () => {
188
+ // start the response listener
189
+ if (context !== null) {
190
+ this.jsPsych.pluginAPI.getKeyboardResponse({
191
+ callback_function: after_response,
192
+ valid_responses: trial.choices,
193
+ rt_method: "audio",
194
+ persist: false,
195
+ allow_held_key: false,
196
+ audio_context: context,
197
+ audio_context_start_time: startTime,
198
+ });
199
+ } else {
200
+ this.jsPsych.pluginAPI.getKeyboardResponse({
201
+ callback_function: after_response,
202
+ valid_responses: trial.choices,
203
+ rt_method: "performance",
204
+ persist: false,
205
+ allow_held_key: false,
206
+ });
207
+ }
208
+ };
209
+
210
+ return new Promise((resolve) => {
211
+ trial_complete = resolve;
212
+ });
213
+ }
214
+ }
215
+
216
+ export default AudioKeyboardResponsePlugin;