Example #1
0
def main():
    def _dump_data():
        try:
            obj = dict(
                params=dict(
                    allowed_rhythm_deviation=allowed_rhythm_deviation,
                    allowed_tempo_deviation=allowed_tempo_deviation,
                    current_level=current_level,
                    experiment_type=experiment_type,
                    trial_on_path=trial_on_path,
                    truth_on_path=truth_on_path,
                ),
                processing=dict(
                    hits=[h.to_dict() for h in hits],
                    messages=[m.to_dict() for m in msgs],
                    messages_tempo_transformed=[
                        tmsg.to_dict() for tmsg in tempoed_msgs
                    ],
                    tempo_estimation=tempo_estimation,
                    tempo_str=tempo_str,
                ),
                results=dict(
                    passed=passed,
                    mistakes=mistakes,
                    played_enough_notes=played_enough_notes,
                ),
            )
            if check_rhythm:
                obj['processing'].update(
                    tempo_ceil=tempo_ceil,
                    tempo_floor=tempo_floor,
                )
            if not passed:  # only time when passed is True (all_hits_correct), advance_trial isn't referenced
                obj['results'].update(advance_trial=advance_trial)

            with open(data_dump_path, mode='w') as f:
                json.dump(obj, f, indent=4, sort_keys=True)
        except:
            pass

    if len(sys.argv) > 1:
        allowed_rhythm_deviation = int(sys.argv[1][:-1])
        allowed_tempo_deviation = int(sys.argv[2][:-1])
        trial_on_path = sys.argv[3]
        truth_on_path = sys.argv[4]
        current_level = json.loads(sys.argv[5])
        experiment_type = sys.argv[6]
    else:
        allowed_rhythm_deviation = 20
        allowed_tempo_deviation = 30
        trial_on_path = r'c:\Sync\Code\Python\Pyano-release\src\experiments\subjects\tests\ORIN\level_1_trial_0_on.txt'
        truth_on_path = r'c:\Sync\Code\Python\Pyano-release\src\experiments\truths\fur_elise_B_on.txt'
        current_level = dict(notes=10, trials=1, rhythm=True, tempo=50)
        experiment_type = 'exam'

    data_dump_path = trial_on_path.rpartition('_on.txt')[0] + '_data.json'
    truths: List[Message] = Message.normalize_chords_in_file(truth_on_path)
    msgs: List[Message] = Message.normalize_chords_in_file(trial_on_path)

    check_rhythm = current_level['rhythm']
    current_level_notes = current_level['notes']
    tempo_estimation = estimate_tempo_percentage(msgs, truths,
                                                 current_level_notes)
    tempoed_msgs: List[Message] = Message.transform_to_tempo(
        msgs, tempo_estimation)

    mistakes = []
    hits = []  # for data dumping
    truth_chords = Message.get_chords(truths[:current_level_notes])
    try:
        Message.normalize_chords(
            tempoed_msgs,
            truth_chords)  # of tempoed_msgs, according to truth_chords
    except Exception as e:
        entry = logger.log(
            dict(tempoed_msgs=tempoed_msgs,
                 truth_chords=truth_chords,
                 msg=msgs,
                 current_level_notes=current_level_notes,
                 check_rhythm=check_rhythm,
                 tempo_estimation=tempo_estimation,
                 truths=truths,
                 e=e),
            title=
            f"Exception at Message.normalize_chords(tempoed_msgs, truth_chords)"
        )
        raise Exception(
            f"check done trial normalize chords exception. see log check_done_trial, entry: {entry}"
        )
    for i in range(min(current_level_notes, len(msgs))):
        hit = Hit(tempoed_msgs[i], truths[i], allowed_rhythm_deviation)
        hits.append(hit)
        mistakes.append(hit.get_mistake_kind())

    played_enough_notes = len(msgs) >= current_level_notes
    if not played_enough_notes:
        # needed to play 4 notes but playeed 3: [ null, null, null, "accuracy" ]
        # if also made a mistake: [ null, "rhythm", null, "accuracy" ]
        # Failed feedback msg could be "[ null, 'rhythm', null, 'accuracy' ], not enough notes and too fast"
        mistakes += ["accuracy"] * (current_level_notes - len(msgs))
    tempo_str = "ok"
    if check_rhythm:
        # Failed feedback msg could be "[ null, 'rhythm', null, 'accuracy' ] and too fast"

        if not (0 <= allowed_tempo_deviation <= 100):
            entry = logger.log(
                dict(trial_on_path=trial_on_path,
                     truth_on_path=truth_on_path,
                     current_level=current_level,
                     allowed_tempo_deviation=allowed_tempo_deviation),
                title="check_done_trial ValueError bad allowed_tempo_deviation"
            )
            raise ValueError(
                f"check_done_trial inside rhythm checking got bad allowed_tempo_deviation, got: {allowed_tempo_deviation}. see classes.log, entry: {entry}"
            )

        level_tempo = current_level['tempo']  # 75
        extra = level_tempo * allowed_tempo_deviation / 100  # 75*0.1 = 7.5
        tempo_floor = level_tempo - extra  # 67.5
        tempo_ceil = max(100, level_tempo + extra)  # max(100, 82.5) = 100
        if tempo_estimation < tempo_floor:
            tempo_str = "slow"
        elif tempo_estimation > tempo_ceil:
            tempo_str = "fast"
        else:
            tempo_str = "ok"

        if tempo_str != 'ok':
            # acc mistake when checking rhythm. if experiemnt is exam, advance anyway
            if experiment_type == 'exam':
                advance_trial = True
            else:
                advance_trial = 'accuracy' not in mistakes
            passed = False
            _dump_data()
            prfl(
                dict(
                    advance_trial=advance_trial,
                    mistakes=mistakes,
                    passed=passed,
                    played_enough_notes=played_enough_notes,
                    tempo_str=tempo_str,
                ))
            return
    else:  # delete rhythm mistakes if not checking rhythm. ["rhythm", null, "accuracy"] => [null, null, "accuracy"]
        mistakes = [None if m == "rhythm" else m for m in mistakes]

    if not played_enough_notes:
        # not enough notes == accuracy mistakes. dont adv if check rhythm. if experiemnt is exam, advance anyway
        if experiment_type == 'exam':
            advance_trial = True
        else:
            advance_trial = not check_rhythm

        passed = False
        played_enough_notes = False
        _dump_data()
        prfl(
            dict(passed=passed,
                 mistakes=mistakes,
                 advance_trial=advance_trial,
                 played_enough_notes=played_enough_notes,
                 tempo_str=tempo_str))
        return

    # Played all notes or too many notes
    all_hits_correct = all([mistake is None for mistake in mistakes])
    if all_hits_correct:
        passed = True
        played_too_many_notes = len(msgs) > current_level_notes
        _dump_data()
        prfl(dict(passed=passed, played_too_many_notes=played_too_many_notes))
        return
    else:
        # Had mistakes
        # Tempo ok, played all required notes
        # if not check rhythm: has accuracy mistakes
        # if check rhythm: has accuracy and/or rhythm mistakes
        # ['accuracy', 'rhythm', None, ...]
        if experiment_type == 'exam':
            advance_trial = True
        else:
            advance_trial = not (check_rhythm and 'accuracy' in mistakes)
        passed = False
        _dump_data()
        prfl(
            dict(passed=passed, advance_trial=advance_trial,
                 mistakes=mistakes))