R/PBET.R

Defines functions conditional_trial_block_find_this_note_trials main_test_paradigms produce_naive_final_pbet_score final_results_pbet present_scores_pbet pbet_arrhythmic_trials pbet_rhythmic_trials conditional_trial_block item_characteristics_sampler_pbet get_trial_characteristics_pbet PBET_instructions PBET_intro PBET PBET_standalone

Documented in item_characteristics_sampler_pbet PBET PBET_standalone

#' Deploy the PBET as a standalone test
#'
#' @param num_items
#' @param melody_length
#' @param arrhythmic_item_bank
#' @param rhythmic_item_bank
#' @param demographics
#' @param demo
#' @param feedback
#' @param admin_password
#' @param SNR_test
#' @param get_range
#' @param absolute_url
#' @param num_examples
#' @param final_results
#' @param item_characteristics_sampler_function
#' @param get_trial_characteristics_function
#' @param max_goes_forced
#' @param max_goes
#' @param test_username
#' @param get_answer_function_midi
#' @param get_answer_function_audio
#' @param give_first_melody_note
#' @param with_final_page
#' @param show_socials
#' @param gold_msi
#' @param headphones_test
#' @param get_user_info
#' @param microphone_test
#' @param allow_repeat_SNR_tests
#' @param append_trial_block_before
#' @param append_trial_block_after
#' @param stop_recording_after
#' @param skip_setup
#' @param concise_wording
#' @param app_name
#' @param get_self_chosen_anonymous_id
#' @param adjust_range
#' @param main_module_name
#' @param input_type
#' @param sampler_function_arrhythmic
#' @param sampler_function_rhythmic
#' @param test_name
#' @param learn_test_paradigm
#' @param rel_to_abs_mel_function
#' @param add_consent_form
#' @param proportion_visual
#' @param default_range
#' @param default_singing_range
#' @param melody_block_paradigm
#' @param show_introduction
#' @param num_items_review
#' @param instrument_id Set what instrument is being used a priori.
#' @param user_id
#' @param experiment_id
#' @param present_continue_to_new_test_page
#' @param asynchronous_api_mode
#' @param left_margin
#' @param right_margin
#' @param css
#' @param content_border
#' @param full_screen
#' @param show_instructions
#' @param presampled_item_bank
#' @param mute_midi_playback
#'
#' @return
#' @export
#'
#' @examples
PBET_standalone <- function(num_items = list(interval_perception = 24L,
                                             find_this_note = 6L,
                                             arrhythmic = list("key_easy" = 5L,
                                                                 "key_hard" = 5L),
                                             rhythmic = list("key_easy" = 5L,
                                                               "key_hard" = 5L),
                                             wjd_audio = list("key_easy" = 5L,
                                                                "key_hard" = 5L)),
                            melody_length = 5:18,
                            arrhythmic_item_bank = PBET::WJD_phrase_item_bank, # this is correct, this is a version dispatched with PBET to avoid the WJD dependency
                            rhythmic_item_bank = PBET::WJD_phrase_item_bank, # this is correct, this is a version dispatched with PBET to avoid the WJD dependency
                            demographics = TRUE,
                            demo = FALSE,
                            feedback = FALSE,
                            admin_password = "demo",
                            SNR_test = TRUE,
                            get_range = TRUE,
                            absolute_url = character(),
                            num_examples = list(
                              "find_this_note" = 2L,
                              "interval_perception" = 2L,
                              "arrhythmic" = list("key_easy" = 1L, "key_hard" = 1L),
                              "rhythmic" = list("key_easy" = 1L, "key_hard" = 1L),
                              "wjd_audio" = 0L
                            ),
                            final_results = FALSE,
                            item_characteristics_sampler_function = item_characteristics_sampler_pbet,
                            get_trial_characteristics_function = get_trial_characteristics_pbet,
                            max_goes_forced = FALSE,
                            max_goes = 4L,
                            test_username = character(),
                            get_answer_function_midi = musicassessr::get_answer_midi_melodic_production,
                            get_answer_function_audio = musicassessr::get_answer_pyin_melodic_production,
                            give_first_melody_note = TRUE,
                            with_final_page = TRUE, # In standalone mode this should be TRUE
                            show_socials = TRUE,
                            gold_msi = TRUE,
                            headphones_test = TRUE,
                            get_user_info = TRUE,
                            microphone_test = TRUE,
                            allow_repeat_SNR_tests = TRUE,
                            append_trial_block_before = psychTestR::module("before"),
                            append_trial_block_after = psychTestR::module("after"),
                            stop_recording_after = 30,
                            skip_setup = FALSE,
                            concise_wording = FALSE,
                            app_name = "",
                            get_self_chosen_anonymous_id = FALSE,
                            adjust_range = TRUE,
                            main_module_name = "PBET",
                            input_type = c("midi_keyboard_or_microphone",
                                           "microphone",
                                           "midi_keyboard"),
                            sampler_function_arrhythmic = musicassessr::sample_arrhythmic,
                            sampler_function_rhythmic = musicassessr::sample_rhythmic,
                            test_name = "Play By Ear Test",
                            learn_test_paradigm = FALSE,
                            rel_to_abs_mel_function = musicassessr::transpose_melody_to_easy_or_hard_key,
                            add_consent_form = FALSE,
                            proportion_visual = list(learn = 0, test = 0),
                            default_range = musicassessr::set_default_range("Piano"),
                            default_singing_range = NULL,
                            melody_block_paradigm = c('standard', 'sing_melody_first', 'learn_phase_visual_display_modality'),
                            show_introduction = TRUE,
                            num_items_review = list("arrhythmic" = 0L, "rhythmic" = 0L),
                            instrument_id = NULL,
                            user_id = NULL,
                            experiment_id = NULL,
                            present_continue_to_new_test_page = TRUE,
                            asynchronous_api_mode = FALSE,
                            left_margin = 1L,
                            right_margin = 1L,
                            css = system.file('www/css/musicassessr.css', package = "musicassessr"),
                            content_border = "solid 3px #bfd5d9",
                            full_screen = FALSE,
                            show_instructions = TRUE,
                            presampled_item_bank = FALSE,
                            mute_midi_playback = FALSE,
                            ...) {


  timeline <- psychTestR::join(

              psychTestR::code_block(function(state, ...) {
                psychTestR::set_global("standalone", TRUE, state)
              }),

              PBET(num_items,
                   melody_length,
                    arrhythmic_item_bank,
                    rhythmic_item_bank,
                    demographics,
                    demo,
                    feedback,
                    admin_password,
                    SNR_test,
                    get_range,
                    absolute_url,
                    num_examples,
                    final_results,
                    item_characteristics_sampler_function,
                    get_trial_characteristics_function,
                    max_goes_forced,
                    max_goes,
                    test_username,
                   get_answer_function_midi,
                   get_answer_function_audio,
                   give_first_melody_note,
                   with_final_page,
                   show_socials,
                   gold_msi,
                   headphones_test,
                   get_user_info,
                   microphone_test,
                   allow_repeat_SNR_tests,
                   append_trial_block_before,
                   append_trial_block_after,
                   stop_recording_after,
                   skip_setup,
                   concise_wording,
                   app_name,
                   get_self_chosen_anonymous_id,
                   adjust_range,
                   main_module_name,
                   input_type,
                   sampler_function_arrhythmic,
                   sampler_function_rhythmic,
                   learn_test_paradigm,
                   rel_to_abs_mel_function,
                   add_consent_form,
                   proportion_visual,
                   default_range,
                   default_singing_range,
                   melody_block_paradigm,
                   show_introduction,
                   num_items_review,
                   instrument_id,
                   user_id,
                   experiment_id,
                   present_continue_to_new_test_page,
                   asynchronous_api_mode,
                   show_instructions,
                   presampled_item_bank,
                   mute_midi_playback,
                   ...)
              )


  # Run the test

  timeline %>%
    psychTestR::make_test(
      elts = .,
      opt = psychTestR::test_options(title = test_name,
                                     admin_password = admin_password,
                                     display = psychTestR::display_options(
                                       full_screen = full_screen,
                                       left_margin = 1L,
                                       right_margin = 1L,
                                       css = css,
                                       content_border = content_border
                                     ),
                                       additional_scripts = musicassessr::musicassessr_js(visual_notation = TRUE,
                                                                                          midi_input = TRUE,
                                                                                          record_audio = grepl("microphone", input_type),
                                                                                          app_name = app_name),
                                     languages = c("en"),
                                     on_stop_fun =  musicassessr::end_session(asynchronous_api_mode),
                                     ...))
}




