@jspsych/plugin-audio-slider-response 1.0.0 → 1.1.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.
- package/dist/index.browser.js +67 -18
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.min.js +1 -1
- package/dist/index.browser.min.js.map +1 -1
- package/dist/index.cjs +67 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +67 -18
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/index.spec.ts +57 -0
- package/src/index.ts +87 -18
package/dist/index.browser.js
CHANGED
|
@@ -111,7 +111,6 @@ var jsPsychAudioSliderResponse = (function (jspsych) {
|
|
|
111
111
|
var half_thumb_width = 7.5;
|
|
112
112
|
// setup stimulus
|
|
113
113
|
var context = this.jsPsych.pluginAPI.audioContext();
|
|
114
|
-
var audio;
|
|
115
114
|
// record webaudio context start time
|
|
116
115
|
var startTime;
|
|
117
116
|
// for storing data related to response
|
|
@@ -119,30 +118,30 @@ var jsPsychAudioSliderResponse = (function (jspsych) {
|
|
|
119
118
|
// load audio file
|
|
120
119
|
this.jsPsych.pluginAPI
|
|
121
120
|
.getAudioBuffer(trial.stimulus)
|
|
122
|
-
.then(
|
|
121
|
+
.then((buffer) => {
|
|
123
122
|
if (context !== null) {
|
|
124
|
-
audio = context.createBufferSource();
|
|
125
|
-
audio.buffer = buffer;
|
|
126
|
-
audio.connect(context.destination);
|
|
123
|
+
this.audio = context.createBufferSource();
|
|
124
|
+
this.audio.buffer = buffer;
|
|
125
|
+
this.audio.connect(context.destination);
|
|
127
126
|
}
|
|
128
127
|
else {
|
|
129
|
-
audio = buffer;
|
|
130
|
-
audio.currentTime = 0;
|
|
128
|
+
this.audio = buffer;
|
|
129
|
+
this.audio.currentTime = 0;
|
|
131
130
|
}
|
|
132
131
|
setupTrial();
|
|
133
132
|
})
|
|
134
|
-
.catch(
|
|
133
|
+
.catch((err) => {
|
|
135
134
|
console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`);
|
|
136
135
|
console.error(err);
|
|
137
136
|
});
|
|
138
137
|
const setupTrial = () => {
|
|
139
138
|
// set up end event if trial needs it
|
|
140
139
|
if (trial.trial_ends_after_audio) {
|
|
141
|
-
audio.addEventListener("ended", end_trial);
|
|
140
|
+
this.audio.addEventListener("ended", end_trial);
|
|
142
141
|
}
|
|
143
142
|
// enable slider after audio ends if necessary
|
|
144
143
|
if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {
|
|
145
|
-
audio.addEventListener("ended", enable_slider);
|
|
144
|
+
this.audio.addEventListener("ended", enable_slider);
|
|
146
145
|
}
|
|
147
146
|
var html = '<div id="jspsych-audio-slider-response-wrapper" style="margin: 100px 0px;">';
|
|
148
147
|
html +=
|
|
@@ -223,10 +222,13 @@ var jsPsychAudioSliderResponse = (function (jspsych) {
|
|
|
223
222
|
display_element
|
|
224
223
|
.querySelector("#jspsych-audio-slider-response-response")
|
|
225
224
|
.addEventListener("touchstart", enable_button);
|
|
225
|
+
display_element
|
|
226
|
+
.querySelector("#jspsych-audio-slider-response-response")
|
|
227
|
+
.addEventListener("change", enable_button);
|
|
226
228
|
}
|
|
227
229
|
display_element
|
|
228
230
|
.querySelector("#jspsych-audio-slider-response-next")
|
|
229
|
-
.addEventListener("click",
|
|
231
|
+
.addEventListener("click", () => {
|
|
230
232
|
// measure response time
|
|
231
233
|
var endTime = performance.now();
|
|
232
234
|
var rt = Math.round(endTime - startTime);
|
|
@@ -247,14 +249,14 @@ var jsPsychAudioSliderResponse = (function (jspsych) {
|
|
|
247
249
|
// start audio
|
|
248
250
|
if (context !== null) {
|
|
249
251
|
startTime = context.currentTime;
|
|
250
|
-
audio.start(startTime);
|
|
252
|
+
this.audio.start(startTime);
|
|
251
253
|
}
|
|
252
254
|
else {
|
|
253
|
-
audio.play();
|
|
255
|
+
this.audio.play();
|
|
254
256
|
}
|
|
255
257
|
// end trial if trial_duration is set
|
|
256
258
|
if (trial.trial_duration !== null) {
|
|
257
|
-
this.jsPsych.pluginAPI.setTimeout(
|
|
259
|
+
this.jsPsych.pluginAPI.setTimeout(() => {
|
|
258
260
|
end_trial();
|
|
259
261
|
}, trial.trial_duration);
|
|
260
262
|
}
|
|
@@ -275,13 +277,13 @@ var jsPsychAudioSliderResponse = (function (jspsych) {
|
|
|
275
277
|
// stop the audio file if it is playing
|
|
276
278
|
// remove end event listeners if they exist
|
|
277
279
|
if (context !== null) {
|
|
278
|
-
audio.stop();
|
|
280
|
+
this.audio.stop();
|
|
279
281
|
}
|
|
280
282
|
else {
|
|
281
|
-
audio.pause();
|
|
283
|
+
this.audio.pause();
|
|
282
284
|
}
|
|
283
|
-
audio.removeEventListener("ended", end_trial);
|
|
284
|
-
audio.removeEventListener("ended", enable_slider);
|
|
285
|
+
this.audio.removeEventListener("ended", end_trial);
|
|
286
|
+
this.audio.removeEventListener("ended", enable_slider);
|
|
285
287
|
// save data
|
|
286
288
|
var trialdata = {
|
|
287
289
|
rt: response.rt,
|
|
@@ -298,6 +300,53 @@ var jsPsychAudioSliderResponse = (function (jspsych) {
|
|
|
298
300
|
trial_complete = resolve;
|
|
299
301
|
});
|
|
300
302
|
}
|
|
303
|
+
simulate(trial, simulation_mode, simulation_options, load_callback) {
|
|
304
|
+
if (simulation_mode == "data-only") {
|
|
305
|
+
load_callback();
|
|
306
|
+
this.simulate_data_only(trial, simulation_options);
|
|
307
|
+
}
|
|
308
|
+
if (simulation_mode == "visual") {
|
|
309
|
+
this.simulate_visual(trial, simulation_options, load_callback);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
create_simulation_data(trial, simulation_options) {
|
|
313
|
+
const default_data = {
|
|
314
|
+
stimulus: trial.stimulus,
|
|
315
|
+
slider_start: trial.slider_start,
|
|
316
|
+
response: this.jsPsych.randomization.randomInt(trial.min, trial.max),
|
|
317
|
+
rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
|
|
318
|
+
};
|
|
319
|
+
const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
|
|
320
|
+
this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
|
|
321
|
+
return data;
|
|
322
|
+
}
|
|
323
|
+
simulate_data_only(trial, simulation_options) {
|
|
324
|
+
const data = this.create_simulation_data(trial, simulation_options);
|
|
325
|
+
this.jsPsych.finishTrial(data);
|
|
326
|
+
}
|
|
327
|
+
simulate_visual(trial, simulation_options, load_callback) {
|
|
328
|
+
const data = this.create_simulation_data(trial, simulation_options);
|
|
329
|
+
const display_element = this.jsPsych.getDisplayElement();
|
|
330
|
+
const respond = () => {
|
|
331
|
+
if (data.rt !== null) {
|
|
332
|
+
const el = display_element.querySelector("input[type='range']");
|
|
333
|
+
setTimeout(() => {
|
|
334
|
+
this.jsPsych.pluginAPI.clickTarget(el);
|
|
335
|
+
el.valueAsNumber = data.response;
|
|
336
|
+
}, data.rt / 2);
|
|
337
|
+
this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("button"), data.rt);
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
this.trial(display_element, trial, () => {
|
|
341
|
+
load_callback();
|
|
342
|
+
if (!trial.response_allowed_while_playing) {
|
|
343
|
+
this.audio.addEventListener("ended", respond);
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
respond();
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
301
350
|
}
|
|
302
351
|
AudioSliderResponsePlugin.info = info;
|
|
303
352
|
|
|
@@ -1 +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-slider-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 /** Sets the minimum value of the slider. */\n min: {\n type: ParameterType.INT,\n pretty_name: \"Min slider\",\n default: 0,\n },\n /** Sets the maximum value of the slider */\n max: {\n type: ParameterType.INT,\n pretty_name: \"Max slider\",\n default: 100,\n },\n /** Sets the starting value of the slider */\n slider_start: {\n type: ParameterType.INT,\n pretty_name: \"Slider starting value\",\n default: 50,\n },\n /** Sets the step of the slider */\n step: {\n type: ParameterType.INT,\n pretty_name: \"Step\",\n default: 1,\n },\n /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */\n labels: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Labels\",\n default: [],\n array: true,\n },\n /** Width of the slider in pixels. */\n slider_width: {\n type: ParameterType.INT,\n pretty_name: \"Slider width\",\n default: null,\n },\n /** Label of the button to advance. */\n button_label: {\n type: ParameterType.STRING,\n pretty_name: \"Button label\",\n default: \"Continue\",\n array: false,\n },\n /** If true, the participant will have to move the slider before continuing. */\n require_movement: {\n type: ParameterType.BOOL,\n pretty_name: \"Require movement\",\n default: false,\n },\n /** Any content here will be displayed below the slider. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to show the trial. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, 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-slider-response**\n *\n * jsPsych plugin for playing audio and getting a slider response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}\n */\nclass AudioSliderResponsePlugin 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 // half of the thumb width value from jspsych.css, used to adjust the label positions\n var half_thumb_width = 7.5;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n var audio;\n\n // record webaudio context start time\n var startTime;\n\n // for storing data related to response\n var response;\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 // enable slider after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", enable_slider);\n }\n\n var html = '<div id=\"jspsych-audio-slider-response-wrapper\" style=\"margin: 100px 0px;\">';\n html +=\n '<div class=\"jspsych-audio-slider-response-container\" style=\"position:relative; margin: 0 auto 3em auto; width:';\n if (trial.slider_width !== null) {\n html += trial.slider_width + \"px;\";\n } else {\n html += \"auto;\";\n }\n html += '\">';\n html +=\n '<input type=\"range\" class=\"jspsych-slider\" value=\"' +\n trial.slider_start +\n '\" min=\"' +\n trial.min +\n '\" max=\"' +\n trial.max +\n '\" step=\"' +\n trial.step +\n '\" id=\"jspsych-audio-slider-response-response\"';\n if (!trial.response_allowed_while_playing) {\n html += \" disabled\";\n }\n html += \"></input><div>\";\n for (var j = 0; j < trial.labels.length; j++) {\n var label_width_perc = 100 / (trial.labels.length - 1);\n var percent_of_range = j * (100 / (trial.labels.length - 1));\n var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;\n var offset = (percent_dist_from_center * half_thumb_width) / 100;\n html +=\n '<div style=\"border: 1px solid transparent; display: inline-block; position: absolute; ' +\n \"left:calc(\" +\n percent_of_range +\n \"% - (\" +\n label_width_perc +\n \"% / 2) - \" +\n offset +\n \"px); text-align: center; width: \" +\n label_width_perc +\n '%;\">';\n html += '<span style=\"text-align: center; font-size: 80%;\">' + trial.labels[j] + \"</span>\";\n html += \"</div>\";\n }\n html += \"</div>\";\n html += \"</div>\";\n html += \"</div>\";\n\n if (trial.prompt !== null) {\n html += trial.prompt;\n }\n\n // add submit button\n var next_disabled_attribute = \"\";\n if (trial.require_movement || !trial.response_allowed_while_playing) {\n next_disabled_attribute = \"disabled\";\n }\n html +=\n '<button id=\"jspsych-audio-slider-response-next\" class=\"jspsych-btn\" ' +\n next_disabled_attribute +\n \">\" +\n trial.button_label +\n \"</button>\";\n\n display_element.innerHTML = html;\n\n response = {\n rt: null,\n response: null,\n };\n\n if (!trial.response_allowed_while_playing) {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).disabled = true;\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n\n if (trial.require_movement) {\n const enable_button = () => {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = false;\n };\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"mousedown\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"touchstart\", enable_button);\n }\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-next\")\n .addEventListener(\"click\", function () {\n // measure response time\n var endTime = performance.now();\n var rt = Math.round(endTime - startTime);\n if (context !== null) {\n endTime = context.currentTime;\n rt = Math.round((endTime - startTime) * 1000);\n }\n response.rt = rt;\n response.response = display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).valueAsNumber;\n\n if (trial.response_ends_trial) {\n end_trial();\n } else {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n });\n\n startTime = performance.now();\n // start audio\n if (context !== null) {\n startTime = context.currentTime;\n audio.start(startTime);\n } else {\n audio.play();\n }\n\n // end trial if trial_duration 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 enable slider after audio ends\n function enable_slider() {\n document.querySelector<HTMLInputElement>(\"#jspsych-audio-slider-response-response\").disabled =\n false;\n if (!trial.require_movement) {\n document.querySelector<HTMLButtonElement>(\"#jspsych-audio-slider-response-next\").disabled =\n false;\n }\n }\n\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\", enable_slider);\n\n // save data\n var trialdata = {\n rt: response.rt,\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: response.response,\n };\n\n display_element.innerHTML = \"\";\n\n // next trial\n this.jsPsych.finishTrial(trialdata);\n\n trial_complete();\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n}\n\nexport default AudioSliderResponsePlugin;\n"],"names":["ParameterType"],"mappings":"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"}
|
|
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-slider-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 /** Sets the minimum value of the slider. */\n min: {\n type: ParameterType.INT,\n pretty_name: \"Min slider\",\n default: 0,\n },\n /** Sets the maximum value of the slider */\n max: {\n type: ParameterType.INT,\n pretty_name: \"Max slider\",\n default: 100,\n },\n /** Sets the starting value of the slider */\n slider_start: {\n type: ParameterType.INT,\n pretty_name: \"Slider starting value\",\n default: 50,\n },\n /** Sets the step of the slider */\n step: {\n type: ParameterType.INT,\n pretty_name: \"Step\",\n default: 1,\n },\n /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */\n labels: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Labels\",\n default: [],\n array: true,\n },\n /** Width of the slider in pixels. */\n slider_width: {\n type: ParameterType.INT,\n pretty_name: \"Slider width\",\n default: null,\n },\n /** Label of the button to advance. */\n button_label: {\n type: ParameterType.STRING,\n pretty_name: \"Button label\",\n default: \"Continue\",\n array: false,\n },\n /** If true, the participant will have to move the slider before continuing. */\n require_movement: {\n type: ParameterType.BOOL,\n pretty_name: \"Require movement\",\n default: false,\n },\n /** Any content here will be displayed below the slider. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to show the trial. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, 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-slider-response**\n *\n * jsPsych plugin for playing audio and getting a slider response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}\n */\nclass AudioSliderResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio;\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 // half of the thumb width value from jspsych.css, used to adjust the label positions\n var half_thumb_width = 7.5;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n\n // record webaudio context start time\n var startTime;\n\n // for storing data related to response\n var response;\n\n // load audio file\n this.jsPsych.pluginAPI\n .getAudioBuffer(trial.stimulus)\n .then((buffer) => {\n if (context !== null) {\n this.audio = context.createBufferSource();\n this.audio.buffer = buffer;\n this.audio.connect(context.destination);\n } else {\n this.audio = buffer;\n this.audio.currentTime = 0;\n }\n setupTrial();\n })\n .catch((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 this.audio.addEventListener(\"ended\", end_trial);\n }\n\n // enable slider after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", enable_slider);\n }\n\n var html = '<div id=\"jspsych-audio-slider-response-wrapper\" style=\"margin: 100px 0px;\">';\n html +=\n '<div class=\"jspsych-audio-slider-response-container\" style=\"position:relative; margin: 0 auto 3em auto; width:';\n if (trial.slider_width !== null) {\n html += trial.slider_width + \"px;\";\n } else {\n html += \"auto;\";\n }\n html += '\">';\n html +=\n '<input type=\"range\" class=\"jspsych-slider\" value=\"' +\n trial.slider_start +\n '\" min=\"' +\n trial.min +\n '\" max=\"' +\n trial.max +\n '\" step=\"' +\n trial.step +\n '\" id=\"jspsych-audio-slider-response-response\"';\n if (!trial.response_allowed_while_playing) {\n html += \" disabled\";\n }\n html += \"></input><div>\";\n for (var j = 0; j < trial.labels.length; j++) {\n var label_width_perc = 100 / (trial.labels.length - 1);\n var percent_of_range = j * (100 / (trial.labels.length - 1));\n var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;\n var offset = (percent_dist_from_center * half_thumb_width) / 100;\n html +=\n '<div style=\"border: 1px solid transparent; display: inline-block; position: absolute; ' +\n \"left:calc(\" +\n percent_of_range +\n \"% - (\" +\n label_width_perc +\n \"% / 2) - \" +\n offset +\n \"px); text-align: center; width: \" +\n label_width_perc +\n '%;\">';\n html += '<span style=\"text-align: center; font-size: 80%;\">' + trial.labels[j] + \"</span>\";\n html += \"</div>\";\n }\n html += \"</div>\";\n html += \"</div>\";\n html += \"</div>\";\n\n if (trial.prompt !== null) {\n html += trial.prompt;\n }\n\n // add submit button\n var next_disabled_attribute = \"\";\n if (trial.require_movement || !trial.response_allowed_while_playing) {\n next_disabled_attribute = \"disabled\";\n }\n html +=\n '<button id=\"jspsych-audio-slider-response-next\" class=\"jspsych-btn\" ' +\n next_disabled_attribute +\n \">\" +\n trial.button_label +\n \"</button>\";\n\n display_element.innerHTML = html;\n\n response = {\n rt: null,\n response: null,\n };\n\n if (!trial.response_allowed_while_playing) {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).disabled = true;\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n\n if (trial.require_movement) {\n const enable_button = () => {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = false;\n };\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"mousedown\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"touchstart\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"change\", enable_button);\n }\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-next\")\n .addEventListener(\"click\", () => {\n // measure response time\n var endTime = performance.now();\n var rt = Math.round(endTime - startTime);\n if (context !== null) {\n endTime = context.currentTime;\n rt = Math.round((endTime - startTime) * 1000);\n }\n response.rt = rt;\n response.response = display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).valueAsNumber;\n\n if (trial.response_ends_trial) {\n end_trial();\n } else {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n });\n\n startTime = performance.now();\n // start audio\n if (context !== null) {\n startTime = context.currentTime;\n this.audio.start(startTime);\n } else {\n this.audio.play();\n }\n\n // end trial if trial_duration is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n };\n\n // function to enable slider after audio ends\n function enable_slider() {\n document.querySelector<HTMLInputElement>(\"#jspsych-audio-slider-response-response\").disabled =\n false;\n if (!trial.require_movement) {\n document.querySelector<HTMLButtonElement>(\"#jspsych-audio-slider-response-next\").disabled =\n false;\n }\n }\n\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 this.audio.stop();\n } else {\n this.audio.pause();\n }\n\n this.audio.removeEventListener(\"ended\", end_trial);\n this.audio.removeEventListener(\"ended\", enable_slider);\n\n // save data\n var trialdata = {\n rt: response.rt,\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: response.response,\n };\n\n display_element.innerHTML = \"\";\n\n // next trial\n this.jsPsych.finishTrial(trialdata);\n\n trial_complete();\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n\n 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 this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: this.jsPsych.randomization.randomInt(trial.min, trial.max),\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\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 private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n this.jsPsych.finishTrial(data);\n }\n\n private simulate_visual(trial: TrialType<Info>, simulation_options, load_callback: () => void) {\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 const el = display_element.querySelector<HTMLInputElement>(\"input[type='range']\");\n\n setTimeout(() => {\n this.jsPsych.pluginAPI.clickTarget(el);\n el.valueAsNumber = data.response;\n }, data.rt / 2);\n\n this.jsPsych.pluginAPI.clickTarget(display_element.querySelector(\"button\"), data.rt);\n }\n };\n\n this.trial(display_element, trial, () => {\n load_callback();\n\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n }\n}\n\nexport default AudioSliderResponsePlugin;\n"],"names":["ParameterType"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var jsPsychAudioSliderResponse=function(e){"use strict";const t={name:"audio-slider-response",parameters:{stimulus:{type:e.ParameterType.AUDIO,pretty_name:"Stimulus",default:void 0},min:{type:e.ParameterType.INT,pretty_name:"Min slider",default:0},max:{type:e.ParameterType.INT,pretty_name:"Max slider",default:100},slider_start:{type:e.ParameterType.INT,pretty_name:"Slider starting value",default:50},step:{type:e.ParameterType.INT,pretty_name:"Step",default:1},labels:{type:e.ParameterType.HTML_STRING,pretty_name:"Labels",default:[],array:!0},slider_width:{type:e.ParameterType.INT,pretty_name:"Slider width",default:null},button_label:{type:e.ParameterType.STRING,pretty_name:"Button label",default:"Continue",array:!1},require_movement:{type:e.ParameterType.BOOL,pretty_name:"Require movement",default:!1},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
|
|
1
|
+
var jsPsychAudioSliderResponse=function(e){"use strict";const t={name:"audio-slider-response",parameters:{stimulus:{type:e.ParameterType.AUDIO,pretty_name:"Stimulus",default:void 0},min:{type:e.ParameterType.INT,pretty_name:"Min slider",default:0},max:{type:e.ParameterType.INT,pretty_name:"Max slider",default:100},slider_start:{type:e.ParameterType.INT,pretty_name:"Slider starting value",default:50},step:{type:e.ParameterType.INT,pretty_name:"Step",default:1},labels:{type:e.ParameterType.HTML_STRING,pretty_name:"Labels",default:[],array:!0},slider_width:{type:e.ParameterType.INT,pretty_name:"Slider width",default:null},button_label:{type:e.ParameterType.STRING,pretty_name:"Button label",default:"Continue",array:!1},require_movement:{type:e.ParameterType.BOOL,pretty_name:"Require movement",default:!1},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 r;var i,a,n=this.jsPsych.pluginAPI.audioContext();this.jsPsych.pluginAPI.getAudioBuffer(t.stimulus).then((e=>{null!==n?(this.audio=n.createBufferSource(),this.audio.buffer=e,this.audio.connect(n.destination)):(this.audio=e,this.audio.currentTime=0),l()})).catch((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 l=()=>{t.trial_ends_after_audio&&this.audio.addEventListener("ended",d),t.response_allowed_while_playing||t.trial_ends_after_audio||this.audio.addEventListener("ended",o);var r='<div id="jspsych-audio-slider-response-wrapper" style="margin: 100px 0px;">';r+='<div class="jspsych-audio-slider-response-container" style="position:relative; margin: 0 auto 3em auto; width:',null!==t.slider_width?r+=t.slider_width+"px;":r+="auto;",r+='">',r+='<input type="range" class="jspsych-slider" value="'+t.slider_start+'" min="'+t.min+'" max="'+t.max+'" step="'+t.step+'" id="jspsych-audio-slider-response-response"',t.response_allowed_while_playing||(r+=" disabled"),r+="></input><div>";for(var l=0;l<t.labels.length;l++){var u=100/(t.labels.length-1),p=l*(100/(t.labels.length-1));r+='<div style="border: 1px solid transparent; display: inline-block; position: absolute; left:calc('+p+"% - ("+u+"% / 2) - "+7.5*((p-50)/50*100)/100+"px); text-align: center; width: "+u+'%;">',r+='<span style="text-align: center; font-size: 80%;">'+t.labels[l]+"</span>",r+="</div>"}r+="</div>",r+="</div>",r+="</div>",null!==t.prompt&&(r+=t.prompt);var y="";if(!t.require_movement&&t.response_allowed_while_playing||(y="disabled"),r+='<button id="jspsych-audio-slider-response-next" class="jspsych-btn" '+y+">"+t.button_label+"</button>",e.innerHTML=r,a={rt:null,response:null},t.response_allowed_while_playing||(e.querySelector("#jspsych-audio-slider-response-response").disabled=!0,e.querySelector("#jspsych-audio-slider-response-next").disabled=!0),t.require_movement){const t=()=>{e.querySelector("#jspsych-audio-slider-response-next").disabled=!1};e.querySelector("#jspsych-audio-slider-response-response").addEventListener("mousedown",t),e.querySelector("#jspsych-audio-slider-response-response").addEventListener("touchstart",t),e.querySelector("#jspsych-audio-slider-response-response").addEventListener("change",t)}e.querySelector("#jspsych-audio-slider-response-next").addEventListener("click",(()=>{var s=performance.now(),r=Math.round(s-i);null!==n&&(s=n.currentTime,r=Math.round(1e3*(s-i))),a.rt=r,a.response=e.querySelector("#jspsych-audio-slider-response-response").valueAsNumber,t.response_ends_trial?d():e.querySelector("#jspsych-audio-slider-response-next").disabled=!0})),i=performance.now(),null!==n?(i=n.currentTime,this.audio.start(i)):this.audio.play(),null!==t.trial_duration&&this.jsPsych.pluginAPI.setTimeout((()=>{d()}),t.trial_duration),s()};function o(){document.querySelector("#jspsych-audio-slider-response-response").disabled=!1,t.require_movement||(document.querySelector("#jspsych-audio-slider-response-next").disabled=!1)}const d=()=>{this.jsPsych.pluginAPI.clearAllTimeouts(),null!==n?this.audio.stop():this.audio.pause(),this.audio.removeEventListener("ended",d),this.audio.removeEventListener("ended",o);var s={rt:a.rt,stimulus:t.stimulus,slider_start:t.slider_start,response:a.response};e.innerHTML="",this.jsPsych.finishTrial(s),r()};return new Promise((e=>{r=e}))}simulate(e,t,s,r){"data-only"==t&&(r(),this.simulate_data_only(e,s)),"visual"==t&&this.simulate_visual(e,s,r)}create_simulation_data(e,t){const s={stimulus:e.stimulus,slider_start:e.slider_start,response:this.jsPsych.randomization.randomInt(e.min,e.max),rt:this.jsPsych.randomization.sampleExGaussian(500,50,1/150,!0)},r=this.jsPsych.pluginAPI.mergeSimulationData(s,t);return this.jsPsych.pluginAPI.ensureSimulationDataConsistency(e,r),r}simulate_data_only(e,t){const s=this.create_simulation_data(e,t);this.jsPsych.finishTrial(s)}simulate_visual(e,t,s){const r=this.create_simulation_data(e,t),i=this.jsPsych.getDisplayElement(),a=()=>{if(null!==r.rt){const e=i.querySelector("input[type='range']");setTimeout((()=>{this.jsPsych.pluginAPI.clickTarget(e),e.valueAsNumber=r.response}),r.rt/2),this.jsPsych.pluginAPI.clickTarget(i.querySelector("button"),r.rt)}};this.trial(i,e,(()=>{s(),e.response_allowed_while_playing?a():this.audio.addEventListener("ended",a)}))}}return s.info=t,s}(jsPsychModule);
|
|
2
2
|
//# sourceMappingURL=index.browser.min.js.map
|
|
@@ -1 +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-slider-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 /** Sets the minimum value of the slider. */\n min: {\n type: ParameterType.INT,\n pretty_name: \"Min slider\",\n default: 0,\n },\n /** Sets the maximum value of the slider */\n max: {\n type: ParameterType.INT,\n pretty_name: \"Max slider\",\n default: 100,\n },\n /** Sets the starting value of the slider */\n slider_start: {\n type: ParameterType.INT,\n pretty_name: \"Slider starting value\",\n default: 50,\n },\n /** Sets the step of the slider */\n step: {\n type: ParameterType.INT,\n pretty_name: \"Step\",\n default: 1,\n },\n /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */\n labels: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Labels\",\n default: [],\n array: true,\n },\n /** Width of the slider in pixels. */\n slider_width: {\n type: ParameterType.INT,\n pretty_name: \"Slider width\",\n default: null,\n },\n /** Label of the button to advance. */\n button_label: {\n type: ParameterType.STRING,\n pretty_name: \"Button label\",\n default: \"Continue\",\n array: false,\n },\n /** If true, the participant will have to move the slider before continuing. */\n require_movement: {\n type: ParameterType.BOOL,\n pretty_name: \"Require movement\",\n default: false,\n },\n /** Any content here will be displayed below the slider. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to show the trial. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, 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-slider-response**\n *\n * jsPsych plugin for playing audio and getting a slider response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}\n */\nclass AudioSliderResponsePlugin 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 // half of the thumb width value from jspsych.css, used to adjust the label positions\n var half_thumb_width = 7.5;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n var audio;\n\n // record webaudio context start time\n var startTime;\n\n // for storing data related to response\n var response;\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 // enable slider after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", enable_slider);\n }\n\n var html = '<div id=\"jspsych-audio-slider-response-wrapper\" style=\"margin: 100px 0px;\">';\n html +=\n '<div class=\"jspsych-audio-slider-response-container\" style=\"position:relative; margin: 0 auto 3em auto; width:';\n if (trial.slider_width !== null) {\n html += trial.slider_width + \"px;\";\n } else {\n html += \"auto;\";\n }\n html += '\">';\n html +=\n '<input type=\"range\" class=\"jspsych-slider\" value=\"' +\n trial.slider_start +\n '\" min=\"' +\n trial.min +\n '\" max=\"' +\n trial.max +\n '\" step=\"' +\n trial.step +\n '\" id=\"jspsych-audio-slider-response-response\"';\n if (!trial.response_allowed_while_playing) {\n html += \" disabled\";\n }\n html += \"></input><div>\";\n for (var j = 0; j < trial.labels.length; j++) {\n var label_width_perc = 100 / (trial.labels.length - 1);\n var percent_of_range = j * (100 / (trial.labels.length - 1));\n var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;\n var offset = (percent_dist_from_center * half_thumb_width) / 100;\n html +=\n '<div style=\"border: 1px solid transparent; display: inline-block; position: absolute; ' +\n \"left:calc(\" +\n percent_of_range +\n \"% - (\" +\n label_width_perc +\n \"% / 2) - \" +\n offset +\n \"px); text-align: center; width: \" +\n label_width_perc +\n '%;\">';\n html += '<span style=\"text-align: center; font-size: 80%;\">' + trial.labels[j] + \"</span>\";\n html += \"</div>\";\n }\n html += \"</div>\";\n html += \"</div>\";\n html += \"</div>\";\n\n if (trial.prompt !== null) {\n html += trial.prompt;\n }\n\n // add submit button\n var next_disabled_attribute = \"\";\n if (trial.require_movement || !trial.response_allowed_while_playing) {\n next_disabled_attribute = \"disabled\";\n }\n html +=\n '<button id=\"jspsych-audio-slider-response-next\" class=\"jspsych-btn\" ' +\n next_disabled_attribute +\n \">\" +\n trial.button_label +\n \"</button>\";\n\n display_element.innerHTML = html;\n\n response = {\n rt: null,\n response: null,\n };\n\n if (!trial.response_allowed_while_playing) {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).disabled = true;\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n\n if (trial.require_movement) {\n const enable_button = () => {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = false;\n };\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"mousedown\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"touchstart\", enable_button);\n }\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-next\")\n .addEventListener(\"click\", function () {\n // measure response time\n var endTime = performance.now();\n var rt = Math.round(endTime - startTime);\n if (context !== null) {\n endTime = context.currentTime;\n rt = Math.round((endTime - startTime) * 1000);\n }\n response.rt = rt;\n response.response = display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).valueAsNumber;\n\n if (trial.response_ends_trial) {\n end_trial();\n } else {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n });\n\n startTime = performance.now();\n // start audio\n if (context !== null) {\n startTime = context.currentTime;\n audio.start(startTime);\n } else {\n audio.play();\n }\n\n // end trial if trial_duration 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 enable slider after audio ends\n function enable_slider() {\n document.querySelector<HTMLInputElement>(\"#jspsych-audio-slider-response-response\").disabled =\n false;\n if (!trial.require_movement) {\n document.querySelector<HTMLButtonElement>(\"#jspsych-audio-slider-response-next\").disabled =\n false;\n }\n }\n\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\", enable_slider);\n\n // save data\n var trialdata = {\n rt: response.rt,\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: response.response,\n };\n\n display_element.innerHTML = \"\";\n\n // next trial\n this.jsPsych.finishTrial(trialdata);\n\n trial_complete();\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n}\n\nexport default AudioSliderResponsePlugin;\n"],"names":["info","name","parameters","stimulus","type","ParameterType","AUDIO","pretty_name","default","undefined","min","INT","max","slider_start","step","labels","HTML_STRING","array","slider_width","button_label","STRING","require_movement","BOOL","prompt","trial_duration","response_ends_trial","trial_ends_after_audio","response_allowed_while_playing","AudioSliderResponsePlugin","constructor","jsPsych","this","trial","display_element","on_load","trial_complete","audio","startTime","response","context","pluginAPI","audioContext","getAudioBuffer","then","buffer","createBufferSource","connect","destination","currentTime","setupTrial","catch","err","console","error","addEventListener","end_trial","enable_slider","html","j","length","label_width_perc","percent_of_range","next_disabled_attribute","innerHTML","rt","querySelector","disabled","enable_button","endTime","performance","now","Math","round","valueAsNumber","start","play","setTimeout","document","clearAllTimeouts","stop","pause","removeEventListener","trialdata","finishTrial","Promise","resolve"],"mappings":"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"}
|
|
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-slider-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 /** Sets the minimum value of the slider. */\n min: {\n type: ParameterType.INT,\n pretty_name: \"Min slider\",\n default: 0,\n },\n /** Sets the maximum value of the slider */\n max: {\n type: ParameterType.INT,\n pretty_name: \"Max slider\",\n default: 100,\n },\n /** Sets the starting value of the slider */\n slider_start: {\n type: ParameterType.INT,\n pretty_name: \"Slider starting value\",\n default: 50,\n },\n /** Sets the step of the slider */\n step: {\n type: ParameterType.INT,\n pretty_name: \"Step\",\n default: 1,\n },\n /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */\n labels: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Labels\",\n default: [],\n array: true,\n },\n /** Width of the slider in pixels. */\n slider_width: {\n type: ParameterType.INT,\n pretty_name: \"Slider width\",\n default: null,\n },\n /** Label of the button to advance. */\n button_label: {\n type: ParameterType.STRING,\n pretty_name: \"Button label\",\n default: \"Continue\",\n array: false,\n },\n /** If true, the participant will have to move the slider before continuing. */\n require_movement: {\n type: ParameterType.BOOL,\n pretty_name: \"Require movement\",\n default: false,\n },\n /** Any content here will be displayed below the slider. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to show the trial. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, 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-slider-response**\n *\n * jsPsych plugin for playing audio and getting a slider response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}\n */\nclass AudioSliderResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio;\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 // half of the thumb width value from jspsych.css, used to adjust the label positions\n var half_thumb_width = 7.5;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n\n // record webaudio context start time\n var startTime;\n\n // for storing data related to response\n var response;\n\n // load audio file\n this.jsPsych.pluginAPI\n .getAudioBuffer(trial.stimulus)\n .then((buffer) => {\n if (context !== null) {\n this.audio = context.createBufferSource();\n this.audio.buffer = buffer;\n this.audio.connect(context.destination);\n } else {\n this.audio = buffer;\n this.audio.currentTime = 0;\n }\n setupTrial();\n })\n .catch((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 this.audio.addEventListener(\"ended\", end_trial);\n }\n\n // enable slider after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", enable_slider);\n }\n\n var html = '<div id=\"jspsych-audio-slider-response-wrapper\" style=\"margin: 100px 0px;\">';\n html +=\n '<div class=\"jspsych-audio-slider-response-container\" style=\"position:relative; margin: 0 auto 3em auto; width:';\n if (trial.slider_width !== null) {\n html += trial.slider_width + \"px;\";\n } else {\n html += \"auto;\";\n }\n html += '\">';\n html +=\n '<input type=\"range\" class=\"jspsych-slider\" value=\"' +\n trial.slider_start +\n '\" min=\"' +\n trial.min +\n '\" max=\"' +\n trial.max +\n '\" step=\"' +\n trial.step +\n '\" id=\"jspsych-audio-slider-response-response\"';\n if (!trial.response_allowed_while_playing) {\n html += \" disabled\";\n }\n html += \"></input><div>\";\n for (var j = 0; j < trial.labels.length; j++) {\n var label_width_perc = 100 / (trial.labels.length - 1);\n var percent_of_range = j * (100 / (trial.labels.length - 1));\n var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;\n var offset = (percent_dist_from_center * half_thumb_width) / 100;\n html +=\n '<div style=\"border: 1px solid transparent; display: inline-block; position: absolute; ' +\n \"left:calc(\" +\n percent_of_range +\n \"% - (\" +\n label_width_perc +\n \"% / 2) - \" +\n offset +\n \"px); text-align: center; width: \" +\n label_width_perc +\n '%;\">';\n html += '<span style=\"text-align: center; font-size: 80%;\">' + trial.labels[j] + \"</span>\";\n html += \"</div>\";\n }\n html += \"</div>\";\n html += \"</div>\";\n html += \"</div>\";\n\n if (trial.prompt !== null) {\n html += trial.prompt;\n }\n\n // add submit button\n var next_disabled_attribute = \"\";\n if (trial.require_movement || !trial.response_allowed_while_playing) {\n next_disabled_attribute = \"disabled\";\n }\n html +=\n '<button id=\"jspsych-audio-slider-response-next\" class=\"jspsych-btn\" ' +\n next_disabled_attribute +\n \">\" +\n trial.button_label +\n \"</button>\";\n\n display_element.innerHTML = html;\n\n response = {\n rt: null,\n response: null,\n };\n\n if (!trial.response_allowed_while_playing) {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).disabled = true;\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n\n if (trial.require_movement) {\n const enable_button = () => {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = false;\n };\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"mousedown\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"touchstart\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"change\", enable_button);\n }\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-next\")\n .addEventListener(\"click\", () => {\n // measure response time\n var endTime = performance.now();\n var rt = Math.round(endTime - startTime);\n if (context !== null) {\n endTime = context.currentTime;\n rt = Math.round((endTime - startTime) * 1000);\n }\n response.rt = rt;\n response.response = display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).valueAsNumber;\n\n if (trial.response_ends_trial) {\n end_trial();\n } else {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n });\n\n startTime = performance.now();\n // start audio\n if (context !== null) {\n startTime = context.currentTime;\n this.audio.start(startTime);\n } else {\n this.audio.play();\n }\n\n // end trial if trial_duration is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n };\n\n // function to enable slider after audio ends\n function enable_slider() {\n document.querySelector<HTMLInputElement>(\"#jspsych-audio-slider-response-response\").disabled =\n false;\n if (!trial.require_movement) {\n document.querySelector<HTMLButtonElement>(\"#jspsych-audio-slider-response-next\").disabled =\n false;\n }\n }\n\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 this.audio.stop();\n } else {\n this.audio.pause();\n }\n\n this.audio.removeEventListener(\"ended\", end_trial);\n this.audio.removeEventListener(\"ended\", enable_slider);\n\n // save data\n var trialdata = {\n rt: response.rt,\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: response.response,\n };\n\n display_element.innerHTML = \"\";\n\n // next trial\n this.jsPsych.finishTrial(trialdata);\n\n trial_complete();\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n\n 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 this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: this.jsPsych.randomization.randomInt(trial.min, trial.max),\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\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 private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n this.jsPsych.finishTrial(data);\n }\n\n private simulate_visual(trial: TrialType<Info>, simulation_options, load_callback: () => void) {\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 const el = display_element.querySelector<HTMLInputElement>(\"input[type='range']\");\n\n setTimeout(() => {\n this.jsPsych.pluginAPI.clickTarget(el);\n el.valueAsNumber = data.response;\n }, data.rt / 2);\n\n this.jsPsych.pluginAPI.clickTarget(display_element.querySelector(\"button\"), data.rt);\n }\n };\n\n this.trial(display_element, trial, () => {\n load_callback();\n\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n }\n}\n\nexport default AudioSliderResponsePlugin;\n"],"names":["info","name","parameters","stimulus","type","ParameterType","AUDIO","pretty_name","default","undefined","min","INT","max","slider_start","step","labels","HTML_STRING","array","slider_width","button_label","STRING","require_movement","BOOL","prompt","trial_duration","response_ends_trial","trial_ends_after_audio","response_allowed_while_playing","AudioSliderResponsePlugin","constructor","jsPsych","this","trial","display_element","on_load","trial_complete","startTime","response","context","pluginAPI","audioContext","getAudioBuffer","then","buffer","audio","createBufferSource","connect","destination","currentTime","setupTrial","catch","err","console","error","addEventListener","end_trial","enable_slider","html","j","length","label_width_perc","percent_of_range","next_disabled_attribute","innerHTML","rt","querySelector","disabled","enable_button","endTime","performance","now","Math","round","valueAsNumber","start","play","setTimeout","document","clearAllTimeouts","stop","pause","removeEventListener","trialdata","finishTrial","Promise","resolve","simulate","simulation_mode","simulation_options","load_callback","simulate_data_only","simulate_visual","create_simulation_data","default_data","randomization","randomInt","sampleExGaussian","data","mergeSimulationData","ensureSimulationDataConsistency","getDisplayElement","respond","el","clickTarget"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -112,7 +112,6 @@ class AudioSliderResponsePlugin {
|
|
|
112
112
|
var half_thumb_width = 7.5;
|
|
113
113
|
// setup stimulus
|
|
114
114
|
var context = this.jsPsych.pluginAPI.audioContext();
|
|
115
|
-
var audio;
|
|
116
115
|
// record webaudio context start time
|
|
117
116
|
var startTime;
|
|
118
117
|
// for storing data related to response
|
|
@@ -120,30 +119,30 @@ class AudioSliderResponsePlugin {
|
|
|
120
119
|
// load audio file
|
|
121
120
|
this.jsPsych.pluginAPI
|
|
122
121
|
.getAudioBuffer(trial.stimulus)
|
|
123
|
-
.then(
|
|
122
|
+
.then((buffer) => {
|
|
124
123
|
if (context !== null) {
|
|
125
|
-
audio = context.createBufferSource();
|
|
126
|
-
audio.buffer = buffer;
|
|
127
|
-
audio.connect(context.destination);
|
|
124
|
+
this.audio = context.createBufferSource();
|
|
125
|
+
this.audio.buffer = buffer;
|
|
126
|
+
this.audio.connect(context.destination);
|
|
128
127
|
}
|
|
129
128
|
else {
|
|
130
|
-
audio = buffer;
|
|
131
|
-
audio.currentTime = 0;
|
|
129
|
+
this.audio = buffer;
|
|
130
|
+
this.audio.currentTime = 0;
|
|
132
131
|
}
|
|
133
132
|
setupTrial();
|
|
134
133
|
})
|
|
135
|
-
.catch(
|
|
134
|
+
.catch((err) => {
|
|
136
135
|
console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`);
|
|
137
136
|
console.error(err);
|
|
138
137
|
});
|
|
139
138
|
const setupTrial = () => {
|
|
140
139
|
// set up end event if trial needs it
|
|
141
140
|
if (trial.trial_ends_after_audio) {
|
|
142
|
-
audio.addEventListener("ended", end_trial);
|
|
141
|
+
this.audio.addEventListener("ended", end_trial);
|
|
143
142
|
}
|
|
144
143
|
// enable slider after audio ends if necessary
|
|
145
144
|
if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {
|
|
146
|
-
audio.addEventListener("ended", enable_slider);
|
|
145
|
+
this.audio.addEventListener("ended", enable_slider);
|
|
147
146
|
}
|
|
148
147
|
var html = '<div id="jspsych-audio-slider-response-wrapper" style="margin: 100px 0px;">';
|
|
149
148
|
html +=
|
|
@@ -224,10 +223,13 @@ class AudioSliderResponsePlugin {
|
|
|
224
223
|
display_element
|
|
225
224
|
.querySelector("#jspsych-audio-slider-response-response")
|
|
226
225
|
.addEventListener("touchstart", enable_button);
|
|
226
|
+
display_element
|
|
227
|
+
.querySelector("#jspsych-audio-slider-response-response")
|
|
228
|
+
.addEventListener("change", enable_button);
|
|
227
229
|
}
|
|
228
230
|
display_element
|
|
229
231
|
.querySelector("#jspsych-audio-slider-response-next")
|
|
230
|
-
.addEventListener("click",
|
|
232
|
+
.addEventListener("click", () => {
|
|
231
233
|
// measure response time
|
|
232
234
|
var endTime = performance.now();
|
|
233
235
|
var rt = Math.round(endTime - startTime);
|
|
@@ -248,14 +250,14 @@ class AudioSliderResponsePlugin {
|
|
|
248
250
|
// start audio
|
|
249
251
|
if (context !== null) {
|
|
250
252
|
startTime = context.currentTime;
|
|
251
|
-
audio.start(startTime);
|
|
253
|
+
this.audio.start(startTime);
|
|
252
254
|
}
|
|
253
255
|
else {
|
|
254
|
-
audio.play();
|
|
256
|
+
this.audio.play();
|
|
255
257
|
}
|
|
256
258
|
// end trial if trial_duration is set
|
|
257
259
|
if (trial.trial_duration !== null) {
|
|
258
|
-
this.jsPsych.pluginAPI.setTimeout(
|
|
260
|
+
this.jsPsych.pluginAPI.setTimeout(() => {
|
|
259
261
|
end_trial();
|
|
260
262
|
}, trial.trial_duration);
|
|
261
263
|
}
|
|
@@ -276,13 +278,13 @@ class AudioSliderResponsePlugin {
|
|
|
276
278
|
// stop the audio file if it is playing
|
|
277
279
|
// remove end event listeners if they exist
|
|
278
280
|
if (context !== null) {
|
|
279
|
-
audio.stop();
|
|
281
|
+
this.audio.stop();
|
|
280
282
|
}
|
|
281
283
|
else {
|
|
282
|
-
audio.pause();
|
|
284
|
+
this.audio.pause();
|
|
283
285
|
}
|
|
284
|
-
audio.removeEventListener("ended", end_trial);
|
|
285
|
-
audio.removeEventListener("ended", enable_slider);
|
|
286
|
+
this.audio.removeEventListener("ended", end_trial);
|
|
287
|
+
this.audio.removeEventListener("ended", enable_slider);
|
|
286
288
|
// save data
|
|
287
289
|
var trialdata = {
|
|
288
290
|
rt: response.rt,
|
|
@@ -299,6 +301,53 @@ class AudioSliderResponsePlugin {
|
|
|
299
301
|
trial_complete = resolve;
|
|
300
302
|
});
|
|
301
303
|
}
|
|
304
|
+
simulate(trial, simulation_mode, simulation_options, load_callback) {
|
|
305
|
+
if (simulation_mode == "data-only") {
|
|
306
|
+
load_callback();
|
|
307
|
+
this.simulate_data_only(trial, simulation_options);
|
|
308
|
+
}
|
|
309
|
+
if (simulation_mode == "visual") {
|
|
310
|
+
this.simulate_visual(trial, simulation_options, load_callback);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
create_simulation_data(trial, simulation_options) {
|
|
314
|
+
const default_data = {
|
|
315
|
+
stimulus: trial.stimulus,
|
|
316
|
+
slider_start: trial.slider_start,
|
|
317
|
+
response: this.jsPsych.randomization.randomInt(trial.min, trial.max),
|
|
318
|
+
rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
|
|
319
|
+
};
|
|
320
|
+
const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
|
|
321
|
+
this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
|
|
322
|
+
return data;
|
|
323
|
+
}
|
|
324
|
+
simulate_data_only(trial, simulation_options) {
|
|
325
|
+
const data = this.create_simulation_data(trial, simulation_options);
|
|
326
|
+
this.jsPsych.finishTrial(data);
|
|
327
|
+
}
|
|
328
|
+
simulate_visual(trial, simulation_options, load_callback) {
|
|
329
|
+
const data = this.create_simulation_data(trial, simulation_options);
|
|
330
|
+
const display_element = this.jsPsych.getDisplayElement();
|
|
331
|
+
const respond = () => {
|
|
332
|
+
if (data.rt !== null) {
|
|
333
|
+
const el = display_element.querySelector("input[type='range']");
|
|
334
|
+
setTimeout(() => {
|
|
335
|
+
this.jsPsych.pluginAPI.clickTarget(el);
|
|
336
|
+
el.valueAsNumber = data.response;
|
|
337
|
+
}, data.rt / 2);
|
|
338
|
+
this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("button"), data.rt);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
this.trial(display_element, trial, () => {
|
|
342
|
+
load_callback();
|
|
343
|
+
if (!trial.response_allowed_while_playing) {
|
|
344
|
+
this.audio.addEventListener("ended", respond);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
respond();
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
}
|
|
302
351
|
}
|
|
303
352
|
AudioSliderResponsePlugin.info = info;
|
|
304
353
|
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +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-slider-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 /** Sets the minimum value of the slider. */\n min: {\n type: ParameterType.INT,\n pretty_name: \"Min slider\",\n default: 0,\n },\n /** Sets the maximum value of the slider */\n max: {\n type: ParameterType.INT,\n pretty_name: \"Max slider\",\n default: 100,\n },\n /** Sets the starting value of the slider */\n slider_start: {\n type: ParameterType.INT,\n pretty_name: \"Slider starting value\",\n default: 50,\n },\n /** Sets the step of the slider */\n step: {\n type: ParameterType.INT,\n pretty_name: \"Step\",\n default: 1,\n },\n /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */\n labels: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Labels\",\n default: [],\n array: true,\n },\n /** Width of the slider in pixels. */\n slider_width: {\n type: ParameterType.INT,\n pretty_name: \"Slider width\",\n default: null,\n },\n /** Label of the button to advance. */\n button_label: {\n type: ParameterType.STRING,\n pretty_name: \"Button label\",\n default: \"Continue\",\n array: false,\n },\n /** If true, the participant will have to move the slider before continuing. */\n require_movement: {\n type: ParameterType.BOOL,\n pretty_name: \"Require movement\",\n default: false,\n },\n /** Any content here will be displayed below the slider. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to show the trial. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, 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-slider-response**\n *\n * jsPsych plugin for playing audio and getting a slider response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}\n */\nclass AudioSliderResponsePlugin 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 // half of the thumb width value from jspsych.css, used to adjust the label positions\n var half_thumb_width = 7.5;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n var audio;\n\n // record webaudio context start time\n var startTime;\n\n // for storing data related to response\n var response;\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 // enable slider after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", enable_slider);\n }\n\n var html = '<div id=\"jspsych-audio-slider-response-wrapper\" style=\"margin: 100px 0px;\">';\n html +=\n '<div class=\"jspsych-audio-slider-response-container\" style=\"position:relative; margin: 0 auto 3em auto; width:';\n if (trial.slider_width !== null) {\n html += trial.slider_width + \"px;\";\n } else {\n html += \"auto;\";\n }\n html += '\">';\n html +=\n '<input type=\"range\" class=\"jspsych-slider\" value=\"' +\n trial.slider_start +\n '\" min=\"' +\n trial.min +\n '\" max=\"' +\n trial.max +\n '\" step=\"' +\n trial.step +\n '\" id=\"jspsych-audio-slider-response-response\"';\n if (!trial.response_allowed_while_playing) {\n html += \" disabled\";\n }\n html += \"></input><div>\";\n for (var j = 0; j < trial.labels.length; j++) {\n var label_width_perc = 100 / (trial.labels.length - 1);\n var percent_of_range = j * (100 / (trial.labels.length - 1));\n var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;\n var offset = (percent_dist_from_center * half_thumb_width) / 100;\n html +=\n '<div style=\"border: 1px solid transparent; display: inline-block; position: absolute; ' +\n \"left:calc(\" +\n percent_of_range +\n \"% - (\" +\n label_width_perc +\n \"% / 2) - \" +\n offset +\n \"px); text-align: center; width: \" +\n label_width_perc +\n '%;\">';\n html += '<span style=\"text-align: center; font-size: 80%;\">' + trial.labels[j] + \"</span>\";\n html += \"</div>\";\n }\n html += \"</div>\";\n html += \"</div>\";\n html += \"</div>\";\n\n if (trial.prompt !== null) {\n html += trial.prompt;\n }\n\n // add submit button\n var next_disabled_attribute = \"\";\n if (trial.require_movement || !trial.response_allowed_while_playing) {\n next_disabled_attribute = \"disabled\";\n }\n html +=\n '<button id=\"jspsych-audio-slider-response-next\" class=\"jspsych-btn\" ' +\n next_disabled_attribute +\n \">\" +\n trial.button_label +\n \"</button>\";\n\n display_element.innerHTML = html;\n\n response = {\n rt: null,\n response: null,\n };\n\n if (!trial.response_allowed_while_playing) {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).disabled = true;\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n\n if (trial.require_movement) {\n const enable_button = () => {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = false;\n };\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"mousedown\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"touchstart\", enable_button);\n }\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-next\")\n .addEventListener(\"click\", function () {\n // measure response time\n var endTime = performance.now();\n var rt = Math.round(endTime - startTime);\n if (context !== null) {\n endTime = context.currentTime;\n rt = Math.round((endTime - startTime) * 1000);\n }\n response.rt = rt;\n response.response = display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).valueAsNumber;\n\n if (trial.response_ends_trial) {\n end_trial();\n } else {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n });\n\n startTime = performance.now();\n // start audio\n if (context !== null) {\n startTime = context.currentTime;\n audio.start(startTime);\n } else {\n audio.play();\n }\n\n // end trial if trial_duration 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 enable slider after audio ends\n function enable_slider() {\n document.querySelector<HTMLInputElement>(\"#jspsych-audio-slider-response-response\").disabled =\n false;\n if (!trial.require_movement) {\n document.querySelector<HTMLButtonElement>(\"#jspsych-audio-slider-response-next\").disabled =\n false;\n }\n }\n\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\", enable_slider);\n\n // save data\n var trialdata = {\n rt: response.rt,\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: response.response,\n };\n\n display_element.innerHTML = \"\";\n\n // next trial\n this.jsPsych.finishTrial(trialdata);\n\n trial_complete();\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n}\n\nexport default AudioSliderResponsePlugin;\n"],"names":["ParameterType"],"mappings":"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"}
|
|
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-slider-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 /** Sets the minimum value of the slider. */\n min: {\n type: ParameterType.INT,\n pretty_name: \"Min slider\",\n default: 0,\n },\n /** Sets the maximum value of the slider */\n max: {\n type: ParameterType.INT,\n pretty_name: \"Max slider\",\n default: 100,\n },\n /** Sets the starting value of the slider */\n slider_start: {\n type: ParameterType.INT,\n pretty_name: \"Slider starting value\",\n default: 50,\n },\n /** Sets the step of the slider */\n step: {\n type: ParameterType.INT,\n pretty_name: \"Step\",\n default: 1,\n },\n /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */\n labels: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Labels\",\n default: [],\n array: true,\n },\n /** Width of the slider in pixels. */\n slider_width: {\n type: ParameterType.INT,\n pretty_name: \"Slider width\",\n default: null,\n },\n /** Label of the button to advance. */\n button_label: {\n type: ParameterType.STRING,\n pretty_name: \"Button label\",\n default: \"Continue\",\n array: false,\n },\n /** If true, the participant will have to move the slider before continuing. */\n require_movement: {\n type: ParameterType.BOOL,\n pretty_name: \"Require movement\",\n default: false,\n },\n /** Any content here will be displayed below the slider. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to show the trial. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, 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-slider-response**\n *\n * jsPsych plugin for playing audio and getting a slider response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}\n */\nclass AudioSliderResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio;\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 // half of the thumb width value from jspsych.css, used to adjust the label positions\n var half_thumb_width = 7.5;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n\n // record webaudio context start time\n var startTime;\n\n // for storing data related to response\n var response;\n\n // load audio file\n this.jsPsych.pluginAPI\n .getAudioBuffer(trial.stimulus)\n .then((buffer) => {\n if (context !== null) {\n this.audio = context.createBufferSource();\n this.audio.buffer = buffer;\n this.audio.connect(context.destination);\n } else {\n this.audio = buffer;\n this.audio.currentTime = 0;\n }\n setupTrial();\n })\n .catch((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 this.audio.addEventListener(\"ended\", end_trial);\n }\n\n // enable slider after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", enable_slider);\n }\n\n var html = '<div id=\"jspsych-audio-slider-response-wrapper\" style=\"margin: 100px 0px;\">';\n html +=\n '<div class=\"jspsych-audio-slider-response-container\" style=\"position:relative; margin: 0 auto 3em auto; width:';\n if (trial.slider_width !== null) {\n html += trial.slider_width + \"px;\";\n } else {\n html += \"auto;\";\n }\n html += '\">';\n html +=\n '<input type=\"range\" class=\"jspsych-slider\" value=\"' +\n trial.slider_start +\n '\" min=\"' +\n trial.min +\n '\" max=\"' +\n trial.max +\n '\" step=\"' +\n trial.step +\n '\" id=\"jspsych-audio-slider-response-response\"';\n if (!trial.response_allowed_while_playing) {\n html += \" disabled\";\n }\n html += \"></input><div>\";\n for (var j = 0; j < trial.labels.length; j++) {\n var label_width_perc = 100 / (trial.labels.length - 1);\n var percent_of_range = j * (100 / (trial.labels.length - 1));\n var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;\n var offset = (percent_dist_from_center * half_thumb_width) / 100;\n html +=\n '<div style=\"border: 1px solid transparent; display: inline-block; position: absolute; ' +\n \"left:calc(\" +\n percent_of_range +\n \"% - (\" +\n label_width_perc +\n \"% / 2) - \" +\n offset +\n \"px); text-align: center; width: \" +\n label_width_perc +\n '%;\">';\n html += '<span style=\"text-align: center; font-size: 80%;\">' + trial.labels[j] + \"</span>\";\n html += \"</div>\";\n }\n html += \"</div>\";\n html += \"</div>\";\n html += \"</div>\";\n\n if (trial.prompt !== null) {\n html += trial.prompt;\n }\n\n // add submit button\n var next_disabled_attribute = \"\";\n if (trial.require_movement || !trial.response_allowed_while_playing) {\n next_disabled_attribute = \"disabled\";\n }\n html +=\n '<button id=\"jspsych-audio-slider-response-next\" class=\"jspsych-btn\" ' +\n next_disabled_attribute +\n \">\" +\n trial.button_label +\n \"</button>\";\n\n display_element.innerHTML = html;\n\n response = {\n rt: null,\n response: null,\n };\n\n if (!trial.response_allowed_while_playing) {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).disabled = true;\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n\n if (trial.require_movement) {\n const enable_button = () => {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = false;\n };\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"mousedown\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"touchstart\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"change\", enable_button);\n }\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-next\")\n .addEventListener(\"click\", () => {\n // measure response time\n var endTime = performance.now();\n var rt = Math.round(endTime - startTime);\n if (context !== null) {\n endTime = context.currentTime;\n rt = Math.round((endTime - startTime) * 1000);\n }\n response.rt = rt;\n response.response = display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).valueAsNumber;\n\n if (trial.response_ends_trial) {\n end_trial();\n } else {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n });\n\n startTime = performance.now();\n // start audio\n if (context !== null) {\n startTime = context.currentTime;\n this.audio.start(startTime);\n } else {\n this.audio.play();\n }\n\n // end trial if trial_duration is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n };\n\n // function to enable slider after audio ends\n function enable_slider() {\n document.querySelector<HTMLInputElement>(\"#jspsych-audio-slider-response-response\").disabled =\n false;\n if (!trial.require_movement) {\n document.querySelector<HTMLButtonElement>(\"#jspsych-audio-slider-response-next\").disabled =\n false;\n }\n }\n\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 this.audio.stop();\n } else {\n this.audio.pause();\n }\n\n this.audio.removeEventListener(\"ended\", end_trial);\n this.audio.removeEventListener(\"ended\", enable_slider);\n\n // save data\n var trialdata = {\n rt: response.rt,\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: response.response,\n };\n\n display_element.innerHTML = \"\";\n\n // next trial\n this.jsPsych.finishTrial(trialdata);\n\n trial_complete();\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n\n 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 this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: this.jsPsych.randomization.randomInt(trial.min, trial.max),\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\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 private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n this.jsPsych.finishTrial(data);\n }\n\n private simulate_visual(trial: TrialType<Info>, simulation_options, load_callback: () => void) {\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 const el = display_element.querySelector<HTMLInputElement>(\"input[type='range']\");\n\n setTimeout(() => {\n this.jsPsych.pluginAPI.clickTarget(el);\n el.valueAsNumber = data.response;\n }, data.rt / 2);\n\n this.jsPsych.pluginAPI.clickTarget(display_element.querySelector(\"button\"), data.rt);\n }\n };\n\n this.trial(display_element, trial, () => {\n load_callback();\n\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n }\n}\n\nexport default AudioSliderResponsePlugin;\n"],"names":["ParameterType"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -192,7 +192,12 @@ declare class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
192
192
|
};
|
|
193
193
|
};
|
|
194
194
|
};
|
|
195
|
+
private audio;
|
|
195
196
|
constructor(jsPsych: JsPsych);
|
|
196
197
|
trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void): Promise<unknown>;
|
|
198
|
+
simulate(trial: TrialType<Info>, simulation_mode: any, simulation_options: any, load_callback: () => void): void;
|
|
199
|
+
private create_simulation_data;
|
|
200
|
+
private simulate_data_only;
|
|
201
|
+
private simulate_visual;
|
|
197
202
|
}
|
|
198
203
|
export default AudioSliderResponsePlugin;
|
package/dist/index.js
CHANGED
|
@@ -110,7 +110,6 @@ class AudioSliderResponsePlugin {
|
|
|
110
110
|
var half_thumb_width = 7.5;
|
|
111
111
|
// setup stimulus
|
|
112
112
|
var context = this.jsPsych.pluginAPI.audioContext();
|
|
113
|
-
var audio;
|
|
114
113
|
// record webaudio context start time
|
|
115
114
|
var startTime;
|
|
116
115
|
// for storing data related to response
|
|
@@ -118,30 +117,30 @@ class AudioSliderResponsePlugin {
|
|
|
118
117
|
// load audio file
|
|
119
118
|
this.jsPsych.pluginAPI
|
|
120
119
|
.getAudioBuffer(trial.stimulus)
|
|
121
|
-
.then(
|
|
120
|
+
.then((buffer) => {
|
|
122
121
|
if (context !== null) {
|
|
123
|
-
audio = context.createBufferSource();
|
|
124
|
-
audio.buffer = buffer;
|
|
125
|
-
audio.connect(context.destination);
|
|
122
|
+
this.audio = context.createBufferSource();
|
|
123
|
+
this.audio.buffer = buffer;
|
|
124
|
+
this.audio.connect(context.destination);
|
|
126
125
|
}
|
|
127
126
|
else {
|
|
128
|
-
audio = buffer;
|
|
129
|
-
audio.currentTime = 0;
|
|
127
|
+
this.audio = buffer;
|
|
128
|
+
this.audio.currentTime = 0;
|
|
130
129
|
}
|
|
131
130
|
setupTrial();
|
|
132
131
|
})
|
|
133
|
-
.catch(
|
|
132
|
+
.catch((err) => {
|
|
134
133
|
console.error(`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`);
|
|
135
134
|
console.error(err);
|
|
136
135
|
});
|
|
137
136
|
const setupTrial = () => {
|
|
138
137
|
// set up end event if trial needs it
|
|
139
138
|
if (trial.trial_ends_after_audio) {
|
|
140
|
-
audio.addEventListener("ended", end_trial);
|
|
139
|
+
this.audio.addEventListener("ended", end_trial);
|
|
141
140
|
}
|
|
142
141
|
// enable slider after audio ends if necessary
|
|
143
142
|
if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {
|
|
144
|
-
audio.addEventListener("ended", enable_slider);
|
|
143
|
+
this.audio.addEventListener("ended", enable_slider);
|
|
145
144
|
}
|
|
146
145
|
var html = '<div id="jspsych-audio-slider-response-wrapper" style="margin: 100px 0px;">';
|
|
147
146
|
html +=
|
|
@@ -222,10 +221,13 @@ class AudioSliderResponsePlugin {
|
|
|
222
221
|
display_element
|
|
223
222
|
.querySelector("#jspsych-audio-slider-response-response")
|
|
224
223
|
.addEventListener("touchstart", enable_button);
|
|
224
|
+
display_element
|
|
225
|
+
.querySelector("#jspsych-audio-slider-response-response")
|
|
226
|
+
.addEventListener("change", enable_button);
|
|
225
227
|
}
|
|
226
228
|
display_element
|
|
227
229
|
.querySelector("#jspsych-audio-slider-response-next")
|
|
228
|
-
.addEventListener("click",
|
|
230
|
+
.addEventListener("click", () => {
|
|
229
231
|
// measure response time
|
|
230
232
|
var endTime = performance.now();
|
|
231
233
|
var rt = Math.round(endTime - startTime);
|
|
@@ -246,14 +248,14 @@ class AudioSliderResponsePlugin {
|
|
|
246
248
|
// start audio
|
|
247
249
|
if (context !== null) {
|
|
248
250
|
startTime = context.currentTime;
|
|
249
|
-
audio.start(startTime);
|
|
251
|
+
this.audio.start(startTime);
|
|
250
252
|
}
|
|
251
253
|
else {
|
|
252
|
-
audio.play();
|
|
254
|
+
this.audio.play();
|
|
253
255
|
}
|
|
254
256
|
// end trial if trial_duration is set
|
|
255
257
|
if (trial.trial_duration !== null) {
|
|
256
|
-
this.jsPsych.pluginAPI.setTimeout(
|
|
258
|
+
this.jsPsych.pluginAPI.setTimeout(() => {
|
|
257
259
|
end_trial();
|
|
258
260
|
}, trial.trial_duration);
|
|
259
261
|
}
|
|
@@ -274,13 +276,13 @@ class AudioSliderResponsePlugin {
|
|
|
274
276
|
// stop the audio file if it is playing
|
|
275
277
|
// remove end event listeners if they exist
|
|
276
278
|
if (context !== null) {
|
|
277
|
-
audio.stop();
|
|
279
|
+
this.audio.stop();
|
|
278
280
|
}
|
|
279
281
|
else {
|
|
280
|
-
audio.pause();
|
|
282
|
+
this.audio.pause();
|
|
281
283
|
}
|
|
282
|
-
audio.removeEventListener("ended", end_trial);
|
|
283
|
-
audio.removeEventListener("ended", enable_slider);
|
|
284
|
+
this.audio.removeEventListener("ended", end_trial);
|
|
285
|
+
this.audio.removeEventListener("ended", enable_slider);
|
|
284
286
|
// save data
|
|
285
287
|
var trialdata = {
|
|
286
288
|
rt: response.rt,
|
|
@@ -297,6 +299,53 @@ class AudioSliderResponsePlugin {
|
|
|
297
299
|
trial_complete = resolve;
|
|
298
300
|
});
|
|
299
301
|
}
|
|
302
|
+
simulate(trial, simulation_mode, simulation_options, load_callback) {
|
|
303
|
+
if (simulation_mode == "data-only") {
|
|
304
|
+
load_callback();
|
|
305
|
+
this.simulate_data_only(trial, simulation_options);
|
|
306
|
+
}
|
|
307
|
+
if (simulation_mode == "visual") {
|
|
308
|
+
this.simulate_visual(trial, simulation_options, load_callback);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
create_simulation_data(trial, simulation_options) {
|
|
312
|
+
const default_data = {
|
|
313
|
+
stimulus: trial.stimulus,
|
|
314
|
+
slider_start: trial.slider_start,
|
|
315
|
+
response: this.jsPsych.randomization.randomInt(trial.min, trial.max),
|
|
316
|
+
rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
|
|
317
|
+
};
|
|
318
|
+
const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
|
|
319
|
+
this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
|
|
320
|
+
return data;
|
|
321
|
+
}
|
|
322
|
+
simulate_data_only(trial, simulation_options) {
|
|
323
|
+
const data = this.create_simulation_data(trial, simulation_options);
|
|
324
|
+
this.jsPsych.finishTrial(data);
|
|
325
|
+
}
|
|
326
|
+
simulate_visual(trial, simulation_options, load_callback) {
|
|
327
|
+
const data = this.create_simulation_data(trial, simulation_options);
|
|
328
|
+
const display_element = this.jsPsych.getDisplayElement();
|
|
329
|
+
const respond = () => {
|
|
330
|
+
if (data.rt !== null) {
|
|
331
|
+
const el = display_element.querySelector("input[type='range']");
|
|
332
|
+
setTimeout(() => {
|
|
333
|
+
this.jsPsych.pluginAPI.clickTarget(el);
|
|
334
|
+
el.valueAsNumber = data.response;
|
|
335
|
+
}, data.rt / 2);
|
|
336
|
+
this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("button"), data.rt);
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
this.trial(display_element, trial, () => {
|
|
340
|
+
load_callback();
|
|
341
|
+
if (!trial.response_allowed_while_playing) {
|
|
342
|
+
this.audio.addEventListener("ended", respond);
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
respond();
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
}
|
|
300
349
|
}
|
|
301
350
|
AudioSliderResponsePlugin.info = info;
|
|
302
351
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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-slider-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 /** Sets the minimum value of the slider. */\n min: {\n type: ParameterType.INT,\n pretty_name: \"Min slider\",\n default: 0,\n },\n /** Sets the maximum value of the slider */\n max: {\n type: ParameterType.INT,\n pretty_name: \"Max slider\",\n default: 100,\n },\n /** Sets the starting value of the slider */\n slider_start: {\n type: ParameterType.INT,\n pretty_name: \"Slider starting value\",\n default: 50,\n },\n /** Sets the step of the slider */\n step: {\n type: ParameterType.INT,\n pretty_name: \"Step\",\n default: 1,\n },\n /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */\n labels: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Labels\",\n default: [],\n array: true,\n },\n /** Width of the slider in pixels. */\n slider_width: {\n type: ParameterType.INT,\n pretty_name: \"Slider width\",\n default: null,\n },\n /** Label of the button to advance. */\n button_label: {\n type: ParameterType.STRING,\n pretty_name: \"Button label\",\n default: \"Continue\",\n array: false,\n },\n /** If true, the participant will have to move the slider before continuing. */\n require_movement: {\n type: ParameterType.BOOL,\n pretty_name: \"Require movement\",\n default: false,\n },\n /** Any content here will be displayed below the slider. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to show the trial. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, 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-slider-response**\n *\n * jsPsych plugin for playing audio and getting a slider response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}\n */\nclass AudioSliderResponsePlugin 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 // half of the thumb width value from jspsych.css, used to adjust the label positions\n var half_thumb_width = 7.5;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n var audio;\n\n // record webaudio context start time\n var startTime;\n\n // for storing data related to response\n var response;\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 // enable slider after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n audio.addEventListener(\"ended\", enable_slider);\n }\n\n var html = '<div id=\"jspsych-audio-slider-response-wrapper\" style=\"margin: 100px 0px;\">';\n html +=\n '<div class=\"jspsych-audio-slider-response-container\" style=\"position:relative; margin: 0 auto 3em auto; width:';\n if (trial.slider_width !== null) {\n html += trial.slider_width + \"px;\";\n } else {\n html += \"auto;\";\n }\n html += '\">';\n html +=\n '<input type=\"range\" class=\"jspsych-slider\" value=\"' +\n trial.slider_start +\n '\" min=\"' +\n trial.min +\n '\" max=\"' +\n trial.max +\n '\" step=\"' +\n trial.step +\n '\" id=\"jspsych-audio-slider-response-response\"';\n if (!trial.response_allowed_while_playing) {\n html += \" disabled\";\n }\n html += \"></input><div>\";\n for (var j = 0; j < trial.labels.length; j++) {\n var label_width_perc = 100 / (trial.labels.length - 1);\n var percent_of_range = j * (100 / (trial.labels.length - 1));\n var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;\n var offset = (percent_dist_from_center * half_thumb_width) / 100;\n html +=\n '<div style=\"border: 1px solid transparent; display: inline-block; position: absolute; ' +\n \"left:calc(\" +\n percent_of_range +\n \"% - (\" +\n label_width_perc +\n \"% / 2) - \" +\n offset +\n \"px); text-align: center; width: \" +\n label_width_perc +\n '%;\">';\n html += '<span style=\"text-align: center; font-size: 80%;\">' + trial.labels[j] + \"</span>\";\n html += \"</div>\";\n }\n html += \"</div>\";\n html += \"</div>\";\n html += \"</div>\";\n\n if (trial.prompt !== null) {\n html += trial.prompt;\n }\n\n // add submit button\n var next_disabled_attribute = \"\";\n if (trial.require_movement || !trial.response_allowed_while_playing) {\n next_disabled_attribute = \"disabled\";\n }\n html +=\n '<button id=\"jspsych-audio-slider-response-next\" class=\"jspsych-btn\" ' +\n next_disabled_attribute +\n \">\" +\n trial.button_label +\n \"</button>\";\n\n display_element.innerHTML = html;\n\n response = {\n rt: null,\n response: null,\n };\n\n if (!trial.response_allowed_while_playing) {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).disabled = true;\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n\n if (trial.require_movement) {\n const enable_button = () => {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = false;\n };\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"mousedown\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"touchstart\", enable_button);\n }\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-next\")\n .addEventListener(\"click\", function () {\n // measure response time\n var endTime = performance.now();\n var rt = Math.round(endTime - startTime);\n if (context !== null) {\n endTime = context.currentTime;\n rt = Math.round((endTime - startTime) * 1000);\n }\n response.rt = rt;\n response.response = display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).valueAsNumber;\n\n if (trial.response_ends_trial) {\n end_trial();\n } else {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n });\n\n startTime = performance.now();\n // start audio\n if (context !== null) {\n startTime = context.currentTime;\n audio.start(startTime);\n } else {\n audio.play();\n }\n\n // end trial if trial_duration 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 enable slider after audio ends\n function enable_slider() {\n document.querySelector<HTMLInputElement>(\"#jspsych-audio-slider-response-response\").disabled =\n false;\n if (!trial.require_movement) {\n document.querySelector<HTMLButtonElement>(\"#jspsych-audio-slider-response-next\").disabled =\n false;\n }\n }\n\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\", enable_slider);\n\n // save data\n var trialdata = {\n rt: response.rt,\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: response.response,\n };\n\n display_element.innerHTML = \"\";\n\n // next trial\n this.jsPsych.finishTrial(trialdata);\n\n trial_complete();\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n}\n\nexport default AudioSliderResponsePlugin;\n"],"names":[],"mappings":"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"}
|
|
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-slider-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 /** Sets the minimum value of the slider. */\n min: {\n type: ParameterType.INT,\n pretty_name: \"Min slider\",\n default: 0,\n },\n /** Sets the maximum value of the slider */\n max: {\n type: ParameterType.INT,\n pretty_name: \"Max slider\",\n default: 100,\n },\n /** Sets the starting value of the slider */\n slider_start: {\n type: ParameterType.INT,\n pretty_name: \"Slider starting value\",\n default: 50,\n },\n /** Sets the step of the slider */\n step: {\n type: ParameterType.INT,\n pretty_name: \"Step\",\n default: 1,\n },\n /** Array containing the labels for the slider. Labels will be displayed at equidistant locations along the slider. */\n labels: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Labels\",\n default: [],\n array: true,\n },\n /** Width of the slider in pixels. */\n slider_width: {\n type: ParameterType.INT,\n pretty_name: \"Slider width\",\n default: null,\n },\n /** Label of the button to advance. */\n button_label: {\n type: ParameterType.STRING,\n pretty_name: \"Button label\",\n default: \"Continue\",\n array: false,\n },\n /** If true, the participant will have to move the slider before continuing. */\n require_movement: {\n type: ParameterType.BOOL,\n pretty_name: \"Require movement\",\n default: false,\n },\n /** Any content here will be displayed below the slider. */\n prompt: {\n type: ParameterType.HTML_STRING,\n pretty_name: \"Prompt\",\n default: null,\n },\n /** How long to show the trial. */\n trial_duration: {\n type: ParameterType.INT,\n pretty_name: \"Trial duration\",\n default: null,\n },\n /** If true, 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-slider-response**\n *\n * jsPsych plugin for playing audio and getting a slider response\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/plugins/jspsych-audio-slider-response/ audio-slider-response plugin documentation on jspsych.org}\n */\nclass AudioSliderResponsePlugin implements JsPsychPlugin<Info> {\n static info = info;\n private audio;\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 // half of the thumb width value from jspsych.css, used to adjust the label positions\n var half_thumb_width = 7.5;\n\n // setup stimulus\n var context = this.jsPsych.pluginAPI.audioContext();\n\n // record webaudio context start time\n var startTime;\n\n // for storing data related to response\n var response;\n\n // load audio file\n this.jsPsych.pluginAPI\n .getAudioBuffer(trial.stimulus)\n .then((buffer) => {\n if (context !== null) {\n this.audio = context.createBufferSource();\n this.audio.buffer = buffer;\n this.audio.connect(context.destination);\n } else {\n this.audio = buffer;\n this.audio.currentTime = 0;\n }\n setupTrial();\n })\n .catch((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 this.audio.addEventListener(\"ended\", end_trial);\n }\n\n // enable slider after audio ends if necessary\n if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {\n this.audio.addEventListener(\"ended\", enable_slider);\n }\n\n var html = '<div id=\"jspsych-audio-slider-response-wrapper\" style=\"margin: 100px 0px;\">';\n html +=\n '<div class=\"jspsych-audio-slider-response-container\" style=\"position:relative; margin: 0 auto 3em auto; width:';\n if (trial.slider_width !== null) {\n html += trial.slider_width + \"px;\";\n } else {\n html += \"auto;\";\n }\n html += '\">';\n html +=\n '<input type=\"range\" class=\"jspsych-slider\" value=\"' +\n trial.slider_start +\n '\" min=\"' +\n trial.min +\n '\" max=\"' +\n trial.max +\n '\" step=\"' +\n trial.step +\n '\" id=\"jspsych-audio-slider-response-response\"';\n if (!trial.response_allowed_while_playing) {\n html += \" disabled\";\n }\n html += \"></input><div>\";\n for (var j = 0; j < trial.labels.length; j++) {\n var label_width_perc = 100 / (trial.labels.length - 1);\n var percent_of_range = j * (100 / (trial.labels.length - 1));\n var percent_dist_from_center = ((percent_of_range - 50) / 50) * 100;\n var offset = (percent_dist_from_center * half_thumb_width) / 100;\n html +=\n '<div style=\"border: 1px solid transparent; display: inline-block; position: absolute; ' +\n \"left:calc(\" +\n percent_of_range +\n \"% - (\" +\n label_width_perc +\n \"% / 2) - \" +\n offset +\n \"px); text-align: center; width: \" +\n label_width_perc +\n '%;\">';\n html += '<span style=\"text-align: center; font-size: 80%;\">' + trial.labels[j] + \"</span>\";\n html += \"</div>\";\n }\n html += \"</div>\";\n html += \"</div>\";\n html += \"</div>\";\n\n if (trial.prompt !== null) {\n html += trial.prompt;\n }\n\n // add submit button\n var next_disabled_attribute = \"\";\n if (trial.require_movement || !trial.response_allowed_while_playing) {\n next_disabled_attribute = \"disabled\";\n }\n html +=\n '<button id=\"jspsych-audio-slider-response-next\" class=\"jspsych-btn\" ' +\n next_disabled_attribute +\n \">\" +\n trial.button_label +\n \"</button>\";\n\n display_element.innerHTML = html;\n\n response = {\n rt: null,\n response: null,\n };\n\n if (!trial.response_allowed_while_playing) {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).disabled = true;\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n\n if (trial.require_movement) {\n const enable_button = () => {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = false;\n };\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"mousedown\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"touchstart\", enable_button);\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-response\")\n .addEventListener(\"change\", enable_button);\n }\n\n display_element\n .querySelector(\"#jspsych-audio-slider-response-next\")\n .addEventListener(\"click\", () => {\n // measure response time\n var endTime = performance.now();\n var rt = Math.round(endTime - startTime);\n if (context !== null) {\n endTime = context.currentTime;\n rt = Math.round((endTime - startTime) * 1000);\n }\n response.rt = rt;\n response.response = display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-response\"\n ).valueAsNumber;\n\n if (trial.response_ends_trial) {\n end_trial();\n } else {\n display_element.querySelector<HTMLInputElement>(\n \"#jspsych-audio-slider-response-next\"\n ).disabled = true;\n }\n });\n\n startTime = performance.now();\n // start audio\n if (context !== null) {\n startTime = context.currentTime;\n this.audio.start(startTime);\n } else {\n this.audio.play();\n }\n\n // end trial if trial_duration is set\n if (trial.trial_duration !== null) {\n this.jsPsych.pluginAPI.setTimeout(() => {\n end_trial();\n }, trial.trial_duration);\n }\n\n on_load();\n };\n\n // function to enable slider after audio ends\n function enable_slider() {\n document.querySelector<HTMLInputElement>(\"#jspsych-audio-slider-response-response\").disabled =\n false;\n if (!trial.require_movement) {\n document.querySelector<HTMLButtonElement>(\"#jspsych-audio-slider-response-next\").disabled =\n false;\n }\n }\n\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 this.audio.stop();\n } else {\n this.audio.pause();\n }\n\n this.audio.removeEventListener(\"ended\", end_trial);\n this.audio.removeEventListener(\"ended\", enable_slider);\n\n // save data\n var trialdata = {\n rt: response.rt,\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: response.response,\n };\n\n display_element.innerHTML = \"\";\n\n // next trial\n this.jsPsych.finishTrial(trialdata);\n\n trial_complete();\n };\n\n return new Promise((resolve) => {\n trial_complete = resolve;\n });\n }\n\n 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 this.simulate_data_only(trial, simulation_options);\n }\n if (simulation_mode == \"visual\") {\n this.simulate_visual(trial, simulation_options, load_callback);\n }\n }\n\n private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n const default_data = {\n stimulus: trial.stimulus,\n slider_start: trial.slider_start,\n response: this.jsPsych.randomization.randomInt(trial.min, trial.max),\n rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),\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 private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n const data = this.create_simulation_data(trial, simulation_options);\n\n this.jsPsych.finishTrial(data);\n }\n\n private simulate_visual(trial: TrialType<Info>, simulation_options, load_callback: () => void) {\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 const el = display_element.querySelector<HTMLInputElement>(\"input[type='range']\");\n\n setTimeout(() => {\n this.jsPsych.pluginAPI.clickTarget(el);\n el.valueAsNumber = data.response;\n }, data.rt / 2);\n\n this.jsPsych.pluginAPI.clickTarget(display_element.querySelector(\"button\"), data.rt);\n }\n };\n\n this.trial(display_element, trial, () => {\n load_callback();\n\n if (!trial.response_allowed_while_playing) {\n this.audio.addEventListener(\"ended\", respond);\n } else {\n respond();\n }\n });\n }\n}\n\nexport default AudioSliderResponsePlugin;\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jspsych/plugin-audio-slider-response",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
},
|
|
35
35
|
"homepage": "https://www.jspsych.org/latest/plugins/audio-slider-response",
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"jspsych": ">=7.
|
|
37
|
+
"jspsych": ">=7.1.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@jspsych/config": "^1.
|
|
41
|
-
"@jspsych/test-utils": "^1.
|
|
40
|
+
"@jspsych/config": "^1.1.0",
|
|
41
|
+
"@jspsych/test-utils": "^1.1.0"
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { pressKey, simulateTimeline, startTimeline } from "@jspsych/test-utils";
|
|
2
|
+
import { initJsPsych } from "jspsych";
|
|
3
|
+
|
|
4
|
+
import audioSliderResponse from ".";
|
|
5
|
+
|
|
6
|
+
jest.useFakeTimers();
|
|
7
|
+
|
|
8
|
+
describe("audio-slider-response simulation", () => {
|
|
9
|
+
test("data mode works", async () => {
|
|
10
|
+
const timeline = [
|
|
11
|
+
{
|
|
12
|
+
type: audioSliderResponse,
|
|
13
|
+
stimulus: "foo.mp3",
|
|
14
|
+
},
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const { expectFinished, getData } = await simulateTimeline(timeline);
|
|
18
|
+
|
|
19
|
+
await expectFinished();
|
|
20
|
+
|
|
21
|
+
expect(getData().values()[0].rt).toBeGreaterThan(0);
|
|
22
|
+
expect(getData().values()[0].response).toBeGreaterThanOrEqual(0);
|
|
23
|
+
expect(getData().values()[0].response).toBeLessThanOrEqual(100);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// can't run this until we mock Audio elements.
|
|
27
|
+
test.skip("visual mode works", async () => {
|
|
28
|
+
const jsPsych = initJsPsych({ use_webaudio: false });
|
|
29
|
+
|
|
30
|
+
const timeline = [
|
|
31
|
+
{
|
|
32
|
+
type: audioSliderResponse,
|
|
33
|
+
stimulus: "foo.mp3",
|
|
34
|
+
prompt: "foo",
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const { expectFinished, expectRunning, getHTML, getData } = await simulateTimeline(
|
|
39
|
+
timeline,
|
|
40
|
+
"visual",
|
|
41
|
+
{},
|
|
42
|
+
jsPsych
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
await expectRunning();
|
|
46
|
+
|
|
47
|
+
expect(getHTML()).toContain("foo");
|
|
48
|
+
|
|
49
|
+
jest.runAllTimers();
|
|
50
|
+
|
|
51
|
+
await expectFinished();
|
|
52
|
+
|
|
53
|
+
expect(getData().values()[0].rt).toBeGreaterThan(0);
|
|
54
|
+
expect(getData().values()[0].response).toBeGreaterThanOrEqual(0);
|
|
55
|
+
expect(getData().values()[0].response).toBeLessThanOrEqual(100);
|
|
56
|
+
});
|
|
57
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -104,6 +104,7 @@ type Info = typeof info;
|
|
|
104
104
|
*/
|
|
105
105
|
class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
|
|
106
106
|
static info = info;
|
|
107
|
+
private audio;
|
|
107
108
|
|
|
108
109
|
constructor(private jsPsych: JsPsych) {}
|
|
109
110
|
|
|
@@ -116,7 +117,6 @@ class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
116
117
|
|
|
117
118
|
// setup stimulus
|
|
118
119
|
var context = this.jsPsych.pluginAPI.audioContext();
|
|
119
|
-
var audio;
|
|
120
120
|
|
|
121
121
|
// record webaudio context start time
|
|
122
122
|
var startTime;
|
|
@@ -127,18 +127,18 @@ class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
127
127
|
// load audio file
|
|
128
128
|
this.jsPsych.pluginAPI
|
|
129
129
|
.getAudioBuffer(trial.stimulus)
|
|
130
|
-
.then(
|
|
130
|
+
.then((buffer) => {
|
|
131
131
|
if (context !== null) {
|
|
132
|
-
audio = context.createBufferSource();
|
|
133
|
-
audio.buffer = buffer;
|
|
134
|
-
audio.connect(context.destination);
|
|
132
|
+
this.audio = context.createBufferSource();
|
|
133
|
+
this.audio.buffer = buffer;
|
|
134
|
+
this.audio.connect(context.destination);
|
|
135
135
|
} else {
|
|
136
|
-
audio = buffer;
|
|
137
|
-
audio.currentTime = 0;
|
|
136
|
+
this.audio = buffer;
|
|
137
|
+
this.audio.currentTime = 0;
|
|
138
138
|
}
|
|
139
139
|
setupTrial();
|
|
140
140
|
})
|
|
141
|
-
.catch(
|
|
141
|
+
.catch((err) => {
|
|
142
142
|
console.error(
|
|
143
143
|
`Failed to load audio file "${trial.stimulus}". Try checking the file path. We recommend using the preload plugin to load audio files.`
|
|
144
144
|
);
|
|
@@ -148,12 +148,12 @@ class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
148
148
|
const setupTrial = () => {
|
|
149
149
|
// set up end event if trial needs it
|
|
150
150
|
if (trial.trial_ends_after_audio) {
|
|
151
|
-
audio.addEventListener("ended", end_trial);
|
|
151
|
+
this.audio.addEventListener("ended", end_trial);
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
// enable slider after audio ends if necessary
|
|
155
155
|
if (!trial.response_allowed_while_playing && !trial.trial_ends_after_audio) {
|
|
156
|
-
audio.addEventListener("ended", enable_slider);
|
|
156
|
+
this.audio.addEventListener("ended", enable_slider);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
var html = '<div id="jspsych-audio-slider-response-wrapper" style="margin: 100px 0px;">';
|
|
@@ -248,11 +248,15 @@ class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
248
248
|
display_element
|
|
249
249
|
.querySelector("#jspsych-audio-slider-response-response")
|
|
250
250
|
.addEventListener("touchstart", enable_button);
|
|
251
|
+
|
|
252
|
+
display_element
|
|
253
|
+
.querySelector("#jspsych-audio-slider-response-response")
|
|
254
|
+
.addEventListener("change", enable_button);
|
|
251
255
|
}
|
|
252
256
|
|
|
253
257
|
display_element
|
|
254
258
|
.querySelector("#jspsych-audio-slider-response-next")
|
|
255
|
-
.addEventListener("click",
|
|
259
|
+
.addEventListener("click", () => {
|
|
256
260
|
// measure response time
|
|
257
261
|
var endTime = performance.now();
|
|
258
262
|
var rt = Math.round(endTime - startTime);
|
|
@@ -278,14 +282,14 @@ class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
278
282
|
// start audio
|
|
279
283
|
if (context !== null) {
|
|
280
284
|
startTime = context.currentTime;
|
|
281
|
-
audio.start(startTime);
|
|
285
|
+
this.audio.start(startTime);
|
|
282
286
|
} else {
|
|
283
|
-
audio.play();
|
|
287
|
+
this.audio.play();
|
|
284
288
|
}
|
|
285
289
|
|
|
286
290
|
// end trial if trial_duration is set
|
|
287
291
|
if (trial.trial_duration !== null) {
|
|
288
|
-
this.jsPsych.pluginAPI.setTimeout(
|
|
292
|
+
this.jsPsych.pluginAPI.setTimeout(() => {
|
|
289
293
|
end_trial();
|
|
290
294
|
}, trial.trial_duration);
|
|
291
295
|
}
|
|
@@ -310,13 +314,13 @@ class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
310
314
|
// stop the audio file if it is playing
|
|
311
315
|
// remove end event listeners if they exist
|
|
312
316
|
if (context !== null) {
|
|
313
|
-
audio.stop();
|
|
317
|
+
this.audio.stop();
|
|
314
318
|
} else {
|
|
315
|
-
audio.pause();
|
|
319
|
+
this.audio.pause();
|
|
316
320
|
}
|
|
317
321
|
|
|
318
|
-
audio.removeEventListener("ended", end_trial);
|
|
319
|
-
audio.removeEventListener("ended", enable_slider);
|
|
322
|
+
this.audio.removeEventListener("ended", end_trial);
|
|
323
|
+
this.audio.removeEventListener("ended", enable_slider);
|
|
320
324
|
|
|
321
325
|
// save data
|
|
322
326
|
var trialdata = {
|
|
@@ -338,6 +342,71 @@ class AudioSliderResponsePlugin implements JsPsychPlugin<Info> {
|
|
|
338
342
|
trial_complete = resolve;
|
|
339
343
|
});
|
|
340
344
|
}
|
|
345
|
+
|
|
346
|
+
simulate(
|
|
347
|
+
trial: TrialType<Info>,
|
|
348
|
+
simulation_mode,
|
|
349
|
+
simulation_options: any,
|
|
350
|
+
load_callback: () => void
|
|
351
|
+
) {
|
|
352
|
+
if (simulation_mode == "data-only") {
|
|
353
|
+
load_callback();
|
|
354
|
+
this.simulate_data_only(trial, simulation_options);
|
|
355
|
+
}
|
|
356
|
+
if (simulation_mode == "visual") {
|
|
357
|
+
this.simulate_visual(trial, simulation_options, load_callback);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private create_simulation_data(trial: TrialType<Info>, simulation_options) {
|
|
362
|
+
const default_data = {
|
|
363
|
+
stimulus: trial.stimulus,
|
|
364
|
+
slider_start: trial.slider_start,
|
|
365
|
+
response: this.jsPsych.randomization.randomInt(trial.min, trial.max),
|
|
366
|
+
rt: this.jsPsych.randomization.sampleExGaussian(500, 50, 1 / 150, true),
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);
|
|
370
|
+
|
|
371
|
+
this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);
|
|
372
|
+
|
|
373
|
+
return data;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
private simulate_data_only(trial: TrialType<Info>, simulation_options) {
|
|
377
|
+
const data = this.create_simulation_data(trial, simulation_options);
|
|
378
|
+
|
|
379
|
+
this.jsPsych.finishTrial(data);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
private simulate_visual(trial: TrialType<Info>, simulation_options, load_callback: () => void) {
|
|
383
|
+
const data = this.create_simulation_data(trial, simulation_options);
|
|
384
|
+
|
|
385
|
+
const display_element = this.jsPsych.getDisplayElement();
|
|
386
|
+
|
|
387
|
+
const respond = () => {
|
|
388
|
+
if (data.rt !== null) {
|
|
389
|
+
const el = display_element.querySelector<HTMLInputElement>("input[type='range']");
|
|
390
|
+
|
|
391
|
+
setTimeout(() => {
|
|
392
|
+
this.jsPsych.pluginAPI.clickTarget(el);
|
|
393
|
+
el.valueAsNumber = data.response;
|
|
394
|
+
}, data.rt / 2);
|
|
395
|
+
|
|
396
|
+
this.jsPsych.pluginAPI.clickTarget(display_element.querySelector("button"), data.rt);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
this.trial(display_element, trial, () => {
|
|
401
|
+
load_callback();
|
|
402
|
+
|
|
403
|
+
if (!trial.response_allowed_while_playing) {
|
|
404
|
+
this.audio.addEventListener("ended", respond);
|
|
405
|
+
} else {
|
|
406
|
+
respond();
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
}
|
|
341
410
|
}
|
|
342
411
|
|
|
343
412
|
export default AudioSliderResponsePlugin;
|