import { JsPsych , JsPsychPlugin , ParameterType , TrialType } from "jspsych" ; const info = < const > { name : "audio-keyboard-response" , parameters : { /** The audio file to be played. */ stimulus : { type : ParameterType . AUDIO , pretty_name : "Stimulus" , default : undefined , } , /** Array containing the key(s) the subject is allowed to press to respond to the stimulus. */ choices : { type : ParameterType . KEYS , pretty_name : "Choices" , default : "ALL_KEYS" , } , /** Any content here will be displayed below the stimulus. */ prompt : { type : ParameterType . HTML_STRING , pretty_name : "Prompt" , default : null , } , /** The maximum duration to wait for a response. */ trial_duration : { type : ParameterType . INT , pretty_name : "Trial duration" , default : null , } , /** If true, the trial will end when user makes a response. */ response_ends_trial : { type : ParameterType . BOOL , pretty_name : "Response ends trial" , default : true , } , /** If true, then the trial will end as soon as the audio file finishes playing. */ trial_ends_after_audio : { type : ParameterType . BOOL , pretty_name : "Trial ends after audio" , default : false , } , /** If true, then responses are allowed while the audio is playing. If false, then the audio must finish playing before a response is accepted. */ response_allowed_while_playing : { type : ParameterType . BOOL , pretty_name : "Response allowed while playing" , default : true , } , } , } ; type Info = typeof info ; author Josh de Leeuw
 *  see https class AudioKeyboardResponsePlugin implements JsPsychPlugin < Info > { static info = info ; private audio ; constructor ( private jsPsych ) { } trial ( display_element , trial , on_load ) { let trial_complete ; var context = this . jsPsych . pluginAPI . audioContext ( ) ; var response = { rt : null , key : null , } ; var startTime ; this . jsPsych . pluginAPI . getAudioBuffer ( trial . stimulus ) . then ( ( buffer ) => { if ( context !== null ) { this . audio = context . createBufferSource ( ) ; this . audio . buffer = buffer ; this . audio . connect ( context . destination ) ; } else { this . audio = buffer ; this . audio . currentTime = 0 ; } setupTrial ( ) ; } ) . catch ( ( err ) => { console . error ( `Failed to load audio file "${ trial . stimulus }". Try checking the file path. We recommend using the preload plugin to load audio files.` ) ; console . error ( err ) ; } ) ; const setupTrial = ( ) => { if ( trial . trial_ends_after_audio ) { this . audio . addEventListener ( "ended" , end_trial ) ; } if ( trial . prompt !== null ) { display_element . innerHTML = trial . prompt ; } if ( context !== null ) { startTime = context . currentTime ; this . audio . start ( startTime ) ; } else { this . audio . play ( ) ; } if ( trial . response_allowed_while_playing ) { setup_keyboard_listener ( ) ; } else if ( ! trial . trial_ends_after_audio ) { this . audio . addEventListener ( "ended" , setup_keyboard_listener ) ; } if ( trial . trial_duration !== null ) { this . jsPsych . pluginAPI . setTimeout ( ( ) => { end_trial ( ) ; } , trial . trial_duration ) ; } on_load ( ) ; } ; const end_trial = ( ) => { this . jsPsych . pluginAPI . clearAllTimeouts ( ) ; if ( context !== null ) { this . audio . stop ( ) ; } else { this . audio . pause ( ) ; } this . audio . removeEventListener ( "ended" , end_trial ) ; this . audio . removeEventListener ( "ended" , setup_keyboard_listener ) ; this . jsPsych . pluginAPI . cancelAllKeyboardResponses ( ) ; var trial_data = { rt : response . rt , stimulus : trial . stimulus , response : response . key , } ; display_element . innerHTML = "" ; this . jsPsych . finishTrial ( trial_data ) ; trial_complete ( ) ; } ; function after_response ( info ) { if ( response . key == null ) { response = info ; } if ( trial . response_ends_trial ) { end_trial ( ) ; } } const setup_keyboard_listener = ( ) => { if ( context !== null ) { this . jsPsych . pluginAPI . getKeyboardResponse ( { callback_function : after_response , valid_responses : trial . choices , rt_method : "audio" , persist : false , allow_held_key : false , audio_context : context , audio_context_start_time : startTime , } ) ; } else { this . jsPsych . pluginAPI . getKeyboardResponse ( { callback_function : after_response , valid_responses : trial . choices , rt_method : "performance" , persist : false , allow_held_key : false , } ) ; } } ; return new Promise ( ( resolve ) => { trial_complete = resolve ; } ) ; } simulate ( trial , simulation_mode , simulation_options , load_callback ) { if ( simulation_mode == "data-only" ) { load_callback ( ) ; this . simulate_data_only ( trial , simulation_options ) ; } if ( simulation_mode == "visual" ) { this . simulate_visual ( trial , simulation_options , load_callback ) ; } } private simulate_data_only ( trial , simulation_options ) { const data = this . create_simulation_data ( trial , simulation_options ) ; this . jsPsych . finishTrial ( data ) ; } private simulate_visual ( trial , simulation_options , load_callback ) { const data = this . create_simulation_data ( trial , simulation_options ) ; const display_element = this . jsPsych . getDisplayElement ( ) ; const respond = ( ) => { if ( data . rt !== null ) { this . jsPsych . pluginAPI . pressKey ( data . response , data . rt ) ; } } ; this . trial ( display_element , trial , ( ) => { load_callback ( ) ; if ( ! trial . response_allowed_while_playing ) { this . audio . addEventListener ( "ended" , respond ) ; } else { respond ( ) ; } } ) ; } private create_simulation_data ( trial , simulation_options ) { const default_data = { stimulus : trial . stimulus , rt : this . jsPsych . randomization . sampleExGaussian ( 500 , 50 , 1 / 150 , true ) , response : this . jsPsych . pluginAPI . getValidKey ( trial . choices ) , } ; const data = this . jsPsych . pluginAPI . mergeSimulationData ( default_data , simulation_options ) ; this . jsPsych . pluginAPI . ensureSimulationDataConsistency ( trial , data ) ; return data ; } } export default AudioKeyboardResponsePlugin ;