#' Deploy the PBET
#'
#' @param num_items
#' @param melody_length
#' @param arrhythmic_item_bank
#' @param rhythmic_item_bank
#' @param demographics
#' @param demo
#' @param feedback
#' @param admin_password
#' @param SNR_test
#' @param get_range
#' @param absolute_url
#' @param num_examples
#' @param final_results
#' @param item_characteristics_sampler_function
#' @param get_trial_characteristics_function
#' @param max_goes_forced
#' @param max_goes
#' @param test_username
#' @param get_answer_function_midi
#' @param get_answer_function_audio
#' @param give_first_melody_note
#' @param with_final_page
#' @param show_socials
#' @param gold_msi
#' @param headphones_test
#' @param get_user_info
#' @param microphone_test
#' @param allow_repeat_SNR_tests
#' @param append_trial_block_before
#' @param append_trial_block_after
#' @param stop_recording_after
#' @param skip_setup
#' @param concise_wording
#' @param app_name
#' @param get_self_chosen_anonymous_id
#' @param adjust_range
#' @param main_module_name
#' @param input_type
#' @param sampler_function_arrhythmic
#' @param sampler_function_rhythmic
#' @param learn_test_paradigm
#' @param rel_to_abs_mel_function
#' @param add_consent_form
#' @param proportion_visual
#' @param default_range
#' @param default_singing_range
#' @param melody_block_paradigm
#' @param show_introduction
#' @param num_items_review
#' @param instrument_id Set what instrument is being used a priori.
#' @param user_id The user's ID, if using asynchronous_api_mode and applicable.
#' @param experiment_id The experiment ID, if using asynchronous_api_mode and applicable.
#' @param present_continue_to_new_test_page Should a "continue to test test" page be presented at the end of the PBET in a broader timeline?
#' @param asynchronous_api_mode
#' @param show_instructions
#' @param presampled_item_bank
#' @param mute_midi_playback
#' @return
#' @export
#'
#' @examples
PBET <- function(num_items = list(interval_perception = 0L,
                                  find_this_note = 0L,
                                  arrhythmic = list(key_easy = 5L, key_hard = 5L),
                                  rhythmic = list(key_easy = 5L, key_hard = 5L),
                                  wjd_audio = list(key_easy = 0L, key_hard = 0L)),
                 melody_length = 5:18,
                 arrhythmic_item_bank = PBET::WJD_phrase_item_bank,
                 rhythmic_item_bank = PBET::WJD_phrase_item_bank,
                 demographics = TRUE,
                 demo = FALSE,
                 feedback = FALSE,
                 admin_password = "demo",
                 SNR_test = TRUE,
                 get_range = TRUE,
                 absolute_url = character(),
                 num_examples = list(
                   "find_this_note" = 2L,
                   "interval_perception" = 2L,
                   "arrhythmic" = list("key_easy" = 1L, "key_hard" = 1L),
                   "rhythmic" = list("key_easy" = 0L, "key_hard" = 0L), # because it's effectively the same task as arrhythmic
                   "wjd_audio" = list("key_easy" = 0L, "key_hard" = 0L)
                  ),
                 final_results = FALSE,
                 item_characteristics_sampler_function = item_characteristics_sampler_pbet,
                 get_trial_characteristics_function = get_trial_characteristics_pbet,
                 max_goes_forced = FALSE,
                 max_goes = 4L,
                 test_username = character(),
                 get_answer_function_midi = musicassessr::get_answer_midi_melodic_production,
                 get_answer_function_audio = musicassessr::get_answer_pyin_melodic_production,
                 give_first_melody_note = TRUE,
                 with_final_page = FALSE, # In non-standalone mode this should be FALSE
                 show_socials = TRUE,
                 gold_msi = TRUE,
                 headphones_test = TRUE,
                 get_user_info = TRUE,
                 microphone_test = TRUE,
                 allow_repeat_SNR_tests = TRUE,
                 append_trial_block_before = psychTestR::module("before"),
                 append_trial_block_after = psychTestR::module("after"),
                 stop_recording_after = 30,
                 skip_setup = FALSE,
                 concise_wording = FALSE,
                 app_name = "",
                 get_self_chosen_anonymous_id = FALSE,
                 adjust_range = TRUE,
                 main_module_name = "PBET",
                 input_type = c("midi_keyboard_or_microphone",
                                "microphone",
                                "midi_keyboard"),
                 sampler_function_arrhythmic = musicassessr::sample_arrhythmic,
                 sampler_function_rhythmic = musicassessr::sample_rhythmic,
                 learn_test_paradigm = FALSE,
                 rel_to_abs_mel_function = musicassessr::transpose_melody_to_easy_or_hard_key,
                 add_consent_form = FALSE,
                 proportion_visual = list(learn = 0, test = 0),
                 default_range = musicassessr::set_default_range('Piano'),
                 default_singing_range = NULL,
                 melody_block_paradigm = c('standard', 'sing_melody_first', 'learn_phase_visual_display_modality'),
                 show_introduction = TRUE,
                 num_items_review = list("arrhythmic" = 0L, "rhythmic" = 0L),
                 instrument_id = NULL,
                 user_id = NULL,
                 experiment_id = NULL,
                 present_continue_to_new_test_page = TRUE,
                 asynchronous_api_mode = FALSE,
                 show_instructions = TRUE,
                 presampled_item_bank = FALSE,
                 mute_midi_playback = FALSE, ...) {

  melody_block_paradigm <- match.arg(melody_block_paradigm)
  input_type <- match.arg(input_type)


  stopifnot(
    is.list(num_items) & length(num_items) == 5,
    is.numeric(melody_length),
    is(arrhythmic_item_bank, "item_bank"),
    is(rhythmic_item_bank, "item_bank"),
    is.scalar.logical(demographics),
    is.scalar.logical(demo),
    is.scalar.logical(feedback),
    is.character(admin_password) & length(admin_password) == 1,
    is.scalar.logical(SNR_test),
    is.scalar.logical(get_range),
    is.character(absolute_url),
    is.list(num_examples) & length(num_examples) == 5,
    is.scalar.logical(final_results),
    is.function(item_characteristics_sampler_function) | is.null(item_characteristics_sampler_function),
    is.function(get_trial_characteristics_function) | is.null(get_trial_characteristics_function),
    is.scalar.logical(max_goes_forced),
    is.integer(max_goes) | is.infinite(max_goes),
    is.character(test_username),
    is.function(get_answer_function_midi),
    is.function(get_answer_function_audio),
    is.numeric(num_items$interval_perception),
    is.numeric(num_items$find_this_note),
    is.list(num_items$arrhythmic) | is.numeric(num_items$arrhythmic), # Numeric for reviews
    is.list(num_items$rhythmic)  | is.numeric(num_items$rhythmic), # Numeric for reviews
    is.list(num_items$wjd_audio),
    is.scalar.logical(give_first_melody_note),
    is.scalar.logical(with_final_page),
    is.scalar.logical(show_socials),
    is.scalar.logical(gold_msi),
    is.scalar.logical(headphones_test),
    is.scalar.logical(get_user_info),
    is.scalar.logical(microphone_test),
    is.scalar.logical(allow_repeat_SNR_tests),
    is.list(append_trial_block_before),
    is.list(append_trial_block_after),
    is.numeric(stop_recording_after) & length(stop_recording_after) == 1,
    is.scalar.logical(skip_setup) | skip_setup == "except_microphone",
    is.scalar.logical(concise_wording),
    assertthat::is.string(app_name),
    is.scalar.logical(get_self_chosen_anonymous_id),
    is.scalar.logical(adjust_range),
    assertthat::is.string(main_module_name),
    input_type %in% c("midi_keyboard_or_microphone", "microphone", "midi_keyboard"),
    is.function(sampler_function_arrhythmic) | is.null(sampler_function_arrhythmic),
    is.function(sampler_function_rhythmic) | is.null(sampler_function_rhythmic),
    is.scalar.logical(learn_test_paradigm),
    is.null.or(rel_to_abs_mel_function, is.function),
    is.scalar.logical(add_consent_form),
    is.list(proportion_visual) & length(proportion_visual) == 2 & setequal(names(proportion_visual), c('test', 'learn')),
    is.list(default_range) & length(default_range) == 3 & setequal(names(default_range), c('bottom_range', 'top_range', 'clef')),
    is.null.or(default_singing_range, function(x)   {
      is.list(x) & length(x) == 3 & setequal(names(x), c('bottom_range', 'top_range', 'clef'))
    }),
    melody_block_paradigm %in% c('standard', 'sing_melody_first', 'learn_phase_visual_display_modality'),
    is.scalar.logical(show_introduction),
    setequal(names(num_items_review), c("arrhythmic", "rhythmic")) & is.integer(num_items_review$arrhythmic) & is.integer(num_items_review$rhythmic),
    is.null.or(instrument_id, is.integer),
    is.null.or(user_id, is.integer),
    is.null.or(experiment_id, is.integer),
    is.scalar.logical(present_continue_to_new_test_page),
    is.scalar.logical(asynchronous_api_mode),
    is.scalar.logical(show_instructions),
    is.scalar.logical(presampled_item_bank),
    is.scalar.logical(mute_midi_playback)
    )


  if(melody_block_paradigm == 'learn_phase_visual_display_modality' && give_first_melody_note) {
    stop("give_first_melody_note must be FALSE if the melody_block_paradigm is learn_phase_visual_display_modality")
  }


  pars_arrhythmic_learn <- c(num_items$arrhythmic, list("melody_length" = melody_length, "proportion_visual" = proportion_visual$learn))
  pars_rhythmic_learn <- c(num_items$rhythmic, list("melody_length" = melody_length, "proportion_visual" = proportion_visual$learn))
  pars_arrhythmic_test <- c(num_items$arrhythmic, list("melody_length" = melody_length, "proportion_visual" = proportion_visual$test))
  pars_rhythmic_test <- c(num_items$rhythmic, list("melody_length" = melody_length, "proportion_visual" = proportion_visual$test))

  # if(learn_test_paradigm) {
  #   deploy_demographics <- FALSE
  #   gold_msi <- FALSE
  #   # We deploy them as a filler task, instead
  # }

  if(demo) warning('Running PBET in demo mode!')


  timeline <- psychTestR::join(

        psychTestR::module(main_module_name,

           psychTestR::new_timeline(
             psychTestR::join(

               # Init musicassessr
               musicassessr::musicassessr_init(app_name = app_name,
                                               experiment_id = experiment_id,
                                               user_id = user_id,
                                               asynchronous_api_mode = asynchronous_api_mode,
                                               default_range = default_range),

                          # Set input type, if not user-controlled
                          if(input_type %in% c("microphone", "midi_keyboard")) musicassessr::set_response_type(input_type),

                           # Set test
                           if(asynchronous_api_mode) musicassessr::set_test(test_name = "PBET", test_id = 2L),

                           # Set instrument
                           musicassessr::set_instrument(instrument_id),

                           # Introduction, same for all (i.e., midi and audio)
                           if (show_introduction)  { PBET_intro(demo,
                                                                SNR_test,
                                                                get_range,
                                                                absolute_url,
                                                                headphones_test,
                                                                get_user_info,
                                                                microphone_test,
                                                                max_goes,
                                                                max_goes_forced,
                                                                allow_repeat_SNR_tests,
                                                                skip_setup,
                                                                concise_wording,
                                                                get_self_chosen_anonymous_id,
                                                                adjust_range,
                                                                input_type,
                                                                instrument_id,
                                                                asynchronous_api_mode,
                                                                (num_items$arrhythmic + num_items$rhythmic),
                                                                melody_length,
                                                                show_instructions)
                             },

                           # Arbitrary and optional trial block to before main content
                           append_trial_block_before

                           ), dict = musicassessr::musicassessr_dict ), # End first timeline


                           # Main test paradigms

           # Optional learning phase
                       if (learn_test_paradigm)  { psychTestR::new_timeline(
                                                      main_test_paradigms(module_label = "learn",
                                                                         num_items,
                                                                         num_examples,
                                                                         feedback,
                                                                         page_type,
                                                                         arrhythmic_item_bank,
                                                                         rhythmic_item_bank,
                                                                         item_characteristics_sampler_function,
                                                                         get_trial_characteristics_function,
                                                                         max_goes,
                                                                         max_goes_forced,
                                                                         pars_arrhythmic_learn,
                                                                         pars_rhythmic_learn,
                                                                         give_first_melody_note,
                                                                         get_answer_function_midi,
                                                                         get_answer_function_audio,
                                                                         sampler_function_arrhythmic,
                                                                         sampler_function_rhythmic,
                                                                         rel_to_abs_mel_function,
                                                                         melody_block_paradigm,
                                                                         review = FALSE,
                                                                         phase = "learn",
                                                                         learn_test_paradigm,
                                                                         arrhythmic_page_text = psychTestR::i18n("arrhythmic_melody_trial_page_text"),
                                                                         arrhythmic_page_title = psychTestR::i18n("arrhythmic_melody_trial_page_title"),
                                                                         arrhythmic_instruction_text = psychTestR::i18n("arrhythmic_melody_trial_instruction_text"),
                                                                         rhythmic_page_text = psychTestR::i18n("rhythmic_melody_trial_page_text"),
                                                                         rhythmic_page_title = psychTestR::i18n("rhythmic_melody_trial_page_title"),
                                                                         rhythmic_instruction_text = psychTestR::i18n("rhythmic_melody_trial_instruction_text"),
                                                                         asynchronous_api_mode = asynchronous_api_mode,
                                                                         mute_midi_playback = mute_midi_playback
                                                                         ), dict = musicassessr::musicassessr_dict )},


                           if (learn_test_paradigm) musicassessr::filler_task(),

                             psychTestR::new_timeline(
                               main_test_paradigms(module_label = "test",
                                                   num_items,
                                                   num_examples,
                                                   feedback,
                                                   page_type,
                                                   arrhythmic_item_bank,
                                                   rhythmic_item_bank,
                                                   item_characteristics_sampler_function,
                                                   get_trial_characteristics_function,
                                                   max_goes,
                                                   max_goes_forced,
                                                   pars_arrhythmic_test,
                                                   pars_rhythmic_test,
                                                   give_first_melody_note,
                                                   get_answer_function_midi,
                                                   get_answer_function_audio,
                                                   sampler_function_arrhythmic,
                                                   sampler_function_rhythmic,
                                                   rel_to_abs_mel_function,
                                                   melody_block_paradigm,
                                                   review = FALSE,
                                                   phase = "test",
                                                   learn_test_paradigm,
                                                   arrhythmic_page_text = psychTestR::i18n("arrhythmic_melody_trial_page_text"),
                                                   arrhythmic_page_title = psychTestR::i18n("arrhythmic_melody_trial_page_title"),
                                                   arrhythmic_instruction_text = psychTestR::i18n("arrhythmic_melody_trial_instruction_text"),
                                                   rhythmic_page_text = psychTestR::i18n("rhythmic_melody_trial_page_text"),
                                                   rhythmic_page_title = psychTestR::i18n("rhythmic_melody_trial_page_title"),
                                                   rhythmic_instruction_text = psychTestR::i18n("rhythmic_melody_trial_instruction_text"),
                                                   presampled_item_bank = presampled_item_bank,
                                                   asynchronous_api_mode = asynchronous_api_mode,
                                                   mute_midi_playback = mute_midi_playback), dict = musicassessr::musicassessr_dict ),

                             if (num_items_review$arrhythmic > 0L | num_items_review$rhythmic > 0L) {
                               psychTestR::new_timeline(
                                 main_test_paradigms(module_label = "review",
                                                     num_items_review,
                                                     num_examples,
                                                     feedback,
                                                     page_type,
                                                     arrhythmic_item_bank,
                                                     rhythmic_item_bank,
                                                     item_characteristics_sampler_function,
                                                     get_trial_characteristics_function,
                                                     max_goes,
                                                     max_goes_forced,
                                                     pars_arrhythmic = NULL,
                                                     pars_rhythmic = NULL,
                                                     give_first_melody_note,
                                                     get_answer_function_midi,
                                                     get_answer_function_audio,
                                                     sampler_function_arrhythmic = NULL,
                                                     sampler_function_rhythmic = NULL,
                                                     rel_to_abs_mel_function,
                                                     melody_block_paradigm,
                                                     review = TRUE,
                                                     phase = "review",
                                                     learn_test_paradigm,
                                                     arrhythmic_page_text = psychTestR::i18n("arrhythmic_melody_trial_page_text"),
                                                     arrhythmic_page_title = psychTestR::i18n("arrhythmic_melody_trial_page_title"),
                                                     arrhythmic_instruction_text = psychTestR::i18n("arrhythmic_melody_trial_instruction_text"),
                                                     rhythmic_page_text = psychTestR::i18n("rhythmic_melody_trial_page_text"),
                                                     rhythmic_page_title = psychTestR::i18n("rhythmic_melody_trial_page_title"),
                                                     rhythmic_instruction_text = psychTestR::i18n("rhythmic_melody_trial_instruction_text"),
                                                     asynchronous_api_mode = asynchronous_api_mode,
                                                     mute_midi_playback = mute_midi_playback), dict = musicassessr::musicassessr_dict ) },

          psychTestR::new_timeline(
                    psychTestR::join(
                           # Arbitrary and optional trial block to go after
                           append_trial_block_after,

                           # Add final session information to DB (if asynchronous_api_mode and not standalone)
                           if(asynchronous_api_mode) {
                             psychTestR::conditional(function(state, ...) {
                               ! is.null(psychTestR::get_global("standalone", state))
                             }, logic = musicassessrdb::elt_add_final_session_info_to_db(asynchronous_api_mode))
                            },

                           psychTestR::elt_save_results_to_disk(complete = FALSE), # the test really finishes later (see below)

                           if(final_results) final_results_pbet(
                             test_name = "PBET",
                             url = absolute_url,
                             num_items$find_this_note,
                             sum(unlist(num_items$arrhythmic)),
                             sum(unlist(num_items$rhythmic)),
                             show_socials
                           )

        ), dict = musicassessr::musicassessr_dict )
      ),

      psychTestR::elt_save_results_to_disk(complete = TRUE),
      if(gold_msi) psyquest::GMS(subscales = c("Musical Training")),
      musicassessr::deploy_demographics(demographics),
      psychTestR::elt_save_results_to_disk(complete = TRUE),
      psychTestR::new_timeline(
        musicassessr::final_page_or_continue_to_new_test(final = with_final_page,
                                                         task_name = psychTestR::i18n("title"),
                                                         present_continue_to_new_test_page = present_continue_to_new_test_page), dict = musicassessr::musicassessr_dict )
  ) # end main join

  if(add_consent_form) {
    timeline <- consentr::consent(timeline)
  }

  timeline

}


PBET_intro <- function(demo = FALSE,
                      SNR_test = TRUE,
                      get_range = TRUE,
                      absolute_url,
                      headphones_test = TRUE,
                      get_user_info = TRUE,
                      microphone_test = TRUE,
                      max_goes = 4L,
                      max_goes_forced = FALSE,
                      allow_repeat_SNR_tests = TRUE,
                      skip_setup = FALSE,
                      concise_wording = FALSE,
                      get_self_chosen_anonymous_id = FALSE,
                      adjust_range = TRUE,
                      input_type = "midi_keyboard_or_microphone",
                      instrument_id = NULL,
                      asynchronous_api_mode = FALSE,
                      num_items = NULL, # Only needed for async API mode
                      melody_length = NULL, # Only needed for async API mode
                      show_instructions = TRUE) {

  psychTestR::join(

    # Introduction page
    psychTestR::one_button_page(body = shiny::tags$div(shiny::tags$h2(psychTestR::i18n("PBET_welcome")),
                                                       shiny::tags$img(src = 'https://adaptiveeartraining.com/assets/img/music.png', height = 100, width = 100),
                                                       shiny::tags$img(src = 'https://adaptiveeartraining.com/assets/img/saxophone.png', height = 100, width = 100),
                                                       shiny::tags$p(psychTestR::i18n("PBET_welcome_1")),
                                                       shiny::tags$p(psychTestR::i18n("PBET_welcome_2")),
                                button_text = psychTestR::i18n("Next"))),

    musicassessr::setup_pages(input_type = input_type,
                              demo = demo,
                              get_instrument_range = get_range,
                              SNR_test = SNR_test,
                              absolute_url = absolute_url,
                              select_instrument = is.null(instrument_id),
                              get_instrument_range_musical_notation = TRUE,
                              headphones = headphones_test,
                              get_user_info = get_user_info,
                              test_type = "instrument",
                              microphone_test = microphone_test,
                              allow_repeat_SNR_tests = allow_repeat_SNR_tests,
                              skip_setup = skip_setup,
                              concise_wording = concise_wording,
                              get_self_chosen_anonymous_id = get_self_chosen_anonymous_id,
                              musical_instrument = TRUE,
                              adjust_range = adjust_range,
                              asynchronous_api_mode = asynchronous_api_mode),

    # Sample from item bank now we have range
    #if(asynchronous_api_mode) musicassessrdb::sample_from_item_bank_elts(item_bank_name = "WJD_ngram", num_items, key_difficulty, melody_length),

    # Instructions
    if(show_instructions) PBET_instructions(max_goes, max_goes_forced)
  )

}

PBET_instructions <- function(max_goes, max_goes_forced) {

  if(max_goes_forced) {
    test_instructions2.1 <- "test_instructions_2.1.1.forced"
  } else {
    test_instructions2.1 <- "test_instructions_2.1.1"
  }

  psychTestR::join(
    psychTestR::one_button_page(body = shiny::tags$div(shiny::tags$h2("Instructions"),
                                                       shiny::tags$p(psychTestR::i18n("test_instructions_1.1")),
                                                       shiny::tags$p(psychTestR::i18n("test_instructions_1.2"))),
                                button_text = psychTestR::i18n("Next")),

    psychTestR::one_button_page(body = shiny::tags$div(shiny::tags$h2("Instructions"),
                                                       shiny::tags$p(if(is.infinite(max_goes)) psychTestR::i18n("test_instructions_2.1.infinite") else paste0(psychTestR::i18n("test_instructions_2.1.1"), " ", max_goes, " ", psychTestR::i18n("test_instructions_2.1.2"))),
                                                       shiny::tags$p(psychTestR::i18n("test_instructions_2.2"))),
                                button_text = psychTestR::i18n("Next")),

    psychTestR::one_button_page(body = shiny::tags$div(shiny::tags$h2("Instructions"),
                                                       shiny::tags$p(psychTestR::i18n("test_instructions_4"))),
                                button_text = psychTestR::i18n("Next")),

    psychTestR::one_button_page(body = shiny::tags$div(shiny::tags$h2("Instructions"),
                                                       shiny::tags$p("Make your notes clear when you play. Particularly, if playing a stringed instrument (e.g., cello, guitar), be careful that you do not leave a string ringing when you change note: the note detection procedure will not work well otherwise.")),
                                button_text = psychTestR::i18n("Next"))
  )

}



get_trial_characteristics_pbet <- function(trial_df, trial_no) {
  # Given the trial number, return what info is needed for the sampler
  trial_df %>%
    dplyr::filter(trial_no == !! trial_no)
}


#' Sampler for PBET
#'
#' @param pars
#'
#' @return
#' @export
#'
#' @examples
item_characteristics_sampler_pbet <- function(pars = list("key_easy" = 5L,
                                                          "key_hard" = 5L,
                                                          "proportion_visual" = 0)
                                                          ) {

  # Given a range of stimuli lengths and a number of difficulties, produce the test parameters
  no_items <- pars$key_easy + pars$key_hard
  no_items_key_easy <- pars$key_easy
  no_items_key_hard <- pars$key_hard

  # Deal with possibility of infinity (i.e., user-determined end time)
  no_items <- if(is.infinite(no_items)) 999 else no_items
  no_items_key_easy <- if(is.infinite(no_items_key_easy)) 999 else no_items_key_easy
  no_items_key_hard <- if(is.infinite(no_items_key_hard)) 999 else no_items_key_hard

  # If the proportion of visual items is > 0, how many visual items should there be?
  num_visual <- round(pars$proportion_visual * no_items)
  num_auditory <- no_items - num_visual

  # Create labels and randomise
  display_modality_labels <- c(rep("visual", num_visual), rep("auditory", num_auditory)) %>%
    sample()

  logging::loginfo("Creating PBET block with %s items: %s auditory and %s visual", no_items, num_auditory, num_visual)

  if(no_items_key_easy == 999 || no_items_key_hard == 999) {
    key_difficulty_labels <- c(rep("easy", 500), rep("hard", 499))
  } else {
    key_difficulty_labels <- c(rep("easy", no_items_key_easy), rep("hard", no_items_key_hard))
  }

  key_difficulty_labels <- sample(key_difficulty_labels)

  tibble::tibble(trial_no = 1:no_items,
                 key_difficulty = key_difficulty_labels,
                 display_modality = display_modality_labels)
}

# t <- item_characteristics_sampler_pbet(list("key_easy" = Inf, "key_hard" = Inf, proportion_visual = 0))

# item_characteristics_sampler_pbet(list("key_easy" = 1, "key_hard" = 1, proportion_visual = 0))




conditional_trial_block <- function(page_type = c("record_midi_page", "record_audio_page"),
                                    selection = c("MIDI", "Microphone"),
                                    trial_block_function,
                                    num_items,
                                    num_examples,
                                    item_bank = NULL,
                                    feedback = NULL,
                                    item_characteristics_pars = NULL,
                                    item_characteristics_sampler_function = NULL,
                                    get_trial_characteristics_function = NULL,
                                    max_goes = NULL,
                                    max_goes_forced = NULL,
                                    get_answer,
                                    give_first_melody_note = NULL,
                                    sampler_function = NULL,
                                    rel_to_abs_mel_function = NULL,
                                    melody_block_paradigm = c('standard', 'sing_melody_first'),
                                    review = FALSE,
                                    phase = c('test', 'learn', 'review'),
                                    singing_trials = FALSE,
                                    learn_test_paradigm = FALSE,
                                    page_text = "",
                                    page_title = "",
                                    instruction_text = "",
                                    presampled_item_bank = FALSE,
                                    asynchronous_api_mode = FALSE,
                                    mute_midi_playback = FALSE) {

  fun_name <- as.character(substitute(trial_block_function))[3]

  stopifnot(is.function(get_answer))

  args <- list(
    page_type = page_type,
    num_items = num_items,
    num_examples = num_examples,
    item_bank = item_bank,
    feedback = feedback,
    item_characteristics_pars = item_characteristics_pars,
    item_characteristics_sampler_function = item_characteristics_sampler_function,
    get_trial_characteristics_function = get_trial_characteristics_function,
    max_goes = max_goes,
    max_goes_forced = max_goes_forced,
    get_answer = get_answer,
    give_first_melody_note = give_first_melody_note,
    sampler_function = sampler_function,
    rel_to_abs_mel_function = rel_to_abs_mel_function,
    melody_block_paradigm = if(fun_name == "find_this_note_trials") NULL else melody_block_paradigm,
    review = if(fun_name == "find_this_note_trials") NULL else review,
    phase = if(fun_name == "find_this_note_trials") NULL else phase,
    singing_trials = singing_trials,
    learn_test_paradigm = if(fun_name == "find_this_note_trials") NULL else learn_test_paradigm,
    page_text = if(fun_name == "find_this_note_trials") NULL else page_text,
    page_title = if(fun_name == "find_this_note_trials") NULL else page_title,
    instruction_text = if(fun_name == "find_this_note_trials") NULL else instruction_text,
    presampled = if(fun_name == "find_this_note_trials") NULL else presampled_item_bank,
    asynchronous_api_mode = if(fun_name == "find_this_note_trials") NULL else asynchronous_api_mode,
    mute_midi_playback = if(fun_name == "find_this_note_trials") NULL else mute_midi_playback
    )

  # Remove empty arguments
  args <- args[!purrr::map_lgl(args, is.null)]

  psychTestR::conditional(test = function(state, ...) {

    response_type <- psychTestR::get_global("response_type", state)

    if(is.null(response_type)) {
      logging::logwarn("No response type set.. assuming a microphone test.")
      response_type <- "Microphone"
    }

    response_type == selection

  }, logic = do.call(trial_block_function, args))
}


pbet_rhythmic_trials <- function(item_bank,
                                 num_items,
                                 num_examples,
                                 feedback,
                                 item_characteristics_sampler_function,
                                 get_trial_characteristics_function,
                                 max_goes,
                                 max_goes_forced,
                                 pars_rhythmic,
                                 get_answer_function_midi = musicassessr::get_answer_midi_melodic_production,
                                 get_answer_function_audio = musicassessr::get_answer_pyin_melodic_production,
                                 give_first_melody_note,
                                 sampler_function,
                                 rel_to_abs_mel_function,
                                 melody_block_paradigm = c('standard', 'sing_melody_first'),
                                 review = FALSE,
                                 phase = c('test', 'learn', 'review'),
                                 learn_test_paradigm = FALSE,
                                 page_text = psychTestR::i18n("rhythmic_melody_trial_page_text"),
                                 page_title = psychTestR::i18n("rhythmic_melody_trial_page_title"),
                                 instruction_text = psychTestR::i18n("rhythmic_melody_trial_instruction_text"),
                                 presampled_item_bank = FALSE,
                                 asynchronous_api_mode = FALSE,
                                 mute_midi_playback = FALSE) {

  psychTestR::join(
    conditional_trial_block(page_type = "record_midi_page",
                            selection = "MIDI",
                            trial_block_function = musicassessr::rhythmic_melody_trials,
                            item_bank = item_bank,
                            num_items = num_items,
                            num_examples = num_examples,
                            feedback = feedback,
                            item_characteristics_pars = pars_rhythmic,
                            item_characteristics_sampler_function,
                            get_trial_characteristics_function,
                            max_goes,
                            max_goes_forced,
                            get_answer = get_answer_function_midi,
                            give_first_melody_note = give_first_melody_note,
                            sampler_function = sampler_function,
                            rel_to_abs_mel_function = rel_to_abs_mel_function,
                            melody_block_paradigm = melody_block_paradigm,
                            review = review,
                            phase = phase,
                            singing_trials = FALSE,
                            learn_test_paradigm = learn_test_paradigm,
                            page_text = page_text,
                            page_title = page_title,
                            instruction_text = instruction_text,
                            presampled_item_bank = presampled_item_bank,
                            asynchronous_api_mode = asynchronous_api_mode,
                            mute_midi_playback = mute_midi_playback),

    conditional_trial_block(page_type = "record_audio_page",
                            selection = "Microphone",
                            trial_block_function = musicassessr::rhythmic_melody_trials,
                            item_bank = item_bank,
                            num_items = num_items,
                            num_examples = num_examples,
                            feedback = feedback,
                            item_characteristics_pars = pars_rhythmic,
                            item_characteristics_sampler_function,
                            get_trial_characteristics_function,
                            max_goes,
                            max_goes_forced,
                            get_answer = get_answer_function_audio,
                            give_first_melody_note = give_first_melody_note,
                            sampler_function = sampler_function,
                            rel_to_abs_mel_function = rel_to_abs_mel_function,
                            melody_block_paradigm = melody_block_paradigm,
                            review = review,
                            phase = phase,
                            singing_trials = FALSE,
                            learn_test_paradigm = learn_test_paradigm,
                            page_text = page_text,
                            page_title = page_title,
                            instruction_text = instruction_text,
                            presampled_item_bank = presampled_item_bank,
                            asynchronous_api_mode = asynchronous_api_mode,
                            mute_midi_playback = mute_midi_playback)
  )
}

pbet_arrhythmic_trials <- function(item_bank,
                                   num_items,
                                   num_examples,
                                   feedback,
                                   item_characteristics_sampler_function,
                                   get_trial_characteristics_function,
                                   max_goes,
                                   max_goes_forced,
                                   pars_arrhythmic,
                                   get_answer_function_midi = musicassessr::get_answer_midi_melodic_production,
                                   get_answer_function_audio = musicassessr::get_answer_pyin_melodic_production,
                                   give_first_melody_note,
                                   sampler_function,
                                   rel_to_abs_mel_function,
                                   melody_block_paradigm = c('standard', 'sing_melody_first'),
                                   review = FALSE,
                                   phase = c('test', 'learn', 'review'),
                                   learn_test_paradigm = FALSE,
                                   page_text = psychTestR::i18n("rhythmic_melody_trial_page_text"),
                                   page_title = psychTestR::i18n("rhythmic_melody_trial_page_title"),
                                   instruction_text = psychTestR::i18n("rhythmic_melody_trial_instruction_text"),
                                   presampled_item_bank = FALSE,
                                   asynchronous_api_mode = FALSE,
                                   mute_midi_playback = FALSE) {

  stopifnot(is.function(get_answer_function_midi))


  psychTestR::join(

    conditional_trial_block(page_type = "record_midi_page",
                            selection = "MIDI",
                            trial_block_function = musicassessr::arrhythmic_melody_trials,
                            item_bank = item_bank,
                            num_items = num_items,
                            num_examples = num_examples,
                            feedback = feedback,
                            item_characteristics_pars = pars_arrhythmic,
                            item_characteristics_sampler_function,
                            get_trial_characteristics_function,
                            max_goes,
                            max_goes_forced,
                            get_answer = get_answer_function_midi,
                            give_first_melody_note = give_first_melody_note,
                            sampler_function = sampler_function,
                            rel_to_abs_mel_function = rel_to_abs_mel_function,
                            melody_block_paradigm = melody_block_paradigm,
                            review = review,
                            phase = phase,
                            singing_trials = FALSE,
                            learn_test_paradigm = learn_test_paradigm,
                            page_text = page_text,
                            page_title = page_title,
                            instruction_text = instruction_text,
                            presampled_item_bank = presampled_item_bank,
                            asynchronous_api_mode = asynchronous_api_mode,
                            mute_midi_playback = mute_midi_playback),

    conditional_trial_block(page_type = "record_audio_page",
                            selection = "Microphone",
                            trial_block_function = musicassessr::arrhythmic_melody_trials,
                            item_bank = item_bank,
                            num_items = num_items,
                            num_examples = num_examples,
                            feedback = feedback,
                            item_characteristics_pars = pars_arrhythmic,
                            item_characteristics_sampler_function,
                            get_trial_characteristics_function,
                            max_goes,
                            max_goes_forced,
                            get_answer = get_answer_function_audio,
                            give_first_melody_note = give_first_melody_note,
                            sampler_function = sampler_function,
                            rel_to_abs_mel_function = rel_to_abs_mel_function,
                            melody_block_paradigm = melody_block_paradigm,
                            review = review,
                            phase = phase,
                            singing_trials = FALSE,
                            learn_test_paradigm = learn_test_paradigm,
                            page_text = page_text,
                            page_title = page_title,
                            instruction_text = instruction_text,
                            presampled_item_bank = presampled_item_bank,
                            asynchronous_api_mode = asynchronous_api_mode,
                            mute_midi_playback = mute_midi_playback)
  )
}



present_scores_pbet <- function(res, num_items_arrhythmic, num_items_rhythmic) {

  if(num_items_arrhythmic > 0) {

    # arrhythmic
    arrhythmic_melodies <- musicassessr::tidy_melodies(res$PBET.arrhythmic_melodies)

    if(is.null(arrhythmic_melodies$error)) {

    arrhythmic_melody_summary <- arrhythmic_melodies %>%
      dplyr::select(opti3) %>%
      dplyr::mutate_if(is.character,as.numeric) %>%
      dplyr::filter(!is.na(opti3)) %>%
      dplyr::pull(opti3) %>%
      mean(na.rm = TRUE)

    } else {
      arrhythmic_melody_summary <- list("opti3" = 0)
    }
  }

  if(num_items_rhythmic > 0) {

    rhythmic_melodies <- musicassessr::tidy_melodies(res$PBET.rhythmic_melodies)

    # rhythmic
    if(is.null(rhythmic_melodies$error)) {

      rhythmic_melody_summary <- rhythmic_melodies %>%
        dplyr::select(opti3) %>%
        dplyr::mutate_if(is.character,as.numeric) %>%
        dplyr::filter(!is.na(opti3)) %>%
        dplyr::pull(opti3) %>%
        mean(na.rm = TRUE)

    } else {
      rhythmic_melody_summary <- list("opti3" = 0)
    }
  }

  list("arrhythmic" = ifelse(is.null(arrhythmic_melody_summary), data.frame(opti3 = 0.01), arrhythmic_melody_summary),
       "rhythmic" = ifelse(is.null(rhythmic_melody_summary), data.frame(opti3 = 0.01), rhythmic_melody_summary))

}





final_results_pbet <- function(test_name = "PBET",
                               url = "https://adaptiveeartraining.com/PBET2022",
                               num_items_find_this_note = 0L,
                               num_items_arrhythmic = 0L,
                               num_items_rhythmic = 0L,
                               socials = TRUE) {

  psychTestR::join(
    psychTestR::reactive_page(function(state, ...) {

      res <- as.list(psychTestR::get_results(state, complete = FALSE))

      processed_results <- present_scores_pbet(res, num_items_arrhythmic, num_items_rhythmic)

      final_score <- produce_naive_final_pbet_score(res, num_items_arrhythmic, num_items_rhythmic)

      psychTestR::set_local("final_score", final_score, state) # leave this in; it gets used by musicassessr



      psychTestR::text_input_page(
        label = "final_score",
        prompt = shiny::tags$div(style = "width: 500px;",
                                 shiny::tags$h2('Final Results'),

                                 shiny::tags$h3('Arrhythmic Melody Scores'),

                                 musicassessr::render_scores_table(processed_results$arrhythmic),

                                 shiny::tags$h3('Rhythmic Melody Scores'),

                                 musicassessr::render_scores_table(processed_results$rhythmic),

                                 shiny::tags$h3('Total Score'),
                                 shiny::tags$p(final_score),
                                 shiny::tags$p("Enter a username to see the scoreboard: ")

        )
      )

    }),

    musicassessr::share_score_page(test_name, url, hashtag = "PlayByEar", socials)
  )
}

produce_naive_final_pbet_score <- function(score_result_object,
                                           num_items_arrhythmic,
                                           num_items_rhythmic) {

  scor <- present_scores_pbet(score_result_object, num_items_arrhythmic, num_items_rhythmic)

  arr_scors <- scor$arrhythmic
  final_arr <- round(arr_scors[[1]] * 1000)

  rhy_scors <- scor$rhythmic

  final_rhy <- round(rhy_scors[[1]] * 1000)

  final_score <- final_arr + final_rhy

  final_score

}




main_test_paradigms <- function(module_label = "test",
                                num_items,
                                num_examples,
                                feedback,
                                page_type,
                                arrhythmic_item_bank,
                                rhythmic_item_bank,
                                item_characteristics_sampler_function,
                                get_trial_characteristics_function,
                                max_goes,
                                max_goes_forced,
                                pars_arrhythmic,
                                pars_rhythmic,
                                give_first_melody_note,
                                get_answer_function_midi = musicassessr::get_answer_midi_melodic_production,
                                get_answer_function_audio = musicassessr::get_answer_pyin_melodic_production,
                                sampler_function_arrhythmic,
                                sampler_function_rhythmic,
                                rel_to_abs_mel_function,
                                melody_block_paradigm,
                                review = FALSE,
                                phase = c('test', 'learn', 'review'),
                                learn_test_paradigm = FALSE,
                                arrhythmic_page_text = psychTestR::i18n("arrhythmic_melody_trial_page_text"),
                                arrhythmic_page_title = psychTestR::i18n("arrhythmic_melody_trial_page_title"),
                                arrhythmic_instruction_text = psychTestR::i18n("arrhythmic_melody_trial_instruction_text"),
                                rhythmic_page_text = psychTestR::i18n("rhythmic_melody_trial_page_text"),
                                rhythmic_page_title = psychTestR::i18n("rhythmic_melody_trial_page_title"),
                                rhythmic_instruction_text = psychTestR::i18n("rhythmic_melody_trial_instruction_text"),
                                presampled_item_bank = FALSE,
                                asynchronous_api_mode = FALSE,
                                mute_midi_playback = FALSE) {

  phase <- match.arg(phase)

  stopifnot(is.function(get_answer_function_midi),
            phase %in% c('test', 'learn', 'review'))

  logging::loginfo("Instantiating PBET block with type %s", phase)

  if(!review) {

    non_melody_based_trials <- psychTestR::join(
      # Interval perception trials

      musicassessr::interval_perception_trials(num_items = num_items$interval_perception),


      # Find that note trials
      conditional_trial_block_find_this_note_trials(num_items = num_items$find_this_note,
                                                    num_examples = num_examples$find_this_note,
                                                    get_answer_function_midi = get_answer_function_midi,
                                                    get_answer_function_audio = get_answer_function_audio)
    )
  }


  # Review only supported for standard melody trials
  melody_based_trials <- psychTestR::join(

      # Arrhythmic melody trials
      pbet_arrhythmic_trials(arrhythmic_item_bank,
                             num_items = num_items$arrhythmic,
                             num_examples$arrhythmic,
                             feedback,
                             item_characteristics_sampler_function,
                             get_trial_characteristics_function,
                             max_goes,
                             max_goes_forced,
                             pars_arrhythmic,
                             get_answer_function_midi = get_answer_function_midi,
                             get_answer_function_audio = get_answer_function_audio,
                             give_first_melody_note = give_first_melody_note,
                             sampler_function = sampler_function_arrhythmic,
                             rel_to_abs_mel_function = rel_to_abs_mel_function,
                             melody_block_paradigm = melody_block_paradigm,
                             review = review,
                             phase = phase,
                             learn_test_paradigm = learn_test_paradigm,
                             page_text = arrhythmic_page_text,
                             page_title = arrhythmic_page_title,
                             instruction_text = arrhythmic_instruction_text,
                             presampled_item_bank = presampled_item_bank,
                             asynchronous_api_mode = asynchronous_api_mode,
                             mute_midi_playback = mute_midi_playback),

    # Rhythmic melody trials
    pbet_rhythmic_trials(rhythmic_item_bank,
                         num_items = num_items$rhythmic,
                         num_examples$rhythmic,
                         feedback,
                         item_characteristics_sampler_function,
                         get_trial_characteristics_function,
                         max_goes,
                         max_goes_forced,
                         pars_rhythmic,
                         get_answer_function_midi = get_answer_function_midi,
                         get_answer_function_audio = get_answer_function_audio,
                         give_first_melody_note = give_first_melody_note,
                         sampler_function = sampler_function_rhythmic,
                         rel_to_abs_mel_function = rel_to_abs_mel_function,
                         melody_block_paradigm = melody_block_paradigm,
                         review = review,
                         phase = phase,
                         learn_test_paradigm = learn_test_paradigm,
                         page_text = rhythmic_page_text,
                         page_title = rhythmic_page_title,
                         instruction_text = rhythmic_instruction_text,
                         presampled_item_bank = presampled_item_bank,
                         asynchronous_api_mode = asynchronous_api_mode,
                         mute_midi_playback = mute_midi_playback)

  )


  psychTestR::module(label = module_label,
      psychTestR::join(

        # Non-melody-based trials
        if (!review) non_melody_based_trials, # Review only supported for standard melody trials

        # Melody-based trials
        melody_based_trials,

        # WJD real audio trials
        if (!review) musicassessr::wjd_audio_melody_trials(item_bank = PBET::WJD_phrase_item_bank, num_items = num_items$wjd_audio, num_examples = num_examples$wjd_audio, feedback = feedback)
  ))

}






conditional_trial_block_find_this_note_trials <- function(num_items,
                                                          num_examples,
                                                          get_answer_function_midi = musicassessr::get_answer_midi_melodic_production,
                                                          get_answer_function_audio = musicassessr::get_answer_pyin_melodic_production) {

  stopifnot(
    is.function(get_answer_function_midi),
    is.function(get_answer_function_audio)
  )

  psychTestR::join(
    conditional_trial_block(page_type = "record_midi_page",
                            selection = "MIDI",
                            trial_block_function = musicassessr::find_this_note_trials,
                            num_items = num_items,
                            num_examples = num_examples,
                            get_answer = get_answer_function_midi),

    conditional_trial_block(page_type = "record_audio_page",
                            selection = "Microphone",
                            trial_block_function = musicassessr::find_this_note_trials,
                            num_items = num_items,
                            num_examples = num_examples,
                            get_answer = get_answer_function_audio)
  )
}
syntheso/PBET documentation built on Jan. 20, 2025, 7:22 p.m.